From 0626e6641f6b467447c81dd7678a69c66f7746cf Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 16 Mar 2021 13:07:11 +0900 Subject: [PATCH 001/417] cifsd: add server handler for central processing and tranport layers This adds server handler for central processing, transport layers(tcp, rdma, ipc) and a document describing cifsd architecture. Signed-off-by: Namjae Jeon Signed-off-by: Sergey Senozhatsky Signed-off-by: Hyunchul Lee Acked-by: Ronnie Sahlberg Signed-off-by: Steve French --- Documentation/filesystems/cifs/cifsd.rst | 136 ++ fs/cifsd/connection.c | 416 +++++ fs/cifsd/connection.h | 212 +++ fs/cifsd/glob.h | 67 + fs/cifsd/ksmbd_server.h | 285 +++ fs/cifsd/ksmbd_work.c | 93 + fs/cifsd/ksmbd_work.h | 124 ++ fs/cifsd/server.c | 635 +++++++ fs/cifsd/server.h | 62 + fs/cifsd/transport_ipc.c | 900 ++++++++++ fs/cifsd/transport_ipc.h | 63 + fs/cifsd/transport_rdma.c | 2050 ++++++++++++++++++++++ fs/cifsd/transport_rdma.h | 61 + fs/cifsd/transport_tcp.c | 624 +++++++ fs/cifsd/transport_tcp.h | 13 + 15 files changed, 5741 insertions(+) create mode 100644 Documentation/filesystems/cifs/cifsd.rst create mode 100644 fs/cifsd/connection.c create mode 100644 fs/cifsd/connection.h create mode 100644 fs/cifsd/glob.h create mode 100644 fs/cifsd/ksmbd_server.h create mode 100644 fs/cifsd/ksmbd_work.c create mode 100644 fs/cifsd/ksmbd_work.h create mode 100644 fs/cifsd/server.c create mode 100644 fs/cifsd/server.h create mode 100644 fs/cifsd/transport_ipc.c create mode 100644 fs/cifsd/transport_ipc.h create mode 100644 fs/cifsd/transport_rdma.c create mode 100644 fs/cifsd/transport_rdma.h create mode 100644 fs/cifsd/transport_tcp.c create mode 100644 fs/cifsd/transport_tcp.h diff --git a/Documentation/filesystems/cifs/cifsd.rst b/Documentation/filesystems/cifs/cifsd.rst new file mode 100644 index 000000000000..e0c33d03f290 --- /dev/null +++ b/Documentation/filesystems/cifs/cifsd.rst @@ -0,0 +1,136 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================= +CIFSD - SMB3 Kernel Server +========================= + +CIFSD is a linux kernel server which implements SMB3 protocol in kernel space +for sharing files over network. + +CIFSD architecture +================== + +The subset of performance related operations belong in kernelspace and +the other subset which belong to operations which are not really related with +performance in userspace. So, DCE/RPC management that has historically resulted +into number of buffer overflow issues and dangerous security bugs and user +account management are implemented in user space as ksmbd.mountd. +File operations that are related with performance (open/read/write/close etc.) +in kernel space (ksmbd). This also allows for easier integration with VFS +interface for all file operations. + +ksmbd (kernel daemon) +--------------------- + +When the server daemon is started, It starts up a forker thread +(ksmbd/interface name) at initialization time and open a dedicated port 445 +for listening to SMB requests. Whenever new clients make request, Forker +thread will accept the client connection and fork a new thread for dedicated +communication channel between the client and the server. It allows for parallel +processing of SMB requests(commands) from clients as well as allowing for new +clients to make new connections. Each instance is named ksmbd/1~n(port number) +to indicate connected clients. Depending on the SMB request types, each new +thread can decide to pass through the commands to the user space (ksmbd.mountd), +currently DCE/RPC commands are identified to be handled through the user space. +To further utilize the linux kernel, it has been chosen to process the commands +as workitems and to be executed in the handlers of the ksmbd-io kworker threads. +It allows for multiplexing of the handlers as the kernel take care of initiating +extra worker threads if the load is increased and vice versa, if the load is +decreased it destroys the extra worker threads. So, after connection is +established with client. Dedicated ksmbd/1..n(port number) takes complete +ownership of receiving/parsing of SMB commands. Each received command is worked +in parallel i.e., There can be multiple clients commands which are worked in +parallel. After receiving each command a separated kernel workitem is prepared +for each command which is further queued to be handled by ksmbd-io kworkers. +So, each SMB workitem is queued to the kworkers. This allows the benefit of load +sharing to be managed optimally by the default kernel and optimizing client +performance by handling client commands in parallel. + +ksmbd.mountd (user space daemon) +-------------------------------- + +ksmbd.mountd is userspace process to, transfer user account and password that +are registered using ksmbd.adduser(part of utils for user space). Further it +allows sharing information parameters that parsed from smb.conf to ksmbd in +kernel. For the execution part it has a daemon which is continuously running +and connected to the kernel interface using netlink socket, it waits for the +requests(dcerpc and share/user info). It handles RPC calls (at a minimum few +dozen) that are most important for file server from NetShareEnum and +NetServerGetInfo. Complete DCE/RPC response is prepared from the user space +and passed over to the associated kernel thread for the client. + +Key Features +============ + +The supported features are: + * SMB3 protocols for basic file sharing + * Auto negotiation + * Compound requests + * Oplock/Lease + * Large MTU + * NTLM/NTLMv2 + * HMAC-SHA256 Signing + * Secure negotiate + * Signing Update + * Pre-authentication integrity(SMB 3.1.1) + * SMB3 encryption(CCM, GCM) + * SMB direct(RDMA) + * SMB3.1.1 POSIX extension support + * ACLs + * Kerberos + +The features that are planned or not supported: + * SMB3 Multi-channel + * Durable handle v1,v2 + * Persistent handles + * Directory lease + * SMB2 notify + +How to run +========== + +1. Download ksmbd-tools and compile them. + - https://github.com/cifsd-team/ksmbd-tools + +2. Create user/password for SMB share. + + # mkdir /etc/ksmbd/ + # ksmbd.adduser -a + +3. Create /etc/ksmbd/smb.conf file, add SMB share in smb.conf file + - Refer smb.conf.example and Documentation/configuration.txt + in ksmbd-tools + +4. Insert ksmbd.ko module + + # insmod ksmbd.ko + +5. Start ksmbd user space daemon + # ksmbd.mountd + +6. Access share from Windows or Linux using CIFS + +Shutdown CIFSD +============== + +1. kill user and kernel space daemon + # sudo ksmbd.control -s + +How to turn debug print on +========================== + +Each layer +/sys/class/ksmbd-control/debug + +1. Enable all component prints + # sudo ksmbd.control -d "all" + +2. Enable one of components(smb, auth, vfs, oplock, ipc, conn, rdma) + # sudo ksmbd.control -d "smb" + +3. Show what prints are enable. + # cat/sys/class/ksmbd-control/debug + [smb] auth vfs oplock ipc conn [rdma] + +4. Disable prints: + If you try the selected component once more, It is disabled without brackets. diff --git a/fs/cifsd/connection.c b/fs/cifsd/connection.c new file mode 100644 index 000000000000..d27553dee2ad --- /dev/null +++ b/fs/cifsd/connection.c @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "server.h" +#include "buffer_pool.h" +#include "smb_common.h" +#include "mgmt/ksmbd_ida.h" +#include "connection.h" +#include "transport_tcp.h" +#include "transport_rdma.h" + +static DEFINE_MUTEX(init_lock); + +static struct ksmbd_conn_ops default_conn_ops; + +static LIST_HEAD(conn_list); +static DEFINE_RWLOCK(conn_list_lock); + +/** + * ksmbd_conn_free() - free resources of the connection instance + * + * @conn: connection instance to be cleand up + * + * During the thread termination, the corresponding conn instance + * resources(sock/memory) are released and finally the conn object is freed. + */ +void ksmbd_conn_free(struct ksmbd_conn *conn) +{ + write_lock(&conn_list_lock); + list_del(&conn->conns_list); + write_unlock(&conn_list_lock); + + ksmbd_free_request(conn->request_buf); + ksmbd_ida_free(conn->async_ida); + kfree(conn->preauth_info); + kfree(conn); +} + +/** + * ksmbd_conn_alloc() - initialize a new connection instance + * + * Return: ksmbd_conn struct on success, otherwise NULL + */ +struct ksmbd_conn *ksmbd_conn_alloc(void) +{ + struct ksmbd_conn *conn; + + conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL); + if (!conn) + return NULL; + + conn->need_neg = true; + conn->status = KSMBD_SESS_NEW; + conn->local_nls = load_nls("utf8"); + if (!conn->local_nls) + conn->local_nls = load_nls_default(); + atomic_set(&conn->req_running, 0); + atomic_set(&conn->r_count, 0); + init_waitqueue_head(&conn->req_running_q); + INIT_LIST_HEAD(&conn->conns_list); + INIT_LIST_HEAD(&conn->sessions); + INIT_LIST_HEAD(&conn->requests); + INIT_LIST_HEAD(&conn->async_requests); + spin_lock_init(&conn->request_lock); + spin_lock_init(&conn->credits_lock); + conn->async_ida = ksmbd_ida_alloc(); + + write_lock(&conn_list_lock); + list_add(&conn->conns_list, &conn_list); + write_unlock(&conn_list_lock); + return conn; +} + +bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) +{ + struct ksmbd_conn *t; + bool ret = false; + + read_lock(&conn_list_lock); + list_for_each_entry(t, &conn_list, conns_list) { + if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) + continue; + + ret = true; + break; + } + read_unlock(&conn_list_lock); + return ret; +} + +void ksmbd_conn_enqueue_request(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct list_head *requests_queue = NULL; + + if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { + requests_queue = &conn->requests; + work->syncronous = true; + } + + if (requests_queue) { + atomic_inc(&conn->req_running); + spin_lock(&conn->request_lock); + list_add_tail(&work->request_entry, requests_queue); + spin_unlock(&conn->request_lock); + } +} + +int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + int ret = 1; + + if (list_empty(&work->request_entry) && + list_empty(&work->async_request_entry)) + return 0; + + atomic_dec(&conn->req_running); + spin_lock(&conn->request_lock); + if (!work->multiRsp) { + list_del_init(&work->request_entry); + if (work->syncronous == false) + list_del_init(&work->async_request_entry); + ret = 0; + } + spin_unlock(&conn->request_lock); + + wake_up_all(&conn->req_running_q); + return ret; +} + +static void ksmbd_conn_lock(struct ksmbd_conn *conn) +{ + mutex_lock(&conn->srv_mutex); +} + +static void ksmbd_conn_unlock(struct ksmbd_conn *conn) +{ + mutex_unlock(&conn->srv_mutex); +} + +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn) +{ + wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); +} + +int ksmbd_conn_write(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_hdr *rsp_hdr = RESPONSE_BUF(work); + size_t len = 0; + int sent; + struct kvec iov[3]; + int iov_idx = 0; + + ksmbd_conn_try_dequeue_request(work); + if (!rsp_hdr) { + ksmbd_err("NULL response header\n"); + return -EINVAL; + } + + if (HAS_TRANSFORM_BUF(work)) { + iov[iov_idx] = (struct kvec) { work->tr_buf, + sizeof(struct smb2_transform_hdr) }; + len += iov[iov_idx++].iov_len; + } + + if (HAS_AUX_PAYLOAD(work)) { + iov[iov_idx] = (struct kvec) { rsp_hdr, RESP_HDR_SIZE(work) }; + len += iov[iov_idx++].iov_len; + iov[iov_idx] = (struct kvec) { AUX_PAYLOAD(work), + AUX_PAYLOAD_SIZE(work) }; + len += iov[iov_idx++].iov_len; + } else { + if (HAS_TRANSFORM_BUF(work)) + iov[iov_idx].iov_len = RESP_HDR_SIZE(work); + else + iov[iov_idx].iov_len = get_rfc1002_len(rsp_hdr) + 4; + iov[iov_idx].iov_base = rsp_hdr; + len += iov[iov_idx++].iov_len; + } + + ksmbd_conn_lock(conn); + sent = conn->transport->ops->writev(conn->transport, &iov[0], + iov_idx, len, + work->need_invalidate_rkey, + work->remote_key); + ksmbd_conn_unlock(conn); + + if (sent < 0) { + ksmbd_err("Failed to send message: %d\n", sent); + return sent; + } + + return 0; +} + +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + u32 remote_key, u64 remote_offset, + u32 remote_len) +{ + int ret = -EINVAL; + + if (conn->transport->ops->rdma_read) + ret = conn->transport->ops->rdma_read(conn->transport, + buf, buflen, + remote_key, remote_offset, + remote_len); + return ret; +} + +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + u32 remote_key, u64 remote_offset, + u32 remote_len) +{ + int ret = -EINVAL; + + if (conn->transport->ops->rdma_write) + ret = conn->transport->ops->rdma_write(conn->transport, + buf, buflen, + remote_key, remote_offset, + remote_len); + return ret; +} + +bool ksmbd_conn_alive(struct ksmbd_conn *conn) +{ + if (!ksmbd_server_running()) + return false; + + if (conn->status == KSMBD_SESS_EXITING) + return false; + + if (kthread_should_stop()) + return false; + + if (atomic_read(&conn->stats.open_files_count) > 0) + return true; + + /* + * Stop current session if the time that get last request from client + * is bigger than deadtime user configured and openning file count is + * zero. + */ + if (server_conf.deadtime > 0 && + time_after(jiffies, conn->last_active + server_conf.deadtime)) { + ksmbd_debug(CONN, "No response from client in %lu minutes\n", + server_conf.deadtime / SMB_ECHO_INTERVAL); + return false; + } + return true; +} + +/** + * ksmbd_conn_handler_loop() - session thread to listen on new smb requests + * @p: connection instance + * + * One thread each per connection + * + * Return: 0 on success + */ +int ksmbd_conn_handler_loop(void *p) +{ + struct ksmbd_conn *conn = (struct ksmbd_conn *)p; + struct ksmbd_transport *t = conn->transport; + unsigned int pdu_size; + char hdr_buf[4] = {0,}; + int size; + + mutex_init(&conn->srv_mutex); + __module_get(THIS_MODULE); + + if (t->ops->prepare && t->ops->prepare(t)) + goto out; + + conn->last_active = jiffies; + while (ksmbd_conn_alive(conn)) { + if (try_to_freeze()) + continue; + + ksmbd_free_request(conn->request_buf); + conn->request_buf = NULL; + + size = t->ops->read(t, hdr_buf, sizeof(hdr_buf)); + if (size != sizeof(hdr_buf)) + break; + + pdu_size = get_rfc1002_len(hdr_buf); + ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size); + + /* make sure we have enough to get to SMB header end */ + if (!ksmbd_pdu_size_has_room(pdu_size)) { + ksmbd_debug(CONN, "SMB request too short (%u bytes)\n", + pdu_size); + continue; + } + + /* 4 for rfc1002 length field */ + size = pdu_size + 4; + conn->request_buf = ksmbd_alloc_request(size); + if (!conn->request_buf) + continue; + + memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); + if (!ksmbd_smb_request(conn)) + break; + + /* + * We already read 4 bytes to find out PDU size, now + * read in PDU + */ + size = t->ops->read(t, conn->request_buf + 4, pdu_size); + if (size < 0) { + ksmbd_err("sock_read failed: %d\n", size); + break; + } + + if (size != pdu_size) { + ksmbd_err("PDU error. Read: %d, Expected: %d\n", + size, + pdu_size); + continue; + } + + if (!default_conn_ops.process_fn) { + ksmbd_err("No connection request callback\n"); + break; + } + + if (default_conn_ops.process_fn(conn)) { + ksmbd_err("Cannot handle request\n"); + break; + } + } + +out: + /* Wait till all reference dropped to the Server object*/ + while (atomic_read(&conn->r_count) > 0) + schedule_timeout(HZ); + + unload_nls(conn->local_nls); + if (default_conn_ops.terminate_fn) + default_conn_ops.terminate_fn(conn); + t->ops->disconnect(t); + module_put(THIS_MODULE); + return 0; +} + +void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops) +{ + default_conn_ops.process_fn = ops->process_fn; + default_conn_ops.terminate_fn = ops->terminate_fn; +} + +int ksmbd_conn_transport_init(void) +{ + int ret; + + mutex_lock(&init_lock); + ret = ksmbd_tcp_init(); + if (ret) { + pr_err("Failed to init TCP subsystem: %d\n", ret); + goto out; + } + + ret = ksmbd_rdma_init(); + if (ret) { + pr_err("Failed to init KSMBD subsystem: %d\n", ret); + goto out; + } +out: + mutex_unlock(&init_lock); + return ret; +} + +static void stop_sessions(void) +{ + struct ksmbd_conn *conn; + +again: + read_lock(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + struct task_struct *task; + + task = conn->transport->handler; + if (task) + ksmbd_debug(CONN, "Stop session handler %s/%d\n", + task->comm, + task_pid_nr(task)); + conn->status = KSMBD_SESS_EXITING; + } + read_unlock(&conn_list_lock); + + if (!list_empty(&conn_list)) { + schedule_timeout_interruptible(HZ/10); /* 100ms */ + goto again; + } +} + +void ksmbd_conn_transport_destroy(void) +{ + mutex_lock(&init_lock); + ksmbd_tcp_destroy(); + ksmbd_rdma_destroy(); + stop_sessions(); + mutex_unlock(&init_lock); +} diff --git a/fs/cifsd/connection.h b/fs/cifsd/connection.h new file mode 100644 index 000000000000..179fb9278999 --- /dev/null +++ b/fs/cifsd/connection.h @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_CONNECTION_H__ +#define __KSMBD_CONNECTION_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smb_common.h" +#include "ksmbd_work.h" + +#define KSMBD_SOCKET_BACKLOG 16 + +/* + * WARNING + * + * This is nothing but a HACK. Session status should move to channel + * or to session. As of now we have 1 tcp_conn : 1 ksmbd_session, but + * we need to change it to 1 tcp_conn : N ksmbd_sessions. + */ +enum { + KSMBD_SESS_NEW = 0, + KSMBD_SESS_GOOD, + KSMBD_SESS_EXITING, + KSMBD_SESS_NEED_RECONNECT, + KSMBD_SESS_NEED_NEGOTIATE +}; + +struct ksmbd_stats { + atomic_t open_files_count; + atomic64_t request_served; +}; + +struct ksmbd_transport; + +struct ksmbd_conn { + struct smb_version_values *vals; + struct smb_version_ops *ops; + struct smb_version_cmds *cmds; + unsigned int max_cmds; + struct mutex srv_mutex; + int status; + unsigned int cli_cap; + char *request_buf; + struct ksmbd_transport *transport; + struct nls_table *local_nls; + struct list_head conns_list; + /* smb session 1 per user */ + struct list_head sessions; + unsigned long last_active; + /* How many request are running currently */ + atomic_t req_running; + /* References which are made for this Server object*/ + atomic_t r_count; + unsigned short total_credits; + unsigned short max_credits; + spinlock_t credits_lock; + wait_queue_head_t req_running_q; + /* Lock to protect requests list*/ + spinlock_t request_lock; + struct list_head requests; + struct list_head async_requests; + int connection_type; + struct ksmbd_stats stats; + char ClientGUID[SMB2_CLIENT_GUID_SIZE]; + union { + /* pending trans request table */ + struct trans_state *recent_trans; + /* Used by ntlmssp */ + char *ntlmssp_cryptkey; + }; + + struct preauth_integrity_info *preauth_info; + + bool need_neg; + unsigned int auth_mechs; + unsigned int preferred_auth_mech; + bool sign; + bool use_spnego:1; + __u16 cli_sec_mode; + __u16 srv_sec_mode; + /* dialect index that server chose */ + __u16 dialect; + + char *mechToken; + + struct ksmbd_conn_ops *conn_ops; + + /* Preauth Session Table */ + struct list_head preauth_sess_table; + + struct sockaddr_storage peer_addr; + + /* Identifier for async message */ + struct ksmbd_ida *async_ida; + + __le16 cipher_type; + __le16 compress_algorithm; + bool posix_ext_supported; +}; + +struct ksmbd_conn_ops { + int (*process_fn)(struct ksmbd_conn *conn); + int (*terminate_fn)(struct ksmbd_conn *conn); +}; + +struct ksmbd_transport_ops { + int (*prepare)(struct ksmbd_transport *t); + void (*disconnect)(struct ksmbd_transport *t); + int (*read)(struct ksmbd_transport *t, + char *buf, unsigned int size); + int (*writev)(struct ksmbd_transport *t, + struct kvec *iovs, int niov, int size, + bool need_invalidate_rkey, unsigned int remote_key); + int (*rdma_read)(struct ksmbd_transport *t, + void *buf, unsigned int len, u32 remote_key, + u64 remote_offset, u32 remote_len); + int (*rdma_write)(struct ksmbd_transport *t, + void *buf, unsigned int len, u32 remote_key, + u64 remote_offset, u32 remote_len); +}; + +struct ksmbd_transport { + struct ksmbd_conn *conn; + struct ksmbd_transport_ops *ops; + struct task_struct *handler; +}; + +#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ) +#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) +#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr)) + +bool ksmbd_conn_alive(struct ksmbd_conn *conn); +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn); + +struct ksmbd_conn *ksmbd_conn_alloc(void); +void ksmbd_conn_free(struct ksmbd_conn *conn); +bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); +int ksmbd_conn_write(struct ksmbd_work *work); +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + u32 remote_key, u64 remote_offset, + u32 remote_len); +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + u32 remote_key, u64 remote_offset, + u32 remote_len); + +void ksmbd_conn_enqueue_request(struct ksmbd_work *work); +int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); +void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); + +int ksmbd_conn_handler_loop(void *p); + +int ksmbd_conn_transport_init(void); +void ksmbd_conn_transport_destroy(void); + +/* + * WARNING + * + * This is a hack. We will move status to a proper place once we land + * a multi-sessions support. + */ +static inline bool ksmbd_conn_good(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_GOOD; +} + +static inline bool ksmbd_conn_need_negotiate(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_NEED_NEGOTIATE; +} + +static inline bool ksmbd_conn_need_reconnect(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_NEED_RECONNECT; +} + +static inline bool ksmbd_conn_exiting(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_EXITING; +} + +static inline void ksmbd_conn_set_good(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_GOOD; +} + +static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_NEED_NEGOTIATE; +} + +static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_NEED_RECONNECT; +} + +static inline void ksmbd_conn_set_exiting(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_EXITING; +} +#endif /* __CONNECTION_H__ */ diff --git a/fs/cifsd/glob.h b/fs/cifsd/glob.h new file mode 100644 index 000000000000..2dc3f603e837 --- /dev/null +++ b/fs/cifsd/glob.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_GLOB_H +#define __KSMBD_GLOB_H + +#include +#include + +#include "unicode.h" +#include "vfs_cache.h" +#include "smberr.h" + +#define KSMBD_VERSION "3.1.9" + +/* @FIXME clean up this code */ + +extern int ksmbd_debug_types; +extern int ksmbd_caseless_search; + +#define DATA_STREAM 1 +#define DIR_STREAM 2 + +#define KSMBD_DEBUG_SMB (1 << 0) +#define KSMBD_DEBUG_AUTH (1 << 1) +#define KSMBD_DEBUG_VFS (1 << 2) +#define KSMBD_DEBUG_OPLOCK (1 << 3) +#define KSMBD_DEBUG_IPC (1 << 4) +#define KSMBD_DEBUG_CONN (1 << 5) +#define KSMBD_DEBUG_RDMA (1 << 6) +#define KSMBD_DEBUG_ALL (KSMBD_DEBUG_SMB | KSMBD_DEBUG_AUTH | \ + KSMBD_DEBUG_VFS | KSMBD_DEBUG_OPLOCK | \ + KSMBD_DEBUG_IPC | KSMBD_DEBUG_CONN | \ + KSMBD_DEBUG_RDMA) + +#ifndef ksmbd_pr_fmt +#ifdef SUBMOD_NAME +#define ksmbd_pr_fmt(fmt) "ksmbd: " SUBMOD_NAME ": " fmt +#else +#define ksmbd_pr_fmt(fmt) "ksmbd: " fmt +#endif +#endif + +#define ksmbd_debug(type, fmt, ...) \ + do { \ + if (ksmbd_debug_types & KSMBD_DEBUG_##type) \ + pr_info(ksmbd_pr_fmt("%s:%d: " fmt), \ + __func__, \ + __LINE__, \ + ##__VA_ARGS__); \ + } while (0) + +#define ksmbd_info(fmt, ...) \ + pr_info(ksmbd_pr_fmt(fmt), ##__VA_ARGS__) + +#define ksmbd_err(fmt, ...) \ + pr_err(ksmbd_pr_fmt("%s:%d: " fmt), \ + __func__, \ + __LINE__, \ + ##__VA_ARGS__) + +#define UNICODE_LEN(x) ((x) * 2) + +#endif /* __KSMBD_GLOB_H */ diff --git a/fs/cifsd/ksmbd_server.h b/fs/cifsd/ksmbd_server.h new file mode 100644 index 000000000000..01eaf9ec4fde --- /dev/null +++ b/fs/cifsd/ksmbd_server.h @@ -0,0 +1,285 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * + * linux-ksmbd-devel@lists.sourceforge.net + */ + +#ifndef _LINUX_KSMBD_SERVER_H +#define _LINUX_KSMBD_SERVER_H + +#include + +#define KSMBD_GENL_NAME "SMBD_GENL" +#define KSMBD_GENL_VERSION 0x01 + +#ifndef ____ksmbd_align +#define ____ksmbd_align __aligned(4) +#endif + +#define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ 48 +#define KSMBD_REQ_MAX_HASH_SZ 18 +#define KSMBD_REQ_MAX_SHARE_NAME 64 + +struct ksmbd_heartbeat { + __u32 handle; +}; + +/* + * Global config flags. + */ +#define KSMBD_GLOBAL_FLAG_INVALID (0) +#define KSMBD_GLOBAL_FLAG_SMB2_LEASES (1 << 0) +#define KSMBD_GLOBAL_FLAG_CACHE_TBUF (1 << 1) +#define KSMBD_GLOBAL_FLAG_CACHE_RBUF (1 << 2) +#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION (1 << 3) +#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE (1 << 4) + +struct ksmbd_startup_request { + __u32 flags; + __s32 signing; + __s8 min_prot[16]; + __s8 max_prot[16]; + __s8 netbios_name[16]; + __s8 work_group[64]; + __s8 server_string[64]; + __u16 tcp_port; + __u16 ipc_timeout; + __u32 deadtime; + __u32 file_max; + __u32 smb2_max_write; + __u32 smb2_max_read; + __u32 smb2_max_trans; + __u32 share_fake_fscaps; + __u32 sub_auth[3]; + __u32 ifc_list_sz; + __s8 ____payload[0]; +} ____ksmbd_align; + +#define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) + +struct ksmbd_shutdown_request { + __s32 reserved; +} ____ksmbd_align; + +struct ksmbd_login_request { + __u32 handle; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; +} ____ksmbd_align; + +struct ksmbd_login_response { + __u32 handle; + __u32 gid; + __u32 uid; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; + __u16 status; + __u16 hash_sz; + __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; +} ____ksmbd_align; + +struct ksmbd_share_config_request { + __u32 handle; + __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; +} ____ksmbd_align; + +struct ksmbd_share_config_response { + __u32 handle; + __u32 flags; + __u16 create_mask; + __u16 directory_mask; + __u16 force_create_mode; + __u16 force_directory_mode; + __u16 force_uid; + __u16 force_gid; + __u32 veto_list_sz; + __s8 ____payload[0]; +} ____ksmbd_align; + +#define KSMBD_SHARE_CONFIG_VETO_LIST(s) ((s)->____payload) +#define KSMBD_SHARE_CONFIG_PATH(s) \ + ({ \ + char *p = (s)->____payload; \ + if ((s)->veto_list_sz) \ + p += (s)->veto_list_sz + 1; \ + p; \ + }) + +struct ksmbd_tree_connect_request { + __u32 handle; + __u16 account_flags; + __u16 flags; + __u64 session_id; + __u64 connect_id; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; + __s8 share[KSMBD_REQ_MAX_SHARE_NAME]; + __s8 peer_addr[64]; +} ____ksmbd_align; + +struct ksmbd_tree_connect_response { + __u32 handle; + __u16 status; + __u16 connection_flags; +} ____ksmbd_align; + +struct ksmbd_tree_disconnect_request { + __u64 session_id; + __u64 connect_id; +} ____ksmbd_align; + +struct ksmbd_logout_request { + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; +} ____ksmbd_align; + +struct ksmbd_rpc_command { + __u32 handle; + __u32 flags; + __u32 payload_sz; + __u8 payload[0]; +} ____ksmbd_align; + +struct ksmbd_spnego_authen_request { + __u32 handle; + __u16 spnego_blob_len; + __u8 spnego_blob[0]; +} ____ksmbd_align; + +struct ksmbd_spnego_authen_response { + __u32 handle; + struct ksmbd_login_response login_response; + __u16 session_key_len; + __u16 spnego_blob_len; + __u8 payload[0]; /* session key + AP_REP */ +} ____ksmbd_align; + +/* + * This also used as NETLINK attribute type value. + * + * NOTE: + * Response message type value should be equal to + * request message type value + 1. + */ +enum ksmbd_event { + KSMBD_EVENT_UNSPEC = 0, + KSMBD_EVENT_HEARTBEAT_REQUEST, + + KSMBD_EVENT_STARTING_UP, + KSMBD_EVENT_SHUTTING_DOWN, + + KSMBD_EVENT_LOGIN_REQUEST, + KSMBD_EVENT_LOGIN_RESPONSE = 5, + + KSMBD_EVENT_SHARE_CONFIG_REQUEST, + KSMBD_EVENT_SHARE_CONFIG_RESPONSE, + + KSMBD_EVENT_TREE_CONNECT_REQUEST, + KSMBD_EVENT_TREE_CONNECT_RESPONSE, + + KSMBD_EVENT_TREE_DISCONNECT_REQUEST = 10, + + KSMBD_EVENT_LOGOUT_REQUEST, + + KSMBD_EVENT_RPC_REQUEST, + KSMBD_EVENT_RPC_RESPONSE, + + KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, + KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, + + KSMBD_EVENT_MAX +}; + +enum KSMBD_TREE_CONN_STATUS { + KSMBD_TREE_CONN_STATUS_OK = 0, + KSMBD_TREE_CONN_STATUS_NOMEM, + KSMBD_TREE_CONN_STATUS_NO_SHARE, + KSMBD_TREE_CONN_STATUS_NO_USER, + KSMBD_TREE_CONN_STATUS_INVALID_USER, + KSMBD_TREE_CONN_STATUS_HOST_DENIED = 5, + KSMBD_TREE_CONN_STATUS_CONN_EXIST, + KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS, + KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS, + KSMBD_TREE_CONN_STATUS_ERROR, +}; + +/* + * User config flags. + */ +#define KSMBD_USER_FLAG_INVALID (0) +#define KSMBD_USER_FLAG_OK (1 << 0) +#define KSMBD_USER_FLAG_BAD_PASSWORD (1 << 1) +#define KSMBD_USER_FLAG_BAD_UID (1 << 2) +#define KSMBD_USER_FLAG_BAD_USER (1 << 3) +#define KSMBD_USER_FLAG_GUEST_ACCOUNT (1 << 4) + +/* + * Share config flags. + */ +#define KSMBD_SHARE_FLAG_INVALID (0) +#define KSMBD_SHARE_FLAG_AVAILABLE (1 << 0) +#define KSMBD_SHARE_FLAG_BROWSEABLE (1 << 1) +#define KSMBD_SHARE_FLAG_WRITEABLE (1 << 2) +#define KSMBD_SHARE_FLAG_READONLY (1 << 3) +#define KSMBD_SHARE_FLAG_GUEST_OK (1 << 4) +#define KSMBD_SHARE_FLAG_GUEST_ONLY (1 << 5) +#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS (1 << 6) +#define KSMBD_SHARE_FLAG_OPLOCKS (1 << 7) +#define KSMBD_SHARE_FLAG_PIPE (1 << 8) +#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES (1 << 9) +#define KSMBD_SHARE_FLAG_INHERIT_SMACK (1 << 10) +#define KSMBD_SHARE_FLAG_INHERIT_OWNER (1 << 11) +#define KSMBD_SHARE_FLAG_STREAMS (1 << 12) +#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS (1 << 13) +#define KSMBD_SHARE_FLAG_ACL_XATTR (1 << 14) + +/* + * Tree connect request flags. + */ +#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB1 (0) +#define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6 (1 << 0) +#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2 (1 << 1) + +/* + * Tree connect flags. + */ +#define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT (1 << 0) +#define KSMBD_TREE_CONN_FLAG_READ_ONLY (1 << 1) +#define KSMBD_TREE_CONN_FLAG_WRITABLE (1 << 2) +#define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT (1 << 3) + +/* + * RPC over IPC. + */ +#define KSMBD_RPC_METHOD_RETURN (1 << 0) +#define KSMBD_RPC_SRVSVC_METHOD_INVOKE (1 << 1) +#define KSMBD_RPC_SRVSVC_METHOD_RETURN ((1 << 1) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_WKSSVC_METHOD_INVOKE (1 << 2) +#define KSMBD_RPC_WKSSVC_METHOD_RETURN ((1 << 2) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_IOCTL_METHOD ((1 << 3) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_OPEN_METHOD (1 << 4) +#define KSMBD_RPC_WRITE_METHOD (1 << 5) +#define KSMBD_RPC_READ_METHOD ((1 << 6) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_CLOSE_METHOD (1 << 7) +#define KSMBD_RPC_RAP_METHOD ((1 << 8) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_RESTRICTED_CONTEXT (1 << 9) +#define KSMBD_RPC_SAMR_METHOD_INVOKE (1 << 10) +#define KSMBD_RPC_SAMR_METHOD_RETURN ((1 << 10) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_LSARPC_METHOD_INVOKE (1 << 11) +#define KSMBD_RPC_LSARPC_METHOD_RETURN ((1 << 11) | KSMBD_RPC_METHOD_RETURN) + +#define KSMBD_RPC_OK 0 +#define KSMBD_RPC_EBAD_FUNC 0x00000001 +#define KSMBD_RPC_EACCESS_DENIED 0x00000005 +#define KSMBD_RPC_EBAD_FID 0x00000006 +#define KSMBD_RPC_ENOMEM 0x00000008 +#define KSMBD_RPC_EBAD_DATA 0x0000000D +#define KSMBD_RPC_ENOTIMPLEMENTED 0x00000040 +#define KSMBD_RPC_EINVALID_PARAMETER 0x00000057 +#define KSMBD_RPC_EMORE_DATA 0x000000EA +#define KSMBD_RPC_EINVALID_LEVEL 0x0000007C +#define KSMBD_RPC_SOME_NOT_MAPPED 0x00000107 + +#define KSMBD_CONFIG_OPT_DISABLED 0 +#define KSMBD_CONFIG_OPT_ENABLED 1 +#define KSMBD_CONFIG_OPT_AUTO 2 +#define KSMBD_CONFIG_OPT_MANDATORY 3 + +#endif /* _LINUX_KSMBD_SERVER_H */ diff --git a/fs/cifsd/ksmbd_work.c b/fs/cifsd/ksmbd_work.c new file mode 100644 index 000000000000..8cd5dff0762d --- /dev/null +++ b/fs/cifsd/ksmbd_work.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "server.h" +#include "connection.h" +#include "ksmbd_work.h" +#include "buffer_pool.h" +#include "mgmt/ksmbd_ida.h" + +/* @FIXME */ +#include "ksmbd_server.h" + +static struct kmem_cache *work_cache; +static struct workqueue_struct *ksmbd_wq; + +struct ksmbd_work *ksmbd_alloc_work_struct(void) +{ + struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL); + + if (work) { + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + INIT_LIST_HEAD(&work->request_entry); + INIT_LIST_HEAD(&work->async_request_entry); + INIT_LIST_HEAD(&work->fp_entry); + INIT_LIST_HEAD(&work->interim_entry); + } + return work; +} + +void ksmbd_free_work_struct(struct ksmbd_work *work) +{ + WARN_ON(work->saved_cred != NULL); + if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_TBUF && + work->set_trans_buf) + ksmbd_release_buffer(RESPONSE_BUF(work)); + else + ksmbd_free_response(RESPONSE_BUF(work)); + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF && + work->set_read_buf) + ksmbd_release_buffer(AUX_PAYLOAD(work)); + else + ksmbd_free_response(AUX_PAYLOAD(work)); + + ksmbd_free_response(TRANSFORM_BUF(work)); + ksmbd_free_request(REQUEST_BUF(work)); + if (work->async_id) + ksmbd_release_id(work->conn->async_ida, work->async_id); + kmem_cache_free(work_cache, work); +} + +void ksmbd_work_pool_destroy(void) +{ + kmem_cache_destroy(work_cache); +} + +int ksmbd_work_pool_init(void) +{ + work_cache = kmem_cache_create("ksmbd_work_cache", + sizeof(struct ksmbd_work), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!work_cache) + return -ENOMEM; + return 0; +} + +int ksmbd_workqueue_init(void) +{ + ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0); + if (!ksmbd_wq) + return -ENOMEM; + return 0; +} + +void ksmbd_workqueue_destroy(void) +{ + flush_workqueue(ksmbd_wq); + destroy_workqueue(ksmbd_wq); + ksmbd_wq = NULL; +} + +bool ksmbd_queue_work(struct ksmbd_work *work) +{ + return queue_work(ksmbd_wq, &work->work); +} diff --git a/fs/cifsd/ksmbd_work.h b/fs/cifsd/ksmbd_work.h new file mode 100644 index 000000000000..405434d4c8ab --- /dev/null +++ b/fs/cifsd/ksmbd_work.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_WORK_H__ +#define __KSMBD_WORK_H__ + +#include +#include + +struct ksmbd_conn; +struct ksmbd_session; +struct ksmbd_tree_connect; + +enum { + KSMBD_WORK_ACTIVE = 0, + KSMBD_WORK_CANCELLED, + KSMBD_WORK_CLOSED, +}; + +/* one of these for every pending CIFS request at the connection */ +struct ksmbd_work { + /* Server corresponding to this mid */ + struct ksmbd_conn *conn; + struct ksmbd_session *sess; + struct ksmbd_tree_connect *tcon; + + /* Pointer to received SMB header */ + char *request_buf; + /* Response buffer */ + char *response_buf; + + /* Read data buffer */ + char *aux_payload_buf; + + /* Next cmd hdr in compound req buf*/ + int next_smb2_rcv_hdr_off; + /* Next cmd hdr in compound rsp buf*/ + int next_smb2_rsp_hdr_off; + + /* + * Current Local FID assigned compound response if SMB2 CREATE + * command is present in compound request + */ + unsigned int compound_fid; + unsigned int compound_pfid; + unsigned int compound_sid; + + const struct cred *saved_cred; + + /* Number of granted credits */ + unsigned int credits_granted; + + /* response smb header size */ + unsigned int resp_hdr_sz; + unsigned int response_sz; + /* Read data count */ + unsigned int aux_payload_sz; + + void *tr_buf; + + unsigned char state; + /* Multiple responses for one request e.g. SMB ECHO */ + bool multiRsp:1; + /* No response for cancelled request */ + bool send_no_response:1; + /* Request is encrypted */ + bool encrypted:1; + /* Is this SYNC or ASYNC ksmbd_work */ + bool syncronous:1; + bool need_invalidate_rkey:1; + bool set_trans_buf:1; + bool set_read_buf:1; + + unsigned int remote_key; + /* cancel works */ + int async_id; + void **cancel_argv; + void (*cancel_fn)(void **argv); + + struct work_struct work; + /* List head at conn->requests */ + struct list_head request_entry; + /* List head at conn->async_requests */ + struct list_head async_request_entry; + struct list_head fp_entry; + struct list_head interim_entry; +}; + +#define WORK_CANCELLED(w) ((w)->state == KSMBD_WORK_CANCELLED) +#define WORK_CLOSED(w) ((w)->state == KSMBD_WORK_CLOSED) +#define WORK_ACTIVE(w) ((w)->state == KSMBD_WORK_ACTIVE) + +#define RESPONSE_BUF(w) ((void *)(w)->response_buf) +#define REQUEST_BUF(w) ((void *)(w)->request_buf) + +#define RESPONSE_BUF_NEXT(w) \ + ((void *)((w)->response_buf + (w)->next_smb2_rsp_hdr_off)) +#define REQUEST_BUF_NEXT(w) \ + ((void *)((w)->request_buf + (w)->next_smb2_rcv_hdr_off)) + +#define RESPONSE_SZ(w) ((w)->response_sz) + +#define INIT_AUX_PAYLOAD(w) ((w)->aux_payload_buf = NULL) +#define HAS_AUX_PAYLOAD(w) ((w)->aux_payload_sz != 0) +#define AUX_PAYLOAD(w) ((void *)((w)->aux_payload_buf)) +#define AUX_PAYLOAD_SIZE(w) ((w)->aux_payload_sz) +#define RESP_HDR_SIZE(w) ((w)->resp_hdr_sz) + +#define HAS_TRANSFORM_BUF(w) ((w)->tr_buf != NULL) +#define TRANSFORM_BUF(w) ((void *)((w)->tr_buf)) + +struct ksmbd_work *ksmbd_alloc_work_struct(void); +void ksmbd_free_work_struct(struct ksmbd_work *work); + +void ksmbd_work_pool_destroy(void); +int ksmbd_work_pool_init(void); + +int ksmbd_workqueue_init(void); +void ksmbd_workqueue_destroy(void); +bool ksmbd_queue_work(struct ksmbd_work *work); + +#endif /* __KSMBD_WORK_H__ */ diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c new file mode 100644 index 000000000000..b9e114f8a5d2 --- /dev/null +++ b/fs/cifsd/server.c @@ -0,0 +1,635 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "oplock.h" +#include "misc.h" +#include +#include +#include +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "buffer_pool.h" +#include "connection.h" +#include "transport_ipc.h" +#include "mgmt/user_session.h" +#include "crypto_ctx.h" +#include "auth.h" + +int ksmbd_debug_types; + +struct ksmbd_server_config server_conf; + +enum SERVER_CTRL_TYPE { + SERVER_CTRL_TYPE_INIT, + SERVER_CTRL_TYPE_RESET, +}; + +struct server_ctrl_struct { + int type; + struct work_struct ctrl_work; +}; + +static DEFINE_MUTEX(ctrl_lock); + +static int ___server_conf_set(int idx, char *val) +{ + if (idx >= ARRAY_SIZE(server_conf.conf)) + return -EINVAL; + + if (!val || val[0] == 0x00) + return -EINVAL; + + kfree(server_conf.conf[idx]); + server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); + if (!server_conf.conf[idx]) + return -ENOMEM; + return 0; +} + +int ksmbd_set_netbios_name(char *v) +{ + return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v); +} + +int ksmbd_set_server_string(char *v) +{ + return ___server_conf_set(SERVER_CONF_SERVER_STRING, v); +} + +int ksmbd_set_work_group(char *v) +{ + return ___server_conf_set(SERVER_CONF_WORK_GROUP, v); +} + +char *ksmbd_netbios_name(void) +{ + return server_conf.conf[SERVER_CONF_NETBIOS_NAME]; +} + +char *ksmbd_server_string(void) +{ + return server_conf.conf[SERVER_CONF_SERVER_STRING]; +} + +char *ksmbd_work_group(void) +{ + return server_conf.conf[SERVER_CONF_WORK_GROUP]; +} + +/** + * check_conn_state() - check state of server thread connection + * @ksmbd_work: smb work containing server thread information + * + * Return: 0 on valid connection, otherwise 1 to reconnect + */ +static inline int check_conn_state(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr; + + if (ksmbd_conn_exiting(work) || ksmbd_conn_need_reconnect(work)) { + rsp_hdr = RESPONSE_BUF(work); + rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; + return 1; + } + return 0; +} + +/* @FIXME what a mess... god help. */ + +#define TCP_HANDLER_CONTINUE 0 +#define TCP_HANDLER_ABORT 1 + +static int __process_request(struct ksmbd_work *work, + struct ksmbd_conn *conn, + uint16_t *cmd) +{ + struct smb_version_cmds *cmds; + uint16_t command; + int ret; + + if (check_conn_state(work)) + return TCP_HANDLER_CONTINUE; + + if (ksmbd_verify_smb_message(work)) + return TCP_HANDLER_ABORT; + + command = conn->ops->get_cmd_val(work); + *cmd = command; + +andx_again: + if (command >= conn->max_cmds) { + conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return TCP_HANDLER_CONTINUE; + } + + cmds = &conn->cmds[command]; + if (!cmds->proc) { + ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); + conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); + return TCP_HANDLER_CONTINUE; + } + + if (work->sess && conn->ops->is_sign_req(work, command)) { + ret = conn->ops->check_sign_req(work); + if (!ret) { + conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); + return TCP_HANDLER_CONTINUE; + } + } + + ret = cmds->proc(work); + + if (ret < 0) + ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); + /* AndX commands - chained request can return positive values */ + else if (ret > 0) { + command = ret; + *cmd = command; + goto andx_again; + } + + if (work->send_no_response) + return TCP_HANDLER_ABORT; + return TCP_HANDLER_CONTINUE; +} + +static void __handle_ksmbd_work(struct ksmbd_work *work, + struct ksmbd_conn *conn) +{ + uint16_t command = 0; + int rc; + + if (conn->ops->allocate_rsp_buf(work)) + return; + + if (conn->ops->is_transform_hdr && + conn->ops->is_transform_hdr(REQUEST_BUF(work))) { + rc = conn->ops->decrypt_req(work); + if (rc < 0) { + conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); + goto send; + } + + work->encrypted = true; + } + + rc = conn->ops->init_rsp_hdr(work); + if (rc) { + /* either uid or tid is not correct */ + conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); + goto send; + } + + if (conn->ops->check_user_session) { + rc = conn->ops->check_user_session(work); + if (rc < 0) { + command = conn->ops->get_cmd_val(work); + conn->ops->set_rsp_status(work, + STATUS_USER_SESSION_DELETED); + goto send; + } else if (rc > 0) { + rc = conn->ops->get_ksmbd_tcon(work); + if (rc < 0) { + conn->ops->set_rsp_status(work, + STATUS_NETWORK_NAME_DELETED); + goto send; + } + } + } + + do { + rc = __process_request(work, conn, &command); + if (rc == TCP_HANDLER_ABORT) + break; + + /* + * Call smb2_set_rsp_credits() function to set number of credits + * granted in hdr of smb2 response. + */ + if (conn->ops->set_rsp_credits) { + spin_lock(&conn->credits_lock); + rc = conn->ops->set_rsp_credits(work); + spin_unlock(&conn->credits_lock); + if (rc < 0) { + conn->ops->set_rsp_status(work, + STATUS_INVALID_PARAMETER); + goto send; + } + } + + if (work->sess && (work->sess->sign || + smb3_11_final_sess_setup_resp(work) || + conn->ops->is_sign_req(work, command))) + conn->ops->set_sign_rsp(work); + } while (is_chained_smb2_message(work)); + + if (work->send_no_response) + return; + +send: + smb3_preauth_hash_rsp(work); + if (work->sess && work->sess->enc && work->encrypted && + conn->ops->encrypt_resp) { + rc = conn->ops->encrypt_resp(work); + if (rc < 0) { + conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); + goto send; + } + } + + ksmbd_conn_write(work); +} + +/** + * handle_ksmbd_work() - process pending smb work requests + * @ksmbd_work: smb work containing request command buffer + * + * called by kworker threads to processing remaining smb work requests + */ +static void handle_ksmbd_work(struct work_struct *wk) +{ + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + + atomic64_inc(&conn->stats.request_served); + + __handle_ksmbd_work(work, conn); + + ksmbd_conn_try_dequeue_request(work); + ksmbd_free_work_struct(work); + atomic_dec(&conn->r_count); +} + +/** + * queue_ksmbd_work() - queue a smb request to worker thread queue + * for proccessing smb command and sending response + * @conn: connection instance + * + * read remaining data from socket create and submit work. + */ +static int queue_ksmbd_work(struct ksmbd_conn *conn) +{ + struct ksmbd_work *work; + + work = ksmbd_alloc_work_struct(); + if (!work) { + ksmbd_err("allocation for work failed\n"); + return -ENOMEM; + } + + work->conn = conn; + work->request_buf = conn->request_buf; + conn->request_buf = NULL; + + if (ksmbd_init_smb_server(work)) { + ksmbd_free_work_struct(work); + return -EINVAL; + } + + ksmbd_conn_enqueue_request(work); + atomic_inc(&conn->r_count); + /* update activity on connection */ + conn->last_active = jiffies; + INIT_WORK(&work->work, handle_ksmbd_work); + ksmbd_queue_work(work); + return 0; +} + +static int ksmbd_server_process_request(struct ksmbd_conn *conn) +{ + return queue_ksmbd_work(conn); +} + +static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn) +{ + ksmbd_sessions_deregister(conn); + destroy_lease_table(conn); + return 0; +} + +static void ksmbd_server_tcp_callbacks_init(void) +{ + struct ksmbd_conn_ops ops; + + ops.process_fn = ksmbd_server_process_request; + ops.terminate_fn = ksmbd_server_terminate_conn; + + ksmbd_conn_init_server_callbacks(&ops); +} + +static void server_conf_free(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { + kfree(server_conf.conf[i]); + server_conf.conf[i] = NULL; + } +} + +static int server_conf_init(void) +{ + WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); + server_conf.enforced_signing = 0; + server_conf.min_protocol = ksmbd_min_protocol(); + server_conf.max_protocol = ksmbd_max_protocol(); + server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP; +#ifdef CONFIG_SMB_SERVER_KERBEROS5 + server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | + KSMBD_AUTH_MSKRB5; +#endif + return 0; +} + +static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) +{ + int ret; + + ret = ksmbd_conn_transport_init(); + if (ret) { + server_queue_ctrl_reset_work(); + return; + } + + WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); +} + +static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) +{ + ksmbd_ipc_soft_reset(); + ksmbd_conn_transport_destroy(); + server_conf_free(); + server_conf_init(); + WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); +} + +static void server_ctrl_handle_work(struct work_struct *work) +{ + struct server_ctrl_struct *ctrl; + + ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); + + mutex_lock(&ctrl_lock); + switch (ctrl->type) { + case SERVER_CTRL_TYPE_INIT: + server_ctrl_handle_init(ctrl); + break; + case SERVER_CTRL_TYPE_RESET: + server_ctrl_handle_reset(ctrl); + break; + default: + pr_err("Unknown server work type: %d\n", ctrl->type); + } + mutex_unlock(&ctrl_lock); + kfree(ctrl); + module_put(THIS_MODULE); +} + +static int __queue_ctrl_work(int type) +{ + struct server_ctrl_struct *ctrl; + + ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + __module_get(THIS_MODULE); + ctrl->type = type; + INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work); + queue_work(system_long_wq, &ctrl->ctrl_work); + return 0; +} + +int server_queue_ctrl_init_work(void) +{ + return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT); +} + +int server_queue_ctrl_reset_work(void) +{ + return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET); +} + +static ssize_t stats_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + /* + * Inc this each time you change stats output format, + * so user space will know what to do. + */ + static int stats_version = 2; + static const char * const state[] = { + "startup", + "running", + "reset", + "shutdown" + }; + + ssize_t sz = scnprintf(buf, + PAGE_SIZE, + "%d %s %d %lu\n", + stats_version, + state[server_conf.state], + server_conf.tcp_port, + server_conf.ipc_last_active / HZ); + return sz; +} + +static ssize_t kill_server_store(struct class *class, + struct class_attribute *attr, + const char *buf, + size_t len) +{ + if (!sysfs_streq(buf, "hard")) + return len; + + ksmbd_info("kill command received\n"); + mutex_lock(&ctrl_lock); + WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); + __module_get(THIS_MODULE); + server_ctrl_handle_reset(NULL); + module_put(THIS_MODULE); + mutex_unlock(&ctrl_lock); + return len; +} + +static const char * const debug_type_strings[] = {"smb", "auth", "vfs", + "oplock", "ipc", "conn", + "rdma"}; + +static ssize_t debug_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + ssize_t sz = 0; + int i, pos = 0; + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if ((ksmbd_debug_types >> i) & 1) { + pos = scnprintf(buf + sz, + PAGE_SIZE - sz, + "[%s] ", + debug_type_strings[i]); + } else { + pos = scnprintf(buf + sz, + PAGE_SIZE - sz, + "%s ", + debug_type_strings[i]); + } + sz += pos; + + } + sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); + return sz; +} + +static ssize_t debug_store(struct class *class, + struct class_attribute *attr, + const char *buf, + size_t len) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if (sysfs_streq(buf, "all")) { + if (ksmbd_debug_types == KSMBD_DEBUG_ALL) + ksmbd_debug_types = 0; + else + ksmbd_debug_types = KSMBD_DEBUG_ALL; + break; + } + + if (sysfs_streq(buf, debug_type_strings[i])) { + if (ksmbd_debug_types & (1 << i)) + ksmbd_debug_types &= ~(1 << i); + else + ksmbd_debug_types |= (1 << i); + break; + } + } + + return len; +} + +static CLASS_ATTR_RO(stats); +static CLASS_ATTR_WO(kill_server); +static CLASS_ATTR_RW(debug); + +static struct attribute *ksmbd_control_class_attrs[] = { + &class_attr_stats.attr, + &class_attr_kill_server.attr, + &class_attr_debug.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ksmbd_control_class); + +static struct class ksmbd_control_class = { + .name = "ksmbd-control", + .owner = THIS_MODULE, + .class_groups = ksmbd_control_class_groups, +}; + +static int ksmbd_server_shutdown(void) +{ + WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); + + class_unregister(&ksmbd_control_class); + ksmbd_workqueue_destroy(); + ksmbd_ipc_release(); + ksmbd_conn_transport_destroy(); + ksmbd_free_session_table(); + ksmbd_crypto_destroy(); + ksmbd_free_global_file_table(); + destroy_lease_table(NULL); + ksmbd_destroy_buffer_pools(); + server_conf_free(); + return 0; +} + +static int __init ksmbd_server_init(void) +{ + int ret; + + ret = class_register(&ksmbd_control_class); + if (ret) { + ksmbd_err("Unable to register ksmbd-control class\n"); + return ret; + } + + ksmbd_server_tcp_callbacks_init(); + + ret = server_conf_init(); + if (ret) + return ret; + + ret = ksmbd_init_buffer_pools(); + if (ret) + return ret; + + ret = ksmbd_init_session_table(); + if (ret) + goto error; + + ret = ksmbd_ipc_init(); + if (ret) + goto error; + + ret = ksmbd_init_global_file_table(); + if (ret) + goto error; + + ret = ksmbd_inode_hash_init(); + if (ret) + goto error; + + ret = ksmbd_crypto_create(); + if (ret) + goto error; + + ret = ksmbd_workqueue_init(); + if (ret) + goto error; + return 0; + +error: + ksmbd_server_shutdown(); + return ret; +} + +/** + * exit_smb_server() - shutdown forker thread and free memory at module exit + */ +static void __exit ksmbd_server_exit(void) +{ + ksmbd_server_shutdown(); + ksmbd_release_inode_hash(); +} + +MODULE_AUTHOR("Namjae Jeon "); +MODULE_VERSION(KSMBD_VERSION); +MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); +MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: arc4"); +MODULE_SOFTDEP("pre: ecb"); +MODULE_SOFTDEP("pre: hmac"); +MODULE_SOFTDEP("pre: md4"); +MODULE_SOFTDEP("pre: md5"); +MODULE_SOFTDEP("pre: nls"); +MODULE_SOFTDEP("pre: aes"); +MODULE_SOFTDEP("pre: cmac"); +MODULE_SOFTDEP("pre: sha256"); +MODULE_SOFTDEP("pre: sha512"); +MODULE_SOFTDEP("pre: aead2"); +MODULE_SOFTDEP("pre: ccm"); +MODULE_SOFTDEP("pre: gcm"); +module_init(ksmbd_server_init) +module_exit(ksmbd_server_exit) diff --git a/fs/cifsd/server.h b/fs/cifsd/server.h new file mode 100644 index 000000000000..7b2f6318fcff --- /dev/null +++ b/fs/cifsd/server.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SERVER_H__ +#define __SERVER_H__ + +#include "smbacl.h" + +#define SERVER_STATE_STARTING_UP 0 +#define SERVER_STATE_RUNNING 1 +#define SERVER_STATE_RESETTING 2 +#define SERVER_STATE_SHUTTING_DOWN 3 + +#define SERVER_CONF_NETBIOS_NAME 0 +#define SERVER_CONF_SERVER_STRING 1 +#define SERVER_CONF_WORK_GROUP 2 + +extern int ksmbd_debugging; + +struct ksmbd_server_config { + unsigned int flags; + unsigned int state; + short signing; + short enforced_signing; + short min_protocol; + short max_protocol; + unsigned short tcp_port; + unsigned short ipc_timeout; + unsigned long ipc_last_active; + unsigned long deadtime; + unsigned int share_fake_fscaps; + struct smb_sid domain_sid; + unsigned int auth_mechs; + + char *conf[SERVER_CONF_WORK_GROUP + 1]; +}; + +extern struct ksmbd_server_config server_conf; + +int ksmbd_set_netbios_name(char *v); +int ksmbd_set_server_string(char *v); +int ksmbd_set_work_group(char *v); + +char *ksmbd_netbios_name(void); +char *ksmbd_server_string(void); +char *ksmbd_work_group(void); + +static inline int ksmbd_server_running(void) +{ + return READ_ONCE(server_conf.state) == SERVER_STATE_RUNNING; +} + +static inline int ksmbd_server_configurable(void) +{ + return READ_ONCE(server_conf.state) < SERVER_STATE_RESETTING; +} + +int server_queue_ctrl_init_work(void); +int server_queue_ctrl_reset_work(void); +#endif /* __SERVER_H__ */ diff --git a/fs/cifsd/transport_ipc.c b/fs/cifsd/transport_ipc.c new file mode 100644 index 000000000000..b91fa265f85d --- /dev/null +++ b/fs/cifsd/transport_ipc.c @@ -0,0 +1,900 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vfs_cache.h" +#include "transport_ipc.h" +#include "buffer_pool.h" +#include "server.h" +#include "smb_common.h" + +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/user_session.h" +#include "mgmt/tree_connect.h" +#include "mgmt/ksmbd_ida.h" +#include "connection.h" +#include "transport_tcp.h" + +/* @FIXME fix this code */ +extern int get_protocol_idx(char *str); + +#define IPC_WAIT_TIMEOUT (2 * HZ) + +#define IPC_MSG_HASH_BITS 3 +static DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS); +static DECLARE_RWSEM(ipc_msg_table_lock); +static DEFINE_MUTEX(startup_lock); + +static struct ksmbd_ida *ida; + +static unsigned int ksmbd_tools_pid; + +#define KSMBD_IPC_MSG_HANDLE(m) (*(unsigned int *)m) + +static bool ksmbd_ipc_validate_version(struct genl_info *m) +{ + if (m->genlhdr->version != KSMBD_GENL_VERSION) { + ksmbd_err("%s. ksmbd: %d, kernel module: %d. %s.\n", + "Daemon and kernel module version mismatch", + m->genlhdr->version, + KSMBD_GENL_VERSION, + "User-space ksmbd should terminate"); + return false; + } + return true; +} + +struct ksmbd_ipc_msg { + unsigned int type; + unsigned int sz; + unsigned char ____payload[0]; +}; + +#define KSMBD_IPC_MSG_PAYLOAD(m) \ + ((void *)(((struct ksmbd_ipc_msg *)(m))->____payload)) + +struct ipc_msg_table_entry { + unsigned int handle; + unsigned int type; + wait_queue_head_t wait; + struct hlist_node ipc_table_hlist; + + void *response; +}; + +static struct delayed_work ipc_timer_work; + +static int handle_startup_event(struct sk_buff *skb, struct genl_info *info); +static int handle_unsupported_event(struct sk_buff *skb, + struct genl_info *info); +static int handle_generic_event(struct sk_buff *skb, struct genl_info *info); +static int ksmbd_ipc_heartbeat_request(void); + +static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = { + [KSMBD_EVENT_UNSPEC] = { + .len = 0, + }, + [KSMBD_EVENT_HEARTBEAT_REQUEST] = { + .len = sizeof(struct ksmbd_heartbeat), + }, + [KSMBD_EVENT_STARTING_UP] = { + .len = sizeof(struct ksmbd_startup_request), + }, + [KSMBD_EVENT_SHUTTING_DOWN] = { + .len = sizeof(struct ksmbd_shutdown_request), + }, + [KSMBD_EVENT_LOGIN_REQUEST] = { + .len = sizeof(struct ksmbd_login_request), + }, + [KSMBD_EVENT_LOGIN_RESPONSE] = { + .len = sizeof(struct ksmbd_login_response), + }, + [KSMBD_EVENT_SHARE_CONFIG_REQUEST] = { + .len = sizeof(struct ksmbd_share_config_request), + }, + [KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = { + .len = sizeof(struct ksmbd_share_config_response), + }, + [KSMBD_EVENT_TREE_CONNECT_REQUEST] = { + .len = sizeof(struct ksmbd_tree_connect_request), + }, + [KSMBD_EVENT_TREE_CONNECT_RESPONSE] = { + .len = sizeof(struct ksmbd_tree_connect_response), + }, + [KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = { + .len = sizeof(struct ksmbd_tree_disconnect_request), + }, + [KSMBD_EVENT_LOGOUT_REQUEST] = { + .len = sizeof(struct ksmbd_logout_request), + }, + [KSMBD_EVENT_RPC_REQUEST] = { + }, + [KSMBD_EVENT_RPC_RESPONSE] = { + }, + [KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = { + }, + [KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = { + }, +}; + +static struct genl_ops ksmbd_genl_ops[] = { + { + .cmd = KSMBD_EVENT_UNSPEC, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_HEARTBEAT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_STARTING_UP, + .doit = handle_startup_event, + }, + { + .cmd = KSMBD_EVENT_SHUTTING_DOWN, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGIN_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGIN_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_SHARE_CONFIG_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_SHARE_CONFIG_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_TREE_CONNECT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_TREE_CONNECT_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_TREE_DISCONNECT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGOUT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_RPC_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_RPC_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, + .doit = handle_generic_event, + }, +}; + +static struct genl_family ksmbd_genl_family = { + .name = KSMBD_GENL_NAME, + .version = KSMBD_GENL_VERSION, + .hdrsize = 0, + .maxattr = KSMBD_EVENT_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = ksmbd_genl_ops, + .n_ops = ARRAY_SIZE(ksmbd_genl_ops), +}; + +static void ksmbd_nl_init_fixup(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ksmbd_genl_ops); i++) + ksmbd_genl_ops[i].validate = GENL_DONT_VALIDATE_STRICT | + GENL_DONT_VALIDATE_DUMP; + + ksmbd_genl_family.policy = ksmbd_nl_policy; +} + +static int rpc_context_flags(struct ksmbd_session *sess) +{ + if (user_guest(sess->user)) + return KSMBD_RPC_RESTRICTED_CONTEXT; + return 0; +} + +static void ipc_update_last_active(void) +{ + if (server_conf.ipc_timeout) + server_conf.ipc_last_active = jiffies; +} + +static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) +{ + struct ksmbd_ipc_msg *msg; + size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); + + msg = ksmbd_alloc(msg_sz); + if (msg) + msg->sz = sz; + return msg; +} + +static void ipc_msg_free(struct ksmbd_ipc_msg *msg) +{ + ksmbd_free(msg); +} + +static void ipc_msg_handle_free(int handle) +{ + if (handle >= 0) + ksmbd_release_id(ida, handle); +} + +static int handle_response(int type, void *payload, size_t sz) +{ + int handle = KSMBD_IPC_MSG_HANDLE(payload); + struct ipc_msg_table_entry *entry; + int ret = 0; + + ipc_update_last_active(); + down_read(&ipc_msg_table_lock); + hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) { + if (handle != entry->handle) + continue; + + entry->response = NULL; + /* + * Response message type value should be equal to + * request message type + 1. + */ + if (entry->type + 1 != type) { + ksmbd_err("Waiting for IPC type %d, got %d. Ignore.\n", + entry->type + 1, type); + } + + entry->response = ksmbd_alloc(sz); + if (!entry->response) { + ret = -ENOMEM; + break; + } + + memcpy(entry->response, payload, sz); + wake_up_interruptible(&entry->wait); + ret = 0; + break; + } + up_read(&ipc_msg_table_lock); + + return ret; +} + +static int ipc_server_config_on_startup(struct ksmbd_startup_request *req) +{ + int ret; + + ksmbd_set_fd_limit(req->file_max); + server_conf.flags = req->flags; + server_conf.signing = req->signing; + server_conf.tcp_port = req->tcp_port; + server_conf.ipc_timeout = req->ipc_timeout * HZ; + server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL; + server_conf.share_fake_fscaps = req->share_fake_fscaps; + ksmbd_init_domain(req->sub_auth); + + if (req->smb2_max_read) + init_smb2_max_read_size(req->smb2_max_read); + if (req->smb2_max_write) + init_smb2_max_write_size(req->smb2_max_write); + if (req->smb2_max_trans) + init_smb2_max_trans_size(req->smb2_max_trans); + + ret = ksmbd_set_netbios_name(req->netbios_name); + ret |= ksmbd_set_server_string(req->server_string); + ret |= ksmbd_set_work_group(req->work_group); + ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req), + req->ifc_list_sz); + if (ret) { + ksmbd_err("Server configuration error: %s %s %s\n", + req->netbios_name, + req->server_string, + req->work_group); + return ret; + } + + if (req->min_prot[0]) { + ret = ksmbd_lookup_protocol_idx(req->min_prot); + if (ret >= 0) + server_conf.min_protocol = ret; + } + if (req->max_prot[0]) { + ret = ksmbd_lookup_protocol_idx(req->max_prot); + if (ret >= 0) + server_conf.max_protocol = ret; + } + + if (server_conf.ipc_timeout) + schedule_delayed_work(&ipc_timer_work, server_conf.ipc_timeout); + return 0; +} + +static int handle_startup_event(struct sk_buff *skb, struct genl_info *info) +{ + int ret = 0; + +#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN + if (!netlink_capable(skb, CAP_NET_ADMIN)) + return -EPERM; +#endif + + if (!ksmbd_ipc_validate_version(info)) + return -EINVAL; + + if (!info->attrs[KSMBD_EVENT_STARTING_UP]) + return -EINVAL; + + mutex_lock(&startup_lock); + if (!ksmbd_server_configurable()) { + mutex_unlock(&startup_lock); + ksmbd_err("Server reset is in progress, can't start daemon\n"); + return -EINVAL; + } + + if (ksmbd_tools_pid) { + if (ksmbd_ipc_heartbeat_request() == 0) { + ret = -EINVAL; + goto out; + } + + ksmbd_err("Reconnect to a new user space daemon\n"); + } else { + struct ksmbd_startup_request *req; + + req = nla_data(info->attrs[info->genlhdr->cmd]); + ret = ipc_server_config_on_startup(req); + if (ret) + goto out; + server_queue_ctrl_init_work(); + } + + ksmbd_tools_pid = info->snd_portid; + ipc_update_last_active(); + +out: + mutex_unlock(&startup_lock); + return ret; +} + +static int handle_unsupported_event(struct sk_buff *skb, + struct genl_info *info) +{ + ksmbd_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd); + return -EINVAL; +} + +static int handle_generic_event(struct sk_buff *skb, struct genl_info *info) +{ + void *payload; + int sz; + int type = info->genlhdr->cmd; + +#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN + if (!netlink_capable(skb, CAP_NET_ADMIN)) + return -EPERM; +#endif + + if (type >= KSMBD_EVENT_MAX) { + WARN_ON(1); + return -EINVAL; + } + + if (!ksmbd_ipc_validate_version(info)) + return -EINVAL; + + if (!info->attrs[type]) + return -EINVAL; + + payload = nla_data(info->attrs[info->genlhdr->cmd]); + sz = nla_len(info->attrs[info->genlhdr->cmd]); + return handle_response(type, payload, sz); +} + +static int ipc_msg_send(struct ksmbd_ipc_msg *msg) +{ + struct genlmsghdr *nlh; + struct sk_buff *skb; + int ret = -EINVAL; + + if (!ksmbd_tools_pid) + return ret; + + skb = genlmsg_new(msg->sz, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + nlh = genlmsg_put(skb, 0, 0, &ksmbd_genl_family, 0, msg->type); + if (!nlh) + goto out; + + ret = nla_put(skb, msg->type, msg->sz, KSMBD_IPC_MSG_PAYLOAD(msg)); + if (ret) { + genlmsg_cancel(skb, nlh); + goto out; + } + + genlmsg_end(skb, nlh); + ret = genlmsg_unicast(&init_net, skb, ksmbd_tools_pid); + if (!ret) + ipc_update_last_active(); + return ret; + +out: + nlmsg_free(skb); + return ret; +} + +static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, + unsigned int handle) +{ + struct ipc_msg_table_entry entry; + int ret; + + if ((int)handle < 0) + return NULL; + + entry.type = msg->type; + entry.response = NULL; + init_waitqueue_head(&entry.wait); + + down_write(&ipc_msg_table_lock); + entry.handle = handle; + hash_add(ipc_msg_table, &entry.ipc_table_hlist, entry.handle); + up_write(&ipc_msg_table_lock); + + ret = ipc_msg_send(msg); + if (ret) + goto out; + + ret = wait_event_interruptible_timeout(entry.wait, + entry.response != NULL, + IPC_WAIT_TIMEOUT); +out: + down_write(&ipc_msg_table_lock); + hash_del(&entry.ipc_table_hlist); + up_write(&ipc_msg_table_lock); + return entry.response; +} + +static int ksmbd_ipc_heartbeat_request(void) +{ + struct ksmbd_ipc_msg *msg; + int ret; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_heartbeat)); + if (!msg) + return -EINVAL; + + msg->type = KSMBD_EVENT_HEARTBEAT_REQUEST; + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_login_request *req; + struct ksmbd_login_response *resp; + + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_LOGIN_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + req->handle = ksmbd_acquire_id(ida); + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_spnego_authen_response * +ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_spnego_authen_request *req; + struct ksmbd_spnego_authen_response *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) + + blob_len + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + req->handle = ksmbd_acquire_id(ida); + req->spnego_blob_len = blob_len; + memcpy(req->spnego_blob, spnego_blob, blob_len); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_tree_connect_response * +ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_tree_connect_request *req; + struct ksmbd_tree_connect_response *resp; + + if (strlen(user_name(sess->user)) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return NULL; + + if (strlen(share->name) >= KSMBD_REQ_MAX_SHARE_NAME) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_connect_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + + req->handle = ksmbd_acquire_id(ida); + req->account_flags = sess->user->flags; + req->session_id = sess->id; + req->connect_id = tree_conn->id; + strscpy(req->account, user_name(sess->user), KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + strscpy(req->share, share->name, KSMBD_REQ_MAX_SHARE_NAME); + snprintf(req->peer_addr, sizeof(req->peer_addr), "%pIS", peer_addr); + + if (peer_addr->sa_family == AF_INET6) + req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_IPV6; + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) + req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_SMB2; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, + unsigned long long connect_id) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_tree_disconnect_request *req; + int ret; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_disconnect_request)); + if (!msg) + return -ENOMEM; + + msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + req->session_id = session_id; + req->connect_id = connect_id; + + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +int ksmbd_ipc_logout_request(const char *account) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_logout_request *req; + int ret; + + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return -EINVAL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_logout_request)); + if (!msg) + return -ENOMEM; + + msg->type = KSMBD_EVENT_LOGOUT_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +struct ksmbd_share_config_response * +ksmbd_ipc_share_config_request(const char *name) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_share_config_request *req; + struct ksmbd_share_config_response *resp; + + if (strlen(name) >= KSMBD_REQ_MAX_SHARE_NAME) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_share_config_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + req->handle = ksmbd_acquire_id(ida); + strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, + int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= KSMBD_RPC_OPEN_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, + int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= KSMBD_RPC_CLOSE_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, + int handle, + void *payload, + size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_WRITE_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, + int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_READ_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, + int handle, + void *payload, + size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_IOCTL_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, + void *payload, + size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = KSMBD_IPC_MSG_PAYLOAD(msg); + req->handle = ksmbd_acquire_id(ida); + req->flags = rpc_context_flags(sess); + req->flags |= KSMBD_RPC_RAP_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +static int __ipc_heartbeat(void) +{ + unsigned long delta; + + if (!ksmbd_server_running()) + return 0; + + if (time_after(jiffies, server_conf.ipc_last_active)) { + delta = (jiffies - server_conf.ipc_last_active); + } else { + ipc_update_last_active(); + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout); + return 0; + } + + if (delta < server_conf.ipc_timeout) { + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout - delta); + return 0; + } + + if (ksmbd_ipc_heartbeat_request() == 0) { + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout); + return 0; + } + + mutex_lock(&startup_lock); + WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); + server_conf.ipc_last_active = 0; + ksmbd_tools_pid = 0; + ksmbd_err("No IPC daemon response for %lus\n", delta / HZ); + mutex_unlock(&startup_lock); + return -EINVAL; +} + +static void ipc_timer_heartbeat(struct work_struct *w) +{ + if (__ipc_heartbeat()) + server_queue_ctrl_reset_work(); +} + +int ksmbd_ipc_id_alloc(void) +{ + return ksmbd_acquire_id(ida); +} + +void ksmbd_rpc_id_free(int handle) +{ + ksmbd_release_id(ida, handle); +} + +void ksmbd_ipc_release(void) +{ + cancel_delayed_work_sync(&ipc_timer_work); + ksmbd_ida_free(ida); + genl_unregister_family(&ksmbd_genl_family); +} + +void ksmbd_ipc_soft_reset(void) +{ + mutex_lock(&startup_lock); + ksmbd_tools_pid = 0; + cancel_delayed_work_sync(&ipc_timer_work); + mutex_unlock(&startup_lock); +} + +int ksmbd_ipc_init(void) +{ + int ret; + + ksmbd_nl_init_fixup(); + INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat); + + ret = genl_register_family(&ksmbd_genl_family); + if (ret) { + ksmbd_err("Failed to register KSMBD netlink interface %d\n", + ret); + return ret; + } + + ida = ksmbd_ida_alloc(); + if (!ida) + return -ENOMEM; + return 0; +} diff --git a/fs/cifsd/transport_ipc.h b/fs/cifsd/transport_ipc.h new file mode 100644 index 000000000000..68c003027811 --- /dev/null +++ b/fs/cifsd/transport_ipc.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_TRANSPORT_IPC_H__ +#define __KSMBD_TRANSPORT_IPC_H__ + +#include +#include "ksmbd_server.h" /* FIXME */ + +#define KSMBD_IPC_MAX_PAYLOAD 4096 + +struct ksmbd_login_response * +ksmbd_ipc_login_request(const char *account); + +struct ksmbd_session; +struct ksmbd_share_config; +struct ksmbd_tree_connect; +struct sockaddr; + +struct ksmbd_tree_connect_response * +ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr); + +int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, + unsigned long long connect_id); +int ksmbd_ipc_logout_request(const char *account); + +struct ksmbd_share_config_response * +ksmbd_ipc_share_config_request(const char *name); + +struct ksmbd_spnego_authen_response * +ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len); + +int ksmbd_ipc_id_alloc(void); +void ksmbd_rpc_id_free(int handle); + +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, + int handle); +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, + int handle); + +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, + int handle, + void *payload, + size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, + int handle); +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, + int handle, + void *payload, + size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, + void *payload, + size_t payload_sz); + +void ksmbd_ipc_release(void); +void ksmbd_ipc_soft_reset(void); +int ksmbd_ipc_init(void); +#endif /* __KSMBD_TRANSPORT_IPC_H__ */ diff --git a/fs/cifsd/transport_rdma.c b/fs/cifsd/transport_rdma.c new file mode 100644 index 000000000000..1698f7ed9c2f --- /dev/null +++ b/fs/cifsd/transport_rdma.c @@ -0,0 +1,2050 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2017, Microsoft Corporation. + * Copyright (C) 2018, LG Electronics. + * + * Author(s): Long Li , + * Hyunchul Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + */ + +#define SUBMOD_NAME "smb_direct" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "connection.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "buffer_pool.h" +#include "transport_rdma.h" + +#define SMB_DIRECT_PORT 5445 + +#define SMB_DIRECT_VERSION_LE cpu_to_le16(0x0100) + +/* SMB_DIRECT negotiation timeout in seconds */ +#define SMB_DIRECT_NEGOTIATE_TIMEOUT 120 + +#define SMB_DIRECT_MAX_SEND_SGES 8 +#define SMB_DIRECT_MAX_RECV_SGES 1 + +/* + * Default maximum number of RDMA read/write outstanding on this connection + * This value is possibly decreased during QP creation on hardware limit + */ +#define SMB_DIRECT_CM_INITIATOR_DEPTH 8 + +/* Maximum number of retries on data transfer operations */ +#define SMB_DIRECT_CM_RETRY 6 +/* No need to retry on Receiver Not Ready since SMB_DIRECT manages credits */ +#define SMB_DIRECT_CM_RNR_RETRY 0 + +/* + * User configurable initial values per SMB_DIRECT transport connection + * as defined in [MS-KSMBD] 3.1.1.1 + * Those may change after a SMB_DIRECT negotiation + */ +/* The local peer's maximum number of credits to grant to the peer */ +static int smb_direct_receive_credit_max = 255; + +/* The remote peer's credit request of local peer */ +static int smb_direct_send_credit_target = 255; + +/* The maximum single message size can be sent to remote peer */ +static int smb_direct_max_send_size = 8192; + +/* The maximum fragmented upper-layer payload receive size supported */ +static int smb_direct_max_fragmented_recv_size = 1024 * 1024; + +/* The maximum single-message size which can be received */ +static int smb_direct_max_receive_size = 8192; + +static int smb_direct_max_read_write_size = 1024 * 1024; + +static int smb_direct_max_outstanding_rw_ops = 8; + +static struct smb_direct_listener { + struct rdma_cm_id *cm_id; +} smb_direct_listener; + + +static struct workqueue_struct *smb_direct_wq; + +enum smb_direct_status { + SMB_DIRECT_CS_NEW = 0, + SMB_DIRECT_CS_CONNECTED, + SMB_DIRECT_CS_DISCONNECTING, + SMB_DIRECT_CS_DISCONNECTED, +}; + +struct smb_direct_transport { + struct ksmbd_transport transport; + + enum smb_direct_status status; + bool full_packet_received; + wait_queue_head_t wait_status; + + struct rdma_cm_id *cm_id; + struct ib_cq *send_cq; + struct ib_cq *recv_cq; + struct ib_pd *pd; + struct ib_qp *qp; + + int max_send_size; + int max_recv_size; + int max_fragmented_send_size; + int max_fragmented_recv_size; + int max_rdma_rw_size; + + spinlock_t reassembly_queue_lock; + struct list_head reassembly_queue; + int reassembly_data_length; + int reassembly_queue_length; + int first_entry_offset; + wait_queue_head_t wait_reassembly_queue; + + spinlock_t receive_credit_lock; + int recv_credits; + int count_avail_recvmsg; + int recv_credit_max; + int recv_credit_target; + + spinlock_t recvmsg_queue_lock; + struct list_head recvmsg_queue; + + spinlock_t empty_recvmsg_queue_lock; + struct list_head empty_recvmsg_queue; + + int send_credit_target; + atomic_t send_credits; + spinlock_t lock_new_recv_credits; + int new_recv_credits; + atomic_t rw_avail_ops; + + wait_queue_head_t wait_send_credits; + wait_queue_head_t wait_rw_avail_ops; + + mempool_t *sendmsg_mempool; + struct kmem_cache *sendmsg_cache; + mempool_t *recvmsg_mempool; + struct kmem_cache *recvmsg_cache; + + wait_queue_head_t wait_send_payload_pending; + atomic_t send_payload_pending; + wait_queue_head_t wait_send_pending; + atomic_t send_pending; + + struct delayed_work post_recv_credits_work; + struct work_struct send_immediate_work; + struct work_struct disconnect_work; + + bool negotiation_requested; +}; + +#define KSMBD_TRANS(t) ((struct ksmbd_transport *)&((t)->transport)) +#define SMB_DIRECT_TRANS(t) ((struct smb_direct_transport *)container_of(t, \ + struct smb_direct_transport, transport)) + +enum { + SMB_DIRECT_MSG_NEGOTIATE_REQ = 0, + SMB_DIRECT_MSG_DATA_TRANSFER +}; + +static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops; + +struct smb_direct_send_ctx { + struct list_head msg_list; + int wr_cnt; + bool need_invalidate_rkey; + unsigned int remote_key; +}; + +struct smb_direct_sendmsg { + struct smb_direct_transport *transport; + struct ib_send_wr wr; + struct list_head list; + int num_sge; + struct ib_sge sge[SMB_DIRECT_MAX_SEND_SGES]; + struct ib_cqe cqe; + u8 packet[]; +}; + +struct smb_direct_recvmsg { + struct smb_direct_transport *transport; + struct list_head list; + int type; + struct ib_sge sge; + struct ib_cqe cqe; + bool first_segment; + u8 packet[]; +}; + +struct smb_direct_rdma_rw_msg { + struct smb_direct_transport *t; + struct ib_cqe cqe; + struct completion *completion; + struct rdma_rw_ctx rw_ctx; + struct sg_table sgt; + struct scatterlist sg_list[0]; +}; + +#define BUFFER_NR_PAGES(buf, len) \ + (DIV_ROUND_UP((unsigned long)(buf) + (len), PAGE_SIZE) \ + - (unsigned long)(buf) / PAGE_SIZE) + +static void smb_direct_destroy_pools(struct smb_direct_transport *transport); +static void smb_direct_post_recv_credits(struct work_struct *work); +static int smb_direct_post_send_data(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, int remaining_data_length); + +static inline void +*smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg) +{ + return (void *)recvmsg->packet; +} + +static inline bool is_receive_credit_post_required(int receive_credits, + int avail_recvmsg_count) +{ + return receive_credits <= (smb_direct_receive_credit_max >> 3) && + avail_recvmsg_count >= (receive_credits >> 2); +} + +static struct +smb_direct_recvmsg *get_free_recvmsg(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg = NULL; + + spin_lock(&t->recvmsg_queue_lock); + if (!list_empty(&t->recvmsg_queue)) { + recvmsg = list_first_entry(&t->recvmsg_queue, + struct smb_direct_recvmsg, + list); + list_del(&recvmsg->list); + } + spin_unlock(&t->recvmsg_queue_lock); + return recvmsg; +} + +static void put_recvmsg(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + spin_lock(&t->recvmsg_queue_lock); + list_add(&recvmsg->list, &t->recvmsg_queue); + spin_unlock(&t->recvmsg_queue_lock); + +} + +static struct +smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg = NULL; + + spin_lock(&t->empty_recvmsg_queue_lock); + if (!list_empty(&t->empty_recvmsg_queue)) { + recvmsg = list_first_entry( + &t->empty_recvmsg_queue, + struct smb_direct_recvmsg, list); + list_del(&recvmsg->list); + } + spin_unlock(&t->empty_recvmsg_queue_lock); + return recvmsg; +} + +static void put_empty_recvmsg(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + spin_lock(&t->empty_recvmsg_queue_lock); + list_add_tail(&recvmsg->list, &t->empty_recvmsg_queue); + spin_unlock(&t->empty_recvmsg_queue_lock); +} + +static void enqueue_reassembly(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg, + int data_length) +{ + spin_lock(&t->reassembly_queue_lock); + list_add_tail(&recvmsg->list, &t->reassembly_queue); + t->reassembly_queue_length++; + /* + * Make sure reassembly_data_length is updated after list and + * reassembly_queue_length are updated. On the dequeue side + * reassembly_data_length is checked without a lock to determine + * if reassembly_queue_length and list is up to date + */ + virt_wmb(); + t->reassembly_data_length += data_length; + spin_unlock(&t->reassembly_queue_lock); + +} + +static struct smb_direct_recvmsg *get_first_reassembly( + struct smb_direct_transport *t) +{ + if (!list_empty(&t->reassembly_queue)) + return list_first_entry(&t->reassembly_queue, + struct smb_direct_recvmsg, list); + else + return NULL; +} + +static void smb_direct_disconnect_rdma_work(struct work_struct *work) +{ + struct smb_direct_transport *t = + container_of(work, struct smb_direct_transport, + disconnect_work); + + if (t->status == SMB_DIRECT_CS_CONNECTED) { + t->status = SMB_DIRECT_CS_DISCONNECTING; + rdma_disconnect(t->cm_id); + } +} + +static void +smb_direct_disconnect_rdma_connection(struct smb_direct_transport *t) +{ + queue_work(smb_direct_wq, &t->disconnect_work); +} + +static void smb_direct_send_immediate_work(struct work_struct *work) +{ + struct smb_direct_transport *t = container_of(work, + struct smb_direct_transport, send_immediate_work); + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return; + + smb_direct_post_send_data(t, NULL, NULL, 0, 0); +} + +static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id) +{ + struct smb_direct_transport *t; + struct ksmbd_conn *conn; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return NULL; + + t->cm_id = cm_id; + cm_id->context = t; + + t->status = SMB_DIRECT_CS_NEW; + init_waitqueue_head(&t->wait_status); + + spin_lock_init(&t->reassembly_queue_lock); + INIT_LIST_HEAD(&t->reassembly_queue); + t->reassembly_data_length = 0; + t->reassembly_queue_length = 0; + init_waitqueue_head(&t->wait_reassembly_queue); + init_waitqueue_head(&t->wait_send_credits); + init_waitqueue_head(&t->wait_rw_avail_ops); + + spin_lock_init(&t->receive_credit_lock); + spin_lock_init(&t->recvmsg_queue_lock); + INIT_LIST_HEAD(&t->recvmsg_queue); + + spin_lock_init(&t->empty_recvmsg_queue_lock); + INIT_LIST_HEAD(&t->empty_recvmsg_queue); + + init_waitqueue_head(&t->wait_send_payload_pending); + atomic_set(&t->send_payload_pending, 0); + init_waitqueue_head(&t->wait_send_pending); + atomic_set(&t->send_pending, 0); + + spin_lock_init(&t->lock_new_recv_credits); + + INIT_DELAYED_WORK(&t->post_recv_credits_work, + smb_direct_post_recv_credits); + INIT_WORK(&t->send_immediate_work, smb_direct_send_immediate_work); + INIT_WORK(&t->disconnect_work, smb_direct_disconnect_rdma_work); + + conn = ksmbd_conn_alloc(); + if (!conn) + goto err; + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_smb_direct_transport_ops; + return t; +err: + kfree(t); + return NULL; +} + +static void free_transport(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg; + + wake_up_interruptible(&t->wait_send_credits); + + ksmbd_debug(RDMA, "wait for all send posted to IB to finish\n"); + wait_event(t->wait_send_payload_pending, + atomic_read(&t->send_payload_pending) == 0); + wait_event(t->wait_send_pending, + atomic_read(&t->send_pending) == 0); + + cancel_work_sync(&t->disconnect_work); + cancel_delayed_work_sync(&t->post_recv_credits_work); + cancel_work_sync(&t->send_immediate_work); + + if (t->qp) { + ib_drain_qp(t->qp); + ib_destroy_qp(t->qp); + } + + ksmbd_debug(RDMA, "drain the reassembly queue\n"); + do { + spin_lock(&t->reassembly_queue_lock); + recvmsg = get_first_reassembly(t); + if (recvmsg) { + list_del(&recvmsg->list); + spin_unlock( + &t->reassembly_queue_lock); + put_recvmsg(t, recvmsg); + } else + spin_unlock(&t->reassembly_queue_lock); + } while (recvmsg); + t->reassembly_data_length = 0; + + if (t->send_cq) + ib_free_cq(t->send_cq); + if (t->recv_cq) + ib_free_cq(t->recv_cq); + if (t->pd) + ib_dealloc_pd(t->pd); + if (t->cm_id) + rdma_destroy_id(t->cm_id); + + smb_direct_destroy_pools(t); + ksmbd_conn_free(KSMBD_TRANS(t)->conn); + kfree(t); +} + +static struct smb_direct_sendmsg +*smb_direct_alloc_sendmsg(struct smb_direct_transport *t) +{ + struct smb_direct_sendmsg *msg; + + msg = mempool_alloc(t->sendmsg_mempool, GFP_KERNEL); + if (!msg) + return ERR_PTR(-ENOMEM); + msg->transport = t; + INIT_LIST_HEAD(&msg->list); + msg->num_sge = 0; + return msg; +} + +static void smb_direct_free_sendmsg(struct smb_direct_transport *t, + struct smb_direct_sendmsg *msg) +{ + int i; + + if (msg->num_sge > 0) { + ib_dma_unmap_single(t->cm_id->device, + msg->sge[0].addr, msg->sge[0].length, + DMA_TO_DEVICE); + for (i = 1; i < msg->num_sge; i++) + ib_dma_unmap_page(t->cm_id->device, + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); + } + mempool_free(msg, t->sendmsg_mempool); +} + +static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) +{ + switch (recvmsg->type) { + case SMB_DIRECT_MSG_DATA_TRANSFER: { + struct smb_direct_data_transfer *req = + (struct smb_direct_data_transfer *) recvmsg->packet; + struct smb2_hdr *hdr = (struct smb2_hdr *) (recvmsg->packet + + le32_to_cpu(req->data_offset) - 4); + ksmbd_debug(RDMA, + "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemaingDataLength: %u, SMB: %x, Command: %u\n", + le16_to_cpu(req->credits_granted), + le16_to_cpu(req->credits_requested), + req->data_length, req->remaining_data_length, + hdr->ProtocolId, hdr->Command); + break; + } + case SMB_DIRECT_MSG_NEGOTIATE_REQ: { + struct smb_direct_negotiate_req *req = + (struct smb_direct_negotiate_req *)recvmsg->packet; + ksmbd_debug(RDMA, + "MinVersion: %u, MaxVersion: %u, CreditRequested: %u, MaxSendSize: %u, MaxRecvSize: %u, MaxFragmentedSize: %u\n", + le16_to_cpu(req->min_version), + le16_to_cpu(req->max_version), + le16_to_cpu(req->credits_requested), + le32_to_cpu(req->preferred_send_size), + le32_to_cpu(req->max_receive_size), + le32_to_cpu(req->max_fragmented_size)); + if (le16_to_cpu(req->min_version) > 0x0100 || + le16_to_cpu(req->max_version) < 0x0100) + return -EOPNOTSUPP; + if (le16_to_cpu(req->credits_requested) <= 0 || + le32_to_cpu(req->max_receive_size) <= 128 || + le32_to_cpu(req->max_fragmented_size) <= + 128*1024) + return -ECONNABORTED; + + break; + } + default: + return -EINVAL; + } + return 0; +} + +static void recv_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_transport *t; + + recvmsg = container_of(wc->wr_cqe, struct smb_direct_recvmsg, cqe); + t = recvmsg->transport; + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { + if (wc->status != IB_WC_WR_FLUSH_ERR) { + ksmbd_err("Recv error. status='%s (%d)' opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + smb_direct_disconnect_rdma_connection(t); + } + put_empty_recvmsg(t, recvmsg); + return; + } + + ksmbd_debug(RDMA, "Recv completed. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + + ib_dma_sync_single_for_cpu(wc->qp->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + switch (recvmsg->type) { + case SMB_DIRECT_MSG_NEGOTIATE_REQ: + t->negotiation_requested = true; + t->full_packet_received = true; + wake_up_interruptible(&t->wait_status); + break; + case SMB_DIRECT_MSG_DATA_TRANSFER: { + struct smb_direct_data_transfer *data_transfer = + (struct smb_direct_data_transfer *)recvmsg->packet; + int data_length = le32_to_cpu(data_transfer->data_length); + int avail_recvmsg_count, receive_credits; + + if (data_length) { + if (t->full_packet_received) + recvmsg->first_segment = true; + + if (le32_to_cpu(data_transfer->remaining_data_length)) + t->full_packet_received = false; + else + t->full_packet_received = true; + + enqueue_reassembly(t, recvmsg, data_length); + wake_up_interruptible(&t->wait_reassembly_queue); + + spin_lock(&t->receive_credit_lock); + receive_credits = --(t->recv_credits); + avail_recvmsg_count = t->count_avail_recvmsg; + spin_unlock(&t->receive_credit_lock); + } else { + put_empty_recvmsg(t, recvmsg); + + spin_lock(&t->receive_credit_lock); + receive_credits = --(t->recv_credits); + avail_recvmsg_count = ++(t->count_avail_recvmsg); + spin_unlock(&t->receive_credit_lock); + } + + t->recv_credit_target = + le16_to_cpu(data_transfer->credits_requested); + atomic_add(le16_to_cpu(data_transfer->credits_granted), + &t->send_credits); + + if (le16_to_cpu(data_transfer->flags) & + SMB_DIRECT_RESPONSE_REQUESTED) + queue_work(smb_direct_wq, &t->send_immediate_work); + + if (atomic_read(&t->send_credits) > 0) + wake_up_interruptible(&t->wait_send_credits); + + if (is_receive_credit_post_required(receive_credits, + avail_recvmsg_count)) + mod_delayed_work(smb_direct_wq, + &t->post_recv_credits_work, 0); + break; + } + default: + break; + } +} + +static int smb_direct_post_recv(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + struct ib_recv_wr wr; + int ret; + + recvmsg->sge.addr = ib_dma_map_single(t->cm_id->device, + recvmsg->packet, t->max_recv_size, + DMA_FROM_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, recvmsg->sge.addr); + if (ret) + return ret; + recvmsg->sge.length = t->max_recv_size; + recvmsg->sge.lkey = t->pd->local_dma_lkey; + recvmsg->cqe.done = recv_done; + + wr.wr_cqe = &recvmsg->cqe; + wr.next = NULL; + wr.sg_list = &recvmsg->sge; + wr.num_sge = 1; + + ret = ib_post_recv(t->qp, &wr, NULL); + if (ret) { + ksmbd_err("Can't post recv: %d\n", ret); + ib_dma_unmap_single(t->cm_id->device, + recvmsg->sge.addr, recvmsg->sge.length, + DMA_FROM_DEVICE); + smb_direct_disconnect_rdma_connection(t); + return ret; + } + return ret; +} + +static int smb_direct_read(struct ksmbd_transport *t, char *buf, + unsigned int size) +{ + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_data_transfer *data_transfer; + int to_copy, to_read, data_read, offset; + u32 data_length, remaining_data_length, data_offset; + int rc; + struct smb_direct_transport *st = SMB_DIRECT_TRANS(t); + +again: + if (st->status != SMB_DIRECT_CS_CONNECTED) { + ksmbd_err("disconnected\n"); + return -ENOTCONN; + } + + /* + * No need to hold the reassembly queue lock all the time as we are + * the only one reading from the front of the queue. The transport + * may add more entries to the back of the queue at the same time + */ + if (st->reassembly_data_length >= size) { + int queue_length; + int queue_removed = 0; + + /* + * Need to make sure reassembly_data_length is read before + * reading reassembly_queue_length and calling + * get_first_reassembly. This call is lock free + * as we never read at the end of the queue which are being + * updated in SOFTIRQ as more data is received + */ + virt_rmb(); + queue_length = st->reassembly_queue_length; + data_read = 0; + to_read = size; + offset = st->first_entry_offset; + while (data_read < size) { + recvmsg = get_first_reassembly(st); + data_transfer = smb_direct_recvmsg_payload(recvmsg); + data_length = le32_to_cpu(data_transfer->data_length); + remaining_data_length = + le32_to_cpu( + data_transfer->remaining_data_length); + data_offset = le32_to_cpu(data_transfer->data_offset); + + /* + * The upper layer expects RFC1002 length at the + * beginning of the payload. Return it to indicate + * the total length of the packet. This minimize the + * change to upper layer packet processing logic. This + * will be eventually remove when an intermediate + * transport layer is added + */ + if (recvmsg->first_segment && size == 4) { + unsigned int rfc1002_len = + data_length + remaining_data_length; + *((__be32 *)buf) = cpu_to_be32(rfc1002_len); + data_read = 4; + recvmsg->first_segment = false; + ksmbd_debug(RDMA, + "returning rfc1002 length %d\n", + rfc1002_len); + goto read_rfc1002_done; + } + + to_copy = min_t(int, data_length - offset, to_read); + memcpy( + buf + data_read, + (char *)data_transfer + data_offset + offset, + to_copy); + + /* move on to the next buffer? */ + if (to_copy == data_length - offset) { + queue_length--; + /* + * No need to lock if we are not at the + * end of the queue + */ + if (queue_length) + list_del(&recvmsg->list); + else { + spin_lock_irq( + &st->reassembly_queue_lock); + list_del(&recvmsg->list); + spin_unlock_irq( + &st->reassembly_queue_lock); + } + queue_removed++; + put_recvmsg(st, recvmsg); + offset = 0; + } else + offset += to_copy; + + to_read -= to_copy; + data_read += to_copy; + } + + spin_lock_irq(&st->reassembly_queue_lock); + st->reassembly_data_length -= data_read; + st->reassembly_queue_length -= queue_removed; + spin_unlock_irq(&st->reassembly_queue_lock); + + spin_lock(&st->receive_credit_lock); + st->count_avail_recvmsg += queue_removed; + if (is_receive_credit_post_required(st->recv_credits, + st->count_avail_recvmsg)) { + spin_unlock(&st->receive_credit_lock); + mod_delayed_work(smb_direct_wq, + &st->post_recv_credits_work, 0); + } else + spin_unlock(&st->receive_credit_lock); + + st->first_entry_offset = offset; + ksmbd_debug(RDMA, + "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", + data_read, st->reassembly_data_length, + st->first_entry_offset); +read_rfc1002_done: + return data_read; + } + + ksmbd_debug(RDMA, "wait_event on more data\n"); + rc = wait_event_interruptible( + st->wait_reassembly_queue, + st->reassembly_data_length >= size || + st->status != SMB_DIRECT_CS_CONNECTED); + if (rc) + return -EINTR; + + goto again; +} + +static void smb_direct_post_recv_credits(struct work_struct *work) +{ + struct smb_direct_transport *t = container_of(work, + struct smb_direct_transport, post_recv_credits_work.work); + struct smb_direct_recvmsg *recvmsg; + int receive_credits, credits = 0; + int ret; + int use_free = 1; + + spin_lock(&t->receive_credit_lock); + receive_credits = t->recv_credits; + spin_unlock(&t->receive_credit_lock); + + if (receive_credits < t->recv_credit_target) { + while (true) { + if (use_free) + recvmsg = get_free_recvmsg(t); + else + recvmsg = get_empty_recvmsg(t); + if (!recvmsg) { + if (use_free) { + use_free = 0; + continue; + } else + break; + } + + recvmsg->type = SMB_DIRECT_MSG_DATA_TRANSFER; + recvmsg->first_segment = false; + + ret = smb_direct_post_recv(t, recvmsg); + if (ret) { + ksmbd_err("Can't post recv: %d\n", ret); + put_recvmsg(t, recvmsg); + break; + } + credits++; + } + } + + spin_lock(&t->receive_credit_lock); + t->recv_credits += credits; + t->count_avail_recvmsg -= credits; + spin_unlock(&t->receive_credit_lock); + + spin_lock(&t->lock_new_recv_credits); + t->new_recv_credits += credits; + spin_unlock(&t->lock_new_recv_credits); + + if (credits) + queue_work(smb_direct_wq, &t->send_immediate_work); +} + +static void send_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smb_direct_sendmsg *sendmsg, *sibling; + struct smb_direct_transport *t; + struct list_head *pos, *prev, *end; + + sendmsg = container_of(wc->wr_cqe, struct smb_direct_sendmsg, cqe); + t = sendmsg->transport; + + ksmbd_debug(RDMA, "Send completed. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { + ksmbd_err("Send error. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + smb_direct_disconnect_rdma_connection(t); + } + + if (sendmsg->num_sge > 1) { + if (atomic_dec_and_test(&t->send_payload_pending)) + wake_up(&t->wait_send_payload_pending); + } else { + if (atomic_dec_and_test(&t->send_pending)) + wake_up(&t->wait_send_pending); + } + + /* iterate and free the list of messages in reverse. the list's head + * is invalid. + */ + for (pos = &sendmsg->list, prev = pos->prev, end = sendmsg->list.next; + prev != end; pos = prev, prev = prev->prev) { + sibling = container_of(pos, struct smb_direct_sendmsg, list); + smb_direct_free_sendmsg(t, sibling); + } + + sibling = container_of(pos, struct smb_direct_sendmsg, list); + smb_direct_free_sendmsg(t, sibling); +} + +static int manage_credits_prior_sending(struct smb_direct_transport *t) +{ + int new_credits; + + spin_lock(&t->lock_new_recv_credits); + new_credits = t->new_recv_credits; + t->new_recv_credits = 0; + spin_unlock(&t->lock_new_recv_credits); + + return new_credits; +} + +static int smb_direct_post_send(struct smb_direct_transport *t, + struct ib_send_wr *wr) +{ + int ret; + + if (wr->num_sge > 1) + atomic_inc(&t->send_payload_pending); + else + atomic_inc(&t->send_pending); + + ret = ib_post_send(t->qp, wr, NULL); + if (ret) { + ksmbd_err("failed to post send: %d\n", ret); + if (wr->num_sge > 1) { + if (atomic_dec_and_test(&t->send_payload_pending)) + wake_up(&t->wait_send_payload_pending); + } else { + if (atomic_dec_and_test(&t->send_pending)) + wake_up(&t->wait_send_pending); + } + smb_direct_disconnect_rdma_connection(t); + } + return ret; +} + +static void smb_direct_send_ctx_init(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + bool need_invalidate_rkey, unsigned int remote_key) +{ + INIT_LIST_HEAD(&send_ctx->msg_list); + send_ctx->wr_cnt = 0; + send_ctx->need_invalidate_rkey = need_invalidate_rkey; + send_ctx->remote_key = remote_key; +} + +static int smb_direct_flush_send_list(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, bool is_last) +{ + struct smb_direct_sendmsg *first, *last; + int ret; + + if (list_empty(&send_ctx->msg_list)) + return 0; + + first = list_first_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + last = list_last_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + + last->wr.send_flags = IB_SEND_SIGNALED; + last->wr.wr_cqe = &last->cqe; + if (is_last && send_ctx->need_invalidate_rkey) { + last->wr.opcode = IB_WR_SEND_WITH_INV; + last->wr.ex.invalidate_rkey = send_ctx->remote_key; + } + + ret = smb_direct_post_send(t, &first->wr); + if (!ret) { + smb_direct_send_ctx_init(t, send_ctx, + send_ctx->need_invalidate_rkey, send_ctx->remote_key); + } else { + atomic_add(send_ctx->wr_cnt, &t->send_credits); + wake_up(&t->wait_send_credits); + list_for_each_entry_safe(first, last, &send_ctx->msg_list, + list) { + smb_direct_free_sendmsg(t, first); + } + } + return ret; +} + +static int wait_for_credits(struct smb_direct_transport *t, + wait_queue_head_t *waitq, atomic_t *credits) +{ + int ret; + + do { + if (atomic_dec_return(credits) >= 0) + return 0; + + atomic_inc(credits); + ret = wait_event_interruptible(*waitq, + atomic_read(credits) > 0 || + t->status != SMB_DIRECT_CS_CONNECTED); + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + else if (ret < 0) + return ret; + } while (true); +} + +static int wait_for_send_credits(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx) +{ + int ret; + + if (send_ctx && (send_ctx->wr_cnt >= 16 || + atomic_read(&t->send_credits) <= 1)) { + ret = smb_direct_flush_send_list(t, send_ctx, false); + if (ret) + return ret; + } + + return wait_for_credits(t, &t->wait_send_credits, &t->send_credits); +} + +static int smb_direct_create_header(struct smb_direct_transport *t, + int size, int remaining_data_length, + struct smb_direct_sendmsg **sendmsg_out) +{ + struct smb_direct_sendmsg *sendmsg; + struct smb_direct_data_transfer *packet; + int header_length; + int ret; + + sendmsg = smb_direct_alloc_sendmsg(t); + if (!sendmsg) + return -ENOMEM; + + /* Fill in the packet header */ + packet = (struct smb_direct_data_transfer *)sendmsg->packet; + packet->credits_requested = cpu_to_le16(t->send_credit_target); + packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); + + packet->flags = 0; + packet->reserved = 0; + if (!size) + packet->data_offset = 0; + else + packet->data_offset = cpu_to_le32(24); + packet->data_length = cpu_to_le32(size); + packet->remaining_data_length = cpu_to_le32(remaining_data_length); + packet->padding = 0; + + ksmbd_debug(RDMA, + "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", + le16_to_cpu(packet->credits_requested), + le16_to_cpu(packet->credits_granted), + le32_to_cpu(packet->data_offset), + le32_to_cpu(packet->data_length), + le32_to_cpu(packet->remaining_data_length)); + + /* Map the packet to DMA */ + header_length = sizeof(struct smb_direct_data_transfer); + /* If this is a packet without payload, don't send padding */ + if (!size) + header_length = + offsetof(struct smb_direct_data_transfer, padding); + + sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + (void *)packet, + header_length, + DMA_TO_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + sendmsg->num_sge = 1; + sendmsg->sge[0].length = header_length; + sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + + *sendmsg_out = sendmsg; + return 0; +} + +static int get_sg_list(void *buf, int size, + struct scatterlist *sg_list, int nentries) +{ + bool high = is_vmalloc_addr(buf); + struct page *page; + int offset, len; + int i = 0; + + if (nentries < BUFFER_NR_PAGES(buf, size)) + return -EINVAL; + + offset = offset_in_page(buf); + buf -= offset; + while (size > 0) { + len = min_t(int, PAGE_SIZE - offset, size); + if (high) + page = vmalloc_to_page(buf); + else + page = kmap_to_page(buf); + + if (!sg_list) + return -EINVAL; + sg_set_page(sg_list, page, len, offset); + sg_list = sg_next(sg_list); + + buf += PAGE_SIZE; + size -= len; + offset = 0; + i++; + } + return i; +} + +static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, + struct scatterlist *sg_list, int nentries, + enum dma_data_direction dir) +{ + int npages; + + npages = get_sg_list(buf, size, sg_list, nentries); + if (npages <= 0) + return -EINVAL; + return ib_dma_map_sg(device, sg_list, npages, dir); +} + +static int post_sendmsg(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct smb_direct_sendmsg *msg) +{ + int i; + + for (i = 0; i < msg->num_sge; i++) + ib_dma_sync_single_for_device(t->cm_id->device, + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); + + msg->cqe.done = send_done; + msg->wr.opcode = IB_WR_SEND; + msg->wr.sg_list = &msg->sge[0]; + msg->wr.num_sge = msg->num_sge; + msg->wr.next = NULL; + + if (send_ctx) { + msg->wr.wr_cqe = NULL; + msg->wr.send_flags = 0; + if (!list_empty(&send_ctx->msg_list)) { + struct smb_direct_sendmsg *last; + + last = list_last_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + last->wr.next = &msg->wr; + } + list_add_tail(&msg->list, &send_ctx->msg_list); + send_ctx->wr_cnt++; + return 0; + } + + msg->wr.wr_cqe = &msg->cqe; + msg->wr.send_flags = IB_SEND_SIGNALED; + return smb_direct_post_send(t, &msg->wr); +} + +static int smb_direct_post_send_data(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, int remaining_data_length) +{ + int i, j, ret; + struct smb_direct_sendmsg *msg; + int data_length; + struct scatterlist sg[SMB_DIRECT_MAX_SEND_SGES-1]; + + ret = wait_for_send_credits(t, send_ctx); + if (ret) + return ret; + + data_length = 0; + for (i = 0; i < niov; i++) + data_length += iov[i].iov_len; + + ret = smb_direct_create_header(t, data_length, remaining_data_length, + &msg); + if (ret) { + atomic_inc(&t->send_credits); + return ret; + } + + for (i = 0; i < niov; i++) { + struct ib_sge *sge; + int sg_cnt; + + sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES-1); + sg_cnt = get_mapped_sg_list(t->cm_id->device, + iov[i].iov_base, iov[i].iov_len, + sg, SMB_DIRECT_MAX_SEND_SGES-1, DMA_TO_DEVICE); + if (sg_cnt <= 0) { + ksmbd_err("failed to map buffer\n"); + goto err; + } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES-1) { + ksmbd_err("buffer not fitted into sges\n"); + ret = -E2BIG; + ib_dma_unmap_sg(t->cm_id->device, sg, sg_cnt, + DMA_TO_DEVICE); + goto err; + } + + for (j = 0; j < sg_cnt; j++) { + sge = &msg->sge[msg->num_sge]; + sge->addr = sg_dma_address(&sg[j]); + sge->length = sg_dma_len(&sg[j]); + sge->lkey = t->pd->local_dma_lkey; + msg->num_sge++; + } + } + + ret = post_sendmsg(t, send_ctx, msg); + if (ret) + goto err; + return 0; +err: + smb_direct_free_sendmsg(t, msg); + atomic_inc(&t->send_credits); + return ret; +} + +static int smb_direct_writev(struct ksmbd_transport *t, + struct kvec *iov, int niovs, int buflen, + bool need_invalidate, unsigned int remote_key) +{ + struct smb_direct_transport *st = SMB_DIRECT_TRANS(t); + int remaining_data_length; + int start, i, j; + int max_iov_size = st->max_send_size - + sizeof(struct smb_direct_data_transfer); + int ret; + struct kvec vec; + struct smb_direct_send_ctx send_ctx; + + if (st->status != SMB_DIRECT_CS_CONNECTED) { + ret = -ENOTCONN; + goto done; + } + + //FIXME: skip RFC1002 header.. + buflen -= 4; + iov[0].iov_base += 4; + iov[0].iov_len -= 4; + + remaining_data_length = buflen; + ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen); + + smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key); + start = i = 0; + buflen = 0; + while (true) { + buflen += iov[i].iov_len; + if (buflen > max_iov_size) { + if (i > start) { + remaining_data_length -= + (buflen-iov[i].iov_len); + ret = smb_direct_post_send_data(st, &send_ctx, + &iov[start], i-start, + remaining_data_length); + if (ret) + goto done; + } else { + /* iov[start] is too big, break it */ + int nvec = (buflen+max_iov_size-1) / + max_iov_size; + + for (j = 0; j < nvec; j++) { + vec.iov_base = + (char *)iov[start].iov_base + + j*max_iov_size; + vec.iov_len = + min_t(int, max_iov_size, + buflen - max_iov_size*j); + remaining_data_length -= vec.iov_len; + ret = smb_direct_post_send_data(st, + &send_ctx, &vec, 1, + remaining_data_length); + if (ret) + goto done; + } + i++; + if (i == niovs) + break; + } + start = i; + buflen = 0; + } else { + i++; + if (i == niovs) { + /* send out all remaining vecs */ + remaining_data_length -= buflen; + ret = smb_direct_post_send_data(st, &send_ctx, + &iov[start], i-start, + remaining_data_length); + if (ret) + goto done; + break; + } + } + } + +done: + ret = smb_direct_flush_send_list(st, &send_ctx, true); + + /* + * As an optimization, we don't wait for individual I/O to finish + * before sending the next one. + * Send them all and wait for pending send count to get to 0 + * that means all the I/Os have been out and we are good to return + */ + + wait_event(st->wait_send_payload_pending, + atomic_read(&st->send_payload_pending) == 0); + return ret; +} + +static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, + enum dma_data_direction dir) +{ + struct smb_direct_rdma_rw_msg *msg = container_of(wc->wr_cqe, + struct smb_direct_rdma_rw_msg, cqe); + struct smb_direct_transport *t = msg->t; + + if (wc->status != IB_WC_SUCCESS) { + ksmbd_err("read/write error. opcode = %d, status = %s(%d)\n", + wc->opcode, ib_wc_status_msg(wc->status), wc->status); + smb_direct_disconnect_rdma_connection(t); + } + + if (atomic_inc_return(&t->rw_avail_ops) > 0) + wake_up(&t->wait_rw_avail_ops); + + rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, + msg->sg_list, msg->sgt.nents, dir); + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + complete(msg->completion); + kfree(msg); +} + +static void read_done(struct ib_cq *cq, struct ib_wc *wc) +{ + read_write_done(cq, wc, DMA_FROM_DEVICE); +} + +static void write_done(struct ib_cq *cq, struct ib_wc *wc) +{ + read_write_done(cq, wc, DMA_TO_DEVICE); +} + +static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, + int buf_len, u32 remote_key, u64 remote_offset, u32 remote_len, + bool is_read) +{ + struct smb_direct_rdma_rw_msg *msg; + int ret; + DECLARE_COMPLETION_ONSTACK(completion); + struct ib_send_wr *first_wr = NULL; + + ret = wait_for_credits(t, &t->wait_rw_avail_ops, &t->rw_avail_ops); + if (ret < 0) + return ret; + + /* TODO: mempool */ + msg = kmalloc(offsetof(struct smb_direct_rdma_rw_msg, sg_list) + + sizeof(struct scatterlist) * SG_CHUNK_SIZE, GFP_KERNEL); + if (!msg) { + atomic_inc(&t->rw_avail_ops); + return -ENOMEM; + } + + msg->sgt.sgl = &msg->sg_list[0]; + ret = sg_alloc_table_chained(&msg->sgt, + BUFFER_NR_PAGES(buf, buf_len), + msg->sg_list, SG_CHUNK_SIZE); + if (ret) { + atomic_inc(&t->rw_avail_ops); + kfree(msg); + return -ENOMEM; + } + + ret = get_sg_list(buf, buf_len, msg->sgt.sgl, msg->sgt.orig_nents); + if (ret <= 0) { + ksmbd_err("failed to get pages\n"); + goto err; + } + + ret = rdma_rw_ctx_init(&msg->rw_ctx, t->qp, t->qp->port, + msg->sg_list, BUFFER_NR_PAGES(buf, buf_len), + 0, remote_offset, remote_key, + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (ret < 0) { + ksmbd_err("failed to init rdma_rw_ctx: %d\n", ret); + goto err; + } + + msg->t = t; + msg->cqe.done = is_read ? read_done : write_done; + msg->completion = &completion; + first_wr = rdma_rw_ctx_wrs(&msg->rw_ctx, t->qp, t->qp->port, + &msg->cqe, NULL); + + ret = ib_post_send(t->qp, first_wr, NULL); + if (ret) { + ksmbd_err("failed to post send wr: %d\n", ret); + goto err; + } + + wait_for_completion(&completion); + return 0; + +err: + atomic_inc(&t->rw_avail_ops); + if (first_wr) + rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, + msg->sg_list, msg->sgt.nents, + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + kfree(msg); + return ret; + +} + +static int smb_direct_rdma_write(struct ksmbd_transport *t, + void *buf, unsigned int buflen, + u32 remote_key, u64 remote_offset, + u32 remote_len) +{ + return smb_direct_rdma_xmit(SMB_DIRECT_TRANS(t), buf, buflen, + remote_key, remote_offset, + remote_len, false); +} + +static int smb_direct_rdma_read(struct ksmbd_transport *t, + void *buf, unsigned int buflen, + u32 remote_key, u64 remote_offset, + u32 remote_len) +{ + return smb_direct_rdma_xmit(SMB_DIRECT_TRANS(t), buf, buflen, + remote_key, remote_offset, + remote_len, true); +} + +static void smb_direct_disconnect(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = SMB_DIRECT_TRANS(t); + + ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", st->cm_id); + + smb_direct_disconnect_rdma_connection(st); + wait_event_interruptible(st->wait_status, + st->status == SMB_DIRECT_CS_DISCONNECTED); + free_transport(st); +} + +static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + struct smb_direct_transport *t = cm_id->context; + + ksmbd_debug(RDMA, "RDMA CM event. cm_id=%p event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), event->event); + + switch (event->event) { + case RDMA_CM_EVENT_ESTABLISHED: { + t->status = SMB_DIRECT_CS_CONNECTED; + wake_up_interruptible(&t->wait_status); + break; + } + case RDMA_CM_EVENT_DEVICE_REMOVAL: + case RDMA_CM_EVENT_DISCONNECTED: { + t->status = SMB_DIRECT_CS_DISCONNECTED; + wake_up_interruptible(&t->wait_status); + wake_up_interruptible(&t->wait_reassembly_queue); + wake_up(&t->wait_send_credits); + break; + } + case RDMA_CM_EVENT_CONNECT_ERROR: { + t->status = SMB_DIRECT_CS_DISCONNECTED; + wake_up_interruptible(&t->wait_status); + break; + } + default: + ksmbd_err("Unexpected RDMA CM event. cm_id=%p, event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), + event->event); + break; + } + return 0; +} + +static void smb_direct_qpair_handler(struct ib_event *event, void *context) +{ + struct smb_direct_transport *t = context; + + ksmbd_debug(RDMA, "Received QP event. cm_id=%p, event=%s (%d)\n", + t->cm_id, ib_event_msg(event->event), event->event); + + switch (event->event) { + case IB_EVENT_CQ_ERR: + case IB_EVENT_QP_FATAL: + smb_direct_disconnect_rdma_connection(t); + break; + default: + break; + } +} + +static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, + int failed) +{ + struct smb_direct_sendmsg *sendmsg; + struct smb_direct_negotiate_resp *resp; + int ret; + + sendmsg = smb_direct_alloc_sendmsg(t); + if (IS_ERR(sendmsg)) + return -ENOMEM; + + resp = (struct smb_direct_negotiate_resp *)sendmsg->packet; + if (failed) { + memset(resp, 0, sizeof(*resp)); + resp->min_version = cpu_to_le16(0x0100); + resp->max_version = cpu_to_le16(0x0100); + resp->status = STATUS_NOT_SUPPORTED; + } else { + resp->status = STATUS_SUCCESS; + resp->min_version = SMB_DIRECT_VERSION_LE; + resp->max_version = SMB_DIRECT_VERSION_LE; + resp->negotiated_version = SMB_DIRECT_VERSION_LE; + resp->reserved = 0; + resp->credits_requested = + cpu_to_le16(t->send_credit_target); + resp->credits_granted = cpu_to_le16( + manage_credits_prior_sending(t)); + resp->max_readwrite_size = cpu_to_le32(t->max_rdma_rw_size); + resp->preferred_send_size = cpu_to_le32(t->max_send_size); + resp->max_receive_size = cpu_to_le32(t->max_recv_size); + resp->max_fragmented_size = + cpu_to_le32(t->max_fragmented_recv_size); + } + + sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + (void *)resp, sizeof(*resp), DMA_TO_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, + sendmsg->sge[0].addr); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + sendmsg->num_sge = 1; + sendmsg->sge[0].length = sizeof(*resp); + sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + + ret = post_sendmsg(t, NULL, sendmsg); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + wait_event(t->wait_send_pending, + atomic_read(&t->send_pending) == 0); + return 0; +} + +static int smb_direct_accept_client(struct smb_direct_transport *t) +{ + struct rdma_conn_param conn_param; + struct ib_port_immutable port_immutable; + u32 ird_ord_hdr[2]; + int ret; + + memset(&conn_param, 0, sizeof(conn_param)); + conn_param.initiator_depth = min_t(u8, + t->cm_id->device->attrs.max_qp_rd_atom, + SMB_DIRECT_CM_INITIATOR_DEPTH); + conn_param.responder_resources = 0; + + t->cm_id->device->ops.get_port_immutable(t->cm_id->device, + t->cm_id->port_num, &port_immutable); + if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { + ird_ord_hdr[0] = conn_param.responder_resources; + ird_ord_hdr[1] = 1; + conn_param.private_data = ird_ord_hdr; + conn_param.private_data_len = sizeof(ird_ord_hdr); + } else { + conn_param.private_data = NULL; + conn_param.private_data_len = 0; + } + conn_param.retry_count = SMB_DIRECT_CM_RETRY; + conn_param.rnr_retry_count = SMB_DIRECT_CM_RNR_RETRY; + conn_param.flow_control = 0; + + ret = rdma_accept(t->cm_id, &conn_param); + if (ret) { + ksmbd_err("error at rdma_accept: %d\n", ret); + return ret; + } + + wait_event_interruptible(t->wait_status, + t->status != SMB_DIRECT_CS_NEW); + if (t->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + return 0; +} + +static int smb_direct_negotiate(struct smb_direct_transport *t) +{ + int ret; + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_negotiate_req *req; + + recvmsg = get_free_recvmsg(t); + if (!recvmsg) + return -ENOMEM; + recvmsg->type = SMB_DIRECT_MSG_NEGOTIATE_REQ; + + ret = smb_direct_post_recv(t, recvmsg); + if (ret) { + ksmbd_err("Can't post recv: %d\n", ret); + goto out; + } + + t->negotiation_requested = false; + ret = smb_direct_accept_client(t); + if (ret) { + ksmbd_err("Can't accept client\n"); + goto out; + } + + smb_direct_post_recv_credits(&t->post_recv_credits_work.work); + + ksmbd_debug(RDMA, "Waiting for SMB_DIRECT negotiate request\n"); + ret = wait_event_interruptible_timeout(t->wait_status, + t->negotiation_requested || + t->status == SMB_DIRECT_CS_DISCONNECTED, + SMB_DIRECT_NEGOTIATE_TIMEOUT * HZ); + if (ret <= 0 || t->status == SMB_DIRECT_CS_DISCONNECTED) { + ret = ret < 0 ? ret : -ETIMEDOUT; + goto out; + } + + ret = smb_direct_check_recvmsg(recvmsg); + if (ret == -ECONNABORTED) + goto out; + + req = (struct smb_direct_negotiate_req *)recvmsg->packet; + t->max_recv_size = min_t(int, t->max_recv_size, + le32_to_cpu(req->preferred_send_size)); + t->max_send_size = min_t(int, t->max_send_size, + le32_to_cpu(req->max_receive_size)); + t->max_fragmented_send_size = + le32_to_cpu(req->max_fragmented_size); + + ret = smb_direct_send_negotiate_response(t, ret); +out: + if (recvmsg) + put_recvmsg(t, recvmsg); + return ret; +} + +static int smb_direct_init_params(struct smb_direct_transport *t, + struct ib_qp_cap *cap) +{ + struct ib_device *device = t->cm_id->device; + int max_send_sges, max_pages, max_rw_wrs, max_send_wrs; + + /* need 2 more sge. because a SMB_DIRECT header will be mapped, + * and maybe a send buffer could be not page aligned. + */ + t->max_send_size = smb_direct_max_send_size; + max_send_sges = DIV_ROUND_UP(t->max_send_size, PAGE_SIZE) + 2; + if (max_send_sges > SMB_DIRECT_MAX_SEND_SGES) { + ksmbd_err("max_send_size %d is too large\n", t->max_send_size); + return -EINVAL; + } + + /* + * allow smb_direct_max_outstanding_rw_ops of in-flight RDMA + * read/writes. HCA guarantees at least max_send_sge of sges for + * a RDMA read/write work request, and if memory registration is used, + * we need reg_mr, local_inv wrs for each read/write. + */ + t->max_rdma_rw_size = smb_direct_max_read_write_size; + max_pages = DIV_ROUND_UP(t->max_rdma_rw_size, PAGE_SIZE) + 1; + max_rw_wrs = DIV_ROUND_UP(max_pages, SMB_DIRECT_MAX_SEND_SGES); + max_rw_wrs += rdma_rw_mr_factor(device, t->cm_id->port_num, + max_pages) * 2; + max_rw_wrs *= smb_direct_max_outstanding_rw_ops; + + max_send_wrs = smb_direct_send_credit_target + max_rw_wrs; + if (max_send_wrs > device->attrs.max_cqe || + max_send_wrs > device->attrs.max_qp_wr) { + ksmbd_err("consider lowering send_credit_target = %d, or max_outstanding_rw_ops = %d\n", + smb_direct_send_credit_target, + smb_direct_max_outstanding_rw_ops); + ksmbd_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); + return -EINVAL; + } + + if (smb_direct_receive_credit_max > device->attrs.max_cqe || + smb_direct_receive_credit_max > device->attrs.max_qp_wr) { + ksmbd_err("consider lowering receive_credit_max = %d\n", + smb_direct_receive_credit_max); + ksmbd_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); + return -EINVAL; + } + + if (device->attrs.max_send_sge < SMB_DIRECT_MAX_SEND_SGES) { + ksmbd_err("warning: device max_send_sge = %d too small\n", + device->attrs.max_send_sge); + return -EINVAL; + } + if (device->attrs.max_recv_sge < SMB_DIRECT_MAX_RECV_SGES) { + ksmbd_err("warning: device max_recv_sge = %d too small\n", + device->attrs.max_recv_sge); + return -EINVAL; + } + + t->recv_credits = 0; + t->count_avail_recvmsg = 0; + + t->recv_credit_max = smb_direct_receive_credit_max; + t->recv_credit_target = 10; + t->new_recv_credits = 0; + + t->send_credit_target = smb_direct_send_credit_target; + atomic_set(&t->send_credits, 0); + atomic_set(&t->rw_avail_ops, smb_direct_max_outstanding_rw_ops); + + t->max_send_size = smb_direct_max_send_size; + t->max_recv_size = smb_direct_max_receive_size; + t->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size; + + cap->max_send_wr = max_send_wrs; + cap->max_recv_wr = t->recv_credit_max; + cap->max_send_sge = SMB_DIRECT_MAX_SEND_SGES; + cap->max_recv_sge = SMB_DIRECT_MAX_RECV_SGES; + cap->max_inline_data = 0; + cap->max_rdma_ctxs = 0; + return 0; +} + +static void smb_direct_destroy_pools(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg; + + while ((recvmsg = get_free_recvmsg(t))) + mempool_free(recvmsg, t->recvmsg_mempool); + while ((recvmsg = get_empty_recvmsg(t))) + mempool_free(recvmsg, t->recvmsg_mempool); + + mempool_destroy(t->recvmsg_mempool); + t->recvmsg_mempool = NULL; + + kmem_cache_destroy(t->recvmsg_cache); + t->recvmsg_cache = NULL; + + mempool_destroy(t->sendmsg_mempool); + t->sendmsg_mempool = NULL; + + kmem_cache_destroy(t->sendmsg_cache); + t->sendmsg_cache = NULL; +} + +static int smb_direct_create_pools(struct smb_direct_transport *t) +{ + char name[80]; + int i; + struct smb_direct_recvmsg *recvmsg; + + snprintf(name, sizeof(name), "smb_direct_rqst_pool_%p", t); + t->sendmsg_cache = kmem_cache_create(name, + sizeof(struct smb_direct_sendmsg) + + sizeof(struct smb_direct_negotiate_resp), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!t->sendmsg_cache) + return -ENOMEM; + + t->sendmsg_mempool = mempool_create(t->send_credit_target, + mempool_alloc_slab, mempool_free_slab, + t->sendmsg_cache); + if (!t->sendmsg_mempool) + goto err; + + snprintf(name, sizeof(name), "smb_direct_resp_%p", t); + t->recvmsg_cache = kmem_cache_create(name, + sizeof(struct smb_direct_recvmsg) + + t->max_recv_size, + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!t->recvmsg_cache) + goto err; + + t->recvmsg_mempool = + mempool_create(t->recv_credit_max, mempool_alloc_slab, + mempool_free_slab, t->recvmsg_cache); + if (!t->recvmsg_mempool) + goto err; + + INIT_LIST_HEAD(&t->recvmsg_queue); + + for (i = 0; i < t->recv_credit_max; i++) { + recvmsg = mempool_alloc(t->recvmsg_mempool, GFP_KERNEL); + if (!recvmsg) + goto err; + recvmsg->transport = t; + list_add(&recvmsg->list, &t->recvmsg_queue); + } + t->count_avail_recvmsg = t->recv_credit_max; + + return 0; +err: + smb_direct_destroy_pools(t); + return -ENOMEM; +} + +static int smb_direct_create_qpair(struct smb_direct_transport *t, + struct ib_qp_cap *cap) +{ + int ret; + struct ib_qp_init_attr qp_attr; + + t->pd = ib_alloc_pd(t->cm_id->device, 0); + if (IS_ERR(t->pd)) { + ksmbd_err("Can't create RDMA PD\n"); + ret = PTR_ERR(t->pd); + t->pd = NULL; + return ret; + } + + t->send_cq = ib_alloc_cq(t->cm_id->device, t, + t->send_credit_target, 0, IB_POLL_WORKQUEUE); + if (IS_ERR(t->send_cq)) { + ksmbd_err("Can't create RDMA send CQ\n"); + ret = PTR_ERR(t->send_cq); + t->send_cq = NULL; + goto err; + } + + t->recv_cq = ib_alloc_cq(t->cm_id->device, t, + cap->max_send_wr + cap->max_rdma_ctxs, + 0, IB_POLL_WORKQUEUE); + if (IS_ERR(t->recv_cq)) { + ksmbd_err("Can't create RDMA recv CQ\n"); + ret = PTR_ERR(t->recv_cq); + t->recv_cq = NULL; + goto err; + } + + memset(&qp_attr, 0, sizeof(qp_attr)); + qp_attr.event_handler = smb_direct_qpair_handler; + qp_attr.qp_context = t; + qp_attr.cap = *cap; + qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; + qp_attr.qp_type = IB_QPT_RC; + qp_attr.send_cq = t->send_cq; + qp_attr.recv_cq = t->recv_cq; + qp_attr.port_num = ~0; + + ret = rdma_create_qp(t->cm_id, t->pd, &qp_attr); + if (ret) { + ksmbd_err("Can't create RDMA QP: %d\n", ret); + goto err; + } + + t->qp = t->cm_id->qp; + t->cm_id->event_handler = smb_direct_cm_handler; + + return 0; +err: + if (t->qp) { + ib_destroy_qp(t->qp); + t->qp = NULL; + } + if (t->recv_cq) { + ib_destroy_cq(t->recv_cq); + t->recv_cq = NULL; + } + if (t->send_cq) { + ib_destroy_cq(t->send_cq); + t->send_cq = NULL; + } + if (t->pd) { + ib_dealloc_pd(t->pd); + t->pd = NULL; + } + return ret; +} + +static int smb_direct_prepare(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = SMB_DIRECT_TRANS(t); + int ret; + struct ib_qp_cap qp_cap; + + ret = smb_direct_init_params(st, &qp_cap); + if (ret) { + ksmbd_err("Can't configure RDMA parameters\n"); + return ret; + } + + ret = smb_direct_create_pools(st); + if (ret) { + ksmbd_err("Can't init RDMA pool: %d\n", ret); + return ret; + } + + ret = smb_direct_create_qpair(st, &qp_cap); + if (ret) { + ksmbd_err("Can't accept RDMA client: %d\n", ret); + return ret; + } + + ret = smb_direct_negotiate(st); + if (ret) { + ksmbd_err("Can't negotiate: %d\n", ret); + return ret; + } + + st->status = SMB_DIRECT_CS_CONNECTED; + return 0; +} + +static bool rdma_frwr_is_supported(struct ib_device_attr *attrs) +{ + if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) + return false; + if (attrs->max_fast_reg_page_list_len == 0) + return false; + return true; +} + +static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) +{ + struct smb_direct_transport *t; + + if (!rdma_frwr_is_supported(&new_cm_id->device->attrs)) { + ksmbd_debug(RDMA, + "Fast Registration Work Requests is not supported. device capabilities=%llx\n", + new_cm_id->device->attrs.device_cap_flags); + return -EPROTONOSUPPORT; + } + + t = alloc_transport(new_cm_id); + if (!t) + return -ENOMEM; + + KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, "ksmbd:r%u", SMB_DIRECT_PORT); + if (IS_ERR(KSMBD_TRANS(t)->handler)) { + int ret = PTR_ERR(KSMBD_TRANS(t)->handler); + + ksmbd_err("Can't start thread\n"); + free_transport(t); + return ret; + } + + return 0; +} + +static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + switch (event->event) { + case RDMA_CM_EVENT_CONNECT_REQUEST: { + int ret = smb_direct_handle_connect_request(cm_id); + + if (ret) { + ksmbd_err("Can't create transport: %d\n", ret); + return ret; + } + + ksmbd_debug(RDMA, "Received connection request. cm_id=%p\n", + cm_id); + break; + } + default: + ksmbd_err("Unexpected listen event. cm_id=%p, event=%s (%d)\n", + cm_id, + rdma_event_msg(event->event), event->event); + break; + } + return 0; +} + +static int smb_direct_listen(int port) +{ + int ret; + struct rdma_cm_id *cm_id; + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), + .sin_port = htons(port), + }; + + cm_id = rdma_create_id(&init_net, smb_direct_listen_handler, + &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); + if (IS_ERR(cm_id)) { + ksmbd_err("Can't create cm id: %ld\n", + PTR_ERR(cm_id)); + return PTR_ERR(cm_id); + } + + ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); + if (ret) { + ksmbd_err("Can't bind: %d\n", ret); + goto err; + } + + smb_direct_listener.cm_id = cm_id; + + ret = rdma_listen(cm_id, 10); + if (ret) { + ksmbd_err("Can't listen: %d\n", ret); + goto err; + } + return 0; +err: + smb_direct_listener.cm_id = NULL; + rdma_destroy_id(cm_id); + return ret; +} + +int ksmbd_rdma_init(void) +{ + int ret; + + smb_direct_listener.cm_id = NULL; + + /* When a client is running out of send credits, the credits are + * granted by the server's sending a packet using this queue. + * This avoids the situation that a clients cannot send packets + * for lack of credits + */ + smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", + WQ_HIGHPRI|WQ_MEM_RECLAIM, 0); + if (!smb_direct_wq) + return -ENOMEM; + + ret = smb_direct_listen(SMB_DIRECT_PORT); + if (ret) { + destroy_workqueue(smb_direct_wq); + smb_direct_wq = NULL; + ksmbd_err("Can't listen: %d\n", ret); + return ret; + } + + ksmbd_debug(RDMA, "init RDMA listener. cm_id=%p\n", + smb_direct_listener.cm_id); + return 0; +} + +int ksmbd_rdma_destroy(void) +{ + if (smb_direct_listener.cm_id) + rdma_destroy_id(smb_direct_listener.cm_id); + smb_direct_listener.cm_id = NULL; + + if (smb_direct_wq) { + flush_workqueue(smb_direct_wq); + destroy_workqueue(smb_direct_wq); + smb_direct_wq = NULL; + } + return 0; +} + +static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { + .prepare = smb_direct_prepare, + .disconnect = smb_direct_disconnect, + .writev = smb_direct_writev, + .read = smb_direct_read, + .rdma_read = smb_direct_rdma_read, + .rdma_write = smb_direct_rdma_write, +}; diff --git a/fs/cifsd/transport_rdma.h b/fs/cifsd/transport_rdma.h new file mode 100644 index 000000000000..da60fcec3ede --- /dev/null +++ b/fs/cifsd/transport_rdma.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017, Microsoft Corporation. + * Copyright (C) 2018, LG Electronics. + */ + +#ifndef __KSMBD_TRANSPORT_RDMA_H__ +#define __KSMBD_TRANSPORT_RDMA_H__ + +#define SMB_DIRECT_PORT 5445 + +/* SMB DIRECT negotiation request packet [MS-KSMBD] 2.2.1 */ +struct smb_direct_negotiate_req { + __le16 min_version; + __le16 max_version; + __le16 reserved; + __le16 credits_requested; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +/* SMB DIRECT negotiation response packet [MS-KSMBD] 2.2.2 */ +struct smb_direct_negotiate_resp { + __le16 min_version; + __le16 max_version; + __le16 negotiated_version; + __le16 reserved; + __le16 credits_requested; + __le16 credits_granted; + __le32 status; + __le32 max_readwrite_size; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 + +/* SMB DIRECT data transfer packet with payload [MS-KSMBD] 2.2.3 */ +struct smb_direct_data_transfer { + __le16 credits_requested; + __le16 credits_granted; + __le16 flags; + __le16 reserved; + __le32 remaining_data_length; + __le32 data_offset; + __le32 data_length; + __le32 padding; + __u8 buffer[]; +} __packed; + +#ifdef CONFIG_SMB_SERVER_SMBDIRECT +int ksmbd_rdma_init(void); +int ksmbd_rdma_destroy(void); +#else +static inline int ksmbd_rdma_init(void) { return 0; } +static inline int ksmbd_rdma_destroy(void) { return 0; } +#endif + +#endif /* __KSMBD_TRANSPORT_RDMA_H__ */ diff --git a/fs/cifsd/transport_tcp.c b/fs/cifsd/transport_tcp.c new file mode 100644 index 000000000000..60ec9b2e0370 --- /dev/null +++ b/fs/cifsd/transport_tcp.c @@ -0,0 +1,624 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "smb_common.h" +#include "server.h" +#include "auth.h" +#include "buffer_pool.h" +#include "connection.h" +#include "transport_tcp.h" + +#define IFACE_STATE_DOWN (1 << 0) +#define IFACE_STATE_CONFIGURED (1 << 1) + +struct interface { + struct task_struct *ksmbd_kthread; + struct socket *ksmbd_socket; + struct list_head entry; + char *name; + struct mutex sock_release_lock; + int state; +}; + +static LIST_HEAD(iface_list); + +static int bind_additional_ifaces; + +struct tcp_transport { + struct ksmbd_transport transport; + struct socket *sock; + struct kvec *iov; + unsigned int nr_iov; +}; + +static struct ksmbd_transport_ops ksmbd_tcp_transport_ops; + +static void tcp_stop_kthread(struct task_struct *kthread); +static struct interface *alloc_iface(char *ifname); + +#define KSMBD_TRANS(t) (&(t)->transport) +#define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \ + struct tcp_transport, transport)) + +static inline void ksmbd_tcp_nodelay(struct socket *sock) +{ + tcp_sock_set_nodelay(sock->sk); +} + +static inline void ksmbd_tcp_reuseaddr(struct socket *sock) +{ + sock_set_reuseaddr(sock->sk); +} + +static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs) +{ + lock_sock(sock->sk); + if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1) + sock->sk->sk_rcvtimeo = secs * HZ; + else + sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; + release_sock(sock->sk); +} + +static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs) +{ + sock_set_sndtimeo(sock->sk, secs); +} + +static struct tcp_transport *alloc_transport(struct socket *client_sk) +{ + struct tcp_transport *t; + struct ksmbd_conn *conn; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return NULL; + t->sock = client_sk; + + conn = ksmbd_conn_alloc(); + if (!conn) { + kfree(t); + return NULL; + } + + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops; + return t; +} + +static void free_transport(struct tcp_transport *t) +{ + kernel_sock_shutdown(t->sock, SHUT_RDWR); + sock_release(t->sock); + t->sock = NULL; + + ksmbd_conn_free(KSMBD_TRANS(t)->conn); + kfree(t->iov); + kfree(t); +} + +/** + * kvec_array_init() - initialize a IO vector segment + * @new: IO vector to be initialized + * @iov: base IO vector + * @nr_segs: number of segments in base iov + * @bytes: total iovec length so far for read + * + * Return: Number of IO segments + */ +static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov, + unsigned int nr_segs, size_t bytes) +{ + size_t base = 0; + + while (bytes || !iov->iov_len) { + int copy = min(bytes, iov->iov_len); + + bytes -= copy; + base += copy; + if (iov->iov_len == base) { + iov++; + nr_segs--; + base = 0; + } + } + + memcpy(new, iov, sizeof(*iov) * nr_segs); + new->iov_base += base; + new->iov_len -= base; + return nr_segs; +} + +/** + * get_conn_iovec() - get connection iovec for reading from socket + * @t: TCP transport instance + * @nr_segs: number of segments in iov + * + * Return: return existing or newly allocate iovec + */ +static struct kvec *get_conn_iovec(struct tcp_transport *t, + unsigned int nr_segs) +{ + struct kvec *new_iov; + + if (t->iov && nr_segs <= t->nr_iov) + return t->iov; + + /* not big enough -- allocate a new one and release the old */ + new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL); + if (new_iov) { + kfree(t->iov); + t->iov = new_iov; + t->nr_iov = nr_segs; + } + return new_iov; +} + +static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET: + return ntohs(((struct sockaddr_in *)sa)->sin_port); + case AF_INET6: + return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); + } + return 0; +} + +/** + * ksmbd_tcp_new_connection() - create a new tcp session on mount + * @sock: socket associated with new connection + * + * whenever a new connection is requested, create a conn thread + * (session thread) to handle new incoming smb requests from the connection + * + * Return: 0 on success, otherwise error + */ +static int ksmbd_tcp_new_connection(struct socket *client_sk) +{ + struct sockaddr *csin; + int rc = 0; + struct tcp_transport *t; + + t = alloc_transport(client_sk); + if (!t) + return -ENOMEM; + + csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn); + if (kernel_getpeername(client_sk, csin) < 0) { + ksmbd_err("client ip resolution failed\n"); + rc = -EINVAL; + goto out_error; + } + + KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, + "ksmbd:%u", ksmbd_tcp_get_port(csin)); + if (IS_ERR(KSMBD_TRANS(t)->handler)) { + ksmbd_err("cannot start conn thread\n"); + rc = PTR_ERR(KSMBD_TRANS(t)->handler); + free_transport(t); + } + return rc; + +out_error: + free_transport(t); + return rc; +} + +/** + * ksmbd_kthread_fn() - listen to new SMB connections and callback server + * @p: arguments to forker thread + * + * Return: Returns a task_struct or ERR_PTR + */ +static int ksmbd_kthread_fn(void *p) +{ + struct socket *client_sk = NULL; + struct interface *iface = (struct interface *)p; + int ret; + + while (!kthread_should_stop()) { + mutex_lock(&iface->sock_release_lock); + if (!iface->ksmbd_socket) { + mutex_unlock(&iface->sock_release_lock); + break; + } + ret = kernel_accept(iface->ksmbd_socket, &client_sk, + O_NONBLOCK); + mutex_unlock(&iface->sock_release_lock); + if (ret) { + if (ret == -EAGAIN) + /* check for new connections every 100 msecs */ + schedule_timeout_interruptible(HZ / 10); + continue; + } + + ksmbd_debug(CONN, "connect success: accepted new connection\n"); + client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; + client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; + + ksmbd_tcp_new_connection(client_sk); + } + + ksmbd_debug(CONN, "releasing socket\n"); + return 0; +} + +/** + * ksmbd_create_ksmbd_kthread() - start forker thread + * + * start forker thread(ksmbd/0) at module init time to listen + * on port 445 for new SMB connection requests. It creates per connection + * server threads(ksmbd/x) + * + * Return: 0 on success or error number + */ +static int ksmbd_tcp_run_kthread(struct interface *iface) +{ + int rc; + struct task_struct *kthread; + + kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, + "ksmbd-%s", iface->name); + if (IS_ERR(kthread)) { + rc = PTR_ERR(kthread); + return rc; + } + iface->ksmbd_kthread = kthread; + + return 0; +} + +/** + * ksmbd_tcp_readv() - read data from socket in given iovec + * @t: TCP transport instance + * @iov_orig: base IO vector + * @nr_segs: number of segments in base iov + * @to_read: number of bytes to read from socket + * + * Return: on success return number of bytes read from socket, + * otherwise return error number + */ +static int ksmbd_tcp_readv(struct tcp_transport *t, + struct kvec *iov_orig, + unsigned int nr_segs, + unsigned int to_read) +{ + int length = 0; + int total_read; + unsigned int segs; + struct msghdr ksmbd_msg; + struct kvec *iov; + struct ksmbd_conn *conn = KSMBD_TRANS(t)->conn; + + iov = get_conn_iovec(t, nr_segs); + if (!iov) + return -ENOMEM; + + ksmbd_msg.msg_control = NULL; + ksmbd_msg.msg_controllen = 0; + + for (total_read = 0; to_read; total_read += length, to_read -= length) { + try_to_freeze(); + + if (!ksmbd_conn_alive(conn)) { + total_read = -ESHUTDOWN; + break; + } + segs = kvec_array_init(iov, iov_orig, nr_segs, total_read); + + length = kernel_recvmsg(t->sock, &ksmbd_msg, + iov, segs, to_read, 0); + + if (length == -EINTR) { + total_read = -ESHUTDOWN; + break; + } else if (conn->status == KSMBD_SESS_NEED_RECONNECT) { + total_read = -EAGAIN; + break; + } else if (length == -ERESTARTSYS || length == -EAGAIN) { + usleep_range(1000, 2000); + length = 0; + continue; + } else if (length <= 0) { + total_read = -EAGAIN; + break; + } + } + return total_read; +} + +/** + * ksmbd_tcp_read() - read data from socket in given buffer + * @t: TCP transport instance + * @buf: buffer to store read data from socket + * @to_read: number of bytes to read from socket + * + * Return: on success return number of bytes read from socket, + * otherwise return error number + */ +static int ksmbd_tcp_read(struct ksmbd_transport *t, + char *buf, + unsigned int to_read) +{ + struct kvec iov; + + iov.iov_base = buf; + iov.iov_len = to_read; + + return ksmbd_tcp_readv(TCP_TRANS(t), &iov, 1, to_read); +} + +static int ksmbd_tcp_writev(struct ksmbd_transport *t, + struct kvec *iov, int nvecs, int size, + bool need_invalidate, unsigned int remote_key) + +{ + struct msghdr smb_msg = {.msg_flags = MSG_NOSIGNAL}; + + return kernel_sendmsg(TCP_TRANS(t)->sock, &smb_msg, iov, nvecs, size); +} + +static void ksmbd_tcp_disconnect(struct ksmbd_transport *t) +{ + free_transport(TCP_TRANS(t)); +} + +static void tcp_destroy_socket(struct socket *ksmbd_socket) +{ + int ret; + + if (!ksmbd_socket) + return; + + /* set zero to timeout */ + ksmbd_tcp_rcv_timeout(ksmbd_socket, 0); + ksmbd_tcp_snd_timeout(ksmbd_socket, 0); + + ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR); + if (ret) + ksmbd_err("Failed to shutdown socket: %d\n", ret); + else + sock_release(ksmbd_socket); +} + +/** + * create_socket - create socket for ksmbd/0 + * + * Return: Returns a task_struct or ERR_PTR + */ +static int create_socket(struct interface *iface) +{ + int ret; + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + struct socket *ksmbd_socket; + bool ipv4 = false; + + ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); + if (ret) { + ksmbd_err("Can't create socket for ipv6, try ipv4: %d\n", ret); + ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, + &ksmbd_socket); + if (ret) { + ksmbd_err("Can't create socket for ipv4: %d\n", ret); + goto out_error; + } + + sin.sin_family = PF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(server_conf.tcp_port); + ipv4 = true; + } else { + sin6.sin6_family = PF_INET6; + sin6.sin6_addr = in6addr_any; + sin6.sin6_port = htons(server_conf.tcp_port); + } + + ksmbd_tcp_nodelay(ksmbd_socket); + ksmbd_tcp_reuseaddr(ksmbd_socket); + + ret = sock_setsockopt(ksmbd_socket, + SOL_SOCKET, + SO_BINDTODEVICE, + KERNEL_SOCKPTR(iface->name), + strlen(iface->name)); + if (ret != -ENODEV && ret < 0) { + ksmbd_err("Failed to set SO_BINDTODEVICE: %d\n", ret); + goto out_error; + } + + if (ipv4) + ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin, + sizeof(sin)); + else + ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6, + sizeof(sin6)); + if (ret) { + ksmbd_err("Failed to bind socket: %d\n", ret); + goto out_error; + } + + ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; + ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; + + ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG); + if (ret) { + ksmbd_err("Port listen() error: %d\n", ret); + goto out_error; + } + + iface->ksmbd_socket = ksmbd_socket; + ret = ksmbd_tcp_run_kthread(iface); + if (ret) { + ksmbd_err("Can't start ksmbd main kthread: %d\n", ret); + goto out_error; + } + iface->state = IFACE_STATE_CONFIGURED; + + return 0; + +out_error: + tcp_destroy_socket(ksmbd_socket); + iface->ksmbd_socket = NULL; + return ret; +} + +static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct interface *iface; + int ret, found = 0; + + switch (event) { + case NETDEV_UP: + if (netdev->priv_flags & IFF_BRIDGE_PORT) + return NOTIFY_OK; + + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name)) { + found = 1; + if (iface->state != IFACE_STATE_DOWN) + break; + ret = create_socket(iface); + if (ret) + return NOTIFY_OK; + break; + } + } + if (!found && bind_additional_ifaces) { + iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL)); + if (!iface) + return NOTIFY_OK; + ret = create_socket(iface); + if (ret) + break; + } + break; + case NETDEV_DOWN: + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name) && + iface->state == IFACE_STATE_CONFIGURED) { + tcp_stop_kthread(iface->ksmbd_kthread); + iface->ksmbd_kthread = NULL; + mutex_lock(&iface->sock_release_lock); + tcp_destroy_socket(iface->ksmbd_socket); + iface->ksmbd_socket = NULL; + mutex_unlock(&iface->sock_release_lock); + + iface->state = IFACE_STATE_DOWN; + break; + } + } + break; + } + + return NOTIFY_DONE; + +} + +static struct notifier_block ksmbd_netdev_notifier = { + .notifier_call = ksmbd_netdev_event, +}; + +int ksmbd_tcp_init(void) +{ + register_netdevice_notifier(&ksmbd_netdev_notifier); + + return 0; +} + +static void tcp_stop_kthread(struct task_struct *kthread) +{ + int ret; + + if (!kthread) + return; + + ret = kthread_stop(kthread); + if (ret) + ksmbd_err("failed to stop forker thread\n"); +} + +void ksmbd_tcp_destroy(void) +{ + struct interface *iface, *tmp; + + unregister_netdevice_notifier(&ksmbd_netdev_notifier); + + list_for_each_entry_safe(iface, tmp, &iface_list, entry) { + list_del(&iface->entry); + kfree(iface->name); + ksmbd_free(iface); + } +} + +static struct interface *alloc_iface(char *ifname) +{ + struct interface *iface; + + if (!ifname) + return NULL; + + iface = ksmbd_alloc(sizeof(struct interface)); + if (!iface) { + kfree(ifname); + return NULL; + } + + iface->name = ifname; + iface->state = IFACE_STATE_DOWN; + list_add(&iface->entry, &iface_list); + mutex_init(&iface->sock_release_lock); + return iface; +} + +int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) +{ + int sz = 0; + + if (!ifc_list_sz) { + struct net_device *netdev; + + rtnl_lock(); + for_each_netdev(&init_net, netdev) { + if (netdev->priv_flags & IFF_BRIDGE_PORT) + continue; + if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) + return -ENOMEM; + } + rtnl_unlock(); + bind_additional_ifaces = 1; + return 0; + } + + while (ifc_list_sz > 0) { + if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL))) + return -ENOMEM; + + sz = strlen(ifc_list); + if (!sz) + break; + + ifc_list += sz + 1; + ifc_list_sz -= (sz + 1); + } + + bind_additional_ifaces = 0; + + return 0; +} + +static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = { + .read = ksmbd_tcp_read, + .writev = ksmbd_tcp_writev, + .disconnect = ksmbd_tcp_disconnect, +}; diff --git a/fs/cifsd/transport_tcp.h b/fs/cifsd/transport_tcp.h new file mode 100644 index 000000000000..e338bebe322f --- /dev/null +++ b/fs/cifsd/transport_tcp.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_TRANSPORT_TCP_H__ +#define __KSMBD_TRANSPORT_TCP_H__ + +int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz); +int ksmbd_tcp_init(void); +void ksmbd_tcp_destroy(void); + +#endif /* __KSMBD_TRANSPORT_TCP_H__ */ From e2f34481b24db2fd634b5edb0a5bd0e4d38cc6e9 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 16 Mar 2021 10:49:09 +0900 Subject: [PATCH 002/417] cifsd: add server-side procedures for SMB3 This adds smb3 engine, NTLM/NTLMv2/Kerberos authentication, oplock/lease cache mechanism for cifsd. Signed-off-by: Namjae Jeon Signed-off-by: Sergey Senozhatsky Signed-off-by: Hyunchul Lee Acked-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifsd/asn1.c | 702 +++ fs/cifsd/asn1.h | 29 + fs/cifsd/auth.c | 1348 ++++++ fs/cifsd/auth.h | 90 + fs/cifsd/crypto_ctx.c | 287 ++ fs/cifsd/crypto_ctx.h | 77 + fs/cifsd/mgmt/ksmbd_ida.c | 69 + fs/cifsd/mgmt/ksmbd_ida.h | 41 + fs/cifsd/mgmt/share_config.c | 239 + fs/cifsd/mgmt/share_config.h | 83 + fs/cifsd/mgmt/tree_connect.c | 129 + fs/cifsd/mgmt/tree_connect.h | 56 + fs/cifsd/mgmt/user_config.c | 70 + fs/cifsd/mgmt/user_config.h | 67 + fs/cifsd/mgmt/user_session.c | 345 ++ fs/cifsd/mgmt/user_session.h | 105 + fs/cifsd/misc.c | 293 ++ fs/cifsd/misc.h | 38 + fs/cifsd/ndr.c | 344 ++ fs/cifsd/ndr.h | 21 + fs/cifsd/netmisc.c | 46 + fs/cifsd/nterr.c | 674 +++ fs/cifsd/nterr.h | 552 +++ fs/cifsd/ntlmssp.h | 169 + fs/cifsd/oplock.c | 1693 +++++++ fs/cifsd/oplock.h | 138 + fs/cifsd/smb2misc.c | 458 ++ fs/cifsd/smb2ops.c | 300 ++ fs/cifsd/smb2pdu.c | 8486 ++++++++++++++++++++++++++++++++++ fs/cifsd/smb2pdu.h | 1649 +++++++ fs/cifsd/smb_common.c | 668 +++ fs/cifsd/smb_common.h | 546 +++ fs/cifsd/smbacl.c | 1309 ++++++ fs/cifsd/smbacl.h | 202 + fs/cifsd/smberr.h | 235 + fs/cifsd/smbfsctl.h | 90 + fs/cifsd/smbstatus.h | 1822 ++++++++ fs/cifsd/time_wrappers.h | 34 + fs/cifsd/unicode.c | 391 ++ fs/cifsd/unicode.h | 374 ++ fs/cifsd/uniupr.h | 268 ++ 41 files changed, 24537 insertions(+) create mode 100644 fs/cifsd/asn1.c create mode 100644 fs/cifsd/asn1.h create mode 100644 fs/cifsd/auth.c create mode 100644 fs/cifsd/auth.h create mode 100644 fs/cifsd/crypto_ctx.c create mode 100644 fs/cifsd/crypto_ctx.h create mode 100644 fs/cifsd/mgmt/ksmbd_ida.c create mode 100644 fs/cifsd/mgmt/ksmbd_ida.h create mode 100644 fs/cifsd/mgmt/share_config.c create mode 100644 fs/cifsd/mgmt/share_config.h create mode 100644 fs/cifsd/mgmt/tree_connect.c create mode 100644 fs/cifsd/mgmt/tree_connect.h create mode 100644 fs/cifsd/mgmt/user_config.c create mode 100644 fs/cifsd/mgmt/user_config.h create mode 100644 fs/cifsd/mgmt/user_session.c create mode 100644 fs/cifsd/mgmt/user_session.h create mode 100644 fs/cifsd/misc.c create mode 100644 fs/cifsd/misc.h create mode 100644 fs/cifsd/ndr.c create mode 100644 fs/cifsd/ndr.h create mode 100644 fs/cifsd/netmisc.c create mode 100644 fs/cifsd/nterr.c create mode 100644 fs/cifsd/nterr.h create mode 100644 fs/cifsd/ntlmssp.h create mode 100644 fs/cifsd/oplock.c create mode 100644 fs/cifsd/oplock.h create mode 100644 fs/cifsd/smb2misc.c create mode 100644 fs/cifsd/smb2ops.c create mode 100644 fs/cifsd/smb2pdu.c create mode 100644 fs/cifsd/smb2pdu.h create mode 100644 fs/cifsd/smb_common.c create mode 100644 fs/cifsd/smb_common.h create mode 100644 fs/cifsd/smbacl.c create mode 100644 fs/cifsd/smbacl.h create mode 100644 fs/cifsd/smberr.h create mode 100644 fs/cifsd/smbfsctl.h create mode 100644 fs/cifsd/smbstatus.h create mode 100644 fs/cifsd/time_wrappers.h create mode 100644 fs/cifsd/unicode.c create mode 100644 fs/cifsd/unicode.h create mode 100644 fs/cifsd/uniupr.h diff --git a/fs/cifsd/asn1.c b/fs/cifsd/asn1.c new file mode 100644 index 000000000000..aa702b665849 --- /dev/null +++ b/fs/cifsd/asn1.c @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in + * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich + * + * Copyright (c) 2000 RP Internet (www.rpi.net.au). + */ + +#include +#include +#include +#include +#include + +#include "glob.h" + +#include "asn1.h" +#include "connection.h" +#include "auth.h" + +/***************************************************************************** + * + * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse) + * + *****************************************************************************/ + +/* Class */ +#define ASN1_UNI 0 /* Universal */ +#define ASN1_APL 1 /* Application */ +#define ASN1_CTX 2 /* Context */ +#define ASN1_PRV 3 /* Private */ + +/* Tag */ +#define ASN1_EOC 0 /* End Of Contents or N/A */ +#define ASN1_BOL 1 /* Boolean */ +#define ASN1_INT 2 /* Integer */ +#define ASN1_BTS 3 /* Bit String */ +#define ASN1_OTS 4 /* Octet String */ +#define ASN1_NUL 5 /* Null */ +#define ASN1_OJI 6 /* Object Identifier */ +#define ASN1_OJD 7 /* Object Description */ +#define ASN1_EXT 8 /* External */ +#define ASN1_ENUM 10 /* Enumerated */ +#define ASN1_SEQ 16 /* Sequence */ +#define ASN1_SET 17 /* Set */ +#define ASN1_NUMSTR 18 /* Numerical String */ +#define ASN1_PRNSTR 19 /* Printable String */ +#define ASN1_TEXSTR 20 /* Teletext String */ +#define ASN1_VIDSTR 21 /* Video String */ +#define ASN1_IA5STR 22 /* IA5 String */ +#define ASN1_UNITIM 23 /* Universal Time */ +#define ASN1_GENTIM 24 /* General Time */ +#define ASN1_GRASTR 25 /* Graphical String */ +#define ASN1_VISSTR 26 /* Visible String */ +#define ASN1_GENSTR 27 /* General String */ + +/* Primitive / Constructed methods*/ +#define ASN1_PRI 0 /* Primitive */ +#define ASN1_CON 1 /* Constructed */ + +/* + * Error codes. + */ +#define ASN1_ERR_NOERROR 0 +#define ASN1_ERR_DEC_EMPTY 2 +#define ASN1_ERR_DEC_EOC_MISMATCH 3 +#define ASN1_ERR_DEC_LENGTH_MISMATCH 4 +#define ASN1_ERR_DEC_BADVALUE 5 + +#define SPNEGO_OID_LEN 7 +#define NTLMSSP_OID_LEN 10 +#define KRB5_OID_LEN 7 +#define KRB5U2U_OID_LEN 8 +#define MSKRB5_OID_LEN 7 +static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 }; +static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 }; +static unsigned long KRB5_OID[7] = { 1, 2, 840, 113554, 1, 2, 2 }; +static unsigned long KRB5U2U_OID[8] = { 1, 2, 840, 113554, 1, 2, 2, 3 }; +static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 }; + +static char NTLMSSP_OID_STR[NTLMSSP_OID_LEN] = { 0x2b, 0x06, 0x01, 0x04, 0x01, + 0x82, 0x37, 0x02, 0x02, 0x0a }; + +/* + * ASN.1 context. + */ +struct asn1_ctx { + int error; /* Error condition */ + unsigned char *pointer; /* Octet just to be decoded */ + unsigned char *begin; /* First octet */ + unsigned char *end; /* Octet after last octet */ +}; + +/* + * Octet string (not null terminated) + */ +struct asn1_octstr { + unsigned char *data; + unsigned int len; +}; + +static void +asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len) +{ + ctx->begin = buf; + ctx->end = buf + len; + ctx->pointer = buf; + ctx->error = ASN1_ERR_NOERROR; +} + +static unsigned char +asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch) +{ + if (ctx->pointer >= ctx->end) { + ctx->error = ASN1_ERR_DEC_EMPTY; + return 0; + } + *ch = *(ctx->pointer)++; + return 1; +} + +static unsigned char +asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag) +{ + unsigned char ch; + + *tag = 0; + + do { + if (!asn1_octet_decode(ctx, &ch)) + return 0; + *tag <<= 7; + *tag |= ch & 0x7F; + } while ((ch & 0x80) == 0x80); + return 1; +} + +static unsigned char +asn1_id_decode(struct asn1_ctx *ctx, + unsigned int *cls, unsigned int *con, unsigned int *tag) +{ + unsigned char ch; + + if (!asn1_octet_decode(ctx, &ch)) + return 0; + + *cls = (ch & 0xC0) >> 6; + *con = (ch & 0x20) >> 5; + *tag = (ch & 0x1F); + + if (*tag == 0x1F) { + if (!asn1_tag_decode(ctx, tag)) + return 0; + } + return 1; +} + +static unsigned char +asn1_length_decode(struct asn1_ctx *ctx, unsigned int *def, unsigned int *len) +{ + unsigned char ch, cnt; + + if (!asn1_octet_decode(ctx, &ch)) + return 0; + + if (ch == 0x80) + *def = 0; + else { + *def = 1; + + if (ch < 0x80) + *len = ch; + else { + cnt = (unsigned char) (ch & 0x7F); + *len = 0; + + while (cnt > 0) { + if (!asn1_octet_decode(ctx, &ch)) + return 0; + *len <<= 8; + *len |= ch; + cnt--; + } + } + } + + /* don't trust len bigger than ctx buffer */ + if (*len > ctx->end - ctx->pointer) + return 0; + + return 1; +} + +static unsigned char +asn1_header_decode(struct asn1_ctx *ctx, + unsigned char **eoc, + unsigned int *cls, unsigned int *con, unsigned int *tag) +{ + unsigned int def = 0; + unsigned int len = 0; + + if (!asn1_id_decode(ctx, cls, con, tag)) + return 0; + + if (!asn1_length_decode(ctx, &def, &len)) + return 0; + + /* primitive shall be definite, indefinite shall be constructed */ + if (*con == ASN1_PRI && !def) + return 0; + + if (def) + *eoc = ctx->pointer + len; + else + *eoc = NULL; + return 1; +} + +static unsigned char +asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc) +{ + unsigned char ch; + + if (!eoc) { + if (!asn1_octet_decode(ctx, &ch)) + return 0; + + if (ch != 0x00) { + ctx->error = ASN1_ERR_DEC_EOC_MISMATCH; + return 0; + } + + if (!asn1_octet_decode(ctx, &ch)) + return 0; + + if (ch != 0x00) { + ctx->error = ASN1_ERR_DEC_EOC_MISMATCH; + return 0; + } + } else { + if (ctx->pointer != eoc) { + ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH; + return 0; + } + } + return 1; +} + +static unsigned char +asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid) +{ + unsigned char ch; + + *subid = 0; + + do { + if (!asn1_octet_decode(ctx, &ch)) + return 0; + + *subid <<= 7; + *subid |= ch & 0x7F; + } while ((ch & 0x80) == 0x80); + return 1; +} + +static int +asn1_oid_decode(struct asn1_ctx *ctx, + unsigned char *eoc, unsigned long **oid, unsigned int *len) +{ + unsigned long subid; + unsigned int size; + unsigned long *optr; + + size = eoc - ctx->pointer + 1; + + /* first subid actually encodes first two subids */ + if (size < 2 || size > UINT_MAX/sizeof(unsigned long)) + return 0; + + *oid = kmalloc(size * sizeof(unsigned long), GFP_KERNEL); + if (!*oid) + return 0; + + optr = *oid; + + if (!asn1_subid_decode(ctx, &subid)) { + kfree(*oid); + *oid = NULL; + return 0; + } + + if (subid < 40) { + optr[0] = 0; + optr[1] = subid; + } else if (subid < 80) { + optr[0] = 1; + optr[1] = subid - 40; + } else { + optr[0] = 2; + optr[1] = subid - 80; + } + + *len = 2; + optr += 2; + + while (ctx->pointer < eoc) { + if (++(*len) > size) { + ctx->error = ASN1_ERR_DEC_BADVALUE; + kfree(*oid); + *oid = NULL; + return 0; + } + + if (!asn1_subid_decode(ctx, optr++)) { + kfree(*oid); + *oid = NULL; + return 0; + } + } + return 1; +} + +static int +compare_oid(unsigned long *oid1, unsigned int oid1len, + unsigned long *oid2, unsigned int oid2len) +{ + unsigned int i; + + if (oid1len != oid2len) + return 0; + + for (i = 0; i < oid1len; i++) { + if (oid1[i] != oid2[i]) + return 0; + } + return 1; +} + +/* BB check for endian conversion issues here */ + +int +ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, + struct ksmbd_conn *conn) +{ + struct asn1_ctx ctx; + unsigned char *end; + unsigned char *sequence_end; + unsigned long *oid = NULL; + unsigned int cls, con, tag, oidlen, rc, mechTokenlen; + unsigned int mech_type; + + ksmbd_debug(AUTH, "Received SecBlob: length %d\n", length); + + asn1_open(&ctx, security_blob, length); + + /* GSSAPI header */ + if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { + ksmbd_debug(AUTH, "Error decoding negTokenInit header\n"); + return 0; + } else if ((cls != ASN1_APL) || (con != ASN1_CON) + || (tag != ASN1_EOC)) { + ksmbd_debug(AUTH, "cls = %d con = %d tag = %d\n", cls, con, + tag); + return 0; + } + + /* Check for SPNEGO OID -- remember to free obj->oid */ + rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag); + if (rc) { + if ((tag == ASN1_OJI) && (con == ASN1_PRI) && + (cls == ASN1_UNI)) { + rc = asn1_oid_decode(&ctx, end, &oid, &oidlen); + if (rc) { + rc = compare_oid(oid, oidlen, SPNEGO_OID, + SPNEGO_OID_LEN); + kfree(oid); + } + } else + rc = 0; + } + + /* SPNEGO OID not present or garbled -- bail out */ + if (!rc) { + ksmbd_debug(AUTH, "Error decoding negTokenInit header\n"); + return 0; + } + + /* SPNEGO */ + if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { + ksmbd_debug(AUTH, "Error decoding negTokenInit\n"); + return 0; + } else if ((cls != ASN1_CTX) || (con != ASN1_CON) + || (tag != ASN1_EOC)) { + ksmbd_debug(AUTH, + "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", + cls, con, tag, end, *end); + return 0; + } + + /* negTokenInit */ + if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { + ksmbd_debug(AUTH, "Error decoding negTokenInit\n"); + return 0; + } else if ((cls != ASN1_UNI) || (con != ASN1_CON) + || (tag != ASN1_SEQ)) { + ksmbd_debug(AUTH, + "cls = %d con = %d tag = %d end = %p (%d) exit 1\n", + cls, con, tag, end, *end); + return 0; + } + + /* sequence */ + if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { + ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n"); + return 0; + } else if ((cls != ASN1_CTX) || (con != ASN1_CON) + || (tag != ASN1_EOC)) { + ksmbd_debug(AUTH, + "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", + cls, con, tag, end, *end); + return 0; + } + + /* sequence of */ + if (asn1_header_decode + (&ctx, &sequence_end, &cls, &con, &tag) == 0) { + ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n"); + return 0; + } else if ((cls != ASN1_UNI) || (con != ASN1_CON) + || (tag != ASN1_SEQ)) { + ksmbd_debug(AUTH, + "cls = %d con = %d tag = %d end = %p (%d) exit 1\n", + cls, con, tag, end, *end); + return 0; + } + + /* list of security mechanisms */ + while (!asn1_eoc_decode(&ctx, sequence_end)) { + rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag); + if (!rc) { + ksmbd_debug(AUTH, + "Error decoding negTokenInit hdr exit2\n"); + return 0; + } + if ((tag == ASN1_OJI) && (con == ASN1_PRI)) { + if (asn1_oid_decode(&ctx, end, &oid, &oidlen)) { + if (compare_oid(oid, oidlen, MSKRB5_OID, + MSKRB5_OID_LEN)) + mech_type = KSMBD_AUTH_MSKRB5; + else if (compare_oid(oid, oidlen, KRB5U2U_OID, + KRB5U2U_OID_LEN)) + mech_type = KSMBD_AUTH_KRB5U2U; + else if (compare_oid(oid, oidlen, KRB5_OID, + KRB5_OID_LEN)) + mech_type = KSMBD_AUTH_KRB5; + else if (compare_oid(oid, oidlen, NTLMSSP_OID, + NTLMSSP_OID_LEN)) + mech_type = KSMBD_AUTH_NTLMSSP; + else { + kfree(oid); + continue; + } + + conn->auth_mechs |= mech_type; + if (conn->preferred_auth_mech == 0) + conn->preferred_auth_mech = mech_type; + kfree(oid); + } + } else { + ksmbd_debug(AUTH, + "Should be an oid what is going on?\n"); + } + } + + /* sequence */ + if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { + ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n"); + return 0; + } else if ((cls != ASN1_CTX) || (con != ASN1_CON) + || (tag != ASN1_INT)) { + ksmbd_debug(AUTH, + "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", + cls, con, tag, end, *end); + return 0; + } + + /* sequence of */ + if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { + ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n"); + return 0; + } else if ((cls != ASN1_UNI) || (con != ASN1_PRI) + || (tag != ASN1_OTS)) { + ksmbd_debug(AUTH, + "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", + cls, con, tag, end, *end); + return 0; + } + + mechTokenlen = ctx.end - ctx.pointer; + conn->mechToken = kmalloc(mechTokenlen + 1, GFP_KERNEL); + if (!conn->mechToken) { + ksmbd_err("memory allocation error\n"); + return 0; + } + + memcpy(conn->mechToken, ctx.pointer, mechTokenlen); + conn->mechToken[mechTokenlen] = '\0'; + + return 1; +} + +int +ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, + struct ksmbd_conn *conn) +{ + struct asn1_ctx ctx; + unsigned char *end; + unsigned int cls, con, tag, mechTokenlen; + + ksmbd_debug(AUTH, "Received Auth SecBlob: length %d\n", length); + + asn1_open(&ctx, security_blob, length); + + /* GSSAPI header */ + if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { + ksmbd_debug(AUTH, "Error decoding negTokenInit header\n"); + return 0; + } else if ((cls != ASN1_CTX) || (con != ASN1_CON) + || (tag != ASN1_BOL)) { + ksmbd_debug(AUTH, "cls = %d con = %d tag = %d\n", cls, con, + tag); + return 0; + } + + /* SPNEGO */ + if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { + ksmbd_debug(AUTH, "Error decoding negTokenInit\n"); + return 0; + } else if ((cls != ASN1_UNI) || (con != ASN1_CON) + || (tag != ASN1_SEQ)) { + ksmbd_debug(AUTH, + "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", + cls, con, tag, end, *end); + return 0; + } + + /* negTokenTarg */ + if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { + ksmbd_debug(AUTH, "Error decoding negTokenInit\n"); + return 0; + } else if ((cls != ASN1_CTX) || (con != ASN1_CON) + || (tag != ASN1_INT)) { + ksmbd_debug(AUTH, + "cls = %d con = %d tag = %d end = %p (%d) exit 1\n", + cls, con, tag, end, *end); + return 0; + } + + /* negTokenTarg */ + if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { + ksmbd_debug(AUTH, "Error decoding negTokenInit\n"); + return 0; + } else if ((cls != ASN1_UNI) || (con != ASN1_PRI) + || (tag != ASN1_OTS)) { + ksmbd_debug(AUTH, + "cls = %d con = %d tag = %d end = %p (%d) exit 1\n", + cls, con, tag, end, *end); + return 0; + } + + mechTokenlen = ctx.end - ctx.pointer; + conn->mechToken = kmalloc(mechTokenlen + 1, GFP_KERNEL); + if (!conn->mechToken) { + ksmbd_err("memory allocation error\n"); + return 0; + } + + memcpy(conn->mechToken, ctx.pointer, mechTokenlen); + conn->mechToken[mechTokenlen] = '\0'; + + return 1; +} + +static int compute_asn_hdr_len_bytes(int len) +{ + if (len > 0xFFFFFF) + return 4; + else if (len > 0xFFFF) + return 3; + else if (len > 0xFF) + return 2; + else if (len > 0x7F) + return 1; + else + return 0; +} + +static void encode_asn_tag(char *buf, + unsigned int *ofs, + char tag, + char seq, + int length) +{ + int i; + int index = *ofs; + char hdr_len = compute_asn_hdr_len_bytes(length); + int len = length + 2 + hdr_len; + + /* insert tag */ + buf[index++] = tag; + + if (!hdr_len) + buf[index++] = len; + else { + buf[index++] = 0x80 | hdr_len; + for (i = hdr_len - 1; i >= 0; i--) + buf[index++] = (len >> (i * 8)) & 0xFF; + } + + /* insert seq */ + len = len - (index - *ofs); + buf[index++] = seq; + + if (!hdr_len) + buf[index++] = len; + else { + buf[index++] = 0x80 | hdr_len; + for (i = hdr_len - 1; i >= 0; i--) + buf[index++] = (len >> (i * 8)) & 0xFF; + } + + *ofs += (index - *ofs); +} + +int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, + char *ntlm_blob, int ntlm_blob_len) +{ + char *buf; + unsigned int ofs = 0; + int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; + int oid_len = 4 + compute_asn_hdr_len_bytes(NTLMSSP_OID_LEN) * 2 + + NTLMSSP_OID_LEN; + int ntlmssp_len = 4 + compute_asn_hdr_len_bytes(ntlm_blob_len) * 2 + + ntlm_blob_len; + int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len + + oid_len + ntlmssp_len) * 2 + + neg_result_len + oid_len + ntlmssp_len; + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* insert main gss header */ + encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len + oid_len + + ntlmssp_len); + + /* insert neg result */ + encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); + buf[ofs++] = 1; + + /* insert oid */ + encode_asn_tag(buf, &ofs, 0xa1, 0x06, NTLMSSP_OID_LEN); + memcpy(buf + ofs, NTLMSSP_OID_STR, NTLMSSP_OID_LEN); + ofs += NTLMSSP_OID_LEN; + + /* insert response token - ntlmssp blob */ + encode_asn_tag(buf, &ofs, 0xa2, 0x04, ntlm_blob_len); + memcpy(buf + ofs, ntlm_blob, ntlm_blob_len); + ofs += ntlm_blob_len; + + *pbuffer = buf; + *buflen = total_len; + return 0; +} + +int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, + int neg_result) +{ + char *buf; + unsigned int ofs = 0; + int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; + int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 + + neg_result_len; + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* insert main gss header */ + encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len); + + /* insert neg result */ + encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); + if (neg_result) + buf[ofs++] = 2; + else + buf[ofs++] = 0; + + *pbuffer = buf; + *buflen = total_len; + return 0; +} diff --git a/fs/cifsd/asn1.h b/fs/cifsd/asn1.h new file mode 100644 index 000000000000..ff2692b502d6 --- /dev/null +++ b/fs/cifsd/asn1.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in + * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich + * + * Copyright (c) 2000 RP Internet (www.rpi.net.au). + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __ASN1_H__ +#define __ASN1_H__ + +int ksmbd_decode_negTokenInit(unsigned char *security_blob, + int length, + struct ksmbd_conn *conn); + +int ksmbd_decode_negTokenTarg(unsigned char *security_blob, + int length, + struct ksmbd_conn *conn); + +int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, + u16 *buflen, + char *ntlm_blob, + int ntlm_blob_len); + +int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, + u16 *buflen, + int neg_result); +#endif /* __ASN1_H__ */ diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c new file mode 100644 index 000000000000..0a49c67a69d6 --- /dev/null +++ b/fs/cifsd/auth.c @@ -0,0 +1,1348 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "auth.h" +#include "glob.h" + +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "connection.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" +#include "crypto_ctx.h" +#include "transport_ipc.h" +#include "buffer_pool.h" + +/* + * Fixed format data defining GSS header and fixed string + * "not_defined_in_RFC4178@please_ignore". + * So sec blob data in neg phase could be generated statically. + */ +static char NEGOTIATE_GSS_HEADER[AUTH_GSS_LENGTH] = { +#ifdef CONFIG_SMB_SERVER_KERBEROS5 + 0x60, 0x5e, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x02, 0xa0, 0x54, 0x30, 0x52, 0xa0, 0x24, + 0x30, 0x22, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, + 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, + 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, 0x30, 0x28, + 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, 0x74, 0x5f, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, + 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, 0x34, 0x31, + 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, 0x61, 0x73, + 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65 +#else + 0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, + 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, + 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, + 0x30, 0x28, 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, + 0x74, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, + 0x34, 0x31, 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, + 0x72, 0x65 +#endif +}; + + +void ksmbd_copy_gss_neg_header(void *buf) +{ + memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); +} + +static void +str_to_key(unsigned char *str, unsigned char *key) +{ + int i; + + key[0] = str[0] >> 1; + key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2); + key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3); + key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4); + key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5); + key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6); + key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7); + key[7] = str[6] & 0x7F; + for (i = 0; i < 8; i++) + key[i] = (key[i] << 1); +} + +static int +smbhash(unsigned char *out, const unsigned char *in, unsigned char *key) +{ + unsigned char key2[8]; + struct des_ctx ctx; + + str_to_key(key, key2); + + if (fips_enabled) { + ksmbd_debug(AUTH, + "FIPS compliance enabled: DES not permitted\n"); + return -ENOENT; + } + + des_expand_key(&ctx, key2, DES_KEY_SIZE); + des_encrypt(&ctx, out, in); + memzero_explicit(&ctx, sizeof(ctx)); + return 0; +} + +static int ksmbd_enc_p24(unsigned char *p21, + const unsigned char *c8, + unsigned char *p24) +{ + int rc; + + rc = smbhash(p24, c8, p21); + if (rc) + return rc; + rc = smbhash(p24 + 8, c8, p21 + 7); + if (rc) + return rc; + rc = smbhash(p24 + 16, c8, p21 + 14); + return rc; +} + +/* produce a md4 message digest from data of length n bytes */ +static int ksmbd_enc_md4(unsigned char *md4_hash, + unsigned char *link_str, + int link_len) +{ + int rc; + struct ksmbd_crypto_ctx *ctx; + + ctx = ksmbd_crypto_ctx_find_md4(); + if (!ctx) { + ksmbd_debug(AUTH, "Crypto md4 allocation error\n"); + return -EINVAL; + } + + rc = crypto_shash_init(CRYPTO_MD4(ctx)); + if (rc) { + ksmbd_debug(AUTH, "Could not init md4 shash\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_MD4(ctx), link_str, link_len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with link_str\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_MD4(ctx), md4_hash); + if (rc) + ksmbd_debug(AUTH, "Could not generate md4 hash\n"); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int ksmbd_enc_update_sess_key(unsigned char *md5_hash, + char *nonce, + char *server_challenge, + int len) +{ + int rc; + struct ksmbd_crypto_ctx *ctx; + + ctx = ksmbd_crypto_ctx_find_md5(); + if (!ctx) { + ksmbd_debug(AUTH, "Crypto md5 allocation error\n"); + return -EINVAL; + } + + rc = crypto_shash_init(CRYPTO_MD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "Could not init md5 shash\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_MD5(ctx), server_challenge, len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with challenge\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_MD5(ctx), nonce, len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with nonce\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_MD5(ctx), md5_hash); + if (rc) + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +/** + * ksmbd_gen_sess_key() - function to generate session key + * @sess: session of connection + * @hash: source hash value to be used for find session key + * @hmac: source hmac value to be used for finding session key + * + */ +static int ksmbd_gen_sess_key(struct ksmbd_session *sess, + char *hash, + char *hmac) +{ + struct ksmbd_crypto_ctx *ctx; + int rc = -EINVAL; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) + goto out; + + rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc); + goto out; + } + + rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc); + goto out; + } + + rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), + hmac, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) { + ksmbd_debug(AUTH, "Could not update with response error %d\n", + rc); + goto out; + } + + rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", + rc); + goto out; + } + +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, + char *dname) +{ + int ret = -EINVAL, len; + wchar_t *domain = NULL; + __le16 *uniname = NULL; + struct ksmbd_crypto_ctx *ctx; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n"); + goto out; + } + + ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + user_passkey(sess->user), + CIFS_ENCPWD_SIZE); + if (ret) { + ksmbd_debug(AUTH, "Could not set NT Hash as a key\n"); + goto out; + } + + ret = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (ret) { + ksmbd_debug(AUTH, "could not init hmacmd5\n"); + goto out; + } + + /* convert user_name to unicode */ + len = strlen(user_name(sess->user)); + uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!uniname) { + ret = -ENOMEM; + goto out; + } + + if (len) { + len = smb_strtoUTF16(uniname, user_name(sess->user), len, + sess->conn->local_nls); + UniStrupr(uniname); + } + + ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), + (char *)uniname, + UNICODE_LEN(len)); + if (ret) { + ksmbd_debug(AUTH, "Could not update with user\n"); + goto out; + } + + /* Convert domain name or conn name to unicode and uppercase */ + len = strlen(dname); + domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!domain) { + ret = -ENOMEM; + goto out; + } + + len = smb_strtoUTF16((__le16 *)domain, dname, len, + sess->conn->local_nls); + + ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), + (char *)domain, + UNICODE_LEN(len)); + if (ret) { + ksmbd_debug(AUTH, "Could not update with domain\n"); + goto out; + } + + ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash); +out: + if (ret) + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); + kfree(uniname); + kfree(domain); + ksmbd_release_crypto_ctx(ctx); + return ret; +} + +/** + * ksmbd_auth_ntlm() - NTLM authentication handler + * @sess: session of connection + * @pw_buf: NTLM challenge response + * @passkey: user password + * + * Return: 0 on success, error number on error + */ +int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf) +{ + int rc; + unsigned char p21[21]; + char key[CIFS_AUTH_RESP_SIZE]; + + memset(p21, '\0', 21); + memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE); + rc = ksmbd_enc_p24(p21, sess->ntlmssp.cryptkey, key); + if (rc) { + ksmbd_err("password processing failed\n"); + return rc; + } + + ksmbd_enc_md4(sess->sess_key, + user_passkey(sess->user), + CIFS_SMB1_SESSKEY_SIZE); + memcpy(sess->sess_key + CIFS_SMB1_SESSKEY_SIZE, key, + CIFS_AUTH_RESP_SIZE); + sess->sequence_number = 1; + + if (strncmp(pw_buf, key, CIFS_AUTH_RESP_SIZE) != 0) { + ksmbd_debug(AUTH, "ntlmv1 authentication failed\n"); + rc = -EINVAL; + } else + ksmbd_debug(AUTH, "ntlmv1 authentication pass\n"); + + return rc; +} + +/** + * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler + * @sess: session of connection + * @ntlmv2: NTLMv2 challenge response + * @blen: NTLMv2 blob length + * @domain_name: domain name + * + * Return: 0 on success, error number on error + */ +int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, + struct ntlmv2_resp *ntlmv2, + int blen, + char *domain_name) +{ + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; + struct ksmbd_crypto_ctx *ctx; + char *construct = NULL; + int rc = -EINVAL, len; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5 rc %d\n", rc); + goto out; + } + + rc = calc_ntlmv2_hash(sess, ntlmv2_hash, domain_name); + if (rc) { + ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); + goto out; + } + + rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + ntlmv2_hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n"); + goto out; + } + + rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "Could not init hmacmd5\n"); + goto out; + } + + len = CIFS_CRYPTO_KEY_SIZE + blen; + construct = kzalloc(len, GFP_KERNEL); + if (!construct) { + rc = -ENOMEM; + goto out; + } + + memcpy(construct, sess->ntlmssp.cryptkey, CIFS_CRYPTO_KEY_SIZE); + memcpy(construct + CIFS_CRYPTO_KEY_SIZE, + (char *)(&ntlmv2->blob_signature), blen); + + rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with response\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp); + if (rc) { + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); + goto out; + } + + rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp); + if (rc) { + ksmbd_debug(AUTH, "Could not generate sess key\n"); + goto out; + } + + rc = memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE); +out: + ksmbd_release_crypto_ctx(ctx); + kfree(construct); + return rc; +} + +/** + * __ksmbd_auth_ntlmv2() - NTLM2(extended security) authentication handler + * @sess: session of connection + * @client_nonce: client nonce from LM response. + * @ntlm_resp: ntlm response data from client. + * + * Return: 0 on success, error number on error + */ +static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess, + char *client_nonce, + char *ntlm_resp) +{ + char sess_key[CIFS_SMB1_SESSKEY_SIZE] = {0}; + int rc; + unsigned char p21[21]; + char key[CIFS_AUTH_RESP_SIZE]; + + rc = ksmbd_enc_update_sess_key(sess_key, + client_nonce, + (char *)sess->ntlmssp.cryptkey, 8); + if (rc) { + ksmbd_err("password processing failed\n"); + goto out; + } + + memset(p21, '\0', 21); + memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE); + rc = ksmbd_enc_p24(p21, sess_key, key); + if (rc) { + ksmbd_err("password processing failed\n"); + goto out; + } + + rc = memcmp(ntlm_resp, key, CIFS_AUTH_RESP_SIZE); +out: + return rc; +} + +/** + * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct + * authenticate blob + * @authblob: authenticate blob source pointer + * @usr: user details + * @sess: session of connection + * + * Return: 0 on success, error number on error + */ +int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + int blob_len, + struct ksmbd_session *sess) +{ + char *domain_name; + unsigned int lm_off, nt_off; + unsigned short nt_len; + int ret; + + if (blob_len < sizeof(struct authenticate_message)) { + ksmbd_debug(AUTH, "negotiate blob len %d too small\n", + blob_len); + return -EINVAL; + } + + if (memcmp(authblob->Signature, "NTLMSSP", 8)) { + ksmbd_debug(AUTH, "blob signature incorrect %s\n", + authblob->Signature); + return -EINVAL; + } + + lm_off = le32_to_cpu(authblob->LmChallengeResponse.BufferOffset); + nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset); + nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length); + + /* process NTLM authentication */ + if (nt_len == CIFS_AUTH_RESP_SIZE) { + if (le32_to_cpu(authblob->NegotiateFlags) + & NTLMSSP_NEGOTIATE_EXTENDED_SEC) + return __ksmbd_auth_ntlmv2(sess, (char *)authblob + + lm_off, (char *)authblob + nt_off); + else + return ksmbd_auth_ntlm(sess, (char *)authblob + + nt_off); + } + + /* TODO : use domain name that imported from configuration file */ + domain_name = smb_strndup_from_utf16( + (const char *)authblob + + le32_to_cpu(authblob->DomainName.BufferOffset), + le16_to_cpu(authblob->DomainName.Length), true, + sess->conn->local_nls); + if (IS_ERR(domain_name)) + return PTR_ERR(domain_name); + + /* process NTLMv2 authentication */ + ksmbd_debug(AUTH, "decode_ntlmssp_authenticate_blob dname%s\n", + domain_name); + ret = ksmbd_auth_ntlmv2(sess, + (struct ntlmv2_resp *)((char *)authblob + nt_off), + nt_len - CIFS_ENCPWD_SIZE, + domain_name); + kfree(domain_name); + return ret; +} + +/** + * ksmbd_decode_ntlmssp_neg_blob() - helper function to construct + * negotiate blob + * @negblob: negotiate blob source pointer + * @rsp: response header pointer to be updated + * @sess: session of connection + * + */ +int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, + int blob_len, + struct ksmbd_session *sess) +{ + if (blob_len < sizeof(struct negotiate_message)) { + ksmbd_debug(AUTH, "negotiate blob len %d too small\n", + blob_len); + return -EINVAL; + } + + if (memcmp(negblob->Signature, "NTLMSSP", 8)) { + ksmbd_debug(AUTH, "blob signature incorrect %s\n", + negblob->Signature); + return -EINVAL; + } + + sess->ntlmssp.client_flags = le32_to_cpu(negblob->NegotiateFlags); + return 0; +} + +/** + * ksmbd_build_ntlmssp_challenge_blob() - helper function to construct + * challenge blob + * @chgblob: challenge blob source pointer to initialize + * @rsp: response header pointer to be updated + * @sess: session of connection + * + */ +unsigned int +ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, + struct ksmbd_session *sess) +{ + struct target_info *tinfo; + wchar_t *name; + __u8 *target_name; + unsigned int len, flags, blob_off, blob_len, type, target_info_len = 0; + int cflags = sess->ntlmssp.client_flags; + + memcpy(chgblob->Signature, NTLMSSP_SIGNATURE, 8); + chgblob->MessageType = NtLmChallenge; + + flags = NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_TARGET_TYPE_SERVER | + NTLMSSP_NEGOTIATE_TARGET_INFO; + + if (cflags & NTLMSSP_NEGOTIATE_SIGN) { + flags |= NTLMSSP_NEGOTIATE_SIGN; + flags |= cflags & (NTLMSSP_NEGOTIATE_128 | + NTLMSSP_NEGOTIATE_56); + } + + if (cflags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; + + if (cflags & NTLMSSP_REQUEST_TARGET) + flags |= NTLMSSP_REQUEST_TARGET; + + if (sess->conn->use_spnego && + (cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) + flags |= NTLMSSP_NEGOTIATE_EXTENDED_SEC; + + chgblob->NegotiateFlags = cpu_to_le32(flags); + len = strlen(ksmbd_netbios_name()); + name = kmalloc(2 + (len * 2), GFP_KERNEL); + if (!name) + return -ENOMEM; + + len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, + sess->conn->local_nls); + len = UNICODE_LEN(len); + + blob_off = sizeof(struct challenge_message); + blob_len = blob_off + len; + + chgblob->TargetName.Length = cpu_to_le16(len); + chgblob->TargetName.MaximumLength = cpu_to_le16(len); + chgblob->TargetName.BufferOffset = cpu_to_le32(blob_off); + + /* Initialize random conn challenge */ + get_random_bytes(sess->ntlmssp.cryptkey, sizeof(__u64)); + memcpy(chgblob->Challenge, sess->ntlmssp.cryptkey, + CIFS_CRYPTO_KEY_SIZE); + + /* Add Target Information to security buffer */ + chgblob->TargetInfoArray.BufferOffset = cpu_to_le32(blob_len); + + target_name = (__u8 *)chgblob + blob_off; + memcpy(target_name, name, len); + tinfo = (struct target_info *)(target_name + len); + + chgblob->TargetInfoArray.Length = 0; + /* Add target info list for NetBIOS/DNS settings */ + for (type = NTLMSSP_AV_NB_COMPUTER_NAME; + type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) { + tinfo->Type = cpu_to_le16(type); + tinfo->Length = cpu_to_le16(len); + memcpy(tinfo->Content, name, len); + tinfo = (struct target_info *)((char *)tinfo + 4 + len); + target_info_len += 4 + len; + } + + /* Add terminator subblock */ + tinfo->Type = 0; + tinfo->Length = 0; + target_info_len += 4; + + chgblob->TargetInfoArray.Length = cpu_to_le16(target_info_len); + chgblob->TargetInfoArray.MaximumLength = cpu_to_le16(target_info_len); + blob_len += target_info_len; + kfree(name); + ksmbd_debug(AUTH, "NTLMSSP SecurityBufferLength %d\n", blob_len); + return blob_len; +} + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, + char *in_blob, int in_len, + char *out_blob, int *out_len) +{ + struct ksmbd_spnego_authen_response *resp; + struct ksmbd_user *user = NULL; + int retval; + + resp = ksmbd_ipc_spnego_authen_request(in_blob, in_len); + if (!resp) { + ksmbd_debug(AUTH, "SPNEGO_AUTHEN_REQUEST failure\n"); + return -EINVAL; + } + + if (!(resp->login_response.status & KSMBD_USER_FLAG_OK)) { + ksmbd_debug(AUTH, "krb5 authentication failure\n"); + retval = -EPERM; + goto out; + } + + if (*out_len <= resp->spnego_blob_len) { + ksmbd_debug(AUTH, "buf len %d, but blob len %d\n", + *out_len, resp->spnego_blob_len); + retval = -EINVAL; + goto out; + } + + if (resp->session_key_len > sizeof(sess->sess_key)) { + ksmbd_debug(AUTH, "session key is too long\n"); + retval = -EINVAL; + goto out; + } + + user = ksmbd_alloc_user(&resp->login_response); + if (!user) { + ksmbd_debug(AUTH, "login failure\n"); + retval = -ENOMEM; + goto out; + } + sess->user = user; + + memcpy(sess->sess_key, resp->payload, resp->session_key_len); + memcpy(out_blob, resp->payload + resp->session_key_len, + resp->spnego_blob_len); + *out_len = resp->spnego_blob_len; + retval = 0; +out: + ksmbd_free(resp); + return retval; +} +#else +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, + char *in_blob, int in_len, + char *out_blob, int *out_len) +{ + return -EOPNOTSUPP; +} +#endif + +/** + * ksmbd_sign_smb2_pdu() - function to generate packet signing + * @conn: connection + * @key: signing key + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, + char *key, + struct kvec *iov, + int n_vec, + char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc = -EINVAL; + int i; + + ctx = ksmbd_crypto_ctx_find_hmacsha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5 rc %d\n", rc); + goto out; + } + + rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), + key, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + goto out; + + rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +/** + * ksmbd_sign_smb3_pdu() - function to generate packet signing + * @conn: connection + * @key: signing key + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, + char *key, + struct kvec *iov, + int n_vec, + char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc = -EINVAL; + int i; + + ctx = ksmbd_crypto_ctx_find_cmacaes(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc cmac rc %d\n", rc); + goto out; + } + + rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx), + key, + SMB2_CMACAES_SIZE); + if (rc) + goto out; + + rc = crypto_shash_init(CRYPTO_CMACAES(ctx)); + if (rc) { + ksmbd_debug(AUTH, "cmaces init error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_CMACAES(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "cmaces update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "cmaces generation error %d\n", rc); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +struct derivation { + struct kvec label; + struct kvec context; + bool binding; +}; + +static int generate_key(struct ksmbd_session *sess, struct kvec label, + struct kvec context, __u8 *key, unsigned int key_size) +{ + unsigned char zero = 0x0; + __u8 i[4] = {0, 0, 0, 1}; + __u8 L[4] = {0, 0, 0, 128}; + int rc = -EINVAL; + unsigned char prfhash[SMB2_HMACSHA256_SIZE]; + unsigned char *hashptr = prfhash; + struct ksmbd_crypto_ctx *ctx; + + memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); + memset(key, 0x0, key_size); + + ctx = ksmbd_crypto_ctx_find_hmacsha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5 rc %d\n", rc); + goto smb3signkey_ret; + } + + rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), + sess->sess_key, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + goto smb3signkey_ret; + + rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + label.iov_base, + label.iov_len); + if (rc) { + ksmbd_debug(AUTH, "could not update with label\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1); + if (rc) { + ksmbd_debug(AUTH, "could not update with zero\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + context.iov_base, + context.iov_len); + if (rc) { + ksmbd_debug(AUTH, "could not update with context\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L, 4); + if (rc) { + ksmbd_debug(AUTH, "could not update with L\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", + rc); + goto smb3signkey_ret; + } + + memcpy(key, hashptr, key_size); + +smb3signkey_ret: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int generate_smb3signingkey(struct ksmbd_session *sess, + const struct derivation *signing) +{ + int rc; + struct channel *chann; + char *key; + + chann = lookup_chann_list(sess); + if (!chann) + return 0; + + if (sess->conn->dialect >= SMB30_PROT_ID && signing->binding) + key = chann->smb3signingkey; + else + key = sess->smb3signingkey; + + rc = generate_key(sess, signing->label, signing->context, key, + SMB3_SIGN_KEY_SIZE); + if (rc) + return rc; + + if (!(sess->conn->dialect >= SMB30_PROT_ID && signing->binding)) + memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE); + + ksmbd_debug(AUTH, "dumping generated AES signing keys\n"); + ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); + ksmbd_debug(AUTH, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + ksmbd_debug(AUTH, "Signing Key %*ph\n", + SMB3_SIGN_KEY_SIZE, key); + return rc; +} + +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess) +{ + struct derivation d; + + d.label.iov_base = "SMB2AESCMAC"; + d.label.iov_len = 12; + d.context.iov_base = "SmbSign"; + d.context.iov_len = 8; + d.binding = false; + + return generate_smb3signingkey(sess, &d); +} + +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess) +{ + struct derivation d; + + d.label.iov_base = "SMBSigningKey"; + d.label.iov_len = 14; + d.context.iov_base = sess->Preauth_HashValue; + d.context.iov_len = 64; + d.binding = false; + + return generate_smb3signingkey(sess, &d); +} + +struct derivation_twin { + struct derivation encryption; + struct derivation decryption; +}; + +static int generate_smb3encryptionkey(struct ksmbd_session *sess, + const struct derivation_twin *ptwin) +{ + int rc; + + rc = generate_key(sess, ptwin->encryption.label, + ptwin->encryption.context, sess->smb3encryptionkey, + SMB3_SIGN_KEY_SIZE); + if (rc) + return rc; + + rc = generate_key(sess, ptwin->decryption.label, + ptwin->decryption.context, + sess->smb3decryptionkey, SMB3_SIGN_KEY_SIZE); + if (rc) + return rc; + + ksmbd_debug(AUTH, "dumping generated AES encryption keys\n"); + ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); + ksmbd_debug(AUTH, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + ksmbd_debug(AUTH, "ServerIn Key %*ph\n", + SMB3_SIGN_KEY_SIZE, sess->smb3encryptionkey); + ksmbd_debug(AUTH, "ServerOut Key %*ph\n", + SMB3_SIGN_KEY_SIZE, sess->smb3decryptionkey); + return rc; +} + +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess) +{ + struct derivation_twin twin; + struct derivation *d; + + d = &twin.encryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerOut"; + d->context.iov_len = 10; + + d = &twin.decryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerIn "; + d->context.iov_len = 10; + + return generate_smb3encryptionkey(sess, &twin); +} + +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess) +{ + struct derivation_twin twin; + struct derivation *d; + + d = &twin.encryption; + d->label.iov_base = "SMBS2CCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = sess->Preauth_HashValue; + d->context.iov_len = 64; + + d = &twin.decryption; + d->label.iov_base = "SMBC2SCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = sess->Preauth_HashValue; + d->context.iov_len = 64; + + return generate_smb3encryptionkey(sess, &twin); +} + +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, + char *buf, + __u8 *pi_hash) +{ + int rc = -1; + struct smb2_hdr *rcv_hdr = (struct smb2_hdr *)buf; + char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; + int msg_size = be32_to_cpu(rcv_hdr->smb2_buf_length); + struct ksmbd_crypto_ctx *ctx = NULL; + + if (conn->preauth_info->Preauth_HashId == + SMB2_PREAUTH_INTEGRITY_SHA512) { + ctx = ksmbd_crypto_ctx_find_sha512(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha512 rc %d\n", rc); + goto out; + } + } else + goto out; + + rc = crypto_shash_init(CRYPTO_SHA512(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init shashn"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); + goto out; + } +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash) +{ + int rc = -1; + struct ksmbd_crypto_ctx *ctx = NULL; + + ctx = ksmbd_crypto_ctx_find_sha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha256 rc %d\n", rc); + goto out; + } + + rc = crypto_shash_init(CRYPTO_SHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init shashn"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); + goto out; + } +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int ksmbd_get_encryption_key(struct ksmbd_conn *conn, + __u64 ses_id, + int enc, + u8 *key) +{ + struct ksmbd_session *sess; + u8 *ses_enc_key; + + sess = ksmbd_session_lookup(conn, ses_id); + if (!sess) + return 1; + + ses_enc_key = enc ? sess->smb3encryptionkey : + sess->smb3decryptionkey; + memcpy(key, ses_enc_key, SMB3_SIGN_KEY_SIZE); + + return 0; +} + +static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, + unsigned int buflen) +{ + void *addr; + + if (is_vmalloc_addr(buf)) + addr = vmalloc_to_page(buf); + else + addr = virt_to_page(buf); + sg_set_page(sg, addr, buflen, offset_in_page(buf)); +} + +static struct scatterlist *ksmbd_init_sg(struct kvec *iov, + unsigned int nvec, + u8 *sign) +{ + struct scatterlist *sg; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; + int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; + + for (i = 0; i < nvec - 1; i++) { + unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; + + if (is_vmalloc_addr(iov[i + 1].iov_base)) { + nr_entries[i] = ((kaddr + iov[i + 1].iov_len + + PAGE_SIZE - 1) >> PAGE_SHIFT) - + (kaddr >> PAGE_SHIFT); + } else + nr_entries[i]++; + total_entries += nr_entries[i]; + } + + /* Add two entries for transform header and signature */ + total_entries += 2; + + sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); + if (!sg) + return NULL; + + sg_init_table(sg, total_entries); + smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len); + for (i = 0; i < nvec - 1; i++) { + void *data = iov[i + 1].iov_base; + int len = iov[i + 1].iov_len; + + if (is_vmalloc_addr(data)) { + int j, offset = offset_in_page(data); + + for (j = 0; j < nr_entries[i]; j++) { + unsigned int bytes = PAGE_SIZE - offset; + + if (len <= 0) + break; + + if (bytes > len) + bytes = len; + + sg_set_page(&sg[sg_idx++], + vmalloc_to_page(data), bytes, + offset_in_page(data)); + + data += bytes; + len -= bytes; + offset = 0; + } + } else { + sg_set_page(&sg[sg_idx++], virt_to_page(data), len, + offset_in_page(data)); + } + + } + smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); + return sg; +} + +int ksmbd_crypt_message(struct ksmbd_conn *conn, + struct kvec *iov, + unsigned int nvec, + int enc) +{ + struct smb2_transform_hdr *tr_hdr = + (struct smb2_transform_hdr *)iov[0].iov_base; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; + int rc = 0; + struct scatterlist *sg; + u8 sign[SMB2_SIGNATURE_SIZE] = {}; + u8 key[SMB3_SIGN_KEY_SIZE]; + struct aead_request *req; + char *iv; + unsigned int iv_len; + struct crypto_aead *tfm; + unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + struct ksmbd_crypto_ctx *ctx; + + rc = ksmbd_get_encryption_key(conn, + le64_to_cpu(tr_hdr->SessionId), + enc, + key); + if (rc) { + ksmbd_err("Could not get %scryption key\n", enc ? "en" : "de"); + return 0; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM) + ctx = ksmbd_crypto_ctx_find_gcm(); + else + ctx = ksmbd_crypto_ctx_find_ccm(); + if (!ctx) { + ksmbd_err("crypto alloc failed\n"); + return -EINVAL; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM) + tfm = CRYPTO_GCM(ctx); + else + tfm = CRYPTO_CCM(ctx); + + rc = crypto_aead_setkey(tfm, key, SMB3_SIGN_KEY_SIZE); + if (rc) { + ksmbd_err("Failed to set aead key %d\n", rc); + goto free_ctx; + } + + rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); + if (rc) { + ksmbd_err("Failed to set authsize %d\n", rc); + goto free_ctx; + } + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + ksmbd_err("Failed to alloc aead request\n"); + rc = -ENOMEM; + goto free_ctx; + } + + if (!enc) { + memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); + crypt_len += SMB2_SIGNATURE_SIZE; + } + + sg = ksmbd_init_sg(iov, nvec, sign); + if (!sg) { + ksmbd_err("Failed to init sg\n"); + rc = -ENOMEM; + goto free_req; + } + + iv_len = crypto_aead_ivsize(tfm); + iv = kzalloc(iv_len, GFP_KERNEL); + if (!iv) { + ksmbd_err("Failed to alloc IV\n"); + rc = -ENOMEM; + goto free_sg; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM) + memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES128GCM_NONCE); + else { + iv[0] = 3; + memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES128CCM_NONCE); + } + + aead_request_set_crypt(req, sg, sg, crypt_len, iv); + aead_request_set_ad(req, assoc_data_len); + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); + + if (enc) + rc = crypto_aead_encrypt(req); + else + rc = crypto_aead_decrypt(req); + if (!rc && enc) + memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); + + kfree(iv); +free_sg: + kfree(sg); +free_req: + kfree(req); +free_ctx: + ksmbd_release_crypto_ctx(ctx); + return rc; +} diff --git a/fs/cifsd/auth.h b/fs/cifsd/auth.h new file mode 100644 index 000000000000..6fcfad5e7e1f --- /dev/null +++ b/fs/cifsd/auth.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __AUTH_H__ +#define __AUTH_H__ + +#include "ntlmssp.h" + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +#define AUTH_GSS_LENGTH 96 +#define AUTH_GSS_PADDING 0 +#else +#define AUTH_GSS_LENGTH 74 +#define AUTH_GSS_PADDING 6 +#endif + +#define CIFS_HMAC_MD5_HASH_SIZE (16) +#define CIFS_NTHASH_SIZE (16) + +/* + * Size of the ntlm client response + */ +#define CIFS_AUTH_RESP_SIZE 24 +#define CIFS_SMB1_SIGNATURE_SIZE 8 +#define CIFS_SMB1_SESSKEY_SIZE 16 + +#define KSMBD_AUTH_NTLMSSP 0x0001 +#define KSMBD_AUTH_KRB5 0x0002 +#define KSMBD_AUTH_MSKRB5 0x0004 +#define KSMBD_AUTH_KRB5U2U 0x0008 + +struct ksmbd_session; +struct ksmbd_conn; +struct kvec; + +int ksmbd_crypt_message(struct ksmbd_conn *conn, + struct kvec *iov, + unsigned int nvec, + int enc); + +void ksmbd_copy_gss_neg_header(void *buf); + +int ksmbd_auth_ntlm(struct ksmbd_session *sess, + char *pw_buf); + +int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, + struct ntlmv2_resp *ntlmv2, + int blen, + char *domain_name); + +int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + int blob_len, + struct ksmbd_session *sess); + +int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, + int blob_len, + struct ksmbd_session *sess); + +unsigned int +ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, + struct ksmbd_session *sess); + +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, + char *in_blob, int in_len, + char *out_blob, int *out_len); + +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, + char *key, + struct kvec *iov, + int n_vec, + char *sig); +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, + char *key, + struct kvec *iov, + int n_vec, + char *sig); + +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess); +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess); +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess); +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess); + +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, + char *buf, + __u8 *pi_hash); +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash); +#endif diff --git a/fs/cifsd/crypto_ctx.c b/fs/cifsd/crypto_ctx.c new file mode 100644 index 000000000000..15d7e2f7c3d7 --- /dev/null +++ b/fs/cifsd/crypto_ctx.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "crypto_ctx.h" +#include "buffer_pool.h" + +struct crypto_ctx_list { + spinlock_t ctx_lock; + int avail_ctx; + struct list_head idle_ctx; + wait_queue_head_t ctx_wait; +}; + +static struct crypto_ctx_list ctx_list; + +static inline void free_aead(struct crypto_aead *aead) +{ + if (aead) + crypto_free_aead(aead); +} + +static void free_shash(struct shash_desc *shash) +{ + if (shash) { + crypto_free_shash(shash->tfm); + kfree(shash); + } +} + +static struct crypto_aead *alloc_aead(int id) +{ + struct crypto_aead *tfm = NULL; + + switch (id) { + case CRYPTO_AEAD_AES128_GCM: + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + break; + case CRYPTO_AEAD_AES128_CCM: + tfm = crypto_alloc_aead("ccm(aes)", 0, 0); + break; + default: + ksmbd_err("Does not support encrypt ahead(id : %d)\n", id); + return NULL; + } + + if (IS_ERR(tfm)) { + ksmbd_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm)); + return NULL; + } + + return tfm; +} + +static struct shash_desc *alloc_shash_desc(int id) +{ + struct crypto_shash *tfm = NULL; + struct shash_desc *shash; + + switch (id) { + case CRYPTO_SHASH_HMACMD5: + tfm = crypto_alloc_shash("hmac(md5)", 0, 0); + break; + case CRYPTO_SHASH_HMACSHA256: + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + break; + case CRYPTO_SHASH_CMACAES: + tfm = crypto_alloc_shash("cmac(aes)", 0, 0); + break; + case CRYPTO_SHASH_SHA256: + tfm = crypto_alloc_shash("sha256", 0, 0); + break; + case CRYPTO_SHASH_SHA512: + tfm = crypto_alloc_shash("sha512", 0, 0); + break; + case CRYPTO_SHASH_MD4: + tfm = crypto_alloc_shash("md4", 0, 0); + break; + case CRYPTO_SHASH_MD5: + tfm = crypto_alloc_shash("md5", 0, 0); + break; + } + + if (IS_ERR(tfm)) + return NULL; + + shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), + GFP_KERNEL); + if (!shash) + crypto_free_shash(tfm); + else + shash->tfm = tfm; + return shash; +} + +static struct ksmbd_crypto_ctx *ctx_alloc(void) +{ + return ksmbd_alloc(sizeof(struct ksmbd_crypto_ctx)); +} + +static void ctx_free(struct ksmbd_crypto_ctx *ctx) +{ + int i; + + for (i = 0; i < CRYPTO_SHASH_MAX; i++) + free_shash(ctx->desc[i]); + for (i = 0; i < CRYPTO_AEAD_MAX; i++) + free_aead(ctx->ccmaes[i]); + ksmbd_free(ctx); +} + +static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) +{ + struct ksmbd_crypto_ctx *ctx; + + while (1) { + spin_lock(&ctx_list.ctx_lock); + if (!list_empty(&ctx_list.idle_ctx)) { + ctx = list_entry(ctx_list.idle_ctx.next, + struct ksmbd_crypto_ctx, + list); + list_del(&ctx->list); + spin_unlock(&ctx_list.ctx_lock); + return ctx; + } + + if (ctx_list.avail_ctx > num_online_cpus()) { + spin_unlock(&ctx_list.ctx_lock); + wait_event(ctx_list.ctx_wait, + !list_empty(&ctx_list.idle_ctx)); + continue; + } + + ctx_list.avail_ctx++; + spin_unlock(&ctx_list.ctx_lock); + + ctx = ctx_alloc(); + if (!ctx) { + spin_lock(&ctx_list.ctx_lock); + ctx_list.avail_ctx--; + spin_unlock(&ctx_list.ctx_lock); + wait_event(ctx_list.ctx_wait, + !list_empty(&ctx_list.idle_ctx)); + continue; + } + break; + } + return ctx; +} + +void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx) +{ + if (!ctx) + return; + + spin_lock(&ctx_list.ctx_lock); + if (ctx_list.avail_ctx <= num_online_cpus()) { + list_add(&ctx->list, &ctx_list.idle_ctx); + spin_unlock(&ctx_list.ctx_lock); + wake_up(&ctx_list.ctx_wait); + return; + } + + ctx_list.avail_ctx--; + spin_unlock(&ctx_list.ctx_lock); + ctx_free(ctx); +} + +static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id) +{ + struct ksmbd_crypto_ctx *ctx; + + if (id >= CRYPTO_SHASH_MAX) + return NULL; + + ctx = ksmbd_find_crypto_ctx(); + if (ctx->desc[id]) + return ctx; + + ctx->desc[id] = alloc_shash_desc(id); + if (ctx->desc[id]) + return ctx; + ksmbd_release_crypto_ctx(ctx); + return NULL; +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD4); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD5); +} + +static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) +{ + struct ksmbd_crypto_ctx *ctx; + + if (id >= CRYPTO_AEAD_MAX) + return NULL; + + ctx = ksmbd_find_crypto_ctx(); + if (ctx->ccmaes[id]) + return ctx; + + ctx->ccmaes[id] = alloc_aead(id); + if (ctx->ccmaes[id]) + return ctx; + ksmbd_release_crypto_ctx(ctx); + return NULL; +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void) +{ + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES128_GCM); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void) +{ + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES128_CCM); +} + +void ksmbd_crypto_destroy(void) +{ + struct ksmbd_crypto_ctx *ctx; + + while (!list_empty(&ctx_list.idle_ctx)) { + ctx = list_entry(ctx_list.idle_ctx.next, + struct ksmbd_crypto_ctx, + list); + list_del(&ctx->list); + ctx_free(ctx); + } +} + +int ksmbd_crypto_create(void) +{ + struct ksmbd_crypto_ctx *ctx; + + spin_lock_init(&ctx_list.ctx_lock); + INIT_LIST_HEAD(&ctx_list.idle_ctx); + init_waitqueue_head(&ctx_list.ctx_wait); + ctx_list.avail_ctx = 1; + + ctx = ctx_alloc(); + if (!ctx) + return -ENOMEM; + list_add(&ctx->list, &ctx_list.idle_ctx); + return 0; +} diff --git a/fs/cifsd/crypto_ctx.h b/fs/cifsd/crypto_ctx.h new file mode 100644 index 000000000000..64a11dfd6c83 --- /dev/null +++ b/fs/cifsd/crypto_ctx.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __CRYPTO_CTX_H__ +#define __CRYPTO_CTX_H__ + +#include +#include + +enum { + CRYPTO_SHASH_HMACMD5 = 0, + CRYPTO_SHASH_HMACSHA256, + CRYPTO_SHASH_CMACAES, + CRYPTO_SHASH_SHA256, + CRYPTO_SHASH_SHA512, + CRYPTO_SHASH_MD4, + CRYPTO_SHASH_MD5, + CRYPTO_SHASH_MAX, +}; + +enum { + CRYPTO_AEAD_AES128_GCM = 16, + CRYPTO_AEAD_AES128_CCM, + CRYPTO_AEAD_MAX, +}; + +enum { + CRYPTO_BLK_ECBDES = 32, + CRYPTO_BLK_MAX, +}; + +struct ksmbd_crypto_ctx { + struct list_head list; + + struct shash_desc *desc[CRYPTO_SHASH_MAX]; + struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX]; +}; + +#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) +#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256]) +#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) +#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256]) +#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512]) +#define CRYPTO_MD4(c) ((c)->desc[CRYPTO_SHASH_MD4]) +#define CRYPTO_MD5(c) ((c)->desc[CRYPTO_SHASH_MD5]) + +#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) +#define CRYPTO_HMACSHA256_TFM(c)\ + ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm) +#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) +#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm) +#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm) +#define CRYPTO_MD4_TFM(c) ((c)->desc[CRYPTO_SHASH_MD4]->tfm) +#define CRYPTO_MD5_TFM(c) ((c)->desc[CRYPTO_SHASH_MD5]->tfm) + +#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES128_GCM]) +#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES128_CCM]) + +void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void); + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); + +void ksmbd_crypto_destroy(void); +int ksmbd_crypto_create(void); + +#endif /* __CRYPTO_CTX_H__ */ diff --git a/fs/cifsd/mgmt/ksmbd_ida.c b/fs/cifsd/mgmt/ksmbd_ida.c new file mode 100644 index 000000000000..cbc9fd049852 --- /dev/null +++ b/fs/cifsd/mgmt/ksmbd_ida.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "ksmbd_ida.h" + +struct ksmbd_ida *ksmbd_ida_alloc(void) +{ + struct ksmbd_ida *ida; + + ida = kmalloc(sizeof(struct ksmbd_ida), GFP_KERNEL); + if (!ida) + return NULL; + + ida_init(&ida->map); + return ida; +} + +void ksmbd_ida_free(struct ksmbd_ida *ida) +{ + if (!ida) + return; + + ida_destroy(&ida->map); + kfree(ida); +} + +static inline int __acquire_id(struct ksmbd_ida *ida, int from, int to) +{ + return ida_simple_get(&ida->map, from, to, GFP_KERNEL); +} + +int ksmbd_acquire_smb2_tid(struct ksmbd_ida *ida) +{ + int id; + + do { + id = __acquire_id(ida, 0, 0); + } while (id == 0xFFFF); + + return id; +} + +int ksmbd_acquire_smb2_uid(struct ksmbd_ida *ida) +{ + int id; + + do { + id = __acquire_id(ida, 1, 0); + } while (id == 0xFFFE); + + return id; +} + +int ksmbd_acquire_async_msg_id(struct ksmbd_ida *ida) +{ + return __acquire_id(ida, 1, 0); +} + +int ksmbd_acquire_id(struct ksmbd_ida *ida) +{ + return __acquire_id(ida, 0, 0); +} + +void ksmbd_release_id(struct ksmbd_ida *ida, int id) +{ + ida_simple_remove(&ida->map, id); +} diff --git a/fs/cifsd/mgmt/ksmbd_ida.h b/fs/cifsd/mgmt/ksmbd_ida.h new file mode 100644 index 000000000000..b075156adf23 --- /dev/null +++ b/fs/cifsd/mgmt/ksmbd_ida.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_IDA_MANAGEMENT_H__ +#define __KSMBD_IDA_MANAGEMENT_H__ + +#include +#include + +struct ksmbd_ida { + struct ida map; +}; + +struct ksmbd_ida *ksmbd_ida_alloc(void); +void ksmbd_ida_free(struct ksmbd_ida *ida); + +/* + * 2.2.1.6.7 TID Generation + * The value 0xFFFF MUST NOT be used as a valid TID. All other + * possible values for TID, including zero (0x0000), are valid. + * The value 0xFFFF is used to specify all TIDs or no TID, + * depending upon the context in which it is used. + */ +int ksmbd_acquire_smb2_tid(struct ksmbd_ida *ida); + +/* + * 2.2.1.6.8 UID Generation + * The value 0xFFFE was declared reserved in the LAN Manager 1.0 + * documentation, so a value of 0xFFFE SHOULD NOT be used as a + * valid UID.<21> All other possible values for a UID, excluding + * zero (0x0000), are valid. + */ +int ksmbd_acquire_smb2_uid(struct ksmbd_ida *ida); +int ksmbd_acquire_async_msg_id(struct ksmbd_ida *ida); + +int ksmbd_acquire_id(struct ksmbd_ida *ida); + +void ksmbd_release_id(struct ksmbd_ida *ida, int id); +#endif /* __KSMBD_IDA_MANAGEMENT_H__ */ diff --git a/fs/cifsd/mgmt/share_config.c b/fs/cifsd/mgmt/share_config.c new file mode 100644 index 000000000000..0593702babfe --- /dev/null +++ b/fs/cifsd/mgmt/share_config.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "share_config.h" +#include "user_config.h" +#include "user_session.h" +#include "../buffer_pool.h" +#include "../transport_ipc.h" +#include "../ksmbd_server.h" /* FIXME */ + +#define SHARE_HASH_BITS 3 +static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); +static DECLARE_RWSEM(shares_table_lock); + +struct ksmbd_veto_pattern { + char *pattern; + struct list_head list; +}; + +static unsigned int share_name_hash(char *name) +{ + return jhash(name, strlen(name), 0); +} + +static void kill_share(struct ksmbd_share_config *share) +{ + while (!list_empty(&share->veto_list)) { + struct ksmbd_veto_pattern *p; + + p = list_entry(share->veto_list.next, + struct ksmbd_veto_pattern, + list); + list_del(&p->list); + kfree(p->pattern); + kfree(p); + } + + if (share->path) + path_put(&share->vfs_path); + kfree(share->name); + kfree(share->path); + kfree(share); +} + +void __ksmbd_share_config_put(struct ksmbd_share_config *share) +{ + down_write(&shares_table_lock); + hash_del(&share->hlist); + up_write(&shares_table_lock); + + kill_share(share); +} + +static struct ksmbd_share_config * +__get_share_config(struct ksmbd_share_config *share) +{ + if (!atomic_inc_not_zero(&share->refcount)) + return NULL; + return share; +} + +static struct ksmbd_share_config *__share_lookup(char *name) +{ + struct ksmbd_share_config *share; + unsigned int key = share_name_hash(name); + + hash_for_each_possible(shares_table, share, hlist, key) { + if (!strcmp(name, share->name)) + return share; + } + return NULL; +} + +static int parse_veto_list(struct ksmbd_share_config *share, + char *veto_list, + int veto_list_sz) +{ + int sz = 0; + + if (!veto_list_sz) + return 0; + + while (veto_list_sz > 0) { + struct ksmbd_veto_pattern *p; + + p = ksmbd_alloc(sizeof(struct ksmbd_veto_pattern)); + if (!p) + return -ENOMEM; + + sz = strlen(veto_list); + if (!sz) + break; + + p->pattern = kstrdup(veto_list, GFP_KERNEL); + if (!p->pattern) { + ksmbd_free(p); + return -ENOMEM; + } + + list_add(&p->list, &share->veto_list); + + veto_list += sz + 1; + veto_list_sz -= (sz + 1); + } + + return 0; +} + +static struct ksmbd_share_config *share_config_request(char *name) +{ + struct ksmbd_share_config_response *resp; + struct ksmbd_share_config *share = NULL; + struct ksmbd_share_config *lookup; + int ret; + + resp = ksmbd_ipc_share_config_request(name); + if (!resp) + return NULL; + + if (resp->flags == KSMBD_SHARE_FLAG_INVALID) + goto out; + + share = ksmbd_alloc(sizeof(struct ksmbd_share_config)); + if (!share) + goto out; + + share->flags = resp->flags; + atomic_set(&share->refcount, 1); + INIT_LIST_HEAD(&share->veto_list); + share->name = kstrdup(name, GFP_KERNEL); + + if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + share->path = kstrdup(KSMBD_SHARE_CONFIG_PATH(resp), + GFP_KERNEL); + if (share->path) + share->path_sz = strlen(share->path); + share->create_mask = resp->create_mask; + share->directory_mask = resp->directory_mask; + share->force_create_mode = resp->force_create_mode; + share->force_directory_mode = resp->force_directory_mode; + share->force_uid = resp->force_uid; + share->force_gid = resp->force_gid; + ret = parse_veto_list(share, + KSMBD_SHARE_CONFIG_VETO_LIST(resp), + resp->veto_list_sz); + if (!ret && share->path) { + ret = kern_path(share->path, 0, &share->vfs_path); + if (ret) { + ksmbd_debug(SMB, "failed to access '%s'\n", + share->path); + /* Avoid put_path() */ + kfree(share->path); + share->path = NULL; + } + } + if (ret || !share->name) { + kill_share(share); + share = NULL; + goto out; + } + } + + down_write(&shares_table_lock); + lookup = __share_lookup(name); + if (lookup) + lookup = __get_share_config(lookup); + if (!lookup) { + hash_add(shares_table, &share->hlist, share_name_hash(name)); + } else { + kill_share(share); + share = lookup; + } + up_write(&shares_table_lock); + +out: + ksmbd_free(resp); + return share; +} + +static void strtolower(char *share_name) +{ + while (*share_name) { + *share_name = tolower(*share_name); + share_name++; + } +} + +struct ksmbd_share_config *ksmbd_share_config_get(char *name) +{ + struct ksmbd_share_config *share; + + strtolower(name); + + down_read(&shares_table_lock); + share = __share_lookup(name); + if (share) + share = __get_share_config(share); + up_read(&shares_table_lock); + + if (share) + return share; + return share_config_request(name); +} + +bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, + const char *filename) +{ + struct ksmbd_veto_pattern *p; + + list_for_each_entry(p, &share->veto_list, list) { + if (match_wildcard(p->pattern, filename)) + return true; + } + return false; +} + +void ksmbd_share_configs_cleanup(void) +{ + struct ksmbd_share_config *share; + struct hlist_node *tmp; + int i; + + down_write(&shares_table_lock); + hash_for_each_safe(shares_table, i, tmp, share, hlist) { + hash_del(&share->hlist); + kill_share(share); + } + up_write(&shares_table_lock); +} diff --git a/fs/cifsd/mgmt/share_config.h b/fs/cifsd/mgmt/share_config.h new file mode 100644 index 000000000000..c47b874bd80b --- /dev/null +++ b/fs/cifsd/mgmt/share_config.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SHARE_CONFIG_MANAGEMENT_H__ +#define __SHARE_CONFIG_MANAGEMENT_H__ + +#include +#include +#include + +#include "../glob.h" /* FIXME */ + +struct ksmbd_share_config { + char *name; + char *path; + + unsigned int path_sz; + unsigned int flags; + struct list_head veto_list; + + struct path vfs_path; + + atomic_t refcount; + struct hlist_node hlist; + unsigned short create_mask; + unsigned short directory_mask; + unsigned short force_create_mode; + unsigned short force_directory_mode; + unsigned short force_uid; + unsigned short force_gid; +}; + +#define KSMBD_SHARE_INVALID_UID ((__u16)-1) +#define KSMBD_SHARE_INVALID_GID ((__u16)-1) + +static inline int share_config_create_mode(struct ksmbd_share_config *share, + umode_t posix_mode) +{ + if (!share->force_create_mode) { + if (!posix_mode) + return share->create_mask; + else + return posix_mode & share->create_mask; + } + return share->force_create_mode & share->create_mask; +} + +static inline int share_config_directory_mode(struct ksmbd_share_config *share, + umode_t posix_mode) +{ + if (!share->force_directory_mode) { + if (!posix_mode) + return share->directory_mask; + else + return posix_mode & share->directory_mask; + } + + return share->force_directory_mode & share->directory_mask; +} + +static inline int test_share_config_flag(struct ksmbd_share_config *share, + int flag) +{ + return share->flags & flag; +} + +extern void __ksmbd_share_config_put(struct ksmbd_share_config *share); + +static inline void ksmbd_share_config_put(struct ksmbd_share_config *share) +{ + if (!atomic_dec_and_test(&share->refcount)) + return; + __ksmbd_share_config_put(share); +} + +struct ksmbd_share_config *ksmbd_share_config_get(char *name); +bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, + const char *filename); +void ksmbd_share_configs_cleanup(void); + +#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/cifsd/mgmt/tree_connect.c b/fs/cifsd/mgmt/tree_connect.c new file mode 100644 index 000000000000..2be7b2e2e3cd --- /dev/null +++ b/fs/cifsd/mgmt/tree_connect.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include + +#include "../ksmbd_server.h" /* FIXME */ +#include "../buffer_pool.h" +#include "../transport_ipc.h" +#include "../connection.h" + +#include "tree_connect.h" +#include "user_config.h" +#include "share_config.h" +#include "user_session.h" + +struct ksmbd_tree_conn_status +ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name) +{ + struct ksmbd_tree_conn_status status = {-EINVAL, NULL}; + struct ksmbd_tree_connect_response *resp = NULL; + struct ksmbd_share_config *sc; + struct ksmbd_tree_connect *tree_conn = NULL; + struct sockaddr *peer_addr; + + sc = ksmbd_share_config_get(share_name); + if (!sc) + return status; + + tree_conn = ksmbd_alloc(sizeof(struct ksmbd_tree_connect)); + if (!tree_conn) { + status.ret = -ENOMEM; + goto out_error; + } + + tree_conn->id = ksmbd_acquire_tree_conn_id(sess); + if (tree_conn->id < 0) { + status.ret = -EINVAL; + goto out_error; + } + + peer_addr = KSMBD_TCP_PEER_SOCKADDR(sess->conn); + resp = ksmbd_ipc_tree_connect_request(sess, + sc, + tree_conn, + peer_addr); + if (!resp) { + status.ret = -EINVAL; + goto out_error; + } + + status.ret = resp->status; + if (status.ret != KSMBD_TREE_CONN_STATUS_OK) + goto out_error; + + tree_conn->flags = resp->connection_flags; + tree_conn->user = sess->user; + tree_conn->share_conf = sc; + status.tree_conn = tree_conn; + + list_add(&tree_conn->list, &sess->tree_conn_list); + + ksmbd_free(resp); + return status; + +out_error: + if (tree_conn) + ksmbd_release_tree_conn_id(sess, tree_conn->id); + ksmbd_share_config_put(sc); + ksmbd_free(tree_conn); + ksmbd_free(resp); + return status; +} + +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn) +{ + int ret; + + ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); + ksmbd_release_tree_conn_id(sess, tree_conn->id); + list_del(&tree_conn->list); + ksmbd_share_config_put(tree_conn->share_conf); + ksmbd_free(tree_conn); + return ret; +} + +struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id) +{ + struct ksmbd_tree_connect *tree_conn; + struct list_head *tmp; + + list_for_each(tmp, &sess->tree_conn_list) { + tree_conn = list_entry(tmp, struct ksmbd_tree_connect, list); + if (tree_conn->id == id) + return tree_conn; + } + return NULL; +} + +struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, + unsigned int id) +{ + struct ksmbd_tree_connect *tc; + + tc = ksmbd_tree_conn_lookup(sess, id); + if (tc) + return tc->share_conf; + return NULL; +} + +int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) +{ + int ret = 0; + + while (!list_empty(&sess->tree_conn_list)) { + struct ksmbd_tree_connect *tc; + + tc = list_entry(sess->tree_conn_list.next, + struct ksmbd_tree_connect, + list); + ret |= ksmbd_tree_conn_disconnect(sess, tc); + } + + return ret; +} diff --git a/fs/cifsd/mgmt/tree_connect.h b/fs/cifsd/mgmt/tree_connect.h new file mode 100644 index 000000000000..46237cd05b9c --- /dev/null +++ b/fs/cifsd/mgmt/tree_connect.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __TREE_CONNECT_MANAGEMENT_H__ +#define __TREE_CONNECT_MANAGEMENT_H__ + +#include + +#include "../ksmbd_server.h" /* FIXME */ + +struct ksmbd_share_config; +struct ksmbd_user; + +struct ksmbd_tree_connect { + int id; + + unsigned int flags; + struct ksmbd_share_config *share_conf; + struct ksmbd_user *user; + + struct list_head list; + + int maximal_access; + bool posix_extensions; +}; + +struct ksmbd_tree_conn_status { + unsigned int ret; + struct ksmbd_tree_connect *tree_conn; +}; + +static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn, + int flag) +{ + return tree_conn->flags & flag; +} + +struct ksmbd_session; + +struct ksmbd_tree_conn_status +ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name); + +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn); + +struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id); + +struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, + unsigned int id); + +int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess); + +#endif /* __TREE_CONNECT_MANAGEMENT_H__ */ diff --git a/fs/cifsd/mgmt/user_config.c b/fs/cifsd/mgmt/user_config.c new file mode 100644 index 000000000000..1ab68f80f72e --- /dev/null +++ b/fs/cifsd/mgmt/user_config.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "user_config.h" +#include "../buffer_pool.h" +#include "../transport_ipc.h" +#include "../ksmbd_server.h" /* FIXME */ + +struct ksmbd_user *ksmbd_login_user(const char *account) +{ + struct ksmbd_login_response *resp; + struct ksmbd_user *user = NULL; + + resp = ksmbd_ipc_login_request(account); + if (!resp) + return NULL; + + if (!(resp->status & KSMBD_USER_FLAG_OK)) + goto out; + + user = ksmbd_alloc_user(resp); +out: + ksmbd_free(resp); + return user; +} + +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) +{ + struct ksmbd_user *user = NULL; + + user = ksmbd_alloc(sizeof(struct ksmbd_user)); + if (!user) + return NULL; + + user->name = kstrdup(resp->account, GFP_KERNEL); + user->flags = resp->status; + user->gid = resp->gid; + user->uid = resp->uid; + user->passkey_sz = resp->hash_sz; + user->passkey = ksmbd_alloc(resp->hash_sz); + if (user->passkey) + memcpy(user->passkey, resp->hash, resp->hash_sz); + + if (!user->name || !user->passkey) { + kfree(user->name); + ksmbd_free(user->passkey); + ksmbd_free(user); + user = NULL; + } + return user; +} + +void ksmbd_free_user(struct ksmbd_user *user) +{ + ksmbd_ipc_logout_request(user->name); + kfree(user->name); + ksmbd_free(user->passkey); + ksmbd_free(user); +} + +int ksmbd_anonymous_user(struct ksmbd_user *user) +{ + if (user->name[0] == '\0') + return 1; + return 0; +} diff --git a/fs/cifsd/mgmt/user_config.h b/fs/cifsd/mgmt/user_config.h new file mode 100644 index 000000000000..5cda4a5d3e2f --- /dev/null +++ b/fs/cifsd/mgmt/user_config.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __USER_CONFIG_MANAGEMENT_H__ +#define __USER_CONFIG_MANAGEMENT_H__ + +#include "../glob.h" /* FIXME */ +#include "../ksmbd_server.h" /* FIXME */ + +struct ksmbd_user { + unsigned short flags; + + unsigned int uid; + unsigned int gid; + + char *name; + + size_t passkey_sz; + char *passkey; +}; + +static inline bool user_guest(struct ksmbd_user *user) +{ + return user->flags & KSMBD_USER_FLAG_GUEST_ACCOUNT; +} + +static inline void set_user_flag(struct ksmbd_user *user, int flag) +{ + user->flags |= flag; +} + +static inline int test_user_flag(struct ksmbd_user *user, int flag) +{ + return user->flags & flag; +} + +static inline void set_user_guest(struct ksmbd_user *user) +{ +} + +static inline char *user_passkey(struct ksmbd_user *user) +{ + return user->passkey; +} + +static inline char *user_name(struct ksmbd_user *user) +{ + return user->name; +} + +static inline unsigned int user_uid(struct ksmbd_user *user) +{ + return user->uid; +} + +static inline unsigned int user_gid(struct ksmbd_user *user) +{ + return user->gid; +} + +struct ksmbd_user *ksmbd_login_user(const char *account); +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp); +void ksmbd_free_user(struct ksmbd_user *user); +int ksmbd_anonymous_user(struct ksmbd_user *user); +#endif /* __USER_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c new file mode 100644 index 000000000000..d9f6dbde850a --- /dev/null +++ b/fs/cifsd/mgmt/user_session.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "ksmbd_ida.h" +#include "user_session.h" +#include "user_config.h" +#include "tree_connect.h" +#include "../transport_ipc.h" +#include "../connection.h" +#include "../buffer_pool.h" +#include "../ksmbd_server.h" /* FIXME */ +#include "../vfs_cache.h" + +static struct ksmbd_ida *session_ida; + +#define SESSION_HASH_BITS 3 +static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS); +static DECLARE_RWSEM(sessions_table_lock); + +struct ksmbd_session_rpc { + int id; + unsigned int method; + struct list_head list; +}; + +static void free_channel_list(struct ksmbd_session *sess) +{ + struct channel *chann; + struct list_head *tmp, *t; + + list_for_each_safe(tmp, t, &sess->ksmbd_chann_list) { + chann = list_entry(tmp, struct channel, chann_list); + if (chann) { + list_del(&chann->chann_list); + kfree(chann); + } + } +} + +static void __session_rpc_close(struct ksmbd_session *sess, + struct ksmbd_session_rpc *entry) +{ + struct ksmbd_rpc_command *resp; + + resp = ksmbd_rpc_close(sess, entry->id); + if (!resp) + pr_err("Unable to close RPC pipe %d\n", entry->id); + + ksmbd_free(resp); + ksmbd_rpc_id_free(entry->id); + ksmbd_free(entry); +} + +static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) +{ + struct ksmbd_session_rpc *entry; + + while (!list_empty(&sess->rpc_handle_list)) { + entry = list_entry(sess->rpc_handle_list.next, + struct ksmbd_session_rpc, + list); + + list_del(&entry->list); + __session_rpc_close(sess, entry); + } +} + +static int __rpc_method(char *rpc_name) +{ + if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc")) + return KSMBD_RPC_SRVSVC_METHOD_INVOKE; + + if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc")) + return KSMBD_RPC_WKSSVC_METHOD_INVOKE; + + if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman")) + return KSMBD_RPC_RAP_METHOD; + + if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr")) + return KSMBD_RPC_SAMR_METHOD_INVOKE; + + if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc")) + return KSMBD_RPC_LSARPC_METHOD_INVOKE; + + ksmbd_err("Unsupported RPC: %s\n", rpc_name); + return 0; +} + +int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) +{ + struct ksmbd_session_rpc *entry; + struct ksmbd_rpc_command *resp; + int method; + + method = __rpc_method(rpc_name); + if (!method) + return -EINVAL; + + entry = ksmbd_alloc(sizeof(struct ksmbd_session_rpc)); + if (!entry) + return -EINVAL; + + list_add(&entry->list, &sess->rpc_handle_list); + entry->method = method; + entry->id = ksmbd_ipc_id_alloc(); + if (entry->id < 0) + goto error; + + resp = ksmbd_rpc_open(sess, entry->id); + if (!resp) + goto error; + + ksmbd_free(resp); + return entry->id; +error: + list_del(&entry->list); + ksmbd_free(entry); + return -EINVAL; +} + +void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) +{ + struct ksmbd_session_rpc *entry; + + list_for_each_entry(entry, &sess->rpc_handle_list, list) { + if (entry->id == id) { + list_del(&entry->list); + __session_rpc_close(sess, entry); + break; + } + } +} + +int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) +{ + struct ksmbd_session_rpc *entry; + + list_for_each_entry(entry, &sess->rpc_handle_list, list) { + if (entry->id == id) + return entry->method; + } + return 0; +} + +void ksmbd_session_destroy(struct ksmbd_session *sess) +{ + if (!sess) + return; + + if (!atomic_dec_and_test(&sess->refcnt)) + return; + + list_del(&sess->sessions_entry); + + if (IS_SMB2(sess->conn)) { + down_write(&sessions_table_lock); + hash_del(&sess->hlist); + up_write(&sessions_table_lock); + } + + if (sess->user) + ksmbd_free_user(sess->user); + + ksmbd_tree_conn_session_logoff(sess); + ksmbd_destroy_file_table(&sess->file_table); + ksmbd_session_rpc_clear_list(sess); + free_channel_list(sess); + kfree(sess->Preauth_HashValue); + ksmbd_release_id(session_ida, sess->id); + + ksmbd_ida_free(sess->tree_conn_ida); + ksmbd_free(sess); +} + +static struct ksmbd_session *__session_lookup(unsigned long long id) +{ + struct ksmbd_session *sess; + + hash_for_each_possible(sessions_table, sess, hlist, id) { + if (id == sess->id) + return sess; + } + return NULL; +} + +void ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + sess->conn = conn; + list_add(&sess->sessions_entry, &conn->sessions); +} + +void ksmbd_sessions_deregister(struct ksmbd_conn *conn) +{ + struct ksmbd_session *sess; + + while (!list_empty(&conn->sessions)) { + sess = list_entry(conn->sessions.next, + struct ksmbd_session, + sessions_entry); + + ksmbd_session_destroy(sess); + } +} + +bool ksmbd_session_id_match(struct ksmbd_session *sess, unsigned long long id) +{ + return sess->id == id; +} + +struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct ksmbd_session *sess = NULL; + + list_for_each_entry(sess, &conn->sessions, sessions_entry) { + if (ksmbd_session_id_match(sess, id)) + return sess; + } + return NULL; +} + +int get_session(struct ksmbd_session *sess) +{ + return atomic_inc_not_zero(&sess->refcnt); +} + +void put_session(struct ksmbd_session *sess) +{ + if (atomic_dec_and_test(&sess->refcnt)) + ksmbd_err("get/%s seems to be mismatched.", __func__); +} + +struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) +{ + struct ksmbd_session *sess; + + down_read(&sessions_table_lock); + sess = __session_lookup(id); + if (sess) { + if (!get_session(sess)) + sess = NULL; + } + up_read(&sessions_table_lock); + + return sess; +} + +static int __init_smb2_session(struct ksmbd_session *sess) +{ + int id = ksmbd_acquire_smb2_uid(session_ida); + + if (id < 0) + return -EINVAL; + sess->id = id; + return 0; +} + +static struct ksmbd_session *__session_create(int protocol) +{ + struct ksmbd_session *sess; + int ret; + + sess = ksmbd_alloc(sizeof(struct ksmbd_session)); + if (!sess) + return NULL; + + if (ksmbd_init_file_table(&sess->file_table)) + goto error; + + set_session_flag(sess, protocol); + INIT_LIST_HEAD(&sess->sessions_entry); + INIT_LIST_HEAD(&sess->tree_conn_list); + INIT_LIST_HEAD(&sess->ksmbd_chann_list); + INIT_LIST_HEAD(&sess->rpc_handle_list); + sess->sequence_number = 1; + atomic_set(&sess->refcnt, 1); + + switch (protocol) { + case CIFDS_SESSION_FLAG_SMB2: + ret = __init_smb2_session(sess); + break; + default: + ret = -EINVAL; + break; + } + + if (ret) + goto error; + + sess->tree_conn_ida = ksmbd_ida_alloc(); + if (!sess->tree_conn_ida) + goto error; + + if (protocol == CIFDS_SESSION_FLAG_SMB2) { + down_read(&sessions_table_lock); + hash_add(sessions_table, &sess->hlist, sess->id); + up_read(&sessions_table_lock); + } + return sess; + +error: + ksmbd_session_destroy(sess); + return NULL; +} + +struct ksmbd_session *ksmbd_smb2_session_create(void) +{ + return __session_create(CIFDS_SESSION_FLAG_SMB2); +} + +int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess) +{ + int id = -EINVAL; + + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) + id = ksmbd_acquire_smb2_tid(sess->tree_conn_ida); + + return id; +} + +void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id) +{ + if (id >= 0) + ksmbd_release_id(sess->tree_conn_ida, id); +} + +int ksmbd_init_session_table(void) +{ + session_ida = ksmbd_ida_alloc(); + if (!session_ida) + return -ENOMEM; + return 0; +} + +void ksmbd_free_session_table(void) +{ + ksmbd_ida_free(session_ida); +} diff --git a/fs/cifsd/mgmt/user_session.h b/fs/cifsd/mgmt/user_session.h new file mode 100644 index 000000000000..0a6eb21647ab --- /dev/null +++ b/fs/cifsd/mgmt/user_session.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __USER_SESSION_MANAGEMENT_H__ +#define __USER_SESSION_MANAGEMENT_H__ + +#include + +#include "../smb_common.h" +#include "../ntlmssp.h" + +#define CIFDS_SESSION_FLAG_SMB2 (1 << 1) + +#define PREAUTH_HASHVALUE_SIZE 64 + +struct ksmbd_ida; +struct ksmbd_file_table; + +struct channel { + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + struct ksmbd_conn *conn; + struct list_head chann_list; +}; + +struct preauth_session { + __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; + uint64_t sess_id; + struct list_head list_entry; +}; + +struct ksmbd_session { + uint64_t id; + + struct ksmbd_user *user; + struct ksmbd_conn *conn; + unsigned int sequence_number; + unsigned int flags; + + bool sign; + bool enc; + bool is_anonymous; + + int state; + __u8 *Preauth_HashValue; + + struct ntlmssp_auth ntlmssp; + char sess_key[CIFS_KEY_SIZE]; + + struct hlist_node hlist; + struct list_head ksmbd_chann_list; + struct list_head tree_conn_list; + struct ksmbd_ida *tree_conn_ida; + struct list_head rpc_handle_list; + + __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + + struct list_head sessions_entry; + struct ksmbd_file_table file_table; + atomic_t refcnt; +}; + +static inline int test_session_flag(struct ksmbd_session *sess, int bit) +{ + return sess->flags & bit; +} + +static inline void set_session_flag(struct ksmbd_session *sess, int bit) +{ + sess->flags |= bit; +} + +static inline void clear_session_flag(struct ksmbd_session *sess, int bit) +{ + sess->flags &= ~bit; +} + +struct ksmbd_session *ksmbd_smb2_session_create(void); + +void ksmbd_session_destroy(struct ksmbd_session *sess); + +bool ksmbd_session_id_match(struct ksmbd_session *sess, unsigned long long id); +struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id); +struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + unsigned long long id); +void ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +void ksmbd_sessions_deregister(struct ksmbd_conn *conn); + +int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess); +void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id); + +int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name); +void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); +int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); +int get_session(struct ksmbd_session *sess); +void put_session(struct ksmbd_session *sess); + +int ksmbd_init_session_table(void); +void ksmbd_free_session_table(void); + +#endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/cifsd/misc.c b/fs/cifsd/misc.c new file mode 100644 index 000000000000..9e689c33f7bb --- /dev/null +++ b/fs/cifsd/misc.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "misc.h" +#include "smb_common.h" +#include "connection.h" +#include "vfs.h" + +#include "mgmt/share_config.h" + +/** + * match_pattern() - compare a string with a pattern which might include + * wildcard '*' and '?' + * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR + * + * @string: string to compare with a pattern + * @pattern: pattern string which might include wildcard '*' and '?' + * + * Return: 0 if pattern matched with the string, otherwise non zero value + */ +int match_pattern(const char *str, const char *pattern) +{ + const char *s = str; + const char *p = pattern; + bool star = false; + + while (*s) { + switch (*p) { + case '?': + s++; + p++; + break; + case '*': + star = true; + str = s; + if (!*++p) + return true; + pattern = p; + break; + default: + if (tolower(*s) == tolower(*p)) { + s++; + p++; + } else { + if (!star) + return false; + str++; + s = str; + p = pattern; + } + break; + } + } + + if (*p == '*') + ++p; + return !*p; +} + +/* + * is_char_allowed() - check for valid character + * @ch: input character to be checked + * + * Return: 1 if char is allowed, otherwise 0 + */ +static inline int is_char_allowed(char ch) +{ + /* check for control chars, wildcards etc. */ + if (!(ch & 0x80) && + (ch <= 0x1f || + ch == '?' || ch == '"' || ch == '<' || + ch == '>' || ch == '|' || ch == '*')) + return 0; + + return 1; +} + +int ksmbd_validate_filename(char *filename) +{ + while (*filename) { + char c = *filename; + + filename++; + if (!is_char_allowed(c)) { + ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c); + return -ENOENT; + } + } + + return 0; +} + +static int ksmbd_validate_stream_name(char *stream_name) +{ + while (*stream_name) { + char c = *stream_name; + + stream_name++; + if (c == '/' || c == ':' || c == '\\') { + ksmbd_err("Stream name validation failed: %c\n", c); + return -ENOENT; + } + } + + return 0; +} + +int parse_stream_name(char *filename, char **stream_name, int *s_type) +{ + char *stream_type; + char *s_name; + int rc = 0; + + s_name = filename; + filename = strsep(&s_name, ":"); + ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name); + if (strchr(s_name, ':')) { + stream_type = s_name; + s_name = strsep(&stream_type, ":"); + + rc = ksmbd_validate_stream_name(s_name); + if (rc < 0) { + rc = -ENOENT; + goto out; + } + + ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name, + stream_type); + if (!strncasecmp("$data", stream_type, 5)) + *s_type = DATA_STREAM; + else if (!strncasecmp("$index_allocation", stream_type, 17)) + *s_type = DIR_STREAM; + else + rc = -ENOENT; + } + + *stream_name = s_name; +out: + return rc; +} + +/** + * convert_to_nt_pathname() - extract and return windows path string + * whose share directory prefix was removed from file path + * @filename : unix filename + * @sharepath: share path string + * + * Return : windows path string or error + */ + +char *convert_to_nt_pathname(char *filename, char *sharepath) +{ + char *ab_pathname; + int len, name_len; + + name_len = strlen(filename); + ab_pathname = kmalloc(name_len, GFP_KERNEL); + if (!ab_pathname) + return NULL; + + ab_pathname[0] = '\\'; + ab_pathname[1] = '\0'; + + len = strlen(sharepath); + if (!strncmp(filename, sharepath, len) && name_len != len) { + strscpy(ab_pathname, &filename[len], name_len); + ksmbd_conv_path_to_windows(ab_pathname); + } + + return ab_pathname; +} + +int get_nlink(struct kstat *st) +{ + int nlink; + + nlink = st->nlink; + if (S_ISDIR(st->mode)) + nlink--; + + return nlink; +} + +void ksmbd_conv_path_to_unix(char *path) +{ + strreplace(path, '\\', '/'); +} + +void ksmbd_strip_last_slash(char *path) +{ + int len = strlen(path); + + while (len && path[len - 1] == '/') { + path[len - 1] = '\0'; + len--; + } +} + +void ksmbd_conv_path_to_windows(char *path) +{ + strreplace(path, '/', '\\'); +} + +/** + * extract_sharename() - get share name from tree connect request + * @treename: buffer containing tree name and share name + * + * Return: share name on success, otherwise error + */ +char *extract_sharename(char *treename) +{ + char *name = treename; + char *dst; + char *pos = strrchr(name, '\\'); + + if (pos) + name = (pos + 1); + + /* caller has to free the memory */ + dst = kstrdup(name, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + return dst; +} + +/** + * convert_to_unix_name() - convert windows name to unix format + * @path: name to be converted + * @tid: tree id of mathing share + * + * Return: converted name on success, otherwise NULL + */ +char *convert_to_unix_name(struct ksmbd_share_config *share, char *name) +{ + int no_slash = 0, name_len, path_len; + char *new_name; + + if (name[0] == '/') + name++; + + path_len = share->path_sz; + name_len = strlen(name); + new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); + if (!new_name) + return new_name; + + memcpy(new_name, share->path, path_len); + if (new_name[path_len - 1] != '/') { + new_name[path_len] = '/'; + no_slash = 1; + } + + memcpy(new_name + path_len + no_slash, name, name_len); + path_len += name_len + no_slash; + new_name[path_len] = 0x00; + return new_name; +} + +char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, + const struct nls_table *local_nls, + int *conv_len) +{ + char *conv; + int sz = min(4 * d_info->name_len, PATH_MAX); + + if (!sz) + return NULL; + + conv = kmalloc(sz, GFP_KERNEL); + if (!conv) + return NULL; + + /* XXX */ + *conv_len = smbConvertToUTF16((__le16 *)conv, + d_info->name, + d_info->name_len, + local_nls, + 0); + *conv_len *= 2; + + /* We allocate buffer twice bigger than needed. */ + conv[*conv_len] = 0x00; + conv[*conv_len + 1] = 0x00; + return conv; +} diff --git a/fs/cifsd/misc.h b/fs/cifsd/misc.h new file mode 100644 index 000000000000..d67843aad509 --- /dev/null +++ b/fs/cifsd/misc.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_MISC_H__ +#define __KSMBD_MISC_H__ + +struct ksmbd_share_config; +struct nls_table; +struct kstat; +struct ksmbd_file; + +int match_pattern(const char *str, const char *pattern); + +int ksmbd_validate_filename(char *filename); + +int parse_stream_name(char *filename, char **stream_name, int *s_type); + +char *convert_to_nt_pathname(char *filename, char *sharepath); + +int get_nlink(struct kstat *st); + +void ksmbd_conv_path_to_unix(char *path); +void ksmbd_strip_last_slash(char *path); +void ksmbd_conv_path_to_windows(char *path); + +char *extract_sharename(char *treename); + +char *convert_to_unix_name(struct ksmbd_share_config *share, char *name); + +#define KSMBD_DIR_INFO_ALIGNMENT 8 + +struct ksmbd_dir_info; +char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, + const struct nls_table *local_nls, + int *conv_len); +#endif /* __KSMBD_MISC_H__ */ diff --git a/fs/cifsd/ndr.c b/fs/cifsd/ndr.c new file mode 100644 index 000000000000..d96dcd9e43c6 --- /dev/null +++ b/fs/cifsd/ndr.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +#include + +#include "glob.h" +#include "ndr.h" + +#define PAYLOAD_HEAD(d) ((d)->data + (d)->offset) + +#define KSMBD_ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) + +#define KSMBD_ALIGN(x, a) \ + ({ \ + typeof(x) ret = (x); \ + if (((x) & ((typeof(x))(a) - 1)) != 0) \ + ret = KSMBD_ALIGN_MASK(x, (typeof(x))(a) - 1); \ + ret; \ + }) + +static void align_offset(struct ndr *ndr, int n) +{ + ndr->offset = KSMBD_ALIGN(ndr->offset, n); +} + +static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) +{ + char *data; + + data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL); + if (!data) + return -ENOMEM; + + n->data = data; + n->length += 1024; + memset(n->data + n->offset, 0, 1024); + return 0; +} + +static void ndr_write_int16(struct ndr *n, __u16 value) +{ + if (n->length <= n->offset + sizeof(value)) + try_to_realloc_ndr_blob(n, sizeof(value)); + + *(__le16 *)PAYLOAD_HEAD(n) = cpu_to_le16(value); + n->offset += sizeof(value); +} + +static void ndr_write_int32(struct ndr *n, __u32 value) +{ + if (n->length <= n->offset + sizeof(value)) + try_to_realloc_ndr_blob(n, sizeof(value)); + + *(__le32 *)PAYLOAD_HEAD(n) = cpu_to_le32(value); + n->offset += sizeof(value); +} + +static void ndr_write_int64(struct ndr *n, __u64 value) +{ + if (n->length <= n->offset + sizeof(value)) + try_to_realloc_ndr_blob(n, sizeof(value)); + + *(__le64 *)PAYLOAD_HEAD(n) = cpu_to_le64(value); + n->offset += sizeof(value); +} + +static int ndr_write_bytes(struct ndr *n, void *value, size_t sz) +{ + if (n->length <= n->offset + sz) + try_to_realloc_ndr_blob(n, sz); + + memcpy(PAYLOAD_HEAD(n), value, sz); + n->offset += sz; + return 0; +} + +static int ndr_write_string(struct ndr *n, void *value, size_t sz) +{ + if (n->length <= n->offset + sz) + try_to_realloc_ndr_blob(n, sz); + + strncpy(PAYLOAD_HEAD(n), value, sz); + sz++; + n->offset += sz; + align_offset(n, 2); + return 0; +} + +static int ndr_read_string(struct ndr *n, void *value, size_t sz) +{ + int len = strnlen(PAYLOAD_HEAD(n), sz); + + memcpy(value, PAYLOAD_HEAD(n), len); + len++; + n->offset += len; + align_offset(n, 2); + return 0; +} + +static int ndr_read_bytes(struct ndr *n, void *value, size_t sz) +{ + memcpy(value, PAYLOAD_HEAD(n), sz); + n->offset += sz; + return 0; +} + +static __u16 ndr_read_int16(struct ndr *n) +{ + __u16 ret; + + ret = le16_to_cpu(*(__le16 *)PAYLOAD_HEAD(n)); + n->offset += sizeof(__u16); + return ret; +} + +static __u32 ndr_read_int32(struct ndr *n) +{ + __u32 ret; + + ret = le32_to_cpu(*(__le32 *)PAYLOAD_HEAD(n)); + n->offset += sizeof(__u32); + return ret; +} + +static __u64 ndr_read_int64(struct ndr *n) +{ + __u64 ret; + + ret = le64_to_cpu(*(__le64 *)PAYLOAD_HEAD(n)); + n->offset += sizeof(__u64); + return ret; +} + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12] = {0}; + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + if (da->version == 3) { + snprintf(hex_attr, 10, "0x%x", da->attr); + ndr_write_string(n, hex_attr, strlen(hex_attr)); + } else { + ndr_write_string(n, "", strlen("")); + } + ndr_write_int16(n, da->version); + ndr_write_int32(n, da->version); + + ndr_write_int32(n, da->flags); + ndr_write_int32(n, da->attr); + if (da->version == 3) { + ndr_write_int32(n, da->ea_size); + ndr_write_int64(n, da->size); + ndr_write_int64(n, da->alloc_size); + } else + ndr_write_int64(n, da->itime); + ndr_write_int64(n, da->create_time); + if (da->version == 3) + ndr_write_int64(n, da->change_time); + return 0; +} + +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12] = {0}; + int version2; + + n->offset = 0; + ndr_read_string(n, hex_attr, n->length - n->offset); + da->version = ndr_read_int16(n); + + if (da->version != 3 && da->version != 4) { + ksmbd_err("v%d version is not supported\n", da->version); + return -EINVAL; + } + + version2 = ndr_read_int32(n); + if (da->version != version2) { + ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n", + da->version, version2); + return -EINVAL; + } + + ndr_read_int32(n); + da->attr = ndr_read_int32(n); + if (da->version == 4) { + da->itime = ndr_read_int64(n); + da->create_time = ndr_read_int64(n); + } else { + ndr_read_int32(n); + ndr_read_int64(n); + ndr_read_int64(n); + da->create_time = ndr_read_int64(n); + ndr_read_int64(n); + } + + return 0; +} + +static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) +{ + int i; + + ndr_write_int32(n, acl->count); + align_offset(n, 8); + ndr_write_int32(n, acl->count); + ndr_write_int32(n, 0); + + for (i = 0; i < acl->count; i++) { + align_offset(n, 8); + ndr_write_int16(n, acl->entries[i].type); + ndr_write_int16(n, acl->entries[i].type); + + if (acl->entries[i].type == SMB_ACL_USER) { + align_offset(n, 8); + ndr_write_int64(n, acl->entries[i].uid); + } else if (acl->entries[i].type == SMB_ACL_GROUP) { + align_offset(n, 8); + ndr_write_int64(n, acl->entries[i].gid); + } + + /* push permission */ + ndr_write_int32(n, acl->entries[i].perm); + } + + return 0; +} + +int ndr_encode_posix_acl(struct ndr *n, struct inode *inode, + struct xattr_smb_acl *acl, struct xattr_smb_acl *def_acl) +{ + int ref_id = 0x00020000; + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + if (acl) { + /* ACL ACCESS */ + ndr_write_int32(n, ref_id); + ref_id += 4; + } else + ndr_write_int32(n, 0); + + if (def_acl) { + /* DEFAULT ACL ACCESS */ + ndr_write_int32(n, ref_id); + ref_id += 4; + } else + ndr_write_int32(n, 0); + + ndr_write_int64(n, from_kuid(&init_user_ns, inode->i_uid)); + ndr_write_int64(n, from_kgid(&init_user_ns, inode->i_gid)); + ndr_write_int32(n, inode->i_mode); + + if (acl) { + ndr_encode_posix_acl_entry(n, acl); + if (def_acl) + ndr_encode_posix_acl_entry(n, def_acl); + } + return 0; +} + +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + int ref_id = 0x00020004; + + n->offset = 0; + n->length = 2048; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + ndr_write_int16(n, acl->version); + ndr_write_int32(n, acl->version); + ndr_write_int16(n, 2); + ndr_write_int32(n, ref_id); + + /* push hash type and hash 64bytes */ + ndr_write_int16(n, acl->hash_type); + ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + ndr_write_bytes(n, acl->desc, acl->desc_len); + ndr_write_int64(n, acl->current_time); + ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + + /* push ndr for security descriptor */ + ndr_write_bytes(n, acl->sd_buf, acl->sd_size); + + return 0; +} + +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + int version2; + + n->offset = 0; + acl->version = ndr_read_int16(n); + if (acl->version != 4) { + ksmbd_err("v%d version is not supported\n", acl->version); + return -EINVAL; + } + + version2 = ndr_read_int32(n); + if (acl->version != version2) { + ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n", + acl->version, version2); + return -EINVAL; + } + + /* Read Level */ + ndr_read_int16(n); + /* Read Ref Id */ + ndr_read_int32(n); + acl->hash_type = ndr_read_int16(n); + ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + + ndr_read_bytes(n, acl->desc, 10); + if (strncmp(acl->desc, "posix_acl", 9)) { + ksmbd_err("Invalid acl desciption : %s\n", acl->desc); + return -EINVAL; + } + + /* Read Time */ + ndr_read_int64(n); + /* Read Posix ACL hash */ + ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + acl->sd_size = n->length - n->offset; + acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL); + if (!acl->sd_buf) + return -ENOMEM; + + ndr_read_bytes(n, acl->sd_buf, acl->sd_size); + + return 0; +} diff --git a/fs/cifsd/ndr.h b/fs/cifsd/ndr.h new file mode 100644 index 000000000000..a9db968b78ac --- /dev/null +++ b/fs/cifsd/ndr.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +struct ndr { + char *data; + int offset; + int length; +}; + +#define NDR_NTSD_OFFSETOF 0xA0 + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_encode_posix_acl(struct ndr *n, struct inode *inode, + struct xattr_smb_acl *acl, struct xattr_smb_acl *def_acl); +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); diff --git a/fs/cifsd/netmisc.c b/fs/cifsd/netmisc.c new file mode 100644 index 000000000000..6f7dd78348a3 --- /dev/null +++ b/fs/cifsd/netmisc.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * fs/ksmbd/netmisc.c + * + * Copyright (c) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Error mapping routines from Samba libsmb/errormap.c + * Copyright (C) Andrew Tridgell 2001 + */ + +#include "glob.h" +#include "smberr.h" +#include "nterr.h" +#include "time_wrappers.h" + +/* + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + */ +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc) +{ + struct timespec64 ts; + + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; + u64 abs_t; + + /* + * Unfortunately can not use normal 64 bit division on 32 bit arch, but + * the alternative, do_div, does not work with negative numbers so have + * to special case them + */ + if (t < 0) { + abs_t = -t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_nsec = -ts.tv_nsec; + ts.tv_sec = -abs_t; + } else { + abs_t = t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_sec = abs_t; + } + + return ts; +} diff --git a/fs/cifsd/nterr.c b/fs/cifsd/nterr.c new file mode 100644 index 000000000000..358a766375b4 --- /dev/null +++ b/fs/cifsd/nterr.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Unix SMB/Netbios implementation. + * Version 1.9. + * RPC Pipe client / server routines + * Copyright (C) Luke Kenneth Casson Leighton 1997-2001. + */ + +/* NT error codes - see nterr.h */ +#include +#include +#include "nterr.h" + +const struct nt_err_code_struct nt_errs[] = { + {"NT_STATUS_OK", NT_STATUS_OK}, + {"NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL}, + {"NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED}, + {"NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS}, + {"NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH}, + {"NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION}, + {"NT_STATUS_BUFFER_OVERFLOW", NT_STATUS_BUFFER_OVERFLOW}, + {"NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR}, + {"NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA}, + {"NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE}, + {"NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK}, + {"NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC}, + {"NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID}, + {"NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED}, + {"NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER}, + {"NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE}, + {"NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE}, + {"NT_STATUS_INVALID_DEVICE_REQUEST", + NT_STATUS_INVALID_DEVICE_REQUEST}, + {"NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE}, + {"NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME}, + {"NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE}, + {"NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA}, + {"NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR}, + {"NT_STATUS_MORE_PROCESSING_REQUIRED", + NT_STATUS_MORE_PROCESSING_REQUIRED}, + {"NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY}, + {"NT_STATUS_CONFLICTING_ADDRESSES", + NT_STATUS_CONFLICTING_ADDRESSES}, + {"NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW}, + {"NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM}, + {"NT_STATUS_UNABLE_TO_DELETE_SECTION", + NT_STATUS_UNABLE_TO_DELETE_SECTION}, + {"NT_STATUS_INVALID_SYSTEM_SERVICE", + NT_STATUS_INVALID_SYSTEM_SERVICE}, + {"NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION}, + {"NT_STATUS_INVALID_LOCK_SEQUENCE", + NT_STATUS_INVALID_LOCK_SEQUENCE}, + {"NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE}, + {"NT_STATUS_INVALID_FILE_FOR_SECTION", + NT_STATUS_INVALID_FILE_FOR_SECTION}, + {"NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED}, + {"NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED}, + {"NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL}, + {"NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH}, + {"NT_STATUS_NONCONTINUABLE_EXCEPTION", + NT_STATUS_NONCONTINUABLE_EXCEPTION}, + {"NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION}, + {"NT_STATUS_UNWIND", NT_STATUS_UNWIND}, + {"NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK}, + {"NT_STATUS_INVALID_UNWIND_TARGET", + NT_STATUS_INVALID_UNWIND_TARGET}, + {"NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED}, + {"NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR}, + {"NT_STATUS_UNABLE_TO_DECOMMIT_VM", + NT_STATUS_UNABLE_TO_DECOMMIT_VM}, + {"NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED}, + {"NT_STATUS_INVALID_PORT_ATTRIBUTES", + NT_STATUS_INVALID_PORT_ATTRIBUTES}, + {"NT_STATUS_PORT_MESSAGE_TOO_LONG", + NT_STATUS_PORT_MESSAGE_TOO_LONG}, + {"NT_STATUS_INVALID_PARAMETER_MIX", + NT_STATUS_INVALID_PARAMETER_MIX}, + {"NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER}, + {"NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR}, + {"NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID}, + {"NT_STATUS_OBJECT_NAME_NOT_FOUND", + NT_STATUS_OBJECT_NAME_NOT_FOUND}, + {"NT_STATUS_OBJECT_NAME_COLLISION", + NT_STATUS_OBJECT_NAME_COLLISION}, + {"NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE}, + {"NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED}, + {"NT_STATUS_DEVICE_ALREADY_ATTACHED", + NT_STATUS_DEVICE_ALREADY_ATTACHED}, + {"NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID}, + {"NT_STATUS_OBJECT_PATH_NOT_FOUND", + NT_STATUS_OBJECT_PATH_NOT_FOUND}, + {"NT_STATUS_OBJECT_PATH_SYNTAX_BAD", + NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, + {"NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN}, + {"NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR}, + {"NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR}, + {"NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR}, + {"NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG}, + {"NT_STATUS_PORT_CONNECTION_REFUSED", + NT_STATUS_PORT_CONNECTION_REFUSED}, + {"NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE}, + {"NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION}, + {"NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED}, + {"NT_STATUS_INVALID_PAGE_PROTECTION", + NT_STATUS_INVALID_PAGE_PROTECTION}, + {"NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED}, + {"NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", + NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, + {"NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET}, + {"NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE}, + {"NT_STATUS_SUSPEND_COUNT_EXCEEDED", + NT_STATUS_SUSPEND_COUNT_EXCEEDED}, + {"NT_STATUS_THREAD_IS_TERMINATING", + NT_STATUS_THREAD_IS_TERMINATING}, + {"NT_STATUS_BAD_WORKING_SET_LIMIT", + NT_STATUS_BAD_WORKING_SET_LIMIT}, + {"NT_STATUS_INCOMPATIBLE_FILE_MAP", + NT_STATUS_INCOMPATIBLE_FILE_MAP}, + {"NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION}, + {"NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED}, + {"NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE}, + {"NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY}, + {"NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE}, + {"NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR}, + {"NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT}, + {"NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED}, + {"NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING}, + {"NT_STATUS_CTL_FILE_NOT_SUPPORTED", + NT_STATUS_CTL_FILE_NOT_SUPPORTED}, + {"NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION}, + {"NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH}, + {"NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER}, + {"NT_STATUS_INVALID_PRIMARY_GROUP", + NT_STATUS_INVALID_PRIMARY_GROUP}, + {"NT_STATUS_NO_IMPERSONATION_TOKEN", + NT_STATUS_NO_IMPERSONATION_TOKEN}, + {"NT_STATUS_CANT_DISABLE_MANDATORY", + NT_STATUS_CANT_DISABLE_MANDATORY}, + {"NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS}, + {"NT_STATUS_NO_SUCH_LOGON_SESSION", + NT_STATUS_NO_SUCH_LOGON_SESSION}, + {"NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE}, + {"NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD}, + {"NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME}, + {"NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS}, + {"NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER}, + {"NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS}, + {"NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP}, + {"NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP}, + {"NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP}, + {"NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN}, + {"NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD}, + {"NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD}, + {"NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION}, + {"NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE}, + {"NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION}, + {"NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS}, + {"NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION}, + {"NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED}, + {"NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED}, + {"NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED}, + {"NT_STATUS_TOO_MANY_LUIDS_REQUESTED", + NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, + {"NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED}, + {"NT_STATUS_INVALID_SUB_AUTHORITY", + NT_STATUS_INVALID_SUB_AUTHORITY}, + {"NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL}, + {"NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID}, + {"NT_STATUS_INVALID_SECURITY_DESCR", + NT_STATUS_INVALID_SECURITY_DESCR}, + {"NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND}, + {"NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT}, + {"NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN}, + {"NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL}, + {"NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED}, + {"NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL}, + {"NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED}, + {"NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED}, + {"NT_STATUS_TOO_MANY_GUIDS_REQUESTED", + NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, + {"NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED}, + {"NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY}, + {"NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED}, + {"NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL}, + {"NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED}, + {"NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA}, + {"NT_STATUS_RESOURCE_DATA_NOT_FOUND", + NT_STATUS_RESOURCE_DATA_NOT_FOUND}, + {"NT_STATUS_RESOURCE_TYPE_NOT_FOUND", + NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, + {"NT_STATUS_RESOURCE_NAME_NOT_FOUND", + NT_STATUS_RESOURCE_NAME_NOT_FOUND}, + {"NT_STATUS_ARRAY_BOUNDS_EXCEEDED", + NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, + {"NT_STATUS_FLOAT_DENORMAL_OPERAND", + NT_STATUS_FLOAT_DENORMAL_OPERAND}, + {"NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, + {"NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT}, + {"NT_STATUS_FLOAT_INVALID_OPERATION", + NT_STATUS_FLOAT_INVALID_OPERATION}, + {"NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW}, + {"NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK}, + {"NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW}, + {"NT_STATUS_INTEGER_DIVIDE_BY_ZERO", + NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, + {"NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW}, + {"NT_STATUS_PRIVILEGED_INSTRUCTION", + NT_STATUS_PRIVILEGED_INSTRUCTION}, + {"NT_STATUS_TOO_MANY_PAGING_FILES", + NT_STATUS_TOO_MANY_PAGING_FILES}, + {"NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID}, + {"NT_STATUS_ALLOTTED_SPACE_EXCEEDED", + NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, + {"NT_STATUS_INSUFFICIENT_RESOURCES", + NT_STATUS_INSUFFICIENT_RESOURCES}, + {"NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND}, + {"NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR}, + {"NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED}, + {"NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE}, + {"NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE}, + {"NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED}, + {"NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA}, + {"NT_STATUS_MEDIA_WRITE_PROTECTED", + NT_STATUS_MEDIA_WRITE_PROTECTED}, + {"NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY}, + {"NT_STATUS_INVALID_GROUP_ATTRIBUTES", + NT_STATUS_INVALID_GROUP_ATTRIBUTES}, + {"NT_STATUS_BAD_IMPERSONATION_LEVEL", + NT_STATUS_BAD_IMPERSONATION_LEVEL}, + {"NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS}, + {"NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS}, + {"NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE}, + {"NT_STATUS_BAD_MASTER_BOOT_RECORD", + NT_STATUS_BAD_MASTER_BOOT_RECORD}, + {"NT_STATUS_INSTRUCTION_MISALIGNMENT", + NT_STATUS_INSTRUCTION_MISALIGNMENT}, + {"NT_STATUS_INSTANCE_NOT_AVAILABLE", + NT_STATUS_INSTANCE_NOT_AVAILABLE}, + {"NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE}, + {"NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE}, + {"NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY}, + {"NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION}, + {"NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED}, + {"NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING}, + {"NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED}, + {"NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING}, + {"NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE}, + {"NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT}, + {"NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED}, + {"NT_STATUS_PROFILING_NOT_STARTED", + NT_STATUS_PROFILING_NOT_STARTED}, + {"NT_STATUS_PROFILING_NOT_STOPPED", + NT_STATUS_PROFILING_NOT_STOPPED}, + {"NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET}, + {"NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY}, + {"NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED}, + {"NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING}, + {"NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME}, + {"NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH}, + {"NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY}, + {"NT_STATUS_DEVICE_DOES_NOT_EXIST", + NT_STATUS_DEVICE_DOES_NOT_EXIST}, + {"NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS}, + {"NT_STATUS_ADAPTER_HARDWARE_ERROR", + NT_STATUS_ADAPTER_HARDWARE_ERROR}, + {"NT_STATUS_INVALID_NETWORK_RESPONSE", + NT_STATUS_INVALID_NETWORK_RESPONSE}, + {"NT_STATUS_UNEXPECTED_NETWORK_ERROR", + NT_STATUS_UNEXPECTED_NETWORK_ERROR}, + {"NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER}, + {"NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL}, + {"NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE}, + {"NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED}, + {"NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED}, + {"NT_STATUS_NETWORK_ACCESS_DENIED", + NT_STATUS_NETWORK_ACCESS_DENIED}, + {"NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE}, + {"NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME}, + {"NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES}, + {"NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS}, + {"NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED}, + {"NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED}, + {"NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED}, + {"NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT}, + {"NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT}, + {"NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE}, + {"NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED}, + {"NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", + NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, + {"NT_STATUS_NO_SECURITY_ON_OBJECT", + NT_STATUS_NO_SECURITY_ON_OBJECT}, + {"NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT}, + {"NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY}, + {"NT_STATUS_CANT_ACCESS_DOMAIN_INFO", + NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, + {"NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF}, + {"NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE}, + {"NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE}, + {"NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE}, + {"NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN}, + {"NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS}, + {"NT_STATUS_DOMAIN_LIMIT_EXCEEDED", + NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, + {"NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED}, + {"NT_STATUS_INVALID_OPLOCK_PROTOCOL", + NT_STATUS_INVALID_OPLOCK_PROTOCOL}, + {"NT_STATUS_INTERNAL_DB_CORRUPTION", + NT_STATUS_INTERNAL_DB_CORRUPTION}, + {"NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR}, + {"NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED}, + {"NT_STATUS_BAD_DESCRIPTOR_FORMAT", + NT_STATUS_BAD_DESCRIPTOR_FORMAT}, + {"NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER}, + {"NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR}, + {"NT_STATUS_UNEXPECTED_MM_CREATE_ERR", + NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, + {"NT_STATUS_UNEXPECTED_MM_MAP_ERROR", + NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, + {"NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", + NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, + {"NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS}, + {"NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS}, + {"NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1}, + {"NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2}, + {"NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3}, + {"NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4}, + {"NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5}, + {"NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6}, + {"NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7}, + {"NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8}, + {"NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9}, + {"NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10}, + {"NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11}, + {"NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12}, + {"NT_STATUS_REDIRECTOR_NOT_STARTED", + NT_STATUS_REDIRECTOR_NOT_STARTED}, + {"NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED}, + {"NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW}, + {"NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE}, + {"NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE}, + {"NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY}, + {"NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR}, + {"NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY}, + {"NT_STATUS_BAD_LOGON_SESSION_STATE", + NT_STATUS_BAD_LOGON_SESSION_STATE}, + {"NT_STATUS_LOGON_SESSION_COLLISION", + NT_STATUS_LOGON_SESSION_COLLISION}, + {"NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG}, + {"NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN}, + {"NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE}, + {"NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND}, + {"NT_STATUS_PROCESS_IS_TERMINATING", + NT_STATUS_PROCESS_IS_TERMINATING}, + {"NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE}, + {"NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION}, + {"NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE}, + {"NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED}, + {"NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT}, + {"NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST}, + {"NT_STATUS_ABIOS_LID_ALREADY_OWNED", + NT_STATUS_ABIOS_LID_ALREADY_OWNED}, + {"NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER}, + {"NT_STATUS_ABIOS_INVALID_COMMAND", + NT_STATUS_ABIOS_INVALID_COMMAND}, + {"NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID}, + {"NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", + NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, + {"NT_STATUS_ABIOS_INVALID_SELECTOR", + NT_STATUS_ABIOS_INVALID_SELECTOR}, + {"NT_STATUS_NO_LDT", NT_STATUS_NO_LDT}, + {"NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE}, + {"NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET}, + {"NT_STATUS_INVALID_LDT_DESCRIPTOR", + NT_STATUS_INVALID_LDT_DESCRIPTOR}, + {"NT_STATUS_INVALID_IMAGE_NE_FORMAT", + NT_STATUS_INVALID_IMAGE_NE_FORMAT}, + {"NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE}, + {"NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE}, + {"NT_STATUS_MAPPED_FILE_SIZE_ZERO", + NT_STATUS_MAPPED_FILE_SIZE_ZERO}, + {"NT_STATUS_TOO_MANY_OPENED_FILES", + NT_STATUS_TOO_MANY_OPENED_FILES}, + {"NT_STATUS_CANCELLED", NT_STATUS_CANCELLED}, + {"NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE}, + {"NT_STATUS_INVALID_COMPUTER_NAME", + NT_STATUS_INVALID_COMPUTER_NAME}, + {"NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED}, + {"NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT}, + {"NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP}, + {"NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER}, + {"NT_STATUS_MEMBERS_PRIMARY_GROUP", + NT_STATUS_MEMBERS_PRIMARY_GROUP}, + {"NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED}, + {"NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS}, + {"NT_STATUS_THREAD_NOT_IN_PROCESS", + NT_STATUS_THREAD_NOT_IN_PROCESS}, + {"NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE}, + {"NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", + NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, + {"NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT}, + {"NT_STATUS_INVALID_IMAGE_LE_FORMAT", + NT_STATUS_INVALID_IMAGE_LE_FORMAT}, + {"NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ}, + {"NT_STATUS_INVALID_IMAGE_PROTECT", + NT_STATUS_INVALID_IMAGE_PROTECT}, + {"NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16}, + {"NT_STATUS_LOGON_SERVER_CONFLICT", + NT_STATUS_LOGON_SERVER_CONFLICT}, + {"NT_STATUS_TIME_DIFFERENCE_AT_DC", + NT_STATUS_TIME_DIFFERENCE_AT_DC}, + {"NT_STATUS_SYNCHRONIZATION_REQUIRED", + NT_STATUS_SYNCHRONIZATION_REQUIRED}, + {"NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND}, + {"NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED}, + {"NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED}, + {"NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND}, + {"NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND}, + {"NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT}, + {"NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT}, + {"NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT}, + {"NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES}, + {"NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED}, + {"NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT}, + {"NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION}, + {"NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS}, + {"NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED}, + {"NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE}, + {"NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION}, + {"NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE}, + {"NT_STATUS_PAGEFILE_CREATE_FAILED", + NT_STATUS_PAGEFILE_CREATE_FAILED}, + {"NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE}, + {"NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL}, + {"NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE}, + {"NT_STATUS_ILLEGAL_FLOAT_CONTEXT", + NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, + {"NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN}, + {"NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT}, + {"NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED}, + {"NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR}, + {"NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME}, + {"NT_STATUS_SERIAL_NO_DEVICE_INITED", + NT_STATUS_SERIAL_NO_DEVICE_INITED}, + {"NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS}, + {"NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS}, + {"NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS}, + {"NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS}, + {"NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED}, + {"NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS}, + {"NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG}, + {"NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR}, + {"NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE}, + {"NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS}, + {"NT_STATUS_LOGON_TYPE_NOT_GRANTED", + NT_STATUS_LOGON_TYPE_NOT_GRANTED}, + {"NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE}, + {"NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", + NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, + {"NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", + NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, + {"NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER}, + {"NT_STATUS_ILL_FORMED_SERVICE_ENTRY", + NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, + {"NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER}, + {"NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER}, + {"NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER}, + {"NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME}, + {"NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", + NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, + {"NT_STATUS_FLOPPY_WRONG_CYLINDER", + NT_STATUS_FLOPPY_WRONG_CYLINDER}, + {"NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR}, + {"NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS}, + {"NT_STATUS_DISK_RECALIBRATE_FAILED", + NT_STATUS_DISK_RECALIBRATE_FAILED}, + {"NT_STATUS_DISK_OPERATION_FAILED", + NT_STATUS_DISK_OPERATION_FAILED}, + {"NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED}, + {"NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY}, + {"NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING}, + {"NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE}, + {"NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH}, + {"NT_STATUS_DEVICE_NOT_PARTITIONED", + NT_STATUS_DEVICE_NOT_PARTITIONED}, + {"NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA}, + {"NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", + NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, + {"NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW}, + {"NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA}, + {"NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER}, + {"NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER}, + {"NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED}, + {"NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE}, + {"NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS}, + {"NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", + NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, + {"NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN}, + {"NT_STATUS_CHILD_MUST_BE_VOLATILE", + NT_STATUS_CHILD_MUST_BE_VOLATILE}, + {"NT_STATUS_DEVICE_CONFIGURATION_ERROR", + NT_STATUS_DEVICE_CONFIGURATION_ERROR}, + {"NT_STATUS_DRIVER_INTERNAL_ERROR", + NT_STATUS_DRIVER_INTERNAL_ERROR}, + {"NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE}, + {"NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR}, + {"NT_STATUS_DEVICE_PROTOCOL_ERROR", + NT_STATUS_DEVICE_PROTOCOL_ERROR}, + {"NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER}, + {"NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL}, + {"NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE}, + {"NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET}, + {"NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT}, + {"NT_STATUS_TRUSTED_DOMAIN_FAILURE", + NT_STATUS_TRUSTED_DOMAIN_FAILURE}, + {"NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", + NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, + {"NT_STATUS_EVENTLOG_FILE_CORRUPT", + NT_STATUS_EVENTLOG_FILE_CORRUPT}, + {"NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START}, + {"NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE}, + {"NT_STATUS_MUTANT_LIMIT_EXCEEDED", + NT_STATUS_MUTANT_LIMIT_EXCEEDED}, + {"NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED}, + {"NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED}, + {"NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK}, + {"NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", + NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, + {"NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT}, + {"NT_STATUS_EVENTLOG_FILE_CHANGED", + NT_STATUS_EVENTLOG_FILE_CHANGED}, + {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", + NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, + {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", + NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, + {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", + NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, + {"NT_STATUS_DOMAIN_TRUST_INCONSISTENT", + NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, + {"NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED}, + {"NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY}, + {"NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED}, + {"NT_STATUS_RESOURCE_LANG_NOT_FOUND", + NT_STATUS_RESOURCE_LANG_NOT_FOUND}, + {"NT_STATUS_INSUFF_SERVER_RESOURCES", + NT_STATUS_INSUFF_SERVER_RESOURCES}, + {"NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE}, + {"NT_STATUS_INVALID_ADDRESS_COMPONENT", + NT_STATUS_INVALID_ADDRESS_COMPONENT}, + {"NT_STATUS_INVALID_ADDRESS_WILDCARD", + NT_STATUS_INVALID_ADDRESS_WILDCARD}, + {"NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES}, + {"NT_STATUS_ADDRESS_ALREADY_EXISTS", + NT_STATUS_ADDRESS_ALREADY_EXISTS}, + {"NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED}, + {"NT_STATUS_CONNECTION_DISCONNECTED", + NT_STATUS_CONNECTION_DISCONNECTED}, + {"NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET}, + {"NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES}, + {"NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED}, + {"NT_STATUS_TRANSACTION_TIMED_OUT", + NT_STATUS_TRANSACTION_TIMED_OUT}, + {"NT_STATUS_TRANSACTION_NO_RELEASE", + NT_STATUS_TRANSACTION_NO_RELEASE}, + {"NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH}, + {"NT_STATUS_TRANSACTION_RESPONDED", + NT_STATUS_TRANSACTION_RESPONDED}, + {"NT_STATUS_TRANSACTION_INVALID_ID", + NT_STATUS_TRANSACTION_INVALID_ID}, + {"NT_STATUS_TRANSACTION_INVALID_TYPE", + NT_STATUS_TRANSACTION_INVALID_TYPE}, + {"NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION}, + {"NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION}, + {"NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", + NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, + {"NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED}, + {"NT_STATUS_SYSTEM_PROCESS_TERMINATED", + NT_STATUS_SYSTEM_PROCESS_TERMINATED}, + {"NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED}, + {"NT_STATUS_NO_BROWSER_SERVERS_FOUND", + NT_STATUS_NO_BROWSER_SERVERS_FOUND}, + {"NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR}, + {"NT_STATUS_DRIVER_CANCEL_TIMEOUT", + NT_STATUS_DRIVER_CANCEL_TIMEOUT}, + {"NT_STATUS_REPLY_MESSAGE_MISMATCH", + NT_STATUS_REPLY_MESSAGE_MISMATCH}, + {"NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT}, + {"NT_STATUS_IMAGE_CHECKSUM_MISMATCH", + NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, + {"NT_STATUS_LOST_WRITEBEHIND_DATA", + NT_STATUS_LOST_WRITEBEHIND_DATA}, + {"NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", + NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, + {"NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE}, + {"NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND}, + {"NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM}, + {"NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE}, + {"NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ}, + {"NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK}, + {"NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID}, + {"NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS}, + {"NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE}, + {"NT_STATUS_RETRY", NT_STATUS_RETRY}, + {"NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE}, + {"NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET}, + {"NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND}, + {"NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW}, + {"NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT}, + {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", + NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, + {"NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT}, + {"NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE}, + {"NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED}, + {"NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT}, + {"NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", + NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, + {"NT_STATUS_ADDRESS_NOT_ASSOCIATED", + NT_STATUS_ADDRESS_NOT_ASSOCIATED}, + {"NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID}, + {"NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE}, + {"NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE}, + {"NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE}, + {"NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE}, + {"NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE}, + {"NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED}, + {"NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED}, + {"NT_STATUS_BAD_COMPRESSION_BUFFER", + NT_STATUS_BAD_COMPRESSION_BUFFER}, + {"NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE}, + {"NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED}, + {"NT_STATUS_TIMER_RESOLUTION_NOT_SET", + NT_STATUS_TIMER_RESOLUTION_NOT_SET}, + {"NT_STATUS_CONNECTION_COUNT_LIMIT", + NT_STATUS_CONNECTION_COUNT_LIMIT}, + {"NT_STATUS_LOGIN_TIME_RESTRICTION", + NT_STATUS_LOGIN_TIME_RESTRICTION}, + {"NT_STATUS_LOGIN_WKSTA_RESTRICTION", + NT_STATUS_LOGIN_WKSTA_RESTRICTION}, + {"NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH}, + {"NT_STATUS_INSUFFICIENT_LOGON_INFO", + NT_STATUS_INSUFFICIENT_LOGON_INFO}, + {"NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT}, + {"NT_STATUS_BAD_SERVICE_ENTRYPOINT", + NT_STATUS_BAD_SERVICE_ENTRYPOINT}, + {"NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST}, + {"NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1}, + {"NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2}, + {"NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT}, + {"NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED}, + {"NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE}, + {"NT_STATUS_LICENSE_QUOTA_EXCEEDED", + NT_STATUS_LICENSE_QUOTA_EXCEEDED}, + {"NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT}, + {"NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT}, + {"NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT}, + {"NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE}, + {"NT_STATUS_UNSUPPORTED_COMPRESSION", + NT_STATUS_UNSUPPORTED_COMPRESSION}, + {"NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE}, + {"NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", + NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, + {"NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", + NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, + {"NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", + NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, + {"NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED}, + {"NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS}, + {"NT_STATUS_QUOTA_LIST_INCONSISTENT", + NT_STATUS_QUOTA_LIST_INCONSISTENT}, + {"NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE}, + {"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES}, + {"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES}, + {"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED}, + {NULL, 0} +}; diff --git a/fs/cifsd/nterr.h b/fs/cifsd/nterr.h new file mode 100644 index 000000000000..9f5004b69d30 --- /dev/null +++ b/fs/cifsd/nterr.h @@ -0,0 +1,552 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Unix SMB/Netbios implementation. + * Version 1.9. + * NT error code constants + * Copyright (C) Andrew Tridgell 1992-2000 + * Copyright (C) John H Terpstra 1996-2000 + * Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + * Copyright (C) Paul Ashton 1998-2000 + */ + + + +#ifndef _NTERR_H +#define _NTERR_H + +struct nt_err_code_struct { + char *nt_errstr; + __u32 nt_errcode; +}; + +extern const struct nt_err_code_struct nt_errs[]; + +/* Win32 Status codes. */ +#define NT_STATUS_MORE_ENTRIES 0x0105 +#define NT_ERROR_INVALID_PARAMETER 0x0057 +#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a +#define NT_STATUS_1804 0x070c +#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c +#define NT_STATUS_INVALID_LOCK_RANGE (0xC0000000 | 0x01a1) +/* + * Win32 Error codes extracted using a loop in smbclient then printing a netmon + * sniff to a file. + */ + +#define NT_STATUS_OK 0x0000 +#define NT_STATUS_SOME_UNMAPPED 0x0107 +#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 +#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a +#define NT_STATUS_MEDIA_CHANGED 0x8000001c +#define NT_STATUS_END_OF_MEDIA 0x8000001e +#define NT_STATUS_MEDIA_CHECK 0x80000020 +#define NT_STATUS_NO_DATA_DETECTED 0x8000001c +#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d +#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 +#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288 +#define NT_STATUS_UNSUCCESSFUL (0xC0000000 | 0x0001) +#define NT_STATUS_NOT_IMPLEMENTED (0xC0000000 | 0x0002) +#define NT_STATUS_INVALID_INFO_CLASS (0xC0000000 | 0x0003) +#define NT_STATUS_INFO_LENGTH_MISMATCH (0xC0000000 | 0x0004) +#define NT_STATUS_ACCESS_VIOLATION (0xC0000000 | 0x0005) +#define NT_STATUS_IN_PAGE_ERROR (0xC0000000 | 0x0006) +#define NT_STATUS_PAGEFILE_QUOTA (0xC0000000 | 0x0007) +#define NT_STATUS_INVALID_HANDLE (0xC0000000 | 0x0008) +#define NT_STATUS_BAD_INITIAL_STACK (0xC0000000 | 0x0009) +#define NT_STATUS_BAD_INITIAL_PC (0xC0000000 | 0x000a) +#define NT_STATUS_INVALID_CID (0xC0000000 | 0x000b) +#define NT_STATUS_TIMER_NOT_CANCELED (0xC0000000 | 0x000c) +#define NT_STATUS_INVALID_PARAMETER (0xC0000000 | 0x000d) +#define NT_STATUS_NO_SUCH_DEVICE (0xC0000000 | 0x000e) +#define NT_STATUS_NO_SUCH_FILE (0xC0000000 | 0x000f) +#define NT_STATUS_INVALID_DEVICE_REQUEST (0xC0000000 | 0x0010) +#define NT_STATUS_END_OF_FILE (0xC0000000 | 0x0011) +#define NT_STATUS_WRONG_VOLUME (0xC0000000 | 0x0012) +#define NT_STATUS_NO_MEDIA_IN_DEVICE (0xC0000000 | 0x0013) +#define NT_STATUS_UNRECOGNIZED_MEDIA (0xC0000000 | 0x0014) +#define NT_STATUS_NONEXISTENT_SECTOR (0xC0000000 | 0x0015) +#define NT_STATUS_MORE_PROCESSING_REQUIRED (0xC0000000 | 0x0016) +#define NT_STATUS_NO_MEMORY (0xC0000000 | 0x0017) +#define NT_STATUS_CONFLICTING_ADDRESSES (0xC0000000 | 0x0018) +#define NT_STATUS_NOT_MAPPED_VIEW (0xC0000000 | 0x0019) +#define NT_STATUS_UNABLE_TO_FREE_VM (0x80000000 | 0x001a) +#define NT_STATUS_UNABLE_TO_DELETE_SECTION (0xC0000000 | 0x001b) +#define NT_STATUS_INVALID_SYSTEM_SERVICE (0xC0000000 | 0x001c) +#define NT_STATUS_ILLEGAL_INSTRUCTION (0xC0000000 | 0x001d) +#define NT_STATUS_INVALID_LOCK_SEQUENCE (0xC0000000 | 0x001e) +#define NT_STATUS_INVALID_VIEW_SIZE (0xC0000000 | 0x001f) +#define NT_STATUS_INVALID_FILE_FOR_SECTION (0xC0000000 | 0x0020) +#define NT_STATUS_ALREADY_COMMITTED (0xC0000000 | 0x0021) +#define NT_STATUS_ACCESS_DENIED (0xC0000000 | 0x0022) +#define NT_STATUS_BUFFER_TOO_SMALL (0xC0000000 | 0x0023) +#define NT_STATUS_OBJECT_TYPE_MISMATCH (0xC0000000 | 0x0024) +#define NT_STATUS_NONCONTINUABLE_EXCEPTION (0xC0000000 | 0x0025) +#define NT_STATUS_INVALID_DISPOSITION (0xC0000000 | 0x0026) +#define NT_STATUS_UNWIND (0xC0000000 | 0x0027) +#define NT_STATUS_BAD_STACK (0xC0000000 | 0x0028) +#define NT_STATUS_INVALID_UNWIND_TARGET (0xC0000000 | 0x0029) +#define NT_STATUS_NOT_LOCKED (0xC0000000 | 0x002a) +#define NT_STATUS_PARITY_ERROR (0xC0000000 | 0x002b) +#define NT_STATUS_UNABLE_TO_DECOMMIT_VM (0xC0000000 | 0x002c) +#define NT_STATUS_NOT_COMMITTED (0xC0000000 | 0x002d) +#define NT_STATUS_INVALID_PORT_ATTRIBUTES (0xC0000000 | 0x002e) +#define NT_STATUS_PORT_MESSAGE_TOO_LONG (0xC0000000 | 0x002f) +#define NT_STATUS_INVALID_PARAMETER_MIX (0xC0000000 | 0x0030) +#define NT_STATUS_INVALID_QUOTA_LOWER (0xC0000000 | 0x0031) +#define NT_STATUS_DISK_CORRUPT_ERROR (0xC0000000 | 0x0032) +#define NT_STATUS_OBJECT_NAME_INVALID (0xC0000000 | 0x0033) +#define NT_STATUS_OBJECT_NAME_NOT_FOUND (0xC0000000 | 0x0034) +#define NT_STATUS_OBJECT_NAME_COLLISION (0xC0000000 | 0x0035) +#define NT_STATUS_HANDLE_NOT_WAITABLE (0xC0000000 | 0x0036) +#define NT_STATUS_PORT_DISCONNECTED (0xC0000000 | 0x0037) +#define NT_STATUS_DEVICE_ALREADY_ATTACHED (0xC0000000 | 0x0038) +#define NT_STATUS_OBJECT_PATH_INVALID (0xC0000000 | 0x0039) +#define NT_STATUS_OBJECT_PATH_NOT_FOUND (0xC0000000 | 0x003a) +#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD (0xC0000000 | 0x003b) +#define NT_STATUS_DATA_OVERRUN (0xC0000000 | 0x003c) +#define NT_STATUS_DATA_LATE_ERROR (0xC0000000 | 0x003d) +#define NT_STATUS_DATA_ERROR (0xC0000000 | 0x003e) +#define NT_STATUS_CRC_ERROR (0xC0000000 | 0x003f) +#define NT_STATUS_SECTION_TOO_BIG (0xC0000000 | 0x0040) +#define NT_STATUS_PORT_CONNECTION_REFUSED (0xC0000000 | 0x0041) +#define NT_STATUS_INVALID_PORT_HANDLE (0xC0000000 | 0x0042) +#define NT_STATUS_SHARING_VIOLATION (0xC0000000 | 0x0043) +#define NT_STATUS_QUOTA_EXCEEDED (0xC0000000 | 0x0044) +#define NT_STATUS_INVALID_PAGE_PROTECTION (0xC0000000 | 0x0045) +#define NT_STATUS_MUTANT_NOT_OWNED (0xC0000000 | 0x0046) +#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED (0xC0000000 | 0x0047) +#define NT_STATUS_PORT_ALREADY_SET (0xC0000000 | 0x0048) +#define NT_STATUS_SECTION_NOT_IMAGE (0xC0000000 | 0x0049) +#define NT_STATUS_SUSPEND_COUNT_EXCEEDED (0xC0000000 | 0x004a) +#define NT_STATUS_THREAD_IS_TERMINATING (0xC0000000 | 0x004b) +#define NT_STATUS_BAD_WORKING_SET_LIMIT (0xC0000000 | 0x004c) +#define NT_STATUS_INCOMPATIBLE_FILE_MAP (0xC0000000 | 0x004d) +#define NT_STATUS_SECTION_PROTECTION (0xC0000000 | 0x004e) +#define NT_STATUS_EAS_NOT_SUPPORTED (0xC0000000 | 0x004f) +#define NT_STATUS_EA_TOO_LARGE (0xC0000000 | 0x0050) +#define NT_STATUS_NONEXISTENT_EA_ENTRY (0xC0000000 | 0x0051) +#define NT_STATUS_NO_EAS_ON_FILE (0xC0000000 | 0x0052) +#define NT_STATUS_EA_CORRUPT_ERROR (0xC0000000 | 0x0053) +#define NT_STATUS_FILE_LOCK_CONFLICT (0xC0000000 | 0x0054) +#define NT_STATUS_LOCK_NOT_GRANTED (0xC0000000 | 0x0055) +#define NT_STATUS_DELETE_PENDING (0xC0000000 | 0x0056) +#define NT_STATUS_CTL_FILE_NOT_SUPPORTED (0xC0000000 | 0x0057) +#define NT_STATUS_UNKNOWN_REVISION (0xC0000000 | 0x0058) +#define NT_STATUS_REVISION_MISMATCH (0xC0000000 | 0x0059) +#define NT_STATUS_INVALID_OWNER (0xC0000000 | 0x005a) +#define NT_STATUS_INVALID_PRIMARY_GROUP (0xC0000000 | 0x005b) +#define NT_STATUS_NO_IMPERSONATION_TOKEN (0xC0000000 | 0x005c) +#define NT_STATUS_CANT_DISABLE_MANDATORY (0xC0000000 | 0x005d) +#define NT_STATUS_NO_LOGON_SERVERS (0xC0000000 | 0x005e) +#define NT_STATUS_NO_SUCH_LOGON_SESSION (0xC0000000 | 0x005f) +#define NT_STATUS_NO_SUCH_PRIVILEGE (0xC0000000 | 0x0060) +#define NT_STATUS_PRIVILEGE_NOT_HELD (0xC0000000 | 0x0061) +#define NT_STATUS_INVALID_ACCOUNT_NAME (0xC0000000 | 0x0062) +#define NT_STATUS_USER_EXISTS (0xC0000000 | 0x0063) +#define NT_STATUS_NO_SUCH_USER (0xC0000000 | 0x0064) +#define NT_STATUS_GROUP_EXISTS (0xC0000000 | 0x0065) +#define NT_STATUS_NO_SUCH_GROUP (0xC0000000 | 0x0066) +#define NT_STATUS_MEMBER_IN_GROUP (0xC0000000 | 0x0067) +#define NT_STATUS_MEMBER_NOT_IN_GROUP (0xC0000000 | 0x0068) +#define NT_STATUS_LAST_ADMIN (0xC0000000 | 0x0069) +#define NT_STATUS_WRONG_PASSWORD (0xC0000000 | 0x006a) +#define NT_STATUS_ILL_FORMED_PASSWORD (0xC0000000 | 0x006b) +#define NT_STATUS_PASSWORD_RESTRICTION (0xC0000000 | 0x006c) +#define NT_STATUS_LOGON_FAILURE (0xC0000000 | 0x006d) +#define NT_STATUS_ACCOUNT_RESTRICTION (0xC0000000 | 0x006e) +#define NT_STATUS_INVALID_LOGON_HOURS (0xC0000000 | 0x006f) +#define NT_STATUS_INVALID_WORKSTATION (0xC0000000 | 0x0070) +#define NT_STATUS_PASSWORD_EXPIRED (0xC0000000 | 0x0071) +#define NT_STATUS_ACCOUNT_DISABLED (0xC0000000 | 0x0072) +#define NT_STATUS_NONE_MAPPED (0xC0000000 | 0x0073) +#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED (0xC0000000 | 0x0074) +#define NT_STATUS_LUIDS_EXHAUSTED (0xC0000000 | 0x0075) +#define NT_STATUS_INVALID_SUB_AUTHORITY (0xC0000000 | 0x0076) +#define NT_STATUS_INVALID_ACL (0xC0000000 | 0x0077) +#define NT_STATUS_INVALID_SID (0xC0000000 | 0x0078) +#define NT_STATUS_INVALID_SECURITY_DESCR (0xC0000000 | 0x0079) +#define NT_STATUS_PROCEDURE_NOT_FOUND (0xC0000000 | 0x007a) +#define NT_STATUS_INVALID_IMAGE_FORMAT (0xC0000000 | 0x007b) +#define NT_STATUS_NO_TOKEN (0xC0000000 | 0x007c) +#define NT_STATUS_BAD_INHERITANCE_ACL (0xC0000000 | 0x007d) +#define NT_STATUS_RANGE_NOT_LOCKED (0xC0000000 | 0x007e) +#define NT_STATUS_DISK_FULL (0xC0000000 | 0x007f) +#define NT_STATUS_SERVER_DISABLED (0xC0000000 | 0x0080) +#define NT_STATUS_SERVER_NOT_DISABLED (0xC0000000 | 0x0081) +#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED (0xC0000000 | 0x0082) +#define NT_STATUS_GUIDS_EXHAUSTED (0xC0000000 | 0x0083) +#define NT_STATUS_INVALID_ID_AUTHORITY (0xC0000000 | 0x0084) +#define NT_STATUS_AGENTS_EXHAUSTED (0xC0000000 | 0x0085) +#define NT_STATUS_INVALID_VOLUME_LABEL (0xC0000000 | 0x0086) +#define NT_STATUS_SECTION_NOT_EXTENDED (0xC0000000 | 0x0087) +#define NT_STATUS_NOT_MAPPED_DATA (0xC0000000 | 0x0088) +#define NT_STATUS_RESOURCE_DATA_NOT_FOUND (0xC0000000 | 0x0089) +#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND (0xC0000000 | 0x008a) +#define NT_STATUS_RESOURCE_NAME_NOT_FOUND (0xC0000000 | 0x008b) +#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED (0xC0000000 | 0x008c) +#define NT_STATUS_FLOAT_DENORMAL_OPERAND (0xC0000000 | 0x008d) +#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO (0xC0000000 | 0x008e) +#define NT_STATUS_FLOAT_INEXACT_RESULT (0xC0000000 | 0x008f) +#define NT_STATUS_FLOAT_INVALID_OPERATION (0xC0000000 | 0x0090) +#define NT_STATUS_FLOAT_OVERFLOW (0xC0000000 | 0x0091) +#define NT_STATUS_FLOAT_STACK_CHECK (0xC0000000 | 0x0092) +#define NT_STATUS_FLOAT_UNDERFLOW (0xC0000000 | 0x0093) +#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO (0xC0000000 | 0x0094) +#define NT_STATUS_INTEGER_OVERFLOW (0xC0000000 | 0x0095) +#define NT_STATUS_PRIVILEGED_INSTRUCTION (0xC0000000 | 0x0096) +#define NT_STATUS_TOO_MANY_PAGING_FILES (0xC0000000 | 0x0097) +#define NT_STATUS_FILE_INVALID (0xC0000000 | 0x0098) +#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED (0xC0000000 | 0x0099) +#define NT_STATUS_INSUFFICIENT_RESOURCES (0xC0000000 | 0x009a) +#define NT_STATUS_DFS_EXIT_PATH_FOUND (0xC0000000 | 0x009b) +#define NT_STATUS_DEVICE_DATA_ERROR (0xC0000000 | 0x009c) +#define NT_STATUS_DEVICE_NOT_CONNECTED (0xC0000000 | 0x009d) +#define NT_STATUS_DEVICE_POWER_FAILURE (0xC0000000 | 0x009e) +#define NT_STATUS_FREE_VM_NOT_AT_BASE (0xC0000000 | 0x009f) +#define NT_STATUS_MEMORY_NOT_ALLOCATED (0xC0000000 | 0x00a0) +#define NT_STATUS_WORKING_SET_QUOTA (0xC0000000 | 0x00a1) +#define NT_STATUS_MEDIA_WRITE_PROTECTED (0xC0000000 | 0x00a2) +#define NT_STATUS_DEVICE_NOT_READY (0xC0000000 | 0x00a3) +#define NT_STATUS_INVALID_GROUP_ATTRIBUTES (0xC0000000 | 0x00a4) +#define NT_STATUS_BAD_IMPERSONATION_LEVEL (0xC0000000 | 0x00a5) +#define NT_STATUS_CANT_OPEN_ANONYMOUS (0xC0000000 | 0x00a6) +#define NT_STATUS_BAD_VALIDATION_CLASS (0xC0000000 | 0x00a7) +#define NT_STATUS_BAD_TOKEN_TYPE (0xC0000000 | 0x00a8) +#define NT_STATUS_BAD_MASTER_BOOT_RECORD (0xC0000000 | 0x00a9) +#define NT_STATUS_INSTRUCTION_MISALIGNMENT (0xC0000000 | 0x00aa) +#define NT_STATUS_INSTANCE_NOT_AVAILABLE (0xC0000000 | 0x00ab) +#define NT_STATUS_PIPE_NOT_AVAILABLE (0xC0000000 | 0x00ac) +#define NT_STATUS_INVALID_PIPE_STATE (0xC0000000 | 0x00ad) +#define NT_STATUS_PIPE_BUSY (0xC0000000 | 0x00ae) +#define NT_STATUS_ILLEGAL_FUNCTION (0xC0000000 | 0x00af) +#define NT_STATUS_PIPE_DISCONNECTED (0xC0000000 | 0x00b0) +#define NT_STATUS_PIPE_CLOSING (0xC0000000 | 0x00b1) +#define NT_STATUS_PIPE_CONNECTED (0xC0000000 | 0x00b2) +#define NT_STATUS_PIPE_LISTENING (0xC0000000 | 0x00b3) +#define NT_STATUS_INVALID_READ_MODE (0xC0000000 | 0x00b4) +#define NT_STATUS_IO_TIMEOUT (0xC0000000 | 0x00b5) +#define NT_STATUS_FILE_FORCED_CLOSED (0xC0000000 | 0x00b6) +#define NT_STATUS_PROFILING_NOT_STARTED (0xC0000000 | 0x00b7) +#define NT_STATUS_PROFILING_NOT_STOPPED (0xC0000000 | 0x00b8) +#define NT_STATUS_COULD_NOT_INTERPRET (0xC0000000 | 0x00b9) +#define NT_STATUS_FILE_IS_A_DIRECTORY (0xC0000000 | 0x00ba) +#define NT_STATUS_NOT_SUPPORTED (0xC0000000 | 0x00bb) +#define NT_STATUS_REMOTE_NOT_LISTENING (0xC0000000 | 0x00bc) +#define NT_STATUS_DUPLICATE_NAME (0xC0000000 | 0x00bd) +#define NT_STATUS_BAD_NETWORK_PATH (0xC0000000 | 0x00be) +#define NT_STATUS_NETWORK_BUSY (0xC0000000 | 0x00bf) +#define NT_STATUS_DEVICE_DOES_NOT_EXIST (0xC0000000 | 0x00c0) +#define NT_STATUS_TOO_MANY_COMMANDS (0xC0000000 | 0x00c1) +#define NT_STATUS_ADAPTER_HARDWARE_ERROR (0xC0000000 | 0x00c2) +#define NT_STATUS_INVALID_NETWORK_RESPONSE (0xC0000000 | 0x00c3) +#define NT_STATUS_UNEXPECTED_NETWORK_ERROR (0xC0000000 | 0x00c4) +#define NT_STATUS_BAD_REMOTE_ADAPTER (0xC0000000 | 0x00c5) +#define NT_STATUS_PRINT_QUEUE_FULL (0xC0000000 | 0x00c6) +#define NT_STATUS_NO_SPOOL_SPACE (0xC0000000 | 0x00c7) +#define NT_STATUS_PRINT_CANCELLED (0xC0000000 | 0x00c8) +#define NT_STATUS_NETWORK_NAME_DELETED (0xC0000000 | 0x00c9) +#define NT_STATUS_NETWORK_ACCESS_DENIED (0xC0000000 | 0x00ca) +#define NT_STATUS_BAD_DEVICE_TYPE (0xC0000000 | 0x00cb) +#define NT_STATUS_BAD_NETWORK_NAME (0xC0000000 | 0x00cc) +#define NT_STATUS_TOO_MANY_NAMES (0xC0000000 | 0x00cd) +#define NT_STATUS_TOO_MANY_SESSIONS (0xC0000000 | 0x00ce) +#define NT_STATUS_SHARING_PAUSED (0xC0000000 | 0x00cf) +#define NT_STATUS_REQUEST_NOT_ACCEPTED (0xC0000000 | 0x00d0) +#define NT_STATUS_REDIRECTOR_PAUSED (0xC0000000 | 0x00d1) +#define NT_STATUS_NET_WRITE_FAULT (0xC0000000 | 0x00d2) +#define NT_STATUS_PROFILING_AT_LIMIT (0xC0000000 | 0x00d3) +#define NT_STATUS_NOT_SAME_DEVICE (0xC0000000 | 0x00d4) +#define NT_STATUS_FILE_RENAMED (0xC0000000 | 0x00d5) +#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED (0xC0000000 | 0x00d6) +#define NT_STATUS_NO_SECURITY_ON_OBJECT (0xC0000000 | 0x00d7) +#define NT_STATUS_CANT_WAIT (0xC0000000 | 0x00d8) +#define NT_STATUS_PIPE_EMPTY (0xC0000000 | 0x00d9) +#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO (0xC0000000 | 0x00da) +#define NT_STATUS_CANT_TERMINATE_SELF (0xC0000000 | 0x00db) +#define NT_STATUS_INVALID_SERVER_STATE (0xC0000000 | 0x00dc) +#define NT_STATUS_INVALID_DOMAIN_STATE (0xC0000000 | 0x00dd) +#define NT_STATUS_INVALID_DOMAIN_ROLE (0xC0000000 | 0x00de) +#define NT_STATUS_NO_SUCH_DOMAIN (0xC0000000 | 0x00df) +#define NT_STATUS_DOMAIN_EXISTS (0xC0000000 | 0x00e0) +#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED (0xC0000000 | 0x00e1) +#define NT_STATUS_OPLOCK_NOT_GRANTED (0xC0000000 | 0x00e2) +#define NT_STATUS_INVALID_OPLOCK_PROTOCOL (0xC0000000 | 0x00e3) +#define NT_STATUS_INTERNAL_DB_CORRUPTION (0xC0000000 | 0x00e4) +#define NT_STATUS_INTERNAL_ERROR (0xC0000000 | 0x00e5) +#define NT_STATUS_GENERIC_NOT_MAPPED (0xC0000000 | 0x00e6) +#define NT_STATUS_BAD_DESCRIPTOR_FORMAT (0xC0000000 | 0x00e7) +#define NT_STATUS_INVALID_USER_BUFFER (0xC0000000 | 0x00e8) +#define NT_STATUS_UNEXPECTED_IO_ERROR (0xC0000000 | 0x00e9) +#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR (0xC0000000 | 0x00ea) +#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR (0xC0000000 | 0x00eb) +#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR (0xC0000000 | 0x00ec) +#define NT_STATUS_NOT_LOGON_PROCESS (0xC0000000 | 0x00ed) +#define NT_STATUS_LOGON_SESSION_EXISTS (0xC0000000 | 0x00ee) +#define NT_STATUS_INVALID_PARAMETER_1 (0xC0000000 | 0x00ef) +#define NT_STATUS_INVALID_PARAMETER_2 (0xC0000000 | 0x00f0) +#define NT_STATUS_INVALID_PARAMETER_3 (0xC0000000 | 0x00f1) +#define NT_STATUS_INVALID_PARAMETER_4 (0xC0000000 | 0x00f2) +#define NT_STATUS_INVALID_PARAMETER_5 (0xC0000000 | 0x00f3) +#define NT_STATUS_INVALID_PARAMETER_6 (0xC0000000 | 0x00f4) +#define NT_STATUS_INVALID_PARAMETER_7 (0xC0000000 | 0x00f5) +#define NT_STATUS_INVALID_PARAMETER_8 (0xC0000000 | 0x00f6) +#define NT_STATUS_INVALID_PARAMETER_9 (0xC0000000 | 0x00f7) +#define NT_STATUS_INVALID_PARAMETER_10 (0xC0000000 | 0x00f8) +#define NT_STATUS_INVALID_PARAMETER_11 (0xC0000000 | 0x00f9) +#define NT_STATUS_INVALID_PARAMETER_12 (0xC0000000 | 0x00fa) +#define NT_STATUS_REDIRECTOR_NOT_STARTED (0xC0000000 | 0x00fb) +#define NT_STATUS_REDIRECTOR_STARTED (0xC0000000 | 0x00fc) +#define NT_STATUS_STACK_OVERFLOW (0xC0000000 | 0x00fd) +#define NT_STATUS_NO_SUCH_PACKAGE (0xC0000000 | 0x00fe) +#define NT_STATUS_BAD_FUNCTION_TABLE (0xC0000000 | 0x00ff) +#define NT_STATUS_DIRECTORY_NOT_EMPTY (0xC0000000 | 0x0101) +#define NT_STATUS_FILE_CORRUPT_ERROR (0xC0000000 | 0x0102) +#define NT_STATUS_NOT_A_DIRECTORY (0xC0000000 | 0x0103) +#define NT_STATUS_BAD_LOGON_SESSION_STATE (0xC0000000 | 0x0104) +#define NT_STATUS_LOGON_SESSION_COLLISION (0xC0000000 | 0x0105) +#define NT_STATUS_NAME_TOO_LONG (0xC0000000 | 0x0106) +#define NT_STATUS_FILES_OPEN (0xC0000000 | 0x0107) +#define NT_STATUS_CONNECTION_IN_USE (0xC0000000 | 0x0108) +#define NT_STATUS_MESSAGE_NOT_FOUND (0xC0000000 | 0x0109) +#define NT_STATUS_PROCESS_IS_TERMINATING (0xC0000000 | 0x010a) +#define NT_STATUS_INVALID_LOGON_TYPE (0xC0000000 | 0x010b) +#define NT_STATUS_NO_GUID_TRANSLATION (0xC0000000 | 0x010c) +#define NT_STATUS_CANNOT_IMPERSONATE (0xC0000000 | 0x010d) +#define NT_STATUS_IMAGE_ALREADY_LOADED (0xC0000000 | 0x010e) +#define NT_STATUS_ABIOS_NOT_PRESENT (0xC0000000 | 0x010f) +#define NT_STATUS_ABIOS_LID_NOT_EXIST (0xC0000000 | 0x0110) +#define NT_STATUS_ABIOS_LID_ALREADY_OWNED (0xC0000000 | 0x0111) +#define NT_STATUS_ABIOS_NOT_LID_OWNER (0xC0000000 | 0x0112) +#define NT_STATUS_ABIOS_INVALID_COMMAND (0xC0000000 | 0x0113) +#define NT_STATUS_ABIOS_INVALID_LID (0xC0000000 | 0x0114) +#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE (0xC0000000 | 0x0115) +#define NT_STATUS_ABIOS_INVALID_SELECTOR (0xC0000000 | 0x0116) +#define NT_STATUS_NO_LDT (0xC0000000 | 0x0117) +#define NT_STATUS_INVALID_LDT_SIZE (0xC0000000 | 0x0118) +#define NT_STATUS_INVALID_LDT_OFFSET (0xC0000000 | 0x0119) +#define NT_STATUS_INVALID_LDT_DESCRIPTOR (0xC0000000 | 0x011a) +#define NT_STATUS_INVALID_IMAGE_NE_FORMAT (0xC0000000 | 0x011b) +#define NT_STATUS_RXACT_INVALID_STATE (0xC0000000 | 0x011c) +#define NT_STATUS_RXACT_COMMIT_FAILURE (0xC0000000 | 0x011d) +#define NT_STATUS_MAPPED_FILE_SIZE_ZERO (0xC0000000 | 0x011e) +#define NT_STATUS_TOO_MANY_OPENED_FILES (0xC0000000 | 0x011f) +#define NT_STATUS_CANCELLED (0xC0000000 | 0x0120) +#define NT_STATUS_CANNOT_DELETE (0xC0000000 | 0x0121) +#define NT_STATUS_INVALID_COMPUTER_NAME (0xC0000000 | 0x0122) +#define NT_STATUS_FILE_DELETED (0xC0000000 | 0x0123) +#define NT_STATUS_SPECIAL_ACCOUNT (0xC0000000 | 0x0124) +#define NT_STATUS_SPECIAL_GROUP (0xC0000000 | 0x0125) +#define NT_STATUS_SPECIAL_USER (0xC0000000 | 0x0126) +#define NT_STATUS_MEMBERS_PRIMARY_GROUP (0xC0000000 | 0x0127) +#define NT_STATUS_FILE_CLOSED (0xC0000000 | 0x0128) +#define NT_STATUS_TOO_MANY_THREADS (0xC0000000 | 0x0129) +#define NT_STATUS_THREAD_NOT_IN_PROCESS (0xC0000000 | 0x012a) +#define NT_STATUS_TOKEN_ALREADY_IN_USE (0xC0000000 | 0x012b) +#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED (0xC0000000 | 0x012c) +#define NT_STATUS_COMMITMENT_LIMIT (0xC0000000 | 0x012d) +#define NT_STATUS_INVALID_IMAGE_LE_FORMAT (0xC0000000 | 0x012e) +#define NT_STATUS_INVALID_IMAGE_NOT_MZ (0xC0000000 | 0x012f) +#define NT_STATUS_INVALID_IMAGE_PROTECT (0xC0000000 | 0x0130) +#define NT_STATUS_INVALID_IMAGE_WIN_16 (0xC0000000 | 0x0131) +#define NT_STATUS_LOGON_SERVER_CONFLICT (0xC0000000 | 0x0132) +#define NT_STATUS_TIME_DIFFERENCE_AT_DC (0xC0000000 | 0x0133) +#define NT_STATUS_SYNCHRONIZATION_REQUIRED (0xC0000000 | 0x0134) +#define NT_STATUS_DLL_NOT_FOUND (0xC0000000 | 0x0135) +#define NT_STATUS_OPEN_FAILED (0xC0000000 | 0x0136) +#define NT_STATUS_IO_PRIVILEGE_FAILED (0xC0000000 | 0x0137) +#define NT_STATUS_ORDINAL_NOT_FOUND (0xC0000000 | 0x0138) +#define NT_STATUS_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0139) +#define NT_STATUS_CONTROL_C_EXIT (0xC0000000 | 0x013a) +#define NT_STATUS_LOCAL_DISCONNECT (0xC0000000 | 0x013b) +#define NT_STATUS_REMOTE_DISCONNECT (0xC0000000 | 0x013c) +#define NT_STATUS_REMOTE_RESOURCES (0xC0000000 | 0x013d) +#define NT_STATUS_LINK_FAILED (0xC0000000 | 0x013e) +#define NT_STATUS_LINK_TIMEOUT (0xC0000000 | 0x013f) +#define NT_STATUS_INVALID_CONNECTION (0xC0000000 | 0x0140) +#define NT_STATUS_INVALID_ADDRESS (0xC0000000 | 0x0141) +#define NT_STATUS_DLL_INIT_FAILED (0xC0000000 | 0x0142) +#define NT_STATUS_MISSING_SYSTEMFILE (0xC0000000 | 0x0143) +#define NT_STATUS_UNHANDLED_EXCEPTION (0xC0000000 | 0x0144) +#define NT_STATUS_APP_INIT_FAILURE (0xC0000000 | 0x0145) +#define NT_STATUS_PAGEFILE_CREATE_FAILED (0xC0000000 | 0x0146) +#define NT_STATUS_NO_PAGEFILE (0xC0000000 | 0x0147) +#define NT_STATUS_INVALID_LEVEL (0xC0000000 | 0x0148) +#define NT_STATUS_WRONG_PASSWORD_CORE (0xC0000000 | 0x0149) +#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT (0xC0000000 | 0x014a) +#define NT_STATUS_PIPE_BROKEN (0xC0000000 | 0x014b) +#define NT_STATUS_REGISTRY_CORRUPT (0xC0000000 | 0x014c) +#define NT_STATUS_REGISTRY_IO_FAILED (0xC0000000 | 0x014d) +#define NT_STATUS_NO_EVENT_PAIR (0xC0000000 | 0x014e) +#define NT_STATUS_UNRECOGNIZED_VOLUME (0xC0000000 | 0x014f) +#define NT_STATUS_SERIAL_NO_DEVICE_INITED (0xC0000000 | 0x0150) +#define NT_STATUS_NO_SUCH_ALIAS (0xC0000000 | 0x0151) +#define NT_STATUS_MEMBER_NOT_IN_ALIAS (0xC0000000 | 0x0152) +#define NT_STATUS_MEMBER_IN_ALIAS (0xC0000000 | 0x0153) +#define NT_STATUS_ALIAS_EXISTS (0xC0000000 | 0x0154) +#define NT_STATUS_LOGON_NOT_GRANTED (0xC0000000 | 0x0155) +#define NT_STATUS_TOO_MANY_SECRETS (0xC0000000 | 0x0156) +#define NT_STATUS_SECRET_TOO_LONG (0xC0000000 | 0x0157) +#define NT_STATUS_INTERNAL_DB_ERROR (0xC0000000 | 0x0158) +#define NT_STATUS_FULLSCREEN_MODE (0xC0000000 | 0x0159) +#define NT_STATUS_TOO_MANY_CONTEXT_IDS (0xC0000000 | 0x015a) +#define NT_STATUS_LOGON_TYPE_NOT_GRANTED (0xC0000000 | 0x015b) +#define NT_STATUS_NOT_REGISTRY_FILE (0xC0000000 | 0x015c) +#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x015d) +#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR (0xC0000000 | 0x015e) +#define NT_STATUS_FT_MISSING_MEMBER (0xC0000000 | 0x015f) +#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY (0xC0000000 | 0x0160) +#define NT_STATUS_ILLEGAL_CHARACTER (0xC0000000 | 0x0161) +#define NT_STATUS_UNMAPPABLE_CHARACTER (0xC0000000 | 0x0162) +#define NT_STATUS_UNDEFINED_CHARACTER (0xC0000000 | 0x0163) +#define NT_STATUS_FLOPPY_VOLUME (0xC0000000 | 0x0164) +#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND (0xC0000000 | 0x0165) +#define NT_STATUS_FLOPPY_WRONG_CYLINDER (0xC0000000 | 0x0166) +#define NT_STATUS_FLOPPY_UNKNOWN_ERROR (0xC0000000 | 0x0167) +#define NT_STATUS_FLOPPY_BAD_REGISTERS (0xC0000000 | 0x0168) +#define NT_STATUS_DISK_RECALIBRATE_FAILED (0xC0000000 | 0x0169) +#define NT_STATUS_DISK_OPERATION_FAILED (0xC0000000 | 0x016a) +#define NT_STATUS_DISK_RESET_FAILED (0xC0000000 | 0x016b) +#define NT_STATUS_SHARED_IRQ_BUSY (0xC0000000 | 0x016c) +#define NT_STATUS_FT_ORPHANING (0xC0000000 | 0x016d) +#define NT_STATUS_PARTITION_FAILURE (0xC0000000 | 0x0172) +#define NT_STATUS_INVALID_BLOCK_LENGTH (0xC0000000 | 0x0173) +#define NT_STATUS_DEVICE_NOT_PARTITIONED (0xC0000000 | 0x0174) +#define NT_STATUS_UNABLE_TO_LOCK_MEDIA (0xC0000000 | 0x0175) +#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA (0xC0000000 | 0x0176) +#define NT_STATUS_EOM_OVERFLOW (0xC0000000 | 0x0177) +#define NT_STATUS_NO_MEDIA (0xC0000000 | 0x0178) +#define NT_STATUS_NO_SUCH_MEMBER (0xC0000000 | 0x017a) +#define NT_STATUS_INVALID_MEMBER (0xC0000000 | 0x017b) +#define NT_STATUS_KEY_DELETED (0xC0000000 | 0x017c) +#define NT_STATUS_NO_LOG_SPACE (0xC0000000 | 0x017d) +#define NT_STATUS_TOO_MANY_SIDS (0xC0000000 | 0x017e) +#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x017f) +#define NT_STATUS_KEY_HAS_CHILDREN (0xC0000000 | 0x0180) +#define NT_STATUS_CHILD_MUST_BE_VOLATILE (0xC0000000 | 0x0181) +#define NT_STATUS_DEVICE_CONFIGURATION_ERROR (0xC0000000 | 0x0182) +#define NT_STATUS_DRIVER_INTERNAL_ERROR (0xC0000000 | 0x0183) +#define NT_STATUS_INVALID_DEVICE_STATE (0xC0000000 | 0x0184) +#define NT_STATUS_IO_DEVICE_ERROR (0xC0000000 | 0x0185) +#define NT_STATUS_DEVICE_PROTOCOL_ERROR (0xC0000000 | 0x0186) +#define NT_STATUS_BACKUP_CONTROLLER (0xC0000000 | 0x0187) +#define NT_STATUS_LOG_FILE_FULL (0xC0000000 | 0x0188) +#define NT_STATUS_TOO_LATE (0xC0000000 | 0x0189) +#define NT_STATUS_NO_TRUST_LSA_SECRET (0xC0000000 | 0x018a) +#define NT_STATUS_NO_TRUST_SAM_ACCOUNT (0xC0000000 | 0x018b) +#define NT_STATUS_TRUSTED_DOMAIN_FAILURE (0xC0000000 | 0x018c) +#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE (0xC0000000 | 0x018d) +#define NT_STATUS_EVENTLOG_FILE_CORRUPT (0xC0000000 | 0x018e) +#define NT_STATUS_EVENTLOG_CANT_START (0xC0000000 | 0x018f) +#define NT_STATUS_TRUST_FAILURE (0xC0000000 | 0x0190) +#define NT_STATUS_MUTANT_LIMIT_EXCEEDED (0xC0000000 | 0x0191) +#define NT_STATUS_NETLOGON_NOT_STARTED (0xC0000000 | 0x0192) +#define NT_STATUS_ACCOUNT_EXPIRED (0xC0000000 | 0x0193) +#define NT_STATUS_POSSIBLE_DEADLOCK (0xC0000000 | 0x0194) +#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT (0xC0000000 | 0x0195) +#define NT_STATUS_REMOTE_SESSION_LIMIT (0xC0000000 | 0x0196) +#define NT_STATUS_EVENTLOG_FILE_CHANGED (0xC0000000 | 0x0197) +#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT (0xC0000000 | 0x0198) +#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT (0xC0000000 | 0x0199) +#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT (0xC0000000 | 0x019a) +#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT (0xC0000000 | 0x019b) +#define NT_STATUS_FS_DRIVER_REQUIRED (0xC0000000 | 0x019c) +#define NT_STATUS_NO_USER_SESSION_KEY (0xC0000000 | 0x0202) +#define NT_STATUS_USER_SESSION_DELETED (0xC0000000 | 0x0203) +#define NT_STATUS_RESOURCE_LANG_NOT_FOUND (0xC0000000 | 0x0204) +#define NT_STATUS_INSUFF_SERVER_RESOURCES (0xC0000000 | 0x0205) +#define NT_STATUS_INVALID_BUFFER_SIZE (0xC0000000 | 0x0206) +#define NT_STATUS_INVALID_ADDRESS_COMPONENT (0xC0000000 | 0x0207) +#define NT_STATUS_INVALID_ADDRESS_WILDCARD (0xC0000000 | 0x0208) +#define NT_STATUS_TOO_MANY_ADDRESSES (0xC0000000 | 0x0209) +#define NT_STATUS_ADDRESS_ALREADY_EXISTS (0xC0000000 | 0x020a) +#define NT_STATUS_ADDRESS_CLOSED (0xC0000000 | 0x020b) +#define NT_STATUS_CONNECTION_DISCONNECTED (0xC0000000 | 0x020c) +#define NT_STATUS_CONNECTION_RESET (0xC0000000 | 0x020d) +#define NT_STATUS_TOO_MANY_NODES (0xC0000000 | 0x020e) +#define NT_STATUS_TRANSACTION_ABORTED (0xC0000000 | 0x020f) +#define NT_STATUS_TRANSACTION_TIMED_OUT (0xC0000000 | 0x0210) +#define NT_STATUS_TRANSACTION_NO_RELEASE (0xC0000000 | 0x0211) +#define NT_STATUS_TRANSACTION_NO_MATCH (0xC0000000 | 0x0212) +#define NT_STATUS_TRANSACTION_RESPONDED (0xC0000000 | 0x0213) +#define NT_STATUS_TRANSACTION_INVALID_ID (0xC0000000 | 0x0214) +#define NT_STATUS_TRANSACTION_INVALID_TYPE (0xC0000000 | 0x0215) +#define NT_STATUS_NOT_SERVER_SESSION (0xC0000000 | 0x0216) +#define NT_STATUS_NOT_CLIENT_SESSION (0xC0000000 | 0x0217) +#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE (0xC0000000 | 0x0218) +#define NT_STATUS_DEBUG_ATTACH_FAILED (0xC0000000 | 0x0219) +#define NT_STATUS_SYSTEM_PROCESS_TERMINATED (0xC0000000 | 0x021a) +#define NT_STATUS_DATA_NOT_ACCEPTED (0xC0000000 | 0x021b) +#define NT_STATUS_NO_BROWSER_SERVERS_FOUND (0xC0000000 | 0x021c) +#define NT_STATUS_VDM_HARD_ERROR (0xC0000000 | 0x021d) +#define NT_STATUS_DRIVER_CANCEL_TIMEOUT (0xC0000000 | 0x021e) +#define NT_STATUS_REPLY_MESSAGE_MISMATCH (0xC0000000 | 0x021f) +#define NT_STATUS_MAPPED_ALIGNMENT (0xC0000000 | 0x0220) +#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH (0xC0000000 | 0x0221) +#define NT_STATUS_LOST_WRITEBEHIND_DATA (0xC0000000 | 0x0222) +#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID (0xC0000000 | 0x0223) +#define NT_STATUS_PASSWORD_MUST_CHANGE (0xC0000000 | 0x0224) +#define NT_STATUS_NOT_FOUND (0xC0000000 | 0x0225) +#define NT_STATUS_NOT_TINY_STREAM (0xC0000000 | 0x0226) +#define NT_STATUS_RECOVERY_FAILURE (0xC0000000 | 0x0227) +#define NT_STATUS_STACK_OVERFLOW_READ (0xC0000000 | 0x0228) +#define NT_STATUS_FAIL_CHECK (0xC0000000 | 0x0229) +#define NT_STATUS_DUPLICATE_OBJECTID (0xC0000000 | 0x022a) +#define NT_STATUS_OBJECTID_EXISTS (0xC0000000 | 0x022b) +#define NT_STATUS_CONVERT_TO_LARGE (0xC0000000 | 0x022c) +#define NT_STATUS_RETRY (0xC0000000 | 0x022d) +#define NT_STATUS_FOUND_OUT_OF_SCOPE (0xC0000000 | 0x022e) +#define NT_STATUS_ALLOCATE_BUCKET (0xC0000000 | 0x022f) +#define NT_STATUS_PROPSET_NOT_FOUND (0xC0000000 | 0x0230) +#define NT_STATUS_MARSHALL_OVERFLOW (0xC0000000 | 0x0231) +#define NT_STATUS_INVALID_VARIANT (0xC0000000 | 0x0232) +#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND (0xC0000000 | 0x0233) +#define NT_STATUS_ACCOUNT_LOCKED_OUT (0xC0000000 | 0x0234) +#define NT_STATUS_HANDLE_NOT_CLOSABLE (0xC0000000 | 0x0235) +#define NT_STATUS_CONNECTION_REFUSED (0xC0000000 | 0x0236) +#define NT_STATUS_GRACEFUL_DISCONNECT (0xC0000000 | 0x0237) +#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED (0xC0000000 | 0x0238) +#define NT_STATUS_ADDRESS_NOT_ASSOCIATED (0xC0000000 | 0x0239) +#define NT_STATUS_CONNECTION_INVALID (0xC0000000 | 0x023a) +#define NT_STATUS_CONNECTION_ACTIVE (0xC0000000 | 0x023b) +#define NT_STATUS_NETWORK_UNREACHABLE (0xC0000000 | 0x023c) +#define NT_STATUS_HOST_UNREACHABLE (0xC0000000 | 0x023d) +#define NT_STATUS_PROTOCOL_UNREACHABLE (0xC0000000 | 0x023e) +#define NT_STATUS_PORT_UNREACHABLE (0xC0000000 | 0x023f) +#define NT_STATUS_REQUEST_ABORTED (0xC0000000 | 0x0240) +#define NT_STATUS_CONNECTION_ABORTED (0xC0000000 | 0x0241) +#define NT_STATUS_BAD_COMPRESSION_BUFFER (0xC0000000 | 0x0242) +#define NT_STATUS_USER_MAPPED_FILE (0xC0000000 | 0x0243) +#define NT_STATUS_AUDIT_FAILED (0xC0000000 | 0x0244) +#define NT_STATUS_TIMER_RESOLUTION_NOT_SET (0xC0000000 | 0x0245) +#define NT_STATUS_CONNECTION_COUNT_LIMIT (0xC0000000 | 0x0246) +#define NT_STATUS_LOGIN_TIME_RESTRICTION (0xC0000000 | 0x0247) +#define NT_STATUS_LOGIN_WKSTA_RESTRICTION (0xC0000000 | 0x0248) +#define NT_STATUS_IMAGE_MP_UP_MISMATCH (0xC0000000 | 0x0249) +#define NT_STATUS_INSUFFICIENT_LOGON_INFO (0xC0000000 | 0x0250) +#define NT_STATUS_BAD_DLL_ENTRYPOINT (0xC0000000 | 0x0251) +#define NT_STATUS_BAD_SERVICE_ENTRYPOINT (0xC0000000 | 0x0252) +#define NT_STATUS_LPC_REPLY_LOST (0xC0000000 | 0x0253) +#define NT_STATUS_IP_ADDRESS_CONFLICT1 (0xC0000000 | 0x0254) +#define NT_STATUS_IP_ADDRESS_CONFLICT2 (0xC0000000 | 0x0255) +#define NT_STATUS_REGISTRY_QUOTA_LIMIT (0xC0000000 | 0x0256) +#define NT_STATUS_PATH_NOT_COVERED (0xC0000000 | 0x0257) +#define NT_STATUS_NO_CALLBACK_ACTIVE (0xC0000000 | 0x0258) +#define NT_STATUS_LICENSE_QUOTA_EXCEEDED (0xC0000000 | 0x0259) +#define NT_STATUS_PWD_TOO_SHORT (0xC0000000 | 0x025a) +#define NT_STATUS_PWD_TOO_RECENT (0xC0000000 | 0x025b) +#define NT_STATUS_PWD_HISTORY_CONFLICT (0xC0000000 | 0x025c) +#define NT_STATUS_PLUGPLAY_NO_DEVICE (0xC0000000 | 0x025e) +#define NT_STATUS_UNSUPPORTED_COMPRESSION (0xC0000000 | 0x025f) +#define NT_STATUS_INVALID_HW_PROFILE (0xC0000000 | 0x0260) +#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH (0xC0000000 | 0x0261) +#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND (0xC0000000 | 0x0262) +#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0263) +#define NT_STATUS_RESOURCE_NOT_OWNED (0xC0000000 | 0x0264) +#define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 0x0265) +#define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 0x0266) +#define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 0x0267) +#define NT_STATUS_NETWORK_SESSION_EXPIRED (0xC0000000 | 0x035c) +#define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) /* scheduler */ +#define NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP (0xC0000000 | 0x5D0000) +#define NT_STATUS_PENDING 0x00000103 +#endif /* _NTERR_H */ diff --git a/fs/cifsd/ntlmssp.h b/fs/cifsd/ntlmssp.h new file mode 100644 index 000000000000..adaf4c0cbe8f --- /dev/null +++ b/fs/cifsd/ntlmssp.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2002,2007 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +#ifndef __KSMBD_NTLMSSP_H +#define __KSMBD_NTLMSSP_H + +#define NTLMSSP_SIGNATURE "NTLMSSP" + +/* Security blob target info data */ +#define TGT_Name "KSMBD" + +/* + * Size of the crypto key returned on the negotiate SMB in bytes + */ +#define CIFS_CRYPTO_KEY_SIZE (8) +#define CIFS_KEY_SIZE (40) + +/* + * Size of encrypted user password in bytes + */ +#define CIFS_ENCPWD_SIZE (16) +#define CIFS_CPHTXT_SIZE (16) + +/* Message Types */ +#define NtLmNegotiate cpu_to_le32(1) +#define NtLmChallenge cpu_to_le32(2) +#define NtLmAuthenticate cpu_to_le32(3) +#define UnknownMessage cpu_to_le32(8) + +/* Negotiate Flags */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */ +#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ +#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */ +/* define reserved9 0x08 */ +#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */ +#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ +#define NTLMSSP_NEGOTIATE_DGRAM 0x0040 +#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */ +/* defined reserved 8 0x0100 */ +#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ +#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */ +#define NTLMSSP_ANONYMOUS 0x0800 +#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */ +#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 +#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */ +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */ +#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 +#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 +#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 +#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/ +/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */ +#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 +#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */ +#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000 +#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 +/* #define reserved4 0x1000000 */ +#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */ +/* #define reserved3 0x4000000 */ +/* #define reserved2 0x8000000 */ +/* #define reserved1 0x10000000 */ +#define NTLMSSP_NEGOTIATE_128 0x20000000 +#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 +#define NTLMSSP_NEGOTIATE_56 0x80000000 + +/* Define AV Pair Field IDs */ +enum av_field_type { + NTLMSSP_AV_EOL = 0, + NTLMSSP_AV_NB_COMPUTER_NAME, + NTLMSSP_AV_NB_DOMAIN_NAME, + NTLMSSP_AV_DNS_COMPUTER_NAME, + NTLMSSP_AV_DNS_DOMAIN_NAME, + NTLMSSP_AV_DNS_TREE_NAME, + NTLMSSP_AV_FLAGS, + NTLMSSP_AV_TIMESTAMP, + NTLMSSP_AV_RESTRICTION, + NTLMSSP_AV_TARGET_NAME, + NTLMSSP_AV_CHANNEL_BINDINGS +}; + +/* Although typedefs are not commonly used for structure definitions */ +/* in the Linux kernel, in this particular case they are useful */ +/* to more closely match the standards document for NTLMSSP from */ +/* OpenGroup and to make the code more closely match the standard in */ +/* appearance */ + +struct security_buffer { + __le16 Length; + __le16 MaximumLength; + __le32 BufferOffset; /* offset to buffer */ +} __packed; + +struct target_info { + __le16 Type; + __le16 Length; + __u8 Content[0]; +} __packed; + +struct negotiate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmNegotiate = 1 */ + __le32 NegotiateFlags; + struct security_buffer DomainName; /* RFC 1001 style and ASCII */ + struct security_buffer WorkstationName; /* RFC 1001 and ASCII */ + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ + char DomainString[0]; + /* followed by WorkstationString */ +} __packed; + +struct challenge_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmChallenge = 2 */ + struct security_buffer TargetName; + __le32 NegotiateFlags; + __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; + __u8 Reserved[8]; + struct security_buffer TargetInfoArray; + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ +} __packed; + +struct authenticate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmsAuthenticate = 3 */ + struct security_buffer LmChallengeResponse; + struct security_buffer NtChallengeResponse; + struct security_buffer DomainName; + struct security_buffer UserName; + struct security_buffer WorkstationName; + struct security_buffer SessionKey; + __le32 NegotiateFlags; + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ + char UserString[0]; +} __packed; + +struct ntlmv2_resp { + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + __le32 blob_signature; + __u32 reserved; + __le64 time; + __u64 client_chal; /* random */ + __u32 reserved2; + /* array of name entries could follow ending in minimum 4 byte struct */ +} __packed; + +/* per smb session structure/fields */ +struct ntlmssp_auth { + /* whether session key is per smb session */ + bool sesskey_per_smbsess; + /* sent by client in type 1 ntlmsssp exchange */ + __u32 client_flags; + /* sent by server in type 2 ntlmssp exchange */ + __u32 conn_flags; + /* sent to server */ + unsigned char ciphertext[CIFS_CPHTXT_SIZE]; + /* used by ntlmssp */ + char cryptkey[CIFS_CRYPTO_KEY_SIZE]; +}; +#endif /* __KSMBD_NTLMSSP_H */ diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c new file mode 100644 index 000000000000..6c3dbc71134e --- /dev/null +++ b/fs/cifsd/oplock.c @@ -0,0 +1,1693 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "glob.h" +#include "oplock.h" + +#include "smb_common.h" +#include "smbstatus.h" +#include "buffer_pool.h" +#include "connection.h" +#include "mgmt/user_session.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" + +static LIST_HEAD(lease_table_list); +static DEFINE_RWLOCK(lease_list_lock); + +/** + * get_new_opinfo() - allocate a new opinfo object for oplock info + * @conn: connection instance + * @id: fid of open file + * @Tid: tree id of connection + * @lctx: lease context information + * + * Return: allocated opinfo object on success, otherwise NULL + */ +static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, + uint64_t id, __u16 Tid) +{ + struct ksmbd_session *sess = work->sess; + struct oplock_info *opinfo; + + opinfo = kzalloc(sizeof(struct oplock_info), GFP_KERNEL); + if (!opinfo) + return NULL; + + opinfo->sess = sess; + opinfo->conn = sess->conn; + opinfo->level = OPLOCK_NONE; + opinfo->op_state = OPLOCK_STATE_NONE; + opinfo->pending_break = 0; + opinfo->fid = id; + opinfo->Tid = Tid; + INIT_LIST_HEAD(&opinfo->op_entry); + INIT_LIST_HEAD(&opinfo->interim_list); + init_waitqueue_head(&opinfo->oplock_q); + init_waitqueue_head(&opinfo->oplock_brk); + atomic_set(&opinfo->refcount, 1); + atomic_set(&opinfo->breaking_cnt, 0); + + return opinfo; +} + +static void lease_add_list(struct oplock_info *opinfo) +{ + struct lease_table *lb = opinfo->o_lease->l_lb; + + spin_lock(&lb->lb_lock); + list_add_rcu(&opinfo->lease_entry, &lb->lease_list); + spin_unlock(&lb->lb_lock); +} + +static void lease_del_list(struct oplock_info *opinfo) +{ + struct lease_table *lb = opinfo->o_lease->l_lb; + + if (!lb) + return; + + spin_lock(&lb->lb_lock); + if (list_empty(&opinfo->lease_entry)) { + spin_unlock(&lb->lb_lock); + return; + } + + list_del_init(&opinfo->lease_entry); + opinfo->o_lease->l_lb = NULL; + spin_unlock(&lb->lb_lock); +} + +static void lb_add(struct lease_table *lb) +{ + write_lock(&lease_list_lock); + list_add(&lb->l_entry, &lease_table_list); + write_unlock(&lease_list_lock); +} + +static int alloc_lease(struct oplock_info *opinfo, + struct lease_ctx_info *lctx) +{ + struct lease *lease; + + lease = kmalloc(sizeof(struct lease), GFP_KERNEL); + if (!lease) + return -ENOMEM; + + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + lease->state = lctx->req_state; + lease->new_state = 0; + lease->flags = lctx->flags; + lease->duration = lctx->duration; + INIT_LIST_HEAD(&opinfo->lease_entry); + opinfo->o_lease = lease; + + return 0; +} + +static void free_lease(struct oplock_info *opinfo) +{ + struct lease *lease; + + lease = opinfo->o_lease; + kfree(lease); +} + +static void free_opinfo(struct oplock_info *opinfo) +{ + if (opinfo->is_lease) + free_lease(opinfo); + kfree(opinfo); +} + +static inline void opinfo_free_rcu(struct rcu_head *rcu_head) +{ + struct oplock_info *opinfo; + + opinfo = container_of(rcu_head, struct oplock_info, rcu_head); + free_opinfo(opinfo); +} + +struct oplock_info *opinfo_get(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo; + + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + rcu_read_unlock(); + + return opinfo; +} + +static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) +{ + struct oplock_info *opinfo; + + if (list_empty(&ci->m_op_list)) + return NULL; + + rcu_read_lock(); + opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, + op_entry); + if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + rcu_read_unlock(); + + return opinfo; +} + +void opinfo_put(struct oplock_info *opinfo) +{ + if (!atomic_dec_and_test(&opinfo->refcount)) + return; + + call_rcu(&opinfo->rcu_head, opinfo_free_rcu); +} + +static void opinfo_add(struct oplock_info *opinfo) +{ + struct ksmbd_inode *ci = opinfo->o_fp->f_ci; + + write_lock(&ci->m_lock); + list_add_rcu(&opinfo->op_entry, &ci->m_op_list); + write_unlock(&ci->m_lock); +} + +static void opinfo_del(struct oplock_info *opinfo) +{ + struct ksmbd_inode *ci = opinfo->o_fp->f_ci; + + if (opinfo->is_lease) { + write_lock(&lease_list_lock); + lease_del_list(opinfo); + write_unlock(&lease_list_lock); + } + write_lock(&ci->m_lock); + list_del_rcu(&opinfo->op_entry); + write_unlock(&ci->m_lock); +} + +static unsigned long opinfo_count(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_read(&fp->f_ci->sop_count); + else + return atomic_read(&fp->f_ci->op_count); +} + +static void opinfo_count_inc(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_inc(&fp->f_ci->sop_count); + else + return atomic_inc(&fp->f_ci->op_count); +} + +static void opinfo_count_dec(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_dec(&fp->f_ci->sop_count); + else + return atomic_dec(&fp->f_ci->op_count); +} + +/** + * opinfo_write_to_read() - convert a write oplock to read oplock + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_write_to_read(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (!((opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) || + (opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE))) { + ksmbd_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + ksmbd_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + if (opinfo->is_lease) + lease->state = lease->new_state; + return 0; +} + +/** + * opinfo_read_handle_to_read() - convert a read/handle oplock to read oplock + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_read_handle_to_read(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + lease->state = lease->new_state; + opinfo->level = SMB2_OPLOCK_LEVEL_II; + return 0; +} + +/** + * opinfo_write_to_none() - convert a write oplock to none + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_write_to_none(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (!((opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) || + (opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE))) { + ksmbd_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + ksmbd_err("lease state(0x%x)\n", + lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; + return 0; +} + +/** + * opinfo_read_to_none() - convert a write read to none + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_read_to_none(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (opinfo->level != SMB2_OPLOCK_LEVEL_II) { + ksmbd_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + ksmbd_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; + return 0; +} + +/** + * lease_read_to_write() - upgrade lease state from read to write + * @opinfo: current lease info + * + * Return: 0 on success, otherwise -EINVAL + */ +int lease_read_to_write(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (!(lease->state & SMB2_LEASE_READ_CACHING_LE)) { + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", + lease->state); + return -EINVAL; + } + + lease->new_state = SMB2_LEASE_NONE_LE; + lease->state |= SMB2_LEASE_WRITE_CACHING_LE; + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + return 0; +} + +/** + * lease_none_upgrade() - upgrade lease state from none + * @opinfo: current lease info + * @new_state: new lease state + * + * Return: 0 on success, otherwise -EINVAL + */ +static int lease_none_upgrade(struct oplock_info *opinfo, + __le32 new_state) +{ + struct lease *lease = opinfo->o_lease; + + if (!(lease->state == SMB2_LEASE_NONE_LE)) { + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", + lease->state); + return -EINVAL; + } + + lease->new_state = SMB2_LEASE_NONE_LE; + lease->state = new_state; + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo->level = SMB2_OPLOCK_LEVEL_II; + else if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + else if (lease->state & SMB2_LEASE_READ_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + return 0; +} + +/** + * close_id_del_oplock() - release oplock object at file close time + * @fp: ksmbd file pointer + */ +void close_id_del_oplock(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo; + + if (S_ISDIR(file_inode(fp->filp)->i_mode)) + return; + + opinfo = opinfo_get(fp); + if (!opinfo) + return; + + opinfo_del(opinfo); + + rcu_assign_pointer(fp->f_opinfo, NULL); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + opinfo->op_state = OPLOCK_CLOSING; + wake_up_interruptible_all(&opinfo->oplock_q); + if (opinfo->is_lease) { + atomic_set(&opinfo->breaking_cnt, 0); + wake_up_interruptible_all(&opinfo->oplock_brk); + } + } + + opinfo_count_dec(fp); + atomic_dec(&opinfo->refcount); + opinfo_put(opinfo); +} + +/** + * grant_write_oplock() - grant exclusive/batch oplock or write lease + * @opinfo_new: new oplock info object + * @req_oplock: request oplock + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + + if (req_oplock == SMB2_OPLOCK_LEVEL_BATCH) + opinfo_new->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo_new->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + if (lctx) { + lease->state = lctx->req_state; + memcpy(lease->lease_key, lctx->lease_key, + SMB2_LEASE_KEY_SIZE); + } +} + +/** + * grant_read_oplock() - grant level2 oplock or read lease + * @opinfo_new: new oplock info object + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_read_oplock(struct oplock_info *opinfo_new, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + + opinfo_new->level = SMB2_OPLOCK_LEVEL_II; + + if (lctx) { + lease->state = SMB2_LEASE_READ_CACHING_LE; + if (lctx->req_state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->state |= SMB2_LEASE_HANDLE_CACHING_LE; + memcpy(lease->lease_key, lctx->lease_key, + SMB2_LEASE_KEY_SIZE); + } +} + +/** + * grant_none_oplock() - grant none oplock or none lease + * @opinfo_new: new oplock info object + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_none_oplock(struct oplock_info *opinfo_new, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + + opinfo_new->level = SMB2_OPLOCK_LEVEL_NONE; + + if (lctx) { + lease->state = 0; + memcpy(lease->lease_key, lctx->lease_key, + SMB2_LEASE_KEY_SIZE); + } +} + +/** + * find_opinfo() - find lease object for given client guid and lease key + * @head: oplock list(read,write or none) head + * @guid1: client guid of matching lease owner + * @key1: lease key of matching lease owner + * + * Return: oplock(lease) object on success, otherwise NULL + */ +static inline int compare_guid_key(struct oplock_info *opinfo, + const char *guid1, const char *key1) +{ + const char *guid2, *key2; + + guid2 = opinfo->conn->ClientGUID; + key2 = opinfo->o_lease->lease_key; + if (!memcmp(guid1, guid2, SMB2_CLIENT_GUID_SIZE) && + !memcmp(key1, key2, SMB2_LEASE_KEY_SIZE)) + return 1; + + return 0; +} + +/** + * same_client_has_lease() - check whether current lease request is + * from lease owner of file + * @ci: master file pointer + * @client_guid: Client GUID + * @lctx: lease context information + * + * Return: oplock(lease) object on success, otherwise NULL + */ +static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, + char *client_guid, struct lease_ctx_info *lctx) +{ + int ret; + struct lease *lease; + struct oplock_info *opinfo; + struct oplock_info *m_opinfo = NULL; + + if (!lctx) + return NULL; + + /* + * Compare lease key and client_guid to know request from same owner + * of same client + */ + read_lock(&ci->m_lock); + list_for_each_entry(opinfo, &ci->m_op_list, op_entry) { + if (!opinfo->is_lease) + continue; + read_unlock(&ci->m_lock); + lease = opinfo->o_lease; + + ret = compare_guid_key(opinfo, client_guid, lctx->lease_key); + if (ret) { + m_opinfo = opinfo; + /* skip upgrading lease about breaking lease */ + if (atomic_read(&opinfo->breaking_cnt)) { + read_lock(&ci->m_lock); + continue; + } + + /* upgrading lease */ + if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) == 1) { + if (lease->state == + (lctx->req_state & lease->state)) { + lease->state |= lctx->req_state; + if (lctx->req_state & + SMB2_LEASE_WRITE_CACHING_LE) + lease_read_to_write(opinfo); + } + } else if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) > 1) { + if (lctx->req_state == + (SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + lease->state = lctx->req_state; + } + + if (lctx->req_state && lease->state == + SMB2_LEASE_NONE_LE) + lease_none_upgrade(opinfo, lctx->req_state); + } + read_lock(&ci->m_lock); + } + read_unlock(&ci->m_lock); + + return m_opinfo; +} + +static void wait_for_break_ack(struct oplock_info *opinfo) +{ + int rc = 0; + + rc = wait_event_interruptible_timeout(opinfo->oplock_q, + opinfo->op_state == OPLOCK_STATE_NONE || + opinfo->op_state == OPLOCK_CLOSING, + OPLOCK_WAIT_TIME); + + /* is this a timeout ? */ + if (!rc) { + if (opinfo->is_lease) + opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->op_state = OPLOCK_STATE_NONE; + } +} + +static void wake_up_oplock_break(struct oplock_info *opinfo) +{ + clear_bit_unlock(0, &opinfo->pending_break); + /* memory barrier is needed for wake_up_bit() */ + smp_mb__after_atomic(); + wake_up_bit(&opinfo->pending_break, 0); +} + +static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) +{ + while (test_and_set_bit(0, &opinfo->pending_break)) { + wait_on_bit(&opinfo->pending_break, 0, TASK_UNINTERRUPTIBLE); + + /* Not immediately break to none. */ + opinfo->open_trunc = 0; + + if (opinfo->op_state == OPLOCK_CLOSING) + return -ENOENT; + else if (!opinfo->is_lease && opinfo->level <= req_op_level) + return 1; + } + + if (!opinfo->is_lease && opinfo->level <= req_op_level) { + wake_up_oplock_break(opinfo); + return 1; + } + return 0; +} + +static inline int allocate_oplock_break_buf(struct ksmbd_work *work) +{ + work->response_buf = ksmbd_alloc_response(MAX_CIFS_SMALL_BUFFER_SIZE); + if (!work->response_buf) + return -ENOMEM; + work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + return 0; +} + +/** + * smb2_oplock_break_noti() - send smb2 oplock break cmd from conn + * to client + * @work: smb work object + * + * There are two ways this function can be called. 1- while file open we break + * from exclusive/batch lock to levelII oplock and 2- while file write/truncate + * we break from levelII oplock no oplock. + * REQUEST_BUF(work) contains oplock_info. + */ +static void __smb2_oplock_break_noti(struct work_struct *wk) +{ + struct smb2_oplock_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + struct oplock_break_info *br_info = REQUEST_BUF(work); + struct smb2_hdr *rsp_hdr; + struct ksmbd_file *fp; + + fp = ksmbd_lookup_durable_fd(br_info->fid); + if (!fp) { + atomic_dec(&conn->r_count); + ksmbd_free_work_struct(work); + return; + } + + if (allocate_oplock_break_buf(work)) { + ksmbd_err("smb2_allocate_rsp_buf failed! "); + atomic_dec(&conn->r_count); + ksmbd_free_work_struct(work); + ksmbd_fd_put(work, fp); + return; + } + + rsp_hdr = RESPONSE_BUF(work); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->smb2_buf_length = cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn)); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); + rsp_hdr->Command = SMB2_OPLOCK_BREAK; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = cpu_to_le64(-1); + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + + rsp = RESPONSE_BUF(work); + + rsp->StructureSize = cpu_to_le16(24); + if (!br_info->open_trunc && + (br_info->level == SMB2_OPLOCK_LEVEL_BATCH || + br_info->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_II; + else + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; + rsp->Reserved = 0; + rsp->Reserved2 = 0; + rsp->PersistentFid = cpu_to_le64(fp->persistent_id); + rsp->VolatileFid = cpu_to_le64(fp->volatile_id); + + inc_rfc1001_len(rsp, 24); + + ksmbd_debug(OPLOCK, + "sending oplock break v_id %llu p_id = %llu lock level = %d\n", + rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); + + ksmbd_fd_put(work, fp); + ksmbd_conn_write(work); + ksmbd_free_work_struct(work); + atomic_dec(&conn->r_count); +} + +/** + * smb2_oplock_break() - send smb2 exclusive/batch to level2 oplock + * break command from server to client + * @opinfo: oplock info object + * @ack_required if requiring ack + * + * Return: 0 on success, otherwise error + */ +static int smb2_oplock_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct oplock_break_info *br_info; + int ret = 0; + struct ksmbd_work *work = ksmbd_alloc_work_struct(); + + if (!work) + return -ENOMEM; + + br_info = kmalloc(sizeof(struct oplock_break_info), GFP_KERNEL); + if (!br_info) { + ksmbd_free_work_struct(work); + return -ENOMEM; + } + + br_info->level = opinfo->level; + br_info->fid = opinfo->fid; + br_info->open_trunc = opinfo->open_trunc; + + work->request_buf = (char *)br_info; + work->conn = conn; + work->sess = opinfo->sess; + + atomic_inc(&conn->r_count); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + INIT_WORK(&work->work, __smb2_oplock_break_noti); + ksmbd_queue_work(work); + + wait_for_break_ack(opinfo); + } else { + __smb2_oplock_break_noti(&work->work); + if (opinfo->level == SMB2_OPLOCK_LEVEL_II) + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + } + return ret; +} + +/** + * __smb2_lease_break_noti() - send lease break command from server + * to client + * @work: smb work object + */ +static void __smb2_lease_break_noti(struct work_struct *wk) +{ + struct smb2_lease_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct lease_break_info *br_info = REQUEST_BUF(work); + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *rsp_hdr; + + if (allocate_oplock_break_buf(work)) { + ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! "); + ksmbd_free_work_struct(work); + atomic_dec(&conn->r_count); + return; + } + + rsp_hdr = RESPONSE_BUF(work); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->smb2_buf_length = cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn)); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); + rsp_hdr->Command = SMB2_OPLOCK_BREAK; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = cpu_to_le64(-1); + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = RESPONSE_BUF(work); + rsp->StructureSize = cpu_to_le16(44); + rsp->Reserved = 0; + rsp->Flags = 0; + + if (br_info->curr_state & (SMB2_LEASE_WRITE_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + rsp->Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; + + memcpy(rsp->LeaseKey, br_info->lease_key, SMB2_LEASE_KEY_SIZE); + rsp->CurrentLeaseState = br_info->curr_state; + rsp->NewLeaseState = br_info->new_state; + rsp->BreakReason = 0; + rsp->AccessMaskHint = 0; + rsp->ShareMaskHint = 0; + + inc_rfc1001_len(rsp, 44); + + ksmbd_conn_write(work); + ksmbd_free_work_struct(work); + atomic_dec(&conn->r_count); +} + +/** + * smb2_break_lease() - break lease when a new client request + * write lease + * @opinfo: conains lease state information + * @ack_required: if requring ack + * + * Return: 0 on success, otherwise error + */ +static int smb2_lease_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct list_head *tmp, *t; + struct ksmbd_work *work; + struct lease_break_info *br_info; + struct lease *lease = opinfo->o_lease; + + work = ksmbd_alloc_work_struct(); + if (!work) + return -ENOMEM; + + br_info = kmalloc(sizeof(struct lease_break_info), GFP_KERNEL); + if (!br_info) { + ksmbd_free_work_struct(work); + return -ENOMEM; + } + + br_info->curr_state = lease->state; + br_info->new_state = lease->new_state; + memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); + + work->request_buf = (char *)br_info; + work->conn = conn; + work->sess = opinfo->sess; + + atomic_inc(&conn->r_count); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + list_for_each_safe(tmp, t, &opinfo->interim_list) { + struct ksmbd_work *in_work; + + in_work = list_entry(tmp, struct ksmbd_work, + interim_entry); + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + list_del(&in_work->interim_entry); + } + INIT_WORK(&work->work, __smb2_lease_break_noti); + ksmbd_queue_work(work); + wait_for_break_ack(opinfo); + } else { + __smb2_lease_break_noti(&work->work); + if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + } + } + return 0; +} + +static void wait_lease_breaking(struct oplock_info *opinfo) +{ + if (!opinfo->is_lease) + return; + + wake_up_interruptible_all(&opinfo->oplock_brk); + if (atomic_read(&opinfo->breaking_cnt)) { + int ret = 0; + + ret = wait_event_interruptible_timeout( + opinfo->oplock_brk, + atomic_read(&opinfo->breaking_cnt) == 0, + HZ); + if (!ret) + atomic_set(&opinfo->breaking_cnt, 0); + } +} + +static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) +{ + int err = 0; + + /* Need to break exclusive/batch oplock, write lease or overwrite_if */ + ksmbd_debug(OPLOCK, + "request to send oplock(level : 0x%x) break notification\n", + brk_opinfo->level); + + if (brk_opinfo->is_lease) { + struct lease *lease = brk_opinfo->o_lease; + + atomic_inc(&brk_opinfo->breaking_cnt); + + err = oplock_break_pending(brk_opinfo, req_op_level); + if (err) + return err < 0 ? err : 0; + + if (brk_opinfo->open_trunc) { + /* + * Create overwrite break trigger the lease break to + * none. + */ + lease->new_state = SMB2_LEASE_NONE_LE; + } else { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) { + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE; + else + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + } else { + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + else + lease->new_state = SMB2_LEASE_NONE_LE; + } + } + + if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + brk_opinfo->op_state = OPLOCK_ACK_WAIT; + else + atomic_dec(&brk_opinfo->breaking_cnt); + } else { + err = oplock_break_pending(brk_opinfo, req_op_level); + if (err) + return err < 0 ? err : 0; + + if (brk_opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + brk_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) + brk_opinfo->op_state = OPLOCK_ACK_WAIT; + } + + if (brk_opinfo->is_lease) + err = smb2_lease_break_noti(brk_opinfo); + else + err = smb2_oplock_break_noti(brk_opinfo); + + ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); + if (brk_opinfo->op_state == OPLOCK_CLOSING) + err = -ENOENT; + wake_up_oplock_break(brk_opinfo); + + wait_lease_breaking(brk_opinfo); + + return err; +} + +void destroy_lease_table(struct ksmbd_conn *conn) +{ + struct lease_table *lb, *lbtmp; + struct oplock_info *opinfo; + + write_lock(&lease_list_lock); + if (list_empty(&lease_table_list)) { + write_unlock(&lease_list_lock); + return; + } + + list_for_each_entry_safe(lb, lbtmp, &lease_table_list, l_entry) { + if (conn && memcmp(lb->client_guid, conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + continue; +again: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, &lb->lease_list, + lease_entry) { + rcu_read_unlock(); + lease_del_list(opinfo); + goto again; + } + rcu_read_unlock(); + list_del(&lb->l_entry); + kfree(lb); + } + write_unlock(&lease_list_lock); +} + +int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx) +{ + struct oplock_info *opinfo; + int err = 0; + struct lease_table *lb; + + if (!lctx) + return err; + + read_lock(&lease_list_lock); + if (list_empty(&lease_table_list)) { + read_unlock(&lease_list_lock); + return 0; + } + + list_for_each_entry(lb, &lease_table_list, l_entry) { + if (!memcmp(lb->client_guid, sess->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + goto found; + } + read_unlock(&lease_list_lock); + + return 0; + +found: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, &lb->lease_list, + lease_entry) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + rcu_read_unlock(); + if (opinfo->o_fp->f_ci == ci) + goto op_next; + err = compare_guid_key(opinfo, + sess->conn->ClientGUID, + lctx->lease_key); + if (err) { + err = -EINVAL; + ksmbd_debug(OPLOCK, + "found same lease key is already used in other files\n"); + opinfo_put(opinfo); + goto out; + } +op_next: + opinfo_put(opinfo); + rcu_read_lock(); + } + rcu_read_unlock(); + +out: + read_unlock(&lease_list_lock); + return err; +} + +static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) +{ + struct lease *lease1 = op1->o_lease; + struct lease *lease2 = op2->o_lease; + + op2->level = op1->level; + lease2->state = lease1->state; + memcpy(lease2->lease_key, lease1->lease_key, + SMB2_LEASE_KEY_SIZE); + lease2->duration = lease1->duration; + lease2->flags = lease1->flags; +} + +static int add_lease_global_list(struct oplock_info *opinfo) +{ + struct lease_table *lb; + + read_lock(&lease_list_lock); + list_for_each_entry(lb, &lease_table_list, l_entry) { + if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) { + opinfo->o_lease->l_lb = lb; + lease_add_list(opinfo); + read_unlock(&lease_list_lock); + return 0; + } + } + read_unlock(&lease_list_lock); + + lb = kmalloc(sizeof(struct lease_table), GFP_KERNEL); + if (!lb) + return -ENOMEM; + + memcpy(lb->client_guid, opinfo->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE); + INIT_LIST_HEAD(&lb->lease_list); + spin_lock_init(&lb->lb_lock); + opinfo->o_lease->l_lb = lb; + lease_add_list(opinfo); + lb_add(lb); + return 0; +} + +static void set_oplock_level(struct oplock_info *opinfo, int level, + struct lease_ctx_info *lctx) +{ + switch (level) { + case SMB2_OPLOCK_LEVEL_BATCH: + case SMB2_OPLOCK_LEVEL_EXCLUSIVE: + grant_write_oplock(opinfo, + level, lctx); + break; + case SMB2_OPLOCK_LEVEL_II: + grant_read_oplock(opinfo, lctx); + break; + default: + grant_none_oplock(opinfo, lctx); + break; + } +} + +/** + * smb_grant_oplock() - handle oplock/lease request on file open + * @fp: ksmbd file pointer + * @oplock: granted oplock type + * @id: fid of open file + * @Tid: Tree id of connection + * @lctx: lease context information on file open + * @attr_only: attribute only file open type + * + * Return: 0 on success, otherwise error + */ +int smb_grant_oplock(struct ksmbd_work *work, + int req_op_level, + uint64_t pid, + struct ksmbd_file *fp, + __u16 tid, + struct lease_ctx_info *lctx, + int share_ret) +{ + struct ksmbd_session *sess = work->sess; + int err = 0; + struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; + struct ksmbd_inode *ci = fp->f_ci; + bool prev_op_has_lease; + __le32 prev_op_state = 0; + + /* not support directory lease */ + if (S_ISDIR(file_inode(fp->filp)->i_mode)) { + if (lctx) + lctx->dlease = 1; + return 0; + } + + opinfo = alloc_opinfo(work, pid, tid); + if (!opinfo) + return -ENOMEM; + + if (lctx) { + err = alloc_lease(opinfo, lctx); + if (err) + goto err_out; + opinfo->is_lease = 1; + } + + /* ci does not have any oplock */ + if (!opinfo_count(fp)) + goto set_lev; + + /* grant none-oplock if second open is trunc */ + if (ATTR_FP(fp)) { + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + goto set_lev; + } + + if (lctx) { + struct oplock_info *m_opinfo; + + /* is lease already granted ? */ + m_opinfo = same_client_has_lease(ci, sess->conn->ClientGUID, + lctx); + if (m_opinfo) { + copy_lease(m_opinfo, opinfo); + if (atomic_read(&m_opinfo->breaking_cnt)) + opinfo->o_lease->flags = + SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; + goto out; + } + } + prev_opinfo = opinfo_get_list(ci); + if (!prev_opinfo || + (prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) + goto set_lev; + prev_op_has_lease = prev_opinfo->is_lease; + if (prev_op_has_lease) + prev_op_state = prev_opinfo->o_lease->state; + + if (share_ret < 0 && + (prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + err = share_ret; + opinfo_put(prev_opinfo); + goto err_out; + } + + if ((prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) && + (prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + opinfo_put(prev_opinfo); + goto op_break_not_needed; + } + + list_add(&work->interim_entry, &prev_opinfo->interim_list); + err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II); + opinfo_put(prev_opinfo); + if (err == -ENOENT) + goto set_lev; + /* Check all oplock was freed by close */ + else if (err < 0) + goto err_out; + +op_break_not_needed: + if (share_ret < 0) { + err = share_ret; + goto err_out; + } + + if (req_op_level != SMB2_OPLOCK_LEVEL_NONE) + req_op_level = SMB2_OPLOCK_LEVEL_II; + + /* grant fixed oplock on stacked locking between lease and oplock */ + if (prev_op_has_lease && !lctx) + if (prev_op_state & SMB2_LEASE_HANDLE_CACHING_LE) + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + + if (!prev_op_has_lease && lctx) { + req_op_level = SMB2_OPLOCK_LEVEL_II; + lctx->req_state = SMB2_LEASE_READ_CACHING_LE; + } + +set_lev: + set_oplock_level(opinfo, req_op_level, lctx); + +out: + rcu_assign_pointer(fp->f_opinfo, opinfo); + opinfo->o_fp = fp; + + opinfo_count_inc(fp); + opinfo_add(opinfo); + if (opinfo->is_lease) { + err = add_lease_global_list(opinfo); + if (err) + goto err_out; + } + + return 0; +err_out: + free_opinfo(opinfo); + return err; +} + +/** + * smb_break_write_oplock() - break batch/exclusive oplock to level2 + * @work: smb work + * @fp: ksmbd file pointer + * @openfile: open file object + */ +static void smb_break_all_write_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc) +{ + struct oplock_info *brk_opinfo; + + brk_opinfo = opinfo_get_list(fp->f_ci); + if (!brk_opinfo) + return; + if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && + brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + opinfo_put(brk_opinfo); + return; + } + + brk_opinfo->open_trunc = is_trunc; + list_add(&work->interim_entry, &brk_opinfo->interim_list); + oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II); + opinfo_put(brk_opinfo); +} + +/** + * smb_break_all_levII_oplock() - send level2 oplock or read lease break command + * from server to client + * @conn: connection instance + * @fp: ksmbd file pointer + * @is_trunc: truncate on open + */ +void smb_break_all_levII_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc) +{ + struct oplock_info *op, *brk_op; + struct ksmbd_inode *ci; + struct ksmbd_conn *conn = work->sess->conn; + + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS)) { + return; + } + + ci = fp->f_ci; + op = opinfo_get(fp); + + rcu_read_lock(); + list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { + if (!atomic_inc_not_zero(&brk_op->refcount)) + continue; + rcu_read_unlock(); + if (brk_op->is_lease && (brk_op->o_lease->state & + (~(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)))) { + ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n", + brk_op->o_lease->state); + goto next; + } else if (brk_op->level != + SMB2_OPLOCK_LEVEL_II) { + ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", + brk_op->level); + goto next; + } + + /* Skip oplock being break to none */ + if (brk_op->is_lease && (brk_op->o_lease->new_state == + SMB2_LEASE_NONE_LE) && + atomic_read(&brk_op->breaking_cnt)) + goto next; + + if (op && op->is_lease && + brk_op->is_lease && + !memcmp(conn->ClientGUID, brk_op->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE) && + !memcmp(op->o_lease->lease_key, + brk_op->o_lease->lease_key, + SMB2_LEASE_KEY_SIZE)) + goto next; + brk_op->open_trunc = is_trunc; + oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE); +next: + opinfo_put(brk_op); + rcu_read_lock(); + } + rcu_read_unlock(); + + if (op) + opinfo_put(op); +} + +/** + * smb_break_all_oplock() - break both batch/exclusive and level2 oplock + * @work: smb work + * @fp: ksmbd file pointer + */ +void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS)) + return; + + smb_break_all_write_oplock(work, fp, 1); + smb_break_all_levII_oplock(work, fp, 1); +} + +/** + * smb2_map_lease_to_oplock() - map lease state to corresponding oplock type + * @lease_state: lease type + * + * Return: 0 if no mapping, otherwise corresponding oplock type + */ +__u8 smb2_map_lease_to_oplock(__le32 lease_state) +{ + if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_WRITE_CACHING_LE)) + return SMB2_OPLOCK_LEVEL_BATCH; + else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE && + lease_state & SMB2_LEASE_WRITE_CACHING_LE) { + if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) + return SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } else if (lease_state & SMB2_LEASE_READ_CACHING_LE) + return SMB2_OPLOCK_LEVEL_II; + return 0; +} + +/** + * create_lease_buf() - create lease context for open cmd response + * @rbuf: buffer to create lease context response + * @lreq: buffer to stored parsed lease state information + */ +void create_lease_buf(u8 *rbuf, struct lease *lease) +{ + struct create_lease *buf = (struct create_lease *)rbuf; + char *LeaseKey = (char *)&lease->lease_key; + + memset(buf, 0, sizeof(struct create_lease)); + buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); + buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; +} + +/** + * parse_lease_state() - parse lease context containted in file open request + * @open_req: buffer containing smb2 file open(create) request + * @lreq: buffer to stored parsed lease state information + * + * Return: oplock state, -ENOENT if create lease context not found + */ +struct lease_ctx_info *parse_lease_state(void *open_req) +{ + char *data_offset; + struct create_context *cc; + unsigned int next = 0; + char *name; + bool found = false; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; + struct lease_ctx_info *lreq = kzalloc(sizeof(struct lease_ctx_info), + GFP_KERNEL); + if (!lreq) + return NULL; + + data_offset = (char *)req + 4 + le32_to_cpu(req->CreateContextsOffset); + cc = (struct create_context *)data_offset; + do { + cc = (struct create_context *)((char *)cc + next); + name = le16_to_cpu(cc->NameOffset) + (char *)cc; + if (le16_to_cpu(cc->NameLength) != 4 || + strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) { + next = le32_to_cpu(cc->Next); + continue; + } + found = true; + break; + } while (next != 0); + + if (found) { + struct create_lease *lc = (struct create_lease *)cc; + *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; + *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + return lreq; + } + + kfree(lreq); + return NULL; +} + +/** + * smb2_find_context_vals() - find a particular context info in open request + * @open_req: buffer containing smb2 file open(create) request + * @str: context name to search for + * + * Return: pointer to requested context, NULL if @str context not found + */ +struct create_context *smb2_find_context_vals(void *open_req, const char *tag) +{ + char *data_offset; + struct create_context *cc; + unsigned int next = 0; + char *name; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; + + data_offset = (char *)req + 4 + le32_to_cpu(req->CreateContextsOffset); + cc = (struct create_context *)data_offset; + do { + int val; + + cc = (struct create_context *)((char *)cc + next); + name = le16_to_cpu(cc->NameOffset) + (char *)cc; + val = le16_to_cpu(cc->NameLength); + if (val < 4) + return ERR_PTR(-EINVAL); + + if (memcmp(name, tag, val) == 0) + return cc; + next = le32_to_cpu(cc->Next); + } while (next != 0); + + return ERR_PTR(-ENOENT); +} + +/** + * create_durable_buf() - create durable handle context + * @cc: buffer to create durable context response + */ +void create_durable_rsp_buf(char *cc) +{ + struct create_durable_rsp *buf; + + buf = (struct create_durable_rsp *)cc; + memset(buf, 0, sizeof(struct create_durable_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Data)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE is "DHnQ" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = 'n'; + buf->Name[3] = 'Q'; +} + +/** + * create_durable_buf() - create durable handle v2 context + * @cc: buffer to create durable context response + */ +void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) +{ + struct create_durable_v2_rsp *buf; + + buf = (struct create_durable_v2_rsp *)cc; + memset(buf, 0, sizeof(struct create_durable_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Data)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2 is "DH2Q" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = '2'; + buf->Name[3] = 'Q'; + + buf->Timeout = cpu_to_le32(fp->durable_timeout); + if (fp->is_persistent) + buf->Flags = SMB2_FLAGS_REPLAY_OPERATIONS; +} + +/** + * create_mxac_buf() - create query maximal access context + * @cc: buffer to create maximal access context response + */ +void create_mxac_rsp_buf(char *cc, int maximal_access) +{ + struct create_mxac_rsp *buf; + + buf = (struct create_mxac_rsp *)cc; + memset(buf, 0, sizeof(struct create_mxac_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, QueryStatus)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE is "MxAc" */ + buf->Name[0] = 'M'; + buf->Name[1] = 'x'; + buf->Name[2] = 'A'; + buf->Name[3] = 'c'; + + buf->QueryStatus = STATUS_SUCCESS; + buf->MaximalAccess = cpu_to_le32(maximal_access); +} + +/** + * create_mxac_buf() - create query maximal access context + * @cc: buffer to create query disk on id context response + */ +void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id) +{ + struct create_disk_id_rsp *buf; + + buf = (struct create_disk_id_rsp *)cc; + memset(buf, 0, sizeof(struct create_disk_id_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_disk_id_rsp, DiskFileId)); + buf->ccontext.DataLength = cpu_to_le32(32); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_ON_DISK_ID_RESPONSE is "QFid" */ + buf->Name[0] = 'Q'; + buf->Name[1] = 'F'; + buf->Name[2] = 'i'; + buf->Name[3] = 'd'; + + buf->DiskFileId = cpu_to_le64(file_id); + buf->VolumeId = cpu_to_le64(vol_id); +} + +/** + * create_posix_buf() - create posix extension context + * @cc: buffer to create posix on posix response + */ +void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) +{ + struct create_posix_rsp *buf; + struct inode *inode = FP_INODE(fp); + + buf = (struct create_posix_rsp *)cc; + memset(buf, 0, sizeof(struct create_posix_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_posix_rsp, nlink)); + buf->ccontext.DataLength = cpu_to_le32(52); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_posix_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + buf->Name[0] = 0x93; + buf->Name[1] = 0xAD; + buf->Name[2] = 0x25; + buf->Name[3] = 0x50; + buf->Name[4] = 0x9C; + buf->Name[5] = 0xB4; + buf->Name[6] = 0x11; + buf->Name[7] = 0xE7; + buf->Name[8] = 0xB4; + buf->Name[9] = 0x23; + buf->Name[10] = 0x83; + buf->Name[11] = 0xDE; + buf->Name[12] = 0x96; + buf->Name[13] = 0x8B; + buf->Name[14] = 0xCD; + buf->Name[15] = 0x7C; + + buf->nlink = cpu_to_le32(inode->i_nlink); + buf->reparse_tag = cpu_to_le32(fp->volatile_id); + buf->mode = cpu_to_le32(inode->i_mode); + id_to_sid(from_kuid(&init_user_ns, inode->i_uid), + SIDNFS_USER, (struct smb_sid *)&buf->SidBuffer[0]); + id_to_sid(from_kgid(&init_user_ns, inode->i_gid), + SIDNFS_GROUP, (struct smb_sid *)&buf->SidBuffer[20]); +} + +/* + * Find lease object(opinfo) for given lease key/fid from lease + * break/file close path. + */ +/** + * lookup_lease_in_table() - find a matching lease info object + * @conn: connection instance + * @lease_key: lease key to be searched for + * + * Return: opinfo if found matching opinfo, otherwise NULL + */ +struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + char *lease_key) +{ + struct oplock_info *opinfo = NULL, *ret_op = NULL; + struct lease_table *lt; + int ret; + + read_lock(&lease_list_lock); + list_for_each_entry(lt, &lease_table_list, l_entry) { + if (!memcmp(lt->client_guid, conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + goto found; + } + + read_unlock(&lease_list_lock); + return NULL; + +found: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, <->lease_list, lease_entry) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + rcu_read_unlock(); + if (!opinfo->op_state || + opinfo->op_state == OPLOCK_CLOSING) + goto op_next; + if (!(opinfo->o_lease->state & + (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE))) + goto op_next; + ret = compare_guid_key(opinfo, conn->ClientGUID, + lease_key); + if (ret) { + ksmbd_debug(OPLOCK, "found opinfo\n"); + ret_op = opinfo; + goto out; + } +op_next: + opinfo_put(opinfo); + rcu_read_lock(); + } + rcu_read_unlock(); + +out: + read_unlock(&lease_list_lock); + return ret_op; +} + +int smb2_check_durable_oplock(struct ksmbd_file *fp, + struct lease_ctx_info *lctx, char *name) +{ + struct oplock_info *opinfo = opinfo_get(fp); + int ret = 0; + + if (opinfo && opinfo->is_lease) { + if (!lctx) { + ksmbd_err("open does not include lease\n"); + ret = -EBADF; + goto out; + } + if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key, + SMB2_LEASE_KEY_SIZE)) { + ksmbd_err("invalid lease key\n"); + ret = -EBADF; + goto out; + } + if (name && strcmp(fp->filename, name)) { + ksmbd_err("invalid name reconnect %s\n", name); + ret = -EINVAL; + goto out; + } + } +out: + if (opinfo) + opinfo_put(opinfo); + return ret; +} diff --git a/fs/cifsd/oplock.h b/fs/cifsd/oplock.h new file mode 100644 index 000000000000..b0e2e795f29a --- /dev/null +++ b/fs/cifsd/oplock.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_OPLOCK_H +#define __KSMBD_OPLOCK_H + +#include "smb_common.h" + +#define OPLOCK_WAIT_TIME (35*HZ) + +/* SMB Oplock levels */ +#define OPLOCK_NONE 0 +#define OPLOCK_EXCLUSIVE 1 +#define OPLOCK_BATCH 2 +#define OPLOCK_READ 3 /* level 2 oplock */ + +/* SMB2 Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF + +/* Oplock states */ +#define OPLOCK_STATE_NONE 0x00 +#define OPLOCK_ACK_WAIT 0x01 +#define OPLOCK_CLOSING 0x02 + +#define OPLOCK_WRITE_TO_READ 0x01 +#define OPLOCK_READ_HANDLE_TO_READ 0x02 +#define OPLOCK_WRITE_TO_NONE 0x04 +#define OPLOCK_READ_TO_NONE 0x08 + +#define SMB2_LEASE_KEY_SIZE 16 + +struct lease_ctx_info { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 req_state; + __le32 flags; + __le64 duration; + int dlease; +}; + +struct lease_table { + char client_guid[SMB2_CLIENT_GUID_SIZE]; + struct list_head lease_list; + struct list_head l_entry; + spinlock_t lb_lock; +}; + +struct lease { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 state; + __le32 new_state; + __le32 flags; + __le64 duration; + struct lease_table *l_lb; +}; + +struct oplock_info { + struct ksmbd_conn *conn; + struct ksmbd_session *sess; + struct ksmbd_work *work; + struct ksmbd_file *o_fp; + int level; + int op_state; + unsigned long pending_break; + uint64_t fid; + atomic_t breaking_cnt; + atomic_t refcount; + __u16 Tid; + bool is_lease; + bool open_trunc; /* truncate on open */ + struct lease *o_lease; + struct list_head interim_list; + struct list_head op_entry; + struct list_head lease_entry; + wait_queue_head_t oplock_q; /* Other server threads */ + wait_queue_head_t oplock_brk; /* oplock breaking wait */ + struct rcu_head rcu_head; +}; + +struct lease_break_info { + __le32 curr_state; + __le32 new_state; + char lease_key[SMB2_LEASE_KEY_SIZE]; +}; + +struct oplock_break_info { + int level; + int open_trunc; + int fid; +}; + +extern int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, + uint64_t pid, struct ksmbd_file *fp, __u16 tid, + struct lease_ctx_info *lctx, int share_ret); +extern void smb_break_all_levII_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc); + +int opinfo_write_to_read(struct oplock_info *opinfo); +int opinfo_read_handle_to_read(struct oplock_info *opinfo); +int opinfo_write_to_none(struct oplock_info *opinfo); +int opinfo_read_to_none(struct oplock_info *opinfo); +void close_id_del_oplock(struct ksmbd_file *fp); +void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp); +struct oplock_info *opinfo_get(struct ksmbd_file *fp); +void opinfo_put(struct oplock_info *opinfo); + +/* Lease related functions */ +void create_lease_buf(u8 *rbuf, struct lease *lease); +struct lease_ctx_info *parse_lease_state(void *open_req); +__u8 smb2_map_lease_to_oplock(__le32 lease_state); +int lease_read_to_write(struct oplock_info *opinfo); + +/* Durable related functions */ +void create_durable_rsp_buf(char *cc); +void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp); +void create_mxac_rsp_buf(char *cc, int maximal_access); +void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); +void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); +struct create_context *smb2_find_context_vals(void *open_req, const char *str); +int ksmbd_durable_verify_and_del_oplock(struct ksmbd_session *curr_sess, + struct ksmbd_session *prev_sess, + int fid, struct file **filp, + uint64_t sess_id); +struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + char *lease_key); +int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx); +void destroy_lease_table(struct ksmbd_conn *conn); +int smb2_check_durable_oplock(struct ksmbd_file *fp, + struct lease_ctx_info *lctx, char *name); + +#endif /* __KSMBD_OPLOCK_H */ diff --git a/fs/cifsd/smb2misc.c b/fs/cifsd/smb2misc.c new file mode 100644 index 000000000000..485f431c776c --- /dev/null +++ b/fs/cifsd/smb2misc.c @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "nterr.h" +#include "smb2pdu.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "mgmt/user_session.h" +#include "connection.h" + +static int check_smb2_hdr(struct smb2_hdr *hdr) +{ + /* + * Make sure that this really is an SMB, that it is a response. + */ + if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return 1; + return 0; +} + +/* + * The following table defines the expected "StructureSize" of SMB2 requests + * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. + * + * Note that commands are defined in smb2pdu.h in le16 but the array below is + * indexed by command in host byte order + */ +static const __le16 smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ cpu_to_le16(36), + /* SMB2_SESSION_SETUP */ cpu_to_le16(25), + /* SMB2_LOGOFF */ cpu_to_le16(4), + /* SMB2_TREE_CONNECT */ cpu_to_le16(9), + /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), + /* SMB2_CREATE */ cpu_to_le16(57), + /* SMB2_CLOSE */ cpu_to_le16(24), + /* SMB2_FLUSH */ cpu_to_le16(24), + /* SMB2_READ */ cpu_to_le16(49), + /* SMB2_WRITE */ cpu_to_le16(49), + /* SMB2_LOCK */ cpu_to_le16(48), + /* SMB2_IOCTL */ cpu_to_le16(57), + /* SMB2_CANCEL */ cpu_to_le16(4), + /* SMB2_ECHO */ cpu_to_le16(4), + /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(33), + /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(32), + /* SMB2_QUERY_INFO */ cpu_to_le16(41), + /* SMB2_SET_INFO */ cpu_to_le16(33), + /* use 44 for lease break */ + /* SMB2_OPLOCK_BREAK */ cpu_to_le16(36) +}; + +/* + * The size of the variable area depends on the offset and length fields + * located in different fields for various SMB2 requests. SMB2 requests + * with no variable length info, show an offset of zero for the offset field. + */ +static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ true, + /* SMB2_SESSION_SETUP */ true, + /* SMB2_LOGOFF */ false, + /* SMB2_TREE_CONNECT */ true, + /* SMB2_TREE_DISCONNECT */ false, + /* SMB2_CREATE */ true, + /* SMB2_CLOSE */ false, + /* SMB2_FLUSH */ false, + /* SMB2_READ */ true, + /* SMB2_WRITE */ true, + /* SMB2_LOCK */ true, + /* SMB2_IOCTL */ true, + /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ + /* SMB2_ECHO */ false, + /* SMB2_QUERY_DIRECTORY */ true, + /* SMB2_CHANGE_NOTIFY */ false, + /* SMB2_QUERY_INFO */ true, + /* SMB2_SET_INFO */ true, + /* SMB2_OPLOCK_BREAK */ false +}; + +/* + * Returns the pointer to the beginning of the data area. Length of the data + * area and the offset to it (from the beginning of the smb are also returned. + */ +static char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) +{ + *off = 0; + *len = 0; + + /* error reqeusts do not have data area */ + if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED && + (((struct smb2_err_rsp *)hdr)->StructureSize) == + SMB2_ERROR_STRUCTURE_SIZE2_LE) + return NULL; + + /* + * Following commands have data areas so we have to get the location + * of the data buffer offset and data buffer length for the particular + * command. + */ + switch (hdr->Command) { + case SMB2_SESSION_SETUP: + *off = le16_to_cpu( + ((struct smb2_sess_setup_req *)hdr)->SecurityBufferOffset); + *len = le16_to_cpu( + ((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); + break; + case SMB2_TREE_CONNECT: + *off = le16_to_cpu( + ((struct smb2_tree_connect_req *)hdr)->PathOffset); + *len = le16_to_cpu( + ((struct smb2_tree_connect_req *)hdr)->PathLength); + break; + case SMB2_CREATE: + { + if (((struct smb2_create_req *)hdr)->CreateContextsLength) { + *off = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsOffset); + *len = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsLength); + break; + } + + *off = le16_to_cpu( + ((struct smb2_create_req *)hdr)->NameOffset); + *len = le16_to_cpu( + ((struct smb2_create_req *)hdr)->NameLength); + break; + } + case SMB2_QUERY_INFO: + *off = le16_to_cpu( + ((struct smb2_query_info_req *)hdr)->InputBufferOffset); + *len = le32_to_cpu( + ((struct smb2_query_info_req *)hdr)->InputBufferLength); + break; + case SMB2_SET_INFO: + *off = le16_to_cpu( + ((struct smb2_set_info_req *)hdr)->BufferOffset); + *len = le32_to_cpu( + ((struct smb2_set_info_req *)hdr)->BufferLength); + break; + case SMB2_READ: + *off = le16_to_cpu( + ((struct smb2_read_req *)hdr)->ReadChannelInfoOffset); + *len = le16_to_cpu( + ((struct smb2_read_req *)hdr)->ReadChannelInfoLength); + break; + case SMB2_WRITE: + if (((struct smb2_write_req *)hdr)->DataOffset) { + *off = le16_to_cpu( + ((struct smb2_write_req *)hdr)->DataOffset); + *len = le32_to_cpu( + ((struct smb2_write_req *)hdr)->Length); + break; + } + + *off = le16_to_cpu( + ((struct smb2_write_req *)hdr)->WriteChannelInfoOffset); + *len = le16_to_cpu( + ((struct smb2_write_req *)hdr)->WriteChannelInfoLength); + break; + case SMB2_QUERY_DIRECTORY: + *off = le16_to_cpu( + ((struct smb2_query_directory_req *)hdr)->FileNameOffset); + *len = le16_to_cpu( + ((struct smb2_query_directory_req *)hdr)->FileNameLength); + break; + case SMB2_LOCK: + { + int lock_count; + + /* + * smb2_lock request size is 48 included single + * smb2_lock_element structure size. + */ + lock_count = le16_to_cpu( + ((struct smb2_lock_req *)hdr)->LockCount) - 1; + if (lock_count > 0) { + *off = __SMB2_HEADER_STRUCTURE_SIZE + 48; + *len = sizeof(struct smb2_lock_element) * lock_count; + } + break; + } + case SMB2_IOCTL: + *off = le32_to_cpu( + ((struct smb2_ioctl_req *)hdr)->InputOffset); + *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); + + break; + default: + ksmbd_debug(SMB, "no length check for command\n"); + break; + } + + /* + * Invalid length or offset probably means data area is invalid, but + * we have little choice but to ignore the data area in this case. + */ + if (*off > 4096) { + ksmbd_debug(SMB, "offset %d too large, data area ignored\n", + *off); + *len = 0; + *off = 0; + } else if (*off < 0) { + ksmbd_debug(SMB, + "negative offset %d to data invalid ignore data area\n", + *off); + *off = 0; + *len = 0; + } else if (*len < 0) { + ksmbd_debug(SMB, + "negative data length %d invalid, data area ignored\n", + *len); + *len = 0; + } else if (*len > 128 * 1024) { + ksmbd_debug(SMB, "data area larger than 128K: %d\n", *len); + *len = 0; + } + + /* return pointer to beginning of data area, ie offset from SMB start */ + if ((*off != 0) && (*len != 0)) + return (char *)hdr + *off; + else + return NULL; +} + +/* + * Calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message. + */ +static unsigned int smb2_calc_size(void *buf) +{ + struct smb2_pdu *pdu = (struct smb2_pdu *)buf; + struct smb2_hdr *hdr = &pdu->hdr; + int offset; /* the offset from the beginning of SMB to data area */ + int data_length; /* the length of the variable length data area */ + /* Structure Size has already been checked to make sure it is 64 */ + int len = le16_to_cpu(hdr->StructureSize); + + /* + * StructureSize2, ie length of fixed parameter area has already + * been checked to make sure it is the correct length. + */ + len += le16_to_cpu(pdu->StructureSize2); + + if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false) + goto calc_size_exit; + + smb2_get_data_area_len(&offset, &data_length, hdr); + ksmbd_debug(SMB, "SMB2 data length %d offset %d\n", data_length, + offset); + + if (data_length > 0) { + /* + * Check to make sure that data area begins after fixed area, + * Note that last byte of the fixed area is part of data area + * for some commands, typically those with odd StructureSize, + * so we must add one to the calculation. + */ + if (offset + 1 < len) + ksmbd_debug(SMB, + "data area offset %d overlaps SMB2 header %d\n", + offset + 1, len); + else + len = offset + data_length; + } +calc_size_exit: + ksmbd_debug(SMB, "SMB2 len %d\n", len); + return len; +} + +static inline int smb2_query_info_req_len(struct smb2_query_info_req *h) +{ + return le32_to_cpu(h->InputBufferLength) + + le32_to_cpu(h->OutputBufferLength); +} + +static inline int smb2_set_info_req_len(struct smb2_set_info_req *h) +{ + return le32_to_cpu(h->BufferLength); +} + +static inline int smb2_read_req_len(struct smb2_read_req *h) +{ + return le32_to_cpu(h->Length); +} + +static inline int smb2_write_req_len(struct smb2_write_req *h) +{ + return le32_to_cpu(h->Length); +} + +static inline int smb2_query_dir_req_len(struct smb2_query_directory_req *h) +{ + return le32_to_cpu(h->OutputBufferLength); +} + +static inline int smb2_ioctl_req_len(struct smb2_ioctl_req *h) +{ + return le32_to_cpu(h->InputCount) + + le32_to_cpu(h->OutputCount); +} + +static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h) +{ + return le32_to_cpu(h->MaxInputResponse) + + le32_to_cpu(h->MaxOutputResponse); +} + +static int smb2_validate_credit_charge(struct smb2_hdr *hdr) +{ + int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len; + int credit_charge = le16_to_cpu(hdr->CreditCharge); + void *__hdr = hdr; + + switch (hdr->Command) { + case SMB2_QUERY_INFO: + req_len = smb2_query_info_req_len(__hdr); + break; + case SMB2_SET_INFO: + req_len = smb2_set_info_req_len(__hdr); + break; + case SMB2_READ: + req_len = smb2_read_req_len(__hdr); + break; + case SMB2_WRITE: + req_len = smb2_write_req_len(__hdr); + break; + case SMB2_QUERY_DIRECTORY: + req_len = smb2_query_dir_req_len(__hdr); + break; + case SMB2_IOCTL: + req_len = smb2_ioctl_req_len(__hdr); + expect_resp_len = smb2_ioctl_resp_len(__hdr); + break; + default: + return 0; + } + + max_len = max(req_len, expect_resp_len); + calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); + if (!credit_charge && max_len > SMB2_MAX_BUFFER_SIZE) { + ksmbd_err("credit charge is zero and payload size(%d) is bigger than 64K\n", + max_len); + return 1; + } else if (credit_charge < calc_credit_num) { + ksmbd_err("credit charge : %d, calc_credit_num : %d\n", + credit_charge, calc_credit_num); + return 1; + } + + return 0; +} + +int ksmbd_smb2_check_message(struct ksmbd_work *work) +{ + struct smb2_pdu *pdu = REQUEST_BUF(work); + struct smb2_hdr *hdr = &pdu->hdr; + int command; + __u32 clc_len; /* calculated length */ + __u32 len = get_rfc1002_len(pdu); + + if (work->next_smb2_rcv_hdr_off) { + pdu = REQUEST_BUF_NEXT(work); + hdr = &pdu->hdr; + } + + if (le32_to_cpu(hdr->NextCommand) > 0) + len = le32_to_cpu(hdr->NextCommand); + else if (work->next_smb2_rcv_hdr_off) { + len -= work->next_smb2_rcv_hdr_off; + len = round_up(len, 8); + } + + if (check_smb2_hdr(hdr)) + return 1; + + if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { + ksmbd_debug(SMB, "Illegal structure size %u\n", + le16_to_cpu(hdr->StructureSize)); + return 1; + } + + command = le16_to_cpu(hdr->Command); + if (command >= NUMBER_OF_SMB2_COMMANDS) { + ksmbd_debug(SMB, "Illegal SMB2 command %d\n", command); + return 1; + } + + if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { + if (command != SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0 || + pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { + /* error packets have 9 byte structure size */ + ksmbd_debug(SMB, + "Illegal request size %u for command %d\n", + le16_to_cpu(pdu->StructureSize2), command); + return 1; + } else if (command == SMB2_OPLOCK_BREAK_HE + && (hdr->Status == 0) + && (le16_to_cpu(pdu->StructureSize2) != + OP_BREAK_STRUCT_SIZE_20) + && (le16_to_cpu(pdu->StructureSize2) != + OP_BREAK_STRUCT_SIZE_21)) { + /* special case for SMB2.1 lease break message */ + ksmbd_debug(SMB, + "Illegal request size %d for oplock break\n", + le16_to_cpu(pdu->StructureSize2)); + return 1; + } + } + + clc_len = smb2_calc_size(hdr); + if (len != clc_len) { + /* server can return one byte more due to implied bcc[0] */ + if (clc_len == len + 1) + return 0; + + /* + * Some windows servers (win2016) will pad also the final + * PDU in a compound to 8 bytes. + */ + if (ALIGN(clc_len, 8) == len) + return 0; + + /* + * windows client also pad up to 8 bytes when compounding. + * If pad is longer than eight bytes, log the server behavior + * (once), since may indicate a problem but allow it and + * continue since the frame is parseable. + */ + if (clc_len < len) { + ksmbd_debug(SMB, + "cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n", + len, clc_len, command, + le64_to_cpu(hdr->MessageId)); + return 0; + } + + if (command == SMB2_LOCK_HE && len == 88) + return 0; + + ksmbd_debug(SMB, + "cli req too short, len %d not %d. cmd:%d mid:%llu\n", + len, clc_len, command, + le64_to_cpu(hdr->MessageId)); + + return 1; + } + + return work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU ? + smb2_validate_credit_charge(hdr) : 0; +} + +int smb2_negotiate_request(struct ksmbd_work *work) +{ + return ksmbd_smb_negotiate_common(work, SMB2_NEGOTIATE_HE); +} diff --git a/fs/cifsd/smb2ops.c b/fs/cifsd/smb2ops.c new file mode 100644 index 000000000000..a47219ea3b80 --- /dev/null +++ b/fs/cifsd/smb2ops.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include "glob.h" +#include "smb2pdu.h" + +#include "auth.h" +#include "connection.h" +#include "smb_common.h" +#include "server.h" +#include "ksmbd_server.h" + +static struct smb_version_values smb21_server_values = { + .version_string = SMB21_VERSION_STRING, + .protocol_id = SMB21_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB21_DEFAULT_IOSIZE, + .max_write_size = SMB21_DEFAULT_IOSIZE, + .max_trans_size = SMB21_DEFAULT_IOSIZE, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb30_server_values = { + .version_string = SMB30_VERSION_STRING, + .protocol_id = SMB30_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb302_server_values = { + .version_string = SMB302_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb311_server_values = { + .version_string = SMB311_VERSION_STRING, + .protocol_id = SMB311_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_ops smb2_0_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb2_check_sign_req, + .set_sign_rsp = smb2_set_sign_rsp +}; + +static struct smb_version_ops smb3_0_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb3_check_sign_req, + .set_sign_rsp = smb3_set_sign_rsp, + .generate_signingkey = ksmbd_gen_smb30_signingkey, + .generate_encryptionkey = ksmbd_gen_smb30_encryptionkey, + .is_transform_hdr = smb3_is_transform_hdr, + .decrypt_req = smb3_decrypt_req, + .encrypt_resp = smb3_encrypt_resp +}; + +static struct smb_version_ops smb3_11_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb3_check_sign_req, + .set_sign_rsp = smb3_set_sign_rsp, + .generate_signingkey = ksmbd_gen_smb311_signingkey, + .generate_encryptionkey = ksmbd_gen_smb311_encryptionkey, + .is_transform_hdr = smb3_is_transform_hdr, + .decrypt_req = smb3_decrypt_req, + .encrypt_resp = smb3_encrypt_resp +}; + +static struct smb_version_cmds smb2_0_server_cmds[NUMBER_OF_SMB2_COMMANDS] = { + [SMB2_NEGOTIATE_HE] = { .proc = smb2_negotiate_request, }, + [SMB2_SESSION_SETUP_HE] = { .proc = smb2_sess_setup, }, + [SMB2_TREE_CONNECT_HE] = { .proc = smb2_tree_connect,}, + [SMB2_TREE_DISCONNECT_HE] = { .proc = smb2_tree_disconnect,}, + [SMB2_LOGOFF_HE] = { .proc = smb2_session_logoff,}, + [SMB2_CREATE_HE] = { .proc = smb2_open}, + [SMB2_QUERY_INFO_HE] = { .proc = smb2_query_info}, + [SMB2_QUERY_DIRECTORY_HE] = { .proc = smb2_query_dir}, + [SMB2_CLOSE_HE] = { .proc = smb2_close}, + [SMB2_ECHO_HE] = { .proc = smb2_echo}, + [SMB2_SET_INFO_HE] = { .proc = smb2_set_info}, + [SMB2_READ_HE] = { .proc = smb2_read}, + [SMB2_WRITE_HE] = { .proc = smb2_write}, + [SMB2_FLUSH_HE] = { .proc = smb2_flush}, + [SMB2_CANCEL_HE] = { .proc = smb2_cancel}, + [SMB2_LOCK_HE] = { .proc = smb2_lock}, + [SMB2_IOCTL_HE] = { .proc = smb2_ioctl}, + [SMB2_OPLOCK_BREAK_HE] = { .proc = smb2_oplock_break}, + [SMB2_CHANGE_NOTIFY_HE] = { .proc = smb2_notify}, +}; + +int init_smb2_0_server(struct ksmbd_conn *conn) +{ + return -EOPNOTSUPP; +} + +/** + * init_smb2_1_server() - initialize a smb server connection with smb2.1 + * command dispatcher + * @conn: connection instance + */ +void init_smb2_1_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb21_server_values; + conn->ops = &smb2_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->max_credits = SMB2_MAX_CREDITS; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; +} + +/** + * init_smb3_0_server() - initialize a smb server connection with smb3.0 + * command dispatcher + * @conn: connection instance + */ +void init_smb3_0_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb30_server_values; + conn->ops = &smb3_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->max_credits = SMB2_MAX_CREDITS; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; +} + +/** + * init_smb3_02_server() - initialize a smb server connection with smb3.02 + * command dispatcher + * @conn: connection instance + */ +void init_smb3_02_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb302_server_values; + conn->ops = &smb3_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->max_credits = SMB2_MAX_CREDITS; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; +} + +/** + * init_smb3_11_server() - initialize a smb server connection with smb3.11 + * command dispatcher + * @conn: connection instance + */ +int init_smb3_11_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb311_server_values; + conn->ops = &smb3_11_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->max_credits = SMB2_MAX_CREDITS; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (conn->cipher_type) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + INIT_LIST_HEAD(&conn->preauth_sess_table); + return 0; +} + +void init_smb2_max_read_size(unsigned int sz) +{ + smb21_server_values.max_read_size = sz; + smb30_server_values.max_read_size = sz; + smb302_server_values.max_read_size = sz; + smb311_server_values.max_read_size = sz; +} + +void init_smb2_max_write_size(unsigned int sz) +{ + smb21_server_values.max_write_size = sz; + smb30_server_values.max_write_size = sz; + smb302_server_values.max_write_size = sz; + smb311_server_values.max_write_size = sz; +} + +void init_smb2_max_trans_size(unsigned int sz) +{ + smb21_server_values.max_trans_size = sz; + smb30_server_values.max_trans_size = sz; + smb302_server_values.max_trans_size = sz; + smb311_server_values.max_trans_size = sz; +} diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c new file mode 100644 index 000000000000..b20cc07ee809 --- /dev/null +++ b/fs/cifsd/smb2pdu.c @@ -0,0 +1,8486 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "smb2pdu.h" +#include "smbfsctl.h" +#include "oplock.h" +#include "smbacl.h" + +#include "auth.h" +#include "asn1.h" +#include "buffer_pool.h" +#include "connection.h" +#include "transport_ipc.h" +#include "vfs.h" +#include "vfs_cache.h" +#include "misc.h" + +#include "time_wrappers.h" +#include "server.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "ksmbd_work.h" +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "mgmt/ksmbd_ida.h" +#include "ndr.h" + +static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) +{ + if (work->next_smb2_rcv_hdr_off) { + *req = REQUEST_BUF_NEXT(work); + *rsp = RESPONSE_BUF_NEXT(work); + } else { + *req = REQUEST_BUF(work); + *rsp = RESPONSE_BUF(work); + } +} + +#define WORK_BUFFERS(w, rq, rs) __wbuf((w), (void **)&(rq), (void **)&(rs)) + +/** + * check_session_id() - check for valid session id in smb header + * @conn: connection instance + * @id: session id from smb header + * + * Return: 1 if valid session id, otherwise 0 + */ +static inline int check_session_id(struct ksmbd_conn *conn, uint64_t id) +{ + struct ksmbd_session *sess; + + if (id == 0 || id == -1) + return 0; + + sess = ksmbd_session_lookup(conn, id); + if (sess) + return 1; + ksmbd_err("Invalid user session id: %llu\n", id); + return 0; +} + +struct channel *lookup_chann_list(struct ksmbd_session *sess) +{ + struct channel *chann; + struct list_head *t; + + list_for_each(t, &sess->ksmbd_chann_list) { + chann = list_entry(t, struct channel, chann_list); + if (chann && chann->conn == sess->conn) + return chann; + } + + return NULL; +} + +/** + * smb2_get_ksmbd_tcon() - get tree connection information for a tree id + * @sess: session containing tree list + * @tid: match tree connection with tree id + * + * Return: matching tree connection on success, otherwise error + */ +int smb2_get_ksmbd_tcon(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = REQUEST_BUF(work); + int tree_id; + + work->tcon = NULL; + if ((work->conn->ops->get_cmd_val(work) == SMB2_TREE_CONNECT_HE) || + (work->conn->ops->get_cmd_val(work) == SMB2_CANCEL_HE) || + (work->conn->ops->get_cmd_val(work) == SMB2_LOGOFF_HE)) { + ksmbd_debug(SMB, "skip to check tree connect request\n"); + return 0; + } + + if (list_empty(&work->sess->tree_conn_list)) { + ksmbd_debug(SMB, "NO tree connected\n"); + return -1; + } + + tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId); + work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); + if (!work->tcon) { + ksmbd_err("Invalid tid %d\n", tree_id); + return -1; + } + + return 1; +} + +/** + * smb2_set_err_rsp() - set error response code on smb response + * @work: smb work containing response buffer + */ +void smb2_set_err_rsp(struct ksmbd_work *work) +{ + struct smb2_err_rsp *err_rsp; + + if (work->next_smb2_rcv_hdr_off) + err_rsp = RESPONSE_BUF_NEXT(work); + else + err_rsp = RESPONSE_BUF(work); + + if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { + err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; + err_rsp->ErrorContextCount = 0; + err_rsp->Reserved = 0; + err_rsp->ByteCount = 0; + err_rsp->ErrorData[0] = 0; + inc_rfc1001_len(RESPONSE_BUF(work), SMB2_ERROR_STRUCTURE_SIZE2); + } +} + +/** + * is_smb2_neg_cmd() - is it smb2 negotiation command + * @work: smb work containing smb header + * + * Return: 1 if smb2 negotiation command, otherwise 0 + */ +int is_smb2_neg_cmd(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = REQUEST_BUF(work); + + /* is it SMB2 header ? */ + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return 0; + + /* make sure it is request not response message */ + if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return 0; + + if (hdr->Command != SMB2_NEGOTIATE) + return 0; + + return 1; +} + +/** + * is_smb2_rsp() - is it smb2 response + * @work: smb work containing smb response buffer + * + * Return: 1 if smb2 response, otherwise 0 + */ +int is_smb2_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = RESPONSE_BUF(work); + + /* is it SMB2 header ? */ + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return 0; + + /* make sure it is response not request message */ + if (!(hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)) + return 0; + + return 1; +} + +/** + * get_smb2_cmd_val() - get smb command code from smb header + * @work: smb work containing smb request buffer + * + * Return: smb2 request command value + */ +uint16_t get_smb2_cmd_val(struct ksmbd_work *work) +{ + struct smb2_hdr *rcv_hdr; + + if (work->next_smb2_rcv_hdr_off) + rcv_hdr = REQUEST_BUF_NEXT(work); + else + rcv_hdr = REQUEST_BUF(work); + return le16_to_cpu(rcv_hdr->Command); +} + +/** + * set_smb2_rsp_status() - set error response code on smb2 header + * @work: smb work containing response buffer + */ +void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) +{ + struct smb2_hdr *rsp_hdr; + + if (work->next_smb2_rcv_hdr_off) + rsp_hdr = RESPONSE_BUF_NEXT(work); + else + rsp_hdr = RESPONSE_BUF(work); + rsp_hdr->Status = err; + smb2_set_err_rsp(work); +} + +/** + * init_smb2_neg_rsp() - initialize smb2 response for negotiate command + * @work: smb work containing smb request buffer + * + * smb2 negotiate response is sent in reply of smb1 negotiate command for + * dialect auto-negotiation. + */ +int init_smb2_neg_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *rsp_hdr; + struct smb2_negotiate_rsp *rsp; + struct ksmbd_conn *conn = work->conn; + + if (conn->need_neg == false) + return -EINVAL; + if (!(conn->dialect >= SMB20_PROT_ID && + conn->dialect <= SMB311_PROT_ID)) + return -EINVAL; + + rsp_hdr = RESPONSE_BUF(work); + + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + + rsp_hdr->smb2_buf_length = + cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn)); + + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(2); + rsp_hdr->Command = SMB2_NEGOTIATE; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = 0; + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = RESPONSE_BUF(work); + + WARN_ON(ksmbd_conn_good(work)); + + rsp->StructureSize = cpu_to_le16(65); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); + rsp->DialectRevision = cpu_to_le16(conn->dialect); + /* Not setting conn guid rsp->ServerGUID, as it + * not used by client for identifying connection + */ + rsp->Capabilities = cpu_to_le32(conn->vals->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); + rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); + + rsp->SystemTime = cpu_to_le64(ksmbd_systime()); + rsp->ServerStartTime = 0; + + rsp->SecurityBufferOffset = cpu_to_le16(128); + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header(((char *)(&rsp->hdr) + + sizeof(rsp->hdr.smb2_buf_length)) + + le16_to_cpu(rsp->SecurityBufferOffset)); + inc_rfc1001_len(rsp, sizeof(struct smb2_negotiate_rsp) - + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + conn->use_spnego = true; + + ksmbd_conn_set_need_negotiate(work); + return 0; +} + +static int smb2_consume_credit_charge(struct ksmbd_work *work, + unsigned short credit_charge) +{ + struct ksmbd_conn *conn = work->conn; + unsigned int rsp_credits = 1; + + if (!conn->total_credits) + return 0; + + if (credit_charge > 0) + rsp_credits = credit_charge; + + conn->total_credits -= rsp_credits; + return rsp_credits; +} + +/** + * smb2_set_rsp_credits() - set number of credits in response buffer + * @work: smb work containing smb response buffer + */ +int smb2_set_rsp_credits(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = REQUEST_BUF_NEXT(work); + struct smb2_hdr *hdr = RESPONSE_BUF_NEXT(work); + struct ksmbd_conn *conn = work->conn; + unsigned short credits_requested = le16_to_cpu(req_hdr->CreditRequest); + unsigned short credit_charge = 1, credits_granted = 0; + unsigned short aux_max, aux_credits, min_credits; + int rsp_credit_charge; + + if (hdr->Command == SMB2_CANCEL) + goto out; + + /* get default minimum credits by shifting maximum credits by 4 */ + min_credits = conn->max_credits >> 4; + + if (conn->total_credits >= conn->max_credits) { + ksmbd_err("Total credits overflow: %d\n", conn->total_credits); + conn->total_credits = min_credits; + } + + rsp_credit_charge = smb2_consume_credit_charge(work, + le16_to_cpu(req_hdr->CreditCharge)); + if (rsp_credit_charge < 0) + return -EINVAL; + + hdr->CreditCharge = cpu_to_le16(rsp_credit_charge); + + if (credits_requested > 0) { + aux_credits = credits_requested - 1; + aux_max = 32; + if (hdr->Command == SMB2_NEGOTIATE) + aux_max = 0; + aux_credits = (aux_credits < aux_max) ? aux_credits : aux_max; + credits_granted = aux_credits + credit_charge; + + /* if credits granted per client is getting bigger than default + * minimum credits then we should wrap it up within the limits. + */ + if ((conn->total_credits + credits_granted) > min_credits) + credits_granted = min_credits - conn->total_credits; + /* + * TODO: Need to adjuct CreditRequest value according to + * current cpu load + */ + } else if (conn->total_credits == 0) { + credits_granted = 1; + } + + conn->total_credits += credits_granted; + work->credits_granted += credits_granted; + + if (!req_hdr->NextCommand) { + /* Update CreditRequest in last request */ + hdr->CreditRequest = cpu_to_le16(work->credits_granted); + } +out: + ksmbd_debug(SMB, + "credits: requested[%d] granted[%d] total_granted[%d]\n", + credits_requested, credits_granted, + conn->total_credits); + return 0; +} + +/** + * init_chained_smb2_rsp() - initialize smb2 chained response + * @work: smb work containing smb response buffer + */ +static void init_chained_smb2_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *req = REQUEST_BUF_NEXT(work); + struct smb2_hdr *rsp = RESPONSE_BUF_NEXT(work); + struct smb2_hdr *rsp_hdr; + struct smb2_hdr *rcv_hdr; + int next_hdr_offset = 0; + int len, new_len; + + /* Len of this response = updated RFC len - offset of previous cmd + * in the compound rsp + */ + + /* Storing the current local FID which may be needed by subsequent + * command in the compound request + */ + if (req->Command == SMB2_CREATE && rsp->Status == STATUS_SUCCESS) { + work->compound_fid = + le64_to_cpu(((struct smb2_create_rsp *)rsp)-> + VolatileFileId); + work->compound_pfid = + le64_to_cpu(((struct smb2_create_rsp *)rsp)-> + PersistentFileId); + work->compound_sid = le64_to_cpu(rsp->SessionId); + } + + len = get_rfc1002_len(RESPONSE_BUF(work)) - work->next_smb2_rsp_hdr_off; + next_hdr_offset = le32_to_cpu(req->NextCommand); + + new_len = ALIGN(len, 8); + inc_rfc1001_len(RESPONSE_BUF(work), ((sizeof(struct smb2_hdr) - 4) + + new_len - len)); + rsp->NextCommand = cpu_to_le32(new_len); + + work->next_smb2_rcv_hdr_off += next_hdr_offset; + work->next_smb2_rsp_hdr_off += new_len; + ksmbd_debug(SMB, + "Compound req new_len = %d rcv off = %d rsp off = %d\n", + new_len, work->next_smb2_rcv_hdr_off, + work->next_smb2_rsp_hdr_off); + + rsp_hdr = RESPONSE_BUF_NEXT(work); + rcv_hdr = REQUEST_BUF_NEXT(work); + + if (!(rcv_hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { + ksmbd_debug(SMB, "related flag should be set\n"); + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + } + memset((char *)rsp_hdr + 4, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. We don't grant oplock yet. + */ + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR | + SMB2_FLAGS_RELATED_OPERATIONS); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = rcv_hdr->MessageId; + rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; + rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); +} + +/** + * is_chained_smb2_message() - check for chained command + * @work: smb work containing smb request buffer + * + * Return: true if chained request, otherwise false + */ +bool is_chained_smb2_message(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = REQUEST_BUF(work); + unsigned int len; + + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + hdr = REQUEST_BUF_NEXT(work); + if (le32_to_cpu(hdr->NextCommand) > 0) { + ksmbd_debug(SMB, "got SMB2 chained command\n"); + init_chained_smb2_rsp(work); + return true; + } else if (work->next_smb2_rcv_hdr_off) { + /* + * This is last request in chained command, + * align response to 8 byte + */ + len = ALIGN(get_rfc1002_len(RESPONSE_BUF(work)), 8); + len = len - get_rfc1002_len(RESPONSE_BUF(work)); + if (len) { + ksmbd_debug(SMB, "padding len %u\n", len); + inc_rfc1001_len(RESPONSE_BUF(work), len); + if (HAS_AUX_PAYLOAD(work)) + work->aux_payload_sz += len; + } + } + return false; +} + +/** + * init_smb2_rsp_hdr() - initialize smb2 response + * @work: smb work containing smb request buffer + * + * Return: 0 + */ +int init_smb2_rsp_hdr(struct ksmbd_work *work) +{ + struct smb2_hdr *rsp_hdr = RESPONSE_BUF(work); + struct smb2_hdr *rcv_hdr = REQUEST_BUF(work); + struct ksmbd_conn *conn = work->conn; + + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->smb2_buf_length = cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn)); + rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. We don't grant oplock yet. + */ + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = rcv_hdr->MessageId; + rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; + rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); + + work->syncronous = true; + if (work->async_id) { + ksmbd_release_id(conn->async_ida, work->async_id); + work->async_id = 0; + } + + return 0; +} + +/** + * smb2_allocate_rsp_buf() - allocate smb2 response buffer + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise -ENOMEM + */ +int smb2_allocate_rsp_buf(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = REQUEST_BUF(work); + size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + size_t large_sz = work->conn->vals->max_trans_size + MAX_SMB2_HDR_SIZE; + size_t sz = small_sz; + int cmd = le16_to_cpu(hdr->Command); + + if (cmd == SMB2_IOCTL_HE || cmd == SMB2_QUERY_DIRECTORY_HE) { + sz = large_sz; + work->set_trans_buf = true; + } + + if (cmd == SMB2_QUERY_INFO_HE) { + struct smb2_query_info_req *req; + + req = REQUEST_BUF(work); + if (req->InfoType == SMB2_O_INFO_FILE && + (req->FileInfoClass == FILE_FULL_EA_INFORMATION || + req->FileInfoClass == FILE_ALL_INFORMATION)) { + sz = large_sz; + work->set_trans_buf = true; + } + } + + /* allocate large response buf for chained commands */ + if (le32_to_cpu(hdr->NextCommand) > 0) + sz = large_sz; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_TBUF && + work->set_trans_buf) + work->response_buf = ksmbd_find_buffer(sz); + else + work->response_buf = ksmbd_alloc_response(sz); + + if (!RESPONSE_BUF(work)) { + ksmbd_err("Failed to allocate %zu bytes buffer\n", sz); + return -ENOMEM; + } + + work->response_sz = sz; + return 0; +} + +/** + * smb2_check_user_session() - check for valid session for a user + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_check_user_session(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = REQUEST_BUF(work); + struct ksmbd_conn *conn = work->conn; + unsigned int cmd = conn->ops->get_cmd_val(work); + unsigned long long sess_id; + + work->sess = NULL; + /* + * SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not + * require a session id, so no need to validate user session's for + * these commands. + */ + if (cmd == SMB2_ECHO_HE || cmd == SMB2_NEGOTIATE_HE || + cmd == SMB2_SESSION_SETUP_HE) + return 0; + + if (!ksmbd_conn_good(work)) + return -EINVAL; + + sess_id = le64_to_cpu(req_hdr->SessionId); + /* Check for validity of user session */ + work->sess = ksmbd_session_lookup(conn, sess_id); + if (work->sess) + return 1; + ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); + return -EINVAL; +} + +static void destroy_previous_session(struct ksmbd_user *user, uint64_t id) +{ + struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); + struct ksmbd_user *prev_user; + + if (!prev_sess) + return; + + prev_user = prev_sess->user; + + if (strcmp(user->name, prev_user->name) || + user->passkey_sz != prev_user->passkey_sz || + memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) { + put_session(prev_sess); + return; + } + + put_session(prev_sess); + ksmbd_session_destroy(prev_sess); +} + +/** + * smb2_get_name() - get filename string from on the wire smb format + * @src: source buffer + * @maxlen: maxlen of source string + * @work: smb work containing smb request buffer + * + * Return: matching converted filename on success, otherwise error ptr + */ +static char * +smb2_get_name(struct ksmbd_share_config *share, + const char *src, + const int maxlen, + struct nls_table *local_nls) +{ + char *name, *unixname; + + name = smb_strndup_from_utf16(src, maxlen, 1, + local_nls); + if (IS_ERR(name)) { + ksmbd_err("failed to get name %ld\n", PTR_ERR(name)); + return name; + } + + /* change it to absolute unix name */ + ksmbd_conv_path_to_unix(name); + ksmbd_strip_last_slash(name); + + unixname = convert_to_unix_name(share, name); + kfree(name); + if (!unixname) { + ksmbd_err("can not convert absolute name\n"); + return ERR_PTR(-ENOMEM); + } + + ksmbd_debug(SMB, "absolute name = %s\n", unixname); + return unixname; +} + +/** + * smb2_put_name() - free memory allocated for filename + * @name: filename pointer to be freed + */ +static void smb2_put_name(void *name) +{ + if (!IS_ERR(name)) + kfree(name); +} + +int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) +{ + struct smb2_hdr *rsp_hdr; + struct ksmbd_conn *conn = work->conn; + int id; + + rsp_hdr = RESPONSE_BUF(work); + rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; + + id = ksmbd_acquire_async_msg_id(conn->async_ida); + if (id < 0) { + ksmbd_err("Failed to alloc async message id\n"); + return id; + } + work->syncronous = false; + work->async_id = id; + rsp_hdr->Id.AsyncId = cpu_to_le64(id); + + ksmbd_debug(SMB, + "Send interim Response to inform async request id : %d\n", + work->async_id); + + work->cancel_fn = fn; + work->cancel_argv = arg; + + spin_lock(&conn->request_lock); + list_add_tail(&work->async_request_entry, &conn->async_requests); + spin_unlock(&conn->request_lock); + + return 0; +} + +void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) +{ + struct smb2_hdr *rsp_hdr; + + rsp_hdr = RESPONSE_BUF(work); + smb2_set_err_rsp(work); + rsp_hdr->Status = status; + + work->multiRsp = 1; + ksmbd_conn_write(work); + rsp_hdr->Status = 0; + work->multiRsp = 0; +} + +static __le32 smb2_get_reparse_tag_special_file(umode_t mode) +{ + if (S_ISDIR(mode) || S_ISREG(mode)) + return 0; + + if (S_ISLNK(mode)) + return IO_REPARSE_TAG_LX_SYMLINK_LE; + else if (S_ISFIFO(mode)) + return IO_REPARSE_TAG_LX_FIFO_LE; + else if (S_ISSOCK(mode)) + return IO_REPARSE_TAG_AF_UNIX_LE; + else if (S_ISCHR(mode)) + return IO_REPARSE_TAG_LX_CHR_LE; + else if (S_ISBLK(mode)) + return IO_REPARSE_TAG_LX_BLK_LE; + + return 0; +} + +/** + * smb2_get_dos_mode() - get file mode in dos format from unix mode + * @stat: kstat containing file mode + * + * Return: converted dos mode + */ +static int smb2_get_dos_mode(struct kstat *stat, int attribute) +{ + int attr = 0; + + if (S_ISDIR(stat->mode)) + attr = ATTR_DIRECTORY | + (attribute & (ATTR_HIDDEN | ATTR_SYSTEM)); + else { + attr = (attribute & 0x00005137) | ATTR_ARCHIVE; + attr &= ~(ATTR_DIRECTORY); + if (S_ISREG(stat->mode) && (server_conf.share_fake_fscaps & + FILE_SUPPORTS_SPARSE_FILES)) + attr |= ATTR_SPARSE; + + if (smb2_get_reparse_tag_special_file(stat->mode)) + attr |= ATTR_REPARSE; + } + + return attr; +} + +static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, + __le16 hash_id) +{ + pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(38); + pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); + get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); + pneg_ctxt->HashAlgorithms = hash_id; +} + +static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, + __le16 cipher_type) +{ + pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(4); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->CipherCount = cpu_to_le16(1); + pneg_ctxt->Ciphers[0] = cipher_type; +} + +static void build_compression_ctxt(struct smb2_compression_ctx *pneg_ctxt, + __le16 comp_algo) +{ + pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16(sizeof(struct smb2_compression_ctx) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->Reserved1 = cpu_to_le32(0); + pneg_ctxt->CompressionAlgorithms[0] = comp_algo; +} + +static void +build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; + pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + pneg_ctxt->Name[0] = 0x93; + pneg_ctxt->Name[1] = 0xAD; + pneg_ctxt->Name[2] = 0x25; + pneg_ctxt->Name[3] = 0x50; + pneg_ctxt->Name[4] = 0x9C; + pneg_ctxt->Name[5] = 0xB4; + pneg_ctxt->Name[6] = 0x11; + pneg_ctxt->Name[7] = 0xE7; + pneg_ctxt->Name[8] = 0xB4; + pneg_ctxt->Name[9] = 0x23; + pneg_ctxt->Name[10] = 0x83; + pneg_ctxt->Name[11] = 0xDE; + pneg_ctxt->Name[12] = 0x96; + pneg_ctxt->Name[13] = 0x8B; + pneg_ctxt->Name[14] = 0xCD; + pneg_ctxt->Name[15] = 0x7C; +} + +static void +assemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_rsp *rsp) +{ + /* +4 is to account for the RFC1001 len field */ + char *pneg_ctxt = (char *)rsp + + le32_to_cpu(rsp->NegotiateContextOffset) + 4; + int neg_ctxt_cnt = 1; + int ctxt_size; + + ksmbd_debug(SMB, + "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, + conn->preauth_info->Preauth_HashId); + rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); + inc_rfc1001_len(rsp, AUTH_GSS_PADDING); + ctxt_size = sizeof(struct smb2_preauth_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_preauth_neg_context), 8); + + if (conn->cipher_type) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + build_encrypt_ctxt( + (struct smb2_encryption_neg_context *)pneg_ctxt, + conn->cipher_type); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_encryption_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += + round_up(sizeof(struct smb2_encryption_neg_context), + 8); + } + + if (conn->compress_algorithm) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); + /* Temporarily set to SMB3_COMPRESS_NONE */ + build_compression_ctxt((struct smb2_compression_ctx *)pneg_ctxt, + conn->compress_algorithm); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_compression_ctx); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_compression_ctx), 8); + } + + if (conn->posix_ext_supported) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_posix_neg_context); + } + + inc_rfc1001_len(rsp, ctxt_size); +} + +static __le32 +decode_preauth_ctxt(struct ksmbd_conn *conn, + struct smb2_preauth_neg_context *pneg_ctxt) +{ + __le32 err = STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; + + if (pneg_ctxt->HashAlgorithms == + SMB2_PREAUTH_INTEGRITY_SHA512) { + conn->preauth_info->Preauth_HashId = + SMB2_PREAUTH_INTEGRITY_SHA512; + err = STATUS_SUCCESS; + } + + return err; +} + +static int decode_encrypt_ctxt(struct ksmbd_conn *conn, + struct smb2_encryption_neg_context *pneg_ctxt) +{ + int i; + int cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); + + conn->cipher_type = 0; + + if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)) + goto out; + + for (i = 0; i < cph_cnt; i++) { + if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM) { + ksmbd_debug(SMB, "Cipher ID = 0x%x\n", + pneg_ctxt->Ciphers[i]); + conn->cipher_type = pneg_ctxt->Ciphers[i]; + break; + } + } + +out: + /* + * Return encrypt context size in request. + * So need to plus extra number of ciphers size. + */ + return sizeof(struct smb2_encryption_neg_context) + + ((cph_cnt - 1) * 2); +} + +static int decode_compress_ctxt(struct ksmbd_conn *conn, + struct smb2_compression_ctx *pneg_ctxt) +{ + int algo_cnt = le16_to_cpu(pneg_ctxt->CompressionAlgorithmCount); + + conn->compress_algorithm = SMB3_COMPRESS_NONE; + + /* + * Return compression context size in request. + * So need to plus extra number of CompressionAlgorithms size. + */ + return sizeof(struct smb2_encryption_neg_context) + + ((algo_cnt - 1) * 2); +} + +static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_req *req) +{ + int i = 0; + __le32 status = 0; + /* +4 is to account for the RFC1001 len field */ + char *pneg_ctxt = (char *)req + + le32_to_cpu(req->NegotiateContextOffset) + 4; + __le16 *ContextType = (__le16 *)pneg_ctxt; + int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); + int ctxt_size; + + ksmbd_debug(SMB, "negotiate context count = %d\n", neg_ctxt_cnt); + status = STATUS_INVALID_PARAMETER; + while (i++ < neg_ctxt_cnt) { + if (*ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + if (conn->preauth_info->Preauth_HashId) + break; + + status = decode_preauth_ctxt(conn, + (struct smb2_preauth_neg_context *)pneg_ctxt); + pneg_ctxt += DIV_ROUND_UP( + sizeof(struct smb2_preauth_neg_context), 8) * 8; + } else if (*ContextType == SMB2_ENCRYPTION_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + if (conn->cipher_type) + break; + + ctxt_size = decode_encrypt_ctxt(conn, + (struct smb2_encryption_neg_context *) + pneg_ctxt); + pneg_ctxt += DIV_ROUND_UP(ctxt_size, 8) * 8; + } else if (*ContextType == SMB2_COMPRESSION_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); + if (conn->compress_algorithm) + break; + + ctxt_size = decode_compress_ctxt(conn, + (struct smb2_compression_ctx *) + pneg_ctxt); + pneg_ctxt += DIV_ROUND_UP(ctxt_size, 8) * 8; + } else if (*ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { + ksmbd_debug(SMB, + "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); + ctxt_size = sizeof(struct smb2_netname_neg_context); + ctxt_size += DIV_ROUND_UP( + le16_to_cpu(((struct smb2_netname_neg_context *) + pneg_ctxt)->DataLength), 8) * 8; + pneg_ctxt += ctxt_size; + } else if (*ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { + ksmbd_debug(SMB, + "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + conn->posix_ext_supported = true; + pneg_ctxt += DIV_ROUND_UP( + sizeof(struct smb2_posix_neg_context), 8) * 8; + } + ContextType = (__le16 *)pneg_ctxt; + + if (status != STATUS_SUCCESS) + break; + } + return status; +} + +/** + * smb2_handle_negotiate() - handler for smb2 negotiate command + * @work: smb work containing smb request buffer + * + * Return: 0 + */ +int smb2_handle_negotiate(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_negotiate_req *req = REQUEST_BUF(work); + struct smb2_negotiate_rsp *rsp = RESPONSE_BUF(work); + int rc = 0; + __le32 status; + + ksmbd_debug(SMB, "Received negotiate request\n"); + conn->need_neg = false; + if (ksmbd_conn_good(work)) { + ksmbd_err("conn->tcp_status is already in CifsGood State\n"); + work->send_no_response = 1; + return rc; + } + + if (req->DialectCount == 0) { + ksmbd_err("malformed packet\n"); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + conn->cli_cap = le32_to_cpu(req->Capabilities); + switch (conn->dialect) { + case SMB311_PROT_ID: + conn->preauth_info = + kzalloc(sizeof(struct preauth_integrity_info), + GFP_KERNEL); + if (!conn->preauth_info) { + rc = -ENOMEM; + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto err_out; + } + + status = deassemble_neg_contexts(conn, req); + if (status != STATUS_SUCCESS) { + ksmbd_err("deassemble_neg_contexts error(0x%x)\n", + status); + rsp->hdr.Status = status; + rc = -EINVAL; + goto err_out; + } + + rc = init_smb3_11_server(conn); + if (rc < 0) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto err_out; + } + + ksmbd_gen_preauth_integrity_hash(conn, + REQUEST_BUF(work), + conn->preauth_info->Preauth_HashValue); + rsp->NegotiateContextOffset = + cpu_to_le32(OFFSET_OF_NEG_CONTEXT); + assemble_neg_contexts(conn, rsp); + break; + case SMB302_PROT_ID: + init_smb3_02_server(conn); + break; + case SMB30_PROT_ID: + init_smb3_0_server(conn); + break; + case SMB21_PROT_ID: + init_smb2_1_server(conn); + break; + case SMB20_PROT_ID: + rc = init_smb2_0_server(conn); + if (rc) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto err_out; + } + break; + case SMB2X_PROT_ID: + case BAD_PROT_ID: + default: + ksmbd_debug(SMB, "Server dialect :0x%x not supported\n", + conn->dialect); + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + rc = -EINVAL; + goto err_out; + } + rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + + /* For stats */ + conn->connection_type = conn->dialect; + + rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); + rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); + rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); + + if (conn->dialect > SMB20_PROT_ID) { + memcpy(conn->ClientGUID, req->ClientGUID, + SMB2_CLIENT_GUID_SIZE); + conn->cli_sec_mode = le16_to_cpu(req->SecurityMode); + } + + rsp->StructureSize = cpu_to_le16(65); + rsp->DialectRevision = cpu_to_le16(conn->dialect); + /* Not setting conn guid rsp->ServerGUID, as it + * not used by client for identifying server + */ + memset(rsp->ServerGUID, 0, SMB2_CLIENT_GUID_SIZE); + + rsp->SystemTime = cpu_to_le64(ksmbd_systime()); + rsp->ServerStartTime = 0; + ksmbd_debug(SMB, "negotiate context offset %d, count %d\n", + le32_to_cpu(rsp->NegotiateContextOffset), + le16_to_cpu(rsp->NegotiateContextCount)); + + rsp->SecurityBufferOffset = cpu_to_le16(128); + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header(((char *)(&rsp->hdr) + + sizeof(rsp->hdr.smb2_buf_length)) + + le16_to_cpu(rsp->SecurityBufferOffset)); + inc_rfc1001_len(rsp, sizeof(struct smb2_negotiate_rsp) - + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + conn->use_spnego = true; + + if ((server_conf.signing == KSMBD_CONFIG_OPT_AUTO || + server_conf.signing == KSMBD_CONFIG_OPT_DISABLED) && + req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED_LE) + conn->sign = true; + else if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) { + server_conf.enforced_signing = true; + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + conn->sign = true; + } + + conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode); + ksmbd_conn_set_need_negotiate(work); + +err_out: + if (rc < 0) + smb2_set_err_rsp(work); + + return rc; +} + +static int alloc_preauth_hash(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + if (sess->Preauth_HashValue) + return 0; + + sess->Preauth_HashValue = ksmbd_alloc(PREAUTH_HASHVALUE_SIZE); + if (!sess->Preauth_HashValue) + return -ENOMEM; + + memcpy(sess->Preauth_HashValue, + conn->preauth_info->Preauth_HashValue, + PREAUTH_HASHVALUE_SIZE); + return 0; +} + +static int generate_preauth_hash(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + + if (conn->dialect != SMB311_PROT_ID) + return 0; + + if (!sess->Preauth_HashValue) { + if (alloc_preauth_hash(sess, conn)) + return -ENOMEM; + } + + ksmbd_gen_preauth_integrity_hash(conn, + REQUEST_BUF(work), + sess->Preauth_HashValue); + return 0; +} + +static int decode_negotiation_token(struct ksmbd_work *work, + struct negotiate_message *negblob) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_sess_setup_req *req; + int sz; + + if (!conn->use_spnego) + return -EINVAL; + + req = REQUEST_BUF(work); + sz = le16_to_cpu(req->SecurityBufferLength); + + if (!ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { + if (!ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { + conn->auth_mechs |= KSMBD_AUTH_NTLMSSP; + conn->preferred_auth_mech = KSMBD_AUTH_NTLMSSP; + conn->use_spnego = false; + } + } + return 0; +} + +static int ntlm_negotiate(struct ksmbd_work *work, + struct negotiate_message *negblob) +{ + struct smb2_sess_setup_req *req = REQUEST_BUF(work); + struct smb2_sess_setup_rsp *rsp = RESPONSE_BUF(work); + struct challenge_message *chgblob; + unsigned char *spnego_blob = NULL; + u16 spnego_blob_len; + char *neg_blob; + int sz, rc; + + ksmbd_debug(SMB, "negotiate phase\n"); + sz = le16_to_cpu(req->SecurityBufferLength); + rc = ksmbd_decode_ntlmssp_neg_blob(negblob, sz, work->sess); + if (rc) + return rc; + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + chgblob = + (struct challenge_message *)((char *)&rsp->hdr.ProtocolId + sz); + memset(chgblob, 0, sizeof(struct challenge_message)); + + if (!work->conn->use_spnego) { + sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->sess); + if (sz < 0) + return -ENOMEM; + + rsp->SecurityBufferLength = cpu_to_le16(sz); + return 0; + } + + sz = sizeof(struct challenge_message); + sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; + + neg_blob = kzalloc(sz, GFP_KERNEL); + if (!neg_blob) + return -ENOMEM; + + chgblob = (struct challenge_message *)neg_blob; + sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->sess); + if (sz < 0) { + rc = -ENOMEM; + goto out; + } + + rc = build_spnego_ntlmssp_neg_blob(&spnego_blob, + &spnego_blob_len, + neg_blob, + sz); + if (rc) { + rc = -ENOMEM; + goto out; + } + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + +out: + kfree(spnego_blob); + kfree(neg_blob); + return rc; +} + +static struct authenticate_message *user_authblob(struct ksmbd_conn *conn, + struct smb2_sess_setup_req *req) +{ + int sz; + + if (conn->use_spnego && conn->mechToken) + return (struct authenticate_message *)conn->mechToken; + + sz = le16_to_cpu(req->SecurityBufferOffset); + return (struct authenticate_message *)((char *)&req->hdr.ProtocolId + + sz); +} + +static struct ksmbd_user *session_user(struct ksmbd_conn *conn, + struct smb2_sess_setup_req *req) +{ + struct authenticate_message *authblob; + struct ksmbd_user *user; + char *name; + int sz; + + authblob = user_authblob(conn, req); + sz = le32_to_cpu(authblob->UserName.BufferOffset); + name = smb_strndup_from_utf16((const char *)authblob + sz, + le16_to_cpu(authblob->UserName.Length), + true, + conn->local_nls); + if (IS_ERR(name)) { + ksmbd_err("cannot allocate memory\n"); + return NULL; + } + + ksmbd_debug(SMB, "session setup request for user %s\n", name); + user = ksmbd_login_user(name); + kfree(name); + return user; +} + +static int ntlm_authenticate(struct ksmbd_work *work) +{ + struct smb2_sess_setup_req *req = REQUEST_BUF(work); + struct smb2_sess_setup_rsp *rsp = RESPONSE_BUF(work); + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct channel *chann = NULL; + struct ksmbd_user *user; + uint64_t prev_id; + int sz, rc; + + ksmbd_debug(SMB, "authenticate phase\n"); + if (conn->use_spnego) { + unsigned char *spnego_blob; + u16 spnego_blob_len; + + rc = build_spnego_ntlmssp_auth_blob(&spnego_blob, + &spnego_blob_len, + 0); + if (rc) + return -ENOMEM; + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + memcpy((char *)&rsp->hdr.ProtocolId + sz, + spnego_blob, + spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + kfree(spnego_blob); + inc_rfc1001_len(rsp, spnego_blob_len - 1); + } + + user = session_user(conn, req); + if (!user) { + ksmbd_debug(SMB, "Unknown user name or an error\n"); + rsp->hdr.Status = STATUS_LOGON_FAILURE; + return -EINVAL; + } + + /* Check for previous session */ + prev_id = le64_to_cpu(req->PreviousSessionId); + if (prev_id && prev_id != sess->id) + destroy_previous_session(user, prev_id); + + if (sess->state == SMB2_SESSION_VALID) { + /* + * Reuse session if anonymous try to connect + * on reauthetication. + */ + if (ksmbd_anonymous_user(user)) { + ksmbd_free_user(user); + return 0; + } + ksmbd_free_user(sess->user); + } + + sess->user = user; + if (user_guest(sess->user)) { + if (conn->sign) { + ksmbd_debug(SMB, + "Guest login not allowed when signing enabled\n"); + rsp->hdr.Status = STATUS_LOGON_FAILURE; + return -EACCES; + } + + rsp->SessionFlags = SMB2_SESSION_FLAG_IS_GUEST_LE; + } else { + struct authenticate_message *authblob; + + authblob = user_authblob(conn, req); + sz = le16_to_cpu(req->SecurityBufferLength); + rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, sess); + if (rc) { + set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); + ksmbd_debug(SMB, "authentication failed\n"); + rsp->hdr.Status = STATUS_LOGON_FAILURE; + return -EINVAL; + } + + /* + * 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) + return 0; + + if ((conn->sign || server_conf.enforced_signing) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + sess->sign = true; + + if (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION && + conn->ops->generate_encryptionkey) { + rc = conn->ops->generate_encryptionkey(sess); + if (rc) { + ksmbd_debug(SMB, + "SMB3 encryption key generation failed\n"); + rsp->hdr.Status = STATUS_LOGON_FAILURE; + return rc; + } + sess->enc = true; + rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + /* + * signing is disable if encryption is enable + * on this session + */ + sess->sign = false; + } + } + + if (conn->dialect >= SMB30_PROT_ID) { + chann = lookup_chann_list(sess); + if (!chann) { + chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + if (!chann) + return -ENOMEM; + + chann->conn = conn; + INIT_LIST_HEAD(&chann->chann_list); + list_add(&chann->chann_list, &sess->ksmbd_chann_list); + } + } + + if (conn->ops->generate_signingkey) { + rc = conn->ops->generate_signingkey(sess); + if (rc) { + ksmbd_debug(SMB, + "SMB3 signing key generation failed\n"); + rsp->hdr.Status = STATUS_LOGON_FAILURE; + return rc; + } + } + + if (conn->dialect > SMB20_PROT_ID) { + if (!ksmbd_conn_lookup_dialect(conn)) { + ksmbd_err("fail to verify the dialect\n"); + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + return -EPERM; + } + } + return 0; +} + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +static int krb5_authenticate(struct ksmbd_work *work) +{ + struct smb2_sess_setup_req *req = REQUEST_BUF(work); + struct smb2_sess_setup_rsp *rsp = RESPONSE_BUF(work); + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + char *in_blob, *out_blob; + struct channel *chann = NULL; + uint64_t prev_sess_id; + int in_len, out_len; + int retval; + + in_blob = (char *)&req->hdr.ProtocolId + + le16_to_cpu(req->SecurityBufferOffset); + in_len = le16_to_cpu(req->SecurityBufferLength); + out_blob = (char *)&rsp->hdr.ProtocolId + + le16_to_cpu(rsp->SecurityBufferOffset); + out_len = work->response_sz - + offsetof(struct smb2_hdr, smb2_buf_length) - + le16_to_cpu(rsp->SecurityBufferOffset); + + /* Check previous session */ + prev_sess_id = le64_to_cpu(req->PreviousSessionId); + if (prev_sess_id && prev_sess_id != sess->id) + destroy_previous_session(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"); + rsp->hdr.Status = STATUS_LOGON_FAILURE; + return retval; + } + rsp->SecurityBufferLength = cpu_to_le16(out_len); + inc_rfc1001_len(rsp, out_len - 1); + + if ((conn->sign || server_conf.enforced_signing) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + sess->sign = true; + + if ((conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) && + conn->ops->generate_encryptionkey) { + retval = conn->ops->generate_encryptionkey(sess); + if (retval) { + ksmbd_debug(SMB, + "SMB3 encryption key generation failed\n"); + rsp->hdr.Status = STATUS_LOGON_FAILURE; + return retval; + } + sess->enc = true; + rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + sess->sign = false; + } + + if (conn->dialect >= SMB30_PROT_ID) { + chann = lookup_chann_list(sess); + if (!chann) { + chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + if (!chann) + return -ENOMEM; + + chann->conn = conn; + INIT_LIST_HEAD(&chann->chann_list); + list_add(&chann->chann_list, &sess->ksmbd_chann_list); + } + } + + if (conn->ops->generate_signingkey) { + retval = conn->ops->generate_signingkey(sess); + if (retval) { + ksmbd_debug(SMB, + "SMB3 signing key generation failed\n"); + rsp->hdr.Status = STATUS_LOGON_FAILURE; + return retval; + } + } + + if (conn->dialect > SMB20_PROT_ID) { + if (!ksmbd_conn_lookup_dialect(conn)) { + ksmbd_err("fail to verify the dialect\n"); + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + return -EPERM; + } + } + return 0; +} +#else +static int krb5_authenticate(struct ksmbd_work *work) +{ + return -EOPNOTSUPP; +} +#endif + +int smb2_sess_setup(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_sess_setup_req *req = REQUEST_BUF(work); + struct smb2_sess_setup_rsp *rsp = RESPONSE_BUF(work); + struct ksmbd_session *sess; + struct negotiate_message *negblob; + int rc = 0; + + ksmbd_debug(SMB, "Received request for session setup\n"); + + rsp->StructureSize = cpu_to_le16(9); + rsp->SessionFlags = 0; + rsp->SecurityBufferOffset = cpu_to_le16(72); + rsp->SecurityBufferLength = 0; + inc_rfc1001_len(rsp, 9); + + if (!req->hdr.SessionId) { + sess = ksmbd_smb2_session_create(); + if (!sess) { + rc = -ENOMEM; + goto out_err; + } + rsp->hdr.SessionId = cpu_to_le64(sess->id); + ksmbd_session_register(conn, sess); + } else { + sess = ksmbd_session_lookup(conn, + le64_to_cpu(req->hdr.SessionId)); + if (!sess) { + rc = -ENOENT; + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + goto out_err; + } + } + work->sess = sess; + + if (sess->state == SMB2_SESSION_EXPIRED) + sess->state = SMB2_SESSION_IN_PROGRESS; + + negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + + le16_to_cpu(req->SecurityBufferOffset)); + + if (decode_negotiation_token(work, negblob) == 0) { + if (conn->mechToken) + negblob = (struct negotiate_message *)conn->mechToken; + } + + if (server_conf.auth_mechs & conn->auth_mechs) { + if (conn->preferred_auth_mech & + (KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5)) { + rc = generate_preauth_hash(work); + if (rc) + goto out_err; + + rc = krb5_authenticate(work); + if (rc) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out_err; + } + + ksmbd_conn_set_good(work); + sess->state = SMB2_SESSION_VALID; + ksmbd_free(sess->Preauth_HashValue); + sess->Preauth_HashValue = NULL; + } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { + rc = generate_preauth_hash(work); + if (rc) + goto out_err; + + if (negblob->MessageType == NtLmNegotiate) { + rc = ntlm_negotiate(work, negblob); + if (rc) + goto out_err; + rsp->hdr.Status = + STATUS_MORE_PROCESSING_REQUIRED; + /* + * Note: here total size -1 is done as an + * adjustment for 0 size blob + */ + inc_rfc1001_len(rsp, + le16_to_cpu(rsp->SecurityBufferLength) + - 1); + + } else if (negblob->MessageType == NtLmAuthenticate) { + rc = ntlm_authenticate(work); + if (rc) + goto out_err; + + ksmbd_conn_set_good(work); + sess->state = SMB2_SESSION_VALID; + ksmbd_free(sess->Preauth_HashValue); + sess->Preauth_HashValue = NULL; + } + } else { + /* TODO: need one more negotiation */ + ksmbd_err("Not support the preferred authentication\n"); + rc = -EINVAL; + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + } + } else { + ksmbd_err("Not support authentication\n"); + rc = -EINVAL; + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + } + +out_err: + if (conn->use_spnego && conn->mechToken) { + kfree(conn->mechToken); + conn->mechToken = NULL; + } + + if (rc < 0 && sess) { + ksmbd_session_destroy(sess); + work->sess = NULL; + } + + return rc; +} + +/** + * smb2_tree_connect() - handler for smb2 tree connect command + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_tree_connect(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_tree_connect_req *req = REQUEST_BUF(work); + struct smb2_tree_connect_rsp *rsp = RESPONSE_BUF(work); + struct ksmbd_session *sess = work->sess; + char *treename = NULL, *name = NULL; + struct ksmbd_tree_conn_status status; + struct ksmbd_share_config *share; + int rc = -EINVAL; + + treename = smb_strndup_from_utf16(req->Buffer, + le16_to_cpu(req->PathLength), true, conn->local_nls); + if (IS_ERR(treename)) { + ksmbd_err("treename is NULL\n"); + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err1; + } + + name = extract_sharename(treename); + if (IS_ERR(name)) { + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err1; + } + + ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", + name, treename); + + status = ksmbd_tree_conn_connect(sess, name); + if (status.ret == KSMBD_TREE_CONN_STATUS_OK) + rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); + else + goto out_err1; + + share = status.tree_conn->share_conf; + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC share path request\n"); + rsp->ShareType = SMB2_SHARE_TYPE_PIPE; + rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | + FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE | + FILE_DELETE_LE | FILE_READ_CONTROL_LE | + FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | + FILE_SYNCHRONIZE_LE; + } else { + rsp->ShareType = SMB2_SHARE_TYPE_DISK; + rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | + FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE; + if (test_tree_conn_flag(status.tree_conn, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + rsp->MaximalAccess |= FILE_WRITE_DATA_LE | + FILE_APPEND_DATA_LE | FILE_WRITE_EA_LE | + FILE_DELETE_CHILD_LE | FILE_DELETE_LE | + FILE_WRITE_ATTRIBUTES_LE | FILE_DELETE_LE | + FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE | + FILE_WRITE_OWNER_LE | FILE_SYNCHRONIZE_LE; + } + } + + status.tree_conn->maximal_access = le32_to_cpu(rsp->MaximalAccess); + if (conn->posix_ext_supported) + status.tree_conn->posix_extensions = true; + +out_err1: + rsp->StructureSize = cpu_to_le16(16); + rsp->Capabilities = 0; + rsp->Reserved = 0; + /* default manual caching */ + rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; + inc_rfc1001_len(rsp, 16); + + if (!IS_ERR(treename)) + kfree(treename); + if (!IS_ERR(name)) + kfree(name); + + switch (status.ret) { + case KSMBD_TREE_CONN_STATUS_OK: + rsp->hdr.Status = STATUS_SUCCESS; + rc = 0; + break; + case KSMBD_TREE_CONN_STATUS_NO_SHARE: + rsp->hdr.Status = STATUS_BAD_NETWORK_PATH; + break; + case -ENOMEM: + case KSMBD_TREE_CONN_STATUS_NOMEM: + rsp->hdr.Status = STATUS_NO_MEMORY; + break; + case KSMBD_TREE_CONN_STATUS_ERROR: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS: + rsp->hdr.Status = STATUS_ACCESS_DENIED; + break; + case -EINVAL: + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + break; + default: + rsp->hdr.Status = STATUS_ACCESS_DENIED; + } + + return rc; +} + +/** + * smb2_create_open_flags() - convert smb open flags to unix open flags + * @file_present: is file already present + * @access: file access flags + * @disposition: file disposition flags + * @work: smb work containing smb request buffer + * + * Return: file open flags + */ +static int smb2_create_open_flags(bool file_present, __le32 access, + __le32 disposition) +{ + int oflags = O_NONBLOCK | O_LARGEFILE; + + if (access & FILE_READ_DESIRED_ACCESS_LE && + access & FILE_WRITE_DESIRE_ACCESS_LE) + oflags |= O_RDWR; + else if (access & FILE_WRITE_DESIRE_ACCESS_LE) + oflags |= O_WRONLY; + else + oflags |= O_RDONLY; + + if (access == FILE_READ_ATTRIBUTES_LE) + oflags |= O_PATH; + + if (file_present) { + switch (disposition & FILE_CREATE_MASK_LE) { + case FILE_OPEN_LE: + case FILE_CREATE_LE: + break; + case FILE_SUPERSEDE_LE: + case FILE_OVERWRITE_LE: + case FILE_OVERWRITE_IF_LE: + oflags |= O_TRUNC; + break; + default: + break; + } + } else { + switch (disposition & FILE_CREATE_MASK_LE) { + case FILE_SUPERSEDE_LE: + case FILE_CREATE_LE: + case FILE_OPEN_IF_LE: + case FILE_OVERWRITE_IF_LE: + oflags |= O_CREAT; + break; + case FILE_OPEN_LE: + case FILE_OVERWRITE_LE: + oflags &= ~O_CREAT; + break; + default: + break; + } + } + return oflags; +} + +/** + * smb2_tree_disconnect() - handler for smb tree connect request + * @work: smb work containing request buffer + * + * Return: 0 + */ +int smb2_tree_disconnect(struct ksmbd_work *work) +{ + struct smb2_tree_disconnect_rsp *rsp = RESPONSE_BUF(work); + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; + + rsp->StructureSize = cpu_to_le16(4); + inc_rfc1001_len(rsp, 4); + + ksmbd_debug(SMB, "request\n"); + + if (!tcon) { + struct smb2_tree_disconnect_req *req = REQUEST_BUF(work); + + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); + return 0; + } + + ksmbd_close_tree_conn_fds(work); + ksmbd_tree_conn_disconnect(sess, tcon); + return 0; +} + +/** + * smb2_session_logoff() - handler for session log off request + * @work: smb work containing request buffer + * + * Return: 0 + */ +int smb2_session_logoff(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_logoff_rsp *rsp = RESPONSE_BUF(work); + struct ksmbd_session *sess = work->sess; + + rsp->StructureSize = cpu_to_le16(4); + inc_rfc1001_len(rsp, 4); + + ksmbd_debug(SMB, "request\n"); + + /* Got a valid session, set connection state */ + WARN_ON(sess->conn != conn); + + /* setting CifsExiting here may race with start_tcp_sess */ + ksmbd_conn_set_need_reconnect(work); + ksmbd_close_session_fds(work); + ksmbd_conn_wait_idle(conn); + + if (ksmbd_tree_conn_session_logoff(sess)) { + struct smb2_logoff_req *req = REQUEST_BUF(work); + + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); + return 0; + } + + ksmbd_destroy_file_table(&sess->file_table); + sess->state = SMB2_SESSION_EXPIRED; + + ksmbd_free_user(sess->user); + sess->user = NULL; + + /* let start_tcp_sess free connection info now */ + ksmbd_conn_set_need_negotiate(work); + return 0; +} + +/** + * create_smb2_pipe() - create IPC pipe + * @work: smb work containing request buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int create_smb2_pipe(struct ksmbd_work *work) +{ + struct smb2_create_rsp *rsp = RESPONSE_BUF(work); + struct smb2_create_req *req = REQUEST_BUF(work); + int id; + int err; + char *name; + + name = smb_strndup_from_utf16(req->Buffer, le16_to_cpu(req->NameLength), + 1, work->conn->local_nls); + if (IS_ERR(name)) { + rsp->hdr.Status = STATUS_NO_MEMORY; + err = PTR_ERR(name); + goto out; + } + + id = ksmbd_session_rpc_open(work->sess, name); + if (id < 0) + ksmbd_err("Unable to open RPC pipe: %d\n", id); + + rsp->StructureSize = cpu_to_le16(89); + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; + rsp->Reserved = 0; + rsp->CreateAction = cpu_to_le32(FILE_OPENED); + + rsp->CreationTime = cpu_to_le64(0); + rsp->LastAccessTime = cpu_to_le64(0); + rsp->ChangeTime = cpu_to_le64(0); + rsp->AllocationSize = cpu_to_le64(0); + rsp->EndofFile = cpu_to_le64(0); + rsp->FileAttributes = ATTR_NORMAL_LE; + rsp->Reserved2 = 0; + rsp->VolatileFileId = cpu_to_le64(id); + rsp->PersistentFileId = 0; + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + + inc_rfc1001_len(rsp, 88); /* StructureSize - 1*/ + kfree(name); + return 0; + +out: + smb2_set_err_rsp(work); + return err; +} + +#define DURABLE_RECONN_V2 1 +#define DURABLE_RECONN 2 +#define DURABLE_REQ_V2 3 +#define DURABLE_REQ 4 +#define APP_INSTANCE_ID 5 + +struct durable_info { + struct ksmbd_file *fp; + int type; + int reconnected; + int persistent; + int timeout; + char *CreateGuid; + char *app_id; +}; + +static int parse_durable_handle_context(struct ksmbd_work *work, + struct smb2_create_req *req, struct lease_ctx_info *lc, + struct durable_info *d_info) +{ + struct ksmbd_conn *conn = work->conn; + struct create_context *context; + int i, err = 0; + uint64_t persistent_id = 0; + int req_op_level; + static const char * const durable_arr[] = {"DH2C", "DHnC", "DH2Q", + "DHnQ", SMB2_CREATE_APP_INSTANCE_ID}; + + req_op_level = req->RequestedOplockLevel; + for (i = 1; i <= 5; i++) { + context = smb2_find_context_vals(req, durable_arr[i - 1]); + if (IS_ERR(context)) { + err = PTR_ERR(context); + if (err == -EINVAL) { + ksmbd_err("bad name length\n"); + goto out; + } + err = 0; + continue; + } + + switch (i) { + case DURABLE_RECONN_V2: + { + struct create_durable_reconn_v2_req *recon_v2; + + recon_v2 = + (struct create_durable_reconn_v2_req *)context; + persistent_id = le64_to_cpu( + recon_v2->Fid.PersistentFileId); + d_info->fp = ksmbd_lookup_durable_fd(persistent_id); + if (!d_info->fp) { + ksmbd_err("Failed to get Durable handle state\n"); + err = -EBADF; + goto out; + } + + if (memcmp(d_info->fp->create_guid, + recon_v2->CreateGuid, + SMB2_CREATE_GUID_SIZE)) { + err = -EBADF; + goto out; + } + d_info->type = i; + d_info->reconnected = 1; + ksmbd_debug(SMB, + "reconnect v2 Persistent-id from reconnect = %llu\n", + persistent_id); + break; + } + case DURABLE_RECONN: + { + struct create_durable_reconn_req *recon; + + if (d_info->type == DURABLE_RECONN_V2 || + d_info->type == DURABLE_REQ_V2) { + err = -EINVAL; + goto out; + } + + recon = + (struct create_durable_reconn_req *)context; + persistent_id = le64_to_cpu( + recon->Data.Fid.PersistentFileId); + d_info->fp = ksmbd_lookup_durable_fd(persistent_id); + if (!d_info->fp) { + ksmbd_err("Failed to get Durable handle state\n"); + err = -EBADF; + goto out; + } + d_info->type = i; + d_info->reconnected = 1; + ksmbd_debug(SMB, + "reconnect Persistent-id from reconnect = %llu\n", + persistent_id); + break; + } + case DURABLE_REQ_V2: + { + struct create_durable_req_v2 *durable_v2_blob; + + if (d_info->type == DURABLE_RECONN || + d_info->type == DURABLE_RECONN_V2) { + err = -EINVAL; + goto out; + } + + durable_v2_blob = + (struct create_durable_req_v2 *)context; + ksmbd_debug(SMB, "Request for durable v2 open\n"); + d_info->fp = ksmbd_lookup_fd_cguid( + durable_v2_blob->CreateGuid); + if (d_info->fp) { + if (!memcmp(conn->ClientGUID, + d_info->fp->client_guid, + SMB2_CLIENT_GUID_SIZE)) { + if (!(req->hdr.Flags & + SMB2_FLAGS_REPLAY_OPERATIONS)) { + err = -ENOEXEC; + goto out; + } + + d_info->fp->conn = conn; + d_info->reconnected = 1; + goto out; + } + } + if (((lc && + (lc->req_state & + SMB2_LEASE_HANDLE_CACHING_LE)) || + (req_op_level == SMB2_OPLOCK_LEVEL_BATCH))) { + d_info->CreateGuid = + durable_v2_blob->CreateGuid; + d_info->persistent = + le32_to_cpu(durable_v2_blob->Flags); + d_info->timeout = + le32_to_cpu(durable_v2_blob->Timeout); + d_info->type = i; + } + break; + } + case DURABLE_REQ: + if (d_info->type == DURABLE_RECONN) + goto out; + if (d_info->type == DURABLE_RECONN_V2 || + d_info->type == DURABLE_REQ_V2) { + err = -EINVAL; + goto out; + } + + 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"); + d_info->type = i; + } + break; + case APP_INSTANCE_ID: + { + struct create_app_inst_id *inst_id; + + inst_id = (struct create_app_inst_id *)context; + ksmbd_close_fd_app_id(work, inst_id->AppInstanceId); + d_info->app_id = inst_id->AppInstanceId; + break; + } + default: + break; + } + } + +out: + + return err; +} + +/** + * smb2_set_ea() - handler for setting extended attributes using set + * info command + * @eabuf: set info command buffer + * @path: dentry path for get ea + * + * Return: 0 on success, otherwise error + */ +static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) +{ + char *attr_name = NULL, *value; + int rc = 0; + int next = 0; + + attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); + if (!attr_name) + return -ENOMEM; + + do { + if (!eabuf->EaNameLength) + goto next; + + ksmbd_debug(SMB, + "name : <%s>, name_len : %u, value_len : %u, next : %u\n", + eabuf->name, eabuf->EaNameLength, + le16_to_cpu(eabuf->EaValueLength), + le32_to_cpu(eabuf->NextEntryOffset)); + + if (eabuf->EaNameLength > + (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { + rc = -EINVAL; + break; + } + + memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); + memcpy(&attr_name[XATTR_USER_PREFIX_LEN], eabuf->name, + eabuf->EaNameLength); + attr_name[XATTR_USER_PREFIX_LEN + eabuf->EaNameLength] = '\0'; + value = (char *)&eabuf->name + eabuf->EaNameLength + 1; + + if (!eabuf->EaValueLength) { + rc = ksmbd_vfs_casexattr_len(path->dentry, + attr_name, + XATTR_USER_PREFIX_LEN + + eabuf->EaNameLength); + + /* delete the EA only when it exits */ + if (rc > 0) { + rc = ksmbd_vfs_remove_xattr(path->dentry, + attr_name); + + if (rc < 0) { + ksmbd_debug(SMB, + "remove xattr failed(%d)\n", + rc); + break; + } + } + + /* if the EA doesn't exist, just do nothing. */ + rc = 0; + } else { + rc = ksmbd_vfs_setxattr(path->dentry, attr_name, value, + le16_to_cpu(eabuf->EaValueLength), 0); + if (rc < 0) { + ksmbd_debug(SMB, + "ksmbd_vfs_setxattr is failed(%d)\n", + rc); + break; + } + } + +next: + next = le32_to_cpu(eabuf->NextEntryOffset); + eabuf = (struct smb2_ea_info *)((char *)eabuf + next); + } while (next != 0); + + kfree(attr_name); + return rc; +} + +static inline int check_context_err(void *ctx, char *str) +{ + int err; + + err = PTR_ERR(ctx); + ksmbd_debug(SMB, "find context %s err %d\n", str, err); + + if (err == -EINVAL) { + ksmbd_err("bad name length\n"); + return err; + } + + return 0; +} + +static noinline int smb2_set_stream_name_xattr(struct path *path, + struct ksmbd_file *fp, + char *stream_name, + int s_type) +{ + size_t xattr_stream_size; + char *xattr_stream_name; + int rc; + + rc = ksmbd_vfs_xattr_stream_name(stream_name, + &xattr_stream_name, + &xattr_stream_size, + s_type); + if (rc) + return rc; + + fp->stream.name = xattr_stream_name; + fp->stream.size = xattr_stream_size; + + /* Check if there is stream prefix in xattr space */ + rc = ksmbd_vfs_casexattr_len(path->dentry, + xattr_stream_name, + xattr_stream_size); + if (rc >= 0) + return 0; + + if (fp->cdoption == FILE_OPEN_LE) { + ksmbd_debug(SMB, "XATTR stream name lookup failed: %d\n", rc); + return -EBADF; + } + + rc = ksmbd_vfs_setxattr(path->dentry, xattr_stream_name, NULL, 0, 0); + if (rc < 0) + ksmbd_err("Failed to store XATTR stream name :%d\n", rc); + return 0; +} + +static int smb2_remove_smb_xattrs(struct dentry *dentry) +{ + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && + strncmp(&name[XATTR_USER_PREFIX_LEN], + DOS_ATTRIBUTE_PREFIX, + DOS_ATTRIBUTE_PREFIX_LEN) && + strncmp(&name[XATTR_USER_PREFIX_LEN], + STREAM_PREFIX, + STREAM_PREFIX_LEN)) + continue; + + err = ksmbd_vfs_remove_xattr(dentry, name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", name); + } +out: + ksmbd_vfs_xattr_free(xattr_list); + return err; +} + +static int smb2_create_truncate(struct path *path) +{ + int rc = vfs_truncate(path, 0); + + if (rc) { + ksmbd_err("vfs_truncate failed, rc %d\n", rc); + return rc; + } + + rc = smb2_remove_smb_xattrs(path->dentry); + if (rc == -EOPNOTSUPP) + rc = 0; + if (rc) + ksmbd_debug(SMB, + "ksmbd_truncate_stream_name_xattr failed, rc %d\n", + rc); + return rc; +} + +static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, + struct path *path, + struct ksmbd_file *fp) +{ + struct xattr_dos_attrib da = {0}; + int rc; + + if (!test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) + return; + + da.version = 4; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + da.itime = da.create_time = fp->create_time; + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + rc = ksmbd_vfs_set_dos_attrib_xattr(path->dentry, &da); + if (rc) + ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); +} + +static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, + struct path *path, + struct ksmbd_file *fp) +{ + struct xattr_dos_attrib da; + int rc; + + fp->f_ci->m_fattr &= ~(ATTR_HIDDEN_LE | ATTR_SYSTEM_LE); + + /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ + if (!test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) + return; + + rc = ksmbd_vfs_get_dos_attrib_xattr(path->dentry, &da); + if (rc > 0) { + fp->f_ci->m_fattr = cpu_to_le32(da.attr); + fp->create_time = da.create_time; + fp->itime = da.itime; + } +} + +static int smb2_creat(struct ksmbd_work *work, + struct path *path, + char *name, + int open_flags, + umode_t posix_mode, + bool is_dir) +{ + struct ksmbd_tree_connect *tcon = work->tcon; + struct ksmbd_share_config *share = tcon->share_conf; + umode_t mode; + int rc; + + if (!(open_flags & O_CREAT)) + return -EBADF; + + ksmbd_debug(SMB, "file does not exist, so creating\n"); + if (is_dir == true) { + ksmbd_debug(SMB, "creating directory\n"); + + mode = share_config_directory_mode(share, posix_mode); + rc = ksmbd_vfs_mkdir(work, name, mode); + if (rc) + return rc; + } else { + ksmbd_debug(SMB, "creating regular file\n"); + + mode = share_config_create_mode(share, posix_mode); + rc = ksmbd_vfs_create(work, name, mode); + if (rc) + return rc; + } + + rc = ksmbd_vfs_kern_path(name, 0, path, 0); + if (rc) { + ksmbd_err("cannot get linux path (%s), err = %d\n", + name, rc); + return rc; + } + return 0; +} + +static int smb2_create_sd_buffer(struct ksmbd_work *work, + struct smb2_create_req *req, struct dentry *dentry) +{ + struct create_context *context; + int rc = -ENOENT; + + if (!req->CreateContextsOffset) + return rc; + + /* Parse SD BUFFER create contexts */ + context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER); + if (context && !IS_ERR(context)) { + struct create_sd_buf_req *sd_buf; + + ksmbd_debug(SMB, + "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); + sd_buf = (struct create_sd_buf_req *)context; + rc = set_info_sec(work->conn, work->tcon, dentry, &sd_buf->ntsd, + le32_to_cpu(sd_buf->ccontext.DataLength), true); + } + + return rc; +} + +/** + * smb2_open() - handler for smb file open request + * @work: smb work containing request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_open(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; + struct smb2_create_req *req; + struct smb2_create_rsp *rsp, *rsp_org; + struct path path; + struct ksmbd_share_config *share = tcon->share_conf; + struct ksmbd_file *fp = NULL; + struct file *filp = NULL; + struct kstat stat; + struct create_context *context; + struct lease_ctx_info *lc = NULL; + struct create_ea_buf_req *ea_buf = NULL; + struct oplock_info *opinfo; + __le32 *next_ptr = NULL; + int req_op_level = 0, open_flags = 0, file_info = 0; + int rc = 0, len = 0; + int contxt_cnt = 0, query_disk_id = 0; + int maximal_access_ctxt = 0, posix_ctxt = 0; + int s_type = 0; + int next_off = 0; + char *name = NULL; + char *stream_name = NULL; + bool file_present = false, created = false, already_permitted = false; + struct durable_info d_info; + int share_ret, need_truncate = 0; + u64 time; + umode_t posix_mode = 0; + __le32 daccess, maximal_access = 0; + + rsp_org = RESPONSE_BUF(work); + WORK_BUFFERS(work, req, rsp); + + if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && + (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { + ksmbd_debug(SMB, "invalid flag in chained command\n"); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + return -EINVAL; + } + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe create request\n"); + return create_smb2_pipe(work); + } + + if (req->NameLength) { + if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && + *(char *)req->Buffer == '\\') { + ksmbd_err("not allow directory name included leadning slash\n"); + rc = -EINVAL; + goto err_out1; + } + + name = smb2_get_name(share, + req->Buffer, + le16_to_cpu(req->NameLength), + work->conn->local_nls); + if (IS_ERR(name)) { + rc = PTR_ERR(name); + if (rc != -ENOMEM) + rc = -ENOENT; + goto err_out1; + } + + 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_out1; + } + rc = parse_stream_name(name, &stream_name, &s_type); + if (rc < 0) + goto err_out1; + } + + rc = ksmbd_validate_filename(name); + if (rc < 0) + goto err_out1; + + if (ksmbd_share_veto_filename(share, name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", + name); + goto err_out1; + } + } else { + len = strlen(share->path); + ksmbd_debug(SMB, "share path len %d\n", len); + name = kmalloc(len + 1, GFP_KERNEL); + if (!name) { + rsp->hdr.Status = STATUS_NO_MEMORY; + rc = -ENOMEM; + goto err_out1; + } + + memcpy(name, share->path, len); + *(name + len) = '\0'; + } + + req_op_level = req->RequestedOplockLevel; + memset(&d_info, 0, sizeof(struct durable_info)); + if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && + req->CreateContextsOffset) { + lc = parse_lease_state(req); + rc = parse_durable_handle_context(work, req, lc, &d_info); + if (rc) { + ksmbd_err("error parsing durable handle context\n"); + goto err_out1; + } + + if (d_info.reconnected) { + fp = d_info.fp; + rc = smb2_check_durable_oplock(d_info.fp, lc, name); + if (rc) + goto err_out1; + rc = ksmbd_reopen_durable_fd(work, d_info.fp); + if (rc) + goto err_out1; + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out1; + } + file_info = FILE_OPENED; + fp = d_info.fp; + goto reconnected; + } + } else { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) + lc = parse_lease_state(req); + } + + if (le32_to_cpu(req->ImpersonationLevel) > + le32_to_cpu(IL_DELEGATE_LE)) { + ksmbd_err("Invalid impersonationlevel : 0x%x\n", + le32_to_cpu(req->ImpersonationLevel)); + rc = -EIO; + rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; + goto err_out1; + } + + if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK)) { + ksmbd_err("Invalid create options : 0x%x\n", + le32_to_cpu(req->CreateOptions)); + rc = -EINVAL; + goto err_out1; + } else { + + if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && + req->CreateOptions & FILE_RANDOM_ACCESS_LE) + req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); + + if (req->CreateOptions & (FILE_OPEN_BY_FILE_ID_LE | + CREATE_TREE_CONNECTION | FILE_RESERVE_OPFILTER_LE)) { + rc = -EOPNOTSUPP; + goto err_out1; + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { + rc = -EINVAL; + goto err_out1; + } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) + req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); + } + } + + if (le32_to_cpu(req->CreateDisposition) > + le32_to_cpu(FILE_OVERWRITE_IF_LE)) { + ksmbd_err("Invalid create disposition : 0x%x\n", + le32_to_cpu(req->CreateDisposition)); + rc = -EINVAL; + goto err_out1; + } + + if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { + ksmbd_err("Invalid disired access : 0x%x\n", + le32_to_cpu(req->DesiredAccess)); + rc = -EACCES; + goto err_out1; + } + + if (req->FileAttributes && + !(req->FileAttributes & ATTR_MASK_LE)) { + ksmbd_err("Invalid file attribute : 0x%x\n", + le32_to_cpu(req->FileAttributes)); + rc = -EINVAL; + goto err_out1; + } + + if (req->CreateContextsOffset) { + /* Parse non-durable handle create contexts */ + context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER); + if (IS_ERR(context)) { + rc = check_context_err(context, SMB2_CREATE_EA_BUFFER); + if (rc < 0) + goto err_out1; + } else { + ea_buf = (struct create_ea_buf_req *)context; + if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + rc = -EACCES; + goto err_out1; + } + } + + context = smb2_find_context_vals(req, + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); + if (IS_ERR(context)) { + rc = check_context_err(context, + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); + if (rc < 0) + goto err_out1; + } else { + ksmbd_debug(SMB, + "get query maximal access context\n"); + maximal_access_ctxt = 1; + } + + context = smb2_find_context_vals(req, + SMB2_CREATE_TIMEWARP_REQUEST); + if (IS_ERR(context)) { + rc = check_context_err(context, + SMB2_CREATE_TIMEWARP_REQUEST); + if (rc < 0) + goto err_out1; + } else { + ksmbd_debug(SMB, "get timewarp context\n"); + rc = -EBADF; + goto err_out1; + } + + if (tcon->posix_extensions) { + context = smb2_find_context_vals(req, + SMB2_CREATE_TAG_POSIX); + if (IS_ERR(context)) { + rc = check_context_err(context, + SMB2_CREATE_TAG_POSIX); + if (rc < 0) + goto err_out1; + } else { + struct create_posix *posix = + (struct create_posix *)context; + ksmbd_debug(SMB, "get posix context\n"); + + posix_mode = le32_to_cpu(posix->Mode); + posix_ctxt = 1; + } + } + } + + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out1; + } + + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + /* + * On delete request, instead of following up, need to + * look the current entity + */ + rc = ksmbd_vfs_kern_path(name, 0, &path, 1); + if (!rc) { + /* + * If file exists with under flags, return access + * denied error. + */ + if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || + req->CreateDisposition == FILE_OPEN_IF_LE) { + rc = -EACCES; + path_put(&path); + goto err_out; + } + + if (!test_tree_conn_flag(tcon, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; + path_put(&path); + goto err_out; + } + } + } else { + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) { + /* + * Use LOOKUP_FOLLOW to follow the path of + * symlink in path buildup + */ + rc = ksmbd_vfs_kern_path(name, LOOKUP_FOLLOW, &path, 1); + if (rc) { /* Case for broken link ?*/ + rc = ksmbd_vfs_kern_path(name, 0, &path, 1); + } + } else { + rc = ksmbd_vfs_kern_path(name, 0, &path, 1); + if (!rc && d_is_symlink(path.dentry)) { + rc = -EACCES; + path_put(&path); + goto err_out; + } + } + } + + if (rc) { + if (rc == -EACCES) { + ksmbd_debug(SMB, + "User does not have right permission\n"); + goto err_out; + } + ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", + name, rc); + rc = 0; + } else { + file_present = true; + generic_fillattr(&init_user_ns, d_inode(path.dentry), &stat); + } + if (stream_name) { + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (s_type == DATA_STREAM) { + rc = -EIO; + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + } + } else { + if (S_ISDIR(stat.mode) && s_type == DATA_STREAM) { + rc = -EIO; + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + } + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE && + req->FileAttributes & ATTR_NORMAL_LE) { + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + rc = -EIO; + } + + if (rc < 0) + goto err_out; + } + + if (file_present && req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE + && S_ISDIR(stat.mode) && + !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + ksmbd_debug(SMB, "open() argument is a directory: %s, %x\n", + name, req->CreateOptions); + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + rc = -EIO; + goto err_out; + } + + if (file_present && (req->CreateOptions & FILE_DIRECTORY_FILE_LE) && + !(req->CreateDisposition == FILE_CREATE_LE) && + !S_ISDIR(stat.mode)) { + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + rc = -EIO; + goto err_out; + } + + if (!stream_name && file_present && + (req->CreateDisposition == FILE_CREATE_LE)) { + rc = -EEXIST; + goto err_out; + } + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && + file_present) + file_present = ksmbd_close_inode_fds(work, + d_inode(path.dentry)); + + daccess = smb_map_generic_desired_access(req->DesiredAccess); + + if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + rc = smb_check_perm_dacl(conn, path.dentry, &daccess, + sess->user->uid); + if (rc) + goto err_out; + } + + if (daccess & FILE_MAXIMAL_ACCESS_LE) { + if (!file_present) { + daccess = cpu_to_le32(GENERIC_ALL_FLAGS); + } else { + rc = ksmbd_vfs_query_maximal_access(path.dentry, + &daccess); + if (rc) + goto err_out; + already_permitted = true; + } + maximal_access = daccess; + } + + open_flags = smb2_create_open_flags(file_present, + daccess, req->CreateDisposition); + + if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (open_flags & O_CREAT) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; + goto err_out; + } + } + + /*create file if not present */ + if (!file_present) { + rc = smb2_creat(work, &path, name, open_flags, posix_mode, + req->CreateOptions & FILE_DIRECTORY_FILE_LE); + if (rc) + goto err_out; + + created = true; + if (ea_buf) { + rc = smb2_set_ea(&ea_buf->ea, &path); + if (rc == -EOPNOTSUPP) + rc = 0; + else if (rc) + goto err_out; + } + } else if (!already_permitted) { + bool may_delete; + + may_delete = daccess & FILE_DELETE_LE || + req->CreateOptions & FILE_DELETE_ON_CLOSE_LE; + + /* FILE_READ_ATTRIBUTE is allowed without inode_permission, + * because execute(search) permission on a parent directory, + * is already granted. + */ + if (daccess & ~(FILE_READ_ATTRIBUTES_LE | + FILE_READ_CONTROL_LE)) { + if (ksmbd_vfs_inode_permission(path.dentry, + open_flags & O_ACCMODE, may_delete)) { + rc = -EACCES; + goto err_out; + } + } + } + + rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); + if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { + rc = -EBUSY; + goto err_out; + } + + rc = 0; + filp = dentry_open(&path, open_flags, current_cred()); + if (IS_ERR(filp)) { + rc = PTR_ERR(filp); + ksmbd_err("dentry open for dir failed, rc %d\n", rc); + goto err_out; + } + + if (file_present) { + if (!(open_flags & O_TRUNC)) + file_info = FILE_OPENED; + else + file_info = FILE_OVERWRITTEN; + + if ((req->CreateDisposition & FILE_CREATE_MASK_LE) + == FILE_SUPERSEDE_LE) + file_info = FILE_SUPERSEDED; + } else if (open_flags & O_CREAT) + file_info = FILE_CREATED; + + ksmbd_vfs_set_fadvise(filp, req->CreateOptions); + + /* Obtain Volatile-ID */ + fp = ksmbd_open_fd(work, filp); + if (IS_ERR(fp)) { + fput(filp); + rc = PTR_ERR(fp); + fp = NULL; + goto err_out; + } + + /* Get Persistent-ID */ + ksmbd_open_durable_fd(fp); + if (!HAS_FILE_ID(fp->persistent_id)) { + rc = -ENOMEM; + goto err_out; + } + + fp->filename = name; + fp->cdoption = req->CreateDisposition; + fp->daccess = daccess; + fp->saccess = req->ShareAccess; + fp->coption = req->CreateOptions; + + /* Set default windows and posix acls if creating new file */ + if (created) { + int posix_acl_rc; + struct inode *inode = path.dentry->d_inode; + + posix_acl_rc = ksmbd_vfs_inherit_posix_acl(inode, path.dentry->d_parent->d_inode); + if (posix_acl_rc) + ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + rc = smb_inherit_dacl(conn, path.dentry, sess->user->uid, + sess->user->gid); + } + + if (rc) { + rc = smb2_create_sd_buffer(work, req, path.dentry); + if (rc) { + if (posix_acl_rc) + ksmbd_vfs_set_init_posix_acl(inode); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + struct smb_fattr fattr; + struct smb_ntsd *pntsd; + int pntsd_size, ace_num; + + fattr.cf_uid = inode->i_uid; + fattr.cf_gid = inode->i_gid; + fattr.cf_mode = inode->i_mode; + fattr.cf_dacls = NULL; + + fattr.cf_acls = ksmbd_vfs_get_acl(inode, ACL_TYPE_ACCESS); + ace_num = fattr.cf_acls->a_count; + if (S_ISDIR(inode->i_mode)) { + fattr.cf_dacls = + ksmbd_vfs_get_acl(inode, ACL_TYPE_DEFAULT); + ace_num += fattr.cf_dacls->a_count; + } + + pntsd = kmalloc(sizeof(struct smb_ntsd) + + sizeof(struct smb_sid)*3 + + sizeof(struct smb_acl) + + sizeof(struct smb_ace)*ace_num*2, + GFP_KERNEL); + if (!pntsd) + goto err_out; + + rc = build_sec_desc(pntsd, NULL, + OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO, + &pntsd_size, &fattr); + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + + rc = ksmbd_vfs_set_sd_xattr(conn, + path.dentry, pntsd, pntsd_size); + if (rc) + ksmbd_err("failed to store ntacl in xattr : %d\n", + rc); + } + } + } + rc = 0; + } + + if (stream_name) { + rc = smb2_set_stream_name_xattr(&path, + fp, + stream_name, + s_type); + if (rc) + goto err_out; + file_info = FILE_CREATED; + } + + fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | + FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); + if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC + && !fp->attrib_only && !stream_name) { + smb_break_all_oplock(work, fp); + need_truncate = 1; + } + + /* fp should be searchable through ksmbd_inode.m_fp_list + * after daccess, saccess, attrib_only, and stream are + * initialized. + */ + write_lock(&fp->f_ci->m_lock); + list_add(&fp->node, &fp->f_ci->m_fp_list); + write_unlock(&fp->f_ci->m_lock); + + rc = ksmbd_vfs_getattr(&path, &stat); + if (rc) { + generic_fillattr(&init_user_ns, d_inode(path.dentry), &stat); + rc = 0; + } + + /* Check delete pending among previous fp before oplock break */ + if (ksmbd_inode_pending_delete(fp)) { + rc = -EBUSY; + goto err_out; + } + + 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))) { + if (share_ret < 0 && !S_ISDIR(FP_INODE(fp)->i_mode)) { + rc = share_ret; + goto err_out; + } + } else { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { + req_op_level = smb2_map_lease_to_oplock(lc->req_state); + ksmbd_debug(SMB, + "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", + name, req_op_level, lc->req_state); + rc = find_same_lease_key(sess, fp->f_ci, lc); + if (rc) + goto err_out; + } else if (open_flags == O_RDONLY && + (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || + req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + req_op_level = SMB2_OPLOCK_LEVEL_II; + + rc = smb_grant_oplock(work, req_op_level, + fp->persistent_id, fp, + le32_to_cpu(req->hdr.Id.SyncId.TreeId), + lc, share_ret); + if (rc < 0) + goto err_out; + } + + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) + ksmbd_fd_set_delete_on_close(fp, file_info); + + if (need_truncate) { + rc = smb2_create_truncate(&path); + if (rc) + goto err_out; + } + + if (req->CreateContextsOffset) { + struct create_alloc_size_req *az_req; + + az_req = (struct create_alloc_size_req *) + smb2_find_context_vals(req, + SMB2_CREATE_ALLOCATION_SIZE); + if (IS_ERR(az_req)) { + rc = check_context_err(az_req, + SMB2_CREATE_ALLOCATION_SIZE); + if (rc < 0) + goto err_out; + } else { + loff_t alloc_size = le64_to_cpu(az_req->AllocationSize); + int err; + + ksmbd_debug(SMB, + "request smb2 create allocate size : %llu\n", + alloc_size); + err = ksmbd_vfs_alloc_size(work, fp, alloc_size); + if (err < 0) + ksmbd_debug(SMB, + "ksmbd_vfs_alloc_size is failed : %d\n", + err); + } + + context = smb2_find_context_vals(req, + SMB2_CREATE_QUERY_ON_DISK_ID); + if (IS_ERR(context)) { + rc = check_context_err(context, + SMB2_CREATE_QUERY_ON_DISK_ID); + if (rc < 0) + goto err_out; + } else { + ksmbd_debug(SMB, "get query on disk id context\n"); + query_disk_id = 1; + } + } + + if (stat.result_mask & STATX_BTIME) + fp->create_time = ksmbd_UnixTimeToNT(stat.btime); + else + fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + if (req->FileAttributes || fp->f_ci->m_fattr == 0) + fp->f_ci->m_fattr = cpu_to_le32(smb2_get_dos_mode(&stat, + le32_to_cpu(req->FileAttributes))); + + if (!created) + smb2_update_xattrs(tcon, &path, fp); + else + smb2_new_xattrs(tcon, &path, fp); + + memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + + if (d_info.type) { + if (d_info.type == DURABLE_REQ_V2 && + d_info.persistent) + fp->is_persistent = 1; + else + fp->is_durable = 1; + + if (d_info.type == DURABLE_REQ_V2) { + memcpy(fp->create_guid, d_info.CreateGuid, + SMB2_CREATE_GUID_SIZE); + if (d_info.timeout) + fp->durable_timeout = d_info.timeout; + else + fp->durable_timeout = 1600; + if (d_info.app_id) + memcpy(fp->app_instance_id, + d_info.app_id, 16); + } + } + +reconnected: + generic_fillattr(&init_user_ns, FP_INODE(fp), &stat); + + rsp->StructureSize = cpu_to_le16(89); + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + rsp->OplockLevel = opinfo != NULL ? opinfo->level : 0; + rcu_read_unlock(); + rsp->Reserved = 0; + rsp->CreateAction = cpu_to_le32(file_info); + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + rsp->ChangeTime = cpu_to_le64(time); + rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : + cpu_to_le64(stat.blocks << 9); + rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + rsp->FileAttributes = fp->f_ci->m_fattr; + + rsp->Reserved2 = 0; + + rsp->PersistentFileId = cpu_to_le64(fp->persistent_id); + rsp->VolatileFileId = cpu_to_le64(fp->volatile_id); + + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + inc_rfc1001_len(rsp_org, 88); /* StructureSize - 1*/ + + /* If lease is request send lease context response */ + if (opinfo && opinfo->is_lease) { + struct create_context *lease_ccontext; + + ksmbd_debug(SMB, "lease granted on(%s) lease state 0x%x\n", + name, opinfo->o_lease->state); + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_LEASE; + + lease_ccontext = (struct create_context *)rsp->Buffer; + contxt_cnt++; + create_lease_buf(rsp->Buffer, opinfo->o_lease); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_lease_size); + inc_rfc1001_len(rsp_org, conn->vals->create_lease_size); + next_ptr = &lease_ccontext->Next; + next_off = conn->vals->create_lease_size; + } + + if (d_info.type == DURABLE_REQ || d_info.type == DURABLE_REQ_V2) { + struct create_context *durable_ccontext; + + durable_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + if (d_info.type == DURABLE_REQ) { + create_durable_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_durable_size); + inc_rfc1001_len(rsp_org, + conn->vals->create_durable_size); + } else { + create_durable_v2_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + fp); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_durable_v2_size); + inc_rfc1001_len(rsp_org, + conn->vals->create_durable_v2_size); + } + + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &durable_ccontext->Next; + next_off = conn->vals->create_durable_size; + } + + if (maximal_access_ctxt) { + struct create_context *mxac_ccontext; + + if (maximal_access == 0) + ksmbd_vfs_query_maximal_access(path.dentry, + &maximal_access); + mxac_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + create_mxac_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + le32_to_cpu(maximal_access)); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_mxac_size); + inc_rfc1001_len(rsp_org, conn->vals->create_mxac_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &mxac_ccontext->Next; + next_off = conn->vals->create_mxac_size; + } + + if (query_disk_id) { + struct create_context *disk_id_ccontext; + + disk_id_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + create_disk_id_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + stat.ino, tcon->id); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_disk_id_size); + inc_rfc1001_len(rsp_org, conn->vals->create_disk_id_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &disk_id_ccontext->Next; + next_off = conn->vals->create_disk_id_size; + } + + if (posix_ctxt) { + struct create_context *posix_ccontext; + + posix_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + create_posix_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + fp); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_posix_size); + inc_rfc1001_len(rsp_org, conn->vals->create_posix_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + } + + if (contxt_cnt > 0) { + rsp->CreateContextsOffset = + cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer) + - 4); + } + +err_out: + if (file_present || created) + path_put(&path); + ksmbd_revert_fsids(work); +err_out1: + if (rc) { + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; + else if (rc == -EPERM) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (rc == -EBUSY) + rsp->hdr.Status = STATUS_DELETE_PENDING; + else if (rc == -EBADF) + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + else if (rc == -ENOEXEC) + rsp->hdr.Status = STATUS_DUPLICATE_OBJECTID; + else if (rc == -ENXIO) + rsp->hdr.Status = STATUS_NO_SUCH_DEVICE; + else if (rc == -EEXIST) + rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; + else if (rc == -EMFILE) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + if (!rsp->hdr.Status) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + + if (!fp || !fp->filename) + kfree(name); + if (fp) + ksmbd_fd_put(work, fp); + smb2_set_err_rsp(work); + ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); + } + + kfree(lc); + + return 0; +} + +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); + case FILE_BOTH_DIRECTORY_INFORMATION: + return sizeof(struct file_both_directory_info); + case FILE_DIRECTORY_INFORMATION: + return sizeof(struct 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); + case FILEID_BOTH_DIRECTORY_INFORMATION: + return sizeof(struct file_id_both_directory_info); + case SMB_FIND_FILE_POSIX_INFO: + return sizeof(struct smb2_posix_info); + default: + return -EOPNOTSUPP; + } +} + +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; + + ffdinfo = (struct 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); + return 0; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct 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); + return 0; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct 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); + return 0; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fninfo->NextEntryOffset); + d_info->name = fninfo->FileName; + d_info->name_len = le32_to_cpu(fninfo->FileNameLength); + return 0; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct 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); + return 0; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fibdinfo->NextEntryOffset); + d_info->name = fibdinfo->FileName; + d_info->name_len = le32_to_cpu(fibdinfo->FileNameLength); + return 0; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + + posix_info = (struct smb2_posix_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(posix_info->NextEntryOffset); + d_info->name = posix_info->name; + d_info->name_len = le32_to_cpu(posix_info->name_len); + return 0; + } + default: + return -EINVAL; + } +} + +/** + * smb2_populate_readdir_entry() - encode directory entry in smb2 response + * buffer + * @conn: connection instance + * @info_level: smb information level + * @d_info: structure included variables for query dir + * @ksmbd_kstat: ksmbd wrapper of dirent stat information + * + * if directory has many entries, find first can't read it fully. + * find next might be called multiple times to read remaining dir entries + * + * Return: 0 on success, otherwise error + */ +static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, + int info_level, + struct ksmbd_dir_info *d_info, + struct ksmbd_kstat *ksmbd_kstat) +{ + int next_entry_offset = 0; + char *conv_name; + int conv_len; + void *kstat; + int struct_sz; + + conv_name = ksmbd_convert_dir_info_name(d_info, + conn->local_nls, + &conv_len); + if (!conv_name) + return -ENOMEM; + + /* Somehow the name has only terminating NULL bytes */ + if (conv_len < 0) { + kfree(conv_name); + return -EINVAL; + } + + struct_sz = readdir_info_level_struct_sz(info_level); + next_entry_offset = ALIGN(struct_sz - 1 + conv_len, + KSMBD_DIR_INFO_ALIGNMENT); + + if (next_entry_offset > d_info->out_buf_len) { + d_info->out_buf_len = 0; + return -ENOSPC; + } + + kstat = d_info->wptr; + if (info_level != FILE_NAMES_INFORMATION) + kstat = ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)kstat; + ffdinfo->FileNameLength = cpu_to_le32(conv_len); + ffdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (ffdinfo->EaSize) + ffdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + if (d_info->hide_dot_file && d_info->name[0] == '.') + ffdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(ffdinfo->FileName, conv_name, conv_len); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)kstat; + fbdinfo->FileNameLength = cpu_to_le32(conv_len); + fbdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (fbdinfo->EaSize) + fbdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + fbdinfo->ShortNameLength = 0; + fbdinfo->Reserved = 0; + if (d_info->hide_dot_file && d_info->name[0] == '.') + fbdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(fbdinfo->FileName, conv_name, conv_len); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)kstat; + fdinfo->FileNameLength = cpu_to_le32(conv_len); + if (d_info->hide_dot_file && d_info->name[0] == '.') + fdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(fdinfo->FileName, conv_name, conv_len); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)kstat; + fninfo->FileNameLength = cpu_to_le32(conv_len); + memcpy(fninfo->FileName, conv_name, conv_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct 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 = ATTR_REPARSE_POINT_LE; + dinfo->Reserved = 0; + dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + if (d_info->hide_dot_file && d_info->name[0] == '.') + dinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(dinfo->FileName, conv_name, conv_len); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)kstat; + fibdinfo->FileNameLength = cpu_to_le32(conv_len); + fibdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (fibdinfo->EaSize) + fibdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + fibdinfo->ShortNameLength = 0; + fibdinfo->Reserved = 0; + fibdinfo->Reserved2 = cpu_to_le16(0); + if (d_info->hide_dot_file && d_info->name[0] == '.') + fibdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(fibdinfo->FileName, conv_name, conv_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + u64 time; + + posix_info = (struct smb2_posix_info *)kstat; + posix_info->Ignored = 0; + posix_info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); + posix_info->ChangeTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->atime); + posix_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->mtime); + posix_info->LastWriteTime = cpu_to_le64(time); + posix_info->EndOfFile = cpu_to_le64(ksmbd_kstat->kstat->size); + posix_info->AllocationSize = cpu_to_le64(ksmbd_kstat->kstat->blocks << 9); + 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); + posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); + posix_info->DosAttributes = + S_ISDIR(ksmbd_kstat->kstat->mode) ? ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; + if (d_info->hide_dot_file && d_info->name[0] == '.') + posix_info->DosAttributes |= ATTR_HIDDEN_LE; + id_to_sid(from_kuid(&init_user_ns, ksmbd_kstat->kstat->uid), + SIDNFS_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); + id_to_sid(from_kgid(&init_user_ns, ksmbd_kstat->kstat->gid), + SIDNFS_GROUP, (struct smb_sid *)&posix_info->SidBuffer[20]); + memcpy(posix_info->name, conv_name, conv_len); + posix_info->name_len = cpu_to_le32(conv_len); + posix_info->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + + } /* switch (info_level) */ + + d_info->last_entry_offset = d_info->data_count; + d_info->data_count += next_entry_offset; + d_info->wptr += next_entry_offset; + kfree(conv_name); + + ksmbd_debug(SMB, + "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", + info_level, d_info->out_buf_len, + next_entry_offset, d_info->data_count); + + return 0; +} + +struct smb2_query_dir_private { + struct ksmbd_work *work; + char *search_pattern; + struct ksmbd_file *dir_fp; + + struct ksmbd_dir_info *d_info; + 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 kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + int rc; + int i; + + for (i = 0; i < priv->d_info->num_entry; i++) { + struct dentry *dent; + + if (dentry_name(priv->d_info, priv->info_level)) + return -EINVAL; + + lock_dir(priv->dir_fp); + dent = lookup_one_len(priv->d_info->name, + priv->dir_fp->filp->f_path.dentry, + priv->d_info->name_len); + unlock_dir(priv->dir_fp); + + if (IS_ERR(dent)) { + ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n", + priv->d_info->name, + PTR_ERR(dent)); + continue; + } + if (unlikely(d_is_negative(dent))) { + dput(dent); + ksmbd_debug(SMB, "Negative dentry `%s'\n", + priv->d_info->name); + continue; + } + + ksmbd_kstat.kstat = &kstat; + if (priv->info_level != FILE_NAMES_INFORMATION) + ksmbd_vfs_fill_dentry_attrs(priv->work, + dent, + &ksmbd_kstat); + + rc = smb2_populate_readdir_entry(priv->work->conn, + priv->info_level, + priv->d_info, + &ksmbd_kstat); + dput(dent); + if (rc) + return rc; + } + return 0; +} + +static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, + int info_level) +{ + int struct_sz; + int conv_len; + int next_entry_offset; + + struct_sz = readdir_info_level_struct_sz(info_level); + if (struct_sz == -EOPNOTSUPP) + return -EOPNOTSUPP; + + conv_len = (d_info->name_len + 1) * 2; + next_entry_offset = ALIGN(struct_sz - 1 + conv_len, + KSMBD_DIR_INFO_ALIGNMENT); + + if (next_entry_offset > d_info->out_buf_len) { + d_info->out_buf_len = 0; + return -ENOSPC; + } + + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct 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); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct 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); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct 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); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)d_info->wptr; + memcpy(fninfo->FileName, d_info->name, d_info->name_len); + fninfo->FileName[d_info->name_len] = 0x00; + fninfo->FileNameLength = cpu_to_le32(d_info->name_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct 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); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)d_info->wptr; + memcpy(fibdinfo->FileName, d_info->name, d_info->name_len); + fibdinfo->FileName[d_info->name_len] = 0x00; + fibdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + + posix_info = (struct smb2_posix_info *)d_info->wptr; + memcpy(posix_info->name, d_info->name, d_info->name_len); + posix_info->name[d_info->name_len] = 0x00; + posix_info->name_len = cpu_to_le32(d_info->name_len); + posix_info->NextEntryOffset = + cpu_to_le32(next_entry_offset); + break; + } + } /* switch (info_level) */ + + d_info->num_entry++; + d_info->out_buf_len -= next_entry_offset; + d_info->wptr += next_entry_offset; + return 0; +} + +static int __query_dir(struct dir_context *ctx, + const char *name, + int namlen, + loff_t offset, + u64 ino, + unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + struct smb2_query_dir_private *priv; + struct ksmbd_dir_info *d_info; + int rc; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + priv = buf->private; + d_info = priv->d_info; + + /* dot and dotdot entries are already reserved */ + if (!strcmp(".", name) || !strcmp("..", name)) + return 0; + if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name)) + return 0; + if (!match_pattern(name, priv->search_pattern)) + return 0; + + d_info->name = name; + d_info->name_len = namlen; + rc = reserve_populate_dentry(d_info, priv->info_level); + if (rc) + return rc; + if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) { + d_info->out_buf_len = 0; + return 0; + } + return 0; +} + +static void restart_ctx(struct dir_context *ctx) +{ + ctx->pos = 0; +} + +static int verify_info_level(int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + case FILE_BOTH_DIRECTORY_INFORMATION: + case FILE_DIRECTORY_INFORMATION: + case FILE_NAMES_INFORMATION: + case FILEID_FULL_DIRECTORY_INFORMATION: + case FILEID_BOTH_DIRECTORY_INFORMATION: + case SMB_FIND_FILE_POSIX_INFO: + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +int smb2_query_dir(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_query_directory_req *req; + struct smb2_query_directory_rsp *rsp, *rsp_org; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct ksmbd_file *dir_fp = NULL; + struct ksmbd_dir_info d_info; + int rc = 0; + char *srch_ptr = NULL; + unsigned char srch_flag; + int buffer_sz; + struct smb2_query_dir_private query_dir_private = {NULL, }; + + rsp_org = RESPONSE_BUF(work); + WORK_BUFFERS(work, req, rsp); + + if (ksmbd_override_fsids(work)) { + rsp->hdr.Status = STATUS_NO_MEMORY; + smb2_set_err_rsp(work); + return -ENOMEM; + } + + rc = verify_info_level(req->FileInformationClass); + if (rc) { + rc = -EFAULT; + goto err_out2; + } + + dir_fp = ksmbd_lookup_fd_slow(work, + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (!dir_fp) { + rc = -EBADF; + goto err_out2; + } + + if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || + inode_permission(&init_user_ns, file_inode(dir_fp->filp), + MAY_READ | MAY_EXEC)) { + ksmbd_err("no right to enumerate directory (%s)\n", + FP_FILENAME(dir_fp)); + rc = -EACCES; + goto err_out2; + } + + if (!S_ISDIR(file_inode(dir_fp->filp)->i_mode)) { + ksmbd_err("can't do query dir for a file\n"); + rc = -EINVAL; + goto err_out2; + } + + srch_flag = req->Flags; + srch_ptr = smb_strndup_from_utf16(req->Buffer, + le16_to_cpu(req->FileNameLength), 1, + conn->local_nls); + if (IS_ERR(srch_ptr)) { + ksmbd_debug(SMB, "Search Pattern not found\n"); + rc = -EINVAL; + goto err_out2; + } else + ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr); + + ksmbd_debug(SMB, "Directory name is %s\n", dir_fp->filename); + + if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) { + ksmbd_debug(SMB, "Restart directory scan\n"); + generic_file_llseek(dir_fp->filp, 0, SEEK_SET); + restart_ctx(&dir_fp->readdir_data.ctx); + } + + memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); + d_info.wptr = (char *)rsp->Buffer; + d_info.rptr = (char *)rsp->Buffer; + d_info.out_buf_len = (work->response_sz - + (get_rfc1002_len(rsp_org) + 4)); + d_info.out_buf_len = min_t(int, d_info.out_buf_len, + le32_to_cpu(req->OutputBufferLength)) - + sizeof(struct smb2_query_directory_rsp); + d_info.flags = srch_flag; + + /* + * reserve dot and dotdot entries in head of buffer + * in first response + */ + rc = ksmbd_populate_dot_dotdot_entries(work, req->FileInformationClass, + dir_fp, &d_info, srch_ptr, smb2_populate_readdir_entry); + if (rc == -ENOSPC) + rc = 0; + else if (rc) + goto err_out; + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES)) + d_info.hide_dot_file = true; + + buffer_sz = d_info.out_buf_len; + d_info.rptr = d_info.wptr; + query_dir_private.work = work; + query_dir_private.search_pattern = srch_ptr; + query_dir_private.dir_fp = dir_fp; + query_dir_private.d_info = &d_info; + 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); + + rc = ksmbd_vfs_readdir(dir_fp->filp, &dir_fp->readdir_data); + if (rc == 0) + restart_ctx(&dir_fp->readdir_data.ctx); + if (rc == -ENOSPC) + rc = 0; + if (rc) + goto err_out; + + d_info.wptr = d_info.rptr; + d_info.out_buf_len = buffer_sz; + rc = process_query_dir_entries(&query_dir_private); + if (rc) + goto err_out; + + if (!d_info.data_count && d_info.out_buf_len >= 0) { + if (srch_flag & SMB2_RETURN_SINGLE_ENTRY && !is_asterisk(srch_ptr)) + rsp->hdr.Status = STATUS_NO_SUCH_FILE; + else { + dir_fp->dot_dotdot[0] = dir_fp->dot_dotdot[1] = 0; + rsp->hdr.Status = STATUS_NO_MORE_FILES; + } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(0); + rsp->OutputBufferLength = cpu_to_le32(0); + rsp->Buffer[0] = 0; + inc_rfc1001_len(rsp_org, 9); + } else { + ((struct file_directory_info *) + ((char *)rsp->Buffer + d_info.last_entry_offset)) + ->NextEntryOffset = 0; + + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); + inc_rfc1001_len(rsp_org, 8 + d_info.data_count); + } + + kfree(srch_ptr); + ksmbd_fd_put(work, dir_fp); + ksmbd_revert_fsids(work); + return 0; + +err_out: + ksmbd_err("error while processing smb2 query dir rc = %d\n", rc); + kfree(srch_ptr); + +err_out2: + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_NO_SUCH_FILE; + else if (rc == -EBADF) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -ENOMEM) + rsp->hdr.Status = STATUS_NO_MEMORY; + else if (rc == -EFAULT) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + if (!rsp->hdr.Status) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, dir_fp); + ksmbd_revert_fsids(work); + return 0; +} + +/** + * buffer_check_err() - helper function to check buffer errors + * @reqOutputBufferLength: max buffer length expected in command response + * @rsp: query info response buffer contains output buffer length + * @infoclass_size: query info class response buffer size + * + * Return: 0 on success, otherwise error + */ +static int buffer_check_err(int reqOutputBufferLength, + struct smb2_query_info_rsp *rsp, int infoclass_size) +{ + if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { + if (reqOutputBufferLength < infoclass_size) { + ksmbd_err("Invalid Buffer Size Requested\n"); + rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; + rsp->hdr.smb2_buf_length = cpu_to_be32( + sizeof(struct smb2_hdr) - 4); + return -EINVAL; + } + + ksmbd_debug(SMB, "Buffer Overflow\n"); + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + rsp->hdr.smb2_buf_length = cpu_to_be32( + sizeof(struct smb2_hdr) - 4 + + reqOutputBufferLength); + rsp->OutputBufferLength = cpu_to_le32( + reqOutputBufferLength); + } + return 0; +} + +static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp) +{ + struct smb2_file_standard_info *sinfo; + + sinfo = (struct smb2_file_standard_info *)rsp->Buffer; + + sinfo->AllocationSize = cpu_to_le64(4096); + sinfo->EndOfFile = cpu_to_le64(0); + sinfo->NumberOfLinks = cpu_to_le32(1); + sinfo->DeletePending = 1; + sinfo->Directory = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); + inc_rfc1001_len(rsp, sizeof(struct smb2_file_standard_info)); +} + +static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, + uint64_t num) +{ + struct smb2_file_internal_info *file_info; + + file_info = (struct smb2_file_internal_info *)rsp->Buffer; + + /* any unique number */ + file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp, sizeof(struct smb2_file_internal_info)); +} + +/** + * smb2_info_file_pipe() - handler for smb2 query info on IPC pipe + * @work: smb work containing query info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb2_get_info_file_pipe(struct ksmbd_session *sess, + struct smb2_query_info_req *req, struct smb2_query_info_rsp *rsp) +{ + uint64_t id; + int rc; + + /* + * Windows can sometime send query file info request on + * pipe without opening it, checking error condition here + */ + id = le64_to_cpu(req->VolatileFileId); + if (!ksmbd_session_rpc_method(sess, id)) + return -ENOENT; + + ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", + req->FileInfoClass, le64_to_cpu(req->VolatileFileId)); + + switch (req->FileInfoClass) { + case FILE_STANDARD_INFORMATION: + get_standard_info_pipe(rsp); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, FILE_STANDARD_INFORMATION_SIZE); + break; + case FILE_INTERNAL_INFORMATION: + get_internal_info_pipe(rsp, id); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, FILE_INTERNAL_INFORMATION_SIZE); + break; + default: + ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", + req->FileInfoClass); + rc = -EOPNOTSUPP; + } + return rc; +} + +/** + * smb2_get_ea() - handler for smb2 get extended attribute command + * @work: smb work containing query info command buffer + * @path: path of file/dir to query info command + * @rq: get extended attribute request + * @resp: response buffer pointer + * @resp_org: base response buffer pointer in case of chained response + * + * Return: 0 on success, otherwise error + */ +static int smb2_get_ea(struct ksmbd_work *work, + struct ksmbd_file *fp, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct smb2_ea_info *eainfo, *prev_eainfo; + char *name, *ptr, *xattr_list = NULL, *buf; + int rc, name_len, value_len, xattr_list_len, idx; + ssize_t buf_free_len, alignment_bytes, next_offset, rsp_data_cnt = 0; + struct smb2_ea_info_req *ea_req = NULL; + struct path *path; + + if (!(fp->daccess & FILE_READ_EA_LE)) { + ksmbd_err("Not permitted to read ext attr : 0x%x\n", + fp->daccess); + return -EACCES; + } + + path = &fp->filp->f_path; + /* single EA entry is requested with given user.* name */ + if (req->InputBufferLength) + ea_req = (struct smb2_ea_info_req *)req->Buffer; + else { + /* need to send all EAs, if no specific EA is requested*/ + if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) + ksmbd_debug(SMB, + "All EAs are requested but need to send single EA entry in rsp flags 0x%x\n", + le32_to_cpu(req->Flags)); + } + + buf_free_len = work->response_sz - + (get_rfc1002_len(rsp_org) + 4) - + sizeof(struct smb2_query_info_rsp); + + if (le32_to_cpu(req->OutputBufferLength) < buf_free_len) + buf_free_len = le32_to_cpu(req->OutputBufferLength); + + rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (rc < 0) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + goto out; + } else if (!rc) { /* there is no EA in the file */ + ksmbd_debug(SMB, "no ea data in the file\n"); + goto done; + } + xattr_list_len = rc; + + ptr = (char *)rsp->Buffer; + eainfo = (struct smb2_ea_info *)ptr; + prev_eainfo = eainfo; + idx = 0; + + while (idx < xattr_list_len) { + name = xattr_list + idx; + name_len = strlen(name); + + ksmbd_debug(SMB, "%s, len %d\n", name, name_len); + idx += name_len + 1; + + /* + * CIFS does not support EA other than user.* namespace, + * still keep the framework generic, to list other attrs + * in future. + */ + if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + continue; + + if (!strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, + STREAM_PREFIX_LEN)) + continue; + + if (req->InputBufferLength && + (strncmp(&name[XATTR_USER_PREFIX_LEN], + ea_req->name, ea_req->EaNameLength))) + continue; + + if (!strncmp(&name[XATTR_USER_PREFIX_LEN], + DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN)) + continue; + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + name_len -= XATTR_USER_PREFIX_LEN; + + ptr = (char *)(&eainfo->name + name_len + 1); + buf_free_len -= (offsetof(struct smb2_ea_info, name) + + name_len + 1); + /* bailout if xattr can't fit in buf_free_len */ + value_len = ksmbd_vfs_getxattr(path->dentry, name, &buf); + if (value_len <= 0) { + rc = -ENOENT; + rsp->hdr.Status = STATUS_INVALID_HANDLE; + goto out; + } + + buf_free_len -= value_len; + if (buf_free_len < 0) { + ksmbd_free(buf); + break; + } + + memcpy(ptr, buf, value_len); + ksmbd_free(buf); + + ptr += value_len; + eainfo->Flags = 0; + eainfo->EaNameLength = name_len; + + if (!strncmp(name, XATTR_USER_PREFIX, + XATTR_USER_PREFIX_LEN)) + memcpy(eainfo->name, &name[XATTR_USER_PREFIX_LEN], + name_len); + else + memcpy(eainfo->name, name, name_len); + + eainfo->name[name_len] = '\0'; + eainfo->EaValueLength = cpu_to_le16(value_len); + next_offset = offsetof(struct smb2_ea_info, name) + + name_len + 1 + value_len; + + /* align next xattr entry at 4 byte bundary */ + alignment_bytes = ((next_offset + 3) & ~3) - next_offset; + if (alignment_bytes) { + memset(ptr, '\0', alignment_bytes); + ptr += alignment_bytes; + next_offset += alignment_bytes; + buf_free_len -= alignment_bytes; + } + eainfo->NextEntryOffset = cpu_to_le32(next_offset); + prev_eainfo = eainfo; + eainfo = (struct smb2_ea_info *)ptr; + rsp_data_cnt += next_offset; + + if (req->InputBufferLength) { + ksmbd_debug(SMB, "single entry requested\n"); + break; + } + } + + /* no more ea entries */ + prev_eainfo->NextEntryOffset = 0; +done: + rc = 0; + if (rsp_data_cnt == 0) + rsp->hdr.Status = STATUS_NO_EAS_ON_FILE; + rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); + inc_rfc1001_len(rsp_org, rsp_data_cnt); +out: + ksmbd_vfs_xattr_free(xattr_list); + return rc; +} + +static void get_file_access_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct smb2_file_access_info *file_info; + + file_info = (struct smb2_file_access_info *)rsp->Buffer; + file_info->AccessFlags = fp->daccess; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_access_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_access_info)); +} + +static int get_file_basic_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct smb2_file_all_info *basic_info; + struct kstat stat; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + ksmbd_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + basic_info = (struct smb2_file_all_info *)rsp->Buffer; + generic_fillattr(&init_user_ns, FP_INODE(fp), &stat); + basic_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + basic_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + basic_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + basic_info->ChangeTime = cpu_to_le64(time); + basic_info->Attributes = fp->f_ci->m_fattr; + basic_info->Pad1 = 0; + rsp->OutputBufferLength = + cpu_to_le32(offsetof(struct smb2_file_all_info, + AllocationSize)); + inc_rfc1001_len(rsp_org, offsetof(struct smb2_file_all_info, + AllocationSize)); + return 0; +} + +static unsigned long long get_allocation_size(struct inode *inode, + struct kstat *stat) +{ + unsigned long long alloc_size = 0; + + if (!S_ISDIR(stat->mode)) { + if ((inode->i_blocks << 9) <= stat->size) + alloc_size = stat->size; + else + alloc_size = inode->i_blocks << 9; + + } + + return alloc_size; +} + +static void get_file_standard_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct smb2_file_standard_info *sinfo; + unsigned int delete_pending; + struct inode *inode; + struct kstat stat; + + inode = FP_INODE(fp); + generic_fillattr(&init_user_ns, inode, &stat); + + sinfo = (struct smb2_file_standard_info *)rsp->Buffer; + delete_pending = ksmbd_inode_pending_delete(fp); + + sinfo->AllocationSize = cpu_to_le64(get_allocation_size(inode, &stat)); + sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); + sinfo->DeletePending = delete_pending; + sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb2_file_standard_info)); +} + +static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct smb2_file_alignment_info *file_info; + + file_info = (struct smb2_file_alignment_info *)rsp->Buffer; + file_info->AlignmentRequirement = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alignment_info)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb2_file_alignment_info)); +} + +static int get_file_all_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_all_info *file_info; + unsigned int delete_pending; + struct inode *inode; + struct kstat stat; + int conv_len; + char *filename; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + filename = convert_to_nt_pathname(fp->filename, + work->tcon->share_conf->path); + if (!filename) + return -ENOMEM; + + inode = FP_INODE(fp); + generic_fillattr(&init_user_ns, inode, &stat); + + ksmbd_debug(SMB, "filename = %s\n", filename); + delete_pending = ksmbd_inode_pending_delete(fp); + file_info = (struct smb2_file_all_info *)rsp->Buffer; + + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + 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(get_allocation_size(inode, &stat)); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + file_info->NumberOfLinks = + cpu_to_le32(get_nlink(&stat) - delete_pending); + file_info->DeletePending = delete_pending; + file_info->Directory = S_ISDIR(stat.mode) ? 1 : 0; + file_info->Pad2 = 0; + 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); + file_info->Mode = fp->coption; + file_info->AlignmentRequirement = 0; + conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, + filename, + PATH_MAX, + conn->local_nls, + 0); + conv_len *= 2; + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1); + kfree(filename); + inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); + return 0; +} + +static void get_file_alternate_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_alt_name_info *file_info; + int conv_len; + char *filename; + + filename = (char *)FP_FILENAME(fp); + file_info = (struct smb2_file_alt_name_info *)rsp->Buffer; + conv_len = ksmbd_extract_shortname(conn, + filename, + file_info->FileName); + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); + inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); +} + +static void get_file_stream_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_stream_info *file_info; + char *stream_name, *xattr_list = NULL, *stream_buf; + struct kstat stat; + struct path *path = &fp->filp->f_path; + ssize_t xattr_list_len; + int nbytes = 0, streamlen, stream_name_len, next, idx = 0; + + generic_fillattr(&init_user_ns, FP_INODE(fp), &stat); + file_info = (struct smb2_file_stream_info *)rsp->Buffer; + + xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + while (idx < xattr_list_len) { + stream_name = xattr_list + idx; + streamlen = strlen(stream_name); + idx += streamlen + 1; + + ksmbd_debug(SMB, "%s, len %d\n", stream_name, streamlen); + + if (strncmp(&stream_name[XATTR_USER_PREFIX_LEN], + STREAM_PREFIX, STREAM_PREFIX_LEN)) + continue; + + stream_name_len = streamlen - (XATTR_USER_PREFIX_LEN + + STREAM_PREFIX_LEN); + streamlen = stream_name_len; + + /* plus : size */ + streamlen += 1; + stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); + if (!stream_buf) + break; + + streamlen = snprintf(stream_buf, streamlen + 1, + ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); + + file_info = (struct smb2_file_stream_info *) + &rsp->Buffer[nbytes]; + streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, + stream_buf, + streamlen, + conn->local_nls, + 0); + streamlen *= 2; + kfree(stream_buf); + file_info->StreamNameLength = cpu_to_le32(streamlen); + file_info->StreamSize = cpu_to_le64(stream_name_len); + file_info->StreamAllocationSize = cpu_to_le64(stream_name_len); + + next = sizeof(struct smb2_file_stream_info) + streamlen; + nbytes += next; + file_info->NextEntryOffset = cpu_to_le32(next); + } + + if (nbytes) { + file_info = (struct smb2_file_stream_info *) + &rsp->Buffer[nbytes]; + streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, + "::$DATA", 7, conn->local_nls, 0); + streamlen *= 2; + file_info->StreamNameLength = cpu_to_le32(streamlen); + file_info->StreamSize = S_ISDIR(stat.mode) ? 0 : + cpu_to_le64(stat.size); + file_info->StreamAllocationSize = S_ISDIR(stat.mode) ? 0 : + cpu_to_le64(stat.size); + nbytes += sizeof(struct smb2_file_stream_info) + streamlen; + } + + /* last entry offset should be 0 */ + file_info->NextEntryOffset = 0; +out: + ksmbd_vfs_xattr_free(xattr_list); + + rsp->OutputBufferLength = cpu_to_le32(nbytes); + inc_rfc1001_len(rsp_org, nbytes); +} + +static void get_file_internal_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct smb2_file_internal_info *file_info; + struct kstat stat; + + generic_fillattr(&init_user_ns, FP_INODE(fp), &stat); + file_info = (struct smb2_file_internal_info *)rsp->Buffer; + file_info->IndexNumber = cpu_to_le64(stat.ino); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); +} + +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 inode *inode; + struct kstat stat; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + ksmbd_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; + + inode = FP_INODE(fp); + generic_fillattr(&init_user_ns, inode, &stat); + + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + file_info->LastWriteTime = cpu_to_le64(time); + 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(get_allocation_size(inode, &stat)); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + file_info->Reserved = cpu_to_le32(0); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ntwrk_info)); + return 0; +} + +static void get_file_ea_info(struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct smb2_file_ea_info *file_info; + + file_info = (struct smb2_file_ea_info *)rsp->Buffer; + file_info->EASize = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ea_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ea_info)); +} + +static void get_file_position_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + 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); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_pos_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_pos_info)); +} + +static void get_file_mode_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct smb2_file_mode_info *file_info; + + file_info = (struct smb2_file_mode_info *)rsp->Buffer; + file_info->Mode = fp->coption & FILE_MODE_INFO_MASK; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_mode_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_mode_info)); +} + +static void get_file_compression_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct smb2_file_comp_info *file_info; + struct kstat stat; + + generic_fillattr(&init_user_ns, FP_INODE(fp), &stat); + + file_info = (struct smb2_file_comp_info *)rsp->Buffer; + file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9); + file_info->CompressionFormat = COMPRESSION_FORMAT_NONE; + file_info->CompressionUnitShift = 0; + file_info->ChunkShift = 0; + file_info->ClusterShift = 0; + memset(&file_info->Reserved[0], 0, 3); + + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_comp_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_comp_info)); +} + +static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct smb2_file_attr_tag_info *file_info; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + ksmbd_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + file_info = (struct smb2_file_attr_tag_info *)rsp->Buffer; + file_info->FileAttributes = fp->f_ci->m_fattr; + file_info->ReparseTag = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb2_file_attr_tag_info)); + return 0; +} + +static int find_file_posix_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct smb311_posix_qinfo *file_info; + struct inode *inode = FP_INODE(fp); + u64 time; + + file_info = (struct smb311_posix_qinfo *)rsp->Buffer; + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(inode->i_atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->DosAttributes = fp->f_ci->m_fattr; + file_info->Inode = cpu_to_le64(inode->i_ino); + file_info->EndOfFile = cpu_to_le64(inode->i_size); + file_info->AllocationSize = cpu_to_le64(inode->i_blocks << 9); + file_info->HardLinks = cpu_to_le32(inode->i_nlink); + file_info->Mode = cpu_to_le32(inode->i_mode); + file_info->DeviceId = cpu_to_le32(inode->i_rdev); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb311_posix_qinfo)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb311_posix_qinfo)); + return 0; +} + +/** + * smb2_get_info_file() - handler for smb2 query info command + * @work: smb work containing query info request buffer + * + * Return: 0 on success, otherwise error + */ +static int smb2_get_info_file(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct ksmbd_file *fp; + int fileinfoclass = 0; + int rc = 0; + int file_infoclass_size; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + /* smb2 info file called for pipe */ + return smb2_get_info_file_pipe(work->sess, req, rsp); + } + + if (work->next_smb2_rcv_hdr_off) { + if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %u\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } + + if (!HAS_FILE_ID(id)) { + id = le64_to_cpu(req->VolatileFileId); + pid = le64_to_cpu(req->PersistentFileId); + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) + return -ENOENT; + + fileinfoclass = req->FileInfoClass; + + switch (fileinfoclass) { + case FILE_ACCESS_INFORMATION: + get_file_access_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE; + break; + + case FILE_BASIC_INFORMATION: + rc = get_file_basic_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_BASIC_INFORMATION_SIZE; + break; + + case FILE_STANDARD_INFORMATION: + get_file_standard_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE; + break; + + case FILE_ALIGNMENT_INFORMATION: + get_file_alignment_info(rsp, rsp_org); + file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE; + break; + + case FILE_ALL_INFORMATION: + rc = get_file_all_info(work, rsp, fp, rsp_org); + file_infoclass_size = FILE_ALL_INFORMATION_SIZE; + break; + + case FILE_ALTERNATE_NAME_INFORMATION: + get_file_alternate_info(work, rsp, fp, rsp_org); + file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE; + break; + + case FILE_STREAM_INFORMATION: + get_file_stream_info(work, rsp, fp, rsp_org); + file_infoclass_size = FILE_STREAM_INFORMATION_SIZE; + break; + + case FILE_INTERNAL_INFORMATION: + get_file_internal_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE; + break; + + case FILE_NETWORK_OPEN_INFORMATION: + rc = get_file_network_open_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE; + break; + + case FILE_EA_INFORMATION: + get_file_ea_info(rsp, rsp_org); + file_infoclass_size = FILE_EA_INFORMATION_SIZE; + break; + + case FILE_FULL_EA_INFORMATION: + rc = smb2_get_ea(work, fp, req, rsp, rsp_org); + file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE; + break; + + case FILE_POSITION_INFORMATION: + get_file_position_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_POSITION_INFORMATION_SIZE; + break; + + case FILE_MODE_INFORMATION: + get_file_mode_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_MODE_INFORMATION_SIZE; + break; + + case FILE_COMPRESSION_INFORMATION: + get_file_compression_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE; + break; + + case FILE_ATTRIBUTE_TAG_INFORMATION: + rc = get_file_attribute_tag_info(rsp, fp, rsp_org); + file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE; + break; + case SMB_FIND_FILE_POSIX_INFO: + if (!work->tcon->posix_extensions) { + ksmbd_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { + rc = find_file_posix_info(rsp, fp, rsp_org); + file_infoclass_size = sizeof(struct smb311_posix_qinfo); + } + break; + default: + ksmbd_debug(SMB, "fileinfoclass %d not supported yet\n", + fileinfoclass); + rc = -EOPNOTSUPP; + } + if (!rc) + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, + file_infoclass_size); + ksmbd_fd_put(work, fp); + return rc; +} + +/** + * smb2_get_info_filesystem() - handler for smb2 query info command + * @work: smb work containing query info request buffer + * + * Return: 0 on success, otherwise error + * TODO: need to implement STATUS_INFO_LENGTH_MISMATCH error handling + */ +static int smb2_get_info_filesystem(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct ksmbd_session *sess = work->sess; + struct ksmbd_conn *conn = sess->conn; + struct ksmbd_share_config *share = work->tcon->share_conf; + int fsinfoclass = 0; + struct kstatfs stfs; + struct path path; + int rc = 0, len; + int fs_infoclass_size = 0; + + rc = ksmbd_vfs_kern_path(share->path, LOOKUP_FOLLOW, &path, 0); + if (rc) { + ksmbd_err("cannot create vfs path\n"); + return -EIO; + } + + rc = vfs_statfs(&path, &stfs); + if (rc) { + ksmbd_err("cannot do stat of path %s\n", share->path); + path_put(&path); + return -EIO; + } + + fsinfoclass = req->FileInfoClass; + + switch (fsinfoclass) { + case FS_DEVICE_INFORMATION: + { + struct filesystem_device_info *info; + + info = (struct filesystem_device_info *)rsp->Buffer; + + info->DeviceType = cpu_to_le32(stfs.f_type); + info->DeviceCharacteristics = cpu_to_le32(0x00000020); + rsp->OutputBufferLength = cpu_to_le32(8); + inc_rfc1001_len(rsp_org, 8); + fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; + break; + } + case FS_ATTRIBUTE_INFORMATION: + { + struct filesystem_attribute_info *info; + size_t sz; + + info = (struct filesystem_attribute_info *)rsp->Buffer; + info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS | + FILE_PERSISTENT_ACLS | + FILE_UNICODE_ON_DISK | + FILE_CASE_PRESERVED_NAMES | + FILE_CASE_SENSITIVE_SEARCH); + + info->Attributes |= cpu_to_le32(server_conf.share_fake_fscaps); + + info->MaxPathNameComponentLength = cpu_to_le32(stfs.f_namelen); + len = smbConvertToUTF16((__le16 *)info->FileSystemName, + "NTFS", PATH_MAX, conn->local_nls, 0); + len = len * 2; + info->FileSystemNameLen = cpu_to_le32(len); + sz = sizeof(struct filesystem_attribute_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); + inc_rfc1001_len(rsp_org, sz); + fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; + break; + } + case FS_VOLUME_INFORMATION: + { + struct filesystem_vol_info *info; + size_t sz; + + info = (struct filesystem_vol_info *)(rsp->Buffer); + info->VolumeCreationTime = 0; + /* Taking dummy value of serial number*/ + info->SerialNumber = cpu_to_le32(0xbc3ac512); + len = smbConvertToUTF16((__le16 *)info->VolumeLabel, + share->name, PATH_MAX, + conn->local_nls, 0); + len = len * 2; + info->VolumeLabelSize = cpu_to_le32(len); + info->Reserved = 0; + sz = sizeof(struct filesystem_vol_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); + inc_rfc1001_len(rsp_org, sz); + fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; + break; + } + case FS_SIZE_INFORMATION: + { + struct filesystem_info *info; + unsigned short logical_sector_size; + + info = (struct filesystem_info *)(rsp->Buffer); + logical_sector_size = + ksmbd_vfs_logical_sector_size(d_inode(path.dentry)); + + info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); + info->SectorsPerAllocationUnit = cpu_to_le32(stfs.f_bsize >> 9); + info->BytesPerSector = cpu_to_le32(logical_sector_size); + rsp->OutputBufferLength = cpu_to_le32(24); + inc_rfc1001_len(rsp_org, 24); + fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; + break; + } + case FS_FULL_SIZE_INFORMATION: + { + struct smb2_fs_full_size_info *info; + unsigned short logical_sector_size; + + info = (struct smb2_fs_full_size_info *)(rsp->Buffer); + logical_sector_size = + ksmbd_vfs_logical_sector_size(d_inode(path.dentry)); + + info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + info->CallerAvailableAllocationUnits = + cpu_to_le64(stfs.f_bavail); + info->ActualAvailableAllocationUnits = + cpu_to_le64(stfs.f_bfree); + info->SectorsPerAllocationUnit = cpu_to_le32(stfs.f_bsize >> 9); + info->BytesPerSector = cpu_to_le32(logical_sector_size); + rsp->OutputBufferLength = cpu_to_le32(32); + inc_rfc1001_len(rsp_org, 32); + fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; + break; + } + case FS_OBJECT_ID_INFORMATION: + { + struct object_id_info *info; + + info = (struct object_id_info *)(rsp->Buffer); + + if (!user_guest(sess->user)) + memcpy(info->objid, user_passkey(sess->user), 16); + else + memset(info->objid, 0, 16); + + info->extended_info.magic = cpu_to_le32(EXTENDED_INFO_MAGIC); + info->extended_info.version = cpu_to_le32(1); + info->extended_info.release = cpu_to_le32(1); + info->extended_info.rel_date = 0; + memcpy(info->extended_info.version_string, + "1.1.0", + strlen("1.1.0")); + rsp->OutputBufferLength = cpu_to_le32(64); + inc_rfc1001_len(rsp_org, 64); + fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; + break; + } + case FS_SECTOR_SIZE_INFORMATION: + { + struct smb3_fs_ss_info *info; + struct ksmbd_fs_sector_size fs_ss; + + info = (struct smb3_fs_ss_info *)(rsp->Buffer); + ksmbd_vfs_smb2_sector_size(d_inode(path.dentry), &fs_ss); + + info->LogicalBytesPerSector = + cpu_to_le32(fs_ss.logical_sector_size); + info->PhysicalBytesPerSectorForAtomicity = + cpu_to_le32(fs_ss.physical_sector_size); + info->PhysicalBytesPerSectorForPerf = + cpu_to_le32(fs_ss.optimal_io_size); + info->FSEffPhysicalBytesPerSectorForAtomicity = + cpu_to_le32(fs_ss.optimal_io_size); + info->Flags = cpu_to_le32(SSINFO_FLAGS_ALIGNED_DEVICE | + SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE); + info->ByteOffsetForSectorAlignment = 0; + info->ByteOffsetForPartitionAlignment = 0; + rsp->OutputBufferLength = cpu_to_le32(28); + inc_rfc1001_len(rsp_org, 28); + fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; + break; + } + case FS_CONTROL_INFORMATION: + { + /* + * TODO : The current implementation is based on + * test result with win7(NTFS) server. It's need to + * modify this to get valid Quota values + * from Linux kernel + */ + struct smb2_fs_control_info *info; + + info = (struct smb2_fs_control_info *)(rsp->Buffer); + info->FreeSpaceStartFiltering = 0; + info->FreeSpaceThreshold = 0; + info->FreeSpaceStopFiltering = 0; + info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID); + info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); + info->Padding = 0; + rsp->OutputBufferLength = cpu_to_le32(48); + inc_rfc1001_len(rsp_org, 48); + fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; + break; + } + case FS_POSIX_INFORMATION: + { + struct filesystem_posix_info *info; + unsigned short logical_sector_size; + + if (!work->tcon->posix_extensions) { + ksmbd_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { + info = (struct filesystem_posix_info *)(rsp->Buffer); + logical_sector_size = + ksmbd_vfs_logical_sector_size(d_inode(path.dentry)); + info->OptimalTransferSize = cpu_to_le32(logical_sector_size); + info->BlockSize = cpu_to_le32(stfs.f_bsize); + info->TotalBlocks = cpu_to_le64(stfs.f_blocks); + info->BlocksAvail = cpu_to_le64(stfs.f_bfree); + info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail); + info->TotalFileNodes = cpu_to_le64(stfs.f_files); + info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); + rsp->OutputBufferLength = cpu_to_le32(56); + inc_rfc1001_len(rsp_org, 56); + fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; + } + break; + } + default: + path_put(&path); + return -EOPNOTSUPP; + } + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, + fs_infoclass_size); + path_put(&path); + return rc; +} + +static int smb2_get_info_sec(struct ksmbd_work *work, + struct smb2_query_info_req *req, struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct ksmbd_file *fp; + struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; + struct smb_fattr fattr = {{0}}; + struct inode *inode; + __u32 secdesclen; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + int addition_info = le32_to_cpu(req->AdditionalInformation); + int rc; + + if (work->next_smb2_rcv_hdr_off) { + if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %u\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } + + if (!HAS_FILE_ID(id)) { + id = le64_to_cpu(req->VolatileFileId); + pid = le64_to_cpu(req->PersistentFileId); + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) + return -ENOENT; + + inode = FP_INODE(fp); + fattr.cf_uid = inode->i_uid; + fattr.cf_gid = inode->i_gid; + fattr.cf_mode = inode->i_mode; + fattr.cf_dacls = NULL; + + fattr.cf_acls = ksmbd_vfs_get_acl(inode, ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + fattr.cf_dacls = ksmbd_vfs_get_acl(inode, ACL_TYPE_DEFAULT); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) + ksmbd_vfs_get_sd_xattr(work->conn, fp->filp->f_path.dentry, &ppntsd); + + rc = build_sec_desc(pntsd, ppntsd, addition_info, &secdesclen, &fattr); + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + kfree(ppntsd); + ksmbd_fd_put(work, fp); + if (rc) + return rc; + + rsp->OutputBufferLength = cpu_to_le32(secdesclen); + inc_rfc1001_len(rsp_org, secdesclen); + return 0; +} + +/** + * smb2_query_info() - handler for smb2 query info command + * @work: smb work containing query info request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_query_info(struct ksmbd_work *work) +{ + struct smb2_query_info_req *req; + struct smb2_query_info_rsp *rsp, *rsp_org; + int rc = 0; + + rsp_org = RESPONSE_BUF(work); + WORK_BUFFERS(work, req, rsp); + + ksmbd_debug(SMB, "GOT query info request\n"); + + switch (req->InfoType) { + case SMB2_O_INFO_FILE: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); + rc = smb2_get_info_file(work, req, rsp, (void *)rsp_org); + break; + case SMB2_O_INFO_FILESYSTEM: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILESYSTEM\n"); + rc = smb2_get_info_filesystem(work, req, rsp, (void *)rsp_org); + break; + case SMB2_O_INFO_SECURITY: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); + rc = smb2_get_info_sec(work, req, rsp, (void *)rsp_org); + break; + default: + ksmbd_debug(SMB, "InfoType %d not supported yet\n", + req->InfoType); + rc = -EOPNOTSUPP; + } + + if (rc < 0) { + if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -EIO) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); + + ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", + rc); + return rc; + } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + inc_rfc1001_len(rsp_org, 8); + return 0; +} + +/** + * smb2_close_pipe() - handler for closing IPC pipe + * @work: smb work containing close request buffer + * + * Return: 0 + */ +static noinline int smb2_close_pipe(struct ksmbd_work *work) +{ + uint64_t id; + struct smb2_close_req *req = REQUEST_BUF(work); + struct smb2_close_rsp *rsp = RESPONSE_BUF(work); + + id = le64_to_cpu(req->VolatileFileId); + ksmbd_session_rpc_close(work->sess, id); + + rsp->StructureSize = cpu_to_le16(60); + rsp->Flags = 0; + rsp->Reserved = 0; + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; + inc_rfc1001_len(rsp, 60); + return 0; +} + +/** + * smb2_close() - handler for smb2 close file command + * @work: smb work containing close request buffer + * + * Return: 0 + */ +int smb2_close(struct ksmbd_work *work) +{ + unsigned int volatile_id = KSMBD_NO_FID; + uint64_t sess_id; + struct smb2_close_req *req; + struct smb2_close_rsp *rsp; + struct smb2_close_rsp *rsp_org; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_file *fp; + struct inode *inode; + u64 time; + int err = 0; + + rsp_org = RESPONSE_BUF(work); + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe close request\n"); + return smb2_close_pipe(work); + } + + sess_id = le64_to_cpu(req->hdr.SessionId); + if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) + sess_id = work->compound_sid; + + work->compound_sid = 0; + if (check_session_id(conn, sess_id)) + work->compound_sid = sess_id; + else { + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + err = -EBADF; + goto out; + } + + if (work->next_smb2_rcv_hdr_off && + !HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { + if (!HAS_FILE_ID(work->compound_fid)) { + /* file already closed, return FILE_CLOSED */ + ksmbd_debug(SMB, "file already closed\n"); + rsp->hdr.Status = STATUS_FILE_CLOSED; + err = -EBADF; + goto out; + } else { + ksmbd_debug(SMB, "Compound request set FID = %u:%u\n", + work->compound_fid, + work->compound_pfid); + volatile_id = work->compound_fid; + + /* file closed, stored id is not valid anymore */ + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + } + } else { + volatile_id = le64_to_cpu(req->VolatileFileId); + } + ksmbd_debug(SMB, "volatile_id = %u\n", volatile_id); + + rsp->StructureSize = cpu_to_le16(60); + rsp->Reserved = 0; + + if (req->Flags == SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) { + fp = ksmbd_lookup_fd_fast(work, volatile_id); + if (!fp) { + err = -ENOENT; + goto out; + } + + inode = FP_INODE(fp); + rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; + rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 : + cpu_to_le64(inode->i_blocks << 9); + rsp->EndOfFile = cpu_to_le64(inode->i_size); + rsp->Attributes = fp->f_ci->m_fattr; + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(inode->i_atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_ctime); + rsp->ChangeTime = cpu_to_le64(time); + ksmbd_fd_put(work, fp); + } else { + rsp->Flags = 0; + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + } + + err = ksmbd_close_fd(work, volatile_id); +out: + if (err) { + if (rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); + } else { + inc_rfc1001_len(rsp_org, 60); + } + + return 0; +} + +/** + * smb2_echo() - handler for smb2 echo(ping) command + * @work: smb work containing echo request buffer + * + * Return: 0 + */ +int smb2_echo(struct ksmbd_work *work) +{ + struct smb2_echo_rsp *rsp = RESPONSE_BUF(work); + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; + inc_rfc1001_len(rsp, 4); + return 0; +} + +/** + * smb2_rename() - handler for rename using smb2 setinfo command + * @work: smb work containing set info command buffer + * @filp: file pointer of source file + * @old_fid: file id of source file + * + * Return: 0 on success, otherwise error + */ +static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_file_rename_info *file_info, + struct nls_table *local_nls) +{ + struct ksmbd_share_config *share = fp->tcon->share_conf; + char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; + char *pathname = NULL; + struct path path; + bool file_present = true; + int rc; + + ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return -ENOMEM; + + abs_oldname = d_path(&fp->filp->f_path, pathname, PATH_MAX); + if (IS_ERR(abs_oldname)) { + rc = -EINVAL; + goto out; + } + old_name = strrchr(abs_oldname, '/'); + if (old_name && old_name[1] != '\0') + old_name++; + else { + ksmbd_debug(SMB, "can't get last component in path %s\n", + abs_oldname); + rc = -ENOENT; + goto out; + } + + new_name = smb2_get_name(share, + file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); + if (IS_ERR(new_name)) { + rc = PTR_ERR(new_name); + goto out; + } + + if (strchr(new_name, ':')) { + int s_type; + char *xattr_stream_name, *stream_name = NULL; + size_t xattr_stream_size; + int len; + + rc = parse_stream_name(new_name, &stream_name, &s_type); + if (rc < 0) + goto out; + + len = strlen(new_name); + if (new_name[len - 1] != '/') { + ksmbd_err("not allow base filename in rename\n"); + rc = -ESHARE; + goto out; + } + + rc = ksmbd_vfs_xattr_stream_name(stream_name, + &xattr_stream_name, + &xattr_stream_size, + s_type); + if (rc) + goto out; + + rc = ksmbd_vfs_setxattr(fp->filp->f_path.dentry, + xattr_stream_name, + NULL, 0, 0); + if (rc < 0) { + ksmbd_err("failed to store stream name in xattr: %d\n", + rc); + rc = -EINVAL; + goto out; + } + + goto out; + } + + ksmbd_debug(SMB, "new name %s\n", new_name); + rc = ksmbd_vfs_kern_path(new_name, 0, &path, 1); + if (rc) + file_present = false; + else + path_put(&path); + + if (ksmbd_share_veto_filename(share, new_name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); + goto out; + } + + if (file_info->ReplaceIfExists) { + if (file_present) { + rc = ksmbd_vfs_remove_file(work, new_name); + if (rc) { + if (rc != -ENOTEMPTY) + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s, rc %d\n", + new_name, rc); + goto out; + } + } + } else { + if (file_present && + strncmp(old_name, path.dentry->d_name.name, + strlen(old_name))) { + rc = -EEXIST; + ksmbd_debug(SMB, + "cannot rename already existing file\n"); + goto out; + } + } + + rc = ksmbd_vfs_fp_rename(work, fp, new_name); +out: + kfree(pathname); + if (!IS_ERR(new_name)) + smb2_put_name(new_name); + return rc; +} + +/** + * smb2_create_link() - handler for creating hardlink using smb2 + * set info command + * @work: smb work containing set info command buffer + * @filp: file pointer of source file + * + * Return: 0 on success, otherwise error + */ +static int smb2_create_link(struct ksmbd_work *work, + struct ksmbd_share_config *share, + struct smb2_file_link_info *file_info, + struct file *filp, + struct nls_table *local_nls) +{ + char *link_name = NULL, *target_name = NULL, *pathname = NULL; + struct path path; + bool file_present = true; + int rc; + + ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return -ENOMEM; + + link_name = smb2_get_name(share, + file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); + if (IS_ERR(link_name) || S_ISDIR(file_inode(filp)->i_mode)) { + rc = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "link name is %s\n", link_name); + target_name = d_path(&filp->f_path, pathname, PATH_MAX); + if (IS_ERR(target_name)) { + rc = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "target name is %s\n", target_name); + rc = ksmbd_vfs_kern_path(link_name, 0, &path, 0); + if (rc) + file_present = false; + else + path_put(&path); + + if (file_info->ReplaceIfExists) { + if (file_present) { + rc = ksmbd_vfs_remove_file(work, link_name); + if (rc) { + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s\n", + link_name); + goto out; + } + } + } else { + if (file_present) { + rc = -EEXIST; + ksmbd_debug(SMB, "link already exists\n"); + goto out; + } + } + + rc = ksmbd_vfs_link(work, target_name, link_name); + if (rc) + rc = -EINVAL; +out: + if (!IS_ERR(link_name)) + smb2_put_name(link_name); + kfree(pathname); + return rc; +} + +static bool is_attributes_write_allowed(struct ksmbd_file *fp) +{ + return fp->daccess & FILE_WRITE_ATTRIBUTES_LE; +} + +static int set_file_basic_info(struct ksmbd_file *fp, + char *buf, + struct ksmbd_share_config *share) +{ + struct smb2_file_all_info *file_info; + struct iattr attrs; + struct iattr temp_attrs; + struct file *filp; + struct inode *inode; + int rc; + + if (!is_attributes_write_allowed(fp)) + return -EACCES; + + file_info = (struct smb2_file_all_info *)buf; + attrs.ia_valid = 0; + filp = fp->filp; + inode = file_inode(filp); + + if (file_info->CreationTime) + fp->create_time = le64_to_cpu(file_info->CreationTime); + + if (file_info->LastAccessTime) { + attrs.ia_atime = ksmbd_NTtimeToUnix(file_info->LastAccessTime); + attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); + } + + if (file_info->ChangeTime) { + temp_attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); + attrs.ia_ctime = temp_attrs.ia_ctime; + attrs.ia_valid |= ATTR_CTIME; + } else + temp_attrs.ia_ctime = inode->i_ctime; + + if (file_info->LastWriteTime) { + attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + } + + if (file_info->Attributes) { + if (!S_ISDIR(inode->i_mode) && + file_info->Attributes & ATTR_DIRECTORY_LE) { + ksmbd_err("can't change a file to a directory\n"); + return -EINVAL; + } + + if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == ATTR_NORMAL_LE)) + fp->f_ci->m_fattr = file_info->Attributes | + (fp->f_ci->m_fattr & ATTR_DIRECTORY_LE); + } + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && + (file_info->CreationTime || file_info->Attributes)) { + struct xattr_dos_attrib da = {0}; + + da.version = 4; + da.itime = fp->itime; + da.create_time = fp->create_time; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + rc = ksmbd_vfs_set_dos_attrib_xattr(filp->f_path.dentry, &da); + if (rc) + ksmbd_debug(SMB, + "failed to restore file attribute in EA\n"); + rc = 0; + } + + /* + * HACK : set ctime here to avoid ctime changed + * when file_info->ChangeTime is zero. + */ + attrs.ia_ctime = temp_attrs.ia_ctime; + attrs.ia_valid |= ATTR_CTIME; + + if (attrs.ia_valid) { + struct dentry *dentry = filp->f_path.dentry; + struct inode *inode = d_inode(dentry); + + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return -EACCES; + + rc = setattr_prepare(&init_user_ns, dentry, &attrs); + if (rc) + return -EINVAL; + + inode_lock(inode); + setattr_copy(&init_user_ns, inode, &attrs); + attrs.ia_valid &= ~ATTR_CTIME; + rc = notify_change(&init_user_ns, dentry, &attrs, NULL); + inode_unlock(inode); + } + return 0; +} + +static int set_file_allocation_info(struct ksmbd_work *work, + struct ksmbd_file *fp, + char *buf) +{ + /* + * TODO : It's working fine only when store dos attributes + * is not yes. need to implement a logic which works + * properly with any smb.conf option + */ + + struct smb2_file_alloc_info *file_alloc_info; + loff_t alloc_blks; + struct inode *inode; + int rc; + + if (!is_attributes_write_allowed(fp)) + return -EACCES; + + file_alloc_info = (struct smb2_file_alloc_info *)buf; + alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; + inode = file_inode(fp->filp); + + if (alloc_blks > inode->i_blocks) { + rc = ksmbd_vfs_alloc_size(work, fp, alloc_blks * 512); + if (rc && rc != -EOPNOTSUPP) { + ksmbd_err("ksmbd_vfs_alloc_size is failed : %d\n", rc); + return rc; + } + } else if (alloc_blks < inode->i_blocks) { + loff_t size; + + /* + * Allocation size could be smaller than original one + * which means allocated blocks in file should be + * deallocated. use truncate to cut out it, but inode + * size is also updated with truncate offset. + * inode size is retained by backup inode size. + */ + size = i_size_read(inode); + rc = ksmbd_vfs_truncate(work, NULL, fp, alloc_blks * 512); + if (rc) { + ksmbd_err("truncate failed! filename : %s, err %d\n", + fp->filename, rc); + return rc; + } + if (size < alloc_blks * 512) + i_size_write(inode, size); + } + return 0; +} + +static int set_end_of_file_info(struct ksmbd_work *work, + struct ksmbd_file *fp, + char *buf) +{ + struct smb2_file_eof_info *file_eof_info; + loff_t newsize; + struct inode *inode; + int rc; + + if (!is_attributes_write_allowed(fp)) + return -EACCES; + + file_eof_info = (struct smb2_file_eof_info *)buf; + newsize = le64_to_cpu(file_eof_info->EndOfFile); + inode = file_inode(fp->filp); + + /* + * If FILE_END_OF_FILE_INFORMATION of set_info_file is called + * on FAT32 shared device, truncate execution time is too long + * and network error could cause from windows client. because + * truncate of some filesystem like FAT32 fill zero data in + * truncated range. + */ + if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { + ksmbd_debug(SMB, "filename : %s truncated to newsize %lld\n", + fp->filename, newsize); + rc = ksmbd_vfs_truncate(work, NULL, fp, newsize); + if (rc) { + ksmbd_debug(SMB, + "truncate failed! filename : %s err %d\n", + fp->filename, rc); + if (rc != -EAGAIN) + rc = -EBADF; + return rc; + } + } + return 0; +} + +static int set_rename_info(struct ksmbd_work *work, + struct ksmbd_file *fp, + char *buf) +{ + struct ksmbd_file *parent_fp; + + if (!(fp->daccess & FILE_DELETE_LE)) { + ksmbd_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + if (ksmbd_stream_fd(fp)) + goto next; + + parent_fp = ksmbd_lookup_fd_inode(PARENT_INODE(fp)); + if (parent_fp) { + if (parent_fp->daccess & FILE_DELETE_LE) { + ksmbd_err("parent dir is opened with delete access\n"); + return -ESHARE; + } + } +next: + return smb2_rename(work, fp, + (struct smb2_file_rename_info *)buf, + work->sess->conn->local_nls); +} + +static int set_file_disposition_info(struct ksmbd_file *fp, + char *buf) +{ + struct smb2_file_disposition_info *file_info; + struct inode *inode; + + if (!(fp->daccess & FILE_DELETE_LE)) { + ksmbd_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + inode = file_inode(fp->filp); + file_info = (struct smb2_file_disposition_info *)buf; + if (file_info->DeletePending) { + if (S_ISDIR(inode->i_mode) && + ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) + return -EBUSY; + ksmbd_set_inode_pending_delete(fp); + } else { + ksmbd_clear_inode_pending_delete(fp); + } + return 0; +} + +static int set_file_position_info(struct ksmbd_file *fp, + char *buf) +{ + struct smb2_file_pos_info *file_info; + loff_t current_byte_offset; + unsigned short sector_size; + struct inode *inode; + + inode = file_inode(fp->filp); + file_info = (struct smb2_file_pos_info *)buf; + current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset); + sector_size = ksmbd_vfs_logical_sector_size(inode); + + if (current_byte_offset < 0 || + (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && + current_byte_offset & (sector_size-1))) { + ksmbd_err("CurrentByteOffset is not valid : %llu\n", + current_byte_offset); + return -EINVAL; + } + + fp->filp->f_pos = current_byte_offset; + return 0; +} + +static int set_file_mode_info(struct ksmbd_file *fp, + char *buf) +{ + struct smb2_file_mode_info *file_info; + __le32 mode; + + file_info = (struct smb2_file_mode_info *)buf; + mode = file_info->Mode; + + if ((mode & (~FILE_MODE_INFO_MASK)) || + (mode & FILE_SYNCHRONOUS_IO_ALERT_LE && + mode & FILE_SYNCHRONOUS_IO_NONALERT_LE)) { + ksmbd_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); + return -EINVAL; + } + + /* + * TODO : need to implement consideration for + * FILE_SYNCHRONOUS_IO_ALERT and FILE_SYNCHRONOUS_IO_NONALERT + */ + ksmbd_vfs_set_fadvise(fp->filp, mode); + fp->coption = mode; + return 0; +} + +/** + * smb2_set_info_file() - handler for smb2 set info command + * @work: smb work containing set info command buffer + * + * 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, + int info_class, + char *buf, + struct ksmbd_share_config *share) +{ + switch (info_class) { + case FILE_BASIC_INFORMATION: + return set_file_basic_info(fp, buf, share); + + case FILE_ALLOCATION_INFORMATION: + return set_file_allocation_info(work, fp, buf); + + case FILE_END_OF_FILE_INFORMATION: + return set_end_of_file_info(work, fp, buf); + + case FILE_RENAME_INFORMATION: + if (!test_tree_conn_flag(work->tcon, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + return -EACCES; + } + return set_rename_info(work, fp, buf); + + case FILE_LINK_INFORMATION: + return smb2_create_link(work, work->tcon->share_conf, + (struct smb2_file_link_info *)buf, fp->filp, + work->sess->conn->local_nls); + + case FILE_DISPOSITION_INFORMATION: + if (!test_tree_conn_flag(work->tcon, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + return -EACCES; + } + return set_file_disposition_info(fp, buf); + + case FILE_FULL_EA_INFORMATION: + { + if (!(fp->daccess & FILE_WRITE_EA_LE)) { + ksmbd_err("Not permitted to write ext attr: 0x%x\n", + fp->daccess); + return -EACCES; + } + + return smb2_set_ea((struct smb2_ea_info *)buf, + &fp->filp->f_path); + } + + case FILE_POSITION_INFORMATION: + return set_file_position_info(fp, buf); + + case FILE_MODE_INFORMATION: + return set_file_mode_info(fp, buf); + } + + ksmbd_err("Unimplemented Fileinfoclass :%d\n", info_class); + return -EOPNOTSUPP; +} + +static int smb2_set_info_sec(struct ksmbd_file *fp, + int addition_info, + char *buffer, + int buf_len) +{ + struct smb_ntsd *pntsd = (struct smb_ntsd *)buffer; + + fp->saccess |= FILE_SHARE_DELETE_LE; + + return set_info_sec(fp->conn, fp->tcon, fp->filp->f_path.dentry, pntsd, + buf_len, false); +} + +/** + * smb2_set_info() - handler for smb2 set info command handler + * @work: smb work containing set info request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_set_info(struct ksmbd_work *work) +{ + struct smb2_set_info_req *req; + struct smb2_set_info_rsp *rsp, *rsp_org; + struct ksmbd_file *fp; + int rc = 0; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + ksmbd_debug(SMB, "Received set info request\n"); + + rsp_org = RESPONSE_BUF(work); + if (work->next_smb2_rcv_hdr_off) { + req = REQUEST_BUF_NEXT(work); + rsp = RESPONSE_BUF_NEXT(work); + if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %u\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } else { + req = REQUEST_BUF(work); + rsp = RESPONSE_BUF(work); + } + + if (!HAS_FILE_ID(id)) { + id = le64_to_cpu(req->VolatileFileId); + pid = le64_to_cpu(req->PersistentFileId); + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) { + ksmbd_debug(SMB, "Invalid id for close: %u\n", id); + rc = -ENOENT; + goto err_out; + } + + switch (req->InfoType) { + case SMB2_O_INFO_FILE: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); + rc = smb2_set_info_file(work, fp, req->FileInfoClass, + req->Buffer, work->tcon->share_conf); + break; + case SMB2_O_INFO_SECURITY: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); + rc = smb2_set_info_sec(fp, + le32_to_cpu(req->AdditionalInformation), req->Buffer, + le32_to_cpu(req->BufferLength)); + break; + default: + rc = -EOPNOTSUPP; + } + + if (rc < 0) + goto err_out; + + rsp->StructureSize = cpu_to_le16(2); + inc_rfc1001_len(rsp_org, 2); + ksmbd_fd_put(work, fp); + return 0; + +err_out: + if (rc == -EACCES || rc == -EPERM) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; + else if (rc == -EBUSY || rc == -ENOTEMPTY) + rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY; + else if (rc == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (rc == -EBADF) + rsp->hdr.Status = STATUS_INVALID_HANDLE; + else if (rc == -EEXIST) + rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; + else if (rsp->hdr.Status == 0 || rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", + rc); + return rc; +} + +/** + * smb2_read_pipe() - handler for smb2 read from IPC pipe + * @work: smb work containing read IPC pipe command buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int smb2_read_pipe(struct ksmbd_work *work) +{ + int nbytes = 0, err; + uint64_t id; + struct ksmbd_rpc_command *rpc_resp; + struct smb2_read_req *req = REQUEST_BUF(work); + struct smb2_read_rsp *rsp = RESPONSE_BUF(work); + + id = le64_to_cpu(req->VolatileFileId); + + inc_rfc1001_len(rsp, 16); + rpc_resp = ksmbd_rpc_read(work->sess, id); + if (rpc_resp) { + if (rpc_resp->flags != KSMBD_RPC_OK) { + err = -EINVAL; + goto out; + } + + work->aux_payload_buf = + ksmbd_alloc_response(rpc_resp->payload_sz); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; + } + + memcpy(work->aux_payload_buf, rpc_resp->payload, + rpc_resp->payload_sz); + + nbytes = rpc_resp->payload_sz; + work->resp_hdr_sz = get_rfc1002_len(rsp) + 4; + work->aux_payload_sz = nbytes; + ksmbd_free(rpc_resp); + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 80; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(rsp, nbytes); + return 0; + +out: + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + smb2_set_err_rsp(work); + ksmbd_free(rpc_resp); + return err; +} + +static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, + struct smb2_read_req *req, + void *data_buf, size_t length) +{ + struct smb2_buffer_desc_v1 *desc = + (struct smb2_buffer_desc_v1 *)&req->Buffer[0]; + int err; + + if (work->conn->dialect == SMB30_PROT_ID + && req->Channel != SMB2_CHANNEL_RDMA_V1) + return -EINVAL; + + if (req->ReadChannelInfoOffset == 0 + || le16_to_cpu(req->ReadChannelInfoLength) < sizeof(*desc)) + return -EINVAL; + + work->need_invalidate_rkey = + (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); + work->remote_key = le32_to_cpu(desc->token); + + err = ksmbd_conn_rdma_write(work->conn, + data_buf, length, + le32_to_cpu(desc->token), + le64_to_cpu(desc->offset), + le32_to_cpu(desc->length)); + if (err) + return err; + + return length; +} + +/** + * smb2_read() - handler for smb2 read from file + * @work: smb work containing read command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_read(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_read_req *req; + struct smb2_read_rsp *rsp, *rsp_org; + struct ksmbd_file *fp; + loff_t offset; + size_t length, mincount; + ssize_t nbytes = 0, remain_bytes = 0; + int err = 0; + + rsp_org = RESPONSE_BUF(work); + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe read request\n"); + return smb2_read_pipe(work); + } + + fp = ksmbd_lookup_fd_slow(work, + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (!fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + return -ENOENT; + } + + if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { + ksmbd_err("Not permitted to read : 0x%x\n", fp->daccess); + err = -EACCES; + goto out; + } + + offset = le64_to_cpu(req->Offset); + length = le32_to_cpu(req->Length); + mincount = le32_to_cpu(req->MinimumCount); + + if (length > conn->vals->max_read_size) { + ksmbd_debug(SMB, "limiting read size to max size(%u)\n", + conn->vals->max_read_size); + err = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "filename %s, offset %lld, len %zu\n", FP_FILENAME(fp), + offset, length); + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) { + work->aux_payload_buf = + ksmbd_find_buffer(conn->vals->max_read_size); + work->set_read_buf = true; + } else { + work->aux_payload_buf = ksmbd_alloc_response(length); + } + if (!work->aux_payload_buf) { + err = nbytes; + goto out; + } + + nbytes = ksmbd_vfs_read(work, fp, length, &offset); + if (nbytes < 0) { + err = nbytes; + goto out; + } + + if ((nbytes == 0 && length != 0) || nbytes < mincount) { + if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) + ksmbd_release_buffer(AUX_PAYLOAD(work)); + else + ksmbd_free_response(AUX_PAYLOAD(work)); + INIT_AUX_PAYLOAD(work); + rsp->hdr.Status = STATUS_END_OF_FILE; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return 0; + } + + ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n", + nbytes, offset, mincount); + + if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || + req->Channel == SMB2_CHANNEL_RDMA_V1) { + /* write data to the client using rdma channel */ + remain_bytes = smb2_read_rdma_channel(work, req, + AUX_PAYLOAD(work), nbytes); + if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) + ksmbd_release_buffer(AUX_PAYLOAD(work)); + else + ksmbd_free_response(AUX_PAYLOAD(work)); + INIT_AUX_PAYLOAD(work); + + nbytes = 0; + if (remain_bytes < 0) { + err = (int)remain_bytes; + goto out; + } + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 80; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = cpu_to_le32(remain_bytes); + rsp->Reserved2 = 0; + inc_rfc1001_len(rsp_org, 16); + work->resp_hdr_sz = get_rfc1002_len(rsp_org) + 4; + work->aux_payload_sz = nbytes; + inc_rfc1001_len(rsp_org, nbytes); + ksmbd_fd_put(work, fp); + return 0; + +out: + if (err) { + if (err == -EISDIR) + rsp->hdr.Status = STATUS_INVALID_DEVICE_REQUEST; + else if (err == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (err == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (err == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status = STATUS_INVALID_HANDLE; + + smb2_set_err_rsp(work); + } + ksmbd_fd_put(work, fp); + return err; +} + +/** + * smb2_write_pipe() - handler for smb2 write on IPC pipe + * @work: smb work containing write IPC pipe command buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int smb2_write_pipe(struct ksmbd_work *work) +{ + struct smb2_write_req *req = REQUEST_BUF(work); + struct smb2_write_rsp *rsp = RESPONSE_BUF(work); + struct ksmbd_rpc_command *rpc_resp; + uint64_t id = 0; + int err = 0, ret = 0; + char *data_buf; + size_t length; + + length = le32_to_cpu(req->Length); + id = le64_to_cpu(req->VolatileFileId); + + if (le16_to_cpu(req->DataOffset) == + (offsetof(struct smb2_write_req, Buffer) - 4)) { + data_buf = (char *)&req->Buffer[0]; + } else { + if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || + (le16_to_cpu(req->DataOffset) + + length > get_rfc1002_len(req))) { + ksmbd_err("invalid write data offset %u, smb_len %u\n", + le16_to_cpu(req->DataOffset), + get_rfc1002_len(req)); + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.ProtocolId) + + le16_to_cpu(req->DataOffset)); + } + + rpc_resp = ksmbd_rpc_write(work->sess, id, data_buf, length); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + ksmbd_free(rpc_resp); + smb2_set_err_rsp(work); + return -EOPNOTSUPP; + } + if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + ksmbd_free(rpc_resp); + return ret; + } + ksmbd_free(rpc_resp); + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(length); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(rsp, 16); + return 0; +out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + } + + return err; +} + +static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, + struct smb2_write_req *req, struct ksmbd_file *fp, + loff_t offset, size_t length, bool sync) +{ + struct smb2_buffer_desc_v1 *desc; + char *data_buf; + int ret; + ssize_t nbytes; + + desc = (struct smb2_buffer_desc_v1 *)&req->Buffer[0]; + + if (work->conn->dialect == SMB30_PROT_ID && + req->Channel != SMB2_CHANNEL_RDMA_V1) + return -EINVAL; + + if (req->Length != 0 || req->DataOffset != 0) + return -EINVAL; + + if (req->WriteChannelInfoOffset == 0 + || le16_to_cpu(req->WriteChannelInfoLength) < sizeof(*desc)) + return -EINVAL; + + work->need_invalidate_rkey = + (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); + work->remote_key = le32_to_cpu(desc->token); + + data_buf = ksmbd_alloc_response(length); + if (!data_buf) + return -ENOMEM; + + ret = ksmbd_conn_rdma_read(work->conn, data_buf, length, + le32_to_cpu(desc->token), + le64_to_cpu(desc->offset), + le32_to_cpu(desc->length)); + + if (ret < 0) { + ksmbd_free_response(data_buf); + return ret; + } + + ret = ksmbd_vfs_write(work, fp, data_buf, length, &offset, + sync, &nbytes); + + ksmbd_free_response(data_buf); + if (ret < 0) + return ret; + + return nbytes; +} + +/** + * smb2_write() - handler for smb2 write from file + * @work: smb work containing write command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_write(struct ksmbd_work *work) +{ + struct smb2_write_req *req; + struct smb2_write_rsp *rsp, *rsp_org; + struct ksmbd_file *fp = NULL; + loff_t offset; + size_t length; + ssize_t nbytes; + char *data_buf; + bool writethrough = false; + int err = 0; + + rsp_org = RESPONSE_BUF(work); + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe write request\n"); + return smb2_write_pipe(work); + } + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, "User does not have write permission\n"); + err = -EACCES; + goto out; + } + + fp = ksmbd_lookup_fd_slow(work, + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (!fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + return -ENOENT; + } + + if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { + ksmbd_err("Not permitted to write : 0x%x\n", fp->daccess); + err = -EACCES; + goto out; + } + + offset = le64_to_cpu(req->Offset); + length = le32_to_cpu(req->Length); + + if (length > work->conn->vals->max_write_size) { + ksmbd_debug(SMB, "limiting write size to max size(%u)\n", + work->conn->vals->max_write_size); + err = -EINVAL; + goto out; + } + + if (le32_to_cpu(req->Flags) & SMB2_WRITEFLAG_WRITE_THROUGH) + writethrough = true; + + if (req->Channel != SMB2_CHANNEL_RDMA_V1 && + req->Channel != SMB2_CHANNEL_RDMA_V1_INVALIDATE) { + + if (le16_to_cpu(req->DataOffset) == + (offsetof(struct smb2_write_req, Buffer) - 4)) { + data_buf = (char *)&req->Buffer[0]; + } else { + if ((le16_to_cpu(req->DataOffset) > + get_rfc1002_len(req)) || + (le16_to_cpu(req->DataOffset) + + length > get_rfc1002_len(req))) { + ksmbd_err("invalid write data offset %u, smb_len %u\n", + le16_to_cpu(req->DataOffset), + get_rfc1002_len(req)); + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.ProtocolId) + + le16_to_cpu(req->DataOffset)); + } + + ksmbd_debug(SMB, "flags %u\n", le32_to_cpu(req->Flags)); + if (le32_to_cpu(req->Flags) & SMB2_WRITEFLAG_WRITE_THROUGH) + writethrough = true; + + ksmbd_debug(SMB, "filename %s, offset %lld, len %zu\n", + FP_FILENAME(fp), offset, length); + err = ksmbd_vfs_write(work, fp, data_buf, length, &offset, + writethrough, &nbytes); + if (err < 0) + goto out; + } else { + /* read data from the client using rdma channel, and + * write the data. + */ + nbytes = smb2_write_rdma_channel(work, req, fp, offset, + le32_to_cpu(req->RemainingBytes), + writethrough); + if (nbytes < 0) { + err = (int)nbytes; + goto out; + } + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(rsp_org, 16); + ksmbd_fd_put(work, fp); + return 0; + +out: + if (err == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (err == -ENOSPC || err == -EFBIG) + rsp->hdr.Status = STATUS_DISK_FULL; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (err == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (err == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status = STATUS_INVALID_HANDLE; + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return err; +} + +/** + * smb2_flush() - handler for smb2 flush file - fsync + * @work: smb work containing flush command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_flush(struct ksmbd_work *work) +{ + struct smb2_flush_req *req; + struct smb2_flush_rsp *rsp, *rsp_org; + int err; + + rsp_org = RESPONSE_BUF(work); + WORK_BUFFERS(work, req, rsp); + + ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", + le64_to_cpu(req->VolatileFileId)); + + err = ksmbd_vfs_fsync(work, + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (err) + goto out; + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; + inc_rfc1001_len(rsp_org, 4); + return 0; + +out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + } + + return err; +} + +/** + * smb2_cancel() - handler for smb2 cancel command + * @work: smb work containing cancel command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_cancel(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *hdr = REQUEST_BUF(work); + struct smb2_hdr *chdr; + struct ksmbd_work *cancel_work = NULL; + struct list_head *tmp; + int canceled = 0; + struct list_head *command_list; + + ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n", + hdr->MessageId, hdr->Flags); + + if (hdr->Flags & SMB2_FLAGS_ASYNC_COMMAND) { + command_list = &conn->async_requests; + + spin_lock(&conn->request_lock); + list_for_each(tmp, command_list) { + cancel_work = list_entry(tmp, struct ksmbd_work, + async_request_entry); + chdr = REQUEST_BUF(cancel_work); + + if (cancel_work->async_id != + le64_to_cpu(hdr->Id.AsyncId)) + continue; + + ksmbd_debug(SMB, + "smb2 with AsyncId %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->Id.AsyncId), + le16_to_cpu(chdr->Command)); + canceled = 1; + break; + } + spin_unlock(&conn->request_lock); + } else { + command_list = &conn->requests; + + spin_lock(&conn->request_lock); + list_for_each(tmp, command_list) { + cancel_work = list_entry(tmp, struct ksmbd_work, + request_entry); + chdr = REQUEST_BUF(cancel_work); + + if (chdr->MessageId != hdr->MessageId || + cancel_work == work) + continue; + + ksmbd_debug(SMB, + "smb2 with mid %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->MessageId), + le16_to_cpu(chdr->Command)); + canceled = 1; + break; + } + spin_unlock(&conn->request_lock); + } + + if (canceled) { + cancel_work->state = KSMBD_WORK_CANCELLED; + if (cancel_work->cancel_fn) + cancel_work->cancel_fn(cancel_work->cancel_argv); + } + + /* For SMB2_CANCEL command itself send no response*/ + work->send_no_response = 1; + return 0; +} + +struct file_lock *smb_flock_init(struct file *f) +{ + struct file_lock *fl; + + fl = locks_alloc_lock(); + if (!fl) + goto out; + + locks_init_lock(fl); + + fl->fl_owner = f; + fl->fl_pid = current->tgid; + fl->fl_file = f; + fl->fl_flags = FL_POSIX; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; + +out: + return fl; +} + +static int smb2_set_flock_flags(struct file_lock *flock, int flags) +{ + int cmd = -EINVAL; + + /* Checking for wrong flag combination during lock request*/ + switch (flags) { + case SMB2_LOCKFLAG_SHARED: + ksmbd_debug(SMB, "received shared request\n"); + cmd = F_SETLKW; + flock->fl_type = F_RDLCK; + flock->fl_flags |= FL_SLEEP; + break; + case SMB2_LOCKFLAG_EXCLUSIVE: + ksmbd_debug(SMB, "received exclusive request\n"); + cmd = F_SETLKW; + flock->fl_type = F_WRLCK; + flock->fl_flags |= FL_SLEEP; + break; + case SMB2_LOCKFLAG_SHARED|SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + ksmbd_debug(SMB, + "received shared & fail immediately request\n"); + cmd = F_SETLK; + flock->fl_type = F_RDLCK; + break; + case SMB2_LOCKFLAG_EXCLUSIVE|SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + ksmbd_debug(SMB, + "received exclusive & fail immediately request\n"); + cmd = F_SETLK; + flock->fl_type = F_WRLCK; + break; + case SMB2_LOCKFLAG_UNLOCK: + ksmbd_debug(SMB, "received unlock request\n"); + flock->fl_type = F_UNLCK; + cmd = 0; + break; + } + + return cmd; +} + +static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, + unsigned int cmd, int flags, struct list_head *lock_list) +{ + struct ksmbd_lock *lock; + + lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); + if (!lock) + return NULL; + + lock->cmd = cmd; + lock->fl = flock; + lock->start = flock->fl_start; + lock->end = flock->fl_end; + lock->flags = flags; + if (lock->start == lock->end) + lock->zero_len = 1; + INIT_LIST_HEAD(&lock->llist); + INIT_LIST_HEAD(&lock->glist); + list_add_tail(&lock->llist, lock_list); + + return lock; +} + +static void smb2_remove_blocked_lock(void **argv) +{ + struct file_lock *flock = (struct file_lock *)argv[0]; + + ksmbd_vfs_posix_lock_unblock(flock); + wake_up(&flock->fl_wait); +} + +static inline bool lock_defer_pending(struct file_lock *fl) +{ + /* check pending lock waiters */ + return waitqueue_active(&fl->fl_wait); +} + +/** + * smb2_lock() - handler for smb2 file lock command + * @work: smb work containing lock command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_lock(struct ksmbd_work *work) +{ + struct smb2_lock_req *req = REQUEST_BUF(work); + struct smb2_lock_rsp *rsp = RESPONSE_BUF(work); + struct smb2_lock_element *lock_ele; + struct ksmbd_file *fp = NULL; + struct file_lock *flock = NULL; + struct file *filp = NULL; + int lock_count; + int flags = 0; + int cmd = 0; + int err = 0, i; + uint64_t lock_length; + struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp; + int nolock = 0; + LIST_HEAD(lock_list); + LIST_HEAD(rollback_list); + int prior_lock = 0; + + ksmbd_debug(SMB, "Received lock request\n"); + fp = ksmbd_lookup_fd_slow(work, + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (!fp) { + ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", + le64_to_cpu(req->VolatileFileId)); + rsp->hdr.Status = STATUS_FILE_CLOSED; + goto out2; + } + + filp = fp->filp; + lock_count = le16_to_cpu(req->LockCount); + lock_ele = req->locks; + + ksmbd_debug(SMB, "lock count is %d\n", lock_count); + if (!lock_count) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out2; + } + + for (i = 0; i < lock_count; i++) { + flags = le32_to_cpu(lock_ele[i].Flags); + + flock = smb_flock_init(filp); + if (!flock) { + rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + goto out; + } + + cmd = smb2_set_flock_flags(flock, flags); + + flock->fl_start = le64_to_cpu(lock_ele[i].Offset); + if (flock->fl_start > OFFSET_MAX) { + ksmbd_err("Invalid lock range requested\n"); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; + goto out; + } + + lock_length = le64_to_cpu(lock_ele[i].Length); + if (lock_length > 0) { + if (lock_length > + OFFSET_MAX - flock->fl_start) { + ksmbd_debug(SMB, + "Invalid lock range requested\n"); + lock_length = OFFSET_MAX - flock->fl_start; + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; + goto out; + } + } else + lock_length = 0; + + flock->fl_end = flock->fl_start + lock_length; + + if (flock->fl_end < flock->fl_start) { + ksmbd_debug(SMB, + "the end offset(%llx) is smaller than the start offset(%llx)\n", + flock->fl_end, flock->fl_start); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; + goto out; + } + + /* Check conflict locks in one request */ + list_for_each_entry(cmp_lock, &lock_list, llist) { + if (cmp_lock->fl->fl_start <= flock->fl_start && + cmp_lock->fl->fl_end >= flock->fl_end) { + if (cmp_lock->fl->fl_type != F_UNLCK && + flock->fl_type != F_UNLCK) { + ksmbd_err("conflict two locks in one request\n"); + rsp->hdr.Status = + STATUS_INVALID_PARAMETER; + goto out; + } + } + } + + smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list); + if (!smb_lock) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + } + + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + if (smb_lock->cmd < 0) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + + if (!(smb_lock->flags & SMB2_LOCKFLAG_MASK)) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + + if ((prior_lock & (SMB2_LOCKFLAG_EXCLUSIVE | + SMB2_LOCKFLAG_SHARED) && + smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) || + (prior_lock == SMB2_LOCKFLAG_UNLOCK && + !(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK))) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + + prior_lock = smb_lock->flags; + + if (!(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) && + !(smb_lock->flags & SMB2_LOCKFLAG_FAIL_IMMEDIATELY)) + goto no_check_gl; + + nolock = 1; + /* check locks in global list */ + list_for_each_entry(cmp_lock, &global_lock_list, glist) { + if (file_inode(cmp_lock->fl->fl_file) != + file_inode(smb_lock->fl->fl_file)) + continue; + + if (smb_lock->fl->fl_type == F_UNLCK) { + if (cmp_lock->fl->fl_file == + smb_lock->fl->fl_file && + cmp_lock->start == smb_lock->start && + cmp_lock->end == smb_lock->end && + !lock_defer_pending(cmp_lock->fl)) { + nolock = 0; + locks_free_lock(cmp_lock->fl); + list_del(&cmp_lock->glist); + kfree(cmp_lock); + break; + } + continue; + } + + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) { + if (smb_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } else { + if (cmp_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } + + /* check zero byte lock range */ + if (cmp_lock->zero_len && !smb_lock->zero_len && + cmp_lock->start > smb_lock->start && + cmp_lock->start < smb_lock->end) { + ksmbd_err("previous lock conflict with zero byte lock range\n"); + rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + goto out; + } + + if (smb_lock->zero_len && !cmp_lock->zero_len && + smb_lock->start > cmp_lock->start && + smb_lock->start < cmp_lock->end) { + ksmbd_err("current lock conflict with zero byte lock range\n"); + rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + goto out; + } + + if (((cmp_lock->start <= smb_lock->start && + cmp_lock->end > smb_lock->start) || + (cmp_lock->start < smb_lock->end && + cmp_lock->end >= smb_lock->end)) && + !cmp_lock->zero_len && !smb_lock->zero_len) { + ksmbd_err("Not allow lock operation on exclusive lock range\n"); + rsp->hdr.Status = + STATUS_LOCK_NOT_GRANTED; + goto out; + } + } + + if (smb_lock->fl->fl_type == F_UNLCK && nolock) { + ksmbd_err("Try to unlock nolocked range\n"); + rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; + goto out; + } + +no_check_gl: + if (smb_lock->zero_len) { + err = 0; + goto skip; + } + + flock = smb_lock->fl; + list_del(&smb_lock->llist); +retry: + err = ksmbd_vfs_lock(filp, smb_lock->cmd, flock); +skip: + if (flags & SMB2_LOCKFLAG_UNLOCK) { + if (!err) + ksmbd_debug(SMB, "File unlocked\n"); + else if (err == -ENOENT) { + rsp->hdr.Status = STATUS_NOT_LOCKED; + goto out; + } + locks_free_lock(flock); + kfree(smb_lock); + } else { + if (err == FILE_LOCK_DEFERRED) { + void **argv; + + ksmbd_debug(SMB, + "would have to wait for getting lock\n"); + list_add_tail(&smb_lock->glist, + &global_lock_list); + list_add(&smb_lock->llist, &rollback_list); + + argv = kmalloc(sizeof(void *), GFP_KERNEL); + if (!argv) { + err = -ENOMEM; + goto out; + } + argv[0] = flock; + + err = setup_async_work(work, + smb2_remove_blocked_lock, argv); + if (err) { + rsp->hdr.Status = + STATUS_INSUFFICIENT_RESOURCES; + goto out; + } + spin_lock(&fp->f_lock); + list_add(&work->fp_entry, &fp->blocked_works); + spin_unlock(&fp->f_lock); + + smb2_send_interim_resp(work, STATUS_PENDING); + + err = ksmbd_vfs_posix_lock_wait(flock); + + if (!WORK_ACTIVE(work)) { + list_del(&smb_lock->llist); + list_del(&smb_lock->glist); + locks_free_lock(flock); + + if (WORK_CANCELLED(work)) { + spin_lock(&fp->f_lock); + list_del(&work->fp_entry); + spin_unlock(&fp->f_lock); + rsp->hdr.Status = + STATUS_CANCELLED; + kfree(smb_lock); + smb2_send_interim_resp(work, + STATUS_CANCELLED); + work->send_no_response = 1; + goto out; + } + init_smb2_rsp_hdr(work); + smb2_set_err_rsp(work); + rsp->hdr.Status = + STATUS_RANGE_NOT_LOCKED; + kfree(smb_lock); + goto out2; + } + + list_del(&smb_lock->llist); + list_del(&smb_lock->glist); + spin_lock(&fp->f_lock); + list_del(&work->fp_entry); + spin_unlock(&fp->f_lock); + goto retry; + } else if (!err) { + list_add_tail(&smb_lock->glist, + &global_lock_list); + list_add(&smb_lock->llist, &rollback_list); + ksmbd_debug(SMB, "successful in taking lock\n"); + } else { + rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + goto out; + } + } + } + + if (atomic_read(&fp->f_ci->op_count) > 1) + smb_break_all_oplock(work, fp); + + rsp->StructureSize = cpu_to_le16(4); + ksmbd_debug(SMB, "successful in taking lock\n"); + rsp->hdr.Status = STATUS_SUCCESS; + rsp->Reserved = 0; + inc_rfc1001_len(rsp, 4); + ksmbd_fd_put(work, fp); + return err; + +out: + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + locks_free_lock(smb_lock->fl); + list_del(&smb_lock->llist); + kfree(smb_lock); + } + + list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { + struct file_lock *rlock = NULL; + + rlock = smb_flock_init(filp); + rlock->fl_type = F_UNLCK; + rlock->fl_start = smb_lock->start; + rlock->fl_end = smb_lock->end; + + err = ksmbd_vfs_lock(filp, 0, rlock); + if (err) + ksmbd_err("rollback unlock fail : %d\n", err); + list_del(&smb_lock->llist); + list_del(&smb_lock->glist); + locks_free_lock(smb_lock->fl); + locks_free_lock(rlock); + kfree(smb_lock); + } +out2: + ksmbd_debug(SMB, "failed in taking lock(flags : %x)\n", flags); + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return 0; +} + +static int fsctl_copychunk(struct ksmbd_work *work, + struct smb2_ioctl_req *req, + struct smb2_ioctl_rsp *rsp) +{ + struct copychunk_ioctl_req *ci_req; + struct copychunk_ioctl_rsp *ci_rsp; + struct ksmbd_file *src_fp = NULL, *dst_fp = NULL; + struct srv_copychunk *chunks; + unsigned int i, chunk_count, chunk_count_written = 0; + unsigned int chunk_size_written = 0; + loff_t total_size_written = 0; + int ret, cnt_code; + + cnt_code = le32_to_cpu(req->CntCode); + ci_req = (struct copychunk_ioctl_req *)&req->Buffer[0]; + ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0]; + + rsp->VolatileFileId = req->VolatileFileId; + rsp->PersistentFileId = req->PersistentFileId; + ci_rsp->ChunksWritten = cpu_to_le32( + ksmbd_server_side_copy_max_chunk_count()); + ci_rsp->ChunkBytesWritten = cpu_to_le32( + ksmbd_server_side_copy_max_chunk_size()); + 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); + total_size_written = 0; + + /* verify the SRV_COPYCHUNK_COPY packet */ + if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || + le32_to_cpu(req->InputCount) < + offsetof(struct copychunk_ioctl_req, Chunks) + + chunk_count * sizeof(struct srv_copychunk)) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + 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()) + break; + total_size_written += le32_to_cpu(chunks[i].Length); + } + if (i < chunk_count || total_size_written > + ksmbd_server_side_copy_max_total_size()) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + src_fp = ksmbd_lookup_foreign_fd(work, + le64_to_cpu(ci_req->ResumeKey[0])); + dst_fp = ksmbd_lookup_fd_slow(work, + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + + ret = -EINVAL; + if (!src_fp || src_fp->persistent_id != + le64_to_cpu(ci_req->ResumeKey[1])) { + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto out; + } + if (!dst_fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + goto out; + } + + /* + * FILE_READ_DATA should only be included in + * the FSCTL_COPYCHUNK case + */ + if (cnt_code == FSCTL_COPYCHUNK && !(dst_fp->daccess & + (FILE_READ_DATA_LE | FILE_GENERIC_READ_LE))) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + goto out; + } + + ret = ksmbd_vfs_copy_file_ranges(work, src_fp, dst_fp, + chunks, chunk_count, + &chunk_count_written, &chunk_size_written, + &total_size_written); + if (ret < 0) { + if (ret == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + if (ret == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (ret == -EBADF) + rsp->hdr.Status = STATUS_INVALID_HANDLE; + else if (ret == -EFBIG || ret == -ENOSPC) + rsp->hdr.Status = STATUS_DISK_FULL; + else if (ret == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (ret == -EISDIR) + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + else if (ret == -E2BIG) + rsp->hdr.Status = STATUS_INVALID_VIEW_SIZE; + else + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + } + + ci_rsp->ChunksWritten = cpu_to_le32(chunk_count_written); + ci_rsp->ChunkBytesWritten = cpu_to_le32(chunk_size_written); + ci_rsp->TotalBytesWritten = cpu_to_le32(total_size_written); +out: + ksmbd_fd_put(work, src_fp); + ksmbd_fd_put(work, dst_fp); + return ret; +} + +static __be32 idev_ipv4_address(struct in_device *idev) +{ + __be32 addr = 0; + + struct in_ifaddr *ifa; + + rcu_read_lock(); + in_dev_for_each_ifa_rcu(ifa, idev) { + if (ifa->ifa_flags & IFA_F_SECONDARY) + continue; + + addr = ifa->ifa_address; + break; + } + rcu_read_unlock(); + return addr; +} + +static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, + struct smb2_ioctl_req *req, + struct smb2_ioctl_rsp *rsp) +{ + struct network_interface_info_ioctl_rsp *nii_rsp = NULL; + int nbytes = 0; + struct net_device *netdev; + struct sockaddr_storage_rsp *sockaddr_storage; + unsigned int flags; + unsigned long long speed; + + rtnl_lock(); + for_each_netdev(&init_net, netdev) { + if (unlikely(!netdev)) { + rtnl_unlock(); + return -EINVAL; + } + + if (netdev->type == ARPHRD_LOOPBACK) + continue; + + flags = dev_get_flags(netdev); + if (!(flags & IFF_RUNNING)) + continue; + + nii_rsp = (struct network_interface_info_ioctl_rsp *) + &rsp->Buffer[nbytes]; + nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); + + /* TODO: specify the RDMA capabilities */ + if (netdev->num_tx_queues > 1) + nii_rsp->Capability = cpu_to_le32(RSS_CAPABLE); + else + nii_rsp->Capability = 0; + + nii_rsp->Next = cpu_to_le32(152); + nii_rsp->Reserved = 0; + + if (netdev->ethtool_ops->get_link_ksettings) { + struct ethtool_link_ksettings cmd; + + netdev->ethtool_ops->get_link_ksettings(netdev, &cmd); + speed = cmd.base.speed; + } else { + ksmbd_err("%s %s %s\n", + netdev->name, + "speed is unknown,", + "defaulting to 1Gb/sec"); + speed = SPEED_1000; + } + + speed *= 1000000; + nii_rsp->LinkSpeed = cpu_to_le64(speed); + + sockaddr_storage = (struct sockaddr_storage_rsp *) + nii_rsp->SockAddr_Storage; + memset(sockaddr_storage, 0, 128); + + if (conn->peer_addr.ss_family == PF_INET) { + struct in_device *idev; + + sockaddr_storage->Family = cpu_to_le16(INTERNETWORK); + sockaddr_storage->addr4.Port = 0; + + idev = __in_dev_get_rtnl(netdev); + if (!idev) + continue; + sockaddr_storage->addr4.IPv4address = + idev_ipv4_address(idev); + } else { + struct inet6_dev *idev6; + struct inet6_ifaddr *ifa; + __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address; + + sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6); + sockaddr_storage->addr6.Port = 0; + sockaddr_storage->addr6.FlowInfo = 0; + + idev6 = __in6_dev_get(netdev); + if (!idev6) + continue; + + list_for_each_entry(ifa, &idev6->addr_list, if_list) { + if (ifa->flags & (IFA_F_TENTATIVE | + IFA_F_DEPRECATED)) + continue; + memcpy(ipv6_addr, ifa->addr.s6_addr, 16); + break; + } + sockaddr_storage->addr6.ScopeId = 0; + } + + nbytes += sizeof(struct network_interface_info_ioctl_rsp); + } + rtnl_unlock(); + + /* zero if this is last one */ + if (nii_rsp) + nii_rsp->Next = 0; + + if (!nbytes) { + rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; + return -EINVAL; + } + + rsp->PersistentFileId = cpu_to_le64(SMB2_NO_FID); + rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID); + return nbytes; +} + + +static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, + struct validate_negotiate_info_req *neg_req, + struct validate_negotiate_info_rsp *neg_rsp) +{ + int ret = 0; + int dialect; + + dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects, + neg_req->DialectCount); + if (dialect == BAD_PROT_ID || dialect != conn->dialect) { + ret = -EINVAL; + goto err_out; + } + + if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { + ret = -EINVAL; + goto err_out; + } + + if (le16_to_cpu(neg_req->SecurityMode) != conn->cli_sec_mode) { + ret = -EINVAL; + goto err_out; + } + + if (le32_to_cpu(neg_req->Capabilities) != conn->cli_cap) { + ret = -EINVAL; + goto err_out; + } + + neg_rsp->Capabilities = cpu_to_le32(conn->vals->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); +err_out: + return ret; +} + +static int fsctl_query_allocated_ranges(struct ksmbd_work *work, uint64_t id, + struct file_allocated_range_buffer *qar_req, + struct file_allocated_range_buffer *qar_rsp, + int in_count, int *out_count) +{ + struct ksmbd_file *fp; + loff_t start, length; + int ret = 0; + + *out_count = 0; + if (in_count == 0) + return -EINVAL; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) + return -ENOENT; + + start = le64_to_cpu(qar_req->file_offset); + length = le64_to_cpu(qar_req->length); + + ret = ksmbd_vfs_fqar_lseek(fp, start, length, + qar_rsp, in_count, out_count); + if (ret && ret != -E2BIG) + *out_count = 0; + + ksmbd_fd_put(work, fp); + return ret; +} + +static int fsctl_pipe_transceive(struct ksmbd_work *work, uint64_t id, + int out_buf_len, struct smb2_ioctl_req *req, struct smb2_ioctl_rsp *rsp) +{ + struct ksmbd_rpc_command *rpc_resp; + char *data_buf = (char *)&req->Buffer[0]; + int nbytes = 0; + + rpc_resp = ksmbd_rpc_ioctl(work->sess, id, + data_buf, + le32_to_cpu(req->InputCount)); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_SOME_NOT_MAPPED) { + /* + * set STATUS_SOME_NOT_MAPPED response + * for unknown domain sid. + */ + rsp->hdr.Status = STATUS_SOME_NOT_MAPPED; + } else if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto out; + } else if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + + nbytes = rpc_resp->payload_sz; + if (rpc_resp->payload_sz > out_buf_len) { + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + nbytes = out_buf_len; + } + + if (!rpc_resp->payload_sz) { + rsp->hdr.Status = + STATUS_UNEXPECTED_IO_ERROR; + goto out; + } + + memcpy((char *)rsp->Buffer, rpc_resp->payload, nbytes); + } +out: + ksmbd_free(rpc_resp); + return nbytes; +} + +static inline int fsctl_set_sparse(struct ksmbd_work *work, uint64_t id, + struct file_sparse *sparse) +{ + struct ksmbd_file *fp; + int ret = 0; + __le32 old_fattr; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) + return -ENOENT; + + old_fattr = fp->f_ci->m_fattr; + if (sparse->SetSparse) + fp->f_ci->m_fattr |= ATTR_SPARSE_FILE_LE; + else + fp->f_ci->m_fattr &= ~ATTR_SPARSE_FILE_LE; + + if (fp->f_ci->m_fattr != old_fattr && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + ret = ksmbd_vfs_get_dos_attrib_xattr(fp->filp->f_path.dentry, &da); + if (ret <= 0) + goto out; + + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + ret = ksmbd_vfs_set_dos_attrib_xattr(fp->filp->f_path.dentry, &da); + if (ret) + fp->f_ci->m_fattr = old_fattr; + } + +out: + ksmbd_fd_put(work, fp); + return ret; +} + +static int fsctl_request_resume_key(struct ksmbd_work *work, + struct smb2_ioctl_req *req, struct resume_key_ioctl_rsp *key_rsp) +{ + struct ksmbd_file *fp; + + fp = ksmbd_lookup_fd_slow(work, + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); + if (!fp) + return -ENOENT; + + memset(key_rsp, 0, sizeof(*key_rsp)); + key_rsp->ResumeKey[0] = req->VolatileFileId; + key_rsp->ResumeKey[1] = req->PersistentFileId; + ksmbd_fd_put(work, fp); + + return 0; +} + +/** + * smb2_ioctl() - handler for smb2 ioctl command + * @work: smb work containing ioctl command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_ioctl(struct ksmbd_work *work) +{ + struct smb2_ioctl_req *req; + struct smb2_ioctl_rsp *rsp, *rsp_org; + int cnt_code, nbytes = 0; + int out_buf_len; + uint64_t id = KSMBD_NO_FID; + struct ksmbd_conn *conn = work->conn; + int ret = 0; + + rsp_org = RESPONSE_BUF(work); + if (work->next_smb2_rcv_hdr_off) { + req = REQUEST_BUF_NEXT(work); + rsp = RESPONSE_BUF_NEXT(work); + if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %u\n", + work->compound_fid); + id = work->compound_fid; + } + } else { + req = REQUEST_BUF(work); + rsp = RESPONSE_BUF(work); + } + + if (!HAS_FILE_ID(id)) + id = le64_to_cpu(req->VolatileFileId); + + if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto out; + } + + cnt_code = le32_to_cpu(req->CntCode); + out_buf_len = le32_to_cpu(req->MaxOutputResponse); + out_buf_len = min(KSMBD_IPC_MAX_PAYLOAD, out_buf_len); + + switch (cnt_code) { + case FSCTL_DFS_GET_REFERRALS: + case FSCTL_DFS_GET_REFERRALS_EX: + /* Not support DFS yet */ + rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED; + goto out; + case FSCTL_CREATE_OR_GET_OBJECT_ID: + { + struct file_object_buf_type1_ioctl_rsp *obj_buf; + + nbytes = sizeof(struct file_object_buf_type1_ioctl_rsp); + obj_buf = (struct file_object_buf_type1_ioctl_rsp *) + &rsp->Buffer[0]; + + /* + * TODO: This is dummy implementation to pass smbtorture + * Need to check correct response later + */ + memset(obj_buf->ObjectId, 0x0, 16); + memset(obj_buf->BirthVolumeId, 0x0, 16); + memset(obj_buf->BirthObjectId, 0x0, 16); + memset(obj_buf->DomainId, 0x0, 16); + + break; + } + case FSCTL_PIPE_TRANSCEIVE: + nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp); + break; + case FSCTL_VALIDATE_NEGOTIATE_INFO: + if (conn->dialect < SMB30_PROT_ID) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = fsctl_validate_negotiate_info(conn, + (struct validate_negotiate_info_req *)&req->Buffer[0], + (struct validate_negotiate_info_rsp *)&rsp->Buffer[0]); + if (ret < 0) + goto out; + + nbytes = sizeof(struct validate_negotiate_info_rsp); + rsp->PersistentFileId = cpu_to_le64(SMB2_NO_FID); + rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID); + break; + case FSCTL_QUERY_NETWORK_INTERFACE_INFO: + nbytes = fsctl_query_iface_info_ioctl(conn, req, rsp); + if (nbytes < 0) + goto out; + break; + case FSCTL_REQUEST_RESUME_KEY: + if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_request_resume_key(work, req, + (struct resume_key_ioctl_rsp *)&rsp->Buffer[0]); + if (ret < 0) + goto out; + rsp->PersistentFileId = req->PersistentFileId; + rsp->VolatileFileId = req->VolatileFileId; + nbytes = sizeof(struct resume_key_ioctl_rsp); + break; + case FSCTL_COPYCHUNK: + case FSCTL_COPYCHUNK_WRITE: + if (!test_tree_conn_flag(work->tcon, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + ret = -EACCES; + goto out; + } + + if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) { + ret = -EINVAL; + goto out; + } + + nbytes = sizeof(struct copychunk_ioctl_rsp); + fsctl_copychunk(work, req, rsp); + break; + case FSCTL_SET_SPARSE: + ret = fsctl_set_sparse(work, id, + (struct file_sparse *)&req->Buffer[0]); + if (ret < 0) + goto out; + break; + case FSCTL_SET_ZERO_DATA: + { + struct file_zero_data_information *zero_data; + struct ksmbd_file *fp; + loff_t off, len; + + if (!test_tree_conn_flag(work->tcon, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + ret = -EACCES; + goto out; + } + + zero_data = + (struct file_zero_data_information *)&req->Buffer[0]; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + ret = -ENOENT; + goto out; + } + + off = le64_to_cpu(zero_data->FileOffset); + len = le64_to_cpu(zero_data->BeyondFinalZero) - off; + + ret = ksmbd_vfs_zero_data(work, fp, off, len); + ksmbd_fd_put(work, fp); + if (ret < 0) + goto out; + break; + } + case FSCTL_QUERY_ALLOCATED_RANGES: + ret = fsctl_query_allocated_ranges(work, id, + (struct file_allocated_range_buffer *)&req->Buffer[0], + (struct file_allocated_range_buffer *)&rsp->Buffer[0], + out_buf_len / + sizeof(struct file_allocated_range_buffer), &nbytes); + if (ret == -E2BIG) { + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + } else if (ret < 0) { + nbytes = 0; + goto out; + } + + nbytes *= sizeof(struct file_allocated_range_buffer); + break; + case FSCTL_GET_REPARSE_POINT: + { + struct reparse_data_buffer *reparse_ptr; + struct ksmbd_file *fp; + + reparse_ptr = (struct reparse_data_buffer *)&rsp->Buffer[0]; + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + ksmbd_err("not found fp!!\n"); + ret = -ENOENT; + goto out; + } + + reparse_ptr->ReparseTag = + smb2_get_reparse_tag_special_file(FP_INODE(fp)->i_mode); + reparse_ptr->ReparseDataLength = 0; + ksmbd_fd_put(work, fp); + nbytes = sizeof(struct reparse_data_buffer); + break; + } + default: + ksmbd_debug(SMB, "not implemented yet ioctl command 0x%x\n", + cnt_code); + ret = -EOPNOTSUPP; + goto out; + } + + rsp->CntCode = cpu_to_le32(cnt_code); + rsp->InputCount = cpu_to_le32(0); + rsp->InputOffset = cpu_to_le32(112); + rsp->OutputOffset = cpu_to_le32(112); + rsp->OutputCount = cpu_to_le32(nbytes); + rsp->StructureSize = cpu_to_le16(49); + rsp->Reserved = cpu_to_le16(0); + rsp->Flags = cpu_to_le32(0); + rsp->Reserved2 = cpu_to_le32(0); + inc_rfc1001_len(rsp_org, 48 + nbytes); + + return 0; + +out: + if (ret == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (ret == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + else if (ret == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (ret < 0 || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + return 0; +} + +/** + * smb20_oplock_break_ack() - handler for smb2.0 oplock break command + * @work: smb work containing oplock break command buffer + * + * Return: 0 + */ +static void smb20_oplock_break_ack(struct ksmbd_work *work) +{ + struct smb2_oplock_break *req = REQUEST_BUF(work); + struct smb2_oplock_break *rsp = RESPONSE_BUF(work); + struct ksmbd_file *fp; + struct oplock_info *opinfo = NULL; + __le32 err = 0; + int ret = 0; + uint64_t volatile_id, persistent_id; + char req_oplevel = 0, rsp_oplevel = 0; + unsigned int oplock_change_type; + + volatile_id = le64_to_cpu(req->VolatileFid); + persistent_id = le64_to_cpu(req->PersistentFid); + req_oplevel = req->OplockLevel; + ksmbd_debug(OPLOCK, "v_id %llu, p_id %llu request oplock level %d\n", + volatile_id, persistent_id, req_oplevel); + + fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); + if (!fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); + return; + } + + opinfo = opinfo_get(fp); + if (!opinfo) { + ksmbd_err("unexpected null oplock_info\n"); + rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return; + } + + if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) { + rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + goto err_out; + } + + if (opinfo->op_state == OPLOCK_STATE_NONE) { + ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + if (((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) || + (opinfo->level == SMB2_OPLOCK_LEVEL_BATCH)) && + ((req_oplevel != SMB2_OPLOCK_LEVEL_II) && + (req_oplevel != SMB2_OPLOCK_LEVEL_NONE))) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + oplock_change_type = OPLOCK_WRITE_TO_NONE; + } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_II) && + (req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + oplock_change_type = OPLOCK_READ_TO_NONE; + } else if ((req_oplevel == SMB2_OPLOCK_LEVEL_II) || + (req_oplevel == SMB2_OPLOCK_LEVEL_NONE)) { + err = STATUS_INVALID_DEVICE_STATE; + if (((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) || + (opinfo->level == SMB2_OPLOCK_LEVEL_BATCH)) && + (req_oplevel == SMB2_OPLOCK_LEVEL_II)) { + oplock_change_type = OPLOCK_WRITE_TO_READ; + } else if (((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) + || (opinfo->level == SMB2_OPLOCK_LEVEL_BATCH)) && + (req_oplevel == SMB2_OPLOCK_LEVEL_NONE)) { + oplock_change_type = OPLOCK_WRITE_TO_NONE; + } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_II) && + (req_oplevel == SMB2_OPLOCK_LEVEL_NONE)) { + oplock_change_type = OPLOCK_READ_TO_NONE; + } else + oplock_change_type = 0; + } else + oplock_change_type = 0; + + switch (oplock_change_type) { + case OPLOCK_WRITE_TO_READ: + ret = opinfo_write_to_read(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_II; + break; + case OPLOCK_WRITE_TO_NONE: + ret = opinfo_write_to_none(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; + break; + case OPLOCK_READ_TO_NONE: + ret = opinfo_read_to_none(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; + break; + default: + ksmbd_err("unknown oplock change 0x%x -> 0x%x\n", + opinfo->level, rsp_oplevel); + } + + if (ret < 0) { + rsp->hdr.Status = err; + goto err_out; + } + + opinfo_put(opinfo); + ksmbd_fd_put(work, fp); + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + + rsp->StructureSize = cpu_to_le16(24); + rsp->OplockLevel = rsp_oplevel; + rsp->Reserved = 0; + rsp->Reserved2 = 0; + rsp->VolatileFid = cpu_to_le64(volatile_id); + rsp->PersistentFid = cpu_to_le64(persistent_id); + inc_rfc1001_len(rsp, 24); + return; + +err_out: + 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) +{ + if ((lease->new_state == + (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) + && !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { + lease->new_state = req_state; + return 0; + } + + if (lease->new_state == req_state) + return 0; + + return 1; +} + +/** + * smb21_lease_break_ack() - handler for smb2.1 lease break command + * @work: smb work containing lease break command buffer + * + * Return: 0 + */ +static void smb21_lease_break_ack(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_lease_ack *req = REQUEST_BUF(work); + struct smb2_lease_ack *rsp = RESPONSE_BUF(work); + struct oplock_info *opinfo; + __le32 err = 0; + int ret = 0; + unsigned int lease_change_type; + __le32 lease_state; + struct lease *lease; + + ksmbd_debug(OPLOCK, "smb21 lease break, lease state(0x%x)\n", + le32_to_cpu(req->LeaseState)); + opinfo = lookup_lease_in_table(conn, req->LeaseKey); + if (!opinfo) { + ksmbd_debug(OPLOCK, "file not opened\n"); + smb2_set_err_rsp(work); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + return; + } + lease = opinfo->o_lease; + + if (opinfo->op_state == OPLOCK_STATE_NONE) { + ksmbd_err("unexpected lease break state 0x%x\n", + opinfo->op_state); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + if (check_lease_state(lease, req->LeaseState)) { + rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; + ksmbd_debug(OPLOCK, + "req lease state: 0x%x, expected state: 0x%x\n", + req->LeaseState, lease->new_state); + goto err_out; + } + + if (!atomic_read(&opinfo->breaking_cnt)) { + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + /* check for bad lease state */ + if (req->LeaseState & (~(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE))) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_NONE; + else + lease_change_type = OPLOCK_READ_TO_NONE; + ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } else if ((lease->state == SMB2_LEASE_READ_CACHING_LE) && + (req->LeaseState != SMB2_LEASE_NONE_LE)) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + lease_change_type = OPLOCK_READ_TO_NONE; + ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } else { + /* valid lease state changes */ + err = STATUS_INVALID_DEVICE_STATE; + if (req->LeaseState == SMB2_LEASE_NONE_LE) { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_NONE; + else + lease_change_type = OPLOCK_READ_TO_NONE; + } else if (req->LeaseState & SMB2_LEASE_READ_CACHING_LE) { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_READ; + else + lease_change_type = OPLOCK_READ_HANDLE_TO_READ; + } else + lease_change_type = 0; + } + + switch (lease_change_type) { + case OPLOCK_WRITE_TO_READ: + ret = opinfo_write_to_read(opinfo); + break; + case OPLOCK_READ_HANDLE_TO_READ: + ret = opinfo_read_handle_to_read(opinfo); + break; + case OPLOCK_WRITE_TO_NONE: + ret = opinfo_write_to_none(opinfo); + break; + case OPLOCK_READ_TO_NONE: + ret = opinfo_read_to_none(opinfo); + break; + default: + ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } + + 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); + + if (ret < 0) { + rsp->hdr.Status = err; + goto err_out; + } + + rsp->StructureSize = cpu_to_le16(36); + rsp->Reserved = 0; + rsp->Flags = 0; + memcpy(rsp->LeaseKey, req->LeaseKey, 16); + rsp->LeaseState = lease_state; + rsp->LeaseDuration = 0; + inc_rfc1001_len(rsp, 36); + return; + +err_out: + 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 + */ +int smb2_oplock_break(struct ksmbd_work *work) +{ + struct smb2_oplock_break *req = REQUEST_BUF(work); + struct smb2_oplock_break *rsp = RESPONSE_BUF(work); + + switch (le16_to_cpu(req->StructureSize)) { + case OP_BREAK_STRUCT_SIZE_20: + smb20_oplock_break_ack(work); + break; + case OP_BREAK_STRUCT_SIZE_21: + smb21_lease_break_ack(work); + break; + default: + ksmbd_debug(OPLOCK, "invalid break cmd %d\n", + le16_to_cpu(req->StructureSize)); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + } + + return 0; +} + +/** + * smb2_notify() - handler for smb2 notify request + * @ksmbd_work: smb work containing notify command buffer + * + * Return: 0 + */ +int smb2_notify(struct ksmbd_work *work) +{ + struct smb2_notify_req *req; + struct smb2_notify_rsp *rsp; + + 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; + } + + smb2_set_err_rsp(work); + rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; + return 0; +} + +/** + * smb2_is_sign_req() - handler for checking packet signing status + * @work:smb work containing notify command buffer + * + * Return: true if packed is signed, false otherwise + */ +bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) +{ + struct smb2_hdr *rcv_hdr2 = REQUEST_BUF(work); + + if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && + command != SMB2_NEGOTIATE_HE && + command != SMB2_SESSION_SETUP_HE && + command != SMB2_OPLOCK_BREAK_HE) + return true; + + return 0; +} + +/** + * smb2_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb2_check_sign_req(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr, *hdr_org; + char signature_req[SMB2_SIGNATURE_SIZE]; + char signature[SMB2_HMACSHA256_SIZE]; + struct kvec iov[1]; + size_t len; + + hdr_org = hdr = REQUEST_BUF(work); + if (work->next_smb2_rcv_hdr_off) + hdr = REQUEST_BUF_NEXT(work); + + if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) + len = be32_to_cpu(hdr_org->smb2_buf_length); + else if (hdr->NextCommand) + len = le32_to_cpu(hdr->NextCommand); + else + len = be32_to_cpu(hdr_org->smb2_buf_length) - + work->next_smb2_rcv_hdr_off; + + memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + 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; + + if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + ksmbd_err("bad smb2 signature\n"); + return 0; + } + + return 1; +} + +/** + * smb2_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb2_set_sign_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr, *hdr_org; + struct smb2_hdr *req_hdr; + char signature[SMB2_HMACSHA256_SIZE]; + struct kvec iov[2]; + size_t len; + int n_vec = 1; + + hdr_org = hdr = RESPONSE_BUF(work); + if (work->next_smb2_rsp_hdr_off) + hdr = RESPONSE_BUF_NEXT(work); + + req_hdr = REQUEST_BUF_NEXT(work); + + if (!work->next_smb2_rsp_hdr_off) { + len = get_rfc1002_len(hdr_org); + if (req_hdr->NextCommand) + len = ALIGN(len, 8); + } else { + len = get_rfc1002_len(hdr_org) - work->next_smb2_rsp_hdr_off; + len = ALIGN(len, 8); + } + + if (req_hdr->NextCommand) + hdr->NextCommand = cpu_to_le32(len); + + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (HAS_AUX_PAYLOAD(work)) { + iov[0].iov_len -= AUX_PAYLOAD_SIZE(work); + + iov[1].iov_base = AUX_PAYLOAD(work); + iov[1].iov_len = AUX_PAYLOAD_SIZE(work); + n_vec++; + } + + if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, + signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); +} + +/** + * smb3_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb3_check_sign_req(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn; + char *signing_key; + struct smb2_hdr *hdr, *hdr_org; + struct channel *chann; + char signature_req[SMB2_SIGNATURE_SIZE]; + char signature[SMB2_CMACAES_SIZE]; + struct kvec iov[1]; + size_t len; + + hdr_org = hdr = REQUEST_BUF(work); + if (work->next_smb2_rcv_hdr_off) + hdr = REQUEST_BUF_NEXT(work); + + if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) + len = be32_to_cpu(hdr_org->smb2_buf_length); + else if (hdr->NextCommand) + len = le32_to_cpu(hdr->NextCommand); + else + len = be32_to_cpu(hdr_org->smb2_buf_length) - + work->next_smb2_rcv_hdr_off; + + if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + signing_key = work->sess->smb3signingkey; + conn = work->sess->conn; + } else { + chann = lookup_chann_list(work->sess); + if (!chann) + return 0; + signing_key = chann->smb3signingkey; + conn = chann->conn; + } + + if (!signing_key) { + ksmbd_err("SMB3 signing key is not generated\n"); + return 0; + } + + memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature)) + return 0; + + if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + ksmbd_err("bad smb2 signature\n"); + return 0; + } + + return 1; +} + +/** + * smb3_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb3_set_sign_rsp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn; + struct smb2_hdr *req_hdr; + struct smb2_hdr *hdr, *hdr_org; + struct channel *chann; + char signature[SMB2_CMACAES_SIZE]; + struct kvec iov[2]; + int n_vec = 1; + size_t len; + char *signing_key; + + hdr_org = hdr = RESPONSE_BUF(work); + if (work->next_smb2_rsp_hdr_off) + hdr = RESPONSE_BUF_NEXT(work); + + req_hdr = REQUEST_BUF_NEXT(work); + + if (!work->next_smb2_rsp_hdr_off) { + len = get_rfc1002_len(hdr_org); + if (req_hdr->NextCommand) + len = ALIGN(len, 8); + } else { + len = get_rfc1002_len(hdr_org) - work->next_smb2_rsp_hdr_off; + len = ALIGN(len, 8); + } + + if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + signing_key = work->sess->smb3signingkey; + conn = work->sess->conn; + } else { + chann = lookup_chann_list(work->sess); + if (!chann) + return; + signing_key = chann->smb3signingkey; + conn = chann->conn; + } + + if (!signing_key) + return; + + if (req_hdr->NextCommand) + hdr->NextCommand = cpu_to_le32(len); + + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + if (HAS_AUX_PAYLOAD(work)) { + iov[0].iov_len -= AUX_PAYLOAD_SIZE(work); + iov[1].iov_base = AUX_PAYLOAD(work); + iov[1].iov_len = AUX_PAYLOAD_SIZE(work); + n_vec++; + } + + if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); +} + +/** + * smb3_preauth_hash_rsp() - handler for computing preauth hash on response + * @work: smb work containing response buffer + * + */ +void smb3_preauth_hash_rsp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct smb2_hdr *req, *rsp; + + if (conn->dialect != SMB311_PROT_ID) + return; + + WORK_BUFFERS(work, req, rsp); + + if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE) + ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, + conn->preauth_info->Preauth_HashValue); + + if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && + sess && sess->state == SMB2_SESSION_IN_PROGRESS) { + __u8 *hash_value; + + hash_value = sess->Preauth_HashValue; + ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, + hash_value); + } +} + +static void fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, + char *old_buf, + __le16 cipher_type) +{ + struct smb2_hdr *hdr = (struct smb2_hdr *)old_buf; + unsigned int orig_len = get_rfc1002_len(old_buf); + + memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); + tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; + tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); + tr_hdr->Flags = cpu_to_le16(0x01); + if (cipher_type == SMB2_ENCRYPTION_AES128_GCM) + get_random_bytes(&tr_hdr->Nonce, SMB3_AES128GCM_NONCE); + else + get_random_bytes(&tr_hdr->Nonce, SMB3_AES128CCM_NONCE); + memcpy(&tr_hdr->SessionId, &hdr->SessionId, 8); + inc_rfc1001_len(tr_hdr, sizeof(struct smb2_transform_hdr) - 4); + inc_rfc1001_len(tr_hdr, orig_len); +} + +int smb3_encrypt_resp(struct ksmbd_work *work) +{ + char *buf = RESPONSE_BUF(work); + struct smb2_transform_hdr *tr_hdr; + struct kvec iov[3]; + int rc = -ENOMEM; + int buf_size = 0, rq_nvec = 2 + (HAS_AUX_PAYLOAD(work) ? 1 : 0); + + if (ARRAY_SIZE(iov) < rq_nvec) + return -ENOMEM; + + tr_hdr = ksmbd_alloc_response(sizeof(struct smb2_transform_hdr)); + if (!tr_hdr) + return rc; + + /* fill transform header */ + fill_transform_hdr(tr_hdr, buf, work->conn->cipher_type); + + iov[0].iov_base = tr_hdr; + iov[0].iov_len = sizeof(struct smb2_transform_hdr); + buf_size += iov[0].iov_len - 4; + + iov[1].iov_base = buf + 4; + iov[1].iov_len = get_rfc1002_len(buf); + if (HAS_AUX_PAYLOAD(work)) { + iov[1].iov_len = RESP_HDR_SIZE(work) - 4; + + iov[2].iov_base = AUX_PAYLOAD(work); + iov[2].iov_len = AUX_PAYLOAD_SIZE(work); + buf_size += iov[2].iov_len; + } + buf_size += iov[1].iov_len; + work->resp_hdr_sz = iov[1].iov_len; + + rc = ksmbd_crypt_message(work->conn, iov, rq_nvec, 1); + if (rc) + return rc; + + memmove(buf, iov[1].iov_base, iov[1].iov_len); + tr_hdr->smb2_buf_length = cpu_to_be32(buf_size); + work->tr_buf = tr_hdr; + + return rc; +} + +int smb3_is_transform_hdr(void *buf) +{ + struct smb2_transform_hdr *trhdr = buf; + + return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; +} + +int smb3_decrypt_req(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess; + char *buf = REQUEST_BUF(work); + struct smb2_hdr *hdr; + unsigned int pdu_length = get_rfc1002_len(buf); + struct kvec iov[2]; + unsigned int buf_data_size = pdu_length + 4 - + sizeof(struct smb2_transform_hdr); + struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; + unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + int rc = 0; + + sess = ksmbd_session_lookup(conn, le64_to_cpu(tr_hdr->SessionId)); + if (!sess) { + ksmbd_err("invalid session id(%llx) in transform header\n", + le64_to_cpu(tr_hdr->SessionId)); + return -ECONNABORTED; + } + + if (pdu_length + 4 < sizeof(struct smb2_transform_hdr) + + sizeof(struct smb2_hdr)) { + ksmbd_err("Transform message is too small (%u)\n", + pdu_length); + return -ECONNABORTED; + } + + if (pdu_length + 4 < orig_len + sizeof(struct smb2_transform_hdr)) { + ksmbd_err("Transform message is broken\n"); + return -ECONNABORTED; + } + + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr); + iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr); + iov[1].iov_len = buf_data_size; + rc = ksmbd_crypt_message(conn, iov, 2, 0); + if (rc) + return rc; + + memmove(buf + 4, iov[1].iov_base, buf_data_size); + hdr = (struct smb2_hdr *)buf; + hdr->smb2_buf_length = cpu_to_be32(buf_data_size); + + return rc; +} + +bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *rsp = RESPONSE_BUF(work); + + if (conn->dialect < SMB30_PROT_ID) + return false; + + if (work->next_smb2_rcv_hdr_off) + rsp = RESPONSE_BUF_NEXT(work); + + if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && + rsp->Status == STATUS_SUCCESS) + return true; + return false; +} diff --git a/fs/cifsd/smb2pdu.h b/fs/cifsd/smb2pdu.h new file mode 100644 index 000000000000..deb3d7444c2a --- /dev/null +++ b/fs/cifsd/smb2pdu.h @@ -0,0 +1,1649 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef _SMB2PDU_H +#define _SMB2PDU_H + +#include "ntlmssp.h" +#include "smbacl.h" + +/* + * Note that, due to trying to use names similar to the protocol specifications, + * there are many mixed case field names in the structures below. Although + * this does not match typical Linux kernel style, it is necessary to be + * able to match against the protocol specfication. + * + * SMB2 commands + * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses + * (ie no useful data other than the SMB error code itself) and are marked such. + * Knowing this helps avoid response buffer allocations and copy in some cases. + */ + +/* List of commands in host endian */ +#define SMB2_NEGOTIATE_HE 0x0000 +#define SMB2_SESSION_SETUP_HE 0x0001 +#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */ +#define SMB2_TREE_CONNECT_HE 0x0003 +#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */ +#define SMB2_CREATE_HE 0x0005 +#define SMB2_CLOSE_HE 0x0006 +#define SMB2_FLUSH_HE 0x0007 /* trivial resp */ +#define SMB2_READ_HE 0x0008 +#define SMB2_WRITE_HE 0x0009 +#define SMB2_LOCK_HE 0x000A +#define SMB2_IOCTL_HE 0x000B +#define SMB2_CANCEL_HE 0x000C +#define SMB2_ECHO_HE 0x000D +#define SMB2_QUERY_DIRECTORY_HE 0x000E +#define SMB2_CHANGE_NOTIFY_HE 0x000F +#define SMB2_QUERY_INFO_HE 0x0010 +#define SMB2_SET_INFO_HE 0x0011 +#define SMB2_OPLOCK_BREAK_HE 0x0012 + +/* The same list in little endian */ +#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) +#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE) +#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE) +#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE) +#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE) +#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE) +#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE) +#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE) +#define SMB2_READ cpu_to_le16(SMB2_READ_HE) +#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE) +#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE) +#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE) +#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE) +#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE) +#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) +#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) +#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE) +#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) +#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) + +/*Create Action Flags*/ +#define FILE_SUPERSEDED 0x00000000 +#define FILE_OPENED 0x00000001 +#define FILE_CREATED 0x00000002 +#define FILE_OVERWRITTEN 0x00000003 + +/* + * Size of the session key (crypto key encrypted with the password + */ +#define SMB2_NTLMV2_SESSKEY_SIZE 16 +#define SMB2_SIGNATURE_SIZE 16 +#define SMB2_HMACSHA256_SIZE 32 +#define SMB2_CMACAES_SIZE 16 + +/* + * Size of the smb3 signing key + */ +#define SMB3_SIGN_KEY_SIZE 16 + +#define CIFS_CLIENT_CHALLENGE_SIZE 8 +#define SMB_SERVER_CHALLENGE_SIZE 8 + +/* SMB2 Max Credits */ +#define SMB2_MAX_CREDITS 8192 + +#define SMB2_CLIENT_GUID_SIZE 16 +#define SMB2_CREATE_GUID_SIZE 16 + +/* Maximum buffer size value we can send with 1 credit */ +#define SMB2_MAX_BUFFER_SIZE 65536 + +#define NUMBER_OF_SMB2_COMMANDS 0x0013 + +/* BB FIXME - analyze following length BB */ +#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ + +#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) /* 'B''M''S' */ +#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) + +#define SMB21_DEFAULT_IOSIZE (1024 * 1024) +#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) +#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) + +/* + * SMB2 Header Definition + * + * "MBZ" : Must be Zero + * "BB" : BugBug, Something to check/review/analyze later + * "PDU" : "Protocol Data Unit" (ie a network "frame") + * + */ + +#define __SMB2_HEADER_STRUCTURE_SIZE 64 +#define SMB2_HEADER_STRUCTURE_SIZE \ + cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE) + +struct smb2_hdr { + __be32 smb2_buf_length; /* big endian on wire */ + /* + * length is only two or three bytes - with + * one or two byte type preceding it that MBZ + */ + __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ + __le16 StructureSize; /* 64 */ + __le16 CreditCharge; /* MBZ */ + __le32 Status; /* Error from server */ + __le16 Command; + __le16 CreditRequest; /* CreditResponse */ + __le32 Flags; + __le32 NextCommand; + __le64 MessageId; + union { + struct { + __le32 ProcessId; + __le32 TreeId; + } __packed SyncId; + __le64 AsyncId; + } __packed Id; + __le64 SessionId; + __u8 Signature[16]; +} __packed; + +struct smb2_pdu { + struct smb2_hdr hdr; + __le16 StructureSize2; /* size of wct area (varies, request specific) */ +} __packed; + +#define SMB3_AES128CCM_NONCE 11 +#define SMB3_AES128GCM_NONCE 12 + +struct smb2_transform_hdr { + __be32 smb2_buf_length; /* big endian on wire */ + /* + * length is only two or three bytes - with + * one or two byte type preceding it that MBZ + */ + __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ + __u8 Signature[16]; + __u8 Nonce[16]; + __le32 OriginalMessageSize; + __u16 Reserved1; + __le16 Flags; /* EncryptionAlgorithm */ + __le64 SessionId; +} __packed; + +/* + * SMB2 flag definitions + */ +#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001) +#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) +#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) +#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) +#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) +#define SMB2_FLAGS_REPLAY_OPERATIONS cpu_to_le32(0x20000000) + +/* + * Definitions for SMB2 Protocol Data Units (network frames) + * + * See MS-SMB2.PDF specification for protocol details. + * The Naming convention is the lower case version of the SMB2 + * command code name for the struct. Note that structures must be packed. + * + */ + +#define SMB2_ERROR_STRUCTURE_SIZE2 9 +#define SMB2_ERROR_STRUCTURE_SIZE2_LE cpu_to_le16(SMB2_ERROR_STRUCTURE_SIZE2) + +struct smb2_err_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; + __u8 ErrorContextCount; + __u8 Reserved; + __le32 ByteCount; /* even if zero, at least one byte follows */ + __u8 ErrorData[1]; /* variable length */ +} __packed; + +struct smb2_negotiate_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 DialectCount; + __le16 SecurityMode; + __le16 Reserved; /* MBZ */ + __le32 Capabilities; + __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE]; + /* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */ + __le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */ + __le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */ + __le16 Reserved2; + __le16 Dialects[1]; /* One dialect (vers=) at a time for now */ +} __packed; + +/* SecurityMode flags */ +#define SMB2_NEGOTIATE_SIGNING_ENABLED_LE cpu_to_le16(0x0001) +#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002 +#define SMB2_NEGOTIATE_SIGNING_REQUIRED_LE cpu_to_le16(0x0002) +/* Capabilities flags */ +#define SMB2_GLOBAL_CAP_DFS 0x00000001 +#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */ +/* Internal types */ +#define SMB2_NT_FIND 0x00100000 +#define SMB2_LARGE_FILES 0x00200000 + +#define SMB311_SALT_SIZE 32 +/* Hash Algorithm Types */ +#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001) + +#define PREAUTH_HASHVALUE_SIZE 64 + +struct preauth_integrity_info { + /* PreAuth integrity Hash ID */ + __le16 Preauth_HashId; + /* PreAuth integrity Hash Value */ + __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; +}; + +/* offset is sizeof smb2_negotiate_rsp - 4 but rounded up to 8 bytes. */ +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +/* sizeof(struct smb2_negotiate_rsp) - 4 = + * header(64) + response(64) + GSS_LENGTH(96) + GSS_PADDING(0) + */ +#define OFFSET_OF_NEG_CONTEXT 0xe0 +#else +/* sizeof(struct smb2_negotiate_rsp) - 4 = + * header(64) + response(64) + GSS_LENGTH(74) + GSS_PADDING(6) + */ +#define OFFSET_OF_NEG_CONTEXT 0xd0 +#endif + +#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1) +#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2) +#define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3) +#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5) +#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100) + +struct smb2_neg_context { + __le16 ContextType; + __le16 DataLength; + __le32 Reserved; + /* Followed by array of data */ +} __packed; + +struct smb2_preauth_neg_context { + __le16 ContextType; /* 1 */ + __le16 DataLength; + __le32 Reserved; + __le16 HashAlgorithmCount; /* 1 */ + __le16 SaltLength; + __le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */ + __u8 Salt[SMB311_SALT_SIZE]; +} __packed; + +/* Encryption Algorithms Ciphers */ +#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) +#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) + +struct smb2_encryption_neg_context { + __le16 ContextType; /* 2 */ + __le16 DataLength; + __le32 Reserved; + __le16 CipherCount; /* AES-128-GCM and AES-128-CCM */ + __le16 Ciphers[1]; /* Ciphers[0] since only one used now */ +} __packed; + +#define SMB3_COMPRESS_NONE cpu_to_le16(0x0000) +#define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001) +#define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002) +#define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003) + +struct smb2_compression_ctx { + __le16 ContextType; /* 3 */ + __le16 DataLength; + __le32 Reserved; + __le16 CompressionAlgorithmCount; + __u16 Padding; + __le32 Reserved1; + __le16 CompressionAlgorithms[1]; +} __packed; + +#define POSIX_CTXT_DATA_LEN 16 +struct smb2_posix_neg_context { + __le16 ContextType; /* 0x100 */ + __le16 DataLength; + __le32 Reserved; + __u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */ +} __packed; + +struct smb2_netname_neg_context { + __le16 ContextType; /* 0x100 */ + __le16 DataLength; + __le32 Reserved; + __le16 NetName[0]; /* hostname of target converted to UCS-2 */ +} __packed; + +struct smb2_negotiate_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 65 */ + __le16 SecurityMode; + __le16 DialectRevision; + __le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */ + __u8 ServerGUID[16]; + __le32 Capabilities; + __le32 MaxTransactSize; + __le32 MaxReadSize; + __le32 MaxWriteSize; + __le64 SystemTime; /* MBZ */ + __le64 ServerStartTime; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */ + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +/* Flags */ +#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 +#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 + +#define SMB2_SESSION_EXPIRED (0) +#define SMB2_SESSION_IN_PROGRESS (1 << 0) +#define SMB2_SESSION_VALID (1 << 1) + +/* Flags */ +#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 +#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 + +struct smb2_sess_setup_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 25 */ + __u8 Flags; + __u8 SecurityMode; + __le32 Capabilities; + __le32 Channel; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le64 PreviousSessionId; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +/* Flags/Reserved for SMB3.1.1 */ +#define SMB2_SHAREFLAG_CLUSTER_RECONNECT 0x0001 + +/* Currently defined SessionFlags */ +#define SMB2_SESSION_FLAG_IS_GUEST_LE cpu_to_le16(0x0001) +#define SMB2_SESSION_FLAG_IS_NULL_LE cpu_to_le16(0x0002) +#define SMB2_SESSION_FLAG_ENCRYPT_DATA_LE cpu_to_le16(0x0004) +struct smb2_sess_setup_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 SessionFlags; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +struct smb2_logoff_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_logoff_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_tree_connect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 Reserved; /* Flags in SMB3.1.1 */ + __le16 PathOffset; + __le16 PathLength; + __u8 Buffer[1]; /* variable length */ +} __packed; + +struct smb2_tree_connect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 16 */ + __u8 ShareType; /* see below */ + __u8 Reserved; + __le32 ShareFlags; /* see below */ + __le32 Capabilities; /* see below */ + __le32 MaximalAccess; +} __packed; + +/* Possible ShareType values */ +#define SMB2_SHARE_TYPE_DISK 0x01 +#define SMB2_SHARE_TYPE_PIPE 0x02 +#define SMB2_SHARE_TYPE_PRINT 0x03 + +/* + * Possible ShareFlags - exactly one and only one of the first 4 caching flags + * must be set (any of the remaining, SHI1005, flags may be set individually + * or in combination. + */ +#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 +#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 +#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 +#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 +#define SHI1005_FLAGS_DFS 0x00000001 +#define SHI1005_FLAGS_DFS_ROOT 0x00000002 +#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 +#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 +#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 +#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 +#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 +#define SHI1005_FLAGS_ENABLE_HASH 0x00002000 + +/* Possible share capabilities */ +#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) + +struct smb2_tree_disconnect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_tree_disconnect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY) +#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN) +#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM) +#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY) +#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE) +#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL) +#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY) +#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE) +#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE) +#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED) +#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE) +#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED) +#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED) +#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000) +#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) +#define ATTR_MASK_LE cpu_to_le32(0x00007FB7) + +/* Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF +/* Non-spec internal type */ +#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99 + +/* Desired Access Flags */ +#define FILE_READ_DATA_LE cpu_to_le32(0x00000001) +#define FILE_LIST_DIRECTORY_LE cpu_to_le32(0x00000001) +#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002) +#define FILE_ADD_FILE_LE cpu_to_le32(0x00000002) +#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004) +#define FILE_ADD_SUBDIRECTORY_LE cpu_to_le32(0x00000004) +#define FILE_READ_EA_LE cpu_to_le32(0x00000008) +#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010) +#define FILE_EXECUTE_LE cpu_to_le32(0x00000020) +#define FILE_TRAVERSE_LE cpu_to_le32(0x00000020) +#define FILE_DELETE_CHILD_LE cpu_to_le32(0x00000040) +#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080) +#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100) +#define FILE_DELETE_LE cpu_to_le32(0x00010000) +#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000) +#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000) +#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000) +#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000) +#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000) +#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000) +#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000) +#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000) +#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000) +#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000) +#define DESIRED_ACCESS_MASK cpu_to_le32(0xF21F01FF) + +/* ShareAccess Flags */ +#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001) +#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002) +#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004) +#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007) + +/* CreateDisposition Flags */ +#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000) +#define FILE_OPEN_LE cpu_to_le32(0x00000001) +#define FILE_CREATE_LE cpu_to_le32(0x00000002) +#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003) +#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004) +#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005) +#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007) + +#define FILE_READ_DESIRED_ACCESS_LE (FILE_READ_DATA_LE | \ + FILE_READ_EA_LE | \ + FILE_GENERIC_READ_LE) +#define FILE_WRITE_DESIRE_ACCESS_LE (FILE_WRITE_DATA_LE | \ + FILE_APPEND_DATA_LE | \ + FILE_WRITE_EA_LE | \ + FILE_WRITE_ATTRIBUTES_LE | \ + FILE_GENERIC_WRITE_LE) + +/* Impersonation Levels */ +#define IL_ANONYMOUS_LE cpu_to_le32(0x00000000) +#define IL_IDENTIFICATION_LE cpu_to_le32(0x00000001) +#define IL_IMPERSONATION_LE cpu_to_le32(0x00000002) +#define IL_DELEGATE_LE cpu_to_le32(0x00000003) + +/* Create Context Values */ +#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */ +#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */ +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC" +#define SMB2_CREATE_ALLOCATION_SIZE "AlSi" +#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" +#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp" +#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid" +#define SMB2_CREATE_REQUEST_LEASE "RqLs" +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 "DH2C" +#define SMB2_CREATE_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74" + #define SMB2_CREATE_APP_INSTANCE_VERSION "\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10" +#define SVHDX_OPEN_DEVICE_CONTEXT 0x83CE6F1AD851E0986E34401CC9BCFCE9 +#define SMB2_CREATE_TAG_POSIX "\x93\xAD\x25\x50\x9C\xB4\x11\xE7\xB4\x23\x83\xDE\x96\x8B\xCD\x7C" + +struct smb2_create_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u8 SecurityFlags; + __u8 RequestedOplockLevel; + __le32 ImpersonationLevel; + __le64 SmbCreateFlags; + __le64 Reserved; + __le32 DesiredAccess; + __le32 FileAttributes; + __le32 ShareAccess; + __le32 CreateDisposition; + __le32 CreateOptions; + __le16 NameOffset; + __le16 NameLength; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[0]; +} __packed; + +struct smb2_create_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 89 */ + __u8 OplockLevel; + __u8 Reserved; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndofFile; + __le32 FileAttributes; + __le32 Reserved2; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[1]; +} __packed; + +struct create_context { + __le32 Next; + __le16 NameOffset; + __le16 NameLength; + __le16 Reserved; + __le16 DataOffset; + __le32 DataLength; + __u8 Buffer[0]; +} __packed; + +struct create_durable_req_v2 { + struct create_context ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; + __u8 Reserved[8]; + __u8 CreateGuid[16]; +} __packed; + +struct create_durable_reconn_req { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[16]; + struct { + __le64 PersistentFileId; + __le64 VolatileFileId; + } Fid; + } Data; +} __packed; + +struct create_durable_reconn_v2_req { + struct create_context ccontext; + __u8 Name[8]; + struct { + __le64 PersistentFileId; + __le64 VolatileFileId; + } Fid; + __u8 CreateGuid[16]; + __le32 Flags; +} __packed; + +struct create_app_inst_id { + struct create_context ccontext; + __u8 Name[8]; + __u8 Reserved[8]; + __u8 AppInstanceId[16]; +} __packed; + +struct create_app_inst_id_vers { + struct create_context ccontext; + __u8 Name[8]; + __u8 Reserved[2]; + __u8 Padding[4]; + __le64 AppInstanceVersionHigh; + __le64 AppInstanceVersionLow; +} __packed; + +struct create_mxac_req { + struct create_context ccontext; + __u8 Name[8]; + __le64 Timestamp; +} __packed; + +struct create_alloc_size_req { + struct create_context ccontext; + __u8 Name[8]; + __le64 AllocationSize; +} __packed; + +struct create_posix { + struct create_context ccontext; + __u8 Name[16]; + __le32 Mode; + __u32 Reserved; +} __packed; + +struct create_durable_rsp { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[8]; + __u64 data; + } Data; +} __packed; + +struct create_durable_v2_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; +} __packed; + +struct create_mxac_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le32 QueryStatus; + __le32 MaximalAccess; +} __packed; + +struct create_disk_id_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le64 DiskFileId; + __le64 VolumeId; + __u8 Reserved[16]; +} __packed; + +/* equivalent of the contents of SMB3.1.1 POSIX open context response */ +struct create_posix_rsp { + struct create_context ccontext; + __u8 Name[16]; + __le32 nlink; + __le32 reparse_tag; + __le32 mode; + u8 SidBuffer[40]; +} __packed; + +#define SMB2_LEASE_NONE_LE cpu_to_le32(0x00) +#define SMB2_LEASE_READ_CACHING_LE cpu_to_le32(0x01) +#define SMB2_LEASE_HANDLE_CACHING_LE cpu_to_le32(0x02) +#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04) + +#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) + +struct lease_context { + __le64 LeaseKeyLow; + __le64 LeaseKeyHigh; + __le32 LeaseState; + __le32 LeaseFlags; + __le64 LeaseDuration; +} __packed; + +struct create_lease { + struct create_context ccontext; + __u8 Name[8]; + struct lease_context lcontext; +} __packed; + +/* Currently defined values for close flags */ +#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) +struct smb2_close_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Flags; + __le32 Reserved; + __le64 PersistentFileId; + __le64 VolatileFileId; +} __packed; + +struct smb2_close_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* 60 */ + __le16 Flags; + __le32 Reserved; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; + __le32 Attributes; +} __packed; + +struct smb2_flush_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Reserved1; + __le32 Reserved2; + __le64 PersistentFileId; + __le64 VolatileFileId; +} __packed; + +struct smb2_flush_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; + __le16 Reserved; +} __packed; + +struct smb2_buffer_desc_v1 { + __le64 offset; + __le32 token; + __le32 length; +} __packed; + +#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000) +#define SMB2_CHANNEL_RDMA_V1 cpu_to_le32(0x00000001) +#define SMB2_CHANNEL_RDMA_V1_INVALIDATE cpu_to_le32(0x00000002) + +struct smb2_read_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __u8 Padding; /* offset from start of SMB2 header to place read */ + __u8 Reserved; + __le32 Length; + __le64 Offset; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le32 MinimumCount; + __le32 Channel; /* Reserved MBZ */ + __le32 RemainingBytes; + __le16 ReadChannelInfoOffset; /* Reserved MBZ */ + __le16 ReadChannelInfoLength; /* Reserved MBZ */ + __u8 Buffer[1]; +} __packed; + +struct smb2_read_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; + __le32 DataLength; + __le32 DataRemaining; + __u32 Reserved2; + __u8 Buffer[1]; +} __packed; + +/* For write request Flags field below the following flag is defined: */ +#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 + +struct smb2_write_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __le16 DataOffset; /* offset from start of SMB2 header to write data */ + __le32 Length; + __le64 Offset; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le32 Channel; /* Reserved MBZ */ + __le32 RemainingBytes; + __le16 WriteChannelInfoOffset; /* Reserved MBZ */ + __le16 WriteChannelInfoLength; /* Reserved MBZ */ + __le32 Flags; + __u8 Buffer[1]; +} __packed; + +struct smb2_write_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; + __le32 DataLength; + __le32 DataRemaining; + __u32 Reserved2; + __u8 Buffer[1]; +} __packed; + +#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 + +struct smb2_ioctl_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __le16 Reserved; /* offset from start of SMB2 header to write data */ + __le32 CntCode; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le32 InputOffset; /* Reserved MBZ */ + __le32 InputCount; + __le32 MaxInputResponse; + __le32 OutputOffset; + __le32 OutputCount; + __le32 MaxOutputResponse; + __le32 Flags; + __le32 Reserved2; + __u8 Buffer[1]; +} __packed; + +struct smb2_ioctl_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __le16 Reserved; /* offset from start of SMB2 header to write data */ + __le32 CntCode; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le32 InputOffset; /* Reserved MBZ */ + __le32 InputCount; + __le32 OutputOffset; + __le32 OutputCount; + __le32 Flags; + __le32 Reserved2; + __u8 Buffer[1]; +} __packed; + +struct validate_negotiate_info_req { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 DialectCount; + __le16 Dialects[1]; /* dialect (someday maybe list) client asked for */ +} __packed; + +struct validate_negotiate_info_rsp { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 Dialect; /* Dialect in use for the connection */ +} __packed; + +struct smb_sockaddr_in { + __be16 Port; + __be32 IPv4address; + __u8 Reserved[8]; +} __packed; + +struct smb_sockaddr_in6 { + __be16 Port; + __be32 FlowInfo; + __u8 IPv6address[16]; + __be32 ScopeId; +} __packed; + +#define INTERNETWORK 0x0002 +#define INTERNETWORKV6 0x0017 + +struct sockaddr_storage_rsp { + __le16 Family; + union { + struct smb_sockaddr_in addr4; + struct smb_sockaddr_in6 addr6; + }; +} __packed; + +#define RSS_CAPABLE 0x00000001 +#define RDMA_CAPABLE 0x00000002 + +struct network_interface_info_ioctl_rsp { + __le32 Next; /* next interface. zero if this is last one */ + __le32 IfIndex; + __le32 Capability; /* RSS or RDMA Capable */ + __le32 Reserved; + __le64 LinkSpeed; + char SockAddr_Storage[128]; +} __packed; + +struct file_object_buf_type1_ioctl_rsp { + __u8 ObjectId[16]; + __u8 BirthVolumeId[16]; + __u8 BirthObjectId[16]; + __u8 DomainId[16]; +} __packed; + +struct resume_key_ioctl_rsp { + __le64 ResumeKey[3]; + __le32 ContextLength; + __u8 Context[4]; /* ignored, Windows sets to 4 bytes of zero */ +} __packed; + +struct copychunk_ioctl_req { + __le64 ResumeKey[3]; + __le32 ChunkCount; + __le32 Reserved; + __u8 Chunks[1]; /* array of srv_copychunk */ +} __packed; + +struct srv_copychunk { + __le64 SourceOffset; + __le64 TargetOffset; + __le32 Length; + __le32 Reserved; +} __packed; + +struct copychunk_ioctl_rsp { + __le32 ChunksWritten; + __le32 ChunkBytesWritten; + __le32 TotalBytesWritten; +} __packed; + +struct file_sparse { + __u8 SetSparse; +} __packed; + +struct file_zero_data_information { + __le64 FileOffset; + __le64 BeyondFinalZero; +} __packed; + +struct file_allocated_range_buffer { + __le64 file_offset; + __le64 length; +} __packed; + +struct reparse_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __u8 DataBuffer[]; /* Variable Length */ +} __packed; + +/* Completion Filter flags for Notify */ +#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 +#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 +#define FILE_NOTIFY_CHANGE_NAME 0x00000003 +#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 +#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 +#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 +#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 +#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 +#define FILE_NOTIFY_CHANGE_EA 0x00000080 +#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 +#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 +#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 +#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 + +/* Flags */ +#define SMB2_WATCH_TREE 0x0001 + +struct smb2_notify_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 32 */ + __le16 Flags; + __le32 OutputBufferLength; + __le64 PersistentFileId; + __le64 VolatileFileId; + __u32 CompletionFileter; + __u32 Reserved; +} __packed; + +struct smb2_notify_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +/* SMB2 Notify Action Flags */ +#define FILE_ACTION_ADDED 0x00000001 +#define FILE_ACTION_REMOVED 0x00000002 +#define FILE_ACTION_MODIFIED 0x00000003 +#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 +#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 +#define FILE_ACTION_ADDED_STREAM 0x00000006 +#define FILE_ACTION_REMOVED_STREAM 0x00000007 +#define FILE_ACTION_MODIFIED_STREAM 0x00000008 +#define FILE_ACTION_REMOVED_BY_DELETE 0x00000009 + +#define SMB2_LOCKFLAG_SHARED 0x0001 +#define SMB2_LOCKFLAG_EXCLUSIVE 0x0002 +#define SMB2_LOCKFLAG_UNLOCK 0x0004 +#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010 +#define SMB2_LOCKFLAG_MASK 0x0007 + +struct smb2_lock_element { + __le64 Offset; + __le64 Length; + __le32 Flags; + __le32 Reserved; +} __packed; + +struct smb2_lock_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 48 */ + __le16 LockCount; + __le32 Reserved; + __le64 PersistentFileId; + __le64 VolatileFileId; + /* Followed by at least one */ + struct smb2_lock_element locks[1]; +} __packed; + +struct smb2_lock_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_echo_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + +struct smb2_echo_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + +/* search (query_directory) Flags field */ +#define SMB2_RESTART_SCANS 0x01 +#define SMB2_RETURN_SINGLE_ENTRY 0x02 +#define SMB2_INDEX_SPECIFIED 0x04 +#define SMB2_REOPEN 0x10 + +struct smb2_query_directory_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 FileInformationClass; + __u8 Flags; + __le32 FileIndex; + __le64 PersistentFileId; + __le64 VolatileFileId; + __le16 FileNameOffset; + __le16 FileNameLength; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +struct smb2_query_directory_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +/* Possible InfoType values */ +#define SMB2_O_INFO_FILE 0x01 +#define SMB2_O_INFO_FILESYSTEM 0x02 +#define SMB2_O_INFO_SECURITY 0x03 +#define SMB2_O_INFO_QUOTA 0x04 + +/* Security info type additionalinfo flags. See MS-SMB2 (2.2.37) or MS-DTYP */ +#define OWNER_SECINFO 0x00000001 +#define GROUP_SECINFO 0x00000002 +#define DACL_SECINFO 0x00000004 +#define SACL_SECINFO 0x00000008 +#define LABEL_SECINFO 0x00000010 +#define ATTRIBUTE_SECINFO 0x00000020 +#define SCOPE_SECINFO 0x00000040 +#define BACKUP_SECINFO 0x00010000 +#define UNPROTECTED_SACL_SECINFO 0x10000000 +#define UNPROTECTED_DACL_SECINFO 0x20000000 +#define PROTECTED_SACL_SECINFO 0x40000000 +#define PROTECTED_DACL_SECINFO 0x80000000 + +struct smb2_query_info_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 41 */ + __u8 InfoType; + __u8 FileInfoClass; + __le32 OutputBufferLength; + __le16 InputBufferOffset; + __u16 Reserved; + __le32 InputBufferLength; + __le32 AdditionalInformation; + __le32 Flags; + __le64 PersistentFileId; + __le64 VolatileFileId; + __u8 Buffer[1]; +} __packed; + +struct smb2_query_info_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +struct smb2_set_info_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 InfoType; + __u8 FileInfoClass; + __le32 BufferLength; + __le16 BufferOffset; + __u16 Reserved; + __le32 AdditionalInformation; + __le64 PersistentFileId; + __le64 VolatileFileId; + __u8 Buffer[1]; +} __packed; + +struct smb2_set_info_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 2 */ +} __packed; + + +/* FILE Info response size */ +#define FILE_DIRECTORY_INFORMATION_SIZE 1 +#define FILE_FULL_DIRECTORY_INFORMATION_SIZE 2 +#define FILE_BOTH_DIRECTORY_INFORMATION_SIZE 3 +#define FILE_BASIC_INFORMATION_SIZE 40 +#define FILE_STANDARD_INFORMATION_SIZE 24 +#define FILE_INTERNAL_INFORMATION_SIZE 8 +#define FILE_EA_INFORMATION_SIZE 4 +#define FILE_ACCESS_INFORMATION_SIZE 4 +#define FILE_NAME_INFORMATION_SIZE 9 +#define FILE_RENAME_INFORMATION_SIZE 10 +#define FILE_LINK_INFORMATION_SIZE 11 +#define FILE_NAMES_INFORMATION_SIZE 12 +#define FILE_DISPOSITION_INFORMATION_SIZE 13 +#define FILE_POSITION_INFORMATION_SIZE 14 +#define FILE_FULL_EA_INFORMATION_SIZE 15 +#define FILE_MODE_INFORMATION_SIZE 4 +#define FILE_ALIGNMENT_INFORMATION_SIZE 4 +#define FILE_ALL_INFORMATION_SIZE 104 +#define FILE_ALLOCATION_INFORMATION_SIZE 19 +#define FILE_END_OF_FILE_INFORMATION_SIZE 20 +#define FILE_ALTERNATE_NAME_INFORMATION_SIZE 8 +#define FILE_STREAM_INFORMATION_SIZE 32 +#define FILE_PIPE_INFORMATION_SIZE 23 +#define FILE_PIPE_LOCAL_INFORMATION_SIZE 24 +#define FILE_PIPE_REMOTE_INFORMATION_SIZE 25 +#define FILE_MAILSLOT_QUERY_INFORMATION_SIZE 26 +#define FILE_MAILSLOT_SET_INFORMATION_SIZE 27 +#define FILE_COMPRESSION_INFORMATION_SIZE 16 +#define FILE_OBJECT_ID_INFORMATION_SIZE 29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION_SIZE 31 +#define FILE_QUOTA_INFORMATION_SIZE 32 +#define FILE_REPARSE_POINT_INFORMATION_SIZE 33 +#define FILE_NETWORK_OPEN_INFORMATION_SIZE 56 +#define FILE_ATTRIBUTE_TAG_INFORMATION_SIZE 8 + + +/* FS Info response size */ +#define FS_DEVICE_INFORMATION_SIZE 8 +#define FS_ATTRIBUTE_INFORMATION_SIZE 16 +#define FS_VOLUME_INFORMATION_SIZE 24 +#define FS_SIZE_INFORMATION_SIZE 24 +#define FS_FULL_SIZE_INFORMATION_SIZE 32 +#define FS_SECTOR_SIZE_INFORMATION_SIZE 28 +#define FS_OBJECT_ID_INFORMATION_SIZE 64 +#define FS_CONTROL_INFORMATION_SIZE 48 +#define FS_POSIX_INFORMATION_SIZE 56 + +/* FS_ATTRIBUTE_File_System_Name */ +#define FS_TYPE_SUPPORT_SIZE 44 +struct fs_type_info { + char *fs_name; + long magic_number; +} __packed; + +struct smb2_oplock_break { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __u8 OplockLevel; + __u8 Reserved; + __le32 Reserved2; + __le64 PersistentFid; + __le64 VolatileFid; +} __packed; + +#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01) + +struct smb2_lease_break { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 44 */ + __le16 Reserved; + __le32 Flags; + __u8 LeaseKey[16]; + __le32 CurrentLeaseState; + __le32 NewLeaseState; + __le32 BreakReason; + __le32 AccessMaskHint; + __le32 ShareMaskHint; +} __packed; + +struct smb2_lease_ack { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 Reserved; + __le32 Flags; + __u8 LeaseKey[16]; + __le32 LeaseState; + __le64 LeaseDuration; +} __packed; + +/* + * PDU infolevel structure definitions + * BB consider moving to a different header + */ + +/* File System Information Classes */ +#define FS_VOLUME_INFORMATION 1 /* Query */ +#define FS_LABEL_INFORMATION 2 /* Set */ +#define FS_SIZE_INFORMATION 3 /* Query */ +#define FS_DEVICE_INFORMATION 4 /* Query */ +#define FS_ATTRIBUTE_INFORMATION 5 /* Query */ +#define FS_CONTROL_INFORMATION 6 /* Query, Set */ +#define FS_FULL_SIZE_INFORMATION 7 /* Query */ +#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */ +#define FS_DRIVER_PATH_INFORMATION 9 /* Query */ +#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */ +#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */ + +struct smb2_fs_full_size_info { + __le64 TotalAllocationUnits; + __le64 CallerAvailableAllocationUnits; + __le64 ActualAvailableAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __packed; + +#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001 +#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 +#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004 +#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008 + +/* sector size info struct */ +struct smb3_fs_ss_info { + __le32 LogicalBytesPerSector; + __le32 PhysicalBytesPerSectorForAtomicity; + __le32 PhysicalBytesPerSectorForPerf; + __le32 FSEffPhysicalBytesPerSectorForAtomicity; + __le32 Flags; + __le32 ByteOffsetForSectorAlignment; + __le32 ByteOffsetForPartitionAlignment; +} __packed; + +/* File System Control Information */ +struct smb2_fs_control_info { + __le64 FreeSpaceStartFiltering; + __le64 FreeSpaceThreshold; + __le64 FreeSpaceStopFiltering; + __le64 DefaultQuotaThreshold; + __le64 DefaultQuotaLimit; + __le32 FileSystemControlFlags; + __le32 Padding; +} __packed; + +/* partial list of QUERY INFO levels */ +#define FILE_DIRECTORY_INFORMATION 1 +#define FILE_FULL_DIRECTORY_INFORMATION 2 +#define FILE_BOTH_DIRECTORY_INFORMATION 3 +#define FILE_BASIC_INFORMATION 4 +#define FILE_STANDARD_INFORMATION 5 +#define FILE_INTERNAL_INFORMATION 6 +#define FILE_EA_INFORMATION 7 +#define FILE_ACCESS_INFORMATION 8 +#define FILE_NAME_INFORMATION 9 +#define FILE_RENAME_INFORMATION 10 +#define FILE_LINK_INFORMATION 11 +#define FILE_NAMES_INFORMATION 12 +#define FILE_DISPOSITION_INFORMATION 13 +#define FILE_POSITION_INFORMATION 14 +#define FILE_FULL_EA_INFORMATION 15 +#define FILE_MODE_INFORMATION 16 +#define FILE_ALIGNMENT_INFORMATION 17 +#define FILE_ALL_INFORMATION 18 +#define FILE_ALLOCATION_INFORMATION 19 +#define FILE_END_OF_FILE_INFORMATION 20 +#define FILE_ALTERNATE_NAME_INFORMATION 21 +#define FILE_STREAM_INFORMATION 22 +#define FILE_PIPE_INFORMATION 23 +#define FILE_PIPE_LOCAL_INFORMATION 24 +#define FILE_PIPE_REMOTE_INFORMATION 25 +#define FILE_MAILSLOT_QUERY_INFORMATION 26 +#define FILE_MAILSLOT_SET_INFORMATION 27 +#define FILE_COMPRESSION_INFORMATION 28 +#define FILE_OBJECT_ID_INFORMATION 29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION 31 +#define FILE_QUOTA_INFORMATION 32 +#define FILE_REPARSE_POINT_INFORMATION 33 +#define FILE_NETWORK_OPEN_INFORMATION 34 +#define FILE_ATTRIBUTE_TAG_INFORMATION 35 +#define FILE_TRACKING_INFORMATION 36 +#define FILEID_BOTH_DIRECTORY_INFORMATION 37 +#define FILEID_FULL_DIRECTORY_INFORMATION 38 +#define FILE_VALID_DATA_LENGTH_INFORMATION 39 +#define FILE_SHORT_NAME_INFORMATION 40 +#define FILE_SFIO_RESERVE_INFORMATION 44 +#define FILE_SFIO_VOLUME_INFORMATION 45 +#define FILE_HARD_LINK_INFORMATION 46 +#define FILE_NORMALIZED_NAME_INFORMATION 48 +#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50 +#define FILE_STANDARD_LINK_INFORMATION 54 + +#define OP_BREAK_STRUCT_SIZE_20 24 +#define OP_BREAK_STRUCT_SIZE_21 36 + +struct smb2_file_access_info { + __le32 AccessFlags; +} __packed; + +struct smb2_file_alignment_info { + __le32 AlignmentRequirement; +} __packed; + +struct smb2_file_internal_info { + __le64 IndexNumber; +} __packed; /* level 6 Query */ + +struct smb2_file_rename_info { /* encoding of request for level 10 */ + __u8 ReplaceIfExists; /* 1 = replace existing target with new */ + /* 0 = fail if target already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + char FileName[0]; /* New name to be assigned */ +} __packed; /* level 10 Set */ + +struct smb2_file_link_info { /* encoding of request for level 11 */ + __u8 ReplaceIfExists; /* 1 = replace existing link with new */ + /* 0 = fail if link already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + char FileName[0]; /* Name to be assigned to new link */ +} __packed; /* level 11 Set */ + +/* + * This level 18, although with struct with same name is different from cifs + * level 0x107. Level 0x107 has an extra u64 between AccessFlags and + * CurrentByteOffset. + */ +struct smb2_file_all_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */ + __le64 IndexNumber; + __le32 EASize; + __le32 AccessFlags; + __le64 CurrentByteOffset; + __le32 Mode; + __le32 AlignmentRequirement; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 18 Query */ + +struct smb2_file_alt_name_info { + __le32 FileNameLength; + char FileName[0]; +} __packed; + +struct smb2_file_stream_info { + __le32 NextEntryOffset; + __le32 StreamNameLength; + __le64 StreamSize; + __le64 StreamAllocationSize; + char StreamName[0]; +} __packed; + +struct smb2_file_eof_info { /* encoding of request for level 10 */ + __le64 EndOfFile; /* new end of file value */ +} __packed; /* level 20 Set */ + +struct smb2_file_ntwrk_info { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndOfFile; + __le32 Attributes; + __le32 Reserved; +} __packed; + +struct smb2_file_standard_info { + __le64 AllocationSize; + __le64 EndOfFile; + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __le16 Reserved; +} __packed; /* level 18 Query */ + +struct smb2_file_ea_info { + __le32 EASize; +} __packed; + +struct smb2_file_alloc_info { + __le64 AllocationSize; +} __packed; + +struct smb2_file_disposition_info { + __u8 DeletePending; +} __packed; + +struct smb2_file_pos_info { + __le64 CurrentByteOffset; +} __packed; + +#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000103e) + +struct smb2_file_mode_info { + __le32 Mode; +} __packed; + +#define COMPRESSION_FORMAT_NONE 0x0000 +#define COMPRESSION_FORMAT_LZNT1 0x0002 + +struct smb2_file_comp_info { + __le64 CompressedFileSize; + __le16 CompressionFormat; + __u8 CompressionUnitShift; + __u8 ChunkShift; + __u8 ClusterShift; + __u8 Reserved[3]; +} __packed; + +struct smb2_file_attr_tag_info { + __le32 FileAttributes; + __le32 ReparseTag; +} __packed; + +#define SL_RESTART_SCAN 0x00000001 +#define SL_RETURN_SINGLE_ENTRY 0x00000002 +#define SL_INDEX_SPECIFIED 0x00000004 + +struct smb2_ea_info_req { + __le32 NextEntryOffset; + __u8 EaNameLength; + char name[1]; +} __packed; /* level 15 Query */ + +struct smb2_ea_info { + __le32 NextEntryOffset; + __u8 Flags; + __u8 EaNameLength; + __le16 EaValueLength; + char name[1]; + /* optionally followed by value */ +} __packed; /* level 15 Query */ + +struct create_ea_buf_req { + struct create_context ccontext; + __u8 Name[8]; + struct smb2_ea_info ea; +} __packed; + +struct create_sd_buf_req { + struct create_context ccontext; + __u8 Name[8]; + struct smb_ntsd ntsd; +} __packed; + +/* Find File infolevels */ +#define SMB_FIND_FILE_POSIX_INFO 0x064 + +/* Level 100 query info */ +struct smb311_posix_qinfo { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + u8 Sids[]; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +struct smb2_posix_info { + __le32 NextEntryOffset; + __u32 Ignored; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + u8 SidBuffer[40]; + __le32 name_len; + u8 name[1]; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +/* functions */ + +extern int init_smb2_0_server(struct ksmbd_conn *conn); +extern void init_smb2_1_server(struct ksmbd_conn *conn); +extern void init_smb3_0_server(struct ksmbd_conn *conn); +extern void init_smb3_02_server(struct ksmbd_conn *conn); +extern int init_smb3_11_server(struct ksmbd_conn *conn); + +extern void init_smb2_max_read_size(unsigned int sz); +extern void init_smb2_max_write_size(unsigned int sz); +extern void init_smb2_max_trans_size(unsigned int sz); + +extern int is_smb2_neg_cmd(struct ksmbd_work *work); +extern int is_smb2_rsp(struct ksmbd_work *work); + +extern uint16_t get_smb2_cmd_val(struct ksmbd_work *work); +extern void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err); +extern int init_smb2_rsp_hdr(struct ksmbd_work *work); +extern int smb2_allocate_rsp_buf(struct ksmbd_work *work); +extern bool is_chained_smb2_message(struct ksmbd_work *work); +extern int init_smb2_neg_rsp(struct ksmbd_work *work); +extern void smb2_set_err_rsp(struct ksmbd_work *work); +extern int smb2_check_user_session(struct ksmbd_work *work); +extern int smb2_get_ksmbd_tcon(struct ksmbd_work *work); +extern bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command); +extern int smb2_check_sign_req(struct ksmbd_work *work); +extern void smb2_set_sign_rsp(struct ksmbd_work *work); +extern int smb3_check_sign_req(struct ksmbd_work *work); +extern void smb3_set_sign_rsp(struct ksmbd_work *work); +extern int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, + __le16 dialects_count); +extern struct file_lock *smb_flock_init(struct file *f); +extern int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), + void **arg); +extern void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); +extern struct channel *lookup_chann_list(struct ksmbd_session *sess); +extern void smb3_preauth_hash_rsp(struct ksmbd_work *work); +extern int smb3_is_transform_hdr(void *buf); +extern int smb3_decrypt_req(struct ksmbd_work *work); +extern int smb3_encrypt_resp(struct ksmbd_work *work); +extern bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work); +extern int smb2_set_rsp_credits(struct ksmbd_work *work); + +/* smb2 misc functions */ +extern int ksmbd_smb2_check_message(struct ksmbd_work *work); + +/* smb2 command handlers */ +extern int smb2_handle_negotiate(struct ksmbd_work *work); +extern int smb2_negotiate_request(struct ksmbd_work *work); +extern int smb2_sess_setup(struct ksmbd_work *work); +extern int smb2_tree_connect(struct ksmbd_work *work); +extern int smb2_tree_disconnect(struct ksmbd_work *work); +extern int smb2_session_logoff(struct ksmbd_work *work); +extern int smb2_open(struct ksmbd_work *work); +extern int smb2_query_info(struct ksmbd_work *work); +extern int smb2_query_dir(struct ksmbd_work *work); +extern int smb2_close(struct ksmbd_work *work); +extern int smb2_echo(struct ksmbd_work *work); +extern int smb2_set_info(struct ksmbd_work *work); +extern int smb2_read(struct ksmbd_work *work); +extern int smb2_write(struct ksmbd_work *work); +extern int smb2_flush(struct ksmbd_work *work); +extern int smb2_cancel(struct ksmbd_work *work); +extern int smb2_lock(struct ksmbd_work *work); +extern int smb2_ioctl(struct ksmbd_work *work); +extern int smb2_oplock_break(struct ksmbd_work *work); +extern int smb2_notify(struct ksmbd_work *ksmbd_work); + +#endif /* _SMB2PDU_H */ diff --git a/fs/cifsd/smb_common.c b/fs/cifsd/smb_common.c new file mode 100644 index 000000000000..f7560b68b820 --- /dev/null +++ b/fs/cifsd/smb_common.c @@ -0,0 +1,668 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * Copyright (C) 2018 Namjae Jeon + */ + +#include "smb_common.h" +#include "server.h" +#include "misc.h" +#include "smbstatus.h" +/* @FIXME */ +#include "connection.h" +#include "ksmbd_work.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/share_config.h" + +/*for shortname implementation */ +static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; +#define MANGLE_BASE (sizeof(basechars)/sizeof(char)-1) +#define MAGIC_CHAR '~' +#define PERIOD '.' +#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) +#define KSMBD_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr)) + +LIST_HEAD(global_lock_list); + +struct smb_protocol { + int index; + char *name; + char *prot; + __u16 prot_id; +}; + +static struct smb_protocol smb_protos[] = { + { + SMB21_PROT, + "\2SMB 2.1", + "SMB2_10", + SMB21_PROT_ID + }, + { + SMB2X_PROT, + "\2SMB 2.???", + "SMB2_22", + SMB2X_PROT_ID + }, + { + SMB30_PROT, + "\2SMB 3.0", + "SMB3_00", + SMB30_PROT_ID + }, + { + SMB302_PROT, + "\2SMB 3.02", + "SMB3_02", + SMB302_PROT_ID + }, + { + SMB311_PROT, + "\2SMB 3.1.1", + "SMB3_11", + SMB311_PROT_ID + }, +}; + +unsigned int ksmbd_server_side_copy_max_chunk_count(void) +{ + return 256; +} + +unsigned int ksmbd_server_side_copy_max_chunk_size(void) +{ + return (2U << 30) - 1; +} + +unsigned int ksmbd_server_side_copy_max_total_size(void) +{ + return (2U << 30) - 1; +} + +inline int ksmbd_min_protocol(void) +{ + return SMB2_PROT; +} + +inline int ksmbd_max_protocol(void) +{ + return SMB311_PROT; +} + +int ksmbd_lookup_protocol_idx(char *str) +{ + int offt = ARRAY_SIZE(smb_protos) - 1; + int len = strlen(str); + + while (offt >= 0) { + if (!strncmp(str, smb_protos[offt].prot, len)) { + ksmbd_debug(SMB, "selected %s dialect idx = %d\n", + smb_protos[offt].prot, offt); + return smb_protos[offt].index; + } + offt--; + } + return -1; +} + +/** + * check_message() - check for valid smb2 request header + * @buf: smb2 header to be checked + * + * check for valid smb signature and packet direction(request/response) + * + * Return: 0 on success, otherwise 1 + */ +int ksmbd_verify_smb_message(struct ksmbd_work *work) +{ + struct smb2_hdr *smb2_hdr = REQUEST_BUF(work); + + if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) + return ksmbd_smb2_check_message(work); + + return 0; +} + +/** + * is_smb_request() - check for valid smb request type + * @conn: connection instance + * @type: smb request type + * + * Return: true on success, otherwise false + */ +bool ksmbd_smb_request(struct ksmbd_conn *conn) +{ + int type = *(char *)conn->request_buf; + + switch (type) { + case RFC1002_SESSION_MESSAGE: + /* Regular SMB request */ + return true; + case RFC1002_SESSION_KEEP_ALIVE: + ksmbd_debug(SMB, "RFC 1002 session keep alive\n"); + break; + default: + ksmbd_debug(SMB, "RFC 1002 unknown request type 0x%x\n", type); + } + + return false; +} + +static bool supported_protocol(int idx) +{ + if (idx == SMB2X_PROT && + (server_conf.min_protocol >= SMB21_PROT || + server_conf.max_protocol <= SMB311_PROT)) + return true; + + return (server_conf.min_protocol <= idx && + idx <= server_conf.max_protocol); +} + +static char *next_dialect(char *dialect, int *next_off) +{ + dialect = dialect + *next_off; + *next_off = strlen(dialect); + return dialect; +} + +static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) +{ + int i, seq_num, bcount, next; + char *dialect; + + for (i = ARRAY_SIZE(smb_protos) - 1; i >= 0; i--) { + seq_num = 0; + next = 0; + dialect = cli_dialects; + bcount = le16_to_cpu(byte_count); + do { + dialect = next_dialect(dialect, &next); + ksmbd_debug(SMB, "client requested dialect %s\n", + dialect); + if (!strcmp(dialect, smb_protos[i].name)) { + if (supported_protocol(smb_protos[i].index)) { + ksmbd_debug(SMB, + "selected %s dialect\n", + smb_protos[i].name); + if (smb_protos[i].index == SMB1_PROT) + return seq_num; + return smb_protos[i].prot_id; + } + } + seq_num++; + bcount -= (++next); + } while (bcount > 0); + } + + return BAD_PROT_ID; +} + +int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) +{ + int i; + int count; + + for (i = ARRAY_SIZE(smb_protos) - 1; i >= 0; i--) { + count = le16_to_cpu(dialects_count); + while (--count >= 0) { + ksmbd_debug(SMB, "client requested dialect 0x%x\n", + le16_to_cpu(cli_dialects[count])); + if (le16_to_cpu(cli_dialects[count]) != + smb_protos[i].prot_id) + continue; + + if (supported_protocol(smb_protos[i].index)) { + ksmbd_debug(SMB, "selected %s dialect\n", + smb_protos[i].name); + return smb_protos[i].prot_id; + } + } + } + + return BAD_PROT_ID; +} + +int ksmbd_negotiate_smb_dialect(void *buf) +{ + __le32 proto; + + proto = ((struct smb2_hdr *)buf)->ProtocolId; + if (proto == SMB2_PROTO_NUMBER) { + struct smb2_negotiate_req *req; + + req = (struct smb2_negotiate_req *)buf; + return ksmbd_lookup_dialect_by_id(req->Dialects, + req->DialectCount); + } + + proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol; + if (proto == SMB1_PROTO_NUMBER) { + struct smb_negotiate_req *req; + + req = (struct smb_negotiate_req *)buf; + return ksmbd_lookup_dialect_by_name(req->DialectsArray, + req->ByteCount); + } + + return BAD_PROT_ID; +} + +#define SMB_COM_NEGOTIATE 0x72 +int ksmbd_init_smb_server(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + + if (conn->need_neg == false) + return 0; + + init_smb3_11_server(conn); + + if (conn->ops->get_cmd_val(work) != SMB_COM_NEGOTIATE) + conn->need_neg = false; + return 0; +} + +bool ksmbd_pdu_size_has_room(unsigned int pdu) +{ + return (pdu >= KSMBD_MIN_SUPPORTED_HEADER_SIZE - 4); +} + +int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, + int info_level, + struct ksmbd_file *dir, + struct ksmbd_dir_info *d_info, + char *search_pattern, + int (*fn)(struct ksmbd_conn *, + int, + struct ksmbd_dir_info *, + struct ksmbd_kstat *)) +{ + int i, rc = 0; + struct ksmbd_conn *conn = work->conn; + + for (i = 0; i < 2; i++) { + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + + if (!dir->dot_dotdot[i]) { /* fill dot entry info */ + if (i == 0) { + d_info->name = "."; + d_info->name_len = 1; + } else { + d_info->name = ".."; + d_info->name_len = 2; + } + + if (!match_pattern(d_info->name, search_pattern)) { + dir->dot_dotdot[i] = 1; + continue; + } + + ksmbd_kstat.kstat = &kstat; + ksmbd_vfs_fill_dentry_attrs(work, + dir->filp->f_path.dentry->d_parent, + &ksmbd_kstat); + rc = fn(conn, info_level, d_info, &ksmbd_kstat); + if (rc) + break; + if (d_info->out_buf_len <= 0) + break; + + dir->dot_dotdot[i] = 1; + if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) { + d_info->out_buf_len = 0; + break; + } + } + } + + return rc; +} + +/** + * ksmbd_extract_shortname() - get shortname from long filename + * @conn: connection instance + * @longname: source long filename + * @shortname: destination short filename + * + * Return: shortname length or 0 when source long name is '.' or '..' + * TODO: Though this function comforms the restriction of 8.3 Filename spec, + * but the result is different with Windows 7's one. need to check. + */ +int ksmbd_extract_shortname(struct ksmbd_conn *conn, + const char *longname, + char *shortname) +{ + const char *p; + char base[9], extension[4]; + char out[13] = {0}; + int baselen = 0; + int extlen = 0, len = 0; + unsigned int csum = 0; + const unsigned char *ptr; + bool dot_present = true; + + p = longname; + if ((*p == '.') || (!(strcmp(p, "..")))) { + /*no mangling required */ + return 0; + } + + p = strrchr(longname, '.'); + if (p == longname) { /*name starts with a dot*/ + strscpy(extension, "___", strlen("___")); + } else { + if (p != NULL) { + p++; + while (*p && extlen < 3) { + if (*p != '.') + extension[extlen++] = toupper(*p); + p++; + } + extension[extlen] = '\0'; + } else + dot_present = false; + } + + p = longname; + if (*p == '.') { + p++; + longname++; + } + while (*p && (baselen < 5)) { + if (*p != '.') + base[baselen++] = toupper(*p); + p++; + } + + base[baselen] = MAGIC_CHAR; + memcpy(out, base, baselen+1); + + ptr = longname; + len = strlen(longname); + for (; len > 0; len--, ptr++) + csum += *ptr; + + csum = csum % (MANGLE_BASE * MANGLE_BASE); + out[baselen+1] = mangle(csum/MANGLE_BASE); + out[baselen+2] = mangle(csum); + out[baselen+3] = PERIOD; + + if (dot_present) + memcpy(&out[baselen+4], extension, 4); + else + out[baselen+4] = '\0'; + smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX, + conn->local_nls, 0); + len = strlen(out) * 2; + return len; +} + +static int __smb2_negotiate(struct ksmbd_conn *conn) +{ + return (conn->dialect >= SMB20_PROT_ID && + conn->dialect <= SMB311_PROT_ID); +} + +static int smb_handle_negotiate(struct ksmbd_work *work) +{ + struct smb_negotiate_rsp *neg_rsp = RESPONSE_BUF(work); + + ksmbd_debug(SMB, "Unsupported SMB protocol\n"); + neg_rsp->hdr.Status.CifsError = STATUS_INVALID_LOGON_TYPE; + return -EINVAL; +} + +int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) +{ + struct ksmbd_conn *conn = work->conn; + int ret; + + conn->dialect = ksmbd_negotiate_smb_dialect(REQUEST_BUF(work)); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); + + if (command == SMB2_NEGOTIATE_HE) { + struct smb2_hdr *smb2_hdr = REQUEST_BUF(work); + + if (smb2_hdr->ProtocolId != SMB2_PROTO_NUMBER) { + ksmbd_debug(SMB, "Downgrade to SMB1 negotiation\n"); + command = SMB_COM_NEGOTIATE; + } + } + + if (command == SMB2_NEGOTIATE_HE) { + ret = smb2_handle_negotiate(work); + init_smb2_neg_rsp(work); + return ret; + } + + if (command == SMB_COM_NEGOTIATE) { + if (__smb2_negotiate(conn)) { + conn->need_neg = true; + init_smb3_11_server(conn); + init_smb2_neg_rsp(work); + ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); + return 0; + } + return smb_handle_negotiate(work); + } + + ksmbd_err("Unknown SMB negotiation command: %u\n", command); + return -EINVAL; +} + +enum SHARED_MODE_ERRORS { + SHARE_DELETE_ERROR, + SHARE_READ_ERROR, + SHARE_WRITE_ERROR, + FILE_READ_ERROR, + FILE_WRITE_ERROR, + FILE_DELETE_ERROR, +}; + +static const char * const shared_mode_errors[] = { + "Current access mode does not permit SHARE_DELETE", + "Current access mode does not permit SHARE_READ", + "Current access mode does not permit SHARE_WRITE", + "Desired access mode does not permit FILE_READ", + "Desired access mode does not permit FILE_WRITE", + "Desired access mode does not permit FILE_DELETE", +}; + +static void smb_shared_mode_error(int error, + struct ksmbd_file *prev_fp, + struct ksmbd_file *curr_fp) +{ + ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]); + ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n", + prev_fp->saccess, curr_fp->daccess); +} + +int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) +{ + int rc = 0; + struct ksmbd_file *prev_fp; + struct list_head *cur; + + /* + * Lookup fp in master fp list, and check desired access and + * shared mode between previous open and current open. + */ + read_lock(&curr_fp->f_ci->m_lock); + list_for_each(cur, &curr_fp->f_ci->m_fp_list) { + prev_fp = list_entry(cur, struct ksmbd_file, node); + if (file_inode(filp) != FP_INODE(prev_fp)) + continue; + + if (filp == prev_fp->filp) + continue; + + if (ksmbd_stream_fd(prev_fp) && ksmbd_stream_fd(curr_fp)) + if (strcmp(prev_fp->stream.name, curr_fp->stream.name)) + continue; + + if (prev_fp->is_durable) { + prev_fp->is_durable = 0; + continue; + } + + if (prev_fp->attrib_only != curr_fp->attrib_only) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE) && + curr_fp->daccess & FILE_DELETE_LE) { + smb_shared_mode_error(SHARE_DELETE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + /* + * Only check FILE_SHARE_DELETE if stream opened and + * normal file opened. + */ + if (ksmbd_stream_fd(prev_fp) && !ksmbd_stream_fd(curr_fp)) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_READ_LE) && + curr_fp->daccess & (FILE_EXECUTE_LE | + FILE_READ_DATA_LE)) { + smb_shared_mode_error(SHARE_READ_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (!(prev_fp->saccess & FILE_SHARE_WRITE_LE) && + curr_fp->daccess & (FILE_WRITE_DATA_LE | + FILE_APPEND_DATA_LE)) { + smb_shared_mode_error(SHARE_WRITE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & (FILE_EXECUTE_LE | + FILE_READ_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_READ_LE)) { + smb_shared_mode_error(FILE_READ_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & (FILE_WRITE_DATA_LE | + FILE_APPEND_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_WRITE_LE)) { + smb_shared_mode_error(FILE_WRITE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & FILE_DELETE_LE && + !(curr_fp->saccess & FILE_SHARE_DELETE_LE)) { + smb_shared_mode_error(FILE_DELETE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + } + read_unlock(&curr_fp->f_ci->m_lock); + + return rc; +} + +bool is_asterisk(char *p) +{ + return p && p[0] == '*'; +} + +int ksmbd_override_fsids(struct ksmbd_work *work) +{ + struct ksmbd_session *sess = work->sess; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct cred *cred; + struct group_info *gi; + unsigned int uid; + unsigned int gid; + + uid = user_uid(sess->user); + gid = user_gid(sess->user); + if (share->force_uid != KSMBD_SHARE_INVALID_UID) + uid = share->force_uid; + if (share->force_gid != KSMBD_SHARE_INVALID_GID) + gid = share->force_gid; + + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + cred->fsuid = make_kuid(current_user_ns(), uid); + cred->fsgid = make_kgid(current_user_ns(), gid); + + gi = groups_alloc(0); + if (!gi) { + abort_creds(cred); + return -ENOMEM; + } + set_groups(cred, gi); + put_group_info(gi); + + if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID)) + cred->cap_effective = cap_drop_fs_set(cred->cap_effective); + + WARN_ON(work->saved_cred != NULL); + work->saved_cred = override_creds(cred); + if (!work->saved_cred) { + abort_creds(cred); + return -EINVAL; + } + return 0; +} + +void ksmbd_revert_fsids(struct ksmbd_work *work) +{ + const struct cred *cred; + + WARN_ON(work->saved_cred == NULL); + + cred = current_cred(); + revert_creds(work->saved_cred); + put_cred(cred); + work->saved_cred = NULL; +} + +__le32 smb_map_generic_desired_access(__le32 daccess) +{ + if (daccess & FILE_GENERIC_READ_LE) { + daccess |= cpu_to_le32(GENERIC_READ_FLAGS); + daccess &= ~FILE_GENERIC_READ_LE; + } + + if (daccess & FILE_GENERIC_WRITE_LE) { + daccess |= cpu_to_le32(GENERIC_WRITE_FLAGS); + daccess &= ~FILE_GENERIC_WRITE_LE; + } + + if (daccess & FILE_GENERIC_EXECUTE_LE) { + daccess |= cpu_to_le32(GENERIC_EXECUTE_FLAGS); + daccess &= ~FILE_GENERIC_EXECUTE_LE; + } + + if (daccess & FILE_GENERIC_ALL_LE) { + daccess |= cpu_to_le32(GENERIC_ALL_FLAGS); + daccess &= ~FILE_GENERIC_ALL_LE; + } + + return daccess; +} diff --git a/fs/cifsd/smb_common.h b/fs/cifsd/smb_common.h new file mode 100644 index 000000000000..ec954e6bc4ae --- /dev/null +++ b/fs/cifsd/smb_common.h @@ -0,0 +1,546 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SMB_COMMON_H__ +#define __SMB_COMMON_H__ + +#include + +#include "glob.h" +#include "nterr.h" +#include "smb2pdu.h" + +/* ksmbd's Specific ERRNO */ +#define ESHARE 50000 + +#define SMB1_PROT 0 +#define SMB2_PROT 1 +#define SMB21_PROT 2 +/* multi-protocol negotiate request */ +#define SMB2X_PROT 3 +#define SMB30_PROT 4 +#define SMB302_PROT 5 +#define SMB311_PROT 6 +#define BAD_PROT 0xFFFF + +#define SMB1_VERSION_STRING "1.0" +#define SMB20_VERSION_STRING "2.0" +#define SMB21_VERSION_STRING "2.1" +#define SMB30_VERSION_STRING "3.0" +#define SMB302_VERSION_STRING "3.02" +#define SMB311_VERSION_STRING "3.1.1" + +/* Dialects */ +#define SMB10_PROT_ID 0x00 +#define SMB20_PROT_ID 0x0202 +#define SMB21_PROT_ID 0x0210 +/* multi-protocol negotiate request */ +#define SMB2X_PROT_ID 0x02FF +#define SMB30_PROT_ID 0x0300 +#define SMB302_PROT_ID 0x0302 +#define SMB311_PROT_ID 0x0311 +#define BAD_PROT_ID 0xFFFF + +#define SMB_ECHO_INTERVAL (60*HZ) + +#define CIFS_DEFAULT_IOSIZE (64 * 1024) +#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ + +extern struct list_head global_lock_list; + +#define IS_SMB2(x) ((x)->vals->protocol_id != SMB10_PROT_ID) + +#define HEADER_SIZE(conn) ((conn)->vals->header_size) +#define HEADER_SIZE_NO_BUF_LEN(conn) ((conn)->vals->header_size - 4) +#define MAX_HEADER_SIZE(conn) ((conn)->vals->max_header_size) + +/* RFC 1002 session packet types */ +#define RFC1002_SESSION_MESSAGE 0x00 +#define RFC1002_SESSION_REQUEST 0x81 +#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82 +#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83 +#define RFC1002_RETARGET_SESSION_RESPONSE 0x84 +#define RFC1002_SESSION_KEEP_ALIVE 0x85 + +/* Responses when opening a file. */ +#define F_SUPERSEDED 0 +#define F_OPENED 1 +#define F_CREATED 2 +#define F_OVERWRITTEN 3 + +/* + * File Attribute flags + */ +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_DIRECTORY 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_DEVICE 0x0040 +#define ATTR_NORMAL 0x0080 +#define ATTR_TEMPORARY 0x0100 +#define ATTR_SPARSE 0x0200 +#define ATTR_REPARSE 0x0400 +#define ATTR_COMPRESSED 0x0800 +#define ATTR_OFFLINE 0x1000 +#define ATTR_NOT_CONTENT_INDEXED 0x2000 +#define ATTR_ENCRYPTED 0x4000 +#define ATTR_POSIX_SEMANTICS 0x01000000 +#define ATTR_BACKUP_SEMANTICS 0x02000000 +#define ATTR_DELETE_ON_CLOSE 0x04000000 +#define ATTR_SEQUENTIAL_SCAN 0x08000000 +#define ATTR_RANDOM_ACCESS 0x10000000 +#define ATTR_NO_BUFFERING 0x20000000 +#define ATTR_WRITE_THROUGH 0x80000000 + +#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY) +#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN) +#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM) +#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY) +#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE) +#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL) +#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY) +#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE) +#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE) +#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED) +#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE) +#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED) +#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED) +#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000) +#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) +#define ATTR_MASK_LE cpu_to_le32(0x00007FB7) + +/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ +#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ +#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ +#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 +#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 +#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 +#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 +#define FILE_SUPPORTS_HARD_LINKS 0x00400000 +#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 +#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 +#define FILE_READ_ONLY_VOLUME 0x00080000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_UNICODE_ON_DISK 0x00000004 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 + +#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */ +#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */ +#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */ +#define FILE_READ_EA 0x00000008 /* Extended attributes associated */ +/* with the file can be read */ +#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */ +/* with the file can be written */ +#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */ +/* the file using system paging I/O */ +#define FILE_DELETE_CHILD 0x00000040 +#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */ +/* file can be read */ +#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */ +/* file can be written */ +#define DELETE 0x00010000 /* The file can be deleted */ +#define READ_CONTROL 0x00020000 /* The access control list and */ +/* ownership associated with the */ +/* file can be read */ +#define WRITE_DAC 0x00040000 /* The access control list and */ +/* ownership associated with the */ +/* file can be written. */ +#define WRITE_OWNER 0x00080000 /* Ownership information associated */ +/* with the file can be written */ +#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */ +/* synchronize with the completion */ +/* of an input/output request */ +#define GENERIC_ALL 0x10000000 +#define GENERIC_EXECUTE 0x20000000 +#define GENERIC_WRITE 0x40000000 +#define GENERIC_READ 0x80000000 +/* In summary - Relevant file */ +/* access flags from CIFS are */ +/* file_read_data, file_write_data */ +/* file_execute, file_read_attributes*/ +/* write_dac, and delete. */ + +#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES) +#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) +#define FILE_EXEC_RIGHTS (FILE_EXECUTE) + +#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ + | FILE_READ_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA \ + | FILE_DELETE_CHILD \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) + +#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ + | READ_CONTROL | SYNCHRONIZE) + +/* generic flags for file open */ +#define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \ + FILE_READ_ATTRIBUTES | \ + FILE_READ_EA | SYNCHRONIZE) + +#define GENERIC_WRITE_FLAGS (READ_CONTROL | FILE_WRITE_DATA | \ + FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | \ + FILE_APPEND_DATA | SYNCHRONIZE) + +#define GENERIC_EXECUTE_FLAGS (READ_CONTROL | FILE_EXECUTE | \ + FILE_READ_ATTRIBUTES | SYNCHRONIZE) + +#define GENERIC_ALL_FLAGS (DELETE | READ_CONTROL | WRITE_DAC | \ + WRITE_OWNER | SYNCHRONIZE | FILE_READ_DATA | \ + FILE_WRITE_DATA | FILE_APPEND_DATA | \ + FILE_READ_EA | FILE_WRITE_EA | \ + FILE_EXECUTE | FILE_DELETE_CHILD | \ + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES) + +#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) + +#define SMB1_CLIENT_GUID_SIZE (16) +struct smb_hdr { + __be32 smb_buf_length; + __u8 Protocol[4]; + __u8 Command; + union { + struct { + __u8 ErrorClass; + __u8 Reserved; + __le16 Error; + } __packed DosError; + __le32 CifsError; + } __packed Status; + __u8 Flags; + __le16 Flags2; /* note: le */ + __le16 PidHigh; + union { + struct { + __le32 SequenceNumber; /* le */ + __u32 Reserved; /* zero */ + } __packed Sequence; + __u8 SecuritySignature[8]; /* le */ + } __packed Signature; + __u8 pad[2]; + __le16 Tid; + __le16 Pid; + __le16 Uid; + __le16 Mid; + __u8 WordCount; +} __packed; + +struct smb_negotiate_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + unsigned char DialectsArray[1]; +} __packed; + +struct smb_negotiate_rsp { + struct smb_hdr hdr; /* wct = 17 */ + __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ + __u8 SecurityMode; + __le16 MaxMpxCount; + __le16 MaxNumberVcs; + __le32 MaxBufferSize; + __le32 MaxRawSize; + __le32 SessionKey; + __le32 Capabilities; /* see below */ + __le32 SystemTimeLow; + __le32 SystemTimeHigh; + __le16 ServerTimeZone; + __u8 EncryptionKeyLength; + __le16 ByteCount; + union { + unsigned char EncryptionKey[8]; /* cap extended security off */ + /* followed by Domain name - if extended security is off */ + /* followed by 16 bytes of server GUID */ + /* then security blob if cap_extended_security negotiated */ + struct { + unsigned char GUID[SMB1_CLIENT_GUID_SIZE]; + unsigned char SecurityBlob[1]; + } __packed extended_response; + } __packed u; +} __packed; + +struct filesystem_attribute_info { + __le32 Attributes; + __le32 MaxPathNameComponentLength; + __le32 FileSystemNameLen; + __le16 FileSystemName[1]; /* do not have to save this - get subset? */ +} __packed; + +struct filesystem_device_info { + __le32 DeviceType; + __le32 DeviceCharacteristics; +} __packed; /* device info level 0x104 */ + +struct filesystem_vol_info { + __le64 VolumeCreationTime; + __le32 SerialNumber; + __le32 VolumeLabelSize; + __le16 Reserved; + __le16 VolumeLabel[1]; +} __packed; + +struct filesystem_info { + __le64 TotalAllocationUnits; + __le64 FreeAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __packed; /* size info, level 0x103 */ + +#define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */ +#define STRING_LENGTH 28 + +struct fs_extended_info { + __le32 magic; + __le32 version; + __le32 release; + __u64 rel_date; + char version_string[STRING_LENGTH]; +} __packed; + +struct object_id_info { + char objid[16]; + struct fs_extended_info extended_info; +} __packed; + +struct file_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 0x101 FF resp data */ + +struct file_names_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 0xc FF resp data */ + +struct file_full_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; + char FileName[1]; +} __packed; /* level 0x102 FF resp */ + +struct file_both_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + char FileName[1]; +} __packed; /* level 0x104 FFrsp data */ + +struct file_id_both_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + __le16 Reserved2; + __le64 UniqueId; + char FileName[1]; +} __packed; + +struct file_id_full_dir_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* EA size */ + __le32 Reserved; + __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ + char FileName[1]; +} __packed; /* level 0x105 FF rsp data */ + +struct smb_version_values { + char *version_string; + __u16 protocol_id; + __le16 lock_cmd; + __u32 capabilities; + __u32 max_read_size; + __u32 max_write_size; + __u32 max_trans_size; + __u32 large_lock_type; + __u32 exclusive_lock_type; + __u32 shared_lock_type; + __u32 unlock_lock_type; + size_t header_size; + size_t max_header_size; + size_t read_rsp_size; + unsigned int cap_unix; + unsigned int cap_nt_find; + unsigned int cap_large_files; + __u16 signing_enabled; + __u16 signing_required; + size_t create_lease_size; + size_t create_durable_size; + size_t create_durable_v2_size; + size_t create_mxac_size; + size_t create_disk_id_size; + size_t create_posix_size; +}; + +struct filesystem_posix_info { + /* For undefined recommended transfer size return -1 in that field */ + __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ + __le32 BlockSize; + /* The next three fields are in terms of the block size. + * (above). If block size is unknown, 4096 would be a + * reasonable block size for a server to report. + * Note that returning the blocks/blocksavail removes need + * to make a second call (to QFSInfo level 0x103 to get this info. + * UserBlockAvail is typically less than or equal to BlocksAvail, + * if no distinction is made return the same value in each + */ + __le64 TotalBlocks; + __le64 BlocksAvail; /* bfree */ + __le64 UserBlocksAvail; /* bavail */ + /* For undefined Node fields or FSID return -1 */ + __le64 TotalFileNodes; + __le64 FreeFileNodes; + __le64 FileSysIdentifier; /* fsid */ + /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ + /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ +} __packed; + +struct smb_version_ops { + uint16_t (*get_cmd_val)(struct ksmbd_work *swork); + int (*init_rsp_hdr)(struct ksmbd_work *swork); + void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err); + int (*allocate_rsp_buf)(struct ksmbd_work *work); + int (*set_rsp_credits)(struct ksmbd_work *work); + int (*check_user_session)(struct ksmbd_work *work); + int (*get_ksmbd_tcon)(struct ksmbd_work *work); + bool (*is_sign_req)(struct ksmbd_work *work, unsigned int command); + int (*check_sign_req)(struct ksmbd_work *work); + void (*set_sign_rsp)(struct ksmbd_work *work); + int (*generate_signingkey)(struct ksmbd_session *sess); + int (*generate_encryptionkey)(struct ksmbd_session *sess); + int (*is_transform_hdr)(void *buf); + int (*decrypt_req)(struct ksmbd_work *work); + int (*encrypt_resp)(struct ksmbd_work *work); +}; + +struct smb_version_cmds { + int (*proc)(struct ksmbd_work *swork); +}; + + + +int ksmbd_min_protocol(void); +int ksmbd_max_protocol(void); + +int ksmbd_lookup_protocol_idx(char *str); + +int ksmbd_verify_smb_message(struct ksmbd_work *work); +bool ksmbd_smb_request(struct ksmbd_conn *conn); + +int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count); + +int ksmbd_negotiate_smb_dialect(void *buf); +int ksmbd_init_smb_server(struct ksmbd_work *work); + +bool ksmbd_pdu_size_has_room(unsigned int pdu); + +struct ksmbd_kstat; +int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, + int info_level, + struct ksmbd_file *dir, + struct ksmbd_dir_info *d_info, + char *search_pattern, + int (*fn)(struct ksmbd_conn *, + int, + struct ksmbd_dir_info *, + struct ksmbd_kstat *)); + +int ksmbd_extract_shortname(struct ksmbd_conn *conn, + const char *longname, + char *shortname); + +int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command); + +int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp); +int ksmbd_override_fsids(struct ksmbd_work *work); +void ksmbd_revert_fsids(struct ksmbd_work *work); + +unsigned int ksmbd_server_side_copy_max_chunk_count(void); +unsigned int ksmbd_server_side_copy_max_chunk_size(void); +unsigned int ksmbd_server_side_copy_max_total_size(void); +bool is_asterisk(char *p); +__le32 smb_map_generic_desired_access(__le32 daccess); + +static inline unsigned int get_rfc1002_len(void *buf) +{ + return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; +} + +static inline void inc_rfc1001_len(void *buf, int count) +{ + be32_add_cpu((__be32 *)buf, count); +} +#endif /* __SMB_COMMON_H__ */ diff --git a/fs/cifsd/smbacl.c b/fs/cifsd/smbacl.c new file mode 100644 index 000000000000..8d8360ca4751 --- /dev/null +++ b/fs/cifsd/smbacl.c @@ -0,0 +1,1309 @@ +// SPDX-License-Identifier: LGPL-2.1+ +/* + * Copyright (C) International Business Machines Corp., 2007,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +#include +#include +#include + +#include "smbacl.h" +#include "smb_common.h" +#include "server.h" +#include "misc.h" +#include "ksmbd_server.h" +#include "mgmt/share_config.h" + +static const struct smb_sid domain = {1, 4, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(21), cpu_to_le32(1), cpu_to_le32(2), cpu_to_le32(3), + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* security id for everyone/world system group */ +static const struct smb_sid creator_owner = { + 1, 1, {0, 0, 0, 0, 0, 3}, {0} }; +/* security id for everyone/world system group */ +static const struct smb_sid creator_group = { + 1, 1, {0, 0, 0, 0, 0, 3}, {cpu_to_le32(1)} }; + +/* security id for everyone/world system group */ +static const struct smb_sid sid_everyone = { + 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; +/* security id for Authenticated Users system group */ +static const struct smb_sid sid_authusers = { + 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; + +/* S-1-22-1 Unmapped Unix users */ +static const struct smb_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-22-2 Unmapped Unix groups */ +static const struct smb_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ + +/* S-1-5-88 MS NFS and Apple style UID/GID/mode */ + +/* S-1-5-88-1 Unix uid */ +static const struct smb_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-2 Unix gid */ +static const struct smb_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-3 Unix mode */ +static const struct smb_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * if the two SIDs (roughly equivalent to a UUID for a user or group) are + * the same returns zero, if they do not match returns non-zero. + */ +int +compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) +{ + int i; + int num_subauth, num_sat, num_saw; + + if ((!ctsid) || (!cwsid)) + return 1; + + /* compare the revision */ + if (ctsid->revision != cwsid->revision) { + if (ctsid->revision > cwsid->revision) + return 1; + else + return -1; + } + + /* compare all of the six auth values */ + for (i = 0; i < NUM_AUTHS; ++i) { + if (ctsid->authority[i] != cwsid->authority[i]) { + if (ctsid->authority[i] > cwsid->authority[i]) + return 1; + else + return -1; + } + } + + /* compare all of the subauth values if any */ + num_sat = ctsid->num_subauth; + num_saw = cwsid->num_subauth; + num_subauth = num_sat < num_saw ? num_sat : num_saw; + if (num_subauth) { + for (i = 0; i < num_subauth; ++i) { + if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { + if (le32_to_cpu(ctsid->sub_auth[i]) > + le32_to_cpu(cwsid->sub_auth[i])) + return 1; + else + return -1; + } + } + } + + return 0; /* sids compare/match */ +} + +static void +smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) +{ + int i; + + dst->revision = src->revision; + dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); + for (i = 0; i < NUM_AUTHS; ++i) + dst->authority[i] = src->authority[i]; + for (i = 0; i < dst->num_subauth; ++i) + dst->sub_auth[i] = src->sub_auth[i]; +} + +/* + * change posix mode to reflect permissions + * pmode is the existing mode (we only want to overwrite part of this + * bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 + */ +static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags, + int type) +{ + __u32 flags = le32_to_cpu(ace_flags); + umode_t mode = 0; + + if (flags & GENERIC_ALL) { + mode = 0777; + ksmbd_debug(SMB, "all perms\n"); + return mode; + } + + if ((flags & GENERIC_READ) || + (flags & FILE_READ_RIGHTS)) + mode = 0444; + if ((flags & GENERIC_WRITE) || + (flags & FILE_WRITE_RIGHTS)) { + mode |= 0222; + if (S_ISDIR(fattr->cf_mode)) + mode |= 0111; + } + if ((flags & GENERIC_EXECUTE) || + (flags & FILE_EXEC_RIGHTS)) + mode |= 0111; + + if (type == ACCESS_DENIED_ACE_TYPE || + type == ACCESS_DENIED_OBJECT_ACE_TYPE) + mode = ~mode; + + ksmbd_debug(SMB, "access flags 0x%x mode now %04o\n", flags, mode); + + return mode; +} + +/* + * Generate access flags to reflect permissions mode is the existing mode. + * This function is called for every ACE in the DACL whose SID matches + * with either owner or group or everyone. + */ +static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, + __u32 *pace_flags) +{ + /* reset access mask */ + *pace_flags = 0x0; + + /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ + mode &= bits_to_use; + + /* + * check for R/W/X UGO since we do not know whose flags + * is this but we have cleared all the bits sans RWX for + * either user or group or other as per bits_to_use + */ + if (mode & 0444) + *pace_flags |= SET_FILE_READ_RIGHTS; + if (mode & 0222) + *pace_flags |= FILE_WRITE_RIGHTS; + if (mode & 0111) + *pace_flags |= SET_FILE_EXEC_RIGHTS; + + ksmbd_debug(SMB, "mode: %o, access flags now 0x%x\n", + mode, *pace_flags); +} + +static __u16 fill_ace_for_sid(struct smb_ace *pntace, + const struct smb_sid *psid, int type, int flags, + umode_t mode, umode_t bits) +{ + int i; + __u16 size = 0; + __u32 access_req = 0; + + pntace->type = type; + pntace->flags = flags; + mode_to_access_flags(mode, bits, &access_req); + if (!access_req) + access_req = SET_MINIMUM_RIGHTS; + pntace->access_req = cpu_to_le32(access_req); + + pntace->sid.revision = psid->revision; + pntace->sid.num_subauth = psid->num_subauth; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = psid->authority[i]; + for (i = 0; i < psid->num_subauth; i++) + pntace->sid.sub_auth[i] = psid->sub_auth[i]; + + size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); + pntace->size = cpu_to_le16(size); + + return size; +} + +void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid) +{ + switch (sidtype) { + case SIDOWNER: + smb_copy_sid(ssid, &server_conf.domain_sid); + break; + case SIDUNIX_USER: + smb_copy_sid(ssid, &sid_unix_users); + break; + case SIDUNIX_GROUP: + smb_copy_sid(ssid, &sid_unix_groups); + break; + case SIDCREATOR_OWNER: + smb_copy_sid(ssid, &creator_owner); + return; + case SIDCREATOR_GROUP: + smb_copy_sid(ssid, &creator_group); + return; + case SIDNFS_USER: + smb_copy_sid(ssid, &sid_unix_NFS_users); + break; + case SIDNFS_GROUP: + smb_copy_sid(ssid, &sid_unix_NFS_groups); + break; + case SIDNFS_MODE: + smb_copy_sid(ssid, &sid_unix_NFS_mode); + break; + default: + return; + } + + /* RID */ + ssid->sub_auth[ssid->num_subauth] = cpu_to_le32(cid); + ssid->num_subauth++; +} + +static int sid_to_id(struct smb_sid *psid, uint sidtype, + struct smb_fattr *fattr) +{ + int rc = -EINVAL; + + /* + * If we have too many subauthorities, then something is really wrong. + * Just return an error. + */ + if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { + ksmbd_err("%s: %u subauthorities is too many!\n", + __func__, psid->num_subauth); + return -EIO; + } + + if (sidtype == SIDOWNER) { + kuid_t uid; + uid_t id; + + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); + if (id > 0) { + uid = make_kuid(&init_user_ns, id); + if (uid_valid(uid) && + kuid_has_mapping(&init_user_ns, uid)) { + fattr->cf_uid = uid; + rc = 0; + } + } + } else { + kgid_t gid; + gid_t id; + + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); + if (id > 0) { + gid = make_kgid(&init_user_ns, id); + if (gid_valid(gid) && + kgid_has_mapping(&init_user_ns, gid)) { + fattr->cf_gid = gid; + rc = 0; + } + } + } + + return rc; +} + +void posix_state_to_acl(struct posix_acl_state *state, + struct posix_acl_entry *pace) +{ + int i; + + pace->e_tag = ACL_USER_OBJ; + pace->e_perm = state->owner.allow; + for (i = 0; i < state->users->n; i++) { + pace++; + pace->e_tag = ACL_USER; + pace->e_uid = state->users->aces[i].uid; + pace->e_perm = state->users->aces[i].perms.allow; + } + + pace++; + pace->e_tag = ACL_GROUP_OBJ; + pace->e_perm = state->group.allow; + + for (i = 0; i < state->groups->n; i++) { + pace++; + pace->e_tag = ACL_GROUP; + pace->e_gid = state->groups->aces[i].gid; + pace->e_perm = state->groups->aces[i].perms.allow; + } + + if (state->users->n || state->groups->n) { + pace++; + pace->e_tag = ACL_MASK; + pace->e_perm = state->mask.allow; + } + + pace++; + pace->e_tag = ACL_OTHER; + pace->e_perm = state->other.allow; +} + +int init_acl_state(struct posix_acl_state *state, int cnt) +{ + int alloc; + + memset(state, 0, sizeof(struct posix_acl_state)); + /* + * In the worst case, each individual acl could be for a distinct + * named user or group, but we don't know which, so we allocate + * enough space for either: + */ + alloc = sizeof(struct posix_ace_state_array) + + cnt*sizeof(struct posix_user_ace_state); + state->users = kzalloc(alloc, GFP_KERNEL); + if (!state->users) + return -ENOMEM; + state->groups = kzalloc(alloc, GFP_KERNEL); + if (!state->groups) { + kfree(state->users); + return -ENOMEM; + } + return 0; +} + +void free_acl_state(struct posix_acl_state *state) +{ + kfree(state->users); + kfree(state->groups); +} + +static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, + struct smb_sid *pownersid, struct smb_sid *pgrpsid, + struct smb_fattr *fattr) +{ + int i, ret; + int num_aces = 0; + int acl_size; + char *acl_base; + struct smb_ace **ppace; + struct posix_acl_entry *cf_pace, *cf_pdace; + struct posix_acl_state acl_state, default_acl_state; + umode_t mode = 0, acl_mode; + bool owner_found = false, group_found = false, others_found = false; + + if (!pdacl) + return; + + /* validate that we do not go past end of acl */ + if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { + ksmbd_err("ACL too small to parse DACL\n"); + return; + } + + ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), + le32_to_cpu(pdacl->num_aces)); + + acl_base = (char *)pdacl; + acl_size = sizeof(struct smb_acl); + + num_aces = le32_to_cpu(pdacl->num_aces); + if (num_aces <= 0) + return; + + if (num_aces > ULONG_MAX / sizeof(struct smb_ace *)) + return; + + ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), + GFP_KERNEL); + if (!ppace) + return; + + ret = init_acl_state(&acl_state, num_aces); + if (ret) + return; + ret = init_acl_state(&default_acl_state, num_aces); + if (ret) { + free_acl_state(&acl_state); + return; + } + + /* + * reset rwx permissions for user/group/other. + * Also, if num_aces is 0 i.e. DACL has no ACEs, + * user/group/other have no permissions + */ + for (i = 0; i < num_aces; ++i) { + ppace[i] = (struct smb_ace *) (acl_base + acl_size); + acl_base = (char *)ppace[i]; + acl_size = le16_to_cpu(ppace[i]->size); + ppace[i]->access_req = + smb_map_generic_desired_access(ppace[i]->access_req); + + if (!(compare_sids(&(ppace[i]->sid), &sid_unix_NFS_mode))) { + fattr->cf_mode = + le32_to_cpu(ppace[i]->sid.sub_auth[2]); + break; + } else if (!compare_sids(&(ppace[i]->sid), pownersid)) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, ppace[i]->type); + acl_mode &= 0700; + + if (!owner_found) { + mode &= ~(0700); + mode |= acl_mode; + } + owner_found = true; + } else if (!compare_sids(&(ppace[i]->sid), pgrpsid) || + ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == + DOMAIN_USER_RID_LE) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, ppace[i]->type); + acl_mode &= 0070; + if (!group_found) { + mode &= ~(0070); + mode |= acl_mode; + } + group_found = true; + } else if (!compare_sids(&(ppace[i]->sid), &sid_everyone)) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, ppace[i]->type); + acl_mode &= 0007; + if (!others_found) { + mode &= ~(0007); + mode |= acl_mode; + } + others_found = true; + } else if (!compare_sids(&(ppace[i]->sid), &creator_owner)) + continue; + else if (!compare_sids(&(ppace[i]->sid), &creator_group)) + continue; + else if (!compare_sids(&(ppace[i]->sid), &sid_authusers)) + continue; + else { + struct smb_fattr temp_fattr; + + acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, + ppace[i]->type); + temp_fattr.cf_uid = INVALID_UID; + ret = sid_to_id(&ppace[i]->sid, SIDOWNER, &temp_fattr); + if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { + ksmbd_err("%s: Error %d mapping Owner SID to uid\n", + __func__, ret); + continue; + } + + acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; + acl_state.users->aces[acl_state.users->n].uid = + temp_fattr.cf_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + ((acl_mode & 0700) >> 6) | 0004; + default_acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; + default_acl_state.users->aces[default_acl_state.users->n].uid = + temp_fattr.cf_uid; + default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = + ((acl_mode & 0700) >> 6) | 0004; + } + } + kfree(ppace); + + if (owner_found) { + /* The owner must be set to at least read-only. */ + acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; + acl_state.users->aces[acl_state.users->n].uid = fattr->cf_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + ((mode & 0700) >> 6) | 0004; + default_acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; + default_acl_state.users->aces[default_acl_state.users->n].uid = + fattr->cf_uid; + default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = + ((mode & 0700) >> 6) | 0004; + } + + if (group_found) { + acl_state.group.allow = (mode & 0070) >> 3; + acl_state.groups->aces[acl_state.groups->n].gid = + fattr->cf_gid; + acl_state.groups->aces[acl_state.groups->n++].perms.allow = + (mode & 0070) >> 3; + default_acl_state.group.allow = mode & 0070 >> 3; + default_acl_state.groups->aces[default_acl_state.groups->n].gid = + fattr->cf_gid; + default_acl_state.groups->aces[default_acl_state.groups->n++].perms.allow = + (mode & 0070) >> 3; + } + + if (others_found) { + fattr->cf_mode &= ~(0007); + fattr->cf_mode |= mode & 0007; + + acl_state.other.allow = mode & 0007; + default_acl_state.other.allow = mode & 0007; + } + + if (acl_state.users->n || acl_state.groups->n) { + acl_state.mask.allow = 0x07; + fattr->cf_acls = ksmbd_vfs_posix_acl_alloc(acl_state.users->n + + acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_acls) { + cf_pace = fattr->cf_acls->a_entries; + posix_state_to_acl(&acl_state, cf_pace); + } + } + + if (default_acl_state.users->n || default_acl_state.groups->n) { + default_acl_state.mask.allow = 0x07; + fattr->cf_dacls = + ksmbd_vfs_posix_acl_alloc(default_acl_state.users->n + + default_acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_dacls) { + cf_pdace = fattr->cf_dacls->a_entries; + posix_state_to_acl(&default_acl_state, cf_pdace); + } + } + free_acl_state(&acl_state); + free_acl_state(&default_acl_state); +} + +static void set_posix_acl_entries_dacl(struct smb_ace *pndace, + struct smb_fattr *fattr, u32 *num_aces, u16 *size, u32 nt_aces_num) +{ + struct posix_acl_entry *pace; + struct smb_sid *sid; + struct smb_ace *ntace; + int i, j; + + if (!fattr->cf_acls) + goto posix_default_acl; + + pace = fattr->cf_acls->a_entries; + for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) { + int flags = 0; + + sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!sid) + break; + + if (pace->e_tag == ACL_USER) { + uid_t uid; + unsigned int sid_type = SIDOWNER; + + uid = from_kuid(&init_user_ns, pace->e_uid); + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, sid); + } else if (pace->e_tag == ACL_GROUP) { + gid_t gid; + + gid = from_kgid(&init_user_ns, pace->e_gid); + id_to_sid(gid, SIDUNIX_GROUP, sid); + } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) { + smb_copy_sid(sid, &sid_everyone); + } else { + kfree(sid); + continue; + } + ntace = pndace; + for (j = 0; j < nt_aces_num; j++) { + if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] == + sid->sub_auth[sid->num_subauth - 1]) + goto pass_same_sid; + ntace = (struct smb_ace *)((char *)ntace + + le16_to_cpu(ntace->size)); + } + + if (S_ISDIR(fattr->cf_mode) && pace->e_tag == ACL_OTHER) + flags = 0x03; + + ntace = (struct smb_ace *) ((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, + pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + + if (S_ISDIR(fattr->cf_mode) && + (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { + ntace = (struct smb_ace *) ((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, + 0x03, pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + } + +pass_same_sid: + kfree(sid); + } + + if (nt_aces_num) + return; + +posix_default_acl: + if (!fattr->cf_dacls) + return; + + pace = fattr->cf_dacls->a_entries; + for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) { + sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!sid) + break; + + if (pace->e_tag == ACL_USER) { + uid_t uid; + + uid = from_kuid(&init_user_ns, pace->e_uid); + id_to_sid(uid, SIDCREATOR_OWNER, sid); + } else if (pace->e_tag == ACL_GROUP) { + gid_t gid; + + gid = from_kgid(&init_user_ns, pace->e_gid); + id_to_sid(gid, SIDCREATOR_GROUP, sid); + } else { + kfree(sid); + continue; + } + + ntace = (struct smb_ace *) ((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, + pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + kfree(sid); + } +} + +static void set_ntacl_dacl(struct smb_acl *pndacl, struct smb_acl *nt_dacl, + const struct smb_sid *pownersid, const struct smb_sid *pgrpsid, + struct smb_fattr *fattr) +{ + struct smb_ace *ntace, *pndace; + int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; + unsigned short size = 0; + int i; + + pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); + if (nt_num_aces) { + ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl)); + for (i = 0; i < nt_num_aces; i++) { + memcpy((char *)pndace + size, ntace, le16_to_cpu(ntace->size)); + size += le16_to_cpu(ntace->size); + ntace = (struct smb_ace *)((char *)ntace + le16_to_cpu(ntace->size)); + num_aces++; + } + } + + set_posix_acl_entries_dacl(pndace, fattr, &num_aces, &size, nt_num_aces); + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); +} + +static void set_mode_dacl(struct smb_acl *pndacl, struct smb_fattr *fattr) +{ + struct smb_ace *pace, *pndace; + u32 num_aces = 0; + u16 size = 0, ace_size = 0; + uid_t uid; + const struct smb_sid *sid; + + pace = pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); + + if (fattr->cf_acls) { + set_posix_acl_entries_dacl(pndace, fattr, &num_aces, &size, num_aces); + goto out; + } + + /* owner RID */ + uid = from_kuid(&init_user_ns, fattr->cf_uid); + if (uid) + sid = &server_conf.domain_sid; + else + sid = &sid_unix_users; + ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0700); + pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); + pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + pace->size = cpu_to_le16(ace_size + 4); + size += le16_to_cpu(pace->size); + pace = (struct smb_ace *)((char *)pndace + size); + + /* Group RID */ + ace_size = fill_ace_for_sid(pace, &sid_unix_groups, + ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); + pace->sid.sub_auth[pace->sid.num_subauth++] = + cpu_to_le32(from_kgid(&init_user_ns, fattr->cf_gid)); + pace->size = cpu_to_le16(ace_size + 4); + size += le16_to_cpu(pace->size); + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 3; + + if (S_ISDIR(fattr->cf_mode)) { + pace = (struct smb_ace *)((char *)pndace + size); + + /* creator owner */ + size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED, + 0x0b, fattr->cf_mode, 0700); + pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + pace = (struct smb_ace *)((char *)pndace + size); + + /* creator group */ + size += fill_ace_for_sid(pace, &creator_group, ACCESS_ALLOWED, + 0x0b, fattr->cf_mode, 0070); + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 5; + } + + /* other */ + size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0007); + +out: + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); +} + +static int parse_sid(struct smb_sid *psid, char *end_of_acl) +{ + /* + * validate that we do not go past end of ACL - sid must be at least 8 + * bytes long (assuming no sub-auths - e.g. the null SID + */ + if (end_of_acl < (char *)psid + 8) { + ksmbd_err("ACL too small to parse SID %p\n", psid); + return -EINVAL; + } + + return 0; +} + +/* Convert CIFS ACL to POSIX form */ +int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, + struct smb_fattr *fattr) +{ + int rc = 0; + struct smb_sid *owner_sid_ptr, *group_sid_ptr; + struct smb_acl *dacl_ptr; /* no need for SACL ptr */ + char *end_of_acl = ((char *)pntsd) + acl_len; + __u32 dacloffset; + int total_ace_size = 0, pntsd_type; + + if (pntsd == NULL) + return -EIO; + + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + group_sid_ptr = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + dacloffset = le32_to_cpu(pntsd->dacloffset); + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); + ksmbd_debug(SMB, + "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", + pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), + le32_to_cpu(pntsd->gsidoffset), + le32_to_cpu(pntsd->sacloffset), dacloffset); + + if (dacloffset && dacl_ptr) + total_ace_size = + le16_to_cpu(dacl_ptr->size) - sizeof(struct smb_acl); + + pntsd_type = le16_to_cpu(pntsd->type); + + if (!(pntsd_type & DACL_PRESENT)) { + ksmbd_debug(SMB, "DACL_PRESENT in DACL type is not set\n"); + return rc; + } + + pntsd->type = cpu_to_le16(DACL_PRESENT); + + if (pntsd->osidoffset) { + rc = parse_sid(owner_sid_ptr, end_of_acl); + if (rc) { + ksmbd_err("%s: Error %d parsing Owner SID\n", __func__, rc); + return rc; + } + + rc = sid_to_id(owner_sid_ptr, SIDOWNER, fattr); + if (rc) { + ksmbd_err("%s: Error %d mapping Owner SID to uid\n", + __func__, rc); + owner_sid_ptr = NULL; + } + } + + if (pntsd->gsidoffset) { + rc = parse_sid(group_sid_ptr, end_of_acl); + if (rc) { + ksmbd_err("%s: Error %d mapping Owner SID to gid\n", + __func__, rc); + return rc; + } + rc = sid_to_id(group_sid_ptr, SIDUNIX_GROUP, fattr); + if (rc) { + ksmbd_err("%s: Error %d mapping Group SID to gid\n", + __func__, rc); + group_sid_ptr = NULL; + } + } + + if ((pntsd_type & + (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) == + (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + if (pntsd_type & DACL_PROTECTED) + pntsd->type |= cpu_to_le16(DACL_PROTECTED); + + if (dacloffset) { + parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr, + fattr); + } + + return 0; +} + +/* Convert permission bits from mode to equivalent CIFS ACL */ +int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, + int addition_info, __u32 *secdesclen, struct smb_fattr *fattr) +{ + int rc = 0; + __u32 offset; + struct smb_sid *owner_sid_ptr, *group_sid_ptr; + struct smb_sid *nowner_sid_ptr, *ngroup_sid_ptr; + struct smb_acl *dacl_ptr = NULL; /* no need for SACL ptr */ + uid_t uid; + gid_t gid; + unsigned int sid_type = SIDOWNER; + + nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!nowner_sid_ptr) + return -ENOMEM; + + uid = from_kuid(&init_user_ns, fattr->cf_uid); + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, nowner_sid_ptr); + + ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!ngroup_sid_ptr) { + kfree(nowner_sid_ptr); + return -ENOMEM; + } + + gid = from_kgid(&init_user_ns, fattr->cf_gid); + id_to_sid(gid, SIDUNIX_GROUP, ngroup_sid_ptr); + + offset = sizeof(struct smb_ntsd); + pntsd->sacloffset = 0; + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE); + if (ppntsd) + pntsd->type |= ppntsd->type; + + if (addition_info & OWNER_SECINFO) { + pntsd->osidoffset = cpu_to_le32(offset); + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); + smb_copy_sid(owner_sid_ptr, nowner_sid_ptr); + offset += 1 + 1 + 6 + (nowner_sid_ptr->num_subauth * 4); + } + + if (addition_info & GROUP_SECINFO) { + pntsd->gsidoffset = cpu_to_le32(offset); + group_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); + smb_copy_sid(group_sid_ptr, ngroup_sid_ptr); + offset += 1 + 1 + 6 + (ngroup_sid_ptr->num_subauth * 4); + } + + if (addition_info & DACL_SECINFO) { + pntsd->type |= cpu_to_le16(DACL_PRESENT); + dacl_ptr = (struct smb_acl *)((char *)pntsd + offset); + dacl_ptr->revision = cpu_to_le16(2); + dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl)); + dacl_ptr->num_aces = 0; + + if (!ppntsd) + set_mode_dacl(dacl_ptr, fattr); + else if (!ppntsd->dacloffset) + goto out; + else { + struct smb_acl *ppdacl_ptr; + + ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + + le32_to_cpu(ppntsd->dacloffset)); + set_ntacl_dacl(dacl_ptr, ppdacl_ptr, nowner_sid_ptr, + ngroup_sid_ptr, fattr); + } + pntsd->dacloffset = cpu_to_le32(offset); + offset += le16_to_cpu(dacl_ptr->size); + } + +out: + kfree(nowner_sid_ptr); + kfree(ngroup_sid_ptr); + *secdesclen = offset; + return rc; +} + +static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, + u8 flags, __le32 access_req) +{ + ace->type = type; + ace->flags = flags; + ace->access_req = access_req; + smb_copy_sid(&ace->sid, sid); + ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4)); +} + +int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, + unsigned int uid, unsigned int gid) +{ + const struct smb_sid *psid, *creator = NULL; + struct smb_ace *parent_aces, *aces; + struct smb_acl *parent_pdacl; + struct smb_ntsd *parent_pntsd = NULL; + struct smb_sid owner_sid, group_sid; + struct dentry *parent = dentry->d_parent; + int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0; + int rc = -ENOENT, num_aces, dacloffset, pntsd_type, acl_len; + char *aces_base; + bool is_dir = S_ISDIR(dentry->d_inode->i_mode); + + acl_len = ksmbd_vfs_get_sd_xattr(conn, parent, &parent_pntsd); + if (acl_len <= 0) + return rc; + dacloffset = le32_to_cpu(parent_pntsd->dacloffset); + if (!dacloffset) + goto out; + + parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); + num_aces = le32_to_cpu(parent_pdacl->num_aces); + pntsd_type = le16_to_cpu(parent_pntsd->type); + + aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); + if (!aces_base) + goto out; + + aces = (struct smb_ace *)aces_base; + parent_aces = (struct smb_ace *)((char *)parent_pdacl + + sizeof(struct smb_acl)); + + if (pntsd_type & DACL_AUTO_INHERITED) + inherited_flags = INHERITED_ACE; + + for (i = 0; i < num_aces; i++) { + flags = parent_aces->flags; + if (!smb_inherit_flags(flags, is_dir)) + goto pass; + if (is_dir) { + flags &= ~(INHERIT_ONLY_ACE | INHERITED_ACE); + if (!(flags & CONTAINER_INHERIT_ACE)) + flags |= INHERIT_ONLY_ACE; + if (flags & NO_PROPAGATE_INHERIT_ACE) + flags = 0; + } else + flags = 0; + + if (!compare_sids(&creator_owner, &parent_aces->sid)) { + creator = &creator_owner; + id_to_sid(uid, SIDOWNER, &owner_sid); + psid = &owner_sid; + } else if (!compare_sids(&creator_group, &parent_aces->sid)) { + creator = &creator_group; + id_to_sid(gid, SIDUNIX_GROUP, &group_sid); + psid = &group_sid; + } else { + creator = NULL; + psid = &parent_aces->sid; + } + + if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) { + smb_set_ace(aces, psid, parent_aces->type, inherited_flags, + parent_aces->access_req); + nt_size += le16_to_cpu(aces->size); + ace_cnt++; + aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); + flags |= INHERIT_ONLY_ACE; + psid = creator; + } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) + psid = &parent_aces->sid; + + smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, + parent_aces->access_req); + nt_size += le16_to_cpu(aces->size); + aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); + ace_cnt++; +pass: + parent_aces = + (struct smb_ace *)((char *)parent_aces + le16_to_cpu(parent_aces->size)); + } + + if (nt_size > 0) { + struct smb_ntsd *pntsd; + struct smb_acl *pdacl; + struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; + int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; + + if (parent_pntsd->osidoffset) { + powner_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->osidoffset)); + powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4); + } + if (parent_pntsd->gsidoffset) { + pgroup_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->gsidoffset)); + pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); + } + + pntsd = kzalloc(sizeof(struct smb_ntsd) + powner_sid_size + + pgroup_sid_size + sizeof(struct smb_acl) + + nt_size, GFP_KERNEL); + if (!pntsd) { + rc = -ENOMEM; + goto out; + } + + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PRESENT); + if (le16_to_cpu(parent_pntsd->type) & DACL_AUTO_INHERITED) + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + pntsd_size = sizeof(struct smb_ntsd); + pntsd->osidoffset = parent_pntsd->osidoffset; + pntsd->gsidoffset = parent_pntsd->gsidoffset; + pntsd->dacloffset = parent_pntsd->dacloffset; + + if (pntsd->osidoffset) { + struct smb_sid *owner_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + memcpy(owner_sid, powner_sid, powner_sid_size); + pntsd_size += powner_sid_size; + } + + if (pntsd->gsidoffset) { + struct smb_sid *group_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + memcpy(group_sid, pgroup_sid, pgroup_sid_size); + pntsd_size += pgroup_sid_size; + } + + if (pntsd->dacloffset) { + struct smb_ace *pace; + + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + pdacl->revision = cpu_to_le16(2); + pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size); + pdacl->num_aces = cpu_to_le32(ace_cnt); + pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + memcpy(pace, aces_base, nt_size); + pntsd_size += sizeof(struct smb_acl) + nt_size; + } + + ksmbd_vfs_set_sd_xattr(conn, dentry, pntsd, pntsd_size); + kfree(pntsd); + rc = 0; + } + + kfree(aces_base); +out: + return rc; +} + +bool smb_inherit_flags(int flags, bool is_dir) +{ + if (!is_dir) + return (flags & OBJECT_INHERIT_ACE) != 0; + + if (flags & OBJECT_INHERIT_ACE && !(flags & NO_PROPAGATE_INHERIT_ACE)) + return true; + + if (flags & CONTAINER_INHERIT_ACE) + return true; + return false; +} + +int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, + __le32 *pdaccess, int uid) +{ + struct smb_ntsd *pntsd = NULL; + struct smb_acl *pdacl; + struct posix_acl *posix_acls; + int rc = 0, acl_size; + struct smb_sid sid; + int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE); + struct smb_ace *ace; + int i, found = 0; + unsigned int access_bits = 0; + struct smb_ace *others_ace = NULL; + struct posix_acl_entry *pa_entry; + unsigned int sid_type = SIDOWNER; + + ksmbd_debug(SMB, "check permission using windows acl\n"); + acl_size = ksmbd_vfs_get_sd_xattr(conn, dentry, &pntsd); + if (acl_size <= 0 || (pntsd && !pntsd->dacloffset)) + return 0; + + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + if (!pdacl->num_aces) { + if (!(le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) && + *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) { + rc = -EACCES; + goto err_out; + } + kfree(pntsd); + return 0; + } + + if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) { + granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | + DELETE; + + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + granted |= le32_to_cpu(ace->access_req); + ace = (struct smb_ace *) ((char *)ace + le16_to_cpu(ace->size)); + } + + if (!pdacl->num_aces) + granted = GENERIC_ALL_FLAGS; + } + + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, &sid); + + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + if (!compare_sids(&sid, &ace->sid) || + !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { + found = 1; + break; + } + if (!compare_sids(&sid_everyone, &ace->sid)) + others_ace = ace; + + ace = (struct smb_ace *) ((char *)ace + le16_to_cpu(ace->size)); + } + + if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) { + granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | + DELETE; + + granted |= le32_to_cpu(ace->access_req); + + if (!pdacl->num_aces) + granted = GENERIC_ALL_FLAGS; + } + + posix_acls = ksmbd_vfs_get_acl(dentry->d_inode, ACL_TYPE_ACCESS); + if (posix_acls && !found) { + unsigned int id = -1; + + pa_entry = posix_acls->a_entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { + if (pa_entry->e_tag == ACL_USER) + id = from_kuid(&init_user_ns, pa_entry->e_uid); + else if (pa_entry->e_tag == ACL_GROUP) + id = from_kgid(&init_user_ns, pa_entry->e_gid); + else + continue; + + if (id == uid) { + mode_to_access_flags(pa_entry->e_perm, 0777, &access_bits); + if (!access_bits) + access_bits = SET_MINIMUM_RIGHTS; + goto check_access_bits; + } + } + } + if (posix_acls) + posix_acl_release(posix_acls); + + if (!found) { + if (others_ace) + ace = others_ace; + else { + ksmbd_debug(SMB, "Can't find corresponding sid\n"); + rc = -EACCES; + goto err_out; + } + } + + switch (ace->type) { + case ACCESS_ALLOWED_ACE_TYPE: + access_bits = le32_to_cpu(ace->access_req); + break; + case ACCESS_DENIED_ACE_TYPE: + case ACCESS_DENIED_CALLBACK_ACE_TYPE: + access_bits = le32_to_cpu(~ace->access_req); + break; + } + +check_access_bits: + if (granted & ~(access_bits | FILE_READ_ATTRIBUTES | + READ_CONTROL | WRITE_DAC | DELETE)) { + ksmbd_debug(SMB, "Access denied with winACL, granted : %x, access_req : %x\n", + granted, le32_to_cpu(ace->access_req)); + rc = -EACCES; + goto err_out; + } + + *pdaccess = cpu_to_le32(granted); +err_out: + kfree(pntsd); + return rc; +} + +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check) +{ + int rc; + struct smb_fattr fattr = {{0}}; + struct inode *inode = dentry->d_inode; + + fattr.cf_uid = INVALID_UID; + fattr.cf_gid = INVALID_GID; + fattr.cf_mode = inode->i_mode; + + rc = parse_sec_desc(pntsd, ntsd_len, &fattr); + if (rc) + goto out; + + inode->i_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); + if (!uid_eq(fattr.cf_uid, INVALID_UID)) + inode->i_uid = fattr.cf_uid; + if (!gid_eq(fattr.cf_gid, INVALID_GID)) + inode->i_gid = fattr.cf_gid; + mark_inode_dirty(inode); + + ksmbd_vfs_remove_acl_xattrs(dentry); + /* Update posix acls */ + if (fattr.cf_dacls) { + rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, + fattr.cf_acls); + if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) + rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, + fattr.cf_dacls); + } + + /* Check it only calling from SD BUFFER context */ + if (type_check && !(le16_to_cpu(pntsd->type) & DACL_PRESENT)) + goto out; + + if (test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + /* Update WinACL in xattr */ + ksmbd_vfs_remove_sd_xattrs(dentry); + ksmbd_vfs_set_sd_xattr(conn, dentry, pntsd, ntsd_len); + } + +out: + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + mark_inode_dirty(inode); + return rc; +} + +void ksmbd_init_domain(u32 *sub_auth) +{ + int i; + + memcpy(&server_conf.domain_sid, &domain, sizeof(struct smb_sid)); + for (i = 0; i < 3; ++i) + server_conf.domain_sid.sub_auth[i + 1] = cpu_to_le32(sub_auth[i]); +} diff --git a/fs/cifsd/smbacl.h b/fs/cifsd/smbacl.h new file mode 100644 index 000000000000..9b22bff4191f --- /dev/null +++ b/fs/cifsd/smbacl.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * Modified by Namjae Jeon (linkinjeon@kernel.org) + */ + +#ifndef _SMBACL_H +#define _SMBACL_H + +#include +#include +#include + +#include "mgmt/tree_connect.h" + +#define NUM_AUTHS (6) /* number of authority fields */ +#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ + +#define ACCESS_ALLOWED 0 +#define ACCESS_DENIED 1 + +#define SIDOWNER 1 +#define SIDGROUP 2 +#define SIDCREATOR_OWNER 3 +#define SIDCREATOR_GROUP 4 +#define SIDUNIX_USER 5 +#define SIDUNIX_GROUP 6 +#define SIDNFS_USER 7 +#define SIDNFS_GROUP 8 +#define SIDNFS_MODE 9 + +/* Revision for ACLs */ +#define SD_REVISION 1 + +/* Control flags for Security Descriptor */ +#define OWNER_DEFAULTED 0x0001 +#define GROUP_DEFAULTED 0x0002 +#define DACL_PRESENT 0x0004 +#define DACL_DEFAULTED 0x0008 +#define SACL_PRESENT 0x0010 +#define SACL_DEFAULTED 0x0020 +#define DACL_TRUSTED 0x0040 +#define SERVER_SECURITY 0x0080 +#define DACL_AUTO_INHERIT_REQ 0x0100 +#define SACL_AUTO_INHERIT_REQ 0x0200 +#define DACL_AUTO_INHERITED 0x0400 +#define SACL_AUTO_INHERITED 0x0800 +#define DACL_PROTECTED 0x1000 +#define SACL_PROTECTED 0x2000 +#define RM_CONTROL_VALID 0x4000 +#define SELF_RELATIVE 0x8000 + +/* ACE types - see MS-DTYP 2.4.4.1 */ +#define ACCESS_ALLOWED_ACE_TYPE 0x00 +#define ACCESS_DENIED_ACE_TYPE 0x01 +#define SYSTEM_AUDIT_ACE_TYPE 0x02 +#define SYSTEM_ALARM_ACE_TYPE 0x03 +#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 +#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 +#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 +#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A +#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B +#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C +#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D +#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ +#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F +#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ +#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 +#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 +#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 + +/* ACE flags */ +#define OBJECT_INHERIT_ACE 0x01 +#define CONTAINER_INHERIT_ACE 0x02 +#define NO_PROPAGATE_INHERIT_ACE 0x04 +#define INHERIT_ONLY_ACE 0x08 +#define INHERITED_ACE 0x10 +#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 +#define FAILED_ACCESS_ACE_FLAG 0x80 + +/* + * Maximum size of a string representation of a SID: + * + * The fields are unsigned values in decimal. So: + * + * u8: max 3 bytes in decimal + * u32: max 10 bytes in decimal + * + * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator + * + * For authority field, max is when all 6 values are non-zero and it must be + * represented in hex. So "-0x" + 12 hex digits. + * + * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') + */ +#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) +#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ + +#define DOMAIN_USER_RID_LE cpu_to_le32(513) + +struct ksmbd_conn; + +struct smb_ntsd { + __le16 revision; /* revision level */ + __le16 type; + __le32 osidoffset; + __le32 gsidoffset; + __le32 sacloffset; + __le32 dacloffset; +} __packed; + +struct smb_sid { + __u8 revision; /* revision level */ + __u8 num_subauth; + __u8 authority[NUM_AUTHS]; + __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ +} __packed; + +/* size of a struct cifs_sid, sans sub_auth array */ +#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) + +struct smb_acl { + __le16 revision; /* revision level */ + __le16 size; + __le32 num_aces; +} __packed; + +struct smb_ace { + __u8 type; + __u8 flags; + __le16 size; + __le32 access_req; + struct smb_sid sid; /* ie UUID of user or group who gets these perms */ +} __packed; + +struct smb_fattr { + kuid_t cf_uid; + kgid_t cf_gid; + umode_t cf_mode; + __le32 daccess; + struct posix_acl *cf_acls; + struct posix_acl *cf_dacls; +}; + +struct posix_ace_state { + u32 allow; + u32 deny; +}; + +struct posix_user_ace_state { + union { + kuid_t uid; + kgid_t gid; + }; + struct posix_ace_state perms; +}; + +struct posix_ace_state_array { + int n; + struct posix_user_ace_state aces[]; +}; + +/* + * while processing the nfsv4 ace, this maintains the partial permissions + * calculated so far: + */ + +struct posix_acl_state { + struct posix_ace_state owner; + struct posix_ace_state group; + struct posix_ace_state other; + struct posix_ace_state everyone; + struct posix_ace_state mask; /* deny unused in this case */ + struct posix_ace_state_array *users; + struct posix_ace_state_array *groups; +}; + +int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, + struct smb_fattr *fattr); +int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, + int addition_info, __u32 *secdesclen, struct smb_fattr *fattr); +int init_acl_state(struct posix_acl_state *state, int cnt); +void free_acl_state(struct posix_acl_state *state); +void posix_state_to_acl(struct posix_acl_state *state, + struct posix_acl_entry *pace); +int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid); +bool smb_inherit_flags(int flags, bool is_dir); +int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, + unsigned int uid, unsigned int gid); +int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, + __le32 *pdaccess, int uid); +int store_init_posix_acl(struct inode *inode, umode_t perm); +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check); +void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); +void ksmbd_init_domain(u32 *sub_auth); +#endif /* _SMBACL_H */ diff --git a/fs/cifsd/smberr.h b/fs/cifsd/smberr.h new file mode 100644 index 000000000000..ce842303ae1f --- /dev/null +++ b/fs/cifsd/smberr.h @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2002,2004 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * See Error Codes section of the SNIA CIFS Specification + * for more information + */ +#ifndef __KSMBD_SMBERR_H +#define __KSMBD_SMBERR_H + +#define SUCCESS 0x00 /* The request was successful. */ +#define ERRDOS 0x01 /* Error is from the core DOS operating system set */ +#define ERRSRV 0x02 /* Error is generated by the file server daemon */ +#define ERRHRD 0x03 /* Error is a hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* The following error codes may be generated with the SUCCESS error class.*/ + +/*#define SUCCESS 0 The request was successful. */ + +/* The following error codes may be generated with the ERRDOS error class.*/ + +#define ERRbadfunc 1 /* + * Invalid function. The server did not + * recognize or could not perform a + * system call generated by the server, + * e.g., set the DIRECTORY attribute on + * a data file, invalid seek mode. + */ +#define ERRbadfile 2 /* + * File not found. The last component + * of a file's pathname could not be + * found. + */ +#define ERRbadpath 3 /* + * Directory invalid. A directory + * component in a pathname could not be + * found. + */ +#define ERRnofids 4 /* + * Too many open files. The server has + * no file handles available. + */ +#define ERRnoaccess 5 /* + * Access denied, the client's context + * does not permit the requested + * function. This includes the + * following conditions: invalid rename + * command, write to Fid open for read + * only, read on Fid open for write + * only, attempt to delete a non-empty + * directory + */ +#define ERRbadfid 6 /* + * Invalid file handle. The file handle + * specified was not recognized by the + * server. + */ +#define ERRbadmcb 7 /* Memory control blocks destroyed. */ +#define ERRnomem 8 /* + * Insufficient server memory to + * perform the requested function. + */ +#define ERRbadmem 9 /* Invalid memory block address. */ +#define ERRbadenv 10 /* Invalid environment. */ +#define ERRbadformat 11 /* Invalid format. */ +#define ERRbadaccess 12 /* Invalid open mode. */ +#define ERRbaddata 13 /* + * Invalid data (generated only by + * IOCTL calls within the server). + */ +#define ERRbaddrive 15 /* Invalid drive specified. */ +#define ERRremcd 16 /* + * A Delete Directory request attempted + * to remove the server's current + * directory. + */ +#define ERRdiffdevice 17 /* + * Not same device (e.g., a cross + * volume rename was attempted + */ +#define ERRnofiles 18 /* + * A File Search command can find no + * more files matching the specified + * criteria. + */ +#define ERRwriteprot 19 /* media is write protected */ +#define ERRgeneral 31 +#define ERRbadshare 32 /* + * The sharing mode specified for an + * Open conflicts with existing FIDs on + * the file. + */ +#define ERRlock 33 /* + * A Lock request conflicted with an + * existing lock or specified an + * invalid mode, or an Unlock requested + * attempted to remove a lock held by + * another process. + */ +#define ERRunsup 50 +#define ERRnosuchshare 67 +#define ERRfilexists 80 /* + * The file named in the request + * already exists. + */ +#define ERRinvparm 87 +#define ERRdiskfull 112 +#define ERRinvname 123 +#define ERRinvlevel 124 +#define ERRdirnotempty 145 +#define ERRnotlocked 158 +#define ERRcancelviolation 173 +#define ERRnoatomiclocks 174 +#define ERRalreadyexists 183 +#define ERRbadpipe 230 +#define ERRpipebusy 231 +#define ERRpipeclosing 232 +#define ERRnotconnected 233 +#define ERRmoredata 234 +#define ERReasnotsupported 282 +#define ErrQuota 0x200 /* + * The operation would cause a quota + * limit to be exceeded. + */ +#define ErrNotALink 0x201 /* + * A link operation was performed on a + * pathname that was not a link. + */ + +/* + * Below errors are used internally (do not come over the wire) for passthrough + * from STATUS codes to POSIX only + */ +#define ERRsymlink 0xFFFD +#define ErrTooManyLinks 0xFFFE + +/* Following error codes may be generated with the ERRSRV error class.*/ + +#define ERRerror 1 /* + * Non-specific error code. It is + * returned under the following + * conditions: resource other than disk + * space exhausted (e.g. TIDs), first + * SMB command was not negotiate, + * multiple negotiates attempted, and + * internal server error. + */ +#define ERRbadpw 2 /* + * Bad password - name/password pair in + * a TreeConnect or Session Setup are + * invalid. + */ +#define ERRbadtype 3 /* + * used for indicating DFS referral + * needed + */ +#define ERRaccess 4 /* + * The client does not have the + * necessary access rights within the + * specified context for requested + * function. + */ +#define ERRinvtid 5 /* + * The Tid specified in a command was + * invalid. + */ +#define ERRinvnetname 6 /* + * Invalid network name in tree + * connect. + */ +#define ERRinvdevice 7 /* + * Invalid device - printer request + * made to non-printer connection or + * non-printer request made to printer + * connection. + */ +#define ERRqfull 49 /* + * Print queue full (files) -- returned + * by open print file. + */ +#define ERRqtoobig 50 /* Print queue full -- no space. */ +#define ERRqeof 51 /* EOF on print queue dump */ +#define ERRinvpfid 52 /* Invalid print file FID. */ +#define ERRsmbcmd 64 /* + * The server did not recognize the + * command received. + */ +#define ERRsrverror 65 /* + * The server encountered an internal + * error, e.g., system file + * unavailable. + */ +#define ERRbadBID 66 /* (obsolete) */ +#define ERRfilespecs 67 /* + * The Fid and pathname parameters + * contained an invalid combination of + * values. + */ +#define ERRbadLink 68 /* (obsolete) */ +#define ERRbadpermits 69 /* + * The access permissions specified for + * a file or directory are not a valid + * combination. + */ +#define ERRbadPID 70 +#define ERRsetattrmode 71 /* attribute (mode) is invalid */ +#define ERRpaused 81 /* Server is paused */ +#define ERRmsgoff 82 /* reserved - messaging off */ +#define ERRnoroom 83 /* reserved - no room for message */ +#define ERRrmuns 87 /* reserved - too many remote names */ +#define ERRtimeout 88 /* operation timed out */ +#define ERRnoresource 89 /* No resources available for request */ +#define ERRtoomanyuids 90 /* + * Too many UIDs active on this session + */ +#define ERRbaduid 91 /* + * The UID is not known as a valid user + */ +#define ERRusempx 250 /* temporarily unable to use raw */ +#define ERRusestd 251 /* + * temporarily unable to use either raw + * or mpx + */ +#define ERR_NOTIFY_ENUM_DIR 1024 +#define ERRnoSuchUser 2238 /* user account does not exist */ +#define ERRaccountexpired 2239 +#define ERRbadclient 2240 /* can not logon from this client */ +#define ERRbadLogonTime 2241 /* logon hours do not allow this */ +#define ERRpasswordExpired 2242 +#define ERRnetlogonNotStarted 2455 +#define ERRnosupport 0xFFFF + +#endif /* __KSMBD_SMBERR_H */ diff --git a/fs/cifsd/smbfsctl.h b/fs/cifsd/smbfsctl.h new file mode 100644 index 000000000000..908c4e68a479 --- /dev/null +++ b/fs/cifsd/smbfsctl.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions + * + * Copyright (c) International Business Machines Corp., 2002,2009 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +/* IOCTL information */ +/* + * List of ioctl/fsctl function codes that are or could be useful in the + * future to remote clients like cifs or SMB2 client. There is probably + * a slightly larger set of fsctls that NTFS local filesystem could handle, + * including the seven below that we do not have struct definitions for. + * Even with protocol definitions for most of these now available, we still + * need to do some experimentation to identify which are practical to do + * remotely. Some of the following, such as the encryption/compression ones + * could be invoked from tools via a specialized hook into the VFS rather + * than via the standard vfs entry points + */ + +#ifndef __KSMBD_SMBFSCTL_H +#define __KSMBD_SMBFSCTL_H + +#define FSCTL_DFS_GET_REFERRALS 0x00060194 +#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 +#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 +#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 +#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 +#define FSCTL_LOCK_VOLUME 0x00090018 +#define FSCTL_UNLOCK_VOLUME 0x0009001C +#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */ +#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */ +#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */ +#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */ +/* Verify the next FSCTL number, we had it as 0x00090090 before */ +#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */ +#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */ +#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */ +#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */ +#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */ +#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C +#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */ +#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */ +#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */ +#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */ +#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */ +#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */ +#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */ +#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ +#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ +#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ +#define FSCTL_SET_ZERO_DATA 0x000980C8 /* BB add struct */ +#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ +#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ +#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ +#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */ +#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */ +#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */ +#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */ +#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */ +#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ +#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ +#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ +#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ +#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ +#define FSCTL_SIS_LINK_FILES 0x0009C104 +#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ +#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ +/* strange that the number for this op is not sequential with previous op */ +#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ +#define FSCTL_REQUEST_RESUME_KEY 0x00140078 +#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ +#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ +#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 +#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC +#define FSCTL_COPYCHUNK 0x001440F2 +#define FSCTL_COPYCHUNK_WRITE 0x001480F2 + +#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 +#define IO_REPARSE_TAG_HSM 0xC0000004 +#define IO_REPARSE_TAG_SIS 0x80000007 + +/* WSL reparse tags */ +#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) +#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) +#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) +#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) +#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) +#endif /* __KSMBD_SMBFSCTL_H */ diff --git a/fs/cifsd/smbstatus.h b/fs/cifsd/smbstatus.h new file mode 100644 index 000000000000..108a8b6ed24a --- /dev/null +++ b/fs/cifsd/smbstatus.h @@ -0,0 +1,1822 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * fs/cifs/smb2status.h + * + * SMB2 Status code (network error) definitions + * Definitions are from MS-ERREF + * + * Copyright (c) International Business Machines Corp., 2009,2011 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +/* + * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F + * SEV C N <-------Facility--------> <------Error Status Code------> + * + * C is set if "customer defined" error, N bit is reserved and MBZ + */ + +#define STATUS_SEVERITY_SUCCESS cpu_to_le32(0x0000) +#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) +#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) +#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) + +struct ntstatus { + /* Facility is the high 12 bits of the following field */ + __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ + __le32 Code; +}; + +#define STATUS_SUCCESS 0x00000000 +#define STATUS_WAIT_0 cpu_to_le32(0x00000000) +#define STATUS_WAIT_1 cpu_to_le32(0x00000001) +#define STATUS_WAIT_2 cpu_to_le32(0x00000002) +#define STATUS_WAIT_3 cpu_to_le32(0x00000003) +#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) +#define STATUS_ABANDONED cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) +#define STATUS_USER_APC cpu_to_le32(0x000000C0) +#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) +#define STATUS_ALERTED cpu_to_le32(0x00000101) +#define STATUS_TIMEOUT cpu_to_le32(0x00000102) +#define STATUS_PENDING cpu_to_le32(0x00000103) +#define STATUS_REPARSE cpu_to_le32(0x00000104) +#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) +#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) +#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) +#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) +#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) +#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) +#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) +#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) +#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) +#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) +#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) +#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) +#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) +#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) +#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) +#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) +#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) +#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) +#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) +#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) +#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) +#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) +#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) +#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) +#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) +#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) +#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) +#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) +#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) +#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) +#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) +#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) +#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) +#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) +#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) +#define DBG_CONTINUE cpu_to_le32(0x00010002) +#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) +#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) +#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) +#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) +#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) +#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) +#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) +#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) +#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) +#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) +#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) +#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) +#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) +#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) +#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) +#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) +#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) +#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) +#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) +#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) +#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) +#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) +#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) +#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) +#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) +#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) +#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) +#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) +#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) +#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) +#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) +#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) +#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) +#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) +#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) +#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) +#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) +#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) +#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) +#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) +#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) +#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) +#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) +#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) +#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) +#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) +#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) +#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) +#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) +#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) +#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) +#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) +#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) +#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) +#define DBG_REPLY_LATER cpu_to_le32(0x40010001) +#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) +#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) +#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) +#define DBG_CONTROL_C cpu_to_le32(0x40010005) +#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) +#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) +#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) +#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) +#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) +#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) +#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) +#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) +#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) +#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) +#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) +#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) +#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) +#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) +#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) +#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) +#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) +#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) +#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED \ + cpu_to_le32(0x401E0351) +#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) +#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) +#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) +#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) +#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) +#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) +#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) +#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) +#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) +#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) +#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) +#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) +#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) +#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) +#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) +#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) +#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) +#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) +#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) +#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) +#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) +#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) +#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) +#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) +#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) +#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) +#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) +#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) +#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) +#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) +#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) +#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) +#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) +#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) +#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) +#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) +#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) +#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) +#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) +#define STATUS_LONGJUMP cpu_to_le32(0x80000026) +#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) +#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) +#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) +#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) +#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) +#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) +#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) +#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) +#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) +#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) +#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) +#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) +#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) +#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) +#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) +#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) +#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) +#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) +#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) +#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) +#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED \ + cpu_to_le32(0x801B00EB) +#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) +#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) +#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) +#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) +#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) +#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) +#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) +#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) +#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) +#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) +#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) +#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) +#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) +#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) +#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) +#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) +#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) +#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) +#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) +#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) +#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) +#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) +#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) +#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) +#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) +#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) +#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) +#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) +#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) +#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) +#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) +#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) +#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) +#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) +#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) +#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) +#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) +#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) +#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) +#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) +#define STATUS_UNWIND cpu_to_le32(0xC0000027) +#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) +#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) +#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) +#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) +#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) +#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) +#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) +#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) +#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) +#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) +#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) +#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) +#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) +#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) +#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) +#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) +#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) +#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) +#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) +#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) +#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) +#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) +#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) +#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) +#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) +#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) +#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) +#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) +#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) +#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) +#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) +#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) +#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) +#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) +#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) +#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) +#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) +#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) +#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) +#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) +#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) +#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) +#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) +#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) +#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) +#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) +#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) +#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) +#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) +#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) +#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) +#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) +#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) +#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) +#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) +#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) +#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) +#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) +#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) +#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) +#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) +#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) +#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) +#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) +#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) +#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) +#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) +#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) +#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) +#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) +#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) +#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) +#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) +#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) +#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) +#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) +#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) +#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) +#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) +#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) +#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) +#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) +#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) +#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) +#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) +#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) +#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) +#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) +#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) +#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) +#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) +#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) +#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) +#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) +#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) +#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) +#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) +#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) +#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) +#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) +#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) +#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) +#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) +#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) +#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) +#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) +#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) +#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) +#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) +#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) +#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) +#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) +#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) +#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) +#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) +#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) +#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) +#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) +#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) +#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) +#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) +#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) +#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) +#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) +#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) +#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) +#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) +#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) +#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) +#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) +#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) +#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) +#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) +#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) +#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) +#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) +#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) +#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) +#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) +#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) +#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) +#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) +#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) +#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) +#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) +#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) +#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) +#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) +#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) +#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) +#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) +#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) +#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) +#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) +#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) +#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) +#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) +#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) +#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) +#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) +#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) +#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) +#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) +#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) +#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) +#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) +#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) +#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) +#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) +#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) +#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) +#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) +#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) +#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) +#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) +#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) +#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) +#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) +#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) +#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) +#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) +#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) +#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) +#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) +#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) +#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) +#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) +#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) +#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) +#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) +#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) +#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) +#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) +#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) +#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) +#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) +#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) +#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) +#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) +#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) +#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) +#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) +#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) +#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) +#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) +#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) +#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) +#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) +#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) +#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) +#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) +#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) +#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) +#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) +#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) +#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) +#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) +#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) +#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) +#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) +#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) +#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) +#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) +#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) +#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) +#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) +#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) +#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) +#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) +#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) +#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) +#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) +#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) +#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) +#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) +#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) +#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) +#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) +#define STATUS_NO_LDT cpu_to_le32(0xC0000117) +#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) +#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) +#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) +#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) +#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) +#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) +#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) +#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) +#define STATUS_CANCELLED cpu_to_le32(0xC0000120) +#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) +#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) +#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) +#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) +#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) +#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) +#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) +#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) +#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) +#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) +#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) +#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) +#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) +#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) +#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) +#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) +#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) +#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) +#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) +#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) +#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) +#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) +#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) +#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) +#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) +#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) +#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) +#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) +#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) +#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) +#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) +#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) +#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) +#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) +#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) +#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) +#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) +#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) +#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) +#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) +#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) +#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) +#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) +#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) +#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) +#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) +#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) +#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) +#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) +#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) +#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) +#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) +#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) +#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) +#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) +#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) +#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) +#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) +#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) +#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) +#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) +#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) +#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) +#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) +#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) +#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) +#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) +#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) +#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) +#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) +#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) +#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) +#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) +#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) +#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) +#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) +#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) +#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) +#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) +#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) +#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) +#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) +#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) +#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) +#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) +#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) +#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) +#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) +#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) +#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) +#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) +#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) +#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) +#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) +#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) +#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) +#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) +#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) +#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) +#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) +#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) +#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) +#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) +#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) +#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) +#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) +#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) +#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) +#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) +#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) +#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) +#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) +#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) +#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) +#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) +#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) +#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) +#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) +#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) +#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) +#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) +#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) +#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) +#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) +#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) +#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) +#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) +#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) +#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) +#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) +#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) +#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) +#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) +#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) +#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) +#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) +#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) +#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) +#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) +#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) +#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) +#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) +#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) +#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) +#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) +#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) +#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) +#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) +#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) +#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) +#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) +#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) +#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) +#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) +#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) +#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) +#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) +#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) +#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) +#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) +#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) +#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) +#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) +#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) +#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) +#define STATUS_RETRY cpu_to_le32(0xC000022D) +#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) +#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) +#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) +#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) +#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) +#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) +#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) +#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) +#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) +#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) +#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) +#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) +#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) +#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) +#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) +#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) +#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) +#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) +#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) +#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) +#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) +#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) +#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) +#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) +#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) +#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) +#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) +#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) +#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) +#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) +#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) +#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) +#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) +#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) +#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) +#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) +#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) +#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) +#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) +#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) +#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) +#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) +#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) +#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) +#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) +#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) +#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) +#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) +#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) +#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) +#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) +#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) +#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) +#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) +#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) +#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) +#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) +#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) +#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) +#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) +#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) +#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) +#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) +#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) +#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) +#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) +#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) +#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) +#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) +#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) +#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) +#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) +#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) +#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) +#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) +#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) +#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) +#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) +#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) +#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) +#define STATUS_NO_EFS cpu_to_le32(0xC000028E) +#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) +#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) +#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) +#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) +#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) +#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) +#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) +#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) +#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) +#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) +#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) +#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) +#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) +#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) +#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) +#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) +#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) +#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) +#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) +#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) +#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) +#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) +#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) +#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) +#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) +#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) +#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) +#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) +#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) +#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) +#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) +#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) +#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) +#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) +#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) +#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) +#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) +#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) +#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) +#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) +#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) +#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) +#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) +#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) +#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) +#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) +#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) +#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) +#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) +#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) +#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) +#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) +#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) +#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) +#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) +#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) +#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) +#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) +#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) +#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) +#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) +#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) +#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) +#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) +#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) +#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) +#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) +#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) +#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER \ + cpu_to_le32(0xC00002DB) +#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) +#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) +#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) +#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) +#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) +#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) +#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) +#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) +#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) +#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) +#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) +#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) +#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) +#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) +#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) +#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) +#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) +#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) +#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) +#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) +#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) +#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) +#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) +#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) +#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) +#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) +#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) +#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) +#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) +#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) +#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) +#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) +#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) +#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) +#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) +#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) +#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) +#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) +#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) +#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) +#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) +#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) +#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) +#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) +#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) +#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) +#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) +#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) +#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) +#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) +#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) +#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) +#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) +#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) +#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) +#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) +#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) +#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) +#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) +#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) +#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) +#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) +#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) +#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) +#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) +#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) +#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) +#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) +#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) +#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) +#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) +#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) +#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) +#define STATUS_MCA_OCCURRED cpu_to_le32(0xC000036A) +#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) +#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) +#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) +#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) +#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) +#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) +#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) +#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) +#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) +#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) +#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) +#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) +#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) +#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) +#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) +#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) +#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) +#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) +#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) +#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) +#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) +#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) +#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) +#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) +#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) +#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) +#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) +#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) +#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) +#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) +#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) +#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) +#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) +#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) +#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) +#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) +#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) +#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) +#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) +#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) +#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) +#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) +#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) +#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) +#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) +#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) +#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE \ + cpu_to_le32(0xC0000416) +#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) +#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) +#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) +#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) +#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) +#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) +#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) +#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) +#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) +#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) +#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) +#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) +#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) +#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) +#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) +#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) +#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) +#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) +#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) +#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) +#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) +#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) +#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) +#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) +#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) +#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) +#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) +#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) +#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) +#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) +#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) +#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) +#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) +#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) +#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) +#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) +#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) +#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) +#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) +#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) +#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) +#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) +#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) +#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) +#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) +#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070C) +#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070D) +#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070E) +#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) +#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) +#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) +#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) +#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) +#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) +#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) +#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) +#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) +#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) +#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) +#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) +#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) +#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) +#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) +#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) +#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) +#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) +#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) +#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) +#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) +#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) +#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) +#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) +#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) +#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) +#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) +#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) +#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) +#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) +#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) +#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) +#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) +#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) +#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) +#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) +#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) +#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) +#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) +#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED \ + cpu_to_le32(0xC000A080) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR \ + cpu_to_le32(0xC000A081) +#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) +#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) +#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) +#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) +#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) +#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) +#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) +#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) +#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) +#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) +#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) +#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) +#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) +#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) +#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) +#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) +#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) +#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) +#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) +#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) +#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) +#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) +#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) +#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) +#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) +#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) +#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) +#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) +#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) +#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) +#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) +#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) +#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) +#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) +#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) +#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) +#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) +#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) +#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) +#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) +#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) +#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) +#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) +#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) +#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) +#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) +#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) +#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) +#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) +#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) +#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) +#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) +#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) +#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) +#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) +#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) +#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) +#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) +#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) +#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) +#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) +#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) +#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) +#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) +#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) +#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) +#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) +#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) +#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) +#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) +#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) +#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) +#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) +#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) +#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) +#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) +#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) +#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) +#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) +#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) +#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) +#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) +#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) +#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) +#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) +#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) +#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) +#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) +#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) +#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) +#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) +#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) +#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) +#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) +#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) +#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) +#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) +#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) +#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) +#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) +#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) +#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) +#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) +#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) +#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) +#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) +#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) +#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) +#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) +#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) +#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) +#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) +#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) +#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) +#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) +#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) +#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) +#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) +#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) +#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) +#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) +#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) +#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) +#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) +#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) +#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) +#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) +#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) +#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) +#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) +#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) +#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) +#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) +#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) +#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) +#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) +#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) +#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) +#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) +#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) +#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) +#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) +#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) +#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) +#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) +#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) +#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) +#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) +#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) +#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) +#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) +#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) +#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) +#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) +#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) +#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) +#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) +#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) +#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) +#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) +#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) +#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) +#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) +#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) +#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) +#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) +#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) +#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) +#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) +#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) +#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) +#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) +#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) +#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) +#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) +#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) +#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) +#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) +#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) +#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) +#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) +#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) +#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) +#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) +#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) +#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) +#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) +#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) +#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) +#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) +#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) +#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) +#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) +#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) +#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) +#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) +#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) +#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) +#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) +#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) +#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) +#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) +#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) +#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) +#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) +#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) +#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) +#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) +#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) +#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) +#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) +#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) +#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) +#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) +#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) +#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) +#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) +#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) +#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) +#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) +#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) +#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) +#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) +#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) +#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) +#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) +#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) +#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) +#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) +#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) +#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) +#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) +#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY \ + cpu_to_le32(0xC0150012) +#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) +#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) +#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) +#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) +#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) +#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) +#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) +#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT \ + cpu_to_le32(0xC015001C) +#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) +#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) +#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) +#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) +#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) +#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) +#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) +#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) +#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) +#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) +#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) +#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) +#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) +#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) +#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) +#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) +#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) +#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) +#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) +#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) +#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) +#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) +#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) +#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) +#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) +#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) +#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) +#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) +#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) +#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) +#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) +#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) +#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) +#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) +#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) +#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) +#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION \ + cpu_to_le32(0xC0190024) +#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) +#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) +#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) +#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) +#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) +#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) +#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) +#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) +#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) +#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) +#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) +#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) +#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) +#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) +#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) +#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) +#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) +#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) +#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) +#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) +#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) +#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) +#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) +#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) +#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) +#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) +#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) +#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) +#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) +#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) +#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) +#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) +#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) +#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION \ + cpu_to_le32(0xC0190053) +#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) +#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) +#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) +#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) +#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) +#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) +#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) +#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) +#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) +#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) +#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) +#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) +#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) +#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) +#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) +#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) +#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) +#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) +#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) +#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) +#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) +#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) +#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) +#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) +#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) +#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) +#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) +#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) +#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) +#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) +#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) +#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) +#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) +#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) +#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) +#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) +#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) +#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) +#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) +#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) +#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) +#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) +#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) +#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) +#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) +#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) +#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) +#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) +#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) +#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) +#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) +#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) +#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) +#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) +#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) +#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) +#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) +#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) +#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) +#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) +#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) +#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) +#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) +#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) +#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) +#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) +#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) +#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) +#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) +#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) +#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) +#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) +#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) +#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) +#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) +#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) +#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) +#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) +#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) +#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) +#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) +#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) +#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) +#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) +#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) +#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) +#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) +#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) +#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) +#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK \ + cpu_to_le32(0xC01D0006) +#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK \ + cpu_to_le32(0xC01D0007) +#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) +#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) +#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) +#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) +#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) +#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) +#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) +#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) +#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) +#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) +#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) +#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) +#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) +#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) +#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) +#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) +#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) +#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) +#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) +#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) +#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) +#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) +#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) +#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0302) +#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) +#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) +#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) +#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) +#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) +#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE \ + cpu_to_le32(0xC01E0310) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE \ + cpu_to_le32(0xC01E0311) +#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) +#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) +#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) +#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) +#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) +#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) +#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET \ + cpu_to_le32(0xC01E031B) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) +#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) +#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) +#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) +#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) +#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) +#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION \ + cpu_to_le32(0xC01E0325) +#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES \ + cpu_to_le32(0xC01E0326) +#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE \ + cpu_to_le32(0xC01E0328) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET \ + cpu_to_le32(0xC01E0329) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE \ + cpu_to_le32(0xC01E032E) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) +#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) +#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) +#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) +#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) +#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER \ + cpu_to_le32(0xC01E0334) +#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) +#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) +#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) +#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) +#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) +#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) +#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) +#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) +#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) +#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) +#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) +#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) +#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT \ + cpu_to_le32(0xC01E0341) +#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) +#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) +#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION \ + cpu_to_le32(0xC01E0345) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0346) +#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) +#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) +#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) +#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON \ + cpu_to_le32(0xC01E034D) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) +#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) +#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS \ + cpu_to_le32(0xC01E0350) +#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) +#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) +#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) +#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) +#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) +#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN \ + cpu_to_le32(0xC01E0357) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT \ + cpu_to_le32(0xC01E0358) +#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) +#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION \ + cpu_to_le32(0xC01E035A) +#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) +#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) +#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED \ + cpu_to_le32(0xC01E0400) +#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) +#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) +#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) +#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) +#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) +#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) +#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS \ + cpu_to_le32(0xC01E051C) +#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) +#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS \ + cpu_to_le32(0xC01E051F) +#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) +#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST \ + cpu_to_le32(0xC01E0521) +#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) +#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) +#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) +#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) +#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) +#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) +#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ + cpu_to_le32(0xC01E0506) +#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ + cpu_to_le32(0xC01E0507) +#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0508) +#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) +#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) +#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) +#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ + cpu_to_le32(0xC01E050D) +#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) +#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) +#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) +#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) +#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA \ + cpu_to_le32(0xC01E0515) +#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) +#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) +#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE \ + cpu_to_le32(0xC01E0518) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS \ + cpu_to_le32(0xC01E051A) +#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS \ + cpu_to_le32(0xC01E051B) +#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) +#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) +#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) +#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) +#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) +#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) +#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE \ + cpu_to_le32(0xC01E0586) +#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING \ + cpu_to_le32(0xC01E0587) +#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) +#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) +#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) +#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) +#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ + cpu_to_le32(0xC01E05E1) +#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ + cpu_to_le32(0xC01E05E2) +#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) +#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) +#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ + cpu_to_le32(0xC01E05E5) +#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) +#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) +#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) +#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) +#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) +#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) +#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) +#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) +#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) +#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) +#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) +#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) +#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) +#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) +#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) +#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) +#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) +#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) +#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) +#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) +#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) +#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) +#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) +#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) +#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) +#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) +#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) +#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) +#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) +#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) +#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) +#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) +#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) +#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) +#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) +#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) +#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) +#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) +#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) +#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) +#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) +#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) +#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) +#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) +#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) +#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) +#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) +#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) +#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) +#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) +#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) +#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) +#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) +#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) +#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) +#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) +#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) +#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) +#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) +#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) +#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) +#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) +#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) +#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) +#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) +#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) +#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) +#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) +#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) +#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) +#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) +#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) +#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) +#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) +#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) +#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) +#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) +#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) +#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) +#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) +#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) +#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) +#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) +#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) +#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) +#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) +#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) +#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) +#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) +#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) +#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) +#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) +#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) +#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) +#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) +#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) +#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) +#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) +#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) +#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) +#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) +#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) +#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) +#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) +#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) +#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) +#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) +#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) +#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) +#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) +#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) +#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) +#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) +#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) +#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) +#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) +#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) +#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) +#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) +#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) +#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) +#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) +#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) +#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) +#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) +#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) +#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) +#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) +#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) +#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) +#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) +#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) +#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) +#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) +#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) +#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) +#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) +#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) +#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) +#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) +#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) + +#define STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000) +#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001a1) diff --git a/fs/cifsd/time_wrappers.h b/fs/cifsd/time_wrappers.h new file mode 100644 index 000000000000..a702ca96947e --- /dev/null +++ b/fs/cifsd/time_wrappers.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_TIME_WRAPPERS_H +#define __KSMBD_TIME_WRAPPERS_H + +/* + * A bunch of ugly hacks to workaoround all the API differences + * between different kernel versions. + */ + +#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) + +/* Convert the Unix UTC into NT UTC. */ +static inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) +{ + /* Convert to 100ns intervals and then add the NTFS time offset. */ + return (u64) t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; +} + +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); + +#define KSMBD_TIME_TO_TM time64_to_tm + +static inline long long ksmbd_systime(void) +{ + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + return ksmbd_UnixTimeToNT(ts); +} +#endif /* __KSMBD_TIME_WRAPPERS_H */ diff --git a/fs/cifsd/unicode.c b/fs/cifsd/unicode.c new file mode 100644 index 000000000000..22a4d10a2000 --- /dev/null +++ b/fs/cifsd/unicode.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Some of the source code in this file came from fs/cifs/cifs_unicode.c + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * Modified by Steve French (sfrench@us.ibm.com) + * Modified by Namjae Jeon (linkinjeon@kernel.org) + */ +#include +#include +#include +#include "glob.h" +#include "unicode.h" +#include "uniupr.h" +#include "smb_common.h" + +/* + * smb_utf16_bytes() - how long will a string be after conversion? + * @from: pointer to input string + * @maxbytes: don't go past this many bytes of input string + * @codepage: destination codepage + * + * Walk a utf16le string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + * + * Return: string length after conversion + */ +static int smb_utf16_bytes(const __le16 *from, + int maxbytes, + const struct nls_table *codepage) +{ + int i; + int charlen, outlen = 0; + int maxwords = maxbytes / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; + + for (i = 0; i < maxwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + + charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE); + if (charlen > 0) + outlen += charlen; + else + outlen++; + } + + return outlen; +} + +/* + * cifs_mapchar() - convert a host-endian char to proper char in codepage + * @target: where converted character should be copied + * @src_char: 2 byte host-endian source character + * @cp: codepage to which character should be converted + * @mapchar: should character be mapped according to mapchars mount option? + * + * This function handles the conversion of a single character. It is the + * responsibility of the caller to ensure that the target buffer is large + * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). + * + * Return: string length after conversion + */ +static int +cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, + bool mapchar) +{ + int len = 1; + + if (!mapchar) + goto cp_convert; + + /* + * BB: Cannot handle remapping UNI_SLASH until all the calls to + * build_path_from_dentry are modified, as they use slash as + * separator. + */ + switch (src_char) { + case UNI_COLON: + *target = ':'; + break; + case UNI_ASTERISK: + *target = '*'; + break; + case UNI_QUESTION: + *target = '?'; + break; + case UNI_PIPE: + *target = '|'; + break; + case UNI_GRTRTHAN: + *target = '>'; + break; + case UNI_LESSTHAN: + *target = '<'; + break; + default: + goto cp_convert; + } + +out: + return len; + +cp_convert: + len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); + if (len <= 0) { + *target = '?'; + len = 1; + } + + goto out; +} + +/* + * is_char_allowed() - check for valid character + * @ch: input character to be checked + * + * Return: 1 if char is allowed, otherwise 0 + */ +static inline int is_char_allowed(char *ch) +{ + /* check for control chars, wildcards etc. */ + if (!(*ch & 0x80) && + (*ch <= 0x1f || + *ch == '?' || *ch == '"' || *ch == '<' || + *ch == '>' || *ch == '|')) + return 0; + + return 1; +} + +/* + * smb_from_utf16() - convert utf16le string to local charset + * @to: destination buffer + * @from: source buffer + * @tolen: destination buffer size (in bytes) + * @fromlen: source buffer size (in bytes) + * @codepage: codepage to which characters should be converted + * @mapchar: should characters be remapped according to the mapchars option? + * + * Convert a little-endian utf16le string (as sent by the server) to a string + * in the provided codepage. The tolen and fromlen parameters are to ensure + * that the code doesn't walk off of the end of the buffer (which is always + * a danger if the alignment of the source buffer is off). The destination + * string is always properly null terminated and fits in the destination + * buffer. Returns the length of the destination string in bytes (including + * null terminator). + * + * Note that some windows versions actually send multiword UTF-16 characters + * instead of straight UTF16-2. The linux nls routines however aren't able to + * deal with those characters properly. In the event that we get some of + * those characters, they won't be translated properly. + * + * Return: string length after conversion + */ +static int smb_from_utf16(char *to, + const __le16 *from, + int tolen, + int fromlen, + const struct nls_table *codepage, + bool mapchar) +{ + int i, charlen, safelen; + int outlen = 0; + int nullsize = nls_nullsize(codepage); + int fromwords = fromlen / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; + + /* + * because the chars can be of varying widths, we need to take care + * not to overflow the destination buffer when we get close to the + * end of it. Until we get to this offset, we don't need to check + * for overflow however. + */ + safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); + + for (i = 0; i < fromwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + + /* + * check to see if converting this character might make the + * conversion bleed into the null terminator + */ + if (outlen >= safelen) { + charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar); + if ((outlen + charlen) > (tolen - nullsize)) + break; + } + + /* put converted char into 'to' buffer */ + charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); + outlen += charlen; + } + + /* properly null-terminate string */ + for (i = 0; i < nullsize; i++) + to[outlen++] = 0; + + return outlen; +} + +/* + * smb_strtoUTF16() - Convert character string to unicode string + * @to: destination buffer + * @from: source buffer + * @len: destination buffer size (in bytes) + * @codepage: codepage to which characters should be converted + * + * Return: string length after conversion + */ +int +smb_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage) +{ + int charlen; + int i; + wchar_t wchar_to; /* needed to quiet sparse */ + + /* special case for utf8 to handle no plane0 chars */ + if (!strcmp(codepage->charset, "utf8")) { + /* + * convert utf8 -> utf16, we assume we have enough space + * as caller should have assumed conversion does not overflow + * in destination len is length in wchar_t units (16bits) + */ + i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, + (wchar_t *) to, len); + + /* if success terminate and exit */ + if (i >= 0) + goto success; + /* + * if fails fall back to UCS encoding as this + * function should not return negative values + * currently can fail only if source contains + * invalid encoded characters + */ + } + + for (i = 0; len > 0 && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &wchar_to); + if (charlen < 1) { + /* A question mark */ + wchar_to = 0x003f; + charlen = 1; + } + put_unaligned_le16(wchar_to, &to[i]); + } + +success: + put_unaligned_le16(0, &to[i]); + return i; +} + +/* + * smb_strndup_from_utf16() - copy a string from wire format to the local + * codepage + * @src: source string + * @maxlen: don't walk past this many bytes in the source string + * @is_unicode: is this a unicode string? + * @codepage: destination codepage + * + * Take a string given by the server, convert it to the local codepage and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + * + * Return: destination string buffer or error ptr + */ +char * +smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, const struct nls_table *codepage) +{ + int len, ret; + char *dst; + + if (is_unicode) { + len = smb_utf16_bytes((__le16 *) src, maxlen, codepage); + len += nls_nullsize(codepage); + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + ret = smb_from_utf16(dst, (__le16 *) src, len, maxlen, codepage, + false); + if (ret < 0) { + kfree(dst); + return ERR_PTR(-EINVAL); + } + } else { + len = strnlen(src, maxlen); + len++; + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + strscpy(dst, src, len); + } + + return dst; +} + +/* + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + */ +/* + * smbConvertToUTF16() - convert string from local charset to utf16 + * @target: destination buffer + * @source: source buffer + * @srclen: source buffer size (in bytes) + * @cp: codepage to which characters should be converted + * @mapchar: should characters be remapped according to the mapchars option? + * + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + * + * Return: char length after conversion + */ +int +smbConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int mapchars) +{ + int i, j, charlen; + char src_char; + __le16 dst_char; + wchar_t tmp; + + if (!mapchars) + return smb_strtoUTF16(target, source, srclen, cp); + + for (i = 0, j = 0; i < srclen; j++) { + src_char = source[i]; + charlen = 1; + switch (src_char) { + case 0: + put_unaligned(0, &target[j]); + return j; + case ':': + dst_char = cpu_to_le16(UNI_COLON); + break; + case '*': + dst_char = cpu_to_le16(UNI_ASTERISK); + break; + case '?': + dst_char = cpu_to_le16(UNI_QUESTION); + break; + case '<': + dst_char = cpu_to_le16(UNI_LESSTHAN); + break; + case '>': + dst_char = cpu_to_le16(UNI_GRTRTHAN); + break; + case '|': + dst_char = cpu_to_le16(UNI_PIPE); + break; + /* + * FIXME: We can not handle remapping backslash (UNI_SLASH) + * until all the calls to build_path_from_dentry are modified, + * as they use backslash as separator. + */ + default: + charlen = cp->char2uni(source + i, srclen - i, &tmp); + dst_char = cpu_to_le16(tmp); + + /* + * if no match, use question mark, which at least in + * some cases serves as wild card + */ + if (charlen < 1) { + dst_char = cpu_to_le16(0x003f); + charlen = 1; + } + } + /* + * character may take more than one byte in the source string, + * but will take exactly two bytes in the target string + */ + i += charlen; + put_unaligned(dst_char, &target[j]); + } + + return j; +} diff --git a/fs/cifsd/unicode.h b/fs/cifsd/unicode.h new file mode 100644 index 000000000000..228a02c9b95d --- /dev/null +++ b/fs/cifsd/unicode.h @@ -0,0 +1,374 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Some of the source code in this file came from fs/cifs/cifs_unicode.c + * cifs_unicode: Unicode kernel case support + * + * Function: + * Convert a unicode character to upper or lower case using + * compressed tables. + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * + * + * Notes: + * These APIs are based on the C library functions. The semantics + * should match the C functions but with expanded size operands. + * + * The upper/lower functions are based on a table created by mkupr. + * This is a compressed table of upper and lower case conversion. + * + */ +#ifndef _CIFS_UNICODE_H +#define _CIFS_UNICODE_H + +#include +#include +#include + +#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ + +/* + * Windows maps these to the user defined 16 bit Unicode range since they are + * reserved symbols (along with \ and /), otherwise illegal to store + * in filenames in NTFS + */ +#define UNI_ASTERISK ((__u16) ('*' + 0xF000)) +#define UNI_QUESTION ((__u16) ('?' + 0xF000)) +#define UNI_COLON ((__u16) (':' + 0xF000)) +#define UNI_GRTRTHAN ((__u16) ('>' + 0xF000)) +#define UNI_LESSTHAN ((__u16) ('<' + 0xF000)) +#define UNI_PIPE ((__u16) ('|' + 0xF000)) +#define UNI_SLASH ((__u16) ('\\' + 0xF000)) + +/* Just define what we want from uniupr.h. We don't want to define the tables + * in each source file. + */ +#ifndef UNICASERANGE_DEFINED +struct UniCaseRange { + wchar_t start; + wchar_t end; + signed char *table; +}; +#endif /* UNICASERANGE_DEFINED */ + +#ifndef UNIUPR_NOUPPER +extern signed char SmbUniUpperTable[512]; +extern const struct UniCaseRange SmbUniUpperRange[]; +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +extern signed char CifsUniLowerTable[512]; +extern const struct UniCaseRange CifsUniLowerRange[]; +#endif /* UNIUPR_NOLOWER */ + +#ifdef __KERNEL__ +int smb_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage); +char *smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage); +extern int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int mapchars); +extern char *extract_sharename(char *treename); +#endif + +wchar_t cifs_toupper(wchar_t in); + +/* + * UniStrcat: Concatenate the second string to the first + * + * Returns: + * Address of the first string + */ + static inline wchar_t * +UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */ + + while (*ucs1++) + /*NULL*/; /* To end of first string */ + ucs1--; /* Return to the null */ + while ((*ucs1++ = *ucs2++)) + /*NULL*/; /* copy string 2 over */ + return anchor; +} + +/* + * UniStrchr: Find a character in a string + * + * Returns: + * Address of first occurrence of character in string + * or NULL if the character is not in the string + */ + static inline wchar_t * +UniStrchr(const wchar_t *ucs, wchar_t uc) +{ + while ((*ucs != uc) && *ucs) + ucs++; + + if (*ucs == uc) + return (wchar_t *) ucs; + return NULL; +} + +/* + * UniStrcmp: Compare two strings + * + * Returns: + * < 0: First string is less than second + * = 0: Strings are equal + * > 0: First string is greater than second + */ + static inline int +UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) +{ + while ((*ucs1 == *ucs2) && *ucs1) { + ucs1++; + ucs2++; + } + return (int) *ucs1 - (int) *ucs2; +} + +/* + * UniStrcpy: Copy a string + */ + static inline wchar_t * +UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save the start of result string */ + + while ((*ucs1++ = *ucs2++)) + /*NULL*/; + return anchor; +} + +/* + * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) + */ + static inline size_t +UniStrlen(const wchar_t *ucs1) +{ + int i = 0; + + while (*ucs1++) + i++; + return i; +} + +/* + * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a + * string (length limited) + */ + static inline size_t +UniStrnlen(const wchar_t *ucs1, int maxlen) +{ + int i = 0; + + while (*ucs1++) { + i++; + if (i >= maxlen) + break; + } + return i; +} + +/* + * UniStrncat: Concatenate length limited string + */ + static inline wchar_t * +UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; /* save pointer to string 1 */ + + while (*ucs1++) + /*NULL*/; + ucs1--; /* point to null terminator of s1 */ + while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ + ucs1++; + ucs2++; + } + *ucs1 = 0; /* Null terminate the result */ + return anchor; +} + +/* + * UniStrncmp: Compare length limited string + */ + static inline int +UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == *ucs2) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int) *ucs1 - (int) *ucs2; +} + +/* + * UniStrncmp_le: Compare length limited string - native to little-endian + */ + static inline int +UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int) *ucs1 - (int) __le16_to_cpu(*ucs2); +} + +/* + * UniStrncpy: Copy length limited string with pad + */ + static inline wchar_t * +UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = *ucs2++; + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrncpy_le: Copy length limited string with pad to little-endian + */ + static inline wchar_t * +UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = __le16_to_cpu(*ucs2++); + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrstr: Find a string in a string + * + * Returns: + * Address of first match found + * NULL if no matching string is found + */ + static inline wchar_t * +UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) +{ + const wchar_t *anchor1 = ucs1; + const wchar_t *anchor2 = ucs2; + + while (*ucs1) { + if (*ucs1 == *ucs2) { + /* Partial match found */ + ucs1++; + ucs2++; + } else { + if (!*ucs2) /* Match found */ + return (wchar_t *) anchor1; + ucs1 = ++anchor1; /* No match */ + ucs2 = anchor2; + } + } + + if (!*ucs2) /* Both end together */ + return (wchar_t *) anchor1; /* Match found */ + return NULL; /* No match */ +} + +#ifndef UNIUPR_NOUPPER +/* + * UniToupper: Convert a unicode character to upper case + */ + static inline wchar_t +UniToupper(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(SmbUniUpperTable)) { + /* Latin characters */ + return uc + SmbUniUpperTable[uc]; /* Use base tables */ + } + + rp = SmbUniUpperRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + return uc; /* Past last range */ +} + +/* + * UniStrupr: Upper case a unicode string + */ + static inline __le16 * +UniStrupr(register __le16 *upin) +{ + register __le16 *up; + + up = upin; + while (*up) { /* For all characters */ + *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); + up++; + } + return upin; /* Return input pointer */ +} +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +/* + * UniTolower: Convert a unicode character to lower case + */ + static inline wchar_t +UniTolower(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(CifsUniLowerTable)) { + /* Latin characters */ + return uc + CifsUniLowerTable[uc]; /* Use base tables */ + } + + rp = CifsUniLowerRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + return uc; /* Past last range */ +} + +/* + * UniStrlwr: Lower case a unicode string + */ + static inline wchar_t * +UniStrlwr(register wchar_t *upin) +{ + register wchar_t *up; + + up = upin; + while (*up) { /* For all characters */ + *up = UniTolower(*up); + up++; + } + return upin; /* Return input pointer */ +} + +#endif + +#endif /* _CIFS_UNICODE_H */ diff --git a/fs/cifsd/uniupr.h b/fs/cifsd/uniupr.h new file mode 100644 index 000000000000..26583b776897 --- /dev/null +++ b/fs/cifsd/uniupr.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Some of the source code in this file came from fs/cifs/uniupr.h + * Copyright (c) International Business Machines Corp., 2000,2002 + * + * uniupr.h - Unicode compressed case ranges + * + */ +#ifndef __KSMBD_UNIUPR_H +#define __KSMBD_UNIUPR_H + +#ifndef UNIUPR_NOUPPER +/* + * Latin upper case + */ +signed char SmbUniUpperTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, /* 060-06f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ + -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, + -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ + 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ + 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ + 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ + -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ + 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ +}; + +/* Upper case range - Greek */ +static signed char UniCaseRangeU03a0[47] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 3b0-3bf */ + -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, + -63, -63, +}; + +/* Upper case range - Cyrillic */ +static signed char UniCaseRangeU0430[48] = { + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 430-43f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 440-44f */ + 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, 0, -80, -80, /* 450-45f */ +}; + +/* Upper case range - Extended cyrillic */ +static signed char UniCaseRangeU0490[61] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ + 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, +}; + +/* Upper case range - Extended latin and greek */ +static signed char UniCaseRangeU1e00[509] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ + 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ + 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ + 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, + 126, 126, 0, 0, /* 1f70-1f7f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ + 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ + 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Upper case range - Wide latin */ +static signed char UniCaseRangeUff40[27] = { + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, /* ff40-ff4f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, +}; + +/* + * Upper Case Range + */ +const struct UniCaseRange SmbUniUpperRange[] = { + {0x03a0, 0x03ce, UniCaseRangeU03a0}, + {0x0430, 0x045f, UniCaseRangeU0430}, + {0x0490, 0x04cc, UniCaseRangeU0490}, + {0x1e00, 0x1ffc, UniCaseRangeU1e00}, + {0xff40, 0xff5a, UniCaseRangeUff40}, + {0} +}; +#endif + +#ifndef UNIUPR_NOLOWER +/* + * Latin lower case + */ +signed char CifsUniLowerTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 040-04f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, + 0, 0, 0, /* 050-05f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, /* 0c0-0cf */ + 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 0, /* 0d0-0df */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ + 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ + 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, + 0, /* 170-17f */ + 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, + 0, /* 180-18f */ + 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ + 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ + 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ +}; + +/* Lower case range - Greek */ +static signed char UniCaseRangeL0380[44] = { + 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 390-39f */ + 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* Lower case range - Cyrillic */ +static signed char UniCaseRangeL0400[48] = { + 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 0, 80, 80, /* 400-40f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 410-41f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 420-42f */ +}; + +/* Lower case range - Extended cyrillic */ +static signed char UniCaseRangeL0490[60] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, +}; + +/* Lower case range - Extended latin and greek */ +static signed char UniCaseRangeL1e00[504] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ + 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, + 0, 0, /* 1fc0-1fcf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, + 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Lower case range - Wide latin */ +static signed char UniCaseRangeLff20[27] = { + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, /* ff20-ff2f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* + * Lower Case Range + */ +const struct UniCaseRange CifsUniLowerRange[] = { + {0x0380, 0x03ab, UniCaseRangeL0380}, + {0x0400, 0x042f, UniCaseRangeL0400}, + {0x0490, 0x04cb, UniCaseRangeL0490}, + {0x1e00, 0x1ff7, UniCaseRangeL1e00}, + {0xff20, 0xff3a, UniCaseRangeLff20}, + {0} +}; +#endif + +#endif /* __KSMBD_UNIUPR_H */ From f44158485826c076335d6860d35872271a83791d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 16 Mar 2021 10:50:04 +0900 Subject: [PATCH 003/417] cifsd: add file operations This adds file operations and buffer pool for cifsd. Signed-off-by: Namjae Jeon Signed-off-by: Sergey Senozhatsky Signed-off-by: Hyunchul Lee Acked-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifsd/buffer_pool.c | 292 ++++++ fs/cifsd/buffer_pool.h | 28 + fs/cifsd/vfs.c | 1989 ++++++++++++++++++++++++++++++++++++++++ fs/cifsd/vfs.h | 314 +++++++ fs/cifsd/vfs_cache.c | 855 +++++++++++++++++ fs/cifsd/vfs_cache.h | 213 +++++ 6 files changed, 3691 insertions(+) create mode 100644 fs/cifsd/buffer_pool.c create mode 100644 fs/cifsd/buffer_pool.h create mode 100644 fs/cifsd/vfs.c create mode 100644 fs/cifsd/vfs.h create mode 100644 fs/cifsd/vfs_cache.c create mode 100644 fs/cifsd/vfs_cache.h diff --git a/fs/cifsd/buffer_pool.c b/fs/cifsd/buffer_pool.c new file mode 100644 index 000000000000..864fea547c68 --- /dev/null +++ b/fs/cifsd/buffer_pool.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "buffer_pool.h" +#include "connection.h" +#include "mgmt/ksmbd_ida.h" + +static struct kmem_cache *filp_cache; + +struct wm { + struct list_head list; + unsigned int sz; + char buffer[0]; +}; + +struct wm_list { + struct list_head list; + unsigned int sz; + + spinlock_t wm_lock; + int avail_wm; + struct list_head idle_wm; + wait_queue_head_t wm_wait; +}; + +static LIST_HEAD(wm_lists); +static DEFINE_RWLOCK(wm_lists_lock); + +void *ksmbd_alloc(size_t size) +{ + return kvmalloc(size, GFP_KERNEL | __GFP_ZERO); +} + +void ksmbd_free(void *ptr) +{ + kvfree(ptr); +} + +static struct wm *wm_alloc(size_t sz, gfp_t flags) +{ + struct wm *wm; + size_t alloc_sz = sz + sizeof(struct wm); + + wm = kvmalloc(alloc_sz, flags); + if (!wm) + return NULL; + wm->sz = sz; + return wm; +} + +static int register_wm_size_class(size_t sz) +{ + struct wm_list *l, *nl; + + nl = kvmalloc(sizeof(struct wm_list), GFP_KERNEL); + if (!nl) + return -ENOMEM; + + nl->sz = sz; + spin_lock_init(&nl->wm_lock); + INIT_LIST_HEAD(&nl->idle_wm); + INIT_LIST_HEAD(&nl->list); + init_waitqueue_head(&nl->wm_wait); + nl->avail_wm = 0; + + write_lock(&wm_lists_lock); + list_for_each_entry(l, &wm_lists, list) { + if (l->sz == sz) { + write_unlock(&wm_lists_lock); + kvfree(nl); + return 0; + } + } + + list_add(&nl->list, &wm_lists); + write_unlock(&wm_lists_lock); + return 0; +} + +static struct wm_list *match_wm_list(size_t size) +{ + struct wm_list *l, *rl = NULL; + + read_lock(&wm_lists_lock); + list_for_each_entry(l, &wm_lists, list) { + if (l->sz == size) { + rl = l; + break; + } + } + read_unlock(&wm_lists_lock); + return rl; +} + +static struct wm *find_wm(size_t size) +{ + struct wm_list *wm_list; + struct wm *wm; + + wm_list = match_wm_list(size); + if (!wm_list) { + if (register_wm_size_class(size)) + return NULL; + wm_list = match_wm_list(size); + } + + if (!wm_list) + return NULL; + + while (1) { + spin_lock(&wm_list->wm_lock); + if (!list_empty(&wm_list->idle_wm)) { + wm = list_entry(wm_list->idle_wm.next, + struct wm, + list); + list_del(&wm->list); + spin_unlock(&wm_list->wm_lock); + return wm; + } + + if (wm_list->avail_wm > num_online_cpus()) { + spin_unlock(&wm_list->wm_lock); + wait_event(wm_list->wm_wait, + !list_empty(&wm_list->idle_wm)); + continue; + } + + wm_list->avail_wm++; + spin_unlock(&wm_list->wm_lock); + + wm = wm_alloc(size, GFP_KERNEL); + if (!wm) { + spin_lock(&wm_list->wm_lock); + wm_list->avail_wm--; + spin_unlock(&wm_list->wm_lock); + wait_event(wm_list->wm_wait, + !list_empty(&wm_list->idle_wm)); + continue; + } + break; + } + + return wm; +} + +static void release_wm(struct wm *wm, struct wm_list *wm_list) +{ + if (!wm) + return; + + spin_lock(&wm_list->wm_lock); + if (wm_list->avail_wm <= num_online_cpus()) { + list_add(&wm->list, &wm_list->idle_wm); + spin_unlock(&wm_list->wm_lock); + wake_up(&wm_list->wm_wait); + return; + } + + wm_list->avail_wm--; + spin_unlock(&wm_list->wm_lock); + ksmbd_free(wm); +} + +static void wm_list_free(struct wm_list *l) +{ + struct wm *wm; + + while (!list_empty(&l->idle_wm)) { + wm = list_entry(l->idle_wm.next, struct wm, list); + list_del(&wm->list); + kvfree(wm); + } + kvfree(l); +} + +static void wm_lists_destroy(void) +{ + struct wm_list *l; + + while (!list_empty(&wm_lists)) { + l = list_entry(wm_lists.next, struct wm_list, list); + list_del(&l->list); + wm_list_free(l); + } +} + +void ksmbd_free_request(void *addr) +{ + kvfree(addr); +} + +void *ksmbd_alloc_request(size_t size) +{ + return kvmalloc(size, GFP_KERNEL); +} + +void ksmbd_free_response(void *buffer) +{ + kvfree(buffer); +} + +void *ksmbd_alloc_response(size_t size) +{ + return kvmalloc(size, GFP_KERNEL | __GFP_ZERO); +} + +void *ksmbd_find_buffer(size_t size) +{ + struct wm *wm; + + wm = find_wm(size); + + WARN_ON(!wm); + if (wm) + return wm->buffer; + return NULL; +} + +void ksmbd_release_buffer(void *buffer) +{ + struct wm_list *wm_list; + struct wm *wm; + + if (!buffer) + return; + + wm = container_of(buffer, struct wm, buffer); + wm_list = match_wm_list(wm->sz); + WARN_ON(!wm_list); + if (wm_list) + release_wm(wm, wm_list); +} + +void *ksmbd_realloc_response(void *ptr, size_t old_sz, size_t new_sz) +{ + size_t sz = min(old_sz, new_sz); + void *nptr; + + nptr = ksmbd_alloc_response(new_sz); + if (!nptr) + return ptr; + memcpy(nptr, ptr, sz); + ksmbd_free_response(ptr); + return nptr; +} + +void ksmbd_free_file_struct(void *filp) +{ + kmem_cache_free(filp_cache, filp); +} + +void *ksmbd_alloc_file_struct(void) +{ + return kmem_cache_zalloc(filp_cache, GFP_KERNEL); +} + +void ksmbd_destroy_buffer_pools(void) +{ + wm_lists_destroy(); + ksmbd_work_pool_destroy(); + kmem_cache_destroy(filp_cache); +} + +int ksmbd_init_buffer_pools(void) +{ + if (ksmbd_work_pool_init()) + goto out; + + filp_cache = kmem_cache_create("ksmbd_file_cache", + sizeof(struct ksmbd_file), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!filp_cache) + goto out; + + return 0; + +out: + ksmbd_err("failed to allocate memory\n"); + ksmbd_destroy_buffer_pools(); + return -ENOMEM; +} diff --git a/fs/cifsd/buffer_pool.h b/fs/cifsd/buffer_pool.h new file mode 100644 index 000000000000..2b3d03afcf27 --- /dev/null +++ b/fs/cifsd/buffer_pool.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_BUFFER_POOL_H__ +#define __KSMBD_BUFFER_POOL_H__ + +void *ksmbd_find_buffer(size_t size); +void ksmbd_release_buffer(void *buffer); + +void *ksmbd_alloc(size_t size); +void ksmbd_free(void *ptr); + +void ksmbd_free_request(void *addr); +void *ksmbd_alloc_request(size_t size); +void ksmbd_free_response(void *buffer); +void *ksmbd_alloc_response(size_t size); + +void *ksmbd_realloc_response(void *ptr, size_t old_sz, size_t new_sz); + +void ksmbd_free_file_struct(void *filp); +void *ksmbd_alloc_file_struct(void); + +void ksmbd_destroy_buffer_pools(void); +int ksmbd_init_buffer_pools(void); + +#endif /* __KSMBD_BUFFER_POOL_H__ */ diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c new file mode 100644 index 000000000000..00f80ca45690 --- /dev/null +++ b/fs/cifsd/vfs.c @@ -0,0 +1,1989 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "oplock.h" +#include "connection.h" +#include "buffer_pool.h" +#include "vfs.h" +#include "vfs_cache.h" +#include "smbacl.h" +#include "ndr.h" +#include "auth.h" + +#include "time_wrappers.h" +#include "smb_common.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" + +static char *extract_last_component(char *path) +{ + char *p = strrchr(path, '/'); + + if (p && p[1] != '\0') { + *p = '\0'; + p++; + } else { + p = NULL; + ksmbd_err("Invalid path %s\n", path); + } + return p; +} + +static void rollback_path_modification(char *filename) +{ + if (filename) { + filename--; + *filename = '/'; + } +} + +static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, + struct inode *parent_inode, + struct inode *inode) +{ + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_INHERIT_OWNER)) + return; + + i_uid_write(inode, i_uid_read(parent_inode)); +} + +static void ksmbd_vfs_inherit_smack(struct ksmbd_work *work, + struct dentry *dir_dentry, + struct dentry *dentry) +{ + char *name, *xattr_list = NULL, *smack_buf; + int value_len, xattr_list_len; + + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_INHERIT_SMACK)) + return; + + xattr_list_len = ksmbd_vfs_listxattr(dir_dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_err("no ea data in the file\n"); + return; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + int rc; + + ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); + if (strcmp(name, XATTR_NAME_SMACK)) + continue; + + value_len = ksmbd_vfs_getxattr(dir_dentry, name, &smack_buf); + if (value_len <= 0) + continue; + + rc = ksmbd_vfs_setxattr(dentry, XATTR_NAME_SMACK, smack_buf, + value_len, 0); + ksmbd_free(smack_buf); + if (rc < 0) + ksmbd_err("ksmbd_vfs_setxattr() failed: %d\n", rc); + } +out: + ksmbd_vfs_xattr_free(xattr_list); +} + +int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete) +{ + int mask; + + mask = 0; + acc_mode &= O_ACCMODE; + + if (acc_mode == O_RDONLY) + mask = MAY_READ; + else if (acc_mode == O_WRONLY) + mask = MAY_WRITE; + else if (acc_mode == O_RDWR) + mask = MAY_READ | MAY_WRITE; + + if (inode_permission(&init_user_ns, d_inode(dentry), mask | MAY_OPEN)) + return -EACCES; + + if (delete) { + struct dentry *parent; + + parent = dget_parent(dentry); + if (!parent) + return -EINVAL; + + if (inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) { + dput(parent); + return -EACCES; + } + dput(parent); + } + return 0; +} + +int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) +{ + struct dentry *parent; + + *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); + + if (!inode_permission(&init_user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE)) + *daccess |= cpu_to_le32(WRITE_DAC | WRITE_OWNER | SYNCHRONIZE | + FILE_WRITE_DATA | FILE_APPEND_DATA | + FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | + FILE_DELETE_CHILD); + + if (!inode_permission(&init_user_ns, d_inode(dentry), MAY_OPEN | MAY_READ)) + *daccess |= FILE_READ_DATA_LE | FILE_READ_EA_LE; + + if (!inode_permission(&init_user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC)) + *daccess |= FILE_EXECUTE_LE; + + parent = dget_parent(dentry); + if (!parent) + return 0; + + if (!inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) + *daccess |= FILE_DELETE_LE; + dput(parent); + return 0; +} + + +/** + * ksmbd_vfs_create() - vfs helper for smb create file + * @work: work + * @name: file name + * @mode: file create mode + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_create(struct ksmbd_work *work, + const char *name, + umode_t mode) +{ + struct path path; + struct dentry *dentry; + int err; + + dentry = kern_path_create(AT_FDCWD, name, &path, 0); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + if (err != -ENOENT) + ksmbd_err("path create failed for %s, err %d\n", + name, err); + return err; + } + + mode |= S_IFREG; + err = vfs_create(&init_user_ns, d_inode(path.dentry), dentry, mode, true); + if (!err) { + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), + d_inode(dentry)); + ksmbd_vfs_inherit_smack(work, path.dentry, dentry); + } else { + ksmbd_err("File(%s): creation failed (err:%d)\n", name, err); + } + done_path_create(&path, dentry); + return err; +} + +/** + * ksmbd_vfs_mkdir() - vfs helper for smb create directory + * @work: work + * @name: directory name + * @mode: directory create mode + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_mkdir(struct ksmbd_work *work, + const char *name, + umode_t mode) +{ + struct path path; + struct dentry *dentry; + int err; + + dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + if (err != -EEXIST) + ksmbd_debug(VFS, "path create failed for %s, err %d\n", + name, err); + return err; + } + + mode |= S_IFDIR; + err = vfs_mkdir(&init_user_ns, d_inode(path.dentry), dentry, mode); + if (!err) { + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), + d_inode(dentry)); + ksmbd_vfs_inherit_smack(work, path.dentry, dentry); + } else + ksmbd_err("mkdir(%s): creation failed (err:%d)\n", name, err); + + done_path_create(&path, dentry); + return err; +} + +static ssize_t ksmbd_vfs_getcasexattr(struct dentry *dentry, + char *attr_name, + int attr_name_len, + char **attr_value) +{ + char *name, *xattr_list = NULL; + ssize_t value_len = -ENOENT, xattr_list_len; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len <= 0) + goto out; + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); + if (strncasecmp(attr_name, name, attr_name_len)) + continue; + + value_len = ksmbd_vfs_getxattr(dentry, + name, + attr_value); + if (value_len < 0) + ksmbd_err("failed to get xattr in file\n"); + break; + } + +out: + ksmbd_vfs_xattr_free(xattr_list); + return value_len; +} + +static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, + size_t count) +{ + ssize_t v_len; + char *stream_buf = NULL; + int err; + + ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", + *pos, count); + + v_len = ksmbd_vfs_getcasexattr(fp->filp->f_path.dentry, + fp->stream.name, + fp->stream.size, + &stream_buf); + if (v_len == -ENOENT) { + ksmbd_err("not found stream in xattr : %zd\n", v_len); + err = -ENOENT; + return err; + } + + memcpy(buf, &stream_buf[*pos], count); + return v_len > count ? count : v_len; +} + +/** + * check_lock_range() - vfs helper for smb byte range file locking + * @filp: the file to apply the lock to + * @start: lock start byte offset + * @end: lock end byte offset + * @type: byte range type read/write + * + * Return: 0 on success, otherwise error + */ +static int check_lock_range(struct file *filp, + loff_t start, + loff_t end, + unsigned char type) +{ + struct file_lock *flock; + struct file_lock_context *ctx = file_inode(filp)->i_flctx; + int error = 0; + + if (!ctx || list_empty_careful(&ctx->flc_posix)) + return 0; + + spin_lock(&ctx->flc_lock); + list_for_each_entry(flock, &ctx->flc_posix, fl_list) { + /* check conflict locks */ + if (flock->fl_end >= start && end >= flock->fl_start) { + if (flock->fl_type == F_RDLCK) { + if (type == WRITE) { + ksmbd_err("not allow write by shared lock\n"); + error = 1; + goto out; + } + } else if (flock->fl_type == F_WRLCK) { + /* check owner in lock */ + if (flock->fl_file != filp) { + error = 1; + ksmbd_err("not allow rw access by exclusive lock from other opens\n"); + goto out; + } + } + } + } +out: + spin_unlock(&ctx->flc_lock); + return error; +} + +/** + * ksmbd_vfs_read() - vfs helper for smb file read + * @work: smb work + * @fid: file id of open file + * @count: read byte count + * @pos: file pos + * + * Return: number of read bytes on success, otherwise error + */ +int ksmbd_vfs_read(struct ksmbd_work *work, + struct ksmbd_file *fp, + size_t count, + loff_t *pos) +{ + struct file *filp; + ssize_t nbytes = 0; + char *rbuf, *name; + struct inode *inode; + char namebuf[NAME_MAX]; + int ret; + + rbuf = AUX_PAYLOAD(work); + filp = fp->filp; + inode = d_inode(filp->f_path.dentry); + if (S_ISDIR(inode->i_mode)) + return -EISDIR; + + if (unlikely(count == 0)) + return 0; + + if (work->conn->connection_type) { + if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { + ksmbd_err("no right to read(%s)\n", FP_FILENAME(fp)); + return -EACCES; + } + } + + if (ksmbd_stream_fd(fp)) + return ksmbd_vfs_stream_read(fp, rbuf, pos, count); + + ret = check_lock_range(filp, *pos, *pos + count - 1, + READ); + if (ret) { + ksmbd_err("unable to read due to lock\n"); + return -EAGAIN; + } + + nbytes = kernel_read(filp, rbuf, count, pos); + if (nbytes < 0) { + name = d_path(&filp->f_path, namebuf, sizeof(namebuf)); + if (IS_ERR(name)) + name = "(error)"; + ksmbd_err("smb read failed for (%s), err = %zd\n", + name, nbytes); + return nbytes; + } + + filp->f_pos = *pos; + return nbytes; +} + +static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + size_t count) +{ + char *stream_buf = NULL, *wbuf; + size_t size, v_len; + int err = 0; + + ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", + *pos, count); + + size = *pos + count; + if (size > XATTR_SIZE_MAX) { + size = XATTR_SIZE_MAX; + count = (*pos + count) - XATTR_SIZE_MAX; + } + + v_len = ksmbd_vfs_getcasexattr(fp->filp->f_path.dentry, + fp->stream.name, + fp->stream.size, + &stream_buf); + if (v_len == -ENOENT) { + ksmbd_err("not found stream in xattr : %zd\n", v_len); + err = -ENOENT; + goto out; + } + + if (v_len < size) { + wbuf = ksmbd_alloc(size); + if (!wbuf) { + err = -ENOMEM; + goto out; + } + + if (v_len > 0) + memcpy(wbuf, stream_buf, v_len); + stream_buf = wbuf; + } + + memcpy(&stream_buf[*pos], buf, count); + + err = ksmbd_vfs_setxattr(fp->filp->f_path.dentry, + fp->stream.name, + (void *)stream_buf, + size, + 0); + if (err < 0) + goto out; + + fp->filp->f_pos = *pos; + err = 0; +out: + ksmbd_free(stream_buf); + return err; +} + +/** + * ksmbd_vfs_write() - vfs helper for smb file write + * @work: work + * @fid: file id of open file + * @buf: buf containing data for writing + * @count: read byte count + * @pos: file pos + * @sync: fsync after write + * @written: number of bytes written + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, ssize_t *written) +{ + struct ksmbd_session *sess = work->sess; + struct file *filp; + loff_t offset = *pos; + int err = 0; + + if (sess->conn->connection_type) { + if (!(fp->daccess & FILE_WRITE_DATA_LE)) { + ksmbd_err("no right to write(%s)\n", FP_FILENAME(fp)); + err = -EACCES; + goto out; + } + } + + filp = fp->filp; + + if (ksmbd_stream_fd(fp)) { + err = ksmbd_vfs_stream_write(fp, buf, pos, count); + if (!err) + *written = count; + goto out; + } + + err = check_lock_range(filp, *pos, *pos + count - 1, WRITE); + if (err) { + ksmbd_err("unable to write due to lock\n"); + err = -EAGAIN; + goto out; + } + + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + + err = kernel_write(filp, buf, count, pos); + if (err < 0) { + ksmbd_debug(VFS, "smb write failed, err = %d\n", err); + goto out; + } + + filp->f_pos = *pos; + *written = err; + err = 0; + if (sync) { + err = vfs_fsync_range(filp, offset, offset + *written, 0); + if (err < 0) + ksmbd_err("fsync failed for filename = %s, err = %d\n", + FP_FILENAME(fp), err); + } + +out: + return err; +} + +/** + * ksmbd_vfs_getattr() - vfs helper for smb getattr + * @work: work + * @fid: file id of open file + * @attrs: inode attributes + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_getattr(struct path *path, struct kstat *stat) +{ + int err; + + err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT); + if (err) + ksmbd_err("getattr failed, err %d\n", err); + return err; +} + +/** + * ksmbd_vfs_fsync() - vfs helper for smb fsync + * @work: work + * @fid: file id of open file + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_fsync(struct ksmbd_work *work, uint64_t fid, uint64_t p_id) +{ + struct ksmbd_file *fp; + int err; + + fp = ksmbd_lookup_fd_slow(work, fid, p_id); + if (!fp) { + ksmbd_err("failed to get filp for fid %llu\n", fid); + return -ENOENT; + } + err = vfs_fsync(fp->filp, 0); + if (err < 0) + ksmbd_err("smb fsync failed, err = %d\n", err); + ksmbd_fd_put(work, fp); + return err; +} + +/** + * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink + * @name: absolute directory or file name + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) +{ + struct path parent; + struct dentry *dir, *dentry; + char *last; + int err = -ENOENT; + + last = extract_last_component(name); + if (!last) + return -ENOENT; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = kern_path(name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &parent); + if (err) { + ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); + ksmbd_revert_fsids(work); + rollback_path_modification(last); + return err; + } + + dir = parent.dentry; + if (!d_inode(dir)) + goto out; + + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + dentry = lookup_one_len(last, dir, strlen(last)); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + ksmbd_debug(VFS, "%s: lookup failed, err %d\n", last, err); + goto out_err; + } + + if (!d_inode(dentry) || !d_inode(dentry)->i_nlink) { + dput(dentry); + err = -ENOENT; + goto out_err; + } + + if (S_ISDIR(d_inode(dentry)->i_mode)) { + err = vfs_rmdir(&init_user_ns, d_inode(dir), dentry); + if (err && err != -ENOTEMPTY) + ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, + err); + } else { + err = vfs_unlink(&init_user_ns, d_inode(dir), dentry, NULL); + if (err) + ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, + err); + } + + dput(dentry); +out_err: + inode_unlock(d_inode(dir)); +out: + rollback_path_modification(last); + path_put(&parent); + ksmbd_revert_fsids(work); + return err; +} + +/** + * ksmbd_vfs_link() - vfs helper for creating smb hardlink + * @oldname: source file name + * @newname: hardlink name + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_link(struct ksmbd_work *work, + const char *oldname, const char *newname) +{ + struct path oldpath, newpath; + struct dentry *dentry; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = kern_path(oldname, LOOKUP_FOLLOW, &oldpath); + if (err) { + ksmbd_err("cannot get linux path for %s, err = %d\n", + oldname, err); + goto out1; + } + + dentry = kern_path_create(AT_FDCWD, newname, &newpath, + LOOKUP_FOLLOW | LOOKUP_REVAL); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + ksmbd_err("path create err for %s, err %d\n", newname, err); + goto out2; + } + + err = -EXDEV; + if (oldpath.mnt != newpath.mnt) { + ksmbd_err("vfs_link failed err %d\n", err); + goto out3; + } + + err = vfs_link(oldpath.dentry, &init_user_ns, d_inode(newpath.dentry), + dentry, NULL); + if (err) + ksmbd_debug(VFS, "vfs_link failed err %d\n", err); + +out3: + done_path_create(&newpath, dentry); +out2: + path_put(&oldpath); +out1: + ksmbd_revert_fsids(work); + return err; +} + +static int __ksmbd_vfs_rename(struct ksmbd_work *work, + struct dentry *src_dent_parent, + struct dentry *src_dent, + struct dentry *dst_dent_parent, + struct dentry *trap_dent, + char *dst_name) +{ + struct dentry *dst_dent; + int err; + + spin_lock(&src_dent->d_lock); + list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { + struct ksmbd_file *child_fp; + + if (d_really_is_negative(dst_dent)) + continue; + + child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); + if (child_fp) { + spin_unlock(&src_dent->d_lock); + ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); + return -EACCES; + } + } + spin_unlock(&src_dent->d_lock); + + if (d_really_is_negative(src_dent_parent)) + return -ENOENT; + if (d_really_is_negative(dst_dent_parent)) + return -ENOENT; + if (d_really_is_negative(src_dent)) + return -ENOENT; + if (src_dent == trap_dent) + return -EINVAL; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + dst_dent = lookup_one_len(dst_name, dst_dent_parent, strlen(dst_name)); + err = PTR_ERR(dst_dent); + if (IS_ERR(dst_dent)) { + ksmbd_err("lookup failed %s [%d]\n", dst_name, err); + goto out; + } + + err = -ENOTEMPTY; + if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) { + struct renamedata rd = { + .old_mnt_userns = &init_user_ns, + .old_dir = d_inode(src_dent_parent), + .old_dentry = src_dent, + .new_mnt_userns = &init_user_ns, + .new_dir = d_inode(dst_dent_parent), + .new_dentry = dst_dent, + }; + err = vfs_rename(&rd); + } + if (err) + ksmbd_err("vfs_rename failed err %d\n", err); + if (dst_dent) + dput(dst_dent); +out: + ksmbd_revert_fsids(work); + return err; +} + +int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, + char *newname) +{ + struct path dst_path; + struct dentry *src_dent_parent, *dst_dent_parent; + struct dentry *src_dent, *trap_dent; + char *dst_name; + int err; + + dst_name = extract_last_component(newname); + if (!dst_name) + return -EINVAL; + + src_dent_parent = dget_parent(fp->filp->f_path.dentry); + if (!src_dent_parent) + return -EINVAL; + + src_dent = fp->filp->f_path.dentry; + dget(src_dent); + + err = kern_path(newname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &dst_path); + if (err) { + ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); + goto out; + } + dst_dent_parent = dst_path.dentry; + dget(dst_dent_parent); + + trap_dent = lock_rename(src_dent_parent, dst_dent_parent); + err = __ksmbd_vfs_rename(work, + src_dent_parent, + src_dent, + dst_dent_parent, + trap_dent, + dst_name); + unlock_rename(src_dent_parent, dst_dent_parent); + dput(dst_dent_parent); + path_put(&dst_path); +out: + dput(src_dent); + dput(src_dent_parent); + return err; +} + +/** + * ksmbd_vfs_truncate() - vfs helper for smb file truncate + * @work: work + * @name: old filename + * @fid: file id of old file + * @size: truncate to given size + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, + struct ksmbd_file *fp, loff_t size) +{ + struct path path; + int err = 0; + struct inode *inode; + + if (name) { + err = kern_path(name, 0, &path); + if (err) { + ksmbd_err("cannot get linux path for %s, err %d\n", + name, err); + return err; + } + err = vfs_truncate(&path, size); + if (err) + ksmbd_err("truncate failed for %s err %d\n", + name, err); + path_put(&path); + } else { + struct file *filp; + + filp = fp->filp; + + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + + inode = file_inode(filp); + if (size < inode->i_size) { + err = check_lock_range(filp, size, + inode->i_size - 1, WRITE); + } else { + err = check_lock_range(filp, inode->i_size, + size - 1, WRITE); + } + + if (err) { + ksmbd_err("failed due to lock\n"); + return -EAGAIN; + } + + err = vfs_truncate(&filp->f_path, size); + if (err) + ksmbd_err("truncate failed for filename : %s err %d\n", + fp->filename, err); + } + + return err; +} + +/** + * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes + * @dentry: dentry of file for listing xattrs + * @list: destination buffer + * @size: destination buffer length + * + * Return: xattr list length on success, otherwise error + */ +ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) +{ + ssize_t size; + char *vlist = NULL; + + size = vfs_listxattr(dentry, NULL, 0); + if (size <= 0) + return size; + + vlist = ksmbd_alloc(size); + if (!vlist) + return -ENOMEM; + + *list = vlist; + size = vfs_listxattr(dentry, vlist, size); + if (size < 0) { + ksmbd_debug(VFS, "listxattr failed\n"); + ksmbd_vfs_xattr_free(vlist); + *list = NULL; + } + + return size; +} + +static ssize_t ksmbd_vfs_xattr_len(struct dentry *dentry, + char *xattr_name) +{ + return vfs_getxattr(&init_user_ns, dentry, xattr_name, NULL, 0); +} + +/** + * ksmbd_vfs_getxattr() - vfs helper for smb get extended attributes value + * @dentry: dentry of file for getting xattrs + * @xattr_name: name of xattr name to query + * @xattr_buf: destination buffer xattr value + * + * Return: read xattr value length on success, otherwise error + */ +ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, + char *xattr_name, + char **xattr_buf) +{ + ssize_t xattr_len; + char *buf; + + *xattr_buf = NULL; + xattr_len = ksmbd_vfs_xattr_len(dentry, xattr_name); + if (xattr_len < 0) + return xattr_len; + + buf = kmalloc(xattr_len + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + xattr_len = vfs_getxattr(&init_user_ns, dentry, xattr_name, (void *)buf, + xattr_len); + if (xattr_len > 0) + *xattr_buf = buf; + else + kfree(buf); + return xattr_len; +} + +/** + * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value + * @dentry: dentry to set XATTR at + * @name: xattr name for setxattr + * @value: xattr value to set + * @size: size of xattr value + * @flags: destination buffer length + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_setxattr(struct dentry *dentry, + const char *attr_name, + const void *attr_value, + size_t attr_size, + int flags) +{ + int err; + + err = vfs_setxattr(&init_user_ns, dentry, + attr_name, + attr_value, + attr_size, + flags); + if (err) + ksmbd_debug(VFS, "setxattr failed, err %d\n", err); + return err; +} + +/** + * ksmbd_vfs_set_fadvise() - convert smb IO caching options to linux options + * @filp: file pointer for IO + * @options: smb IO options + */ +void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) +{ + struct address_space *mapping; + + mapping = filp->f_mapping; + + if (!option || !mapping) + return; + + if (option & FILE_WRITE_THROUGH_LE) + filp->f_flags |= O_SYNC; + else if (option & FILE_SEQUENTIAL_ONLY_LE) { + filp->f_ra.ra_pages = inode_to_bdi(mapping->host)->ra_pages * 2; + spin_lock(&filp->f_lock); + filp->f_mode &= ~FMODE_RANDOM; + spin_unlock(&filp->f_lock); + } else if (option & FILE_RANDOM_ACCESS_LE) { + spin_lock(&filp->f_lock); + filp->f_mode |= FMODE_RANDOM; + spin_unlock(&filp->f_lock); + } +} + +/** + * ksmbd_vfs_lock() - vfs helper for smb file locking + * @filp: the file to apply the lock to + * @cmd: type of locking operation (F_SETLK, F_GETLK, etc.) + * @flock: The lock to be applied + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_lock(struct file *filp, int cmd, + struct file_lock *flock) +{ + ksmbd_debug(VFS, "calling vfs_lock_file\n"); + return vfs_lock_file(filp, cmd, flock, NULL); +} + +int ksmbd_vfs_readdir(struct file *file, struct ksmbd_readdir_data *rdata) +{ + return iterate_dir(file, &rdata->ctx); +} + +int ksmbd_vfs_alloc_size(struct ksmbd_work *work, + struct ksmbd_file *fp, + loff_t len) +{ + smb_break_all_levII_oplock(work, fp, 1); + return vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, len); +} + +int ksmbd_vfs_zero_data(struct ksmbd_work *work, + struct ksmbd_file *fp, + loff_t off, + loff_t len) +{ + smb_break_all_levII_oplock(work, fp, 1); + if (fp->f_ci->m_fattr & ATTR_SPARSE_FILE_LE) + return vfs_fallocate(fp->filp, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, off, len); + + return vfs_fallocate(fp->filp, FALLOC_FL_ZERO_RANGE, off, len); +} + +int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + int in_count, int *out_count) +{ + struct file *f = fp->filp; + struct inode *inode = FP_INODE(fp); + loff_t maxbytes = (u64)inode->i_sb->s_maxbytes, end; + loff_t extent_start, extent_end; + int ret = 0; + + if (start > maxbytes) + return -EFBIG; + + if (!in_count) + return 0; + + /* + * Shrink request scope to what the fs can actually handle. + */ + if (length > maxbytes || (maxbytes - length) < start) + length = maxbytes - start; + + if (start + length > inode->i_size) + length = inode->i_size - start; + + *out_count = 0; + end = start + length; + while (start < end && *out_count < in_count) { + extent_start = f->f_op->llseek(f, start, SEEK_DATA); + if (extent_start < 0) { + if (extent_start != -ENXIO) + ret = (int)extent_start; + break; + } + + if (extent_start >= end) + break; + + extent_end = f->f_op->llseek(f, extent_start, SEEK_HOLE); + if (extent_end < 0) { + if (extent_end != -ENXIO) + ret = (int)extent_end; + break; + } else if (extent_start >= extent_end) + break; + + ranges[*out_count].file_offset = cpu_to_le64(extent_start); + ranges[(*out_count)++].length = + cpu_to_le64(min(extent_end, end) - extent_start); + + start = extent_end; + } + + return ret; +} + +int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name) +{ + return vfs_removexattr(&init_user_ns, dentry, attr_name); +} + +void ksmbd_vfs_xattr_free(char *xattr) +{ + ksmbd_free(xattr); +} + +int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry) +{ + int err = 0; + + dget(dentry); + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + if (!d_inode(dentry) || !d_inode(dentry)->i_nlink) { + err = -ENOENT; + goto out; + } + + if (S_ISDIR(d_inode(dentry)->i_mode)) + err = vfs_rmdir(&init_user_ns, d_inode(dir), dentry); + else + err = vfs_unlink(&init_user_ns, d_inode(dir), dentry, NULL); + +out: + inode_unlock(d_inode(dir)); + dput(dentry); + if (err) + ksmbd_debug(VFS, "failed to delete, err %d\n", err); + + return err; +} + +/* + * ksmbd_vfs_get_logical_sector_size() - get logical sector size from inode + * @inode: inode + * + * Return: logical sector size + */ +unsigned short ksmbd_vfs_logical_sector_size(struct inode *inode) +{ + struct request_queue *q; + unsigned short ret_val = 512; + + if (!inode->i_sb->s_bdev) + return ret_val; + + q = inode->i_sb->s_bdev->bd_disk->queue; + + if (q && q->limits.logical_block_size) + ret_val = q->limits.logical_block_size; + + return ret_val; +} + +/* + * ksmbd_vfs_get_smb2_sector_size() - get fs sector sizes + * @inode: inode + * @fs_ss: fs sector size struct + */ +void ksmbd_vfs_smb2_sector_size(struct inode *inode, + struct ksmbd_fs_sector_size *fs_ss) +{ + struct request_queue *q; + + fs_ss->logical_sector_size = 512; + fs_ss->physical_sector_size = 512; + fs_ss->optimal_io_size = 512; + + if (!inode->i_sb->s_bdev) + return; + + q = inode->i_sb->s_bdev->bd_disk->queue; + + if (q) { + if (q->limits.logical_block_size) + fs_ss->logical_sector_size = + q->limits.logical_block_size; + if (q->limits.physical_block_size) + fs_ss->physical_sector_size = + q->limits.physical_block_size; + if (q->limits.io_opt) + fs_ss->optimal_io_size = q->limits.io_opt; + } +} + +static int __dir_empty(struct dir_context *ctx, + const char *name, + int namlen, + loff_t offset, + u64 ino, + unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + buf->dirent_count++; + + if (buf->dirent_count > 2) + return -ENOTEMPTY; + return 0; +} + +/** + * ksmbd_vfs_empty_dir() - check for empty directory + * @fp: ksmbd file pointer + * + * Return: true if directory empty, otherwise false + */ +int ksmbd_vfs_empty_dir(struct ksmbd_file *fp) +{ + int err; + struct ksmbd_readdir_data readdir_data; + + memset(&readdir_data, 0, sizeof(struct ksmbd_readdir_data)); + + set_ctx_actor(&readdir_data.ctx, __dir_empty); + readdir_data.dirent_count = 0; + + err = ksmbd_vfs_readdir(fp->filp, &readdir_data); + if (readdir_data.dirent_count > 2) + err = -ENOTEMPTY; + else + err = 0; + return err; +} + +static int __caseless_lookup(struct dir_context *ctx, + const char *name, + int namlen, + loff_t offset, + u64 ino, + unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + + if (buf->used != namlen) + return 0; + if (!strncasecmp((char *)buf->private, name, namlen)) { + memcpy((char *)buf->private, name, namlen); + buf->dirent_count = 1; + return -EEXIST; + } + return 0; +} + +/** + * ksmbd_vfs_lookup_in_dir() - lookup a file in a directory + * @dirname: directory name + * @filename: filename to lookup + * + * Return: 0 on success, otherwise error + */ +static int ksmbd_vfs_lookup_in_dir(char *dirname, char *filename) +{ + struct path dir_path; + int ret; + struct file *dfilp; + int flags = O_RDONLY|O_LARGEFILE; + int dirnamelen = strlen(dirname); + struct ksmbd_readdir_data readdir_data = { + .ctx.actor = __caseless_lookup, + .private = filename, + .used = strlen(filename), + }; + + ret = ksmbd_vfs_kern_path(dirname, 0, &dir_path, true); + if (ret) + goto error; + + dfilp = dentry_open(&dir_path, flags, current_cred()); + if (IS_ERR(dfilp)) { + path_put(&dir_path); + ksmbd_err("cannot open directory %s\n", dirname); + ret = -EINVAL; + goto error; + } + + ret = ksmbd_vfs_readdir(dfilp, &readdir_data); + if (readdir_data.dirent_count > 0) + ret = 0; + + fput(dfilp); + path_put(&dir_path); +error: + dirname[dirnamelen] = '/'; + return ret; +} + +/** + * ksmbd_vfs_kern_path() - lookup a file and get path info + * @name: name of file for lookup + * @flags: lookup flags + * @path: if lookup succeed, return path info + * @caseless: caseless filename lookup + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, + bool caseless) +{ + char *filename = NULL; + int err; + + err = kern_path(name, flags, path); + if (!err) + return err; + + if (caseless) { + filename = extract_last_component(name); + if (!filename) + goto out; + + /* root reached */ + if (strlen(name) == 0) + goto out; + + err = ksmbd_vfs_lookup_in_dir(name, filename); + if (err) + goto out; + err = kern_path(name, flags, path); + } + +out: + rollback_path_modification(filename); + return err; +} + +int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry) +{ + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS, + sizeof(XATTR_NAME_POSIX_ACL_ACCESS)-1) || + !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, + sizeof(XATTR_NAME_POSIX_ACL_DEFAULT)-1)) { + err = ksmbd_vfs_remove_xattr(dentry, name); + if (err) + ksmbd_debug(SMB, + "remove acl xattr failed : %s\n", name); + } + } +out: + ksmbd_vfs_xattr_free(xattr_list); + return err; +} + +int ksmbd_vfs_remove_sd_xattrs(struct dentry *dentry) +{ + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { + err = ksmbd_vfs_remove_xattr(dentry, name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", name); + } + } +out: + ksmbd_vfs_xattr_free(xattr_list); + return err; +} + +static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct inode *inode, + int acl_type) +{ + struct xattr_smb_acl *smb_acl = NULL; + struct posix_acl *posix_acls; + struct posix_acl_entry *pa_entry; + struct xattr_acl_entry *xa_entry; + int i; + + posix_acls = ksmbd_vfs_get_acl(inode, acl_type); + if (!posix_acls) + return NULL; + + smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + + sizeof(struct xattr_acl_entry) * posix_acls->a_count, + GFP_KERNEL); + if (!smb_acl) + goto out; + + smb_acl->count = posix_acls->a_count; + pa_entry = posix_acls->a_entries; + xa_entry = smb_acl->entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++, xa_entry++) { + switch (pa_entry->e_tag) { + case ACL_USER: + xa_entry->type = SMB_ACL_USER; + xa_entry->uid = from_kuid(&init_user_ns, pa_entry->e_uid); + break; + case ACL_USER_OBJ: + xa_entry->type = SMB_ACL_USER_OBJ; + break; + case ACL_GROUP: + xa_entry->type = SMB_ACL_GROUP; + xa_entry->gid = from_kgid(&init_user_ns, pa_entry->e_gid); + break; + case ACL_GROUP_OBJ: + xa_entry->type = SMB_ACL_GROUP_OBJ; + break; + case ACL_OTHER: + xa_entry->type = SMB_ACL_OTHER; + break; + case ACL_MASK: + xa_entry->type = SMB_ACL_MASK; + break; + default: + ksmbd_err("unknown type : 0x%x\n", pa_entry->e_tag); + goto out; + } + + if (pa_entry->e_perm & ACL_READ) + xa_entry->perm |= SMB_ACL_READ; + if (pa_entry->e_perm & ACL_WRITE) + xa_entry->perm |= SMB_ACL_WRITE; + if (pa_entry->e_perm & ACL_EXECUTE) + xa_entry->perm |= SMB_ACL_EXECUTE; + } +out: + posix_acl_release(posix_acls); + return smb_acl; +} + +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, + struct smb_ntsd *pntsd, int len) +{ + int rc; + struct ndr sd_ndr = {0}, acl_ndr = {0}; + struct xattr_ntacl acl = {0}; + struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; + struct inode *inode = dentry->d_inode; + + acl.version = 4; + acl.hash_type = XATTR_SD_HASH_TYPE_SHA256; + acl.current_time = ksmbd_UnixTimeToNT(current_time(dentry->d_inode)); + + memcpy(acl.desc, "posix_acl", 9); + acl.desc_len = 10; + + pntsd->osidoffset = + cpu_to_le32(le32_to_cpu(pntsd->osidoffset) + NDR_NTSD_OFFSETOF); + pntsd->gsidoffset = + cpu_to_le32(le32_to_cpu(pntsd->gsidoffset) + NDR_NTSD_OFFSETOF); + pntsd->dacloffset = + cpu_to_le32(le32_to_cpu(pntsd->dacloffset) + NDR_NTSD_OFFSETOF); + + acl.sd_buf = (char *)pntsd; + acl.sd_size = len; + + rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash); + if (rc) { + ksmbd_err("failed to generate hash for ndr acl\n"); + return rc; + } + + smb_acl = ksmbd_vfs_make_xattr_posix_acl(dentry->d_inode, ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(dentry->d_inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, inode, smb_acl, def_smb_acl); + if (rc) { + ksmbd_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, + acl.posix_acl_hash); + if (rc) { + ksmbd_err("failed to generate hash for ndr acl\n"); + goto out; + } + + rc = ndr_encode_v4_ntacl(&sd_ndr, &acl); + if (rc) { + ksmbd_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_vfs_setxattr(dentry, XATTR_NAME_SD, sd_ndr.data, + sd_ndr.offset, 0); + if (rc < 0) + ksmbd_err("Failed to store XATTR ntacl :%d\n", rc); + + kfree(sd_ndr.data); +out: + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + return rc; +} + +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, + struct smb_ntsd **pntsd) +{ + int rc; + struct ndr n; + + rc = ksmbd_vfs_getxattr(dentry, XATTR_NAME_SD, &n.data); + if (rc > 0) { + struct inode *inode = dentry->d_inode; + struct ndr acl_ndr = {0}; + struct xattr_ntacl acl; + struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; + __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; + + n.length = rc; + rc = ndr_decode_v4_ntacl(&n, &acl); + if (rc) + return rc; + + smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, + ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, inode, smb_acl, def_smb_acl); + if (rc) { + ksmbd_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, + cmp_hash); + if (rc) { + ksmbd_err("failed to generate hash for ndr acl\n"); + goto out; + } + + if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { + ksmbd_err("hash value diff\n"); + rc = -EINVAL; + goto out; + } + + *pntsd = acl.sd_buf; + (*pntsd)->osidoffset = + cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - NDR_NTSD_OFFSETOF); + (*pntsd)->gsidoffset = + cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - NDR_NTSD_OFFSETOF); + (*pntsd)->dacloffset = + cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) - NDR_NTSD_OFFSETOF); + + rc = acl.sd_size; +out: + kfree(n.data); + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + } + + return rc; +} + +int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ndr_encode_dos_attr(&n, da); + if (err) + return err; + + err = ksmbd_vfs_setxattr(dentry, + XATTR_NAME_DOS_ATTRIBUTE, + (void *)n.data, + n.offset, + 0); + if (err) + ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); + kfree(n.data); + + return err; +} + +int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ksmbd_vfs_getxattr(dentry, + XATTR_NAME_DOS_ATTRIBUTE, + (char **)&n.data); + if (err > 0) { + n.length = err; + if (ndr_decode_dos_attr(&n, da)) + err = -EINVAL; + ksmbd_free(n.data); + } else + ksmbd_debug(SMB, "failed to load dos attribute in xattr\n"); + + return err; +} + +struct posix_acl *ksmbd_vfs_posix_acl_alloc(int count, gfp_t flags) +{ +#if IS_ENABLED(CONFIG_FS_POSIX_ACL) + return posix_acl_alloc(count, flags); +#else + return NULL; +#endif +} + +struct posix_acl *ksmbd_vfs_get_acl(struct inode *inode, int type) +{ +#if IS_ENABLED(CONFIG_FS_POSIX_ACL) + return get_acl(inode, type); +#else + return NULL; +#endif +} + +int ksmbd_vfs_set_posix_acl(struct inode *inode, int type, + struct posix_acl *acl) +{ +#if IS_ENABLED(CONFIG_FS_POSIX_ACL) + return set_posix_acl(&init_user_ns, inode, type, acl); +#else + return -EOPNOTSUPP; +#endif +} + +/** + * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format + * @p: destination buffer + * @ksmbd_kstat: ksmbd kstat wrapper + */ +void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) +{ + struct file_directory_info *info = (struct file_directory_info *)(*p); + struct kstat *kstat = ksmbd_kstat->kstat; + u64 time; + + info->FileIndex = 0; + info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); + time = ksmbd_UnixTimeToNT(kstat->atime); + info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(kstat->mtime); + info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(kstat->ctime); + info->ChangeTime = cpu_to_le64(time); + + if (ksmbd_kstat->file_attributes & ATTR_DIRECTORY_LE) { + info->EndOfFile = 0; + info->AllocationSize = 0; + } else { + info->EndOfFile = cpu_to_le64(kstat->size); + info->AllocationSize = cpu_to_le64(kstat->blocks << 9); + } + info->ExtFileAttributes = ksmbd_kstat->file_attributes; + + return info; +} + +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat) +{ + u64 time; + int rc; + + generic_fillattr(&init_user_ns, d_inode(dentry), ksmbd_kstat->kstat); + + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); + ksmbd_kstat->create_time = time; + + /* + * set default value for the case that store dos attributes is not yes + * or that acl is disable in server's filesystem and the config is yes. + */ + if (S_ISDIR(ksmbd_kstat->kstat->mode)) + ksmbd_kstat->file_attributes = ATTR_DIRECTORY_LE; + else + ksmbd_kstat->file_attributes = ATTR_ARCHIVE_LE; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + rc = ksmbd_vfs_get_dos_attrib_xattr(dentry, &da); + if (rc > 0) { + ksmbd_kstat->file_attributes = cpu_to_le32(da.attr); + ksmbd_kstat->create_time = da.create_time; + } else + ksmbd_debug(VFS, "fail to load dos attribute.\n"); + } + + return 0; +} + +ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, + char *attr_name, + int attr_name_len) +{ + char *name, *xattr_list = NULL; + ssize_t value_len = -ENOENT, xattr_list_len; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len <= 0) + goto out; + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); + if (strncasecmp(attr_name, name, attr_name_len)) + continue; + + value_len = ksmbd_vfs_xattr_len(dentry, name); + break; + } + +out: + ksmbd_vfs_xattr_free(xattr_list); + return value_len; +} + +int ksmbd_vfs_xattr_stream_name(char *stream_name, + char **xattr_stream_name, + size_t *xattr_stream_name_size, + int s_type) +{ + int stream_name_size; + char *xattr_stream_name_buf; + char *type; + int type_len; + + if (s_type == DIR_STREAM) + type = ":$INDEX_ALLOCATION"; + else + type = ":$DATA"; + + type_len = strlen(type); + stream_name_size = strlen(stream_name); + *xattr_stream_name_size = stream_name_size + XATTR_NAME_STREAM_LEN + 1; + xattr_stream_name_buf = kmalloc(*xattr_stream_name_size + type_len, + GFP_KERNEL); + if (!xattr_stream_name_buf) + return -ENOMEM; + + memcpy(xattr_stream_name_buf, + XATTR_NAME_STREAM, + XATTR_NAME_STREAM_LEN); + + if (stream_name_size) { + memcpy(&xattr_stream_name_buf[XATTR_NAME_STREAM_LEN], + stream_name, + stream_name_size); + } + memcpy(&xattr_stream_name_buf[*xattr_stream_name_size - 1], type, type_len); + *xattr_stream_name_size += type_len; + + xattr_stream_name_buf[*xattr_stream_name_size - 1] = '\0'; + *xattr_stream_name = xattr_stream_name_buf; + + return 0; +} + +static int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, + struct file *file_out, loff_t pos_out, + size_t len) +{ + struct inode *inode_in = file_inode(file_in); + struct inode *inode_out = file_inode(file_out); + int ret; + + ret = vfs_copy_file_range(file_in, pos_in, file_out, pos_out, len, 0); + /* do splice for the copy between different file systems */ + if (ret != -EXDEV) + return ret; + + if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode)) + return -EISDIR; + if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode)) + return -EINVAL; + + if (!(file_in->f_mode & FMODE_READ) || + !(file_out->f_mode & FMODE_WRITE)) + return -EBADF; + + if (len == 0) + return 0; + + file_start_write(file_out); + + /* + * skip the verification of the range of data. it will be done + * in do_splice_direct + */ + ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out, + len > MAX_RW_COUNT ? MAX_RW_COUNT : len, 0); + if (ret > 0) { + fsnotify_access(file_in); + add_rchar(current, ret); + fsnotify_modify(file_out); + add_wchar(current, ret); + } + + inc_syscr(current); + inc_syscw(current); + + file_end_write(file_out); + return ret; +} + +int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written) +{ + unsigned int i; + loff_t src_off, dst_off, src_file_size; + size_t len; + int ret; + + *chunk_count_written = 0; + *chunk_size_written = 0; + *total_size_written = 0; + + if (!(src_fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { + ksmbd_err("no right to read(%s)\n", FP_FILENAME(src_fp)); + return -EACCES; + } + if (!(dst_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) { + ksmbd_err("no right to write(%s)\n", FP_FILENAME(dst_fp)); + return -EACCES; + } + + if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp)) + return -EBADF; + + smb_break_all_levII_oplock(work, dst_fp, 1); + + for (i = 0; i < chunk_count; i++) { + src_off = le64_to_cpu(chunks[i].SourceOffset); + dst_off = le64_to_cpu(chunks[i].TargetOffset); + len = le32_to_cpu(chunks[i].Length); + + if (check_lock_range(src_fp->filp, src_off, + src_off + len - 1, READ)) + return -EAGAIN; + if (check_lock_range(dst_fp->filp, dst_off, + dst_off + len - 1, WRITE)) + return -EAGAIN; + } + + src_file_size = i_size_read(file_inode(src_fp->filp)); + + for (i = 0; i < chunk_count; i++) { + src_off = le64_to_cpu(chunks[i].SourceOffset); + dst_off = le64_to_cpu(chunks[i].TargetOffset); + len = le32_to_cpu(chunks[i].Length); + + if (src_off + len > src_file_size) + return -E2BIG; + + ret = ksmbd_vfs_copy_file_range(src_fp->filp, src_off, + dst_fp->filp, dst_off, len); + if (ret < 0) + return ret; + + *chunk_count_written += 1; + *total_size_written += ret; + } + return 0; +} + +int ksmbd_vfs_posix_lock_wait(struct file_lock *flock) +{ + return wait_event_interruptible(flock->fl_wait, !flock->fl_blocker); +} + +int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout) +{ + return wait_event_interruptible_timeout(flock->fl_wait, + !flock->fl_blocker, + timeout); +} + +void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) +{ + locks_delete_block(flock); +} + +int ksmbd_vfs_set_init_posix_acl(struct inode *inode) +{ + struct posix_acl_state acl_state; + struct posix_acl *acls; + int rc; + + ksmbd_debug(SMB, "Set posix acls\n"); + rc = init_acl_state(&acl_state, 1); + if (rc) + return rc; + + /* Set default owner group */ + acl_state.owner.allow = (inode->i_mode & 0700) >> 6; + acl_state.group.allow = (inode->i_mode & 0070) >> 3; + acl_state.other.allow = inode->i_mode & 0007; + acl_state.users->aces[acl_state.users->n].uid = inode->i_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + acl_state.owner.allow; + acl_state.groups->aces[acl_state.groups->n].gid = inode->i_gid; + acl_state.groups->aces[acl_state.groups->n++].perms.allow = + acl_state.group.allow; + acl_state.mask.allow = 0x07; + + acls = ksmbd_vfs_posix_acl_alloc(6, GFP_KERNEL); + if (!acls) { + free_acl_state(&acl_state); + return -ENOMEM; + } + posix_state_to_acl(&acl_state, acls->a_entries); + rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + else if (S_ISDIR(inode->i_mode)) { + posix_state_to_acl(&acl_state, acls->a_entries); + rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + free_acl_state(&acl_state); + posix_acl_release(acls); + return rc; +} + +int ksmbd_vfs_inherit_posix_acl(struct inode *inode, struct inode *parent_inode) +{ + struct posix_acl *acls; + struct posix_acl_entry *pace; + int rc, i; + + acls = ksmbd_vfs_get_acl(parent_inode, ACL_TYPE_DEFAULT); + if (!acls) + return -ENOENT; + pace = acls->a_entries; + + for (i = 0; i < acls->a_count; i++, pace++) { + if (pace->e_tag == ACL_MASK) { + pace->e_perm = 0x07; + break; + } + } + + rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + if (S_ISDIR(inode->i_mode)) { + rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + posix_acl_release(acls); + return rc; +} diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h new file mode 100644 index 000000000000..bbef5c20c146 --- /dev/null +++ b/fs/cifsd/vfs.h @@ -0,0 +1,314 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_VFS_H__ +#define __KSMBD_VFS_H__ + +#include +#include +#include +#include +#include + +#include "smbacl.h" + +/* STREAM XATTR PREFIX */ +#define STREAM_PREFIX "DosStream." +#define STREAM_PREFIX_LEN (sizeof(STREAM_PREFIX) - 1) +#define XATTR_NAME_STREAM (XATTR_USER_PREFIX STREAM_PREFIX) +#define XATTR_NAME_STREAM_LEN (sizeof(XATTR_NAME_STREAM) - 1) + +enum { + XATTR_DOSINFO_ATTRIB = 0x00000001, + XATTR_DOSINFO_EA_SIZE = 0x00000002, + XATTR_DOSINFO_SIZE = 0x00000004, + XATTR_DOSINFO_ALLOC_SIZE = 0x00000008, + XATTR_DOSINFO_CREATE_TIME = 0x00000010, + XATTR_DOSINFO_CHANGE_TIME = 0x00000020, + XATTR_DOSINFO_ITIME = 0x00000040 +}; + +struct xattr_dos_attrib { + __u16 version; + __u32 flags; + __u32 attr; + __u32 ea_size; + __u64 size; + __u64 alloc_size; + __u64 create_time; + __u64 change_time; + __u64 itime; +}; + +/* DOS ATTRIBUITE XATTR PREFIX */ +#define DOS_ATTRIBUTE_PREFIX "DOSATTRIB" +#define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1) +#define XATTR_NAME_DOS_ATTRIBUTE \ + (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) +#define XATTR_NAME_DOS_ATTRIBUTE_LEN \ + (sizeof(XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) - 1) + +#define XATTR_SD_HASH_TYPE_SHA256 0x1 +#define XATTR_SD_HASH_SIZE 64 + +#define SMB_ACL_READ 4 +#define SMB_ACL_WRITE 2 +#define SMB_ACL_EXECUTE 1 + +enum { + SMB_ACL_TAG_INVALID = 0, + SMB_ACL_USER, + SMB_ACL_USER_OBJ, + SMB_ACL_GROUP, + SMB_ACL_GROUP_OBJ, + SMB_ACL_OTHER, + SMB_ACL_MASK +}; + +struct xattr_acl_entry { + int type; + uid_t uid; + gid_t gid; + mode_t perm; +}; + +struct xattr_smb_acl { + int count; + int next; + struct xattr_acl_entry entries[0]; +}; + +struct xattr_ntacl { + __u16 version; + void *sd_buf; + __u32 sd_size; + __u16 hash_type; + __u8 desc[10]; + __u16 desc_len; + __u64 current_time; + __u8 hash[XATTR_SD_HASH_SIZE]; + __u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; +}; + +/* SECURITY DESCRIPTOR XATTR PREFIX */ +#define SD_PREFIX "NTACL" +#define SD_PREFIX_LEN (sizeof(SD_PREFIX) - 1) +#define XATTR_NAME_SD \ + (XATTR_SECURITY_PREFIX SD_PREFIX) +#define XATTR_NAME_SD_LEN \ + (sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1) + + +/* CreateOptions */ +/* Flag is set, it must not be a file , valid for directory only */ +#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) +#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002) +#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004) + +/* Should not buffer on server*/ +#define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008) +/* MBZ */ +#define FILE_SYNCHRONOUS_IO_ALERT_LE cpu_to_le32(0x00000010) +/* MBZ */ +#define FILE_SYNCHRONOUS_IO_NONALERT_LE cpu_to_le32(0x00000020) + +/* Flaf must not be set for directory */ +#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040) + +/* Should be zero */ +#define CREATE_TREE_CONNECTION cpu_to_le32(0x00000080) +#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100) +#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200) +#define FILE_OPEN_REMOTE_INSTANCE cpu_to_le32(0x00000400) + +/** + * Doc says this is obsolete "open for recovery" flag should be zero + * in any case. + */ +#define CREATE_OPEN_FOR_RECOVERY cpu_to_le32(0x00000400) +#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800) +#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) +#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000) +#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000) +#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000) + +/* Should be zero*/ +#define FILE_OPEN_REQUIRING_OPLOCK cpu_to_le32(0x00010000) +#define FILE_DISALLOW_EXCLUSIVE cpu_to_le32(0x00020000) +#define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000) +#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000) +#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000) + +/* Should be zero */ +#define FILE_OPEN_FOR_FREE_SPACE_QUERY_LE cpu_to_le32(0x00800000) +#define CREATE_OPTIONS_MASK cpu_to_le32(0x00FFFFFF) +#define CREATE_OPTION_READONLY 0x10000000 +/* system. NB not sent over wire */ +#define CREATE_OPTION_SPECIAL 0x20000000 + +struct ksmbd_work; +struct ksmbd_file; +struct ksmbd_conn; + +struct ksmbd_dir_info { + const char *name; + char *wptr; + char *rptr; + int name_len; + int out_buf_len; + int num_entry; + int data_count; + int last_entry_offset; + bool hide_dot_file; + int flags; +}; + +struct ksmbd_readdir_data { + struct dir_context ctx; + union { + void *private; + char *dirent; + }; + + unsigned int used; + unsigned int dirent_count; + unsigned int file_attr; +}; + +/* ksmbd kstat wrapper to get valid create time when reading dir entry */ +struct ksmbd_kstat { + struct kstat *kstat; + unsigned long long create_time; + __le32 file_attributes; +}; + +struct ksmbd_fs_sector_size { + unsigned short logical_sector_size; + unsigned int physical_sector_size; + unsigned int optimal_io_size; +}; + +int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, + bool delete); +int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess); +int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); +int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); +int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, + size_t count, loff_t *pos); +int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, ssize_t *written); +int ksmbd_vfs_fsync(struct ksmbd_work *work, uint64_t fid, uint64_t p_id); +int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); +int ksmbd_vfs_link(struct ksmbd_work *work, + const char *oldname, const char *newname); +int ksmbd_vfs_getattr(struct path *path, struct kstat *stat); +int ksmbd_vfs_symlink(const char *name, const char *symname); +int ksmbd_vfs_readlink(struct path *path, char *buf, int lenp); + +int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, + char *newname); +int ksmbd_vfs_rename_slowpath(struct ksmbd_work *work, + char *oldname, char *newname); + +int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, + struct ksmbd_file *fp, loff_t size); + +struct srv_copychunk; +int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written); + +struct ksmbd_file *ksmbd_vfs_dentry_open(struct ksmbd_work *work, + const struct path *path, + int flags, + __le32 option, + int fexist); + +ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); +ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, + char *xattr_name, + char **xattr_buf); + +ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, + char *attr_name, + int attr_name_len); + +int ksmbd_vfs_setxattr(struct dentry *dentry, + const char *attr_name, + const void *attr_value, + size_t attr_size, + int flags); + +int ksmbd_vfs_fsetxattr(const char *filename, + const char *attr_name, + const void *attr_value, + size_t attr_size, + int flags); + +int ksmbd_vfs_xattr_stream_name(char *stream_name, + char **xattr_stream_name, + size_t *xattr_stream_name_size, + int s_type); + +int ksmbd_vfs_truncate_xattr(struct dentry *dentry, int wo_streams); +int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name); +void ksmbd_vfs_xattr_free(char *xattr); + +int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, + bool caseless); +int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); +void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); +int ksmbd_vfs_lock(struct file *filp, int cmd, struct file_lock *flock); +int ksmbd_vfs_readdir(struct file *file, struct ksmbd_readdir_data *rdata); +int ksmbd_vfs_alloc_size(struct ksmbd_work *work, + struct ksmbd_file *fp, + loff_t len); +int ksmbd_vfs_zero_data(struct ksmbd_work *work, + struct ksmbd_file *fp, + loff_t off, + loff_t len); + +struct file_allocated_range_buffer; +int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + int in_count, int *out_count); +int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry); +unsigned short ksmbd_vfs_logical_sector_size(struct inode *inode); +void ksmbd_vfs_smb2_sector_size(struct inode *inode, + struct ksmbd_fs_sector_size *fs_ss); +void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); + +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat); + +int ksmbd_vfs_posix_lock_wait(struct file_lock *flock); +int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); +void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); + +int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry); +int ksmbd_vfs_remove_sd_xattrs(struct dentry *dentry); +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, + struct smb_ntsd *pntsd, int len); +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, + struct smb_ntsd **pntsd); +int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, + struct xattr_dos_attrib *da); +int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, + struct xattr_dos_attrib *da); +struct posix_acl *ksmbd_vfs_posix_acl_alloc(int count, gfp_t flags); +struct posix_acl *ksmbd_vfs_get_acl(struct inode *inode, int type); +int ksmbd_vfs_set_posix_acl(struct inode *inode, int type, + struct posix_acl *acl); +int ksmbd_vfs_set_init_posix_acl(struct inode *inode); +int ksmbd_vfs_inherit_posix_acl(struct inode *inode, + struct inode *parent_inode); +#endif /* __KSMBD_VFS_H__ */ diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c new file mode 100644 index 000000000000..8d8c4e373308 --- /dev/null +++ b/fs/cifsd/vfs_cache.c @@ -0,0 +1,855 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +/* @FIXME */ +#include "glob.h" +#include "vfs_cache.h" +#include "buffer_pool.h" + +#include "oplock.h" +#include "vfs.h" +#include "connection.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" + +/* @FIXME */ +#include "smb_common.h" + +#define S_DEL_PENDING 1 +#define S_DEL_ON_CLS 2 +#define S_DEL_ON_CLS_STREAM 8 + +static unsigned int inode_hash_mask __read_mostly; +static unsigned int inode_hash_shift __read_mostly; +static struct hlist_head *inode_hashtable __read_mostly; +static DEFINE_RWLOCK(inode_hash_lock); + +static struct ksmbd_file_table global_ft; +static atomic_long_t fd_limit; + +void ksmbd_set_fd_limit(unsigned long limit) +{ + limit = min(limit, get_max_files()); + atomic_long_set(&fd_limit, limit); +} + +static bool fd_limit_depleted(void) +{ + long v = atomic_long_dec_return(&fd_limit); + + if (v >= 0) + return false; + atomic_long_inc(&fd_limit); + return true; +} + +static void fd_limit_close(void) +{ + atomic_long_inc(&fd_limit); +} + +/* + * INODE hash + */ + +static unsigned long inode_hash(struct super_block *sb, unsigned long hashval) +{ + unsigned long tmp; + + tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) / + L1_CACHE_BYTES; + tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> inode_hash_shift); + return tmp & inode_hash_mask; +} + +static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) +{ + struct hlist_head *head = inode_hashtable + + inode_hash(inode->i_sb, inode->i_ino); + struct ksmbd_inode *ci = NULL, *ret_ci = NULL; + + hlist_for_each_entry(ci, head, m_hash) { + if (ci->m_inode == inode) { + if (atomic_inc_not_zero(&ci->m_count)) + ret_ci = ci; + break; + } + } + return ret_ci; +} + +static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) +{ + return __ksmbd_inode_lookup(FP_INODE(fp)); +} + +static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) +{ + struct ksmbd_inode *ci; + + read_lock(&inode_hash_lock); + ci = __ksmbd_inode_lookup(inode); + read_unlock(&inode_hash_lock); + return ci; +} + +int ksmbd_query_inode_status(struct inode *inode) +{ + struct ksmbd_inode *ci; + int ret = KSMBD_INODE_STATUS_UNKNOWN; + + read_lock(&inode_hash_lock); + ci = __ksmbd_inode_lookup(inode); + if (ci) { + ret = KSMBD_INODE_STATUS_OK; + if (ci->m_flags & S_DEL_PENDING) + ret = KSMBD_INODE_STATUS_PENDING_DELETE; + atomic_dec(&ci->m_count); + } + read_unlock(&inode_hash_lock); + return ret; +} + +bool ksmbd_inode_pending_delete(struct ksmbd_file *fp) +{ + return (fp->f_ci->m_flags & S_DEL_PENDING); +} + +void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp) +{ + fp->f_ci->m_flags |= S_DEL_PENDING; +} + +void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) +{ + fp->f_ci->m_flags &= ~S_DEL_PENDING; +} + +void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + int file_info) +{ + if (ksmbd_stream_fd(fp)) { + fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM; + return; + } + + fp->f_ci->m_flags |= S_DEL_ON_CLS; +} + +static void ksmbd_inode_hash(struct ksmbd_inode *ci) +{ + struct hlist_head *b = inode_hashtable + + inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino); + + hlist_add_head(&ci->m_hash, b); +} + +static void ksmbd_inode_unhash(struct ksmbd_inode *ci) +{ + write_lock(&inode_hash_lock); + hlist_del_init(&ci->m_hash); + write_unlock(&inode_hash_lock); +} + +static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) +{ + ci->m_inode = FP_INODE(fp); + atomic_set(&ci->m_count, 1); + atomic_set(&ci->op_count, 0); + atomic_set(&ci->sop_count, 0); + ci->m_flags = 0; + ci->m_fattr = 0; + INIT_LIST_HEAD(&ci->m_fp_list); + INIT_LIST_HEAD(&ci->m_op_list); + rwlock_init(&ci->m_lock); + return 0; +} + +static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp) +{ + struct ksmbd_inode *ci, *tmpci; + int rc; + + read_lock(&inode_hash_lock); + ci = ksmbd_inode_lookup(fp); + read_unlock(&inode_hash_lock); + if (ci) + return ci; + + ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL); + if (!ci) + return NULL; + + rc = ksmbd_inode_init(ci, fp); + if (rc) { + ksmbd_err("inode initialized failed\n"); + kfree(ci); + return NULL; + } + + write_lock(&inode_hash_lock); + tmpci = ksmbd_inode_lookup(fp); + if (!tmpci) { + ksmbd_inode_hash(ci); + } else { + kfree(ci); + ci = tmpci; + } + write_unlock(&inode_hash_lock); + return ci; +} + +static void ksmbd_inode_free(struct ksmbd_inode *ci) +{ + ksmbd_inode_unhash(ci); + kfree(ci); +} + +static void ksmbd_inode_put(struct ksmbd_inode *ci) +{ + if (atomic_dec_and_test(&ci->m_count)) + ksmbd_inode_free(ci); +} + +int __init ksmbd_inode_hash_init(void) +{ + unsigned int loop; + unsigned long numentries = 16384; + unsigned long bucketsize = sizeof(struct hlist_head); + unsigned long size; + + inode_hash_shift = ilog2(numentries); + inode_hash_mask = (1 << inode_hash_shift) - 1; + + size = bucketsize << inode_hash_shift; + + /* init master fp hash table */ + inode_hashtable = vmalloc(size); + if (!inode_hashtable) + return -ENOMEM; + + for (loop = 0; loop < (1U << inode_hash_shift); loop++) + INIT_HLIST_HEAD(&inode_hashtable[loop]); + return 0; +} + +void __exit ksmbd_release_inode_hash(void) +{ + vfree(inode_hashtable); +} + +static void __ksmbd_inode_close(struct ksmbd_file *fp) +{ + struct dentry *dir, *dentry; + struct ksmbd_inode *ci = fp->f_ci; + int err; + struct file *filp; + + filp = fp->filp; + if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { + ci->m_flags &= ~S_DEL_ON_CLS_STREAM; + err = ksmbd_vfs_remove_xattr(filp->f_path.dentry, + fp->stream.name); + if (err) + ksmbd_err("remove xattr failed : %s\n", + fp->stream.name); + } + + if (atomic_dec_and_test(&ci->m_count)) { + write_lock(&ci->m_lock); + if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { + dentry = filp->f_path.dentry; + dir = dentry->d_parent; + ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); + write_unlock(&ci->m_lock); + ksmbd_vfs_unlink(dir, dentry); + write_lock(&ci->m_lock); + } + write_unlock(&ci->m_lock); + + ksmbd_inode_free(ci); + } +} + +static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) +{ + if (!HAS_FILE_ID(fp->persistent_id)) + return; + + write_lock(&global_ft.lock); + idr_remove(global_ft.idr, fp->persistent_id); + write_unlock(&global_ft.lock); +} + +static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, + struct ksmbd_file *fp) +{ + if (!HAS_FILE_ID(fp->volatile_id)) + return; + + write_lock(&fp->f_ci->m_lock); + list_del_init(&fp->node); + write_unlock(&fp->f_ci->m_lock); + + write_lock(&ft->lock); + idr_remove(ft->idr, fp->volatile_id); + write_unlock(&ft->lock); +} + +static void __ksmbd_close_fd(struct ksmbd_file_table *ft, + struct ksmbd_file *fp) +{ + struct file *filp; + + fd_limit_close(); + __ksmbd_remove_durable_fd(fp); + __ksmbd_remove_fd(ft, fp); + + close_id_del_oplock(fp); + filp = fp->filp; + + __ksmbd_inode_close(fp); + if (!IS_ERR_OR_NULL(filp)) + fput(filp); + kfree(fp->filename); + if (ksmbd_stream_fd(fp)) + kfree(fp->stream.name); + ksmbd_free_file_struct(fp); +} + +static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) +{ + if (!atomic_inc_not_zero(&fp->refcount)) + return NULL; + return fp; +} + +static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, + unsigned int id) +{ + bool unclaimed = true; + struct ksmbd_file *fp; + + read_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) + fp = ksmbd_fp_get(fp); + + if (fp && fp->f_ci) { + read_lock(&fp->f_ci->m_lock); + unclaimed = list_empty(&fp->node); + read_unlock(&fp->f_ci->m_lock); + } + read_unlock(&ft->lock); + + if (fp && unclaimed) { + atomic_dec(&fp->refcount); + return NULL; + } + return fp; +} + +static void __put_fd_final(struct ksmbd_work *work, + struct ksmbd_file *fp) +{ + __ksmbd_close_fd(&work->sess->file_table, fp); + atomic_dec(&work->conn->stats.open_files_count); +} + +static void set_close_state_blocked_works(struct ksmbd_file *fp) +{ + struct ksmbd_work *cancel_work, *ctmp; + + spin_lock(&fp->f_lock); + list_for_each_entry_safe(cancel_work, ctmp, &fp->blocked_works, + fp_entry) { + list_del(&cancel_work->fp_entry); + cancel_work->state = KSMBD_WORK_CLOSED; + cancel_work->cancel_fn(cancel_work->cancel_argv); + } + spin_unlock(&fp->f_lock); +} + +int ksmbd_close_fd(struct ksmbd_work *work, unsigned int id) +{ + struct ksmbd_file *fp; + struct ksmbd_file_table *ft; + + if (!HAS_FILE_ID(id)) + return 0; + + ft = &work->sess->file_table; + read_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) { + set_close_state_blocked_works(fp); + + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } + read_unlock(&ft->lock); + + if (!fp) + return -EINVAL; + + __put_fd_final(work, fp); + return 0; +} + +void ksmbd_fd_put(struct ksmbd_work *work, + struct ksmbd_file *fp) +{ + if (!fp) + return; + + if (!atomic_dec_and_test(&fp->refcount)) + return; + __put_fd_final(work, fp); +} + +static bool __sanity_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) +{ + if (!fp) + return false; + if (fp->tcon != tcon) + return false; + return true; +} + +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, + unsigned int id) +{ + return __ksmbd_lookup_fd(&work->sess->file_table, id); +} + +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, + unsigned int id) +{ + struct ksmbd_file *fp = __ksmbd_lookup_fd(&work->sess->file_table, id); + + if (__sanity_check(work->tcon, fp)) + return fp; + + ksmbd_fd_put(work, fp); + return NULL; +} + +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, + unsigned int id, + unsigned int pid) +{ + struct ksmbd_file *fp; + + if (!HAS_FILE_ID(id)) { + id = work->compound_fid; + pid = work->compound_pfid; + } + + if (!HAS_FILE_ID(id)) + return NULL; + + fp = __ksmbd_lookup_fd(&work->sess->file_table, id); + if (!__sanity_check(work->tcon, fp)) { + ksmbd_fd_put(work, fp); + return NULL; + } + if (fp->persistent_id != pid) { + ksmbd_fd_put(work, fp); + return NULL; + } + return fp; +} + +struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) +{ + return __ksmbd_lookup_fd(&global_ft, id); +} + +int ksmbd_close_fd_app_id(struct ksmbd_work *work, + char *app_id) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + if (!memcmp(fp->app_instance_id, + app_id, + SMB2_CREATE_GUID_SIZE)) { + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + break; + } + } + read_unlock(&global_ft.lock); + + if (!fp) + return -EINVAL; + + __put_fd_final(work, fp); + return 0; +} + +struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + if (!memcmp(fp->create_guid, + cguid, + SMB2_CREATE_GUID_SIZE)) { + fp = ksmbd_fp_get(fp); + break; + } + } + read_unlock(&global_ft.lock); + + return fp; +} + +struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, + char *filename) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + read_lock(&work->sess->file_table.lock); + idr_for_each_entry(work->sess->file_table.idr, fp, id) { + if (!strcmp(fp->filename, filename)) { + fp = ksmbd_fp_get(fp); + break; + } + } + read_unlock(&work->sess->file_table.lock); + + return fp; +} + +struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) +{ + struct ksmbd_file *lfp; + struct ksmbd_inode *ci; + struct list_head *cur; + + ci = ksmbd_inode_lookup_by_vfsinode(inode); + if (!ci) + return NULL; + + read_lock(&ci->m_lock); + list_for_each(cur, &ci->m_fp_list) { + lfp = list_entry(cur, struct ksmbd_file, node); + if (inode == FP_INODE(lfp)) { + atomic_dec(&ci->m_count); + read_unlock(&ci->m_lock); + return lfp; + } + } + atomic_dec(&ci->m_count); + read_unlock(&ci->m_lock); + return NULL; +} + +#define OPEN_ID_TYPE_VOLATILE_ID (0) +#define OPEN_ID_TYPE_PERSISTENT_ID (1) + +static void __open_id_set(struct ksmbd_file *fp, unsigned int id, int type) +{ + if (type == OPEN_ID_TYPE_VOLATILE_ID) + fp->volatile_id = id; + if (type == OPEN_ID_TYPE_PERSISTENT_ID) + fp->persistent_id = id; +} + +static int __open_id(struct ksmbd_file_table *ft, + struct ksmbd_file *fp, + int type) +{ + unsigned int id = 0; + int ret; + + if (type == OPEN_ID_TYPE_VOLATILE_ID && fd_limit_depleted()) { + __open_id_set(fp, KSMBD_NO_FID, type); + return -EMFILE; + } + + idr_preload(GFP_KERNEL); + write_lock(&ft->lock); + ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX, GFP_NOWAIT); + if (ret >= 0) { + id = ret; + ret = 0; + } else { + id = KSMBD_NO_FID; + fd_limit_close(); + } + + __open_id_set(fp, id, type); + write_unlock(&ft->lock); + idr_preload_end(); + return ret; +} + +unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp) +{ + __open_id(&global_ft, fp, OPEN_ID_TYPE_PERSISTENT_ID); + return fp->persistent_id; +} + +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, + struct file *filp) +{ + struct ksmbd_file *fp; + int ret; + + fp = ksmbd_alloc_file_struct(); + if (!fp) { + ksmbd_err("Failed to allocate memory\n"); + return ERR_PTR(-ENOMEM); + } + + INIT_LIST_HEAD(&fp->blocked_works); + INIT_LIST_HEAD(&fp->node); + spin_lock_init(&fp->f_lock); + atomic_set(&fp->refcount, 1); + + fp->filp = filp; + fp->conn = work->sess->conn; + fp->tcon = work->tcon; + fp->volatile_id = KSMBD_NO_FID; + fp->persistent_id = KSMBD_NO_FID; + fp->f_ci = ksmbd_inode_get(fp); + + if (!fp->f_ci) { + ksmbd_free_file_struct(fp); + return ERR_PTR(-ENOMEM); + } + + ret = __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); + if (ret) { + ksmbd_inode_put(fp->f_ci); + ksmbd_free_file_struct(fp); + return ERR_PTR(ret); + } + + atomic_inc(&work->conn->stats.open_files_count); + return fp; +} + +static inline bool is_reconnectable(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo = opinfo_get(fp); + bool reconn = false; + + if (!opinfo) + return false; + + if (opinfo->op_state != OPLOCK_STATE_NONE) { + opinfo_put(opinfo); + return false; + } + + if (fp->is_resilient || fp->is_persistent) + reconn = true; + else if (fp->is_durable && opinfo->is_lease && + opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + reconn = true; + + else if (fp->is_durable && opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) + reconn = true; + + opinfo_put(opinfo); + return reconn; +} + +static int +__close_file_table_ids(struct ksmbd_file_table *ft, + struct ksmbd_tree_connect *tcon, + bool (*skip)(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp)) +{ + unsigned int id; + struct ksmbd_file *fp; + int num = 0; + + idr_for_each_entry(ft->idr, fp, id) { + if (skip(tcon, fp)) + continue; + + set_close_state_blocked_works(fp); + + if (!atomic_dec_and_test(&fp->refcount)) + continue; + __ksmbd_close_fd(ft, fp); + num++; + } + return num; +} + +static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) +{ + return fp->tcon != tcon; +} + +static bool session_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) +{ + if (!is_reconnectable(fp)) + return false; + + fp->conn = NULL; + fp->tcon = NULL; + fp->volatile_id = KSMBD_NO_FID; + return true; +} + +void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) +{ + int num = __close_file_table_ids(&work->sess->file_table, + work->tcon, + tree_conn_fd_check); + + atomic_sub(num, &work->conn->stats.open_files_count); +} + +void ksmbd_close_session_fds(struct ksmbd_work *work) +{ + int num = __close_file_table_ids(&work->sess->file_table, + work->tcon, + session_fd_check); + + atomic_sub(num, &work->conn->stats.open_files_count); +} + +int ksmbd_init_global_file_table(void) +{ + return ksmbd_init_file_table(&global_ft); +} + +void ksmbd_free_global_file_table(void) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + idr_for_each_entry(global_ft.idr, fp, id) { + __ksmbd_remove_durable_fd(fp); + ksmbd_free_file_struct(fp); + } + + ksmbd_destroy_file_table(&global_ft); +} + +int ksmbd_reopen_durable_fd(struct ksmbd_work *work, + struct ksmbd_file *fp) +{ + if (!fp->is_durable || fp->conn || fp->tcon) { + ksmbd_err("Invalid durable fd [%p:%p]\n", + fp->conn, fp->tcon); + return -EBADF; + } + + if (HAS_FILE_ID(fp->volatile_id)) { + ksmbd_err("Still in use durable fd: %u\n", fp->volatile_id); + return -EBADF; + } + + fp->conn = work->sess->conn; + fp->tcon = work->tcon; + + __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); + if (!HAS_FILE_ID(fp->volatile_id)) { + fp->conn = NULL; + fp->tcon = NULL; + return -EBADF; + } + return 0; +} + +static void close_fd_list(struct ksmbd_work *work, struct list_head *head) +{ + while (!list_empty(head)) { + struct ksmbd_file *fp; + + fp = list_first_entry(head, struct ksmbd_file, node); + list_del_init(&fp->node); + + __ksmbd_close_fd(&work->sess->file_table, fp); + } +} + +int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode) +{ + struct ksmbd_inode *ci; + bool unlinked = true; + struct ksmbd_file *fp, *fptmp; + LIST_HEAD(dispose); + + ci = ksmbd_inode_lookup_by_vfsinode(inode); + if (!ci) + return true; + + if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) + unlinked = false; + + write_lock(&ci->m_lock); + list_for_each_entry_safe(fp, fptmp, &ci->m_fp_list, node) { + if (fp->conn) + continue; + + list_del(&fp->node); + list_add(&fp->node, &dispose); + } + atomic_dec(&ci->m_count); + write_unlock(&ci->m_lock); + + close_fd_list(work, &dispose); + return unlinked; +} + +int ksmbd_file_table_flush(struct ksmbd_work *work) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + int ret; + + read_lock(&work->sess->file_table.lock); + idr_for_each_entry(work->sess->file_table.idr, fp, id) { + ret = ksmbd_vfs_fsync(work, fp->volatile_id, KSMBD_NO_FID); + if (ret) + break; + } + read_unlock(&work->sess->file_table.lock); + return ret; +} + +int ksmbd_init_file_table(struct ksmbd_file_table *ft) +{ + ft->idr = ksmbd_alloc(sizeof(struct idr)); + if (!ft->idr) + return -ENOMEM; + + idr_init(ft->idr); + rwlock_init(&ft->lock); + return 0; +} + +void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) +{ + if (!ft->idr) + return; + + __close_file_table_ids(ft, NULL, session_fd_check); + idr_destroy(ft->idr); + ksmbd_free(ft->idr); + ft->idr = NULL; +} diff --git a/fs/cifsd/vfs_cache.h b/fs/cifsd/vfs_cache.h new file mode 100644 index 000000000000..7d23657c86c6 --- /dev/null +++ b/fs/cifsd/vfs_cache.h @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __VFS_CACHE_H__ +#define __VFS_CACHE_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include "vfs.h" + +/* Windows style file permissions for extended response */ +#define FILE_GENERIC_ALL 0x1F01FF +#define FILE_GENERIC_READ 0x120089 +#define FILE_GENERIC_WRITE 0x120116 +#define FILE_GENERIC_EXECUTE 0X1200a0 + +#define KSMBD_START_FID 0 +#define KSMBD_NO_FID (UINT_MAX) +#define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) + +#define FP_FILENAME(fp) fp->filp->f_path.dentry->d_name.name +#define FP_INODE(fp) fp->filp->f_path.dentry->d_inode +#define PARENT_INODE(fp) fp->filp->f_path.dentry->d_parent->d_inode + +#define ATTR_FP(fp) (fp->attrib_only && \ + (fp->cdoption != FILE_OVERWRITE_IF_LE && \ + fp->cdoption != FILE_OVERWRITE_LE && \ + fp->cdoption != FILE_SUPERSEDE_LE)) + +struct ksmbd_conn; +struct ksmbd_session; + +struct ksmbd_lock { + struct file_lock *fl; + struct list_head glist; + struct list_head llist; + unsigned int flags; + int cmd; + int zero_len; + unsigned long long start; + unsigned long long end; +}; + +struct stream { + char *name; + ssize_t size; +}; + +struct ksmbd_inode { + rwlock_t m_lock; + atomic_t m_count; + atomic_t op_count; + /* opinfo count for streams */ + atomic_t sop_count; + struct inode *m_inode; + unsigned int m_flags; + struct hlist_node m_hash; + struct list_head m_fp_list; + struct list_head m_op_list; + struct oplock_info *m_opinfo; + __le32 m_fattr; +}; + +struct ksmbd_file { + struct file *filp; + char *filename; + unsigned int persistent_id; + unsigned int volatile_id; + + spinlock_t f_lock; + + struct ksmbd_inode *f_ci; + struct ksmbd_inode *f_parent_ci; + struct oplock_info __rcu *f_opinfo; + struct ksmbd_conn *conn; + struct ksmbd_tree_connect *tcon; + + atomic_t refcount; + __le32 daccess; + __le32 saccess; + __le32 coption; + __le32 cdoption; + __u64 create_time; + __u64 itime; + + bool is_durable; + bool is_resilient; + bool is_persistent; + bool is_nt_open; + bool attrib_only; + + char client_guid[16]; + char create_guid[16]; + char app_instance_id[16]; + + struct stream stream; + struct list_head node; + struct list_head blocked_works; + + int durable_timeout; + + /* for SMB1 */ + int pid; + + /* conflict lock fail count for SMB1 */ + unsigned int cflock_cnt; + /* last lock failure start offset for SMB1 */ + unsigned long long llock_fstart; + + int dirent_offset; + + /* if ls is happening on directory, below is valid*/ + struct ksmbd_readdir_data readdir_data; + int dot_dotdot[2]; +}; + +static inline void set_ctx_actor(struct dir_context *ctx, + filldir_t actor) +{ + ctx->actor = actor; +} + +#define KSMBD_NR_OPEN_DEFAULT BITS_PER_LONG + +struct ksmbd_file_table { + rwlock_t lock; + struct idr *idr; +}; + +static inline bool HAS_FILE_ID(unsigned long long req) +{ + unsigned int id = (unsigned int)req; + + return id < KSMBD_NO_FID; +} + +static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) +{ + return fp->stream.name != NULL; +} + +int ksmbd_init_file_table(struct ksmbd_file_table *ft); +void ksmbd_destroy_file_table(struct ksmbd_file_table *ft); + +int ksmbd_close_fd(struct ksmbd_work *work, unsigned int id); + +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, + unsigned int id); +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, + unsigned int id); +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, + unsigned int id, + unsigned int pid); + +void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); + +int ksmbd_close_fd_app_id(struct ksmbd_work *work, char *app_id); +struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); +struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); +struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, + char *filename); +struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); + +unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); + +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, + struct file *filp); + +void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); +void ksmbd_close_session_fds(struct ksmbd_work *work); + +int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); + +int ksmbd_reopen_durable_fd(struct ksmbd_work *work, + struct ksmbd_file *fp); + +int ksmbd_init_global_file_table(void); +void ksmbd_free_global_file_table(void); + +int ksmbd_file_table_flush(struct ksmbd_work *work); + +void ksmbd_set_fd_limit(unsigned long limit); + +/* + * INODE hash + */ + +int __init ksmbd_inode_hash_init(void); +void __exit ksmbd_release_inode_hash(void); + +enum KSMBD_INODE_STATUS { + KSMBD_INODE_STATUS_OK, + KSMBD_INODE_STATUS_UNKNOWN, + KSMBD_INODE_STATUS_PENDING_DELETE, +}; + +int ksmbd_query_inode_status(struct inode *inode); + +bool ksmbd_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); + +void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + int file_info); +#endif /* __VFS_CACHE_H__ */ From a848c4f15ab6d5d405dbee7de5da71839b2bf35e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 16 Mar 2021 10:51:34 +0900 Subject: [PATCH 004/417] cifsd: add Kconfig and Makefile This adds the Kconfig and Makefile for cifsd. Signed-off-by: Namjae Jeon Signed-off-by: Sergey Senozhatsky Signed-off-by: Hyunchul Lee Acked-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/Kconfig | 1 + fs/Makefile | 1 + fs/cifsd/Kconfig | 64 +++++++++++++++++++++++++++++++++++++++++++++++ fs/cifsd/Makefile | 13 ++++++++++ 4 files changed, 79 insertions(+) create mode 100644 fs/cifsd/Kconfig create mode 100644 fs/cifsd/Makefile diff --git a/fs/Kconfig b/fs/Kconfig index 141a856c50e7..7462761ebd2f 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -344,6 +344,7 @@ config NFS_V4_2_SSC_HELPER source "net/sunrpc/Kconfig" source "fs/ceph/Kconfig" source "fs/cifs/Kconfig" +source "fs/cifsd/Kconfig" source "fs/coda/Kconfig" source "fs/afs/Kconfig" source "fs/9p/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 9c708e1fbe8f..542a77374d12 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -98,6 +98,7 @@ obj-$(CONFIG_NLS) += nls/ obj-$(CONFIG_UNICODE) += unicode/ obj-$(CONFIG_SYSV_FS) += sysv/ obj-$(CONFIG_CIFS) += cifs/ +obj-$(CONFIG_SMB_SERVER) += cifsd/ obj-$(CONFIG_HPFS_FS) += hpfs/ obj-$(CONFIG_NTFS_FS) += ntfs/ obj-$(CONFIG_UFS_FS) += ufs/ diff --git a/fs/cifsd/Kconfig b/fs/cifsd/Kconfig new file mode 100644 index 000000000000..6e78960be5b1 --- /dev/null +++ b/fs/cifsd/Kconfig @@ -0,0 +1,64 @@ +config SMB_SERVER + tristate "SMB server support (EXPERIMENTAL)" + depends on INET + select NLS + select NLS_UTF8 + select CRYPTO + select CRYPTO_MD4 + select CRYPTO_MD5 + select CRYPTO_HMAC + select CRYPTO_ARC4 + select CRYPTO_ECB + select CRYPTO_LIB_DES + select CRYPTO_SHA256 + select CRYPTO_CMAC + select CRYPTO_SHA512 + select CRYPTO_AEAD2 + select CRYPTO_CCM + select CRYPTO_GCM + default n + help + Choose Y here if you want to allow SMB3 compliant clients + to access files residing on this system using SMB3 protocol. + To compile the SMB3 server support as a module, + choose M here: the module will be called ksmbd. + + You may choose to use a samba server instead, in which + case you can choose N here. + + You also need to install user space programs which can be found + in cifsd-tools, available from + https://github.com/cifsd-team/cifsd-tools. + More detail about how to run the cifsd kernel server is + available via README file + (https://github.com/cifsd-team/cifsd-tools/blob/master/README). + + cifsd kernel server includes support for auto-negotiation, + Secure negotiate, Pre-authentication integrity, oplock/lease, + compound requests, multi-credit, packet signing, RDMA(smbdirect), + smb3 encryption, copy-offload, secure per-user session + establishment via NTLM or NTLMv2. + +config SMB_SERVER_SMBDIRECT + bool "Support for SMB Direct protocol" + depends on SMB_SERVER=m && INFINIBAND && INFINIBAND_ADDR_TRANS || SMB_SERVER=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y + default n + + help + Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. + + SMB Direct allows transferring SMB packets over RDMA. If unsure, + say N. + +config SMB_SERVER_CHECK_CAP_NET_ADMIN + bool "Enable check network administration capability" + depends on SMB_SERVER + default y + + help + Prevent unprivileged processes to start the cifsd kernel server. + +config SMB_SERVER_KERBEROS5 + bool "Support for Kerberos 5" + depends on SMB_SERVER + default n diff --git a/fs/cifsd/Makefile b/fs/cifsd/Makefile new file mode 100644 index 000000000000..a6c03c4ba51e --- /dev/null +++ b/fs/cifsd/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile for Linux SMB3 kernel server +# +obj-$(CONFIG_SMB_SERVER) += ksmbd.o + +ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o buffer_pool.o \ + misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \ + mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ + mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ + transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ + smb2ops.o smb2misc.o asn1.o netmisc.o ndr.o +ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o From 3a3fd9d4939f8e1ee9b082e7f0066f41d434aa16 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 16 Mar 2021 10:53:11 +0900 Subject: [PATCH 005/417] MAINTAINERS: add cifsd kernel server Add myself, Steve French, Sergey Senozhatsky and Hyunchul Lee as cifsd maintainer. Signed-off-by: Namjae Jeon Signed-off-by: Sergey Senozhatsky Signed-off-by: Hyunchul Lee Acked-by: Ronnie Sahlberg Signed-off-by: Steve French --- MAINTAINERS | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index bd7aff0c120f..f23bb5cbfd70 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4530,7 +4530,7 @@ F: include/linux/clk/ F: include/linux/of_clk.h X: drivers/clk/clkdev.c -COMMON INTERNET FILE SYSTEM (CIFS) +COMMON INTERNET FILE SYSTEM CLIENT (CIFS) M: Steve French L: linux-cifs@vger.kernel.org L: samba-technical@lists.samba.org (moderated for non-subscribers) @@ -4540,6 +4540,16 @@ T: git git://git.samba.org/sfrench/cifs-2.6.git F: Documentation/admin-guide/cifs/ F: fs/cifs/ +COMMON INTERNET FILE SYSTEM SERVER (CIFSD) +M: Namjae Jeon +M: Sergey Senozhatsky +M: Steve French +M: Hyunchul Lee +L: linux-cifs@vger.kernel.org +L: linux-cifsd-devel@lists.sourceforge.net +S: Maintained +F: fs/cifsd/ + COMPACTPCI HOTPLUG CORE M: Scott Murray L: linux-pci@vger.kernel.org From c0e8110e6c75758c4567f8e713f26e5dbd88cc7c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 17 Mar 2021 16:52:17 +0900 Subject: [PATCH 006/417] cifsd: fix WARNING: Title overline too short Stephen reported a warning message from cifsd.rst file. Documentation/filesystems/cifs/cifsd.rst:3: WARNING: Title overline too short. Reported-by: Stephen Rothwell Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- Documentation/filesystems/cifs/cifsd.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/filesystems/cifs/cifsd.rst b/Documentation/filesystems/cifs/cifsd.rst index e0c33d03f290..af3589da6923 100644 --- a/Documentation/filesystems/cifs/cifsd.rst +++ b/Documentation/filesystems/cifs/cifsd.rst @@ -1,8 +1,8 @@ .. SPDX-License-Identifier: GPL-2.0 -========================= +========================== CIFSD - SMB3 Kernel Server -========================= +========================== CIFSD is a linux kernel server which implements SMB3 protocol in kernel space for sharing files over network. From 42da4086b987fbb35562e93e534e57ad3f81f855 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 17 Mar 2021 16:55:28 +0900 Subject: [PATCH 007/417] cifsd: fix WARNING: document isn't included in any toctree Stephen reported a warning message from cifsd.rst file. Documentation/filesystems/cifs/cifsd.rst: WARNING: document isn't included in any toctree Reported-by: Stephen Rothwell Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- Documentation/filesystems/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystems/index.rst index d4853cb919d2..ac9396f2bb8a 100644 --- a/Documentation/filesystems/index.rst +++ b/Documentation/filesystems/index.rst @@ -72,6 +72,7 @@ Documentation for filesystem implementations. befs bfs btrfs + cifs/cifsd cifs/cifsroot ceph coda From 36ba38663be0a1b34aee1c79f3bb359fcac96c55 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 17 Mar 2021 17:01:15 +0900 Subject: [PATCH 008/417] cifsd: uniquify extract_sharename() uniquify extract_sharename(). Signed-off-by: Stephen Rothwell Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/misc.c | 4 ++-- fs/cifsd/misc.h | 2 +- fs/cifsd/smb2pdu.c | 2 +- fs/cifsd/unicode.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/cifsd/misc.c b/fs/cifsd/misc.c index 9e689c33f7bb..68983b08d519 100644 --- a/fs/cifsd/misc.c +++ b/fs/cifsd/misc.c @@ -210,12 +210,12 @@ void ksmbd_conv_path_to_windows(char *path) } /** - * extract_sharename() - get share name from tree connect request + * ksmbd_extract_sharename() - get share name from tree connect request * @treename: buffer containing tree name and share name * * Return: share name on success, otherwise error */ -char *extract_sharename(char *treename) +char *ksmbd_extract_sharename(char *treename) { char *name = treename; char *dst; diff --git a/fs/cifsd/misc.h b/fs/cifsd/misc.h index d67843aad509..41b3dac2f5fc 100644 --- a/fs/cifsd/misc.h +++ b/fs/cifsd/misc.h @@ -25,7 +25,7 @@ void ksmbd_conv_path_to_unix(char *path); void ksmbd_strip_last_slash(char *path); void ksmbd_conv_path_to_windows(char *path); -char *extract_sharename(char *treename); +char *ksmbd_extract_sharename(char *treename); char *convert_to_unix_name(struct ksmbd_share_config *share, char *name); diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index b20cc07ee809..a4e78ebb0773 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -1709,7 +1709,7 @@ int smb2_tree_connect(struct ksmbd_work *work) goto out_err1; } - name = extract_sharename(treename); + name = ksmbd_extract_sharename(treename); if (IS_ERR(name)) { status.ret = KSMBD_TREE_CONN_STATUS_ERROR; goto out_err1; diff --git a/fs/cifsd/unicode.h b/fs/cifsd/unicode.h index 228a02c9b95d..7135d62bf9b0 100644 --- a/fs/cifsd/unicode.h +++ b/fs/cifsd/unicode.h @@ -69,7 +69,7 @@ char *smb_strndup_from_utf16(const char *src, const int maxlen, const struct nls_table *codepage); extern int smbConvertToUTF16(__le16 *target, const char *source, int srclen, const struct nls_table *cp, int mapchars); -extern char *extract_sharename(char *treename); +extern char *ksmbd_extract_sharename(char *treename); #endif wchar_t cifs_toupper(wchar_t in); From 1e853b937b2fcc51ff3939c7ae657d0726681ca1 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 17 Mar 2021 09:36:58 +0000 Subject: [PATCH 009/417] cifsd: Fix a handful of spelling mistakes There are several spelling mistakes in various ksmbd_err and ksmbd_debug messages. Fix these. Signed-off-by: Colin Ian King Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/ndr.c | 2 +- fs/cifsd/smb2pdu.c | 4 ++-- fs/cifsd/transport_rdma.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/cifsd/ndr.c b/fs/cifsd/ndr.c index d96dcd9e43c6..aa0cb8fc555d 100644 --- a/fs/cifsd/ndr.c +++ b/fs/cifsd/ndr.c @@ -325,7 +325,7 @@ int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) ndr_read_bytes(n, acl->desc, 10); if (strncmp(acl->desc, "posix_acl", 9)) { - ksmbd_err("Invalid acl desciption : %s\n", acl->desc); + ksmbd_err("Invalid acl description : %s\n", acl->desc); return -EINVAL; } diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index a4e78ebb0773..730bddbc8152 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -2508,7 +2508,7 @@ int smb2_open(struct ksmbd_work *work) if (req->NameLength) { if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && *(char *)req->Buffer == '\\') { - ksmbd_err("not allow directory name included leadning slash\n"); + ksmbd_err("not allow directory name included leading slash\n"); rc = -EINVAL; goto err_out1; } @@ -2636,7 +2636,7 @@ int smb2_open(struct ksmbd_work *work) } if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { - ksmbd_err("Invalid disired access : 0x%x\n", + ksmbd_err("Invalid desired access : 0x%x\n", le32_to_cpu(req->DesiredAccess)); rc = -EACCES; goto err_out1; diff --git a/fs/cifsd/transport_rdma.c b/fs/cifsd/transport_rdma.c index 1698f7ed9c2f..4f4806d67ab0 100644 --- a/fs/cifsd/transport_rdma.c +++ b/fs/cifsd/transport_rdma.c @@ -485,7 +485,7 @@ static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) struct smb2_hdr *hdr = (struct smb2_hdr *) (recvmsg->packet + le32_to_cpu(req->data_offset) - 4); ksmbd_debug(RDMA, - "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemaingDataLength: %u, SMB: %x, Command: %u\n", + "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", le16_to_cpu(req->credits_granted), le16_to_cpu(req->credits_requested), req->data_length, req->remaining_data_length, From e3f70873289ae84d42fe9cd3f01f99ae7a2b1f09 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 19 Mar 2021 09:44:31 +0900 Subject: [PATCH 010/417] cifsd: fix WARNING: unmet direct dependencies detected for CRYPTO_ARC4 Randy reported warning message from fs/cifsd/Kconfig. WARNING: unmet direct dependencies detected for CRYPTO_ARC4 Depends on [n]: CRYPTO [=y] && CRYPTO_USER_API_ENABLE_OBSOLETE [=n] Selected by [y]: - SMB_SERVER [=y] && NETWORK_FILESYSTEMS [=y] && INET [=y] arc4 library is not currently in use. So this patch eliminates unnecessary library set in cifsd. Reported-by: Randy Dunlap Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Kconfig | 1 - fs/cifsd/server.c | 1 - 2 files changed, 2 deletions(-) diff --git a/fs/cifsd/Kconfig b/fs/cifsd/Kconfig index 6e78960be5b1..8c5dd9a44401 100644 --- a/fs/cifsd/Kconfig +++ b/fs/cifsd/Kconfig @@ -7,7 +7,6 @@ config SMB_SERVER select CRYPTO_MD4 select CRYPTO_MD5 select CRYPTO_HMAC - select CRYPTO_ARC4 select CRYPTO_ECB select CRYPTO_LIB_DES select CRYPTO_SHA256 diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c index b9e114f8a5d2..a70d311a29dc 100644 --- a/fs/cifsd/server.c +++ b/fs/cifsd/server.c @@ -618,7 +618,6 @@ MODULE_AUTHOR("Namjae Jeon "); MODULE_VERSION(KSMBD_VERSION); MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); MODULE_LICENSE("GPL"); -MODULE_SOFTDEP("pre: arc4"); MODULE_SOFTDEP("pre: ecb"); MODULE_SOFTDEP("pre: hmac"); MODULE_SOFTDEP("pre: md4"); From 86df49e105afa6a205abb7d90809c3c76136eaa9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 18 Mar 2021 16:10:21 +0300 Subject: [PATCH 011/417] cifsd: fix a precedence bug in parse_dacl() The shift has higher precedence than mask so this doesn't work as intended. Signed-off-by: Dan Carpenter Reviewed-by: Sergey Senozhatsky Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smbacl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/smbacl.c b/fs/cifsd/smbacl.c index 8d8360ca4751..294c5a8fe9af 100644 --- a/fs/cifsd/smbacl.c +++ b/fs/cifsd/smbacl.c @@ -520,7 +520,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, fattr->cf_gid; acl_state.groups->aces[acl_state.groups->n++].perms.allow = (mode & 0070) >> 3; - default_acl_state.group.allow = mode & 0070 >> 3; + default_acl_state.group.allow = (mode & 0070) >> 3; default_acl_state.groups->aces[default_acl_state.groups->n].gid = fattr->cf_gid; default_acl_state.groups->aces[default_acl_state.groups->n++].perms.allow = From 8ef32967065737dac51974efae333436354bea0a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 18 Mar 2021 16:09:37 +0300 Subject: [PATCH 012/417] cifsd: fix a IS_ERR() vs NULL bug The smb_direct_alloc_sendmsg() function never returns NULL, it only returns error pointers so the check needs to be updated. Signed-off-by: Dan Carpenter Reviewed-by: Sergey Senozhatsky Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/transport_rdma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifsd/transport_rdma.c b/fs/cifsd/transport_rdma.c index 4f4806d67ab0..d235051dc5b1 100644 --- a/fs/cifsd/transport_rdma.c +++ b/fs/cifsd/transport_rdma.c @@ -997,8 +997,8 @@ static int smb_direct_create_header(struct smb_direct_transport *t, int ret; sendmsg = smb_direct_alloc_sendmsg(t); - if (!sendmsg) - return -ENOMEM; + if (IS_ERR(sendmsg)) + return PTR_ERR(sendmsg); /* Fill in the packet header */ packet = (struct smb_direct_data_transfer *)sendmsg->packet; From a2ba2709f5e465b316ef1f18605190d249847aad Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 18 Mar 2021 16:12:54 +0300 Subject: [PATCH 013/417] cifsd: Fix a use after free on error path The ksmbd_free_work_struct() frees "work" so we need to swap the order of these two function calls to avoid a use after free. Signed-off-by: Dan Carpenter Reviewed-by: Sergey Senozhatsky Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/oplock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index 6c3dbc71134e..f694c14be0df 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -638,8 +638,8 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) if (allocate_oplock_break_buf(work)) { ksmbd_err("smb2_allocate_rsp_buf failed! "); atomic_dec(&conn->r_count); - ksmbd_free_work_struct(work); ksmbd_fd_put(work, fp); + ksmbd_free_work_struct(work); return; } From bc3fcc9462ef4ba3ae66593cbaf47bf7af703ed3 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 19 Mar 2021 13:51:15 +0900 Subject: [PATCH 014/417] cifsd: fix static checker warning from smb_direct_post_send_data() Dan reported static checker warning: fs/cifsd/transport_rdma.c:1168 smb_direct_post_send_data() warn: missing error code 'ret' This patch add missing ret error code. Reported-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/transport_rdma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifsd/transport_rdma.c b/fs/cifsd/transport_rdma.c index d235051dc5b1..45b76847f1e7 100644 --- a/fs/cifsd/transport_rdma.c +++ b/fs/cifsd/transport_rdma.c @@ -1165,6 +1165,7 @@ static int smb_direct_post_send_data(struct smb_direct_transport *t, sg, SMB_DIRECT_MAX_SEND_SGES-1, DMA_TO_DEVICE); if (sg_cnt <= 0) { ksmbd_err("failed to map buffer\n"); + ret = -ENOMEM; goto err; } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES-1) { ksmbd_err("buffer not fitted into sges\n"); From 50355b0b20103a2be39e269a92909fa69f16f2d0 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 19 Mar 2021 13:52:12 +0900 Subject: [PATCH 015/417] cifsd: fix static checker warning from smb_check_perm_dacl() Dan reported static checker warning: fs/cifsd/smbacl.c:1140 smb_check_perm_dacl() error: we previously assumed 'pntsd' could be null (see line 1137) This patch validate bounds of pntsd buffer. Reported-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smbacl.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/fs/cifsd/smbacl.c b/fs/cifsd/smbacl.c index 294c5a8fe9af..77c79cf4afd0 100644 --- a/fs/cifsd/smbacl.c +++ b/fs/cifsd/smbacl.c @@ -800,9 +800,13 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, le32_to_cpu(pntsd->gsidoffset), le32_to_cpu(pntsd->sacloffset), dacloffset); - if (dacloffset && dacl_ptr) + if (dacloffset) { + if (end_of_acl <= (char *)dacl_ptr || + end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) + return -EIO; total_ace_size = le16_to_cpu(dacl_ptr->size) - sizeof(struct smb_acl); + } pntsd_type = le16_to_cpu(pntsd->type); @@ -1131,13 +1135,28 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, struct smb_ace *others_ace = NULL; struct posix_acl_entry *pa_entry; unsigned int sid_type = SIDOWNER; + char *end_of_acl; ksmbd_debug(SMB, "check permission using windows acl\n"); acl_size = ksmbd_vfs_get_sd_xattr(conn, dentry, &pntsd); - if (acl_size <= 0 || (pntsd && !pntsd->dacloffset)) + if (acl_size <= 0 || !pntsd || !pntsd->dacloffset) { + kfree(pntsd); return 0; + } pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + end_of_acl = ((char *)pntsd) + acl_size; + if (end_of_acl <= (char *)pdacl) { + kfree(pntsd); + return 0; + } + + if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size) || + le16_to_cpu(pdacl->size) < sizeof(struct smb_acl)) { + kfree(pntsd); + return 0; + } + if (!pdacl->num_aces) { if (!(le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) && *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) { @@ -1156,6 +1175,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { granted |= le32_to_cpu(ace->access_req); ace = (struct smb_ace *) ((char *)ace + le16_to_cpu(ace->size)); + if (end_of_acl < (char *)ace) + goto err_out; } if (!pdacl->num_aces) @@ -1177,6 +1198,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, others_ace = ace; ace = (struct smb_ace *) ((char *)ace + le16_to_cpu(ace->size)); + if (end_of_acl < (char *)ace) + goto err_out; } if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) { From 04bee6e336be1accb7f28d8e86454f42b58a860f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 20 Mar 2021 16:06:59 +0900 Subject: [PATCH 016/417] cifsd: update cifsd.rst document Add work flow of cifsd and feature stats table. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- Documentation/filesystems/cifs/cifsd.rst | 92 +++++++++++++++++------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/Documentation/filesystems/cifs/cifsd.rst b/Documentation/filesystems/cifs/cifsd.rst index af3589da6923..7eac7e459c2d 100644 --- a/Documentation/filesystems/cifs/cifsd.rst +++ b/Documentation/filesystems/cifs/cifsd.rst @@ -10,6 +10,34 @@ for sharing files over network. CIFSD architecture ================== + |--- ... + --------|--- ksmbd/3 - Client 3 + |-------|--- ksmbd/2 - Client 2 + | | ____________________________________________________ + | | |- Client 1 | +<--- Socket ---|--- ksmbd/1 <<= Authentication : NTLM/NTLM2, Kerberos | + | | | | <<= SMB engine : SMB2, SMB2.1, SMB3, SMB3.0.2, | + | | | | SMB3.1.1 | + | | | |____________________________________________________| + | | | + | | |--- VFS --- Local Filesystem + | | +KERNEL |--- ksmbd/0(forker kthread) +---------------||--------------------------------------------------------------- +USER || + || communication using NETLINK + || ______________________________________________ + || | | + ksmbd.mountd <<= DCE/RPC(srvsvc, wkssvc, smar, lsarpc) | + ^ | <<= configure shares setting, user accounts | + | |______________________________________________| + | + |------ smb.conf(config file) + | + |------ ksmbdpwd.db(user account/password file) + ^ + ksmbd.adduser ---------------| + The subset of performance related operations belong in kernelspace and the other subset which belong to operations which are not really related with performance in userspace. So, DCE/RPC management that has historically resulted @@ -59,32 +87,48 @@ dozen) that are most important for file server from NetShareEnum and NetServerGetInfo. Complete DCE/RPC response is prepared from the user space and passed over to the associated kernel thread for the client. -Key Features -============ -The supported features are: - * SMB3 protocols for basic file sharing - * Auto negotiation - * Compound requests - * Oplock/Lease - * Large MTU - * NTLM/NTLMv2 - * HMAC-SHA256 Signing - * Secure negotiate - * Signing Update - * Pre-authentication integrity(SMB 3.1.1) - * SMB3 encryption(CCM, GCM) - * SMB direct(RDMA) - * SMB3.1.1 POSIX extension support - * ACLs - * Kerberos +CIFSD Feature Status +==================== + +============================== ================================================= +Feature name Status +============================== ================================================= +Dialects Supported. SMB2.1 SMB3.0, SMB3.1.1 dialects + excluding security vulnerable SMB1. +Auto Negotiation Supported. +Compound Request Supported. +Oplock Cache Mechanism Supported. +SMB2 leases(v1 lease) Supported. +Directory leases(v2 lease) Planned for future. +Multi-credits Supported. +NTLM/NTLMv2 Supported. +HMAC-SHA256 Signing Supported. +Secure negotiate Supported. +Signing Update Supported. +Pre-authentication integrity Supported. +SMB3 encryption(CCM, GCM) Supported. +SMB direct(RDMA) Partial Supported. SMB3 Multi-channel is required + to connect to Windows client. +SMB3 Multi-channel In Progress. +SMB3.1.1 POSIX extension Supported. +ACLs Partial Supported. only DACLs available, SACLs is + planned for future. ksmbd generate random subauth + values(then store it to disk) and use uid/gid + get from inode as RID for local domain SID. + The current acl implementation is limited to + standalone server, not a domain member. +Kerberos Supported. +Durable handle v1,v2 Planned for future. +Persistent handle Planned for future. +SMB2 notify Planned for future. +Sparse file support Supported. +DCE/RPC support Partial Supported. a few calls(NetShareEnumAll, + NetServerGetInfo, SAMR, LSARPC) that needed as + file server via netlink interface from + ksmbd.mountd. +============================== ================================================= -The features that are planned or not supported: - * SMB3 Multi-channel - * Durable handle v1,v2 - * Persistent handles - * Directory lease - * SMB2 notify How to run ========== From 04165366515a2ba36c78540da776d3a12164f824 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 20 Mar 2021 16:19:01 +0900 Subject: [PATCH 017/417] cifsd: add index.rst in cifs documentation Since more than one file is in the cifs document directory, This patch add an index.rst. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- Documentation/filesystems/cifs/index.rst | 10 ++++++++++ Documentation/filesystems/index.rst | 3 +-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 Documentation/filesystems/cifs/index.rst diff --git a/Documentation/filesystems/cifs/index.rst b/Documentation/filesystems/cifs/index.rst new file mode 100644 index 000000000000..e762586b5dc7 --- /dev/null +++ b/Documentation/filesystems/cifs/index.rst @@ -0,0 +1,10 @@ +=============================== +CIFS +=============================== + + +.. toctree:: + :maxdepth: 1 + + cifsd + cifsroot diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystems/index.rst index ac9396f2bb8a..bdba80ae2bb1 100644 --- a/Documentation/filesystems/index.rst +++ b/Documentation/filesystems/index.rst @@ -72,8 +72,7 @@ Documentation for filesystem implementations. befs bfs btrfs - cifs/cifsd - cifs/cifsroot + cifs/index ceph coda configfs From 2e2b0dda188993c86490cca02892a9a6e1449f5d Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Sat, 20 Mar 2021 16:23:22 +0900 Subject: [PATCH 018/417] cifsd: remove unneeded FIXME comments Remove unneeded FIXME comments. Signed-off-by: Sergey Senozhatsky Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/mgmt/share_config.c | 1 - fs/cifsd/mgmt/share_config.h | 2 -- fs/cifsd/mgmt/tree_connect.c | 1 - fs/cifsd/mgmt/tree_connect.h | 2 +- fs/cifsd/mgmt/user_config.c | 1 - fs/cifsd/mgmt/user_config.h | 3 +-- fs/cifsd/mgmt/user_session.c | 1 - fs/cifsd/server.c | 2 -- fs/cifsd/smb_common.c | 1 - fs/cifsd/transport_ipc.c | 3 --- fs/cifsd/transport_ipc.h | 1 - fs/cifsd/vfs_cache.c | 4 ---- 12 files changed, 2 insertions(+), 20 deletions(-) diff --git a/fs/cifsd/mgmt/share_config.c b/fs/cifsd/mgmt/share_config.c index 0593702babfe..9bc7f7555ee2 100644 --- a/fs/cifsd/mgmt/share_config.c +++ b/fs/cifsd/mgmt/share_config.c @@ -16,7 +16,6 @@ #include "user_session.h" #include "../buffer_pool.h" #include "../transport_ipc.h" -#include "../ksmbd_server.h" /* FIXME */ #define SHARE_HASH_BITS 3 static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); diff --git a/fs/cifsd/mgmt/share_config.h b/fs/cifsd/mgmt/share_config.h index c47b874bd80b..49ca89667991 100644 --- a/fs/cifsd/mgmt/share_config.h +++ b/fs/cifsd/mgmt/share_config.h @@ -10,8 +10,6 @@ #include #include -#include "../glob.h" /* FIXME */ - struct ksmbd_share_config { char *name; char *path; diff --git a/fs/cifsd/mgmt/tree_connect.c b/fs/cifsd/mgmt/tree_connect.c index 2be7b2e2e3cd..d5670f2596a3 100644 --- a/fs/cifsd/mgmt/tree_connect.c +++ b/fs/cifsd/mgmt/tree_connect.c @@ -6,7 +6,6 @@ #include #include -#include "../ksmbd_server.h" /* FIXME */ #include "../buffer_pool.h" #include "../transport_ipc.h" #include "../connection.h" diff --git a/fs/cifsd/mgmt/tree_connect.h b/fs/cifsd/mgmt/tree_connect.h index 46237cd05b9c..4e40ec3f4774 100644 --- a/fs/cifsd/mgmt/tree_connect.h +++ b/fs/cifsd/mgmt/tree_connect.h @@ -8,7 +8,7 @@ #include -#include "../ksmbd_server.h" /* FIXME */ +#include "../ksmbd_server.h" struct ksmbd_share_config; struct ksmbd_user; diff --git a/fs/cifsd/mgmt/user_config.c b/fs/cifsd/mgmt/user_config.c index 1ab68f80f72e..a1a454bfb57b 100644 --- a/fs/cifsd/mgmt/user_config.c +++ b/fs/cifsd/mgmt/user_config.c @@ -8,7 +8,6 @@ #include "user_config.h" #include "../buffer_pool.h" #include "../transport_ipc.h" -#include "../ksmbd_server.h" /* FIXME */ struct ksmbd_user *ksmbd_login_user(const char *account) { diff --git a/fs/cifsd/mgmt/user_config.h b/fs/cifsd/mgmt/user_config.h index 5cda4a5d3e2f..b2bb074a0150 100644 --- a/fs/cifsd/mgmt/user_config.h +++ b/fs/cifsd/mgmt/user_config.h @@ -6,8 +6,7 @@ #ifndef __USER_CONFIG_MANAGEMENT_H__ #define __USER_CONFIG_MANAGEMENT_H__ -#include "../glob.h" /* FIXME */ -#include "../ksmbd_server.h" /* FIXME */ +#include "../glob.h" struct ksmbd_user { unsigned short flags; diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index d9f6dbde850a..afcdf76a3851 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -14,7 +14,6 @@ #include "../transport_ipc.h" #include "../connection.h" #include "../buffer_pool.h" -#include "../ksmbd_server.h" /* FIXME */ #include "../vfs_cache.h" static struct ksmbd_ida *session_ida; diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c index a70d311a29dc..3670dcc9ba03 100644 --- a/fs/cifsd/server.c +++ b/fs/cifsd/server.c @@ -102,8 +102,6 @@ static inline int check_conn_state(struct ksmbd_work *work) return 0; } -/* @FIXME what a mess... god help. */ - #define TCP_HANDLER_CONTINUE 0 #define TCP_HANDLER_ABORT 1 diff --git a/fs/cifsd/smb_common.c b/fs/cifsd/smb_common.c index f7560b68b820..7eb6d98656c7 100644 --- a/fs/cifsd/smb_common.c +++ b/fs/cifsd/smb_common.c @@ -8,7 +8,6 @@ #include "server.h" #include "misc.h" #include "smbstatus.h" -/* @FIXME */ #include "connection.h" #include "ksmbd_work.h" #include "mgmt/user_session.h" diff --git a/fs/cifsd/transport_ipc.c b/fs/cifsd/transport_ipc.c index b91fa265f85d..c49e46fda9b1 100644 --- a/fs/cifsd/transport_ipc.c +++ b/fs/cifsd/transport_ipc.c @@ -28,9 +28,6 @@ #include "connection.h" #include "transport_tcp.h" -/* @FIXME fix this code */ -extern int get_protocol_idx(char *str); - #define IPC_WAIT_TIMEOUT (2 * HZ) #define IPC_MSG_HASH_BITS 3 diff --git a/fs/cifsd/transport_ipc.h b/fs/cifsd/transport_ipc.h index 68c003027811..6ed7cbea727e 100644 --- a/fs/cifsd/transport_ipc.h +++ b/fs/cifsd/transport_ipc.h @@ -7,7 +7,6 @@ #define __KSMBD_TRANSPORT_IPC_H__ #include -#include "ksmbd_server.h" /* FIXME */ #define KSMBD_IPC_MAX_PAYLOAD 4096 diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index 8d8c4e373308..af92fab5b7ae 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -8,18 +8,14 @@ #include #include -/* @FIXME */ #include "glob.h" #include "vfs_cache.h" #include "buffer_pool.h" - #include "oplock.h" #include "vfs.h" #include "connection.h" #include "mgmt/tree_connect.h" #include "mgmt/user_session.h" - -/* @FIXME */ #include "smb_common.h" #define S_DEL_PENDING 1 From 95fa1ce947d60b1bb4a0b6c92989cbe3612c1e68 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sun, 21 Mar 2021 17:05:56 +0900 Subject: [PATCH 019/417] cifsd: fix incorrect comments kernel test bot reports some incorrect comments. This patch fixes these comments. Reported-by: kernel test bot Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/oplock.c | 66 ++++++++++++++++------------------------ fs/cifsd/server.c | 6 ++-- fs/cifsd/smb2pdu.c | 60 +++++++++--------------------------- fs/cifsd/smb_common.c | 7 ++--- fs/cifsd/transport_tcp.c | 5 +-- 5 files changed, 51 insertions(+), 93 deletions(-) diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index f694c14be0df..e56c938a8f7a 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -21,11 +21,10 @@ static LIST_HEAD(lease_table_list); static DEFINE_RWLOCK(lease_list_lock); /** - * get_new_opinfo() - allocate a new opinfo object for oplock info - * @conn: connection instance + * alloc_opinfo() - allocate a new opinfo object for oplock info + * @work: smb work * @id: fid of open file * @Tid: tree id of connection - * @lctx: lease context information * * Return: allocated opinfo object on success, otherwise NULL */ @@ -462,14 +461,6 @@ static void grant_none_oplock(struct oplock_info *opinfo_new, } } -/** - * find_opinfo() - find lease object for given client guid and lease key - * @head: oplock list(read,write or none) head - * @guid1: client guid of matching lease owner - * @key1: lease key of matching lease owner - * - * Return: oplock(lease) object on success, otherwise NULL - */ static inline int compare_guid_key(struct oplock_info *opinfo, const char *guid1, const char *key1) { @@ -610,9 +601,9 @@ static inline int allocate_oplock_break_buf(struct ksmbd_work *work) } /** - * smb2_oplock_break_noti() - send smb2 oplock break cmd from conn + * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn * to client - * @work: smb work object + * @wk: smb work object * * There are two ways this function can be called. 1- while file open we break * from exclusive/batch lock to levelII oplock and 2- while file write/truncate @@ -686,10 +677,9 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) } /** - * smb2_oplock_break() - send smb2 exclusive/batch to level2 oplock + * smb2_oplock_break_noti() - send smb2 exclusive/batch to level2 oplock * break command from server to client * @opinfo: oplock info object - * @ack_required if requiring ack * * Return: 0 on success, otherwise error */ @@ -734,7 +724,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) /** * __smb2_lease_break_noti() - send lease break command from server * to client - * @work: smb work object + * @wk: smb work object */ static void __smb2_lease_break_noti(struct work_struct *wk) { @@ -790,10 +780,9 @@ static void __smb2_lease_break_noti(struct work_struct *wk) } /** - * smb2_break_lease() - break lease when a new client request + * smb2_lease_break_noti() - break lease when a new client request * write lease * @opinfo: conains lease state information - * @ack_required: if requring ack * * Return: 0 on success, otherwise error */ @@ -1085,12 +1074,13 @@ static void set_oplock_level(struct oplock_info *opinfo, int level, /** * smb_grant_oplock() - handle oplock/lease request on file open - * @fp: ksmbd file pointer - * @oplock: granted oplock type - * @id: fid of open file - * @Tid: Tree id of connection - * @lctx: lease context information on file open - * @attr_only: attribute only file open type + * @work: smb work + * @req_op_level: oplock level + * @pid: id of open file + * @fp: ksmbd file pointer + * @tid: Tree id of connection + * @lctx: lease context information on file open + * @share_ret: share mode * * Return: 0 on success, otherwise error */ @@ -1222,10 +1212,10 @@ err_out: } /** - * smb_break_write_oplock() - break batch/exclusive oplock to level2 + * smb_break_all_write_oplock() - break batch/exclusive oplock to level2 * @work: smb work * @fp: ksmbd file pointer - * @openfile: open file object + * @is_trunc: truncate on open */ static void smb_break_all_write_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, int is_trunc) @@ -1250,7 +1240,7 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work, /** * smb_break_all_levII_oplock() - send level2 oplock or read lease break command * from server to client - * @conn: connection instance + * @work: smb work * @fp: ksmbd file pointer * @is_trunc: truncate on open */ @@ -1351,7 +1341,7 @@ __u8 smb2_map_lease_to_oplock(__le32 lease_state) /** * create_lease_buf() - create lease context for open cmd response * @rbuf: buffer to create lease context response - * @lreq: buffer to stored parsed lease state information + * @lease: buffer to stored parsed lease state information */ void create_lease_buf(u8 *rbuf, struct lease *lease) { @@ -1378,7 +1368,6 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) /** * parse_lease_state() - parse lease context containted in file open request * @open_req: buffer containing smb2 file open(create) request - * @lreq: buffer to stored parsed lease state information * * Return: oplock state, -ENOENT if create lease context not found */ @@ -1426,7 +1415,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req) /** * smb2_find_context_vals() - find a particular context info in open request * @open_req: buffer containing smb2 file open(create) request - * @str: context name to search for + * @tag: context name to search for * * Return: pointer to requested context, NULL if @str context not found */ @@ -1458,7 +1447,7 @@ struct create_context *smb2_find_context_vals(void *open_req, const char *tag) } /** - * create_durable_buf() - create durable handle context + * create_durable_rsp__buf() - create durable handle context * @cc: buffer to create durable context response */ void create_durable_rsp_buf(char *cc) @@ -1481,8 +1470,9 @@ void create_durable_rsp_buf(char *cc) } /** - * create_durable_buf() - create durable handle v2 context + * create_durable_v2_rsp_buf() - create durable handle v2 context * @cc: buffer to create durable context response + * @fp: ksmbd file pointer */ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) { @@ -1508,8 +1498,9 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) } /** - * create_mxac_buf() - create query maximal access context - * @cc: buffer to create maximal access context response + * create_mxac_rsp_buf() - create query maximal access context + * @cc: buffer to create maximal access context response + * @maximal_access: maximal access */ void create_mxac_rsp_buf(char *cc, int maximal_access) { @@ -1533,10 +1524,6 @@ void create_mxac_rsp_buf(char *cc, int maximal_access) buf->MaximalAccess = cpu_to_le32(maximal_access); } -/** - * create_mxac_buf() - create query maximal access context - * @cc: buffer to create query disk on id context response - */ void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id) { struct create_disk_id_rsp *buf; @@ -1560,8 +1547,9 @@ void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id) } /** - * create_posix_buf() - create posix extension context + * create_posix_rsp_buf() - create posix extension context * @cc: buffer to create posix on posix response + * @fp: ksmbd file pointer */ void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) { diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c index 3670dcc9ba03..85862c3ea7c0 100644 --- a/fs/cifsd/server.c +++ b/fs/cifsd/server.c @@ -86,7 +86,7 @@ char *ksmbd_work_group(void) /** * check_conn_state() - check state of server thread connection - * @ksmbd_work: smb work containing server thread information + * @work: smb work containing server thread information * * Return: 0 on valid connection, otherwise 1 to reconnect */ @@ -248,7 +248,7 @@ send: /** * handle_ksmbd_work() - process pending smb work requests - * @ksmbd_work: smb work containing request command buffer + * @wk: smb work containing request command buffer * * called by kworker threads to processing remaining smb work requests */ @@ -604,7 +604,7 @@ error: } /** - * exit_smb_server() - shutdown forker thread and free memory at module exit + * ksmbd_server_exit() - shutdown forker thread and free memory at module exit */ static void __exit ksmbd_server_exit(void) { diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 730bddbc8152..e4b91838d35c 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -88,8 +88,7 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess) /** * smb2_get_ksmbd_tcon() - get tree connection information for a tree id - * @sess: session containing tree list - * @tid: match tree connection with tree id + * @work: smb work * * Return: matching tree connection on success, otherwise error */ @@ -209,6 +208,7 @@ uint16_t get_smb2_cmd_val(struct ksmbd_work *work) /** * set_smb2_rsp_status() - set error response code on smb2 header * @work: smb work containing response buffer + * @err: error response code */ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) { @@ -633,9 +633,10 @@ static void destroy_previous_session(struct ksmbd_user *user, uint64_t id) /** * smb2_get_name() - get filename string from on the wire smb format + * @share: ksmbd_share_config pointer * @src: source buffer * @maxlen: maxlen of source string - * @work: smb work containing smb request buffer + * @nls_table: nls_table pointer * * Return: matching converted filename on success, otherwise error ptr */ @@ -747,6 +748,7 @@ static __le32 smb2_get_reparse_tag_special_file(umode_t mode) /** * smb2_get_dos_mode() - get file mode in dos format from unix mode * @stat: kstat containing file mode + * @attribute: attribute flags * * Return: converted dos mode */ @@ -1797,7 +1799,6 @@ out_err1: * @file_present: is file already present * @access: file access flags * @disposition: file disposition flags - * @work: smb work containing smb request buffer * * Return: file open flags */ @@ -4112,12 +4113,6 @@ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, inc_rfc1001_len(rsp, sizeof(struct smb2_file_internal_info)); } -/** - * smb2_info_file_pipe() - handler for smb2 query info on IPC pipe - * @work: smb work containing query info command buffer - * - * Return: 0 on success, otherwise error - */ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, struct smb2_query_info_req *req, struct smb2_query_info_rsp *rsp) { @@ -4157,10 +4152,10 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, /** * smb2_get_ea() - handler for smb2 get extended attribute command * @work: smb work containing query info command buffer - * @path: path of file/dir to query info command - * @rq: get extended attribute request - * @resp: response buffer pointer - * @resp_org: base response buffer pointer in case of chained response + * @fp: ksmbd_file pointer + * @req: get extended attribute request + * @rsp: response buffer pointer + * @rsp_org: base response buffer pointer in case of chained response * * Return: 0 on success, otherwise error */ @@ -4761,12 +4756,6 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, return 0; } -/** - * smb2_get_info_file() - handler for smb2 query info command - * @work: smb work containing query info request buffer - * - * Return: 0 on success, otherwise error - */ static int smb2_get_info_file(struct ksmbd_work *work, struct smb2_query_info_req *req, struct smb2_query_info_rsp *rsp, @@ -4901,13 +4890,6 @@ static int smb2_get_info_file(struct ksmbd_work *work, return rc; } -/** - * smb2_get_info_filesystem() - handler for smb2 query info command - * @work: smb work containing query info request buffer - * - * Return: 0 on success, otherwise error - * TODO: need to implement STATUS_INFO_LENGTH_MISMATCH error handling - */ static int smb2_get_info_filesystem(struct ksmbd_work *work, struct smb2_query_info_req *req, struct smb2_query_info_rsp *rsp, @@ -5416,14 +5398,6 @@ int smb2_echo(struct ksmbd_work *work) return 0; } -/** - * smb2_rename() - handler for rename using smb2 setinfo command - * @work: smb work containing set info command buffer - * @filp: file pointer of source file - * @old_fid: file id of source file - * - * Return: 0 on success, otherwise error - */ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, struct smb2_file_rename_info *file_info, struct nls_table *local_nls) @@ -5544,14 +5518,6 @@ out: return rc; } -/** - * smb2_create_link() - handler for creating hardlink using smb2 - * set info command - * @work: smb work containing set info command buffer - * @filp: file pointer of source file - * - * Return: 0 on success, otherwise error - */ static int smb2_create_link(struct ksmbd_work *work, struct ksmbd_share_config *share, struct smb2_file_link_info *file_info, @@ -5914,6 +5880,9 @@ static int set_file_mode_info(struct ksmbd_file *fp, /** * smb2_set_info_file() - handler for smb2 set info command * @work: smb work containing set info command buffer + * @fp: ksmbd_file pointer + * @info_class: smb2 set info class + * @share: ksmbd_share_config pointer * * Return: 0 on success, otherwise error * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH @@ -8057,7 +8026,7 @@ int smb2_oplock_break(struct ksmbd_work *work) /** * smb2_notify() - handler for smb2 notify request - * @ksmbd_work: smb work containing notify command buffer + * @work: smb work containing notify command buffer * * Return: 0 */ @@ -8081,7 +8050,8 @@ int smb2_notify(struct ksmbd_work *work) /** * smb2_is_sign_req() - handler for checking packet signing status - * @work:smb work containing notify command buffer + * @work: smb work containing notify command buffer + * @command: SMB2 command id * * Return: true if packed is signed, false otherwise */ diff --git a/fs/cifsd/smb_common.c b/fs/cifsd/smb_common.c index 7eb6d98656c7..f779aae3fd6c 100644 --- a/fs/cifsd/smb_common.c +++ b/fs/cifsd/smb_common.c @@ -107,8 +107,8 @@ int ksmbd_lookup_protocol_idx(char *str) } /** - * check_message() - check for valid smb2 request header - * @buf: smb2 header to be checked + * ksmbd_verify_smb_message() - check for valid smb2 request header + * @work: smb work * * check for valid smb signature and packet direction(request/response) * @@ -125,9 +125,8 @@ int ksmbd_verify_smb_message(struct ksmbd_work *work) } /** - * is_smb_request() - check for valid smb request type + * ksmbd_smb_request() - check for valid smb request type * @conn: connection instance - * @type: smb request type * * Return: true on success, otherwise false */ diff --git a/fs/cifsd/transport_tcp.c b/fs/cifsd/transport_tcp.c index 60ec9b2e0370..359401227d93 100644 --- a/fs/cifsd/transport_tcp.c +++ b/fs/cifsd/transport_tcp.c @@ -173,7 +173,7 @@ static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa) /** * ksmbd_tcp_new_connection() - create a new tcp session on mount - * @sock: socket associated with new connection + * @client_sk: socket associated with new connection * * whenever a new connection is requested, create a conn thread * (session thread) to handle new incoming smb requests from the connection @@ -252,7 +252,8 @@ static int ksmbd_kthread_fn(void *p) } /** - * ksmbd_create_ksmbd_kthread() - start forker thread + * ksmbd_tcp_run_kthread() - start forker thread + * @iface: pointer to struct interface * * start forker thread(ksmbd/0) at module init time to listen * on port 445 for new SMB connection requests. It creates per connection From 548e9ad317393b0439081454d2110f519431d5ef Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Mar 2021 17:30:49 +0900 Subject: [PATCH 020/417] cifsd: fix warning: variable 'total_ace_size' and 'posix_ccontext' set but not used kernel test robot reported warnings: fs/cifsd/smbacl.c: In function 'parse_sec_desc': >> fs/cifsd/smbacl.c:786:6: warning: variable 'total_ace_size' set but not used [-Wunused-but-set-variable] 786 | int total_ace_size = 0, pntsd_type; | ^~~~~~~~~~~~~~ -- fs/cifsd/smb2pdu.c: In function 'smb2_open': >> fs/cifsd/smb2pdu.c:3285:26: warning: variable 'posix_ccontext' set but not used [-Wunused-but-set-variable] 3285 | struct create_context *posix_ccontext; | ^~~~~~~~~~~~~~ Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 4 ---- fs/cifsd/smbacl.c | 14 +++----------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index e4b91838d35c..4ec45c3fa00e 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -3283,10 +3283,6 @@ reconnected: } if (posix_ctxt) { - struct create_context *posix_ccontext; - - posix_ccontext = (struct create_context *)(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength)); contxt_cnt++; create_posix_rsp_buf(rsp->Buffer + le32_to_cpu(rsp->CreateContextsLength), diff --git a/fs/cifsd/smbacl.c b/fs/cifsd/smbacl.c index 77c79cf4afd0..7f6d5313a02c 100644 --- a/fs/cifsd/smbacl.c +++ b/fs/cifsd/smbacl.c @@ -389,7 +389,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, return; /* validate that we do not go past end of acl */ - if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { + if (end_of_acl <= (char *)pdacl || + end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { ksmbd_err("ACL too small to parse DACL\n"); return; } @@ -783,7 +784,7 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, struct smb_acl *dacl_ptr; /* no need for SACL ptr */ char *end_of_acl = ((char *)pntsd) + acl_len; __u32 dacloffset; - int total_ace_size = 0, pntsd_type; + int pntsd_type; if (pntsd == NULL) return -EIO; @@ -800,16 +801,7 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, le32_to_cpu(pntsd->gsidoffset), le32_to_cpu(pntsd->sacloffset), dacloffset); - if (dacloffset) { - if (end_of_acl <= (char *)dacl_ptr || - end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) - return -EIO; - total_ace_size = - le16_to_cpu(dacl_ptr->size) - sizeof(struct smb_acl); - } - pntsd_type = le16_to_cpu(pntsd->type); - if (!(pntsd_type & DACL_PRESENT)) { ksmbd_debug(SMB, "DACL_PRESENT in DACL type is not set\n"); return rc; From b24c93358035e3c20630a45c0bcdbb45aad9707d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Mar 2021 17:32:19 +0900 Subject: [PATCH 021/417] cifsd: Pass string length parameter to match_pattern() When iterating through a directory, a file's name may not be null-terminated (depending on the underlying filesystem implementation). Modify match_pattern to take the string's length into account when matching it against the request pattern. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/misc.c | 7 +++++-- fs/cifsd/misc.h | 2 +- fs/cifsd/smb2pdu.c | 2 +- fs/cifsd/smb_common.c | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/fs/cifsd/misc.c b/fs/cifsd/misc.c index 68983b08d519..189b90414976 100644 --- a/fs/cifsd/misc.c +++ b/fs/cifsd/misc.c @@ -22,20 +22,22 @@ * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR * * @string: string to compare with a pattern + * @len: string length * @pattern: pattern string which might include wildcard '*' and '?' * * Return: 0 if pattern matched with the string, otherwise non zero value */ -int match_pattern(const char *str, const char *pattern) +int match_pattern(const char *str, size_t len, const char *pattern) { const char *s = str; const char *p = pattern; bool star = false; - while (*s) { + while (*s && len) { switch (*p) { case '?': s++; + len--; p++; break; case '*': @@ -48,6 +50,7 @@ int match_pattern(const char *str, const char *pattern) default: if (tolower(*s) == tolower(*p)) { s++; + len--; p++; } else { if (!star) diff --git a/fs/cifsd/misc.h b/fs/cifsd/misc.h index 41b3dac2f5fc..73b21709b6c9 100644 --- a/fs/cifsd/misc.h +++ b/fs/cifsd/misc.h @@ -11,7 +11,7 @@ struct nls_table; struct kstat; struct ksmbd_file; -int match_pattern(const char *str, const char *pattern); +int match_pattern(const char *str, size_t len, const char *pattern); int ksmbd_validate_filename(char *filename); diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 4ec45c3fa00e..32816baa8a99 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -3837,7 +3837,7 @@ static int __query_dir(struct dir_context *ctx, return 0; if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name)) return 0; - if (!match_pattern(name, priv->search_pattern)) + if (!match_pattern(name, namlen, priv->search_pattern)) return 0; d_info->name = name; diff --git a/fs/cifsd/smb_common.c b/fs/cifsd/smb_common.c index f779aae3fd6c..2f58ef003238 100644 --- a/fs/cifsd/smb_common.c +++ b/fs/cifsd/smb_common.c @@ -294,7 +294,8 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, d_info->name_len = 2; } - if (!match_pattern(d_info->name, search_pattern)) { + if (!match_pattern(d_info->name, d_info->name_len, + search_pattern)) { dir->dot_dotdot[i] = 1; continue; } From 269d3feec1b0f0c286ff3cc3eef43416614ee261 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 23 Mar 2021 15:17:00 +0900 Subject: [PATCH 022/417] cifsd: fix build warnings from cifsd.rst Stephen reported a build warnings from cifsd.rst: Documentation/filesystems/cifs/cifsd.rst:13: WARNING: Inline substitution_reference start-string without end-string. Documentation/filesystems/cifs/cifsd.rst:14: WARNING: Block quote ends without a blank line; unexpected unindent. Documentation/filesystems/cifs/cifsd.rst:14: WARNING: Inline substitution_reference start-string without end-string. Documentation/filesystems/cifs/cifsd.rst:18: WARNING: Block quote ends without a blank line; unexpected unindent. Documentation/filesystems/cifs/cifsd.rst:23: WARNING: Inline substitution_reference start-string without end-string. Documentation/filesystems/cifs/cifsd.rst:23: WARNING: Inline substitution_reference start-string without end-string. Documentation/filesystems/cifs/cifsd.rst:24: WARNING: Inline substitution_reference start-string without end-string. Documentation/filesystems/cifs/cifsd.rst:25: WARNING: Definition list ends without a blank line; unexpected unindent. Documentation/filesystems/cifs/cifsd.rst:28: WARNING: Unexpected indentation. Documentation/filesystems/cifs/cifsd.rst:31: WARNING: Block quote ends without a blank line; unexpected unindent. Documentation/filesystems/cifs/cifsd.rst:38: WARNING: Unexpected indentation. Documentation/filesystems/cifs/cifsd.rst:32: WARNING: Inline substitution_reference start-string without end-string. Documentation/filesystems/cifs/cifsd.rst:32: WARNING: Inline substitution_reference start-string without end-string. Documentation/filesystems/cifs/cifsd.rst:39: WARNING: Block quote ends without a blank line; unexpected unindent. Documentation/filesystems/cifs/cifsd.rst:14: WARNING: Undefined substitution referenced: "--- ksmbd/3 - Client 3 |-------". Documentation/filesystems/cifs/cifsd.rst:0: WARNING: Undefined substitution referenced: "____________________________________________________". Documentation/filesystems/cifs/cifsd.rst:25: WARNING: Undefined substitution referenced: "--- ksmbd/0(forker kthread) ---------------|". Documentation/filesystems/cifs/cifsd.rst:32: WARNING: Undefined substitution referenced: "______________________________________________". Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- Documentation/filesystems/cifs/cifsd.rst | 28 ------------------------ 1 file changed, 28 deletions(-) diff --git a/Documentation/filesystems/cifs/cifsd.rst b/Documentation/filesystems/cifs/cifsd.rst index 7eac7e459c2d..48ae58f2a53c 100644 --- a/Documentation/filesystems/cifs/cifsd.rst +++ b/Documentation/filesystems/cifs/cifsd.rst @@ -10,34 +10,6 @@ for sharing files over network. CIFSD architecture ================== - |--- ... - --------|--- ksmbd/3 - Client 3 - |-------|--- ksmbd/2 - Client 2 - | | ____________________________________________________ - | | |- Client 1 | -<--- Socket ---|--- ksmbd/1 <<= Authentication : NTLM/NTLM2, Kerberos | - | | | | <<= SMB engine : SMB2, SMB2.1, SMB3, SMB3.0.2, | - | | | | SMB3.1.1 | - | | | |____________________________________________________| - | | | - | | |--- VFS --- Local Filesystem - | | -KERNEL |--- ksmbd/0(forker kthread) ----------------||--------------------------------------------------------------- -USER || - || communication using NETLINK - || ______________________________________________ - || | | - ksmbd.mountd <<= DCE/RPC(srvsvc, wkssvc, smar, lsarpc) | - ^ | <<= configure shares setting, user accounts | - | |______________________________________________| - | - |------ smb.conf(config file) - | - |------ ksmbdpwd.db(user account/password file) - ^ - ksmbd.adduser ---------------| - The subset of performance related operations belong in kernelspace and the other subset which belong to operations which are not really related with performance in userspace. So, DCE/RPC management that has historically resulted From c1ea111fd1bb4c4020503f5c53cd05a703d1a30b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 22 Mar 2021 17:50:11 +0300 Subject: [PATCH 023/417] cifsd: Fix an error code in smb2_read() This code is assigning the wrong variable to "err" so it returns zero/success instead of -ENOMEM. Fixes: 788b6f45c1d2 ("cifsd: add server-side procedures for SMB3") Signed-off-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 32816baa8a99..6770ebedc24a 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -6200,7 +6200,7 @@ int smb2_read(struct ksmbd_work *work) work->aux_payload_buf = ksmbd_alloc_response(length); } if (!work->aux_payload_buf) { - err = nbytes; + err = -ENOMEM; goto out; } From 849f59e1a18adecf0617afc82efbfc5d126c49f8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 23 Mar 2021 16:27:04 +0300 Subject: [PATCH 024/417] cifsd: fix error handling in ksmbd_server_init() The error handling in ksmbd_server_init() uses "one function to free everything style" which is impossible to audit and leads to several canonical bugs. When we free something that wasn't allocated it may be uninitialized, an error pointer, freed in a different function or we try freeing "foo->bar" when "foo" is a NULL pointer. And since the code is impossible to audit then it leads to memory leaks. In the ksmbd_server_init() function, every goto will lead to a crash because we have not allocated the work queue but we call ksmbd_workqueue_destroy() which tries to flush a NULL work queue. Another bug is if ksmbd_init_buffer_pools() fails then it leads to a double free because we free "work_cache" twice. A third type of bug is that we forgot to call ksmbd_release_inode_hash() so that is a resource leak. A better way to write error handling is for every function to clean up after itself and never leave things partially allocated. Then we can use "free the last successfully allocated resource" style. That way when someone is reading the code they can just track the last resource in their head and verify that the goto matches what they expect. In this patch I modified ksmbd_ipc_init() to clean up after itself and then I converted ksmbd_server_init() to use gotos to clean up. Fixes: cabcebc31de4 ("cifsd: introduce SMB3 kernel server") Signed-off-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/server.c | 33 +++++++++++++++++++++++---------- fs/cifsd/transport_ipc.c | 14 +++++++++++--- fs/cifsd/vfs_cache.c | 2 +- fs/cifsd/vfs_cache.h | 2 +- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c index 85862c3ea7c0..31e454cb3ce2 100644 --- a/fs/cifsd/server.c +++ b/fs/cifsd/server.c @@ -567,39 +567,52 @@ static int __init ksmbd_server_init(void) ret = server_conf_init(); if (ret) - return ret; + goto err_unregister; ret = ksmbd_init_buffer_pools(); if (ret) - return ret; + goto err_unregister; ret = ksmbd_init_session_table(); if (ret) - goto error; + goto err_destroy_pools; ret = ksmbd_ipc_init(); if (ret) - goto error; + goto err_free_session_table; ret = ksmbd_init_global_file_table(); if (ret) - goto error; + goto err_ipc_release; ret = ksmbd_inode_hash_init(); if (ret) - goto error; + goto err_destroy_file_table; ret = ksmbd_crypto_create(); if (ret) - goto error; + goto err_release_inode_hash; ret = ksmbd_workqueue_init(); if (ret) - goto error; + goto err_crypto_destroy; return 0; -error: - ksmbd_server_shutdown(); +err_crypto_destroy: + ksmbd_crypto_destroy(); +err_release_inode_hash: + ksmbd_release_inode_hash(); +err_destroy_file_table: + ksmbd_free_global_file_table(); +err_ipc_release: + ksmbd_ipc_release(); +err_free_session_table: + ksmbd_free_session_table(); +err_destroy_pools: + ksmbd_destroy_buffer_pools(); +err_unregister: + class_unregister(&ksmbd_control_class); + return ret; } diff --git a/fs/cifsd/transport_ipc.c b/fs/cifsd/transport_ipc.c index c49e46fda9b1..e5f4d97b2924 100644 --- a/fs/cifsd/transport_ipc.c +++ b/fs/cifsd/transport_ipc.c @@ -887,11 +887,19 @@ int ksmbd_ipc_init(void) if (ret) { ksmbd_err("Failed to register KSMBD netlink interface %d\n", ret); - return ret; + goto cancel_work; } ida = ksmbd_ida_alloc(); - if (!ida) - return -ENOMEM; + if (!ida) { + ret = -ENOMEM; + goto unregister; + } return 0; + +unregister: + genl_unregister_family(&ksmbd_genl_family); +cancel_work: + cancel_delayed_work_sync(&ipc_timer_work); + return ret; } diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index af92fab5b7ae..34e045f27230 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -236,7 +236,7 @@ int __init ksmbd_inode_hash_init(void) return 0; } -void __exit ksmbd_release_inode_hash(void) +void ksmbd_release_inode_hash(void) { vfree(inode_hashtable); } diff --git a/fs/cifsd/vfs_cache.h b/fs/cifsd/vfs_cache.h index 7d23657c86c6..04ab5967a9ae 100644 --- a/fs/cifsd/vfs_cache.h +++ b/fs/cifsd/vfs_cache.h @@ -194,7 +194,7 @@ void ksmbd_set_fd_limit(unsigned long limit); */ int __init ksmbd_inode_hash_init(void); -void __exit ksmbd_release_inode_hash(void); +void ksmbd_release_inode_hash(void); enum KSMBD_INODE_STATUS { KSMBD_INODE_STATUS_OK, From 3161ad3a717e69b26ea3d73467ed8399023b5075 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 25 Mar 2021 17:35:38 +0000 Subject: [PATCH 025/417] cifsd: remove redundant assignment to variable err The variable err is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 00f80ca45690..3d7413b8f526 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -587,7 +587,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) struct path parent; struct dentry *dir, *dentry; char *last; - int err = -ENOENT; + int err; last = extract_last_component(name); if (!last) From e5066499079de0e1dac094baf4cb62eb86cbdd4f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 30 Mar 2021 12:35:23 +0900 Subject: [PATCH 026/417] cifsd: remove unneeded macros Remove unneeded RESPONSE_BUF, REQUEST_BUF, RESPONSE_SZ, INIT_AUX_PAYLOAD, HAS_AUX_PAYLOAD, AUX_PAYLOAD, AUX_PAYLOAD_SIZE, RESP_HDR_SIZE, HAS_TRANSFORM_BUF and TRANSFORM_BUF macros. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/connection.c | 15 ++- fs/cifsd/ksmbd_work.c | 12 +-- fs/cifsd/ksmbd_work.h | 24 +---- fs/cifsd/oplock.c | 14 +-- fs/cifsd/server.c | 4 +- fs/cifsd/smb2misc.c | 2 +- fs/cifsd/smb2pdu.c | 212 +++++++++++++++++++++--------------------- fs/cifsd/smb_common.c | 8 +- fs/cifsd/vfs.c | 2 +- 9 files changed, 139 insertions(+), 154 deletions(-) diff --git a/fs/cifsd/connection.c b/fs/cifsd/connection.c index d27553dee2ad..bdfde5ca2ded 100644 --- a/fs/cifsd/connection.c +++ b/fs/cifsd/connection.c @@ -154,7 +154,7 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn) int ksmbd_conn_write(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb_hdr *rsp_hdr = RESPONSE_BUF(work); + struct smb_hdr *rsp_hdr = work->response_buf; size_t len = 0; int sent; struct kvec iov[3]; @@ -166,21 +166,20 @@ int ksmbd_conn_write(struct ksmbd_work *work) return -EINVAL; } - if (HAS_TRANSFORM_BUF(work)) { + if (work->tr_buf) { iov[iov_idx] = (struct kvec) { work->tr_buf, sizeof(struct smb2_transform_hdr) }; len += iov[iov_idx++].iov_len; } - if (HAS_AUX_PAYLOAD(work)) { - iov[iov_idx] = (struct kvec) { rsp_hdr, RESP_HDR_SIZE(work) }; + if (work->aux_payload_sz) { + iov[iov_idx] = (struct kvec) { rsp_hdr, work->resp_hdr_sz }; len += iov[iov_idx++].iov_len; - iov[iov_idx] = (struct kvec) { AUX_PAYLOAD(work), - AUX_PAYLOAD_SIZE(work) }; + iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; len += iov[iov_idx++].iov_len; } else { - if (HAS_TRANSFORM_BUF(work)) - iov[iov_idx].iov_len = RESP_HDR_SIZE(work); + if (work->tr_buf) + iov[iov_idx].iov_len = work->resp_hdr_sz; else iov[iov_idx].iov_len = get_rfc1002_len(rsp_hdr) + 4; iov[iov_idx].iov_base = rsp_hdr; diff --git a/fs/cifsd/ksmbd_work.c b/fs/cifsd/ksmbd_work.c index 8cd5dff0762d..505e59df3071 100644 --- a/fs/cifsd/ksmbd_work.c +++ b/fs/cifsd/ksmbd_work.c @@ -40,18 +40,18 @@ void ksmbd_free_work_struct(struct ksmbd_work *work) WARN_ON(work->saved_cred != NULL); if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_TBUF && work->set_trans_buf) - ksmbd_release_buffer(RESPONSE_BUF(work)); + ksmbd_release_buffer(work->response_buf); else - ksmbd_free_response(RESPONSE_BUF(work)); + ksmbd_free_response(work->response_buf); if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF && work->set_read_buf) - ksmbd_release_buffer(AUX_PAYLOAD(work)); + ksmbd_release_buffer(work->aux_payload_buf); else - ksmbd_free_response(AUX_PAYLOAD(work)); + ksmbd_free_response(work->aux_payload_buf); - ksmbd_free_response(TRANSFORM_BUF(work)); - ksmbd_free_request(REQUEST_BUF(work)); + ksmbd_free_response(work->tr_buf); + ksmbd_free_request(work->request_buf); if (work->async_id) ksmbd_release_id(work->conn->async_ida, work->async_id); kmem_cache_free(work_cache, work); diff --git a/fs/cifsd/ksmbd_work.h b/fs/cifsd/ksmbd_work.h index 405434d4c8ab..28a1692ed37f 100644 --- a/fs/cifsd/ksmbd_work.h +++ b/fs/cifsd/ksmbd_work.h @@ -27,12 +27,12 @@ struct ksmbd_work { struct ksmbd_tree_connect *tcon; /* Pointer to received SMB header */ - char *request_buf; + void *request_buf; /* Response buffer */ - char *response_buf; + void *response_buf; /* Read data buffer */ - char *aux_payload_buf; + void *aux_payload_buf; /* Next cmd hdr in compound req buf*/ int next_smb2_rcv_hdr_off; @@ -92,24 +92,10 @@ struct ksmbd_work { #define WORK_CLOSED(w) ((w)->state == KSMBD_WORK_CLOSED) #define WORK_ACTIVE(w) ((w)->state == KSMBD_WORK_ACTIVE) -#define RESPONSE_BUF(w) ((void *)(w)->response_buf) -#define REQUEST_BUF(w) ((void *)(w)->request_buf) - #define RESPONSE_BUF_NEXT(w) \ - ((void *)((w)->response_buf + (w)->next_smb2_rsp_hdr_off)) + (((w)->response_buf + (w)->next_smb2_rsp_hdr_off)) #define REQUEST_BUF_NEXT(w) \ - ((void *)((w)->request_buf + (w)->next_smb2_rcv_hdr_off)) - -#define RESPONSE_SZ(w) ((w)->response_sz) - -#define INIT_AUX_PAYLOAD(w) ((w)->aux_payload_buf = NULL) -#define HAS_AUX_PAYLOAD(w) ((w)->aux_payload_sz != 0) -#define AUX_PAYLOAD(w) ((void *)((w)->aux_payload_buf)) -#define AUX_PAYLOAD_SIZE(w) ((w)->aux_payload_sz) -#define RESP_HDR_SIZE(w) ((w)->resp_hdr_sz) - -#define HAS_TRANSFORM_BUF(w) ((w)->tr_buf != NULL) -#define TRANSFORM_BUF(w) ((void *)((w)->tr_buf)) + (((w)->request_buf + (w)->next_smb2_rcv_hdr_off)) struct ksmbd_work *ksmbd_alloc_work_struct(void); void ksmbd_free_work_struct(struct ksmbd_work *work); diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index e56c938a8f7a..25823bb7d086 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -608,14 +608,14 @@ static inline int allocate_oplock_break_buf(struct ksmbd_work *work) * There are two ways this function can be called. 1- while file open we break * from exclusive/batch lock to levelII oplock and 2- while file write/truncate * we break from levelII oplock no oplock. - * REQUEST_BUF(work) contains oplock_info. + * work->request_buf contains oplock_info. */ static void __smb2_oplock_break_noti(struct work_struct *wk) { struct smb2_oplock_break *rsp = NULL; struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); struct ksmbd_conn *conn = work->conn; - struct oplock_break_info *br_info = REQUEST_BUF(work); + struct oplock_break_info *br_info = work->request_buf; struct smb2_hdr *rsp_hdr; struct ksmbd_file *fp; @@ -634,7 +634,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) return; } - rsp_hdr = RESPONSE_BUF(work); + rsp_hdr = work->response_buf; memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); rsp_hdr->smb2_buf_length = cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn)); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; @@ -650,7 +650,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) memset(rsp_hdr->Signature, 0, 16); - rsp = RESPONSE_BUF(work); + rsp = work->response_buf; rsp->StructureSize = cpu_to_le16(24); if (!br_info->open_trunc && @@ -730,7 +730,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) { struct smb2_lease_break *rsp = NULL; struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); - struct lease_break_info *br_info = REQUEST_BUF(work); + struct lease_break_info *br_info = work->request_buf; struct ksmbd_conn *conn = work->conn; struct smb2_hdr *rsp_hdr; @@ -741,7 +741,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) return; } - rsp_hdr = RESPONSE_BUF(work); + rsp_hdr = work->response_buf; memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); rsp_hdr->smb2_buf_length = cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn)); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; @@ -756,7 +756,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = RESPONSE_BUF(work); + rsp = work->response_buf; rsp->StructureSize = cpu_to_le16(44); rsp->Reserved = 0; rsp->Flags = 0; diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c index 31e454cb3ce2..60027e74f0ed 100644 --- a/fs/cifsd/server.c +++ b/fs/cifsd/server.c @@ -95,7 +95,7 @@ static inline int check_conn_state(struct ksmbd_work *work) struct smb_hdr *rsp_hdr; if (ksmbd_conn_exiting(work) || ksmbd_conn_need_reconnect(work)) { - rsp_hdr = RESPONSE_BUF(work); + rsp_hdr = work->response_buf; rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; return 1; } @@ -169,7 +169,7 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, return; if (conn->ops->is_transform_hdr && - conn->ops->is_transform_hdr(REQUEST_BUF(work))) { + conn->ops->is_transform_hdr(work->request_buf)) { rc = conn->ops->decrypt_req(work); if (rc < 0) { conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); diff --git a/fs/cifsd/smb2misc.c b/fs/cifsd/smb2misc.c index 485f431c776c..e6b87d9d33ed 100644 --- a/fs/cifsd/smb2misc.c +++ b/fs/cifsd/smb2misc.c @@ -355,7 +355,7 @@ static int smb2_validate_credit_charge(struct smb2_hdr *hdr) int ksmbd_smb2_check_message(struct ksmbd_work *work) { - struct smb2_pdu *pdu = REQUEST_BUF(work); + struct smb2_pdu *pdu = work->request_buf; struct smb2_hdr *hdr = &pdu->hdr; int command; __u32 clc_len; /* calculated length */ diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 6770ebedc24a..460d5ba275bf 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -44,8 +44,8 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) *req = REQUEST_BUF_NEXT(work); *rsp = RESPONSE_BUF_NEXT(work); } else { - *req = REQUEST_BUF(work); - *rsp = RESPONSE_BUF(work); + *req = work->request_buf; + *rsp = work->response_buf; } } @@ -94,7 +94,7 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess) */ int smb2_get_ksmbd_tcon(struct ksmbd_work *work) { - struct smb2_hdr *req_hdr = REQUEST_BUF(work); + struct smb2_hdr *req_hdr = work->request_buf; int tree_id; work->tcon = NULL; @@ -131,7 +131,7 @@ void smb2_set_err_rsp(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) err_rsp = RESPONSE_BUF_NEXT(work); else - err_rsp = RESPONSE_BUF(work); + err_rsp = work->response_buf; if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; @@ -139,7 +139,7 @@ void smb2_set_err_rsp(struct ksmbd_work *work) err_rsp->Reserved = 0; err_rsp->ByteCount = 0; err_rsp->ErrorData[0] = 0; - inc_rfc1001_len(RESPONSE_BUF(work), SMB2_ERROR_STRUCTURE_SIZE2); + inc_rfc1001_len(work->response_buf, SMB2_ERROR_STRUCTURE_SIZE2); } } @@ -151,7 +151,7 @@ void smb2_set_err_rsp(struct ksmbd_work *work) */ int is_smb2_neg_cmd(struct ksmbd_work *work) { - struct smb2_hdr *hdr = REQUEST_BUF(work); + struct smb2_hdr *hdr = work->request_buf; /* is it SMB2 header ? */ if (hdr->ProtocolId != SMB2_PROTO_NUMBER) @@ -175,7 +175,7 @@ int is_smb2_neg_cmd(struct ksmbd_work *work) */ int is_smb2_rsp(struct ksmbd_work *work) { - struct smb2_hdr *hdr = RESPONSE_BUF(work); + struct smb2_hdr *hdr = work->response_buf; /* is it SMB2 header ? */ if (hdr->ProtocolId != SMB2_PROTO_NUMBER) @@ -201,7 +201,7 @@ uint16_t get_smb2_cmd_val(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) rcv_hdr = REQUEST_BUF_NEXT(work); else - rcv_hdr = REQUEST_BUF(work); + rcv_hdr = work->request_buf; return le16_to_cpu(rcv_hdr->Command); } @@ -217,7 +217,7 @@ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) if (work->next_smb2_rcv_hdr_off) rsp_hdr = RESPONSE_BUF_NEXT(work); else - rsp_hdr = RESPONSE_BUF(work); + rsp_hdr = work->response_buf; rsp_hdr->Status = err; smb2_set_err_rsp(work); } @@ -241,7 +241,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) conn->dialect <= SMB311_PROT_ID)) return -EINVAL; - rsp_hdr = RESPONSE_BUF(work); + rsp_hdr = work->response_buf; memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); @@ -260,7 +260,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = RESPONSE_BUF(work); + rsp = work->response_buf; WARN_ON(ksmbd_conn_good(work)); @@ -410,11 +410,11 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) work->compound_sid = le64_to_cpu(rsp->SessionId); } - len = get_rfc1002_len(RESPONSE_BUF(work)) - work->next_smb2_rsp_hdr_off; + len = get_rfc1002_len(work->response_buf) - work->next_smb2_rsp_hdr_off; next_hdr_offset = le32_to_cpu(req->NextCommand); new_len = ALIGN(len, 8); - inc_rfc1001_len(RESPONSE_BUF(work), ((sizeof(struct smb2_hdr) - 4) + inc_rfc1001_len(work->response_buf, ((sizeof(struct smb2_hdr) - 4) + new_len - len)); rsp->NextCommand = cpu_to_le32(new_len); @@ -459,7 +459,7 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) */ bool is_chained_smb2_message(struct ksmbd_work *work) { - struct smb2_hdr *hdr = REQUEST_BUF(work); + struct smb2_hdr *hdr = work->request_buf; unsigned int len; if (hdr->ProtocolId != SMB2_PROTO_NUMBER) @@ -475,12 +475,12 @@ bool is_chained_smb2_message(struct ksmbd_work *work) * This is last request in chained command, * align response to 8 byte */ - len = ALIGN(get_rfc1002_len(RESPONSE_BUF(work)), 8); - len = len - get_rfc1002_len(RESPONSE_BUF(work)); + len = ALIGN(get_rfc1002_len(work->response_buf), 8); + len = len - get_rfc1002_len(work->response_buf); if (len) { ksmbd_debug(SMB, "padding len %u\n", len); - inc_rfc1001_len(RESPONSE_BUF(work), len); - if (HAS_AUX_PAYLOAD(work)) + inc_rfc1001_len(work->response_buf, len); + if (work->aux_payload_sz) work->aux_payload_sz += len; } } @@ -495,8 +495,8 @@ bool is_chained_smb2_message(struct ksmbd_work *work) */ int init_smb2_rsp_hdr(struct ksmbd_work *work) { - struct smb2_hdr *rsp_hdr = RESPONSE_BUF(work); - struct smb2_hdr *rcv_hdr = REQUEST_BUF(work); + struct smb2_hdr *rsp_hdr = work->response_buf; + struct smb2_hdr *rcv_hdr = work->request_buf; struct ksmbd_conn *conn = work->conn; memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); @@ -533,7 +533,7 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) */ int smb2_allocate_rsp_buf(struct ksmbd_work *work) { - struct smb2_hdr *hdr = REQUEST_BUF(work); + struct smb2_hdr *hdr = work->request_buf; size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; size_t large_sz = work->conn->vals->max_trans_size + MAX_SMB2_HDR_SIZE; size_t sz = small_sz; @@ -547,7 +547,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) if (cmd == SMB2_QUERY_INFO_HE) { struct smb2_query_info_req *req; - req = REQUEST_BUF(work); + req = work->request_buf; if (req->InfoType == SMB2_O_INFO_FILE && (req->FileInfoClass == FILE_FULL_EA_INFORMATION || req->FileInfoClass == FILE_ALL_INFORMATION)) { @@ -566,7 +566,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) else work->response_buf = ksmbd_alloc_response(sz); - if (!RESPONSE_BUF(work)) { + if (!work->response_buf) { ksmbd_err("Failed to allocate %zu bytes buffer\n", sz); return -ENOMEM; } @@ -583,7 +583,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) */ int smb2_check_user_session(struct ksmbd_work *work) { - struct smb2_hdr *req_hdr = REQUEST_BUF(work); + struct smb2_hdr *req_hdr = work->request_buf; struct ksmbd_conn *conn = work->conn; unsigned int cmd = conn->ops->get_cmd_val(work); unsigned long long sess_id; @@ -686,7 +686,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) struct ksmbd_conn *conn = work->conn; int id; - rsp_hdr = RESPONSE_BUF(work); + rsp_hdr = work->response_buf; rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; id = ksmbd_acquire_async_msg_id(conn->async_ida); @@ -716,7 +716,7 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) { struct smb2_hdr *rsp_hdr; - rsp_hdr = RESPONSE_BUF(work); + rsp_hdr = work->response_buf; smb2_set_err_rsp(work); rsp_hdr->Status = status; @@ -1030,8 +1030,8 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, int smb2_handle_negotiate(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_negotiate_req *req = REQUEST_BUF(work); - struct smb2_negotiate_rsp *rsp = RESPONSE_BUF(work); + struct smb2_negotiate_req *req = work->request_buf; + struct smb2_negotiate_rsp *rsp = work->response_buf; int rc = 0; __le32 status; @@ -1078,7 +1078,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) } ksmbd_gen_preauth_integrity_hash(conn, - REQUEST_BUF(work), + work->request_buf, conn->preauth_info->Preauth_HashValue); rsp->NegotiateContextOffset = cpu_to_le32(OFFSET_OF_NEG_CONTEXT); @@ -1198,7 +1198,7 @@ static int generate_preauth_hash(struct ksmbd_work *work) } ksmbd_gen_preauth_integrity_hash(conn, - REQUEST_BUF(work), + work->request_buf, sess->Preauth_HashValue); return 0; } @@ -1213,7 +1213,7 @@ static int decode_negotiation_token(struct ksmbd_work *work, if (!conn->use_spnego) return -EINVAL; - req = REQUEST_BUF(work); + req = work->request_buf; sz = le16_to_cpu(req->SecurityBufferLength); if (!ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { @@ -1229,8 +1229,8 @@ static int decode_negotiation_token(struct ksmbd_work *work, static int ntlm_negotiate(struct ksmbd_work *work, struct negotiate_message *negblob) { - struct smb2_sess_setup_req *req = REQUEST_BUF(work); - struct smb2_sess_setup_rsp *rsp = RESPONSE_BUF(work); + struct smb2_sess_setup_req *req = work->request_buf; + struct smb2_sess_setup_rsp *rsp = work->response_buf; struct challenge_message *chgblob; unsigned char *spnego_blob = NULL; u16 spnego_blob_len; @@ -1330,8 +1330,8 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn, static int ntlm_authenticate(struct ksmbd_work *work) { - struct smb2_sess_setup_req *req = REQUEST_BUF(work); - struct smb2_sess_setup_rsp *rsp = RESPONSE_BUF(work); + struct smb2_sess_setup_req *req = work->request_buf; + struct smb2_sess_setup_rsp *rsp = work->response_buf; struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; struct channel *chann = NULL; @@ -1473,8 +1473,8 @@ static int ntlm_authenticate(struct ksmbd_work *work) #ifdef CONFIG_SMB_SERVER_KERBEROS5 static int krb5_authenticate(struct ksmbd_work *work) { - struct smb2_sess_setup_req *req = REQUEST_BUF(work); - struct smb2_sess_setup_rsp *rsp = RESPONSE_BUF(work); + struct smb2_sess_setup_req *req = work->request_buf; + struct smb2_sess_setup_rsp *rsp = work->response_buf; struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; char *in_blob, *out_blob; @@ -1570,8 +1570,8 @@ static int krb5_authenticate(struct ksmbd_work *work) int smb2_sess_setup(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_sess_setup_req *req = REQUEST_BUF(work); - struct smb2_sess_setup_rsp *rsp = RESPONSE_BUF(work); + struct smb2_sess_setup_req *req = work->request_buf; + struct smb2_sess_setup_rsp *rsp = work->response_buf; struct ksmbd_session *sess; struct negotiate_message *negblob; int rc = 0; @@ -1695,8 +1695,8 @@ out_err: int smb2_tree_connect(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_tree_connect_req *req = REQUEST_BUF(work); - struct smb2_tree_connect_rsp *rsp = RESPONSE_BUF(work); + struct smb2_tree_connect_req *req = work->request_buf; + struct smb2_tree_connect_rsp *rsp = work->response_buf; struct ksmbd_session *sess = work->sess; char *treename = NULL, *name = NULL; struct ksmbd_tree_conn_status status; @@ -1858,7 +1858,7 @@ static int smb2_create_open_flags(bool file_present, __le32 access, */ int smb2_tree_disconnect(struct ksmbd_work *work) { - struct smb2_tree_disconnect_rsp *rsp = RESPONSE_BUF(work); + struct smb2_tree_disconnect_rsp *rsp = work->response_buf; struct ksmbd_session *sess = work->sess; struct ksmbd_tree_connect *tcon = work->tcon; @@ -1868,7 +1868,7 @@ int smb2_tree_disconnect(struct ksmbd_work *work) ksmbd_debug(SMB, "request\n"); if (!tcon) { - struct smb2_tree_disconnect_req *req = REQUEST_BUF(work); + struct smb2_tree_disconnect_req *req = work->request_buf; ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; @@ -1890,7 +1890,7 @@ int smb2_tree_disconnect(struct ksmbd_work *work) int smb2_session_logoff(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_logoff_rsp *rsp = RESPONSE_BUF(work); + struct smb2_logoff_rsp *rsp = work->response_buf; struct ksmbd_session *sess = work->sess; rsp->StructureSize = cpu_to_le16(4); @@ -1907,7 +1907,7 @@ int smb2_session_logoff(struct ksmbd_work *work) ksmbd_conn_wait_idle(conn); if (ksmbd_tree_conn_session_logoff(sess)) { - struct smb2_logoff_req *req = REQUEST_BUF(work); + struct smb2_logoff_req *req = work->request_buf; ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; @@ -1934,8 +1934,8 @@ int smb2_session_logoff(struct ksmbd_work *work) */ static noinline int create_smb2_pipe(struct ksmbd_work *work) { - struct smb2_create_rsp *rsp = RESPONSE_BUF(work); - struct smb2_create_req *req = REQUEST_BUF(work); + struct smb2_create_rsp *rsp = work->response_buf; + struct smb2_create_req *req = work->request_buf; int id; int err; char *name; @@ -2490,7 +2490,7 @@ int smb2_open(struct ksmbd_work *work) umode_t posix_mode = 0; __le32 daccess, maximal_access = 0; - rsp_org = RESPONSE_BUF(work); + rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && @@ -3889,7 +3889,7 @@ int smb2_query_dir(struct ksmbd_work *work) int buffer_sz; struct smb2_query_dir_private query_dir_private = {NULL, }; - rsp_org = RESPONSE_BUF(work); + rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (ksmbd_override_fsids(work)) { @@ -5190,7 +5190,7 @@ int smb2_query_info(struct ksmbd_work *work) struct smb2_query_info_rsp *rsp, *rsp_org; int rc = 0; - rsp_org = RESPONSE_BUF(work); + rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); ksmbd_debug(SMB, "GOT query info request\n"); @@ -5244,8 +5244,8 @@ int smb2_query_info(struct ksmbd_work *work) static noinline int smb2_close_pipe(struct ksmbd_work *work) { uint64_t id; - struct smb2_close_req *req = REQUEST_BUF(work); - struct smb2_close_rsp *rsp = RESPONSE_BUF(work); + struct smb2_close_req *req = work->request_buf; + struct smb2_close_rsp *rsp = work->response_buf; id = le64_to_cpu(req->VolatileFileId); ksmbd_session_rpc_close(work->sess, id); @@ -5283,7 +5283,7 @@ int smb2_close(struct ksmbd_work *work) u64 time; int err = 0; - rsp_org = RESPONSE_BUF(work); + rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, @@ -5386,7 +5386,7 @@ out: */ int smb2_echo(struct ksmbd_work *work) { - struct smb2_echo_rsp *rsp = RESPONSE_BUF(work); + struct smb2_echo_rsp *rsp = work->response_buf; rsp->StructureSize = cpu_to_le16(4); rsp->Reserved = 0; @@ -5974,7 +5974,7 @@ int smb2_set_info(struct ksmbd_work *work) ksmbd_debug(SMB, "Received set info request\n"); - rsp_org = RESPONSE_BUF(work); + rsp_org = work->response_buf; if (work->next_smb2_rcv_hdr_off) { req = REQUEST_BUF_NEXT(work); rsp = RESPONSE_BUF_NEXT(work); @@ -5985,8 +5985,8 @@ int smb2_set_info(struct ksmbd_work *work) pid = work->compound_pfid; } } else { - req = REQUEST_BUF(work); - rsp = RESPONSE_BUF(work); + req = work->request_buf; + rsp = work->response_buf; } if (!HAS_FILE_ID(id)) { @@ -6062,8 +6062,8 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) int nbytes = 0, err; uint64_t id; struct ksmbd_rpc_command *rpc_resp; - struct smb2_read_req *req = REQUEST_BUF(work); - struct smb2_read_rsp *rsp = RESPONSE_BUF(work); + struct smb2_read_req *req = work->request_buf; + struct smb2_read_rsp *rsp = work->response_buf; id = le64_to_cpu(req->VolatileFileId); @@ -6155,7 +6155,7 @@ int smb2_read(struct ksmbd_work *work) ssize_t nbytes = 0, remain_bytes = 0; int err = 0; - rsp_org = RESPONSE_BUF(work); + rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, @@ -6212,10 +6212,10 @@ int smb2_read(struct ksmbd_work *work) if ((nbytes == 0 && length != 0) || nbytes < mincount) { if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) - ksmbd_release_buffer(AUX_PAYLOAD(work)); + ksmbd_release_buffer(work->aux_payload_buf); else - ksmbd_free_response(AUX_PAYLOAD(work)); - INIT_AUX_PAYLOAD(work); + ksmbd_free_response(work->aux_payload_buf); + work->aux_payload_buf = NULL; rsp->hdr.Status = STATUS_END_OF_FILE; smb2_set_err_rsp(work); ksmbd_fd_put(work, fp); @@ -6229,12 +6229,12 @@ int smb2_read(struct ksmbd_work *work) req->Channel == SMB2_CHANNEL_RDMA_V1) { /* write data to the client using rdma channel */ remain_bytes = smb2_read_rdma_channel(work, req, - AUX_PAYLOAD(work), nbytes); + work->aux_payload_buf, nbytes); if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) - ksmbd_release_buffer(AUX_PAYLOAD(work)); + ksmbd_release_buffer(work->aux_payload_buf); else - ksmbd_free_response(AUX_PAYLOAD(work)); - INIT_AUX_PAYLOAD(work); + ksmbd_free_response(work->aux_payload_buf); + work->aux_payload_buf = NULL; nbytes = 0; if (remain_bytes < 0) { @@ -6287,8 +6287,8 @@ out: */ static noinline int smb2_write_pipe(struct ksmbd_work *work) { - struct smb2_write_req *req = REQUEST_BUF(work); - struct smb2_write_rsp *rsp = RESPONSE_BUF(work); + struct smb2_write_req *req = work->request_buf; + struct smb2_write_rsp *rsp = work->response_buf; struct ksmbd_rpc_command *rpc_resp; uint64_t id = 0; int err = 0, ret = 0; @@ -6418,7 +6418,7 @@ int smb2_write(struct ksmbd_work *work) bool writethrough = false; int err = 0; - rsp_org = RESPONSE_BUF(work); + rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, @@ -6548,7 +6548,7 @@ int smb2_flush(struct ksmbd_work *work) struct smb2_flush_rsp *rsp, *rsp_org; int err; - rsp_org = RESPONSE_BUF(work); + rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", @@ -6583,7 +6583,7 @@ out: int smb2_cancel(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *hdr = REQUEST_BUF(work); + struct smb2_hdr *hdr = work->request_buf; struct smb2_hdr *chdr; struct ksmbd_work *cancel_work = NULL; struct list_head *tmp; @@ -6600,7 +6600,7 @@ int smb2_cancel(struct ksmbd_work *work) list_for_each(tmp, command_list) { cancel_work = list_entry(tmp, struct ksmbd_work, async_request_entry); - chdr = REQUEST_BUF(cancel_work); + chdr = cancel_work->request_buf; if (cancel_work->async_id != le64_to_cpu(hdr->Id.AsyncId)) @@ -6621,7 +6621,7 @@ int smb2_cancel(struct ksmbd_work *work) list_for_each(tmp, command_list) { cancel_work = list_entry(tmp, struct ksmbd_work, request_entry); - chdr = REQUEST_BUF(cancel_work); + chdr = cancel_work->request_buf; if (chdr->MessageId != hdr->MessageId || cancel_work == work) @@ -6754,8 +6754,8 @@ static inline bool lock_defer_pending(struct file_lock *fl) */ int smb2_lock(struct ksmbd_work *work) { - struct smb2_lock_req *req = REQUEST_BUF(work); - struct smb2_lock_rsp *rsp = RESPONSE_BUF(work); + struct smb2_lock_req *req = work->request_buf; + struct smb2_lock_rsp *rsp = work->response_buf; struct smb2_lock_element *lock_ele; struct ksmbd_file *fp = NULL; struct file_lock *flock = NULL; @@ -7504,7 +7504,7 @@ int smb2_ioctl(struct ksmbd_work *work) struct ksmbd_conn *conn = work->conn; int ret = 0; - rsp_org = RESPONSE_BUF(work); + rsp_org = work->response_buf; if (work->next_smb2_rcv_hdr_off) { req = REQUEST_BUF_NEXT(work); rsp = RESPONSE_BUF_NEXT(work); @@ -7514,8 +7514,8 @@ int smb2_ioctl(struct ksmbd_work *work) id = work->compound_fid; } } else { - req = REQUEST_BUF(work); - rsp = RESPONSE_BUF(work); + req = work->request_buf; + rsp = work->response_buf; } if (!HAS_FILE_ID(id)) @@ -7725,8 +7725,8 @@ out: */ static void smb20_oplock_break_ack(struct ksmbd_work *work) { - struct smb2_oplock_break *req = REQUEST_BUF(work); - struct smb2_oplock_break *rsp = RESPONSE_BUF(work); + struct smb2_oplock_break *req = work->request_buf; + struct smb2_oplock_break *rsp = work->response_buf; struct ksmbd_file *fp; struct oplock_info *opinfo = NULL; __le32 err = 0; @@ -7867,8 +7867,8 @@ static int check_lease_state(struct lease *lease, __le32 req_state) static void smb21_lease_break_ack(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_lease_ack *req = REQUEST_BUF(work); - struct smb2_lease_ack *rsp = RESPONSE_BUF(work); + struct smb2_lease_ack *req = work->request_buf; + struct smb2_lease_ack *rsp = work->response_buf; struct oplock_info *opinfo; __le32 err = 0; int ret = 0; @@ -8000,8 +8000,8 @@ err_out: */ int smb2_oplock_break(struct ksmbd_work *work) { - struct smb2_oplock_break *req = REQUEST_BUF(work); - struct smb2_oplock_break *rsp = RESPONSE_BUF(work); + struct smb2_oplock_break *req = work->request_buf; + struct smb2_oplock_break *rsp = work->response_buf; switch (le16_to_cpu(req->StructureSize)) { case OP_BREAK_STRUCT_SIZE_20: @@ -8053,7 +8053,7 @@ int smb2_notify(struct ksmbd_work *work) */ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) { - struct smb2_hdr *rcv_hdr2 = REQUEST_BUF(work); + struct smb2_hdr *rcv_hdr2 = work->request_buf; if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && command != SMB2_NEGOTIATE_HE && @@ -8078,7 +8078,7 @@ int smb2_check_sign_req(struct ksmbd_work *work) struct kvec iov[1]; size_t len; - hdr_org = hdr = REQUEST_BUF(work); + hdr_org = hdr = work->request_buf; if (work->next_smb2_rcv_hdr_off) hdr = REQUEST_BUF_NEXT(work); @@ -8122,7 +8122,7 @@ void smb2_set_sign_rsp(struct ksmbd_work *work) size_t len; int n_vec = 1; - hdr_org = hdr = RESPONSE_BUF(work); + hdr_org = hdr = work->response_buf; if (work->next_smb2_rsp_hdr_off) hdr = RESPONSE_BUF_NEXT(work); @@ -8146,11 +8146,11 @@ void smb2_set_sign_rsp(struct ksmbd_work *work) iov[0].iov_base = (char *)&hdr->ProtocolId; iov[0].iov_len = len; - if (HAS_AUX_PAYLOAD(work)) { - iov[0].iov_len -= AUX_PAYLOAD_SIZE(work); + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; - iov[1].iov_base = AUX_PAYLOAD(work); - iov[1].iov_len = AUX_PAYLOAD_SIZE(work); + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; n_vec++; } @@ -8176,7 +8176,7 @@ int smb3_check_sign_req(struct ksmbd_work *work) struct kvec iov[1]; size_t len; - hdr_org = hdr = REQUEST_BUF(work); + hdr_org = hdr = work->request_buf; if (work->next_smb2_rcv_hdr_off) hdr = REQUEST_BUF_NEXT(work); @@ -8237,7 +8237,7 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) size_t len; char *signing_key; - hdr_org = hdr = RESPONSE_BUF(work); + hdr_org = hdr = work->response_buf; if (work->next_smb2_rsp_hdr_off) hdr = RESPONSE_BUF_NEXT(work); @@ -8273,10 +8273,10 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); iov[0].iov_base = (char *)&hdr->ProtocolId; iov[0].iov_len = len; - if (HAS_AUX_PAYLOAD(work)) { - iov[0].iov_len -= AUX_PAYLOAD_SIZE(work); - iov[1].iov_base = AUX_PAYLOAD(work); - iov[1].iov_len = AUX_PAYLOAD_SIZE(work); + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; n_vec++; } @@ -8336,11 +8336,11 @@ static void fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, int smb3_encrypt_resp(struct ksmbd_work *work) { - char *buf = RESPONSE_BUF(work); + char *buf = work->response_buf; struct smb2_transform_hdr *tr_hdr; struct kvec iov[3]; int rc = -ENOMEM; - int buf_size = 0, rq_nvec = 2 + (HAS_AUX_PAYLOAD(work) ? 1 : 0); + int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); if (ARRAY_SIZE(iov) < rq_nvec) return -ENOMEM; @@ -8358,11 +8358,11 @@ int smb3_encrypt_resp(struct ksmbd_work *work) iov[1].iov_base = buf + 4; iov[1].iov_len = get_rfc1002_len(buf); - if (HAS_AUX_PAYLOAD(work)) { - iov[1].iov_len = RESP_HDR_SIZE(work) - 4; + if (work->aux_payload_sz) { + iov[1].iov_len = work->resp_hdr_sz - 4; - iov[2].iov_base = AUX_PAYLOAD(work); - iov[2].iov_len = AUX_PAYLOAD_SIZE(work); + iov[2].iov_base = work->aux_payload_buf; + iov[2].iov_len = work->aux_payload_sz; buf_size += iov[2].iov_len; } buf_size += iov[1].iov_len; @@ -8390,7 +8390,7 @@ int smb3_decrypt_req(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess; - char *buf = REQUEST_BUF(work); + char *buf = work->request_buf; struct smb2_hdr *hdr; unsigned int pdu_length = get_rfc1002_len(buf); struct kvec iov[2]; @@ -8437,7 +8437,7 @@ int smb3_decrypt_req(struct ksmbd_work *work) bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *rsp = RESPONSE_BUF(work); + struct smb2_hdr *rsp = work->response_buf; if (conn->dialect < SMB30_PROT_ID) return false; diff --git a/fs/cifsd/smb_common.c b/fs/cifsd/smb_common.c index 2f58ef003238..da1928b948f8 100644 --- a/fs/cifsd/smb_common.c +++ b/fs/cifsd/smb_common.c @@ -116,7 +116,7 @@ int ksmbd_lookup_protocol_idx(char *str) */ int ksmbd_verify_smb_message(struct ksmbd_work *work) { - struct smb2_hdr *smb2_hdr = REQUEST_BUF(work); + struct smb2_hdr *smb2_hdr = work->request_buf; if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) return ksmbd_smb2_check_message(work); @@ -408,7 +408,7 @@ static int __smb2_negotiate(struct ksmbd_conn *conn) static int smb_handle_negotiate(struct ksmbd_work *work) { - struct smb_negotiate_rsp *neg_rsp = RESPONSE_BUF(work); + struct smb_negotiate_rsp *neg_rsp = work->response_buf; ksmbd_debug(SMB, "Unsupported SMB protocol\n"); neg_rsp->hdr.Status.CifsError = STATUS_INVALID_LOGON_TYPE; @@ -420,11 +420,11 @@ int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) struct ksmbd_conn *conn = work->conn; int ret; - conn->dialect = ksmbd_negotiate_smb_dialect(REQUEST_BUF(work)); + conn->dialect = ksmbd_negotiate_smb_dialect(work->request_buf); ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); if (command == SMB2_NEGOTIATE_HE) { - struct smb2_hdr *smb2_hdr = REQUEST_BUF(work); + struct smb2_hdr *smb2_hdr = work->request_buf; if (smb2_hdr->ProtocolId != SMB2_PROTO_NUMBER) { ksmbd_debug(SMB, "Downgrade to SMB1 negotiation\n"); diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 3d7413b8f526..e860ff9145a7 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -372,7 +372,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, char namebuf[NAME_MAX]; int ret; - rbuf = AUX_PAYLOAD(work); + rbuf = work->aux_payload_buf; filp = fp->filp; inode = d_inode(filp->f_path.dentry); if (S_ISDIR(inode->i_mode)) From 69f447be15130b57cc00fa0a5c2d3fa949a46165 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 30 Mar 2021 12:38:03 +0900 Subject: [PATCH 027/417] cifsd: fix wrong use of rw semaphore in __session_create() Adding list to session table should be protected by down_write/up_write(). Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/mgmt/user_session.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index afcdf76a3851..1b71a20dacdb 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -298,9 +298,9 @@ static struct ksmbd_session *__session_create(int protocol) goto error; if (protocol == CIFDS_SESSION_FLAG_SMB2) { - down_read(&sessions_table_lock); + down_write(&sessions_table_lock); hash_add(sessions_table, &sess->hlist, sess->id); - up_read(&sessions_table_lock); + up_write(&sessions_table_lock); } return sess; From 20ea7fd2ac7513c90b5d0675360298ca6722593d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 30 Mar 2021 12:40:47 +0900 Subject: [PATCH 028/417] cifsd: use kmalloc() for small allocations Just use kmalloc() for small allocations. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/buffer_pool.c | 2 +- fs/cifsd/mgmt/share_config.c | 4 ++-- fs/cifsd/mgmt/user_config.c | 4 ++-- fs/cifsd/mgmt/user_session.c | 4 ++-- fs/cifsd/oplock.c | 2 +- fs/cifsd/smb2pdu.c | 4 ++-- fs/cifsd/transport_tcp.c | 2 +- fs/cifsd/vfs_cache.c | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/cifsd/buffer_pool.c b/fs/cifsd/buffer_pool.c index 864fea547c68..91c04879e931 100644 --- a/fs/cifsd/buffer_pool.c +++ b/fs/cifsd/buffer_pool.c @@ -63,7 +63,7 @@ static int register_wm_size_class(size_t sz) { struct wm_list *l, *nl; - nl = kvmalloc(sizeof(struct wm_list), GFP_KERNEL); + nl = kmalloc(sizeof(struct wm_list), GFP_KERNEL); if (!nl) return -ENOMEM; diff --git a/fs/cifsd/mgmt/share_config.c b/fs/cifsd/mgmt/share_config.c index 9bc7f7555ee2..db780febd692 100644 --- a/fs/cifsd/mgmt/share_config.c +++ b/fs/cifsd/mgmt/share_config.c @@ -92,7 +92,7 @@ static int parse_veto_list(struct ksmbd_share_config *share, while (veto_list_sz > 0) { struct ksmbd_veto_pattern *p; - p = ksmbd_alloc(sizeof(struct ksmbd_veto_pattern)); + p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); if (!p) return -ENOMEM; @@ -129,7 +129,7 @@ static struct ksmbd_share_config *share_config_request(char *name) if (resp->flags == KSMBD_SHARE_FLAG_INVALID) goto out; - share = ksmbd_alloc(sizeof(struct ksmbd_share_config)); + share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL); if (!share) goto out; diff --git a/fs/cifsd/mgmt/user_config.c b/fs/cifsd/mgmt/user_config.c index a1a454bfb57b..f0c2f8994a6b 100644 --- a/fs/cifsd/mgmt/user_config.c +++ b/fs/cifsd/mgmt/user_config.c @@ -31,7 +31,7 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) { struct ksmbd_user *user = NULL; - user = ksmbd_alloc(sizeof(struct ksmbd_user)); + user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL); if (!user) return NULL; @@ -40,7 +40,7 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) user->gid = resp->gid; user->uid = resp->uid; user->passkey_sz = resp->hash_sz; - user->passkey = ksmbd_alloc(resp->hash_sz); + user->passkey = kmalloc(resp->hash_sz, GFP_KERNEL); if (user->passkey) memcpy(user->passkey, resp->hash, resp->hash_sz); diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index 1b71a20dacdb..5a2113bf18ef 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -101,7 +101,7 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) if (!method) return -EINVAL; - entry = ksmbd_alloc(sizeof(struct ksmbd_session_rpc)); + entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL); if (!entry) return -EINVAL; @@ -266,7 +266,7 @@ static struct ksmbd_session *__session_create(int protocol) struct ksmbd_session *sess; int ret; - sess = ksmbd_alloc(sizeof(struct ksmbd_session)); + sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL); if (!sess) return NULL; diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index 25823bb7d086..d76aa47e19e4 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -593,7 +593,7 @@ static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) static inline int allocate_oplock_break_buf(struct ksmbd_work *work) { - work->response_buf = ksmbd_alloc_response(MAX_CIFS_SMALL_BUFFER_SIZE); + work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); if (!work->response_buf) return -ENOMEM; work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 460d5ba275bf..a1aa42b52597 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -1174,7 +1174,7 @@ static int alloc_preauth_hash(struct ksmbd_session *sess, if (sess->Preauth_HashValue) return 0; - sess->Preauth_HashValue = ksmbd_alloc(PREAUTH_HASHVALUE_SIZE); + sess->Preauth_HashValue = kmalloc(PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); if (!sess->Preauth_HashValue) return -ENOMEM; @@ -8345,7 +8345,7 @@ int smb3_encrypt_resp(struct ksmbd_work *work) if (ARRAY_SIZE(iov) < rq_nvec) return -ENOMEM; - tr_hdr = ksmbd_alloc_response(sizeof(struct smb2_transform_hdr)); + tr_hdr = kzalloc(sizeof(struct smb2_transform_hdr), GFP_KERNEL); if (!tr_hdr) return rc; diff --git a/fs/cifsd/transport_tcp.c b/fs/cifsd/transport_tcp.c index 359401227d93..5dd8641f66ba 100644 --- a/fs/cifsd/transport_tcp.c +++ b/fs/cifsd/transport_tcp.c @@ -569,7 +569,7 @@ static struct interface *alloc_iface(char *ifname) if (!ifname) return NULL; - iface = ksmbd_alloc(sizeof(struct interface)); + iface = kzalloc(sizeof(struct interface), GFP_KERNEL); if (!iface) { kfree(ifname); return NULL; diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index 34e045f27230..2b38628e1cb8 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -830,7 +830,7 @@ int ksmbd_file_table_flush(struct ksmbd_work *work) int ksmbd_init_file_table(struct ksmbd_file_table *ft) { - ft->idr = ksmbd_alloc(sizeof(struct idr)); + ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); if (!ft->idr) return -ENOMEM; From c36fca8630dda0fba7b9672f3c99ac4e260a0fd0 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 30 Mar 2021 12:43:18 +0900 Subject: [PATCH 029/417] cifsd: add the check to work file lock and rename behaviors like Windows unless POSIX extensions are negotiated This patch add the check to work file lock and rename behaviors like Windows if POSIX extensions are not negotiated. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 99 ++++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index e860ff9145a7..f93cc55ea153 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -370,7 +370,6 @@ int ksmbd_vfs_read(struct ksmbd_work *work, char *rbuf, *name; struct inode *inode; char namebuf[NAME_MAX]; - int ret; rbuf = work->aux_payload_buf; filp = fp->filp; @@ -391,11 +390,15 @@ int ksmbd_vfs_read(struct ksmbd_work *work, if (ksmbd_stream_fd(fp)) return ksmbd_vfs_stream_read(fp, rbuf, pos, count); - ret = check_lock_range(filp, *pos, *pos + count - 1, - READ); - if (ret) { - ksmbd_err("unable to read due to lock\n"); - return -EAGAIN; + if (!work->tcon->posix_extensions) { + int ret; + + ret = check_lock_range(filp, *pos, *pos + count - 1, + READ); + if (ret) { + ksmbd_err("unable to read due to lock\n"); + return -EAGAIN; + } } nbytes = kernel_read(filp, rbuf, count, pos); @@ -504,11 +507,13 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, goto out; } - err = check_lock_range(filp, *pos, *pos + count - 1, WRITE); - if (err) { - ksmbd_err("unable to write due to lock\n"); - err = -EAGAIN; - goto out; + if (!work->tcon->posix_extensions) { + err = check_lock_range(filp, *pos, *pos + count - 1, WRITE); + if (err) { + ksmbd_err("unable to write due to lock\n"); + err = -EAGAIN; + goto out; + } } /* Do we need to break any of a levelII oplock? */ @@ -706,21 +711,23 @@ static int __ksmbd_vfs_rename(struct ksmbd_work *work, struct dentry *dst_dent; int err; - spin_lock(&src_dent->d_lock); - list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { - struct ksmbd_file *child_fp; + if (!work->tcon->posix_extensions) { + spin_lock(&src_dent->d_lock); + list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { + struct ksmbd_file *child_fp; - if (d_really_is_negative(dst_dent)) - continue; + if (d_really_is_negative(dst_dent)) + continue; - child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); - if (child_fp) { - spin_unlock(&src_dent->d_lock); - ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); - return -EACCES; + child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); + if (child_fp) { + spin_unlock(&src_dent->d_lock); + ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); + return -EACCES; + } } + spin_unlock(&src_dent->d_lock); } - spin_unlock(&src_dent->d_lock); if (d_really_is_negative(src_dent_parent)) return -ENOENT; @@ -820,7 +827,6 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, { struct path path; int err = 0; - struct inode *inode; if (name) { err = kern_path(name, 0, &path); @@ -842,18 +848,21 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, /* Do we need to break any of a levelII oplock? */ smb_break_all_levII_oplock(work, fp, 1); - inode = file_inode(filp); - if (size < inode->i_size) { - err = check_lock_range(filp, size, - inode->i_size - 1, WRITE); - } else { - err = check_lock_range(filp, inode->i_size, - size - 1, WRITE); - } + if (!work->tcon->posix_extensions) { + struct inode *inode = file_inode(filp); - if (err) { - ksmbd_err("failed due to lock\n"); - return -EAGAIN; + if (size < inode->i_size) { + err = check_lock_range(filp, size, + inode->i_size - 1, WRITE); + } else { + err = check_lock_range(filp, inode->i_size, + size - 1, WRITE); + } + + if (err) { + ksmbd_err("failed due to lock\n"); + return -EAGAIN; + } } err = vfs_truncate(&filp->f_path, size); @@ -1860,17 +1869,19 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, smb_break_all_levII_oplock(work, dst_fp, 1); - for (i = 0; i < chunk_count; i++) { - src_off = le64_to_cpu(chunks[i].SourceOffset); - dst_off = le64_to_cpu(chunks[i].TargetOffset); - len = le32_to_cpu(chunks[i].Length); + if (!work->tcon->posix_extensions) { + for (i = 0; i < chunk_count; i++) { + src_off = le64_to_cpu(chunks[i].SourceOffset); + dst_off = le64_to_cpu(chunks[i].TargetOffset); + len = le32_to_cpu(chunks[i].Length); - if (check_lock_range(src_fp->filp, src_off, - src_off + len - 1, READ)) - return -EAGAIN; - if (check_lock_range(dst_fp->filp, dst_off, - dst_off + len - 1, WRITE)) - return -EAGAIN; + if (check_lock_range(src_fp->filp, src_off, + src_off + len - 1, READ)) + return -EAGAIN; + if (check_lock_range(dst_fp->filp, dst_off, + dst_off + len - 1, WRITE)) + return -EAGAIN; + } } src_file_size = i_size_read(file_inode(src_fp->filp)); From 7cb82de3cdf2da0acd6fc3e670c7271ded37e116 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 30 Mar 2021 12:58:26 +0900 Subject: [PATCH 030/417] cifsd: fix error return code in ksmbd_vfs_remove_file() Change -ENOENT error to -EINVAL to response STATUS_INVALID_PARAMETER. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index f93cc55ea153..da44d131e25b 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -596,7 +596,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) last = extract_last_component(name); if (!last) - return -ENOENT; + return -EINVAL; if (ksmbd_override_fsids(work)) return -ENOMEM; From 64b39f4a2fd293cf899dd8062c57ce3715dd7ee9 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 30 Mar 2021 14:25:35 +0900 Subject: [PATCH 031/417] cifsd: clean-up codes using chechpatch.pl --strict Dan Carpenter suggested to run chechpatch.pl --strict on ksmbd to fix check warnings. This patch does not fix all warnings but only things that I can understand. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 117 ++--- fs/cifsd/buffer_pool.c | 3 +- fs/cifsd/connection.c | 23 +- fs/cifsd/connection.h | 34 +- fs/cifsd/glob.h | 14 +- fs/cifsd/ksmbd_server.h | 94 ++-- fs/cifsd/mgmt/user_session.h | 4 +- fs/cifsd/misc.c | 9 +- fs/cifsd/ndr.c | 9 +- fs/cifsd/netmisc.c | 2 - fs/cifsd/oplock.c | 118 +++-- fs/cifsd/oplock.h | 24 +- fs/cifsd/server.c | 35 +- fs/cifsd/smb2misc.c | 81 ++-- fs/cifsd/smb2ops.c | 2 +- fs/cifsd/smb2pdu.c | 827 +++++++++++++++-------------------- fs/cifsd/smb2pdu.h | 123 +++--- fs/cifsd/smb_common.c | 68 ++- fs/cifsd/smb_common.h | 4 +- fs/cifsd/smbacl.c | 85 ++-- fs/cifsd/time_wrappers.h | 4 +- fs/cifsd/transport_ipc.c | 46 +- fs/cifsd/transport_ipc.h | 32 +- fs/cifsd/transport_rdma.c | 165 ++++--- fs/cifsd/transport_tcp.c | 27 +- fs/cifsd/unicode.c | 40 +- fs/cifsd/unicode.h | 80 ++-- fs/cifsd/vfs.c | 158 +++---- fs/cifsd/vfs.h | 89 ++-- fs/cifsd/vfs_cache.c | 57 +-- fs/cifsd/vfs_cache.h | 35 +- 31 files changed, 1008 insertions(+), 1401 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 0a49c67a69d6..b9fd62f77e1c 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -64,7 +64,6 @@ static char NEGOTIATE_GSS_HEADER[AUTH_GSS_LENGTH] = { #endif }; - void ksmbd_copy_gss_neg_header(void *buf) { memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); @@ -107,9 +106,7 @@ smbhash(unsigned char *out, const unsigned char *in, unsigned char *key) return 0; } -static int ksmbd_enc_p24(unsigned char *p21, - const unsigned char *c8, - unsigned char *p24) +static int ksmbd_enc_p24(unsigned char *p21, const unsigned char *c8, unsigned char *p24) { int rc; @@ -124,9 +121,8 @@ static int ksmbd_enc_p24(unsigned char *p21, } /* produce a md4 message digest from data of length n bytes */ -static int ksmbd_enc_md4(unsigned char *md4_hash, - unsigned char *link_str, - int link_len) +static int ksmbd_enc_md4(unsigned char *md4_hash, unsigned char *link_str, + int link_len) { int rc; struct ksmbd_crypto_ctx *ctx; @@ -157,10 +153,8 @@ out: return rc; } -static int ksmbd_enc_update_sess_key(unsigned char *md5_hash, - char *nonce, - char *server_challenge, - int len) +static int ksmbd_enc_update_sess_key(unsigned char *md5_hash, char *nonce, + char *server_challenge, int len) { int rc; struct ksmbd_crypto_ctx *ctx; @@ -204,9 +198,8 @@ out: * @hmac: source hmac value to be used for finding session key * */ -static int ksmbd_gen_sess_key(struct ksmbd_session *sess, - char *hash, - char *hmac) +static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, + char *hmac) { struct ksmbd_crypto_ctx *ctx; int rc = -EINVAL; @@ -251,7 +244,7 @@ out: } static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, - char *dname) + char *dname) { int ret = -EINVAL, len; wchar_t *domain = NULL; @@ -361,8 +354,9 @@ int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf) if (strncmp(pw_buf, key, CIFS_AUTH_RESP_SIZE) != 0) { ksmbd_debug(AUTH, "ntlmv1 authentication failed\n"); rc = -EINVAL; - } else + } else { ksmbd_debug(AUTH, "ntlmv1 authentication pass\n"); + } return rc; } @@ -376,10 +370,8 @@ int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf) * * Return: 0 on success, error number on error */ -int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, - struct ntlmv2_resp *ntlmv2, - int blen, - char *domain_name) +int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, + int blen, char *domain_name) { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; @@ -457,9 +449,8 @@ out: * * Return: 0 on success, error number on error */ -static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess, - char *client_nonce, - char *ntlm_resp) +static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess, char *client_nonce, + char *ntlm_resp) { char sess_key[CIFS_SMB1_SESSKEY_SIZE] = {0}; int rc; @@ -497,8 +488,7 @@ out: * Return: 0 on success, error number on error */ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, - int blob_len, - struct ksmbd_session *sess) + int blob_len, struct ksmbd_session *sess) { char *domain_name; unsigned int lm_off, nt_off; @@ -523,8 +513,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, /* process NTLM authentication */ if (nt_len == CIFS_AUTH_RESP_SIZE) { - if (le32_to_cpu(authblob->NegotiateFlags) - & NTLMSSP_NEGOTIATE_EXTENDED_SEC) + if (le32_to_cpu(authblob->NegotiateFlags) & + NTLMSSP_NEGOTIATE_EXTENDED_SEC) return __ksmbd_auth_ntlmv2(sess, (char *)authblob + lm_off, (char *)authblob + nt_off); else @@ -533,8 +523,7 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, } /* TODO : use domain name that imported from configuration file */ - domain_name = smb_strndup_from_utf16( - (const char *)authblob + + domain_name = smb_strndup_from_utf16((const char *)authblob + le32_to_cpu(authblob->DomainName.BufferOffset), le16_to_cpu(authblob->DomainName.Length), true, sess->conn->local_nls); @@ -561,8 +550,7 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, * */ int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, - int blob_len, - struct ksmbd_session *sess) + int blob_len, struct ksmbd_session *sess) { if (blob_len < sizeof(struct negotiate_message)) { ksmbd_debug(AUTH, "negotiate blob len %d too small\n", @@ -618,7 +606,7 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, flags |= NTLMSSP_REQUEST_TARGET; if (sess->conn->use_spnego && - (cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) + (cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) flags |= NTLMSSP_NEGOTIATE_EXTENDED_SEC; chgblob->NegotiateFlags = cpu_to_le32(flags); @@ -675,9 +663,8 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, } #ifdef CONFIG_SMB_SERVER_KERBEROS5 -int ksmbd_krb5_authenticate(struct ksmbd_session *sess, - char *in_blob, int in_len, - char *out_blob, int *out_len) +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len) { struct ksmbd_spnego_authen_response *resp; struct ksmbd_user *user = NULL; @@ -726,9 +713,8 @@ out: return retval; } #else -int ksmbd_krb5_authenticate(struct ksmbd_session *sess, - char *in_blob, int in_len, - char *out_blob, int *out_len) +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len) { return -EOPNOTSUPP; } @@ -743,11 +729,8 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, * @sig: signature value generated for client request packet * */ -int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, - char *key, - struct kvec *iov, - int n_vec, - char *sig) +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) { struct ksmbd_crypto_ctx *ctx; int rc = -EINVAL; @@ -798,11 +781,8 @@ out: * @sig: signature value generated for client request packet * */ -int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, - char *key, - struct kvec *iov, - int n_vec, - char *sig) +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) { struct ksmbd_crypto_ctx *ctx; int rc = -EINVAL; @@ -851,7 +831,7 @@ struct derivation { }; static int generate_key(struct ksmbd_session *sess, struct kvec label, - struct kvec context, __u8 *key, unsigned int key_size) + struct kvec context, __u8 *key, unsigned int key_size) { unsigned char zero = 0x0; __u8 i[4] = {0, 0, 0, 1}; @@ -931,7 +911,7 @@ smb3signkey_ret: } static int generate_smb3signingkey(struct ksmbd_session *sess, - const struct derivation *signing) + const struct derivation *signing) { int rc; struct channel *chann; @@ -995,7 +975,7 @@ struct derivation_twin { }; static int generate_smb3encryptionkey(struct ksmbd_session *sess, - const struct derivation_twin *ptwin) + const struct derivation_twin *ptwin) { int rc; @@ -1062,9 +1042,8 @@ int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess) return generate_smb3encryptionkey(sess, &twin); } -int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, - char *buf, - __u8 *pi_hash) +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, + __u8 *pi_hash) { int rc = -1; struct smb2_hdr *rcv_hdr = (struct smb2_hdr *)buf; @@ -1073,14 +1052,15 @@ int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, struct ksmbd_crypto_ctx *ctx = NULL; if (conn->preauth_info->Preauth_HashId == - SMB2_PREAUTH_INTEGRITY_SHA512) { + SMB2_PREAUTH_INTEGRITY_SHA512) { ctx = ksmbd_crypto_ctx_find_sha512(); if (!ctx) { ksmbd_debug(AUTH, "could not alloc sha512 rc %d\n", rc); goto out; } - } else + } else { goto out; + } rc = crypto_shash_init(CRYPTO_SHA512(ctx)); if (rc) { @@ -1144,10 +1124,8 @@ out: return rc; } -static int ksmbd_get_encryption_key(struct ksmbd_conn *conn, - __u64 ses_id, - int enc, - u8 *key) +static int ksmbd_get_encryption_key(struct ksmbd_conn *conn, __u64 ses_id, + int enc, u8 *key) { struct ksmbd_session *sess; u8 *ses_enc_key; @@ -1175,9 +1153,8 @@ static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, sg_set_page(sg, addr, buflen, offset_in_page(buf)); } -static struct scatterlist *ksmbd_init_sg(struct kvec *iov, - unsigned int nvec, - u8 *sign) +static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, + u8 *sign) { struct scatterlist *sg; unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; @@ -1190,8 +1167,9 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, nr_entries[i] = ((kaddr + iov[i + 1].iov_len + PAGE_SIZE - 1) >> PAGE_SHIFT) - (kaddr >> PAGE_SHIFT); - } else + } else { nr_entries[i]++; + } total_entries += nr_entries[i]; } @@ -1232,16 +1210,13 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, sg_set_page(&sg[sg_idx++], virt_to_page(data), len, offset_in_page(data)); } - } smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); return sg; } -int ksmbd_crypt_message(struct ksmbd_conn *conn, - struct kvec *iov, - unsigned int nvec, - int enc) +int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, + unsigned int nvec, int enc) { struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)iov[0].iov_base; @@ -1319,9 +1294,9 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, goto free_sg; } - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM) + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM) { memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES128GCM_NONCE); - else { + } else { iv[0] = 3; memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES128CCM_NONCE); } diff --git a/fs/cifsd/buffer_pool.c b/fs/cifsd/buffer_pool.c index 91c04879e931..ad2a2c885a2c 100644 --- a/fs/cifsd/buffer_pool.c +++ b/fs/cifsd/buffer_pool.c @@ -278,8 +278,7 @@ int ksmbd_init_buffer_pools(void) goto out; filp_cache = kmem_cache_create("ksmbd_file_cache", - sizeof(struct ksmbd_file), 0, - SLAB_HWCACHE_ALIGN, NULL); + sizeof(struct ksmbd_file), 0, SLAB_HWCACHE_ALIGN, NULL); if (!filp_cache) goto out; diff --git a/fs/cifsd/connection.c b/fs/cifsd/connection.c index bdfde5ca2ded..df56e347b709 100644 --- a/fs/cifsd/connection.c +++ b/fs/cifsd/connection.c @@ -119,7 +119,7 @@ int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) int ret = 1; if (list_empty(&work->request_entry) && - list_empty(&work->async_request_entry)) + list_empty(&work->async_request_entry)) return 0; atomic_dec(&conn->req_running); @@ -201,10 +201,9 @@ int ksmbd_conn_write(struct ksmbd_work *work) return 0; } -int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - u32 remote_key, u64 remote_offset, - u32 remote_len) +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, void *buf, + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len) { int ret = -EINVAL; @@ -216,10 +215,9 @@ int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, return ret; } -int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - u32 remote_key, u64 remote_offset, - u32 remote_len) +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, void *buf, + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len) { int ret = -EINVAL; @@ -251,7 +249,7 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn) * zero. */ if (server_conf.deadtime > 0 && - time_after(jiffies, conn->last_active + server_conf.deadtime)) { + time_after(jiffies, conn->last_active + server_conf.deadtime)) { ksmbd_debug(CONN, "No response from client in %lu minutes\n", server_conf.deadtime / SMB_ECHO_INTERVAL); return false; @@ -393,14 +391,13 @@ again: task = conn->transport->handler; if (task) ksmbd_debug(CONN, "Stop session handler %s/%d\n", - task->comm, - task_pid_nr(task)); + task->comm, task_pid_nr(task)); conn->status = KSMBD_SESS_EXITING; } read_unlock(&conn_list_lock); if (!list_empty(&conn_list)) { - schedule_timeout_interruptible(HZ/10); /* 100ms */ + schedule_timeout_interruptible(HZ / 10); /* 100ms */ goto again; } } diff --git a/fs/cifsd/connection.h b/fs/cifsd/connection.h index 179fb9278999..021dada3d76d 100644 --- a/fs/cifsd/connection.h +++ b/fs/cifsd/connection.h @@ -116,17 +116,15 @@ struct ksmbd_conn_ops { struct ksmbd_transport_ops { int (*prepare)(struct ksmbd_transport *t); void (*disconnect)(struct ksmbd_transport *t); - int (*read)(struct ksmbd_transport *t, - char *buf, unsigned int size); - int (*writev)(struct ksmbd_transport *t, - struct kvec *iovs, int niov, int size, - bool need_invalidate_rkey, unsigned int remote_key); - int (*rdma_read)(struct ksmbd_transport *t, - void *buf, unsigned int len, u32 remote_key, - u64 remote_offset, u32 remote_len); - int (*rdma_write)(struct ksmbd_transport *t, - void *buf, unsigned int len, u32 remote_key, - u64 remote_offset, u32 remote_len); + int (*read)(struct ksmbd_transport *t, char *buf, unsigned int size); + int (*writev)(struct ksmbd_transport *t, struct kvec *iovs, int niov, + int size, bool need_invalidate_rkey, + unsigned int remote_key); + int (*rdma_read)(struct ksmbd_transport *t, void *buf, unsigned int len, + u32 remote_key, u64 remote_offset, u32 remote_len); + int (*rdma_write)(struct ksmbd_transport *t, void *buf, + unsigned int len, u32 remote_key, u64 remote_offset, + u32 remote_len); }; struct ksmbd_transport { @@ -146,14 +144,12 @@ struct ksmbd_conn *ksmbd_conn_alloc(void); void ksmbd_conn_free(struct ksmbd_conn *conn); bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); int ksmbd_conn_write(struct ksmbd_work *work); -int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - u32 remote_key, u64 remote_offset, - u32 remote_len); -int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - u32 remote_key, u64 remote_offset, - u32 remote_len); +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, void *buf, + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len); +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, void *buf, + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len); void ksmbd_conn_enqueue_request(struct ksmbd_work *work); int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); diff --git a/fs/cifsd/glob.h b/fs/cifsd/glob.h index 2dc3f603e837..27500afbeaf5 100644 --- a/fs/cifsd/glob.h +++ b/fs/cifsd/glob.h @@ -24,13 +24,13 @@ extern int ksmbd_caseless_search; #define DATA_STREAM 1 #define DIR_STREAM 2 -#define KSMBD_DEBUG_SMB (1 << 0) -#define KSMBD_DEBUG_AUTH (1 << 1) -#define KSMBD_DEBUG_VFS (1 << 2) -#define KSMBD_DEBUG_OPLOCK (1 << 3) -#define KSMBD_DEBUG_IPC (1 << 4) -#define KSMBD_DEBUG_CONN (1 << 5) -#define KSMBD_DEBUG_RDMA (1 << 6) +#define KSMBD_DEBUG_SMB BIT(0) +#define KSMBD_DEBUG_AUTH BIT(1) +#define KSMBD_DEBUG_VFS BIT(2) +#define KSMBD_DEBUG_OPLOCK BIT(3) +#define KSMBD_DEBUG_IPC BIT(4) +#define KSMBD_DEBUG_CONN BIT(5) +#define KSMBD_DEBUG_RDMA BIT(6) #define KSMBD_DEBUG_ALL (KSMBD_DEBUG_SMB | KSMBD_DEBUG_AUTH | \ KSMBD_DEBUG_VFS | KSMBD_DEBUG_OPLOCK | \ KSMBD_DEBUG_IPC | KSMBD_DEBUG_CONN | \ diff --git a/fs/cifsd/ksmbd_server.h b/fs/cifsd/ksmbd_server.h index 01eaf9ec4fde..c5181a2702ff 100644 --- a/fs/cifsd/ksmbd_server.h +++ b/fs/cifsd/ksmbd_server.h @@ -29,11 +29,11 @@ struct ksmbd_heartbeat { * Global config flags. */ #define KSMBD_GLOBAL_FLAG_INVALID (0) -#define KSMBD_GLOBAL_FLAG_SMB2_LEASES (1 << 0) -#define KSMBD_GLOBAL_FLAG_CACHE_TBUF (1 << 1) -#define KSMBD_GLOBAL_FLAG_CACHE_RBUF (1 << 2) -#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION (1 << 3) -#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE (1 << 4) +#define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0) +#define KSMBD_GLOBAL_FLAG_CACHE_TBUF BIT(1) +#define KSMBD_GLOBAL_FLAG_CACHE_RBUF BIT(2) +#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(3) +#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE BIT(4) struct ksmbd_startup_request { __u32 flags; @@ -204,66 +204,66 @@ enum KSMBD_TREE_CONN_STATUS { * User config flags. */ #define KSMBD_USER_FLAG_INVALID (0) -#define KSMBD_USER_FLAG_OK (1 << 0) -#define KSMBD_USER_FLAG_BAD_PASSWORD (1 << 1) -#define KSMBD_USER_FLAG_BAD_UID (1 << 2) -#define KSMBD_USER_FLAG_BAD_USER (1 << 3) -#define KSMBD_USER_FLAG_GUEST_ACCOUNT (1 << 4) +#define KSMBD_USER_FLAG_OK BIT(0) +#define KSMBD_USER_FLAG_BAD_PASSWORD BIT(1) +#define KSMBD_USER_FLAG_BAD_UID BIT(2) +#define KSMBD_USER_FLAG_BAD_USER BIT(3) +#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) /* * Share config flags. */ #define KSMBD_SHARE_FLAG_INVALID (0) -#define KSMBD_SHARE_FLAG_AVAILABLE (1 << 0) -#define KSMBD_SHARE_FLAG_BROWSEABLE (1 << 1) -#define KSMBD_SHARE_FLAG_WRITEABLE (1 << 2) -#define KSMBD_SHARE_FLAG_READONLY (1 << 3) -#define KSMBD_SHARE_FLAG_GUEST_OK (1 << 4) -#define KSMBD_SHARE_FLAG_GUEST_ONLY (1 << 5) -#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS (1 << 6) -#define KSMBD_SHARE_FLAG_OPLOCKS (1 << 7) -#define KSMBD_SHARE_FLAG_PIPE (1 << 8) -#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES (1 << 9) -#define KSMBD_SHARE_FLAG_INHERIT_SMACK (1 << 10) -#define KSMBD_SHARE_FLAG_INHERIT_OWNER (1 << 11) -#define KSMBD_SHARE_FLAG_STREAMS (1 << 12) -#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS (1 << 13) -#define KSMBD_SHARE_FLAG_ACL_XATTR (1 << 14) +#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0) +#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1) +#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2) +#define KSMBD_SHARE_FLAG_READONLY BIT(3) +#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4) +#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5) +#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6) +#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) +#define KSMBD_SHARE_FLAG_PIPE BIT(8) +#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) +#define KSMBD_SHARE_FLAG_INHERIT_SMACK BIT(10) +#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(11) +#define KSMBD_SHARE_FLAG_STREAMS BIT(12) +#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(13) +#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(14) /* * Tree connect request flags. */ #define KSMBD_TREE_CONN_FLAG_REQUEST_SMB1 (0) -#define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6 (1 << 0) -#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2 (1 << 1) +#define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6 BIT(0) +#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2 BIT(1) /* * Tree connect flags. */ -#define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT (1 << 0) -#define KSMBD_TREE_CONN_FLAG_READ_ONLY (1 << 1) -#define KSMBD_TREE_CONN_FLAG_WRITABLE (1 << 2) -#define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT (1 << 3) +#define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT BIT(0) +#define KSMBD_TREE_CONN_FLAG_READ_ONLY BIT(1) +#define KSMBD_TREE_CONN_FLAG_WRITABLE BIT(2) +#define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT BIT(3) /* * RPC over IPC. */ -#define KSMBD_RPC_METHOD_RETURN (1 << 0) -#define KSMBD_RPC_SRVSVC_METHOD_INVOKE (1 << 1) -#define KSMBD_RPC_SRVSVC_METHOD_RETURN ((1 << 1) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_WKSSVC_METHOD_INVOKE (1 << 2) -#define KSMBD_RPC_WKSSVC_METHOD_RETURN ((1 << 2) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_IOCTL_METHOD ((1 << 3) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_OPEN_METHOD (1 << 4) -#define KSMBD_RPC_WRITE_METHOD (1 << 5) -#define KSMBD_RPC_READ_METHOD ((1 << 6) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_CLOSE_METHOD (1 << 7) -#define KSMBD_RPC_RAP_METHOD ((1 << 8) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_RESTRICTED_CONTEXT (1 << 9) -#define KSMBD_RPC_SAMR_METHOD_INVOKE (1 << 10) -#define KSMBD_RPC_SAMR_METHOD_RETURN ((1 << 10) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_LSARPC_METHOD_INVOKE (1 << 11) -#define KSMBD_RPC_LSARPC_METHOD_RETURN ((1 << 11) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_METHOD_RETURN BIT(0) +#define KSMBD_RPC_SRVSVC_METHOD_INVOKE BIT(1) +#define KSMBD_RPC_SRVSVC_METHOD_RETURN (KSMBD_RPC_SRVSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_WKSSVC_METHOD_INVOKE BIT(2) +#define KSMBD_RPC_WKSSVC_METHOD_RETURN (KSMBD_RPC_WKSSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_IOCTL_METHOD (BIT(3) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_OPEN_METHOD BIT(4) +#define KSMBD_RPC_WRITE_METHOD BIT(5) +#define KSMBD_RPC_READ_METHOD (BIT(6) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_CLOSE_METHOD BIT(7) +#define KSMBD_RPC_RAP_METHOD (BIT(8) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_RESTRICTED_CONTEXT BIT(9) +#define KSMBD_RPC_SAMR_METHOD_INVOKE BIT(10) +#define KSMBD_RPC_SAMR_METHOD_RETURN (KSMBD_RPC_SAMR_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_LSARPC_METHOD_INVOKE BIT(11) +#define KSMBD_RPC_LSARPC_METHOD_RETURN (KSMBD_RPC_LSARPC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) #define KSMBD_RPC_OK 0 #define KSMBD_RPC_EBAD_FUNC 0x00000001 diff --git a/fs/cifsd/mgmt/user_session.h b/fs/cifsd/mgmt/user_session.h index 0a6eb21647ab..68018f0f5c0b 100644 --- a/fs/cifsd/mgmt/user_session.h +++ b/fs/cifsd/mgmt/user_session.h @@ -26,12 +26,12 @@ struct channel { struct preauth_session { __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; - uint64_t sess_id; + u64 sess_id; struct list_head list_entry; }; struct ksmbd_session { - uint64_t id; + u64 id; struct ksmbd_user *user; struct ksmbd_conn *conn; diff --git a/fs/cifsd/misc.c b/fs/cifsd/misc.c index 189b90414976..b6f3f0818217 100644 --- a/fs/cifsd/misc.c +++ b/fs/cifsd/misc.c @@ -78,9 +78,9 @@ static inline int is_char_allowed(char ch) { /* check for control chars, wildcards etc. */ if (!(ch & 0x80) && - (ch <= 0x1f || - ch == '?' || ch == '"' || ch == '<' || - ch == '>' || ch == '|' || ch == '*')) + (ch <= 0x1f || + ch == '?' || ch == '"' || ch == '<' || + ch == '>' || ch == '|' || ch == '*')) return 0; return 1; @@ -268,8 +268,7 @@ char *convert_to_unix_name(struct ksmbd_share_config *share, char *name) } char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, - const struct nls_table *local_nls, - int *conv_len) + const struct nls_table *local_nls, int *conv_len) { char *conv; int sz = min(4 * d_info->name_len, PATH_MAX); diff --git a/fs/cifsd/ndr.c b/fs/cifsd/ndr.c index aa0cb8fc555d..1838fc2b1d7c 100644 --- a/fs/cifsd/ndr.c +++ b/fs/cifsd/ndr.c @@ -159,8 +159,9 @@ int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) ndr_write_int32(n, da->ea_size); ndr_write_int64(n, da->size); ndr_write_int64(n, da->alloc_size); - } else + } else { ndr_write_int64(n, da->itime); + } ndr_write_int64(n, da->create_time); if (da->version == 3) ndr_write_int64(n, da->change_time); @@ -248,15 +249,17 @@ int ndr_encode_posix_acl(struct ndr *n, struct inode *inode, /* ACL ACCESS */ ndr_write_int32(n, ref_id); ref_id += 4; - } else + } else { ndr_write_int32(n, 0); + } if (def_acl) { /* DEFAULT ACL ACCESS */ ndr_write_int32(n, ref_id); ref_id += 4; - } else + } else { ndr_write_int32(n, 0); + } ndr_write_int64(n, from_kuid(&init_user_ns, inode->i_uid)); ndr_write_int64(n, from_kgid(&init_user_ns, inode->i_gid)); diff --git a/fs/cifsd/netmisc.c b/fs/cifsd/netmisc.c index 6f7dd78348a3..55393667abcc 100644 --- a/fs/cifsd/netmisc.c +++ b/fs/cifsd/netmisc.c @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * fs/ksmbd/netmisc.c - * * Copyright (c) International Business Machines Corp., 2002,2008 * Author(s): Steve French (sfrench@us.ibm.com) * diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index d76aa47e19e4..8e072c3e7b89 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -29,7 +29,7 @@ static DEFINE_RWLOCK(lease_list_lock); * Return: allocated opinfo object on success, otherwise NULL */ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, - uint64_t id, __u16 Tid) + u64 id, __u16 Tid) { struct ksmbd_session *sess = work->sess; struct oplock_info *opinfo; @@ -89,8 +89,7 @@ static void lb_add(struct lease_table *lb) write_unlock(&lease_list_lock); } -static int alloc_lease(struct oplock_info *opinfo, - struct lease_ctx_info *lctx) +static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) { struct lease *lease; @@ -227,8 +226,8 @@ int opinfo_write_to_read(struct oplock_info *opinfo) { struct lease *lease = opinfo->o_lease; - if (!((opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) || - (opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE))) { + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { ksmbd_err("bad oplock(0x%x)\n", opinfo->level); if (opinfo->is_lease) ksmbd_err("lease state(0x%x)\n", lease->state); @@ -266,8 +265,8 @@ int opinfo_write_to_none(struct oplock_info *opinfo) { struct lease *lease = opinfo->o_lease; - if (!((opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) || - (opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE))) { + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { ksmbd_err("bad oplock(0x%x)\n", opinfo->level); if (opinfo->is_lease) ksmbd_err("lease state(0x%x)\n", @@ -334,8 +333,7 @@ int lease_read_to_write(struct oplock_info *opinfo) * * Return: 0 on success, otherwise -EINVAL */ -static int lease_none_upgrade(struct oplock_info *opinfo, - __le32 new_state) +static int lease_none_upgrade(struct oplock_info *opinfo, __le32 new_state) { struct lease *lease = opinfo->o_lease; @@ -401,7 +399,7 @@ void close_id_del_oplock(struct ksmbd_file *fp) * Return: 0 */ static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, - struct lease_ctx_info *lctx) + struct lease_ctx_info *lctx) { struct lease *lease = opinfo_new->o_lease; @@ -425,7 +423,7 @@ static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, * Return: 0 */ static void grant_read_oplock(struct oplock_info *opinfo_new, - struct lease_ctx_info *lctx) + struct lease_ctx_info *lctx) { struct lease *lease = opinfo_new->o_lease; @@ -448,7 +446,7 @@ static void grant_read_oplock(struct oplock_info *opinfo_new, * Return: 0 */ static void grant_none_oplock(struct oplock_info *opinfo_new, - struct lease_ctx_info *lctx) + struct lease_ctx_info *lctx) { struct lease *lease = opinfo_new->o_lease; @@ -469,7 +467,7 @@ static inline int compare_guid_key(struct oplock_info *opinfo, guid2 = opinfo->conn->ClientGUID; key2 = opinfo->o_lease->lease_key; if (!memcmp(guid1, guid2, SMB2_CLIENT_GUID_SIZE) && - !memcmp(key1, key2, SMB2_LEASE_KEY_SIZE)) + !memcmp(key1, key2, SMB2_LEASE_KEY_SIZE)) return 1; return 0; @@ -485,7 +483,7 @@ static inline int compare_guid_key(struct oplock_info *opinfo, * Return: oplock(lease) object on success, otherwise NULL */ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, - char *client_guid, struct lease_ctx_info *lctx) + char *client_guid, struct lease_ctx_info *lctx) { int ret; struct lease *lease; @@ -649,13 +647,12 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = work->response_buf; rsp->StructureSize = cpu_to_le16(24); if (!br_info->open_trunc && - (br_info->level == SMB2_OPLOCK_LEVEL_BATCH || - br_info->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + (br_info->level == SMB2_OPLOCK_LEVEL_BATCH || + br_info->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) rsp->OplockLevel = SMB2_OPLOCK_LEVEL_II; else rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; @@ -845,10 +842,8 @@ static void wait_lease_breaking(struct oplock_info *opinfo) if (atomic_read(&opinfo->breaking_cnt)) { int ret = 0; - ret = wait_event_interruptible_timeout( - opinfo->oplock_brk, - atomic_read(&opinfo->breaking_cnt) == 0, - HZ); + ret = wait_event_interruptible_timeout(opinfo->oplock_brk, + atomic_read(&opinfo->breaking_cnt) == 0, HZ); if (!ret) atomic_set(&opinfo->breaking_cnt, 0); } @@ -907,7 +902,7 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) return err < 0 ? err : 0; if (brk_opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || - brk_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) + brk_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) brk_opinfo->op_state = OPLOCK_ACK_WAIT; } @@ -939,7 +934,7 @@ void destroy_lease_table(struct ksmbd_conn *conn) list_for_each_entry_safe(lb, lbtmp, &lease_table_list, l_entry) { if (conn && memcmp(lb->client_guid, conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) + SMB2_CLIENT_GUID_SIZE)) continue; again: rcu_read_lock(); @@ -974,7 +969,7 @@ int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, list_for_each_entry(lb, &lease_table_list, l_entry) { if (!memcmp(lb->client_guid, sess->conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) + SMB2_CLIENT_GUID_SIZE)) goto found; } read_unlock(&lease_list_lock); @@ -1031,7 +1026,7 @@ static int add_lease_global_list(struct oplock_info *opinfo) read_lock(&lease_list_lock); list_for_each_entry(lb, &lease_table_list, l_entry) { if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) { + SMB2_CLIENT_GUID_SIZE)) { opinfo->o_lease->l_lb = lb; lease_add_list(opinfo); read_unlock(&lease_list_lock); @@ -1055,13 +1050,12 @@ static int add_lease_global_list(struct oplock_info *opinfo) } static void set_oplock_level(struct oplock_info *opinfo, int level, - struct lease_ctx_info *lctx) + struct lease_ctx_info *lctx) { switch (level) { case SMB2_OPLOCK_LEVEL_BATCH: case SMB2_OPLOCK_LEVEL_EXCLUSIVE: - grant_write_oplock(opinfo, - level, lctx); + grant_write_oplock(opinfo, level, lctx); break; case SMB2_OPLOCK_LEVEL_II: grant_read_oplock(opinfo, lctx); @@ -1084,13 +1078,9 @@ static void set_oplock_level(struct oplock_info *opinfo, int level, * * Return: 0 on success, otherwise error */ -int smb_grant_oplock(struct ksmbd_work *work, - int req_op_level, - uint64_t pid, - struct ksmbd_file *fp, - __u16 tid, - struct lease_ctx_info *lctx, - int share_ret) +int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, + struct ksmbd_file *fp, __u16 tid, struct lease_ctx_info *lctx, + int share_ret) { struct ksmbd_session *sess = work->sess; int err = 0; @@ -1150,14 +1140,14 @@ int smb_grant_oplock(struct ksmbd_work *work, prev_op_state = prev_opinfo->o_lease->state; if (share_ret < 0 && - (prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { err = share_ret; opinfo_put(prev_opinfo); goto err_out; } - if ((prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) && - (prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && + prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { opinfo_put(prev_opinfo); goto op_break_not_needed; } @@ -1218,7 +1208,7 @@ err_out: * @is_trunc: truncate on open */ static void smb_break_all_write_oplock(struct ksmbd_work *work, - struct ksmbd_file *fp, int is_trunc) + struct ksmbd_file *fp, int is_trunc) { struct oplock_info *brk_opinfo; @@ -1226,7 +1216,7 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work, if (!brk_opinfo) return; if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && - brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { opinfo_put(brk_opinfo); return; } @@ -1244,17 +1234,16 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work, * @fp: ksmbd file pointer * @is_trunc: truncate on open */ -void smb_break_all_levII_oplock(struct ksmbd_work *work, - struct ksmbd_file *fp, int is_trunc) +void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, + int is_trunc) { struct oplock_info *op, *brk_op; struct ksmbd_inode *ci; struct ksmbd_conn *conn = work->sess->conn; if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_OPLOCKS)) { + KSMBD_SHARE_FLAG_OPLOCKS)) return; - } ci = fp->f_ci; op = opinfo_get(fp); @@ -1283,13 +1272,11 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, atomic_read(&brk_op->breaking_cnt)) goto next; - if (op && op->is_lease && - brk_op->is_lease && - !memcmp(conn->ClientGUID, brk_op->conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE) && - !memcmp(op->o_lease->lease_key, - brk_op->o_lease->lease_key, - SMB2_LEASE_KEY_SIZE)) + if (op && op->is_lease && brk_op->is_lease && + !memcmp(conn->ClientGUID, brk_op->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE) && + !memcmp(op->o_lease->lease_key, brk_op->o_lease->lease_key, + SMB2_LEASE_KEY_SIZE)) goto next; brk_op->open_trunc = is_trunc; oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE); @@ -1311,7 +1298,7 @@ next: void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) { if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_OPLOCKS)) + KSMBD_SHARE_FLAG_OPLOCKS)) return; smb_break_all_write_oplock(work, fp, 1); @@ -1327,14 +1314,16 @@ void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) __u8 smb2_map_lease_to_oplock(__le32 lease_state) { if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE | - SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_WRITE_CACHING_LE)) + SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE)) { return SMB2_OPLOCK_LEVEL_BATCH; - else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE && - lease_state & SMB2_LEASE_WRITE_CACHING_LE) { + } else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE && + lease_state & SMB2_LEASE_WRITE_CACHING_LE) { if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) return SMB2_OPLOCK_LEVEL_EXCLUSIVE; - } else if (lease_state & SMB2_LEASE_READ_CACHING_LE) + } else if (lease_state & SMB2_LEASE_READ_CACHING_LE) { return SMB2_OPLOCK_LEVEL_II; + } return 0; } @@ -1390,7 +1379,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req) cc = (struct create_context *)((char *)cc + next); name = le16_to_cpu(cc->NameOffset) + (char *)cc; if (le16_to_cpu(cc->NameLength) != 4 || - strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) { + strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) { next = le32_to_cpu(cc->Next); continue; } @@ -1603,7 +1592,7 @@ void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) * Return: opinfo if found matching opinfo, otherwise NULL */ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, - char *lease_key) + char *lease_key) { struct oplock_info *opinfo = NULL, *ret_op = NULL; struct lease_table *lt; @@ -1612,7 +1601,7 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, read_lock(&lease_list_lock); list_for_each_entry(lt, &lease_table_list, l_entry) { if (!memcmp(lt->client_guid, conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) + SMB2_CLIENT_GUID_SIZE)) goto found; } @@ -1625,12 +1614,11 @@ found: if (!atomic_inc_not_zero(&opinfo->refcount)) continue; rcu_read_unlock(); - if (!opinfo->op_state || - opinfo->op_state == OPLOCK_CLOSING) + if (!opinfo->op_state || opinfo->op_state == OPLOCK_CLOSING) goto op_next; if (!(opinfo->o_lease->state & - (SMB2_LEASE_HANDLE_CACHING_LE | - SMB2_LEASE_WRITE_CACHING_LE))) + (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE))) goto op_next; ret = compare_guid_key(opinfo, conn->ClientGUID, lease_key); @@ -1651,7 +1639,7 @@ out: } int smb2_check_durable_oplock(struct ksmbd_file *fp, - struct lease_ctx_info *lctx, char *name) + struct lease_ctx_info *lctx, char *name) { struct oplock_info *opinfo = opinfo_get(fp); int ret = 0; @@ -1663,7 +1651,7 @@ int smb2_check_durable_oplock(struct ksmbd_file *fp, goto out; } if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key, - SMB2_LEASE_KEY_SIZE)) { + SMB2_LEASE_KEY_SIZE)) { ksmbd_err("invalid lease key\n"); ret = -EBADF; goto out; diff --git a/fs/cifsd/oplock.h b/fs/cifsd/oplock.h index b0e2e795f29a..5b6615f99c76 100644 --- a/fs/cifsd/oplock.h +++ b/fs/cifsd/oplock.h @@ -9,7 +9,7 @@ #include "smb_common.h" -#define OPLOCK_WAIT_TIME (35*HZ) +#define OPLOCK_WAIT_TIME (35 * HZ) /* SMB Oplock levels */ #define OPLOCK_NONE 0 @@ -68,7 +68,7 @@ struct oplock_info { int level; int op_state; unsigned long pending_break; - uint64_t fid; + u64 fid; atomic_t breaking_cnt; atomic_t refcount; __u16 Tid; @@ -95,11 +95,11 @@ struct oplock_break_info { int fid; }; -extern int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, - uint64_t pid, struct ksmbd_file *fp, __u16 tid, +int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, + u64 pid, struct ksmbd_file *fp, __u16 tid, struct lease_ctx_info *lctx, int share_ret); -extern void smb_break_all_levII_oplock(struct ksmbd_work *work, - struct ksmbd_file *fp, int is_trunc); +void smb_break_all_levII_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc); int opinfo_write_to_read(struct oplock_info *opinfo); int opinfo_read_handle_to_read(struct oplock_info *opinfo); @@ -124,15 +124,13 @@ void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); struct create_context *smb2_find_context_vals(void *open_req, const char *str); int ksmbd_durable_verify_and_del_oplock(struct ksmbd_session *curr_sess, - struct ksmbd_session *prev_sess, - int fid, struct file **filp, - uint64_t sess_id); + struct ksmbd_session *prev_sess, int fid, struct file **filp, + u64 sess_id); struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, - char *lease_key); + char *lease_key); int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, - struct lease_ctx_info *lctx); + struct lease_ctx_info *lctx); void destroy_lease_table(struct ksmbd_conn *conn); int smb2_check_durable_oplock(struct ksmbd_file *fp, - struct lease_ctx_info *lctx, char *name); - + struct lease_ctx_info *lctx, char *name); #endif /* __KSMBD_OPLOCK_H */ diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c index 60027e74f0ed..3e858100d5a6 100644 --- a/fs/cifsd/server.c +++ b/fs/cifsd/server.c @@ -105,9 +105,8 @@ static inline int check_conn_state(struct ksmbd_work *work) #define TCP_HANDLER_CONTINUE 0 #define TCP_HANDLER_ABORT 1 -static int __process_request(struct ksmbd_work *work, - struct ksmbd_conn *conn, - uint16_t *cmd) +static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, + uint16_t *cmd) { struct smb_version_cmds *cmds; uint16_t command; @@ -160,16 +159,16 @@ andx_again: } static void __handle_ksmbd_work(struct ksmbd_work *work, - struct ksmbd_conn *conn) + struct ksmbd_conn *conn) { - uint16_t command = 0; + u16 command = 0; int rc; if (conn->ops->allocate_rsp_buf(work)) return; if (conn->ops->is_transform_hdr && - conn->ops->is_transform_hdr(work->request_buf)) { + conn->ops->is_transform_hdr(work->request_buf)) { rc = conn->ops->decrypt_req(work); if (rc < 0) { conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); @@ -224,7 +223,7 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, } if (work->sess && (work->sess->sign || - smb3_11_final_sess_setup_resp(work) || + smb3_11_final_sess_setup_resp(work) || conn->ops->is_sign_req(work, command))) conn->ops->set_sign_rsp(work); } while (is_chained_smb2_message(work)); @@ -235,7 +234,7 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, send: smb3_preauth_hash_rsp(work); if (work->sess && work->sess->enc && work->encrypted && - conn->ops->encrypt_resp) { + conn->ops->encrypt_resp) { rc = conn->ops->encrypt_resp(work); if (rc < 0) { conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); @@ -416,9 +415,8 @@ int server_queue_ctrl_reset_work(void) return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET); } -static ssize_t stats_show(struct class *class, - struct class_attribute *attr, - char *buf) +static ssize_t stats_show(struct class *class, struct class_attribute *attr, + char *buf) { /* * Inc this each time you change stats output format, @@ -443,9 +441,8 @@ static ssize_t stats_show(struct class *class, } static ssize_t kill_server_store(struct class *class, - struct class_attribute *attr, - const char *buf, - size_t len) + struct class_attribute *attr, const char *buf, + size_t len) { if (!sysfs_streq(buf, "hard")) return len; @@ -464,8 +461,7 @@ static const char * const debug_type_strings[] = {"smb", "auth", "vfs", "oplock", "ipc", "conn", "rdma"}; -static ssize_t debug_show(struct class *class, - struct class_attribute *attr, +static ssize_t debug_show(struct class *class, struct class_attribute *attr, char *buf) { ssize_t sz = 0; @@ -484,16 +480,13 @@ static ssize_t debug_show(struct class *class, debug_type_strings[i]); } sz += pos; - } sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); return sz; } -static ssize_t debug_store(struct class *class, - struct class_attribute *attr, - const char *buf, - size_t len) +static ssize_t debug_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t len) { int i; diff --git a/fs/cifsd/smb2misc.c b/fs/cifsd/smb2misc.c index e6b87d9d33ed..5a50c4ec43f2 100644 --- a/fs/cifsd/smb2misc.c +++ b/fs/cifsd/smb2misc.c @@ -90,8 +90,7 @@ static char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) /* error reqeusts do not have data area */ if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED && - (((struct smb2_err_rsp *)hdr)->StructureSize) == - SMB2_ERROR_STRUCTURE_SIZE2_LE) + (((struct smb2_err_rsp *)hdr)->StructureSize) == SMB2_ERROR_STRUCTURE_SIZE2_LE) return NULL; /* @@ -101,16 +100,12 @@ static char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) */ switch (hdr->Command) { case SMB2_SESSION_SETUP: - *off = le16_to_cpu( - ((struct smb2_sess_setup_req *)hdr)->SecurityBufferOffset); - *len = le16_to_cpu( - ((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); + *off = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferOffset); + *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); break; case SMB2_TREE_CONNECT: - *off = le16_to_cpu( - ((struct smb2_tree_connect_req *)hdr)->PathOffset); - *len = le16_to_cpu( - ((struct smb2_tree_connect_req *)hdr)->PathLength); + *off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset); + *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength); break; case SMB2_CREATE: { @@ -122,49 +117,35 @@ static char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) break; } - *off = le16_to_cpu( - ((struct smb2_create_req *)hdr)->NameOffset); - *len = le16_to_cpu( - ((struct smb2_create_req *)hdr)->NameLength); + *off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); + *len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); break; } case SMB2_QUERY_INFO: - *off = le16_to_cpu( - ((struct smb2_query_info_req *)hdr)->InputBufferOffset); - *len = le32_to_cpu( - ((struct smb2_query_info_req *)hdr)->InputBufferLength); + *off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset); + *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength); break; case SMB2_SET_INFO: - *off = le16_to_cpu( - ((struct smb2_set_info_req *)hdr)->BufferOffset); - *len = le32_to_cpu( - ((struct smb2_set_info_req *)hdr)->BufferLength); + *off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset); + *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength); break; case SMB2_READ: - *off = le16_to_cpu( - ((struct smb2_read_req *)hdr)->ReadChannelInfoOffset); - *len = le16_to_cpu( - ((struct smb2_read_req *)hdr)->ReadChannelInfoLength); + *off = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoOffset); + *len = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoLength); break; case SMB2_WRITE: if (((struct smb2_write_req *)hdr)->DataOffset) { - *off = le16_to_cpu( - ((struct smb2_write_req *)hdr)->DataOffset); - *len = le32_to_cpu( - ((struct smb2_write_req *)hdr)->Length); + *off = le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset); + *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); break; } - *off = le16_to_cpu( - ((struct smb2_write_req *)hdr)->WriteChannelInfoOffset); - *len = le16_to_cpu( - ((struct smb2_write_req *)hdr)->WriteChannelInfoLength); + *off = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoOffset); + *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength); break; case SMB2_QUERY_DIRECTORY: - *off = le16_to_cpu( - ((struct smb2_query_directory_req *)hdr)->FileNameOffset); - *len = le16_to_cpu( - ((struct smb2_query_directory_req *)hdr)->FileNameLength); + *off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset); + *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength); break; case SMB2_LOCK: { @@ -174,8 +155,7 @@ static char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) * smb2_lock request size is 48 included single * smb2_lock_element structure size. */ - lock_count = le16_to_cpu( - ((struct smb2_lock_req *)hdr)->LockCount) - 1; + lock_count = le16_to_cpu(((struct smb2_lock_req *)hdr)->LockCount) - 1; if (lock_count > 0) { *off = __SMB2_HEADER_STRUCTURE_SIZE + 48; *len = sizeof(struct smb2_lock_element) * lock_count; @@ -183,8 +163,7 @@ static char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) break; } case SMB2_IOCTL: - *off = le32_to_cpu( - ((struct smb2_ioctl_req *)hdr)->InputOffset); + *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset); *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); break; @@ -366,9 +345,9 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) hdr = &pdu->hdr; } - if (le32_to_cpu(hdr->NextCommand) > 0) + if (le32_to_cpu(hdr->NextCommand) > 0) { len = le32_to_cpu(hdr->NextCommand); - else if (work->next_smb2_rcv_hdr_off) { + } else if (work->next_smb2_rcv_hdr_off) { len -= work->next_smb2_rcv_hdr_off; len = round_up(len, 8); } @@ -389,19 +368,17 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) } if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { - if (command != SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0 || - pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { + if (command != SMB2_OPLOCK_BREAK_HE && + (hdr->Status == 0 || pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { /* error packets have 9 byte structure size */ ksmbd_debug(SMB, "Illegal request size %u for command %d\n", le16_to_cpu(pdu->StructureSize2), command); return 1; - } else if (command == SMB2_OPLOCK_BREAK_HE - && (hdr->Status == 0) - && (le16_to_cpu(pdu->StructureSize2) != - OP_BREAK_STRUCT_SIZE_20) - && (le16_to_cpu(pdu->StructureSize2) != - OP_BREAK_STRUCT_SIZE_21)) { + } else if (command == SMB2_OPLOCK_BREAK_HE && + hdr->Status == 0 && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) { /* special case for SMB2.1 lease break message */ ksmbd_debug(SMB, "Illegal request size %d for oplock break\n", diff --git a/fs/cifsd/smb2ops.c b/fs/cifsd/smb2ops.c index a47219ea3b80..945bc6a78d3c 100644 --- a/fs/cifsd/smb2ops.c +++ b/fs/cifsd/smb2ops.c @@ -248,7 +248,7 @@ void init_smb3_02_server(struct ksmbd_conn *conn) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && - conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; } diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index a1aa42b52597..3e8f1a3800dd 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -58,7 +58,7 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) * * Return: 1 if valid session id, otherwise 0 */ -static inline int check_session_id(struct ksmbd_conn *conn, uint64_t id) +static inline int check_session_id(struct ksmbd_conn *conn, u64 id) { struct ksmbd_session *sess; @@ -98,9 +98,9 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work) int tree_id; work->tcon = NULL; - if ((work->conn->ops->get_cmd_val(work) == SMB2_TREE_CONNECT_HE) || - (work->conn->ops->get_cmd_val(work) == SMB2_CANCEL_HE) || - (work->conn->ops->get_cmd_val(work) == SMB2_LOGOFF_HE)) { + if (work->conn->ops->get_cmd_val(work) == SMB2_TREE_CONNECT_HE || + work->conn->ops->get_cmd_val(work) == SMB2_CANCEL_HE || + work->conn->ops->get_cmd_val(work) == SMB2_LOGOFF_HE) { ksmbd_debug(SMB, "skip to check tree connect request\n"); return 0; } @@ -238,7 +238,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) if (conn->need_neg == false) return -EINVAL; if (!(conn->dialect >= SMB20_PROT_ID && - conn->dialect <= SMB311_PROT_ID)) + conn->dialect <= SMB311_PROT_ID)) return -EINVAL; rsp_hdr = work->response_buf; @@ -549,8 +549,8 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) req = work->request_buf; if (req->InfoType == SMB2_O_INFO_FILE && - (req->FileInfoClass == FILE_FULL_EA_INFORMATION || - req->FileInfoClass == FILE_ALL_INFORMATION)) { + (req->FileInfoClass == FILE_FULL_EA_INFORMATION || + req->FileInfoClass == FILE_ALL_INFORMATION)) { sz = large_sz; work->set_trans_buf = true; } @@ -595,7 +595,7 @@ int smb2_check_user_session(struct ksmbd_work *work) * these commands. */ if (cmd == SMB2_ECHO_HE || cmd == SMB2_NEGOTIATE_HE || - cmd == SMB2_SESSION_SETUP_HE) + cmd == SMB2_SESSION_SETUP_HE) return 0; if (!ksmbd_conn_good(work)) @@ -610,7 +610,7 @@ int smb2_check_user_session(struct ksmbd_work *work) return -EINVAL; } -static void destroy_previous_session(struct ksmbd_user *user, uint64_t id) +static void destroy_previous_session(struct ksmbd_user *user, u64 id) { struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); struct ksmbd_user *prev_user; @@ -641,15 +641,12 @@ static void destroy_previous_session(struct ksmbd_user *user, uint64_t id) * Return: matching converted filename on success, otherwise error ptr */ static char * -smb2_get_name(struct ksmbd_share_config *share, - const char *src, - const int maxlen, - struct nls_table *local_nls) +smb2_get_name(struct ksmbd_share_config *share, const char *src, + const int maxlen, struct nls_table *local_nls) { char *name, *unixname; - name = smb_strndup_from_utf16(src, maxlen, 1, - local_nls); + name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); if (IS_ERR(name)) { ksmbd_err("failed to get name %ld\n", PTR_ERR(name)); return name; @@ -756,10 +753,10 @@ static int smb2_get_dos_mode(struct kstat *stat, int attribute) { int attr = 0; - if (S_ISDIR(stat->mode)) + if (S_ISDIR(stat->mode)) { attr = ATTR_DIRECTORY | (attribute & (ATTR_HIDDEN | ATTR_SYSTEM)); - else { + } else { attr = (attribute & 0x00005137) | ATTR_ARCHIVE; attr &= ~(ATTR_DIRECTORY); if (S_ISREG(stat->mode) && (server_conf.share_fake_fscaps & @@ -774,7 +771,7 @@ static int smb2_get_dos_mode(struct kstat *stat, int attribute) } static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, - __le16 hash_id) + __le16 hash_id) { pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; pneg_ctxt->DataLength = cpu_to_le16(38); @@ -786,7 +783,7 @@ static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, } static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, - __le16 cipher_type) + __le16 cipher_type) { pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; pneg_ctxt->DataLength = cpu_to_le16(4); @@ -796,7 +793,7 @@ static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, } static void build_compression_ctxt(struct smb2_compression_ctx *pneg_ctxt, - __le16 comp_algo) + __le16 comp_algo) { pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; pneg_ctxt->DataLength = @@ -808,8 +805,7 @@ static void build_compression_ctxt(struct smb2_compression_ctx *pneg_ctxt, pneg_ctxt->CompressionAlgorithms[0] = comp_algo; } -static void -build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) +static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) { pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); @@ -832,9 +828,8 @@ build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) pneg_ctxt->Name[15] = 0x7C; } -static void -assemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_rsp *rsp) +static void assemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_rsp *rsp) { /* +4 is to account for the RFC1001 len field */ char *pneg_ctxt = (char *)rsp + @@ -856,8 +851,7 @@ assemble_neg_contexts(struct ksmbd_conn *conn, ctxt_size = round_up(ctxt_size, 8); ksmbd_debug(SMB, "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); - build_encrypt_ctxt( - (struct smb2_encryption_neg_context *)pneg_ctxt, + build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, conn->cipher_type); rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ctxt_size += sizeof(struct smb2_encryption_neg_context); @@ -892,9 +886,8 @@ assemble_neg_contexts(struct ksmbd_conn *conn, inc_rfc1001_len(rsp, ctxt_size); } -static __le32 -decode_preauth_ctxt(struct ksmbd_conn *conn, - struct smb2_preauth_neg_context *pneg_ctxt) +static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, + struct smb2_preauth_neg_context *pneg_ctxt) { __le32 err = STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; @@ -909,7 +902,7 @@ decode_preauth_ctxt(struct ksmbd_conn *conn, } static int decode_encrypt_ctxt(struct ksmbd_conn *conn, - struct smb2_encryption_neg_context *pneg_ctxt) + struct smb2_encryption_neg_context *pneg_ctxt) { int i; int cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); @@ -921,7 +914,7 @@ static int decode_encrypt_ctxt(struct ksmbd_conn *conn, for (i = 0; i < cph_cnt; i++) { if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM || - pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM) { + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM) { ksmbd_debug(SMB, "Cipher ID = 0x%x\n", pneg_ctxt->Ciphers[i]); conn->cipher_type = pneg_ctxt->Ciphers[i]; @@ -939,7 +932,7 @@ out: } static int decode_compress_ctxt(struct ksmbd_conn *conn, - struct smb2_compression_ctx *pneg_ctxt) + struct smb2_compression_ctx *pneg_ctxt) { int algo_cnt = le16_to_cpu(pneg_ctxt->CompressionAlgorithmCount); @@ -954,7 +947,7 @@ static int decode_compress_ctxt(struct ksmbd_conn *conn, } static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_req *req) + struct smb2_negotiate_req *req) { int i = 0; __le32 status = 0; @@ -976,8 +969,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, status = decode_preauth_ctxt(conn, (struct smb2_preauth_neg_context *)pneg_ctxt); - pneg_ctxt += DIV_ROUND_UP( - sizeof(struct smb2_preauth_neg_context), 8) * 8; + pneg_ctxt += DIV_ROUND_UP(sizeof(struct smb2_preauth_neg_context), 8) * 8; } else if (*ContextType == SMB2_ENCRYPTION_CAPABILITIES) { ksmbd_debug(SMB, "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); @@ -985,8 +977,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, break; ctxt_size = decode_encrypt_ctxt(conn, - (struct smb2_encryption_neg_context *) - pneg_ctxt); + (struct smb2_encryption_neg_context *)pneg_ctxt); pneg_ctxt += DIV_ROUND_UP(ctxt_size, 8) * 8; } else if (*ContextType == SMB2_COMPRESSION_CAPABILITIES) { ksmbd_debug(SMB, @@ -995,23 +986,20 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, break; ctxt_size = decode_compress_ctxt(conn, - (struct smb2_compression_ctx *) - pneg_ctxt); + (struct smb2_compression_ctx *) pneg_ctxt); pneg_ctxt += DIV_ROUND_UP(ctxt_size, 8) * 8; } else if (*ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { ksmbd_debug(SMB, "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); ctxt_size = sizeof(struct smb2_netname_neg_context); - ctxt_size += DIV_ROUND_UP( - le16_to_cpu(((struct smb2_netname_neg_context *) + ctxt_size += DIV_ROUND_UP(le16_to_cpu(((struct smb2_netname_neg_context *) pneg_ctxt)->DataLength), 8) * 8; pneg_ctxt += ctxt_size; } else if (*ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { ksmbd_debug(SMB, "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); conn->posix_ext_supported = true; - pneg_ctxt += DIV_ROUND_UP( - sizeof(struct smb2_posix_neg_context), 8) * 8; + pneg_ctxt += DIV_ROUND_UP(sizeof(struct smb2_posix_neg_context), 8) * 8; } ContextType = (__le16 *)pneg_ctxt; @@ -1149,8 +1137,8 @@ int smb2_handle_negotiate(struct ksmbd_work *work) conn->use_spnego = true; if ((server_conf.signing == KSMBD_CONFIG_OPT_AUTO || - server_conf.signing == KSMBD_CONFIG_OPT_DISABLED) && - req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED_LE) + server_conf.signing == KSMBD_CONFIG_OPT_DISABLED) && + req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED_LE) conn->sign = true; else if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) { server_conf.enforced_signing = true; @@ -1169,7 +1157,7 @@ err_out: } static int alloc_preauth_hash(struct ksmbd_session *sess, - struct ksmbd_conn *conn) + struct ksmbd_conn *conn) { if (sess->Preauth_HashValue) return 0; @@ -1204,7 +1192,7 @@ static int generate_preauth_hash(struct ksmbd_work *work) } static int decode_negotiation_token(struct ksmbd_work *work, - struct negotiate_message *negblob) + struct negotiate_message *negblob) { struct ksmbd_conn *conn = work->conn; struct smb2_sess_setup_req *req; @@ -1227,7 +1215,7 @@ static int decode_negotiation_token(struct ksmbd_work *work, } static int ntlm_negotiate(struct ksmbd_work *work, - struct negotiate_message *negblob) + struct negotiate_message *negblob) { struct smb2_sess_setup_req *req = work->request_buf; struct smb2_sess_setup_rsp *rsp = work->response_buf; @@ -1291,7 +1279,7 @@ out: } static struct authenticate_message *user_authblob(struct ksmbd_conn *conn, - struct smb2_sess_setup_req *req) + struct smb2_sess_setup_req *req) { int sz; @@ -1304,7 +1292,7 @@ static struct authenticate_message *user_authblob(struct ksmbd_conn *conn, } static struct ksmbd_user *session_user(struct ksmbd_conn *conn, - struct smb2_sess_setup_req *req) + struct smb2_sess_setup_req *req) { struct authenticate_message *authblob; struct ksmbd_user *user; @@ -1336,7 +1324,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) struct ksmbd_session *sess = work->sess; struct channel *chann = NULL; struct ksmbd_user *user; - uint64_t prev_id; + u64 prev_id; int sz, rc; ksmbd_debug(SMB, "authenticate phase\n"); @@ -1351,9 +1339,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) return -ENOMEM; sz = le16_to_cpu(rsp->SecurityBufferOffset); - memcpy((char *)&rsp->hdr.ProtocolId + sz, - spnego_blob, - spnego_blob_len); + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); kfree(spnego_blob); inc_rfc1001_len(rsp, spnego_blob_len - 1); @@ -1386,8 +1372,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) sess->user = user; if (user_guest(sess->user)) { if (conn->sign) { - ksmbd_debug(SMB, - "Guest login not allowed when signing enabled\n"); + ksmbd_debug(SMB, "Guest login not allowed when signing enabled\n"); rsp->hdr.Status = STATUS_LOGON_FAILURE; return -EACCES; } @@ -1415,11 +1400,11 @@ static int ntlm_authenticate(struct ksmbd_work *work) return 0; if ((conn->sign || server_conf.enforced_signing) || - (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) sess->sign = true; if (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION && - conn->ops->generate_encryptionkey) { + conn->ops->generate_encryptionkey) { rc = conn->ops->generate_encryptionkey(sess); if (rc) { ksmbd_debug(SMB, @@ -1453,8 +1438,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) if (conn->ops->generate_signingkey) { rc = conn->ops->generate_signingkey(sess); if (rc) { - ksmbd_debug(SMB, - "SMB3 signing key generation failed\n"); + ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); rsp->hdr.Status = STATUS_LOGON_FAILURE; return rc; } @@ -1479,7 +1463,7 @@ static int krb5_authenticate(struct ksmbd_work *work) struct ksmbd_session *sess = work->sess; char *in_blob, *out_blob; struct channel *chann = NULL; - uint64_t prev_sess_id; + u64 prev_sess_id; int in_len, out_len; int retval; @@ -1511,11 +1495,11 @@ static int krb5_authenticate(struct ksmbd_work *work) inc_rfc1001_len(rsp, out_len - 1); if ((conn->sign || server_conf.enforced_signing) || - (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) sess->sign = true; if ((conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) && - conn->ops->generate_encryptionkey) { + conn->ops->generate_encryptionkey) { retval = conn->ops->generate_encryptionkey(sess); if (retval) { ksmbd_debug(SMB, @@ -1544,8 +1528,7 @@ static int krb5_authenticate(struct ksmbd_work *work) if (conn->ops->generate_signingkey) { retval = conn->ops->generate_signingkey(sess); if (retval) { - ksmbd_debug(SMB, - "SMB3 signing key generation failed\n"); + ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); rsp->hdr.Status = STATUS_LOGON_FAILURE; return retval; } @@ -1646,9 +1629,7 @@ int smb2_sess_setup(struct ksmbd_work *work) * Note: here total size -1 is done as an * adjustment for 0 size blob */ - inc_rfc1001_len(rsp, - le16_to_cpu(rsp->SecurityBufferLength) - - 1); + inc_rfc1001_len(rsp, le16_to_cpu(rsp->SecurityBufferLength) - 1); } else if (negblob->MessageType == NtLmAuthenticate) { rc = ntlm_authenticate(work); @@ -1704,7 +1685,7 @@ int smb2_tree_connect(struct ksmbd_work *work) int rc = -EINVAL; treename = smb_strndup_from_utf16(req->Buffer, - le16_to_cpu(req->PathLength), true, conn->local_nls); + le16_to_cpu(req->PathLength), true, conn->local_nls); if (IS_ERR(treename)) { ksmbd_err("treename is NULL\n"); status.ret = KSMBD_TREE_CONN_STATUS_ERROR; @@ -1808,7 +1789,7 @@ static int smb2_create_open_flags(bool file_present, __le32 access, int oflags = O_NONBLOCK | O_LARGEFILE; if (access & FILE_READ_DESIRED_ACCESS_LE && - access & FILE_WRITE_DESIRE_ACCESS_LE) + access & FILE_WRITE_DESIRE_ACCESS_LE) oflags |= O_RDWR; else if (access & FILE_WRITE_DESIRE_ACCESS_LE) oflags |= O_WRONLY; @@ -1995,13 +1976,13 @@ struct durable_info { }; static int parse_durable_handle_context(struct ksmbd_work *work, - struct smb2_create_req *req, struct lease_ctx_info *lc, - struct durable_info *d_info) + struct smb2_create_req *req, struct lease_ctx_info *lc, + struct durable_info *d_info) { struct ksmbd_conn *conn = work->conn; struct create_context *context; int i, err = 0; - uint64_t persistent_id = 0; + u64 persistent_id = 0; int req_op_level; static const char * const durable_arr[] = {"DH2C", "DHnC", "DH2Q", "DHnQ", SMB2_CREATE_APP_INSTANCE_ID}; @@ -2026,8 +2007,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work, recon_v2 = (struct create_durable_reconn_v2_req *)context; - persistent_id = le64_to_cpu( - recon_v2->Fid.PersistentFileId); + persistent_id = le64_to_cpu(recon_v2->Fid.PersistentFileId); d_info->fp = ksmbd_lookup_durable_fd(persistent_id); if (!d_info->fp) { ksmbd_err("Failed to get Durable handle state\n"); @@ -2035,9 +2015,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } - if (memcmp(d_info->fp->create_guid, - recon_v2->CreateGuid, - SMB2_CREATE_GUID_SIZE)) { + if (memcmp(d_info->fp->create_guid, recon_v2->CreateGuid, + SMB2_CREATE_GUID_SIZE)) { err = -EBADF; goto out; } @@ -2053,15 +2032,14 @@ static int parse_durable_handle_context(struct ksmbd_work *work, struct create_durable_reconn_req *recon; if (d_info->type == DURABLE_RECONN_V2 || - d_info->type == DURABLE_REQ_V2) { + d_info->type == DURABLE_REQ_V2) { err = -EINVAL; goto out; } recon = (struct create_durable_reconn_req *)context; - persistent_id = le64_to_cpu( - recon->Data.Fid.PersistentFileId); + persistent_id = le64_to_cpu(recon->Data.Fid.PersistentFileId); d_info->fp = ksmbd_lookup_durable_fd(persistent_id); if (!d_info->fp) { ksmbd_err("Failed to get Durable handle state\n"); @@ -2080,7 +2058,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work, struct create_durable_req_v2 *durable_v2_blob; if (d_info->type == DURABLE_RECONN || - d_info->type == DURABLE_RECONN_V2) { + d_info->type == DURABLE_RECONN_V2) { err = -EINVAL; goto out; } @@ -2088,14 +2066,11 @@ static int parse_durable_handle_context(struct ksmbd_work *work, durable_v2_blob = (struct create_durable_req_v2 *)context; ksmbd_debug(SMB, "Request for durable v2 open\n"); - d_info->fp = ksmbd_lookup_fd_cguid( - durable_v2_blob->CreateGuid); + d_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->CreateGuid); if (d_info->fp) { - if (!memcmp(conn->ClientGUID, - d_info->fp->client_guid, - SMB2_CLIENT_GUID_SIZE)) { - if (!(req->hdr.Flags & - SMB2_FLAGS_REPLAY_OPERATIONS)) { + if (!memcmp(conn->ClientGUID, d_info->fp->client_guid, + SMB2_CLIENT_GUID_SIZE)) { + if (!(req->hdr.Flags & SMB2_FLAGS_REPLAY_OPERATIONS)) { err = -ENOEXEC; goto out; } @@ -2105,10 +2080,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)) { d_info->CreateGuid = durable_v2_blob->CreateGuid; d_info->persistent = @@ -2123,15 +2096,13 @@ static int parse_durable_handle_context(struct ksmbd_work *work, if (d_info->type == DURABLE_RECONN) goto out; if (d_info->type == DURABLE_RECONN_V2 || - d_info->type == DURABLE_REQ_V2) { + d_info->type == DURABLE_REQ_V2) { err = -EINVAL; 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"); d_info->type = i; } @@ -2252,9 +2223,7 @@ static inline int check_context_err(void *ctx, char *str) } static noinline int smb2_set_stream_name_xattr(struct path *path, - struct ksmbd_file *fp, - char *stream_name, - int s_type) + struct ksmbd_file *fp, char *stream_name, int s_type) { size_t xattr_stream_size; char *xattr_stream_name; @@ -2307,12 +2276,9 @@ static int smb2_remove_smb_xattrs(struct dentry *dentry) ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && - strncmp(&name[XATTR_USER_PREFIX_LEN], - DOS_ATTRIBUTE_PREFIX, - DOS_ATTRIBUTE_PREFIX_LEN) && - strncmp(&name[XATTR_USER_PREFIX_LEN], - STREAM_PREFIX, - STREAM_PREFIX_LEN)) + strncmp(&name[XATTR_USER_PREFIX_LEN], DOS_ATTRIBUTE_PREFIX, + DOS_ATTRIBUTE_PREFIX_LEN) && + strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, STREAM_PREFIX_LEN)) continue; err = ksmbd_vfs_remove_xattr(dentry, name); @@ -2343,9 +2309,8 @@ static int smb2_create_truncate(struct path *path) return rc; } -static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, - struct path *path, - struct ksmbd_file *fp) +static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, struct path *path, + struct ksmbd_file *fp) { struct xattr_dos_attrib da = {0}; int rc; @@ -2366,8 +2331,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, } static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, - struct path *path, - struct ksmbd_file *fp) + struct path *path, struct ksmbd_file *fp) { struct xattr_dos_attrib da; int rc; @@ -2376,7 +2340,7 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ if (!test_share_config_flag(tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) return; rc = ksmbd_vfs_get_dos_attrib_xattr(path->dentry, &da); @@ -2387,12 +2351,8 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, } } -static int smb2_creat(struct ksmbd_work *work, - struct path *path, - char *name, - int open_flags, - umode_t posix_mode, - bool is_dir) +static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, + int open_flags, umode_t posix_mode, bool is_dir) { struct ksmbd_tree_connect *tcon = work->tcon; struct ksmbd_share_config *share = tcon->share_conf; @@ -2494,7 +2454,7 @@ int smb2_open(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && - (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { + (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { ksmbd_debug(SMB, "invalid flag in chained command\n"); rsp->hdr.Status = STATUS_INVALID_PARAMETER; smb2_set_err_rsp(work); @@ -2508,7 +2468,7 @@ int smb2_open(struct ksmbd_work *work) if (req->NameLength) { if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && - *(char *)req->Buffer == '\\') { + *(char *)req->Buffer == '\\') { ksmbd_err("not allow directory name included leading slash\n"); rc = -EINVAL; goto err_out1; @@ -2528,7 +2488,7 @@ 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)) { + KSMBD_SHARE_FLAG_STREAMS)) { rc = -EBADF; goto err_out1; } @@ -2564,7 +2524,7 @@ int smb2_open(struct ksmbd_work *work) req_op_level = req->RequestedOplockLevel; memset(&d_info, 0, sizeof(struct durable_info)); if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && - req->CreateContextsOffset) { + req->CreateContextsOffset) { lc = parse_lease_state(req); rc = parse_durable_handle_context(work, req, lc, &d_info); if (rc) { @@ -2593,8 +2553,7 @@ int smb2_open(struct ksmbd_work *work) lc = parse_lease_state(req); } - if (le32_to_cpu(req->ImpersonationLevel) > - le32_to_cpu(IL_DELEGATE_LE)) { + if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE_LE)) { ksmbd_err("Invalid impersonationlevel : 0x%x\n", le32_to_cpu(req->ImpersonationLevel)); rc = -EIO; @@ -2610,7 +2569,7 @@ int smb2_open(struct ksmbd_work *work) } else { if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && - req->CreateOptions & FILE_RANDOM_ACCESS_LE) + req->CreateOptions & FILE_RANDOM_ACCESS_LE) req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); if (req->CreateOptions & (FILE_OPEN_BY_FILE_ID_LE | @@ -2623,8 +2582,9 @@ int smb2_open(struct ksmbd_work *work) if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { rc = -EINVAL; goto err_out1; - } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) + } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); + } } } @@ -2643,8 +2603,7 @@ int smb2_open(struct ksmbd_work *work) goto err_out1; } - if (req->FileAttributes && - !(req->FileAttributes & ATTR_MASK_LE)) { + if (req->FileAttributes && !(req->FileAttributes & ATTR_MASK_LE)) { ksmbd_err("Invalid file attribute : 0x%x\n", le32_to_cpu(req->FileAttributes)); rc = -EINVAL; @@ -2729,14 +2688,13 @@ int smb2_open(struct ksmbd_work *work) * denied error. */ if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || - req->CreateDisposition == FILE_OPEN_IF_LE) { + req->CreateDisposition == FILE_OPEN_IF_LE) { rc = -EACCES; path_put(&path); goto err_out; } - if (!test_tree_conn_flag(tcon, - KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, "User does not have write permission\n"); rc = -EACCES; @@ -2746,7 +2704,7 @@ int smb2_open(struct ksmbd_work *work) } } else { if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) { + KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) { /* * Use LOOKUP_FOLLOW to follow the path of * symlink in path buildup @@ -2792,7 +2750,7 @@ int smb2_open(struct ksmbd_work *work) } if (req->CreateOptions & FILE_DIRECTORY_FILE_LE && - req->FileAttributes & ATTR_NORMAL_LE) { + req->FileAttributes & ATTR_NORMAL_LE) { rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; rc = -EIO; } @@ -2801,9 +2759,8 @@ int smb2_open(struct ksmbd_work *work) goto err_out; } - if (file_present && req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE - && S_ISDIR(stat.mode) && - !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + if (file_present && req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE && + S_ISDIR(stat.mode) && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { ksmbd_debug(SMB, "open() argument is a directory: %s, %x\n", name, req->CreateOptions); rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; @@ -2812,21 +2769,21 @@ int smb2_open(struct ksmbd_work *work) } if (file_present && (req->CreateOptions & FILE_DIRECTORY_FILE_LE) && - !(req->CreateDisposition == FILE_CREATE_LE) && - !S_ISDIR(stat.mode)) { + !(req->CreateDisposition == FILE_CREATE_LE) && + !S_ISDIR(stat.mode)) { rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; rc = -EIO; goto err_out; } if (!stream_name && file_present && - (req->CreateDisposition == FILE_CREATE_LE)) { + req->CreateDisposition == FILE_CREATE_LE) { rc = -EEXIST; goto err_out; } if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && - file_present) + file_present) file_present = ksmbd_close_inode_fds(work, d_inode(path.dentry)); @@ -2889,10 +2846,10 @@ int smb2_open(struct ksmbd_work *work) * because execute(search) permission on a parent directory, * is already granted. */ - if (daccess & ~(FILE_READ_ATTRIBUTES_LE | - FILE_READ_CONTROL_LE)) { + if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { if (ksmbd_vfs_inode_permission(path.dentry, - open_flags & O_ACCMODE, may_delete)) { + open_flags & O_ACCMODE, + may_delete)) { rc = -EACCES; goto err_out; } @@ -2922,8 +2879,9 @@ int smb2_open(struct ksmbd_work *work) if ((req->CreateDisposition & FILE_CREATE_MASK_LE) == FILE_SUPERSEDE_LE) file_info = FILE_SUPERSEDED; - } else if (open_flags & O_CREAT) + } else if (open_flags & O_CREAT) { file_info = FILE_CREATED; + } ksmbd_vfs_set_fadvise(filp, req->CreateOptions); @@ -2959,7 +2917,7 @@ int smb2_open(struct ksmbd_work *work) ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) { + KSMBD_SHARE_FLAG_ACL_XATTR)) { rc = smb_inherit_dacl(conn, path.dentry, sess->user->uid, sess->user->gid); } @@ -2971,7 +2929,7 @@ int smb2_open(struct ksmbd_work *work) ksmbd_vfs_set_init_posix_acl(inode); if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) { + KSMBD_SHARE_FLAG_ACL_XATTR)) { struct smb_fattr fattr; struct smb_ntsd *pntsd; int pntsd_size, ace_num; @@ -2990,9 +2948,9 @@ int smb2_open(struct ksmbd_work *work) } pntsd = kmalloc(sizeof(struct smb_ntsd) + - sizeof(struct smb_sid)*3 + + sizeof(struct smb_sid) * 3 + sizeof(struct smb_acl) + - sizeof(struct smb_ace)*ace_num*2, + sizeof(struct smb_ace) * ace_num * 2, GFP_KERNEL); if (!pntsd) goto err_out; @@ -3026,8 +2984,8 @@ int smb2_open(struct ksmbd_work *work) fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); - if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC - && !fp->attrib_only && !stream_name) { + if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && + !fp->attrib_only && !stream_name) { smb_break_all_oplock(work, fp); need_truncate = 1; } @@ -3053,10 +3011,9 @@ 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))) { + 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))) { if (share_ret < 0 && !S_ISDIR(FP_INODE(fp)->i_mode)) { rc = share_ret; goto err_out; @@ -3071,8 +3028,8 @@ int smb2_open(struct ksmbd_work *work) if (rc) goto err_out; } else if (open_flags == O_RDONLY && - (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || - req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || + req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) req_op_level = SMB2_OPLOCK_LEVEL_II; rc = smb_grant_oplock(work, req_op_level, @@ -3117,11 +3074,9 @@ int smb2_open(struct ksmbd_work *work) err); } - context = smb2_find_context_vals(req, - SMB2_CREATE_QUERY_ON_DISK_ID); + context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID); if (IS_ERR(context)) { - rc = check_context_err(context, - SMB2_CREATE_QUERY_ON_DISK_ID); + rc = check_context_err(context, SMB2_CREATE_QUERY_ON_DISK_ID); if (rc < 0) goto err_out; } else { @@ -3146,8 +3101,7 @@ int smb2_open(struct ksmbd_work *work) memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); if (d_info.type) { - if (d_info.type == DURABLE_REQ_V2 && - d_info.persistent) + if (d_info.type == DURABLE_REQ_V2 && d_info.persistent) fp->is_persistent = 1; else fp->is_durable = 1; @@ -3160,8 +3114,7 @@ int smb2_open(struct ksmbd_work *work) else fp->durable_timeout = 1600; if (d_info.app_id) - memcpy(fp->app_instance_id, - d_info.app_id, 16); + memcpy(fp->app_instance_id, d_info.app_id, 16); } } @@ -3457,10 +3410,8 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) * * Return: 0 on success, otherwise error */ -static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, - int info_level, - struct ksmbd_dir_info *d_info, - struct ksmbd_kstat *ksmbd_kstat) +static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, + struct ksmbd_dir_info *d_info, struct ksmbd_kstat *ksmbd_kstat) { int next_entry_offset = 0; char *conv_name; @@ -3710,7 +3661,7 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv) } static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, - int info_level) + int info_level) { int struct_sz; int conv_len; @@ -3816,12 +3767,8 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, return 0; } -static int __query_dir(struct dir_context *ctx, - const char *name, - int namlen, - loff_t offset, - u64 ino, - unsigned int d_type) +static int __query_dir(struct dir_context *ctx, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) { struct ksmbd_readdir_data *buf; struct smb2_query_dir_private *priv; @@ -3913,8 +3860,7 @@ int smb2_query_dir(struct ksmbd_work *work) } if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || - inode_permission(&init_user_ns, file_inode(dir_fp->filp), - MAY_READ | MAY_EXEC)) { + inode_permission(&init_user_ns, file_inode(dir_fp->filp), MAY_READ | MAY_EXEC)) { ksmbd_err("no right to enumerate directory (%s)\n", FP_FILENAME(dir_fp)); rc = -EACCES; @@ -3935,8 +3881,9 @@ int smb2_query_dir(struct ksmbd_work *work) ksmbd_debug(SMB, "Search Pattern not found\n"); rc = -EINVAL; goto err_out2; - } else + } else { ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr); + } ksmbd_debug(SMB, "Directory name is %s\n", dir_fp->filename); @@ -3949,11 +3896,9 @@ int smb2_query_dir(struct ksmbd_work *work) memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); d_info.wptr = (char *)rsp->Buffer; d_info.rptr = (char *)rsp->Buffer; - d_info.out_buf_len = (work->response_sz - - (get_rfc1002_len(rsp_org) + 4)); + d_info.out_buf_len = (work->response_sz - (get_rfc1002_len(rsp_org) + 4)); d_info.out_buf_len = min_t(int, d_info.out_buf_len, - le32_to_cpu(req->OutputBufferLength)) - - sizeof(struct smb2_query_directory_rsp); + le32_to_cpu(req->OutputBufferLength)) - sizeof(struct smb2_query_directory_rsp); d_info.flags = srch_flag; /* @@ -3995,9 +3940,9 @@ int smb2_query_dir(struct ksmbd_work *work) goto err_out; if (!d_info.data_count && d_info.out_buf_len >= 0) { - if (srch_flag & SMB2_RETURN_SINGLE_ENTRY && !is_asterisk(srch_ptr)) + if (srch_flag & SMB2_RETURN_SINGLE_ENTRY && !is_asterisk(srch_ptr)) { rsp->hdr.Status = STATUS_NO_SUCH_FILE; - else { + } else { dir_fp->dot_dotdot[0] = dir_fp->dot_dotdot[1] = 0; rsp->hdr.Status = STATUS_NO_MORE_FILES; } @@ -4057,24 +4002,21 @@ err_out2: * Return: 0 on success, otherwise error */ static int buffer_check_err(int reqOutputBufferLength, - struct smb2_query_info_rsp *rsp, int infoclass_size) + struct smb2_query_info_rsp *rsp, int infoclass_size) { if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { if (reqOutputBufferLength < infoclass_size) { ksmbd_err("Invalid Buffer Size Requested\n"); rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; - rsp->hdr.smb2_buf_length = cpu_to_be32( - sizeof(struct smb2_hdr) - 4); + rsp->hdr.smb2_buf_length = cpu_to_be32(sizeof(struct smb2_hdr) - 4); return -EINVAL; } ksmbd_debug(SMB, "Buffer Overflow\n"); rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; - rsp->hdr.smb2_buf_length = cpu_to_be32( - sizeof(struct smb2_hdr) - 4 - + reqOutputBufferLength); - rsp->OutputBufferLength = cpu_to_le32( - reqOutputBufferLength); + rsp->hdr.smb2_buf_length = cpu_to_be32(sizeof(struct smb2_hdr) - 4 + + reqOutputBufferLength); + rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); } return 0; } @@ -4096,7 +4038,7 @@ static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp) } static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, - uint64_t num) + u64 num) { struct smb2_file_internal_info *file_info; @@ -4110,9 +4052,10 @@ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, } static int smb2_get_info_file_pipe(struct ksmbd_session *sess, - struct smb2_query_info_req *req, struct smb2_query_info_rsp *rsp) + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) { - uint64_t id; + u64 id; int rc; /* @@ -4155,11 +4098,9 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, * * Return: 0 on success, otherwise error */ -static int smb2_get_ea(struct ksmbd_work *work, - struct ksmbd_file *fp, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, - void *rsp_org) +static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) { struct smb2_ea_info *eainfo, *prev_eainfo; char *name, *ptr, *xattr_list = NULL, *buf; @@ -4176,9 +4117,9 @@ static int smb2_get_ea(struct ksmbd_work *work, path = &fp->filp->f_path; /* single EA entry is requested with given user.* name */ - if (req->InputBufferLength) + if (req->InputBufferLength) { ea_req = (struct smb2_ea_info_req *)req->Buffer; - else { + } else { /* need to send all EAs, if no specific EA is requested*/ if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) ksmbd_debug(SMB, @@ -4224,16 +4165,16 @@ static int smb2_get_ea(struct ksmbd_work *work, continue; if (!strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, - STREAM_PREFIX_LEN)) + STREAM_PREFIX_LEN)) continue; if (req->InputBufferLength && - (strncmp(&name[XATTR_USER_PREFIX_LEN], - ea_req->name, ea_req->EaNameLength))) + strncmp(&name[XATTR_USER_PREFIX_LEN], ea_req->name, + ea_req->EaNameLength)) continue; if (!strncmp(&name[XATTR_USER_PREFIX_LEN], - DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN)) + DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN)) continue; if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) @@ -4263,8 +4204,7 @@ static int smb2_get_ea(struct ksmbd_work *work, eainfo->Flags = 0; eainfo->EaNameLength = name_len; - if (!strncmp(name, XATTR_USER_PREFIX, - XATTR_USER_PREFIX_LEN)) + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) memcpy(eainfo->name, &name[XATTR_USER_PREFIX_LEN], name_len); else @@ -4308,8 +4248,7 @@ out: } static void get_file_access_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_access_info *file_info; @@ -4321,8 +4260,7 @@ static void get_file_access_info(struct smb2_query_info_rsp *rsp, } static int get_file_basic_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_all_info *basic_info; struct kstat stat; @@ -4346,8 +4284,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, basic_info->Attributes = fp->f_ci->m_fattr; basic_info->Pad1 = 0; rsp->OutputBufferLength = - cpu_to_le32(offsetof(struct smb2_file_all_info, - AllocationSize)); + cpu_to_le32(offsetof(struct smb2_file_all_info, AllocationSize)); inc_rfc1001_len(rsp_org, offsetof(struct smb2_file_all_info, AllocationSize)); return 0; @@ -4363,15 +4300,13 @@ static unsigned long long get_allocation_size(struct inode *inode, alloc_size = stat->size; else alloc_size = inode->i_blocks << 9; - } return alloc_size; } static void get_file_standard_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_standard_info *sinfo; unsigned int delete_pending; @@ -4396,7 +4331,7 @@ static void get_file_standard_info(struct smb2_query_info_rsp *rsp, } static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, - void *rsp_org) + void *rsp_org) { struct smb2_file_alignment_info *file_info; @@ -4409,9 +4344,8 @@ static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, } static int get_file_all_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, + void *rsp_org) { struct ksmbd_conn *conn = work->conn; struct smb2_file_all_info *file_info; @@ -4478,9 +4412,8 @@ static int get_file_all_info(struct ksmbd_work *work, } static void get_file_alternate_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, + void *rsp_org) { struct ksmbd_conn *conn = work->conn; struct smb2_file_alt_name_info *file_info; @@ -4499,9 +4432,8 @@ static void get_file_alternate_info(struct ksmbd_work *work, } static void get_file_stream_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, + void *rsp_org) { struct ksmbd_conn *conn = work->conn; struct smb2_file_stream_info *file_info; @@ -4530,7 +4462,7 @@ static void get_file_stream_info(struct ksmbd_work *work, ksmbd_debug(SMB, "%s, len %d\n", stream_name, streamlen); if (strncmp(&stream_name[XATTR_USER_PREFIX_LEN], - STREAM_PREFIX, STREAM_PREFIX_LEN)) + STREAM_PREFIX, STREAM_PREFIX_LEN)) continue; stream_name_len = streamlen - (XATTR_USER_PREFIX_LEN + @@ -4588,8 +4520,7 @@ out: } static void get_file_internal_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_internal_info *file_info; struct kstat stat; @@ -4603,8 +4534,7 @@ static void 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 ksmbd_file *fp, void *rsp_org) { struct smb2_file_ntwrk_info *file_info; struct inode *inode; @@ -4640,8 +4570,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, return 0; } -static void get_file_ea_info(struct smb2_query_info_rsp *rsp, - void *rsp_org) +static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) { struct smb2_file_ea_info *file_info; @@ -4653,8 +4582,7 @@ static void get_file_ea_info(struct smb2_query_info_rsp *rsp, } static void get_file_position_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_pos_info *file_info; @@ -4666,8 +4594,7 @@ static void get_file_position_info(struct smb2_query_info_rsp *rsp, } static void get_file_mode_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_mode_info *file_info; @@ -4679,8 +4606,7 @@ static void get_file_mode_info(struct smb2_query_info_rsp *rsp, } static void get_file_compression_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_comp_info *file_info; struct kstat stat; @@ -4701,8 +4627,7 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp, } static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_attr_tag_info *file_info; @@ -4723,8 +4648,7 @@ static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, } static int find_file_posix_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb311_posix_qinfo *file_info; struct inode *inode = FP_INODE(fp); @@ -4747,15 +4671,13 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, file_info->DeviceId = cpu_to_le32(inode->i_rdev); rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb311_posix_qinfo)); - inc_rfc1001_len(rsp_org, - sizeof(struct smb311_posix_qinfo)); + inc_rfc1001_len(rsp_org, sizeof(struct smb311_posix_qinfo)); return 0; } static int smb2_get_info_file(struct ksmbd_work *work, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, - void *rsp_org) + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) { struct ksmbd_file *fp; int fileinfoclass = 0; @@ -4764,7 +4686,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_PIPE)) { + KSMBD_SHARE_FLAG_PIPE)) { /* smb2 info file called for pipe */ return smb2_get_info_file_pipe(work->sess, req, rsp); } @@ -4887,9 +4809,8 @@ static int smb2_get_info_file(struct ksmbd_work *work, } static int smb2_get_info_filesystem(struct ksmbd_work *work, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, - void *rsp_org) + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) { struct ksmbd_session *sess = work->sess; struct ksmbd_conn *conn = sess->conn; @@ -5029,9 +4950,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->extended_info.version = cpu_to_le32(1); info->extended_info.release = cpu_to_le32(1); info->extended_info.rel_date = 0; - memcpy(info->extended_info.version_string, - "1.1.0", - strlen("1.1.0")); + memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); rsp->OutputBufferLength = cpu_to_le32(64); inc_rfc1001_len(rsp_org, 64); fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; @@ -5121,8 +5040,8 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, } static int smb2_get_info_sec(struct ksmbd_work *work, - struct smb2_query_info_req *req, struct smb2_query_info_rsp *rsp, - void *rsp_org) + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) { struct ksmbd_file *fp; struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; @@ -5162,7 +5081,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, fattr.cf_dacls = ksmbd_vfs_get_acl(inode, ACL_TYPE_DEFAULT); if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) + KSMBD_SHARE_FLAG_ACL_XATTR)) ksmbd_vfs_get_sd_xattr(work->conn, fp->filp->f_path.dentry, &ppntsd); rc = build_sec_desc(pntsd, ppntsd, addition_info, &secdesclen, &fattr); @@ -5243,7 +5162,7 @@ int smb2_query_info(struct ksmbd_work *work) */ static noinline int smb2_close_pipe(struct ksmbd_work *work) { - uint64_t id; + u64 id; struct smb2_close_req *req = work->request_buf; struct smb2_close_rsp *rsp = work->response_buf; @@ -5273,7 +5192,7 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work) int smb2_close(struct ksmbd_work *work) { unsigned int volatile_id = KSMBD_NO_FID; - uint64_t sess_id; + u64 sess_id; struct smb2_close_req *req; struct smb2_close_rsp *rsp; struct smb2_close_rsp *rsp_org; @@ -5297,9 +5216,9 @@ int smb2_close(struct ksmbd_work *work) sess_id = work->compound_sid; work->compound_sid = 0; - if (check_session_id(conn, sess_id)) + if (check_session_id(conn, sess_id)) { work->compound_sid = sess_id; - else { + } else { rsp->hdr.Status = STATUS_USER_SESSION_DELETED; if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) rsp->hdr.Status = STATUS_INVALID_PARAMETER; @@ -5308,7 +5227,7 @@ int smb2_close(struct ksmbd_work *work) } if (work->next_smb2_rcv_hdr_off && - !HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { + !HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { if (!HAS_FILE_ID(work->compound_fid)) { /* file already closed, return FILE_CLOSED */ ksmbd_debug(SMB, "file already closed\n"); @@ -5395,8 +5314,8 @@ int smb2_echo(struct ksmbd_work *work) } static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, - struct smb2_file_rename_info *file_info, - struct nls_table *local_nls) + struct smb2_file_rename_info *file_info, + struct nls_table *local_nls) { struct ksmbd_share_config *share = fp->tcon->share_conf; char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; @@ -5416,9 +5335,9 @@ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, goto out; } old_name = strrchr(abs_oldname, '/'); - if (old_name && old_name[1] != '\0') + if (old_name && old_name[1] != '\0') { old_name++; - else { + } else { ksmbd_debug(SMB, "can't get last component in path %s\n", abs_oldname); rc = -ENOENT; @@ -5497,8 +5416,7 @@ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, } } else { if (file_present && - strncmp(old_name, path.dentry->d_name.name, - strlen(old_name))) { + strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) { rc = -EEXIST; ksmbd_debug(SMB, "cannot rename already existing file\n"); @@ -5515,10 +5433,9 @@ out: } static int smb2_create_link(struct ksmbd_work *work, - struct ksmbd_share_config *share, - struct smb2_file_link_info *file_info, - struct file *filp, - struct nls_table *local_nls) + struct ksmbd_share_config *share, + struct smb2_file_link_info *file_info, struct file *filp, + struct nls_table *local_nls) { char *link_name = NULL, *target_name = NULL, *pathname = NULL; struct path path; @@ -5586,9 +5503,8 @@ static bool is_attributes_write_allowed(struct ksmbd_file *fp) return fp->daccess & FILE_WRITE_ATTRIBUTES_LE; } -static int set_file_basic_info(struct ksmbd_file *fp, - char *buf, - struct ksmbd_share_config *share) +static int set_file_basic_info(struct ksmbd_file *fp, char *buf, + struct ksmbd_share_config *share) { struct smb2_file_all_info *file_info; struct iattr attrs; @@ -5617,8 +5533,9 @@ static int set_file_basic_info(struct ksmbd_file *fp, temp_attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); attrs.ia_ctime = temp_attrs.ia_ctime; attrs.ia_valid |= ATTR_CTIME; - } else + } else { temp_attrs.ia_ctime = inode->i_ctime; + } if (file_info->LastWriteTime) { attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); @@ -5627,7 +5544,7 @@ static int set_file_basic_info(struct ksmbd_file *fp, if (file_info->Attributes) { if (!S_ISDIR(inode->i_mode) && - file_info->Attributes & ATTR_DIRECTORY_LE) { + file_info->Attributes & ATTR_DIRECTORY_LE) { ksmbd_err("can't change a file to a directory\n"); return -EINVAL; } @@ -5683,8 +5600,7 @@ static int set_file_basic_info(struct ksmbd_file *fp, } static int set_file_allocation_info(struct ksmbd_work *work, - struct ksmbd_file *fp, - char *buf) + struct ksmbd_file *fp, char *buf) { /* * TODO : It's working fine only when store dos attributes @@ -5733,9 +5649,8 @@ static int set_file_allocation_info(struct ksmbd_work *work, return 0; } -static int set_end_of_file_info(struct ksmbd_work *work, - struct ksmbd_file *fp, - char *buf) +static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf) { struct smb2_file_eof_info *file_eof_info; loff_t newsize; @@ -5761,8 +5676,7 @@ static int set_end_of_file_info(struct ksmbd_work *work, fp->filename, newsize); rc = ksmbd_vfs_truncate(work, NULL, fp, newsize); if (rc) { - ksmbd_debug(SMB, - "truncate failed! filename : %s err %d\n", + ksmbd_debug(SMB, "truncate failed! filename : %s err %d\n", fp->filename, rc); if (rc != -EAGAIN) rc = -EBADF; @@ -5772,9 +5686,8 @@ static int set_end_of_file_info(struct ksmbd_work *work, return 0; } -static int set_rename_info(struct ksmbd_work *work, - struct ksmbd_file *fp, - char *buf) +static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf) { struct ksmbd_file *parent_fp; @@ -5799,8 +5712,7 @@ next: work->sess->conn->local_nls); } -static int set_file_disposition_info(struct ksmbd_file *fp, - char *buf) +static int set_file_disposition_info(struct ksmbd_file *fp, char *buf) { struct smb2_file_disposition_info *file_info; struct inode *inode; @@ -5814,7 +5726,7 @@ static int set_file_disposition_info(struct ksmbd_file *fp, file_info = (struct smb2_file_disposition_info *)buf; if (file_info->DeletePending) { if (S_ISDIR(inode->i_mode) && - ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) + ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) return -EBUSY; ksmbd_set_inode_pending_delete(fp); } else { @@ -5823,8 +5735,7 @@ static int set_file_disposition_info(struct ksmbd_file *fp, return 0; } -static int set_file_position_info(struct ksmbd_file *fp, - char *buf) +static int set_file_position_info(struct ksmbd_file *fp, char *buf) { struct smb2_file_pos_info *file_info; loff_t current_byte_offset; @@ -5837,8 +5748,8 @@ static int set_file_position_info(struct ksmbd_file *fp, sector_size = ksmbd_vfs_logical_sector_size(inode); if (current_byte_offset < 0 || - (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && - current_byte_offset & (sector_size-1))) { + (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && + current_byte_offset & (sector_size - 1))) { ksmbd_err("CurrentByteOffset is not valid : %llu\n", current_byte_offset); return -EINVAL; @@ -5848,8 +5759,7 @@ static int set_file_position_info(struct ksmbd_file *fp, return 0; } -static int set_file_mode_info(struct ksmbd_file *fp, - char *buf) +static int set_file_mode_info(struct ksmbd_file *fp, char *buf) { struct smb2_file_mode_info *file_info; __le32 mode; @@ -5857,9 +5767,9 @@ static int set_file_mode_info(struct ksmbd_file *fp, file_info = (struct smb2_file_mode_info *)buf; mode = file_info->Mode; - if ((mode & (~FILE_MODE_INFO_MASK)) || - (mode & FILE_SYNCHRONOUS_IO_ALERT_LE && - mode & FILE_SYNCHRONOUS_IO_NONALERT_LE)) { + if ((mode & ~FILE_MODE_INFO_MASK) || + (mode & FILE_SYNCHRONOUS_IO_ALERT_LE && + mode & FILE_SYNCHRONOUS_IO_NONALERT_LE)) { ksmbd_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); return -EINVAL; } @@ -5883,11 +5793,8 @@ static int set_file_mode_info(struct ksmbd_file *fp, * 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, - int info_class, - char *buf, - struct ksmbd_share_config *share) +static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + int info_class, char *buf, struct ksmbd_share_config *share) { switch (info_class) { case FILE_BASIC_INFORMATION: @@ -5900,8 +5807,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, return set_end_of_file_info(work, fp, buf); case FILE_RENAME_INFORMATION: - if (!test_tree_conn_flag(work->tcon, - KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, "User does not have write permission\n"); return -EACCES; @@ -5914,8 +5820,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, work->sess->conn->local_nls); case FILE_DISPOSITION_INFORMATION: - if (!test_tree_conn_flag(work->tcon, - KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, "User does not have write permission\n"); return -EACCES; @@ -5945,10 +5850,8 @@ static int smb2_set_info_file(struct ksmbd_work *work, return -EOPNOTSUPP; } -static int smb2_set_info_sec(struct ksmbd_file *fp, - int addition_info, - char *buffer, - int buf_len) +static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, + char *buffer, int buf_len) { struct smb_ntsd *pntsd = (struct smb_ntsd *)buffer; @@ -6060,7 +5963,7 @@ err_out: static noinline int smb2_read_pipe(struct ksmbd_work *work) { int nbytes = 0, err; - uint64_t id; + u64 id; struct ksmbd_rpc_command *rpc_resp; struct smb2_read_req *req = work->request_buf; struct smb2_read_rsp *rsp = work->response_buf; @@ -6108,30 +6011,27 @@ out: } static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, - struct smb2_read_req *req, - void *data_buf, size_t length) + struct smb2_read_req *req, void *data_buf, size_t length) { struct smb2_buffer_desc_v1 *desc = (struct smb2_buffer_desc_v1 *)&req->Buffer[0]; int err; - if (work->conn->dialect == SMB30_PROT_ID - && req->Channel != SMB2_CHANNEL_RDMA_V1) + if (work->conn->dialect == SMB30_PROT_ID && + req->Channel != SMB2_CHANNEL_RDMA_V1) return -EINVAL; - if (req->ReadChannelInfoOffset == 0 - || le16_to_cpu(req->ReadChannelInfoLength) < sizeof(*desc)) + if (req->ReadChannelInfoOffset == 0 || + le16_to_cpu(req->ReadChannelInfoLength) < sizeof(*desc)) return -EINVAL; work->need_invalidate_rkey = (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); work->remote_key = le32_to_cpu(desc->token); - err = ksmbd_conn_rdma_write(work->conn, - data_buf, length, - le32_to_cpu(desc->token), - le64_to_cpu(desc->offset), - le32_to_cpu(desc->length)); + err = ksmbd_conn_rdma_write(work->conn, data_buf, length, + le32_to_cpu(desc->token), le64_to_cpu(desc->offset), + le32_to_cpu(desc->length)); if (err) return err; @@ -6226,7 +6126,7 @@ int smb2_read(struct ksmbd_work *work) nbytes, offset, mincount); if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || - req->Channel == SMB2_CHANNEL_RDMA_V1) { + req->Channel == SMB2_CHANNEL_RDMA_V1) { /* write data to the client using rdma channel */ remain_bytes = smb2_read_rdma_channel(work, req, work->aux_payload_buf, nbytes); @@ -6290,7 +6190,7 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) struct smb2_write_req *req = work->request_buf; struct smb2_write_rsp *rsp = work->response_buf; struct ksmbd_rpc_command *rpc_resp; - uint64_t id = 0; + u64 id = 0; int err = 0, ret = 0; char *data_buf; size_t length; @@ -6299,15 +6199,13 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) id = le64_to_cpu(req->VolatileFileId); if (le16_to_cpu(req->DataOffset) == - (offsetof(struct smb2_write_req, Buffer) - 4)) { + (offsetof(struct smb2_write_req, Buffer) - 4)) { data_buf = (char *)&req->Buffer[0]; } else { if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || - (le16_to_cpu(req->DataOffset) + - length > get_rfc1002_len(req))) { + (le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req))) { ksmbd_err("invalid write data offset %u, smb_len %u\n", - le16_to_cpu(req->DataOffset), - get_rfc1002_len(req)); + le16_to_cpu(req->DataOffset), get_rfc1002_len(req)); err = -EINVAL; goto out; } @@ -6351,8 +6249,8 @@ out: } static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, - struct smb2_write_req *req, struct ksmbd_file *fp, - loff_t offset, size_t length, bool sync) + struct smb2_write_req *req, struct ksmbd_file *fp, + loff_t offset, size_t length, bool sync) { struct smb2_buffer_desc_v1 *desc; char *data_buf; @@ -6362,14 +6260,14 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, desc = (struct smb2_buffer_desc_v1 *)&req->Buffer[0]; if (work->conn->dialect == SMB30_PROT_ID && - req->Channel != SMB2_CHANNEL_RDMA_V1) + req->Channel != SMB2_CHANNEL_RDMA_V1) return -EINVAL; if (req->Length != 0 || req->DataOffset != 0) return -EINVAL; - if (req->WriteChannelInfoOffset == 0 - || le16_to_cpu(req->WriteChannelInfoLength) < sizeof(*desc)) + if (req->WriteChannelInfoOffset == 0 || + le16_to_cpu(req->WriteChannelInfoLength) < sizeof(*desc)) return -EINVAL; work->need_invalidate_rkey = @@ -6384,15 +6282,12 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, le32_to_cpu(desc->token), le64_to_cpu(desc->offset), le32_to_cpu(desc->length)); - if (ret < 0) { ksmbd_free_response(data_buf); return ret; } - ret = ksmbd_vfs_write(work, fp, data_buf, length, &offset, - sync, &nbytes); - + ret = ksmbd_vfs_write(work, fp, data_buf, length, &offset, sync, &nbytes); ksmbd_free_response(data_buf); if (ret < 0) return ret; @@ -6421,8 +6316,7 @@ int smb2_write(struct ksmbd_work *work) rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_PIPE)) { + if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { ksmbd_debug(SMB, "IPC pipe write request\n"); return smb2_write_pipe(work); } @@ -6433,9 +6327,8 @@ int smb2_write(struct ksmbd_work *work) goto out; } - fp = ksmbd_lookup_fd_slow(work, - le64_to_cpu(req->VolatileFileId), - le64_to_cpu(req->PersistentFileId)); + fp = ksmbd_lookup_fd_slow(work, le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); if (!fp) { rsp->hdr.Status = STATUS_FILE_CLOSED; return -ENOENT; @@ -6461,16 +6354,13 @@ int smb2_write(struct ksmbd_work *work) writethrough = true; if (req->Channel != SMB2_CHANNEL_RDMA_V1 && - req->Channel != SMB2_CHANNEL_RDMA_V1_INVALIDATE) { - + req->Channel != SMB2_CHANNEL_RDMA_V1_INVALIDATE) { if (le16_to_cpu(req->DataOffset) == (offsetof(struct smb2_write_req, Buffer) - 4)) { data_buf = (char *)&req->Buffer[0]; } else { - if ((le16_to_cpu(req->DataOffset) > - get_rfc1002_len(req)) || - (le16_to_cpu(req->DataOffset) + - length > get_rfc1002_len(req))) { + if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || + (le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req))) { ksmbd_err("invalid write data offset %u, smb_len %u\n", le16_to_cpu(req->DataOffset), get_rfc1002_len(req)); @@ -6603,7 +6493,7 @@ int smb2_cancel(struct ksmbd_work *work) chdr = cancel_work->request_buf; if (cancel_work->async_id != - le64_to_cpu(hdr->Id.AsyncId)) + le64_to_cpu(hdr->Id.AsyncId)) continue; ksmbd_debug(SMB, @@ -6624,7 +6514,7 @@ int smb2_cancel(struct ksmbd_work *work) chdr = cancel_work->request_buf; if (chdr->MessageId != hdr->MessageId || - cancel_work == work) + cancel_work == work) continue; ksmbd_debug(SMB, @@ -6687,13 +6577,13 @@ static int smb2_set_flock_flags(struct file_lock *flock, int flags) flock->fl_type = F_WRLCK; flock->fl_flags |= FL_SLEEP; break; - case SMB2_LOCKFLAG_SHARED|SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + case SMB2_LOCKFLAG_SHARED | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: ksmbd_debug(SMB, "received shared & fail immediately request\n"); cmd = F_SETLK; flock->fl_type = F_RDLCK; break; - case SMB2_LOCKFLAG_EXCLUSIVE|SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + case SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: ksmbd_debug(SMB, "received exclusive & fail immediately request\n"); cmd = F_SETLK; @@ -6710,7 +6600,7 @@ static int smb2_set_flock_flags(struct file_lock *flock, int flags) } static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, - unsigned int cmd, int flags, struct list_head *lock_list) + unsigned int cmd, int flags, struct list_head *lock_list) { struct ksmbd_lock *lock; @@ -6764,7 +6654,7 @@ int smb2_lock(struct ksmbd_work *work) int flags = 0; int cmd = 0; int err = 0, i; - uint64_t lock_length; + u64 lock_length; struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp; int nolock = 0; LIST_HEAD(lock_list); @@ -6773,8 +6663,8 @@ int smb2_lock(struct ksmbd_work *work) ksmbd_debug(SMB, "Received lock request\n"); fp = ksmbd_lookup_fd_slow(work, - le64_to_cpu(req->VolatileFileId), - le64_to_cpu(req->PersistentFileId)); + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); if (!fp) { ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", le64_to_cpu(req->VolatileFileId)); @@ -6812,16 +6702,16 @@ int smb2_lock(struct ksmbd_work *work) lock_length = le64_to_cpu(lock_ele[i].Length); if (lock_length > 0) { - if (lock_length > - OFFSET_MAX - flock->fl_start) { + if (lock_length > OFFSET_MAX - flock->fl_start) { ksmbd_debug(SMB, "Invalid lock range requested\n"); lock_length = OFFSET_MAX - flock->fl_start; rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; goto out; } - } else + } else { lock_length = 0; + } flock->fl_end = flock->fl_start + lock_length; @@ -6836,9 +6726,9 @@ int smb2_lock(struct ksmbd_work *work) /* Check conflict locks in one request */ list_for_each_entry(cmp_lock, &lock_list, llist) { if (cmp_lock->fl->fl_start <= flock->fl_start && - cmp_lock->fl->fl_end >= flock->fl_end) { + cmp_lock->fl->fl_end >= flock->fl_end) { if (cmp_lock->fl->fl_type != F_UNLCK && - flock->fl_type != F_UNLCK) { + flock->fl_type != F_UNLCK) { ksmbd_err("conflict two locks in one request\n"); rsp->hdr.Status = STATUS_INVALID_PARAMETER; @@ -6865,11 +6755,10 @@ int smb2_lock(struct ksmbd_work *work) goto out; } - if ((prior_lock & (SMB2_LOCKFLAG_EXCLUSIVE | - SMB2_LOCKFLAG_SHARED) && - smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) || - (prior_lock == SMB2_LOCKFLAG_UNLOCK && - !(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK))) { + if ((prior_lock & (SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_SHARED) && + smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) || + (prior_lock == SMB2_LOCKFLAG_UNLOCK && + !(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK))) { rsp->hdr.Status = STATUS_INVALID_PARAMETER; goto out; } @@ -6877,22 +6766,21 @@ int smb2_lock(struct ksmbd_work *work) prior_lock = smb_lock->flags; if (!(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) && - !(smb_lock->flags & SMB2_LOCKFLAG_FAIL_IMMEDIATELY)) + !(smb_lock->flags & SMB2_LOCKFLAG_FAIL_IMMEDIATELY)) goto no_check_gl; nolock = 1; /* check locks in global list */ list_for_each_entry(cmp_lock, &global_lock_list, glist) { if (file_inode(cmp_lock->fl->fl_file) != - file_inode(smb_lock->fl->fl_file)) + file_inode(smb_lock->fl->fl_file)) continue; if (smb_lock->fl->fl_type == F_UNLCK) { - if (cmp_lock->fl->fl_file == - smb_lock->fl->fl_file && - cmp_lock->start == smb_lock->start && - cmp_lock->end == smb_lock->end && - !lock_defer_pending(cmp_lock->fl)) { + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file && + cmp_lock->start == smb_lock->start && + cmp_lock->end == smb_lock->end && + !lock_defer_pending(cmp_lock->fl)) { nolock = 0; locks_free_lock(cmp_lock->fl); list_del(&cmp_lock->glist); @@ -6912,26 +6800,25 @@ int smb2_lock(struct ksmbd_work *work) /* check zero byte lock range */ if (cmp_lock->zero_len && !smb_lock->zero_len && - cmp_lock->start > smb_lock->start && - cmp_lock->start < smb_lock->end) { + cmp_lock->start > smb_lock->start && + cmp_lock->start < smb_lock->end) { ksmbd_err("previous lock conflict with zero byte lock range\n"); rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; goto out; } if (smb_lock->zero_len && !cmp_lock->zero_len && - smb_lock->start > cmp_lock->start && - smb_lock->start < cmp_lock->end) { + smb_lock->start > cmp_lock->start && + smb_lock->start < cmp_lock->end) { ksmbd_err("current lock conflict with zero byte lock range\n"); rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; goto out; } if (((cmp_lock->start <= smb_lock->start && - cmp_lock->end > smb_lock->start) || - (cmp_lock->start < smb_lock->end && - cmp_lock->end >= smb_lock->end)) && - !cmp_lock->zero_len && !smb_lock->zero_len) { + cmp_lock->end > smb_lock->start) || + (cmp_lock->start < smb_lock->end && cmp_lock->end >= smb_lock->end)) && + !cmp_lock->zero_len && !smb_lock->zero_len) { ksmbd_err("Not allow lock operation on exclusive lock range\n"); rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; @@ -6957,9 +6844,9 @@ retry: err = ksmbd_vfs_lock(filp, smb_lock->cmd, flock); skip: if (flags & SMB2_LOCKFLAG_UNLOCK) { - if (!err) + if (!err) { ksmbd_debug(SMB, "File unlocked\n"); - else if (err == -ENOENT) { + } else if (err == -ENOENT) { rsp->hdr.Status = STATUS_NOT_LOCKED; goto out; } @@ -7082,9 +6969,8 @@ out2: return 0; } -static int fsctl_copychunk(struct ksmbd_work *work, - struct smb2_ioctl_req *req, - struct smb2_ioctl_rsp *rsp) +static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, + struct smb2_ioctl_rsp *rsp) { struct copychunk_ioctl_req *ci_req; struct copychunk_ioctl_rsp *ci_rsp; @@ -7101,12 +6987,12 @@ static int fsctl_copychunk(struct ksmbd_work *work, rsp->VolatileFileId = req->VolatileFileId; rsp->PersistentFileId = req->PersistentFileId; - ci_rsp->ChunksWritten = cpu_to_le32( - ksmbd_server_side_copy_max_chunk_count()); - ci_rsp->ChunkBytesWritten = cpu_to_le32( - ksmbd_server_side_copy_max_chunk_size()); - ci_rsp->TotalBytesWritten = cpu_to_le32( - ksmbd_server_side_copy_max_total_size()); + ci_rsp->ChunksWritten = + cpu_to_le32(ksmbd_server_side_copy_max_chunk_count()); + ci_rsp->ChunkBytesWritten = + cpu_to_le32(ksmbd_server_side_copy_max_chunk_size()); + 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); @@ -7114,22 +7000,22 @@ static int fsctl_copychunk(struct ksmbd_work *work, /* verify the SRV_COPYCHUNK_COPY packet */ if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || - le32_to_cpu(req->InputCount) < - offsetof(struct copychunk_ioctl_req, Chunks) + - chunk_count * sizeof(struct srv_copychunk)) { + le32_to_cpu(req->InputCount) < + offsetof(struct copychunk_ioctl_req, Chunks) + + chunk_count * sizeof(struct srv_copychunk)) { rsp->hdr.Status = STATUS_INVALID_PARAMETER; return -EINVAL; } 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()) + le32_to_cpu(chunks[i].Length) > ksmbd_server_side_copy_max_chunk_size()) break; total_size_written += le32_to_cpu(chunks[i].Length); } - if (i < chunk_count || total_size_written > - ksmbd_server_side_copy_max_total_size()) { + + if (i < chunk_count || + total_size_written > ksmbd_server_side_copy_max_total_size()) { rsp->hdr.Status = STATUS_INVALID_PARAMETER; return -EINVAL; } @@ -7139,13 +7025,13 @@ static int fsctl_copychunk(struct ksmbd_work *work, dst_fp = ksmbd_lookup_fd_slow(work, le64_to_cpu(req->VolatileFileId), le64_to_cpu(req->PersistentFileId)); - ret = -EINVAL; - if (!src_fp || src_fp->persistent_id != - le64_to_cpu(ci_req->ResumeKey[1])) { + if (!src_fp || + src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; goto out; } + if (!dst_fp) { rsp->hdr.Status = STATUS_FILE_CLOSED; goto out; @@ -7212,8 +7098,7 @@ static __be32 idev_ipv4_address(struct in_device *idev) } static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, - struct smb2_ioctl_req *req, - struct smb2_ioctl_rsp *rsp) + struct smb2_ioctl_req *req, struct smb2_ioctl_rsp *rsp) { struct network_interface_info_ioctl_rsp *nii_rsp = NULL; int nbytes = 0; @@ -7255,10 +7140,8 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, netdev->ethtool_ops->get_link_ksettings(netdev, &cmd); speed = cmd.base.speed; } else { - ksmbd_err("%s %s %s\n", - netdev->name, - "speed is unknown,", - "defaulting to 1Gb/sec"); + ksmbd_err("%s %s\n", netdev->name, + "speed is unknown, defaulting to 1Gb/sec"); speed = SPEED_1000; } @@ -7321,10 +7204,9 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, return nbytes; } - static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, - struct validate_negotiate_info_req *neg_req, - struct validate_negotiate_info_rsp *neg_rsp) + struct validate_negotiate_info_req *neg_req, + struct validate_negotiate_info_rsp *neg_rsp) { int ret = 0; int dialect; @@ -7359,10 +7241,10 @@ err_out: return ret; } -static int fsctl_query_allocated_ranges(struct ksmbd_work *work, uint64_t id, - struct file_allocated_range_buffer *qar_req, - struct file_allocated_range_buffer *qar_rsp, - int in_count, int *out_count) +static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, + struct file_allocated_range_buffer *qar_req, + struct file_allocated_range_buffer *qar_rsp, + int in_count, int *out_count) { struct ksmbd_file *fp; loff_t start, length; @@ -7388,15 +7270,15 @@ static int fsctl_query_allocated_ranges(struct ksmbd_work *work, uint64_t id, return ret; } -static int fsctl_pipe_transceive(struct ksmbd_work *work, uint64_t id, - int out_buf_len, struct smb2_ioctl_req *req, struct smb2_ioctl_rsp *rsp) +static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, + int out_buf_len, struct smb2_ioctl_req *req, + struct smb2_ioctl_rsp *rsp) { struct ksmbd_rpc_command *rpc_resp; char *data_buf = (char *)&req->Buffer[0]; int nbytes = 0; - rpc_resp = ksmbd_rpc_ioctl(work->sess, id, - data_buf, + rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, le32_to_cpu(req->InputCount)); if (rpc_resp) { if (rpc_resp->flags == KSMBD_RPC_SOME_NOT_MAPPED) { @@ -7432,8 +7314,8 @@ out: return nbytes; } -static inline int fsctl_set_sparse(struct ksmbd_work *work, uint64_t id, - struct file_sparse *sparse) +static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, + struct file_sparse *sparse) { struct ksmbd_file *fp; int ret = 0; @@ -7450,8 +7332,8 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, uint64_t id, fp->f_ci->m_fattr &= ~ATTR_SPARSE_FILE_LE; if (fp->f_ci->m_fattr != old_fattr && - test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { struct xattr_dos_attrib da; ret = ksmbd_vfs_get_dos_attrib_xattr(fp->filp->f_path.dentry, &da); @@ -7470,7 +7352,8 @@ out: } static int fsctl_request_resume_key(struct ksmbd_work *work, - struct smb2_ioctl_req *req, struct resume_key_ioctl_rsp *key_rsp) + struct smb2_ioctl_req *req, + struct resume_key_ioctl_rsp *key_rsp) { struct ksmbd_file *fp; @@ -7500,7 +7383,7 @@ int smb2_ioctl(struct ksmbd_work *work) struct smb2_ioctl_rsp *rsp, *rsp_org; int cnt_code, nbytes = 0; int out_buf_len; - uint64_t id = KSMBD_NO_FID; + u64 id = KSMBD_NO_FID; struct ksmbd_conn *conn = work->conn; int ret = 0; @@ -7595,8 +7478,7 @@ int smb2_ioctl(struct ksmbd_work *work) break; case FSCTL_COPYCHUNK: case FSCTL_COPYCHUNK_WRITE: - if (!test_tree_conn_flag(work->tcon, - KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, "User does not have write permission\n"); ret = -EACCES; @@ -7623,8 +7505,7 @@ int smb2_ioctl(struct ksmbd_work *work) struct ksmbd_file *fp; loff_t off, len; - if (!test_tree_conn_flag(work->tcon, - KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, "User does not have write permission\n"); ret = -EACCES; @@ -7731,7 +7612,7 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) struct oplock_info *opinfo = NULL; __le32 err = 0; int ret = 0; - uint64_t volatile_id, persistent_id; + u64 volatile_id, persistent_id; char req_oplevel = 0, rsp_oplevel = 0; unsigned int oplock_change_type; @@ -7768,34 +7649,36 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) goto err_out; } - if (((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) || - (opinfo->level == SMB2_OPLOCK_LEVEL_BATCH)) && - ((req_oplevel != SMB2_OPLOCK_LEVEL_II) && - (req_oplevel != SMB2_OPLOCK_LEVEL_NONE))) { + if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + (req_oplevel != SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { err = STATUS_INVALID_OPLOCK_PROTOCOL; oplock_change_type = OPLOCK_WRITE_TO_NONE; - } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_II) && - (req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { + } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { err = STATUS_INVALID_OPLOCK_PROTOCOL; oplock_change_type = OPLOCK_READ_TO_NONE; - } else if ((req_oplevel == SMB2_OPLOCK_LEVEL_II) || - (req_oplevel == SMB2_OPLOCK_LEVEL_NONE)) { + } else if (req_oplevel == SMB2_OPLOCK_LEVEL_II || + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { err = STATUS_INVALID_DEVICE_STATE; - if (((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) || - (opinfo->level == SMB2_OPLOCK_LEVEL_BATCH)) && - (req_oplevel == SMB2_OPLOCK_LEVEL_II)) { + if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + req_oplevel == SMB2_OPLOCK_LEVEL_II) { oplock_change_type = OPLOCK_WRITE_TO_READ; - } else if (((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) - || (opinfo->level == SMB2_OPLOCK_LEVEL_BATCH)) && - (req_oplevel == SMB2_OPLOCK_LEVEL_NONE)) { + } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { oplock_change_type = OPLOCK_WRITE_TO_NONE; - } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_II) && - (req_oplevel == SMB2_OPLOCK_LEVEL_NONE)) { + } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { oplock_change_type = OPLOCK_READ_TO_NONE; - } else + } else { oplock_change_type = 0; - } else + } + } else { oplock_change_type = 0; + } switch (oplock_change_type) { case OPLOCK_WRITE_TO_READ: @@ -7846,8 +7729,8 @@ err_out: static int check_lease_state(struct lease *lease, __le32 req_state) { if ((lease->new_state == - (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) - && !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { + (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) && + !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { lease->new_state = req_state; return 0; } @@ -7909,7 +7792,7 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) /* check for bad lease state */ if (req->LeaseState & (~(SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE))) { + SMB2_LEASE_HANDLE_CACHING_LE))) { err = STATUS_INVALID_OPLOCK_PROTOCOL; if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) lease_change_type = OPLOCK_WRITE_TO_NONE; @@ -7918,8 +7801,8 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", le32_to_cpu(lease->state), le32_to_cpu(req->LeaseState)); - } else if ((lease->state == SMB2_LEASE_READ_CACHING_LE) && - (req->LeaseState != SMB2_LEASE_NONE_LE)) { + } else if (lease->state == SMB2_LEASE_READ_CACHING_LE && + req->LeaseState != SMB2_LEASE_NONE_LE) { err = STATUS_INVALID_OPLOCK_PROTOCOL; lease_change_type = OPLOCK_READ_TO_NONE; ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", @@ -7938,8 +7821,9 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) lease_change_type = OPLOCK_WRITE_TO_READ; else lease_change_type = OPLOCK_READ_HANDLE_TO_READ; - } else + } else { lease_change_type = 0; + } } switch (lease_change_type) { @@ -8056,9 +7940,9 @@ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) struct smb2_hdr *rcv_hdr2 = work->request_buf; if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && - command != SMB2_NEGOTIATE_HE && - command != SMB2_SESSION_SETUP_HE && - command != SMB2_OPLOCK_BREAK_HE) + command != SMB2_NEGOTIATE_HE && + command != SMB2_SESSION_SETUP_HE && + command != SMB2_OPLOCK_BREAK_HE) return true; return 0; @@ -8097,7 +7981,7 @@ int smb2_check_sign_req(struct ksmbd_work *work) iov[0].iov_len = len; if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, - signature)) + signature)) return 0; if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { @@ -8155,7 +8039,7 @@ void smb2_set_sign_rsp(struct ksmbd_work *work) } if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, - signature)) + signature)) memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); } @@ -8305,7 +8189,7 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) conn->preauth_info->Preauth_HashValue); if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && - sess && sess->state == SMB2_SESSION_IN_PROGRESS) { + sess && sess->state == SMB2_SESSION_IN_PROGRESS) { __u8 *hash_value; hash_value = sess->Preauth_HashValue; @@ -8314,9 +8198,8 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) } } -static void fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, - char *old_buf, - __le16 cipher_type) +static void fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, char *old_buf, + __le16 cipher_type) { struct smb2_hdr *hdr = (struct smb2_hdr *)old_buf; unsigned int orig_len = get_rfc1002_len(old_buf); @@ -8403,7 +8286,7 @@ int smb3_decrypt_req(struct ksmbd_work *work) sess = ksmbd_session_lookup(conn, le64_to_cpu(tr_hdr->SessionId)); if (!sess) { ksmbd_err("invalid session id(%llx) in transform header\n", - le64_to_cpu(tr_hdr->SessionId)); + le64_to_cpu(tr_hdr->SessionId)); return -ECONNABORTED; } @@ -8446,7 +8329,7 @@ bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) rsp = RESPONSE_BUF_NEXT(work); if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && - rsp->Status == STATUS_SUCCESS) + rsp->Status == STATUS_SUCCESS) return true; return false; } diff --git a/fs/cifsd/smb2pdu.h b/fs/cifsd/smb2pdu.h index deb3d7444c2a..156ff6a2968b 100644 --- a/fs/cifsd/smb2pdu.h +++ b/fs/cifsd/smb2pdu.h @@ -346,8 +346,8 @@ struct smb2_negotiate_rsp { #define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 #define SMB2_SESSION_EXPIRED (0) -#define SMB2_SESSION_IN_PROGRESS (1 << 0) -#define SMB2_SESSION_VALID (1 << 1) +#define SMB2_SESSION_IN_PROGRESS BIT(0) +#define SMB2_SESSION_VALID BIT(1) /* Flags */ #define SMB2_SESSION_REQ_FLAG_BINDING 0x01 @@ -1161,7 +1161,6 @@ struct smb2_set_info_rsp { __le16 StructureSize; /* Must be 2 */ } __packed; - /* FILE Info response size */ #define FILE_DIRECTORY_INFORMATION_SIZE 1 #define FILE_FULL_DIRECTORY_INFORMATION_SIZE 2 @@ -1199,7 +1198,6 @@ struct smb2_set_info_rsp { #define FILE_NETWORK_OPEN_INFORMATION_SIZE 56 #define FILE_ATTRIBUTE_TAG_INFORMATION_SIZE 8 - /* FS Info response size */ #define FS_DEVICE_INFORMATION_SIZE 8 #define FS_ATTRIBUTE_INFORMATION_SIZE 16 @@ -1579,71 +1577,70 @@ struct smb2_posix_info { } __packed; /* functions */ +int init_smb2_0_server(struct ksmbd_conn *conn); +void init_smb2_1_server(struct ksmbd_conn *conn); +void init_smb3_0_server(struct ksmbd_conn *conn); +void init_smb3_02_server(struct ksmbd_conn *conn); +int init_smb3_11_server(struct ksmbd_conn *conn); -extern int init_smb2_0_server(struct ksmbd_conn *conn); -extern void init_smb2_1_server(struct ksmbd_conn *conn); -extern void init_smb3_0_server(struct ksmbd_conn *conn); -extern void init_smb3_02_server(struct ksmbd_conn *conn); -extern int init_smb3_11_server(struct ksmbd_conn *conn); +void init_smb2_max_read_size(unsigned int sz); +void init_smb2_max_write_size(unsigned int sz); +void init_smb2_max_trans_size(unsigned int sz); -extern void init_smb2_max_read_size(unsigned int sz); -extern void init_smb2_max_write_size(unsigned int sz); -extern void init_smb2_max_trans_size(unsigned int sz); +int is_smb2_neg_cmd(struct ksmbd_work *work); +int is_smb2_rsp(struct ksmbd_work *work); -extern int is_smb2_neg_cmd(struct ksmbd_work *work); -extern int is_smb2_rsp(struct ksmbd_work *work); - -extern uint16_t get_smb2_cmd_val(struct ksmbd_work *work); -extern void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err); -extern int init_smb2_rsp_hdr(struct ksmbd_work *work); -extern int smb2_allocate_rsp_buf(struct ksmbd_work *work); -extern bool is_chained_smb2_message(struct ksmbd_work *work); -extern int init_smb2_neg_rsp(struct ksmbd_work *work); -extern void smb2_set_err_rsp(struct ksmbd_work *work); -extern int smb2_check_user_session(struct ksmbd_work *work); -extern int smb2_get_ksmbd_tcon(struct ksmbd_work *work); -extern bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command); -extern int smb2_check_sign_req(struct ksmbd_work *work); -extern void smb2_set_sign_rsp(struct ksmbd_work *work); -extern int smb3_check_sign_req(struct ksmbd_work *work); -extern void smb3_set_sign_rsp(struct ksmbd_work *work); -extern int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, - __le16 dialects_count); -extern struct file_lock *smb_flock_init(struct file *f); -extern int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), - void **arg); -extern void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); -extern struct channel *lookup_chann_list(struct ksmbd_session *sess); -extern void smb3_preauth_hash_rsp(struct ksmbd_work *work); -extern int smb3_is_transform_hdr(void *buf); -extern int smb3_decrypt_req(struct ksmbd_work *work); -extern int smb3_encrypt_resp(struct ksmbd_work *work); -extern bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work); -extern int smb2_set_rsp_credits(struct ksmbd_work *work); +u16 get_smb2_cmd_val(struct ksmbd_work *work); +void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err); +int init_smb2_rsp_hdr(struct ksmbd_work *work); +int smb2_allocate_rsp_buf(struct ksmbd_work *work); +bool is_chained_smb2_message(struct ksmbd_work *work); +int init_smb2_neg_rsp(struct ksmbd_work *work); +void smb2_set_err_rsp(struct ksmbd_work *work); +int smb2_check_user_session(struct ksmbd_work *work); +int smb2_get_ksmbd_tcon(struct ksmbd_work *work); +bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command); +int smb2_check_sign_req(struct ksmbd_work *work); +void smb2_set_sign_rsp(struct ksmbd_work *work); +int smb3_check_sign_req(struct ksmbd_work *work); +void smb3_set_sign_rsp(struct ksmbd_work *work); +int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, + __le16 dialects_count); +struct file_lock *smb_flock_init(struct file *f); +int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), + void **arg); +void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); +struct channel *lookup_chann_list(struct ksmbd_session *sess); +void smb3_preauth_hash_rsp(struct ksmbd_work *work); +int smb3_is_transform_hdr(void *buf); +int smb3_decrypt_req(struct ksmbd_work *work); +int smb3_encrypt_resp(struct ksmbd_work *work); +bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work); +int smb2_set_rsp_credits(struct ksmbd_work *work); /* smb2 misc functions */ -extern int ksmbd_smb2_check_message(struct ksmbd_work *work); +int ksmbd_smb2_check_message(struct ksmbd_work *work); /* smb2 command handlers */ -extern int smb2_handle_negotiate(struct ksmbd_work *work); -extern int smb2_negotiate_request(struct ksmbd_work *work); -extern int smb2_sess_setup(struct ksmbd_work *work); -extern int smb2_tree_connect(struct ksmbd_work *work); -extern int smb2_tree_disconnect(struct ksmbd_work *work); -extern int smb2_session_logoff(struct ksmbd_work *work); -extern int smb2_open(struct ksmbd_work *work); -extern int smb2_query_info(struct ksmbd_work *work); -extern int smb2_query_dir(struct ksmbd_work *work); -extern int smb2_close(struct ksmbd_work *work); -extern int smb2_echo(struct ksmbd_work *work); -extern int smb2_set_info(struct ksmbd_work *work); -extern int smb2_read(struct ksmbd_work *work); -extern int smb2_write(struct ksmbd_work *work); -extern int smb2_flush(struct ksmbd_work *work); -extern int smb2_cancel(struct ksmbd_work *work); -extern int smb2_lock(struct ksmbd_work *work); -extern int smb2_ioctl(struct ksmbd_work *work); -extern int smb2_oplock_break(struct ksmbd_work *work); -extern int smb2_notify(struct ksmbd_work *ksmbd_work); +int smb2_handle_negotiate(struct ksmbd_work *work); +int smb2_negotiate_request(struct ksmbd_work *work); +int smb2_sess_setup(struct ksmbd_work *work); +int smb2_tree_connect(struct ksmbd_work *work); +int smb2_tree_disconnect(struct ksmbd_work *work); +int smb2_session_logoff(struct ksmbd_work *work); +int smb2_open(struct ksmbd_work *work); +int smb2_query_info(struct ksmbd_work *work); +int smb2_query_dir(struct ksmbd_work *work); +int smb2_close(struct ksmbd_work *work); +int smb2_echo(struct ksmbd_work *work); +int smb2_set_info(struct ksmbd_work *work); +int smb2_read(struct ksmbd_work *work); +int smb2_write(struct ksmbd_work *work); +int smb2_flush(struct ksmbd_work *work); +int smb2_cancel(struct ksmbd_work *work); +int smb2_lock(struct ksmbd_work *work); +int smb2_ioctl(struct ksmbd_work *work); +int smb2_oplock_break(struct ksmbd_work *work); +int smb2_notify(struct ksmbd_work *ksmbd_work); #endif /* _SMB2PDU_H */ diff --git a/fs/cifsd/smb_common.c b/fs/cifsd/smb_common.c index da1928b948f8..b0510213eb6d 100644 --- a/fs/cifsd/smb_common.c +++ b/fs/cifsd/smb_common.c @@ -17,7 +17,7 @@ /*for shortname implementation */ static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; -#define MANGLE_BASE (sizeof(basechars)/sizeof(char)-1) +#define MANGLE_BASE (sizeof(basechars) / sizeof(char) - 1) #define MAGIC_CHAR '~' #define PERIOD '.' #define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) @@ -268,15 +268,10 @@ bool ksmbd_pdu_size_has_room(unsigned int pdu) return (pdu >= KSMBD_MIN_SUPPORTED_HEADER_SIZE - 4); } -int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, - int info_level, - struct ksmbd_file *dir, - struct ksmbd_dir_info *d_info, - char *search_pattern, - int (*fn)(struct ksmbd_conn *, - int, - struct ksmbd_dir_info *, - struct ksmbd_kstat *)) +int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, + struct ksmbd_file *dir, struct ksmbd_dir_info *d_info, + char *search_pattern, int (*fn)(struct ksmbd_conn *, int, + struct ksmbd_dir_info *, struct ksmbd_kstat *)) { int i, rc = 0; struct ksmbd_conn *conn = work->conn; @@ -295,7 +290,7 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, } if (!match_pattern(d_info->name, d_info->name_len, - search_pattern)) { + search_pattern)) { dir->dot_dotdot[i] = 1; continue; } @@ -331,9 +326,8 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, * TODO: Though this function comforms the restriction of 8.3 Filename spec, * but the result is different with Windows 7's one. need to check. */ -int ksmbd_extract_shortname(struct ksmbd_conn *conn, - const char *longname, - char *shortname) +int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, + char *shortname) { const char *p; char base[9], extension[4]; @@ -354,7 +348,7 @@ int ksmbd_extract_shortname(struct ksmbd_conn *conn, if (p == longname) { /*name starts with a dot*/ strscpy(extension, "___", strlen("___")); } else { - if (p != NULL) { + if (p) { p++; while (*p && extlen < 3) { if (*p != '.') @@ -362,8 +356,9 @@ int ksmbd_extract_shortname(struct ksmbd_conn *conn, p++; } extension[extlen] = '\0'; - } else + } else { dot_present = false; + } } p = longname; @@ -378,7 +373,7 @@ int ksmbd_extract_shortname(struct ksmbd_conn *conn, } base[baselen] = MAGIC_CHAR; - memcpy(out, base, baselen+1); + memcpy(out, base, baselen + 1); ptr = longname; len = strlen(longname); @@ -386,14 +381,14 @@ int ksmbd_extract_shortname(struct ksmbd_conn *conn, csum += *ptr; csum = csum % (MANGLE_BASE * MANGLE_BASE); - out[baselen+1] = mangle(csum/MANGLE_BASE); - out[baselen+2] = mangle(csum); - out[baselen+3] = PERIOD; + out[baselen + 1] = mangle(csum / MANGLE_BASE); + out[baselen + 2] = mangle(csum); + out[baselen + 3] = PERIOD; if (dot_present) - memcpy(&out[baselen+4], extension, 4); + memcpy(&out[baselen + 4], extension, 4); else - out[baselen+4] = '\0'; + out[baselen + 4] = '\0'; smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX, conn->local_nls, 0); len = strlen(out) * 2; @@ -471,9 +466,8 @@ static const char * const shared_mode_errors[] = { "Desired access mode does not permit FILE_DELETE", }; -static void smb_shared_mode_error(int error, - struct ksmbd_file *prev_fp, - struct ksmbd_file *curr_fp) +static void smb_shared_mode_error(int error, struct ksmbd_file *prev_fp, + struct ksmbd_file *curr_fp) { ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]); ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n", @@ -512,7 +506,7 @@ int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) continue; if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE) && - curr_fp->daccess & FILE_DELETE_LE) { + curr_fp->daccess & FILE_DELETE_LE) { smb_shared_mode_error(SHARE_DELETE_ERROR, prev_fp, curr_fp); @@ -528,8 +522,7 @@ int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) continue; if (!(prev_fp->saccess & FILE_SHARE_READ_LE) && - curr_fp->daccess & (FILE_EXECUTE_LE | - FILE_READ_DATA_LE)) { + curr_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE)) { smb_shared_mode_error(SHARE_READ_ERROR, prev_fp, curr_fp); @@ -538,8 +531,7 @@ int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) } if (!(prev_fp->saccess & FILE_SHARE_WRITE_LE) && - curr_fp->daccess & (FILE_WRITE_DATA_LE | - FILE_APPEND_DATA_LE)) { + curr_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE)) { smb_shared_mode_error(SHARE_WRITE_ERROR, prev_fp, curr_fp); @@ -547,9 +539,8 @@ int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) break; } - if (prev_fp->daccess & (FILE_EXECUTE_LE | - FILE_READ_DATA_LE) && - !(curr_fp->saccess & FILE_SHARE_READ_LE)) { + if (prev_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_READ_LE)) { smb_shared_mode_error(FILE_READ_ERROR, prev_fp, curr_fp); @@ -557,9 +548,8 @@ int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) break; } - if (prev_fp->daccess & (FILE_WRITE_DATA_LE | - FILE_APPEND_DATA_LE) && - !(curr_fp->saccess & FILE_SHARE_WRITE_LE)) { + if (prev_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_WRITE_LE)) { smb_shared_mode_error(FILE_WRITE_ERROR, prev_fp, curr_fp); @@ -568,7 +558,7 @@ int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) } if (prev_fp->daccess & FILE_DELETE_LE && - !(curr_fp->saccess & FILE_SHARE_DELETE_LE)) { + !(curr_fp->saccess & FILE_SHARE_DELETE_LE)) { smb_shared_mode_error(FILE_DELETE_ERROR, prev_fp, curr_fp); @@ -620,7 +610,7 @@ int ksmbd_override_fsids(struct ksmbd_work *work) if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID)) cred->cap_effective = cap_drop_fs_set(cred->cap_effective); - WARN_ON(work->saved_cred != NULL); + WARN_ON(work->saved_cred); work->saved_cred = override_creds(cred); if (!work->saved_cred) { abort_creds(cred); @@ -633,7 +623,7 @@ void ksmbd_revert_fsids(struct ksmbd_work *work) { const struct cred *cred; - WARN_ON(work->saved_cred == NULL); + WARN_ON(!work->saved_cred); cred = current_cred(); revert_creds(work->saved_cred); diff --git a/fs/cifsd/smb_common.h b/fs/cifsd/smb_common.h index ec954e6bc4ae..2d7b1c693ff4 100644 --- a/fs/cifsd/smb_common.h +++ b/fs/cifsd/smb_common.h @@ -43,7 +43,7 @@ #define SMB311_PROT_ID 0x0311 #define BAD_PROT_ID 0xFFFF -#define SMB_ECHO_INTERVAL (60*HZ) +#define SMB_ECHO_INTERVAL (60 * HZ) #define CIFS_DEFAULT_IOSIZE (64 * 1024) #define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ @@ -490,8 +490,6 @@ struct smb_version_cmds { int (*proc)(struct ksmbd_work *swork); }; - - int ksmbd_min_protocol(void); int ksmbd_max_protocol(void); diff --git a/fs/cifsd/smbacl.c b/fs/cifsd/smbacl.c index 7f6d5313a02c..a3675aa837b9 100644 --- a/fs/cifsd/smbacl.c +++ b/fs/cifsd/smbacl.c @@ -68,13 +68,12 @@ static const struct smb_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, * if the two SIDs (roughly equivalent to a UUID for a user or group) are * the same returns zero, if they do not match returns non-zero. */ -int -compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) +int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) { int i; int num_subauth, num_sat, num_saw; - if ((!ctsid) || (!cwsid)) + if (!ctsid || !cwsid) return 1; /* compare the revision */ @@ -103,7 +102,7 @@ compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) for (i = 0; i < num_subauth; ++i) { if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { if (le32_to_cpu(ctsid->sub_auth[i]) > - le32_to_cpu(cwsid->sub_auth[i])) + le32_to_cpu(cwsid->sub_auth[i])) return 1; else return -1; @@ -114,8 +113,7 @@ compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) return 0; /* sids compare/match */ } -static void -smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) +static void smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) { int i; @@ -144,21 +142,17 @@ static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags, return mode; } - if ((flags & GENERIC_READ) || - (flags & FILE_READ_RIGHTS)) + if ((flags & GENERIC_READ) || (flags & FILE_READ_RIGHTS)) mode = 0444; - if ((flags & GENERIC_WRITE) || - (flags & FILE_WRITE_RIGHTS)) { + if ((flags & GENERIC_WRITE) || (flags & FILE_WRITE_RIGHTS)) { mode |= 0222; if (S_ISDIR(fattr->cf_mode)) mode |= 0111; } - if ((flags & GENERIC_EXECUTE) || - (flags & FILE_EXEC_RIGHTS)) + if ((flags & GENERIC_EXECUTE) || (flags & FILE_EXEC_RIGHTS)) mode |= 0111; - if (type == ACCESS_DENIED_ACE_TYPE || - type == ACCESS_DENIED_OBJECT_ACE_TYPE) + if (type == ACCESS_DENIED_ACE_TYPE || type == ACCESS_DENIED_OBJECT_ACE_TYPE) mode = ~mode; ksmbd_debug(SMB, "access flags 0x%x mode now %04o\n", flags, mode); @@ -282,8 +276,7 @@ static int sid_to_id(struct smb_sid *psid, uint sidtype, id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); if (id > 0) { uid = make_kuid(&init_user_ns, id); - if (uid_valid(uid) && - kuid_has_mapping(&init_user_ns, uid)) { + if (uid_valid(uid) && kuid_has_mapping(&init_user_ns, uid)) { fattr->cf_uid = uid; rc = 0; } @@ -295,8 +288,7 @@ static int sid_to_id(struct smb_sid *psid, uint sidtype, id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); if (id > 0) { gid = make_kgid(&init_user_ns, id); - if (gid_valid(gid) && - kgid_has_mapping(&init_user_ns, gid)) { + if (gid_valid(gid) && kgid_has_mapping(&init_user_ns, gid)) { fattr->cf_gid = gid; rc = 0; } @@ -353,7 +345,7 @@ int init_acl_state(struct posix_acl_state *state, int cnt) * enough space for either: */ alloc = sizeof(struct posix_ace_state_array) - + cnt*sizeof(struct posix_user_ace_state); + + cnt * sizeof(struct posix_user_ace_state); state->users = kzalloc(alloc, GFP_KERNEL); if (!state->users) return -ENOMEM; @@ -429,17 +421,17 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, * user/group/other have no permissions */ for (i = 0; i < num_aces; ++i) { - ppace[i] = (struct smb_ace *) (acl_base + acl_size); + ppace[i] = (struct smb_ace *)(acl_base + acl_size); acl_base = (char *)ppace[i]; acl_size = le16_to_cpu(ppace[i]->size); ppace[i]->access_req = smb_map_generic_desired_access(ppace[i]->access_req); - if (!(compare_sids(&(ppace[i]->sid), &sid_unix_NFS_mode))) { + if (!(compare_sids(&ppace[i]->sid, &sid_unix_NFS_mode))) { fattr->cf_mode = le32_to_cpu(ppace[i]->sid.sub_auth[2]); break; - } else if (!compare_sids(&(ppace[i]->sid), pownersid)) { + } else if (!compare_sids(&ppace[i]->sid, pownersid)) { acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, ppace[i]->type); acl_mode &= 0700; @@ -449,9 +441,9 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, mode |= acl_mode; } owner_found = true; - } else if (!compare_sids(&(ppace[i]->sid), pgrpsid) || - ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == - DOMAIN_USER_RID_LE) { + } else if (!compare_sids(&ppace[i]->sid, pgrpsid) || + ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == + DOMAIN_USER_RID_LE) { acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, ppace[i]->type); acl_mode &= 0070; @@ -460,7 +452,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, mode |= acl_mode; } group_found = true; - } else if (!compare_sids(&(ppace[i]->sid), &sid_everyone)) { + } else if (!compare_sids(&ppace[i]->sid, &sid_everyone)) { acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, ppace[i]->type); acl_mode &= 0007; @@ -469,13 +461,13 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, mode |= acl_mode; } others_found = true; - } else if (!compare_sids(&(ppace[i]->sid), &creator_owner)) + } else if (!compare_sids(&ppace[i]->sid, &creator_owner)) { continue; - else if (!compare_sids(&(ppace[i]->sid), &creator_group)) + } else if (!compare_sids(&ppace[i]->sid, &creator_group)) { continue; - else if (!compare_sids(&(ppace[i]->sid), &sid_authusers)) + } else if (!compare_sids(&ppace[i]->sid, &sid_authusers)) { continue; - else { + } else { struct smb_fattr temp_fattr; acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, @@ -610,7 +602,7 @@ static void set_posix_acl_entries_dacl(struct smb_ace *pndace, if (S_ISDIR(fattr->cf_mode) && pace->e_tag == ACL_OTHER) flags = 0x03; - ntace = (struct smb_ace *) ((char *)pndace + *size); + ntace = (struct smb_ace *)((char *)pndace + *size); *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, pace->e_perm, 0777); (*num_aces)++; @@ -619,8 +611,8 @@ static void set_posix_acl_entries_dacl(struct smb_ace *pndace, FILE_DELETE_LE | FILE_DELETE_CHILD_LE; if (S_ISDIR(fattr->cf_mode) && - (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { - ntace = (struct smb_ace *) ((char *)pndace + *size); + (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { + ntace = (struct smb_ace *)((char *)pndace + *size); *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x03, pace->e_perm, 0777); (*num_aces)++; @@ -661,7 +653,7 @@ posix_default_acl: continue; } - ntace = (struct smb_ace *) ((char *)pndace + *size); + ntace = (struct smb_ace *)((char *)pndace + *size); *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, pace->e_perm, 0777); (*num_aces)++; @@ -786,7 +778,7 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, __u32 dacloffset; int pntsd_type; - if (pntsd == NULL) + if (!pntsd) return -EIO; owner_sid_ptr = (struct smb_sid *)((char *)pntsd + @@ -913,11 +905,11 @@ int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl)); dacl_ptr->num_aces = 0; - if (!ppntsd) + if (!ppntsd) { set_mode_dacl(dacl_ptr, fattr); - else if (!ppntsd->dacloffset) + } else if (!ppntsd->dacloffset) { goto out; - else { + } else { struct smb_acl *ppdacl_ptr; ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + @@ -992,8 +984,9 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, flags |= INHERIT_ONLY_ACE; if (flags & NO_PROPAGATE_INHERIT_ACE) flags = 0; - } else + } else { flags = 0; + } if (!compare_sids(&creator_owner, &parent_aces->sid)) { creator = &creator_owner; @@ -1016,8 +1009,9 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); flags |= INHERIT_ONLY_ACE; psid = creator; - } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) + } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) { psid = &parent_aces->sid; + } smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, parent_aces->access_req); @@ -1166,7 +1160,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { granted |= le32_to_cpu(ace->access_req); - ace = (struct smb_ace *) ((char *)ace + le16_to_cpu(ace->size)); + ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); if (end_of_acl < (char *)ace) goto err_out; } @@ -1189,7 +1183,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, if (!compare_sids(&sid_everyone, &ace->sid)) others_ace = ace; - ace = (struct smb_ace *) ((char *)ace + le16_to_cpu(ace->size)); + ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); if (end_of_acl < (char *)ace) goto err_out; } @@ -1229,9 +1223,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, posix_acl_release(posix_acls); if (!found) { - if (others_ace) + if (others_ace) { ace = others_ace; - else { + } else { ksmbd_debug(SMB, "Can't find corresponding sid\n"); rc = -EACCES; goto err_out; @@ -1300,8 +1294,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, if (type_check && !(le16_to_cpu(pntsd->type) & DACL_PRESENT)) goto out; - if (test_share_config_flag(tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) { + if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { /* Update WinACL in xattr */ ksmbd_vfs_remove_sd_xattrs(dentry); ksmbd_vfs_set_sd_xattr(conn, dentry, pntsd, ntsd_len); diff --git a/fs/cifsd/time_wrappers.h b/fs/cifsd/time_wrappers.h index a702ca96947e..31bea2058f88 100644 --- a/fs/cifsd/time_wrappers.h +++ b/fs/cifsd/time_wrappers.h @@ -11,13 +11,13 @@ * between different kernel versions. */ -#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) +#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) /* Convert the Unix UTC into NT UTC. */ static inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) { /* Convert to 100ns intervals and then add the NTFS time offset. */ - return (u64) t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; + return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; } struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); diff --git a/fs/cifsd/transport_ipc.c b/fs/cifsd/transport_ipc.c index e5f4d97b2924..1bbff53436b3 100644 --- a/fs/cifsd/transport_ipc.c +++ b/fs/cifsd/transport_ipc.c @@ -75,8 +75,7 @@ struct ipc_msg_table_entry { static struct delayed_work ipc_timer_work; static int handle_startup_event(struct sk_buff *skb, struct genl_info *info); -static int handle_unsupported_event(struct sk_buff *skb, - struct genl_info *info); +static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info); static int handle_generic_event(struct sk_buff *skb, struct genl_info *info); static int ksmbd_ipc_heartbeat_request(void); @@ -385,8 +384,7 @@ out: return ret; } -static int handle_unsupported_event(struct sk_buff *skb, - struct genl_info *info) +static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info) { ksmbd_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd); return -EINVAL; @@ -453,8 +451,7 @@ out: return ret; } -static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, - unsigned int handle) +static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle) { struct ipc_msg_table_entry entry; int ret; @@ -550,9 +547,9 @@ ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) struct ksmbd_tree_connect_response * ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, - struct ksmbd_share_config *share, - struct ksmbd_tree_connect *tree_conn, - struct sockaddr *peer_addr) + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr) { struct ksmbd_ipc_msg *msg; struct ksmbd_tree_connect_request *req; @@ -591,7 +588,7 @@ ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, } int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, - unsigned long long connect_id) + unsigned long long connect_id) { struct ksmbd_ipc_msg *msg; struct ksmbd_tree_disconnect_request *req; @@ -658,8 +655,7 @@ ksmbd_ipc_share_config_request(const char *name) return resp; } -struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, - int handle) +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle) { struct ksmbd_ipc_msg *msg; struct ksmbd_rpc_command *req; @@ -681,8 +677,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, return resp; } -struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, - int handle) +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle) { struct ksmbd_ipc_msg *msg; struct ksmbd_rpc_command *req; @@ -704,10 +699,8 @@ struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, return resp; } -struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, - int handle, - void *payload, - size_t payload_sz) +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz) { struct ksmbd_ipc_msg *msg; struct ksmbd_rpc_command *req; @@ -731,8 +724,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, return resp; } -struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, - int handle) +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) { struct ksmbd_ipc_msg *msg; struct ksmbd_rpc_command *req; @@ -755,10 +747,8 @@ struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, return resp; } -struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, - int handle, - void *payload, - size_t payload_sz) +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz) { struct ksmbd_ipc_msg *msg; struct ksmbd_rpc_command *req; @@ -782,9 +772,8 @@ struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, return resp; } -struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, - void *payload, - size_t payload_sz) +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, + size_t payload_sz) { struct ksmbd_ipc_msg *msg; struct ksmbd_rpc_command *req; @@ -885,8 +874,7 @@ int ksmbd_ipc_init(void) ret = genl_register_family(&ksmbd_genl_family); if (ret) { - ksmbd_err("Failed to register KSMBD netlink interface %d\n", - ret); + ksmbd_err("Failed to register KSMBD netlink interface %d\n", ret); goto cancel_work; } diff --git a/fs/cifsd/transport_ipc.h b/fs/cifsd/transport_ipc.h index 6ed7cbea727e..c3744ed7a085 100644 --- a/fs/cifsd/transport_ipc.h +++ b/fs/cifsd/transport_ipc.h @@ -20,9 +20,9 @@ struct sockaddr; struct ksmbd_tree_connect_response * ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, - struct ksmbd_share_config *share, - struct ksmbd_tree_connect *tree_conn, - struct sockaddr *peer_addr); + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr); int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, unsigned long long connect_id); @@ -37,24 +37,16 @@ ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len); int ksmbd_ipc_id_alloc(void); void ksmbd_rpc_id_free(int handle); -struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, - int handle); -struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, - int handle); +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle); -struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, - int handle, - void *payload, - size_t payload_sz); -struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, - int handle); -struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, - int handle, - void *payload, - size_t payload_sz); -struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, - void *payload, - size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, + size_t payload_sz); void ksmbd_ipc_release(void); void ksmbd_ipc_soft_reset(void); diff --git a/fs/cifsd/transport_rdma.c b/fs/cifsd/transport_rdma.c index 45b76847f1e7..8174a97bade4 100644 --- a/fs/cifsd/transport_rdma.c +++ b/fs/cifsd/transport_rdma.c @@ -85,7 +85,6 @@ static struct smb_direct_listener { struct rdma_cm_id *cm_id; } smb_direct_listener; - static struct workqueue_struct *smb_direct_wq; enum smb_direct_status { @@ -213,8 +212,8 @@ struct smb_direct_rdma_rw_msg { static void smb_direct_destroy_pools(struct smb_direct_transport *transport); static void smb_direct_post_recv_credits(struct work_struct *work); static int smb_direct_post_send_data(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct kvec *iov, int niov, int remaining_data_length); + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, int remaining_data_length); static inline void *smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg) @@ -223,7 +222,7 @@ static inline void } static inline bool is_receive_credit_post_required(int receive_credits, - int avail_recvmsg_count) + int avail_recvmsg_count) { return receive_credits <= (smb_direct_receive_credit_max >> 3) && avail_recvmsg_count >= (receive_credits >> 2); @@ -246,7 +245,7 @@ smb_direct_recvmsg *get_free_recvmsg(struct smb_direct_transport *t) } static void put_recvmsg(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) + struct smb_direct_recvmsg *recvmsg) { ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, recvmsg->sge.length, DMA_FROM_DEVICE); @@ -254,7 +253,6 @@ static void put_recvmsg(struct smb_direct_transport *t, spin_lock(&t->recvmsg_queue_lock); list_add(&recvmsg->list, &t->recvmsg_queue); spin_unlock(&t->recvmsg_queue_lock); - } static struct @@ -264,8 +262,7 @@ smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) spin_lock(&t->empty_recvmsg_queue_lock); if (!list_empty(&t->empty_recvmsg_queue)) { - recvmsg = list_first_entry( - &t->empty_recvmsg_queue, + recvmsg = list_first_entry(&t->empty_recvmsg_queue, struct smb_direct_recvmsg, list); list_del(&recvmsg->list); } @@ -274,7 +271,7 @@ smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) } static void put_empty_recvmsg(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) + struct smb_direct_recvmsg *recvmsg) { ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, recvmsg->sge.length, DMA_FROM_DEVICE); @@ -285,8 +282,7 @@ static void put_empty_recvmsg(struct smb_direct_transport *t, } static void enqueue_reassembly(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg, - int data_length) + struct smb_direct_recvmsg *recvmsg, int data_length) { spin_lock(&t->reassembly_queue_lock); list_add_tail(&recvmsg->list, &t->reassembly_queue); @@ -300,11 +296,9 @@ static void enqueue_reassembly(struct smb_direct_transport *t, virt_wmb(); t->reassembly_data_length += data_length; spin_unlock(&t->reassembly_queue_lock); - } -static struct smb_direct_recvmsg *get_first_reassembly( - struct smb_direct_transport *t) +static struct smb_direct_recvmsg *get_first_reassembly(struct smb_direct_transport *t) { if (!list_empty(&t->reassembly_queue)) return list_first_entry(&t->reassembly_queue, @@ -423,11 +417,11 @@ static void free_transport(struct smb_direct_transport *t) recvmsg = get_first_reassembly(t); if (recvmsg) { list_del(&recvmsg->list); - spin_unlock( - &t->reassembly_queue_lock); - put_recvmsg(t, recvmsg); - } else spin_unlock(&t->reassembly_queue_lock); + put_recvmsg(t, recvmsg); + } else { + spin_unlock(&t->reassembly_queue_lock); + } } while (recvmsg); t->reassembly_data_length = 0; @@ -460,7 +454,7 @@ static struct smb_direct_sendmsg } static void smb_direct_free_sendmsg(struct smb_direct_transport *t, - struct smb_direct_sendmsg *msg) + struct smb_direct_sendmsg *msg) { int i; @@ -481,8 +475,8 @@ static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) switch (recvmsg->type) { case SMB_DIRECT_MSG_DATA_TRANSFER: { struct smb_direct_data_transfer *req = - (struct smb_direct_data_transfer *) recvmsg->packet; - struct smb2_hdr *hdr = (struct smb2_hdr *) (recvmsg->packet + (struct smb_direct_data_transfer *)recvmsg->packet; + struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet + le32_to_cpu(req->data_offset) - 4); ksmbd_debug(RDMA, "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", @@ -504,12 +498,12 @@ static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) le32_to_cpu(req->max_receive_size), le32_to_cpu(req->max_fragmented_size)); if (le16_to_cpu(req->min_version) > 0x0100 || - le16_to_cpu(req->max_version) < 0x0100) + le16_to_cpu(req->max_version) < 0x0100) return -EOPNOTSUPP; if (le16_to_cpu(req->credits_requested) <= 0 || - le32_to_cpu(req->max_receive_size) <= 128 || - le32_to_cpu(req->max_fragmented_size) <= - 128*1024) + le32_to_cpu(req->max_receive_size) <= 128 || + le32_to_cpu(req->max_fragmented_size) <= + 128 * 1024) return -ECONNABORTED; break; @@ -595,8 +589,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) if (atomic_read(&t->send_credits) > 0) wake_up_interruptible(&t->wait_send_credits); - if (is_receive_credit_post_required(receive_credits, - avail_recvmsg_count)) + if (is_receive_credit_post_required(receive_credits, avail_recvmsg_count)) mod_delayed_work(smb_direct_wq, &t->post_recv_credits_work, 0); break; @@ -607,7 +600,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) } static int smb_direct_post_recv(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) + struct smb_direct_recvmsg *recvmsg) { struct ib_recv_wr wr; int ret; @@ -681,8 +674,7 @@ again: data_transfer = smb_direct_recvmsg_payload(recvmsg); data_length = le32_to_cpu(data_transfer->data_length); remaining_data_length = - le32_to_cpu( - data_transfer->remaining_data_length); + le32_to_cpu(data_transfer->remaining_data_length); data_offset = le32_to_cpu(data_transfer->data_offset); /* @@ -706,9 +698,7 @@ again: } to_copy = min_t(int, data_length - offset, to_read); - memcpy( - buf + data_read, - (char *)data_transfer + data_offset + offset, + memcpy(buf + data_read, (char *)data_transfer + data_offset + offset, to_copy); /* move on to the next buffer? */ @@ -718,20 +708,19 @@ again: * No need to lock if we are not at the * end of the queue */ - if (queue_length) + if (queue_length) { list_del(&recvmsg->list); - else { - spin_lock_irq( - &st->reassembly_queue_lock); + } else { + spin_lock_irq(&st->reassembly_queue_lock); list_del(&recvmsg->list); - spin_unlock_irq( - &st->reassembly_queue_lock); + spin_unlock_irq(&st->reassembly_queue_lock); } queue_removed++; put_recvmsg(st, recvmsg); offset = 0; - } else + } else { offset += to_copy; + } to_read -= to_copy; data_read += to_copy; @@ -744,13 +733,13 @@ again: spin_lock(&st->receive_credit_lock); st->count_avail_recvmsg += queue_removed; - if (is_receive_credit_post_required(st->recv_credits, - st->count_avail_recvmsg)) { + if (is_receive_credit_post_required(st->recv_credits, st->count_avail_recvmsg)) { spin_unlock(&st->receive_credit_lock); mod_delayed_work(smb_direct_wq, &st->post_recv_credits_work, 0); - } else + } else { spin_unlock(&st->receive_credit_lock); + } st->first_entry_offset = offset; ksmbd_debug(RDMA, @@ -762,10 +751,8 @@ read_rfc1002_done: } ksmbd_debug(RDMA, "wait_event on more data\n"); - rc = wait_event_interruptible( - st->wait_reassembly_queue, - st->reassembly_data_length >= size || - st->status != SMB_DIRECT_CS_CONNECTED); + rc = wait_event_interruptible(st->wait_reassembly_queue, + st->reassembly_data_length >= size || st->status != SMB_DIRECT_CS_CONNECTED); if (rc) return -EINTR; @@ -795,8 +782,9 @@ static void smb_direct_post_recv_credits(struct work_struct *work) if (use_free) { use_free = 0; continue; - } else + } else { break; + } } recvmsg->type = SMB_DIRECT_MSG_DATA_TRANSFER; @@ -904,8 +892,8 @@ static int smb_direct_post_send(struct smb_direct_transport *t, } static void smb_direct_send_ctx_init(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - bool need_invalidate_rkey, unsigned int remote_key) + struct smb_direct_send_ctx *send_ctx, + bool need_invalidate_rkey, unsigned int remote_key) { INIT_LIST_HEAD(&send_ctx->msg_list); send_ctx->wr_cnt = 0; @@ -914,7 +902,7 @@ static void smb_direct_send_ctx_init(struct smb_direct_transport *t, } static int smb_direct_flush_send_list(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, bool is_last) + struct smb_direct_send_ctx *send_ctx, bool is_last) { struct smb_direct_sendmsg *first, *last; int ret; @@ -973,12 +961,12 @@ static int wait_for_credits(struct smb_direct_transport *t, } static int wait_for_send_credits(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx) + struct smb_direct_send_ctx *send_ctx) { int ret; if (send_ctx && (send_ctx->wr_cnt >= 16 || - atomic_read(&t->send_credits) <= 1)) { + atomic_read(&t->send_credits) <= 1)) { ret = smb_direct_flush_send_list(t, send_ctx, false); if (ret) return ret; @@ -1048,8 +1036,7 @@ static int smb_direct_create_header(struct smb_direct_transport *t, return 0; } -static int get_sg_list(void *buf, int size, - struct scatterlist *sg_list, int nentries) +static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nentries) { bool high = is_vmalloc_addr(buf); struct page *page; @@ -1082,8 +1069,8 @@ static int get_sg_list(void *buf, int size, } static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, - struct scatterlist *sg_list, int nentries, - enum dma_data_direction dir) + struct scatterlist *sg_list, int nentries, + enum dma_data_direction dir) { int npages; @@ -1094,8 +1081,8 @@ static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, } static int post_sendmsg(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct smb_direct_sendmsg *msg) + struct smb_direct_send_ctx *send_ctx, + struct smb_direct_sendmsg *msg) { int i; @@ -1132,13 +1119,13 @@ static int post_sendmsg(struct smb_direct_transport *t, } static int smb_direct_post_send_data(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct kvec *iov, int niov, int remaining_data_length) + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, int remaining_data_length) { int i, j, ret; struct smb_direct_sendmsg *msg; int data_length; - struct scatterlist sg[SMB_DIRECT_MAX_SEND_SGES-1]; + struct scatterlist sg[SMB_DIRECT_MAX_SEND_SGES - 1]; ret = wait_for_send_credits(t, send_ctx); if (ret) @@ -1159,15 +1146,15 @@ static int smb_direct_post_send_data(struct smb_direct_transport *t, struct ib_sge *sge; int sg_cnt; - sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES-1); + sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES - 1); sg_cnt = get_mapped_sg_list(t->cm_id->device, iov[i].iov_base, iov[i].iov_len, - sg, SMB_DIRECT_MAX_SEND_SGES-1, DMA_TO_DEVICE); + sg, SMB_DIRECT_MAX_SEND_SGES - 1, DMA_TO_DEVICE); if (sg_cnt <= 0) { ksmbd_err("failed to map buffer\n"); ret = -ENOMEM; goto err; - } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES-1) { + } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES - 1) { ksmbd_err("buffer not fitted into sges\n"); ret = -E2BIG; ib_dma_unmap_sg(t->cm_id->device, sg, sg_cnt, @@ -1195,8 +1182,8 @@ err: } static int smb_direct_writev(struct ksmbd_transport *t, - struct kvec *iov, int niovs, int buflen, - bool need_invalidate, unsigned int remote_key) + struct kvec *iov, int niovs, int buflen, + bool need_invalidate, unsigned int remote_key) { struct smb_direct_transport *st = SMB_DIRECT_TRANS(t); int remaining_data_length; @@ -1228,24 +1215,24 @@ static int smb_direct_writev(struct ksmbd_transport *t, if (buflen > max_iov_size) { if (i > start) { remaining_data_length -= - (buflen-iov[i].iov_len); + (buflen - iov[i].iov_len); ret = smb_direct_post_send_data(st, &send_ctx, - &iov[start], i-start, + &iov[start], i - start, remaining_data_length); if (ret) goto done; } else { /* iov[start] is too big, break it */ - int nvec = (buflen+max_iov_size-1) / + int nvec = (buflen + max_iov_size - 1) / max_iov_size; for (j = 0; j < nvec; j++) { vec.iov_base = (char *)iov[start].iov_base + - j*max_iov_size; + j * max_iov_size; vec.iov_len = min_t(int, max_iov_size, - buflen - max_iov_size*j); + buflen - max_iov_size * j); remaining_data_length -= vec.iov_len; ret = smb_direct_post_send_data(st, &send_ctx, &vec, 1, @@ -1265,7 +1252,7 @@ static int smb_direct_writev(struct ksmbd_transport *t, /* send out all remaining vecs */ remaining_data_length -= buflen; ret = smb_direct_post_send_data(st, &send_ctx, - &iov[start], i-start, + &iov[start], i - start, remaining_data_length); if (ret) goto done; @@ -1290,7 +1277,7 @@ done: } static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, - enum dma_data_direction dir) + enum dma_data_direction dir) { struct smb_direct_rdma_rw_msg *msg = container_of(wc->wr_cqe, struct smb_direct_rdma_rw_msg, cqe); @@ -1323,8 +1310,8 @@ static void write_done(struct ib_cq *cq, struct ib_wc *wc) } static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, - int buf_len, u32 remote_key, u64 remote_offset, u32 remote_len, - bool is_read) + int buf_len, u32 remote_key, u64 remote_offset, u32 remote_len, + bool is_read) { struct smb_direct_rdma_rw_msg *msg; int ret; @@ -1392,23 +1379,20 @@ err: sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); kfree(msg); return ret; - } -static int smb_direct_rdma_write(struct ksmbd_transport *t, - void *buf, unsigned int buflen, - u32 remote_key, u64 remote_offset, - u32 remote_len) +static int smb_direct_rdma_write(struct ksmbd_transport *t, void *buf, + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len) { return smb_direct_rdma_xmit(SMB_DIRECT_TRANS(t), buf, buflen, remote_key, remote_offset, remote_len, false); } -static int smb_direct_rdma_read(struct ksmbd_transport *t, - void *buf, unsigned int buflen, - u32 remote_key, u64 remote_offset, - u32 remote_len) +static int smb_direct_rdma_read(struct ksmbd_transport *t, void *buf, + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len) { return smb_direct_rdma_xmit(SMB_DIRECT_TRANS(t), buf, buflen, remote_key, remote_offset, @@ -1428,7 +1412,7 @@ static void smb_direct_disconnect(struct ksmbd_transport *t) } static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, - struct rdma_cm_event *event) + struct rdma_cm_event *event) { struct smb_direct_transport *t = cm_id->context; @@ -1505,8 +1489,7 @@ static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, resp->reserved = 0; resp->credits_requested = cpu_to_le16(t->send_credit_target); - resp->credits_granted = cpu_to_le16( - manage_credits_prior_sending(t)); + resp->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); resp->max_readwrite_size = cpu_to_le32(t->max_rdma_rw_size); resp->preferred_send_size = cpu_to_le32(t->max_send_size); resp->max_receive_size = cpu_to_le32(t->max_recv_size); @@ -1665,7 +1648,7 @@ static int smb_direct_init_params(struct smb_direct_transport *t, max_send_wrs = smb_direct_send_credit_target + max_rw_wrs; if (max_send_wrs > device->attrs.max_cqe || - max_send_wrs > device->attrs.max_qp_wr) { + max_send_wrs > device->attrs.max_qp_wr) { ksmbd_err("consider lowering send_credit_target = %d, or max_outstanding_rw_ops = %d\n", smb_direct_send_credit_target, smb_direct_max_outstanding_rw_ops); @@ -1936,7 +1919,7 @@ static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) } static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, - struct rdma_cm_event *event) + struct rdma_cm_event *event) { switch (event->event) { case RDMA_CM_EVENT_CONNECT_REQUEST: { @@ -2010,7 +1993,7 @@ int ksmbd_rdma_init(void) * for lack of credits */ smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", - WQ_HIGHPRI|WQ_MEM_RECLAIM, 0); + WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); if (!smb_direct_wq) return -ENOMEM; diff --git a/fs/cifsd/transport_tcp.c b/fs/cifsd/transport_tcp.c index 5dd8641f66ba..67163efcf472 100644 --- a/fs/cifsd/transport_tcp.c +++ b/fs/cifsd/transport_tcp.c @@ -13,8 +13,8 @@ #include "connection.h" #include "transport_tcp.h" -#define IFACE_STATE_DOWN (1 << 0) -#define IFACE_STATE_CONFIGURED (1 << 1) +#define IFACE_STATE_DOWN BIT(0) +#define IFACE_STATE_CONFIGURED BIT(1) struct interface { struct task_struct *ksmbd_kthread; @@ -113,7 +113,7 @@ static void free_transport(struct tcp_transport *t) * Return: Number of IO segments */ static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov, - unsigned int nr_segs, size_t bytes) + unsigned int nr_segs, size_t bytes) { size_t base = 0; @@ -142,8 +142,7 @@ static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov, * * Return: return existing or newly allocate iovec */ -static struct kvec *get_conn_iovec(struct tcp_transport *t, - unsigned int nr_segs) +static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs) { struct kvec *new_iov; @@ -287,10 +286,8 @@ static int ksmbd_tcp_run_kthread(struct interface *iface) * Return: on success return number of bytes read from socket, * otherwise return error number */ -static int ksmbd_tcp_readv(struct tcp_transport *t, - struct kvec *iov_orig, - unsigned int nr_segs, - unsigned int to_read) +static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig, + unsigned int nr_segs, unsigned int to_read) { int length = 0; int total_read; @@ -345,9 +342,7 @@ static int ksmbd_tcp_readv(struct tcp_transport *t, * Return: on success return number of bytes read from socket, * otherwise return error number */ -static int ksmbd_tcp_read(struct ksmbd_transport *t, - char *buf, - unsigned int to_read) +static int ksmbd_tcp_read(struct ksmbd_transport *t, char *buf, unsigned int to_read) { struct kvec iov; @@ -357,9 +352,8 @@ static int ksmbd_tcp_read(struct ksmbd_transport *t, return ksmbd_tcp_readv(TCP_TRANS(t), &iov, 1, to_read); } -static int ksmbd_tcp_writev(struct ksmbd_transport *t, - struct kvec *iov, int nvecs, int size, - bool need_invalidate, unsigned int remote_key) +static int ksmbd_tcp_writev(struct ksmbd_transport *t, struct kvec *iov, + int nvecs, int size, bool need_invalidate, unsigned int remote_key) { struct msghdr smb_msg = {.msg_flags = MSG_NOSIGNAL}; @@ -473,7 +467,7 @@ out_error: } static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, - void *ptr) + void *ptr) { struct net_device *netdev = netdev_notifier_info_to_dev(ptr); struct interface *iface; @@ -523,7 +517,6 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, } return NOTIFY_DONE; - } static struct notifier_block ksmbd_netdev_notifier = { diff --git a/fs/cifsd/unicode.c b/fs/cifsd/unicode.c index 22a4d10a2000..38bba50c4f16 100644 --- a/fs/cifsd/unicode.c +++ b/fs/cifsd/unicode.c @@ -26,9 +26,8 @@ * * Return: string length after conversion */ -static int smb_utf16_bytes(const __le16 *from, - int maxbytes, - const struct nls_table *codepage) +static int smb_utf16_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage) { int i; int charlen, outlen = 0; @@ -66,7 +65,7 @@ static int smb_utf16_bytes(const __le16 *from, */ static int cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, - bool mapchar) + bool mapchar) { int len = 1; @@ -124,9 +123,9 @@ static inline int is_char_allowed(char *ch) { /* check for control chars, wildcards etc. */ if (!(*ch & 0x80) && - (*ch <= 0x1f || - *ch == '?' || *ch == '"' || *ch == '<' || - *ch == '>' || *ch == '|')) + (*ch <= 0x1f || + *ch == '?' || *ch == '"' || *ch == '<' || + *ch == '>' || *ch == '|')) return 0; return 1; @@ -156,12 +155,8 @@ static inline int is_char_allowed(char *ch) * * Return: string length after conversion */ -static int smb_from_utf16(char *to, - const __le16 *from, - int tolen, - int fromlen, - const struct nls_table *codepage, - bool mapchar) +static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar) { int i, charlen, safelen; int outlen = 0; @@ -214,8 +209,7 @@ static int smb_from_utf16(char *to, * * Return: string length after conversion */ -int -smb_strtoUTF16(__le16 *to, const char *from, int len, +int smb_strtoUTF16(__le16 *to, const char *from, int len, const struct nls_table *codepage) { int charlen; @@ -230,7 +224,7 @@ smb_strtoUTF16(__le16 *to, const char *from, int len, * in destination len is length in wchar_t units (16bits) */ i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, - (wchar_t *) to, len); + (wchar_t *)to, len); /* if success terminate and exit */ if (i >= 0) @@ -272,20 +266,19 @@ success: * * Return: destination string buffer or error ptr */ -char * -smb_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, const struct nls_table *codepage) +char *smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, const struct nls_table *codepage) { int len, ret; char *dst; if (is_unicode) { - len = smb_utf16_bytes((__le16 *) src, maxlen, codepage); + len = smb_utf16_bytes((__le16 *)src, maxlen, codepage); len += nls_nullsize(codepage); dst = kmalloc(len, GFP_KERNEL); if (!dst) return ERR_PTR(-ENOMEM); - ret = smb_from_utf16(dst, (__le16 *) src, len, maxlen, codepage, + ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage, false); if (ret < 0) { kfree(dst); @@ -324,9 +317,8 @@ smb_strndup_from_utf16(const char *src, const int maxlen, * * Return: char length after conversion */ -int -smbConvertToUTF16(__le16 *target, const char *source, int srclen, - const struct nls_table *cp, int mapchars) +int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int mapchars) { int i, j, charlen; char src_char; diff --git a/fs/cifsd/unicode.h b/fs/cifsd/unicode.h index 7135d62bf9b0..c37d7024cf60 100644 --- a/fs/cifsd/unicode.h +++ b/fs/cifsd/unicode.h @@ -32,13 +32,13 @@ * reserved symbols (along with \ and /), otherwise illegal to store * in filenames in NTFS */ -#define UNI_ASTERISK ((__u16) ('*' + 0xF000)) -#define UNI_QUESTION ((__u16) ('?' + 0xF000)) -#define UNI_COLON ((__u16) (':' + 0xF000)) -#define UNI_GRTRTHAN ((__u16) ('>' + 0xF000)) -#define UNI_LESSTHAN ((__u16) ('<' + 0xF000)) -#define UNI_PIPE ((__u16) ('|' + 0xF000)) -#define UNI_SLASH ((__u16) ('\\' + 0xF000)) +#define UNI_ASTERISK ((__u16)('*' + 0xF000)) +#define UNI_QUESTION ((__u16)('?' + 0xF000)) +#define UNI_COLON ((__u16)(':' + 0xF000)) +#define UNI_GRTRTHAN ((__u16)('>' + 0xF000)) +#define UNI_LESSTHAN ((__u16)('<' + 0xF000)) +#define UNI_PIPE ((__u16)('|' + 0xF000)) +#define UNI_SLASH ((__u16)('\\' + 0xF000)) /* Just define what we want from uniupr.h. We don't want to define the tables * in each source file. @@ -63,13 +63,12 @@ extern const struct UniCaseRange CifsUniLowerRange[]; #ifdef __KERNEL__ int smb_strtoUTF16(__le16 *to, const char *from, int len, - const struct nls_table *codepage); -char *smb_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, const struct nls_table *codepage); -extern int smbConvertToUTF16(__le16 *target, const char *source, int srclen, +char *smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, const struct nls_table *codepage); +int smbConvertToUTF16(__le16 *target, const char *source, int srclen, const struct nls_table *cp, int mapchars); -extern char *ksmbd_extract_sharename(char *treename); +char *ksmbd_extract_sharename(char *treename); #endif wchar_t cifs_toupper(wchar_t in); @@ -80,8 +79,7 @@ wchar_t cifs_toupper(wchar_t in); * Returns: * Address of the first string */ - static inline wchar_t * -UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) +static inline wchar_t *UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) { wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */ @@ -100,14 +98,13 @@ UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) * Address of first occurrence of character in string * or NULL if the character is not in the string */ - static inline wchar_t * -UniStrchr(const wchar_t *ucs, wchar_t uc) +static inline wchar_t *UniStrchr(const wchar_t *ucs, wchar_t uc) { while ((*ucs != uc) && *ucs) ucs++; if (*ucs == uc) - return (wchar_t *) ucs; + return (wchar_t *)ucs; return NULL; } @@ -119,21 +116,19 @@ UniStrchr(const wchar_t *ucs, wchar_t uc) * = 0: Strings are equal * > 0: First string is greater than second */ - static inline int -UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) +static inline int UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) { while ((*ucs1 == *ucs2) && *ucs1) { ucs1++; ucs2++; } - return (int) *ucs1 - (int) *ucs2; + return (int)*ucs1 - (int)*ucs2; } /* * UniStrcpy: Copy a string */ - static inline wchar_t * -UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) +static inline wchar_t *UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) { wchar_t *anchor = ucs1; /* save the start of result string */ @@ -145,8 +140,7 @@ UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) /* * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) */ - static inline size_t -UniStrlen(const wchar_t *ucs1) +static inline size_t UniStrlen(const wchar_t *ucs1) { int i = 0; @@ -159,8 +153,7 @@ UniStrlen(const wchar_t *ucs1) * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a * string (length limited) */ - static inline size_t -UniStrnlen(const wchar_t *ucs1, int maxlen) +static inline size_t UniStrnlen(const wchar_t *ucs1, int maxlen) { int i = 0; @@ -175,8 +168,7 @@ UniStrnlen(const wchar_t *ucs1, int maxlen) /* * UniStrncat: Concatenate length limited string */ - static inline wchar_t * -UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +static inline wchar_t *UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) { wchar_t *anchor = ucs1; /* save pointer to string 1 */ @@ -194,8 +186,7 @@ UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) /* * UniStrncmp: Compare length limited string */ - static inline int -UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) { if (!n) return 0; /* Null strings are equal */ @@ -203,7 +194,7 @@ UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) ucs1++; ucs2++; } - return (int) *ucs1 - (int) *ucs2; + return (int)*ucs1 - (int)*ucs2; } /* @@ -218,14 +209,13 @@ UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) ucs1++; ucs2++; } - return (int) *ucs1 - (int) __le16_to_cpu(*ucs2); + return (int)*ucs1 - (int)__le16_to_cpu(*ucs2); } /* * UniStrncpy: Copy length limited string with pad */ - static inline wchar_t * -UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +static inline wchar_t *UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) { wchar_t *anchor = ucs1; @@ -241,8 +231,7 @@ UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) /* * UniStrncpy_le: Copy length limited string with pad to little-endian */ - static inline wchar_t * -UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +static inline wchar_t *UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) { wchar_t *anchor = ucs1; @@ -262,8 +251,7 @@ UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) * Address of first match found * NULL if no matching string is found */ - static inline wchar_t * -UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) +static inline wchar_t *UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) { const wchar_t *anchor1 = ucs1; const wchar_t *anchor2 = ucs2; @@ -275,14 +263,14 @@ UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) ucs2++; } else { if (!*ucs2) /* Match found */ - return (wchar_t *) anchor1; + return (wchar_t *)anchor1; ucs1 = ++anchor1; /* No match */ ucs2 = anchor2; } } if (!*ucs2) /* Both end together */ - return (wchar_t *) anchor1; /* Match found */ + return (wchar_t *)anchor1; /* Match found */ return NULL; /* No match */ } @@ -290,8 +278,7 @@ UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) /* * UniToupper: Convert a unicode character to upper case */ - static inline wchar_t -UniToupper(register wchar_t uc) +static inline wchar_t UniToupper(register wchar_t uc) { register const struct UniCaseRange *rp; @@ -314,8 +301,7 @@ UniToupper(register wchar_t uc) /* * UniStrupr: Upper case a unicode string */ - static inline __le16 * -UniStrupr(register __le16 *upin) +static inline __le16 *UniStrupr(register __le16 *upin) { register __le16 *up; @@ -332,8 +318,7 @@ UniStrupr(register __le16 *upin) /* * UniTolower: Convert a unicode character to lower case */ - static inline wchar_t -UniTolower(register wchar_t uc) +static inline wchar_t UniTolower(register wchar_t uc) { register const struct UniCaseRange *rp; @@ -356,8 +341,7 @@ UniTolower(register wchar_t uc) /* * UniStrlwr: Lower case a unicode string */ - static inline wchar_t * -UniStrlwr(register wchar_t *upin) +static inline wchar_t *UniStrlwr(register wchar_t *upin) { register wchar_t *up; diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index da44d131e25b..69dc1ee0fc75 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -61,19 +61,17 @@ static void rollback_path_modification(char *filename) } static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, - struct inode *parent_inode, - struct inode *inode) + struct inode *parent_inode, struct inode *inode) { if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_INHERIT_OWNER)) + KSMBD_SHARE_FLAG_INHERIT_OWNER)) return; i_uid_write(inode, i_uid_read(parent_inode)); } static void ksmbd_vfs_inherit_smack(struct ksmbd_work *work, - struct dentry *dir_dentry, - struct dentry *dentry) + struct dentry *dir_dentry, struct dentry *dentry) { char *name, *xattr_list = NULL, *smack_buf; int value_len, xattr_list_len; @@ -173,7 +171,6 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) return 0; } - /** * ksmbd_vfs_create() - vfs helper for smb create file * @work: work @@ -182,9 +179,7 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) * * Return: 0 on success, otherwise error */ -int ksmbd_vfs_create(struct ksmbd_work *work, - const char *name, - umode_t mode) +int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) { struct path path; struct dentry *dentry; @@ -220,9 +215,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, * * Return: 0 on success, otherwise error */ -int ksmbd_vfs_mkdir(struct ksmbd_work *work, - const char *name, - umode_t mode) +int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) { struct path path; struct dentry *dentry; @@ -243,17 +236,16 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(dentry)); ksmbd_vfs_inherit_smack(work, path.dentry, dentry); - } else + } else { ksmbd_err("mkdir(%s): creation failed (err:%d)\n", name, err); + } done_path_create(&path, dentry); return err; } -static ssize_t ksmbd_vfs_getcasexattr(struct dentry *dentry, - char *attr_name, - int attr_name_len, - char **attr_value) +static ssize_t ksmbd_vfs_getcasexattr(struct dentry *dentry, char *attr_name, + int attr_name_len, char **attr_value) { char *name, *xattr_list = NULL; ssize_t value_len = -ENOENT, xattr_list_len; @@ -282,7 +274,7 @@ out: } static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, - size_t count) + size_t count) { ssize_t v_len; char *stream_buf = NULL; @@ -314,10 +306,8 @@ static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, * * Return: 0 on success, otherwise error */ -static int check_lock_range(struct file *filp, - loff_t start, - loff_t end, - unsigned char type) +static int check_lock_range(struct file *filp, loff_t start, loff_t end, + unsigned char type) { struct file_lock *flock; struct file_lock_context *ctx = file_inode(filp)->i_flctx; @@ -360,9 +350,7 @@ out: * * Return: number of read bytes on success, otherwise error */ -int ksmbd_vfs_read(struct ksmbd_work *work, - struct ksmbd_file *fp, - size_t count, +int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, loff_t *pos) { struct file *filp; @@ -416,7 +404,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, } static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, - size_t count) + size_t count) { char *stream_buf = NULL, *wbuf; size_t size, v_len; @@ -483,7 +471,8 @@ out: * Return: 0 on success, otherwise error */ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf, size_t count, loff_t *pos, bool sync, ssize_t *written) + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written) { struct ksmbd_session *sess = work->sess; struct file *filp; @@ -564,7 +553,7 @@ int ksmbd_vfs_getattr(struct path *path, struct kstat *stat) * * Return: 0 on success, otherwise error */ -int ksmbd_vfs_fsync(struct ksmbd_work *work, uint64_t fid, uint64_t p_id) +int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) { struct ksmbd_file *fp; int err; @@ -656,8 +645,8 @@ out: * * Return: 0 on success, otherwise error */ -int ksmbd_vfs_link(struct ksmbd_work *work, - const char *oldname, const char *newname) +int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, + const char *newname) { struct path oldpath, newpath; struct dentry *dentry; @@ -702,11 +691,9 @@ out1: } static int __ksmbd_vfs_rename(struct ksmbd_work *work, - struct dentry *src_dent_parent, - struct dentry *src_dent, - struct dentry *dst_dent_parent, - struct dentry *trap_dent, - char *dst_name) + struct dentry *src_dent_parent, struct dentry *src_dent, + struct dentry *dst_dent_parent, struct dentry *trap_dent, + char *dst_name) { struct dentry *dst_dent; int err; @@ -823,7 +810,7 @@ out: * Return: 0 on success, otherwise error */ int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, - struct ksmbd_file *fp, loff_t size) + struct ksmbd_file *fp, loff_t size) { struct path path; int err = 0; @@ -906,8 +893,7 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) return size; } -static ssize_t ksmbd_vfs_xattr_len(struct dentry *dentry, - char *xattr_name) +static ssize_t ksmbd_vfs_xattr_len(struct dentry *dentry, char *xattr_name) { return vfs_getxattr(&init_user_ns, dentry, xattr_name, NULL, 0); } @@ -920,9 +906,8 @@ static ssize_t ksmbd_vfs_xattr_len(struct dentry *dentry, * * Return: read xattr value length on success, otherwise error */ -ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, - char *xattr_name, - char **xattr_buf) +ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, + char **xattr_buf) { ssize_t xattr_len; char *buf; @@ -955,11 +940,8 @@ ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, * * Return: 0 on success, otherwise error */ -int ksmbd_vfs_setxattr(struct dentry *dentry, - const char *attr_name, - const void *attr_value, - size_t attr_size, - int flags) +int ksmbd_vfs_setxattr(struct dentry *dentry, const char *attr_name, + const void *attr_value, size_t attr_size, int flags) { int err; @@ -987,9 +969,9 @@ void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) if (!option || !mapping) return; - if (option & FILE_WRITE_THROUGH_LE) + if (option & FILE_WRITE_THROUGH_LE) { filp->f_flags |= O_SYNC; - else if (option & FILE_SEQUENTIAL_ONLY_LE) { + } else if (option & FILE_SEQUENTIAL_ONLY_LE) { filp->f_ra.ra_pages = inode_to_bdi(mapping->host)->ra_pages * 2; spin_lock(&filp->f_lock); filp->f_mode &= ~FMODE_RANDOM; @@ -1021,18 +1003,15 @@ int ksmbd_vfs_readdir(struct file *file, struct ksmbd_readdir_data *rdata) return iterate_dir(file, &rdata->ctx); } -int ksmbd_vfs_alloc_size(struct ksmbd_work *work, - struct ksmbd_file *fp, - loff_t len) +int ksmbd_vfs_alloc_size(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t len) { smb_break_all_levII_oplock(work, fp, 1); return vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, len); } -int ksmbd_vfs_zero_data(struct ksmbd_work *work, - struct ksmbd_file *fp, - loff_t off, - loff_t len) +int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t off, loff_t len) { smb_break_all_levII_oplock(work, fp, 1); if (fp->f_ci->m_fattr & ATTR_SPARSE_FILE_LE) @@ -1085,8 +1064,9 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, if (extent_end != -ENXIO) ret = (int)extent_end; break; - } else if (extent_start >= extent_end) + } else if (extent_start >= extent_end) { break; + } ranges[*out_count].file_offset = cpu_to_le64(extent_start); ranges[(*out_count)++].length = @@ -1161,7 +1141,7 @@ unsigned short ksmbd_vfs_logical_sector_size(struct inode *inode) * @fs_ss: fs sector size struct */ void ksmbd_vfs_smb2_sector_size(struct inode *inode, - struct ksmbd_fs_sector_size *fs_ss) + struct ksmbd_fs_sector_size *fs_ss) { struct request_queue *q; @@ -1186,12 +1166,8 @@ void ksmbd_vfs_smb2_sector_size(struct inode *inode, } } -static int __dir_empty(struct dir_context *ctx, - const char *name, - int namlen, - loff_t offset, - u64 ino, - unsigned int d_type) +static int __dir_empty(struct dir_context *ctx, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) { struct ksmbd_readdir_data *buf; @@ -1227,12 +1203,8 @@ int ksmbd_vfs_empty_dir(struct ksmbd_file *fp) return err; } -static int __caseless_lookup(struct dir_context *ctx, - const char *name, - int namlen, - loff_t offset, - u64 ino, - unsigned int d_type) +static int __caseless_lookup(struct dir_context *ctx, const char *name, + int namlen, loff_t offset, u64 ino, unsigned int d_type) { struct ksmbd_readdir_data *buf; @@ -1260,7 +1232,7 @@ static int ksmbd_vfs_lookup_in_dir(char *dirname, char *filename) struct path dir_path; int ret; struct file *dfilp; - int flags = O_RDONLY|O_LARGEFILE; + int flags = O_RDONLY | O_LARGEFILE; int dirnamelen = strlen(dirname); struct ksmbd_readdir_data readdir_data = { .ctx.actor = __caseless_lookup, @@ -1349,9 +1321,9 @@ int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry) ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); if (!strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS, - sizeof(XATTR_NAME_POSIX_ACL_ACCESS)-1) || + sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) || !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, - sizeof(XATTR_NAME_POSIX_ACL_DEFAULT)-1)) { + sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) { err = ksmbd_vfs_remove_xattr(dentry, name); if (err) ksmbd_debug(SMB, @@ -1621,8 +1593,9 @@ int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, if (ndr_decode_dos_attr(&n, da)) err = -EINVAL; ksmbd_free(n.data); - } else + } else { ksmbd_debug(SMB, "failed to load dos attribute in xattr\n"); + } return err; } @@ -1687,9 +1660,8 @@ void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) return info; } -int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, - struct dentry *dentry, - struct ksmbd_kstat *ksmbd_kstat) +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat) { u64 time; int rc; @@ -1709,23 +1681,23 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, ksmbd_kstat->file_attributes = ATTR_ARCHIVE_LE; if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { struct xattr_dos_attrib da; rc = ksmbd_vfs_get_dos_attrib_xattr(dentry, &da); if (rc > 0) { ksmbd_kstat->file_attributes = cpu_to_le32(da.attr); ksmbd_kstat->create_time = da.create_time; - } else + } else { ksmbd_debug(VFS, "fail to load dos attribute.\n"); + } } return 0; } -ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, - char *attr_name, - int attr_name_len) +ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, char *attr_name, + int attr_name_len) { char *name, *xattr_list = NULL; ssize_t value_len = -ENOENT, xattr_list_len; @@ -1749,10 +1721,8 @@ out: return value_len; } -int ksmbd_vfs_xattr_stream_name(char *stream_name, - char **xattr_stream_name, - size_t *xattr_stream_name_size, - int s_type) +int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type) { int stream_name_size; char *xattr_stream_name_buf; @@ -1791,8 +1761,7 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, } static int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, - struct file *file_out, loff_t pos_out, - size_t len) + struct file *file_out, loff_t pos_out, size_t len) { struct inode *inode_in = file_inode(file_in); struct inode *inode_out = file_inode(file_out); @@ -1838,13 +1807,10 @@ static int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, } int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, - struct ksmbd_file *src_fp, - struct ksmbd_file *dst_fp, - struct srv_copychunk *chunks, - unsigned int chunk_count, - unsigned int *chunk_count_written, - unsigned int *chunk_size_written, - loff_t *total_size_written) + struct ksmbd_file *src_fp, struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, loff_t *total_size_written) { unsigned int i; loff_t src_off, dst_off, src_file_size; @@ -1876,10 +1842,10 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, len = le32_to_cpu(chunks[i].Length); if (check_lock_range(src_fp->filp, src_off, - src_off + len - 1, READ)) + src_off + len - 1, READ)) return -EAGAIN; if (check_lock_range(dst_fp->filp, dst_off, - dst_off + len - 1, WRITE)) + dst_off + len - 1, WRITE)) return -EAGAIN; } } diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index bbef5c20c146..e1ca9ac11ba5 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -101,7 +101,6 @@ struct xattr_ntacl { #define XATTR_NAME_SD_LEN \ (sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1) - /* CreateOptions */ /* Flag is set, it must not be a file , valid for directory only */ #define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) @@ -197,10 +196,11 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess); int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, - size_t count, loff_t *pos); + size_t count, loff_t *pos); int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf, size_t count, loff_t *pos, bool sync, ssize_t *written); -int ksmbd_vfs_fsync(struct ksmbd_work *work, uint64_t fid, uint64_t p_id); + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written); +int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, const char *newname); @@ -210,90 +210,53 @@ int ksmbd_vfs_readlink(struct path *path, char *buf, int lenp); int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, char *newname); -int ksmbd_vfs_rename_slowpath(struct ksmbd_work *work, - char *oldname, char *newname); int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, - struct ksmbd_file *fp, loff_t size); + struct ksmbd_file *fp, loff_t size); struct srv_copychunk; int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, - struct ksmbd_file *src_fp, - struct ksmbd_file *dst_fp, - struct srv_copychunk *chunks, - unsigned int chunk_count, - unsigned int *chunk_count_written, - unsigned int *chunk_size_written, - loff_t *total_size_written); - -struct ksmbd_file *ksmbd_vfs_dentry_open(struct ksmbd_work *work, - const struct path *path, - int flags, - __le32 option, - int fexist); + struct ksmbd_file *src_fp, struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, loff_t *total_size_written); ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); -ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, - char *xattr_name, - char **xattr_buf); - -ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, - char *attr_name, - int attr_name_len); - -int ksmbd_vfs_setxattr(struct dentry *dentry, - const char *attr_name, - const void *attr_value, - size_t attr_size, - int flags); - -int ksmbd_vfs_fsetxattr(const char *filename, - const char *attr_name, - const void *attr_value, - size_t attr_size, - int flags); - -int ksmbd_vfs_xattr_stream_name(char *stream_name, - char **xattr_stream_name, - size_t *xattr_stream_name_size, - int s_type); - +ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, + char **xattr_buf); +ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, char *attr_name, + int attr_name_len); +int ksmbd_vfs_setxattr(struct dentry *dentry, const char *attr_name, + const void *attr_value, size_t attr_size, int flags); +int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type); int ksmbd_vfs_truncate_xattr(struct dentry *dentry, int wo_streams); int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name); void ksmbd_vfs_xattr_free(char *xattr); - int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, bool caseless); int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); int ksmbd_vfs_lock(struct file *filp, int cmd, struct file_lock *flock); int ksmbd_vfs_readdir(struct file *file, struct ksmbd_readdir_data *rdata); -int ksmbd_vfs_alloc_size(struct ksmbd_work *work, - struct ksmbd_file *fp, - loff_t len); -int ksmbd_vfs_zero_data(struct ksmbd_work *work, - struct ksmbd_file *fp, - loff_t off, - loff_t len); - +int ksmbd_vfs_alloc_size(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t len); +int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t off, loff_t len); struct file_allocated_range_buffer; int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, - struct file_allocated_range_buffer *ranges, - int in_count, int *out_count); + struct file_allocated_range_buffer *ranges, + int in_count, int *out_count); int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry); unsigned short ksmbd_vfs_logical_sector_size(struct inode *inode); void ksmbd_vfs_smb2_sector_size(struct inode *inode, - struct ksmbd_fs_sector_size *fs_ss); + struct ksmbd_fs_sector_size *fs_ss); void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); - -int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, - struct dentry *dentry, - struct ksmbd_kstat *ksmbd_kstat); - +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat); int ksmbd_vfs_posix_lock_wait(struct file_lock *flock); int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); - int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry); int ksmbd_vfs_remove_sd_xattrs(struct dentry *dentry); int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index 2b38628e1cb8..ec631dc6f1fb 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -284,8 +284,7 @@ static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) write_unlock(&global_ft.lock); } -static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, - struct ksmbd_file *fp) +static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) { if (!HAS_FILE_ID(fp->volatile_id)) return; @@ -299,8 +298,7 @@ static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, write_unlock(&ft->lock); } -static void __ksmbd_close_fd(struct ksmbd_file_table *ft, - struct ksmbd_file *fp) +static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) { struct file *filp; @@ -328,7 +326,7 @@ static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) } static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, - unsigned int id) + unsigned int id) { bool unclaimed = true; struct ksmbd_file *fp; @@ -352,8 +350,7 @@ static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, return fp; } -static void __put_fd_final(struct ksmbd_work *work, - struct ksmbd_file *fp) +static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp) { __ksmbd_close_fd(&work->sess->file_table, fp); atomic_dec(&work->conn->stats.open_files_count); @@ -399,8 +396,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, unsigned int id) return 0; } -void ksmbd_fd_put(struct ksmbd_work *work, - struct ksmbd_file *fp) +void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp) { if (!fp) return; @@ -410,8 +406,7 @@ void ksmbd_fd_put(struct ksmbd_work *work, __put_fd_final(work, fp); } -static bool __sanity_check(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp) +static bool __sanity_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) { if (!fp) return false; @@ -420,14 +415,12 @@ static bool __sanity_check(struct ksmbd_tree_connect *tcon, return true; } -struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, - unsigned int id) +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, unsigned int id) { return __ksmbd_lookup_fd(&work->sess->file_table, id); } -struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, - unsigned int id) +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, unsigned int id) { struct ksmbd_file *fp = __ksmbd_lookup_fd(&work->sess->file_table, id); @@ -438,9 +431,8 @@ struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, return NULL; } -struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, - unsigned int id, - unsigned int pid) +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, unsigned int id, + unsigned int pid) { struct ksmbd_file *fp; @@ -469,8 +461,7 @@ struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) return __ksmbd_lookup_fd(&global_ft, id); } -int ksmbd_close_fd_app_id(struct ksmbd_work *work, - char *app_id) +int ksmbd_close_fd_app_id(struct ksmbd_work *work, char *app_id) { struct ksmbd_file *fp = NULL; unsigned int id; @@ -513,8 +504,7 @@ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) return fp; } -struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, - char *filename) +struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, char *filename) { struct ksmbd_file *fp = NULL; unsigned int id; @@ -566,8 +556,7 @@ static void __open_id_set(struct ksmbd_file *fp, unsigned int id, int type) fp->persistent_id = id; } -static int __open_id(struct ksmbd_file_table *ft, - struct ksmbd_file *fp, +static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, int type) { unsigned int id = 0; @@ -601,8 +590,7 @@ unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp) return fp->persistent_id; } -struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, - struct file *filp) +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) { struct ksmbd_file *fp; int ret; @@ -657,7 +645,7 @@ static inline bool is_reconnectable(struct ksmbd_file *fp) if (fp->is_resilient || fp->is_persistent) reconn = true; else if (fp->is_durable && opinfo->is_lease && - opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE) reconn = true; else if (fp->is_durable && opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) @@ -668,10 +656,8 @@ static inline bool is_reconnectable(struct ksmbd_file *fp) } static int -__close_file_table_ids(struct ksmbd_file_table *ft, - struct ksmbd_tree_connect *tcon, - bool (*skip)(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp)) +__close_file_table_ids(struct ksmbd_file_table *ft, struct ksmbd_tree_connect *tcon, + bool (*skip)(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp)) { unsigned int id; struct ksmbd_file *fp; @@ -691,14 +677,12 @@ __close_file_table_ids(struct ksmbd_file_table *ft, return num; } -static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp) +static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) { return fp->tcon != tcon; } -static bool session_fd_check(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp) +static bool session_fd_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) { if (!is_reconnectable(fp)) return false; @@ -745,8 +729,7 @@ void ksmbd_free_global_file_table(void) ksmbd_destroy_file_table(&global_ft); } -int ksmbd_reopen_durable_fd(struct ksmbd_work *work, - struct ksmbd_file *fp) +int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) { if (!fp->is_durable || fp->conn || fp->tcon) { ksmbd_err("Invalid durable fd [%p:%p]\n", diff --git a/fs/cifsd/vfs_cache.h b/fs/cifsd/vfs_cache.h index 04ab5967a9ae..318dcb1a297a 100644 --- a/fs/cifsd/vfs_cache.h +++ b/fs/cifsd/vfs_cache.h @@ -149,50 +149,31 @@ static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) int ksmbd_init_file_table(struct ksmbd_file_table *ft); void ksmbd_destroy_file_table(struct ksmbd_file_table *ft); - int ksmbd_close_fd(struct ksmbd_work *work, unsigned int id); - -struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, - unsigned int id); -struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, - unsigned int id); -struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, - unsigned int id, - unsigned int pid); - +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, unsigned int id); +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, unsigned int id); +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, unsigned int id, + unsigned int pid); void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); - int ksmbd_close_fd_app_id(struct ksmbd_work *work, char *app_id); struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); -struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, - char *filename); +struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, char *filename); struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); - unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); - -struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, - struct file *filp); - +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); void ksmbd_close_session_fds(struct ksmbd_work *work); - int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); - -int ksmbd_reopen_durable_fd(struct ksmbd_work *work, - struct ksmbd_file *fp); - +int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp); int ksmbd_init_global_file_table(void); void ksmbd_free_global_file_table(void); - int ksmbd_file_table_flush(struct ksmbd_work *work); - void ksmbd_set_fd_limit(unsigned long limit); /* * INODE hash */ - int __init ksmbd_inode_hash_init(void); void ksmbd_release_inode_hash(void); @@ -203,11 +184,9 @@ enum KSMBD_INODE_STATUS { }; int ksmbd_query_inode_status(struct inode *inode); - bool ksmbd_inode_pending_delete(struct ksmbd_file *fp); void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); - void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, int file_info); #endif /* __VFS_CACHE_H__ */ From a648d8aff84beedaff6302df47a947a56533ec41 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 30 Mar 2021 14:56:26 +0900 Subject: [PATCH 032/417] cifsd: merge time_wrappers.h into smb_common.h This patch merge time_wrappers.h into smb_common.h. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/netmisc.c | 17 ++++++++++++++++- fs/cifsd/smb2pdu.c | 1 - fs/cifsd/smb_common.h | 6 ++++++ fs/cifsd/time_wrappers.h | 34 ---------------------------------- fs/cifsd/vfs.c | 1 - 5 files changed, 22 insertions(+), 37 deletions(-) delete mode 100644 fs/cifsd/time_wrappers.h diff --git a/fs/cifsd/netmisc.c b/fs/cifsd/netmisc.c index 55393667abcc..5d0327d87397 100644 --- a/fs/cifsd/netmisc.c +++ b/fs/cifsd/netmisc.c @@ -10,7 +10,7 @@ #include "glob.h" #include "smberr.h" #include "nterr.h" -#include "time_wrappers.h" +#include "smb_common.h" /* * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) @@ -42,3 +42,18 @@ struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc) return ts; } + +/* Convert the Unix UTC into NT UTC. */ +inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) +{ + /* Convert to 100ns intervals and then add the NTFS time offset. */ + return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; +} + +inline long long ksmbd_systime(void) +{ + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + return ksmbd_UnixTimeToNT(ts); +} diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 3e8f1a3800dd..139041768f65 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -26,7 +26,6 @@ #include "vfs_cache.h" #include "misc.h" -#include "time_wrappers.h" #include "server.h" #include "smb_common.h" #include "smbstatus.h" diff --git a/fs/cifsd/smb_common.h b/fs/cifsd/smb_common.h index 2d7b1c693ff4..2e171c9002b2 100644 --- a/fs/cifsd/smb_common.h +++ b/fs/cifsd/smb_common.h @@ -541,4 +541,10 @@ static inline void inc_rfc1001_len(void *buf, int count) { be32_add_cpu((__be32 *)buf, count); } + +#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) + +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); +u64 ksmbd_UnixTimeToNT(struct timespec64 t); +long long ksmbd_systime(void); #endif /* __SMB_COMMON_H__ */ diff --git a/fs/cifsd/time_wrappers.h b/fs/cifsd/time_wrappers.h deleted file mode 100644 index 31bea2058f88..000000000000 --- a/fs/cifsd/time_wrappers.h +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_TIME_WRAPPERS_H -#define __KSMBD_TIME_WRAPPERS_H - -/* - * A bunch of ugly hacks to workaoround all the API differences - * between different kernel versions. - */ - -#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) - -/* Convert the Unix UTC into NT UTC. */ -static inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) -{ - /* Convert to 100ns intervals and then add the NTFS time offset. */ - return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; -} - -struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); - -#define KSMBD_TIME_TO_TM time64_to_tm - -static inline long long ksmbd_systime(void) -{ - struct timespec64 ts; - - ktime_get_real_ts64(&ts); - return ksmbd_UnixTimeToNT(ts); -} -#endif /* __KSMBD_TIME_WRAPPERS_H */ diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 69dc1ee0fc75..264f8932d40f 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -31,7 +31,6 @@ #include "ndr.h" #include "auth.h" -#include "time_wrappers.h" #include "smb_common.h" #include "mgmt/share_config.h" #include "mgmt/tree_connect.h" From 5365564901778d96a81e00e34c804d4fb05f0093 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 30 Mar 2021 14:42:05 +0900 Subject: [PATCH 033/417] cifsd: fix wrong prototype in comment kernel test robot reported: >> fs/cifsd/oplock.c:1454: warning: expecting prototype for create_durable_rsp__buf(). Prototype was for create_durable_rsp_buf() instead This patch fix wrong prototype in comment. Reported-by: kernel test robot Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/oplock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index 8e072c3e7b89..4ff23aee69fa 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -1436,7 +1436,7 @@ struct create_context *smb2_find_context_vals(void *open_req, const char *tag) } /** - * create_durable_rsp__buf() - create durable handle context + * create_durable_rsp_buf() - create durable handle context * @cc: buffer to create durable context response */ void create_durable_rsp_buf(char *cc) From a36abeaaf00f8bd07af4b530f12b9a64dc15c777 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 30 Mar 2021 14:43:16 +0900 Subject: [PATCH 034/417] cifsd: fix implicit declaration of function 'groups_alloc' kernel test robot reported: fs/cifsd/smb_common.c: In function 'ksmbd_override_fsids': >> fs/cifsd/smb_common.c:613:7: error: implicit declaration of function >> 'groups_alloc'; did you mean 'cgroup_sk_alloc'? >> [-Werror=implicit-function-declaration] 613 | gi = groups_alloc(0); | ^~~~~~~~~~~~ | cgroup_sk_alloc fs/cifsd/smb_common.c:613:5: warning: assignment to 'struct group_info *' from 'int' makes pointer from integer without a cast [-Wint-conversion] 613 | gi = groups_alloc(0); | ^ >> fs/cifsd/smb_common.c:618:2: error: implicit declaration of function >> 'set_groups'; did you mean 'get_cgroup_ns'? >> [-Werror=implicit-function-declaration] 618 | set_groups(cred, gi); | ^~~~~~~~~~ | get_cgroup_ns cc1: some warnings being treated as errors This patch add depends on MULTIUSER. Reported-by: kernel test robot Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifsd/Kconfig b/fs/cifsd/Kconfig index 8c5dd9a44401..37fd9124129b 100644 --- a/fs/cifsd/Kconfig +++ b/fs/cifsd/Kconfig @@ -1,6 +1,7 @@ config SMB_SERVER tristate "SMB server support (EXPERIMENTAL)" depends on INET + depends on MULTIUSER select NLS select NLS_UTF8 select CRYPTO From 17af7d5b8a95bd9ea93edebe7f79a82709a17f2d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 30 Mar 2021 14:49:16 +0900 Subject: [PATCH 035/417] cifsd: fix implicit declaration of function 'locks_alloc_lock' Randy reported build failure: ../fs/cifsd/smb2pdu.c:6655:7: error: implicit declaration of function 'locks_alloc_lock'; did you mean 'locks_copy_lock'? This patch add depend on FILE_LOCKING. Reported-by: Randy Dunlap Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifsd/Kconfig b/fs/cifsd/Kconfig index 37fd9124129b..d1ac53c83125 100644 --- a/fs/cifsd/Kconfig +++ b/fs/cifsd/Kconfig @@ -2,6 +2,7 @@ config SMB_SERVER tristate "SMB server support (EXPERIMENTAL)" depends on INET depends on MULTIUSER + depends on FILE_LOCKING select NLS select NLS_UTF8 select CRYPTO From d710f37c7bcd7f2cedab4762fff3e11c83aebf3f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 1 Apr 2021 17:29:23 +0900 Subject: [PATCH 036/417] cifsd: remove smack inherit leftovers smack inherit was added for internal product beofre. It is no longer used. This patch remove it's left overs. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/ksmbd_server.h | 9 ++++----- fs/cifsd/vfs.c | 42 ----------------------------------------- 2 files changed, 4 insertions(+), 47 deletions(-) diff --git a/fs/cifsd/ksmbd_server.h b/fs/cifsd/ksmbd_server.h index c5181a2702ff..e46be4084087 100644 --- a/fs/cifsd/ksmbd_server.h +++ b/fs/cifsd/ksmbd_server.h @@ -224,11 +224,10 @@ enum KSMBD_TREE_CONN_STATUS { #define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) #define KSMBD_SHARE_FLAG_PIPE BIT(8) #define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) -#define KSMBD_SHARE_FLAG_INHERIT_SMACK BIT(10) -#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(11) -#define KSMBD_SHARE_FLAG_STREAMS BIT(12) -#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(13) -#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(14) +#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10) +#define KSMBD_SHARE_FLAG_STREAMS BIT(11) +#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) +#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) /* * Tree connect request flags. diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 264f8932d40f..0ecdb5121c3a 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -69,46 +69,6 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, i_uid_write(inode, i_uid_read(parent_inode)); } -static void ksmbd_vfs_inherit_smack(struct ksmbd_work *work, - struct dentry *dir_dentry, struct dentry *dentry) -{ - char *name, *xattr_list = NULL, *smack_buf; - int value_len, xattr_list_len; - - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_INHERIT_SMACK)) - return; - - xattr_list_len = ksmbd_vfs_listxattr(dir_dentry, &xattr_list); - if (xattr_list_len < 0) { - goto out; - } else if (!xattr_list_len) { - ksmbd_err("no ea data in the file\n"); - return; - } - - for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { - int rc; - - ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); - if (strcmp(name, XATTR_NAME_SMACK)) - continue; - - value_len = ksmbd_vfs_getxattr(dir_dentry, name, &smack_buf); - if (value_len <= 0) - continue; - - rc = ksmbd_vfs_setxattr(dentry, XATTR_NAME_SMACK, smack_buf, - value_len, 0); - ksmbd_free(smack_buf); - if (rc < 0) - ksmbd_err("ksmbd_vfs_setxattr() failed: %d\n", rc); - } -out: - ksmbd_vfs_xattr_free(xattr_list); -} - int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete) { int mask; @@ -198,7 +158,6 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) if (!err) { ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(dentry)); - ksmbd_vfs_inherit_smack(work, path.dentry, dentry); } else { ksmbd_err("File(%s): creation failed (err:%d)\n", name, err); } @@ -234,7 +193,6 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) if (!err) { ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(dentry)); - ksmbd_vfs_inherit_smack(work, path.dentry, dentry); } else { ksmbd_err("mkdir(%s): creation failed (err:%d)\n", name, err); } From 96a34377dc5a0969b7b0404fce84159b7c8f89d7 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 1 Apr 2021 17:23:21 +0900 Subject: [PATCH 037/417] cifsd: remove calling d_path in error paths calling d_path is excessive in error paths. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 0ecdb5121c3a..b509c90d911f 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -312,9 +312,8 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, { struct file *filp; ssize_t nbytes = 0; - char *rbuf, *name; + char *rbuf; struct inode *inode; - char namebuf[NAME_MAX]; rbuf = work->aux_payload_buf; filp = fp->filp; @@ -348,11 +347,8 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, nbytes = kernel_read(filp, rbuf, count, pos); if (nbytes < 0) { - name = d_path(&filp->f_path, namebuf, sizeof(namebuf)); - if (IS_ERR(name)) - name = "(error)"; ksmbd_err("smb read failed for (%s), err = %zd\n", - name, nbytes); + fp->filename, nbytes); return nbytes; } From 1637023594c1fd11fa4d77dd0c9493a864aa0d17 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 1 Apr 2021 17:32:24 +0900 Subject: [PATCH 038/417] cifsd: handle unhashed dentry in ksmbd_vfs_mkdir vfs_mkdir could return the dentry left unhashed negative on success. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index b509c90d911f..bdc30a7b6d52 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -190,14 +190,32 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) mode |= S_IFDIR; err = vfs_mkdir(&init_user_ns, d_inode(path.dentry), dentry, mode); - if (!err) { - ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), - d_inode(dentry)); - } else { - ksmbd_err("mkdir(%s): creation failed (err:%d)\n", name, err); - } + if (err) + goto out; + else if (d_unhashed(dentry)) { + struct dentry *d; + d = lookup_one_len(dentry->d_name.name, + dentry->d_parent, + dentry->d_name.len); + if (IS_ERR(d)) { + err = PTR_ERR(d); + goto out; + } + if (unlikely(d_is_negative(d))) { + dput(d); + err = -ENOENT; + goto out; + } + + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), + d_inode(d)); + dput(d); + } +out: done_path_create(&path, dentry); + if (err) + ksmbd_err("mkdir(%s): creation failed (err:%d)\n", name, err); return err; } From d2f72ed8fa0c0e6c90af8ee0bbb39d41ab2d5465 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 1 Apr 2021 17:33:47 +0900 Subject: [PATCH 039/417] cifsd: use file_inode() instead of d_inode() use file_inode() to get layerd filesystems right. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index bdc30a7b6d52..6313d5ca4b46 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -335,7 +335,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, rbuf = work->aux_payload_buf; filp = fp->filp; - inode = d_inode(filp->f_path.dentry); + inode = file_inode(filp); if (S_ISDIR(inode->i_mode)) return -EISDIR; From 8044ee8e64b4fdb068e504ec3ade597d1ccad456 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 1 Apr 2021 17:47:19 +0900 Subject: [PATCH 040/417] cifsd: remove useless error handling in ksmbd_vfs_read dentry->d_inode never happen to be NULL if we hold the dentry. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 6313d5ca4b46..ef823679f6be 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -550,7 +550,7 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) { struct path parent; - struct dentry *dir, *dentry; + struct dentry *dentry; char *last; int err; @@ -569,12 +569,8 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) return err; } - dir = parent.dentry; - if (!d_inode(dir)) - goto out; - - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); - dentry = lookup_one_len(last, dir, strlen(last)); + inode_lock_nested(d_inode(parent.dentry), I_MUTEX_PARENT); + dentry = lookup_one_len(last, parent.dentry, strlen(last)); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); ksmbd_debug(VFS, "%s: lookup failed, err %d\n", last, err); @@ -588,12 +584,12 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) } if (S_ISDIR(d_inode(dentry)->i_mode)) { - err = vfs_rmdir(&init_user_ns, d_inode(dir), dentry); + err = vfs_rmdir(&init_user_ns, d_inode(parent.dentry), dentry); if (err && err != -ENOTEMPTY) ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, err); } else { - err = vfs_unlink(&init_user_ns, d_inode(dir), dentry, NULL); + err = vfs_unlink(&init_user_ns, d_inode(parent.dentry), dentry, NULL); if (err) ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, err); @@ -601,8 +597,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) dput(dentry); out_err: - inode_unlock(d_inode(dir)); -out: + inode_unlock(d_inode(parent.dentry)); rollback_path_modification(last); path_put(&parent); ksmbd_revert_fsids(work); From 02b68b2065c91ce706f462fd509032a77db5d9dc Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 1 Apr 2021 17:45:33 +0900 Subject: [PATCH 041/417] cifsd: use xarray instead of linked list for tree connect list Matthew suggest to change linked list of tree connect list to xarray. It will be tree connect lookup in O(log(n)) time instead of O(n) time. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/mgmt/tree_connect.c | 35 +++++++++++++++-------------------- fs/cifsd/mgmt/user_session.c | 4 +++- fs/cifsd/mgmt/user_session.h | 6 +++++- fs/cifsd/smb2pdu.c | 2 +- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/fs/cifsd/mgmt/tree_connect.c b/fs/cifsd/mgmt/tree_connect.c index d5670f2596a3..0c8374e8240f 100644 --- a/fs/cifsd/mgmt/tree_connect.c +++ b/fs/cifsd/mgmt/tree_connect.c @@ -5,6 +5,8 @@ #include #include +#include +#include #include "../buffer_pool.h" #include "../transport_ipc.h" @@ -23,6 +25,7 @@ ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name) struct ksmbd_share_config *sc; struct ksmbd_tree_connect *tree_conn = NULL; struct sockaddr *peer_addr; + int ret; sc = ksmbd_share_config_get(share_name); if (!sc) @@ -59,8 +62,12 @@ ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name) tree_conn->share_conf = sc; status.tree_conn = tree_conn; - list_add(&tree_conn->list, &sess->tree_conn_list); - + ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, + GFP_KERNEL)); + if (ret) { + status.ret = -ENOMEM; + goto out_error; + } ksmbd_free(resp); return status; @@ -80,7 +87,7 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); ksmbd_release_tree_conn_id(sess, tree_conn->id); - list_del(&tree_conn->list); + xa_erase(&sess->tree_conns, tree_conn->id); ksmbd_share_config_put(tree_conn->share_conf); ksmbd_free(tree_conn); return ret; @@ -89,15 +96,7 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, unsigned int id) { - struct ksmbd_tree_connect *tree_conn; - struct list_head *tmp; - - list_for_each(tmp, &sess->tree_conn_list) { - tree_conn = list_entry(tmp, struct ksmbd_tree_connect, list); - if (tree_conn->id == id) - return tree_conn; - } - return NULL; + return xa_load(&sess->tree_conns, id); } struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, @@ -114,15 +113,11 @@ struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) { int ret = 0; + struct ksmbd_tree_connect *tc; + unsigned long id; - while (!list_empty(&sess->tree_conn_list)) { - struct ksmbd_tree_connect *tc; - - tc = list_entry(sess->tree_conn_list.next, - struct ksmbd_tree_connect, - list); + xa_for_each(&sess->tree_conns, id, tc) ret |= ksmbd_tree_conn_disconnect(sess, tc); - } - + xa_destroy(&sess->tree_conns); return ret; } diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index 5a2113bf18ef..f5cc7a62d848 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "ksmbd_ida.h" #include "user_session.h" @@ -275,7 +277,7 @@ static struct ksmbd_session *__session_create(int protocol) set_session_flag(sess, protocol); INIT_LIST_HEAD(&sess->sessions_entry); - INIT_LIST_HEAD(&sess->tree_conn_list); + xa_init(&sess->tree_conns); INIT_LIST_HEAD(&sess->ksmbd_chann_list); INIT_LIST_HEAD(&sess->rpc_handle_list); sess->sequence_number = 1; diff --git a/fs/cifsd/mgmt/user_session.h b/fs/cifsd/mgmt/user_session.h index 68018f0f5c0b..1a97c851f2fc 100644 --- a/fs/cifsd/mgmt/user_session.h +++ b/fs/cifsd/mgmt/user_session.h @@ -7,6 +7,8 @@ #define __USER_SESSION_MANAGEMENT_H__ #include +#include +#include #include "../smb_common.h" #include "../ntlmssp.h" @@ -50,10 +52,12 @@ struct ksmbd_session { struct hlist_node hlist; struct list_head ksmbd_chann_list; - struct list_head tree_conn_list; + struct xarray tree_conns; struct ksmbd_ida *tree_conn_ida; struct list_head rpc_handle_list; + + __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 139041768f65..0b7199444f73 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -104,7 +104,7 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work) return 0; } - if (list_empty(&work->sess->tree_conn_list)) { + if (xa_empty(&work->sess->tree_conns)) { ksmbd_debug(SMB, "NO tree connected\n"); return -1; } From 5da64d8784d36c0601743a5159a598f5888089c7 Mon Sep 17 00:00:00 2001 From: Gibeom Kim Date: Thu, 1 Apr 2021 17:52:46 +0900 Subject: [PATCH 042/417] cifsd: remove stale prototype and variables Remove unused function prototype and variables. Signed-off-by: Gibeom Kim Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/glob.h | 1 - fs/cifsd/oplock.h | 3 --- fs/cifsd/server.h | 2 -- fs/cifsd/smbacl.h | 1 - fs/cifsd/unicode.h | 2 -- fs/cifsd/vfs.h | 3 --- 6 files changed, 12 deletions(-) diff --git a/fs/cifsd/glob.h b/fs/cifsd/glob.h index 27500afbeaf5..d0bc6edd0477 100644 --- a/fs/cifsd/glob.h +++ b/fs/cifsd/glob.h @@ -19,7 +19,6 @@ /* @FIXME clean up this code */ extern int ksmbd_debug_types; -extern int ksmbd_caseless_search; #define DATA_STREAM 1 #define DIR_STREAM 2 diff --git a/fs/cifsd/oplock.h b/fs/cifsd/oplock.h index 5b6615f99c76..f8b4b486eb93 100644 --- a/fs/cifsd/oplock.h +++ b/fs/cifsd/oplock.h @@ -123,9 +123,6 @@ void create_mxac_rsp_buf(char *cc, int maximal_access); void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); struct create_context *smb2_find_context_vals(void *open_req, const char *str); -int ksmbd_durable_verify_and_del_oplock(struct ksmbd_session *curr_sess, - struct ksmbd_session *prev_sess, int fid, struct file **filp, - u64 sess_id); struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, char *lease_key); int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, diff --git a/fs/cifsd/server.h b/fs/cifsd/server.h index 7b2f6318fcff..b682d28963e8 100644 --- a/fs/cifsd/server.h +++ b/fs/cifsd/server.h @@ -17,8 +17,6 @@ #define SERVER_CONF_SERVER_STRING 1 #define SERVER_CONF_WORK_GROUP 2 -extern int ksmbd_debugging; - struct ksmbd_server_config { unsigned int flags; unsigned int state; diff --git a/fs/cifsd/smbacl.h b/fs/cifsd/smbacl.h index 9b22bff4191f..032b6a3ec6f4 100644 --- a/fs/cifsd/smbacl.h +++ b/fs/cifsd/smbacl.h @@ -193,7 +193,6 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, unsigned int uid, unsigned int gid); int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, __le32 *pdaccess, int uid); -int store_init_posix_acl(struct inode *inode, umode_t perm); int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len, bool type_check); diff --git a/fs/cifsd/unicode.h b/fs/cifsd/unicode.h index c37d7024cf60..68f1c8290911 100644 --- a/fs/cifsd/unicode.h +++ b/fs/cifsd/unicode.h @@ -71,8 +71,6 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen, char *ksmbd_extract_sharename(char *treename); #endif -wchar_t cifs_toupper(wchar_t in); - /* * UniStrcat: Concatenate the second string to the first * diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index e1ca9ac11ba5..b41b23d40636 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -205,8 +205,6 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, const char *newname); int ksmbd_vfs_getattr(struct path *path, struct kstat *stat); -int ksmbd_vfs_symlink(const char *name, const char *symname); -int ksmbd_vfs_readlink(struct path *path, char *buf, int lenp); int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, char *newname); @@ -230,7 +228,6 @@ int ksmbd_vfs_setxattr(struct dentry *dentry, const char *attr_name, const void *attr_value, size_t attr_size, int flags); int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, size_t *xattr_stream_name_size, int s_type); -int ksmbd_vfs_truncate_xattr(struct dentry *dentry, int wo_streams); int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name); void ksmbd_vfs_xattr_free(char *xattr); int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, From c250e8f5566f2e1a0ea177837520eff8e59c0b7d Mon Sep 17 00:00:00 2001 From: Muhammad Usama Anjum Date: Thu, 1 Apr 2021 17:54:43 +0900 Subject: [PATCH 043/417] cifsd: fix memory leak when loop ends Memory is being allocated and if veto_list is zero, the loop breaks without cleaning up the allocated memory. In this patch, the length check has been moved before allocation. If loop breaks, the memory isn't allocated in the first place. Thus the memory is being protected from leaking. Reported-by: coverity-bot Addresses-Coverity-ID: 1503590 ("Resource leaks") Signed-off-by: Muhammad Usama Anjum Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/mgmt/share_config.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/cifsd/mgmt/share_config.c b/fs/cifsd/mgmt/share_config.c index db780febd692..b2bd789af945 100644 --- a/fs/cifsd/mgmt/share_config.c +++ b/fs/cifsd/mgmt/share_config.c @@ -92,14 +92,14 @@ static int parse_veto_list(struct ksmbd_share_config *share, while (veto_list_sz > 0) { struct ksmbd_veto_pattern *p; - p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); - if (!p) - return -ENOMEM; - sz = strlen(veto_list); if (!sz) break; + p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); + if (!p) + return -ENOMEM; + p->pattern = kstrdup(veto_list, GFP_KERNEL); if (!p->pattern) { ksmbd_free(p); From 822bc8ea514ecd4a8bbb86237858146ca8845eba Mon Sep 17 00:00:00 2001 From: Muhammad Usama Anjum Date: Fri, 2 Apr 2021 09:25:35 +0900 Subject: [PATCH 044/417] cifsd: use kfree to free memory allocated by kmalloc or kzalloc kfree should be used to free memory allocated by kmalloc or kzalloc to avoid any overhead and for maintaining consistency. Signed-off-by: Muhammad Usama Anjum Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/buffer_pool.c | 4 ++-- fs/cifsd/mgmt/share_config.c | 2 +- fs/cifsd/mgmt/user_config.c | 8 ++++---- fs/cifsd/mgmt/user_session.c | 6 +++--- fs/cifsd/smb2pdu.c | 4 ++-- fs/cifsd/transport_tcp.c | 2 +- fs/cifsd/vfs_cache.c | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/fs/cifsd/buffer_pool.c b/fs/cifsd/buffer_pool.c index ad2a2c885a2c..a9ef3e703232 100644 --- a/fs/cifsd/buffer_pool.c +++ b/fs/cifsd/buffer_pool.c @@ -78,7 +78,7 @@ static int register_wm_size_class(size_t sz) list_for_each_entry(l, &wm_lists, list) { if (l->sz == sz) { write_unlock(&wm_lists_lock); - kvfree(nl); + kfree(nl); return 0; } } @@ -181,7 +181,7 @@ static void wm_list_free(struct wm_list *l) list_del(&wm->list); kvfree(wm); } - kvfree(l); + kfree(l); } static void wm_lists_destroy(void) diff --git a/fs/cifsd/mgmt/share_config.c b/fs/cifsd/mgmt/share_config.c index b2bd789af945..11abdbc8a533 100644 --- a/fs/cifsd/mgmt/share_config.c +++ b/fs/cifsd/mgmt/share_config.c @@ -102,7 +102,7 @@ static int parse_veto_list(struct ksmbd_share_config *share, p->pattern = kstrdup(veto_list, GFP_KERNEL); if (!p->pattern) { - ksmbd_free(p); + kfree(p); return -ENOMEM; } diff --git a/fs/cifsd/mgmt/user_config.c b/fs/cifsd/mgmt/user_config.c index f0c2f8994a6b..c31e2c4d2d6f 100644 --- a/fs/cifsd/mgmt/user_config.c +++ b/fs/cifsd/mgmt/user_config.c @@ -46,8 +46,8 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) if (!user->name || !user->passkey) { kfree(user->name); - ksmbd_free(user->passkey); - ksmbd_free(user); + kfree(user->passkey); + kfree(user); user = NULL; } return user; @@ -57,8 +57,8 @@ void ksmbd_free_user(struct ksmbd_user *user) { ksmbd_ipc_logout_request(user->name); kfree(user->name); - ksmbd_free(user->passkey); - ksmbd_free(user); + kfree(user->passkey); + kfree(user); } int ksmbd_anonymous_user(struct ksmbd_user *user) diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index f5cc7a62d848..9dfe222e51ab 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -55,7 +55,7 @@ static void __session_rpc_close(struct ksmbd_session *sess, ksmbd_free(resp); ksmbd_rpc_id_free(entry->id); - ksmbd_free(entry); + kfree(entry); } static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) @@ -121,7 +121,7 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) return entry->id; error: list_del(&entry->list); - ksmbd_free(entry); + kfree(entry); return -EINVAL; } @@ -176,7 +176,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) ksmbd_release_id(session_ida, sess->id); ksmbd_ida_free(sess->tree_conn_ida); - ksmbd_free(sess); + kfree(sess); } static struct ksmbd_session *__session_lookup(unsigned long long id) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 0b7199444f73..7549b35bb792 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -1611,7 +1611,7 @@ int smb2_sess_setup(struct ksmbd_work *work) ksmbd_conn_set_good(work); sess->state = SMB2_SESSION_VALID; - ksmbd_free(sess->Preauth_HashValue); + kfree(sess->Preauth_HashValue); sess->Preauth_HashValue = NULL; } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { rc = generate_preauth_hash(work); @@ -1637,7 +1637,7 @@ int smb2_sess_setup(struct ksmbd_work *work) ksmbd_conn_set_good(work); sess->state = SMB2_SESSION_VALID; - ksmbd_free(sess->Preauth_HashValue); + kfree(sess->Preauth_HashValue); sess->Preauth_HashValue = NULL; } } else { diff --git a/fs/cifsd/transport_tcp.c b/fs/cifsd/transport_tcp.c index 67163efcf472..040881893417 100644 --- a/fs/cifsd/transport_tcp.c +++ b/fs/cifsd/transport_tcp.c @@ -551,7 +551,7 @@ void ksmbd_tcp_destroy(void) list_for_each_entry_safe(iface, tmp, &iface_list, entry) { list_del(&iface->entry); kfree(iface->name); - ksmbd_free(iface); + kfree(iface); } } diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index ec631dc6f1fb..f2a863542dc7 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -829,6 +829,6 @@ void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) __close_file_table_ids(ft, NULL, session_fd_check); idr_destroy(ft->idr); - ksmbd_free(ft->idr); + kfree(ft->idr); ft->idr = NULL; } From 86f52978465b8f4e384880a5fd0543e9e455fb62 Mon Sep 17 00:00:00 2001 From: kernel test robot Date: Fri, 2 Apr 2021 12:17:24 +0900 Subject: [PATCH 045/417] cifsd: fix memdup.cocci warnings fs/cifsd/smb2pdu.c:1177:27-34: WARNING opportunity for kmemdup Use kmemdup rather than duplicating its implementation Generated by: scripts/coccinelle/api/memdup.cocci Reported-by: kernel test robot Signed-off-by: kernel test robot Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 7549b35bb792..e6cdc3b89d85 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -1161,13 +1161,11 @@ static int alloc_preauth_hash(struct ksmbd_session *sess, if (sess->Preauth_HashValue) return 0; - sess->Preauth_HashValue = kmalloc(PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); + sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, + PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); if (!sess->Preauth_HashValue) return -ENOMEM; - memcpy(sess->Preauth_HashValue, - conn->preauth_info->Preauth_HashValue, - PREAUTH_HASHVALUE_SIZE); return 0; } From 79f6b11a104f3a32f4f4a6f7808a02c301c19710 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 2 Apr 2021 12:47:14 +0900 Subject: [PATCH 046/417] cifsd: remove wrappers of kvmalloc/kvfree Do directly call kvmalloc/kvfree(). Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 2 +- fs/cifsd/buffer_pool.c | 36 +++------------------------------- fs/cifsd/buffer_pool.h | 8 -------- fs/cifsd/connection.c | 6 +++--- fs/cifsd/crypto_ctx.c | 4 ++-- fs/cifsd/ksmbd_work.c | 8 ++++---- fs/cifsd/mgmt/share_config.c | 3 ++- fs/cifsd/mgmt/tree_connect.c | 10 +++++----- fs/cifsd/mgmt/user_config.c | 3 ++- fs/cifsd/mgmt/user_session.c | 4 ++-- fs/cifsd/smb2pdu.c | 38 ++++++++++++++++++------------------ fs/cifsd/transport_ipc.c | 6 +++--- fs/cifsd/vfs.c | 23 +++++++++------------- fs/cifsd/vfs.h | 1 - 14 files changed, 55 insertions(+), 97 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index b9fd62f77e1c..437e58a0826d 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -709,7 +709,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, *out_len = resp->spnego_blob_len; retval = 0; out: - ksmbd_free(resp); + kvfree(resp); return retval; } #else diff --git a/fs/cifsd/buffer_pool.c b/fs/cifsd/buffer_pool.c index a9ef3e703232..caf22c190634 100644 --- a/fs/cifsd/buffer_pool.c +++ b/fs/cifsd/buffer_pool.c @@ -37,16 +37,6 @@ struct wm_list { static LIST_HEAD(wm_lists); static DEFINE_RWLOCK(wm_lists_lock); -void *ksmbd_alloc(size_t size) -{ - return kvmalloc(size, GFP_KERNEL | __GFP_ZERO); -} - -void ksmbd_free(void *ptr) -{ - kvfree(ptr); -} - static struct wm *wm_alloc(size_t sz, gfp_t flags) { struct wm *wm; @@ -169,7 +159,7 @@ static void release_wm(struct wm *wm, struct wm_list *wm_list) wm_list->avail_wm--; spin_unlock(&wm_list->wm_lock); - ksmbd_free(wm); + kvfree(wm); } static void wm_list_free(struct wm_list *l) @@ -195,26 +185,6 @@ static void wm_lists_destroy(void) } } -void ksmbd_free_request(void *addr) -{ - kvfree(addr); -} - -void *ksmbd_alloc_request(size_t size) -{ - return kvmalloc(size, GFP_KERNEL); -} - -void ksmbd_free_response(void *buffer) -{ - kvfree(buffer); -} - -void *ksmbd_alloc_response(size_t size) -{ - return kvmalloc(size, GFP_KERNEL | __GFP_ZERO); -} - void *ksmbd_find_buffer(size_t size) { struct wm *wm; @@ -247,11 +217,11 @@ void *ksmbd_realloc_response(void *ptr, size_t old_sz, size_t new_sz) size_t sz = min(old_sz, new_sz); void *nptr; - nptr = ksmbd_alloc_response(new_sz); + nptr = kvmalloc(new_sz, GFP_KERNEL | __GFP_ZERO); if (!nptr) return ptr; memcpy(nptr, ptr, sz); - ksmbd_free_response(ptr); + kvfree(ptr); return nptr; } diff --git a/fs/cifsd/buffer_pool.h b/fs/cifsd/buffer_pool.h index 2b3d03afcf27..f7157144a92f 100644 --- a/fs/cifsd/buffer_pool.h +++ b/fs/cifsd/buffer_pool.h @@ -9,14 +9,6 @@ void *ksmbd_find_buffer(size_t size); void ksmbd_release_buffer(void *buffer); -void *ksmbd_alloc(size_t size); -void ksmbd_free(void *ptr); - -void ksmbd_free_request(void *addr); -void *ksmbd_alloc_request(size_t size); -void ksmbd_free_response(void *buffer); -void *ksmbd_alloc_response(size_t size); - void *ksmbd_realloc_response(void *ptr, size_t old_sz, size_t new_sz); void ksmbd_free_file_struct(void *filp); diff --git a/fs/cifsd/connection.c b/fs/cifsd/connection.c index df56e347b709..e1814492fb58 100644 --- a/fs/cifsd/connection.c +++ b/fs/cifsd/connection.c @@ -37,7 +37,7 @@ void ksmbd_conn_free(struct ksmbd_conn *conn) list_del(&conn->conns_list); write_unlock(&conn_list_lock); - ksmbd_free_request(conn->request_buf); + kvfree(conn->request_buf); ksmbd_ida_free(conn->async_ida); kfree(conn->preauth_info); kfree(conn); @@ -284,7 +284,7 @@ int ksmbd_conn_handler_loop(void *p) if (try_to_freeze()) continue; - ksmbd_free_request(conn->request_buf); + kvfree(conn->request_buf); conn->request_buf = NULL; size = t->ops->read(t, hdr_buf, sizeof(hdr_buf)); @@ -303,7 +303,7 @@ int ksmbd_conn_handler_loop(void *p) /* 4 for rfc1002 length field */ size = pdu_size + 4; - conn->request_buf = ksmbd_alloc_request(size); + conn->request_buf = kvmalloc(size, GFP_KERNEL); if (!conn->request_buf) continue; diff --git a/fs/cifsd/crypto_ctx.c b/fs/cifsd/crypto_ctx.c index 15d7e2f7c3d7..2c31e8b32de7 100644 --- a/fs/cifsd/crypto_ctx.c +++ b/fs/cifsd/crypto_ctx.c @@ -105,7 +105,7 @@ static struct shash_desc *alloc_shash_desc(int id) static struct ksmbd_crypto_ctx *ctx_alloc(void) { - return ksmbd_alloc(sizeof(struct ksmbd_crypto_ctx)); + return kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); } static void ctx_free(struct ksmbd_crypto_ctx *ctx) @@ -116,7 +116,7 @@ static void ctx_free(struct ksmbd_crypto_ctx *ctx) free_shash(ctx->desc[i]); for (i = 0; i < CRYPTO_AEAD_MAX; i++) free_aead(ctx->ccmaes[i]); - ksmbd_free(ctx); + kfree(ctx); } static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) diff --git a/fs/cifsd/ksmbd_work.c b/fs/cifsd/ksmbd_work.c index 505e59df3071..33ee52c1829f 100644 --- a/fs/cifsd/ksmbd_work.c +++ b/fs/cifsd/ksmbd_work.c @@ -42,16 +42,16 @@ void ksmbd_free_work_struct(struct ksmbd_work *work) work->set_trans_buf) ksmbd_release_buffer(work->response_buf); else - ksmbd_free_response(work->response_buf); + kvfree(work->response_buf); if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF && work->set_read_buf) ksmbd_release_buffer(work->aux_payload_buf); else - ksmbd_free_response(work->aux_payload_buf); + kvfree(work->aux_payload_buf); - ksmbd_free_response(work->tr_buf); - ksmbd_free_request(work->request_buf); + kfree(work->tr_buf); + kvfree(work->request_buf); if (work->async_id) ksmbd_release_id(work->conn->async_ida, work->async_id); kmem_cache_free(work_cache, work); diff --git a/fs/cifsd/mgmt/share_config.c b/fs/cifsd/mgmt/share_config.c index 11abdbc8a533..910d03516b73 100644 --- a/fs/cifsd/mgmt/share_config.c +++ b/fs/cifsd/mgmt/share_config.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "share_config.h" #include "user_config.h" @@ -182,7 +183,7 @@ static struct ksmbd_share_config *share_config_request(char *name) up_write(&shares_table_lock); out: - ksmbd_free(resp); + kvfree(resp); return share; } diff --git a/fs/cifsd/mgmt/tree_connect.c b/fs/cifsd/mgmt/tree_connect.c index 0c8374e8240f..d3f28b10db4b 100644 --- a/fs/cifsd/mgmt/tree_connect.c +++ b/fs/cifsd/mgmt/tree_connect.c @@ -31,7 +31,7 @@ ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name) if (!sc) return status; - tree_conn = ksmbd_alloc(sizeof(struct ksmbd_tree_connect)); + tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL); if (!tree_conn) { status.ret = -ENOMEM; goto out_error; @@ -68,15 +68,15 @@ ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name) status.ret = -ENOMEM; goto out_error; } - ksmbd_free(resp); + kvfree(resp); return status; out_error: if (tree_conn) ksmbd_release_tree_conn_id(sess, tree_conn->id); ksmbd_share_config_put(sc); - ksmbd_free(tree_conn); - ksmbd_free(resp); + kfree(tree_conn); + kvfree(resp); return status; } @@ -89,7 +89,7 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, ksmbd_release_tree_conn_id(sess, tree_conn->id); xa_erase(&sess->tree_conns, tree_conn->id); ksmbd_share_config_put(tree_conn->share_conf); - ksmbd_free(tree_conn); + kfree(tree_conn); return ret; } diff --git a/fs/cifsd/mgmt/user_config.c b/fs/cifsd/mgmt/user_config.c index c31e2c4d2d6f..7f898c5bda25 100644 --- a/fs/cifsd/mgmt/user_config.c +++ b/fs/cifsd/mgmt/user_config.c @@ -4,6 +4,7 @@ */ #include +#include #include "user_config.h" #include "../buffer_pool.h" @@ -23,7 +24,7 @@ struct ksmbd_user *ksmbd_login_user(const char *account) user = ksmbd_alloc_user(resp); out: - ksmbd_free(resp); + kvfree(resp); return user; } diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index 9dfe222e51ab..bd5789b7e08e 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -53,7 +53,7 @@ static void __session_rpc_close(struct ksmbd_session *sess, if (!resp) pr_err("Unable to close RPC pipe %d\n", entry->id); - ksmbd_free(resp); + kvfree(resp); ksmbd_rpc_id_free(entry->id); kfree(entry); } @@ -117,7 +117,7 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) if (!resp) goto error; - ksmbd_free(resp); + kvfree(resp); return entry->id; error: list_del(&entry->list); diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index e6cdc3b89d85..c1f6361603b9 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -563,7 +563,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) work->set_trans_buf) work->response_buf = ksmbd_find_buffer(sz); else - work->response_buf = ksmbd_alloc_response(sz); + work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); if (!work->response_buf) { ksmbd_err("Failed to allocate %zu bytes buffer\n", sz); @@ -2283,7 +2283,7 @@ static int smb2_remove_smb_xattrs(struct dentry *dentry) ksmbd_debug(SMB, "remove xattr failed : %s\n", name); } out: - ksmbd_vfs_xattr_free(xattr_list); + kvfree(xattr_list); return err; } @@ -4190,12 +4190,12 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, buf_free_len -= value_len; if (buf_free_len < 0) { - ksmbd_free(buf); + kfree(buf); break; } memcpy(ptr, buf, value_len); - ksmbd_free(buf); + kfree(buf); ptr += value_len; eainfo->Flags = 0; @@ -4240,7 +4240,7 @@ done: rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); inc_rfc1001_len(rsp_org, rsp_data_cnt); out: - ksmbd_vfs_xattr_free(xattr_list); + kvfree(xattr_list); return rc; } @@ -4510,7 +4510,7 @@ static void get_file_stream_info(struct ksmbd_work *work, /* last entry offset should be 0 */ file_info->NextEntryOffset = 0; out: - ksmbd_vfs_xattr_free(xattr_list); + kvfree(xattr_list); rsp->OutputBufferLength = cpu_to_le32(nbytes); inc_rfc1001_len(rsp_org, nbytes); @@ -5976,7 +5976,7 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) } work->aux_payload_buf = - ksmbd_alloc_response(rpc_resp->payload_sz); + kvmalloc(rpc_resp->payload_sz, GFP_KERNEL | __GFP_ZERO); if (!work->aux_payload_buf) { err = -ENOMEM; goto out; @@ -5988,7 +5988,7 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) nbytes = rpc_resp->payload_sz; work->resp_hdr_sz = get_rfc1002_len(rsp) + 4; work->aux_payload_sz = nbytes; - ksmbd_free(rpc_resp); + kvfree(rpc_resp); } rsp->StructureSize = cpu_to_le16(17); @@ -6003,7 +6003,7 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) out: rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; smb2_set_err_rsp(work); - ksmbd_free(rpc_resp); + kvfree(rpc_resp); return err; } @@ -6094,7 +6094,7 @@ int smb2_read(struct ksmbd_work *work) ksmbd_find_buffer(conn->vals->max_read_size); work->set_read_buf = true; } else { - work->aux_payload_buf = ksmbd_alloc_response(length); + work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); } if (!work->aux_payload_buf) { err = -ENOMEM; @@ -6111,7 +6111,7 @@ int smb2_read(struct ksmbd_work *work) if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) ksmbd_release_buffer(work->aux_payload_buf); else - ksmbd_free_response(work->aux_payload_buf); + kvfree(work->aux_payload_buf); work->aux_payload_buf = NULL; rsp->hdr.Status = STATUS_END_OF_FILE; smb2_set_err_rsp(work); @@ -6130,7 +6130,7 @@ int smb2_read(struct ksmbd_work *work) if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) ksmbd_release_buffer(work->aux_payload_buf); else - ksmbd_free_response(work->aux_payload_buf); + kvfree(work->aux_payload_buf); work->aux_payload_buf = NULL; nbytes = 0; @@ -6215,17 +6215,17 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) if (rpc_resp) { if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { rsp->hdr.Status = STATUS_NOT_SUPPORTED; - ksmbd_free(rpc_resp); + kvfree(rpc_resp); smb2_set_err_rsp(work); return -EOPNOTSUPP; } if (rpc_resp->flags != KSMBD_RPC_OK) { rsp->hdr.Status = STATUS_INVALID_HANDLE; smb2_set_err_rsp(work); - ksmbd_free(rpc_resp); + kvfree(rpc_resp); return ret; } - ksmbd_free(rpc_resp); + kvfree(rpc_resp); } rsp->StructureSize = cpu_to_le16(17); @@ -6271,7 +6271,7 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); work->remote_key = le32_to_cpu(desc->token); - data_buf = ksmbd_alloc_response(length); + data_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); if (!data_buf) return -ENOMEM; @@ -6280,12 +6280,12 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, le64_to_cpu(desc->offset), le32_to_cpu(desc->length)); if (ret < 0) { - ksmbd_free_response(data_buf); + kvfree(data_buf); return ret; } ret = ksmbd_vfs_write(work, fp, data_buf, length, &offset, sync, &nbytes); - ksmbd_free_response(data_buf); + kvfree(data_buf); if (ret < 0) return ret; @@ -7307,7 +7307,7 @@ static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, memcpy((char *)rsp->Buffer, rpc_resp->payload, nbytes); } out: - ksmbd_free(rpc_resp); + kvfree(rpc_resp); return nbytes; } diff --git a/fs/cifsd/transport_ipc.c b/fs/cifsd/transport_ipc.c index 1bbff53436b3..60c0289402c1 100644 --- a/fs/cifsd/transport_ipc.c +++ b/fs/cifsd/transport_ipc.c @@ -233,7 +233,7 @@ static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) struct ksmbd_ipc_msg *msg; size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); - msg = ksmbd_alloc(msg_sz); + msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO); if (msg) msg->sz = sz; return msg; @@ -241,7 +241,7 @@ static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) static void ipc_msg_free(struct ksmbd_ipc_msg *msg) { - ksmbd_free(msg); + kvfree(msg); } static void ipc_msg_handle_free(int handle) @@ -272,7 +272,7 @@ static int handle_response(int type, void *payload, size_t sz) entry->type + 1, type); } - entry->response = ksmbd_alloc(sz); + entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); if (!entry->response) { ret = -ENOMEM; break; diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index ef823679f6be..d3882208a259 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -244,7 +244,7 @@ static ssize_t ksmbd_vfs_getcasexattr(struct dentry *dentry, char *attr_name, } out: - ksmbd_vfs_xattr_free(xattr_list); + kvfree(xattr_list); return value_len; } @@ -401,7 +401,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, } if (v_len < size) { - wbuf = ksmbd_alloc(size); + wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); if (!wbuf) { err = -ENOMEM; goto out; @@ -425,7 +425,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, fp->filp->f_pos = *pos; err = 0; out: - ksmbd_free(stream_buf); + kvfree(stream_buf); return err; } @@ -844,7 +844,7 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) if (size <= 0) return size; - vlist = ksmbd_alloc(size); + vlist = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); if (!vlist) return -ENOMEM; @@ -852,7 +852,7 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) size = vfs_listxattr(dentry, vlist, size); if (size < 0) { ksmbd_debug(VFS, "listxattr failed\n"); - ksmbd_vfs_xattr_free(vlist); + kvfree(vlist); *list = NULL; } @@ -1049,11 +1049,6 @@ int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name) return vfs_removexattr(&init_user_ns, dentry, attr_name); } -void ksmbd_vfs_xattr_free(char *xattr) -{ - ksmbd_free(xattr); -} - int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry) { int err = 0; @@ -1297,7 +1292,7 @@ int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry) } } out: - ksmbd_vfs_xattr_free(xattr_list); + kvfree(xattr_list); return err; } @@ -1326,7 +1321,7 @@ int ksmbd_vfs_remove_sd_xattrs(struct dentry *dentry) } } out: - ksmbd_vfs_xattr_free(xattr_list); + kvfree(xattr_list); return err; } @@ -1558,7 +1553,7 @@ int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, n.length = err; if (ndr_decode_dos_attr(&n, da)) err = -EINVAL; - ksmbd_free(n.data); + kfree(n.data); } else { ksmbd_debug(SMB, "failed to load dos attribute in xattr\n"); } @@ -1683,7 +1678,7 @@ ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, char *attr_name, } out: - ksmbd_vfs_xattr_free(xattr_list); + kvfree(xattr_list); return value_len; } diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index b41b23d40636..0163be4297de 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -229,7 +229,6 @@ int ksmbd_vfs_setxattr(struct dentry *dentry, const char *attr_name, int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, size_t *xattr_stream_name_size, int s_type); int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name); -void ksmbd_vfs_xattr_free(char *xattr); int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, bool caseless); int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); From 9cca7516f4c6373223d6059f1a69548fed74c5ed Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 2 Apr 2021 13:17:04 +0900 Subject: [PATCH 047/417] doc: cifsd: change the reference to configuration.txt added documentation for cifsd. There, it points to a file named: Documentation/configuration.txt This confuses Kernel scripts, as they think that this is a document within the Kernel tree, instead of a file from some other place. Replace it by an hyperlink to the ksmbd-tools tree, in order to avoid false-positives. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- Documentation/filesystems/cifs/cifsd.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/filesystems/cifs/cifsd.rst b/Documentation/filesystems/cifs/cifsd.rst index 48ae58f2a53c..cb9f87b8529f 100644 --- a/Documentation/filesystems/cifs/cifsd.rst +++ b/Documentation/filesystems/cifs/cifsd.rst @@ -114,8 +114,8 @@ How to run # ksmbd.adduser -a 3. Create /etc/ksmbd/smb.conf file, add SMB share in smb.conf file - - Refer smb.conf.example and Documentation/configuration.txt - in ksmbd-tools + - Refer smb.conf.example and + https://github.com/cifsd-team/ksmbd-tools/blob/master/Documentation/configuration.txt 4. Insert ksmbd.ko module From 4030b278368d89bba99a31e87766968cbf7909d2 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 4 Apr 2021 17:52:58 +0900 Subject: [PATCH 048/417] cifsd: prevent a integer overflow in wm_alloc() Dan Carpenter pointed out that there there is a possibility of integer overflow. This patch prevent a integer overflow in wm_alloc(). Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/buffer_pool.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/cifsd/buffer_pool.c b/fs/cifsd/buffer_pool.c index caf22c190634..1ee1feef1bb4 100644 --- a/fs/cifsd/buffer_pool.c +++ b/fs/cifsd/buffer_pool.c @@ -42,6 +42,9 @@ static struct wm *wm_alloc(size_t sz, gfp_t flags) struct wm *wm; size_t alloc_sz = sz + sizeof(struct wm); + if (sz > SIZE_MAX - sizeof(struct wm)) + return NULL; + wm = kvmalloc(alloc_sz, flags); if (!wm) return NULL; From 0ab777453f80341f0eb2aa1b569523636708f5d6 Mon Sep 17 00:00:00 2001 From: Zhang Xiaoxu Date: Wed, 7 Apr 2021 13:08:07 +0900 Subject: [PATCH 049/417] cifsd: Select SG_POOL for SMB_SERVER_SMBDIRECT hulk-robot following build error: fs/cifsd/transport_rdma.c: In function 'read_write_done': fs/cifsd/transport_rdma.c:1297:2: error: implicit declaration of function 'sg_free_table_chained' [-Werror=implicit-function-declaration] 1297 | sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); The reason is CONFIG_SG_POOL is not enabled in the config, to avoid such failure, select SG_POOL in Kconfig for SMB_SERVER_SMBDIRECT. Signed-off-by: Zhang Xiaoxu Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifsd/Kconfig b/fs/cifsd/Kconfig index d1ac53c83125..b94cf1158182 100644 --- a/fs/cifsd/Kconfig +++ b/fs/cifsd/Kconfig @@ -43,6 +43,7 @@ config SMB_SERVER config SMB_SERVER_SMBDIRECT bool "Support for SMB Direct protocol" depends on SMB_SERVER=m && INFINIBAND && INFINIBAND_ADDR_TRANS || SMB_SERVER=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y + select SG_POOL default n help From 1920bb1f8022202530eeae3e488d6f5156799faf Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Thu, 8 Apr 2021 17:05:21 +0900 Subject: [PATCH 050/417] cifsd: remove unused including Remove including that don't need it. Signed-off-by: Tian Tao Signed-off-by: Zhiqi Song Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/crypto_ctx.c | 1 - fs/cifsd/glob.h | 1 - fs/cifsd/mgmt/tree_connect.c | 1 - fs/cifsd/mgmt/user_session.c | 1 - fs/cifsd/mgmt/user_session.h | 1 - fs/cifsd/misc.c | 1 - fs/cifsd/vfs.c | 1 - fs/cifsd/vfs_cache.h | 1 - 8 files changed, 8 deletions(-) diff --git a/fs/cifsd/crypto_ctx.c b/fs/cifsd/crypto_ctx.c index 2c31e8b32de7..8322b0f7a7fc 100644 --- a/fs/cifsd/crypto_ctx.c +++ b/fs/cifsd/crypto_ctx.c @@ -9,7 +9,6 @@ #include #include #include -#include #include "glob.h" #include "crypto_ctx.h" diff --git a/fs/cifsd/glob.h b/fs/cifsd/glob.h index d0bc6edd0477..9d70093a837a 100644 --- a/fs/cifsd/glob.h +++ b/fs/cifsd/glob.h @@ -8,7 +8,6 @@ #define __KSMBD_GLOB_H #include -#include #include "unicode.h" #include "vfs_cache.h" diff --git a/fs/cifsd/mgmt/tree_connect.c b/fs/cifsd/mgmt/tree_connect.c index d3f28b10db4b..b9cd8fc46e5e 100644 --- a/fs/cifsd/mgmt/tree_connect.c +++ b/fs/cifsd/mgmt/tree_connect.c @@ -5,7 +5,6 @@ #include #include -#include #include #include "../buffer_pool.h" diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index bd5789b7e08e..52c5c036ecf9 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include "ksmbd_ida.h" diff --git a/fs/cifsd/mgmt/user_session.h b/fs/cifsd/mgmt/user_session.h index 1a97c851f2fc..ad5c0430b62a 100644 --- a/fs/cifsd/mgmt/user_session.h +++ b/fs/cifsd/mgmt/user_session.h @@ -7,7 +7,6 @@ #define __USER_SESSION_MANAGEMENT_H__ #include -#include #include #include "../smb_common.h" diff --git a/fs/cifsd/misc.c b/fs/cifsd/misc.c index b6f3f0818217..cbaaecf2eca1 100644 --- a/fs/cifsd/misc.c +++ b/fs/cifsd/misc.c @@ -5,7 +5,6 @@ */ #include -#include #include #include diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index d3882208a259..5985d2d1f276 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/cifsd/vfs_cache.h b/fs/cifsd/vfs_cache.h index 318dcb1a297a..8226fdf882e4 100644 --- a/fs/cifsd/vfs_cache.h +++ b/fs/cifsd/vfs_cache.h @@ -6,7 +6,6 @@ #ifndef __VFS_CACHE_H__ #define __VFS_CACHE_H__ -#include #include #include #include From d40012a83f87f47967ad0b3c346179c7e5339ae7 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 13 Apr 2021 13:06:30 +0900 Subject: [PATCH 051/417] cifsd: declare ida statically Matthew pointed out that embedding struct ida into the struct is better than having a pointer to it. This patch initialise it statically using DEFINE_IDA() or ida_init() and remove ksmbd_ida_alloc/free(). Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/connection.c | 3 +-- fs/cifsd/connection.h | 2 +- fs/cifsd/ksmbd_work.c | 2 +- fs/cifsd/mgmt/ksmbd_ida.c | 45 ++++++++++-------------------------- fs/cifsd/mgmt/ksmbd_ida.h | 17 ++++---------- fs/cifsd/mgmt/user_session.c | 29 +++++------------------ fs/cifsd/mgmt/user_session.h | 7 +----- fs/cifsd/server.c | 7 ------ fs/cifsd/smb2pdu.c | 4 ++-- fs/cifsd/transport_ipc.c | 34 +++++++++------------------ 10 files changed, 40 insertions(+), 110 deletions(-) diff --git a/fs/cifsd/connection.c b/fs/cifsd/connection.c index e1814492fb58..4785dd59fcc5 100644 --- a/fs/cifsd/connection.c +++ b/fs/cifsd/connection.c @@ -38,7 +38,6 @@ void ksmbd_conn_free(struct ksmbd_conn *conn) write_unlock(&conn_list_lock); kvfree(conn->request_buf); - ksmbd_ida_free(conn->async_ida); kfree(conn->preauth_info); kfree(conn); } @@ -70,7 +69,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) INIT_LIST_HEAD(&conn->async_requests); spin_lock_init(&conn->request_lock); spin_lock_init(&conn->credits_lock); - conn->async_ida = ksmbd_ida_alloc(); + ida_init(&conn->async_ida); write_lock(&conn_list_lock); list_add(&conn->conns_list, &conn_list); diff --git a/fs/cifsd/connection.h b/fs/cifsd/connection.h index 021dada3d76d..00ede7a67199 100644 --- a/fs/cifsd/connection.h +++ b/fs/cifsd/connection.h @@ -101,7 +101,7 @@ struct ksmbd_conn { struct sockaddr_storage peer_addr; /* Identifier for async message */ - struct ksmbd_ida *async_ida; + struct ida async_ida; __le16 cipher_type; __le16 compress_algorithm; diff --git a/fs/cifsd/ksmbd_work.c b/fs/cifsd/ksmbd_work.c index 33ee52c1829f..eb8c8a34acab 100644 --- a/fs/cifsd/ksmbd_work.c +++ b/fs/cifsd/ksmbd_work.c @@ -53,7 +53,7 @@ void ksmbd_free_work_struct(struct ksmbd_work *work) kfree(work->tr_buf); kvfree(work->request_buf); if (work->async_id) - ksmbd_release_id(work->conn->async_ida, work->async_id); + ksmbd_release_id(&work->conn->async_ida, work->async_id); kmem_cache_free(work_cache, work); } diff --git a/fs/cifsd/mgmt/ksmbd_ida.c b/fs/cifsd/mgmt/ksmbd_ida.c index cbc9fd049852..3dbc27cb5385 100644 --- a/fs/cifsd/mgmt/ksmbd_ida.c +++ b/fs/cifsd/mgmt/ksmbd_ida.c @@ -5,65 +5,44 @@ #include "ksmbd_ida.h" -struct ksmbd_ida *ksmbd_ida_alloc(void) +static inline int __acquire_id(struct ida *ida, int from, int to) { - struct ksmbd_ida *ida; - - ida = kmalloc(sizeof(struct ksmbd_ida), GFP_KERNEL); - if (!ida) - return NULL; - - ida_init(&ida->map); - return ida; + return ida_simple_get(ida, from, to, GFP_KERNEL); } -void ksmbd_ida_free(struct ksmbd_ida *ida) -{ - if (!ida) - return; - - ida_destroy(&ida->map); - kfree(ida); -} - -static inline int __acquire_id(struct ksmbd_ida *ida, int from, int to) -{ - return ida_simple_get(&ida->map, from, to, GFP_KERNEL); -} - -int ksmbd_acquire_smb2_tid(struct ksmbd_ida *ida) +int ksmbd_acquire_smb2_tid(struct ida *ida) { int id; - do { + id = __acquire_id(ida, 0, 0); + if (id == 0xFFFF) id = __acquire_id(ida, 0, 0); - } while (id == 0xFFFF); return id; } -int ksmbd_acquire_smb2_uid(struct ksmbd_ida *ida) +int ksmbd_acquire_smb2_uid(struct ida *ida) { int id; - do { + id = __acquire_id(ida, 1, 0); + if (id == 0xFFFE) id = __acquire_id(ida, 1, 0); - } while (id == 0xFFFE); return id; } -int ksmbd_acquire_async_msg_id(struct ksmbd_ida *ida) +int ksmbd_acquire_async_msg_id(struct ida *ida) { return __acquire_id(ida, 1, 0); } -int ksmbd_acquire_id(struct ksmbd_ida *ida) +int ksmbd_acquire_id(struct ida *ida) { return __acquire_id(ida, 0, 0); } -void ksmbd_release_id(struct ksmbd_ida *ida, int id) +void ksmbd_release_id(struct ida *ida, int id) { - ida_simple_remove(&ida->map, id); + ida_simple_remove(ida, id); } diff --git a/fs/cifsd/mgmt/ksmbd_ida.h b/fs/cifsd/mgmt/ksmbd_ida.h index b075156adf23..2bc07b16cfde 100644 --- a/fs/cifsd/mgmt/ksmbd_ida.h +++ b/fs/cifsd/mgmt/ksmbd_ida.h @@ -9,13 +9,6 @@ #include #include -struct ksmbd_ida { - struct ida map; -}; - -struct ksmbd_ida *ksmbd_ida_alloc(void); -void ksmbd_ida_free(struct ksmbd_ida *ida); - /* * 2.2.1.6.7 TID Generation * The value 0xFFFF MUST NOT be used as a valid TID. All other @@ -23,7 +16,7 @@ void ksmbd_ida_free(struct ksmbd_ida *ida); * The value 0xFFFF is used to specify all TIDs or no TID, * depending upon the context in which it is used. */ -int ksmbd_acquire_smb2_tid(struct ksmbd_ida *ida); +int ksmbd_acquire_smb2_tid(struct ida *ida); /* * 2.2.1.6.8 UID Generation @@ -32,10 +25,10 @@ int ksmbd_acquire_smb2_tid(struct ksmbd_ida *ida); * valid UID.<21> All other possible values for a UID, excluding * zero (0x0000), are valid. */ -int ksmbd_acquire_smb2_uid(struct ksmbd_ida *ida); -int ksmbd_acquire_async_msg_id(struct ksmbd_ida *ida); +int ksmbd_acquire_smb2_uid(struct ida *ida); +int ksmbd_acquire_async_msg_id(struct ida *ida); -int ksmbd_acquire_id(struct ksmbd_ida *ida); +int ksmbd_acquire_id(struct ida *ida); -void ksmbd_release_id(struct ksmbd_ida *ida, int id); +void ksmbd_release_id(struct ida *ida, int id); #endif /* __KSMBD_IDA_MANAGEMENT_H__ */ diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index 52c5c036ecf9..739588a6c96a 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -17,7 +17,7 @@ #include "../buffer_pool.h" #include "../vfs_cache.h" -static struct ksmbd_ida *session_ida; +static DEFINE_IDA(session_ida); #define SESSION_HASH_BITS 3 static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS); @@ -172,9 +172,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) ksmbd_session_rpc_clear_list(sess); free_channel_list(sess); kfree(sess->Preauth_HashValue); - ksmbd_release_id(session_ida, sess->id); - - ksmbd_ida_free(sess->tree_conn_ida); + ksmbd_release_id(&session_ida, sess->id); kfree(sess); } @@ -254,7 +252,7 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) static int __init_smb2_session(struct ksmbd_session *sess) { - int id = ksmbd_acquire_smb2_uid(session_ida); + int id = ksmbd_acquire_smb2_uid(&session_ida); if (id < 0) return -EINVAL; @@ -294,9 +292,7 @@ static struct ksmbd_session *__session_create(int protocol) if (ret) goto error; - sess->tree_conn_ida = ksmbd_ida_alloc(); - if (!sess->tree_conn_ida) - goto error; + ida_init(&sess->tree_conn_ida); if (protocol == CIFDS_SESSION_FLAG_SMB2) { down_write(&sessions_table_lock); @@ -320,7 +316,7 @@ int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess) int id = -EINVAL; if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) - id = ksmbd_acquire_smb2_tid(sess->tree_conn_ida); + id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida); return id; } @@ -328,18 +324,5 @@ int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess) void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id) { if (id >= 0) - ksmbd_release_id(sess->tree_conn_ida, id); -} - -int ksmbd_init_session_table(void) -{ - session_ida = ksmbd_ida_alloc(); - if (!session_ida) - return -ENOMEM; - return 0; -} - -void ksmbd_free_session_table(void) -{ - ksmbd_ida_free(session_ida); + ksmbd_release_id(&sess->tree_conn_ida, id); } diff --git a/fs/cifsd/mgmt/user_session.h b/fs/cifsd/mgmt/user_session.h index ad5c0430b62a..72b40348bdc4 100644 --- a/fs/cifsd/mgmt/user_session.h +++ b/fs/cifsd/mgmt/user_session.h @@ -16,7 +16,6 @@ #define PREAUTH_HASHVALUE_SIZE 64 -struct ksmbd_ida; struct ksmbd_file_table; struct channel { @@ -52,7 +51,7 @@ struct ksmbd_session { struct hlist_node hlist; struct list_head ksmbd_chann_list; struct xarray tree_conns; - struct ksmbd_ida *tree_conn_ida; + struct ida tree_conn_ida; struct list_head rpc_handle_list; @@ -101,8 +100,4 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); int get_session(struct ksmbd_session *sess); void put_session(struct ksmbd_session *sess); - -int ksmbd_init_session_table(void); -void ksmbd_free_session_table(void); - #endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c index 3e858100d5a6..a4a4e10cf172 100644 --- a/fs/cifsd/server.c +++ b/fs/cifsd/server.c @@ -537,7 +537,6 @@ static int ksmbd_server_shutdown(void) ksmbd_workqueue_destroy(); ksmbd_ipc_release(); ksmbd_conn_transport_destroy(); - ksmbd_free_session_table(); ksmbd_crypto_destroy(); ksmbd_free_global_file_table(); destroy_lease_table(NULL); @@ -566,10 +565,6 @@ static int __init ksmbd_server_init(void) if (ret) goto err_unregister; - ret = ksmbd_init_session_table(); - if (ret) - goto err_destroy_pools; - ret = ksmbd_ipc_init(); if (ret) goto err_free_session_table; @@ -600,8 +595,6 @@ err_destroy_file_table: err_ipc_release: ksmbd_ipc_release(); err_free_session_table: - ksmbd_free_session_table(); -err_destroy_pools: ksmbd_destroy_buffer_pools(); err_unregister: class_unregister(&ksmbd_control_class); diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index c1f6361603b9..cc4e8f11c487 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -517,7 +517,7 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) work->syncronous = true; if (work->async_id) { - ksmbd_release_id(conn->async_ida, work->async_id); + ksmbd_release_id(&conn->async_ida, work->async_id); work->async_id = 0; } @@ -685,7 +685,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) rsp_hdr = work->response_buf; rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; - id = ksmbd_acquire_async_msg_id(conn->async_ida); + id = ksmbd_acquire_async_msg_id(&conn->async_ida); if (id < 0) { ksmbd_err("Failed to alloc async message id\n"); return id; diff --git a/fs/cifsd/transport_ipc.c b/fs/cifsd/transport_ipc.c index 60c0289402c1..78061fecf816 100644 --- a/fs/cifsd/transport_ipc.c +++ b/fs/cifsd/transport_ipc.c @@ -35,7 +35,7 @@ static DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS); static DECLARE_RWSEM(ipc_msg_table_lock); static DEFINE_MUTEX(startup_lock); -static struct ksmbd_ida *ida; +static DEFINE_IDA(ipc_ida); static unsigned int ksmbd_tools_pid; @@ -247,7 +247,7 @@ static void ipc_msg_free(struct ksmbd_ipc_msg *msg) static void ipc_msg_handle_free(int handle) { if (handle >= 0) - ksmbd_release_id(ida, handle); + ksmbd_release_id(&ipc_ida, handle); } static int handle_response(int type, void *payload, size_t sz) @@ -512,7 +512,7 @@ struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account) msg->type = KSMBD_EVENT_LOGIN_REQUEST; req = KSMBD_IPC_MSG_PAYLOAD(msg); - req->handle = ksmbd_acquire_id(ida); + req->handle = ksmbd_acquire_id(&ipc_ida); strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); resp = ipc_msg_send_request(msg, req->handle); @@ -535,7 +535,7 @@ ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST; req = KSMBD_IPC_MSG_PAYLOAD(msg); - req->handle = ksmbd_acquire_id(ida); + req->handle = ksmbd_acquire_id(&ipc_ida); req->spnego_blob_len = blob_len; memcpy(req->spnego_blob, spnego_blob, blob_len); @@ -568,7 +568,7 @@ ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST; req = KSMBD_IPC_MSG_PAYLOAD(msg); - req->handle = ksmbd_acquire_id(ida); + req->handle = ksmbd_acquire_id(&ipc_ida); req->account_flags = sess->user->flags; req->session_id = sess->id; req->connect_id = tree_conn->id; @@ -646,7 +646,7 @@ ksmbd_ipc_share_config_request(const char *name) msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST; req = KSMBD_IPC_MSG_PAYLOAD(msg); - req->handle = ksmbd_acquire_id(ida); + req->handle = ksmbd_acquire_id(&ipc_ida); strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME); resp = ipc_msg_send_request(msg, req->handle); @@ -785,7 +785,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payloa msg->type = KSMBD_EVENT_RPC_REQUEST; req = KSMBD_IPC_MSG_PAYLOAD(msg); - req->handle = ksmbd_acquire_id(ida); + req->handle = ksmbd_acquire_id(&ipc_ida); req->flags = rpc_context_flags(sess); req->flags |= KSMBD_RPC_RAP_METHOD; req->payload_sz = payload_sz; @@ -842,18 +842,17 @@ static void ipc_timer_heartbeat(struct work_struct *w) int ksmbd_ipc_id_alloc(void) { - return ksmbd_acquire_id(ida); + return ksmbd_acquire_id(&ipc_ida); } void ksmbd_rpc_id_free(int handle) { - ksmbd_release_id(ida, handle); + ksmbd_release_id(&ipc_ida, handle); } void ksmbd_ipc_release(void) { cancel_delayed_work_sync(&ipc_timer_work); - ksmbd_ida_free(ida); genl_unregister_family(&ksmbd_genl_family); } @@ -867,7 +866,7 @@ void ksmbd_ipc_soft_reset(void) int ksmbd_ipc_init(void) { - int ret; + int ret = 0; ksmbd_nl_init_fixup(); INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat); @@ -875,19 +874,8 @@ int ksmbd_ipc_init(void) ret = genl_register_family(&ksmbd_genl_family); if (ret) { ksmbd_err("Failed to register KSMBD netlink interface %d\n", ret); - goto cancel_work; + cancel_delayed_work_sync(&ipc_timer_work); } - ida = ksmbd_ida_alloc(); - if (!ida) { - ret = -ENOMEM; - goto unregister; - } - return 0; - -unregister: - genl_unregister_family(&ksmbd_genl_family); -cancel_work: - cancel_delayed_work_sync(&ipc_timer_work); return ret; } From ff1d57272552e4d48e0aab015a457d0297915e0b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 13 Apr 2021 13:18:10 +0900 Subject: [PATCH 052/417] cifsd: add the check if parent is stable by unexpected rename This patch add the check if parent is stable by unexpected rename. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 12 +++--- fs/cifsd/vfs.c | 98 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 80 insertions(+), 30 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index cc4e8f11c487..3fbd8e4925bb 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -2844,12 +2844,10 @@ int smb2_open(struct ksmbd_work *work) * is already granted. */ if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { - if (ksmbd_vfs_inode_permission(path.dentry, - open_flags & O_ACCMODE, - may_delete)) { - rc = -EACCES; + rc = ksmbd_vfs_inode_permission(path.dentry, + open_flags & O_ACCMODE, may_delete); + if (rc) goto err_out; - } } } @@ -3260,7 +3258,7 @@ err_out1: rsp->hdr.Status = STATUS_INVALID_PARAMETER; else if (rc == -EOPNOTSUPP) rsp->hdr.Status = STATUS_NOT_SUPPORTED; - else if (rc == -EACCES) + else if (rc == -EACCES || rc == -ESTALE) rsp->hdr.Status = STATUS_ACCESS_DENIED; else if (rc == -ENOENT) rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; @@ -5938,7 +5936,7 @@ err_out: rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY; else if (rc == -EAGAIN) rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; - else if (rc == -EBADF) + else if (rc == -EBADF || rc == -ESTALE) rsp->hdr.Status = STATUS_INVALID_HANDLE; else if (rc == -EEXIST) rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 5985d2d1f276..f818aeff244f 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -70,7 +70,7 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete) { - int mask; + int mask, ret = 0; mask = 0; acc_mode &= O_ACCMODE; @@ -86,24 +86,39 @@ int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete) return -EACCES; if (delete) { - struct dentry *parent; + struct dentry *child, *parent; parent = dget_parent(dentry); - if (!parent) - return -EINVAL; + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); + child = lookup_one_len(dentry->d_name.name, parent, + dentry->d_name.len); + if (IS_ERR(child)) { + ret = PTR_ERR(child); + goto out_lock; + } + + if (child != dentry) { + ret = -ESTALE; + dput(child); + goto out_lock; + } + dput(child); if (inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) { - dput(parent); - return -EACCES; + ret = -EACCES; + goto out_lock; } +out_lock: + inode_unlock(d_inode(parent)); dput(parent); } - return 0; + return ret; } int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) { - struct dentry *parent; + struct dentry *parent, *child; + int ret = 0; *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); @@ -120,13 +135,28 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) *daccess |= FILE_EXECUTE_LE; parent = dget_parent(dentry); - if (!parent) - return 0; + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); + child = lookup_one_len(dentry->d_name.name, parent, + dentry->d_name.len); + if (IS_ERR(child)) { + ret = PTR_ERR(child); + goto out_lock; + } + + if (child != dentry) { + ret = -ESTALE; + dput(child); + goto out_lock; + } + dput(child); if (!inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) *daccess |= FILE_DELETE_LE; + +out_lock: + inode_unlock(d_inode(parent)); dput(parent); - return 0; + return ret; } /** @@ -726,7 +756,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, { struct path dst_path; struct dentry *src_dent_parent, *dst_dent_parent; - struct dentry *src_dent, *trap_dent; + struct dentry *src_dent, *trap_dent, *src_child; char *dst_name; int err; @@ -735,11 +765,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, return -EINVAL; src_dent_parent = dget_parent(fp->filp->f_path.dentry); - if (!src_dent_parent) - return -EINVAL; - src_dent = fp->filp->f_path.dentry; - dget(src_dent); err = kern_path(newname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &dst_path); if (err) { @@ -747,20 +773,36 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, goto out; } dst_dent_parent = dst_path.dentry; - dget(dst_dent_parent); trap_dent = lock_rename(src_dent_parent, dst_dent_parent); + dget(src_dent); + dget(dst_dent_parent); + src_child = lookup_one_len(src_dent->d_name.name, src_dent_parent, + src_dent->d_name.len); + if (IS_ERR(src_child)) { + err = PTR_ERR(src_child); + goto out_lock; + } + + if (src_child != src_dent) { + err = -ESTALE; + dput(src_child); + goto out_lock; + } + dput(src_child); + err = __ksmbd_vfs_rename(work, src_dent_parent, src_dent, dst_dent_parent, trap_dent, dst_name); - unlock_rename(src_dent_parent, dst_dent_parent); +out_lock: + dput(src_dent); dput(dst_dent_parent); + unlock_rename(src_dent_parent, dst_dent_parent); path_put(&dst_path); out: - dput(src_dent); dput(src_dent_parent); return err; } @@ -1050,23 +1092,33 @@ int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name) int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry) { + struct dentry *child; int err = 0; - dget(dentry); inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); - if (!d_inode(dentry) || !d_inode(dentry)->i_nlink) { - err = -ENOENT; + dget(dentry); + child = lookup_one_len(dentry->d_name.name, dir, + dentry->d_name.len); + if (IS_ERR(child)) { + err = PTR_ERR(child); goto out; } + if (child != dentry) { + err = -ESTALE; + dput(child); + goto out; + } + dput(child); + if (S_ISDIR(d_inode(dentry)->i_mode)) err = vfs_rmdir(&init_user_ns, d_inode(dir), dentry); else err = vfs_unlink(&init_user_ns, d_inode(dir), dentry, NULL); out: - inode_unlock(d_inode(dir)); dput(dentry); + inode_unlock(d_inode(dir)); if (err) ksmbd_debug(VFS, "failed to delete, err %d\n", err); From 7c3d3e99ca29f0abd5443353fe018a1368f08c43 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 13 Apr 2021 13:20:52 +0900 Subject: [PATCH 053/417] cifsd: get parent dentry from child in ksmbd_vfs_remove_file() To remove the file, We have parsed full pathname to divide parent path and filename. It is a better way to get parent dentry from child dentry that obtained by lookup with given pathname. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index f818aeff244f..010dfddb6240 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -578,31 +578,28 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) */ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) { - struct path parent; - struct dentry *dentry; - char *last; + struct path path; + struct dentry *dentry, *parent; int err; - last = extract_last_component(name); - if (!last) - return -EINVAL; - if (ksmbd_override_fsids(work)) return -ENOMEM; - err = kern_path(name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &parent); + err = kern_path(name, LOOKUP_FOLLOW, &path); if (err) { ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); ksmbd_revert_fsids(work); - rollback_path_modification(last); return err; } - inode_lock_nested(d_inode(parent.dentry), I_MUTEX_PARENT); - dentry = lookup_one_len(last, parent.dentry, strlen(last)); + parent = dget_parent(path.dentry); + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); + dentry = lookup_one_len(path.dentry->d_name.name, parent, + strlen(path.dentry->d_name.name)); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); - ksmbd_debug(VFS, "%s: lookup failed, err %d\n", last, err); + ksmbd_debug(VFS, "%s: lookup failed, err %d\n", + path.dentry->d_name.name, err); goto out_err; } @@ -613,12 +610,12 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) } if (S_ISDIR(d_inode(dentry)->i_mode)) { - err = vfs_rmdir(&init_user_ns, d_inode(parent.dentry), dentry); + err = vfs_rmdir(&init_user_ns, d_inode(parent), dentry); if (err && err != -ENOTEMPTY) ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, err); } else { - err = vfs_unlink(&init_user_ns, d_inode(parent.dentry), dentry, NULL); + err = vfs_unlink(&init_user_ns, d_inode(parent), dentry, NULL); if (err) ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, err); @@ -626,9 +623,9 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) dput(dentry); out_err: - inode_unlock(d_inode(parent.dentry)); - rollback_path_modification(last); - path_put(&parent); + inode_unlock(d_inode(parent)); + dput(parent); + path_put(&path); ksmbd_revert_fsids(work); return err; } From 3c20378325c710e7257b22ba333310771be51192 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Tue, 13 Apr 2021 13:22:31 +0900 Subject: [PATCH 054/417] cifsd: re-implement ksmbd_vfs_kern_path re-implement ksmbd_vfs_kern_path() to change recursion to iteration. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 103 +++++++++++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 42 deletions(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 010dfddb6240..d8259ca2493e 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -50,14 +50,6 @@ static char *extract_last_component(char *path) return p; } -static void rollback_path_modification(char *filename) -{ - if (filename) { - filename--; - *filename = '/'; - } -} - static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, struct inode *parent_inode, struct inode *inode) { @@ -1231,44 +1223,32 @@ static int __caseless_lookup(struct dir_context *ctx, const char *name, /** * ksmbd_vfs_lookup_in_dir() - lookup a file in a directory - * @dirname: directory name - * @filename: filename to lookup + * @dir: path info + * @name: filename to lookup + * @namelen: filename length * * Return: 0 on success, otherwise error */ -static int ksmbd_vfs_lookup_in_dir(char *dirname, char *filename) +static int ksmbd_vfs_lookup_in_dir(struct path *dir, char *name, size_t namelen) { - struct path dir_path; int ret; struct file *dfilp; int flags = O_RDONLY | O_LARGEFILE; - int dirnamelen = strlen(dirname); struct ksmbd_readdir_data readdir_data = { .ctx.actor = __caseless_lookup, - .private = filename, - .used = strlen(filename), + .private = name, + .used = namelen, + .dirent_count = 0, }; - ret = ksmbd_vfs_kern_path(dirname, 0, &dir_path, true); - if (ret) - goto error; - - dfilp = dentry_open(&dir_path, flags, current_cred()); - if (IS_ERR(dfilp)) { - path_put(&dir_path); - ksmbd_err("cannot open directory %s\n", dirname); - ret = -EINVAL; - goto error; - } + dfilp = dentry_open(dir, flags, current_cred()); + if (IS_ERR(dfilp)) + return PTR_ERR(dfilp); ret = ksmbd_vfs_readdir(dfilp, &readdir_data); if (readdir_data.dirent_count > 0) ret = 0; - fput(dfilp); - path_put(&dir_path); -error: - dirname[dirnamelen] = '/'; return ret; } @@ -1284,30 +1264,69 @@ error: int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, bool caseless) { - char *filename = NULL; int err; + if (name[0] != '/') + return -EINVAL; + err = kern_path(name, flags, path); if (!err) - return err; + return 0; if (caseless) { - filename = extract_last_component(name); - if (!filename) - goto out; + char *filepath; + struct path parent; + size_t path_len, remain_len; - /* root reached */ - if (strlen(name) == 0) - goto out; + filepath = kstrdup(name, GFP_KERNEL); + if (!filepath) + return -ENOMEM; - err = ksmbd_vfs_lookup_in_dir(name, filename); + path_len = strlen(filepath); + remain_len = path_len - 1; + + err = kern_path("/", flags, &parent); if (err) goto out; - err = kern_path(name, flags, path); - } + while (d_can_lookup(parent.dentry)) { + char *filename = filepath + path_len - remain_len; + char *next = strchrnul(filename, '/'); + size_t filename_len = next - filename; + bool is_last = !next[0]; + + if (filename_len == 0) + break; + + err = ksmbd_vfs_lookup_in_dir(&parent, filename, + filename_len); + if (err) { + path_put(&parent); + goto out; + } + + path_put(&parent); + next[0] = '\0'; + + err = kern_path(filepath, flags, &parent); + if (err) + goto out; + + if (is_last) { + path->mnt = parent.mnt; + path->dentry = parent.dentry; + goto out; + } + + next[0] = '/'; + remain_len -= filename_len + 1; + } + + path_put(&parent); + err = -EINVAL; out: - rollback_path_modification(filename); + kfree(filepath); + } return err; } From 24b626967d9574a477acf2ab94f55c847d04939a Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Tue, 13 Apr 2021 13:24:43 +0900 Subject: [PATCH 055/417] cifsd: fix reference count decrement of unclaimed file in __ksmbd_lookup_fd __ksmbd_lookup_fd could decrement the reference count of unclaimed ksmbd_file to 0 but not release this ksmbd_file. ksmbd_file cannot be unclaimed except ksmbd_close_inode_fds(), because ksmbd_file is only removed from the m_fp_list list after the reference count of ksmbd_file becomes 0. And if the count is 0, __ksmbd_lookup_fd does not use ksmbd_file found from idr due to atomic_inc_not_zero. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 5 ----- fs/cifsd/vfs_cache.c | 53 -------------------------------------------- 2 files changed, 58 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 3fbd8e4925bb..08b06ec97e22 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -2779,11 +2779,6 @@ int smb2_open(struct ksmbd_work *work) goto err_out; } - if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && - file_present) - file_present = ksmbd_close_inode_fds(work, - d_inode(path.dentry)); - daccess = smb_map_generic_desired_access(req->DesiredAccess); if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index f2a863542dc7..3ab06e0b723c 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -328,25 +328,13 @@ static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, unsigned int id) { - bool unclaimed = true; struct ksmbd_file *fp; read_lock(&ft->lock); fp = idr_find(ft->idr, id); if (fp) fp = ksmbd_fp_get(fp); - - if (fp && fp->f_ci) { - read_lock(&fp->f_ci->m_lock); - unclaimed = list_empty(&fp->node); - read_unlock(&fp->f_ci->m_lock); - } read_unlock(&ft->lock); - - if (fp && unclaimed) { - atomic_dec(&fp->refcount); - return NULL; - } return fp; } @@ -754,47 +742,6 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) return 0; } -static void close_fd_list(struct ksmbd_work *work, struct list_head *head) -{ - while (!list_empty(head)) { - struct ksmbd_file *fp; - - fp = list_first_entry(head, struct ksmbd_file, node); - list_del_init(&fp->node); - - __ksmbd_close_fd(&work->sess->file_table, fp); - } -} - -int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode) -{ - struct ksmbd_inode *ci; - bool unlinked = true; - struct ksmbd_file *fp, *fptmp; - LIST_HEAD(dispose); - - ci = ksmbd_inode_lookup_by_vfsinode(inode); - if (!ci) - return true; - - if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) - unlinked = false; - - write_lock(&ci->m_lock); - list_for_each_entry_safe(fp, fptmp, &ci->m_fp_list, node) { - if (fp->conn) - continue; - - list_del(&fp->node); - list_add(&fp->node, &dispose); - } - atomic_dec(&ci->m_count); - write_unlock(&ci->m_lock); - - close_fd_list(work, &dispose); - return unlinked; -} - int ksmbd_file_table_flush(struct ksmbd_work *work) { struct ksmbd_file *fp = NULL; From 915f570a971b4e5abd95e8b169dd41c120ab5a5b Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Tue, 13 Apr 2021 13:25:57 +0900 Subject: [PATCH 056/417] cifsd: Remove smb2_put_name() smb2_put_name() is called twice, and both call sites do the IS_ERR() check before. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 08b06ec97e22..1ff0b20ff7b8 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -666,16 +666,6 @@ smb2_get_name(struct ksmbd_share_config *share, const char *src, return unixname; } -/** - * smb2_put_name() - free memory allocated for filename - * @name: filename pointer to be freed - */ -static void smb2_put_name(void *name) -{ - if (!IS_ERR(name)) - kfree(name); -} - int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) { struct smb2_hdr *rsp_hdr; @@ -5418,7 +5408,7 @@ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, out: kfree(pathname); if (!IS_ERR(new_name)) - smb2_put_name(new_name); + kfree(new_name); return rc; } @@ -5483,7 +5473,7 @@ static int smb2_create_link(struct ksmbd_work *work, rc = -EINVAL; out: if (!IS_ERR(link_name)) - smb2_put_name(link_name); + kfree(link_name); kfree(pathname); return rc; } From 7e8094a73e522635a85fb5ad82847b544f4448bf Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 13 Apr 2021 17:26:02 +0900 Subject: [PATCH 057/417] cifsd: remove unused smberr.h smberr.h is a leftover of SMB1. This patch remove unused smberr.h. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/glob.h | 1 - fs/cifsd/netmisc.c | 1 - fs/cifsd/smberr.h | 235 --------------------------------------------- 3 files changed, 237 deletions(-) delete mode 100644 fs/cifsd/smberr.h diff --git a/fs/cifsd/glob.h b/fs/cifsd/glob.h index 9d70093a837a..ffeaf8aa5595 100644 --- a/fs/cifsd/glob.h +++ b/fs/cifsd/glob.h @@ -11,7 +11,6 @@ #include "unicode.h" #include "vfs_cache.h" -#include "smberr.h" #define KSMBD_VERSION "3.1.9" diff --git a/fs/cifsd/netmisc.c b/fs/cifsd/netmisc.c index 5d0327d87397..8f052434b64c 100644 --- a/fs/cifsd/netmisc.c +++ b/fs/cifsd/netmisc.c @@ -8,7 +8,6 @@ */ #include "glob.h" -#include "smberr.h" #include "nterr.h" #include "smb_common.h" diff --git a/fs/cifsd/smberr.h b/fs/cifsd/smberr.h deleted file mode 100644 index ce842303ae1f..000000000000 --- a/fs/cifsd/smberr.h +++ /dev/null @@ -1,235 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * Copyright (c) International Business Machines Corp., 2002,2004 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * See Error Codes section of the SNIA CIFS Specification - * for more information - */ -#ifndef __KSMBD_SMBERR_H -#define __KSMBD_SMBERR_H - -#define SUCCESS 0x00 /* The request was successful. */ -#define ERRDOS 0x01 /* Error is from the core DOS operating system set */ -#define ERRSRV 0x02 /* Error is generated by the file server daemon */ -#define ERRHRD 0x03 /* Error is a hardware error. */ -#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ - -/* The following error codes may be generated with the SUCCESS error class.*/ - -/*#define SUCCESS 0 The request was successful. */ - -/* The following error codes may be generated with the ERRDOS error class.*/ - -#define ERRbadfunc 1 /* - * Invalid function. The server did not - * recognize or could not perform a - * system call generated by the server, - * e.g., set the DIRECTORY attribute on - * a data file, invalid seek mode. - */ -#define ERRbadfile 2 /* - * File not found. The last component - * of a file's pathname could not be - * found. - */ -#define ERRbadpath 3 /* - * Directory invalid. A directory - * component in a pathname could not be - * found. - */ -#define ERRnofids 4 /* - * Too many open files. The server has - * no file handles available. - */ -#define ERRnoaccess 5 /* - * Access denied, the client's context - * does not permit the requested - * function. This includes the - * following conditions: invalid rename - * command, write to Fid open for read - * only, read on Fid open for write - * only, attempt to delete a non-empty - * directory - */ -#define ERRbadfid 6 /* - * Invalid file handle. The file handle - * specified was not recognized by the - * server. - */ -#define ERRbadmcb 7 /* Memory control blocks destroyed. */ -#define ERRnomem 8 /* - * Insufficient server memory to - * perform the requested function. - */ -#define ERRbadmem 9 /* Invalid memory block address. */ -#define ERRbadenv 10 /* Invalid environment. */ -#define ERRbadformat 11 /* Invalid format. */ -#define ERRbadaccess 12 /* Invalid open mode. */ -#define ERRbaddata 13 /* - * Invalid data (generated only by - * IOCTL calls within the server). - */ -#define ERRbaddrive 15 /* Invalid drive specified. */ -#define ERRremcd 16 /* - * A Delete Directory request attempted - * to remove the server's current - * directory. - */ -#define ERRdiffdevice 17 /* - * Not same device (e.g., a cross - * volume rename was attempted - */ -#define ERRnofiles 18 /* - * A File Search command can find no - * more files matching the specified - * criteria. - */ -#define ERRwriteprot 19 /* media is write protected */ -#define ERRgeneral 31 -#define ERRbadshare 32 /* - * The sharing mode specified for an - * Open conflicts with existing FIDs on - * the file. - */ -#define ERRlock 33 /* - * A Lock request conflicted with an - * existing lock or specified an - * invalid mode, or an Unlock requested - * attempted to remove a lock held by - * another process. - */ -#define ERRunsup 50 -#define ERRnosuchshare 67 -#define ERRfilexists 80 /* - * The file named in the request - * already exists. - */ -#define ERRinvparm 87 -#define ERRdiskfull 112 -#define ERRinvname 123 -#define ERRinvlevel 124 -#define ERRdirnotempty 145 -#define ERRnotlocked 158 -#define ERRcancelviolation 173 -#define ERRnoatomiclocks 174 -#define ERRalreadyexists 183 -#define ERRbadpipe 230 -#define ERRpipebusy 231 -#define ERRpipeclosing 232 -#define ERRnotconnected 233 -#define ERRmoredata 234 -#define ERReasnotsupported 282 -#define ErrQuota 0x200 /* - * The operation would cause a quota - * limit to be exceeded. - */ -#define ErrNotALink 0x201 /* - * A link operation was performed on a - * pathname that was not a link. - */ - -/* - * Below errors are used internally (do not come over the wire) for passthrough - * from STATUS codes to POSIX only - */ -#define ERRsymlink 0xFFFD -#define ErrTooManyLinks 0xFFFE - -/* Following error codes may be generated with the ERRSRV error class.*/ - -#define ERRerror 1 /* - * Non-specific error code. It is - * returned under the following - * conditions: resource other than disk - * space exhausted (e.g. TIDs), first - * SMB command was not negotiate, - * multiple negotiates attempted, and - * internal server error. - */ -#define ERRbadpw 2 /* - * Bad password - name/password pair in - * a TreeConnect or Session Setup are - * invalid. - */ -#define ERRbadtype 3 /* - * used for indicating DFS referral - * needed - */ -#define ERRaccess 4 /* - * The client does not have the - * necessary access rights within the - * specified context for requested - * function. - */ -#define ERRinvtid 5 /* - * The Tid specified in a command was - * invalid. - */ -#define ERRinvnetname 6 /* - * Invalid network name in tree - * connect. - */ -#define ERRinvdevice 7 /* - * Invalid device - printer request - * made to non-printer connection or - * non-printer request made to printer - * connection. - */ -#define ERRqfull 49 /* - * Print queue full (files) -- returned - * by open print file. - */ -#define ERRqtoobig 50 /* Print queue full -- no space. */ -#define ERRqeof 51 /* EOF on print queue dump */ -#define ERRinvpfid 52 /* Invalid print file FID. */ -#define ERRsmbcmd 64 /* - * The server did not recognize the - * command received. - */ -#define ERRsrverror 65 /* - * The server encountered an internal - * error, e.g., system file - * unavailable. - */ -#define ERRbadBID 66 /* (obsolete) */ -#define ERRfilespecs 67 /* - * The Fid and pathname parameters - * contained an invalid combination of - * values. - */ -#define ERRbadLink 68 /* (obsolete) */ -#define ERRbadpermits 69 /* - * The access permissions specified for - * a file or directory are not a valid - * combination. - */ -#define ERRbadPID 70 -#define ERRsetattrmode 71 /* attribute (mode) is invalid */ -#define ERRpaused 81 /* Server is paused */ -#define ERRmsgoff 82 /* reserved - messaging off */ -#define ERRnoroom 83 /* reserved - no room for message */ -#define ERRrmuns 87 /* reserved - too many remote names */ -#define ERRtimeout 88 /* operation timed out */ -#define ERRnoresource 89 /* No resources available for request */ -#define ERRtoomanyuids 90 /* - * Too many UIDs active on this session - */ -#define ERRbaduid 91 /* - * The UID is not known as a valid user - */ -#define ERRusempx 250 /* temporarily unable to use raw */ -#define ERRusestd 251 /* - * temporarily unable to use either raw - * or mpx - */ -#define ERR_NOTIFY_ENUM_DIR 1024 -#define ERRnoSuchUser 2238 /* user account does not exist */ -#define ERRaccountexpired 2239 -#define ERRbadclient 2240 /* can not logon from this client */ -#define ERRbadLogonTime 2241 /* logon hours do not allow this */ -#define ERRpasswordExpired 2242 -#define ERRnetlogonNotStarted 2455 -#define ERRnosupport 0xFFFF - -#endif /* __KSMBD_SMBERR_H */ From 2efec2dee861000263d255a24f7a7c6d82c749d1 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 14 Apr 2021 09:16:27 +0900 Subject: [PATCH 058/417] cifsd: remove unused nterr.c file Remove unused nterr.c file. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/nterr.c | 674 ----------------------------------------------- fs/cifsd/nterr.h | 7 - 2 files changed, 681 deletions(-) delete mode 100644 fs/cifsd/nterr.c diff --git a/fs/cifsd/nterr.c b/fs/cifsd/nterr.c deleted file mode 100644 index 358a766375b4..000000000000 --- a/fs/cifsd/nterr.c +++ /dev/null @@ -1,674 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Unix SMB/Netbios implementation. - * Version 1.9. - * RPC Pipe client / server routines - * Copyright (C) Luke Kenneth Casson Leighton 1997-2001. - */ - -/* NT error codes - see nterr.h */ -#include -#include -#include "nterr.h" - -const struct nt_err_code_struct nt_errs[] = { - {"NT_STATUS_OK", NT_STATUS_OK}, - {"NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL}, - {"NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED}, - {"NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS}, - {"NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH}, - {"NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION}, - {"NT_STATUS_BUFFER_OVERFLOW", NT_STATUS_BUFFER_OVERFLOW}, - {"NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR}, - {"NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA}, - {"NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE}, - {"NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK}, - {"NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC}, - {"NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID}, - {"NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED}, - {"NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER}, - {"NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE}, - {"NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE}, - {"NT_STATUS_INVALID_DEVICE_REQUEST", - NT_STATUS_INVALID_DEVICE_REQUEST}, - {"NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE}, - {"NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME}, - {"NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE}, - {"NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA}, - {"NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR}, - {"NT_STATUS_MORE_PROCESSING_REQUIRED", - NT_STATUS_MORE_PROCESSING_REQUIRED}, - {"NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY}, - {"NT_STATUS_CONFLICTING_ADDRESSES", - NT_STATUS_CONFLICTING_ADDRESSES}, - {"NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW}, - {"NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM}, - {"NT_STATUS_UNABLE_TO_DELETE_SECTION", - NT_STATUS_UNABLE_TO_DELETE_SECTION}, - {"NT_STATUS_INVALID_SYSTEM_SERVICE", - NT_STATUS_INVALID_SYSTEM_SERVICE}, - {"NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION}, - {"NT_STATUS_INVALID_LOCK_SEQUENCE", - NT_STATUS_INVALID_LOCK_SEQUENCE}, - {"NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE}, - {"NT_STATUS_INVALID_FILE_FOR_SECTION", - NT_STATUS_INVALID_FILE_FOR_SECTION}, - {"NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED}, - {"NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED}, - {"NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL}, - {"NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH}, - {"NT_STATUS_NONCONTINUABLE_EXCEPTION", - NT_STATUS_NONCONTINUABLE_EXCEPTION}, - {"NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION}, - {"NT_STATUS_UNWIND", NT_STATUS_UNWIND}, - {"NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK}, - {"NT_STATUS_INVALID_UNWIND_TARGET", - NT_STATUS_INVALID_UNWIND_TARGET}, - {"NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED}, - {"NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR}, - {"NT_STATUS_UNABLE_TO_DECOMMIT_VM", - NT_STATUS_UNABLE_TO_DECOMMIT_VM}, - {"NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED}, - {"NT_STATUS_INVALID_PORT_ATTRIBUTES", - NT_STATUS_INVALID_PORT_ATTRIBUTES}, - {"NT_STATUS_PORT_MESSAGE_TOO_LONG", - NT_STATUS_PORT_MESSAGE_TOO_LONG}, - {"NT_STATUS_INVALID_PARAMETER_MIX", - NT_STATUS_INVALID_PARAMETER_MIX}, - {"NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER}, - {"NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR}, - {"NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID}, - {"NT_STATUS_OBJECT_NAME_NOT_FOUND", - NT_STATUS_OBJECT_NAME_NOT_FOUND}, - {"NT_STATUS_OBJECT_NAME_COLLISION", - NT_STATUS_OBJECT_NAME_COLLISION}, - {"NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE}, - {"NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED}, - {"NT_STATUS_DEVICE_ALREADY_ATTACHED", - NT_STATUS_DEVICE_ALREADY_ATTACHED}, - {"NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID}, - {"NT_STATUS_OBJECT_PATH_NOT_FOUND", - NT_STATUS_OBJECT_PATH_NOT_FOUND}, - {"NT_STATUS_OBJECT_PATH_SYNTAX_BAD", - NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, - {"NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN}, - {"NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR}, - {"NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR}, - {"NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR}, - {"NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG}, - {"NT_STATUS_PORT_CONNECTION_REFUSED", - NT_STATUS_PORT_CONNECTION_REFUSED}, - {"NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE}, - {"NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION}, - {"NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED}, - {"NT_STATUS_INVALID_PAGE_PROTECTION", - NT_STATUS_INVALID_PAGE_PROTECTION}, - {"NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED}, - {"NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", - NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, - {"NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET}, - {"NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE}, - {"NT_STATUS_SUSPEND_COUNT_EXCEEDED", - NT_STATUS_SUSPEND_COUNT_EXCEEDED}, - {"NT_STATUS_THREAD_IS_TERMINATING", - NT_STATUS_THREAD_IS_TERMINATING}, - {"NT_STATUS_BAD_WORKING_SET_LIMIT", - NT_STATUS_BAD_WORKING_SET_LIMIT}, - {"NT_STATUS_INCOMPATIBLE_FILE_MAP", - NT_STATUS_INCOMPATIBLE_FILE_MAP}, - {"NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION}, - {"NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED}, - {"NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE}, - {"NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY}, - {"NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE}, - {"NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR}, - {"NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT}, - {"NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED}, - {"NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING}, - {"NT_STATUS_CTL_FILE_NOT_SUPPORTED", - NT_STATUS_CTL_FILE_NOT_SUPPORTED}, - {"NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION}, - {"NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH}, - {"NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER}, - {"NT_STATUS_INVALID_PRIMARY_GROUP", - NT_STATUS_INVALID_PRIMARY_GROUP}, - {"NT_STATUS_NO_IMPERSONATION_TOKEN", - NT_STATUS_NO_IMPERSONATION_TOKEN}, - {"NT_STATUS_CANT_DISABLE_MANDATORY", - NT_STATUS_CANT_DISABLE_MANDATORY}, - {"NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS}, - {"NT_STATUS_NO_SUCH_LOGON_SESSION", - NT_STATUS_NO_SUCH_LOGON_SESSION}, - {"NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE}, - {"NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD}, - {"NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME}, - {"NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS}, - {"NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER}, - {"NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS}, - {"NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP}, - {"NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP}, - {"NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP}, - {"NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN}, - {"NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD}, - {"NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD}, - {"NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION}, - {"NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE}, - {"NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION}, - {"NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS}, - {"NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION}, - {"NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED}, - {"NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED}, - {"NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED}, - {"NT_STATUS_TOO_MANY_LUIDS_REQUESTED", - NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, - {"NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED}, - {"NT_STATUS_INVALID_SUB_AUTHORITY", - NT_STATUS_INVALID_SUB_AUTHORITY}, - {"NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL}, - {"NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID}, - {"NT_STATUS_INVALID_SECURITY_DESCR", - NT_STATUS_INVALID_SECURITY_DESCR}, - {"NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND}, - {"NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT}, - {"NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN}, - {"NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL}, - {"NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED}, - {"NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL}, - {"NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED}, - {"NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED}, - {"NT_STATUS_TOO_MANY_GUIDS_REQUESTED", - NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, - {"NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED}, - {"NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY}, - {"NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED}, - {"NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL}, - {"NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED}, - {"NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA}, - {"NT_STATUS_RESOURCE_DATA_NOT_FOUND", - NT_STATUS_RESOURCE_DATA_NOT_FOUND}, - {"NT_STATUS_RESOURCE_TYPE_NOT_FOUND", - NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, - {"NT_STATUS_RESOURCE_NAME_NOT_FOUND", - NT_STATUS_RESOURCE_NAME_NOT_FOUND}, - {"NT_STATUS_ARRAY_BOUNDS_EXCEEDED", - NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, - {"NT_STATUS_FLOAT_DENORMAL_OPERAND", - NT_STATUS_FLOAT_DENORMAL_OPERAND}, - {"NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, - {"NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT}, - {"NT_STATUS_FLOAT_INVALID_OPERATION", - NT_STATUS_FLOAT_INVALID_OPERATION}, - {"NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW}, - {"NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK}, - {"NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW}, - {"NT_STATUS_INTEGER_DIVIDE_BY_ZERO", - NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, - {"NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW}, - {"NT_STATUS_PRIVILEGED_INSTRUCTION", - NT_STATUS_PRIVILEGED_INSTRUCTION}, - {"NT_STATUS_TOO_MANY_PAGING_FILES", - NT_STATUS_TOO_MANY_PAGING_FILES}, - {"NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID}, - {"NT_STATUS_ALLOTTED_SPACE_EXCEEDED", - NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, - {"NT_STATUS_INSUFFICIENT_RESOURCES", - NT_STATUS_INSUFFICIENT_RESOURCES}, - {"NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND}, - {"NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR}, - {"NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED}, - {"NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE}, - {"NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE}, - {"NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED}, - {"NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA}, - {"NT_STATUS_MEDIA_WRITE_PROTECTED", - NT_STATUS_MEDIA_WRITE_PROTECTED}, - {"NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY}, - {"NT_STATUS_INVALID_GROUP_ATTRIBUTES", - NT_STATUS_INVALID_GROUP_ATTRIBUTES}, - {"NT_STATUS_BAD_IMPERSONATION_LEVEL", - NT_STATUS_BAD_IMPERSONATION_LEVEL}, - {"NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS}, - {"NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS}, - {"NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE}, - {"NT_STATUS_BAD_MASTER_BOOT_RECORD", - NT_STATUS_BAD_MASTER_BOOT_RECORD}, - {"NT_STATUS_INSTRUCTION_MISALIGNMENT", - NT_STATUS_INSTRUCTION_MISALIGNMENT}, - {"NT_STATUS_INSTANCE_NOT_AVAILABLE", - NT_STATUS_INSTANCE_NOT_AVAILABLE}, - {"NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE}, - {"NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE}, - {"NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY}, - {"NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION}, - {"NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED}, - {"NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING}, - {"NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED}, - {"NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING}, - {"NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE}, - {"NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT}, - {"NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED}, - {"NT_STATUS_PROFILING_NOT_STARTED", - NT_STATUS_PROFILING_NOT_STARTED}, - {"NT_STATUS_PROFILING_NOT_STOPPED", - NT_STATUS_PROFILING_NOT_STOPPED}, - {"NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET}, - {"NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY}, - {"NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED}, - {"NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING}, - {"NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME}, - {"NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH}, - {"NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY}, - {"NT_STATUS_DEVICE_DOES_NOT_EXIST", - NT_STATUS_DEVICE_DOES_NOT_EXIST}, - {"NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS}, - {"NT_STATUS_ADAPTER_HARDWARE_ERROR", - NT_STATUS_ADAPTER_HARDWARE_ERROR}, - {"NT_STATUS_INVALID_NETWORK_RESPONSE", - NT_STATUS_INVALID_NETWORK_RESPONSE}, - {"NT_STATUS_UNEXPECTED_NETWORK_ERROR", - NT_STATUS_UNEXPECTED_NETWORK_ERROR}, - {"NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER}, - {"NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL}, - {"NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE}, - {"NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED}, - {"NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED}, - {"NT_STATUS_NETWORK_ACCESS_DENIED", - NT_STATUS_NETWORK_ACCESS_DENIED}, - {"NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE}, - {"NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME}, - {"NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES}, - {"NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS}, - {"NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED}, - {"NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED}, - {"NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED}, - {"NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT}, - {"NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT}, - {"NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE}, - {"NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED}, - {"NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", - NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, - {"NT_STATUS_NO_SECURITY_ON_OBJECT", - NT_STATUS_NO_SECURITY_ON_OBJECT}, - {"NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT}, - {"NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY}, - {"NT_STATUS_CANT_ACCESS_DOMAIN_INFO", - NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, - {"NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF}, - {"NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE}, - {"NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE}, - {"NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE}, - {"NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN}, - {"NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS}, - {"NT_STATUS_DOMAIN_LIMIT_EXCEEDED", - NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, - {"NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED}, - {"NT_STATUS_INVALID_OPLOCK_PROTOCOL", - NT_STATUS_INVALID_OPLOCK_PROTOCOL}, - {"NT_STATUS_INTERNAL_DB_CORRUPTION", - NT_STATUS_INTERNAL_DB_CORRUPTION}, - {"NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR}, - {"NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED}, - {"NT_STATUS_BAD_DESCRIPTOR_FORMAT", - NT_STATUS_BAD_DESCRIPTOR_FORMAT}, - {"NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER}, - {"NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR}, - {"NT_STATUS_UNEXPECTED_MM_CREATE_ERR", - NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, - {"NT_STATUS_UNEXPECTED_MM_MAP_ERROR", - NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, - {"NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", - NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, - {"NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS}, - {"NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS}, - {"NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1}, - {"NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2}, - {"NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3}, - {"NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4}, - {"NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5}, - {"NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6}, - {"NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7}, - {"NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8}, - {"NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9}, - {"NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10}, - {"NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11}, - {"NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12}, - {"NT_STATUS_REDIRECTOR_NOT_STARTED", - NT_STATUS_REDIRECTOR_NOT_STARTED}, - {"NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED}, - {"NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW}, - {"NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE}, - {"NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE}, - {"NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY}, - {"NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR}, - {"NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY}, - {"NT_STATUS_BAD_LOGON_SESSION_STATE", - NT_STATUS_BAD_LOGON_SESSION_STATE}, - {"NT_STATUS_LOGON_SESSION_COLLISION", - NT_STATUS_LOGON_SESSION_COLLISION}, - {"NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG}, - {"NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN}, - {"NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE}, - {"NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND}, - {"NT_STATUS_PROCESS_IS_TERMINATING", - NT_STATUS_PROCESS_IS_TERMINATING}, - {"NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE}, - {"NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION}, - {"NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE}, - {"NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED}, - {"NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT}, - {"NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST}, - {"NT_STATUS_ABIOS_LID_ALREADY_OWNED", - NT_STATUS_ABIOS_LID_ALREADY_OWNED}, - {"NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER}, - {"NT_STATUS_ABIOS_INVALID_COMMAND", - NT_STATUS_ABIOS_INVALID_COMMAND}, - {"NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID}, - {"NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", - NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, - {"NT_STATUS_ABIOS_INVALID_SELECTOR", - NT_STATUS_ABIOS_INVALID_SELECTOR}, - {"NT_STATUS_NO_LDT", NT_STATUS_NO_LDT}, - {"NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE}, - {"NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET}, - {"NT_STATUS_INVALID_LDT_DESCRIPTOR", - NT_STATUS_INVALID_LDT_DESCRIPTOR}, - {"NT_STATUS_INVALID_IMAGE_NE_FORMAT", - NT_STATUS_INVALID_IMAGE_NE_FORMAT}, - {"NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE}, - {"NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE}, - {"NT_STATUS_MAPPED_FILE_SIZE_ZERO", - NT_STATUS_MAPPED_FILE_SIZE_ZERO}, - {"NT_STATUS_TOO_MANY_OPENED_FILES", - NT_STATUS_TOO_MANY_OPENED_FILES}, - {"NT_STATUS_CANCELLED", NT_STATUS_CANCELLED}, - {"NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE}, - {"NT_STATUS_INVALID_COMPUTER_NAME", - NT_STATUS_INVALID_COMPUTER_NAME}, - {"NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED}, - {"NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT}, - {"NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP}, - {"NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER}, - {"NT_STATUS_MEMBERS_PRIMARY_GROUP", - NT_STATUS_MEMBERS_PRIMARY_GROUP}, - {"NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED}, - {"NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS}, - {"NT_STATUS_THREAD_NOT_IN_PROCESS", - NT_STATUS_THREAD_NOT_IN_PROCESS}, - {"NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE}, - {"NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", - NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, - {"NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT}, - {"NT_STATUS_INVALID_IMAGE_LE_FORMAT", - NT_STATUS_INVALID_IMAGE_LE_FORMAT}, - {"NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ}, - {"NT_STATUS_INVALID_IMAGE_PROTECT", - NT_STATUS_INVALID_IMAGE_PROTECT}, - {"NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16}, - {"NT_STATUS_LOGON_SERVER_CONFLICT", - NT_STATUS_LOGON_SERVER_CONFLICT}, - {"NT_STATUS_TIME_DIFFERENCE_AT_DC", - NT_STATUS_TIME_DIFFERENCE_AT_DC}, - {"NT_STATUS_SYNCHRONIZATION_REQUIRED", - NT_STATUS_SYNCHRONIZATION_REQUIRED}, - {"NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND}, - {"NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED}, - {"NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED}, - {"NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND}, - {"NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND}, - {"NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT}, - {"NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT}, - {"NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT}, - {"NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES}, - {"NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED}, - {"NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT}, - {"NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION}, - {"NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS}, - {"NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED}, - {"NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE}, - {"NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION}, - {"NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE}, - {"NT_STATUS_PAGEFILE_CREATE_FAILED", - NT_STATUS_PAGEFILE_CREATE_FAILED}, - {"NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE}, - {"NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL}, - {"NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE}, - {"NT_STATUS_ILLEGAL_FLOAT_CONTEXT", - NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, - {"NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN}, - {"NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT}, - {"NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED}, - {"NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR}, - {"NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME}, - {"NT_STATUS_SERIAL_NO_DEVICE_INITED", - NT_STATUS_SERIAL_NO_DEVICE_INITED}, - {"NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS}, - {"NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS}, - {"NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS}, - {"NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS}, - {"NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED}, - {"NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS}, - {"NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG}, - {"NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR}, - {"NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE}, - {"NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS}, - {"NT_STATUS_LOGON_TYPE_NOT_GRANTED", - NT_STATUS_LOGON_TYPE_NOT_GRANTED}, - {"NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE}, - {"NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", - NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, - {"NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", - NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, - {"NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER}, - {"NT_STATUS_ILL_FORMED_SERVICE_ENTRY", - NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, - {"NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER}, - {"NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER}, - {"NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER}, - {"NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME}, - {"NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", - NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, - {"NT_STATUS_FLOPPY_WRONG_CYLINDER", - NT_STATUS_FLOPPY_WRONG_CYLINDER}, - {"NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR}, - {"NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS}, - {"NT_STATUS_DISK_RECALIBRATE_FAILED", - NT_STATUS_DISK_RECALIBRATE_FAILED}, - {"NT_STATUS_DISK_OPERATION_FAILED", - NT_STATUS_DISK_OPERATION_FAILED}, - {"NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED}, - {"NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY}, - {"NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING}, - {"NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE}, - {"NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH}, - {"NT_STATUS_DEVICE_NOT_PARTITIONED", - NT_STATUS_DEVICE_NOT_PARTITIONED}, - {"NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA}, - {"NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", - NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, - {"NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW}, - {"NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA}, - {"NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER}, - {"NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER}, - {"NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED}, - {"NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE}, - {"NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS}, - {"NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", - NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, - {"NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN}, - {"NT_STATUS_CHILD_MUST_BE_VOLATILE", - NT_STATUS_CHILD_MUST_BE_VOLATILE}, - {"NT_STATUS_DEVICE_CONFIGURATION_ERROR", - NT_STATUS_DEVICE_CONFIGURATION_ERROR}, - {"NT_STATUS_DRIVER_INTERNAL_ERROR", - NT_STATUS_DRIVER_INTERNAL_ERROR}, - {"NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE}, - {"NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR}, - {"NT_STATUS_DEVICE_PROTOCOL_ERROR", - NT_STATUS_DEVICE_PROTOCOL_ERROR}, - {"NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER}, - {"NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL}, - {"NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE}, - {"NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET}, - {"NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT}, - {"NT_STATUS_TRUSTED_DOMAIN_FAILURE", - NT_STATUS_TRUSTED_DOMAIN_FAILURE}, - {"NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", - NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, - {"NT_STATUS_EVENTLOG_FILE_CORRUPT", - NT_STATUS_EVENTLOG_FILE_CORRUPT}, - {"NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START}, - {"NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE}, - {"NT_STATUS_MUTANT_LIMIT_EXCEEDED", - NT_STATUS_MUTANT_LIMIT_EXCEEDED}, - {"NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED}, - {"NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED}, - {"NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK}, - {"NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", - NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, - {"NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT}, - {"NT_STATUS_EVENTLOG_FILE_CHANGED", - NT_STATUS_EVENTLOG_FILE_CHANGED}, - {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", - NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, - {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", - NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, - {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", - NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, - {"NT_STATUS_DOMAIN_TRUST_INCONSISTENT", - NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, - {"NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED}, - {"NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY}, - {"NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED}, - {"NT_STATUS_RESOURCE_LANG_NOT_FOUND", - NT_STATUS_RESOURCE_LANG_NOT_FOUND}, - {"NT_STATUS_INSUFF_SERVER_RESOURCES", - NT_STATUS_INSUFF_SERVER_RESOURCES}, - {"NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE}, - {"NT_STATUS_INVALID_ADDRESS_COMPONENT", - NT_STATUS_INVALID_ADDRESS_COMPONENT}, - {"NT_STATUS_INVALID_ADDRESS_WILDCARD", - NT_STATUS_INVALID_ADDRESS_WILDCARD}, - {"NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES}, - {"NT_STATUS_ADDRESS_ALREADY_EXISTS", - NT_STATUS_ADDRESS_ALREADY_EXISTS}, - {"NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED}, - {"NT_STATUS_CONNECTION_DISCONNECTED", - NT_STATUS_CONNECTION_DISCONNECTED}, - {"NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET}, - {"NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES}, - {"NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED}, - {"NT_STATUS_TRANSACTION_TIMED_OUT", - NT_STATUS_TRANSACTION_TIMED_OUT}, - {"NT_STATUS_TRANSACTION_NO_RELEASE", - NT_STATUS_TRANSACTION_NO_RELEASE}, - {"NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH}, - {"NT_STATUS_TRANSACTION_RESPONDED", - NT_STATUS_TRANSACTION_RESPONDED}, - {"NT_STATUS_TRANSACTION_INVALID_ID", - NT_STATUS_TRANSACTION_INVALID_ID}, - {"NT_STATUS_TRANSACTION_INVALID_TYPE", - NT_STATUS_TRANSACTION_INVALID_TYPE}, - {"NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION}, - {"NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION}, - {"NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", - NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, - {"NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED}, - {"NT_STATUS_SYSTEM_PROCESS_TERMINATED", - NT_STATUS_SYSTEM_PROCESS_TERMINATED}, - {"NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED}, - {"NT_STATUS_NO_BROWSER_SERVERS_FOUND", - NT_STATUS_NO_BROWSER_SERVERS_FOUND}, - {"NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR}, - {"NT_STATUS_DRIVER_CANCEL_TIMEOUT", - NT_STATUS_DRIVER_CANCEL_TIMEOUT}, - {"NT_STATUS_REPLY_MESSAGE_MISMATCH", - NT_STATUS_REPLY_MESSAGE_MISMATCH}, - {"NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT}, - {"NT_STATUS_IMAGE_CHECKSUM_MISMATCH", - NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, - {"NT_STATUS_LOST_WRITEBEHIND_DATA", - NT_STATUS_LOST_WRITEBEHIND_DATA}, - {"NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", - NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, - {"NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE}, - {"NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND}, - {"NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM}, - {"NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE}, - {"NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ}, - {"NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK}, - {"NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID}, - {"NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS}, - {"NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE}, - {"NT_STATUS_RETRY", NT_STATUS_RETRY}, - {"NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE}, - {"NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET}, - {"NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND}, - {"NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW}, - {"NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT}, - {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", - NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, - {"NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT}, - {"NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE}, - {"NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED}, - {"NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT}, - {"NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", - NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, - {"NT_STATUS_ADDRESS_NOT_ASSOCIATED", - NT_STATUS_ADDRESS_NOT_ASSOCIATED}, - {"NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID}, - {"NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE}, - {"NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE}, - {"NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE}, - {"NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE}, - {"NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE}, - {"NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED}, - {"NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED}, - {"NT_STATUS_BAD_COMPRESSION_BUFFER", - NT_STATUS_BAD_COMPRESSION_BUFFER}, - {"NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE}, - {"NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED}, - {"NT_STATUS_TIMER_RESOLUTION_NOT_SET", - NT_STATUS_TIMER_RESOLUTION_NOT_SET}, - {"NT_STATUS_CONNECTION_COUNT_LIMIT", - NT_STATUS_CONNECTION_COUNT_LIMIT}, - {"NT_STATUS_LOGIN_TIME_RESTRICTION", - NT_STATUS_LOGIN_TIME_RESTRICTION}, - {"NT_STATUS_LOGIN_WKSTA_RESTRICTION", - NT_STATUS_LOGIN_WKSTA_RESTRICTION}, - {"NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH}, - {"NT_STATUS_INSUFFICIENT_LOGON_INFO", - NT_STATUS_INSUFFICIENT_LOGON_INFO}, - {"NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT}, - {"NT_STATUS_BAD_SERVICE_ENTRYPOINT", - NT_STATUS_BAD_SERVICE_ENTRYPOINT}, - {"NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST}, - {"NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1}, - {"NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2}, - {"NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT}, - {"NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED}, - {"NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE}, - {"NT_STATUS_LICENSE_QUOTA_EXCEEDED", - NT_STATUS_LICENSE_QUOTA_EXCEEDED}, - {"NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT}, - {"NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT}, - {"NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT}, - {"NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE}, - {"NT_STATUS_UNSUPPORTED_COMPRESSION", - NT_STATUS_UNSUPPORTED_COMPRESSION}, - {"NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE}, - {"NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", - NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, - {"NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", - NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, - {"NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", - NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, - {"NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED}, - {"NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS}, - {"NT_STATUS_QUOTA_LIST_INCONSISTENT", - NT_STATUS_QUOTA_LIST_INCONSISTENT}, - {"NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE}, - {"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES}, - {"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES}, - {"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED}, - {NULL, 0} -}; diff --git a/fs/cifsd/nterr.h b/fs/cifsd/nterr.h index 9f5004b69d30..a66100e74741 100644 --- a/fs/cifsd/nterr.h +++ b/fs/cifsd/nterr.h @@ -14,13 +14,6 @@ #ifndef _NTERR_H #define _NTERR_H -struct nt_err_code_struct { - char *nt_errstr; - __u32 nt_errcode; -}; - -extern const struct nt_err_code_struct nt_errs[]; - /* Win32 Status codes. */ #define NT_STATUS_MORE_ENTRIES 0x0105 #define NT_ERROR_INVALID_PARAMETER 0x0057 From 5626518ecaa50ffa5797e516a47a0b1392db1aa9 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 14 Apr 2021 09:24:11 +0900 Subject: [PATCH 059/417] cifsd: move nt time functions to misc.c Move nt time functions in netmisc.c to misc.c to remove netmisc.c file. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Makefile | 2 +- fs/cifsd/misc.c | 46 ++++++++++++++++++++++++++++++++++ fs/cifsd/misc.h | 6 +++++ fs/cifsd/netmisc.c | 58 ------------------------------------------- fs/cifsd/smb_common.h | 6 ----- fs/cifsd/vfs.c | 1 + 6 files changed, 54 insertions(+), 65 deletions(-) delete mode 100644 fs/cifsd/netmisc.c diff --git a/fs/cifsd/Makefile b/fs/cifsd/Makefile index a6c03c4ba51e..75ce0c6f0862 100644 --- a/fs/cifsd/Makefile +++ b/fs/cifsd/Makefile @@ -9,5 +9,5 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o buffer_pool.o \ mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ - smb2ops.o smb2misc.o asn1.o netmisc.o ndr.o + smb2ops.o smb2misc.o asn1.o ndr.o ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o diff --git a/fs/cifsd/misc.c b/fs/cifsd/misc.c index cbaaecf2eca1..7fa6649fadfd 100644 --- a/fs/cifsd/misc.c +++ b/fs/cifsd/misc.c @@ -292,3 +292,49 @@ char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, conv[*conv_len + 1] = 0x00; return conv; } + +/* + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + */ +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc) +{ + struct timespec64 ts; + + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; + u64 abs_t; + + /* + * Unfortunately can not use normal 64 bit division on 32 bit arch, but + * the alternative, do_div, does not work with negative numbers so have + * to special case them + */ + if (t < 0) { + abs_t = -t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_nsec = -ts.tv_nsec; + ts.tv_sec = -abs_t; + } else { + abs_t = t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_sec = abs_t; + } + + return ts; +} + +/* Convert the Unix UTC into NT UTC. */ +inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) +{ + /* Convert to 100ns intervals and then add the NTFS time offset. */ + return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; +} + +inline long long ksmbd_systime(void) +{ + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + return ksmbd_UnixTimeToNT(ts); +} diff --git a/fs/cifsd/misc.h b/fs/cifsd/misc.h index 73b21709b6c9..e4bd02a8d45f 100644 --- a/fs/cifsd/misc.h +++ b/fs/cifsd/misc.h @@ -35,4 +35,10 @@ struct ksmbd_dir_info; char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, const struct nls_table *local_nls, int *conv_len); + +#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) + +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); +u64 ksmbd_UnixTimeToNT(struct timespec64 t); +long long ksmbd_systime(void); #endif /* __KSMBD_MISC_H__ */ diff --git a/fs/cifsd/netmisc.c b/fs/cifsd/netmisc.c deleted file mode 100644 index 8f052434b64c..000000000000 --- a/fs/cifsd/netmisc.c +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * Error mapping routines from Samba libsmb/errormap.c - * Copyright (C) Andrew Tridgell 2001 - */ - -#include "glob.h" -#include "nterr.h" -#include "smb_common.h" - -/* - * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) - * into Unix UTC (based 1970-01-01, in seconds). - */ -struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc) -{ - struct timespec64 ts; - - /* Subtract the NTFS time offset, then convert to 1s intervals. */ - s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; - u64 abs_t; - - /* - * Unfortunately can not use normal 64 bit division on 32 bit arch, but - * the alternative, do_div, does not work with negative numbers so have - * to special case them - */ - if (t < 0) { - abs_t = -t; - ts.tv_nsec = do_div(abs_t, 10000000) * 100; - ts.tv_nsec = -ts.tv_nsec; - ts.tv_sec = -abs_t; - } else { - abs_t = t; - ts.tv_nsec = do_div(abs_t, 10000000) * 100; - ts.tv_sec = abs_t; - } - - return ts; -} - -/* Convert the Unix UTC into NT UTC. */ -inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) -{ - /* Convert to 100ns intervals and then add the NTFS time offset. */ - return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; -} - -inline long long ksmbd_systime(void) -{ - struct timespec64 ts; - - ktime_get_real_ts64(&ts); - return ksmbd_UnixTimeToNT(ts); -} diff --git a/fs/cifsd/smb_common.h b/fs/cifsd/smb_common.h index 2e171c9002b2..2d7b1c693ff4 100644 --- a/fs/cifsd/smb_common.h +++ b/fs/cifsd/smb_common.h @@ -541,10 +541,4 @@ static inline void inc_rfc1001_len(void *buf, int count) { be32_add_cpu((__be32 *)buf, count); } - -#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) - -struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); -u64 ksmbd_UnixTimeToNT(struct timespec64 t); -long long ksmbd_systime(void); #endif /* __SMB_COMMON_H__ */ diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index d8259ca2493e..7c8ab19ab014 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -29,6 +29,7 @@ #include "smbacl.h" #include "ndr.h" #include "auth.h" +#include "misc.h" #include "smb_common.h" #include "mgmt/share_config.h" From e6b1059ffaeac794bf1a76fd35947c7c6ac4cb57 Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Thu, 15 Apr 2021 10:24:56 +0900 Subject: [PATCH 060/417] cifsd: Fix potential null-ptr-deref in smb2_open() Fix potential null-ptr-deref in smb2_open(). Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 1ff0b20ff7b8..ba552b8f2127 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -2918,13 +2918,16 @@ int smb2_open(struct ksmbd_work *work) fattr.cf_gid = inode->i_gid; fattr.cf_mode = inode->i_mode; fattr.cf_dacls = NULL; + ace_num = 0; fattr.cf_acls = ksmbd_vfs_get_acl(inode, ACL_TYPE_ACCESS); - ace_num = fattr.cf_acls->a_count; + if (fattr.cf_acls) + ace_num = fattr.cf_acls->a_count; if (S_ISDIR(inode->i_mode)) { fattr.cf_dacls = ksmbd_vfs_get_acl(inode, ACL_TYPE_DEFAULT); - ace_num += fattr.cf_dacls->a_count; + if (fattr.cf_dacls) + ace_num += fattr.cf_dacls->a_count; } pntsd = kmalloc(sizeof(struct smb_ntsd) + From fba08fa005e44b18d6956de3abbe104f45e74697 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 15 Apr 2021 10:29:39 +0900 Subject: [PATCH 061/417] cifsd: use d_inode() Use d_inode(). Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 4 ++-- fs/cifsd/smbacl.c | 6 +++--- fs/cifsd/vfs.c | 10 +++++----- fs/cifsd/vfs_cache.h | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index ba552b8f2127..7d6013ea23e7 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -2890,9 +2890,9 @@ int smb2_open(struct ksmbd_work *work) /* Set default windows and posix acls if creating new file */ if (created) { int posix_acl_rc; - struct inode *inode = path.dentry->d_inode; + struct inode *inode = d_inode(path.dentry); - posix_acl_rc = ksmbd_vfs_inherit_posix_acl(inode, path.dentry->d_parent->d_inode); + posix_acl_rc = ksmbd_vfs_inherit_posix_acl(inode, d_inode(path.dentry->d_parent)); if (posix_acl_rc) ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); diff --git a/fs/cifsd/smbacl.c b/fs/cifsd/smbacl.c index a3675aa837b9..d65e853ab00f 100644 --- a/fs/cifsd/smbacl.c +++ b/fs/cifsd/smbacl.c @@ -950,7 +950,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0; int rc = -ENOENT, num_aces, dacloffset, pntsd_type, acl_len; char *aces_base; - bool is_dir = S_ISDIR(dentry->d_inode->i_mode); + bool is_dir = S_ISDIR(d_inode(dentry)->i_mode); acl_len = ksmbd_vfs_get_sd_xattr(conn, parent, &parent_pntsd); if (acl_len <= 0) @@ -1198,7 +1198,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, granted = GENERIC_ALL_FLAGS; } - posix_acls = ksmbd_vfs_get_acl(dentry->d_inode, ACL_TYPE_ACCESS); + posix_acls = ksmbd_vfs_get_acl(d_inode(dentry), ACL_TYPE_ACCESS); if (posix_acls && !found) { unsigned int id = -1; @@ -1263,7 +1263,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, { int rc; struct smb_fattr fattr = {{0}}; - struct inode *inode = dentry->d_inode; + struct inode *inode = d_inode(dentry); fattr.cf_uid = INVALID_UID; fattr.cf_gid = INVALID_GID; diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 7c8ab19ab014..29f31db4e07e 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -1461,11 +1461,11 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, struct ndr sd_ndr = {0}, acl_ndr = {0}; struct xattr_ntacl acl = {0}; struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; - struct inode *inode = dentry->d_inode; + struct inode *inode = d_inode(dentry); acl.version = 4; acl.hash_type = XATTR_SD_HASH_TYPE_SHA256; - acl.current_time = ksmbd_UnixTimeToNT(current_time(dentry->d_inode)); + acl.current_time = ksmbd_UnixTimeToNT(current_time(inode)); memcpy(acl.desc, "posix_acl", 9); acl.desc_len = 10; @@ -1486,9 +1486,9 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, return rc; } - smb_acl = ksmbd_vfs_make_xattr_posix_acl(dentry->d_inode, ACL_TYPE_ACCESS); + smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, ACL_TYPE_ACCESS); if (S_ISDIR(inode->i_mode)) - def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(dentry->d_inode, + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, ACL_TYPE_DEFAULT); rc = ndr_encode_posix_acl(&acl_ndr, inode, smb_acl, def_smb_acl); @@ -1531,7 +1531,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, rc = ksmbd_vfs_getxattr(dentry, XATTR_NAME_SD, &n.data); if (rc > 0) { - struct inode *inode = dentry->d_inode; + struct inode *inode = d_inode(dentry); struct ndr acl_ndr = {0}; struct xattr_ntacl acl; struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; diff --git a/fs/cifsd/vfs_cache.h b/fs/cifsd/vfs_cache.h index 8226fdf882e4..ce2047dda36a 100644 --- a/fs/cifsd/vfs_cache.h +++ b/fs/cifsd/vfs_cache.h @@ -26,8 +26,8 @@ #define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) #define FP_FILENAME(fp) fp->filp->f_path.dentry->d_name.name -#define FP_INODE(fp) fp->filp->f_path.dentry->d_inode -#define PARENT_INODE(fp) fp->filp->f_path.dentry->d_parent->d_inode +#define FP_INODE(fp) d_inode(fp->filp->f_path.dentry) +#define PARENT_INODE(fp) d_inode(fp->filp->f_path.dentry->d_parent) #define ATTR_FP(fp) (fp->attrib_only && \ (fp->cdoption != FILE_OVERWRITE_IF_LE && \ From 73f9dad511e8c5d53a6565192eb0b3a213863563 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 16 Apr 2021 14:12:06 +0900 Subject: [PATCH 062/417] cifsd: remove the dead code of unimplemented durable handle Remove the dead code of unimplemented durable handle. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/ksmbd_server.h | 1 - fs/cifsd/oplock.c | 2 - fs/cifsd/smb2pdu.c | 246 +--------------------------------------- fs/cifsd/smb_common.c | 5 - fs/cifsd/vfs_cache.c | 100 +--------------- fs/cifsd/vfs_cache.h | 6 - 6 files changed, 2 insertions(+), 358 deletions(-) diff --git a/fs/cifsd/ksmbd_server.h b/fs/cifsd/ksmbd_server.h index e46be4084087..442077a1e77b 100644 --- a/fs/cifsd/ksmbd_server.h +++ b/fs/cifsd/ksmbd_server.h @@ -33,7 +33,6 @@ struct ksmbd_heartbeat { #define KSMBD_GLOBAL_FLAG_CACHE_TBUF BIT(1) #define KSMBD_GLOBAL_FLAG_CACHE_RBUF BIT(2) #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(3) -#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE BIT(4) struct ksmbd_startup_request { __u32 flags; diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index 4ff23aee69fa..e77f1385a8c1 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -1482,8 +1482,6 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) buf->Name[3] = 'Q'; buf->Timeout = cpu_to_le32(fp->durable_timeout); - if (fp->is_persistent) - buf->Flags = SMB2_FLAGS_REPLAY_OPERATIONS; } /** diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 7d6013ea23e7..343e96ccdd4c 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -1946,173 +1946,6 @@ out: return err; } -#define DURABLE_RECONN_V2 1 -#define DURABLE_RECONN 2 -#define DURABLE_REQ_V2 3 -#define DURABLE_REQ 4 -#define APP_INSTANCE_ID 5 - -struct durable_info { - struct ksmbd_file *fp; - int type; - int reconnected; - int persistent; - int timeout; - char *CreateGuid; - char *app_id; -}; - -static int parse_durable_handle_context(struct ksmbd_work *work, - struct smb2_create_req *req, struct lease_ctx_info *lc, - struct durable_info *d_info) -{ - struct ksmbd_conn *conn = work->conn; - struct create_context *context; - int i, err = 0; - u64 persistent_id = 0; - int req_op_level; - static const char * const durable_arr[] = {"DH2C", "DHnC", "DH2Q", - "DHnQ", SMB2_CREATE_APP_INSTANCE_ID}; - - req_op_level = req->RequestedOplockLevel; - for (i = 1; i <= 5; i++) { - context = smb2_find_context_vals(req, durable_arr[i - 1]); - if (IS_ERR(context)) { - err = PTR_ERR(context); - if (err == -EINVAL) { - ksmbd_err("bad name length\n"); - goto out; - } - err = 0; - continue; - } - - switch (i) { - case DURABLE_RECONN_V2: - { - struct create_durable_reconn_v2_req *recon_v2; - - recon_v2 = - (struct create_durable_reconn_v2_req *)context; - persistent_id = le64_to_cpu(recon_v2->Fid.PersistentFileId); - d_info->fp = ksmbd_lookup_durable_fd(persistent_id); - if (!d_info->fp) { - ksmbd_err("Failed to get Durable handle state\n"); - err = -EBADF; - goto out; - } - - if (memcmp(d_info->fp->create_guid, recon_v2->CreateGuid, - SMB2_CREATE_GUID_SIZE)) { - err = -EBADF; - goto out; - } - d_info->type = i; - d_info->reconnected = 1; - ksmbd_debug(SMB, - "reconnect v2 Persistent-id from reconnect = %llu\n", - persistent_id); - break; - } - case DURABLE_RECONN: - { - struct create_durable_reconn_req *recon; - - if (d_info->type == DURABLE_RECONN_V2 || - d_info->type == DURABLE_REQ_V2) { - err = -EINVAL; - goto out; - } - - recon = - (struct create_durable_reconn_req *)context; - persistent_id = le64_to_cpu(recon->Data.Fid.PersistentFileId); - d_info->fp = ksmbd_lookup_durable_fd(persistent_id); - if (!d_info->fp) { - ksmbd_err("Failed to get Durable handle state\n"); - err = -EBADF; - goto out; - } - d_info->type = i; - d_info->reconnected = 1; - ksmbd_debug(SMB, - "reconnect Persistent-id from reconnect = %llu\n", - persistent_id); - break; - } - case DURABLE_REQ_V2: - { - struct create_durable_req_v2 *durable_v2_blob; - - if (d_info->type == DURABLE_RECONN || - d_info->type == DURABLE_RECONN_V2) { - err = -EINVAL; - goto out; - } - - durable_v2_blob = - (struct create_durable_req_v2 *)context; - ksmbd_debug(SMB, "Request for durable v2 open\n"); - d_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->CreateGuid); - if (d_info->fp) { - if (!memcmp(conn->ClientGUID, d_info->fp->client_guid, - SMB2_CLIENT_GUID_SIZE)) { - if (!(req->hdr.Flags & SMB2_FLAGS_REPLAY_OPERATIONS)) { - err = -ENOEXEC; - goto out; - } - - d_info->fp->conn = conn; - d_info->reconnected = 1; - goto out; - } - } - if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) || - req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) { - d_info->CreateGuid = - durable_v2_blob->CreateGuid; - d_info->persistent = - le32_to_cpu(durable_v2_blob->Flags); - d_info->timeout = - le32_to_cpu(durable_v2_blob->Timeout); - d_info->type = i; - } - break; - } - case DURABLE_REQ: - if (d_info->type == DURABLE_RECONN) - goto out; - if (d_info->type == DURABLE_RECONN_V2 || - d_info->type == DURABLE_REQ_V2) { - err = -EINVAL; - goto out; - } - - 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"); - d_info->type = i; - } - break; - case APP_INSTANCE_ID: - { - struct create_app_inst_id *inst_id; - - inst_id = (struct create_app_inst_id *)context; - ksmbd_close_fd_app_id(work, inst_id->AppInstanceId); - d_info->app_id = inst_id->AppInstanceId; - break; - } - default: - break; - } - } - -out: - - return err; -} - /** * smb2_set_ea() - handler for setting extended attributes using set * info command @@ -2431,7 +2264,6 @@ int smb2_open(struct ksmbd_work *work) char *name = NULL; char *stream_name = NULL; bool file_present = false, created = false, already_permitted = false; - struct durable_info d_info; int share_ret, need_truncate = 0; u64 time; umode_t posix_mode = 0; @@ -2509,36 +2341,8 @@ int smb2_open(struct ksmbd_work *work) } req_op_level = req->RequestedOplockLevel; - memset(&d_info, 0, sizeof(struct durable_info)); - if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && - req->CreateContextsOffset) { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) lc = parse_lease_state(req); - rc = parse_durable_handle_context(work, req, lc, &d_info); - if (rc) { - ksmbd_err("error parsing durable handle context\n"); - goto err_out1; - } - - if (d_info.reconnected) { - fp = d_info.fp; - rc = smb2_check_durable_oplock(d_info.fp, lc, name); - if (rc) - goto err_out1; - rc = ksmbd_reopen_durable_fd(work, d_info.fp); - if (rc) - goto err_out1; - if (ksmbd_override_fsids(work)) { - rc = -ENOMEM; - goto err_out1; - } - file_info = FILE_OPENED; - fp = d_info.fp; - goto reconnected; - } - } else { - if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) - lc = parse_lease_state(req); - } if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE_LE)) { ksmbd_err("Invalid impersonationlevel : 0x%x\n", @@ -3083,25 +2887,6 @@ int smb2_open(struct ksmbd_work *work) memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); - if (d_info.type) { - if (d_info.type == DURABLE_REQ_V2 && d_info.persistent) - fp->is_persistent = 1; - else - fp->is_durable = 1; - - if (d_info.type == DURABLE_REQ_V2) { - memcpy(fp->create_guid, d_info.CreateGuid, - SMB2_CREATE_GUID_SIZE); - if (d_info.timeout) - fp->durable_timeout = d_info.timeout; - else - fp->durable_timeout = 1600; - if (d_info.app_id) - memcpy(fp->app_instance_id, d_info.app_id, 16); - } - } - -reconnected: generic_fillattr(&init_user_ns, FP_INODE(fp), &stat); rsp->StructureSize = cpu_to_le16(89); @@ -3150,35 +2935,6 @@ reconnected: next_off = conn->vals->create_lease_size; } - if (d_info.type == DURABLE_REQ || d_info.type == DURABLE_REQ_V2) { - struct create_context *durable_ccontext; - - durable_ccontext = (struct create_context *)(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength)); - contxt_cnt++; - if (d_info.type == DURABLE_REQ) { - create_durable_rsp_buf(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength)); - le32_add_cpu(&rsp->CreateContextsLength, - conn->vals->create_durable_size); - inc_rfc1001_len(rsp_org, - conn->vals->create_durable_size); - } else { - create_durable_v2_rsp_buf(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength), - fp); - le32_add_cpu(&rsp->CreateContextsLength, - conn->vals->create_durable_v2_size); - inc_rfc1001_len(rsp_org, - conn->vals->create_durable_v2_size); - } - - if (next_ptr) - *next_ptr = cpu_to_le32(next_off); - next_ptr = &durable_ccontext->Next; - next_off = conn->vals->create_durable_size; - } - if (maximal_access_ctxt) { struct create_context *mxac_ccontext; diff --git a/fs/cifsd/smb_common.c b/fs/cifsd/smb_common.c index b0510213eb6d..985171cbf192 100644 --- a/fs/cifsd/smb_common.c +++ b/fs/cifsd/smb_common.c @@ -497,11 +497,6 @@ int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) if (strcmp(prev_fp->stream.name, curr_fp->stream.name)) continue; - if (prev_fp->is_durable) { - prev_fp->is_durable = 0; - continue; - } - if (prev_fp->attrib_only != curr_fp->attrib_only) continue; diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index 3ab06e0b723c..3286e74e2a56 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -449,30 +449,6 @@ struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) return __ksmbd_lookup_fd(&global_ft, id); } -int ksmbd_close_fd_app_id(struct ksmbd_work *work, char *app_id) -{ - struct ksmbd_file *fp = NULL; - unsigned int id; - - read_lock(&global_ft.lock); - idr_for_each_entry(global_ft.idr, fp, id) { - if (!memcmp(fp->app_instance_id, - app_id, - SMB2_CREATE_GUID_SIZE)) { - if (!atomic_dec_and_test(&fp->refcount)) - fp = NULL; - break; - } - } - read_unlock(&global_ft.lock); - - if (!fp) - return -EINVAL; - - __put_fd_final(work, fp); - return 0; -} - struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) { struct ksmbd_file *fp = NULL; @@ -492,23 +468,6 @@ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) return fp; } -struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, char *filename) -{ - struct ksmbd_file *fp = NULL; - unsigned int id; - - read_lock(&work->sess->file_table.lock); - idr_for_each_entry(work->sess->file_table.idr, fp, id) { - if (!strcmp(fp->filename, filename)) { - fp = ksmbd_fp_get(fp); - break; - } - } - read_unlock(&work->sess->file_table.lock); - - return fp; -} - struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) { struct ksmbd_file *lfp; @@ -617,32 +576,6 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) return fp; } -static inline bool is_reconnectable(struct ksmbd_file *fp) -{ - struct oplock_info *opinfo = opinfo_get(fp); - bool reconn = false; - - if (!opinfo) - return false; - - if (opinfo->op_state != OPLOCK_STATE_NONE) { - opinfo_put(opinfo); - return false; - } - - if (fp->is_resilient || fp->is_persistent) - reconn = true; - else if (fp->is_durable && opinfo->is_lease && - opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - reconn = true; - - else if (fp->is_durable && opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) - reconn = true; - - opinfo_put(opinfo); - return reconn; -} - static int __close_file_table_ids(struct ksmbd_file_table *ft, struct ksmbd_tree_connect *tcon, bool (*skip)(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp)) @@ -672,13 +605,7 @@ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, struct ksmbd_fil static bool session_fd_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) { - if (!is_reconnectable(fp)) - return false; - - fp->conn = NULL; - fp->tcon = NULL; - fp->volatile_id = KSMBD_NO_FID; - return true; + return false; } void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) @@ -717,31 +644,6 @@ void ksmbd_free_global_file_table(void) ksmbd_destroy_file_table(&global_ft); } -int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) -{ - if (!fp->is_durable || fp->conn || fp->tcon) { - ksmbd_err("Invalid durable fd [%p:%p]\n", - fp->conn, fp->tcon); - return -EBADF; - } - - if (HAS_FILE_ID(fp->volatile_id)) { - ksmbd_err("Still in use durable fd: %u\n", fp->volatile_id); - return -EBADF; - } - - fp->conn = work->sess->conn; - fp->tcon = work->tcon; - - __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); - if (!HAS_FILE_ID(fp->volatile_id)) { - fp->conn = NULL; - fp->tcon = NULL; - return -EBADF; - } - return 0; -} - int ksmbd_file_table_flush(struct ksmbd_work *work) { struct ksmbd_file *fp = NULL; diff --git a/fs/cifsd/vfs_cache.h b/fs/cifsd/vfs_cache.h index ce2047dda36a..5638641dd0cf 100644 --- a/fs/cifsd/vfs_cache.h +++ b/fs/cifsd/vfs_cache.h @@ -90,9 +90,6 @@ struct ksmbd_file { __u64 create_time; __u64 itime; - bool is_durable; - bool is_resilient; - bool is_persistent; bool is_nt_open; bool attrib_only; @@ -154,17 +151,14 @@ struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, unsigned int struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, unsigned int id, unsigned int pid); void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); -int ksmbd_close_fd_app_id(struct ksmbd_work *work, char *app_id); struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); -struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, char *filename); struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); void ksmbd_close_session_fds(struct ksmbd_work *work); int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); -int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp); int ksmbd_init_global_file_table(void); void ksmbd_free_global_file_table(void); int ksmbd_file_table_flush(struct ksmbd_work *work); From a299669b2c3d26cdb787ba4a87603f6de4fd7714 Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Tue, 27 Apr 2021 15:29:01 +0900 Subject: [PATCH 063/417] cifsd: Update access check in set_file_allocation_info/set_end_of_file_info [MS-SMB2] 3.3.5.21.1 If the object store supports security and FileInfoClass is FileAllocationInformation, FileEndOfFileInformation, or FileValidDataLengthInformation, and Open.GrantedAccess does not include FILE_WRITE_DATA, the server MUST fail the request with STATUS_ACCESS_DENIED. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 343e96ccdd4c..73c6154170cf 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -5352,7 +5352,7 @@ static int set_file_allocation_info(struct ksmbd_work *work, struct inode *inode; int rc; - if (!is_attributes_write_allowed(fp)) + if (!(fp->daccess & FILE_WRITE_DATA_LE)) return -EACCES; file_alloc_info = (struct smb2_file_alloc_info *)buf; @@ -5396,7 +5396,7 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, struct inode *inode; int rc; - if (!is_attributes_write_allowed(fp)) + if (!(fp->daccess & FILE_WRITE_DATA_LE)) return -EACCES; file_eof_info = (struct smb2_file_eof_info *)buf; From 7adfd4f6f78eb1c2561bcfdc20f7cc39f2d89437 Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Tue, 27 Apr 2021 15:30:22 +0900 Subject: [PATCH 064/417] cifsd: Remove is_attributes_write_allowed() wrapper Inline it in the only place it is used and remove it. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 73c6154170cf..eeb3f09e8765 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -5237,11 +5237,6 @@ out: return rc; } -static bool is_attributes_write_allowed(struct ksmbd_file *fp) -{ - return fp->daccess & FILE_WRITE_ATTRIBUTES_LE; -} - static int set_file_basic_info(struct ksmbd_file *fp, char *buf, struct ksmbd_share_config *share) { @@ -5252,7 +5247,7 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, struct inode *inode; int rc; - if (!is_attributes_write_allowed(fp)) + if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE)) return -EACCES; file_info = (struct smb2_file_all_info *)buf; From ced2b26a76cd1db0b6ccb39e0bc873177c9bda21 Mon Sep 17 00:00:00 2001 From: Sebastian Gottschall Date: Tue, 27 Apr 2021 15:33:54 +0900 Subject: [PATCH 065/417] cifsd: Fix regression in smb2_get_info a Windows 10 client isn't able to store files from ksmbd servers due unknown local permission problems (code 0x8007003A) if smb3 encryption is enabled. Windows 10 is requesting for ATTRIBUTE_SECINFO (mask 0x20) which is not yet handled by ksmbd, this leads to a invalid response. For now we just reintroduce the old check to avoid processing of unhandled flags until ATTRIBUTE_SECINFO is properly handled. Signed-off-by: Sebastian Gottschall Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index eeb3f09e8765..3b8da5dfd4a1 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -4791,6 +4791,24 @@ static int smb2_get_info_sec(struct ksmbd_work *work, int addition_info = le32_to_cpu(req->AdditionalInformation); int rc; + if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO)) { + ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", + addition_info); + + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); + pntsd->osidoffset = 0; + pntsd->gsidoffset = 0; + pntsd->sacloffset = 0; + pntsd->dacloffset = 0; + + secdesclen = sizeof(struct smb_ntsd); + rsp->OutputBufferLength = cpu_to_le32(secdesclen); + inc_rfc1001_len(rsp_org, secdesclen); + + return 0; + } + if (work->next_smb2_rcv_hdr_off) { if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { ksmbd_debug(SMB, "Compound request set FID = %u\n", From 204fcceb7ccf43034da8e97078153c7c6d0bc84d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 28 Apr 2021 13:17:47 +0900 Subject: [PATCH 066/417] cifsd: add ksmbd/nfsd interoperability to feature table Add ksmbd/nfsd interoperability to feature table and sync with a table in patch cover letter. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- Documentation/filesystems/cifs/cifsd.rst | 32 ++++++++++++++++-------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/Documentation/filesystems/cifs/cifsd.rst b/Documentation/filesystems/cifs/cifsd.rst index cb9f87b8529f..01a0be272ce6 100644 --- a/Documentation/filesystems/cifs/cifsd.rst +++ b/Documentation/filesystems/cifs/cifsd.rst @@ -67,7 +67,8 @@ CIFSD Feature Status Feature name Status ============================== ================================================= Dialects Supported. SMB2.1 SMB3.0, SMB3.1.1 dialects - excluding security vulnerable SMB1. + (intentionally excludes security vulnerable SMB1 + dialect). Auto Negotiation Supported. Compound Request Supported. Oplock Cache Mechanism Supported. @@ -79,26 +80,37 @@ HMAC-SHA256 Signing Supported. Secure negotiate Supported. Signing Update Supported. Pre-authentication integrity Supported. -SMB3 encryption(CCM, GCM) Supported. -SMB direct(RDMA) Partial Supported. SMB3 Multi-channel is required - to connect to Windows client. +SMB3 encryption(CCM, GCM) Supported. (CCM and GCM128 supported, GCM256 in + progress) +SMB direct(RDMA) Partially Supported. SMB3 Multi-channel is + required to connect to Windows client. SMB3 Multi-channel In Progress. SMB3.1.1 POSIX extension Supported. -ACLs Partial Supported. only DACLs available, SACLs is - planned for future. ksmbd generate random subauth +ACLs Partially Supported. only DACLs available, SACLs + (auditing) is planned for the future. For + ownership (SIDs) ksmbd generates random subauth values(then store it to disk) and use uid/gid get from inode as RID for local domain SID. The current acl implementation is limited to standalone server, not a domain member. + Integration with Samba tools is being worked on + to allow future support for running as a domain + member. Kerberos Supported. Durable handle v1,v2 Planned for future. Persistent handle Planned for future. SMB2 notify Planned for future. Sparse file support Supported. -DCE/RPC support Partial Supported. a few calls(NetShareEnumAll, - NetServerGetInfo, SAMR, LSARPC) that needed as - file server via netlink interface from - ksmbd.mountd. +DCE/RPC support Partially Supported. a few calls(NetShareEnumAll, + NetServerGetInfo, SAMR, LSARPC) that are needed + for file server handled via netlink interface + from ksmbd.mountd. Additional integration with + Samba tools and libraries via upcall is being + investigated to allow support for additional + DCE/RPC management calls (and future support + for Witness protocol e.g.) +ksmbd/nfsd interoperability Planned for future. The features that ksmbd + support are Leases, Notify, ACLs and Share modes. ============================== ================================================= From a4382db9bac314440f662be91ec8010465e67603 Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Thu, 6 May 2021 11:34:52 +0900 Subject: [PATCH 067/417] cifsd: Call smb2_set_err_rsp() in smb2_read/smb2_write error path Call smb2_set_err_rsp() in smb2_read/smb2_write error path. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 3b8da5dfd4a1..e5aff1ca11e1 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -5820,8 +5820,8 @@ int smb2_read(struct ksmbd_work *work) le64_to_cpu(req->VolatileFileId), le64_to_cpu(req->PersistentFileId)); if (!fp) { - rsp->hdr.Status = STATUS_FILE_CLOSED; - return -ENOENT; + err = -ENOENT; + goto out; } if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { @@ -6057,7 +6057,7 @@ int smb2_write(struct ksmbd_work *work) { struct smb2_write_req *req; struct smb2_write_rsp *rsp, *rsp_org; - struct ksmbd_file *fp = NULL; + struct ksmbd_file *fp; loff_t offset; size_t length; ssize_t nbytes; @@ -6082,8 +6082,8 @@ int smb2_write(struct ksmbd_work *work) fp = ksmbd_lookup_fd_slow(work, le64_to_cpu(req->VolatileFileId), le64_to_cpu(req->PersistentFileId)); if (!fp) { - rsp->hdr.Status = STATUS_FILE_CLOSED; - return -ENOENT; + err = -ENOENT; + goto out; } if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { From 79caa9606df1504b3b5104457cbb5d759f0e5fae Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Thu, 6 May 2021 11:38:35 +0900 Subject: [PATCH 068/417] cifsd: Handle ksmbd_session_rpc_open() failure in create_smb2_pipe() Currently, a SMB2 client does not receive an error message if ksmbd_session_rpc_open() fails when opening a pipe. Fix this by responding with STATUS_NO_MEMORY or STATUS_INVALID_PARAMETER depending on the error that occurred. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index e5aff1ca11e1..fec385318ff3 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -1917,9 +1917,13 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) } id = ksmbd_session_rpc_open(work->sess, name); - if (id < 0) + if (id < 0) { ksmbd_err("Unable to open RPC pipe: %d\n", id); + err = id; + goto out; + } + rsp->hdr.Status = STATUS_SUCCESS; rsp->StructureSize = cpu_to_le16(89); rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; rsp->Reserved = 0; @@ -1942,6 +1946,19 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) return 0; out: + switch (err) { + case -EINVAL: + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + break; + case -ENOSPC: + case -ENOMEM: + rsp->hdr.Status = STATUS_NO_MEMORY; + break; + } + + if (!IS_ERR(name)) + kfree(name); + smb2_set_err_rsp(work); return err; } From e7735c854880084a6d97e60465f19daa42842eff Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Thu, 6 May 2021 11:40:02 +0900 Subject: [PATCH 069/417] cifsd: Update out_buf_len in smb2_populate_readdir_entry() When processing a SMB2 QUERY_DIRECTORY request, smb2_populate_readdir_entry() is called first to fill the dot/dotdot entries. This moves the d_info->wptr pointer but out_buf_len remains unchanged. As a result, reserve_populate_dentry() may end up writing past the end of the buffer since the bounds checking is done on invalid values. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index fec385318ff3..54df9a30bd23 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -3333,6 +3333,7 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, d_info->last_entry_offset = d_info->data_count; d_info->data_count += next_entry_offset; + d_info->out_buf_len -= next_entry_offset; d_info->wptr += next_entry_offset; kfree(conv_name); From 1fca8038e9f10bc14eb3484d212b3f03b49ac3f5 Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Thu, 6 May 2021 11:41:54 +0900 Subject: [PATCH 070/417] cifsd: Fix potential null-ptr-deref in destroy_previous_session() The user field in the session structure is allocated when the client is authenticated. If the client explicitly logs off, the user field is freed, but the session is kept around in case the user reconnects. If the TCP connection hasn't been closed and the client sends a session setup with a PreviousSessionId set, destroy_previous_session() will be called to check if the session needs to be cleaned up. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 54df9a30bd23..ebae992f88a0 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -619,7 +619,8 @@ static void destroy_previous_session(struct ksmbd_user *user, u64 id) prev_user = prev_sess->user; - if (strcmp(user->name, prev_user->name) || + if (!prev_user || + strcmp(user->name, prev_user->name) || user->passkey_sz != prev_user->passkey_sz || memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) { put_session(prev_sess); From 5a0ca7700591a5275875920cf0c3113435e4b6f7 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 6 May 2021 11:43:37 +0900 Subject: [PATCH 071/417] cifsd: add support for AES256 encryption Now that 256 bit encryption can be negotiated, update names of the nonces to match the updated official protocol documentation (e.g. AES_GCM_NONCE instead of AES_128GCM_NONCE) since they apply to both 128 bit and 256 bit encryption. update smb encryption code to set 32 byte key length and to set gcm256/ccm256 when requested on mount. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 53 +++++++++++++++++++++++++----------- fs/cifsd/crypto_ctx.c | 8 +++--- fs/cifsd/crypto_ctx.h | 8 +++--- fs/cifsd/mgmt/user_session.h | 4 +-- fs/cifsd/smb2pdu.c | 11 +++++--- fs/cifsd/smb2pdu.h | 18 +++++++++--- 6 files changed, 68 insertions(+), 34 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 437e58a0826d..6b90aac86fcc 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -835,7 +835,8 @@ static int generate_key(struct ksmbd_session *sess, struct kvec label, { unsigned char zero = 0x0; __u8 i[4] = {0, 0, 0, 1}; - __u8 L[4] = {0, 0, 0, 128}; + __u8 L128[4] = {0, 0, 0, 128}; + __u8 L256[4] = {0, 0, 1, 0}; int rc = -EINVAL; unsigned char prfhash[SMB2_HMACSHA256_SIZE]; unsigned char *hashptr = prfhash; @@ -890,7 +891,11 @@ static int generate_key(struct ksmbd_session *sess, struct kvec label, goto smb3signkey_ret; } - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L, 4); + if (sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4); + else + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4); if (rc) { ksmbd_debug(AUTH, "could not update with L\n"); goto smb3signkey_ret; @@ -981,24 +986,33 @@ static int generate_smb3encryptionkey(struct ksmbd_session *sess, rc = generate_key(sess, ptwin->encryption.label, ptwin->encryption.context, sess->smb3encryptionkey, - SMB3_SIGN_KEY_SIZE); + SMB3_ENC_DEC_KEY_SIZE); if (rc) return rc; rc = generate_key(sess, ptwin->decryption.label, ptwin->decryption.context, - sess->smb3decryptionkey, SMB3_SIGN_KEY_SIZE); + sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); if (rc) return rc; ksmbd_debug(AUTH, "dumping generated AES encryption keys\n"); + ksmbd_debug(AUTH, "Cipher type %d\n", sess->conn->cipher_type); ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); ksmbd_debug(AUTH, "Session Key %*ph\n", SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); - ksmbd_debug(AUTH, "ServerIn Key %*ph\n", - SMB3_SIGN_KEY_SIZE, sess->smb3encryptionkey); - ksmbd_debug(AUTH, "ServerOut Key %*ph\n", - SMB3_SIGN_KEY_SIZE, sess->smb3decryptionkey); + if (sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { + ksmbd_debug(AUTH, "ServerIn Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3encryptionkey); + ksmbd_debug(AUTH, "ServerOut Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3decryptionkey); + } else { + ksmbd_debug(AUTH, "ServerIn Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3encryptionkey); + ksmbd_debug(AUTH, "ServerOut Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); + } return rc; } @@ -1136,7 +1150,7 @@ static int ksmbd_get_encryption_key(struct ksmbd_conn *conn, __u64 ses_id, ses_enc_key = enc ? sess->smb3encryptionkey : sess->smb3decryptionkey; - memcpy(key, ses_enc_key, SMB3_SIGN_KEY_SIZE); + memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); return 0; } @@ -1224,7 +1238,7 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, int rc = 0; struct scatterlist *sg; u8 sign[SMB2_SIGNATURE_SIZE] = {}; - u8 key[SMB3_SIGN_KEY_SIZE]; + u8 key[SMB3_ENC_DEC_KEY_SIZE]; struct aead_request *req; char *iv; unsigned int iv_len; @@ -1241,7 +1255,8 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, return 0; } - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM) + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) ctx = ksmbd_crypto_ctx_find_gcm(); else ctx = ksmbd_crypto_ctx_find_ccm(); @@ -1250,12 +1265,17 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, return -EINVAL; } - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM) + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) tfm = CRYPTO_GCM(ctx); else tfm = CRYPTO_CCM(ctx); - rc = crypto_aead_setkey(tfm, key, SMB3_SIGN_KEY_SIZE); + if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); + else + rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); if (rc) { ksmbd_err("Failed to set aead key %d\n", rc); goto free_ctx; @@ -1294,11 +1314,12 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, goto free_sg; } - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM) { - memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES128GCM_NONCE); + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { + memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); } else { iv[0] = 3; - memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES128CCM_NONCE); + memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); } aead_request_set_crypt(req, sg, sg, crypt_len, iv); diff --git a/fs/cifsd/crypto_ctx.c b/fs/cifsd/crypto_ctx.c index 8322b0f7a7fc..1830ae1b5ed3 100644 --- a/fs/cifsd/crypto_ctx.c +++ b/fs/cifsd/crypto_ctx.c @@ -42,10 +42,10 @@ static struct crypto_aead *alloc_aead(int id) struct crypto_aead *tfm = NULL; switch (id) { - case CRYPTO_AEAD_AES128_GCM: + case CRYPTO_AEAD_AES_GCM: tfm = crypto_alloc_aead("gcm(aes)", 0, 0); break; - case CRYPTO_AEAD_AES128_CCM: + case CRYPTO_AEAD_AES_CCM: tfm = crypto_alloc_aead("ccm(aes)", 0, 0); break; default: @@ -248,12 +248,12 @@ static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void) { - return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES128_GCM); + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_GCM); } struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void) { - return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES128_CCM); + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_CCM); } void ksmbd_crypto_destroy(void) diff --git a/fs/cifsd/crypto_ctx.h b/fs/cifsd/crypto_ctx.h index 64a11dfd6c83..b0d3cd650485 100644 --- a/fs/cifsd/crypto_ctx.h +++ b/fs/cifsd/crypto_ctx.h @@ -21,8 +21,8 @@ enum { }; enum { - CRYPTO_AEAD_AES128_GCM = 16, - CRYPTO_AEAD_AES128_CCM, + CRYPTO_AEAD_AES_GCM = 16, + CRYPTO_AEAD_AES_CCM, CRYPTO_AEAD_MAX, }; @@ -55,8 +55,8 @@ struct ksmbd_crypto_ctx { #define CRYPTO_MD4_TFM(c) ((c)->desc[CRYPTO_SHASH_MD4]->tfm) #define CRYPTO_MD5_TFM(c) ((c)->desc[CRYPTO_SHASH_MD5]->tfm) -#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES128_GCM]) -#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES128_CCM]) +#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) +#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); diff --git a/fs/cifsd/mgmt/user_session.h b/fs/cifsd/mgmt/user_session.h index 72b40348bdc4..1709563d718b 100644 --- a/fs/cifsd/mgmt/user_session.h +++ b/fs/cifsd/mgmt/user_session.h @@ -56,8 +56,8 @@ struct ksmbd_session { - __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE]; - __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; + __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; struct list_head sessions_entry; diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index ebae992f88a0..d07d7c45f899 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -904,7 +904,9 @@ static int decode_encrypt_ctxt(struct ksmbd_conn *conn, for (i = 0; i < cph_cnt; i++) { if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM || - pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM) { + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_CCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_GCM) { ksmbd_debug(SMB, "Cipher ID = 0x%x\n", pneg_ctxt->Ciphers[i]); conn->cipher_type = pneg_ctxt->Ciphers[i]; @@ -7979,10 +7981,11 @@ static void fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, char *old_buf, tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); tr_hdr->Flags = cpu_to_le16(0x01); - if (cipher_type == SMB2_ENCRYPTION_AES128_GCM) - get_random_bytes(&tr_hdr->Nonce, SMB3_AES128GCM_NONCE); + if (cipher_type == SMB2_ENCRYPTION_AES128_GCM || + cipher_type == SMB2_ENCRYPTION_AES256_GCM) + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); else - get_random_bytes(&tr_hdr->Nonce, SMB3_AES128CCM_NONCE); + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); memcpy(&tr_hdr->SessionId, &hdr->SessionId, 8); inc_rfc1001_len(tr_hdr, sizeof(struct smb2_transform_hdr) - 4); inc_rfc1001_len(tr_hdr, orig_len); diff --git a/fs/cifsd/smb2pdu.h b/fs/cifsd/smb2pdu.h index 156ff6a2968b..c5c32610aafe 100644 --- a/fs/cifsd/smb2pdu.h +++ b/fs/cifsd/smb2pdu.h @@ -77,6 +77,13 @@ #define SMB2_SIGNATURE_SIZE 16 #define SMB2_HMACSHA256_SIZE 32 #define SMB2_CMACAES_SIZE 16 +#define SMB3_GCM128_CRYPTKEY_SIZE 16 +#define SMB3_GCM256_CRYPTKEY_SIZE 32 + +/* + * Size of the smb3 encryption/decryption keys + */ +#define SMB3_ENC_DEC_KEY_SIZE 32 /* * Size of the smb3 signing key @@ -151,8 +158,8 @@ struct smb2_pdu { __le16 StructureSize2; /* size of wct area (varies, request specific) */ } __packed; -#define SMB3_AES128CCM_NONCE 11 -#define SMB3_AES128GCM_NONCE 12 +#define SMB3_AES_CCM_NONCE 11 +#define SMB3_AES_GCM_NONCE 12 struct smb2_transform_hdr { __be32 smb2_buf_length; /* big endian on wire */ @@ -283,13 +290,16 @@ struct smb2_preauth_neg_context { /* Encryption Algorithms Ciphers */ #define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) #define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) +#define SMB2_ENCRYPTION_AES256_CCM cpu_to_le16(0x0003) +#define SMB2_ENCRYPTION_AES256_GCM cpu_to_le16(0x0004) struct smb2_encryption_neg_context { __le16 ContextType; /* 2 */ __le16 DataLength; __le32 Reserved; - __le16 CipherCount; /* AES-128-GCM and AES-128-CCM */ - __le16 Ciphers[1]; /* Ciphers[0] since only one used now */ + /* CipherCount usally 2, but can be 3 when AES256-GCM enabled */ + __le16 CipherCount; /* AES-128-GCM and AES-128-CCM by default */ + __le16 Ciphers[1]; } __packed; #define SMB3_COMPRESS_NONE cpu_to_le16(0x0000) From bcd62a368314deeea8bd0823399b649a236b7d5b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 10 May 2021 09:08:19 +0900 Subject: [PATCH 072/417] cifsd: fix invalid memory access in smb2_write() Add missing fp initialzation to prevent invalid memory access in smb2_write(). Reported-by: Coverity Scan Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index d07d7c45f899..18de8a763209 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -6078,7 +6078,7 @@ int smb2_write(struct ksmbd_work *work) { struct smb2_write_req *req; struct smb2_write_rsp *rsp, *rsp_org; - struct ksmbd_file *fp; + struct ksmbd_file *fp = NULL; loff_t offset; size_t length; ssize_t nbytes; From fad4161b5cd01a24202234976ebbb133f7adc0b5 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 19 Apr 2021 17:26:15 +0900 Subject: [PATCH 073/417] cifsd: decoding gss token using lib/asn1_decoder.c Decode gss token of SMB2_SESSSION_SETUP using lib/asn1_decoder.c Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Kconfig | 1 + fs/cifsd/Makefile | 3 +- fs/cifsd/asn1.c | 614 +++++++----------------------- fs/cifsd/smb2pdu.c | 4 +- fs/cifsd/spnego_negtokeninit.asn1 | 43 +++ fs/cifsd/spnego_negtokentarg.asn1 | 19 + 6 files changed, 199 insertions(+), 485 deletions(-) create mode 100644 fs/cifsd/spnego_negtokeninit.asn1 create mode 100644 fs/cifsd/spnego_negtokentarg.asn1 diff --git a/fs/cifsd/Kconfig b/fs/cifsd/Kconfig index b94cf1158182..5316b1035fbe 100644 --- a/fs/cifsd/Kconfig +++ b/fs/cifsd/Kconfig @@ -17,6 +17,7 @@ config SMB_SERVER select CRYPTO_AEAD2 select CRYPTO_CCM select CRYPTO_GCM + select ASN1 default n help Choose Y here if you want to allow SMB3 compliant clients diff --git a/fs/cifsd/Makefile b/fs/cifsd/Makefile index 75ce0c6f0862..05d32264fa8c 100644 --- a/fs/cifsd/Makefile +++ b/fs/cifsd/Makefile @@ -9,5 +9,6 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o buffer_pool.o \ mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ - smb2ops.o smb2misc.o asn1.o ndr.o + smb2ops.o smb2misc.o spnego_negtokeninit.asn1.o \ + spnego_negtokentarg.asn1.o asn1.o ndr.o ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o diff --git a/fs/cifsd/asn1.c b/fs/cifsd/asn1.c index aa702b665849..aa6ea855c422 100644 --- a/fs/cifsd/asn1.c +++ b/fs/cifsd/asn1.c @@ -11,61 +11,15 @@ #include #include #include +#include #include "glob.h" #include "asn1.h" #include "connection.h" #include "auth.h" - -/***************************************************************************** - * - * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse) - * - *****************************************************************************/ - -/* Class */ -#define ASN1_UNI 0 /* Universal */ -#define ASN1_APL 1 /* Application */ -#define ASN1_CTX 2 /* Context */ -#define ASN1_PRV 3 /* Private */ - -/* Tag */ -#define ASN1_EOC 0 /* End Of Contents or N/A */ -#define ASN1_BOL 1 /* Boolean */ -#define ASN1_INT 2 /* Integer */ -#define ASN1_BTS 3 /* Bit String */ -#define ASN1_OTS 4 /* Octet String */ -#define ASN1_NUL 5 /* Null */ -#define ASN1_OJI 6 /* Object Identifier */ -#define ASN1_OJD 7 /* Object Description */ -#define ASN1_EXT 8 /* External */ -#define ASN1_ENUM 10 /* Enumerated */ -#define ASN1_SEQ 16 /* Sequence */ -#define ASN1_SET 17 /* Set */ -#define ASN1_NUMSTR 18 /* Numerical String */ -#define ASN1_PRNSTR 19 /* Printable String */ -#define ASN1_TEXSTR 20 /* Teletext String */ -#define ASN1_VIDSTR 21 /* Video String */ -#define ASN1_IA5STR 22 /* IA5 String */ -#define ASN1_UNITIM 23 /* Universal Time */ -#define ASN1_GENTIM 24 /* General Time */ -#define ASN1_GRASTR 25 /* Graphical String */ -#define ASN1_VISSTR 26 /* Visible String */ -#define ASN1_GENSTR 27 /* General String */ - -/* Primitive / Constructed methods*/ -#define ASN1_PRI 0 /* Primitive */ -#define ASN1_CON 1 /* Constructed */ - -/* - * Error codes. - */ -#define ASN1_ERR_NOERROR 0 -#define ASN1_ERR_DEC_EMPTY 2 -#define ASN1_ERR_DEC_EOC_MISMATCH 3 -#define ASN1_ERR_DEC_LENGTH_MISMATCH 4 -#define ASN1_ERR_DEC_BADVALUE 5 +#include "spnego_negtokeninit.asn1.h" +#include "spnego_negtokentarg.asn1.h" #define SPNEGO_OID_LEN 7 #define NTLMSSP_OID_LEN 10 @@ -81,212 +35,49 @@ static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 }; static char NTLMSSP_OID_STR[NTLMSSP_OID_LEN] = { 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a }; -/* - * ASN.1 context. - */ -struct asn1_ctx { - int error; /* Error condition */ - unsigned char *pointer; /* Octet just to be decoded */ - unsigned char *begin; /* First octet */ - unsigned char *end; /* Octet after last octet */ -}; - -/* - * Octet string (not null terminated) - */ -struct asn1_octstr { - unsigned char *data; - unsigned int len; -}; - -static void -asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len) -{ - ctx->begin = buf; - ctx->end = buf + len; - ctx->pointer = buf; - ctx->error = ASN1_ERR_NOERROR; -} - -static unsigned char -asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch) -{ - if (ctx->pointer >= ctx->end) { - ctx->error = ASN1_ERR_DEC_EMPTY; - return 0; - } - *ch = *(ctx->pointer)++; - return 1; -} - -static unsigned char -asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag) -{ - unsigned char ch; - - *tag = 0; - - do { - if (!asn1_octet_decode(ctx, &ch)) - return 0; - *tag <<= 7; - *tag |= ch & 0x7F; - } while ((ch & 0x80) == 0x80); - return 1; -} - -static unsigned char -asn1_id_decode(struct asn1_ctx *ctx, - unsigned int *cls, unsigned int *con, unsigned int *tag) -{ - unsigned char ch; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - *cls = (ch & 0xC0) >> 6; - *con = (ch & 0x20) >> 5; - *tag = (ch & 0x1F); - - if (*tag == 0x1F) { - if (!asn1_tag_decode(ctx, tag)) - return 0; - } - return 1; -} - -static unsigned char -asn1_length_decode(struct asn1_ctx *ctx, unsigned int *def, unsigned int *len) -{ - unsigned char ch, cnt; - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - if (ch == 0x80) - *def = 0; - else { - *def = 1; - - if (ch < 0x80) - *len = ch; - else { - cnt = (unsigned char) (ch & 0x7F); - *len = 0; - - while (cnt > 0) { - if (!asn1_octet_decode(ctx, &ch)) - return 0; - *len <<= 8; - *len |= ch; - cnt--; - } - } - } - - /* don't trust len bigger than ctx buffer */ - if (*len > ctx->end - ctx->pointer) - return 0; - - return 1; -} - -static unsigned char -asn1_header_decode(struct asn1_ctx *ctx, - unsigned char **eoc, - unsigned int *cls, unsigned int *con, unsigned int *tag) -{ - unsigned int def = 0; - unsigned int len = 0; - - if (!asn1_id_decode(ctx, cls, con, tag)) - return 0; - - if (!asn1_length_decode(ctx, &def, &len)) - return 0; - - /* primitive shall be definite, indefinite shall be constructed */ - if (*con == ASN1_PRI && !def) - return 0; - - if (def) - *eoc = ctx->pointer + len; - else - *eoc = NULL; - return 1; -} - -static unsigned char -asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc) -{ - unsigned char ch; - - if (!eoc) { - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - if (ch != 0x00) { - ctx->error = ASN1_ERR_DEC_EOC_MISMATCH; - return 0; - } - - if (!asn1_octet_decode(ctx, &ch)) - return 0; - - if (ch != 0x00) { - ctx->error = ASN1_ERR_DEC_EOC_MISMATCH; - return 0; - } - } else { - if (ctx->pointer != eoc) { - ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH; - return 0; - } - } - return 1; -} - -static unsigned char -asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid) +static bool +asn1_subid_decode(const unsigned char **begin, const unsigned char *end, + unsigned long *subid) { + const unsigned char *ptr = *begin; unsigned char ch; *subid = 0; do { - if (!asn1_octet_decode(ctx, &ch)) - return 0; + if (ptr >= end) + return false; + ch = *ptr++; *subid <<= 7; *subid |= ch & 0x7F; } while ((ch & 0x80) == 0x80); - return 1; + + *begin = ptr; + return true; } -static int -asn1_oid_decode(struct asn1_ctx *ctx, - unsigned char *eoc, unsigned long **oid, unsigned int *len) +static bool asn1_oid_decode(const unsigned char *value, size_t vlen, + unsigned long **oid, size_t *oidlen) { - unsigned long subid; - unsigned int size; + const unsigned char *iptr = value, *end = value + vlen; unsigned long *optr; + unsigned long subid; - size = eoc - ctx->pointer + 1; + vlen += 1; + if (vlen < 2 || vlen > UINT_MAX/sizeof(unsigned long)) + return false; - /* first subid actually encodes first two subids */ - if (size < 2 || size > UINT_MAX/sizeof(unsigned long)) - return 0; - - *oid = kmalloc(size * sizeof(unsigned long), GFP_KERNEL); + *oid = kmalloc(vlen * sizeof(unsigned long), GFP_KERNEL); if (!*oid) - return 0; + return false; optr = *oid; - if (!asn1_subid_decode(ctx, &subid)) { + if (!asn1_subid_decode(&iptr, end, &subid)) { kfree(*oid); *oid = NULL; - return 0; + return false; } if (subid < 40) { @@ -300,285 +91,55 @@ asn1_oid_decode(struct asn1_ctx *ctx, optr[1] = subid - 80; } - *len = 2; + *oidlen = 2; optr += 2; - while (ctx->pointer < eoc) { - if (++(*len) > size) { - ctx->error = ASN1_ERR_DEC_BADVALUE; + while (iptr < end) { + if (++(*oidlen) > vlen) { kfree(*oid); *oid = NULL; - return 0; + return false; } - if (!asn1_subid_decode(ctx, optr++)) { + if (!asn1_subid_decode(&iptr, end, optr++)) { kfree(*oid); *oid = NULL; - return 0; + return false; } } - return 1; + return true; } -static int -compare_oid(unsigned long *oid1, unsigned int oid1len, - unsigned long *oid2, unsigned int oid2len) +static bool +oid_eq(unsigned long *oid1, unsigned int oid1len, + unsigned long *oid2, unsigned int oid2len) { unsigned int i; if (oid1len != oid2len) - return 0; + return false; for (i = 0; i < oid1len; i++) { if (oid1[i] != oid2[i]) - return 0; + return false; } - return 1; + return true; } -/* BB check for endian conversion issues here */ - int ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, - struct ksmbd_conn *conn) + struct ksmbd_conn *conn) { - struct asn1_ctx ctx; - unsigned char *end; - unsigned char *sequence_end; - unsigned long *oid = NULL; - unsigned int cls, con, tag, oidlen, rc, mechTokenlen; - unsigned int mech_type; - - ksmbd_debug(AUTH, "Received SecBlob: length %d\n", length); - - asn1_open(&ctx, security_blob, length); - - /* GSSAPI header */ - if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { - ksmbd_debug(AUTH, "Error decoding negTokenInit header\n"); - return 0; - } else if ((cls != ASN1_APL) || (con != ASN1_CON) - || (tag != ASN1_EOC)) { - ksmbd_debug(AUTH, "cls = %d con = %d tag = %d\n", cls, con, - tag); - return 0; - } - - /* Check for SPNEGO OID -- remember to free obj->oid */ - rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag); - if (rc) { - if ((tag == ASN1_OJI) && (con == ASN1_PRI) && - (cls == ASN1_UNI)) { - rc = asn1_oid_decode(&ctx, end, &oid, &oidlen); - if (rc) { - rc = compare_oid(oid, oidlen, SPNEGO_OID, - SPNEGO_OID_LEN); - kfree(oid); - } - } else - rc = 0; - } - - /* SPNEGO OID not present or garbled -- bail out */ - if (!rc) { - ksmbd_debug(AUTH, "Error decoding negTokenInit header\n"); - return 0; - } - - /* SPNEGO */ - if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { - ksmbd_debug(AUTH, "Error decoding negTokenInit\n"); - return 0; - } else if ((cls != ASN1_CTX) || (con != ASN1_CON) - || (tag != ASN1_EOC)) { - ksmbd_debug(AUTH, - "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", - cls, con, tag, end, *end); - return 0; - } - - /* negTokenInit */ - if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { - ksmbd_debug(AUTH, "Error decoding negTokenInit\n"); - return 0; - } else if ((cls != ASN1_UNI) || (con != ASN1_CON) - || (tag != ASN1_SEQ)) { - ksmbd_debug(AUTH, - "cls = %d con = %d tag = %d end = %p (%d) exit 1\n", - cls, con, tag, end, *end); - return 0; - } - - /* sequence */ - if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { - ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n"); - return 0; - } else if ((cls != ASN1_CTX) || (con != ASN1_CON) - || (tag != ASN1_EOC)) { - ksmbd_debug(AUTH, - "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", - cls, con, tag, end, *end); - return 0; - } - - /* sequence of */ - if (asn1_header_decode - (&ctx, &sequence_end, &cls, &con, &tag) == 0) { - ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n"); - return 0; - } else if ((cls != ASN1_UNI) || (con != ASN1_CON) - || (tag != ASN1_SEQ)) { - ksmbd_debug(AUTH, - "cls = %d con = %d tag = %d end = %p (%d) exit 1\n", - cls, con, tag, end, *end); - return 0; - } - - /* list of security mechanisms */ - while (!asn1_eoc_decode(&ctx, sequence_end)) { - rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag); - if (!rc) { - ksmbd_debug(AUTH, - "Error decoding negTokenInit hdr exit2\n"); - return 0; - } - if ((tag == ASN1_OJI) && (con == ASN1_PRI)) { - if (asn1_oid_decode(&ctx, end, &oid, &oidlen)) { - if (compare_oid(oid, oidlen, MSKRB5_OID, - MSKRB5_OID_LEN)) - mech_type = KSMBD_AUTH_MSKRB5; - else if (compare_oid(oid, oidlen, KRB5U2U_OID, - KRB5U2U_OID_LEN)) - mech_type = KSMBD_AUTH_KRB5U2U; - else if (compare_oid(oid, oidlen, KRB5_OID, - KRB5_OID_LEN)) - mech_type = KSMBD_AUTH_KRB5; - else if (compare_oid(oid, oidlen, NTLMSSP_OID, - NTLMSSP_OID_LEN)) - mech_type = KSMBD_AUTH_NTLMSSP; - else { - kfree(oid); - continue; - } - - conn->auth_mechs |= mech_type; - if (conn->preferred_auth_mech == 0) - conn->preferred_auth_mech = mech_type; - kfree(oid); - } - } else { - ksmbd_debug(AUTH, - "Should be an oid what is going on?\n"); - } - } - - /* sequence */ - if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { - ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n"); - return 0; - } else if ((cls != ASN1_CTX) || (con != ASN1_CON) - || (tag != ASN1_INT)) { - ksmbd_debug(AUTH, - "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", - cls, con, tag, end, *end); - return 0; - } - - /* sequence of */ - if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { - ksmbd_debug(AUTH, "Error decoding 2nd part of negTokenInit\n"); - return 0; - } else if ((cls != ASN1_UNI) || (con != ASN1_PRI) - || (tag != ASN1_OTS)) { - ksmbd_debug(AUTH, - "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", - cls, con, tag, end, *end); - return 0; - } - - mechTokenlen = ctx.end - ctx.pointer; - conn->mechToken = kmalloc(mechTokenlen + 1, GFP_KERNEL); - if (!conn->mechToken) { - ksmbd_err("memory allocation error\n"); - return 0; - } - - memcpy(conn->mechToken, ctx.pointer, mechTokenlen); - conn->mechToken[mechTokenlen] = '\0'; - - return 1; + return asn1_ber_decoder(&spnego_negtokeninit_decoder, conn, + security_blob, length); } int ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, - struct ksmbd_conn *conn) + struct ksmbd_conn *conn) { - struct asn1_ctx ctx; - unsigned char *end; - unsigned int cls, con, tag, mechTokenlen; - - ksmbd_debug(AUTH, "Received Auth SecBlob: length %d\n", length); - - asn1_open(&ctx, security_blob, length); - - /* GSSAPI header */ - if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { - ksmbd_debug(AUTH, "Error decoding negTokenInit header\n"); - return 0; - } else if ((cls != ASN1_CTX) || (con != ASN1_CON) - || (tag != ASN1_BOL)) { - ksmbd_debug(AUTH, "cls = %d con = %d tag = %d\n", cls, con, - tag); - return 0; - } - - /* SPNEGO */ - if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { - ksmbd_debug(AUTH, "Error decoding negTokenInit\n"); - return 0; - } else if ((cls != ASN1_UNI) || (con != ASN1_CON) - || (tag != ASN1_SEQ)) { - ksmbd_debug(AUTH, - "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", - cls, con, tag, end, *end); - return 0; - } - - /* negTokenTarg */ - if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { - ksmbd_debug(AUTH, "Error decoding negTokenInit\n"); - return 0; - } else if ((cls != ASN1_CTX) || (con != ASN1_CON) - || (tag != ASN1_INT)) { - ksmbd_debug(AUTH, - "cls = %d con = %d tag = %d end = %p (%d) exit 1\n", - cls, con, tag, end, *end); - return 0; - } - - /* negTokenTarg */ - if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { - ksmbd_debug(AUTH, "Error decoding negTokenInit\n"); - return 0; - } else if ((cls != ASN1_UNI) || (con != ASN1_PRI) - || (tag != ASN1_OTS)) { - ksmbd_debug(AUTH, - "cls = %d con = %d tag = %d end = %p (%d) exit 1\n", - cls, con, tag, end, *end); - return 0; - } - - mechTokenlen = ctx.end - ctx.pointer; - conn->mechToken = kmalloc(mechTokenlen + 1, GFP_KERNEL); - if (!conn->mechToken) { - ksmbd_err("memory allocation error\n"); - return 0; - } - - memcpy(conn->mechToken, ctx.pointer, mechTokenlen); - conn->mechToken[mechTokenlen] = '\0'; - - return 1; + return asn1_ber_decoder(&spnego_negtokentarg_decoder, conn, + security_blob, length); } static int compute_asn_hdr_len_bytes(int len) @@ -700,3 +261,92 @@ int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, *buflen = total_len; return 0; } + +int gssapi_this_mech(void *context, size_t hdrlen, + unsigned char tag, const void *value, size_t vlen) +{ + unsigned long *oid; + size_t oidlen; + int err = 0; + + if (!asn1_oid_decode(value, vlen, &oid, &oidlen)) { + err = -EBADMSG; + goto out; + } + + if (!oid_eq(oid, oidlen, SPNEGO_OID, SPNEGO_OID_LEN)) + err = -EBADMSG; + kfree(oid); +out: + if (err) { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); + } + return err; +} + +int neg_token_init_mech_type(void *context, size_t hdrlen, + unsigned char tag, const void *value, size_t vlen) +{ + struct ksmbd_conn *conn = context; + unsigned long *oid; + size_t oidlen; + int mech_type; + + if (!asn1_oid_decode(value, vlen, &oid, &oidlen)) { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); + return -EBADMSG; + } + + if (oid_eq(oid, oidlen, NTLMSSP_OID, NTLMSSP_OID_LEN)) + mech_type = KSMBD_AUTH_NTLMSSP; + else if (oid_eq(oid, oidlen, MSKRB5_OID, MSKRB5_OID_LEN)) + mech_type = KSMBD_AUTH_MSKRB5; + else if (oid_eq(oid, oidlen, KRB5_OID, KRB5_OID_LEN)) + mech_type = KSMBD_AUTH_KRB5; + else if (oid_eq(oid, oidlen, KRB5U2U_OID, KRB5U2U_OID_LEN)) + mech_type = KSMBD_AUTH_KRB5U2U; + else + goto out; + + conn->auth_mechs |= mech_type; + if (conn->preferred_auth_mech == 0) + conn->preferred_auth_mech = mech_type; + +out: + kfree(oid); + return 0; +} + +int neg_token_init_mech_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, size_t vlen) +{ + struct ksmbd_conn *conn = context; + + conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); + if (!conn->mechToken) + return -ENOMEM; + + memcpy(conn->mechToken, value, vlen); + conn->mechToken[vlen] = '\0'; + return 0; +} + +int neg_token_targ_resp_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, size_t vlen) +{ + struct ksmbd_conn *conn = context; + + conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); + if (!conn->mechToken) + return -ENOMEM; + + memcpy(conn->mechToken, value, vlen); + conn->mechToken[vlen] = '\0'; + return 0; +} diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 18de8a763209..92b5020ae778 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -1194,8 +1194,8 @@ static int decode_negotiation_token(struct ksmbd_work *work, req = work->request_buf; sz = le16_to_cpu(req->SecurityBufferLength); - if (!ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { - if (!ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { + if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { + if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { conn->auth_mechs |= KSMBD_AUTH_NTLMSSP; conn->preferred_auth_mech = KSMBD_AUTH_NTLMSSP; conn->use_spnego = false; diff --git a/fs/cifsd/spnego_negtokeninit.asn1 b/fs/cifsd/spnego_negtokeninit.asn1 new file mode 100644 index 000000000000..1b153cb6a39e --- /dev/null +++ b/fs/cifsd/spnego_negtokeninit.asn1 @@ -0,0 +1,43 @@ +GSSAPI ::= + [APPLICATION 0] IMPLICIT SEQUENCE { + thisMech + OBJECT IDENTIFIER ({gssapi_this_mech}), + negotiationToken + NegotiationToken + } + +MechType ::= OBJECT IDENTIFIER ({neg_token_init_mech_type}) + +MechTypeList ::= SEQUENCE OF MechType + +NegTokenInit ::= + SEQUENCE { + mechTypes + [0] MechTypeList, + reqFlags + [1] BIT STRING OPTIONAL, + mechToken + [2] OCTET STRING OPTIONAL ({neg_token_init_mech_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } + +NegTokenTarg ::= + SEQUENCE { + negResult + [0] ENUMERATED OPTIONAL, + supportedMech + [1] OBJECT IDENTIFIER OPTIONAL, + responseToken + [2] OCTET STRING OPTIONAL ({neg_token_targ_resp_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } + +NegotiationToken ::= + CHOICE { + negTokenInit + [0] NegTokenInit, + negTokenTarg + [1] ANY + } diff --git a/fs/cifsd/spnego_negtokentarg.asn1 b/fs/cifsd/spnego_negtokentarg.asn1 new file mode 100644 index 000000000000..8324bcd1bbd7 --- /dev/null +++ b/fs/cifsd/spnego_negtokentarg.asn1 @@ -0,0 +1,19 @@ +GSSAPI ::= + CHOICE { + negTokenInit + [0] ANY, + negTokenTarg + [1] NegTokenTarg + } + +NegTokenTarg ::= + SEQUENCE { + negResult + [0] ENUMERATED OPTIONAL, + supportedMech + [1] OBJECT IDENTIFIER OPTIONAL, + responseToken + [2] OCTET STRING OPTIONAL ({neg_token_targ_resp_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } From 63c454f8392832a770d9cfcf9baa1733959b71e3 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 20 Apr 2021 14:24:28 +0900 Subject: [PATCH 074/417] cifsd: fix WARNING: Possible unnecessary 'out of memory' message WARNING: Possible unnecessary 'out of memory' message 902: FILE: fs/cifsd/smb2pdu.c:569: + if (!work->response_buf) { + ksmbd_err("Failed to allocate %zu bytes buffer\n", sz); Fix a warning from checkpatch.pl. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 92b5020ae778..a18792ce96be 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -565,10 +565,8 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) else work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); - if (!work->response_buf) { - ksmbd_err("Failed to allocate %zu bytes buffer\n", sz); + if (!work->response_buf) return -ENOMEM; - } work->response_sz = sz; return 0; From 3d47e54623897020e996b952bdf3ed9df447b5bf Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 20 Apr 2021 14:25:35 +0900 Subject: [PATCH 075/417] cifsd: fix WARNING: Too many leading tabs WARNING: Too many leading tabs - consider code refactoring 3066: FILE: fs/cifsd/smb2pdu.c:2733: + if (fattr.cf_dacls) Fix a warning from checkpatch.pl. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index a18792ce96be..18576148f530 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -2250,6 +2250,19 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work, return rc; } + +static void ksmbd_acls_fattr(struct smb_fattr *fattr, struct inode *inode) +{ + fattr->cf_uid = inode->i_uid; + fattr->cf_gid = inode->i_gid; + fattr->cf_mode = inode->i_mode; + fattr->cf_dacls = NULL; + + fattr->cf_acls = ksmbd_vfs_get_acl(inode, ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + fattr->cf_dacls = ksmbd_vfs_get_acl(inode, ACL_TYPE_DEFAULT); +} + /** * smb2_open() - handler for smb file open request * @work: smb work containing request buffer @@ -2734,23 +2747,13 @@ int smb2_open(struct ksmbd_work *work) KSMBD_SHARE_FLAG_ACL_XATTR)) { struct smb_fattr fattr; struct smb_ntsd *pntsd; - int pntsd_size, ace_num; + int pntsd_size, ace_num = 0; - fattr.cf_uid = inode->i_uid; - fattr.cf_gid = inode->i_gid; - fattr.cf_mode = inode->i_mode; - fattr.cf_dacls = NULL; - ace_num = 0; - - fattr.cf_acls = ksmbd_vfs_get_acl(inode, ACL_TYPE_ACCESS); + ksmbd_acls_fattr(&fattr, inode); if (fattr.cf_acls) ace_num = fattr.cf_acls->a_count; - if (S_ISDIR(inode->i_mode)) { - fattr.cf_dacls = - ksmbd_vfs_get_acl(inode, ACL_TYPE_DEFAULT); - if (fattr.cf_dacls) - ace_num += fattr.cf_dacls->a_count; - } + if (fattr.cf_dacls) + ace_num += fattr.cf_dacls->a_count; pntsd = kmalloc(sizeof(struct smb_ntsd) + sizeof(struct smb_sid) * 3 + @@ -2768,6 +2771,7 @@ int smb2_open(struct ksmbd_work *work) rc = ksmbd_vfs_set_sd_xattr(conn, path.dentry, pntsd, pntsd_size); + kfree(pntsd); if (rc) ksmbd_err("failed to store ntacl in xattr : %d\n", rc); @@ -4847,14 +4851,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, return -ENOENT; inode = FP_INODE(fp); - fattr.cf_uid = inode->i_uid; - fattr.cf_gid = inode->i_gid; - fattr.cf_mode = inode->i_mode; - fattr.cf_dacls = NULL; - - fattr.cf_acls = ksmbd_vfs_get_acl(inode, ACL_TYPE_ACCESS); - if (S_ISDIR(inode->i_mode)) - fattr.cf_dacls = ksmbd_vfs_get_acl(inode, ACL_TYPE_DEFAULT); + ksmbd_acls_fattr(&fattr, inode); if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) From 5ce071659302aa8d0eed18bfa289c7dfaf459b63 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 21 Apr 2021 12:35:38 +0900 Subject: [PATCH 076/417] cifsd: fix build break from asn1 build break from asn1 happened on some environment. CHECK /home/smfrench/smb3-kernel/fs/cifsd/smb2misc.c CC [M] /home/smfrench/smb3-kernel/fs/cifsd/asn1.o /home/smfrench/smb3-kernel/fs/cifsd/asn1.c:21:10: fatal error: spnego_negtokeninit.asn1.h: No such file or directory 21 | #include "spnego_negtokeninit.asn1.h" | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ compilation terminated. make[1]: *** [scripts/Makefile.build:271: /home/smfrench/smb3-kernel/fs/cifsd/asn1.o] Error 1 make: *** [Makefile:1857: /home/smfrench/smb3-kernel/fs/cifsd] Error 2 make: Leaving directory '/usr/src/linux-headers-5.12.0-051200rc8-generic' This patch fix that asn1 compiler build *.asn1 file before compiling asn.c Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/cifsd/Makefile b/fs/cifsd/Makefile index 05d32264fa8c..ccacb798a932 100644 --- a/fs/cifsd/Makefile +++ b/fs/cifsd/Makefile @@ -4,6 +4,9 @@ # obj-$(CONFIG_SMB_SERVER) += ksmbd.o +$(obj)/spnego_negtokeninit.asn1.o: $(obj)/spnego_negtokeninit.asn1.c $(obj)/spnego_negtokeninit.asn1.h +$(obj)/spnego_negtokentarg.asn1.o: $(obj)/spnego_negtokentarg.asn1.c $(obj)/spnego_negtokentarg.asn1.h + ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o buffer_pool.o \ misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \ mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ From 5616015f548a9beda791d8d607e1b17ebdc1e09d Mon Sep 17 00:00:00 2001 From: kernel test robot Date: Wed, 12 May 2021 09:24:37 +0900 Subject: [PATCH 077/417] cifsd: fix boolreturn.cocci warnings fs/cifsd/smb2pdu.c:8098:8-9: WARNING: return of 0/1 in function 'smb2_is_sign_req' with return type bool Return statements in functions returning bool should use true/false instead of 1/0. Generated by: scripts/coccinelle/misc/boolreturn.cocci Reported-by: kernel test robot Signed-off-by: kernel test robot Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 18576148f530..e17ad2032fc7 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -7713,7 +7713,7 @@ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) command != SMB2_OPLOCK_BREAK_HE) return true; - return 0; + return false; } /** From 50bf80a553ccb5eca0bc2426e5a082eaf65cb602 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 14 May 2021 12:20:07 +0900 Subject: [PATCH 078/417] cifsd: fix xfstests generic/504 test failure If lock length in smb2 lock request from client is over flock max length size, lock length is changed to flock max length and don't return error response. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index e17ad2032fc7..3fd266a94996 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -6422,7 +6422,7 @@ int smb2_lock(struct ksmbd_work *work) int flags = 0; int cmd = 0; int err = 0, i; - u64 lock_length; + u64 lock_start, lock_length; struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp; int nolock = 0; LIST_HEAD(lock_list); @@ -6461,25 +6461,22 @@ int smb2_lock(struct ksmbd_work *work) cmd = smb2_set_flock_flags(flock, flags); - flock->fl_start = le64_to_cpu(lock_ele[i].Offset); - if (flock->fl_start > OFFSET_MAX) { + lock_start = le64_to_cpu(lock_ele[i].Offset); + lock_length = le64_to_cpu(lock_ele[i].Length); + if (lock_start > U64_MAX - lock_length) { ksmbd_err("Invalid lock range requested\n"); rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; goto out; } + if (lock_start > OFFSET_MAX) + flock->fl_start = OFFSET_MAX; + else + flock->fl_start = lock_start; + lock_length = le64_to_cpu(lock_ele[i].Length); - if (lock_length > 0) { - if (lock_length > OFFSET_MAX - flock->fl_start) { - ksmbd_debug(SMB, - "Invalid lock range requested\n"); - lock_length = OFFSET_MAX - flock->fl_start; - rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; - goto out; - } - } else { - lock_length = 0; - } + if (lock_length > OFFSET_MAX - flock->fl_start) + lock_length = OFFSET_MAX - flock->fl_start; flock->fl_end = flock->fl_start + lock_length; From 8602c3e2ceef5f50f5718e8442a8ea17530101b4 Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Tue, 18 May 2021 10:29:25 +0900 Subject: [PATCH 079/417] cifsd: Do not use 0 or 0xFFFFFFFF for TreeID Returning TreeID=0 is valid behaviour according to [MS-SMB2] 2.2.1.2: TreeId (4 bytes): Uniquely identifies the tree connect for the command. This MUST be 0 for the SMB2 TREE_CONNECT Request. The TreeId can be any unsigned 32-bit integer that is received from a previous SMB2 TREE_CONNECT Response. TreeId SHOULD be set to 0 for the following commands: [...] However, some client implementations reject it as invalid. Windows10 assigns ids starting from 1, and samba4 returns a random uint32_t which suggests there may be other clients that consider it is invalid behaviour. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/mgmt/ksmbd_ida.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/cifsd/mgmt/ksmbd_ida.c b/fs/cifsd/mgmt/ksmbd_ida.c index 3dbc27cb5385..54194d959a5e 100644 --- a/fs/cifsd/mgmt/ksmbd_ida.c +++ b/fs/cifsd/mgmt/ksmbd_ida.c @@ -14,9 +14,7 @@ int ksmbd_acquire_smb2_tid(struct ida *ida) { int id; - id = __acquire_id(ida, 0, 0); - if (id == 0xFFFF) - id = __acquire_id(ida, 0, 0); + id = __acquire_id(ida, 1, 0xFFFFFFFF); return id; } From eb817368f50c1cbe1bd07044124aad7db6330e3a Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 18 May 2021 10:37:59 +0900 Subject: [PATCH 080/417] cifsd: add support for FSCTL_DUPLICATE_EXTENTS_TO_FILE Add support for FSCTL_DUPLICATE_EXTENTS_TO_FILE in smb2 ioctl. Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 52 ++++++++++++++++++++++++++++++++++++++++++++- fs/cifsd/smb2pdu.h | 8 +++++++ fs/cifsd/smbfsctl.h | 1 + fs/cifsd/vfs.c | 2 +- fs/cifsd/vfs.h | 3 ++- 5 files changed, 63 insertions(+), 3 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 3fd266a94996..e5d3a5790a81 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -4622,7 +4622,8 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, FILE_PERSISTENT_ACLS | FILE_UNICODE_ON_DISK | FILE_CASE_PRESERVED_NAMES | - FILE_CASE_SENSITIVE_SEARCH); + FILE_CASE_SENSITIVE_SEARCH | + FILE_SUPPORTS_BLOCK_REFCOUNTING); info->Attributes |= cpu_to_le32(server_conf.share_fake_fscaps); @@ -7330,6 +7331,55 @@ int smb2_ioctl(struct ksmbd_work *work) nbytes = sizeof(struct reparse_data_buffer); break; } + case FSCTL_DUPLICATE_EXTENTS_TO_FILE: + { + struct ksmbd_file *fp_in, *fp_out = NULL; + struct duplicate_extents_to_file *dup_ext; + loff_t src_off, dst_off, length, cloned; + + dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; + + fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, + dup_ext->PersistentFileHandle); + if (!fp_in) { + ksmbd_err("not found file handle in duplicate extent to file\n"); + ret = -ENOENT; + goto out; + } + + fp_out = ksmbd_lookup_fd_fast(work, id); + if (!fp_out) { + ksmbd_err("not found fp\n"); + ret = -ENOENT; + goto dup_ext_out; + } + + src_off = le64_to_cpu(dup_ext->SourceFileOffset); + dst_off = le64_to_cpu(dup_ext->TargetFileOffset); + length = le64_to_cpu(dup_ext->ByteCount); + cloned = vfs_clone_file_range(fp_in->filp, src_off, fp_out->filp, + dst_off, length, 0); + if (cloned == -EXDEV || cloned == -EOPNOTSUPP) { + ret = -EOPNOTSUPP; + goto dup_ext_out; + } else if (cloned != length) { + cloned = ksmbd_vfs_copy_file_range(fp_in->filp, src_off, + fp_out->filp, dst_off, length); + if (cloned != length) { + if (cloned < 0) + ret = cloned; + else + ret = -EINVAL; + } + } + +dup_ext_out: + ksmbd_fd_put(work, fp_in); + ksmbd_fd_put(work, fp_out); + if (ret < 0) + goto out; + break; + } default: ksmbd_debug(SMB, "not implemented yet ioctl command 0x%x\n", cnt_code); diff --git a/fs/cifsd/smb2pdu.h b/fs/cifsd/smb2pdu.h index c5c32610aafe..1a8da2122b75 100644 --- a/fs/cifsd/smb2pdu.h +++ b/fs/cifsd/smb2pdu.h @@ -851,6 +851,14 @@ struct smb2_write_rsp { #define SMB2_0_IOCTL_IS_FSCTL 0x00000001 +struct duplicate_extents_to_file { + __u64 PersistentFileHandle; /* source file handle, opaque endianness */ + __u64 VolatileFileHandle; + __le64 SourceFileOffset; + __le64 TargetFileOffset; + __le64 ByteCount; /* Bytes to be copied */ +} __packed; + struct smb2_ioctl_req { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 57 */ diff --git a/fs/cifsd/smbfsctl.h b/fs/cifsd/smbfsctl.h index 908c4e68a479..b98418aae20c 100644 --- a/fs/cifsd/smbfsctl.h +++ b/fs/cifsd/smbfsctl.h @@ -64,6 +64,7 @@ #define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ #define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ #define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 #define FSCTL_SIS_LINK_FILES 0x0009C104 #define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ #define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 29f31db4e07e..cdbb844fddad 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -1789,7 +1789,7 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, return 0; } -static int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, +int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out, size_t len) { struct inode *inode_in = file_inode(file_in); diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index 0163be4297de..2d19e2bac33a 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -218,7 +218,8 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, struct srv_copychunk *chunks, unsigned int chunk_count, unsigned int *chunk_count_written, unsigned int *chunk_size_written, loff_t *total_size_written); - +int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, + struct file *file_out, loff_t pos_out, size_t len); ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, char **xattr_buf); From be29a3709b89374c6ae287b19dd651a8c0f10b8a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 20 May 2021 13:42:11 +0000 Subject: [PATCH 081/417] cifsd: fix build error without CONFIG_OID_REGISTRY Fix build error when CONFIG_OID_REGISTRY is not set: mips-linux-gnu-ld: fs/cifsd/asn1.o: in function `gssapi_this_mech': asn1.c:(.text+0xaa0): undefined reference to `sprint_oid' mips-linux-gnu-ld: fs/cifsd/asn1.o: in function `neg_token_init_mech_type': asn1.c:(.text+0xbec): undefined reference to `sprint_oid' Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifsd/Kconfig b/fs/cifsd/Kconfig index 5316b1035fbe..e6448b04f46e 100644 --- a/fs/cifsd/Kconfig +++ b/fs/cifsd/Kconfig @@ -18,6 +18,7 @@ config SMB_SERVER select CRYPTO_CCM select CRYPTO_GCM select ASN1 + select OID_REGISTRY default n help Choose Y here if you want to allow SMB3 compliant clients From cdd10398e71a1843ef99ed545bbb872b6cb9d249 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:22:37 +0900 Subject: [PATCH 082/417] cifsd: add goto fail in asn1_oid_decode() Add goto fail in asn1_oid_decode() to clean-up exception handling code. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/asn1.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/fs/cifsd/asn1.c b/fs/cifsd/asn1.c index aa6ea855c422..f2628dc3490f 100644 --- a/fs/cifsd/asn1.c +++ b/fs/cifsd/asn1.c @@ -74,11 +74,8 @@ static bool asn1_oid_decode(const unsigned char *value, size_t vlen, optr = *oid; - if (!asn1_subid_decode(&iptr, end, &subid)) { - kfree(*oid); - *oid = NULL; - return false; - } + if (!asn1_subid_decode(&iptr, end, &subid)) + goto fail; if (subid < 40) { optr[0] = 0; @@ -95,19 +92,18 @@ static bool asn1_oid_decode(const unsigned char *value, size_t vlen, optr += 2; while (iptr < end) { - if (++(*oidlen) > vlen) { - kfree(*oid); - *oid = NULL; - return false; - } + if (++(*oidlen) > vlen) + goto fail; - if (!asn1_subid_decode(&iptr, end, optr++)) { - kfree(*oid); - *oid = NULL; - return false; - } + if (!asn1_subid_decode(&iptr, end, optr++)) + goto fail; } return true; + +fail: + kfree(*oid); + *oid = NULL; + return false; } static bool From 3566a2b0f73a46eb93beafd70b8386a3b59d5acb Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:23:55 +0900 Subject: [PATCH 083/417] cifsd: use memcmp instead of for loop check in oid_eq() Use memcmp instead of for loop check in oid_eq(). Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/asn1.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/fs/cifsd/asn1.c b/fs/cifsd/asn1.c index f2628dc3490f..a7db37eef2a9 100644 --- a/fs/cifsd/asn1.c +++ b/fs/cifsd/asn1.c @@ -110,16 +110,10 @@ static bool oid_eq(unsigned long *oid1, unsigned int oid1len, unsigned long *oid2, unsigned int oid2len) { - unsigned int i; - if (oid1len != oid2len) return false; - for (i = 0; i < oid1len; i++) { - if (oid1[i] != oid2[i]) - return false; - } - return true; + return memcmp(oid1, oid2, oid1len) == 0; } int From 8bae4419ce636f6f8414193a206a2dc2e6dd37db Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:24:39 +0900 Subject: [PATCH 084/417] cifsd: add goto fail in neg_token_init_mech_type() Add goto fail in neg_token_init_mech_type(). Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/asn1.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/fs/cifsd/asn1.c b/fs/cifsd/asn1.c index a7db37eef2a9..769fa328c020 100644 --- a/fs/cifsd/asn1.c +++ b/fs/cifsd/asn1.c @@ -284,14 +284,10 @@ int neg_token_init_mech_type(void *context, size_t hdrlen, unsigned long *oid; size_t oidlen; int mech_type; + char buf[50]; - if (!asn1_oid_decode(value, vlen, &oid, &oidlen)) { - char buf[50]; - - sprint_oid(value, vlen, buf, sizeof(buf)); - ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); - return -EBADMSG; - } + if (!asn1_oid_decode(value, vlen, &oid, &oidlen)) + goto fail; if (oid_eq(oid, oidlen, NTLMSSP_OID, NTLMSSP_OID_LEN)) mech_type = KSMBD_AUTH_NTLMSSP; @@ -302,15 +298,20 @@ int neg_token_init_mech_type(void *context, size_t hdrlen, else if (oid_eq(oid, oidlen, KRB5U2U_OID, KRB5U2U_OID_LEN)) mech_type = KSMBD_AUTH_KRB5U2U; else - goto out; + goto fail; conn->auth_mechs |= mech_type; if (conn->preferred_auth_mech == 0) conn->preferred_auth_mech = mech_type; -out: kfree(oid); return 0; + +fail: + kfree(oid); + sprint_oid(value, vlen, buf, sizeof(buf)); + ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); + return -EBADMSG; } int neg_token_init_mech_token(void *context, size_t hdrlen, From 94096702376ecb99c86cbee9dd95fc3675231b8a Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:25:40 +0900 Subject: [PATCH 085/417] cifsd: move fips_enabled check before the str_to_key() Move fips_enabled check before the str_to_key(). Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 6b90aac86fcc..9263c9ce2dd2 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -92,14 +92,13 @@ smbhash(unsigned char *out, const unsigned char *in, unsigned char *key) unsigned char key2[8]; struct des_ctx ctx; - str_to_key(key, key2); - if (fips_enabled) { ksmbd_debug(AUTH, "FIPS compliance enabled: DES not permitted\n"); return -ENOENT; } + str_to_key(key, key2); des_expand_key(&ctx, key2, DES_KEY_SIZE); des_encrypt(&ctx, out, in); memzero_explicit(&ctx, sizeof(ctx)); From fd43cbbe0af5e528ec7e1f85e6e942d1b77ff781 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:26:33 +0900 Subject: [PATCH 086/417] cifsd: just return smbhash() instead of using rc return value Just return smbhash() instead of using rc return value. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 9263c9ce2dd2..ab698093f728 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -115,8 +115,7 @@ static int ksmbd_enc_p24(unsigned char *p21, const unsigned char *c8, unsigned c rc = smbhash(p24 + 8, c8, p21 + 7); if (rc) return rc; - rc = smbhash(p24 + 16, c8, p21 + 14); - return rc; + return smbhash(p24 + 16, c8, p21 + 14); } /* produce a md4 message digest from data of length n bytes */ From 7e38ea254c8274ea25ffc28df65ac2683c5f8a72 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:27:11 +0900 Subject: [PATCH 087/417] cifsd: move ret check before the out label Move ret check before the out label. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index ab698093f728..092db15e4234 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -311,9 +311,9 @@ static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, } ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash); -out: if (ret) ksmbd_debug(AUTH, "Could not generate md5 hash\n"); +out: kfree(uniname); kfree(domain); ksmbd_release_crypto_ctx(ctx); From 4a6b02282632f0b4e88a85f26266f7674e0ce288 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:28:09 +0900 Subject: [PATCH 088/417] cifsd: simplify error handling in ksmbd_auth_ntlm() simplify error handling in ksmbd_auth_ntlm(). Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 092db15e4234..7771429f55a4 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -351,12 +351,11 @@ int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf) if (strncmp(pw_buf, key, CIFS_AUTH_RESP_SIZE) != 0) { ksmbd_debug(AUTH, "ntlmv1 authentication failed\n"); - rc = -EINVAL; - } else { - ksmbd_debug(AUTH, "ntlmv1 authentication pass\n"); + return -EINVAL; } - return rc; + ksmbd_debug(AUTH, "ntlmv1 authentication pass\n"); + return 0; } /** From 192cc732c65a7c22da77cf21baba5e8a3efdea29 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:28:48 +0900 Subject: [PATCH 089/417] cifsd: remove unneeded type casting Remove unneeded type casting. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 7771429f55a4..ed32052fbf93 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -410,8 +410,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, } memcpy(construct, sess->ntlmssp.cryptkey, CIFS_CRYPTO_KEY_SIZE); - memcpy(construct + CIFS_CRYPTO_KEY_SIZE, - (char *)(&ntlmv2->blob_signature), blen); + memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen); rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len); if (rc) { From b72802aa77dc2729b848057e96b6a2126182f75e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:29:24 +0900 Subject: [PATCH 090/417] cifsd: set error return value for memcmp() difference Set error return value for memcmp() difference. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index ed32052fbf93..adfb3b33f2e5 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -430,7 +430,8 @@ int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, goto out; } - rc = memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE); + if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0) + rc = -EINVAL; out: ksmbd_release_crypto_ctx(ctx); kfree(construct); @@ -469,7 +470,8 @@ static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess, char *client_nonce, goto out; } - rc = memcmp(ntlm_resp, key, CIFS_AUTH_RESP_SIZE); + if (memcmp(ntlm_resp, key, CIFS_AUTH_RESP_SIZE) != 0) + rc = -EINVAL; out: return rc; } From 876edcc4cffd26f83eae591e906384dab7f25a51 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:30:04 +0900 Subject: [PATCH 091/417] cifsd: return zero in always success case Return zero in always success case. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index adfb3b33f2e5..a89de24aa576 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -943,7 +943,7 @@ static int generate_smb3signingkey(struct ksmbd_session *sess, SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); ksmbd_debug(AUTH, "Signing Key %*ph\n", SMB3_SIGN_KEY_SIZE, key); - return rc; + return 0; } int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess) @@ -1011,7 +1011,7 @@ static int generate_smb3encryptionkey(struct ksmbd_session *sess, ksmbd_debug(AUTH, "ServerOut Key %*ph\n", SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); } - return rc; + return 0; } int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess) From 522dcc76269fcc27a3a0128ca7699270fae61b60 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:30:50 +0900 Subject: [PATCH 092/417] cifsd: never return 1 on failure Never return 1 on failure. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index a89de24aa576..7e56966f87d4 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -1144,7 +1144,7 @@ static int ksmbd_get_encryption_key(struct ksmbd_conn *conn, __u64 ses_id, sess = ksmbd_session_lookup(conn, ses_id); if (!sess) - return 1; + return -EINVAL; ses_enc_key = enc ? sess->smb3encryptionkey : sess->smb3decryptionkey; From 41a7848a01b3f4401b8b87815e643584b86895f2 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:31:37 +0900 Subject: [PATCH 093/417] cifsd: add the check if nvec is zero Dan Carpenter pointed out that memory can be corrupted when nvec is zero. This patch add the check to prevent it. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 7e56966f87d4..9f957c8c123c 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -1172,6 +1172,9 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; + if (!nvec) + return NULL; + for (i = 0; i < nvec - 1; i++) { unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; From 08591ccfdd4f237b3d931e0ebf05690b1ab91399 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:32:26 +0900 Subject: [PATCH 094/417] cifsd: len can never be negative in ksmbd_init_sg() Dan pointed out len can not be negative. This patch remove unneeded negative check in loop. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 9f957c8c123c..9af1b334be82 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -1207,7 +1207,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, for (j = 0; j < nr_entries[i]; j++) { unsigned int bytes = PAGE_SIZE - offset; - if (len <= 0) + if (!len) break; if (bytes > len) From 03f1c3d38887803266ec4d5a820b08b01b2766d8 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:34:37 +0900 Subject: [PATCH 095/417] cifsd: remove unneeded initialization of rc variable in ksmbd_crypt_message() Remove unneeded initialization of rc variable in ksmbd_crypt_message(). Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 9af1b334be82..711f8dec38e1 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -1236,7 +1236,7 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)iov[0].iov_base; unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; - int rc = 0; + int rc; struct scatterlist *sg; u8 sign[SMB2_SIGNATURE_SIZE] = {}; u8 key[SMB3_ENC_DEC_KEY_SIZE]; From 27aa646db0f0465c5abf8e5cd545e070e7f14120 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:35:26 +0900 Subject: [PATCH 096/417] cifsd: fix wrong return value in ksmbd_crypt_message() Change error return instead of returning always success return. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 711f8dec38e1..5a56dd65fa06 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -1253,7 +1253,7 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, key); if (rc) { ksmbd_err("Could not get %scryption key\n", enc ? "en" : "de"); - return 0; + return rc; } if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || From 73b8b08539423a888ed76b53401a6366e0e2af2b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:53:26 +0900 Subject: [PATCH 097/417] cifsd: change success handling to failure handling Change success handling to failure handling in ksmbd_crypt_message(). Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 5a56dd65fa06..b0a9e4591fa5 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -1331,9 +1331,13 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, rc = crypto_aead_encrypt(req); else rc = crypto_aead_decrypt(req); - if (!rc && enc) + if (rc) + goto free_iv; + + if (enc) memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); +free_iv: kfree(iv); free_sg: kfree(sg); From 533a45da1a8900267a667648450976bc334b71a8 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:54:25 +0900 Subject: [PATCH 098/417] cifsd: add default case in switch statment in alloc_shash_desc() Add default case in switch statment in alloc_shash_desc(). Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/crypto_ctx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/cifsd/crypto_ctx.c b/fs/cifsd/crypto_ctx.c index 1830ae1b5ed3..dbfe36ee9be1 100644 --- a/fs/cifsd/crypto_ctx.c +++ b/fs/cifsd/crypto_ctx.c @@ -88,6 +88,8 @@ static struct shash_desc *alloc_shash_desc(int id) case CRYPTO_SHASH_MD5: tfm = crypto_alloc_shash("md5", 0, 0); break; + default: + return NULL; } if (IS_ERR(tfm)) From 12fc704441ad86a0a29e60708490109954f097fa Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:55:35 +0900 Subject: [PATCH 099/417] cifsd: call kzalloc() directly instead of wrapper Call kzalloc() directly instead of wrapper function. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/crypto_ctx.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/cifsd/crypto_ctx.c b/fs/cifsd/crypto_ctx.c index dbfe36ee9be1..9685bf963702 100644 --- a/fs/cifsd/crypto_ctx.c +++ b/fs/cifsd/crypto_ctx.c @@ -104,11 +104,6 @@ static struct shash_desc *alloc_shash_desc(int id) return shash; } -static struct ksmbd_crypto_ctx *ctx_alloc(void) -{ - return kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); -} - static void ctx_free(struct ksmbd_crypto_ctx *ctx) { int i; @@ -145,7 +140,7 @@ static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) ctx_list.avail_ctx++; spin_unlock(&ctx_list.ctx_lock); - ctx = ctx_alloc(); + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); if (!ctx) { spin_lock(&ctx_list.ctx_lock); ctx_list.avail_ctx--; @@ -280,7 +275,7 @@ int ksmbd_crypto_create(void) init_waitqueue_head(&ctx_list.ctx_wait); ctx_list.avail_ctx = 1; - ctx = ctx_alloc(); + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; list_add(&ctx->list, &ctx_list.idle_ctx); From d3cd8c491559ca9eb7ce81242df3b3927466e6d9 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 15:56:18 +0900 Subject: [PATCH 100/417] cifsd: simplify error handling in ksmbd_gen_preauth_integrity_hash() Simplify error handling in ksmbd_gen_preauth_integrity_hash(). Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index b0a9e4591fa5..1d4c4e6d28cd 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -1063,14 +1063,13 @@ int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, int msg_size = be32_to_cpu(rcv_hdr->smb2_buf_length); struct ksmbd_crypto_ctx *ctx = NULL; - if (conn->preauth_info->Preauth_HashId == - SMB2_PREAUTH_INTEGRITY_SHA512) { - ctx = ksmbd_crypto_ctx_find_sha512(); - if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha512 rc %d\n", rc); - goto out; - } - } else { + if (conn->preauth_info->Preauth_HashId != + SMB2_PREAUTH_INTEGRITY_SHA512) + return -EINVAL; + + ctx = ksmbd_crypto_ctx_find_sha512(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha512 rc %d\n", rc); goto out; } From 0e579cd17f8e9c2e70a68edb66a1457b2c6e9926 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 16:34:56 +0900 Subject: [PATCH 101/417] cifsd: return -ENOMEM about error from ksmbd_crypto_ctx_find_xxx calls Return -ENOMEM about error from ksmbd_crypto_ctx_find_xxx calls. And remove unneeded return value print in debug message. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 56 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 1d4c4e6d28cd..cc13d0eedd80 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -128,7 +128,7 @@ static int ksmbd_enc_md4(unsigned char *md4_hash, unsigned char *link_str, ctx = ksmbd_crypto_ctx_find_md4(); if (!ctx) { ksmbd_debug(AUTH, "Crypto md4 allocation error\n"); - return -EINVAL; + return -ENOMEM; } rc = crypto_shash_init(CRYPTO_MD4(ctx)); @@ -160,7 +160,7 @@ static int ksmbd_enc_update_sess_key(unsigned char *md5_hash, char *nonce, ctx = ksmbd_crypto_ctx_find_md5(); if (!ctx) { ksmbd_debug(AUTH, "Crypto md5 allocation error\n"); - return -EINVAL; + return -ENOMEM; } rc = crypto_shash_init(CRYPTO_MD5(ctx)); @@ -200,11 +200,13 @@ static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, char *hmac) { struct ksmbd_crypto_ctx *ctx; - int rc = -EINVAL; + int rc; ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) - goto out; + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), hash, @@ -244,7 +246,7 @@ out: static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, char *dname) { - int ret = -EINVAL, len; + int ret, len; wchar_t *domain = NULL; __le16 *uniname = NULL; struct ksmbd_crypto_ctx *ctx; @@ -252,7 +254,7 @@ static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, ctx = ksmbd_crypto_ctx_find_hmacmd5(); if (!ctx) { ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n"); - goto out; + return -ENOMEM; } ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), @@ -374,12 +376,12 @@ int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; struct ksmbd_crypto_ctx *ctx; char *construct = NULL; - int rc = -EINVAL, len; + int rc, len; ctx = ksmbd_crypto_ctx_find_hmacmd5(); if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5 rc %d\n", rc); - goto out; + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; } rc = calc_ntlmv2_hash(sess, ntlmv2_hash, domain_name); @@ -731,13 +733,12 @@ int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, int n_vec, char *sig) { struct ksmbd_crypto_ctx *ctx; - int rc = -EINVAL; - int i; + int rc, i; ctx = ksmbd_crypto_ctx_find_hmacsha256(); if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5 rc %d\n", rc); - goto out; + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; } rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), @@ -783,13 +784,12 @@ int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, int n_vec, char *sig) { struct ksmbd_crypto_ctx *ctx; - int rc = -EINVAL; - int i; + int rc, i; ctx = ksmbd_crypto_ctx_find_cmacaes(); if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc cmac rc %d\n", rc); - goto out; + ksmbd_debug(AUTH, "could not crypto alloc cmac\n"); + return -ENOMEM; } rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx), @@ -835,7 +835,7 @@ static int generate_key(struct ksmbd_session *sess, struct kvec label, __u8 i[4] = {0, 0, 0, 1}; __u8 L128[4] = {0, 0, 0, 128}; __u8 L256[4] = {0, 0, 1, 0}; - int rc = -EINVAL; + int rc; unsigned char prfhash[SMB2_HMACSHA256_SIZE]; unsigned char *hashptr = prfhash; struct ksmbd_crypto_ctx *ctx; @@ -845,8 +845,8 @@ static int generate_key(struct ksmbd_session *sess, struct kvec label, ctx = ksmbd_crypto_ctx_find_hmacsha256(); if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5 rc %d\n", rc); - goto smb3signkey_ret; + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; } rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), @@ -1057,7 +1057,7 @@ int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess) int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, __u8 *pi_hash) { - int rc = -1; + int rc; struct smb2_hdr *rcv_hdr = (struct smb2_hdr *)buf; char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; int msg_size = be32_to_cpu(rcv_hdr->smb2_buf_length); @@ -1069,8 +1069,8 @@ int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, ctx = ksmbd_crypto_ctx_find_sha512(); if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha512 rc %d\n", rc); - goto out; + ksmbd_debug(AUTH, "could not alloc sha512\n"); + return -ENOMEM; } rc = crypto_shash_init(CRYPTO_SHA512(ctx)); @@ -1104,13 +1104,13 @@ out: int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, __u8 *pi_hash) { - int rc = -1; + int rc; struct ksmbd_crypto_ctx *ctx = NULL; ctx = ksmbd_crypto_ctx_find_sha256(); if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha256 rc %d\n", rc); - goto out; + ksmbd_debug(AUTH, "could not alloc sha256\n"); + return -ENOMEM; } rc = crypto_shash_init(CRYPTO_SHA256(ctx)); @@ -1262,7 +1262,7 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, ctx = ksmbd_crypto_ctx_find_ccm(); if (!ctx) { ksmbd_err("crypto alloc failed\n"); - return -EINVAL; + return -ENOMEM; } if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || From 70478059762688d9a975477cf6903cc170901c4c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 16:36:15 +0900 Subject: [PATCH 102/417] cifsd: alignment match open parenthesis Alignment match open parenthesis. Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index cc13d0eedd80..8c80f918c8d7 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -596,7 +596,7 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, if (cflags & NTLMSSP_NEGOTIATE_SIGN) { flags |= NTLMSSP_NEGOTIATE_SIGN; flags |= cflags & (NTLMSSP_NEGOTIATE_128 | - NTLMSSP_NEGOTIATE_56); + NTLMSSP_NEGOTIATE_56); } if (cflags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) @@ -641,7 +641,7 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, chgblob->TargetInfoArray.Length = 0; /* Add target info list for NetBIOS/DNS settings */ for (type = NTLMSSP_AV_NB_COMPUTER_NAME; - type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) { + type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) { tinfo->Type = cpu_to_le16(type); tinfo->Length = cpu_to_le16(len); memcpy(tinfo->Content, name, len); From a2d0b5034a5fff029ec1be08d3264f8407d47602 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 16:37:05 +0900 Subject: [PATCH 103/417] cifsd: add the check to prevent potential overflow with smb_strtoUTF16() and UNICODE_LEN() Add the check to prevent potential overflow with smb_strtoUTF16() and UNICODE_LEN(). Reviewed-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 50 ++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 8c80f918c8d7..f742870a930b 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -246,7 +246,7 @@ out: static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, char *dname) { - int ret, len; + int ret, len, conv_len; wchar_t *domain = NULL; __le16 *uniname = NULL; struct ksmbd_crypto_ctx *ctx; @@ -279,15 +279,17 @@ static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, goto out; } - if (len) { - len = smb_strtoUTF16(uniname, user_name(sess->user), len, + conv_len = smb_strtoUTF16(uniname, user_name(sess->user), len, sess->conn->local_nls); - UniStrupr(uniname); + if (conv_len < 0 || conv_len > len) { + ret = -EINVAL; + goto out; } + UniStrupr(uniname); ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), (char *)uniname, - UNICODE_LEN(len)); + UNICODE_LEN(conv_len)); if (ret) { ksmbd_debug(AUTH, "Could not update with user\n"); goto out; @@ -301,12 +303,16 @@ static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, goto out; } - len = smb_strtoUTF16((__le16 *)domain, dname, len, + conv_len = smb_strtoUTF16((__le16 *)domain, dname, len, sess->conn->local_nls); + if (conv_len < 0 || conv_len > len) { + ret = -EINVAL; + goto out; + } ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), (char *)domain, - UNICODE_LEN(len)); + UNICODE_LEN(conv_len)); if (ret) { ksmbd_debug(AUTH, "Could not update with domain\n"); goto out; @@ -584,6 +590,7 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, wchar_t *name; __u8 *target_name; unsigned int len, flags, blob_off, blob_len, type, target_info_len = 0; + unsigned int uni_len, conv_len; int cflags = sess->ntlmssp.client_flags; memcpy(chgblob->Signature, NTLMSSP_SIGNATURE, 8); @@ -611,19 +618,24 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, chgblob->NegotiateFlags = cpu_to_le32(flags); len = strlen(ksmbd_netbios_name()); - name = kmalloc(2 + (len * 2), GFP_KERNEL); + name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL); if (!name) return -ENOMEM; - len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, + conv_len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, sess->conn->local_nls); - len = UNICODE_LEN(len); + if (conv_len < 0 || conv_len > len) { + kfree(name); + return -EINVAL; + } + + uni_len = UNICODE_LEN(conv_len); blob_off = sizeof(struct challenge_message); - blob_len = blob_off + len; + blob_len = blob_off + uni_len; - chgblob->TargetName.Length = cpu_to_le16(len); - chgblob->TargetName.MaximumLength = cpu_to_le16(len); + chgblob->TargetName.Length = cpu_to_le16(uni_len); + chgblob->TargetName.MaximumLength = cpu_to_le16(uni_len); chgblob->TargetName.BufferOffset = cpu_to_le32(blob_off); /* Initialize random conn challenge */ @@ -635,18 +647,18 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, chgblob->TargetInfoArray.BufferOffset = cpu_to_le32(blob_len); target_name = (__u8 *)chgblob + blob_off; - memcpy(target_name, name, len); - tinfo = (struct target_info *)(target_name + len); + memcpy(target_name, name, uni_len); + tinfo = (struct target_info *)(target_name + uni_len); chgblob->TargetInfoArray.Length = 0; /* Add target info list for NetBIOS/DNS settings */ for (type = NTLMSSP_AV_NB_COMPUTER_NAME; type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) { tinfo->Type = cpu_to_le16(type); - tinfo->Length = cpu_to_le16(len); - memcpy(tinfo->Content, name, len); - tinfo = (struct target_info *)((char *)tinfo + 4 + len); - target_info_len += 4 + len; + tinfo->Length = cpu_to_le16(uni_len); + memcpy(tinfo->Content, name, uni_len); + tinfo = (struct target_info *)((char *)tinfo + 4 + uni_len); + target_info_len += 4 + uni_len; } /* Add terminator subblock */ From a2d6321b459aee5f2b4380271a79668c24165c56 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 16:40:39 +0900 Subject: [PATCH 104/417] cifsd: braces {} should be used on all arms of this statement Fix "CHECK: braces {} should be used on all arms of this statement" from checkpatch.pl --strict. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/asn1.c | 8 ++++---- fs/cifsd/vfs.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/cifsd/asn1.c b/fs/cifsd/asn1.c index 769fa328c020..479a9c1fcbbe 100644 --- a/fs/cifsd/asn1.c +++ b/fs/cifsd/asn1.c @@ -160,9 +160,9 @@ static void encode_asn_tag(char *buf, /* insert tag */ buf[index++] = tag; - if (!hdr_len) + if (!hdr_len) { buf[index++] = len; - else { + } else { buf[index++] = 0x80 | hdr_len; for (i = hdr_len - 1; i >= 0; i--) buf[index++] = (len >> (i * 8)) & 0xFF; @@ -172,9 +172,9 @@ static void encode_asn_tag(char *buf, len = len - (index - *ofs); buf[index++] = seq; - if (!hdr_len) + if (!hdr_len) { buf[index++] = len; - else { + } else { buf[index++] = 0x80 | hdr_len; for (i = hdr_len - 1; i >= 0; i--) buf[index++] = (len >> (i * 8)) & 0xFF; diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index cdbb844fddad..e1295b72c410 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -212,9 +212,9 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) mode |= S_IFDIR; err = vfs_mkdir(&init_user_ns, d_inode(path.dentry), dentry, mode); - if (err) + if (err) { goto out; - else if (d_unhashed(dentry)) { + } else if (d_unhashed(dentry)) { struct dentry *d; d = lookup_one_len(dentry->d_name.name, From c8ed11522b4acbe378687b6388ceffd72e72d736 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 16:42:12 +0900 Subject: [PATCH 105/417] cifsd: spaces preferred around that '/' Fix "CHECK: paces preferred around that '/' from checkpatch.pl --strict. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/asn1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/asn1.c b/fs/cifsd/asn1.c index 479a9c1fcbbe..846f4e73abbf 100644 --- a/fs/cifsd/asn1.c +++ b/fs/cifsd/asn1.c @@ -65,7 +65,7 @@ static bool asn1_oid_decode(const unsigned char *value, size_t vlen, unsigned long subid; vlen += 1; - if (vlen < 2 || vlen > UINT_MAX/sizeof(unsigned long)) + if (vlen < 2 || vlen > UINT_MAX / sizeof(unsigned long)) return false; *oid = kmalloc(vlen * sizeof(unsigned long), GFP_KERNEL); From 2f5205c2377c0a3c501a33092c91a45e074bfc04 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 16:43:31 +0900 Subject: [PATCH 106/417] cifsd: don't use multiple blank lines don't use multiple blank lines. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/nterr.h | 2 -- fs/cifsd/smb2pdu.c | 1 - 2 files changed, 3 deletions(-) diff --git a/fs/cifsd/nterr.h b/fs/cifsd/nterr.h index a66100e74741..2f358f88a018 100644 --- a/fs/cifsd/nterr.h +++ b/fs/cifsd/nterr.h @@ -9,8 +9,6 @@ * Copyright (C) Paul Ashton 1998-2000 */ - - #ifndef _NTERR_H #define _NTERR_H diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index e5d3a5790a81..c0c0cf600ce7 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -2250,7 +2250,6 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work, return rc; } - static void ksmbd_acls_fattr(struct smb_fattr *fattr, struct inode *inode) { fattr->cf_uid = inode->i_uid; From 10268f7d5755f42e50e862505e7bac992d284546 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 16:44:21 +0900 Subject: [PATCH 107/417] cifsd: No space is necessary after a cast No space is necessary after a cast. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index c0c0cf600ce7..bdaa69facee5 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -976,7 +976,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, break; ctxt_size = decode_compress_ctxt(conn, - (struct smb2_compression_ctx *) pneg_ctxt); + (struct smb2_compression_ctx *)pneg_ctxt); pneg_ctxt += DIV_ROUND_UP(ctxt_size, 8) * 8; } else if (*ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { ksmbd_debug(SMB, From fe30ea69ff81f99607b0e4002ef9ae12e4694b31 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 16:45:24 +0900 Subject: [PATCH 108/417] cifsd: Blank lines aren't necessary after an open brace '{' Blank lines aren't necessary after an open brace '{'. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index bdaa69facee5..16290ad710fa 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -2388,7 +2388,6 @@ int smb2_open(struct ksmbd_work *work) rc = -EINVAL; goto err_out1; } else { - if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && req->CreateOptions & FILE_RANDOM_ACCESS_LE) req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); From 070fb21e5912b6aa22509083aaca030d1f4e7d57 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 17:57:12 +0900 Subject: [PATCH 109/417] cifsd: Alignment should match open parenthesis Fix warnings "Alignment should match open parenthesis" from checkpatch.pl --strict. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/asn1.c | 38 +- fs/cifsd/asn1.h | 18 +- fs/cifsd/auth.c | 115 +++---- fs/cifsd/auth.h | 57 +-- fs/cifsd/buffer_pool.c | 3 +- fs/cifsd/buffer_pool.h | 3 - fs/cifsd/connection.c | 24 +- fs/cifsd/connection.h | 22 +- fs/cifsd/crypto_ctx.c | 4 +- fs/cifsd/crypto_ctx.h | 3 - fs/cifsd/ksmbd_work.c | 8 +- fs/cifsd/misc.c | 12 +- fs/cifsd/misc.h | 9 - fs/cifsd/ndr.c | 7 +- fs/cifsd/ndr.h | 3 +- fs/cifsd/oplock.c | 108 +++--- fs/cifsd/oplock.h | 13 +- fs/cifsd/server.c | 32 +- fs/cifsd/smb2misc.c | 42 +-- fs/cifsd/smb2pdu.c | 707 +++++++++++++++++++------------------- fs/cifsd/smb_common.c | 37 +- fs/cifsd/smbacl.c | 106 +++--- fs/cifsd/transport_ipc.c | 21 +- fs/cifsd/transport_ipc.h | 13 +- fs/cifsd/transport_rdma.c | 358 +++++++++---------- fs/cifsd/transport_tcp.c | 26 +- fs/cifsd/unicode.c | 17 +- fs/cifsd/unicode.h | 9 +- fs/cifsd/vfs.c | 196 ++++++----- fs/cifsd/vfs.h | 65 ++-- fs/cifsd/vfs_cache.c | 14 +- fs/cifsd/vfs_cache.h | 2 +- 32 files changed, 1021 insertions(+), 1071 deletions(-) diff --git a/fs/cifsd/asn1.c b/fs/cifsd/asn1.c index 846f4e73abbf..1be3072fee1a 100644 --- a/fs/cifsd/asn1.c +++ b/fs/cifsd/asn1.c @@ -37,7 +37,7 @@ static char NTLMSSP_OID_STR[NTLMSSP_OID_LEN] = { 0x2b, 0x06, 0x01, 0x04, 0x01, static bool asn1_subid_decode(const unsigned char **begin, const unsigned char *end, - unsigned long *subid) + unsigned long *subid) { const unsigned char *ptr = *begin; unsigned char ch; @@ -58,7 +58,7 @@ asn1_subid_decode(const unsigned char **begin, const unsigned char *end, } static bool asn1_oid_decode(const unsigned char *value, size_t vlen, - unsigned long **oid, size_t *oidlen) + unsigned long **oid, size_t *oidlen) { const unsigned char *iptr = value, *end = value + vlen; unsigned long *optr; @@ -106,9 +106,8 @@ fail: return false; } -static bool -oid_eq(unsigned long *oid1, unsigned int oid1len, - unsigned long *oid2, unsigned int oid2len) +static bool oid_eq(unsigned long *oid1, unsigned int oid1len, + unsigned long *oid2, unsigned int oid2len) { if (oid1len != oid2len) return false; @@ -118,7 +117,7 @@ oid_eq(unsigned long *oid1, unsigned int oid1len, int ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, - struct ksmbd_conn *conn) + struct ksmbd_conn *conn) { return asn1_ber_decoder(&spnego_negtokeninit_decoder, conn, security_blob, length); @@ -126,7 +125,7 @@ ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, int ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, - struct ksmbd_conn *conn) + struct ksmbd_conn *conn) { return asn1_ber_decoder(&spnego_negtokentarg_decoder, conn, security_blob, length); @@ -146,10 +145,7 @@ static int compute_asn_hdr_len_bytes(int len) return 0; } -static void encode_asn_tag(char *buf, - unsigned int *ofs, - char tag, - char seq, +static void encode_asn_tag(char *buf, unsigned int *ofs, char tag, char seq, int length) { int i; @@ -184,7 +180,7 @@ static void encode_asn_tag(char *buf, } int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, - char *ntlm_blob, int ntlm_blob_len) + char *ntlm_blob, int ntlm_blob_len) { char *buf; unsigned int ofs = 0; @@ -225,7 +221,7 @@ int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, } int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, - int neg_result) + int neg_result) { char *buf; unsigned int ofs = 0; @@ -252,8 +248,8 @@ int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, return 0; } -int gssapi_this_mech(void *context, size_t hdrlen, - unsigned char tag, const void *value, size_t vlen) +int gssapi_this_mech(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) { unsigned long *oid; size_t oidlen; @@ -277,8 +273,8 @@ out: return err; } -int neg_token_init_mech_type(void *context, size_t hdrlen, - unsigned char tag, const void *value, size_t vlen) +int neg_token_init_mech_type(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) { struct ksmbd_conn *conn = context; unsigned long *oid; @@ -314,8 +310,8 @@ fail: return -EBADMSG; } -int neg_token_init_mech_token(void *context, size_t hdrlen, - unsigned char tag, const void *value, size_t vlen) +int neg_token_init_mech_token(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) { struct ksmbd_conn *conn = context; @@ -328,8 +324,8 @@ int neg_token_init_mech_token(void *context, size_t hdrlen, return 0; } -int neg_token_targ_resp_token(void *context, size_t hdrlen, - unsigned char tag, const void *value, size_t vlen) +int neg_token_targ_resp_token(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) { struct ksmbd_conn *conn = context; diff --git a/fs/cifsd/asn1.h b/fs/cifsd/asn1.h index ff2692b502d6..ce105f4ce305 100644 --- a/fs/cifsd/asn1.h +++ b/fs/cifsd/asn1.h @@ -10,20 +10,12 @@ #ifndef __ASN1_H__ #define __ASN1_H__ -int ksmbd_decode_negTokenInit(unsigned char *security_blob, - int length, +int ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, struct ksmbd_conn *conn); - -int ksmbd_decode_negTokenTarg(unsigned char *security_blob, - int length, +int ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, struct ksmbd_conn *conn); - -int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, - u16 *buflen, - char *ntlm_blob, - int ntlm_blob_len); - -int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, - u16 *buflen, +int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, + char *ntlm_blob, int ntlm_blob_len); +int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, int neg_result); #endif /* __ASN1_H__ */ diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index f742870a930b..9b86cf4fd73f 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -93,8 +93,7 @@ smbhash(unsigned char *out, const unsigned char *in, unsigned char *key) struct des_ctx ctx; if (fips_enabled) { - ksmbd_debug(AUTH, - "FIPS compliance enabled: DES not permitted\n"); + ksmbd_debug(AUTH, "FIPS compliance enabled: DES not permitted\n"); return -ENOENT; } @@ -120,7 +119,7 @@ static int ksmbd_enc_p24(unsigned char *p21, const unsigned char *c8, unsigned c /* produce a md4 message digest from data of length n bytes */ static int ksmbd_enc_md4(unsigned char *md4_hash, unsigned char *link_str, - int link_len) + int link_len) { int rc; struct ksmbd_crypto_ctx *ctx; @@ -152,7 +151,7 @@ out: } static int ksmbd_enc_update_sess_key(unsigned char *md5_hash, char *nonce, - char *server_challenge, int len) + char *server_challenge, int len) { int rc; struct ksmbd_crypto_ctx *ctx; @@ -197,7 +196,7 @@ out: * */ static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, - char *hmac) + char *hmac) { struct ksmbd_crypto_ctx *ctx; int rc; @@ -226,15 +225,13 @@ static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, hmac, SMB2_NTLMV2_SESSKEY_SIZE); if (rc) { - ksmbd_debug(AUTH, "Could not update with response error %d\n", - rc); + ksmbd_debug(AUTH, "Could not update with response error %d\n", rc); goto out; } rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key); if (rc) { - ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", - rc); + ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc); goto out; } @@ -244,7 +241,7 @@ out: } static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, - char *dname) + char *dname) { int ret, len, conv_len; wchar_t *domain = NULL; @@ -280,7 +277,7 @@ static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, } conv_len = smb_strtoUTF16(uniname, user_name(sess->user), len, - sess->conn->local_nls); + sess->conn->local_nls); if (conv_len < 0 || conv_len > len) { ret = -EINVAL; goto out; @@ -304,7 +301,7 @@ static int calc_ntlmv2_hash(struct ksmbd_session *sess, char *ntlmv2_hash, } conv_len = smb_strtoUTF16((__le16 *)domain, dname, len, - sess->conn->local_nls); + sess->conn->local_nls); if (conv_len < 0 || conv_len > len) { ret = -EINVAL; goto out; @@ -350,11 +347,10 @@ int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf) return rc; } - ksmbd_enc_md4(sess->sess_key, - user_passkey(sess->user), - CIFS_SMB1_SESSKEY_SIZE); + ksmbd_enc_md4(sess->sess_key, user_passkey(sess->user), + CIFS_SMB1_SESSKEY_SIZE); memcpy(sess->sess_key + CIFS_SMB1_SESSKEY_SIZE, key, - CIFS_AUTH_RESP_SIZE); + CIFS_AUTH_RESP_SIZE); sess->sequence_number = 1; if (strncmp(pw_buf, key, CIFS_AUTH_RESP_SIZE) != 0) { @@ -376,7 +372,7 @@ int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf) * Return: 0 on success, error number on error */ int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, - int blen, char *domain_name) + int blen, char *domain_name) { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; @@ -455,7 +451,7 @@ out: * Return: 0 on success, error number on error */ static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess, char *client_nonce, - char *ntlm_resp) + char *ntlm_resp) { char sess_key[CIFS_SMB1_SESSKEY_SIZE] = {0}; int rc; @@ -494,7 +490,7 @@ out: * Return: 0 on success, error number on error */ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, - int blob_len, struct ksmbd_session *sess) + int blob_len, struct ksmbd_session *sess) { char *domain_name; unsigned int lm_off, nt_off; @@ -503,13 +499,13 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, if (blob_len < sizeof(struct authenticate_message)) { ksmbd_debug(AUTH, "negotiate blob len %d too small\n", - blob_len); + blob_len); return -EINVAL; } if (memcmp(authblob->Signature, "NTLMSSP", 8)) { ksmbd_debug(AUTH, "blob signature incorrect %s\n", - authblob->Signature); + authblob->Signature); return -EINVAL; } @@ -538,11 +534,10 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, /* process NTLMv2 authentication */ ksmbd_debug(AUTH, "decode_ntlmssp_authenticate_blob dname%s\n", - domain_name); - ret = ksmbd_auth_ntlmv2(sess, - (struct ntlmv2_resp *)((char *)authblob + nt_off), - nt_len - CIFS_ENCPWD_SIZE, - domain_name); + domain_name); + ret = ksmbd_auth_ntlmv2(sess, (struct ntlmv2_resp *)((char *)authblob + nt_off), + nt_len - CIFS_ENCPWD_SIZE, + domain_name); kfree(domain_name); return ret; } @@ -556,17 +551,17 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, * */ int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, - int blob_len, struct ksmbd_session *sess) + int blob_len, struct ksmbd_session *sess) { if (blob_len < sizeof(struct negotiate_message)) { ksmbd_debug(AUTH, "negotiate blob len %d too small\n", - blob_len); + blob_len); return -EINVAL; } if (memcmp(negblob->Signature, "NTLMSSP", 8)) { ksmbd_debug(AUTH, "blob signature incorrect %s\n", - negblob->Signature); + negblob->Signature); return -EINVAL; } @@ -584,7 +579,7 @@ int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, */ unsigned int ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, - struct ksmbd_session *sess) + struct ksmbd_session *sess) { struct target_info *tinfo; wchar_t *name; @@ -623,7 +618,7 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, return -ENOMEM; conv_len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, - sess->conn->local_nls); + sess->conn->local_nls); if (conv_len < 0 || conv_len > len) { kfree(name); return -EINVAL; @@ -641,7 +636,7 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, /* Initialize random conn challenge */ get_random_bytes(sess->ntlmssp.cryptkey, sizeof(__u64)); memcpy(chgblob->Challenge, sess->ntlmssp.cryptkey, - CIFS_CRYPTO_KEY_SIZE); + CIFS_CRYPTO_KEY_SIZE); /* Add Target Information to security buffer */ chgblob->TargetInfoArray.BufferOffset = cpu_to_le32(blob_len); @@ -676,7 +671,7 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, #ifdef CONFIG_SMB_SERVER_KERBEROS5 int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) + int in_len, char *out_blob, int *out_len) { struct ksmbd_spnego_authen_response *resp; struct ksmbd_user *user = NULL; @@ -696,7 +691,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, if (*out_len <= resp->spnego_blob_len) { ksmbd_debug(AUTH, "buf len %d, but blob len %d\n", - *out_len, resp->spnego_blob_len); + *out_len, resp->spnego_blob_len); retval = -EINVAL; goto out; } @@ -717,7 +712,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, memcpy(sess->sess_key, resp->payload, resp->session_key_len); memcpy(out_blob, resp->payload + resp->session_key_len, - resp->spnego_blob_len); + resp->spnego_blob_len); *out_len = resp->spnego_blob_len; retval = 0; out: @@ -726,7 +721,7 @@ out: } #else int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) + int in_len, char *out_blob, int *out_len) { return -EOPNOTSUPP; } @@ -742,7 +737,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, * */ int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig) + int n_vec, char *sig) { struct ksmbd_crypto_ctx *ctx; int rc, i; @@ -793,7 +788,7 @@ out: * */ int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig) + int n_vec, char *sig) { struct ksmbd_crypto_ctx *ctx; int rc, i; @@ -841,7 +836,7 @@ struct derivation { }; static int generate_key(struct ksmbd_session *sess, struct kvec label, - struct kvec context, __u8 *key, unsigned int key_size) + struct kvec context, __u8 *key, unsigned int key_size) { unsigned char zero = 0x0; __u8 i[4] = {0, 0, 0, 1}; @@ -914,7 +909,7 @@ static int generate_key(struct ksmbd_session *sess, struct kvec label, rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr); if (rc) { ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", - rc); + rc); goto smb3signkey_ret; } @@ -926,7 +921,7 @@ smb3signkey_ret: } static int generate_smb3signingkey(struct ksmbd_session *sess, - const struct derivation *signing) + const struct derivation *signing) { int rc; struct channel *chann; @@ -942,7 +937,7 @@ static int generate_smb3signingkey(struct ksmbd_session *sess, key = sess->smb3signingkey; rc = generate_key(sess, signing->label, signing->context, key, - SMB3_SIGN_KEY_SIZE); + SMB3_SIGN_KEY_SIZE); if (rc) return rc; @@ -952,9 +947,9 @@ static int generate_smb3signingkey(struct ksmbd_session *sess, ksmbd_debug(AUTH, "dumping generated AES signing keys\n"); ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); ksmbd_debug(AUTH, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); ksmbd_debug(AUTH, "Signing Key %*ph\n", - SMB3_SIGN_KEY_SIZE, key); + SMB3_SIGN_KEY_SIZE, key); return 0; } @@ -990,19 +985,19 @@ struct derivation_twin { }; static int generate_smb3encryptionkey(struct ksmbd_session *sess, - const struct derivation_twin *ptwin) + const struct derivation_twin *ptwin) { int rc; rc = generate_key(sess, ptwin->encryption.label, - ptwin->encryption.context, sess->smb3encryptionkey, - SMB3_ENC_DEC_KEY_SIZE); + ptwin->encryption.context, sess->smb3encryptionkey, + SMB3_ENC_DEC_KEY_SIZE); if (rc) return rc; rc = generate_key(sess, ptwin->decryption.label, - ptwin->decryption.context, - sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); + ptwin->decryption.context, + sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); if (rc) return rc; @@ -1010,18 +1005,18 @@ static int generate_smb3encryptionkey(struct ksmbd_session *sess, ksmbd_debug(AUTH, "Cipher type %d\n", sess->conn->cipher_type); ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); ksmbd_debug(AUTH, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); if (sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || sess->conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { ksmbd_debug(AUTH, "ServerIn Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3encryptionkey); + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3encryptionkey); ksmbd_debug(AUTH, "ServerOut Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3decryptionkey); + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3decryptionkey); } else { ksmbd_debug(AUTH, "ServerIn Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3encryptionkey); + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3encryptionkey); ksmbd_debug(AUTH, "ServerOut Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); } return 0; } @@ -1067,7 +1062,7 @@ int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess) } int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, - __u8 *pi_hash) + __u8 *pi_hash) { int rc; struct smb2_hdr *rcv_hdr = (struct smb2_hdr *)buf; @@ -1114,7 +1109,7 @@ out: } int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, - __u8 *pi_hash) + __u8 *pi_hash) { int rc; struct ksmbd_crypto_ctx *ctx = NULL; @@ -1148,7 +1143,7 @@ out: } static int ksmbd_get_encryption_key(struct ksmbd_conn *conn, __u64 ses_id, - int enc, u8 *key) + int enc, u8 *key) { struct ksmbd_session *sess; u8 *ses_enc_key; @@ -1165,7 +1160,7 @@ static int ksmbd_get_encryption_key(struct ksmbd_conn *conn, __u64 ses_id, } static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, - unsigned int buflen) + unsigned int buflen) { void *addr; @@ -1177,7 +1172,7 @@ static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, } static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, - u8 *sign) + u8 *sign) { struct scatterlist *sg; unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; @@ -1242,7 +1237,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, } int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, - unsigned int nvec, int enc) + unsigned int nvec, int enc) { struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)iov[0].iov_base; diff --git a/fs/cifsd/auth.h b/fs/cifsd/auth.h index 6fcfad5e7e1f..650bd7dd6750 100644 --- a/fs/cifsd/auth.h +++ b/fs/cifsd/auth.h @@ -35,56 +35,31 @@ struct ksmbd_session; struct ksmbd_conn; struct kvec; -int ksmbd_crypt_message(struct ksmbd_conn *conn, - struct kvec *iov, - unsigned int nvec, - int enc); - +int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, + unsigned int nvec, int enc); void ksmbd_copy_gss_neg_header(void *buf); - -int ksmbd_auth_ntlm(struct ksmbd_session *sess, - char *pw_buf); - -int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, - struct ntlmv2_resp *ntlmv2, - int blen, - char *domain_name); - +int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf); +int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2, + int blen, char *domain_name); int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, - int blob_len, - struct ksmbd_session *sess); - + int blob_len, struct ksmbd_session *sess); int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, - int blob_len, - struct ksmbd_session *sess); - + int blob_len, struct ksmbd_session *sess); unsigned int ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, - struct ksmbd_session *sess); - -int ksmbd_krb5_authenticate(struct ksmbd_session *sess, - char *in_blob, int in_len, - char *out_blob, int *out_len); - -int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, - char *key, - struct kvec *iov, - int n_vec, - char *sig); -int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, - char *key, - struct kvec *iov, - int n_vec, - char *sig); - + struct ksmbd_session *sess); +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len); +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess); int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess); int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess); int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess); - -int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, - char *buf, +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, __u8 *pi_hash); int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, - __u8 *pi_hash); + __u8 *pi_hash); #endif diff --git a/fs/cifsd/buffer_pool.c b/fs/cifsd/buffer_pool.c index 1ee1feef1bb4..ea7d2d1a056a 100644 --- a/fs/cifsd/buffer_pool.c +++ b/fs/cifsd/buffer_pool.c @@ -251,7 +251,8 @@ int ksmbd_init_buffer_pools(void) goto out; filp_cache = kmem_cache_create("ksmbd_file_cache", - sizeof(struct ksmbd_file), 0, SLAB_HWCACHE_ALIGN, NULL); + sizeof(struct ksmbd_file), 0, + SLAB_HWCACHE_ALIGN, NULL); if (!filp_cache) goto out; diff --git a/fs/cifsd/buffer_pool.h b/fs/cifsd/buffer_pool.h index f7157144a92f..088aa07ba09b 100644 --- a/fs/cifsd/buffer_pool.h +++ b/fs/cifsd/buffer_pool.h @@ -8,12 +8,9 @@ void *ksmbd_find_buffer(size_t size); void ksmbd_release_buffer(void *buffer); - void *ksmbd_realloc_response(void *ptr, size_t old_sz, size_t new_sz); - void ksmbd_free_file_struct(void *filp); void *ksmbd_alloc_file_struct(void); - void ksmbd_destroy_buffer_pools(void); int ksmbd_init_buffer_pools(void); diff --git a/fs/cifsd/connection.c b/fs/cifsd/connection.c index 4785dd59fcc5..06c42309be72 100644 --- a/fs/cifsd/connection.c +++ b/fs/cifsd/connection.c @@ -201,30 +201,30 @@ int ksmbd_conn_write(struct ksmbd_work *work) } int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, void *buf, - unsigned int buflen, u32 remote_key, u64 remote_offset, - u32 remote_len) + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len) { int ret = -EINVAL; if (conn->transport->ops->rdma_read) ret = conn->transport->ops->rdma_read(conn->transport, - buf, buflen, - remote_key, remote_offset, - remote_len); + buf, buflen, + remote_key, remote_offset, + remote_len); return ret; } int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, void *buf, - unsigned int buflen, u32 remote_key, u64 remote_offset, - u32 remote_len) + unsigned int buflen, u32 remote_key, + u64 remote_offset, u32 remote_len) { int ret = -EINVAL; if (conn->transport->ops->rdma_write) ret = conn->transport->ops->rdma_write(conn->transport, - buf, buflen, - remote_key, remote_offset, - remote_len); + buf, buflen, + remote_key, remote_offset, + remote_len); return ret; } @@ -250,7 +250,7 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn) if (server_conf.deadtime > 0 && time_after(jiffies, conn->last_active + server_conf.deadtime)) { ksmbd_debug(CONN, "No response from client in %lu minutes\n", - server_conf.deadtime / SMB_ECHO_INTERVAL); + server_conf.deadtime / SMB_ECHO_INTERVAL); return false; } return true; @@ -390,7 +390,7 @@ again: task = conn->transport->handler; if (task) ksmbd_debug(CONN, "Stop session handler %s/%d\n", - task->comm, task_pid_nr(task)); + task->comm, task_pid_nr(task)); conn->status = KSMBD_SESS_EXITING; } read_unlock(&conn_list_lock); diff --git a/fs/cifsd/connection.h b/fs/cifsd/connection.h index 00ede7a67199..1658442b27b0 100644 --- a/fs/cifsd/connection.h +++ b/fs/cifsd/connection.h @@ -118,13 +118,13 @@ struct ksmbd_transport_ops { void (*disconnect)(struct ksmbd_transport *t); int (*read)(struct ksmbd_transport *t, char *buf, unsigned int size); int (*writev)(struct ksmbd_transport *t, struct kvec *iovs, int niov, - int size, bool need_invalidate_rkey, - unsigned int remote_key); + int size, bool need_invalidate_rkey, + unsigned int remote_key); int (*rdma_read)(struct ksmbd_transport *t, void *buf, unsigned int len, - u32 remote_key, u64 remote_offset, u32 remote_len); + u32 remote_key, u64 remote_offset, u32 remote_len); int (*rdma_write)(struct ksmbd_transport *t, void *buf, - unsigned int len, u32 remote_key, u64 remote_offset, - u32 remote_len); + unsigned int len, u32 remote_key, u64 remote_offset, + u32 remote_len); }; struct ksmbd_transport { @@ -139,24 +139,20 @@ struct ksmbd_transport { bool ksmbd_conn_alive(struct ksmbd_conn *conn); void ksmbd_conn_wait_idle(struct ksmbd_conn *conn); - struct ksmbd_conn *ksmbd_conn_alloc(void); void ksmbd_conn_free(struct ksmbd_conn *conn); bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); int ksmbd_conn_write(struct ksmbd_work *work); int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, void *buf, - unsigned int buflen, u32 remote_key, u64 remote_offset, - u32 remote_len); + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len); int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, void *buf, - unsigned int buflen, u32 remote_key, u64 remote_offset, - u32 remote_len); - + unsigned int buflen, u32 remote_key, u64 remote_offset, + u32 remote_len); void ksmbd_conn_enqueue_request(struct ksmbd_work *work); int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); - int ksmbd_conn_handler_loop(void *p); - int ksmbd_conn_transport_init(void); void ksmbd_conn_transport_destroy(void); diff --git a/fs/cifsd/crypto_ctx.c b/fs/cifsd/crypto_ctx.c index 9685bf963702..cfea4c4db30f 100644 --- a/fs/cifsd/crypto_ctx.c +++ b/fs/cifsd/crypto_ctx.c @@ -123,8 +123,8 @@ static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) spin_lock(&ctx_list.ctx_lock); if (!list_empty(&ctx_list.idle_ctx)) { ctx = list_entry(ctx_list.idle_ctx.next, - struct ksmbd_crypto_ctx, - list); + struct ksmbd_crypto_ctx, + list); list_del(&ctx->list); spin_unlock(&ctx_list.ctx_lock); return ctx; diff --git a/fs/cifsd/crypto_ctx.h b/fs/cifsd/crypto_ctx.h index b0d3cd650485..ef11154b43df 100644 --- a/fs/cifsd/crypto_ctx.h +++ b/fs/cifsd/crypto_ctx.h @@ -59,7 +59,6 @@ struct ksmbd_crypto_ctx { #define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); - struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); @@ -67,10 +66,8 @@ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void); - struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); - void ksmbd_crypto_destroy(void); int ksmbd_crypto_create(void); diff --git a/fs/cifsd/ksmbd_work.c b/fs/cifsd/ksmbd_work.c index eb8c8a34acab..f284a2a803d6 100644 --- a/fs/cifsd/ksmbd_work.c +++ b/fs/cifsd/ksmbd_work.c @@ -39,13 +39,13 @@ void ksmbd_free_work_struct(struct ksmbd_work *work) { WARN_ON(work->saved_cred != NULL); if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_TBUF && - work->set_trans_buf) + work->set_trans_buf) ksmbd_release_buffer(work->response_buf); else kvfree(work->response_buf); if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF && - work->set_read_buf) + work->set_read_buf) ksmbd_release_buffer(work->aux_payload_buf); else kvfree(work->aux_payload_buf); @@ -65,8 +65,8 @@ void ksmbd_work_pool_destroy(void) int ksmbd_work_pool_init(void) { work_cache = kmem_cache_create("ksmbd_work_cache", - sizeof(struct ksmbd_work), 0, - SLAB_HWCACHE_ALIGN, NULL); + sizeof(struct ksmbd_work), 0, + SLAB_HWCACHE_ALIGN, NULL); if (!work_cache) return -ENOMEM; return 0; diff --git a/fs/cifsd/misc.c b/fs/cifsd/misc.c index 7fa6649fadfd..1c6ed20f4a18 100644 --- a/fs/cifsd/misc.c +++ b/fs/cifsd/misc.c @@ -135,7 +135,7 @@ int parse_stream_name(char *filename, char **stream_name, int *s_type) } ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name, - stream_type); + stream_type); if (!strncasecmp("$data", stream_type, 5)) *s_type = DATA_STREAM; else if (!strncasecmp("$index_allocation", stream_type, 17)) @@ -267,7 +267,8 @@ char *convert_to_unix_name(struct ksmbd_share_config *share, char *name) } char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, - const struct nls_table *local_nls, int *conv_len) + const struct nls_table *local_nls, + int *conv_len) { char *conv; int sz = min(4 * d_info->name_len, PATH_MAX); @@ -280,11 +281,8 @@ char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, return NULL; /* XXX */ - *conv_len = smbConvertToUTF16((__le16 *)conv, - d_info->name, - d_info->name_len, - local_nls, - 0); + *conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name, + d_info->name_len, local_nls, 0); *conv_len *= 2; /* We allocate buffer twice bigger than needed. */ diff --git a/fs/cifsd/misc.h b/fs/cifsd/misc.h index e4bd02a8d45f..af8717d4d85b 100644 --- a/fs/cifsd/misc.h +++ b/fs/cifsd/misc.h @@ -12,32 +12,23 @@ struct kstat; struct ksmbd_file; int match_pattern(const char *str, size_t len, const char *pattern); - int ksmbd_validate_filename(char *filename); - int parse_stream_name(char *filename, char **stream_name, int *s_type); - char *convert_to_nt_pathname(char *filename, char *sharepath); - int get_nlink(struct kstat *st); - void ksmbd_conv_path_to_unix(char *path); void ksmbd_strip_last_slash(char *path); void ksmbd_conv_path_to_windows(char *path); - char *ksmbd_extract_sharename(char *treename); - char *convert_to_unix_name(struct ksmbd_share_config *share, char *name); #define KSMBD_DIR_INFO_ALIGNMENT 8 - struct ksmbd_dir_info; char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, const struct nls_table *local_nls, int *conv_len); #define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) - struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); u64 ksmbd_UnixTimeToNT(struct timespec64 t); long long ksmbd_systime(void); diff --git a/fs/cifsd/ndr.c b/fs/cifsd/ndr.c index 1838fc2b1d7c..14189832c65e 100644 --- a/fs/cifsd/ndr.c +++ b/fs/cifsd/ndr.c @@ -185,7 +185,7 @@ int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) version2 = ndr_read_int32(n); if (da->version != version2) { ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n", - da->version, version2); + da->version, version2); return -EINVAL; } @@ -235,7 +235,8 @@ static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) } int ndr_encode_posix_acl(struct ndr *n, struct inode *inode, - struct xattr_smb_acl *acl, struct xattr_smb_acl *def_acl) + struct xattr_smb_acl *acl, + struct xattr_smb_acl *def_acl) { int ref_id = 0x00020000; @@ -315,7 +316,7 @@ int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) version2 = ndr_read_int32(n); if (acl->version != version2) { ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n", - acl->version, version2); + acl->version, version2); return -EINVAL; } diff --git a/fs/cifsd/ndr.h b/fs/cifsd/ndr.h index a9db968b78ac..77b2d1ac93a0 100644 --- a/fs/cifsd/ndr.h +++ b/fs/cifsd/ndr.h @@ -15,7 +15,8 @@ struct ndr { int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); int ndr_encode_posix_acl(struct ndr *n, struct inode *inode, - struct xattr_smb_acl *acl, struct xattr_smb_acl *def_acl); + struct xattr_smb_acl *acl, + struct xattr_smb_acl *def_acl); int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl); int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index e77f1385a8c1..56c68e9cb7ff 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -29,7 +29,7 @@ static DEFINE_RWLOCK(lease_list_lock); * Return: allocated opinfo object on success, otherwise NULL */ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, - u64 id, __u16 Tid) + u64 id, __u16 Tid) { struct ksmbd_session *sess = work->sess; struct oplock_info *opinfo; @@ -153,7 +153,7 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) rcu_read_lock(); opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, - op_entry); + op_entry); if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) opinfo = NULL; rcu_read_unlock(); @@ -269,8 +269,7 @@ int opinfo_write_to_none(struct oplock_info *opinfo) opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { ksmbd_err("bad oplock(0x%x)\n", opinfo->level); if (opinfo->is_lease) - ksmbd_err("lease state(0x%x)\n", - lease->state); + ksmbd_err("lease state(0x%x)\n", lease->state); return -EINVAL; } opinfo->level = SMB2_OPLOCK_LEVEL_NONE; @@ -312,8 +311,7 @@ int lease_read_to_write(struct oplock_info *opinfo) struct lease *lease = opinfo->o_lease; if (!(lease->state & SMB2_LEASE_READ_CACHING_LE)) { - ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", - lease->state); + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); return -EINVAL; } @@ -338,8 +336,7 @@ static int lease_none_upgrade(struct oplock_info *opinfo, __le32 new_state) struct lease *lease = opinfo->o_lease; if (!(lease->state == SMB2_LEASE_NONE_LE)) { - ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", - lease->state); + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); return -EINVAL; } @@ -399,7 +396,7 @@ void close_id_del_oplock(struct ksmbd_file *fp) * Return: 0 */ static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, - struct lease_ctx_info *lctx) + struct lease_ctx_info *lctx) { struct lease *lease = opinfo_new->o_lease; @@ -410,8 +407,7 @@ static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, if (lctx) { lease->state = lctx->req_state; - memcpy(lease->lease_key, lctx->lease_key, - SMB2_LEASE_KEY_SIZE); + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); } } @@ -423,7 +419,7 @@ static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, * Return: 0 */ static void grant_read_oplock(struct oplock_info *opinfo_new, - struct lease_ctx_info *lctx) + struct lease_ctx_info *lctx) { struct lease *lease = opinfo_new->o_lease; @@ -433,8 +429,7 @@ static void grant_read_oplock(struct oplock_info *opinfo_new, lease->state = SMB2_LEASE_READ_CACHING_LE; if (lctx->req_state & SMB2_LEASE_HANDLE_CACHING_LE) lease->state |= SMB2_LEASE_HANDLE_CACHING_LE; - memcpy(lease->lease_key, lctx->lease_key, - SMB2_LEASE_KEY_SIZE); + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); } } @@ -446,7 +441,7 @@ static void grant_read_oplock(struct oplock_info *opinfo_new, * Return: 0 */ static void grant_none_oplock(struct oplock_info *opinfo_new, - struct lease_ctx_info *lctx) + struct lease_ctx_info *lctx) { struct lease *lease = opinfo_new->o_lease; @@ -454,13 +449,12 @@ static void grant_none_oplock(struct oplock_info *opinfo_new, if (lctx) { lease->state = 0; - memcpy(lease->lease_key, lctx->lease_key, - SMB2_LEASE_KEY_SIZE); + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); } } static inline int compare_guid_key(struct oplock_info *opinfo, - const char *guid1, const char *key1) + const char *guid1, const char *key1) { const char *guid2, *key2; @@ -483,7 +477,8 @@ static inline int compare_guid_key(struct oplock_info *opinfo, * Return: oplock(lease) object on success, otherwise NULL */ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, - char *client_guid, struct lease_ctx_info *lctx) + char *client_guid, + struct lease_ctx_info *lctx) { int ret; struct lease *lease; @@ -517,7 +512,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, if ((atomic_read(&ci->op_count) + atomic_read(&ci->sop_count)) == 1) { if (lease->state == - (lctx->req_state & lease->state)) { + (lctx->req_state & lease->state)) { lease->state |= lctx->req_state; if (lctx->req_state & SMB2_LEASE_WRITE_CACHING_LE) @@ -526,13 +521,13 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, } else if ((atomic_read(&ci->op_count) + atomic_read(&ci->sop_count)) > 1) { if (lctx->req_state == - (SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)) + (SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) lease->state = lctx->req_state; } if (lctx->req_state && lease->state == - SMB2_LEASE_NONE_LE) + SMB2_LEASE_NONE_LE) lease_none_upgrade(opinfo, lctx->req_state); } read_lock(&ci->m_lock); @@ -547,9 +542,9 @@ static void wait_for_break_ack(struct oplock_info *opinfo) int rc = 0; rc = wait_event_interruptible_timeout(opinfo->oplock_q, - opinfo->op_state == OPLOCK_STATE_NONE || - opinfo->op_state == OPLOCK_CLOSING, - OPLOCK_WAIT_TIME); + opinfo->op_state == OPLOCK_STATE_NONE || + opinfo->op_state == OPLOCK_CLOSING, + OPLOCK_WAIT_TIME); /* is this a timeout ? */ if (!rc) { @@ -664,8 +659,8 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) inc_rfc1001_len(rsp, 24); ksmbd_debug(OPLOCK, - "sending oplock break v_id %llu p_id = %llu lock level = %d\n", - rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); + "sending oplock break v_id %llu p_id = %llu lock level = %d\n", + rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); ksmbd_fd_put(work, fp); ksmbd_conn_write(work); @@ -815,7 +810,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) struct ksmbd_work *in_work; in_work = list_entry(tmp, struct ksmbd_work, - interim_entry); + interim_entry); setup_async_work(in_work, NULL, NULL); smb2_send_interim_resp(in_work, STATUS_PENDING); list_del(&in_work->interim_entry); @@ -843,7 +838,8 @@ static void wait_lease_breaking(struct oplock_info *opinfo) int ret = 0; ret = wait_event_interruptible_timeout(opinfo->oplock_brk, - atomic_read(&opinfo->breaking_cnt) == 0, HZ); + atomic_read(&opinfo->breaking_cnt) == 0, + HZ); if (!ret) atomic_set(&opinfo->breaking_cnt, 0); } @@ -855,8 +851,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) /* Need to break exclusive/batch oplock, write lease or overwrite_if */ ksmbd_debug(OPLOCK, - "request to send oplock(level : 0x%x) break notification\n", - brk_opinfo->level); + "request to send oplock(level : 0x%x) break notification\n", + brk_opinfo->level); if (brk_opinfo->is_lease) { struct lease *lease = brk_opinfo->o_lease; @@ -939,7 +935,7 @@ void destroy_lease_table(struct ksmbd_conn *conn) again: rcu_read_lock(); list_for_each_entry_rcu(opinfo, &lb->lease_list, - lease_entry) { + lease_entry) { rcu_read_unlock(); lease_del_list(opinfo); goto again; @@ -952,7 +948,7 @@ again: } int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, - struct lease_ctx_info *lctx) + struct lease_ctx_info *lctx) { struct oplock_info *opinfo; int err = 0; @@ -978,20 +974,18 @@ int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, found: rcu_read_lock(); - list_for_each_entry_rcu(opinfo, &lb->lease_list, - lease_entry) { + list_for_each_entry_rcu(opinfo, &lb->lease_list, lease_entry) { if (!atomic_inc_not_zero(&opinfo->refcount)) continue; rcu_read_unlock(); if (opinfo->o_fp->f_ci == ci) goto op_next; - err = compare_guid_key(opinfo, - sess->conn->ClientGUID, - lctx->lease_key); + err = compare_guid_key(opinfo, sess->conn->ClientGUID, + lctx->lease_key); if (err) { err = -EINVAL; ksmbd_debug(OPLOCK, - "found same lease key is already used in other files\n"); + "found same lease key is already used in other files\n"); opinfo_put(opinfo); goto out; } @@ -1014,7 +1008,7 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) op2->level = op1->level; lease2->state = lease1->state; memcpy(lease2->lease_key, lease1->lease_key, - SMB2_LEASE_KEY_SIZE); + SMB2_LEASE_KEY_SIZE); lease2->duration = lease1->duration; lease2->flags = lease1->flags; } @@ -1040,7 +1034,7 @@ static int add_lease_global_list(struct oplock_info *opinfo) return -ENOMEM; memcpy(lb->client_guid, opinfo->conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE); + SMB2_CLIENT_GUID_SIZE); INIT_LIST_HEAD(&lb->lease_list); spin_lock_init(&lb->lb_lock); opinfo->o_lease->l_lb = lb; @@ -1050,7 +1044,7 @@ static int add_lease_global_list(struct oplock_info *opinfo) } static void set_oplock_level(struct oplock_info *opinfo, int level, - struct lease_ctx_info *lctx) + struct lease_ctx_info *lctx) { switch (level) { case SMB2_OPLOCK_LEVEL_BATCH: @@ -1079,8 +1073,8 @@ static void set_oplock_level(struct oplock_info *opinfo, int level, * Return: 0 on success, otherwise error */ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, - struct ksmbd_file *fp, __u16 tid, struct lease_ctx_info *lctx, - int share_ret) + struct ksmbd_file *fp, __u16 tid, + struct lease_ctx_info *lctx, int share_ret) { struct ksmbd_session *sess = work->sess; int err = 0; @@ -1122,7 +1116,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, /* is lease already granted ? */ m_opinfo = same_client_has_lease(ci, sess->conn->ClientGUID, - lctx); + lctx); if (m_opinfo) { copy_lease(m_opinfo, opinfo); if (atomic_read(&m_opinfo->breaking_cnt)) @@ -1208,7 +1202,7 @@ err_out: * @is_trunc: truncate on open */ static void smb_break_all_write_oplock(struct ksmbd_work *work, - struct ksmbd_file *fp, int is_trunc) + struct ksmbd_file *fp, int is_trunc) { struct oplock_info *brk_opinfo; @@ -1235,7 +1229,7 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work, * @is_trunc: truncate on open */ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, - int is_trunc) + int is_trunc) { struct oplock_info *op, *brk_op; struct ksmbd_inode *ci; @@ -1257,18 +1251,18 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)))) { ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n", - brk_op->o_lease->state); + brk_op->o_lease->state); goto next; } else if (brk_op->level != SMB2_OPLOCK_LEVEL_II) { ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", - brk_op->level); + brk_op->level); goto next; } /* Skip oplock being break to none */ - if (brk_op->is_lease && (brk_op->o_lease->new_state == - SMB2_LEASE_NONE_LE) && + if (brk_op->is_lease && + (brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE) && atomic_read(&brk_op->breaking_cnt)) goto next; @@ -1573,9 +1567,9 @@ void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) buf->reparse_tag = cpu_to_le32(fp->volatile_id); buf->mode = cpu_to_le32(inode->i_mode); id_to_sid(from_kuid(&init_user_ns, inode->i_uid), - SIDNFS_USER, (struct smb_sid *)&buf->SidBuffer[0]); + SIDNFS_USER, (struct smb_sid *)&buf->SidBuffer[0]); id_to_sid(from_kgid(&init_user_ns, inode->i_gid), - SIDNFS_GROUP, (struct smb_sid *)&buf->SidBuffer[20]); + SIDNFS_GROUP, (struct smb_sid *)&buf->SidBuffer[20]); } /* @@ -1590,7 +1584,7 @@ void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) * Return: opinfo if found matching opinfo, otherwise NULL */ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, - char *lease_key) + char *lease_key) { struct oplock_info *opinfo = NULL, *ret_op = NULL; struct lease_table *lt; @@ -1619,7 +1613,7 @@ found: SMB2_LEASE_WRITE_CACHING_LE))) goto op_next; ret = compare_guid_key(opinfo, conn->ClientGUID, - lease_key); + lease_key); if (ret) { ksmbd_debug(OPLOCK, "found opinfo\n"); ret_op = opinfo; @@ -1637,7 +1631,7 @@ out: } int smb2_check_durable_oplock(struct ksmbd_file *fp, - struct lease_ctx_info *lctx, char *name) + struct lease_ctx_info *lctx, char *name) { struct oplock_info *opinfo = opinfo_get(fp); int ret = 0; diff --git a/fs/cifsd/oplock.h b/fs/cifsd/oplock.h index f8b4b486eb93..0abd26123f6d 100644 --- a/fs/cifsd/oplock.h +++ b/fs/cifsd/oplock.h @@ -96,11 +96,10 @@ struct oplock_break_info { }; int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, - u64 pid, struct ksmbd_file *fp, __u16 tid, - struct lease_ctx_info *lctx, int share_ret); + u64 pid, struct ksmbd_file *fp, __u16 tid, + struct lease_ctx_info *lctx, int share_ret); void smb_break_all_levII_oplock(struct ksmbd_work *work, - struct ksmbd_file *fp, int is_trunc); - + struct ksmbd_file *fp, int is_trunc); int opinfo_write_to_read(struct oplock_info *opinfo); int opinfo_read_handle_to_read(struct oplock_info *opinfo); int opinfo_write_to_none(struct oplock_info *opinfo); @@ -124,10 +123,10 @@ void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); struct create_context *smb2_find_context_vals(void *open_req, const char *str); struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, - char *lease_key); + char *lease_key); int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, - struct lease_ctx_info *lctx); + struct lease_ctx_info *lctx); void destroy_lease_table(struct ksmbd_conn *conn); int smb2_check_durable_oplock(struct ksmbd_file *fp, - struct lease_ctx_info *lctx, char *name); + struct lease_ctx_info *lctx, char *name); #endif /* __KSMBD_OPLOCK_H */ diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c index a4a4e10cf172..4ba43e788ce9 100644 --- a/fs/cifsd/server.c +++ b/fs/cifsd/server.c @@ -106,7 +106,7 @@ static inline int check_conn_state(struct ksmbd_work *work) #define TCP_HANDLER_ABORT 1 static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, - uint16_t *cmd) + uint16_t *cmd) { struct smb_version_cmds *cmds; uint16_t command; @@ -159,7 +159,7 @@ andx_again: } static void __handle_ksmbd_work(struct ksmbd_work *work, - struct ksmbd_conn *conn) + struct ksmbd_conn *conn) { u16 command = 0; int rc; @@ -222,8 +222,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, } } - if (work->sess && (work->sess->sign || - smb3_11_final_sess_setup_resp(work) || + if (work->sess && + (work->sess->sign || smb3_11_final_sess_setup_resp(work) || conn->ops->is_sign_req(work, command))) conn->ops->set_sign_rsp(work); } while (is_chained_smb2_message(work)); @@ -416,7 +416,7 @@ int server_queue_ctrl_reset_work(void) } static ssize_t stats_show(struct class *class, struct class_attribute *attr, - char *buf) + char *buf) { /* * Inc this each time you change stats output format, @@ -430,19 +430,15 @@ static ssize_t stats_show(struct class *class, struct class_attribute *attr, "shutdown" }; - ssize_t sz = scnprintf(buf, - PAGE_SIZE, - "%d %s %d %lu\n", - stats_version, - state[server_conf.state], - server_conf.tcp_port, - server_conf.ipc_last_active / HZ); + ssize_t sz = scnprintf(buf, PAGE_SIZE, "%d %s %d %lu\n", stats_version, + state[server_conf.state], server_conf.tcp_port, + server_conf.ipc_last_active / HZ); return sz; } static ssize_t kill_server_store(struct class *class, - struct class_attribute *attr, const char *buf, - size_t len) + struct class_attribute *attr, const char *buf, + size_t len) { if (!sysfs_streq(buf, "hard")) return len; @@ -458,11 +454,11 @@ static ssize_t kill_server_store(struct class *class, } static const char * const debug_type_strings[] = {"smb", "auth", "vfs", - "oplock", "ipc", "conn", - "rdma"}; + "oplock", "ipc", "conn", + "rdma"}; static ssize_t debug_show(struct class *class, struct class_attribute *attr, - char *buf) + char *buf) { ssize_t sz = 0; int i, pos = 0; @@ -486,7 +482,7 @@ static ssize_t debug_show(struct class *class, struct class_attribute *attr, } static ssize_t debug_store(struct class *class, struct class_attribute *attr, - const char *buf, size_t len) + const char *buf, size_t len) { int i; diff --git a/fs/cifsd/smb2misc.c b/fs/cifsd/smb2misc.c index 5a50c4ec43f2..c4b870dbf683 100644 --- a/fs/cifsd/smb2misc.c +++ b/fs/cifsd/smb2misc.c @@ -178,19 +178,19 @@ static char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) */ if (*off > 4096) { ksmbd_debug(SMB, "offset %d too large, data area ignored\n", - *off); + *off); *len = 0; *off = 0; } else if (*off < 0) { ksmbd_debug(SMB, - "negative offset %d to data invalid ignore data area\n", - *off); + "negative offset %d to data invalid ignore data area\n", + *off); *off = 0; *len = 0; } else if (*len < 0) { ksmbd_debug(SMB, - "negative data length %d invalid, data area ignored\n", - *len); + "negative data length %d invalid, data area ignored\n", + *len); *len = 0; } else if (*len > 128 * 1024) { ksmbd_debug(SMB, "data area larger than 128K: %d\n", *len); @@ -228,7 +228,7 @@ static unsigned int smb2_calc_size(void *buf) smb2_get_data_area_len(&offset, &data_length, hdr); ksmbd_debug(SMB, "SMB2 data length %d offset %d\n", data_length, - offset); + offset); if (data_length > 0) { /* @@ -239,8 +239,8 @@ static unsigned int smb2_calc_size(void *buf) */ if (offset + 1 < len) ksmbd_debug(SMB, - "data area offset %d overlaps SMB2 header %d\n", - offset + 1, len); + "data area offset %d overlaps SMB2 header %d\n", + offset + 1, len); else len = offset + data_length; } @@ -321,11 +321,11 @@ static int smb2_validate_credit_charge(struct smb2_hdr *hdr) calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); if (!credit_charge && max_len > SMB2_MAX_BUFFER_SIZE) { ksmbd_err("credit charge is zero and payload size(%d) is bigger than 64K\n", - max_len); + max_len); return 1; } else if (credit_charge < calc_credit_num) { ksmbd_err("credit charge : %d, calc_credit_num : %d\n", - credit_charge, calc_credit_num); + credit_charge, calc_credit_num); return 1; } @@ -357,7 +357,7 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { ksmbd_debug(SMB, "Illegal structure size %u\n", - le16_to_cpu(hdr->StructureSize)); + le16_to_cpu(hdr->StructureSize)); return 1; } @@ -372,8 +372,8 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) (hdr->Status == 0 || pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { /* error packets have 9 byte structure size */ ksmbd_debug(SMB, - "Illegal request size %u for command %d\n", - le16_to_cpu(pdu->StructureSize2), command); + "Illegal request size %u for command %d\n", + le16_to_cpu(pdu->StructureSize2), command); return 1; } else if (command == SMB2_OPLOCK_BREAK_HE && hdr->Status == 0 && @@ -381,8 +381,8 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) { /* special case for SMB2.1 lease break message */ ksmbd_debug(SMB, - "Illegal request size %d for oplock break\n", - le16_to_cpu(pdu->StructureSize2)); + "Illegal request size %d for oplock break\n", + le16_to_cpu(pdu->StructureSize2)); return 1; } } @@ -408,9 +408,9 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) */ if (clc_len < len) { ksmbd_debug(SMB, - "cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n", - len, clc_len, command, - le64_to_cpu(hdr->MessageId)); + "cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n", + len, clc_len, command, + le64_to_cpu(hdr->MessageId)); return 0; } @@ -418,9 +418,9 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) return 0; ksmbd_debug(SMB, - "cli req too short, len %d not %d. cmd:%d mid:%llu\n", - len, clc_len, command, - le64_to_cpu(hdr->MessageId)); + "cli req too short, len %d not %d. cmd:%d mid:%llu\n", + len, clc_len, command, + le64_to_cpu(hdr->MessageId)); return 1; } diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 16290ad710fa..84b243b3895a 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -296,7 +296,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) } static int smb2_consume_credit_charge(struct ksmbd_work *work, - unsigned short credit_charge) + unsigned short credit_charge) { struct ksmbd_conn *conn = work->conn; unsigned int rsp_credits = 1; @@ -336,8 +336,8 @@ int smb2_set_rsp_credits(struct ksmbd_work *work) conn->total_credits = min_credits; } - rsp_credit_charge = smb2_consume_credit_charge(work, - le16_to_cpu(req_hdr->CreditCharge)); + rsp_credit_charge = + smb2_consume_credit_charge(work, le16_to_cpu(req_hdr->CreditCharge)); if (rsp_credit_charge < 0) return -EINVAL; @@ -373,9 +373,9 @@ int smb2_set_rsp_credits(struct ksmbd_work *work) } out: ksmbd_debug(SMB, - "credits: requested[%d] granted[%d] total_granted[%d]\n", - credits_requested, credits_granted, - conn->total_credits); + "credits: requested[%d] granted[%d] total_granted[%d]\n", + credits_requested, credits_granted, + conn->total_credits); return 0; } @@ -420,9 +420,9 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) work->next_smb2_rcv_hdr_off += next_hdr_offset; work->next_smb2_rsp_hdr_off += new_len; ksmbd_debug(SMB, - "Compound req new_len = %d rcv off = %d rsp off = %d\n", - new_len, work->next_smb2_rcv_hdr_off, - work->next_smb2_rsp_hdr_off); + "Compound req new_len = %d rcv off = %d rsp off = %d\n", + new_len, work->next_smb2_rcv_hdr_off, + work->next_smb2_rsp_hdr_off); rsp_hdr = RESPONSE_BUF_NEXT(work); rcv_hdr = REQUEST_BUF_NEXT(work); @@ -640,7 +640,7 @@ static void destroy_previous_session(struct ksmbd_user *user, u64 id) */ static char * smb2_get_name(struct ksmbd_share_config *share, const char *src, - const int maxlen, struct nls_table *local_nls) + const int maxlen, struct nls_table *local_nls) { char *name, *unixname; @@ -684,8 +684,8 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) rsp_hdr->Id.AsyncId = cpu_to_le64(id); ksmbd_debug(SMB, - "Send interim Response to inform async request id : %d\n", - work->async_id); + "Send interim Response to inform async request id : %d\n", + work->async_id); work->cancel_fn = fn; work->cancel_argv = arg; @@ -759,7 +759,7 @@ static int smb2_get_dos_mode(struct kstat *stat, int attribute) } static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, - __le16 hash_id) + __le16 hash_id) { pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; pneg_ctxt->DataLength = cpu_to_le16(38); @@ -771,7 +771,7 @@ static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, } static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, - __le16 cipher_type) + __le16 cipher_type) { pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; pneg_ctxt->DataLength = cpu_to_le16(4); @@ -781,7 +781,7 @@ static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, } static void build_compression_ctxt(struct smb2_compression_ctx *pneg_ctxt, - __le16 comp_algo) + __le16 comp_algo) { pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; pneg_ctxt->DataLength = @@ -817,7 +817,7 @@ static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) } static void assemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_rsp *rsp) + struct smb2_negotiate_rsp *rsp) { /* +4 is to account for the RFC1001 len field */ char *pneg_ctxt = (char *)rsp + @@ -826,9 +826,9 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, int ctxt_size; ksmbd_debug(SMB, - "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, - conn->preauth_info->Preauth_HashId); + conn->preauth_info->Preauth_HashId); rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); inc_rfc1001_len(rsp, AUTH_GSS_PADDING); ctxt_size = sizeof(struct smb2_preauth_neg_context); @@ -838,9 +838,9 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, if (conn->cipher_type) { ctxt_size = round_up(ctxt_size, 8); ksmbd_debug(SMB, - "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, - conn->cipher_type); + conn->cipher_type); rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ctxt_size += sizeof(struct smb2_encryption_neg_context); /* Round to 8 byte boundary */ @@ -852,10 +852,10 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, if (conn->compress_algorithm) { ctxt_size = round_up(ctxt_size, 8); ksmbd_debug(SMB, - "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); + "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); /* Temporarily set to SMB3_COMPRESS_NONE */ build_compression_ctxt((struct smb2_compression_ctx *)pneg_ctxt, - conn->compress_algorithm); + conn->compress_algorithm); rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ctxt_size += sizeof(struct smb2_compression_ctx); /* Round to 8 byte boundary */ @@ -865,7 +865,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, if (conn->posix_ext_supported) { ctxt_size = round_up(ctxt_size, 8); ksmbd_debug(SMB, - "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ctxt_size += sizeof(struct smb2_posix_neg_context); @@ -875,12 +875,11 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, } static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, - struct smb2_preauth_neg_context *pneg_ctxt) + struct smb2_preauth_neg_context *pneg_ctxt) { __le32 err = STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; - if (pneg_ctxt->HashAlgorithms == - SMB2_PREAUTH_INTEGRITY_SHA512) { + if (pneg_ctxt->HashAlgorithms == SMB2_PREAUTH_INTEGRITY_SHA512) { conn->preauth_info->Preauth_HashId = SMB2_PREAUTH_INTEGRITY_SHA512; err = STATUS_SUCCESS; @@ -890,7 +889,7 @@ static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, } static int decode_encrypt_ctxt(struct ksmbd_conn *conn, - struct smb2_encryption_neg_context *pneg_ctxt) + struct smb2_encryption_neg_context *pneg_ctxt) { int i; int cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); @@ -906,7 +905,7 @@ static int decode_encrypt_ctxt(struct ksmbd_conn *conn, pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_CCM || pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_GCM) { ksmbd_debug(SMB, "Cipher ID = 0x%x\n", - pneg_ctxt->Ciphers[i]); + pneg_ctxt->Ciphers[i]); conn->cipher_type = pneg_ctxt->Ciphers[i]; break; } @@ -922,7 +921,7 @@ out: } static int decode_compress_ctxt(struct ksmbd_conn *conn, - struct smb2_compression_ctx *pneg_ctxt) + struct smb2_compression_ctx *pneg_ctxt) { int algo_cnt = le16_to_cpu(pneg_ctxt->CompressionAlgorithmCount); @@ -937,7 +936,7 @@ static int decode_compress_ctxt(struct ksmbd_conn *conn, } static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_req *req) + struct smb2_negotiate_req *req) { int i = 0; __le32 status = 0; @@ -953,16 +952,16 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, while (i++ < neg_ctxt_cnt) { if (*ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { ksmbd_debug(SMB, - "deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + "deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); if (conn->preauth_info->Preauth_HashId) break; status = decode_preauth_ctxt(conn, - (struct smb2_preauth_neg_context *)pneg_ctxt); + (struct smb2_preauth_neg_context *)pneg_ctxt); pneg_ctxt += DIV_ROUND_UP(sizeof(struct smb2_preauth_neg_context), 8) * 8; } else if (*ContextType == SMB2_ENCRYPTION_CAPABILITIES) { ksmbd_debug(SMB, - "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); if (conn->cipher_type) break; @@ -971,7 +970,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, pneg_ctxt += DIV_ROUND_UP(ctxt_size, 8) * 8; } else if (*ContextType == SMB2_COMPRESSION_CAPABILITIES) { ksmbd_debug(SMB, - "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); + "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); if (conn->compress_algorithm) break; @@ -980,14 +979,14 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, pneg_ctxt += DIV_ROUND_UP(ctxt_size, 8) * 8; } else if (*ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { ksmbd_debug(SMB, - "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); + "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); ctxt_size = sizeof(struct smb2_netname_neg_context); ctxt_size += DIV_ROUND_UP(le16_to_cpu(((struct smb2_netname_neg_context *) - pneg_ctxt)->DataLength), 8) * 8; + pneg_ctxt)->DataLength), 8) * 8; pneg_ctxt += ctxt_size; } else if (*ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { ksmbd_debug(SMB, - "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); conn->posix_ext_supported = true; pneg_ctxt += DIV_ROUND_UP(sizeof(struct smb2_posix_neg_context), 8) * 8; } @@ -1033,7 +1032,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) case SMB311_PROT_ID: conn->preauth_info = kzalloc(sizeof(struct preauth_integrity_info), - GFP_KERNEL); + GFP_KERNEL); if (!conn->preauth_info) { rc = -ENOMEM; rsp->hdr.Status = STATUS_INVALID_PARAMETER; @@ -1043,7 +1042,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) status = deassemble_neg_contexts(conn, req); if (status != STATUS_SUCCESS) { ksmbd_err("deassemble_neg_contexts error(0x%x)\n", - status); + status); rsp->hdr.Status = status; rc = -EINVAL; goto err_out; @@ -1056,8 +1055,8 @@ int smb2_handle_negotiate(struct ksmbd_work *work) } ksmbd_gen_preauth_integrity_hash(conn, - work->request_buf, - conn->preauth_info->Preauth_HashValue); + work->request_buf, + conn->preauth_info->Preauth_HashValue); rsp->NegotiateContextOffset = cpu_to_le32(OFFSET_OF_NEG_CONTEXT); assemble_neg_contexts(conn, rsp); @@ -1082,7 +1081,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) case BAD_PROT_ID: default: ksmbd_debug(SMB, "Server dialect :0x%x not supported\n", - conn->dialect); + conn->dialect); rsp->hdr.Status = STATUS_NOT_SUPPORTED; rc = -EINVAL; goto err_out; @@ -1098,7 +1097,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) if (conn->dialect > SMB20_PROT_ID) { memcpy(conn->ClientGUID, req->ClientGUID, - SMB2_CLIENT_GUID_SIZE); + SMB2_CLIENT_GUID_SIZE); conn->cli_sec_mode = le16_to_cpu(req->SecurityMode); } @@ -1112,17 +1111,17 @@ int smb2_handle_negotiate(struct ksmbd_work *work) rsp->SystemTime = cpu_to_le64(ksmbd_systime()); rsp->ServerStartTime = 0; ksmbd_debug(SMB, "negotiate context offset %d, count %d\n", - le32_to_cpu(rsp->NegotiateContextOffset), - le16_to_cpu(rsp->NegotiateContextCount)); + le32_to_cpu(rsp->NegotiateContextOffset), + le16_to_cpu(rsp->NegotiateContextCount)); rsp->SecurityBufferOffset = cpu_to_le16(128); rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); ksmbd_copy_gss_neg_header(((char *)(&rsp->hdr) + - sizeof(rsp->hdr.smb2_buf_length)) + - le16_to_cpu(rsp->SecurityBufferOffset)); + sizeof(rsp->hdr.smb2_buf_length)) + + le16_to_cpu(rsp->SecurityBufferOffset)); inc_rfc1001_len(rsp, sizeof(struct smb2_negotiate_rsp) - - sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + - AUTH_GSS_LENGTH); + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; conn->use_spnego = true; @@ -1147,13 +1146,13 @@ err_out: } static int alloc_preauth_hash(struct ksmbd_session *sess, - struct ksmbd_conn *conn) + struct ksmbd_conn *conn) { if (sess->Preauth_HashValue) return 0; sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, - PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); + PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); if (!sess->Preauth_HashValue) return -ENOMEM; @@ -1180,7 +1179,7 @@ static int generate_preauth_hash(struct ksmbd_work *work) } static int decode_negotiation_token(struct ksmbd_work *work, - struct negotiate_message *negblob) + struct negotiate_message *negblob) { struct ksmbd_conn *conn = work->conn; struct smb2_sess_setup_req *req; @@ -1203,7 +1202,7 @@ static int decode_negotiation_token(struct ksmbd_work *work, } static int ntlm_negotiate(struct ksmbd_work *work, - struct negotiate_message *negblob) + struct negotiate_message *negblob) { struct smb2_sess_setup_req *req = work->request_buf; struct smb2_sess_setup_rsp *rsp = work->response_buf; @@ -1247,10 +1246,8 @@ static int ntlm_negotiate(struct ksmbd_work *work, goto out; } - rc = build_spnego_ntlmssp_neg_blob(&spnego_blob, - &spnego_blob_len, - neg_blob, - sz); + rc = build_spnego_ntlmssp_neg_blob(&spnego_blob, &spnego_blob_len, + neg_blob, sz); if (rc) { rc = -ENOMEM; goto out; @@ -1267,7 +1264,7 @@ out: } static struct authenticate_message *user_authblob(struct ksmbd_conn *conn, - struct smb2_sess_setup_req *req) + struct smb2_sess_setup_req *req) { int sz; @@ -1280,7 +1277,7 @@ static struct authenticate_message *user_authblob(struct ksmbd_conn *conn, } static struct ksmbd_user *session_user(struct ksmbd_conn *conn, - struct smb2_sess_setup_req *req) + struct smb2_sess_setup_req *req) { struct authenticate_message *authblob; struct ksmbd_user *user; @@ -1396,7 +1393,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) rc = conn->ops->generate_encryptionkey(sess); if (rc) { ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); + "SMB3 encryption key generation failed\n"); rsp->hdr.Status = STATUS_LOGON_FAILURE; return rc; } @@ -1473,7 +1470,7 @@ static int krb5_authenticate(struct ksmbd_work *work) ksmbd_free_user(sess->user); retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, - out_blob, &out_len); + out_blob, &out_len); if (retval) { ksmbd_debug(SMB, "krb5 authentication failed\n"); rsp->hdr.Status = STATUS_LOGON_FAILURE; @@ -1491,7 +1488,7 @@ static int krb5_authenticate(struct ksmbd_work *work) retval = conn->ops->generate_encryptionkey(sess); if (retval) { ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); + "SMB3 encryption key generation failed\n"); rsp->hdr.Status = STATUS_LOGON_FAILURE; return retval; } @@ -1565,7 +1562,7 @@ int smb2_sess_setup(struct ksmbd_work *work) ksmbd_session_register(conn, sess); } else { sess = ksmbd_session_lookup(conn, - le64_to_cpu(req->hdr.SessionId)); + le64_to_cpu(req->hdr.SessionId)); if (!sess) { rc = -ENOENT; rsp->hdr.Status = STATUS_USER_SESSION_DELETED; @@ -1673,7 +1670,8 @@ int smb2_tree_connect(struct ksmbd_work *work) int rc = -EINVAL; treename = smb_strndup_from_utf16(req->Buffer, - le16_to_cpu(req->PathLength), true, conn->local_nls); + le16_to_cpu(req->PathLength), true, + conn->local_nls); if (IS_ERR(treename)) { ksmbd_err("treename is NULL\n"); status.ret = KSMBD_TREE_CONN_STATUS_ERROR; @@ -1687,7 +1685,7 @@ int smb2_tree_connect(struct ksmbd_work *work) } ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", - name, treename); + name, treename); status = ksmbd_tree_conn_connect(sess, name); if (status.ret == KSMBD_TREE_CONN_STATUS_OK) @@ -1772,7 +1770,7 @@ out_err1: * Return: file open flags */ static int smb2_create_open_flags(bool file_present, __le32 access, - __le32 disposition) + __le32 disposition) { int oflags = O_NONBLOCK | O_LARGEFILE; @@ -1910,7 +1908,7 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) char *name; name = smb_strndup_from_utf16(req->Buffer, le16_to_cpu(req->NameLength), - 1, work->conn->local_nls); + 1, work->conn->local_nls); if (IS_ERR(name)) { rsp->hdr.Status = STATUS_NO_MEMORY; err = PTR_ERR(name); @@ -1987,20 +1985,20 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) goto next; ksmbd_debug(SMB, - "name : <%s>, name_len : %u, value_len : %u, next : %u\n", - eabuf->name, eabuf->EaNameLength, - le16_to_cpu(eabuf->EaValueLength), - le32_to_cpu(eabuf->NextEntryOffset)); + "name : <%s>, name_len : %u, value_len : %u, next : %u\n", + eabuf->name, eabuf->EaNameLength, + le16_to_cpu(eabuf->EaValueLength), + le32_to_cpu(eabuf->NextEntryOffset)); if (eabuf->EaNameLength > - (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { + (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { rc = -EINVAL; break; } memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); memcpy(&attr_name[XATTR_USER_PREFIX_LEN], eabuf->name, - eabuf->EaNameLength); + eabuf->EaNameLength); attr_name[XATTR_USER_PREFIX_LEN + eabuf->EaNameLength] = '\0'; value = (char *)&eabuf->name + eabuf->EaNameLength + 1; @@ -2017,8 +2015,8 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) if (rc < 0) { ksmbd_debug(SMB, - "remove xattr failed(%d)\n", - rc); + "remove xattr failed(%d)\n", + rc); break; } } @@ -2027,11 +2025,11 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) rc = 0; } else { rc = ksmbd_vfs_setxattr(path->dentry, attr_name, value, - le16_to_cpu(eabuf->EaValueLength), 0); + le16_to_cpu(eabuf->EaValueLength), 0); if (rc < 0) { ksmbd_debug(SMB, - "ksmbd_vfs_setxattr is failed(%d)\n", - rc); + "ksmbd_vfs_setxattr is failed(%d)\n", + rc); break; } } @@ -2061,7 +2059,8 @@ static inline int check_context_err(void *ctx, char *str) } static noinline int smb2_set_stream_name_xattr(struct path *path, - struct ksmbd_file *fp, char *stream_name, int s_type) + struct ksmbd_file *fp, + char *stream_name, int s_type) { size_t xattr_stream_size; char *xattr_stream_name; @@ -2142,13 +2141,13 @@ static int smb2_create_truncate(struct path *path) rc = 0; if (rc) ksmbd_debug(SMB, - "ksmbd_truncate_stream_name_xattr failed, rc %d\n", - rc); + "ksmbd_truncate_stream_name_xattr failed, rc %d\n", + rc); return rc; } static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, struct path *path, - struct ksmbd_file *fp) + struct ksmbd_file *fp) { struct xattr_dos_attrib da = {0}; int rc; @@ -2169,7 +2168,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, struct path *path, } static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, - struct path *path, struct ksmbd_file *fp) + struct path *path, struct ksmbd_file *fp) { struct xattr_dos_attrib da; int rc; @@ -2190,7 +2189,7 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, } static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, - int open_flags, umode_t posix_mode, bool is_dir) + int open_flags, umode_t posix_mode, bool is_dir) { struct ksmbd_tree_connect *tcon = work->tcon; struct ksmbd_share_config *share = tcon->share_conf; @@ -2220,14 +2219,15 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, rc = ksmbd_vfs_kern_path(name, 0, path, 0); if (rc) { ksmbd_err("cannot get linux path (%s), err = %d\n", - name, rc); + name, rc); return rc; } return 0; } static int smb2_create_sd_buffer(struct ksmbd_work *work, - struct smb2_create_req *req, struct dentry *dentry) + struct smb2_create_req *req, + struct dentry *dentry) { struct create_context *context; int rc = -ENOENT; @@ -2241,10 +2241,10 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work, struct create_sd_buf_req *sd_buf; ksmbd_debug(SMB, - "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); + "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); sd_buf = (struct create_sd_buf_req *)context; rc = set_info_sec(work->conn, work->tcon, dentry, &sd_buf->ntsd, - le32_to_cpu(sd_buf->ccontext.DataLength), true); + le32_to_cpu(sd_buf->ccontext.DataLength), true); } return rc; @@ -2353,7 +2353,7 @@ int smb2_open(struct ksmbd_work *work) if (ksmbd_share_veto_filename(share, name)) { rc = -ENOENT; ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", - name); + name); goto err_out1; } } else { @@ -2376,7 +2376,7 @@ int smb2_open(struct ksmbd_work *work) if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE_LE)) { ksmbd_err("Invalid impersonationlevel : 0x%x\n", - le32_to_cpu(req->ImpersonationLevel)); + le32_to_cpu(req->ImpersonationLevel)); rc = -EIO; rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; goto err_out1; @@ -2384,7 +2384,7 @@ int smb2_open(struct ksmbd_work *work) if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK)) { ksmbd_err("Invalid create options : 0x%x\n", - le32_to_cpu(req->CreateOptions)); + le32_to_cpu(req->CreateOptions)); rc = -EINVAL; goto err_out1; } else { @@ -2392,8 +2392,9 @@ int smb2_open(struct ksmbd_work *work) req->CreateOptions & FILE_RANDOM_ACCESS_LE) req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); - if (req->CreateOptions & (FILE_OPEN_BY_FILE_ID_LE | - CREATE_TREE_CONNECTION | FILE_RESERVE_OPFILTER_LE)) { + if (req->CreateOptions & + (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | + FILE_RESERVE_OPFILTER_LE)) { rc = -EOPNOTSUPP; goto err_out1; } @@ -2409,23 +2410,23 @@ int smb2_open(struct ksmbd_work *work) } if (le32_to_cpu(req->CreateDisposition) > - le32_to_cpu(FILE_OVERWRITE_IF_LE)) { + le32_to_cpu(FILE_OVERWRITE_IF_LE)) { ksmbd_err("Invalid create disposition : 0x%x\n", - le32_to_cpu(req->CreateDisposition)); + le32_to_cpu(req->CreateDisposition)); rc = -EINVAL; goto err_out1; } if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { ksmbd_err("Invalid desired access : 0x%x\n", - le32_to_cpu(req->DesiredAccess)); + le32_to_cpu(req->DesiredAccess)); rc = -EACCES; goto err_out1; } if (req->FileAttributes && !(req->FileAttributes & ATTR_MASK_LE)) { ksmbd_err("Invalid file attribute : 0x%x\n", - le32_to_cpu(req->FileAttributes)); + le32_to_cpu(req->FileAttributes)); rc = -EINVAL; goto err_out1; } @@ -2447,23 +2448,23 @@ int smb2_open(struct ksmbd_work *work) } context = smb2_find_context_vals(req, - SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); if (IS_ERR(context)) { rc = check_context_err(context, - SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); if (rc < 0) goto err_out1; } else { ksmbd_debug(SMB, - "get query maximal access context\n"); + "get query maximal access context\n"); maximal_access_ctxt = 1; } context = smb2_find_context_vals(req, - SMB2_CREATE_TIMEWARP_REQUEST); + SMB2_CREATE_TIMEWARP_REQUEST); if (IS_ERR(context)) { rc = check_context_err(context, - SMB2_CREATE_TIMEWARP_REQUEST); + SMB2_CREATE_TIMEWARP_REQUEST); if (rc < 0) goto err_out1; } else { @@ -2474,10 +2475,10 @@ int smb2_open(struct ksmbd_work *work) if (tcon->posix_extensions) { context = smb2_find_context_vals(req, - SMB2_CREATE_TAG_POSIX); + SMB2_CREATE_TAG_POSIX); if (IS_ERR(context)) { rc = check_context_err(context, - SMB2_CREATE_TAG_POSIX); + SMB2_CREATE_TAG_POSIX); if (rc < 0) goto err_out1; } else { @@ -2516,7 +2517,7 @@ int smb2_open(struct ksmbd_work *work) if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, - "User does not have write permission\n"); + "User does not have write permission\n"); rc = -EACCES; path_put(&path); goto err_out; @@ -2546,11 +2547,11 @@ int smb2_open(struct ksmbd_work *work) if (rc) { if (rc == -EACCES) { ksmbd_debug(SMB, - "User does not have right permission\n"); + "User does not have right permission\n"); goto err_out; } ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", - name, rc); + name, rc); rc = 0; } else { file_present = true; @@ -2582,7 +2583,7 @@ int smb2_open(struct ksmbd_work *work) if (file_present && req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE && S_ISDIR(stat.mode) && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { ksmbd_debug(SMB, "open() argument is a directory: %s, %x\n", - name, req->CreateOptions); + name, req->CreateOptions); rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; rc = -EIO; goto err_out; @@ -2606,7 +2607,7 @@ int smb2_open(struct ksmbd_work *work) if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { rc = smb_check_perm_dacl(conn, path.dentry, &daccess, - sess->user->uid); + sess->user->uid); if (rc) goto err_out; } @@ -2624,13 +2625,13 @@ int smb2_open(struct ksmbd_work *work) maximal_access = daccess; } - open_flags = smb2_create_open_flags(file_present, - daccess, req->CreateDisposition); + open_flags = smb2_create_open_flags(file_present, daccess, + req->CreateDisposition); if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { if (open_flags & O_CREAT) { ksmbd_debug(SMB, - "User does not have write permission\n"); + "User does not have write permission\n"); rc = -EACCES; goto err_out; } @@ -2639,7 +2640,7 @@ int smb2_open(struct ksmbd_work *work) /*create file if not present */ if (!file_present) { rc = smb2_creat(work, &path, name, open_flags, posix_mode, - req->CreateOptions & FILE_DIRECTORY_FILE_LE); + req->CreateOptions & FILE_DIRECTORY_FILE_LE); if (rc) goto err_out; @@ -2663,7 +2664,8 @@ int smb2_open(struct ksmbd_work *work) */ if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { rc = ksmbd_vfs_inode_permission(path.dentry, - open_flags & O_ACCMODE, may_delete); + open_flags & O_ACCMODE, + may_delete); if (rc) goto err_out; } @@ -2689,8 +2691,8 @@ int smb2_open(struct ksmbd_work *work) else file_info = FILE_OVERWRITTEN; - if ((req->CreateDisposition & FILE_CREATE_MASK_LE) - == FILE_SUPERSEDE_LE) + if ((req->CreateDisposition & FILE_CREATE_MASK_LE) == + FILE_SUPERSEDE_LE) file_info = FILE_SUPERSEDED; } else if (open_flags & O_CREAT) { file_info = FILE_CREATED; @@ -2732,7 +2734,7 @@ int smb2_open(struct ksmbd_work *work) if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { rc = smb_inherit_dacl(conn, path.dentry, sess->user->uid, - sess->user->gid); + sess->user->gid); } if (rc) { @@ -2762,17 +2764,21 @@ int smb2_open(struct ksmbd_work *work) goto err_out; rc = build_sec_desc(pntsd, NULL, - OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO, - &pntsd_size, &fattr); + OWNER_SECINFO | + GROUP_SECINFO | + DACL_SECINFO, + &pntsd_size, &fattr); posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); rc = ksmbd_vfs_set_sd_xattr(conn, - path.dentry, pntsd, pntsd_size); + path.dentry, + pntsd, + pntsd_size); kfree(pntsd); if (rc) ksmbd_err("failed to store ntacl in xattr : %d\n", - rc); + rc); } } } @@ -2829,8 +2835,8 @@ int smb2_open(struct ksmbd_work *work) if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { req_op_level = smb2_map_lease_to_oplock(lc->req_state); ksmbd_debug(SMB, - "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", - name, req_op_level, lc->req_state); + "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", + name, req_op_level, lc->req_state); rc = find_same_lease_key(sess, fp->f_ci, lc); if (rc) goto err_out; @@ -2859,12 +2865,11 @@ int smb2_open(struct ksmbd_work *work) if (req->CreateContextsOffset) { struct create_alloc_size_req *az_req; - az_req = (struct create_alloc_size_req *) - smb2_find_context_vals(req, - SMB2_CREATE_ALLOCATION_SIZE); + az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, + SMB2_CREATE_ALLOCATION_SIZE); if (IS_ERR(az_req)) { rc = check_context_err(az_req, - SMB2_CREATE_ALLOCATION_SIZE); + SMB2_CREATE_ALLOCATION_SIZE); if (rc < 0) goto err_out; } else { @@ -2872,13 +2877,13 @@ int smb2_open(struct ksmbd_work *work) int err; ksmbd_debug(SMB, - "request smb2 create allocate size : %llu\n", - alloc_size); + "request smb2 create allocate size : %llu\n", + alloc_size); err = ksmbd_vfs_alloc_size(work, fp, alloc_size); if (err < 0) ksmbd_debug(SMB, - "ksmbd_vfs_alloc_size is failed : %d\n", - err); + "ksmbd_vfs_alloc_size is failed : %d\n", + err); } context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID); @@ -2897,8 +2902,8 @@ int smb2_open(struct ksmbd_work *work) else fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); if (req->FileAttributes || fp->f_ci->m_fattr == 0) - fp->f_ci->m_fattr = cpu_to_le32(smb2_get_dos_mode(&stat, - le32_to_cpu(req->FileAttributes))); + fp->f_ci->m_fattr = + cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); if (!created) smb2_update_xattrs(tcon, &path, fp); @@ -2942,7 +2947,7 @@ int smb2_open(struct ksmbd_work *work) struct create_context *lease_ccontext; ksmbd_debug(SMB, "lease granted on(%s) lease state 0x%x\n", - name, opinfo->o_lease->state); + name, opinfo->o_lease->state); rsp->OplockLevel = SMB2_OPLOCK_LEVEL_LEASE; lease_ccontext = (struct create_context *)rsp->Buffer; @@ -3170,7 +3175,8 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) * Return: 0 on success, otherwise error */ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, - struct ksmbd_dir_info *d_info, struct ksmbd_kstat *ksmbd_kstat) + struct ksmbd_dir_info *d_info, + struct ksmbd_kstat *ksmbd_kstat) { int next_entry_offset = 0; char *conv_name; @@ -3323,9 +3329,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, if (d_info->hide_dot_file && d_info->name[0] == '.') posix_info->DosAttributes |= ATTR_HIDDEN_LE; id_to_sid(from_kuid(&init_user_ns, ksmbd_kstat->kstat->uid), - SIDNFS_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); + SIDNFS_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); id_to_sid(from_kgid(&init_user_ns, ksmbd_kstat->kstat->gid), - SIDNFS_GROUP, (struct smb_sid *)&posix_info->SidBuffer[20]); + SIDNFS_GROUP, (struct smb_sid *)&posix_info->SidBuffer[20]); memcpy(posix_info->name, conv_name, conv_len); posix_info->name_len = cpu_to_le32(conv_len); posix_info->NextEntryOffset = cpu_to_le32(next_entry_offset); @@ -3341,9 +3347,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, kfree(conv_name); ksmbd_debug(SMB, - "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", - info_level, d_info->out_buf_len, - next_entry_offset, d_info->data_count); + "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", + info_level, d_info->out_buf_len, + next_entry_offset, d_info->data_count); return 0; } @@ -3392,8 +3398,8 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv) if (IS_ERR(dent)) { ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n", - priv->d_info->name, - PTR_ERR(dent)); + priv->d_info->name, + PTR_ERR(dent)); continue; } if (unlikely(d_is_negative(dent))) { @@ -3421,7 +3427,7 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv) } static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, - int info_level) + int info_level) { int struct_sz; int conv_len; @@ -3528,7 +3534,7 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, } static int __query_dir(struct dir_context *ctx, const char *name, int namlen, - loff_t offset, u64 ino, unsigned int d_type) + loff_t offset, u64 ino, unsigned int d_type) { struct ksmbd_readdir_data *buf; struct smb2_query_dir_private *priv; @@ -3612,17 +3618,18 @@ int smb2_query_dir(struct ksmbd_work *work) } dir_fp = ksmbd_lookup_fd_slow(work, - le64_to_cpu(req->VolatileFileId), - le64_to_cpu(req->PersistentFileId)); + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); if (!dir_fp) { rc = -EBADF; goto err_out2; } if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || - inode_permission(&init_user_ns, file_inode(dir_fp->filp), MAY_READ | MAY_EXEC)) { + inode_permission(&init_user_ns, file_inode(dir_fp->filp), + MAY_READ | MAY_EXEC)) { ksmbd_err("no right to enumerate directory (%s)\n", - FP_FILENAME(dir_fp)); + FP_FILENAME(dir_fp)); rc = -EACCES; goto err_out2; } @@ -3635,8 +3642,8 @@ int smb2_query_dir(struct ksmbd_work *work) srch_flag = req->Flags; srch_ptr = smb_strndup_from_utf16(req->Buffer, - le16_to_cpu(req->FileNameLength), 1, - conn->local_nls); + le16_to_cpu(req->FileNameLength), 1, + conn->local_nls); if (IS_ERR(srch_ptr)) { ksmbd_debug(SMB, "Search Pattern not found\n"); rc = -EINVAL; @@ -3657,8 +3664,8 @@ int smb2_query_dir(struct ksmbd_work *work) d_info.wptr = (char *)rsp->Buffer; d_info.rptr = (char *)rsp->Buffer; d_info.out_buf_len = (work->response_sz - (get_rfc1002_len(rsp_org) + 4)); - d_info.out_buf_len = min_t(int, d_info.out_buf_len, - le32_to_cpu(req->OutputBufferLength)) - sizeof(struct smb2_query_directory_rsp); + d_info.out_buf_len = min_t(int, d_info.out_buf_len, le32_to_cpu(req->OutputBufferLength)) - + sizeof(struct smb2_query_directory_rsp); d_info.flags = srch_flag; /* @@ -3666,7 +3673,8 @@ int smb2_query_dir(struct ksmbd_work *work) * in first response */ rc = ksmbd_populate_dot_dotdot_entries(work, req->FileInformationClass, - dir_fp, &d_info, srch_ptr, smb2_populate_readdir_entry); + dir_fp, &d_info, srch_ptr, + smb2_populate_readdir_entry); if (rc == -ENOSPC) rc = 0; else if (rc) @@ -3762,7 +3770,7 @@ err_out2: * Return: 0 on success, otherwise error */ static int buffer_check_err(int reqOutputBufferLength, - struct smb2_query_info_rsp *rsp, int infoclass_size) + struct smb2_query_info_rsp *rsp, int infoclass_size) { if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { if (reqOutputBufferLength < infoclass_size) { @@ -3797,8 +3805,7 @@ static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp) inc_rfc1001_len(rsp, sizeof(struct smb2_file_standard_info)); } -static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, - u64 num) +static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num) { struct smb2_file_internal_info *file_info; @@ -3812,8 +3819,8 @@ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, } static int smb2_get_info_file_pipe(struct ksmbd_session *sess, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp) + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) { u64 id; int rc; @@ -3827,22 +3834,22 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, return -ENOENT; ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", - req->FileInfoClass, le64_to_cpu(req->VolatileFileId)); + req->FileInfoClass, le64_to_cpu(req->VolatileFileId)); switch (req->FileInfoClass) { case FILE_STANDARD_INFORMATION: get_standard_info_pipe(rsp); rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, FILE_STANDARD_INFORMATION_SIZE); + rsp, FILE_STANDARD_INFORMATION_SIZE); break; case FILE_INTERNAL_INFORMATION: get_internal_info_pipe(rsp, id); rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, FILE_INTERNAL_INFORMATION_SIZE); + rsp, FILE_INTERNAL_INFORMATION_SIZE); break; default: ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", - req->FileInfoClass); + req->FileInfoClass); rc = -EOPNOTSUPP; } return rc; @@ -3859,8 +3866,8 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, * Return: 0 on success, otherwise error */ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) { struct smb2_ea_info *eainfo, *prev_eainfo; char *name, *ptr, *xattr_list = NULL, *buf; @@ -3883,8 +3890,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, /* need to send all EAs, if no specific EA is requested*/ if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) ksmbd_debug(SMB, - "All EAs are requested but need to send single EA entry in rsp flags 0x%x\n", - le32_to_cpu(req->Flags)); + "All EAs are requested but need to send single EA entry in rsp flags 0x%x\n", + le32_to_cpu(req->Flags)); } buf_free_len = work->response_sz - @@ -3966,7 +3973,7 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) memcpy(eainfo->name, &name[XATTR_USER_PREFIX_LEN], - name_len); + name_len); else memcpy(eainfo->name, name, name_len); @@ -4008,7 +4015,7 @@ out: } static void get_file_access_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_access_info *file_info; @@ -4020,7 +4027,7 @@ static void get_file_access_info(struct smb2_query_info_rsp *rsp, } static int get_file_basic_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_all_info *basic_info; struct kstat stat; @@ -4028,7 +4035,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { ksmbd_err("no right to read the attributes : 0x%x\n", - fp->daccess); + fp->daccess); return -EACCES; } @@ -4051,7 +4058,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, } static unsigned long long get_allocation_size(struct inode *inode, - struct kstat *stat) + struct kstat *stat) { unsigned long long alloc_size = 0; @@ -4066,7 +4073,7 @@ static unsigned long long get_allocation_size(struct inode *inode, } static void get_file_standard_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_standard_info *sinfo; unsigned int delete_pending; @@ -4091,7 +4098,7 @@ static void get_file_standard_info(struct smb2_query_info_rsp *rsp, } static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, - void *rsp_org) + void *rsp_org) { struct smb2_file_alignment_info *file_info; @@ -4104,8 +4111,9 @@ static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, } static int get_file_all_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, - void *rsp_org) + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) { struct ksmbd_conn *conn = work->conn; struct smb2_file_all_info *file_info; @@ -4118,7 +4126,7 @@ static int get_file_all_info(struct ksmbd_work *work, if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n", - fp->daccess); + fp->daccess); return -EACCES; } @@ -4157,11 +4165,8 @@ static int get_file_all_info(struct ksmbd_work *work, file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); file_info->Mode = fp->coption; file_info->AlignmentRequirement = 0; - conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, - filename, - PATH_MAX, - conn->local_nls, - 0); + conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename, + PATH_MAX, conn->local_nls, 0); conv_len *= 2; file_info->FileNameLength = cpu_to_le32(conv_len); rsp->OutputBufferLength = @@ -4172,8 +4177,9 @@ static int get_file_all_info(struct ksmbd_work *work, } static void get_file_alternate_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, - void *rsp_org) + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) { struct ksmbd_conn *conn = work->conn; struct smb2_file_alt_name_info *file_info; @@ -4192,8 +4198,9 @@ static void get_file_alternate_info(struct ksmbd_work *work, } static void get_file_stream_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, - void *rsp_org) + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) { struct ksmbd_conn *conn = work->conn; struct smb2_file_stream_info *file_info; @@ -4236,15 +4243,12 @@ static void get_file_stream_info(struct ksmbd_work *work, break; streamlen = snprintf(stream_buf, streamlen + 1, - ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); + ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); - file_info = (struct smb2_file_stream_info *) - &rsp->Buffer[nbytes]; + file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes]; streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, - stream_buf, - streamlen, - conn->local_nls, - 0); + stream_buf, streamlen, + conn->local_nls, 0); streamlen *= 2; kfree(stream_buf); file_info->StreamNameLength = cpu_to_le32(streamlen); @@ -4260,7 +4264,7 @@ static void get_file_stream_info(struct ksmbd_work *work, file_info = (struct smb2_file_stream_info *) &rsp->Buffer[nbytes]; streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, - "::$DATA", 7, conn->local_nls, 0); + "::$DATA", 7, conn->local_nls, 0); streamlen *= 2; file_info->StreamNameLength = cpu_to_le32(streamlen); file_info->StreamSize = S_ISDIR(stat.mode) ? 0 : @@ -4280,7 +4284,7 @@ out: } static void get_file_internal_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_internal_info *file_info; struct kstat stat; @@ -4294,7 +4298,7 @@ static void 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 ksmbd_file *fp, void *rsp_org) { struct smb2_file_ntwrk_info *file_info; struct inode *inode; @@ -4342,7 +4346,7 @@ static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) } static void get_file_position_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_pos_info *file_info; @@ -4354,7 +4358,7 @@ static void get_file_position_info(struct smb2_query_info_rsp *rsp, } static void get_file_mode_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_mode_info *file_info; @@ -4366,7 +4370,7 @@ static void get_file_mode_info(struct smb2_query_info_rsp *rsp, } static void get_file_compression_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_comp_info *file_info; struct kstat stat; @@ -4387,7 +4391,7 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp, } static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_attr_tag_info *file_info; @@ -4402,13 +4406,12 @@ static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, file_info->ReparseTag = 0; rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); - inc_rfc1001_len(rsp_org, - sizeof(struct smb2_file_attr_tag_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_attr_tag_info)); return 0; } static int find_file_posix_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) + struct ksmbd_file *fp, void *rsp_org) { struct smb311_posix_qinfo *file_info; struct inode *inode = FP_INODE(fp); @@ -4436,8 +4439,8 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, } static int smb2_get_info_file(struct ksmbd_work *work, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) { struct ksmbd_file *fp; int fileinfoclass = 0; @@ -4454,7 +4457,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, if (work->next_smb2_rcv_hdr_off) { if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { ksmbd_debug(SMB, "Compound request set FID = %u\n", - work->compound_fid); + work->compound_fid); id = work->compound_fid; pid = work->compound_pfid; } @@ -4569,8 +4572,8 @@ static int smb2_get_info_file(struct ksmbd_work *work, } static int smb2_get_info_filesystem(struct ksmbd_work *work, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) { struct ksmbd_session *sess = work->sess; struct ksmbd_conn *conn = sess->conn; @@ -4801,8 +4804,8 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, } static int smb2_get_info_sec(struct ksmbd_work *work, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) { struct ksmbd_file *fp; struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; @@ -4815,7 +4818,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO)) { ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", - addition_info); + addition_info); pntsd->revision = cpu_to_le16(1); pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); @@ -4834,7 +4837,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, if (work->next_smb2_rcv_hdr_off) { if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { ksmbd_debug(SMB, "Compound request set FID = %u\n", - work->compound_fid); + work->compound_fid); id = work->compound_fid; pid = work->compound_pfid; } @@ -4901,7 +4904,7 @@ int smb2_query_info(struct ksmbd_work *work) break; default: ksmbd_debug(SMB, "InfoType %d not supported yet\n", - req->InfoType); + req->InfoType); rc = -EOPNOTSUPP; } @@ -4917,7 +4920,7 @@ int smb2_query_info(struct ksmbd_work *work) smb2_set_err_rsp(work); ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", - rc); + rc); return rc; } rsp->StructureSize = cpu_to_le16(9); @@ -5008,8 +5011,8 @@ int smb2_close(struct ksmbd_work *work) goto out; } else { ksmbd_debug(SMB, "Compound request set FID = %u:%u\n", - work->compound_fid, - work->compound_pfid); + work->compound_fid, + work->compound_pfid); volatile_id = work->compound_fid; /* file closed, stored id is not valid anymore */ @@ -5086,8 +5089,8 @@ int smb2_echo(struct ksmbd_work *work) } static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, - struct smb2_file_rename_info *file_info, - struct nls_table *local_nls) + struct smb2_file_rename_info *file_info, + struct nls_table *local_nls) { struct ksmbd_share_config *share = fp->tcon->share_conf; char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; @@ -5111,7 +5114,7 @@ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, old_name++; } else { ksmbd_debug(SMB, "can't get last component in path %s\n", - abs_oldname); + abs_oldname); rc = -ENOENT; goto out; } @@ -5154,7 +5157,7 @@ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, NULL, 0, 0); if (rc < 0) { ksmbd_err("failed to store stream name in xattr: %d\n", - rc); + rc); rc = -EINVAL; goto out; } @@ -5182,7 +5185,7 @@ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, if (rc != -ENOTEMPTY) rc = -EINVAL; ksmbd_debug(SMB, "cannot delete %s, rc %d\n", - new_name, rc); + new_name, rc); goto out; } } @@ -5191,7 +5194,7 @@ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) { rc = -EEXIST; ksmbd_debug(SMB, - "cannot rename already existing file\n"); + "cannot rename already existing file\n"); goto out; } } @@ -5205,9 +5208,10 @@ out: } static int smb2_create_link(struct ksmbd_work *work, - struct ksmbd_share_config *share, - struct smb2_file_link_info *file_info, struct file *filp, - struct nls_table *local_nls) + struct ksmbd_share_config *share, + struct smb2_file_link_info *file_info, + struct file *filp, + struct nls_table *local_nls) { char *link_name = NULL, *target_name = NULL, *pathname = NULL; struct path path; @@ -5248,7 +5252,7 @@ static int smb2_create_link(struct ksmbd_work *work, if (rc) { rc = -EINVAL; ksmbd_debug(SMB, "cannot delete %s\n", - link_name); + link_name); goto out; } } @@ -5271,7 +5275,7 @@ out: } static int set_file_basic_info(struct ksmbd_file *fp, char *buf, - struct ksmbd_share_config *share) + struct ksmbd_share_config *share) { struct smb2_file_all_info *file_info; struct iattr attrs; @@ -5335,7 +5339,7 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, rc = ksmbd_vfs_set_dos_attrib_xattr(filp->f_path.dentry, &da); if (rc) ksmbd_debug(SMB, - "failed to restore file attribute in EA\n"); + "failed to restore file attribute in EA\n"); rc = 0; } @@ -5367,7 +5371,7 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, } static int set_file_allocation_info(struct ksmbd_work *work, - struct ksmbd_file *fp, char *buf) + struct ksmbd_file *fp, char *buf) { /* * TODO : It's working fine only when store dos attributes @@ -5417,7 +5421,7 @@ static int set_file_allocation_info(struct ksmbd_work *work, } static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf) + char *buf) { struct smb2_file_eof_info *file_eof_info; loff_t newsize; @@ -5440,11 +5444,11 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, */ if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { ksmbd_debug(SMB, "filename : %s truncated to newsize %lld\n", - fp->filename, newsize); + fp->filename, newsize); rc = ksmbd_vfs_truncate(work, NULL, fp, newsize); if (rc) { ksmbd_debug(SMB, "truncate failed! filename : %s err %d\n", - fp->filename, rc); + fp->filename, rc); if (rc != -EAGAIN) rc = -EBADF; return rc; @@ -5454,7 +5458,7 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, } static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf) + char *buf) { struct ksmbd_file *parent_fp; @@ -5518,7 +5522,7 @@ static int set_file_position_info(struct ksmbd_file *fp, char *buf) (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && current_byte_offset & (sector_size - 1))) { ksmbd_err("CurrentByteOffset is not valid : %llu\n", - current_byte_offset); + current_byte_offset); return -EINVAL; } @@ -5561,7 +5565,8 @@ static int set_file_mode_info(struct ksmbd_file *fp, char *buf) * 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, - int info_class, char *buf, struct ksmbd_share_config *share) + int info_class, char *buf, + struct ksmbd_share_config *share) { switch (info_class) { case FILE_BASIC_INFORMATION: @@ -5576,20 +5581,20 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, case FILE_RENAME_INFORMATION: if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, - "User does not have write permission\n"); + "User does not have write permission\n"); return -EACCES; } return set_rename_info(work, fp, buf); case FILE_LINK_INFORMATION: return smb2_create_link(work, work->tcon->share_conf, - (struct smb2_file_link_info *)buf, fp->filp, - work->sess->conn->local_nls); + (struct smb2_file_link_info *)buf, fp->filp, + work->sess->conn->local_nls); case FILE_DISPOSITION_INFORMATION: if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, - "User does not have write permission\n"); + "User does not have write permission\n"); return -EACCES; } return set_file_disposition_info(fp, buf); @@ -5618,7 +5623,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, } static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, - char *buffer, int buf_len) + char *buffer, int buf_len) { struct smb_ntsd *pntsd = (struct smb_ntsd *)buffer; @@ -5650,7 +5655,7 @@ int smb2_set_info(struct ksmbd_work *work) rsp = RESPONSE_BUF_NEXT(work); if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { ksmbd_debug(SMB, "Compound request set FID = %u\n", - work->compound_fid); + work->compound_fid); id = work->compound_fid; pid = work->compound_pfid; } @@ -5680,8 +5685,9 @@ int smb2_set_info(struct ksmbd_work *work) case SMB2_O_INFO_SECURITY: ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); rc = smb2_set_info_sec(fp, - le32_to_cpu(req->AdditionalInformation), req->Buffer, - le32_to_cpu(req->BufferLength)); + le32_to_cpu(req->AdditionalInformation), + req->Buffer, + le32_to_cpu(req->BufferLength)); break; default: rc = -EOPNOTSUPP; @@ -5716,8 +5722,7 @@ err_out: rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; smb2_set_err_rsp(work); ksmbd_fd_put(work, fp); - ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", - rc); + ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", rc); return rc; } @@ -5753,7 +5758,7 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) } memcpy(work->aux_payload_buf, rpc_resp->payload, - rpc_resp->payload_sz); + rpc_resp->payload_sz); nbytes = rpc_resp->payload_sz; work->resp_hdr_sz = get_rfc1002_len(rsp) + 4; @@ -5778,7 +5783,8 @@ out: } static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, - struct smb2_read_req *req, void *data_buf, size_t length) + struct smb2_read_req *req, void *data_buf, + size_t length) { struct smb2_buffer_desc_v1 *desc = (struct smb2_buffer_desc_v1 *)&req->Buffer[0]; @@ -5797,8 +5803,9 @@ static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, work->remote_key = le32_to_cpu(desc->token); err = ksmbd_conn_rdma_write(work->conn, data_buf, length, - le32_to_cpu(desc->token), le64_to_cpu(desc->offset), - le32_to_cpu(desc->length)); + le32_to_cpu(desc->token), + le64_to_cpu(desc->offset), + le32_to_cpu(desc->length)); if (err) return err; @@ -5831,9 +5838,8 @@ int smb2_read(struct ksmbd_work *work) return smb2_read_pipe(work); } - fp = ksmbd_lookup_fd_slow(work, - le64_to_cpu(req->VolatileFileId), - le64_to_cpu(req->PersistentFileId)); + fp = ksmbd_lookup_fd_slow(work, le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); if (!fp) { err = -ENOENT; goto out; @@ -5857,7 +5863,7 @@ int smb2_read(struct ksmbd_work *work) } ksmbd_debug(SMB, "filename %s, offset %lld, len %zu\n", FP_FILENAME(fp), - offset, length); + offset, length); if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) { work->aux_payload_buf = @@ -5890,13 +5896,14 @@ int smb2_read(struct ksmbd_work *work) } ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n", - nbytes, offset, mincount); + nbytes, offset, mincount); if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || req->Channel == SMB2_CHANNEL_RDMA_V1) { /* write data to the client using rdma channel */ remain_bytes = smb2_read_rdma_channel(work, req, - work->aux_payload_buf, nbytes); + work->aux_payload_buf, + nbytes); if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) ksmbd_release_buffer(work->aux_payload_buf); else @@ -5972,7 +5979,8 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || (le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req))) { ksmbd_err("invalid write data offset %u, smb_len %u\n", - le16_to_cpu(req->DataOffset), get_rfc1002_len(req)); + le16_to_cpu(req->DataOffset), + get_rfc1002_len(req)); err = -EINVAL; goto out; } @@ -6016,8 +6024,9 @@ out: } static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, - struct smb2_write_req *req, struct ksmbd_file *fp, - loff_t offset, size_t length, bool sync) + struct smb2_write_req *req, + struct ksmbd_file *fp, + loff_t offset, size_t length, bool sync) { struct smb2_buffer_desc_v1 *desc; char *data_buf; @@ -6046,9 +6055,9 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, return -ENOMEM; ret = ksmbd_conn_rdma_read(work->conn, data_buf, length, - le32_to_cpu(desc->token), - le64_to_cpu(desc->offset), - le32_to_cpu(desc->length)); + le32_to_cpu(desc->token), + le64_to_cpu(desc->offset), + le32_to_cpu(desc->length)); if (ret < 0) { kvfree(data_buf); return ret; @@ -6095,7 +6104,7 @@ int smb2_write(struct ksmbd_work *work) } fp = ksmbd_lookup_fd_slow(work, le64_to_cpu(req->VolatileFileId), - le64_to_cpu(req->PersistentFileId)); + le64_to_cpu(req->PersistentFileId)); if (!fp) { err = -ENOENT; goto out; @@ -6123,14 +6132,14 @@ int smb2_write(struct ksmbd_work *work) if (req->Channel != SMB2_CHANNEL_RDMA_V1 && req->Channel != SMB2_CHANNEL_RDMA_V1_INVALIDATE) { if (le16_to_cpu(req->DataOffset) == - (offsetof(struct smb2_write_req, Buffer) - 4)) { + (offsetof(struct smb2_write_req, Buffer) - 4)) { data_buf = (char *)&req->Buffer[0]; } else { if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || (le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req))) { ksmbd_err("invalid write data offset %u, smb_len %u\n", - le16_to_cpu(req->DataOffset), - get_rfc1002_len(req)); + le16_to_cpu(req->DataOffset), + get_rfc1002_len(req)); err = -EINVAL; goto out; } @@ -6144,7 +6153,7 @@ int smb2_write(struct ksmbd_work *work) writethrough = true; ksmbd_debug(SMB, "filename %s, offset %lld, len %zu\n", - FP_FILENAME(fp), offset, length); + FP_FILENAME(fp), offset, length); err = ksmbd_vfs_write(work, fp, data_buf, length, &offset, writethrough, &nbytes); if (err < 0) @@ -6154,8 +6163,8 @@ int smb2_write(struct ksmbd_work *work) * write the data. */ nbytes = smb2_write_rdma_channel(work, req, fp, offset, - le32_to_cpu(req->RemainingBytes), - writethrough); + le32_to_cpu(req->RemainingBytes), + writethrough); if (nbytes < 0) { err = (int)nbytes; goto out; @@ -6209,7 +6218,7 @@ int smb2_flush(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", - le64_to_cpu(req->VolatileFileId)); + le64_to_cpu(req->VolatileFileId)); err = ksmbd_vfs_fsync(work, le64_to_cpu(req->VolatileFileId), @@ -6248,7 +6257,7 @@ int smb2_cancel(struct ksmbd_work *work) struct list_head *command_list; ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n", - hdr->MessageId, hdr->Flags); + hdr->MessageId, hdr->Flags); if (hdr->Flags & SMB2_FLAGS_ASYNC_COMMAND) { command_list = &conn->async_requests; @@ -6256,7 +6265,7 @@ int smb2_cancel(struct ksmbd_work *work) spin_lock(&conn->request_lock); list_for_each(tmp, command_list) { cancel_work = list_entry(tmp, struct ksmbd_work, - async_request_entry); + async_request_entry); chdr = cancel_work->request_buf; if (cancel_work->async_id != @@ -6264,9 +6273,9 @@ int smb2_cancel(struct ksmbd_work *work) continue; ksmbd_debug(SMB, - "smb2 with AsyncId %llu cancelled command = 0x%x\n", - le64_to_cpu(hdr->Id.AsyncId), - le16_to_cpu(chdr->Command)); + "smb2 with AsyncId %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->Id.AsyncId), + le16_to_cpu(chdr->Command)); canceled = 1; break; } @@ -6277,7 +6286,7 @@ int smb2_cancel(struct ksmbd_work *work) spin_lock(&conn->request_lock); list_for_each(tmp, command_list) { cancel_work = list_entry(tmp, struct ksmbd_work, - request_entry); + request_entry); chdr = cancel_work->request_buf; if (chdr->MessageId != hdr->MessageId || @@ -6285,9 +6294,9 @@ int smb2_cancel(struct ksmbd_work *work) continue; ksmbd_debug(SMB, - "smb2 with mid %llu cancelled command = 0x%x\n", - le64_to_cpu(hdr->MessageId), - le16_to_cpu(chdr->Command)); + "smb2 with mid %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->MessageId), + le16_to_cpu(chdr->Command)); canceled = 1; break; } @@ -6346,13 +6355,13 @@ static int smb2_set_flock_flags(struct file_lock *flock, int flags) break; case SMB2_LOCKFLAG_SHARED | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: ksmbd_debug(SMB, - "received shared & fail immediately request\n"); + "received shared & fail immediately request\n"); cmd = F_SETLK; flock->fl_type = F_RDLCK; break; case SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: ksmbd_debug(SMB, - "received exclusive & fail immediately request\n"); + "received exclusive & fail immediately request\n"); cmd = F_SETLK; flock->fl_type = F_WRLCK; break; @@ -6367,7 +6376,8 @@ static int smb2_set_flock_flags(struct file_lock *flock, int flags) } static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, - unsigned int cmd, int flags, struct list_head *lock_list) + unsigned int cmd, int flags, + struct list_head *lock_list) { struct ksmbd_lock *lock; @@ -6430,11 +6440,11 @@ int smb2_lock(struct ksmbd_work *work) ksmbd_debug(SMB, "Received lock request\n"); fp = ksmbd_lookup_fd_slow(work, - le64_to_cpu(req->VolatileFileId), - le64_to_cpu(req->PersistentFileId)); + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); if (!fp) { ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", - le64_to_cpu(req->VolatileFileId)); + le64_to_cpu(req->VolatileFileId)); rsp->hdr.Status = STATUS_FILE_CLOSED; goto out2; } @@ -6444,7 +6454,7 @@ int smb2_lock(struct ksmbd_work *work) lock_ele = req->locks; ksmbd_debug(SMB, "lock count is %d\n", lock_count); - if (!lock_count) { + if (!lock_count) { rsp->hdr.Status = STATUS_INVALID_PARAMETER; goto out2; } @@ -6481,8 +6491,8 @@ int smb2_lock(struct ksmbd_work *work) if (flock->fl_end < flock->fl_start) { ksmbd_debug(SMB, - "the end offset(%llx) is smaller than the start offset(%llx)\n", - flock->fl_end, flock->fl_start); + "the end offset(%llx) is smaller than the start offset(%llx)\n", + flock->fl_end, flock->fl_start); rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; goto out; } @@ -6621,9 +6631,9 @@ skip: void **argv; ksmbd_debug(SMB, - "would have to wait for getting lock\n"); + "would have to wait for getting lock\n"); list_add_tail(&smb_lock->glist, - &global_lock_list); + &global_lock_list); list_add(&smb_lock->llist, &rollback_list); argv = kmalloc(sizeof(void *), GFP_KERNEL); @@ -6634,7 +6644,8 @@ skip: argv[0] = flock; err = setup_async_work(work, - smb2_remove_blocked_lock, argv); + smb2_remove_blocked_lock, + argv); if (err) { rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; @@ -6661,7 +6672,7 @@ skip: STATUS_CANCELLED; kfree(smb_lock); smb2_send_interim_resp(work, - STATUS_CANCELLED); + STATUS_CANCELLED); work->send_no_response = 1; goto out; } @@ -6681,7 +6692,7 @@ skip: goto retry; } else if (!err) { list_add_tail(&smb_lock->glist, - &global_lock_list); + &global_lock_list); list_add(&smb_lock->llist, &rollback_list); ksmbd_debug(SMB, "successful in taking lock\n"); } else { @@ -6734,7 +6745,7 @@ out2: } static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, - struct smb2_ioctl_rsp *rsp) + struct smb2_ioctl_rsp *rsp) { struct copychunk_ioctl_req *ci_req; struct copychunk_ioctl_rsp *ci_rsp; @@ -6785,10 +6796,10 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, } src_fp = ksmbd_lookup_foreign_fd(work, - le64_to_cpu(ci_req->ResumeKey[0])); + le64_to_cpu(ci_req->ResumeKey[0])); dst_fp = ksmbd_lookup_fd_slow(work, - le64_to_cpu(req->VolatileFileId), - le64_to_cpu(req->PersistentFileId)); + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); ret = -EINVAL; if (!src_fp || src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { @@ -6805,16 +6816,17 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, * FILE_READ_DATA should only be included in * the FSCTL_COPYCHUNK case */ - if (cnt_code == FSCTL_COPYCHUNK && !(dst_fp->daccess & - (FILE_READ_DATA_LE | FILE_GENERIC_READ_LE))) { + if (cnt_code == FSCTL_COPYCHUNK && + !(dst_fp->daccess & (FILE_READ_DATA_LE | FILE_GENERIC_READ_LE))) { rsp->hdr.Status = STATUS_ACCESS_DENIED; goto out; } ret = ksmbd_vfs_copy_file_ranges(work, src_fp, dst_fp, - chunks, chunk_count, - &chunk_count_written, &chunk_size_written, - &total_size_written); + chunks, chunk_count, + &chunk_count_written, + &chunk_size_written, + &total_size_written); if (ret < 0) { if (ret == -EACCES) rsp->hdr.Status = STATUS_ACCESS_DENIED; @@ -6862,7 +6874,8 @@ static __be32 idev_ipv4_address(struct in_device *idev) } static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, - struct smb2_ioctl_req *req, struct smb2_ioctl_rsp *rsp) + struct smb2_ioctl_req *req, + struct smb2_ioctl_rsp *rsp) { struct network_interface_info_ioctl_rsp *nii_rsp = NULL; int nbytes = 0; @@ -6905,7 +6918,7 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, speed = cmd.base.speed; } else { ksmbd_err("%s %s\n", netdev->name, - "speed is unknown, defaulting to 1Gb/sec"); + "speed is unknown, defaulting to 1Gb/sec"); speed = SPEED_1000; } @@ -6969,14 +6982,14 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, } static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, - struct validate_negotiate_info_req *neg_req, - struct validate_negotiate_info_rsp *neg_rsp) + struct validate_negotiate_info_req *neg_req, + struct validate_negotiate_info_rsp *neg_rsp) { int ret = 0; int dialect; dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects, - neg_req->DialectCount); + neg_req->DialectCount); if (dialect == BAD_PROT_ID || dialect != conn->dialect) { ret = -EINVAL; goto err_out; @@ -7006,9 +7019,9 @@ err_out: } static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, - struct file_allocated_range_buffer *qar_req, - struct file_allocated_range_buffer *qar_rsp, - int in_count, int *out_count) + struct file_allocated_range_buffer *qar_req, + struct file_allocated_range_buffer *qar_rsp, + int in_count, int *out_count) { struct ksmbd_file *fp; loff_t start, length; @@ -7026,7 +7039,7 @@ static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, length = le64_to_cpu(qar_req->length); ret = ksmbd_vfs_fqar_lseek(fp, start, length, - qar_rsp, in_count, out_count); + qar_rsp, in_count, out_count); if (ret && ret != -E2BIG) *out_count = 0; @@ -7035,15 +7048,15 @@ static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, } static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, - int out_buf_len, struct smb2_ioctl_req *req, - struct smb2_ioctl_rsp *rsp) + int out_buf_len, struct smb2_ioctl_req *req, + struct smb2_ioctl_rsp *rsp) { struct ksmbd_rpc_command *rpc_resp; char *data_buf = (char *)&req->Buffer[0]; int nbytes = 0; rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, - le32_to_cpu(req->InputCount)); + le32_to_cpu(req->InputCount)); if (rpc_resp) { if (rpc_resp->flags == KSMBD_RPC_SOME_NOT_MAPPED) { /* @@ -7079,7 +7092,7 @@ out: } static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, - struct file_sparse *sparse) + struct file_sparse *sparse) { struct ksmbd_file *fp; int ret = 0; @@ -7116,14 +7129,14 @@ out: } static int fsctl_request_resume_key(struct ksmbd_work *work, - struct smb2_ioctl_req *req, - struct resume_key_ioctl_rsp *key_rsp) + struct smb2_ioctl_req *req, + struct resume_key_ioctl_rsp *key_rsp) { struct ksmbd_file *fp; fp = ksmbd_lookup_fd_slow(work, - le64_to_cpu(req->VolatileFileId), - le64_to_cpu(req->PersistentFileId)); + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId)); if (!fp) return -ENOENT; @@ -7157,7 +7170,7 @@ int smb2_ioctl(struct ksmbd_work *work) rsp = RESPONSE_BUF_NEXT(work); if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { ksmbd_debug(SMB, "Compound request set FID = %u\n", - work->compound_fid); + work->compound_fid); id = work->compound_fid; } } else { @@ -7233,7 +7246,7 @@ int smb2_ioctl(struct ksmbd_work *work) } ret = fsctl_request_resume_key(work, req, - (struct resume_key_ioctl_rsp *)&rsp->Buffer[0]); + (struct resume_key_ioctl_rsp *)&rsp->Buffer[0]); if (ret < 0) goto out; rsp->PersistentFileId = req->PersistentFileId; @@ -7244,7 +7257,7 @@ int smb2_ioctl(struct ksmbd_work *work) case FSCTL_COPYCHUNK_WRITE: if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, - "User does not have write permission\n"); + "User does not have write permission\n"); ret = -EACCES; goto out; } @@ -7259,7 +7272,7 @@ int smb2_ioctl(struct ksmbd_work *work) break; case FSCTL_SET_SPARSE: ret = fsctl_set_sparse(work, id, - (struct file_sparse *)&req->Buffer[0]); + (struct file_sparse *)&req->Buffer[0]); if (ret < 0) goto out; break; @@ -7271,7 +7284,7 @@ int smb2_ioctl(struct ksmbd_work *work) if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, - "User does not have write permission\n"); + "User does not have write permission\n"); ret = -EACCES; goto out; } @@ -7338,7 +7351,7 @@ int smb2_ioctl(struct ksmbd_work *work) dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, - dup_ext->PersistentFileHandle); + dup_ext->PersistentFileHandle); if (!fp_in) { ksmbd_err("not found file handle in duplicate extent to file\n"); ret = -ENOENT; @@ -7356,13 +7369,13 @@ int smb2_ioctl(struct ksmbd_work *work) dst_off = le64_to_cpu(dup_ext->TargetFileOffset); length = le64_to_cpu(dup_ext->ByteCount); cloned = vfs_clone_file_range(fp_in->filp, src_off, fp_out->filp, - dst_off, length, 0); + dst_off, length, 0); if (cloned == -EXDEV || cloned == -EOPNOTSUPP) { ret = -EOPNOTSUPP; goto dup_ext_out; } else if (cloned != length) { cloned = ksmbd_vfs_copy_file_range(fp_in->filp, src_off, - fp_out->filp, dst_off, length); + fp_out->filp, dst_off, length); if (cloned != length) { if (cloned < 0) ret = cloned; @@ -7380,7 +7393,7 @@ dup_ext_out: } default: ksmbd_debug(SMB, "not implemented yet ioctl command 0x%x\n", - cnt_code); + cnt_code); ret = -EOPNOTSUPP; goto out; } @@ -7508,7 +7521,7 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) break; default: ksmbd_err("unknown oplock change 0x%x -> 0x%x\n", - opinfo->level, rsp_oplevel); + opinfo->level, rsp_oplevel); } if (ret < 0) { @@ -7573,7 +7586,7 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) struct lease *lease; ksmbd_debug(OPLOCK, "smb21 lease break, lease state(0x%x)\n", - le32_to_cpu(req->LeaseState)); + le32_to_cpu(req->LeaseState)); opinfo = lookup_lease_in_table(conn, req->LeaseKey); if (!opinfo) { ksmbd_debug(OPLOCK, "file not opened\n"); @@ -7585,7 +7598,7 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) if (opinfo->op_state == OPLOCK_STATE_NONE) { ksmbd_err("unexpected lease break state 0x%x\n", - opinfo->op_state); + opinfo->op_state); rsp->hdr.Status = STATUS_UNSUCCESSFUL; goto err_out; } @@ -7593,8 +7606,8 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) if (check_lease_state(lease, req->LeaseState)) { rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; ksmbd_debug(OPLOCK, - "req lease state: 0x%x, expected state: 0x%x\n", - req->LeaseState, lease->new_state); + "req lease state: 0x%x, expected state: 0x%x\n", + req->LeaseState, lease->new_state); goto err_out; } @@ -7604,23 +7617,23 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) } /* check for bad lease state */ - if (req->LeaseState & (~(SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE))) { + if (req->LeaseState & + (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE))) { err = STATUS_INVALID_OPLOCK_PROTOCOL; if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) lease_change_type = OPLOCK_WRITE_TO_NONE; else lease_change_type = OPLOCK_READ_TO_NONE; ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); } else if (lease->state == SMB2_LEASE_READ_CACHING_LE && req->LeaseState != SMB2_LEASE_NONE_LE) { err = STATUS_INVALID_OPLOCK_PROTOCOL; lease_change_type = OPLOCK_READ_TO_NONE; ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); } else { /* valid lease state changes */ err = STATUS_INVALID_DEVICE_STATE; @@ -7654,8 +7667,8 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) break; default: ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); } lease_state = lease->state; @@ -7709,7 +7722,7 @@ int smb2_oplock_break(struct ksmbd_work *work) break; default: ksmbd_debug(OPLOCK, "invalid break cmd %d\n", - le16_to_cpu(req->StructureSize)); + le16_to_cpu(req->StructureSize)); rsp->hdr.Status = STATUS_INVALID_PARAMETER; smb2_set_err_rsp(work); } @@ -7999,7 +8012,7 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE) ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, - conn->preauth_info->Preauth_HashValue); + conn->preauth_info->Preauth_HashValue); if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess && sess->state == SMB2_SESSION_IN_PROGRESS) { @@ -8007,12 +8020,12 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) hash_value = sess->Preauth_HashValue; ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, - hash_value); + hash_value); } } static void fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, char *old_buf, - __le16 cipher_type) + __le16 cipher_type) { struct smb2_hdr *hdr = (struct smb2_hdr *)old_buf; unsigned int orig_len = get_rfc1002_len(old_buf); @@ -8100,14 +8113,14 @@ int smb3_decrypt_req(struct ksmbd_work *work) sess = ksmbd_session_lookup(conn, le64_to_cpu(tr_hdr->SessionId)); if (!sess) { ksmbd_err("invalid session id(%llx) in transform header\n", - le64_to_cpu(tr_hdr->SessionId)); + le64_to_cpu(tr_hdr->SessionId)); return -ECONNABORTED; } - if (pdu_length + 4 < sizeof(struct smb2_transform_hdr) + - sizeof(struct smb2_hdr)) { + if (pdu_length + 4 < + sizeof(struct smb2_transform_hdr) + sizeof(struct smb2_hdr)) { ksmbd_err("Transform message is too small (%u)\n", - pdu_length); + pdu_length); return -ECONNABORTED; } diff --git a/fs/cifsd/smb_common.c b/fs/cifsd/smb_common.c index 985171cbf192..039030968b50 100644 --- a/fs/cifsd/smb_common.c +++ b/fs/cifsd/smb_common.c @@ -98,7 +98,7 @@ int ksmbd_lookup_protocol_idx(char *str) while (offt >= 0) { if (!strncmp(str, smb_protos[offt].prot, len)) { ksmbd_debug(SMB, "selected %s dialect idx = %d\n", - smb_protos[offt].prot, offt); + smb_protos[offt].prot, offt); return smb_protos[offt].index; } offt--; @@ -156,7 +156,7 @@ static bool supported_protocol(int idx) return true; return (server_conf.min_protocol <= idx && - idx <= server_conf.max_protocol); + idx <= server_conf.max_protocol); } static char *next_dialect(char *dialect, int *next_off) @@ -179,12 +179,12 @@ static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) do { dialect = next_dialect(dialect, &next); ksmbd_debug(SMB, "client requested dialect %s\n", - dialect); + dialect); if (!strcmp(dialect, smb_protos[i].name)) { if (supported_protocol(smb_protos[i].index)) { ksmbd_debug(SMB, - "selected %s dialect\n", - smb_protos[i].name); + "selected %s dialect\n", + smb_protos[i].name); if (smb_protos[i].index == SMB1_PROT) return seq_num; return smb_protos[i].prot_id; @@ -207,14 +207,14 @@ int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) count = le16_to_cpu(dialects_count); while (--count >= 0) { ksmbd_debug(SMB, "client requested dialect 0x%x\n", - le16_to_cpu(cli_dialects[count])); + le16_to_cpu(cli_dialects[count])); if (le16_to_cpu(cli_dialects[count]) != smb_protos[i].prot_id) continue; if (supported_protocol(smb_protos[i].index)) { ksmbd_debug(SMB, "selected %s dialect\n", - smb_protos[i].name); + smb_protos[i].name); return smb_protos[i].prot_id; } } @@ -269,9 +269,12 @@ bool ksmbd_pdu_size_has_room(unsigned int pdu) } int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, - struct ksmbd_file *dir, struct ksmbd_dir_info *d_info, - char *search_pattern, int (*fn)(struct ksmbd_conn *, int, - struct ksmbd_dir_info *, struct ksmbd_kstat *)) + struct ksmbd_file *dir, + struct ksmbd_dir_info *d_info, + char *search_pattern, + int (*fn)(struct ksmbd_conn *, int, + struct ksmbd_dir_info *, + struct ksmbd_kstat *)) { int i, rc = 0; struct ksmbd_conn *conn = work->conn; @@ -297,8 +300,8 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, ksmbd_kstat.kstat = &kstat; ksmbd_vfs_fill_dentry_attrs(work, - dir->filp->f_path.dentry->d_parent, - &ksmbd_kstat); + dir->filp->f_path.dentry->d_parent, + &ksmbd_kstat); rc = fn(conn, info_level, d_info, &ksmbd_kstat); if (rc) break; @@ -327,7 +330,7 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, * but the result is different with Windows 7's one. need to check. */ int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, - char *shortname) + char *shortname) { const char *p; char base[9], extension[4]; @@ -390,7 +393,7 @@ int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, else out[baselen + 4] = '\0'; smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX, - conn->local_nls, 0); + conn->local_nls, 0); len = strlen(out) * 2; return len; } @@ -398,7 +401,7 @@ int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, static int __smb2_negotiate(struct ksmbd_conn *conn) { return (conn->dialect >= SMB20_PROT_ID && - conn->dialect <= SMB311_PROT_ID); + conn->dialect <= SMB311_PROT_ID); } static int smb_handle_negotiate(struct ksmbd_work *work) @@ -467,11 +470,11 @@ static const char * const shared_mode_errors[] = { }; static void smb_shared_mode_error(int error, struct ksmbd_file *prev_fp, - struct ksmbd_file *curr_fp) + struct ksmbd_file *curr_fp) { ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]); ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n", - prev_fp->saccess, curr_fp->daccess); + prev_fp->saccess, curr_fp->daccess); } int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) diff --git a/fs/cifsd/smbacl.c b/fs/cifsd/smbacl.c index d65e853ab00f..63db8c015f9d 100644 --- a/fs/cifsd/smbacl.c +++ b/fs/cifsd/smbacl.c @@ -131,7 +131,7 @@ static void smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) * bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 */ static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags, - int type) + int type) { __u32 flags = le32_to_cpu(ace_flags); umode_t mode = 0; @@ -166,7 +166,7 @@ static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags, * with either owner or group or everyone. */ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, - __u32 *pace_flags) + __u32 *pace_flags) { /* reset access mask */ *pace_flags = 0x0; @@ -187,12 +187,12 @@ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, *pace_flags |= SET_FILE_EXEC_RIGHTS; ksmbd_debug(SMB, "mode: %o, access flags now 0x%x\n", - mode, *pace_flags); + mode, *pace_flags); } static __u16 fill_ace_for_sid(struct smb_ace *pntace, - const struct smb_sid *psid, int type, int flags, - umode_t mode, umode_t bits) + const struct smb_sid *psid, int type, int flags, + umode_t mode, umode_t bits) { int i; __u16 size = 0; @@ -255,7 +255,7 @@ void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid) } static int sid_to_id(struct smb_sid *psid, uint sidtype, - struct smb_fattr *fattr) + struct smb_fattr *fattr) { int rc = -EINVAL; @@ -265,7 +265,7 @@ static int sid_to_id(struct smb_sid *psid, uint sidtype, */ if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { ksmbd_err("%s: %u subauthorities is too many!\n", - __func__, psid->num_subauth); + __func__, psid->num_subauth); return -EIO; } @@ -299,7 +299,7 @@ static int sid_to_id(struct smb_sid *psid, uint sidtype, } void posix_state_to_acl(struct posix_acl_state *state, - struct posix_acl_entry *pace) + struct posix_acl_entry *pace) { int i; @@ -364,8 +364,8 @@ void free_acl_state(struct posix_acl_state *state) } static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, - struct smb_sid *pownersid, struct smb_sid *pgrpsid, - struct smb_fattr *fattr) + struct smb_sid *pownersid, struct smb_sid *pgrpsid, + struct smb_fattr *fattr) { int i, ret; int num_aces = 0; @@ -388,8 +388,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, } ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", - le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), - le32_to_cpu(pdacl->num_aces)); + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), + le32_to_cpu(pdacl->num_aces)); acl_base = (char *)pdacl; acl_size = sizeof(struct smb_acl); @@ -401,8 +401,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, if (num_aces > ULONG_MAX / sizeof(struct smb_ace *)) return; - ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), - GFP_KERNEL); + ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); if (!ppace) return; @@ -433,7 +432,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, break; } else if (!compare_sids(&ppace[i]->sid, pownersid)) { acl_mode = access_flags_to_mode(fattr, - ppace[i]->access_req, ppace[i]->type); + ppace[i]->access_req, + ppace[i]->type); acl_mode &= 0700; if (!owner_found) { @@ -445,7 +445,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == DOMAIN_USER_RID_LE) { acl_mode = access_flags_to_mode(fattr, - ppace[i]->access_req, ppace[i]->type); + ppace[i]->access_req, + ppace[i]->type); acl_mode &= 0070; if (!group_found) { mode &= ~(0070); @@ -454,7 +455,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, group_found = true; } else if (!compare_sids(&ppace[i]->sid, &sid_everyone)) { acl_mode = access_flags_to_mode(fattr, - ppace[i]->access_req, ppace[i]->type); + ppace[i]->access_req, + ppace[i]->type); acl_mode &= 0007; if (!others_found) { mode &= ~(0007); @@ -471,12 +473,12 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, struct smb_fattr temp_fattr; acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, - ppace[i]->type); + ppace[i]->type); temp_fattr.cf_uid = INVALID_UID; ret = sid_to_id(&ppace[i]->sid, SIDOWNER, &temp_fattr); if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { ksmbd_err("%s: Error %d mapping Owner SID to uid\n", - __func__, ret); + __func__, ret); continue; } @@ -553,7 +555,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, } static void set_posix_acl_entries_dacl(struct smb_ace *pndace, - struct smb_fattr *fattr, u32 *num_aces, u16 *size, u32 nt_aces_num) + struct smb_fattr *fattr, u32 *num_aces, + u16 *size, u32 nt_aces_num) { struct posix_acl_entry *pace; struct smb_sid *sid; @@ -665,8 +668,9 @@ posix_default_acl: } static void set_ntacl_dacl(struct smb_acl *pndacl, struct smb_acl *nt_dacl, - const struct smb_sid *pownersid, const struct smb_sid *pgrpsid, - struct smb_fattr *fattr) + const struct smb_sid *pownersid, + const struct smb_sid *pgrpsid, + struct smb_fattr *fattr) { struct smb_ace *ntace, *pndace; int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; @@ -711,7 +715,7 @@ static void set_mode_dacl(struct smb_acl *pndacl, struct smb_fattr *fattr) else sid = &sid_unix_users; ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0, - fattr->cf_mode, 0700); + fattr->cf_mode, 0700); pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; pace->size = cpu_to_le16(ace_size + 4); @@ -720,7 +724,7 @@ static void set_mode_dacl(struct smb_acl *pndacl, struct smb_fattr *fattr) /* Group RID */ ace_size = fill_ace_for_sid(pace, &sid_unix_groups, - ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); + ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(from_kgid(&init_user_ns, fattr->cf_gid)); pace->size = cpu_to_le16(ace_size + 4); @@ -733,20 +737,20 @@ static void set_mode_dacl(struct smb_acl *pndacl, struct smb_fattr *fattr) /* creator owner */ size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED, - 0x0b, fattr->cf_mode, 0700); + 0x0b, fattr->cf_mode, 0700); pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; pace = (struct smb_ace *)((char *)pndace + size); /* creator group */ size += fill_ace_for_sid(pace, &creator_group, ACCESS_ALLOWED, - 0x0b, fattr->cf_mode, 0070); + 0x0b, fattr->cf_mode, 0070); pace = (struct smb_ace *)((char *)pndace + size); num_aces = 5; } /* other */ size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0, - fattr->cf_mode, 0007); + fattr->cf_mode, 0007); out: pndacl->num_aces = cpu_to_le32(num_aces); @@ -769,7 +773,7 @@ static int parse_sid(struct smb_sid *psid, char *end_of_acl) /* Convert CIFS ACL to POSIX form */ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, - struct smb_fattr *fattr) + struct smb_fattr *fattr) { int rc = 0; struct smb_sid *owner_sid_ptr, *group_sid_ptr; @@ -788,10 +792,10 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, dacloffset = le32_to_cpu(pntsd->dacloffset); dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); ksmbd_debug(SMB, - "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", - pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), - le32_to_cpu(pntsd->gsidoffset), - le32_to_cpu(pntsd->sacloffset), dacloffset); + "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", + pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), + le32_to_cpu(pntsd->gsidoffset), + le32_to_cpu(pntsd->sacloffset), dacloffset); pntsd_type = le16_to_cpu(pntsd->type); if (!(pntsd_type & DACL_PRESENT)) { @@ -811,7 +815,7 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, rc = sid_to_id(owner_sid_ptr, SIDOWNER, fattr); if (rc) { ksmbd_err("%s: Error %d mapping Owner SID to uid\n", - __func__, rc); + __func__, rc); owner_sid_ptr = NULL; } } @@ -820,19 +824,18 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, rc = parse_sid(group_sid_ptr, end_of_acl); if (rc) { ksmbd_err("%s: Error %d mapping Owner SID to gid\n", - __func__, rc); + __func__, rc); return rc; } rc = sid_to_id(group_sid_ptr, SIDUNIX_GROUP, fattr); if (rc) { ksmbd_err("%s: Error %d mapping Group SID to gid\n", - __func__, rc); + __func__, rc); group_sid_ptr = NULL; } } - if ((pntsd_type & - (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) == + if ((pntsd_type & (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) == (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); if (pntsd_type & DACL_PROTECTED) @@ -840,7 +843,7 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, if (dacloffset) { parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr, - fattr); + fattr); } return 0; @@ -848,7 +851,8 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, /* Convert permission bits from mode to equivalent CIFS ACL */ int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, - int addition_info, __u32 *secdesclen, struct smb_fattr *fattr) + int addition_info, __u32 *secdesclen, + struct smb_fattr *fattr) { int rc = 0; __u32 offset; @@ -929,7 +933,7 @@ out: } static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, - u8 flags, __le32 access_req) + u8 flags, __le32 access_req) { ace->type = type; ace->flags = flags; @@ -939,7 +943,7 @@ static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, } int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, - unsigned int uid, unsigned int gid) + unsigned int uid, unsigned int gid) { const struct smb_sid *psid, *creator = NULL; struct smb_ace *parent_aces, *aces; @@ -1003,7 +1007,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) { smb_set_ace(aces, psid, parent_aces->type, inherited_flags, - parent_aces->access_req); + parent_aces->access_req); nt_size += le16_to_cpu(aces->size); ace_cnt++; aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); @@ -1014,7 +1018,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, } smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, - parent_aces->access_req); + parent_aces->access_req); nt_size += le16_to_cpu(aces->size); aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); ace_cnt++; @@ -1107,7 +1111,7 @@ bool smb_inherit_flags(int flags, bool is_dir) } int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, - __le32 *pdaccess, int uid) + __le32 *pdaccess, int uid) { struct smb_ntsd *pntsd = NULL; struct smb_acl *pdacl; @@ -1243,10 +1247,10 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, } check_access_bits: - if (granted & ~(access_bits | FILE_READ_ATTRIBUTES | - READ_CONTROL | WRITE_DAC | DELETE)) { + if (granted & + ~(access_bits | FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | DELETE)) { ksmbd_debug(SMB, "Access denied with winACL, granted : %x, access_req : %x\n", - granted, le32_to_cpu(ace->access_req)); + granted, le32_to_cpu(ace->access_req)); rc = -EACCES; goto err_out; } @@ -1258,8 +1262,8 @@ err_out: } int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, - struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len, - bool type_check) + struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check) { int rc; struct smb_fattr fattr = {{0}}; @@ -1284,10 +1288,10 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, /* Update posix acls */ if (fattr.cf_dacls) { rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, - fattr.cf_acls); + fattr.cf_acls); if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, - fattr.cf_dacls); + fattr.cf_dacls); } /* Check it only calling from SD BUFFER context */ diff --git a/fs/cifsd/transport_ipc.c b/fs/cifsd/transport_ipc.c index 78061fecf816..b09df832431f 100644 --- a/fs/cifsd/transport_ipc.c +++ b/fs/cifsd/transport_ipc.c @@ -269,7 +269,7 @@ static int handle_response(int type, void *payload, size_t sz) */ if (entry->type + 1 != type) { ksmbd_err("Waiting for IPC type %d, got %d. Ignore.\n", - entry->type + 1, type); + entry->type + 1, type); } entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); @@ -315,9 +315,8 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req) req->ifc_list_sz); if (ret) { ksmbd_err("Server configuration error: %s %s %s\n", - req->netbios_name, - req->server_string, - req->work_group); + req->netbios_name, req->server_string, + req->work_group); return ret; } @@ -547,9 +546,9 @@ ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) struct ksmbd_tree_connect_response * ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, - struct ksmbd_share_config *share, - struct ksmbd_tree_connect *tree_conn, - struct sockaddr *peer_addr) + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr) { struct ksmbd_ipc_msg *msg; struct ksmbd_tree_connect_request *req; @@ -588,7 +587,7 @@ ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, } int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, - unsigned long long connect_id) + unsigned long long connect_id) { struct ksmbd_ipc_msg *msg; struct ksmbd_tree_disconnect_request *req; @@ -700,7 +699,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle } struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz) + void *payload, size_t payload_sz) { struct ksmbd_ipc_msg *msg; struct ksmbd_rpc_command *req; @@ -748,7 +747,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) } struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz) + void *payload, size_t payload_sz) { struct ksmbd_ipc_msg *msg; struct ksmbd_rpc_command *req; @@ -773,7 +772,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle } struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, - size_t payload_sz) + size_t payload_sz) { struct ksmbd_ipc_msg *msg; struct ksmbd_rpc_command *req; diff --git a/fs/cifsd/transport_ipc.h b/fs/cifsd/transport_ipc.h index c3744ed7a085..523b4df2c783 100644 --- a/fs/cifsd/transport_ipc.h +++ b/fs/cifsd/transport_ipc.h @@ -23,31 +23,24 @@ ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, struct ksmbd_share_config *share, struct ksmbd_tree_connect *tree_conn, struct sockaddr *peer_addr); - int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, unsigned long long connect_id); int ksmbd_ipc_logout_request(const char *account); - struct ksmbd_share_config_response * ksmbd_ipc_share_config_request(const char *name); - struct ksmbd_spnego_authen_response * ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len); - int ksmbd_ipc_id_alloc(void); void ksmbd_rpc_id_free(int handle); - struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle); struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle); - struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz); + void *payload, size_t payload_sz); struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle); struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz); + void *payload, size_t payload_sz); struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, - size_t payload_sz); - + size_t payload_sz); void ksmbd_ipc_release(void); void ksmbd_ipc_soft_reset(void); int ksmbd_ipc_init(void); diff --git a/fs/cifsd/transport_rdma.c b/fs/cifsd/transport_rdma.c index 8174a97bade4..efaa9776841f 100644 --- a/fs/cifsd/transport_rdma.c +++ b/fs/cifsd/transport_rdma.c @@ -212,8 +212,9 @@ struct smb_direct_rdma_rw_msg { static void smb_direct_destroy_pools(struct smb_direct_transport *transport); static void smb_direct_post_recv_credits(struct work_struct *work); static int smb_direct_post_send_data(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct kvec *iov, int niov, int remaining_data_length); + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, + int remaining_data_length); static inline void *smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg) @@ -222,7 +223,7 @@ static inline void } static inline bool is_receive_credit_post_required(int receive_credits, - int avail_recvmsg_count) + int avail_recvmsg_count) { return receive_credits <= (smb_direct_receive_credit_max >> 3) && avail_recvmsg_count >= (receive_credits >> 2); @@ -245,10 +246,10 @@ smb_direct_recvmsg *get_free_recvmsg(struct smb_direct_transport *t) } static void put_recvmsg(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) + struct smb_direct_recvmsg *recvmsg) { ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, - recvmsg->sge.length, DMA_FROM_DEVICE); + recvmsg->sge.length, DMA_FROM_DEVICE); spin_lock(&t->recvmsg_queue_lock); list_add(&recvmsg->list, &t->recvmsg_queue); @@ -263,7 +264,7 @@ smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) spin_lock(&t->empty_recvmsg_queue_lock); if (!list_empty(&t->empty_recvmsg_queue)) { recvmsg = list_first_entry(&t->empty_recvmsg_queue, - struct smb_direct_recvmsg, list); + struct smb_direct_recvmsg, list); list_del(&recvmsg->list); } spin_unlock(&t->empty_recvmsg_queue_lock); @@ -271,10 +272,10 @@ smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) } static void put_empty_recvmsg(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) + struct smb_direct_recvmsg *recvmsg) { ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, - recvmsg->sge.length, DMA_FROM_DEVICE); + recvmsg->sge.length, DMA_FROM_DEVICE); spin_lock(&t->empty_recvmsg_queue_lock); list_add_tail(&recvmsg->list, &t->empty_recvmsg_queue); @@ -282,7 +283,8 @@ static void put_empty_recvmsg(struct smb_direct_transport *t, } static void enqueue_reassembly(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg, int data_length) + struct smb_direct_recvmsg *recvmsg, + int data_length) { spin_lock(&t->reassembly_queue_lock); list_add_tail(&recvmsg->list, &t->reassembly_queue); @@ -398,9 +400,9 @@ static void free_transport(struct smb_direct_transport *t) ksmbd_debug(RDMA, "wait for all send posted to IB to finish\n"); wait_event(t->wait_send_payload_pending, - atomic_read(&t->send_payload_pending) == 0); + atomic_read(&t->send_payload_pending) == 0); wait_event(t->wait_send_pending, - atomic_read(&t->send_pending) == 0); + atomic_read(&t->send_pending) == 0); cancel_work_sync(&t->disconnect_work); cancel_delayed_work_sync(&t->post_recv_credits_work); @@ -454,18 +456,18 @@ static struct smb_direct_sendmsg } static void smb_direct_free_sendmsg(struct smb_direct_transport *t, - struct smb_direct_sendmsg *msg) + struct smb_direct_sendmsg *msg) { int i; if (msg->num_sge > 0) { ib_dma_unmap_single(t->cm_id->device, - msg->sge[0].addr, msg->sge[0].length, - DMA_TO_DEVICE); + msg->sge[0].addr, msg->sge[0].length, + DMA_TO_DEVICE); for (i = 1; i < msg->num_sge; i++) ib_dma_unmap_page(t->cm_id->device, - msg->sge[i].addr, msg->sge[i].length, - DMA_TO_DEVICE); + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); } mempool_free(msg, t->sendmsg_mempool); } @@ -479,24 +481,24 @@ static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet + le32_to_cpu(req->data_offset) - 4); ksmbd_debug(RDMA, - "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", - le16_to_cpu(req->credits_granted), - le16_to_cpu(req->credits_requested), - req->data_length, req->remaining_data_length, - hdr->ProtocolId, hdr->Command); + "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", + le16_to_cpu(req->credits_granted), + le16_to_cpu(req->credits_requested), + req->data_length, req->remaining_data_length, + hdr->ProtocolId, hdr->Command); break; } case SMB_DIRECT_MSG_NEGOTIATE_REQ: { struct smb_direct_negotiate_req *req = (struct smb_direct_negotiate_req *)recvmsg->packet; ksmbd_debug(RDMA, - "MinVersion: %u, MaxVersion: %u, CreditRequested: %u, MaxSendSize: %u, MaxRecvSize: %u, MaxFragmentedSize: %u\n", - le16_to_cpu(req->min_version), - le16_to_cpu(req->max_version), - le16_to_cpu(req->credits_requested), - le32_to_cpu(req->preferred_send_size), - le32_to_cpu(req->max_receive_size), - le32_to_cpu(req->max_fragmented_size)); + "MinVersion: %u, MaxVersion: %u, CreditRequested: %u, MaxSendSize: %u, MaxRecvSize: %u, MaxFragmentedSize: %u\n", + le16_to_cpu(req->min_version), + le16_to_cpu(req->max_version), + le16_to_cpu(req->credits_requested), + le32_to_cpu(req->preferred_send_size), + le32_to_cpu(req->max_receive_size), + le32_to_cpu(req->max_fragmented_size)); if (le16_to_cpu(req->min_version) > 0x0100 || le16_to_cpu(req->max_version) < 0x0100) return -EOPNOTSUPP; @@ -525,8 +527,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { if (wc->status != IB_WC_WR_FLUSH_ERR) { ksmbd_err("Recv error. status='%s (%d)' opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); smb_direct_disconnect_rdma_connection(t); } put_empty_recvmsg(t, recvmsg); @@ -534,11 +536,11 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) } ksmbd_debug(RDMA, "Recv completed. status='%s (%d)', opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); ib_dma_sync_single_for_cpu(wc->qp->device, recvmsg->sge.addr, - recvmsg->sge.length, DMA_FROM_DEVICE); + recvmsg->sge.length, DMA_FROM_DEVICE); switch (recvmsg->type) { case SMB_DIRECT_MSG_NEGOTIATE_REQ: @@ -580,10 +582,10 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) t->recv_credit_target = le16_to_cpu(data_transfer->credits_requested); atomic_add(le16_to_cpu(data_transfer->credits_granted), - &t->send_credits); + &t->send_credits); if (le16_to_cpu(data_transfer->flags) & - SMB_DIRECT_RESPONSE_REQUESTED) + SMB_DIRECT_RESPONSE_REQUESTED) queue_work(smb_direct_wq, &t->send_immediate_work); if (atomic_read(&t->send_credits) > 0) @@ -591,7 +593,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) if (is_receive_credit_post_required(receive_credits, avail_recvmsg_count)) mod_delayed_work(smb_direct_wq, - &t->post_recv_credits_work, 0); + &t->post_recv_credits_work, 0); break; } default: @@ -600,14 +602,14 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) } static int smb_direct_post_recv(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) + struct smb_direct_recvmsg *recvmsg) { struct ib_recv_wr wr; int ret; recvmsg->sge.addr = ib_dma_map_single(t->cm_id->device, - recvmsg->packet, t->max_recv_size, - DMA_FROM_DEVICE); + recvmsg->packet, t->max_recv_size, + DMA_FROM_DEVICE); ret = ib_dma_mapping_error(t->cm_id->device, recvmsg->sge.addr); if (ret) return ret; @@ -624,8 +626,8 @@ static int smb_direct_post_recv(struct smb_direct_transport *t, if (ret) { ksmbd_err("Can't post recv: %d\n", ret); ib_dma_unmap_single(t->cm_id->device, - recvmsg->sge.addr, recvmsg->sge.length, - DMA_FROM_DEVICE); + recvmsg->sge.addr, recvmsg->sge.length, + DMA_FROM_DEVICE); smb_direct_disconnect_rdma_connection(t); return ret; } @@ -633,7 +635,7 @@ static int smb_direct_post_recv(struct smb_direct_transport *t, } static int smb_direct_read(struct ksmbd_transport *t, char *buf, - unsigned int size) + unsigned int size) { struct smb_direct_recvmsg *recvmsg; struct smb_direct_data_transfer *data_transfer; @@ -692,14 +694,14 @@ again: data_read = 4; recvmsg->first_segment = false; ksmbd_debug(RDMA, - "returning rfc1002 length %d\n", - rfc1002_len); + "returning rfc1002 length %d\n", + rfc1002_len); goto read_rfc1002_done; } to_copy = min_t(int, data_length - offset, to_read); memcpy(buf + data_read, (char *)data_transfer + data_offset + offset, - to_copy); + to_copy); /* move on to the next buffer? */ if (to_copy == data_length - offset) { @@ -736,23 +738,24 @@ again: if (is_receive_credit_post_required(st->recv_credits, st->count_avail_recvmsg)) { spin_unlock(&st->receive_credit_lock); mod_delayed_work(smb_direct_wq, - &st->post_recv_credits_work, 0); + &st->post_recv_credits_work, 0); } else { spin_unlock(&st->receive_credit_lock); } st->first_entry_offset = offset; ksmbd_debug(RDMA, - "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", - data_read, st->reassembly_data_length, - st->first_entry_offset); + "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", + data_read, st->reassembly_data_length, + st->first_entry_offset); read_rfc1002_done: return data_read; } ksmbd_debug(RDMA, "wait_event on more data\n"); rc = wait_event_interruptible(st->wait_reassembly_queue, - st->reassembly_data_length >= size || st->status != SMB_DIRECT_CS_CONNECTED); + st->reassembly_data_length >= size || + st->status != SMB_DIRECT_CS_CONNECTED); if (rc) return -EINTR; @@ -823,13 +826,13 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) t = sendmsg->transport; ksmbd_debug(RDMA, "Send completed. status='%s (%d)', opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { ksmbd_err("Send error. status='%s (%d)', opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); smb_direct_disconnect_rdma_connection(t); } @@ -845,7 +848,7 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) * is invalid. */ for (pos = &sendmsg->list, prev = pos->prev, end = sendmsg->list.next; - prev != end; pos = prev, prev = prev->prev) { + prev != end; pos = prev, prev = prev->prev) { sibling = container_of(pos, struct smb_direct_sendmsg, list); smb_direct_free_sendmsg(t, sibling); } @@ -867,7 +870,7 @@ static int manage_credits_prior_sending(struct smb_direct_transport *t) } static int smb_direct_post_send(struct smb_direct_transport *t, - struct ib_send_wr *wr) + struct ib_send_wr *wr) { int ret; @@ -892,8 +895,9 @@ static int smb_direct_post_send(struct smb_direct_transport *t, } static void smb_direct_send_ctx_init(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - bool need_invalidate_rkey, unsigned int remote_key) + struct smb_direct_send_ctx *send_ctx, + bool need_invalidate_rkey, + unsigned int remote_key) { INIT_LIST_HEAD(&send_ctx->msg_list); send_ctx->wr_cnt = 0; @@ -902,7 +906,8 @@ static void smb_direct_send_ctx_init(struct smb_direct_transport *t, } static int smb_direct_flush_send_list(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, bool is_last) + struct smb_direct_send_ctx *send_ctx, + bool is_last) { struct smb_direct_sendmsg *first, *last; int ret; @@ -911,11 +916,11 @@ static int smb_direct_flush_send_list(struct smb_direct_transport *t, return 0; first = list_first_entry(&send_ctx->msg_list, - struct smb_direct_sendmsg, - list); + struct smb_direct_sendmsg, + list); last = list_last_entry(&send_ctx->msg_list, - struct smb_direct_sendmsg, - list); + struct smb_direct_sendmsg, + list); last->wr.send_flags = IB_SEND_SIGNALED; last->wr.wr_cqe = &last->cqe; @@ -927,12 +932,13 @@ static int smb_direct_flush_send_list(struct smb_direct_transport *t, ret = smb_direct_post_send(t, &first->wr); if (!ret) { smb_direct_send_ctx_init(t, send_ctx, - send_ctx->need_invalidate_rkey, send_ctx->remote_key); + send_ctx->need_invalidate_rkey, + send_ctx->remote_key); } else { atomic_add(send_ctx->wr_cnt, &t->send_credits); wake_up(&t->wait_send_credits); list_for_each_entry_safe(first, last, &send_ctx->msg_list, - list) { + list) { smb_direct_free_sendmsg(t, first); } } @@ -940,7 +946,7 @@ static int smb_direct_flush_send_list(struct smb_direct_transport *t, } static int wait_for_credits(struct smb_direct_transport *t, - wait_queue_head_t *waitq, atomic_t *credits) + wait_queue_head_t *waitq, atomic_t *credits) { int ret; @@ -950,8 +956,8 @@ static int wait_for_credits(struct smb_direct_transport *t, atomic_inc(credits); ret = wait_event_interruptible(*waitq, - atomic_read(credits) > 0 || - t->status != SMB_DIRECT_CS_CONNECTED); + atomic_read(credits) > 0 || + t->status != SMB_DIRECT_CS_CONNECTED); if (t->status != SMB_DIRECT_CS_CONNECTED) return -ENOTCONN; @@ -961,12 +967,12 @@ static int wait_for_credits(struct smb_direct_transport *t, } static int wait_for_send_credits(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx) + struct smb_direct_send_ctx *send_ctx) { int ret; - if (send_ctx && (send_ctx->wr_cnt >= 16 || - atomic_read(&t->send_credits) <= 1)) { + if (send_ctx && + (send_ctx->wr_cnt >= 16 || atomic_read(&t->send_credits) <= 1)) { ret = smb_direct_flush_send_list(t, send_ctx, false); if (ret) return ret; @@ -976,8 +982,8 @@ static int wait_for_send_credits(struct smb_direct_transport *t, } static int smb_direct_create_header(struct smb_direct_transport *t, - int size, int remaining_data_length, - struct smb_direct_sendmsg **sendmsg_out) + int size, int remaining_data_length, + struct smb_direct_sendmsg **sendmsg_out) { struct smb_direct_sendmsg *sendmsg; struct smb_direct_data_transfer *packet; @@ -1004,12 +1010,12 @@ static int smb_direct_create_header(struct smb_direct_transport *t, packet->padding = 0; ksmbd_debug(RDMA, - "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", - le16_to_cpu(packet->credits_requested), - le16_to_cpu(packet->credits_granted), - le32_to_cpu(packet->data_offset), - le32_to_cpu(packet->data_length), - le32_to_cpu(packet->remaining_data_length)); + "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", + le16_to_cpu(packet->credits_requested), + le16_to_cpu(packet->credits_granted), + le32_to_cpu(packet->data_offset), + le32_to_cpu(packet->data_length), + le32_to_cpu(packet->remaining_data_length)); /* Map the packet to DMA */ header_length = sizeof(struct smb_direct_data_transfer); @@ -1069,8 +1075,8 @@ static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nen } static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, - struct scatterlist *sg_list, int nentries, - enum dma_data_direction dir) + struct scatterlist *sg_list, int nentries, + enum dma_data_direction dir) { int npages; @@ -1081,15 +1087,15 @@ static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, } static int post_sendmsg(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct smb_direct_sendmsg *msg) + struct smb_direct_send_ctx *send_ctx, + struct smb_direct_sendmsg *msg) { int i; for (i = 0; i < msg->num_sge; i++) ib_dma_sync_single_for_device(t->cm_id->device, - msg->sge[i].addr, msg->sge[i].length, - DMA_TO_DEVICE); + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); msg->cqe.done = send_done; msg->wr.opcode = IB_WR_SEND; @@ -1119,8 +1125,9 @@ static int post_sendmsg(struct smb_direct_transport *t, } static int smb_direct_post_send_data(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct kvec *iov, int niov, int remaining_data_length) + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, + int remaining_data_length) { int i, j, ret; struct smb_direct_sendmsg *msg; @@ -1148,8 +1155,9 @@ static int smb_direct_post_send_data(struct smb_direct_transport *t, sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES - 1); sg_cnt = get_mapped_sg_list(t->cm_id->device, - iov[i].iov_base, iov[i].iov_len, - sg, SMB_DIRECT_MAX_SEND_SGES - 1, DMA_TO_DEVICE); + iov[i].iov_base, iov[i].iov_len, + sg, SMB_DIRECT_MAX_SEND_SGES - 1, + DMA_TO_DEVICE); if (sg_cnt <= 0) { ksmbd_err("failed to map buffer\n"); ret = -ENOMEM; @@ -1182,8 +1190,8 @@ err: } static int smb_direct_writev(struct ksmbd_transport *t, - struct kvec *iov, int niovs, int buflen, - bool need_invalidate, unsigned int remote_key) + struct kvec *iov, int niovs, int buflen, + bool need_invalidate, unsigned int remote_key) { struct smb_direct_transport *st = SMB_DIRECT_TRANS(t); int remaining_data_length; @@ -1217,8 +1225,8 @@ static int smb_direct_writev(struct ksmbd_transport *t, remaining_data_length -= (buflen - iov[i].iov_len); ret = smb_direct_post_send_data(st, &send_ctx, - &iov[start], i - start, - remaining_data_length); + &iov[start], i - start, + remaining_data_length); if (ret) goto done; } else { @@ -1232,11 +1240,10 @@ static int smb_direct_writev(struct ksmbd_transport *t, j * max_iov_size; vec.iov_len = min_t(int, max_iov_size, - buflen - max_iov_size * j); + buflen - max_iov_size * j); remaining_data_length -= vec.iov_len; - ret = smb_direct_post_send_data(st, - &send_ctx, &vec, 1, - remaining_data_length); + ret = smb_direct_post_send_data(st, &send_ctx, &vec, 1, + remaining_data_length); if (ret) goto done; } @@ -1252,8 +1259,8 @@ static int smb_direct_writev(struct ksmbd_transport *t, /* send out all remaining vecs */ remaining_data_length -= buflen; ret = smb_direct_post_send_data(st, &send_ctx, - &iov[start], i - start, - remaining_data_length); + &iov[start], i - start, + remaining_data_length); if (ret) goto done; break; @@ -1272,20 +1279,20 @@ done: */ wait_event(st->wait_send_payload_pending, - atomic_read(&st->send_payload_pending) == 0); + atomic_read(&st->send_payload_pending) == 0); return ret; } static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, - enum dma_data_direction dir) + enum dma_data_direction dir) { struct smb_direct_rdma_rw_msg *msg = container_of(wc->wr_cqe, - struct smb_direct_rdma_rw_msg, cqe); + struct smb_direct_rdma_rw_msg, cqe); struct smb_direct_transport *t = msg->t; if (wc->status != IB_WC_SUCCESS) { ksmbd_err("read/write error. opcode = %d, status = %s(%d)\n", - wc->opcode, ib_wc_status_msg(wc->status), wc->status); + wc->opcode, ib_wc_status_msg(wc->status), wc->status); smb_direct_disconnect_rdma_connection(t); } @@ -1293,7 +1300,7 @@ static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, wake_up(&t->wait_rw_avail_ops); rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, - msg->sg_list, msg->sgt.nents, dir); + msg->sg_list, msg->sgt.nents, dir); sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); complete(msg->completion); kfree(msg); @@ -1310,8 +1317,8 @@ static void write_done(struct ib_cq *cq, struct ib_wc *wc) } static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, - int buf_len, u32 remote_key, u64 remote_offset, u32 remote_len, - bool is_read) + int buf_len, u32 remote_key, u64 remote_offset, + u32 remote_len, bool is_read) { struct smb_direct_rdma_rw_msg *msg; int ret; @@ -1324,7 +1331,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, /* TODO: mempool */ msg = kmalloc(offsetof(struct smb_direct_rdma_rw_msg, sg_list) + - sizeof(struct scatterlist) * SG_CHUNK_SIZE, GFP_KERNEL); + sizeof(struct scatterlist) * SG_CHUNK_SIZE, GFP_KERNEL); if (!msg) { atomic_inc(&t->rw_avail_ops); return -ENOMEM; @@ -1332,8 +1339,8 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, msg->sgt.sgl = &msg->sg_list[0]; ret = sg_alloc_table_chained(&msg->sgt, - BUFFER_NR_PAGES(buf, buf_len), - msg->sg_list, SG_CHUNK_SIZE); + BUFFER_NR_PAGES(buf, buf_len), + msg->sg_list, SG_CHUNK_SIZE); if (ret) { atomic_inc(&t->rw_avail_ops); kfree(msg); @@ -1347,9 +1354,9 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, } ret = rdma_rw_ctx_init(&msg->rw_ctx, t->qp, t->qp->port, - msg->sg_list, BUFFER_NR_PAGES(buf, buf_len), - 0, remote_offset, remote_key, - is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + msg->sg_list, BUFFER_NR_PAGES(buf, buf_len), + 0, remote_offset, remote_key, + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); if (ret < 0) { ksmbd_err("failed to init rdma_rw_ctx: %d\n", ret); goto err; @@ -1359,7 +1366,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, msg->cqe.done = is_read ? read_done : write_done; msg->completion = &completion; first_wr = rdma_rw_ctx_wrs(&msg->rw_ctx, t->qp, t->qp->port, - &msg->cqe, NULL); + &msg->cqe, NULL); ret = ib_post_send(t->qp, first_wr, NULL); if (ret) { @@ -1374,29 +1381,29 @@ err: atomic_inc(&t->rw_avail_ops); if (first_wr) rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, - msg->sg_list, msg->sgt.nents, - is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + msg->sg_list, msg->sgt.nents, + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); kfree(msg); return ret; } static int smb_direct_rdma_write(struct ksmbd_transport *t, void *buf, - unsigned int buflen, u32 remote_key, u64 remote_offset, - u32 remote_len) + unsigned int buflen, u32 remote_key, + u64 remote_offset, u32 remote_len) { return smb_direct_rdma_xmit(SMB_DIRECT_TRANS(t), buf, buflen, - remote_key, remote_offset, - remote_len, false); + remote_key, remote_offset, + remote_len, false); } static int smb_direct_rdma_read(struct ksmbd_transport *t, void *buf, - unsigned int buflen, u32 remote_key, u64 remote_offset, - u32 remote_len) + unsigned int buflen, u32 remote_key, + u64 remote_offset, u32 remote_len) { return smb_direct_rdma_xmit(SMB_DIRECT_TRANS(t), buf, buflen, - remote_key, remote_offset, - remote_len, true); + remote_key, remote_offset, + remote_len, true); } static void smb_direct_disconnect(struct ksmbd_transport *t) @@ -1407,17 +1414,17 @@ static void smb_direct_disconnect(struct ksmbd_transport *t) smb_direct_disconnect_rdma_connection(st); wait_event_interruptible(st->wait_status, - st->status == SMB_DIRECT_CS_DISCONNECTED); + st->status == SMB_DIRECT_CS_DISCONNECTED); free_transport(st); } static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, - struct rdma_cm_event *event) + struct rdma_cm_event *event) { struct smb_direct_transport *t = cm_id->context; ksmbd_debug(RDMA, "RDMA CM event. cm_id=%p event=%s (%d)\n", - cm_id, rdma_event_msg(event->event), event->event); + cm_id, rdma_event_msg(event->event), event->event); switch (event->event) { case RDMA_CM_EVENT_ESTABLISHED: { @@ -1440,8 +1447,8 @@ static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, } default: ksmbd_err("Unexpected RDMA CM event. cm_id=%p, event=%s (%d)\n", - cm_id, rdma_event_msg(event->event), - event->event); + cm_id, rdma_event_msg(event->event), + event->event); break; } return 0; @@ -1452,7 +1459,7 @@ static void smb_direct_qpair_handler(struct ib_event *event, void *context) struct smb_direct_transport *t = context; ksmbd_debug(RDMA, "Received QP event. cm_id=%p, event=%s (%d)\n", - t->cm_id, ib_event_msg(event->event), event->event); + t->cm_id, ib_event_msg(event->event), event->event); switch (event->event) { case IB_EVENT_CQ_ERR: @@ -1465,7 +1472,7 @@ static void smb_direct_qpair_handler(struct ib_event *event, void *context) } static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, - int failed) + int failed) { struct smb_direct_sendmsg *sendmsg; struct smb_direct_negotiate_resp *resp; @@ -1498,9 +1505,9 @@ static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, } sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, - (void *)resp, sizeof(*resp), DMA_TO_DEVICE); - ret = ib_dma_mapping_error(t->cm_id->device, - sendmsg->sge[0].addr); + (void *)resp, sizeof(*resp), + DMA_TO_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); if (ret) { smb_direct_free_sendmsg(t, sendmsg); return ret; @@ -1517,7 +1524,7 @@ static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, } wait_event(t->wait_send_pending, - atomic_read(&t->send_pending) == 0); + atomic_read(&t->send_pending) == 0); return 0; } @@ -1529,13 +1536,13 @@ static int smb_direct_accept_client(struct smb_direct_transport *t) int ret; memset(&conn_param, 0, sizeof(conn_param)); - conn_param.initiator_depth = min_t(u8, - t->cm_id->device->attrs.max_qp_rd_atom, - SMB_DIRECT_CM_INITIATOR_DEPTH); + conn_param.initiator_depth = min_t(u8, t->cm_id->device->attrs.max_qp_rd_atom, + SMB_DIRECT_CM_INITIATOR_DEPTH); conn_param.responder_resources = 0; t->cm_id->device->ops.get_port_immutable(t->cm_id->device, - t->cm_id->port_num, &port_immutable); + t->cm_id->port_num, + &port_immutable); if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { ird_ord_hdr[0] = conn_param.responder_resources; ird_ord_hdr[1] = 1; @@ -1590,9 +1597,9 @@ static int smb_direct_negotiate(struct smb_direct_transport *t) ksmbd_debug(RDMA, "Waiting for SMB_DIRECT negotiate request\n"); ret = wait_event_interruptible_timeout(t->wait_status, - t->negotiation_requested || - t->status == SMB_DIRECT_CS_DISCONNECTED, - SMB_DIRECT_NEGOTIATE_TIMEOUT * HZ); + t->negotiation_requested || + t->status == SMB_DIRECT_CS_DISCONNECTED, + SMB_DIRECT_NEGOTIATE_TIMEOUT * HZ); if (ret <= 0 || t->status == SMB_DIRECT_CS_DISCONNECTED) { ret = ret < 0 ? ret : -ETIMEDOUT; goto out; @@ -1604,9 +1611,9 @@ static int smb_direct_negotiate(struct smb_direct_transport *t) req = (struct smb_direct_negotiate_req *)recvmsg->packet; t->max_recv_size = min_t(int, t->max_recv_size, - le32_to_cpu(req->preferred_send_size)); + le32_to_cpu(req->preferred_send_size)); t->max_send_size = min_t(int, t->max_send_size, - le32_to_cpu(req->max_receive_size)); + le32_to_cpu(req->max_receive_size)); t->max_fragmented_send_size = le32_to_cpu(req->max_fragmented_size); @@ -1618,7 +1625,7 @@ out: } static int smb_direct_init_params(struct smb_direct_transport *t, - struct ib_qp_cap *cap) + struct ib_qp_cap *cap) { struct ib_device *device = t->cm_id->device; int max_send_sges, max_pages, max_rw_wrs, max_send_wrs; @@ -1650,30 +1657,30 @@ static int smb_direct_init_params(struct smb_direct_transport *t, if (max_send_wrs > device->attrs.max_cqe || max_send_wrs > device->attrs.max_qp_wr) { ksmbd_err("consider lowering send_credit_target = %d, or max_outstanding_rw_ops = %d\n", - smb_direct_send_credit_target, - smb_direct_max_outstanding_rw_ops); + smb_direct_send_credit_target, + smb_direct_max_outstanding_rw_ops); ksmbd_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", - device->attrs.max_cqe, device->attrs.max_qp_wr); + device->attrs.max_cqe, device->attrs.max_qp_wr); return -EINVAL; } if (smb_direct_receive_credit_max > device->attrs.max_cqe || smb_direct_receive_credit_max > device->attrs.max_qp_wr) { ksmbd_err("consider lowering receive_credit_max = %d\n", - smb_direct_receive_credit_max); + smb_direct_receive_credit_max); ksmbd_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", - device->attrs.max_cqe, device->attrs.max_qp_wr); + device->attrs.max_cqe, device->attrs.max_qp_wr); return -EINVAL; } if (device->attrs.max_send_sge < SMB_DIRECT_MAX_SEND_SGES) { ksmbd_err("warning: device max_send_sge = %d too small\n", - device->attrs.max_send_sge); + device->attrs.max_send_sge); return -EINVAL; } if (device->attrs.max_recv_sge < SMB_DIRECT_MAX_RECV_SGES) { ksmbd_err("warning: device max_recv_sge = %d too small\n", - device->attrs.max_recv_sge); + device->attrs.max_recv_sge); return -EINVAL; } @@ -1731,29 +1738,29 @@ static int smb_direct_create_pools(struct smb_direct_transport *t) snprintf(name, sizeof(name), "smb_direct_rqst_pool_%p", t); t->sendmsg_cache = kmem_cache_create(name, - sizeof(struct smb_direct_sendmsg) + - sizeof(struct smb_direct_negotiate_resp), - 0, SLAB_HWCACHE_ALIGN, NULL); + sizeof(struct smb_direct_sendmsg) + + sizeof(struct smb_direct_negotiate_resp), + 0, SLAB_HWCACHE_ALIGN, NULL); if (!t->sendmsg_cache) return -ENOMEM; t->sendmsg_mempool = mempool_create(t->send_credit_target, - mempool_alloc_slab, mempool_free_slab, - t->sendmsg_cache); + mempool_alloc_slab, mempool_free_slab, + t->sendmsg_cache); if (!t->sendmsg_mempool) goto err; snprintf(name, sizeof(name), "smb_direct_resp_%p", t); t->recvmsg_cache = kmem_cache_create(name, - sizeof(struct smb_direct_recvmsg) + - t->max_recv_size, - 0, SLAB_HWCACHE_ALIGN, NULL); + sizeof(struct smb_direct_recvmsg) + + t->max_recv_size, + 0, SLAB_HWCACHE_ALIGN, NULL); if (!t->recvmsg_cache) goto err; t->recvmsg_mempool = mempool_create(t->recv_credit_max, mempool_alloc_slab, - mempool_free_slab, t->recvmsg_cache); + mempool_free_slab, t->recvmsg_cache); if (!t->recvmsg_mempool) goto err; @@ -1775,7 +1782,7 @@ err: } static int smb_direct_create_qpair(struct smb_direct_transport *t, - struct ib_qp_cap *cap) + struct ib_qp_cap *cap) { int ret; struct ib_qp_init_attr qp_attr; @@ -1789,7 +1796,7 @@ static int smb_direct_create_qpair(struct smb_direct_transport *t, } t->send_cq = ib_alloc_cq(t->cm_id->device, t, - t->send_credit_target, 0, IB_POLL_WORKQUEUE); + t->send_credit_target, 0, IB_POLL_WORKQUEUE); if (IS_ERR(t->send_cq)) { ksmbd_err("Can't create RDMA send CQ\n"); ret = PTR_ERR(t->send_cq); @@ -1798,8 +1805,8 @@ static int smb_direct_create_qpair(struct smb_direct_transport *t, } t->recv_cq = ib_alloc_cq(t->cm_id->device, t, - cap->max_send_wr + cap->max_rdma_ctxs, - 0, IB_POLL_WORKQUEUE); + cap->max_send_wr + cap->max_rdma_ctxs, + 0, IB_POLL_WORKQUEUE); if (IS_ERR(t->recv_cq)) { ksmbd_err("Can't create RDMA recv CQ\n"); ret = PTR_ERR(t->recv_cq); @@ -1896,8 +1903,8 @@ static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) if (!rdma_frwr_is_supported(&new_cm_id->device->attrs)) { ksmbd_debug(RDMA, - "Fast Registration Work Requests is not supported. device capabilities=%llx\n", - new_cm_id->device->attrs.device_cap_flags); + "Fast Registration Work Requests is not supported. device capabilities=%llx\n", + new_cm_id->device->attrs.device_cap_flags); return -EPROTONOSUPPORT; } @@ -1906,7 +1913,8 @@ static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) return -ENOMEM; KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, - KSMBD_TRANS(t)->conn, "ksmbd:r%u", SMB_DIRECT_PORT); + KSMBD_TRANS(t)->conn, "ksmbd:r%u", + SMB_DIRECT_PORT); if (IS_ERR(KSMBD_TRANS(t)->handler)) { int ret = PTR_ERR(KSMBD_TRANS(t)->handler); @@ -1919,7 +1927,7 @@ static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) } static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, - struct rdma_cm_event *event) + struct rdma_cm_event *event) { switch (event->event) { case RDMA_CM_EVENT_CONNECT_REQUEST: { @@ -1931,13 +1939,12 @@ static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, } ksmbd_debug(RDMA, "Received connection request. cm_id=%p\n", - cm_id); + cm_id); break; } default: ksmbd_err("Unexpected listen event. cm_id=%p, event=%s (%d)\n", - cm_id, - rdma_event_msg(event->event), event->event); + cm_id, rdma_event_msg(event->event), event->event); break; } return 0; @@ -1954,10 +1961,9 @@ static int smb_direct_listen(int port) }; cm_id = rdma_create_id(&init_net, smb_direct_listen_handler, - &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); + &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); if (IS_ERR(cm_id)) { - ksmbd_err("Can't create cm id: %ld\n", - PTR_ERR(cm_id)); + ksmbd_err("Can't create cm id: %ld\n", PTR_ERR(cm_id)); return PTR_ERR(cm_id); } @@ -1993,7 +1999,7 @@ int ksmbd_rdma_init(void) * for lack of credits */ smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", - WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); + WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); if (!smb_direct_wq) return -ENOMEM; @@ -2006,7 +2012,7 @@ int ksmbd_rdma_init(void) } ksmbd_debug(RDMA, "init RDMA listener. cm_id=%p\n", - smb_direct_listener.cm_id); + smb_direct_listener.cm_id); return 0; } diff --git a/fs/cifsd/transport_tcp.c b/fs/cifsd/transport_tcp.c index 040881893417..5bd332a58596 100644 --- a/fs/cifsd/transport_tcp.c +++ b/fs/cifsd/transport_tcp.c @@ -113,7 +113,7 @@ static void free_transport(struct tcp_transport *t) * Return: Number of IO segments */ static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov, - unsigned int nr_segs, size_t bytes) + unsigned int nr_segs, size_t bytes) { size_t base = 0; @@ -197,8 +197,9 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk) } KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, - KSMBD_TRANS(t)->conn, - "ksmbd:%u", ksmbd_tcp_get_port(csin)); + KSMBD_TRANS(t)->conn, + "ksmbd:%u", + ksmbd_tcp_get_port(csin)); if (IS_ERR(KSMBD_TRANS(t)->handler)) { ksmbd_err("cannot start conn thread\n"); rc = PTR_ERR(KSMBD_TRANS(t)->handler); @@ -230,7 +231,7 @@ static int ksmbd_kthread_fn(void *p) break; } ret = kernel_accept(iface->ksmbd_socket, &client_sk, - O_NONBLOCK); + O_NONBLOCK); mutex_unlock(&iface->sock_release_lock); if (ret) { if (ret == -EAGAIN) @@ -265,8 +266,8 @@ static int ksmbd_tcp_run_kthread(struct interface *iface) int rc; struct task_struct *kthread; - kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, - "ksmbd-%s", iface->name); + kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s", + iface->name); if (IS_ERR(kthread)) { rc = PTR_ERR(kthread); return rc; @@ -287,7 +288,7 @@ static int ksmbd_tcp_run_kthread(struct interface *iface) * otherwise return error number */ static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig, - unsigned int nr_segs, unsigned int to_read) + unsigned int nr_segs, unsigned int to_read) { int length = 0; int total_read; @@ -353,7 +354,8 @@ static int ksmbd_tcp_read(struct ksmbd_transport *t, char *buf, unsigned int to_ } static int ksmbd_tcp_writev(struct ksmbd_transport *t, struct kvec *iov, - int nvecs, int size, bool need_invalidate, unsigned int remote_key) + int nvecs, int size, bool need_invalidate, + unsigned int remote_key) { struct msghdr smb_msg = {.msg_flags = MSG_NOSIGNAL}; @@ -401,7 +403,7 @@ static int create_socket(struct interface *iface) if (ret) { ksmbd_err("Can't create socket for ipv6, try ipv4: %d\n", ret); ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, - &ksmbd_socket); + &ksmbd_socket); if (ret) { ksmbd_err("Can't create socket for ipv4: %d\n", ret); goto out_error; @@ -432,10 +434,10 @@ static int create_socket(struct interface *iface) if (ipv4) ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin, - sizeof(sin)); + sizeof(sin)); else ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6, - sizeof(sin6)); + sizeof(sin6)); if (ret) { ksmbd_err("Failed to bind socket: %d\n", ret); goto out_error; @@ -467,7 +469,7 @@ out_error: } static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, - void *ptr) + void *ptr) { struct net_device *netdev = netdev_notifier_info_to_dev(ptr); struct interface *iface; diff --git a/fs/cifsd/unicode.c b/fs/cifsd/unicode.c index 38bba50c4f16..a0db699ddafd 100644 --- a/fs/cifsd/unicode.c +++ b/fs/cifsd/unicode.c @@ -27,7 +27,7 @@ * Return: string length after conversion */ static int smb_utf16_bytes(const __le16 *from, int maxbytes, - const struct nls_table *codepage) + const struct nls_table *codepage) { int i; int charlen, outlen = 0; @@ -65,7 +65,7 @@ static int smb_utf16_bytes(const __le16 *from, int maxbytes, */ static int cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, - bool mapchar) + bool mapchar) { int len = 1; @@ -156,7 +156,7 @@ static inline int is_char_allowed(char *ch) * Return: string length after conversion */ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, - const struct nls_table *codepage, bool mapchar) + const struct nls_table *codepage, bool mapchar) { int i, charlen, safelen; int outlen = 0; @@ -210,7 +210,7 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, * Return: string length after conversion */ int smb_strtoUTF16(__le16 *to, const char *from, int len, - const struct nls_table *codepage) + const struct nls_table *codepage) { int charlen; int i; @@ -224,7 +224,7 @@ int smb_strtoUTF16(__le16 *to, const char *from, int len, * in destination len is length in wchar_t units (16bits) */ i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, - (wchar_t *)to, len); + (wchar_t *)to, len); /* if success terminate and exit */ if (i >= 0) @@ -267,7 +267,8 @@ success: * Return: destination string buffer or error ptr */ char *smb_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, const struct nls_table *codepage) + const bool is_unicode, + const struct nls_table *codepage) { int len, ret; char *dst; @@ -279,7 +280,7 @@ char *smb_strndup_from_utf16(const char *src, const int maxlen, if (!dst) return ERR_PTR(-ENOMEM); ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage, - false); + false); if (ret < 0) { kfree(dst); return ERR_PTR(-EINVAL); @@ -318,7 +319,7 @@ char *smb_strndup_from_utf16(const char *src, const int maxlen, * Return: char length after conversion */ int smbConvertToUTF16(__le16 *target, const char *source, int srclen, - const struct nls_table *cp, int mapchars) + const struct nls_table *cp, int mapchars) { int i, j, charlen; char src_char; diff --git a/fs/cifsd/unicode.h b/fs/cifsd/unicode.h index 68f1c8290911..5593024230ae 100644 --- a/fs/cifsd/unicode.h +++ b/fs/cifsd/unicode.h @@ -63,11 +63,12 @@ extern const struct UniCaseRange CifsUniLowerRange[]; #ifdef __KERNEL__ int smb_strtoUTF16(__le16 *to, const char *from, int len, - const struct nls_table *codepage); + const struct nls_table *codepage); char *smb_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, const struct nls_table *codepage); + const bool is_unicode, + const struct nls_table *codepage); int smbConvertToUTF16(__le16 *target, const char *source, int srclen, - const struct nls_table *cp, int mapchars); + const struct nls_table *cp, int mapchars); char *ksmbd_extract_sharename(char *treename); #endif @@ -198,7 +199,7 @@ static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) /* * UniStrncmp_le: Compare length limited string - native to little-endian */ - static inline int +static inline int UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) { if (!n) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index e1295b72c410..355e1a5a893b 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -52,7 +52,8 @@ static char *extract_last_component(char *path) } static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, - struct inode *parent_inode, struct inode *inode) + struct inode *parent_inode, + struct inode *inode) { if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_INHERIT_OWNER)) @@ -84,7 +85,7 @@ int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete) parent = dget_parent(dentry); inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); child = lookup_one_len(dentry->d_name.name, parent, - dentry->d_name.len); + dentry->d_name.len); if (IS_ERR(child)) { ret = PTR_ERR(child); goto out_lock; @@ -130,7 +131,7 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) parent = dget_parent(dentry); inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); child = lookup_one_len(dentry->d_name.name, parent, - dentry->d_name.len); + dentry->d_name.len); if (IS_ERR(child)) { ret = PTR_ERR(child); goto out_lock; @@ -171,7 +172,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) err = PTR_ERR(dentry); if (err != -ENOENT) ksmbd_err("path create failed for %s, err %d\n", - name, err); + name, err); return err; } @@ -179,7 +180,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) err = vfs_create(&init_user_ns, d_inode(path.dentry), dentry, mode, true); if (!err) { ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), - d_inode(dentry)); + d_inode(dentry)); } else { ksmbd_err("File(%s): creation failed (err:%d)\n", name, err); } @@ -206,7 +207,7 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) err = PTR_ERR(dentry); if (err != -EEXIST) ksmbd_debug(VFS, "path create failed for %s, err %d\n", - name, err); + name, err); return err; } @@ -217,9 +218,8 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) } else if (d_unhashed(dentry)) { struct dentry *d; - d = lookup_one_len(dentry->d_name.name, - dentry->d_parent, - dentry->d_name.len); + d = lookup_one_len(dentry->d_name.name, dentry->d_parent, + dentry->d_name.len); if (IS_ERR(d)) { err = PTR_ERR(d); goto out; @@ -230,8 +230,7 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) goto out; } - ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), - d_inode(d)); + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); dput(d); } out: @@ -242,7 +241,7 @@ out: } static ssize_t ksmbd_vfs_getcasexattr(struct dentry *dentry, char *attr_name, - int attr_name_len, char **attr_value) + int attr_name_len, char **attr_value) { char *name, *xattr_list = NULL; ssize_t value_len = -ENOENT, xattr_list_len; @@ -271,14 +270,14 @@ out: } static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, - size_t count) + size_t count) { ssize_t v_len; char *stream_buf = NULL; int err; ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", - *pos, count); + *pos, count); v_len = ksmbd_vfs_getcasexattr(fp->filp->f_path.dentry, fp->stream.name, @@ -304,7 +303,7 @@ static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, * Return: 0 on success, otherwise error */ static int check_lock_range(struct file *filp, loff_t start, loff_t end, - unsigned char type) + unsigned char type) { struct file_lock *flock; struct file_lock_context *ctx = file_inode(filp)->i_flctx; @@ -348,7 +347,7 @@ out: * Return: number of read bytes on success, otherwise error */ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, - loff_t *pos) + loff_t *pos) { struct file *filp; ssize_t nbytes = 0; @@ -377,8 +376,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, if (!work->tcon->posix_extensions) { int ret; - ret = check_lock_range(filp, *pos, *pos + count - 1, - READ); + ret = check_lock_range(filp, *pos, *pos + count - 1, READ); if (ret) { ksmbd_err("unable to read due to lock\n"); return -EAGAIN; @@ -388,7 +386,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, nbytes = kernel_read(filp, rbuf, count, pos); if (nbytes < 0) { ksmbd_err("smb read failed for (%s), err = %zd\n", - fp->filename, nbytes); + fp->filename, nbytes); return nbytes; } @@ -397,14 +395,14 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, } static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, - size_t count) + size_t count) { char *stream_buf = NULL, *wbuf; size_t size, v_len; int err = 0; ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", - *pos, count); + *pos, count); size = *pos + count; if (size > XATTR_SIZE_MAX) { @@ -464,8 +462,8 @@ out: * Return: 0 on success, otherwise error */ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf, size_t count, loff_t *pos, bool sync, - ssize_t *written) + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written) { struct ksmbd_session *sess = work->sess; struct file *filp; @@ -514,7 +512,7 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, err = vfs_fsync_range(filp, offset, offset + *written, 0); if (err < 0) ksmbd_err("fsync failed for filename = %s, err = %d\n", - FP_FILENAME(fp), err); + FP_FILENAME(fp), err); } out: @@ -588,11 +586,11 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) parent = dget_parent(path.dentry); inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); dentry = lookup_one_len(path.dentry->d_name.name, parent, - strlen(path.dentry->d_name.name)); + strlen(path.dentry->d_name.name)); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); ksmbd_debug(VFS, "%s: lookup failed, err %d\n", - path.dentry->d_name.name, err); + path.dentry->d_name.name, err); goto out_err; } @@ -606,12 +604,12 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) err = vfs_rmdir(&init_user_ns, d_inode(parent), dentry); if (err && err != -ENOTEMPTY) ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, - err); + err); } else { err = vfs_unlink(&init_user_ns, d_inode(parent), dentry, NULL); if (err) ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, - err); + err); } dput(dentry); @@ -631,7 +629,7 @@ out_err: * Return: 0 on success, otherwise error */ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, - const char *newname) + const char *newname) { struct path oldpath, newpath; struct dentry *dentry; @@ -643,12 +641,12 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, err = kern_path(oldname, LOOKUP_FOLLOW, &oldpath); if (err) { ksmbd_err("cannot get linux path for %s, err = %d\n", - oldname, err); + oldname, err); goto out1; } dentry = kern_path_create(AT_FDCWD, newname, &newpath, - LOOKUP_FOLLOW | LOOKUP_REVAL); + LOOKUP_FOLLOW | LOOKUP_REVAL); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); ksmbd_err("path create err for %s, err %d\n", newname, err); @@ -662,7 +660,7 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, } err = vfs_link(oldpath.dentry, &init_user_ns, d_inode(newpath.dentry), - dentry, NULL); + dentry, NULL); if (err) ksmbd_debug(VFS, "vfs_link failed err %d\n", err); @@ -676,9 +674,11 @@ out1: } static int __ksmbd_vfs_rename(struct ksmbd_work *work, - struct dentry *src_dent_parent, struct dentry *src_dent, - struct dentry *dst_dent_parent, struct dentry *trap_dent, - char *dst_name) + struct dentry *src_dent_parent, + struct dentry *src_dent, + struct dentry *dst_dent_parent, + struct dentry *trap_dent, + char *dst_name) { struct dentry *dst_dent; int err; @@ -742,7 +742,7 @@ out: } int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, - char *newname) + char *newname) { struct path dst_path; struct dentry *src_dent_parent, *dst_dent_parent; @@ -768,7 +768,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, dget(src_dent); dget(dst_dent_parent); src_child = lookup_one_len(src_dent->d_name.name, src_dent_parent, - src_dent->d_name.len); + src_dent->d_name.len); if (IS_ERR(src_child)) { err = PTR_ERR(src_child); goto out_lock; @@ -807,7 +807,7 @@ out: * Return: 0 on success, otherwise error */ int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, - struct ksmbd_file *fp, loff_t size) + struct ksmbd_file *fp, loff_t size) { struct path path; int err = 0; @@ -816,13 +816,13 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, err = kern_path(name, 0, &path); if (err) { ksmbd_err("cannot get linux path for %s, err %d\n", - name, err); + name, err); return err; } err = vfs_truncate(&path, size); if (err) ksmbd_err("truncate failed for %s err %d\n", - name, err); + name, err); path_put(&path); } else { struct file *filp; @@ -837,10 +837,10 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, if (size < inode->i_size) { err = check_lock_range(filp, size, - inode->i_size - 1, WRITE); + inode->i_size - 1, WRITE); } else { err = check_lock_range(filp, inode->i_size, - size - 1, WRITE); + size - 1, WRITE); } if (err) { @@ -852,7 +852,7 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, err = vfs_truncate(&filp->f_path, size); if (err) ksmbd_err("truncate failed for filename : %s err %d\n", - fp->filename, err); + fp->filename, err); } return err; @@ -904,7 +904,7 @@ static ssize_t ksmbd_vfs_xattr_len(struct dentry *dentry, char *xattr_name) * Return: read xattr value length on success, otherwise error */ ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, - char **xattr_buf) + char **xattr_buf) { ssize_t xattr_len; char *buf; @@ -938,7 +938,7 @@ ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, * Return: 0 on success, otherwise error */ int ksmbd_vfs_setxattr(struct dentry *dentry, const char *attr_name, - const void *attr_value, size_t attr_size, int flags) + const void *attr_value, size_t attr_size, int flags) { int err; @@ -988,8 +988,7 @@ void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) * * Return: 0 on success, otherwise error */ -int ksmbd_vfs_lock(struct file *filp, int cmd, - struct file_lock *flock) +int ksmbd_vfs_lock(struct file *filp, int cmd, struct file_lock *flock) { ksmbd_debug(VFS, "calling vfs_lock_file\n"); return vfs_lock_file(filp, cmd, flock, NULL); @@ -1001,26 +1000,27 @@ int ksmbd_vfs_readdir(struct file *file, struct ksmbd_readdir_data *rdata) } int ksmbd_vfs_alloc_size(struct ksmbd_work *work, struct ksmbd_file *fp, - loff_t len) + loff_t len) { smb_break_all_levII_oplock(work, fp, 1); return vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, len); } int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, - loff_t off, loff_t len) + loff_t off, loff_t len) { smb_break_all_levII_oplock(work, fp, 1); if (fp->f_ci->m_fattr & ATTR_SPARSE_FILE_LE) return vfs_fallocate(fp->filp, - FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, off, len); + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + off, len); return vfs_fallocate(fp->filp, FALLOC_FL_ZERO_RANGE, off, len); } int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, - struct file_allocated_range_buffer *ranges, - int in_count, int *out_count) + struct file_allocated_range_buffer *ranges, + int in_count, int *out_count) { struct file *f = fp->filp; struct inode *inode = FP_INODE(fp); @@ -1087,8 +1087,7 @@ int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry) inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); dget(dentry); - child = lookup_one_len(dentry->d_name.name, dir, - dentry->d_name.len); + child = lookup_one_len(dentry->d_name.name, dir, dentry->d_name.len); if (IS_ERR(child)) { err = PTR_ERR(child); goto out; @@ -1143,7 +1142,7 @@ unsigned short ksmbd_vfs_logical_sector_size(struct inode *inode) * @fs_ss: fs sector size struct */ void ksmbd_vfs_smb2_sector_size(struct inode *inode, - struct ksmbd_fs_sector_size *fs_ss) + struct ksmbd_fs_sector_size *fs_ss) { struct request_queue *q; @@ -1169,7 +1168,7 @@ void ksmbd_vfs_smb2_sector_size(struct inode *inode, } static int __dir_empty(struct dir_context *ctx, const char *name, int namlen, - loff_t offset, u64 ino, unsigned int d_type) + loff_t offset, u64 ino, unsigned int d_type) { struct ksmbd_readdir_data *buf; @@ -1206,7 +1205,8 @@ int ksmbd_vfs_empty_dir(struct ksmbd_file *fp) } static int __caseless_lookup(struct dir_context *ctx, const char *name, - int namlen, loff_t offset, u64 ino, unsigned int d_type) + int namlen, loff_t offset, u64 ino, + unsigned int d_type) { struct ksmbd_readdir_data *buf; @@ -1263,7 +1263,7 @@ static int ksmbd_vfs_lookup_in_dir(struct path *dir, char *name, size_t namelen) * Return: 0 on success, otherwise error */ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, - bool caseless) + bool caseless) { int err; @@ -1346,7 +1346,7 @@ int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry) } for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { + name += strlen(name) + 1) { ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); if (!strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS, @@ -1356,7 +1356,7 @@ int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry) err = ksmbd_vfs_remove_xattr(dentry, name); if (err) ksmbd_debug(SMB, - "remove acl xattr failed : %s\n", name); + "remove acl xattr failed : %s\n", name); } } out: @@ -1394,7 +1394,7 @@ out: } static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct inode *inode, - int acl_type) + int acl_type) { struct xattr_smb_acl *smb_acl = NULL; struct posix_acl *posix_acls; @@ -1455,7 +1455,7 @@ out: } int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, - struct smb_ntsd *pntsd, int len) + struct smb_ntsd *pntsd, int len) { int rc; struct ndr sd_ndr = {0}, acl_ndr = {0}; @@ -1489,7 +1489,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, ACL_TYPE_ACCESS); if (S_ISDIR(inode->i_mode)) def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, - ACL_TYPE_DEFAULT); + ACL_TYPE_DEFAULT); rc = ndr_encode_posix_acl(&acl_ndr, inode, smb_acl, def_smb_acl); if (rc) { @@ -1498,7 +1498,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, } rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, - acl.posix_acl_hash); + acl.posix_acl_hash); if (rc) { ksmbd_err("failed to generate hash for ndr acl\n"); goto out; @@ -1511,7 +1511,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, } rc = ksmbd_vfs_setxattr(dentry, XATTR_NAME_SD, sd_ndr.data, - sd_ndr.offset, 0); + sd_ndr.offset, 0); if (rc < 0) ksmbd_err("Failed to store XATTR ntacl :%d\n", rc); @@ -1524,7 +1524,7 @@ out: } int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, - struct smb_ntsd **pntsd) + struct smb_ntsd **pntsd) { int rc; struct ndr n; @@ -1543,10 +1543,10 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, return rc; smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, - ACL_TYPE_ACCESS); + ACL_TYPE_ACCESS); if (S_ISDIR(inode->i_mode)) def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, - ACL_TYPE_DEFAULT); + ACL_TYPE_DEFAULT); rc = ndr_encode_posix_acl(&acl_ndr, inode, smb_acl, def_smb_acl); if (rc) { @@ -1555,7 +1555,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, } rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, - cmp_hash); + cmp_hash); if (rc) { ksmbd_err("failed to generate hash for ndr acl\n"); goto out; @@ -1587,7 +1587,7 @@ out: } int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, - struct xattr_dos_attrib *da) + struct xattr_dos_attrib *da) { struct ndr n; int err; @@ -1596,11 +1596,8 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, if (err) return err; - err = ksmbd_vfs_setxattr(dentry, - XATTR_NAME_DOS_ATTRIBUTE, - (void *)n.data, - n.offset, - 0); + err = ksmbd_vfs_setxattr(dentry, XATTR_NAME_DOS_ATTRIBUTE, + (void *)n.data, n.offset, 0); if (err) ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); kfree(n.data); @@ -1609,14 +1606,13 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, } int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, - struct xattr_dos_attrib *da) + struct xattr_dos_attrib *da) { struct ndr n; int err; - err = ksmbd_vfs_getxattr(dentry, - XATTR_NAME_DOS_ATTRIBUTE, - (char **)&n.data); + err = ksmbd_vfs_getxattr(dentry, XATTR_NAME_DOS_ATTRIBUTE, + (char **)&n.data); if (err > 0) { n.length = err; if (ndr_decode_dos_attr(&n, da)) @@ -1648,7 +1644,7 @@ struct posix_acl *ksmbd_vfs_get_acl(struct inode *inode, int type) } int ksmbd_vfs_set_posix_acl(struct inode *inode, int type, - struct posix_acl *acl) + struct posix_acl *acl) { #if IS_ENABLED(CONFIG_FS_POSIX_ACL) return set_posix_acl(&init_user_ns, inode, type, acl); @@ -1690,7 +1686,7 @@ void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) } int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, - struct ksmbd_kstat *ksmbd_kstat) + struct ksmbd_kstat *ksmbd_kstat) { u64 time; int rc; @@ -1726,7 +1722,7 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, } ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, char *attr_name, - int attr_name_len) + int attr_name_len) { char *name, *xattr_list = NULL; ssize_t value_len = -ENOENT, xattr_list_len; @@ -1751,7 +1747,7 @@ out: } int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, - size_t *xattr_stream_name_size, int s_type) + size_t *xattr_stream_name_size, int s_type) { int stream_name_size; char *xattr_stream_name_buf; @@ -1767,18 +1763,15 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, stream_name_size = strlen(stream_name); *xattr_stream_name_size = stream_name_size + XATTR_NAME_STREAM_LEN + 1; xattr_stream_name_buf = kmalloc(*xattr_stream_name_size + type_len, - GFP_KERNEL); + GFP_KERNEL); if (!xattr_stream_name_buf) return -ENOMEM; - memcpy(xattr_stream_name_buf, - XATTR_NAME_STREAM, - XATTR_NAME_STREAM_LEN); + memcpy(xattr_stream_name_buf, XATTR_NAME_STREAM, XATTR_NAME_STREAM_LEN); if (stream_name_size) { memcpy(&xattr_stream_name_buf[XATTR_NAME_STREAM_LEN], - stream_name, - stream_name_size); + stream_name, stream_name_size); } memcpy(&xattr_stream_name_buf[*xattr_stream_name_size - 1], type, type_len); *xattr_stream_name_size += type_len; @@ -1790,7 +1783,7 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, } int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, - struct file *file_out, loff_t pos_out, size_t len) + struct file *file_out, loff_t pos_out, size_t len) { struct inode *inode_in = file_inode(file_in); struct inode *inode_out = file_inode(file_out); @@ -1820,7 +1813,7 @@ int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, * in do_splice_direct */ ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out, - len > MAX_RW_COUNT ? MAX_RW_COUNT : len, 0); + len > MAX_RW_COUNT ? MAX_RW_COUNT : len, 0); if (ret > 0) { fsnotify_access(file_in); add_rchar(current, ret); @@ -1836,10 +1829,13 @@ int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, } int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, - struct ksmbd_file *src_fp, struct ksmbd_file *dst_fp, - struct srv_copychunk *chunks, unsigned int chunk_count, - unsigned int *chunk_count_written, - unsigned int *chunk_size_written, loff_t *total_size_written) + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written) { unsigned int i; loff_t src_off, dst_off, src_file_size; @@ -1890,7 +1886,7 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, return -E2BIG; ret = ksmbd_vfs_copy_file_range(src_fp->filp, src_off, - dst_fp->filp, dst_off, len); + dst_fp->filp, dst_off, len); if (ret < 0) return ret; @@ -1949,13 +1945,13 @@ int ksmbd_vfs_set_init_posix_acl(struct inode *inode) rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", - rc); + rc); else if (S_ISDIR(inode->i_mode)) { posix_state_to_acl(&acl_state, acls->a_entries); rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", - rc); + rc); } free_acl_state(&acl_state); posix_acl_release(acls); @@ -1983,12 +1979,12 @@ int ksmbd_vfs_inherit_posix_acl(struct inode *inode, struct inode *parent_inode) rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", - rc); + rc); if (S_ISDIR(inode->i_mode)) { rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", - rc); + rc); } posix_acl_release(acls); return rc; diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index 2d19e2bac33a..5db1e9e2a754 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -191,84 +191,85 @@ struct ksmbd_fs_sector_size { }; int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, - bool delete); + bool delete); int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess); int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, - size_t count, loff_t *pos); + size_t count, loff_t *pos); int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf, size_t count, loff_t *pos, bool sync, - ssize_t *written); + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written); int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); int ksmbd_vfs_link(struct ksmbd_work *work, - const char *oldname, const char *newname); + const char *oldname, const char *newname); int ksmbd_vfs_getattr(struct path *path, struct kstat *stat); - int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, - char *newname); - + char *newname); int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, - struct ksmbd_file *fp, loff_t size); - + struct ksmbd_file *fp, loff_t size); struct srv_copychunk; int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, - struct ksmbd_file *src_fp, struct ksmbd_file *dst_fp, - struct srv_copychunk *chunks, unsigned int chunk_count, - unsigned int *chunk_count_written, - unsigned int *chunk_size_written, loff_t *total_size_written); + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written); int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, - struct file *file_out, loff_t pos_out, size_t len); + struct file *file_out, loff_t pos_out, + size_t len); ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, - char **xattr_buf); + char **xattr_buf); ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, char *attr_name, - int attr_name_len); + int attr_name_len); int ksmbd_vfs_setxattr(struct dentry *dentry, const char *attr_name, - const void *attr_value, size_t attr_size, int flags); + const void *attr_value, size_t attr_size, int flags); int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, - size_t *xattr_stream_name_size, int s_type); + size_t *xattr_stream_name_size, int s_type); int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name); int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, - bool caseless); + bool caseless); int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); int ksmbd_vfs_lock(struct file *filp, int cmd, struct file_lock *flock); int ksmbd_vfs_readdir(struct file *file, struct ksmbd_readdir_data *rdata); int ksmbd_vfs_alloc_size(struct ksmbd_work *work, struct ksmbd_file *fp, - loff_t len); + loff_t len); int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, - loff_t off, loff_t len); + loff_t off, loff_t len); struct file_allocated_range_buffer; int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, - struct file_allocated_range_buffer *ranges, - int in_count, int *out_count); + struct file_allocated_range_buffer *ranges, + int in_count, int *out_count); int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry); unsigned short ksmbd_vfs_logical_sector_size(struct inode *inode); void ksmbd_vfs_smb2_sector_size(struct inode *inode, - struct ksmbd_fs_sector_size *fs_ss); + struct ksmbd_fs_sector_size *fs_ss); void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, - struct ksmbd_kstat *ksmbd_kstat); + struct ksmbd_kstat *ksmbd_kstat); int ksmbd_vfs_posix_lock_wait(struct file_lock *flock); int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry); int ksmbd_vfs_remove_sd_xattrs(struct dentry *dentry); int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, - struct smb_ntsd *pntsd, int len); + struct smb_ntsd *pntsd, int len); int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, - struct smb_ntsd **pntsd); + struct smb_ntsd **pntsd); int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, - struct xattr_dos_attrib *da); + struct xattr_dos_attrib *da); int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, - struct xattr_dos_attrib *da); + struct xattr_dos_attrib *da); struct posix_acl *ksmbd_vfs_posix_acl_alloc(int count, gfp_t flags); struct posix_acl *ksmbd_vfs_get_acl(struct inode *inode, int type); int ksmbd_vfs_set_posix_acl(struct inode *inode, int type, - struct posix_acl *acl); + struct posix_acl *acl); int ksmbd_vfs_set_init_posix_acl(struct inode *inode); int ksmbd_vfs_inherit_posix_acl(struct inode *inode, - struct inode *parent_inode); + struct inode *parent_inode); #endif /* __KSMBD_VFS_H__ */ diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index 3286e74e2a56..6ea09fe82814 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -255,7 +255,7 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) fp->stream.name); if (err) ksmbd_err("remove xattr failed : %s\n", - fp->stream.name); + fp->stream.name); } if (atomic_dec_and_test(&ci->m_count)) { @@ -326,7 +326,7 @@ static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) } static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, - unsigned int id) + unsigned int id) { struct ksmbd_file *fp; @@ -350,7 +350,7 @@ static void set_close_state_blocked_works(struct ksmbd_file *fp) spin_lock(&fp->f_lock); list_for_each_entry_safe(cancel_work, ctmp, &fp->blocked_works, - fp_entry) { + fp_entry) { list_del(&cancel_work->fp_entry); cancel_work->state = KSMBD_WORK_CLOSED; cancel_work->cancel_fn(cancel_work->cancel_argv); @@ -420,7 +420,7 @@ struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, unsigned int id } struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, unsigned int id, - unsigned int pid) + unsigned int pid) { struct ksmbd_file *fp; @@ -577,8 +577,10 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) } static int -__close_file_table_ids(struct ksmbd_file_table *ft, struct ksmbd_tree_connect *tcon, - bool (*skip)(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp)) +__close_file_table_ids(struct ksmbd_file_table *ft, + struct ksmbd_tree_connect *tcon, + bool (*skip)(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp)) { unsigned int id; struct ksmbd_file *fp; diff --git a/fs/cifsd/vfs_cache.h b/fs/cifsd/vfs_cache.h index 5638641dd0cf..823fcb257a42 100644 --- a/fs/cifsd/vfs_cache.h +++ b/fs/cifsd/vfs_cache.h @@ -149,7 +149,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, unsigned int id); struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, unsigned int id); struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, unsigned int id); struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, unsigned int id, - unsigned int pid); + unsigned int pid); void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); From c986ed981ae6a622a453c533389994b6aed6359b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 17:59:56 +0900 Subject: [PATCH 110/417] cifsd: remove unnecessary parentheses around Fix warnings from checkpatch.pl --strict : CHECK: Unnecessary parentheses around 'brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE' #1511: FILE: oplock.c:1511: + if (brk_op->is_lease && + (brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE) && + atomic_read(&brk_op->breaking_cnt)) Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/oplock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index 56c68e9cb7ff..f76de7861e7b 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -1262,7 +1262,7 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, /* Skip oplock being break to none */ if (brk_op->is_lease && - (brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE) && + brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE && atomic_read(&brk_op->breaking_cnt)) goto next; From fc2d1b58c4f2c7240093d738ca99cfcf7a8b3107 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 26 May 2021 18:01:08 +0900 Subject: [PATCH 111/417] cifsd: Prefer kernel type 'u16' over 'uint16_t' Fix a warning from checkpatch.pl --strict: CHECK: Prefer kernel type 'u16' over 'uint16_t' #112: FILE: server.c:112: + uint16_t command; Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/server.c | 4 ++-- fs/cifsd/smb2pdu.c | 2 +- fs/cifsd/smb_common.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c index 4ba43e788ce9..a99963b849d5 100644 --- a/fs/cifsd/server.c +++ b/fs/cifsd/server.c @@ -106,10 +106,10 @@ static inline int check_conn_state(struct ksmbd_work *work) #define TCP_HANDLER_ABORT 1 static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, - uint16_t *cmd) + u16 *cmd) { struct smb_version_cmds *cmds; - uint16_t command; + u16 command; int ret; if (check_conn_state(work)) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 84b243b3895a..212cdffd27bc 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -193,7 +193,7 @@ int is_smb2_rsp(struct ksmbd_work *work) * * Return: smb2 request command value */ -uint16_t get_smb2_cmd_val(struct ksmbd_work *work) +u16 get_smb2_cmd_val(struct ksmbd_work *work) { struct smb2_hdr *rcv_hdr; diff --git a/fs/cifsd/smb_common.h b/fs/cifsd/smb_common.h index 2d7b1c693ff4..6e7404b8db96 100644 --- a/fs/cifsd/smb_common.h +++ b/fs/cifsd/smb_common.h @@ -469,7 +469,7 @@ struct filesystem_posix_info { } __packed; struct smb_version_ops { - uint16_t (*get_cmd_val)(struct ksmbd_work *swork); + u16 (*get_cmd_val)(struct ksmbd_work *swork); int (*init_rsp_hdr)(struct ksmbd_work *swork); void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err); int (*allocate_rsp_buf)(struct ksmbd_work *work); From a6a5fa77805b291afc90291a6ae705b1759b9735 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Wed, 26 May 2021 18:59:06 +0900 Subject: [PATCH 112/417] cifsd: lookup a file with LOOKUP_FOLLOW only if 'follow symlinks = yes' Some vfs help functions lookup a file with LOOKUP_FOLLOW regardless of the "follow symlinks" option. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 6 +++++- fs/cifsd/vfs.c | 24 ++++++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 212cdffd27bc..f68e2638d629 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -4583,8 +4583,12 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, struct path path; int rc = 0, len; int fs_infoclass_size = 0; + int lookup_flags = 0; - rc = ksmbd_vfs_kern_path(share->path, LOOKUP_FOLLOW, &path, 0); + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) + lookup_flags = LOOKUP_FOLLOW; + + rc = ksmbd_vfs_kern_path(share->path, lookup_flags, &path, 0); if (rc) { ksmbd_err("cannot create vfs path\n"); return -EIO; diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 355e1a5a893b..291953eff5fa 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -572,11 +572,16 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) struct path path; struct dentry *dentry, *parent; int err; + int flags = 0; if (ksmbd_override_fsids(work)) return -ENOMEM; - err = kern_path(name, LOOKUP_FOLLOW, &path); + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) + flags = LOOKUP_FOLLOW; + + err = kern_path(name, flags, &path); if (err) { ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); ksmbd_revert_fsids(work); @@ -634,11 +639,16 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, struct path oldpath, newpath; struct dentry *dentry; int err; + int flags = 0; if (ksmbd_override_fsids(work)) return -ENOMEM; - err = kern_path(oldname, LOOKUP_FOLLOW, &oldpath); + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) + flags = LOOKUP_FOLLOW; + + err = kern_path(oldname, flags, &oldpath); if (err) { ksmbd_err("cannot get linux path for %s, err = %d\n", oldname, err); @@ -646,7 +656,7 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, } dentry = kern_path_create(AT_FDCWD, newname, &newpath, - LOOKUP_FOLLOW | LOOKUP_REVAL); + flags | LOOKUP_REVAL); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); ksmbd_err("path create err for %s, err %d\n", newname, err); @@ -749,6 +759,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, struct dentry *src_dent, *trap_dent, *src_child; char *dst_name; int err; + int flags; dst_name = extract_last_component(newname); if (!dst_name) @@ -757,7 +768,12 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, src_dent_parent = dget_parent(fp->filp->f_path.dentry); src_dent = fp->filp->f_path.dentry; - err = kern_path(newname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &dst_path); + flags = LOOKUP_DIRECTORY; + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) + flags |= LOOKUP_FOLLOW; + + err = kern_path(newname, flags, &dst_path); if (err) { ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); goto out; From 152de8c68d13845592e8e511136842bcdb691063 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 29 May 2021 07:59:40 +0900 Subject: [PATCH 113/417] cifsd: fix Control flow issues in ksmbd_build_ntlmssp_challenge_blob() Fix a defect reported by Coverity Scan. *** CID 1504970: Control flow issues (NO_EFFECT) /fs/cifsd/auth.c: 622 in ksmbd_build_ntlmssp_challenge_blob() 616 name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL); 617 if (!name) 618 return -ENOMEM; 619 620 conv_len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, 621 sess->conn->local_nls); >>> CID 1504970: Control flow issues (NO_EFFECT) >>> This less-than-zero comparison of an unsigned value is never true. 622 if (conv_len < 0 || conv_len > len) { 623 kfree(name); 624 return -EINVAL; 625 } 626 627 uni_len = UNICODE_LEN(conv_len); Reported-by: Coverity Scan Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 9b86cf4fd73f..5f47de49c05d 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -584,8 +584,8 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, struct target_info *tinfo; wchar_t *name; __u8 *target_name; - unsigned int len, flags, blob_off, blob_len, type, target_info_len = 0; - unsigned int uni_len, conv_len; + unsigned int flags, blob_off, blob_len, type, target_info_len = 0; + int len, uni_len, conv_len; int cflags = sess->ntlmssp.client_flags; memcpy(chgblob->Signature, NTLMSSP_SIGNATURE, 8); From 40c594b647660bf91bc95fe7c9358bff7f56cf2e Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 29 May 2021 22:46:53 +0900 Subject: [PATCH 114/417] cifsd: enclose macro variables in parenthesis checkpatch.pl complains as the following: CHECK: Macro argument 'fp' may be better as '(fp)' to avoid precedence issues. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs_cache.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/cifsd/vfs_cache.h b/fs/cifsd/vfs_cache.h index 823fcb257a42..635eedbd497c 100644 --- a/fs/cifsd/vfs_cache.h +++ b/fs/cifsd/vfs_cache.h @@ -25,14 +25,14 @@ #define KSMBD_NO_FID (UINT_MAX) #define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) -#define FP_FILENAME(fp) fp->filp->f_path.dentry->d_name.name -#define FP_INODE(fp) d_inode(fp->filp->f_path.dentry) -#define PARENT_INODE(fp) d_inode(fp->filp->f_path.dentry->d_parent) +#define FP_FILENAME(fp) ((fp)->filp->f_path.dentry->d_name.name) +#define FP_INODE(fp) d_inode((fp)->filp->f_path.dentry) +#define PARENT_INODE(fp) d_inode((fp)->filp->f_path.dentry->d_parent) -#define ATTR_FP(fp) (fp->attrib_only && \ - (fp->cdoption != FILE_OVERWRITE_IF_LE && \ - fp->cdoption != FILE_OVERWRITE_LE && \ - fp->cdoption != FILE_SUPERSEDE_LE)) +#define ATTR_FP(fp) ((fp)->attrib_only && \ + ((fp)->cdoption != FILE_OVERWRITE_IF_LE && \ + (fp)->cdoption != FILE_OVERWRITE_LE && \ + (fp)->cdoption != FILE_SUPERSEDE_LE)) struct ksmbd_conn; struct ksmbd_session; From d7e5852b4deb121e2c929b2bb7440c5db3e2f90a Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 29 May 2021 09:59:59 +0900 Subject: [PATCH 115/417] cifsd: make alignment match open parenthesis checkpatch.pl complains as the following: Alignment should match open parenthesis. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2ops.c | 2 +- fs/cifsd/smb2pdu.c | 2 +- fs/cifsd/smb2pdu.h | 4 ++-- fs/cifsd/smbacl.h | 15 ++++++++------- fs/cifsd/transport_ipc.h | 6 +++--- fs/cifsd/transport_tcp.c | 8 ++++---- fs/cifsd/vfs.c | 4 ++-- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/fs/cifsd/smb2ops.c b/fs/cifsd/smb2ops.c index 945bc6a78d3c..c47d60bce9d4 100644 --- a/fs/cifsd/smb2ops.c +++ b/fs/cifsd/smb2ops.c @@ -227,7 +227,7 @@ void init_smb3_0_server(struct ksmbd_conn *conn) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && - conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; } diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index f68e2638d629..3e112fbdc2d9 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -560,7 +560,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) sz = large_sz; if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_TBUF && - work->set_trans_buf) + work->set_trans_buf) work->response_buf = ksmbd_find_buffer(sz); else work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); diff --git a/fs/cifsd/smb2pdu.h b/fs/cifsd/smb2pdu.h index 1a8da2122b75..b3d3365d7070 100644 --- a/fs/cifsd/smb2pdu.h +++ b/fs/cifsd/smb2pdu.h @@ -1623,10 +1623,10 @@ void smb2_set_sign_rsp(struct ksmbd_work *work); int smb3_check_sign_req(struct ksmbd_work *work); void smb3_set_sign_rsp(struct ksmbd_work *work); int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, - __le16 dialects_count); + __le16 dialects_count); struct file_lock *smb_flock_init(struct file *f); int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), - void **arg); + void **arg); void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); struct channel *lookup_chann_list(struct ksmbd_session *sess); void smb3_preauth_hash_rsp(struct ksmbd_work *work); diff --git a/fs/cifsd/smbacl.h b/fs/cifsd/smbacl.h index 032b6a3ec6f4..fb5480f0aa89 100644 --- a/fs/cifsd/smbacl.h +++ b/fs/cifsd/smbacl.h @@ -180,22 +180,23 @@ struct posix_acl_state { }; int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, - struct smb_fattr *fattr); + struct smb_fattr *fattr); int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, - int addition_info, __u32 *secdesclen, struct smb_fattr *fattr); + int addition_info, __u32 *secdesclen, + struct smb_fattr *fattr); int init_acl_state(struct posix_acl_state *state, int cnt); void free_acl_state(struct posix_acl_state *state); void posix_state_to_acl(struct posix_acl_state *state, - struct posix_acl_entry *pace); + struct posix_acl_entry *pace); int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid); bool smb_inherit_flags(int flags, bool is_dir); int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, - unsigned int uid, unsigned int gid); + unsigned int uid, unsigned int gid); int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, - __le32 *pdaccess, int uid); + __le32 *pdaccess, int uid); int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, - struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len, - bool type_check); + struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check); void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); void ksmbd_init_domain(u32 *sub_auth); #endif /* _SMBACL_H */ diff --git a/fs/cifsd/transport_ipc.h b/fs/cifsd/transport_ipc.h index 523b4df2c783..9eacc895ffdb 100644 --- a/fs/cifsd/transport_ipc.h +++ b/fs/cifsd/transport_ipc.h @@ -20,9 +20,9 @@ struct sockaddr; struct ksmbd_tree_connect_response * ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, - struct ksmbd_share_config *share, - struct ksmbd_tree_connect *tree_conn, - struct sockaddr *peer_addr); + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr); int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, unsigned long long connect_id); int ksmbd_ipc_logout_request(const char *account); diff --git a/fs/cifsd/transport_tcp.c b/fs/cifsd/transport_tcp.c index 5bd332a58596..d6d5c0038dea 100644 --- a/fs/cifsd/transport_tcp.c +++ b/fs/cifsd/transport_tcp.c @@ -423,10 +423,10 @@ static int create_socket(struct interface *iface) ksmbd_tcp_reuseaddr(ksmbd_socket); ret = sock_setsockopt(ksmbd_socket, - SOL_SOCKET, - SO_BINDTODEVICE, - KERNEL_SOCKPTR(iface->name), - strlen(iface->name)); + SOL_SOCKET, + SO_BINDTODEVICE, + KERNEL_SOCKPTR(iface->name), + strlen(iface->name)); if (ret != -ENODEV && ret < 0) { ksmbd_err("Failed to set SO_BINDTODEVICE: %d\n", ret); goto out_error; diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 291953eff5fa..cd037594f486 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -934,8 +934,8 @@ ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, if (!buf) return -ENOMEM; - xattr_len = vfs_getxattr(&init_user_ns, dentry, xattr_name, (void *)buf, - xattr_len); + xattr_len = vfs_getxattr(&init_user_ns, dentry, xattr_name, + (void *)buf, xattr_len); if (xattr_len > 0) *xattr_buf = buf; else From 113ef68d47f5d36611c16a6ef4bd2a837aa344ab Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Sat, 29 May 2021 16:20:56 +0800 Subject: [PATCH 116/417] cifsd: fix memleak in ksmbd_vfs_stream_write() Before assigning wbuf to stream_buf, memory allocate in ksmbd_vfs_getcasexattr() need be freed. Signed-off-by: Yang Yingliang Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index cd037594f486..e70b67e41cd4 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -429,6 +429,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, if (v_len > 0) memcpy(wbuf, stream_buf, v_len); + kvfree(stream_buf); stream_buf = wbuf; } From 673b9ba7a1404fa5beda936b8ad509b70a516b52 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Sat, 29 May 2021 16:20:57 +0800 Subject: [PATCH 117/417] cifsd: fix memleak in ksmbd_vfs_stream_read() Before ksmbd_vfs_stream_read() return, memory allocate in ksmbd_vfs_getcasexattr() need be freed. Signed-off-by: Yang Yingliang Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index e70b67e41cd4..85872416bf9b 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -290,6 +290,7 @@ static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, } memcpy(buf, &stream_buf[*pos], count); + kvfree(stream_buf); return v_len > count ? count : v_len; } From fd6de099d7fabc2b86f51dc622453eb279f7cce9 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 31 May 2021 17:25:05 +0900 Subject: [PATCH 118/417] cifsd: check return value of ksmbd_vfs_getcasexattr() correctly If ksmbd_vfs_getcasexattr() returns -ENOMEM, stream_buf is NULL, it will cause null-ptr-deref when using it to copy memory. So we need check the return value of ksmbd_vfs_getcasexattr() by comparing with 0. Signed-off-by: Yang Yingliang Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 85872416bf9b..56b1091473b9 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -274,7 +274,6 @@ static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, { ssize_t v_len; char *stream_buf = NULL; - int err; ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", *pos, count); @@ -283,11 +282,8 @@ static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, fp->stream.name, fp->stream.size, &stream_buf); - if (v_len == -ENOENT) { - ksmbd_err("not found stream in xattr : %zd\n", v_len); - err = -ENOENT; - return err; - } + if ((int)v_len <= 0) + return (int)v_len; memcpy(buf, &stream_buf[*pos], count); kvfree(stream_buf); @@ -415,9 +411,9 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, fp->stream.name, fp->stream.size, &stream_buf); - if (v_len == -ENOENT) { + if ((int)v_len < 0) { ksmbd_err("not found stream in xattr : %zd\n", v_len); - err = -ENOENT; + err = (int)v_len; goto out; } From 2ae1a6cc43027d84e33819ac4376c5e5e11b4152 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 31 May 2021 17:26:43 +0900 Subject: [PATCH 119/417] cifsd: fix potential read overflow in ksmbd_vfs_stream_read() If *pos or *pos + count is greater than v_len, It will read beyond the stream_buf buffer. This patch add the check and cut down count with size of the buffer. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 56b1091473b9..9111b485d611 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -285,9 +285,19 @@ static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, if ((int)v_len <= 0) return (int)v_len; + if (v_len <= *pos) { + count = -EINVAL; + goto free_buf; + } + + if (v_len - *pos < count) + count = v_len - *pos; + memcpy(buf, &stream_buf[*pos], count); + +free_buf: kvfree(stream_buf); - return v_len > count ? count : v_len; + return count; } /** From 97d7f3d3e0e719db42c4f413531e4e417fadf0c1 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 1 Jun 2021 13:18:44 +0900 Subject: [PATCH 120/417] cifsd: fix additional warnings from checkpatch.pl --strict Fix additional warnings from checkpatch.pl --strict. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/mgmt/share_config.c | 2 +- fs/cifsd/mgmt/share_config.h | 6 +++--- fs/cifsd/mgmt/tree_connect.c | 2 +- fs/cifsd/mgmt/user_session.h | 4 +--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/fs/cifsd/mgmt/share_config.c b/fs/cifsd/mgmt/share_config.c index 910d03516b73..bcc4ae4381b9 100644 --- a/fs/cifsd/mgmt/share_config.c +++ b/fs/cifsd/mgmt/share_config.c @@ -157,7 +157,7 @@ static struct ksmbd_share_config *share_config_request(char *name) ret = kern_path(share->path, 0, &share->vfs_path); if (ret) { ksmbd_debug(SMB, "failed to access '%s'\n", - share->path); + share->path); /* Avoid put_path() */ kfree(share->path); share->path = NULL; diff --git a/fs/cifsd/mgmt/share_config.h b/fs/cifsd/mgmt/share_config.h index 49ca89667991..953befc94e84 100644 --- a/fs/cifsd/mgmt/share_config.h +++ b/fs/cifsd/mgmt/share_config.h @@ -34,7 +34,7 @@ struct ksmbd_share_config { #define KSMBD_SHARE_INVALID_GID ((__u16)-1) static inline int share_config_create_mode(struct ksmbd_share_config *share, - umode_t posix_mode) + umode_t posix_mode) { if (!share->force_create_mode) { if (!posix_mode) @@ -46,7 +46,7 @@ static inline int share_config_create_mode(struct ksmbd_share_config *share, } static inline int share_config_directory_mode(struct ksmbd_share_config *share, - umode_t posix_mode) + umode_t posix_mode) { if (!share->force_directory_mode) { if (!posix_mode) @@ -64,7 +64,7 @@ static inline int test_share_config_flag(struct ksmbd_share_config *share, return share->flags & flag; } -extern void __ksmbd_share_config_put(struct ksmbd_share_config *share); +void __ksmbd_share_config_put(struct ksmbd_share_config *share); static inline void ksmbd_share_config_put(struct ksmbd_share_config *share) { diff --git a/fs/cifsd/mgmt/tree_connect.c b/fs/cifsd/mgmt/tree_connect.c index b9cd8fc46e5e..029a9e81e844 100644 --- a/fs/cifsd/mgmt/tree_connect.c +++ b/fs/cifsd/mgmt/tree_connect.c @@ -62,7 +62,7 @@ ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name) status.tree_conn = tree_conn; ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, - GFP_KERNEL)); + GFP_KERNEL)); if (ret) { status.ret = -ENOMEM; goto out_error; diff --git a/fs/cifsd/mgmt/user_session.h b/fs/cifsd/mgmt/user_session.h index 1709563d718b..761bf4776cf1 100644 --- a/fs/cifsd/mgmt/user_session.h +++ b/fs/cifsd/mgmt/user_session.h @@ -12,7 +12,7 @@ #include "../smb_common.h" #include "../ntlmssp.h" -#define CIFDS_SESSION_FLAG_SMB2 (1 << 1) +#define CIFDS_SESSION_FLAG_SMB2 BIT(1) #define PREAUTH_HASHVALUE_SIZE 64 @@ -54,8 +54,6 @@ struct ksmbd_session { struct ida tree_conn_ida; struct list_head rpc_handle_list; - - __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; From 6c4e675ad3594526d6604a7d30f1defdd08a42e4 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 7 Jun 2021 09:08:45 +0900 Subject: [PATCH 121/417] cifsd: fix list_add double add BUG_ON trap in setup_async_work() BUG_ON trap is coming when running xfstests generic/591 and smb2 leases = yes in smb.conf. [ 597.224978] list_add double add: new=ffff9110d292bb20, prev=ffff9110d292bb20, next=ffff9110d6c389e8. [ 597.225073] ------------[ cut here ]------------ [ 597.225077] kernel BUG at lib/list_debug.c:31! [ 597.225090] invalid opcode: 0000 [#1] SMP PTI [ 597.225095] CPU: 2 PID: 501 Comm: kworker/2:3 Tainted: G OE 5.13.0-rc1+ #2 [ 597.225099] Hardware name: SAMSUNG ELECTRONICS CO., LTD. Samsung DeskTop System/SAMSUNG_DT1234567890, BIOS P04KBM.022.121023.SK 10/23/2012 [ 597.225102] Workqueue: ksmbd-io handle_ksmbd_work [ksmbd] [ 597.225125] RIP: 0010:__list_add_valid+0x66/0x70 [ 597.225132] Code: 0b 48 89 c1 4c 89 c6 48 c7 c7 c8 08 c0 95 e8 fd 54 66 00 0f 0b 48 89 f2 4c 89 c1 48 89 fe 48 c7 c7 20 09 c0 95 e8 e6 54 66 00 <0f> 0b 0f 1f 84 00 00 00 00 00 55 48 8b 07 48 b9 00 01 00 00 00 00 [ 597.225136] RSP: 0018:ffffb9c9408dbac0 EFLAGS: 00010282 [ 597.225139] RAX: 0000000000000058 RBX: ffff9110d292ba40 RCX: 0000000000000000 [ 597.225142] RDX: 0000000000000000 RSI: ffff9111da328c30 RDI: ffff9111da328c30 [ 597.225144] RBP: ffffb9c9408dbac0 R08: 0000000000000001 R09: 0000000000000001 [ 597.225147] R10: 0000000003dd35ed R11: ffffb9c9408db888 R12: ffff9110d6c38998 [ 597.225149] R13: ffff9110d6c38800 R14: ffff9110d292bb20 R15: ffff9110d292bb20 [ 597.225152] FS: 0000000000000000(0000) GS:ffff9111da300000(0000) knlGS:0000000000000000 [ 597.225155] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 597.225157] CR2: 00007fd1629f84d0 CR3: 00000000c9a12006 CR4: 00000000001706e0 [ 597.225160] Call Trace: [ 597.225163] setup_async_work+0xa2/0x120 [ksmbd] [ 597.225191] oplock_break+0x396/0x5d0 [ksmbd] [ 597.225206] smb_grant_oplock+0x7a1/0x900 [ksmbd] [ 597.225218] ? smb_grant_oplock+0x7a1/0x900 [ksmbd] [ 597.225231] smb2_open+0xbbb/0x2960 [ksmbd] [ 597.225243] ? smb2_open+0xbbb/0x2960 [ksmbd] [ 597.225257] ? find_held_lock+0x35/0xa0 [ 597.225261] ? xa_load+0xaf/0x160 [ 597.225268] handle_ksmbd_work+0x2e0/0x420 [ksmbd] [ 597.225280] ? handle_ksmbd_work+0x2e0/0x420 [ksmbd] [ 597.225292] process_one_work+0x25a/0x5d0 [ 597.225298] worker_thread+0x3f/0x3a0 [ 597.225302] ? __kthread_parkme+0x6f/0xa0 [ 597.225306] ? process_one_work+0x5d0/0x5d0 [ 597.225309] kthread+0x142/0x160 [ 597.225313] ? kthread_park+0x90/0x90 [ 597.225316] ret_from_fork+0x22/0x30 same work struct can be add to list in smb_break_all_write_oplock() and smb_grant_oplock(). If client send invalid lease break ack to server, This issue can occur by calling both functions. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 3e112fbdc2d9..5b92e00881bb 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -690,9 +690,11 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) work->cancel_fn = fn; work->cancel_argv = arg; - spin_lock(&conn->request_lock); - list_add_tail(&work->async_request_entry, &conn->async_requests); - spin_unlock(&conn->request_lock); + if (list_empty(&work->async_request_entry)) { + spin_lock(&conn->request_lock); + list_add_tail(&work->async_request_entry, &conn->async_requests); + spin_unlock(&conn->request_lock); + } return 0; } From ade62d8b429fe49325593785316bdee3cabaec44 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 7 Jun 2021 09:22:22 +0900 Subject: [PATCH 122/417] cifsd: set epoch in smb2_lease_break response When running generic/591 after smb2 leases is enable, all smb2 lease ack requests failed in ksmbd. because cifs client seems to support only smb2 v2 lease. So cifs doesn't update lease state in ack request if epoch is not set in smb2 lease break request from ksmbd. epoch is used for smb2 v2 leases. So this patch add smb2 create v2 lease context and set increased epoch in smb2 lease break response. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/oplock.c | 95 +++++++++++++++++++++++++++++++++------------- fs/cifsd/oplock.h | 15 +++++--- fs/cifsd/smb2ops.c | 6 +-- fs/cifsd/smb2pdu.h | 21 +++++++++- 4 files changed, 102 insertions(+), 35 deletions(-) diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index f76de7861e7b..5868cdca7187 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -102,6 +102,9 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) lease->new_state = 0; lease->flags = lctx->flags; lease->duration = lctx->duration; + memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); + lease->version = lctx->version; + lease->epoch = 0; INIT_LIST_HEAD(&opinfo->lease_entry); opinfo->o_lease = lease; @@ -750,7 +753,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) rsp = work->response_buf; rsp->StructureSize = cpu_to_le16(44); - rsp->Reserved = 0; + rsp->Epoch = br_info->epoch; rsp->Flags = 0; if (br_info->curr_state & (SMB2_LEASE_WRITE_CACHING_LE | @@ -798,6 +801,10 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) br_info->curr_state = lease->state; br_info->new_state = lease->new_state; + if (lease->version == 2) + br_info->epoch = cpu_to_le16(++lease->epoch); + else + br_info->epoch = 0; memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); work->request_buf = (char *)br_info; @@ -1084,11 +1091,8 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, __le32 prev_op_state = 0; /* not support directory lease */ - if (S_ISDIR(file_inode(fp->filp)->i_mode)) { - if (lctx) - lctx->dlease = 1; + if (S_ISDIR(file_inode(fp->filp)->i_mode)) return 0; - } opinfo = alloc_opinfo(work, pid, tid); if (!opinfo) @@ -1328,24 +1332,48 @@ __u8 smb2_map_lease_to_oplock(__le32 lease_state) */ void create_lease_buf(u8 *rbuf, struct lease *lease) { - struct create_lease *buf = (struct create_lease *)rbuf; char *LeaseKey = (char *)&lease->lease_key; - memset(buf, 0, sizeof(struct create_lease)); - buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); - buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); - buf->lcontext.LeaseFlags = lease->flags; - buf->lcontext.LeaseState = lease->state; - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_lease, lcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof + if (lease->version == 2) { + struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; + char *ParentLeaseKey = (char *)&lease->parent_lease_key; + + memset(buf, 0, sizeof(struct create_lease_v2)); + buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); + buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + buf->lcontext.ParentLeaseKeyLow = *((__le64 *)ParentLeaseKey); + buf->lcontext.ParentLeaseKeyHigh = *((__le64 *)(ParentLeaseKey + 8)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease_v2, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease_v2, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } else { + struct create_lease *buf = (struct create_lease *)rbuf; + + memset(buf, 0, sizeof(struct create_lease)); + buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); + buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof (struct create_lease, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - buf->Name[0] = 'R'; - buf->Name[1] = 'q'; - buf->Name[2] = 'L'; - buf->Name[3] = 's'; + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } } /** @@ -1382,12 +1410,27 @@ struct lease_ctx_info *parse_lease_state(void *open_req) } while (next != 0); if (found) { - struct create_lease *lc = (struct create_lease *)cc; - *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; - *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; - lreq->req_state = lc->lcontext.LeaseState; - lreq->flags = lc->lcontext.LeaseFlags; - lreq->duration = lc->lcontext.LeaseDuration; + if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { + struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; + + *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; + *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + *((__le64 *)lreq->parent_lease_key) = lc->lcontext.ParentLeaseKeyLow; + *((__le64 *)(lreq->parent_lease_key + 8)) = lc->lcontext.ParentLeaseKeyHigh; + lreq->version = 2; + } else { + struct create_lease *lc = (struct create_lease *)cc; + + *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; + *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + lreq->version = 1; + } return lreq; } diff --git a/fs/cifsd/oplock.h b/fs/cifsd/oplock.h index 0abd26123f6d..9fb7ea74e86c 100644 --- a/fs/cifsd/oplock.h +++ b/fs/cifsd/oplock.h @@ -37,11 +37,12 @@ #define SMB2_LEASE_KEY_SIZE 16 struct lease_ctx_info { - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; - __le32 req_state; - __le32 flags; - __le64 duration; - int dlease; + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 req_state; + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; }; struct lease_table { @@ -57,6 +58,9 @@ struct lease { __le32 new_state; __le32 flags; __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; + unsigned short epoch; struct lease_table *l_lb; }; @@ -86,6 +90,7 @@ struct oplock_info { struct lease_break_info { __le32 curr_state; __le32 new_state; + __le16 epoch; char lease_key[SMB2_LEASE_KEY_SIZE]; }; diff --git a/fs/cifsd/smb2ops.c b/fs/cifsd/smb2ops.c index c47d60bce9d4..8999c3faf4fc 100644 --- a/fs/cifsd/smb2ops.c +++ b/fs/cifsd/smb2ops.c @@ -57,7 +57,7 @@ static struct smb_version_values smb30_server_values = { .cap_unix = 0, .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease), + .create_lease_size = sizeof(struct create_lease_v2), .create_durable_size = sizeof(struct create_durable_rsp), .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), .create_mxac_size = sizeof(struct create_mxac_rsp), @@ -83,7 +83,7 @@ static struct smb_version_values smb302_server_values = { .cap_unix = 0, .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease), + .create_lease_size = sizeof(struct create_lease_v2), .create_durable_size = sizeof(struct create_durable_rsp), .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), .create_mxac_size = sizeof(struct create_mxac_rsp), @@ -109,7 +109,7 @@ static struct smb_version_values smb311_server_values = { .cap_unix = 0, .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease), + .create_lease_size = sizeof(struct create_lease_v2), .create_durable_size = sizeof(struct create_durable_rsp), .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), .create_mxac_size = sizeof(struct create_mxac_rsp), diff --git a/fs/cifsd/smb2pdu.h b/fs/cifsd/smb2pdu.h index b3d3365d7070..0d5349e75dd9 100644 --- a/fs/cifsd/smb2pdu.h +++ b/fs/cifsd/smb2pdu.h @@ -735,12 +735,31 @@ struct lease_context { __le64 LeaseDuration; } __packed; +struct lease_context_v2 { + __le64 LeaseKeyLow; + __le64 LeaseKeyHigh; + __le32 LeaseState; + __le32 LeaseFlags; + __le64 LeaseDuration; + __le64 ParentLeaseKeyLow; + __le64 ParentLeaseKeyHigh; + __le16 Epoch; + __le16 Reserved; +} __packed; + struct create_lease { struct create_context ccontext; __u8 Name[8]; struct lease_context lcontext; } __packed; +struct create_lease_v2 { + struct create_context ccontext; + __u8 Name[8]; + struct lease_context_v2 lcontext; + __u8 Pad[4]; +} __packed; + /* Currently defined values for close flags */ #define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) struct smb2_close_req { @@ -1249,7 +1268,7 @@ struct smb2_oplock_break { struct smb2_lease_break { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 44 */ - __le16 Reserved; + __le16 Epoch; __le32 Flags; __u8 LeaseKey[16]; __le32 CurrentLeaseState; From d4b26c285802d3088342ff4ddf4d287d540c929e Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sun, 6 Jun 2021 11:42:25 +0900 Subject: [PATCH 123/417] cifsd: fix possible compile error for asn1.c spnego_negtokeninit.asn1.h and spnego_negtokentarg.asn1.h have to be generated before asn1.o is compiled. Because of parallel build, the dependency could be broken, we need to specify the dependency in Makefile. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/cifsd/Makefile b/fs/cifsd/Makefile index ccacb798a932..e422e9853579 100644 --- a/fs/cifsd/Makefile +++ b/fs/cifsd/Makefile @@ -4,9 +4,6 @@ # obj-$(CONFIG_SMB_SERVER) += ksmbd.o -$(obj)/spnego_negtokeninit.asn1.o: $(obj)/spnego_negtokeninit.asn1.c $(obj)/spnego_negtokeninit.asn1.h -$(obj)/spnego_negtokentarg.asn1.o: $(obj)/spnego_negtokentarg.asn1.c $(obj)/spnego_negtokentarg.asn1.h - ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o buffer_pool.o \ misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \ mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ @@ -14,4 +11,10 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o buffer_pool.o \ transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ smb2ops.o smb2misc.o spnego_negtokeninit.asn1.o \ spnego_negtokentarg.asn1.o asn1.o ndr.o + +$(obj)/asn1.o: $(obj)/spnego_negtokeninit.asn1.h $(obj)/spnego_negtokentarg.asn1.h + +$(obj)/spnego_negtokeninit.asn1.o: $(obj)/spnego_negtokeninit.asn1.c $(obj)/spnego_negtokeninit.asn1.h +$(obj)/spnego_negtokentarg.asn1.o: $(obj)/spnego_negtokentarg.asn1.c $(obj)/spnego_negtokentarg.asn1.h + ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o From 3aefd54da5ec6e7ec1f1e682007f5819c99d8588 Mon Sep 17 00:00:00 2001 From: Wan Jiabing Date: Mon, 7 Jun 2021 12:54:32 +0800 Subject: [PATCH 124/417] cifsd: remove duplicated argument Fix the following coccicheck warning: ./fs/cifsd/smb2pdu.c:1713:27-41: duplicated argument to & or | FILE_DELETE_LE is duplicated. Remove one and reorder argument to make coding style reasonable. Signed-off-by: Wan Jiabing Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 5b92e00881bb..ac15a9287310 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -1712,10 +1712,10 @@ int smb2_tree_connect(struct ksmbd_work *work) KSMBD_TREE_CONN_FLAG_WRITABLE)) { rsp->MaximalAccess |= FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE | FILE_WRITE_EA_LE | - FILE_DELETE_CHILD_LE | FILE_DELETE_LE | - FILE_WRITE_ATTRIBUTES_LE | FILE_DELETE_LE | - FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE | - FILE_WRITE_OWNER_LE | FILE_SYNCHRONIZE_LE; + FILE_DELETE_LE | FILE_WRITE_ATTRIBUTES_LE | + FILE_DELETE_CHILD_LE | FILE_READ_CONTROL_LE | + FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | + FILE_SYNCHRONIZE_LE; } } From 99f45259fe121a10881f486e075019260f403b6a Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Wed, 9 Jun 2021 10:06:57 +0900 Subject: [PATCH 125/417] cifsd: append ksmbd prefix into names for asn1 decoder Because functions and variables generated from ASN1 compiler aren't static, append ksmbd prefix into thoses to avoid link errors. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Makefile | 10 ++--- fs/cifsd/asn1.c | 27 ++++++------ fs/cifsd/ksmbd_spnego_negtokeninit.asn1 | 31 +++++++++++++ ...rg.asn1 => ksmbd_spnego_negtokentarg.asn1} | 2 +- fs/cifsd/spnego_negtokeninit.asn1 | 43 ------------------- 5 files changed, 52 insertions(+), 61 deletions(-) create mode 100644 fs/cifsd/ksmbd_spnego_negtokeninit.asn1 rename fs/cifsd/{spnego_negtokentarg.asn1 => ksmbd_spnego_negtokentarg.asn1} (80%) delete mode 100644 fs/cifsd/spnego_negtokeninit.asn1 diff --git a/fs/cifsd/Makefile b/fs/cifsd/Makefile index e422e9853579..30f64b87cf61 100644 --- a/fs/cifsd/Makefile +++ b/fs/cifsd/Makefile @@ -9,12 +9,12 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o buffer_pool.o \ mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ - smb2ops.o smb2misc.o spnego_negtokeninit.asn1.o \ - spnego_negtokentarg.asn1.o asn1.o ndr.o + smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \ + ksmbd_spnego_negtokentarg.asn1.o asn1.o ndr.o -$(obj)/asn1.o: $(obj)/spnego_negtokeninit.asn1.h $(obj)/spnego_negtokentarg.asn1.h +$(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h -$(obj)/spnego_negtokeninit.asn1.o: $(obj)/spnego_negtokeninit.asn1.c $(obj)/spnego_negtokeninit.asn1.h -$(obj)/spnego_negtokentarg.asn1.o: $(obj)/spnego_negtokentarg.asn1.c $(obj)/spnego_negtokentarg.asn1.h +$(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c $(obj)/ksmbd_spnego_negtokeninit.asn1.h +$(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o diff --git a/fs/cifsd/asn1.c b/fs/cifsd/asn1.c index 1be3072fee1a..2c63a3e5618b 100644 --- a/fs/cifsd/asn1.c +++ b/fs/cifsd/asn1.c @@ -18,8 +18,8 @@ #include "asn1.h" #include "connection.h" #include "auth.h" -#include "spnego_negtokeninit.asn1.h" -#include "spnego_negtokentarg.asn1.h" +#include "ksmbd_spnego_negtokeninit.asn1.h" +#include "ksmbd_spnego_negtokentarg.asn1.h" #define SPNEGO_OID_LEN 7 #define NTLMSSP_OID_LEN 10 @@ -119,7 +119,7 @@ int ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, struct ksmbd_conn *conn) { - return asn1_ber_decoder(&spnego_negtokeninit_decoder, conn, + return asn1_ber_decoder(&ksmbd_spnego_negtokeninit_decoder, conn, security_blob, length); } @@ -127,7 +127,7 @@ int ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, struct ksmbd_conn *conn) { - return asn1_ber_decoder(&spnego_negtokentarg_decoder, conn, + return asn1_ber_decoder(&ksmbd_spnego_negtokentarg_decoder, conn, security_blob, length); } @@ -248,8 +248,8 @@ int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, return 0; } -int gssapi_this_mech(void *context, size_t hdrlen, unsigned char tag, - const void *value, size_t vlen) +int ksmbd_gssapi_this_mech(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) { unsigned long *oid; size_t oidlen; @@ -273,8 +273,9 @@ out: return err; } -int neg_token_init_mech_type(void *context, size_t hdrlen, unsigned char tag, - const void *value, size_t vlen) +int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) { struct ksmbd_conn *conn = context; unsigned long *oid; @@ -310,8 +311,9 @@ fail: return -EBADMSG; } -int neg_token_init_mech_token(void *context, size_t hdrlen, unsigned char tag, - const void *value, size_t vlen) +int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) { struct ksmbd_conn *conn = context; @@ -324,8 +326,9 @@ int neg_token_init_mech_token(void *context, size_t hdrlen, unsigned char tag, return 0; } -int neg_token_targ_resp_token(void *context, size_t hdrlen, unsigned char tag, - const void *value, size_t vlen) +int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) { struct ksmbd_conn *conn = context; diff --git a/fs/cifsd/ksmbd_spnego_negtokeninit.asn1 b/fs/cifsd/ksmbd_spnego_negtokeninit.asn1 new file mode 100644 index 000000000000..0065f191b54b --- /dev/null +++ b/fs/cifsd/ksmbd_spnego_negtokeninit.asn1 @@ -0,0 +1,31 @@ +GSSAPI ::= + [APPLICATION 0] IMPLICIT SEQUENCE { + thisMech + OBJECT IDENTIFIER ({ksmbd_gssapi_this_mech}), + negotiationToken + NegotiationToken + } + +MechType ::= OBJECT IDENTIFIER ({ksmbd_neg_token_init_mech_type}) + +MechTypeList ::= SEQUENCE OF MechType + +NegTokenInit ::= + SEQUENCE { + mechTypes + [0] MechTypeList, + reqFlags + [1] BIT STRING OPTIONAL, + mechToken + [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_init_mech_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } + +NegotiationToken ::= + CHOICE { + negTokenInit + [0] NegTokenInit, + negTokenTarg + [1] ANY + } diff --git a/fs/cifsd/spnego_negtokentarg.asn1 b/fs/cifsd/ksmbd_spnego_negtokentarg.asn1 similarity index 80% rename from fs/cifsd/spnego_negtokentarg.asn1 rename to fs/cifsd/ksmbd_spnego_negtokentarg.asn1 index 8324bcd1bbd7..1151933e7b9c 100644 --- a/fs/cifsd/spnego_negtokentarg.asn1 +++ b/fs/cifsd/ksmbd_spnego_negtokentarg.asn1 @@ -13,7 +13,7 @@ NegTokenTarg ::= supportedMech [1] OBJECT IDENTIFIER OPTIONAL, responseToken - [2] OCTET STRING OPTIONAL ({neg_token_targ_resp_token}), + [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_targ_resp_token}), mechListMIC [3] OCTET STRING OPTIONAL } diff --git a/fs/cifsd/spnego_negtokeninit.asn1 b/fs/cifsd/spnego_negtokeninit.asn1 deleted file mode 100644 index 1b153cb6a39e..000000000000 --- a/fs/cifsd/spnego_negtokeninit.asn1 +++ /dev/null @@ -1,43 +0,0 @@ -GSSAPI ::= - [APPLICATION 0] IMPLICIT SEQUENCE { - thisMech - OBJECT IDENTIFIER ({gssapi_this_mech}), - negotiationToken - NegotiationToken - } - -MechType ::= OBJECT IDENTIFIER ({neg_token_init_mech_type}) - -MechTypeList ::= SEQUENCE OF MechType - -NegTokenInit ::= - SEQUENCE { - mechTypes - [0] MechTypeList, - reqFlags - [1] BIT STRING OPTIONAL, - mechToken - [2] OCTET STRING OPTIONAL ({neg_token_init_mech_token}), - mechListMIC - [3] OCTET STRING OPTIONAL - } - -NegTokenTarg ::= - SEQUENCE { - negResult - [0] ENUMERATED OPTIONAL, - supportedMech - [1] OBJECT IDENTIFIER OPTIONAL, - responseToken - [2] OCTET STRING OPTIONAL ({neg_token_targ_resp_token}), - mechListMIC - [3] OCTET STRING OPTIONAL - } - -NegotiationToken ::= - CHOICE { - negTokenInit - [0] NegTokenInit, - negTokenTarg - [1] ANY - } From 5fb68864674faa3e0a4fc767c4a87f51ece218c6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 18 Jun 2021 09:54:53 +0900 Subject: [PATCH 126/417] ksmbd: fix kfree of uninitialized pointer oid Currently function ksmbd_neg_token_init_mech_type can kfree an uninitialized pointer oid when the call to asn1_oid_decode fails when vlen is out of range. All the other failure cases in function asn1_oid_decode set *oid to NULL on an error, so fix the issue by ensuring the vlen out of range error also nullifies the pointer. Addresses-Coverity: ("Uninitialized pointer read") Signed-off-by: Colin Ian King Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/asn1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/cifsd/asn1.c b/fs/cifsd/asn1.c index 2c63a3e5618b..b014f4638610 100644 --- a/fs/cifsd/asn1.c +++ b/fs/cifsd/asn1.c @@ -66,7 +66,7 @@ static bool asn1_oid_decode(const unsigned char *value, size_t vlen, vlen += 1; if (vlen < 2 || vlen > UINT_MAX / sizeof(unsigned long)) - return false; + goto fail_nullify; *oid = kmalloc(vlen * sizeof(unsigned long), GFP_KERNEL); if (!*oid) @@ -102,6 +102,7 @@ static bool asn1_oid_decode(const unsigned char *value, size_t vlen, fail: kfree(*oid); +fail_nullify: *oid = NULL; return false; } From f5a544e3bab78142207e0242d22442db85ba1eff Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 18 Jun 2021 10:04:19 +0900 Subject: [PATCH 127/417] ksmbd: add support for SMB3 multichannel Add support for SMB3 multichannel. It will be enable by setting 'server multi channel support = yes' in smb.conf. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 32 ++++--- fs/cifsd/auth.h | 6 +- fs/cifsd/connection.h | 1 + fs/cifsd/ksmbd_server.h | 1 + fs/cifsd/mgmt/user_session.c | 49 ++++++++++- fs/cifsd/mgmt/user_session.h | 11 ++- fs/cifsd/smb2ops.c | 9 ++ fs/cifsd/smb2pdu.c | 165 ++++++++++++++++++++++++++--------- fs/cifsd/smb2pdu.h | 3 +- fs/cifsd/smb_common.h | 2 +- 10 files changed, 221 insertions(+), 58 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 5f47de49c05d..1ba03a7c3201 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -921,13 +921,14 @@ smb3signkey_ret: } static int generate_smb3signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn, const struct derivation *signing) { int rc; struct channel *chann; char *key; - chann = lookup_chann_list(sess); + chann = lookup_chann_list(sess, conn); if (!chann) return 0; @@ -953,7 +954,8 @@ static int generate_smb3signingkey(struct ksmbd_session *sess, return 0; } -int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess) +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn) { struct derivation d; @@ -961,22 +963,32 @@ int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess) d.label.iov_len = 12; d.context.iov_base = "SmbSign"; d.context.iov_len = 8; - d.binding = false; + d.binding = conn->binding; - return generate_smb3signingkey(sess, &d); + return generate_smb3signingkey(sess, conn, &d); } -int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess) +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn) { struct derivation d; d.label.iov_base = "SMBSigningKey"; d.label.iov_len = 14; - d.context.iov_base = sess->Preauth_HashValue; - d.context.iov_len = 64; - d.binding = false; + if (conn->binding) { + struct preauth_session *preauth_sess; - return generate_smb3signingkey(sess, &d); + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) + return -ENOENT; + d.context.iov_base = preauth_sess->Preauth_HashValue; + } else { + d.context.iov_base = sess->Preauth_HashValue; + } + d.context.iov_len = 64; + d.binding = conn->binding; + + return generate_smb3signingkey(sess, conn, &d); } struct derivation_twin { @@ -1148,7 +1160,7 @@ static int ksmbd_get_encryption_key(struct ksmbd_conn *conn, __u64 ses_id, struct ksmbd_session *sess; u8 *ses_enc_key; - sess = ksmbd_session_lookup(conn, ses_id); + sess = ksmbd_session_lookup_all(conn, ses_id); if (!sess) return -EINVAL; diff --git a/fs/cifsd/auth.h b/fs/cifsd/auth.h index 650bd7dd6750..9c2d4badd05d 100644 --- a/fs/cifsd/auth.h +++ b/fs/cifsd/auth.h @@ -54,8 +54,10 @@ int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, int n_vec, char *sig); int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, int n_vec, char *sig); -int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess); -int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess); +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn); int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess); int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess); int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, diff --git a/fs/cifsd/connection.h b/fs/cifsd/connection.h index 1658442b27b0..98108b41f739 100644 --- a/fs/cifsd/connection.h +++ b/fs/cifsd/connection.h @@ -106,6 +106,7 @@ struct ksmbd_conn { __le16 cipher_type; __le16 compress_algorithm; bool posix_ext_supported; + bool binding; }; struct ksmbd_conn_ops { diff --git a/fs/cifsd/ksmbd_server.h b/fs/cifsd/ksmbd_server.h index 442077a1e77b..5ae3fe91bfb4 100644 --- a/fs/cifsd/ksmbd_server.h +++ b/fs/cifsd/ksmbd_server.h @@ -33,6 +33,7 @@ struct ksmbd_heartbeat { #define KSMBD_GLOBAL_FLAG_CACHE_TBUF BIT(1) #define KSMBD_GLOBAL_FLAG_CACHE_RBUF BIT(2) #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(3) +#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(4) struct ksmbd_startup_request { __u32 flags; diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index 739588a6c96a..c3487b1a004c 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -207,7 +207,8 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) } } -bool ksmbd_session_id_match(struct ksmbd_session *sess, unsigned long long id) +static bool ksmbd_session_id_match(struct ksmbd_session *sess, + unsigned long long id) { return sess->id == id; } @@ -250,6 +251,52 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) return sess; } +struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct ksmbd_session *sess; + + sess = ksmbd_session_lookup(conn, id); + if (!sess && conn->binding) + sess = ksmbd_session_lookup_slowpath(id); + return sess; +} + +struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + u64 sess_id) +{ + struct preauth_session *sess; + + sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL); + if (!sess) + return NULL; + + sess->id = sess_id; + memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue, + PREAUTH_HASHVALUE_SIZE); + list_add(&sess->preauth_entry, &conn->preauth_sess_table); + + return sess; +} + +static bool ksmbd_preauth_session_id_match(struct preauth_session *sess, + unsigned long long id) +{ + return sess->id == id; +} + +struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct preauth_session *sess = NULL; + + list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) { + if (ksmbd_preauth_session_id_match(sess, id)) + return sess; + } + return NULL; +} + static int __init_smb2_session(struct ksmbd_session *sess) { int id = ksmbd_acquire_smb2_uid(&session_ida); diff --git a/fs/cifsd/mgmt/user_session.h b/fs/cifsd/mgmt/user_session.h index 761bf4776cf1..82289c3cbd2b 100644 --- a/fs/cifsd/mgmt/user_session.h +++ b/fs/cifsd/mgmt/user_session.h @@ -26,8 +26,8 @@ struct channel { struct preauth_session { __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; - u64 sess_id; - struct list_head list_entry; + u64 id; + struct list_head preauth_entry; }; struct ksmbd_session { @@ -82,13 +82,18 @@ struct ksmbd_session *ksmbd_smb2_session_create(void); void ksmbd_session_destroy(struct ksmbd_session *sess); -bool ksmbd_session_id_match(struct ksmbd_session *sess, unsigned long long id); struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id); struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, unsigned long long id); void ksmbd_session_register(struct ksmbd_conn *conn, struct ksmbd_session *sess); void ksmbd_sessions_deregister(struct ksmbd_conn *conn); +struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, + unsigned long long id); +struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + u64 sess_id); +struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, + unsigned long long id); int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess); void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id); diff --git a/fs/cifsd/smb2ops.c b/fs/cifsd/smb2ops.c index 8999c3faf4fc..f7e5f21d4ae2 100644 --- a/fs/cifsd/smb2ops.c +++ b/fs/cifsd/smb2ops.c @@ -229,6 +229,9 @@ void init_smb3_0_server(struct ksmbd_conn *conn) if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; } /** @@ -250,6 +253,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn) if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; } /** @@ -271,6 +277,9 @@ int init_smb3_11_server(struct ksmbd_conn *conn) if (conn->cipher_type) conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + INIT_LIST_HEAD(&conn->preauth_sess_table); return 0; } diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index ac15a9287310..12c954dac51a 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -64,21 +64,21 @@ static inline int check_session_id(struct ksmbd_conn *conn, u64 id) if (id == 0 || id == -1) return 0; - sess = ksmbd_session_lookup(conn, id); + sess = ksmbd_session_lookup_all(conn, id); if (sess) return 1; ksmbd_err("Invalid user session id: %llu\n", id); return 0; } -struct channel *lookup_chann_list(struct ksmbd_session *sess) +struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) { struct channel *chann; struct list_head *t; list_for_each(t, &sess->ksmbd_chann_list) { chann = list_entry(t, struct channel, chann_list); - if (chann && chann->conn == sess->conn) + if (chann && chann->conn == conn) return chann; } @@ -600,7 +600,7 @@ int smb2_check_user_session(struct ksmbd_work *work) sess_id = le64_to_cpu(req_hdr->SessionId); /* Check for validity of user session */ - work->sess = ksmbd_session_lookup(conn, sess_id); + work->sess = ksmbd_session_lookup_all(conn, sess_id); if (work->sess) return 1; ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); @@ -1165,18 +1165,30 @@ static int generate_preauth_hash(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; + u8 *preauth_hash; if (conn->dialect != SMB311_PROT_ID) return 0; - if (!sess->Preauth_HashValue) { - if (alloc_preauth_hash(sess, conn)) - return -ENOMEM; + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) { + preauth_sess = ksmbd_preauth_session_alloc(conn, sess->id); + if (!preauth_sess) + return -ENOMEM; + } + + preauth_hash = preauth_sess->Preauth_HashValue; + } else { + if (!sess->Preauth_HashValue) + if (alloc_preauth_hash(sess, conn)) + return -ENOMEM; + preauth_hash = sess->Preauth_HashValue; } - ksmbd_gen_preauth_integrity_hash(conn, - work->request_buf, - sess->Preauth_HashValue); + ksmbd_gen_preauth_integrity_hash(conn, work->request_buf, preauth_hash); return 0; } @@ -1383,15 +1395,19 @@ static int ntlm_authenticate(struct ksmbd_work *work) * that it is reauthentication. And the user/password * has been verified, so return it here. */ - if (sess->state == SMB2_SESSION_VALID) + if (sess->state == SMB2_SESSION_VALID) { + if (conn->binding) + goto binding_session; return 0; + } if ((conn->sign || server_conf.enforced_signing) || (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) sess->sign = true; if (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION && - conn->ops->generate_encryptionkey) { + conn->ops->generate_encryptionkey && + !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { rc = conn->ops->generate_encryptionkey(sess); if (rc) { ksmbd_debug(SMB, @@ -1409,8 +1425,9 @@ static int ntlm_authenticate(struct ksmbd_work *work) } } +binding_session: if (conn->dialect >= SMB30_PROT_ID) { - chann = lookup_chann_list(sess); + chann = lookup_chann_list(sess, conn); if (!chann) { chann = kmalloc(sizeof(struct channel), GFP_KERNEL); if (!chann) @@ -1423,7 +1440,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) } if (conn->ops->generate_signingkey) { - rc = conn->ops->generate_signingkey(sess); + rc = conn->ops->generate_signingkey(sess, conn); if (rc) { ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); rsp->hdr.Status = STATUS_LOGON_FAILURE; @@ -1500,7 +1517,7 @@ static int krb5_authenticate(struct ksmbd_work *work) } if (conn->dialect >= SMB30_PROT_ID) { - chann = lookup_chann_list(sess); + chann = lookup_chann_list(sess, conn); if (!chann) { chann = kmalloc(sizeof(struct channel), GFP_KERNEL); if (!chann) @@ -1513,7 +1530,7 @@ static int krb5_authenticate(struct ksmbd_work *work) } if (conn->ops->generate_signingkey) { - retval = conn->ops->generate_signingkey(sess); + retval = conn->ops->generate_signingkey(sess, conn); if (retval) { ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); rsp->hdr.Status = STATUS_LOGON_FAILURE; @@ -1562,12 +1579,59 @@ int smb2_sess_setup(struct ksmbd_work *work) } rsp->hdr.SessionId = cpu_to_le64(sess->id); ksmbd_session_register(conn, sess); + } else if (conn->dialect >= SMB30_PROT_ID && + (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && + req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) { + u64 sess_id = le64_to_cpu(req->hdr.SessionId); + + sess = ksmbd_session_lookup_slowpath(sess_id); + if (!sess) { + rc = -ENOENT; + goto out_err; + } + + if (conn->dialect != sess->conn->dialect) { + rc = -EINVAL; + goto out_err; + } + + if (!(req->hdr.Flags & SMB2_FLAGS_SIGNED)) { + rc = -EINVAL; + goto out_err; + } + + if (strncmp(conn->ClientGUID, sess->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) { + rc = -ENOENT; + goto out_err; + } + + if (sess->state == SMB2_SESSION_IN_PROGRESS) { + rc = -EACCES; + goto out_err; + } + + if (sess->state == SMB2_SESSION_EXPIRED) { + rc = -EFAULT; + goto out_err; + } + + if (ksmbd_session_lookup(conn, sess_id)) { + rc = -EACCES; + goto out_err; + } + + conn->binding = true; + } else if ((conn->dialect < SMB30_PROT_ID || + server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && + (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + rc = -EACCES; + goto out_err; } else { sess = ksmbd_session_lookup(conn, le64_to_cpu(req->hdr.SessionId)); if (!sess) { rc = -ENOENT; - rsp->hdr.Status = STATUS_USER_SESSION_DELETED; goto out_err; } } @@ -1585,15 +1649,15 @@ int smb2_sess_setup(struct ksmbd_work *work) } if (server_conf.auth_mechs & conn->auth_mechs) { + rc = generate_preauth_hash(work); + if (rc) + goto out_err; + if (conn->preferred_auth_mech & (KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5)) { - rc = generate_preauth_hash(work); - if (rc) - goto out_err; - rc = krb5_authenticate(work); if (rc) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; goto out_err; } @@ -1602,10 +1666,6 @@ int smb2_sess_setup(struct ksmbd_work *work) kfree(sess->Preauth_HashValue); sess->Preauth_HashValue = NULL; } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { - rc = generate_preauth_hash(work); - if (rc) - goto out_err; - if (negblob->MessageType == NtLmNegotiate) { rc = ntlm_negotiate(work, negblob); if (rc) @@ -1625,6 +1685,16 @@ int smb2_sess_setup(struct ksmbd_work *work) ksmbd_conn_set_good(work); sess->state = SMB2_SESSION_VALID; + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = + ksmbd_preauth_session_lookup(conn, sess->id); + if (preauth_sess) { + list_del(&preauth_sess->preauth_entry); + kfree(preauth_sess); + } + } kfree(sess->Preauth_HashValue); sess->Preauth_HashValue = NULL; } @@ -1632,15 +1702,24 @@ int smb2_sess_setup(struct ksmbd_work *work) /* TODO: need one more negotiation */ ksmbd_err("Not support the preferred authentication\n"); rc = -EINVAL; - rsp->hdr.Status = STATUS_INVALID_PARAMETER; } } else { ksmbd_err("Not support authentication\n"); rc = -EINVAL; - rsp->hdr.Status = STATUS_INVALID_PARAMETER; } out_err: + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + else if (rc == -EACCES) + rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; + else if (rc == -EFAULT) + rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; + else if (rc) + rsp->hdr.Status = STATUS_LOGON_FAILURE; + if (conn->use_spnego && conn->mechToken) { kfree(conn->mechToken); conn->mechToken = NULL; @@ -7883,7 +7962,7 @@ void smb2_set_sign_rsp(struct ksmbd_work *work) */ int smb3_check_sign_req(struct ksmbd_work *work) { - struct ksmbd_conn *conn; + struct ksmbd_conn *conn = work->conn; char *signing_key; struct smb2_hdr *hdr, *hdr_org; struct channel *chann; @@ -7906,13 +7985,11 @@ int smb3_check_sign_req(struct ksmbd_work *work) if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { signing_key = work->sess->smb3signingkey; - conn = work->sess->conn; } else { - chann = lookup_chann_list(work->sess); + chann = lookup_chann_list(work->sess, conn); if (!chann) return 0; signing_key = chann->smb3signingkey; - conn = chann->conn; } if (!signing_key) { @@ -7943,7 +8020,7 @@ int smb3_check_sign_req(struct ksmbd_work *work) */ void smb3_set_sign_rsp(struct ksmbd_work *work) { - struct ksmbd_conn *conn; + struct ksmbd_conn *conn = work->conn; struct smb2_hdr *req_hdr; struct smb2_hdr *hdr, *hdr_org; struct channel *chann; @@ -7970,13 +8047,11 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { signing_key = work->sess->smb3signingkey; - conn = work->sess->conn; } else { - chann = lookup_chann_list(work->sess); + chann = lookup_chann_list(work->sess, work->conn); if (!chann) return; signing_key = chann->smb3signingkey; - conn = chann->conn; } if (!signing_key) @@ -8020,11 +8095,21 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, conn->preauth_info->Preauth_HashValue); - if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && - sess && sess->state == SMB2_SESSION_IN_PROGRESS) { + if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { __u8 *hash_value; - hash_value = sess->Preauth_HashValue; + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) + return; + hash_value = preauth_sess->Preauth_HashValue; + } else { + hash_value = sess->Preauth_HashValue; + if (!hash_value) + return; + } ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, hash_value); } @@ -8116,7 +8201,7 @@ int smb3_decrypt_req(struct ksmbd_work *work) unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize); int rc = 0; - sess = ksmbd_session_lookup(conn, le64_to_cpu(tr_hdr->SessionId)); + sess = ksmbd_session_lookup_all(conn, le64_to_cpu(tr_hdr->SessionId)); if (!sess) { ksmbd_err("invalid session id(%llx) in transform header\n", le64_to_cpu(tr_hdr->SessionId)); diff --git a/fs/cifsd/smb2pdu.h b/fs/cifsd/smb2pdu.h index 0d5349e75dd9..0eac40e1ba65 100644 --- a/fs/cifsd/smb2pdu.h +++ b/fs/cifsd/smb2pdu.h @@ -1647,7 +1647,8 @@ struct file_lock *smb_flock_init(struct file *f); int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg); void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); -struct channel *lookup_chann_list(struct ksmbd_session *sess); +struct channel *lookup_chann_list(struct ksmbd_session *sess, + struct ksmbd_conn *conn); void smb3_preauth_hash_rsp(struct ksmbd_work *work); int smb3_is_transform_hdr(void *buf); int smb3_decrypt_req(struct ksmbd_work *work); diff --git a/fs/cifsd/smb_common.h b/fs/cifsd/smb_common.h index 6e7404b8db96..084166ba7654 100644 --- a/fs/cifsd/smb_common.h +++ b/fs/cifsd/smb_common.h @@ -479,7 +479,7 @@ struct smb_version_ops { bool (*is_sign_req)(struct ksmbd_work *work, unsigned int command); int (*check_sign_req)(struct ksmbd_work *work); void (*set_sign_rsp)(struct ksmbd_work *work); - int (*generate_signingkey)(struct ksmbd_session *sess); + int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn); int (*generate_encryptionkey)(struct ksmbd_session *sess); int (*is_transform_hdr)(void *buf); int (*decrypt_req)(struct ksmbd_work *work); From c30f4eb84badf7476824c38f874542a2e653b46b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 18 Jun 2021 10:17:37 +0900 Subject: [PATCH 128/417] ksmbd: remove cache read/trans buffer support As vmalloc performance improvement patch for big allocation is merged into linux kernel, This feature is no longer not needed. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Makefile | 4 +- fs/cifsd/auth.c | 1 - fs/cifsd/buffer_pool.c | 265 ----------------------------------- fs/cifsd/buffer_pool.h | 17 --- fs/cifsd/connection.c | 1 - fs/cifsd/crypto_ctx.c | 1 - fs/cifsd/ksmbd_server.h | 6 +- fs/cifsd/ksmbd_work.c | 14 +- fs/cifsd/ksmbd_work.h | 2 - fs/cifsd/mgmt/share_config.c | 1 - fs/cifsd/mgmt/tree_connect.c | 1 - fs/cifsd/mgmt/user_config.c | 1 - fs/cifsd/mgmt/user_session.c | 1 - fs/cifsd/oplock.c | 1 - fs/cifsd/server.c | 18 ++- fs/cifsd/smb2pdu.c | 34 +---- fs/cifsd/transport_ipc.c | 1 - fs/cifsd/transport_rdma.c | 1 - fs/cifsd/transport_tcp.c | 1 - fs/cifsd/vfs.c | 1 - fs/cifsd/vfs_cache.c | 34 ++++- fs/cifsd/vfs_cache.h | 2 + 22 files changed, 53 insertions(+), 355 deletions(-) delete mode 100644 fs/cifsd/buffer_pool.c delete mode 100644 fs/cifsd/buffer_pool.h diff --git a/fs/cifsd/Makefile b/fs/cifsd/Makefile index 30f64b87cf61..7d6337a7dee4 100644 --- a/fs/cifsd/Makefile +++ b/fs/cifsd/Makefile @@ -4,13 +4,13 @@ # obj-$(CONFIG_SMB_SERVER) += ksmbd.o -ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o buffer_pool.o \ +ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \ misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \ mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \ - ksmbd_spnego_negtokentarg.asn1.o asn1.o ndr.o + ksmbd_spnego_negtokentarg.asn1.o asn1.o $(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index 1ba03a7c3201..daf31c9f0880 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -29,7 +29,6 @@ #include "mgmt/user_config.h" #include "crypto_ctx.h" #include "transport_ipc.h" -#include "buffer_pool.h" /* * Fixed format data defining GSS header and fixed string diff --git a/fs/cifsd/buffer_pool.c b/fs/cifsd/buffer_pool.c deleted file mode 100644 index ea7d2d1a056a..000000000000 --- a/fs/cifsd/buffer_pool.c +++ /dev/null @@ -1,265 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "glob.h" -#include "buffer_pool.h" -#include "connection.h" -#include "mgmt/ksmbd_ida.h" - -static struct kmem_cache *filp_cache; - -struct wm { - struct list_head list; - unsigned int sz; - char buffer[0]; -}; - -struct wm_list { - struct list_head list; - unsigned int sz; - - spinlock_t wm_lock; - int avail_wm; - struct list_head idle_wm; - wait_queue_head_t wm_wait; -}; - -static LIST_HEAD(wm_lists); -static DEFINE_RWLOCK(wm_lists_lock); - -static struct wm *wm_alloc(size_t sz, gfp_t flags) -{ - struct wm *wm; - size_t alloc_sz = sz + sizeof(struct wm); - - if (sz > SIZE_MAX - sizeof(struct wm)) - return NULL; - - wm = kvmalloc(alloc_sz, flags); - if (!wm) - return NULL; - wm->sz = sz; - return wm; -} - -static int register_wm_size_class(size_t sz) -{ - struct wm_list *l, *nl; - - nl = kmalloc(sizeof(struct wm_list), GFP_KERNEL); - if (!nl) - return -ENOMEM; - - nl->sz = sz; - spin_lock_init(&nl->wm_lock); - INIT_LIST_HEAD(&nl->idle_wm); - INIT_LIST_HEAD(&nl->list); - init_waitqueue_head(&nl->wm_wait); - nl->avail_wm = 0; - - write_lock(&wm_lists_lock); - list_for_each_entry(l, &wm_lists, list) { - if (l->sz == sz) { - write_unlock(&wm_lists_lock); - kfree(nl); - return 0; - } - } - - list_add(&nl->list, &wm_lists); - write_unlock(&wm_lists_lock); - return 0; -} - -static struct wm_list *match_wm_list(size_t size) -{ - struct wm_list *l, *rl = NULL; - - read_lock(&wm_lists_lock); - list_for_each_entry(l, &wm_lists, list) { - if (l->sz == size) { - rl = l; - break; - } - } - read_unlock(&wm_lists_lock); - return rl; -} - -static struct wm *find_wm(size_t size) -{ - struct wm_list *wm_list; - struct wm *wm; - - wm_list = match_wm_list(size); - if (!wm_list) { - if (register_wm_size_class(size)) - return NULL; - wm_list = match_wm_list(size); - } - - if (!wm_list) - return NULL; - - while (1) { - spin_lock(&wm_list->wm_lock); - if (!list_empty(&wm_list->idle_wm)) { - wm = list_entry(wm_list->idle_wm.next, - struct wm, - list); - list_del(&wm->list); - spin_unlock(&wm_list->wm_lock); - return wm; - } - - if (wm_list->avail_wm > num_online_cpus()) { - spin_unlock(&wm_list->wm_lock); - wait_event(wm_list->wm_wait, - !list_empty(&wm_list->idle_wm)); - continue; - } - - wm_list->avail_wm++; - spin_unlock(&wm_list->wm_lock); - - wm = wm_alloc(size, GFP_KERNEL); - if (!wm) { - spin_lock(&wm_list->wm_lock); - wm_list->avail_wm--; - spin_unlock(&wm_list->wm_lock); - wait_event(wm_list->wm_wait, - !list_empty(&wm_list->idle_wm)); - continue; - } - break; - } - - return wm; -} - -static void release_wm(struct wm *wm, struct wm_list *wm_list) -{ - if (!wm) - return; - - spin_lock(&wm_list->wm_lock); - if (wm_list->avail_wm <= num_online_cpus()) { - list_add(&wm->list, &wm_list->idle_wm); - spin_unlock(&wm_list->wm_lock); - wake_up(&wm_list->wm_wait); - return; - } - - wm_list->avail_wm--; - spin_unlock(&wm_list->wm_lock); - kvfree(wm); -} - -static void wm_list_free(struct wm_list *l) -{ - struct wm *wm; - - while (!list_empty(&l->idle_wm)) { - wm = list_entry(l->idle_wm.next, struct wm, list); - list_del(&wm->list); - kvfree(wm); - } - kfree(l); -} - -static void wm_lists_destroy(void) -{ - struct wm_list *l; - - while (!list_empty(&wm_lists)) { - l = list_entry(wm_lists.next, struct wm_list, list); - list_del(&l->list); - wm_list_free(l); - } -} - -void *ksmbd_find_buffer(size_t size) -{ - struct wm *wm; - - wm = find_wm(size); - - WARN_ON(!wm); - if (wm) - return wm->buffer; - return NULL; -} - -void ksmbd_release_buffer(void *buffer) -{ - struct wm_list *wm_list; - struct wm *wm; - - if (!buffer) - return; - - wm = container_of(buffer, struct wm, buffer); - wm_list = match_wm_list(wm->sz); - WARN_ON(!wm_list); - if (wm_list) - release_wm(wm, wm_list); -} - -void *ksmbd_realloc_response(void *ptr, size_t old_sz, size_t new_sz) -{ - size_t sz = min(old_sz, new_sz); - void *nptr; - - nptr = kvmalloc(new_sz, GFP_KERNEL | __GFP_ZERO); - if (!nptr) - return ptr; - memcpy(nptr, ptr, sz); - kvfree(ptr); - return nptr; -} - -void ksmbd_free_file_struct(void *filp) -{ - kmem_cache_free(filp_cache, filp); -} - -void *ksmbd_alloc_file_struct(void) -{ - return kmem_cache_zalloc(filp_cache, GFP_KERNEL); -} - -void ksmbd_destroy_buffer_pools(void) -{ - wm_lists_destroy(); - ksmbd_work_pool_destroy(); - kmem_cache_destroy(filp_cache); -} - -int ksmbd_init_buffer_pools(void) -{ - if (ksmbd_work_pool_init()) - goto out; - - filp_cache = kmem_cache_create("ksmbd_file_cache", - sizeof(struct ksmbd_file), 0, - SLAB_HWCACHE_ALIGN, NULL); - if (!filp_cache) - goto out; - - return 0; - -out: - ksmbd_err("failed to allocate memory\n"); - ksmbd_destroy_buffer_pools(); - return -ENOMEM; -} diff --git a/fs/cifsd/buffer_pool.h b/fs/cifsd/buffer_pool.h deleted file mode 100644 index 088aa07ba09b..000000000000 --- a/fs/cifsd/buffer_pool.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_BUFFER_POOL_H__ -#define __KSMBD_BUFFER_POOL_H__ - -void *ksmbd_find_buffer(size_t size); -void ksmbd_release_buffer(void *buffer); -void *ksmbd_realloc_response(void *ptr, size_t old_sz, size_t new_sz); -void ksmbd_free_file_struct(void *filp); -void *ksmbd_alloc_file_struct(void); -void ksmbd_destroy_buffer_pools(void); -int ksmbd_init_buffer_pools(void); - -#endif /* __KSMBD_BUFFER_POOL_H__ */ diff --git a/fs/cifsd/connection.c b/fs/cifsd/connection.c index 06c42309be72..a0d15093dd6f 100644 --- a/fs/cifsd/connection.c +++ b/fs/cifsd/connection.c @@ -9,7 +9,6 @@ #include #include "server.h" -#include "buffer_pool.h" #include "smb_common.h" #include "mgmt/ksmbd_ida.h" #include "connection.h" diff --git a/fs/cifsd/crypto_ctx.c b/fs/cifsd/crypto_ctx.c index cfea4c4db30f..7b727fe141a6 100644 --- a/fs/cifsd/crypto_ctx.c +++ b/fs/cifsd/crypto_ctx.c @@ -12,7 +12,6 @@ #include "glob.h" #include "crypto_ctx.h" -#include "buffer_pool.h" struct crypto_ctx_list { spinlock_t ctx_lock; diff --git a/fs/cifsd/ksmbd_server.h b/fs/cifsd/ksmbd_server.h index 5ae3fe91bfb4..c2467a709144 100644 --- a/fs/cifsd/ksmbd_server.h +++ b/fs/cifsd/ksmbd_server.h @@ -30,10 +30,8 @@ struct ksmbd_heartbeat { */ #define KSMBD_GLOBAL_FLAG_INVALID (0) #define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0) -#define KSMBD_GLOBAL_FLAG_CACHE_TBUF BIT(1) -#define KSMBD_GLOBAL_FLAG_CACHE_RBUF BIT(2) -#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(3) -#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(4) +#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) +#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) struct ksmbd_startup_request { __u32 flags; diff --git a/fs/cifsd/ksmbd_work.c b/fs/cifsd/ksmbd_work.c index f284a2a803d6..a88c25965012 100644 --- a/fs/cifsd/ksmbd_work.c +++ b/fs/cifsd/ksmbd_work.c @@ -11,7 +11,6 @@ #include "server.h" #include "connection.h" #include "ksmbd_work.h" -#include "buffer_pool.h" #include "mgmt/ksmbd_ida.h" /* @FIXME */ @@ -38,18 +37,9 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void) void ksmbd_free_work_struct(struct ksmbd_work *work) { WARN_ON(work->saved_cred != NULL); - if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_TBUF && - work->set_trans_buf) - ksmbd_release_buffer(work->response_buf); - else - kvfree(work->response_buf); - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF && - work->set_read_buf) - ksmbd_release_buffer(work->aux_payload_buf); - else - kvfree(work->aux_payload_buf); + kvfree(work->response_buf); + kvfree(work->aux_payload_buf); kfree(work->tr_buf); kvfree(work->request_buf); if (work->async_id) diff --git a/fs/cifsd/ksmbd_work.h b/fs/cifsd/ksmbd_work.h index 28a1692ed37f..0e2d4f3fc49f 100644 --- a/fs/cifsd/ksmbd_work.h +++ b/fs/cifsd/ksmbd_work.h @@ -70,8 +70,6 @@ struct ksmbd_work { /* Is this SYNC or ASYNC ksmbd_work */ bool syncronous:1; bool need_invalidate_rkey:1; - bool set_trans_buf:1; - bool set_read_buf:1; unsigned int remote_key; /* cancel works */ diff --git a/fs/cifsd/mgmt/share_config.c b/fs/cifsd/mgmt/share_config.c index bcc4ae4381b9..fac6034b97a9 100644 --- a/fs/cifsd/mgmt/share_config.c +++ b/fs/cifsd/mgmt/share_config.c @@ -15,7 +15,6 @@ #include "share_config.h" #include "user_config.h" #include "user_session.h" -#include "../buffer_pool.h" #include "../transport_ipc.h" #define SHARE_HASH_BITS 3 diff --git a/fs/cifsd/mgmt/tree_connect.c b/fs/cifsd/mgmt/tree_connect.c index 029a9e81e844..0d28e723a28c 100644 --- a/fs/cifsd/mgmt/tree_connect.c +++ b/fs/cifsd/mgmt/tree_connect.c @@ -7,7 +7,6 @@ #include #include -#include "../buffer_pool.h" #include "../transport_ipc.h" #include "../connection.h" diff --git a/fs/cifsd/mgmt/user_config.c b/fs/cifsd/mgmt/user_config.c index 7f898c5bda25..d21629ae5c89 100644 --- a/fs/cifsd/mgmt/user_config.c +++ b/fs/cifsd/mgmt/user_config.c @@ -7,7 +7,6 @@ #include #include "user_config.h" -#include "../buffer_pool.h" #include "../transport_ipc.h" struct ksmbd_user *ksmbd_login_user(const char *account) diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index c3487b1a004c..77bdf3642f72 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -14,7 +14,6 @@ #include "tree_connect.h" #include "../transport_ipc.h" #include "../connection.h" -#include "../buffer_pool.h" #include "../vfs_cache.h" static DEFINE_IDA(session_ida); diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index 5868cdca7187..1ef2acbea2bb 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -11,7 +11,6 @@ #include "smb_common.h" #include "smbstatus.h" -#include "buffer_pool.h" #include "connection.h" #include "mgmt/user_session.h" #include "mgmt/share_config.h" diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c index a99963b849d5..6d14daae1b07 100644 --- a/fs/cifsd/server.c +++ b/fs/cifsd/server.c @@ -16,7 +16,6 @@ #include "server.h" #include "smb_common.h" #include "smbstatus.h" -#include "buffer_pool.h" #include "connection.h" #include "transport_ipc.h" #include "mgmt/user_session.h" @@ -536,7 +535,8 @@ static int ksmbd_server_shutdown(void) ksmbd_crypto_destroy(); ksmbd_free_global_file_table(); destroy_lease_table(NULL); - ksmbd_destroy_buffer_pools(); + ksmbd_work_pool_destroy(); + ksmbd_exit_file_cache(); server_conf_free(); return 0; } @@ -557,13 +557,17 @@ static int __init ksmbd_server_init(void) if (ret) goto err_unregister; - ret = ksmbd_init_buffer_pools(); + ret = ksmbd_work_pool_init(); if (ret) goto err_unregister; + ret = ksmbd_init_file_cache(); + if (ret) + goto err_destroy_work_pools; + ret = ksmbd_ipc_init(); if (ret) - goto err_free_session_table; + goto err_exit_file_cache; ret = ksmbd_init_global_file_table(); if (ret) @@ -590,8 +594,10 @@ err_destroy_file_table: ksmbd_free_global_file_table(); err_ipc_release: ksmbd_ipc_release(); -err_free_session_table: - ksmbd_destroy_buffer_pools(); +err_exit_file_cache: + ksmbd_exit_file_cache(); +err_destroy_work_pools: + ksmbd_work_pool_destroy(); err_unregister: class_unregister(&ksmbd_control_class); diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 12c954dac51a..345c4c75da9a 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -19,7 +19,6 @@ #include "auth.h" #include "asn1.h" -#include "buffer_pool.h" #include "connection.h" #include "transport_ipc.h" #include "vfs.h" @@ -538,10 +537,8 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) size_t sz = small_sz; int cmd = le16_to_cpu(hdr->Command); - if (cmd == SMB2_IOCTL_HE || cmd == SMB2_QUERY_DIRECTORY_HE) { + if (cmd == SMB2_IOCTL_HE || cmd == SMB2_QUERY_DIRECTORY_HE) sz = large_sz; - work->set_trans_buf = true; - } if (cmd == SMB2_QUERY_INFO_HE) { struct smb2_query_info_req *req; @@ -549,22 +546,15 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) req = work->request_buf; if (req->InfoType == SMB2_O_INFO_FILE && (req->FileInfoClass == FILE_FULL_EA_INFORMATION || - req->FileInfoClass == FILE_ALL_INFORMATION)) { + req->FileInfoClass == FILE_ALL_INFORMATION)) sz = large_sz; - work->set_trans_buf = true; - } } /* allocate large response buf for chained commands */ if (le32_to_cpu(hdr->NextCommand) > 0) sz = large_sz; - if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_TBUF && - work->set_trans_buf) - work->response_buf = ksmbd_find_buffer(sz); - else - work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); - + work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); if (!work->response_buf) return -ENOMEM; @@ -5950,13 +5940,7 @@ int smb2_read(struct ksmbd_work *work) ksmbd_debug(SMB, "filename %s, offset %lld, len %zu\n", FP_FILENAME(fp), offset, length); - if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) { - work->aux_payload_buf = - ksmbd_find_buffer(conn->vals->max_read_size); - work->set_read_buf = true; - } else { - work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); - } + work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); if (!work->aux_payload_buf) { err = -ENOMEM; goto out; @@ -5969,10 +5953,7 @@ int smb2_read(struct ksmbd_work *work) } if ((nbytes == 0 && length != 0) || nbytes < mincount) { - if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) - ksmbd_release_buffer(work->aux_payload_buf); - else - kvfree(work->aux_payload_buf); + kvfree(work->aux_payload_buf); work->aux_payload_buf = NULL; rsp->hdr.Status = STATUS_END_OF_FILE; smb2_set_err_rsp(work); @@ -5989,10 +5970,7 @@ int smb2_read(struct ksmbd_work *work) remain_bytes = smb2_read_rdma_channel(work, req, work->aux_payload_buf, nbytes); - if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF) - ksmbd_release_buffer(work->aux_payload_buf); - else - kvfree(work->aux_payload_buf); + kvfree(work->aux_payload_buf); work->aux_payload_buf = NULL; nbytes = 0; diff --git a/fs/cifsd/transport_ipc.c b/fs/cifsd/transport_ipc.c index b09df832431f..2bcc1cad6037 100644 --- a/fs/cifsd/transport_ipc.c +++ b/fs/cifsd/transport_ipc.c @@ -16,7 +16,6 @@ #include "vfs_cache.h" #include "transport_ipc.h" -#include "buffer_pool.h" #include "server.h" #include "smb_common.h" diff --git a/fs/cifsd/transport_rdma.c b/fs/cifsd/transport_rdma.c index efaa9776841f..52237f023b66 100644 --- a/fs/cifsd/transport_rdma.c +++ b/fs/cifsd/transport_rdma.c @@ -33,7 +33,6 @@ #include "connection.h" #include "smb_common.h" #include "smbstatus.h" -#include "buffer_pool.h" #include "transport_rdma.h" #define SMB_DIRECT_PORT 5445 diff --git a/fs/cifsd/transport_tcp.c b/fs/cifsd/transport_tcp.c index d6d5c0038dea..16702b7874f4 100644 --- a/fs/cifsd/transport_tcp.c +++ b/fs/cifsd/transport_tcp.c @@ -9,7 +9,6 @@ #include "smb_common.h" #include "server.h" #include "auth.h" -#include "buffer_pool.h" #include "connection.h" #include "transport_tcp.h" diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 9111b485d611..fb31c1ccb1bd 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -23,7 +23,6 @@ #include "glob.h" #include "oplock.h" #include "connection.h" -#include "buffer_pool.h" #include "vfs.h" #include "vfs_cache.h" #include "smbacl.h" diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index 6ea09fe82814..dcac1f0a29e4 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -10,7 +10,6 @@ #include "glob.h" #include "vfs_cache.h" -#include "buffer_pool.h" #include "oplock.h" #include "vfs.h" #include "connection.h" @@ -29,6 +28,7 @@ static DEFINE_RWLOCK(inode_hash_lock); static struct ksmbd_file_table global_ft; static atomic_long_t fd_limit; +static struct kmem_cache *filp_cache; void ksmbd_set_fd_limit(unsigned long limit) { @@ -315,7 +315,7 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) kfree(fp->filename); if (ksmbd_stream_fd(fp)) kfree(fp->stream.name); - ksmbd_free_file_struct(fp); + kmem_cache_free(filp_cache, fp); } static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) @@ -539,10 +539,10 @@ unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp) struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) { - struct ksmbd_file *fp; + struct ksmbd_file *fp; int ret; - fp = ksmbd_alloc_file_struct(); + fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL); if (!fp) { ksmbd_err("Failed to allocate memory\n"); return ERR_PTR(-ENOMEM); @@ -561,14 +561,14 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) fp->f_ci = ksmbd_inode_get(fp); if (!fp->f_ci) { - ksmbd_free_file_struct(fp); + kmem_cache_free(filp_cache, fp); return ERR_PTR(-ENOMEM); } ret = __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); if (ret) { ksmbd_inode_put(fp->f_ci); - ksmbd_free_file_struct(fp); + kmem_cache_free(filp_cache, fp); return ERR_PTR(ret); } @@ -640,7 +640,7 @@ void ksmbd_free_global_file_table(void) idr_for_each_entry(global_ft.idr, fp, id) { __ksmbd_remove_durable_fd(fp); - ksmbd_free_file_struct(fp); + kmem_cache_free(filp_cache, fp); } ksmbd_destroy_file_table(&global_ft); @@ -683,3 +683,23 @@ void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) kfree(ft->idr); ft->idr = NULL; } + +int ksmbd_init_file_cache(void) +{ + filp_cache = kmem_cache_create("ksmbd_file_cache", + sizeof(struct ksmbd_file), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!filp_cache) + goto out; + + return 0; + +out: + ksmbd_err("failed to allocate file cache\n"); + return -ENOMEM; +} + +void ksmbd_exit_file_cache(void) +{ + kmem_cache_destroy(filp_cache); +} diff --git a/fs/cifsd/vfs_cache.h b/fs/cifsd/vfs_cache.h index 635eedbd497c..745855367106 100644 --- a/fs/cifsd/vfs_cache.h +++ b/fs/cifsd/vfs_cache.h @@ -182,4 +182,6 @@ void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, int file_info); +int ksmbd_init_file_cache(void); +void ksmbd_exit_file_cache(void); #endif /* __VFS_CACHE_H__ */ From afa8f016c5a527bd004042ea47ca8b8007e4185f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 18 Jun 2021 10:18:34 +0900 Subject: [PATCH 129/417] ksmbd: initialize variables on the declaration Initialize variables on the declaration. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index fb31c1ccb1bd..4e0cf1b95419 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -355,14 +355,11 @@ out: int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, loff_t *pos) { - struct file *filp; + struct file *filp = fp->filp; ssize_t nbytes = 0; - char *rbuf; - struct inode *inode; + char *rbuf = work->aux_payload_buf; + struct inode *inode = file_inode(filp); - rbuf = work->aux_payload_buf; - filp = fp->filp; - inode = file_inode(filp); if (S_ISDIR(inode->i_mode)) return -EISDIR; From f8524776f1bbf2895de757438b41915a9b3d9bbc Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 18 Jun 2021 10:28:00 +0900 Subject: [PATCH 130/417] ksmbd: remove ksmbd_vfs_copy_file_range vfs_copy_file_range and cifs client already does this type of fallback, so this is dead code. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 4 ++-- fs/cifsd/vfs.c | 50 ++-------------------------------------------- fs/cifsd/vfs.h | 3 --- 3 files changed, 4 insertions(+), 53 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 345c4c75da9a..2df8217c7395 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -7437,8 +7437,8 @@ int smb2_ioctl(struct ksmbd_work *work) ret = -EOPNOTSUPP; goto dup_ext_out; } else if (cloned != length) { - cloned = ksmbd_vfs_copy_file_range(fp_in->filp, src_off, - fp_out->filp, dst_off, length); + cloned = vfs_copy_file_range(fp_in->filp, src_off, + fp_out->filp, dst_off, length, 0); if (cloned != length) { if (cloned < 0) ret = cloned; diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 4e0cf1b95419..ef74e56cd05f 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -1802,52 +1802,6 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, return 0; } -int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, - struct file *file_out, loff_t pos_out, size_t len) -{ - struct inode *inode_in = file_inode(file_in); - struct inode *inode_out = file_inode(file_out); - int ret; - - ret = vfs_copy_file_range(file_in, pos_in, file_out, pos_out, len, 0); - /* do splice for the copy between different file systems */ - if (ret != -EXDEV) - return ret; - - if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode)) - return -EISDIR; - if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode)) - return -EINVAL; - - if (!(file_in->f_mode & FMODE_READ) || - !(file_out->f_mode & FMODE_WRITE)) - return -EBADF; - - if (len == 0) - return 0; - - file_start_write(file_out); - - /* - * skip the verification of the range of data. it will be done - * in do_splice_direct - */ - ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out, - len > MAX_RW_COUNT ? MAX_RW_COUNT : len, 0); - if (ret > 0) { - fsnotify_access(file_in); - add_rchar(current, ret); - fsnotify_modify(file_out); - add_wchar(current, ret); - } - - inc_syscr(current); - inc_syscw(current); - - file_end_write(file_out); - return ret; -} - int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, struct ksmbd_file *src_fp, struct ksmbd_file *dst_fp, @@ -1905,8 +1859,8 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, if (src_off + len > src_file_size) return -E2BIG; - ret = ksmbd_vfs_copy_file_range(src_fp->filp, src_off, - dst_fp->filp, dst_off, len); + ret = vfs_copy_file_range(src_fp->filp, src_off, + dst_fp->filp, dst_off, len, 0); if (ret < 0) return ret; diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index 5db1e9e2a754..03b877e6520b 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -218,9 +218,6 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, unsigned int *chunk_count_written, unsigned int *chunk_size_written, loff_t *total_size_written); -int ksmbd_vfs_copy_file_range(struct file *file_in, loff_t pos_in, - struct file *file_out, loff_t pos_out, - size_t len); ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, char **xattr_buf); From 6f3d5eeec744727bf017be3bb12e7fbf1c4438ed Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 18 Jun 2021 10:28:52 +0900 Subject: [PATCH 131/417] ksmbd: use list_for_each_entry instead of list_for_each Use list_for_each_entry instead of list_for_each. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 14 ++++---------- fs/cifsd/smb_common.c | 4 +--- fs/cifsd/vfs_cache.c | 4 +--- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 2df8217c7395..f1642fffe4e1 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -73,10 +73,8 @@ static inline int check_session_id(struct ksmbd_conn *conn, u64 id) struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) { struct channel *chann; - struct list_head *t; - list_for_each(t, &sess->ksmbd_chann_list) { - chann = list_entry(t, struct channel, chann_list); + list_for_each_entry(chann, &sess->ksmbd_chann_list, chann_list) { if (chann && chann->conn == conn) return chann; } @@ -6315,7 +6313,6 @@ int smb2_cancel(struct ksmbd_work *work) struct smb2_hdr *hdr = work->request_buf; struct smb2_hdr *chdr; struct ksmbd_work *cancel_work = NULL; - struct list_head *tmp; int canceled = 0; struct list_head *command_list; @@ -6326,9 +6323,8 @@ int smb2_cancel(struct ksmbd_work *work) command_list = &conn->async_requests; spin_lock(&conn->request_lock); - list_for_each(tmp, command_list) { - cancel_work = list_entry(tmp, struct ksmbd_work, - async_request_entry); + list_for_each_entry(cancel_work, command_list, + async_request_entry) { chdr = cancel_work->request_buf; if (cancel_work->async_id != @@ -6347,9 +6343,7 @@ int smb2_cancel(struct ksmbd_work *work) command_list = &conn->requests; spin_lock(&conn->request_lock); - list_for_each(tmp, command_list) { - cancel_work = list_entry(tmp, struct ksmbd_work, - request_entry); + list_for_each_entry(cancel_work, command_list, request_entry) { chdr = cancel_work->request_buf; if (chdr->MessageId != hdr->MessageId || diff --git a/fs/cifsd/smb_common.c b/fs/cifsd/smb_common.c index 039030968b50..d74b2ce08187 100644 --- a/fs/cifsd/smb_common.c +++ b/fs/cifsd/smb_common.c @@ -481,15 +481,13 @@ int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) { int rc = 0; struct ksmbd_file *prev_fp; - struct list_head *cur; /* * Lookup fp in master fp list, and check desired access and * shared mode between previous open and current open. */ read_lock(&curr_fp->f_ci->m_lock); - list_for_each(cur, &curr_fp->f_ci->m_fp_list) { - prev_fp = list_entry(cur, struct ksmbd_file, node); + list_for_each_entry(prev_fp, &curr_fp->f_ci->m_fp_list, node) { if (file_inode(filp) != FP_INODE(prev_fp)) continue; diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index dcac1f0a29e4..3f18018668b6 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -472,15 +472,13 @@ struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) { struct ksmbd_file *lfp; struct ksmbd_inode *ci; - struct list_head *cur; ci = ksmbd_inode_lookup_by_vfsinode(inode); if (!ci) return NULL; read_lock(&ci->m_lock); - list_for_each(cur, &ci->m_fp_list) { - lfp = list_entry(cur, struct ksmbd_file, node); + list_for_each_entry(lfp, &ci->m_fp_list, node) { if (inode == FP_INODE(lfp)) { atomic_dec(&ci->m_count); read_unlock(&ci->m_lock); From 1dfb8242e8d982d036399766c4af62ddc221e38d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 18 Jun 2021 10:29:56 +0900 Subject: [PATCH 132/417] ksmbd: use goto instead of duplicating the resoure cleanup in ksmbd_open_fd Use goto instead of duplicating the resoure cleanup in ksmbd_open_fd. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs_cache.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index 3f18018668b6..71a11128d908 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -559,19 +559,22 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) fp->f_ci = ksmbd_inode_get(fp); if (!fp->f_ci) { - kmem_cache_free(filp_cache, fp); - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto err_out; } ret = __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); if (ret) { ksmbd_inode_put(fp->f_ci); - kmem_cache_free(filp_cache, fp); - return ERR_PTR(ret); + goto err_out; } atomic_inc(&work->conn->stats.open_files_count); return fp; + +err_out: + kmem_cache_free(filp_cache, fp); + return ERR_PTR(ret); } static int From 79a8a71db4084d7536fc45ed2a33ce7b451ba127 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 18 Jun 2021 10:30:46 +0900 Subject: [PATCH 133/417] ksmbd: fix overly long line Fix overly long line. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs_cache.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index 71a11128d908..4cf14c247e9e 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -601,12 +601,14 @@ __close_file_table_ids(struct ksmbd_file_table *ft, return num; } -static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) +static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) { return fp->tcon != tcon; } -static bool session_fd_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) +static bool session_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) { return false; } From 9c78ad067faf605e0cd16d557859310e5f5312be Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 18 Jun 2021 10:40:56 +0900 Subject: [PATCH 134/417] ksmbd: remove unneeded FIXME comment Remove unneeded FIXME comment. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/glob.h | 2 -- fs/cifsd/ksmbd_work.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/fs/cifsd/glob.h b/fs/cifsd/glob.h index ffeaf8aa5595..da8f804a3ee2 100644 --- a/fs/cifsd/glob.h +++ b/fs/cifsd/glob.h @@ -14,8 +14,6 @@ #define KSMBD_VERSION "3.1.9" -/* @FIXME clean up this code */ - extern int ksmbd_debug_types; #define DATA_STREAM 1 diff --git a/fs/cifsd/ksmbd_work.c b/fs/cifsd/ksmbd_work.c index a88c25965012..7c914451bbe1 100644 --- a/fs/cifsd/ksmbd_work.c +++ b/fs/cifsd/ksmbd_work.c @@ -12,8 +12,6 @@ #include "connection.h" #include "ksmbd_work.h" #include "mgmt/ksmbd_ida.h" - -/* @FIXME */ #include "ksmbd_server.h" static struct kmem_cache *work_cache; From 9f88af04f03d585b8257740745d19897b48a9795 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 18 Jun 2021 10:41:42 +0900 Subject: [PATCH 135/417] ksmbd: remove ____ksmbd_align in ksmbd_server.h None of structures needs the attribute. So remove ____ksmbd_align in ksmbd_server.h. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/ksmbd_server.h | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/fs/cifsd/ksmbd_server.h b/fs/cifsd/ksmbd_server.h index c2467a709144..a915ca5596dc 100644 --- a/fs/cifsd/ksmbd_server.h +++ b/fs/cifsd/ksmbd_server.h @@ -13,10 +13,6 @@ #define KSMBD_GENL_NAME "SMBD_GENL" #define KSMBD_GENL_VERSION 0x01 -#ifndef ____ksmbd_align -#define ____ksmbd_align __aligned(4) -#endif - #define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ 48 #define KSMBD_REQ_MAX_HASH_SZ 18 #define KSMBD_REQ_MAX_SHARE_NAME 64 @@ -51,19 +47,19 @@ struct ksmbd_startup_request { __u32 share_fake_fscaps; __u32 sub_auth[3]; __u32 ifc_list_sz; - __s8 ____payload[0]; -} ____ksmbd_align; + __s8 ____payload[]; +}; #define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) struct ksmbd_shutdown_request { __s32 reserved; -} ____ksmbd_align; +}; struct ksmbd_login_request { __u32 handle; __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; -} ____ksmbd_align; +}; struct ksmbd_login_response { __u32 handle; @@ -73,12 +69,12 @@ struct ksmbd_login_response { __u16 status; __u16 hash_sz; __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; -} ____ksmbd_align; +}; struct ksmbd_share_config_request { __u32 handle; __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; -} ____ksmbd_align; +}; struct ksmbd_share_config_response { __u32 handle; @@ -90,8 +86,8 @@ struct ksmbd_share_config_response { __u16 force_uid; __u16 force_gid; __u32 veto_list_sz; - __s8 ____payload[0]; -} ____ksmbd_align; + __s8 ____payload[]; +}; #define KSMBD_SHARE_CONFIG_VETO_LIST(s) ((s)->____payload) #define KSMBD_SHARE_CONFIG_PATH(s) \ @@ -111,43 +107,43 @@ struct ksmbd_tree_connect_request { __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; __s8 share[KSMBD_REQ_MAX_SHARE_NAME]; __s8 peer_addr[64]; -} ____ksmbd_align; +}; struct ksmbd_tree_connect_response { __u32 handle; __u16 status; __u16 connection_flags; -} ____ksmbd_align; +}; struct ksmbd_tree_disconnect_request { __u64 session_id; __u64 connect_id; -} ____ksmbd_align; +}; struct ksmbd_logout_request { __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; -} ____ksmbd_align; +}; struct ksmbd_rpc_command { __u32 handle; __u32 flags; __u32 payload_sz; - __u8 payload[0]; -} ____ksmbd_align; + __u8 payload[]; +}; struct ksmbd_spnego_authen_request { __u32 handle; __u16 spnego_blob_len; __u8 spnego_blob[0]; -} ____ksmbd_align; +}; struct ksmbd_spnego_authen_response { __u32 handle; struct ksmbd_login_response login_response; __u16 session_key_len; __u16 spnego_blob_len; - __u8 payload[0]; /* session key + AP_REP */ -} ____ksmbd_align; + __u8 payload[]; /* session key + AP_REP */ +}; /* * This also used as NETLINK attribute type value. From 3fbe43c9f577cadd6b5136fda2e6a6c0b4e0651e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 18 Jun 2021 10:42:32 +0900 Subject: [PATCH 136/417] ksmbd: replace KSMBD_SHARE_CONFIG_PATH with inline function replace KSMBD_SHARE_CONFIG_PATH with inline function. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/ksmbd_server.h | 18 +++++++++++------- fs/cifsd/mgmt/share_config.c | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/fs/cifsd/ksmbd_server.h b/fs/cifsd/ksmbd_server.h index a915ca5596dc..55b7602b79bd 100644 --- a/fs/cifsd/ksmbd_server.h +++ b/fs/cifsd/ksmbd_server.h @@ -90,13 +90,17 @@ struct ksmbd_share_config_response { }; #define KSMBD_SHARE_CONFIG_VETO_LIST(s) ((s)->____payload) -#define KSMBD_SHARE_CONFIG_PATH(s) \ - ({ \ - char *p = (s)->____payload; \ - if ((s)->veto_list_sz) \ - p += (s)->veto_list_sz + 1; \ - p; \ - }) + +static inline char * +ksmbd_share_config_path(struct ksmbd_share_config_response *sc) +{ + char *p = sc->____payload; + + if (sc->veto_list_sz) + p += sc->veto_list_sz + 1; + + return p; +} struct ksmbd_tree_connect_request { __u32 handle; diff --git a/fs/cifsd/mgmt/share_config.c b/fs/cifsd/mgmt/share_config.c index fac6034b97a9..cb72d30f5b71 100644 --- a/fs/cifsd/mgmt/share_config.c +++ b/fs/cifsd/mgmt/share_config.c @@ -139,7 +139,7 @@ static struct ksmbd_share_config *share_config_request(char *name) share->name = kstrdup(name, GFP_KERNEL); if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { - share->path = kstrdup(KSMBD_SHARE_CONFIG_PATH(resp), + share->path = kstrdup(ksmbd_share_config_path(resp), GFP_KERNEL); if (share->path) share->path_sz = strlen(share->path); From bde1694aecdb535970787b4f1d07ddb317e191e3 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 28 Jun 2021 15:23:19 +0900 Subject: [PATCH 137/417] ksmbd: remove ksmbd_err/info Use the pr_fmt built into pr_*. and use pr_err/info after removing wrapper ksmbd_err/info. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/auth.c | 18 ++-- fs/cifsd/connection.c | 15 ++- fs/cifsd/crypto_ctx.c | 4 +- fs/cifsd/glob.h | 26 ++--- fs/cifsd/mgmt/user_session.c | 4 +- fs/cifsd/misc.c | 2 +- fs/cifsd/ndr.c | 14 +-- fs/cifsd/oplock.c | 20 ++-- fs/cifsd/server.c | 6 +- fs/cifsd/smb2misc.c | 8 +- fs/cifsd/smb2pdu.c | 204 +++++++++++++++++------------------ fs/cifsd/smb_common.c | 2 +- fs/cifsd/smbacl.c | 26 ++--- fs/cifsd/transport_ipc.c | 30 +++--- fs/cifsd/transport_rdma.c | 106 +++++++++--------- fs/cifsd/transport_tcp.c | 20 ++-- fs/cifsd/vfs.c | 88 +++++++-------- fs/cifsd/vfs_cache.c | 10 +- 18 files changed, 295 insertions(+), 308 deletions(-) diff --git a/fs/cifsd/auth.c b/fs/cifsd/auth.c index daf31c9f0880..de36f12070bf 100644 --- a/fs/cifsd/auth.c +++ b/fs/cifsd/auth.c @@ -342,7 +342,7 @@ int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf) memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE); rc = ksmbd_enc_p24(p21, sess->ntlmssp.cryptkey, key); if (rc) { - ksmbd_err("password processing failed\n"); + pr_err("password processing failed\n"); return rc; } @@ -461,7 +461,7 @@ static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess, char *client_nonce, client_nonce, (char *)sess->ntlmssp.cryptkey, 8); if (rc) { - ksmbd_err("password processing failed\n"); + pr_err("password processing failed\n"); goto out; } @@ -469,7 +469,7 @@ static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess, char *client_nonce, memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE); rc = ksmbd_enc_p24(p21, sess_key, key); if (rc) { - ksmbd_err("password processing failed\n"); + pr_err("password processing failed\n"); goto out; } @@ -1269,7 +1269,7 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, enc, key); if (rc) { - ksmbd_err("Could not get %scryption key\n", enc ? "en" : "de"); + pr_err("Could not get %scryption key\n", enc ? "en" : "de"); return rc; } @@ -1279,7 +1279,7 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, else ctx = ksmbd_crypto_ctx_find_ccm(); if (!ctx) { - ksmbd_err("crypto alloc failed\n"); + pr_err("crypto alloc failed\n"); return -ENOMEM; } @@ -1295,19 +1295,18 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, else rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); if (rc) { - ksmbd_err("Failed to set aead key %d\n", rc); + pr_err("Failed to set aead key %d\n", rc); goto free_ctx; } rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); if (rc) { - ksmbd_err("Failed to set authsize %d\n", rc); + pr_err("Failed to set authsize %d\n", rc); goto free_ctx; } req = aead_request_alloc(tfm, GFP_KERNEL); if (!req) { - ksmbd_err("Failed to alloc aead request\n"); rc = -ENOMEM; goto free_ctx; } @@ -1319,7 +1318,7 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, sg = ksmbd_init_sg(iov, nvec, sign); if (!sg) { - ksmbd_err("Failed to init sg\n"); + pr_err("Failed to init sg\n"); rc = -ENOMEM; goto free_req; } @@ -1327,7 +1326,6 @@ int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, iv_len = crypto_aead_ivsize(tfm); iv = kzalloc(iv_len, GFP_KERNEL); if (!iv) { - ksmbd_err("Failed to alloc IV\n"); rc = -ENOMEM; goto free_sg; } diff --git a/fs/cifsd/connection.c b/fs/cifsd/connection.c index a0d15093dd6f..928e22e19def 100644 --- a/fs/cifsd/connection.c +++ b/fs/cifsd/connection.c @@ -160,7 +160,7 @@ int ksmbd_conn_write(struct ksmbd_work *work) ksmbd_conn_try_dequeue_request(work); if (!rsp_hdr) { - ksmbd_err("NULL response header\n"); + pr_err("NULL response header\n"); return -EINVAL; } @@ -192,7 +192,7 @@ int ksmbd_conn_write(struct ksmbd_work *work) ksmbd_conn_unlock(conn); if (sent < 0) { - ksmbd_err("Failed to send message: %d\n", sent); + pr_err("Failed to send message: %d\n", sent); return sent; } @@ -315,24 +315,23 @@ int ksmbd_conn_handler_loop(void *p) */ size = t->ops->read(t, conn->request_buf + 4, pdu_size); if (size < 0) { - ksmbd_err("sock_read failed: %d\n", size); + pr_err("sock_read failed: %d\n", size); break; } if (size != pdu_size) { - ksmbd_err("PDU error. Read: %d, Expected: %d\n", - size, - pdu_size); + pr_err("PDU error. Read: %d, Expected: %d\n", + size, pdu_size); continue; } if (!default_conn_ops.process_fn) { - ksmbd_err("No connection request callback\n"); + pr_err("No connection request callback\n"); break; } if (default_conn_ops.process_fn(conn)) { - ksmbd_err("Cannot handle request\n"); + pr_err("Cannot handle request\n"); break; } } diff --git a/fs/cifsd/crypto_ctx.c b/fs/cifsd/crypto_ctx.c index 7b727fe141a6..5f4b1008d17e 100644 --- a/fs/cifsd/crypto_ctx.c +++ b/fs/cifsd/crypto_ctx.c @@ -48,12 +48,12 @@ static struct crypto_aead *alloc_aead(int id) tfm = crypto_alloc_aead("ccm(aes)", 0, 0); break; default: - ksmbd_err("Does not support encrypt ahead(id : %d)\n", id); + pr_err("Does not support encrypt ahead(id : %d)\n", id); return NULL; } if (IS_ERR(tfm)) { - ksmbd_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm)); + pr_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm)); return NULL; } diff --git a/fs/cifsd/glob.h b/fs/cifsd/glob.h index da8f804a3ee2..8119cb7ddbed 100644 --- a/fs/cifsd/glob.h +++ b/fs/cifsd/glob.h @@ -31,32 +31,22 @@ extern int ksmbd_debug_types; KSMBD_DEBUG_IPC | KSMBD_DEBUG_CONN | \ KSMBD_DEBUG_RDMA) -#ifndef ksmbd_pr_fmt -#ifdef SUBMOD_NAME -#define ksmbd_pr_fmt(fmt) "ksmbd: " SUBMOD_NAME ": " fmt -#else -#define ksmbd_pr_fmt(fmt) "ksmbd: " fmt +#ifdef pr_fmt +#undef pr_fmt #endif + +#ifdef SUBMOD_NAME +#define pr_fmt(fmt) "ksmbd: " SUBMOD_NAME ": " fmt +#else +#define pr_fmt(fmt) "ksmbd: " fmt #endif #define ksmbd_debug(type, fmt, ...) \ do { \ if (ksmbd_debug_types & KSMBD_DEBUG_##type) \ - pr_info(ksmbd_pr_fmt("%s:%d: " fmt), \ - __func__, \ - __LINE__, \ - ##__VA_ARGS__); \ + pr_info(fmt, ##__VA_ARGS__); \ } while (0) -#define ksmbd_info(fmt, ...) \ - pr_info(ksmbd_pr_fmt(fmt), ##__VA_ARGS__) - -#define ksmbd_err(fmt, ...) \ - pr_err(ksmbd_pr_fmt("%s:%d: " fmt), \ - __func__, \ - __LINE__, \ - ##__VA_ARGS__) - #define UNICODE_LEN(x) ((x) * 2) #endif /* __KSMBD_GLOB_H */ diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index 77bdf3642f72..615b46f0762b 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -87,7 +87,7 @@ static int __rpc_method(char *rpc_name) if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc")) return KSMBD_RPC_LSARPC_METHOD_INVOKE; - ksmbd_err("Unsupported RPC: %s\n", rpc_name); + pr_err("Unsupported RPC: %s\n", rpc_name); return 0; } @@ -232,7 +232,7 @@ int get_session(struct ksmbd_session *sess) void put_session(struct ksmbd_session *sess) { if (atomic_dec_and_test(&sess->refcnt)) - ksmbd_err("get/%s seems to be mismatched.", __func__); + pr_err("get/%s seems to be mismatched.", __func__); } struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) diff --git a/fs/cifsd/misc.c b/fs/cifsd/misc.c index 1c6ed20f4a18..0b307ca28a19 100644 --- a/fs/cifsd/misc.c +++ b/fs/cifsd/misc.c @@ -107,7 +107,7 @@ static int ksmbd_validate_stream_name(char *stream_name) stream_name++; if (c == '/' || c == ':' || c == '\\') { - ksmbd_err("Stream name validation failed: %c\n", c); + pr_err("Stream name validation failed: %c\n", c); return -ENOENT; } } diff --git a/fs/cifsd/ndr.c b/fs/cifsd/ndr.c index 14189832c65e..46cc01475d38 100644 --- a/fs/cifsd/ndr.c +++ b/fs/cifsd/ndr.c @@ -178,14 +178,14 @@ int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) da->version = ndr_read_int16(n); if (da->version != 3 && da->version != 4) { - ksmbd_err("v%d version is not supported\n", da->version); + pr_err("v%d version is not supported\n", da->version); return -EINVAL; } version2 = ndr_read_int32(n); if (da->version != version2) { - ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n", - da->version, version2); + pr_err("ndr version mismatched(version: %d, version2: %d)\n", + da->version, version2); return -EINVAL; } @@ -309,14 +309,14 @@ int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) n->offset = 0; acl->version = ndr_read_int16(n); if (acl->version != 4) { - ksmbd_err("v%d version is not supported\n", acl->version); + pr_err("v%d version is not supported\n", acl->version); return -EINVAL; } version2 = ndr_read_int32(n); if (acl->version != version2) { - ksmbd_err("ndr version mismatched(version: %d, version2: %d)\n", - acl->version, version2); + pr_err("ndr version mismatched(version: %d, version2: %d)\n", + acl->version, version2); return -EINVAL; } @@ -329,7 +329,7 @@ int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) ndr_read_bytes(n, acl->desc, 10); if (strncmp(acl->desc, "posix_acl", 9)) { - ksmbd_err("Invalid acl description : %s\n", acl->desc); + pr_err("Invalid acl description : %s\n", acl->desc); return -EINVAL; } diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index 1ef2acbea2bb..9027cb7d970f 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -230,9 +230,9 @@ int opinfo_write_to_read(struct oplock_info *opinfo) if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { - ksmbd_err("bad oplock(0x%x)\n", opinfo->level); + pr_err("bad oplock(0x%x)\n", opinfo->level); if (opinfo->is_lease) - ksmbd_err("lease state(0x%x)\n", lease->state); + pr_err("lease state(0x%x)\n", lease->state); return -EINVAL; } opinfo->level = SMB2_OPLOCK_LEVEL_II; @@ -269,9 +269,9 @@ int opinfo_write_to_none(struct oplock_info *opinfo) if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { - ksmbd_err("bad oplock(0x%x)\n", opinfo->level); + pr_err("bad oplock(0x%x)\n", opinfo->level); if (opinfo->is_lease) - ksmbd_err("lease state(0x%x)\n", lease->state); + pr_err("lease state(0x%x)\n", lease->state); return -EINVAL; } opinfo->level = SMB2_OPLOCK_LEVEL_NONE; @@ -291,9 +291,9 @@ int opinfo_read_to_none(struct oplock_info *opinfo) struct lease *lease = opinfo->o_lease; if (opinfo->level != SMB2_OPLOCK_LEVEL_II) { - ksmbd_err("bad oplock(0x%x)\n", opinfo->level); + pr_err("bad oplock(0x%x)\n", opinfo->level); if (opinfo->is_lease) - ksmbd_err("lease state(0x%x)\n", lease->state); + pr_err("lease state(0x%x)\n", lease->state); return -EINVAL; } opinfo->level = SMB2_OPLOCK_LEVEL_NONE; @@ -622,7 +622,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) } if (allocate_oplock_break_buf(work)) { - ksmbd_err("smb2_allocate_rsp_buf failed! "); + pr_err("smb2_allocate_rsp_buf failed! "); atomic_dec(&conn->r_count); ksmbd_fd_put(work, fp); ksmbd_free_work_struct(work); @@ -1680,18 +1680,18 @@ int smb2_check_durable_oplock(struct ksmbd_file *fp, if (opinfo && opinfo->is_lease) { if (!lctx) { - ksmbd_err("open does not include lease\n"); + pr_err("open does not include lease\n"); ret = -EBADF; goto out; } if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE)) { - ksmbd_err("invalid lease key\n"); + pr_err("invalid lease key\n"); ret = -EBADF; goto out; } if (name && strcmp(fp->filename, name)) { - ksmbd_err("invalid name reconnect %s\n", name); + pr_err("invalid name reconnect %s\n", name); ret = -EINVAL; goto out; } diff --git a/fs/cifsd/server.c b/fs/cifsd/server.c index 6d14daae1b07..a8c59e96a2f7 100644 --- a/fs/cifsd/server.c +++ b/fs/cifsd/server.c @@ -277,7 +277,7 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn) work = ksmbd_alloc_work_struct(); if (!work) { - ksmbd_err("allocation for work failed\n"); + pr_err("allocation for work failed\n"); return -ENOMEM; } @@ -442,7 +442,7 @@ static ssize_t kill_server_store(struct class *class, if (!sysfs_streq(buf, "hard")) return len; - ksmbd_info("kill command received\n"); + pr_info("kill command received\n"); mutex_lock(&ctrl_lock); WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); __module_get(THIS_MODULE); @@ -547,7 +547,7 @@ static int __init ksmbd_server_init(void) ret = class_register(&ksmbd_control_class); if (ret) { - ksmbd_err("Unable to register ksmbd-control class\n"); + pr_err("Unable to register ksmbd-control class\n"); return ret; } diff --git a/fs/cifsd/smb2misc.c b/fs/cifsd/smb2misc.c index c4b870dbf683..e412d69690ed 100644 --- a/fs/cifsd/smb2misc.c +++ b/fs/cifsd/smb2misc.c @@ -320,12 +320,12 @@ static int smb2_validate_credit_charge(struct smb2_hdr *hdr) max_len = max(req_len, expect_resp_len); calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); if (!credit_charge && max_len > SMB2_MAX_BUFFER_SIZE) { - ksmbd_err("credit charge is zero and payload size(%d) is bigger than 64K\n", - max_len); + pr_err("credit charge is zero and payload size(%d) is bigger than 64K\n", + max_len); return 1; } else if (credit_charge < calc_credit_num) { - ksmbd_err("credit charge : %d, calc_credit_num : %d\n", - credit_charge, calc_credit_num); + pr_err("credit charge : %d, calc_credit_num : %d\n", + credit_charge, calc_credit_num); return 1; } diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index f1642fffe4e1..84f4cd7f545f 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -66,7 +66,7 @@ static inline int check_session_id(struct ksmbd_conn *conn, u64 id) sess = ksmbd_session_lookup_all(conn, id); if (sess) return 1; - ksmbd_err("Invalid user session id: %llu\n", id); + pr_err("Invalid user session id: %llu\n", id); return 0; } @@ -109,7 +109,7 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work) tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId); work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); if (!work->tcon) { - ksmbd_err("Invalid tid %d\n", tree_id); + pr_err("Invalid tid %d\n", tree_id); return -1; } @@ -329,7 +329,7 @@ int smb2_set_rsp_credits(struct ksmbd_work *work) min_credits = conn->max_credits >> 4; if (conn->total_credits >= conn->max_credits) { - ksmbd_err("Total credits overflow: %d\n", conn->total_credits); + pr_err("Total credits overflow: %d\n", conn->total_credits); conn->total_credits = min_credits; } @@ -634,7 +634,7 @@ smb2_get_name(struct ksmbd_share_config *share, const char *src, name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); if (IS_ERR(name)) { - ksmbd_err("failed to get name %ld\n", PTR_ERR(name)); + pr_err("failed to get name %ld\n", PTR_ERR(name)); return name; } @@ -645,7 +645,7 @@ smb2_get_name(struct ksmbd_share_config *share, const char *src, unixname = convert_to_unix_name(share, name); kfree(name); if (!unixname) { - ksmbd_err("can not convert absolute name\n"); + pr_err("can not convert absolute name\n"); return ERR_PTR(-ENOMEM); } @@ -664,7 +664,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) id = ksmbd_acquire_async_msg_id(&conn->async_ida); if (id < 0) { - ksmbd_err("Failed to alloc async message id\n"); + pr_err("Failed to alloc async message id\n"); return id; } work->syncronous = false; @@ -1005,13 +1005,13 @@ int smb2_handle_negotiate(struct ksmbd_work *work) ksmbd_debug(SMB, "Received negotiate request\n"); conn->need_neg = false; if (ksmbd_conn_good(work)) { - ksmbd_err("conn->tcp_status is already in CifsGood State\n"); + pr_err("conn->tcp_status is already in CifsGood State\n"); work->send_no_response = 1; return rc; } if (req->DialectCount == 0) { - ksmbd_err("malformed packet\n"); + pr_err("malformed packet\n"); rsp->hdr.Status = STATUS_INVALID_PARAMETER; rc = -EINVAL; goto err_out; @@ -1031,8 +1031,8 @@ int smb2_handle_negotiate(struct ksmbd_work *work) status = deassemble_neg_contexts(conn, req); if (status != STATUS_SUCCESS) { - ksmbd_err("deassemble_neg_contexts error(0x%x)\n", - status); + pr_err("deassemble_neg_contexts error(0x%x)\n", + status); rsp->hdr.Status = status; rc = -EINVAL; goto err_out; @@ -1293,7 +1293,7 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn, true, conn->local_nls); if (IS_ERR(name)) { - ksmbd_err("cannot allocate memory\n"); + pr_err("cannot allocate memory\n"); return NULL; } @@ -1438,7 +1438,7 @@ binding_session: if (conn->dialect > SMB20_PROT_ID) { if (!ksmbd_conn_lookup_dialect(conn)) { - ksmbd_err("fail to verify the dialect\n"); + pr_err("fail to verify the dialect\n"); rsp->hdr.Status = STATUS_USER_SESSION_DELETED; return -EPERM; } @@ -1528,7 +1528,7 @@ static int krb5_authenticate(struct ksmbd_work *work) if (conn->dialect > SMB20_PROT_ID) { if (!ksmbd_conn_lookup_dialect(conn)) { - ksmbd_err("fail to verify the dialect\n"); + pr_err("fail to verify the dialect\n"); rsp->hdr.Status = STATUS_USER_SESSION_DELETED; return -EPERM; } @@ -1688,11 +1688,11 @@ int smb2_sess_setup(struct ksmbd_work *work) } } else { /* TODO: need one more negotiation */ - ksmbd_err("Not support the preferred authentication\n"); + pr_err("Not support the preferred authentication\n"); rc = -EINVAL; } } else { - ksmbd_err("Not support authentication\n"); + pr_err("Not support authentication\n"); rc = -EINVAL; } @@ -1742,7 +1742,7 @@ int smb2_tree_connect(struct ksmbd_work *work) le16_to_cpu(req->PathLength), true, conn->local_nls); if (IS_ERR(treename)) { - ksmbd_err("treename is NULL\n"); + pr_err("treename is NULL\n"); status.ret = KSMBD_TREE_CONN_STATUS_ERROR; goto out_err1; } @@ -1986,7 +1986,7 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) id = ksmbd_session_rpc_open(work->sess, name); if (id < 0) { - ksmbd_err("Unable to open RPC pipe: %d\n", id); + pr_err("Unable to open RPC pipe: %d\n", id); err = id; goto out; } @@ -2120,7 +2120,7 @@ static inline int check_context_err(void *ctx, char *str) ksmbd_debug(SMB, "find context %s err %d\n", str, err); if (err == -EINVAL) { - ksmbd_err("bad name length\n"); + pr_err("bad name length\n"); return err; } @@ -2159,7 +2159,7 @@ static noinline int smb2_set_stream_name_xattr(struct path *path, rc = ksmbd_vfs_setxattr(path->dentry, xattr_stream_name, NULL, 0, 0); if (rc < 0) - ksmbd_err("Failed to store XATTR stream name :%d\n", rc); + pr_err("Failed to store XATTR stream name :%d\n", rc); return 0; } @@ -2201,7 +2201,7 @@ static int smb2_create_truncate(struct path *path) int rc = vfs_truncate(path, 0); if (rc) { - ksmbd_err("vfs_truncate failed, rc %d\n", rc); + pr_err("vfs_truncate failed, rc %d\n", rc); return rc; } @@ -2287,8 +2287,8 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, rc = ksmbd_vfs_kern_path(name, 0, path, 0); if (rc) { - ksmbd_err("cannot get linux path (%s), err = %d\n", - name, rc); + pr_err("cannot get linux path (%s), err = %d\n", + name, rc); return rc; } return 0; @@ -2387,7 +2387,7 @@ int smb2_open(struct ksmbd_work *work) if (req->NameLength) { if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && *(char *)req->Buffer == '\\') { - ksmbd_err("not allow directory name included leading slash\n"); + pr_err("not allow directory name included leading slash\n"); rc = -EINVAL; goto err_out1; } @@ -2444,16 +2444,16 @@ int smb2_open(struct ksmbd_work *work) lc = parse_lease_state(req); if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE_LE)) { - ksmbd_err("Invalid impersonationlevel : 0x%x\n", - le32_to_cpu(req->ImpersonationLevel)); + pr_err("Invalid impersonationlevel : 0x%x\n", + le32_to_cpu(req->ImpersonationLevel)); rc = -EIO; rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; goto err_out1; } if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK)) { - ksmbd_err("Invalid create options : 0x%x\n", - le32_to_cpu(req->CreateOptions)); + pr_err("Invalid create options : 0x%x\n", + le32_to_cpu(req->CreateOptions)); rc = -EINVAL; goto err_out1; } else { @@ -2480,22 +2480,22 @@ int smb2_open(struct ksmbd_work *work) if (le32_to_cpu(req->CreateDisposition) > le32_to_cpu(FILE_OVERWRITE_IF_LE)) { - ksmbd_err("Invalid create disposition : 0x%x\n", - le32_to_cpu(req->CreateDisposition)); + pr_err("Invalid create disposition : 0x%x\n", + le32_to_cpu(req->CreateDisposition)); rc = -EINVAL; goto err_out1; } if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { - ksmbd_err("Invalid desired access : 0x%x\n", - le32_to_cpu(req->DesiredAccess)); + pr_err("Invalid desired access : 0x%x\n", + le32_to_cpu(req->DesiredAccess)); rc = -EACCES; goto err_out1; } if (req->FileAttributes && !(req->FileAttributes & ATTR_MASK_LE)) { - ksmbd_err("Invalid file attribute : 0x%x\n", - le32_to_cpu(req->FileAttributes)); + pr_err("Invalid file attribute : 0x%x\n", + le32_to_cpu(req->FileAttributes)); rc = -EINVAL; goto err_out1; } @@ -2750,7 +2750,7 @@ int smb2_open(struct ksmbd_work *work) filp = dentry_open(&path, open_flags, current_cred()); if (IS_ERR(filp)) { rc = PTR_ERR(filp); - ksmbd_err("dentry open for dir failed, rc %d\n", rc); + pr_err("dentry open for dir failed, rc %d\n", rc); goto err_out; } @@ -2846,8 +2846,8 @@ int smb2_open(struct ksmbd_work *work) pntsd_size); kfree(pntsd); if (rc) - ksmbd_err("failed to store ntacl in xattr : %d\n", - rc); + pr_err("failed to store ntacl in xattr : %d\n", + rc); } } } @@ -3697,14 +3697,14 @@ int smb2_query_dir(struct ksmbd_work *work) if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || inode_permission(&init_user_ns, file_inode(dir_fp->filp), MAY_READ | MAY_EXEC)) { - ksmbd_err("no right to enumerate directory (%s)\n", - FP_FILENAME(dir_fp)); + pr_err("no right to enumerate directory (%s)\n", + FP_FILENAME(dir_fp)); rc = -EACCES; goto err_out2; } if (!S_ISDIR(file_inode(dir_fp->filp)->i_mode)) { - ksmbd_err("can't do query dir for a file\n"); + pr_err("can't do query dir for a file\n"); rc = -EINVAL; goto err_out2; } @@ -3805,7 +3805,7 @@ int smb2_query_dir(struct ksmbd_work *work) return 0; err_out: - ksmbd_err("error while processing smb2 query dir rc = %d\n", rc); + pr_err("error while processing smb2 query dir rc = %d\n", rc); kfree(srch_ptr); err_out2: @@ -3843,7 +3843,7 @@ static int buffer_check_err(int reqOutputBufferLength, { if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { if (reqOutputBufferLength < infoclass_size) { - ksmbd_err("Invalid Buffer Size Requested\n"); + pr_err("Invalid Buffer Size Requested\n"); rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; rsp->hdr.smb2_buf_length = cpu_to_be32(sizeof(struct smb2_hdr) - 4); return -EINVAL; @@ -3946,8 +3946,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, struct path *path; if (!(fp->daccess & FILE_READ_EA_LE)) { - ksmbd_err("Not permitted to read ext attr : 0x%x\n", - fp->daccess); + pr_err("Not permitted to read ext attr : 0x%x\n", + fp->daccess); return -EACCES; } @@ -4103,8 +4103,8 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, u64 time; if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { - ksmbd_err("no right to read the attributes : 0x%x\n", - fp->daccess); + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); return -EACCES; } @@ -4375,8 +4375,8 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, u64 time; if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { - ksmbd_err("no right to read the attributes : 0x%x\n", - fp->daccess); + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); return -EACCES; } @@ -4465,8 +4465,8 @@ static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, struct smb2_file_attr_tag_info *file_info; if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { - ksmbd_err("no right to read the attributes : 0x%x\n", - fp->daccess); + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); return -EACCES; } @@ -4620,7 +4620,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, break; case SMB_FIND_FILE_POSIX_INFO: if (!work->tcon->posix_extensions) { - ksmbd_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); rc = -EOPNOTSUPP; } else { rc = find_file_posix_info(rsp, fp, rsp_org); @@ -4659,13 +4659,13 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, rc = ksmbd_vfs_kern_path(share->path, lookup_flags, &path, 0); if (rc) { - ksmbd_err("cannot create vfs path\n"); + pr_err("cannot create vfs path\n"); return -EIO; } rc = vfs_statfs(&path, &stfs); if (rc) { - ksmbd_err("cannot do stat of path %s\n", share->path); + pr_err("cannot do stat of path %s\n", share->path); path_put(&path); return -EIO; } @@ -4846,7 +4846,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, unsigned short logical_sector_size; if (!work->tcon->posix_extensions) { - ksmbd_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); rc = -EOPNOTSUPP; } else { info = (struct filesystem_posix_info *)(rsp->Buffer); @@ -5213,7 +5213,7 @@ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, len = strlen(new_name); if (new_name[len - 1] != '/') { - ksmbd_err("not allow base filename in rename\n"); + pr_err("not allow base filename in rename\n"); rc = -ESHARE; goto out; } @@ -5229,8 +5229,8 @@ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, xattr_stream_name, NULL, 0, 0); if (rc < 0) { - ksmbd_err("failed to store stream name in xattr: %d\n", - rc); + pr_err("failed to store stream name in xattr: %d\n", + rc); rc = -EINVAL; goto out; } @@ -5389,7 +5389,7 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, if (file_info->Attributes) { if (!S_ISDIR(inode->i_mode) && file_info->Attributes & ATTR_DIRECTORY_LE) { - ksmbd_err("can't change a file to a directory\n"); + pr_err("can't change a file to a directory\n"); return -EINVAL; } @@ -5467,7 +5467,7 @@ static int set_file_allocation_info(struct ksmbd_work *work, if (alloc_blks > inode->i_blocks) { rc = ksmbd_vfs_alloc_size(work, fp, alloc_blks * 512); if (rc && rc != -EOPNOTSUPP) { - ksmbd_err("ksmbd_vfs_alloc_size is failed : %d\n", rc); + pr_err("ksmbd_vfs_alloc_size is failed : %d\n", rc); return rc; } } else if (alloc_blks < inode->i_blocks) { @@ -5483,8 +5483,8 @@ static int set_file_allocation_info(struct ksmbd_work *work, size = i_size_read(inode); rc = ksmbd_vfs_truncate(work, NULL, fp, alloc_blks * 512); if (rc) { - ksmbd_err("truncate failed! filename : %s, err %d\n", - fp->filename, rc); + pr_err("truncate failed! filename : %s, err %d\n", + fp->filename, rc); return rc; } if (size < alloc_blks * 512) @@ -5536,7 +5536,7 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, struct ksmbd_file *parent_fp; if (!(fp->daccess & FILE_DELETE_LE)) { - ksmbd_err("no right to delete : 0x%x\n", fp->daccess); + pr_err("no right to delete : 0x%x\n", fp->daccess); return -EACCES; } @@ -5546,7 +5546,7 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, parent_fp = ksmbd_lookup_fd_inode(PARENT_INODE(fp)); if (parent_fp) { if (parent_fp->daccess & FILE_DELETE_LE) { - ksmbd_err("parent dir is opened with delete access\n"); + pr_err("parent dir is opened with delete access\n"); return -ESHARE; } } @@ -5562,7 +5562,7 @@ static int set_file_disposition_info(struct ksmbd_file *fp, char *buf) struct inode *inode; if (!(fp->daccess & FILE_DELETE_LE)) { - ksmbd_err("no right to delete : 0x%x\n", fp->daccess); + pr_err("no right to delete : 0x%x\n", fp->daccess); return -EACCES; } @@ -5594,8 +5594,8 @@ static int set_file_position_info(struct ksmbd_file *fp, char *buf) if (current_byte_offset < 0 || (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && current_byte_offset & (sector_size - 1))) { - ksmbd_err("CurrentByteOffset is not valid : %llu\n", - current_byte_offset); + pr_err("CurrentByteOffset is not valid : %llu\n", + current_byte_offset); return -EINVAL; } @@ -5614,7 +5614,7 @@ static int set_file_mode_info(struct ksmbd_file *fp, char *buf) if ((mode & ~FILE_MODE_INFO_MASK) || (mode & FILE_SYNCHRONOUS_IO_ALERT_LE && mode & FILE_SYNCHRONOUS_IO_NONALERT_LE)) { - ksmbd_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); + pr_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); return -EINVAL; } @@ -5675,8 +5675,8 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, case FILE_FULL_EA_INFORMATION: { if (!(fp->daccess & FILE_WRITE_EA_LE)) { - ksmbd_err("Not permitted to write ext attr: 0x%x\n", - fp->daccess); + pr_err("Not permitted to write ext attr: 0x%x\n", + fp->daccess); return -EACCES; } @@ -5691,7 +5691,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, return set_file_mode_info(fp, buf); } - ksmbd_err("Unimplemented Fileinfoclass :%d\n", info_class); + pr_err("Unimplemented Fileinfoclass :%d\n", info_class); return -EOPNOTSUPP; } @@ -5919,7 +5919,7 @@ int smb2_read(struct ksmbd_work *work) } if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { - ksmbd_err("Not permitted to read : 0x%x\n", fp->daccess); + pr_err("Not permitted to read : 0x%x\n", fp->daccess); err = -EACCES; goto out; } @@ -6039,9 +6039,9 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) } else { if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || (le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req))) { - ksmbd_err("invalid write data offset %u, smb_len %u\n", - le16_to_cpu(req->DataOffset), - get_rfc1002_len(req)); + pr_err("invalid write data offset %u, smb_len %u\n", + le16_to_cpu(req->DataOffset), + get_rfc1002_len(req)); err = -EINVAL; goto out; } @@ -6172,7 +6172,7 @@ int smb2_write(struct ksmbd_work *work) } if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { - ksmbd_err("Not permitted to write : 0x%x\n", fp->daccess); + pr_err("Not permitted to write : 0x%x\n", fp->daccess); err = -EACCES; goto out; } @@ -6198,9 +6198,9 @@ int smb2_write(struct ksmbd_work *work) } else { if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || (le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req))) { - ksmbd_err("invalid write data offset %u, smb_len %u\n", - le16_to_cpu(req->DataOffset), - get_rfc1002_len(req)); + pr_err("invalid write data offset %u, smb_len %u\n", + le16_to_cpu(req->DataOffset), + get_rfc1002_len(req)); err = -EINVAL; goto out; } @@ -6530,7 +6530,7 @@ int smb2_lock(struct ksmbd_work *work) lock_start = le64_to_cpu(lock_ele[i].Offset); lock_length = le64_to_cpu(lock_ele[i].Length); if (lock_start > U64_MAX - lock_length) { - ksmbd_err("Invalid lock range requested\n"); + pr_err("Invalid lock range requested\n"); rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; goto out; } @@ -6560,7 +6560,7 @@ int smb2_lock(struct ksmbd_work *work) cmp_lock->fl->fl_end >= flock->fl_end) { if (cmp_lock->fl->fl_type != F_UNLCK && flock->fl_type != F_UNLCK) { - ksmbd_err("conflict two locks in one request\n"); + pr_err("conflict two locks in one request\n"); rsp->hdr.Status = STATUS_INVALID_PARAMETER; goto out; @@ -6633,7 +6633,7 @@ int smb2_lock(struct ksmbd_work *work) if (cmp_lock->zero_len && !smb_lock->zero_len && cmp_lock->start > smb_lock->start && cmp_lock->start < smb_lock->end) { - ksmbd_err("previous lock conflict with zero byte lock range\n"); + pr_err("previous lock conflict with zero byte lock range\n"); rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; goto out; } @@ -6641,7 +6641,7 @@ int smb2_lock(struct ksmbd_work *work) if (smb_lock->zero_len && !cmp_lock->zero_len && smb_lock->start > cmp_lock->start && smb_lock->start < cmp_lock->end) { - ksmbd_err("current lock conflict with zero byte lock range\n"); + pr_err("current lock conflict with zero byte lock range\n"); rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; goto out; } @@ -6650,7 +6650,7 @@ int smb2_lock(struct ksmbd_work *work) cmp_lock->end > smb_lock->start) || (cmp_lock->start < smb_lock->end && cmp_lock->end >= smb_lock->end)) && !cmp_lock->zero_len && !smb_lock->zero_len) { - ksmbd_err("Not allow lock operation on exclusive lock range\n"); + pr_err("Not allow lock operation on exclusive lock range\n"); rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; goto out; @@ -6658,7 +6658,7 @@ int smb2_lock(struct ksmbd_work *work) } if (smb_lock->fl->fl_type == F_UNLCK && nolock) { - ksmbd_err("Try to unlock nolocked range\n"); + pr_err("Try to unlock nolocked range\n"); rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; goto out; } @@ -6787,7 +6787,7 @@ out: err = ksmbd_vfs_lock(filp, 0, rlock); if (err) - ksmbd_err("rollback unlock fail : %d\n", err); + pr_err("rollback unlock fail : %d\n", err); list_del(&smb_lock->llist); list_del(&smb_lock->glist); locks_free_lock(smb_lock->fl); @@ -6974,8 +6974,8 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, netdev->ethtool_ops->get_link_ksettings(netdev, &cmd); speed = cmd.base.speed; } else { - ksmbd_err("%s %s\n", netdev->name, - "speed is unknown, defaulting to 1Gb/sec"); + pr_err("%s %s\n", netdev->name, + "speed is unknown, defaulting to 1Gb/sec"); speed = SPEED_1000; } @@ -7387,7 +7387,7 @@ int smb2_ioctl(struct ksmbd_work *work) reparse_ptr = (struct reparse_data_buffer *)&rsp->Buffer[0]; fp = ksmbd_lookup_fd_fast(work, id); if (!fp) { - ksmbd_err("not found fp!!\n"); + pr_err("not found fp!!\n"); ret = -ENOENT; goto out; } @@ -7410,14 +7410,14 @@ int smb2_ioctl(struct ksmbd_work *work) fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, dup_ext->PersistentFileHandle); if (!fp_in) { - ksmbd_err("not found file handle in duplicate extent to file\n"); + pr_err("not found file handle in duplicate extent to file\n"); ret = -ENOENT; goto out; } fp_out = ksmbd_lookup_fd_fast(work, id); if (!fp_out) { - ksmbd_err("not found fp\n"); + pr_err("not found fp\n"); ret = -ENOENT; goto dup_ext_out; } @@ -7514,7 +7514,7 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) opinfo = opinfo_get(fp); if (!opinfo) { - ksmbd_err("unexpected null oplock_info\n"); + pr_err("unexpected null oplock_info\n"); rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; smb2_set_err_rsp(work); ksmbd_fd_put(work, fp); @@ -7577,8 +7577,8 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; break; default: - ksmbd_err("unknown oplock change 0x%x -> 0x%x\n", - opinfo->level, rsp_oplevel); + pr_err("unknown oplock change 0x%x -> 0x%x\n", + opinfo->level, rsp_oplevel); } if (ret < 0) { @@ -7654,8 +7654,8 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) lease = opinfo->o_lease; if (opinfo->op_state == OPLOCK_STATE_NONE) { - ksmbd_err("unexpected lease break state 0x%x\n", - opinfo->op_state); + pr_err("unexpected lease break state 0x%x\n", + opinfo->op_state); rsp->hdr.Status = STATUS_UNSUCCESSFUL; goto err_out; } @@ -7868,7 +7868,7 @@ int smb2_check_sign_req(struct ksmbd_work *work) return 0; if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { - ksmbd_err("bad smb2 signature\n"); + pr_err("bad smb2 signature\n"); return 0; } @@ -7965,7 +7965,7 @@ int smb3_check_sign_req(struct ksmbd_work *work) } if (!signing_key) { - ksmbd_err("SMB3 signing key is not generated\n"); + pr_err("SMB3 signing key is not generated\n"); return 0; } @@ -7978,7 +7978,7 @@ int smb3_check_sign_req(struct ksmbd_work *work) return 0; if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { - ksmbd_err("bad smb2 signature\n"); + pr_err("bad smb2 signature\n"); return 0; } @@ -8175,20 +8175,20 @@ int smb3_decrypt_req(struct ksmbd_work *work) sess = ksmbd_session_lookup_all(conn, le64_to_cpu(tr_hdr->SessionId)); if (!sess) { - ksmbd_err("invalid session id(%llx) in transform header\n", - le64_to_cpu(tr_hdr->SessionId)); + pr_err("invalid session id(%llx) in transform header\n", + le64_to_cpu(tr_hdr->SessionId)); return -ECONNABORTED; } if (pdu_length + 4 < sizeof(struct smb2_transform_hdr) + sizeof(struct smb2_hdr)) { - ksmbd_err("Transform message is too small (%u)\n", - pdu_length); + pr_err("Transform message is too small (%u)\n", + pdu_length); return -ECONNABORTED; } if (pdu_length + 4 < orig_len + sizeof(struct smb2_transform_hdr)) { - ksmbd_err("Transform message is broken\n"); + pr_err("Transform message is broken\n"); return -ECONNABORTED; } diff --git a/fs/cifsd/smb_common.c b/fs/cifsd/smb_common.c index d74b2ce08187..5bf644d7e321 100644 --- a/fs/cifsd/smb_common.c +++ b/fs/cifsd/smb_common.c @@ -447,7 +447,7 @@ int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) return smb_handle_negotiate(work); } - ksmbd_err("Unknown SMB negotiation command: %u\n", command); + pr_err("Unknown SMB negotiation command: %u\n", command); return -EINVAL; } diff --git a/fs/cifsd/smbacl.c b/fs/cifsd/smbacl.c index 63db8c015f9d..23c952612db4 100644 --- a/fs/cifsd/smbacl.c +++ b/fs/cifsd/smbacl.c @@ -264,8 +264,8 @@ static int sid_to_id(struct smb_sid *psid, uint sidtype, * Just return an error. */ if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { - ksmbd_err("%s: %u subauthorities is too many!\n", - __func__, psid->num_subauth); + pr_err("%s: %u subauthorities is too many!\n", + __func__, psid->num_subauth); return -EIO; } @@ -383,7 +383,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, /* validate that we do not go past end of acl */ if (end_of_acl <= (char *)pdacl || end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { - ksmbd_err("ACL too small to parse DACL\n"); + pr_err("ACL too small to parse DACL\n"); return; } @@ -477,8 +477,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, temp_fattr.cf_uid = INVALID_UID; ret = sid_to_id(&ppace[i]->sid, SIDOWNER, &temp_fattr); if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { - ksmbd_err("%s: Error %d mapping Owner SID to uid\n", - __func__, ret); + pr_err("%s: Error %d mapping Owner SID to uid\n", + __func__, ret); continue; } @@ -764,7 +764,7 @@ static int parse_sid(struct smb_sid *psid, char *end_of_acl) * bytes long (assuming no sub-auths - e.g. the null SID */ if (end_of_acl < (char *)psid + 8) { - ksmbd_err("ACL too small to parse SID %p\n", psid); + pr_err("ACL too small to parse SID %p\n", psid); return -EINVAL; } @@ -808,14 +808,14 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, if (pntsd->osidoffset) { rc = parse_sid(owner_sid_ptr, end_of_acl); if (rc) { - ksmbd_err("%s: Error %d parsing Owner SID\n", __func__, rc); + pr_err("%s: Error %d parsing Owner SID\n", __func__, rc); return rc; } rc = sid_to_id(owner_sid_ptr, SIDOWNER, fattr); if (rc) { - ksmbd_err("%s: Error %d mapping Owner SID to uid\n", - __func__, rc); + pr_err("%s: Error %d mapping Owner SID to uid\n", + __func__, rc); owner_sid_ptr = NULL; } } @@ -823,14 +823,14 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, if (pntsd->gsidoffset) { rc = parse_sid(group_sid_ptr, end_of_acl); if (rc) { - ksmbd_err("%s: Error %d mapping Owner SID to gid\n", - __func__, rc); + pr_err("%s: Error %d mapping Owner SID to gid\n", + __func__, rc); return rc; } rc = sid_to_id(group_sid_ptr, SIDUNIX_GROUP, fattr); if (rc) { - ksmbd_err("%s: Error %d mapping Group SID to gid\n", - __func__, rc); + pr_err("%s: Error %d mapping Group SID to gid\n", + __func__, rc); group_sid_ptr = NULL; } } diff --git a/fs/cifsd/transport_ipc.c b/fs/cifsd/transport_ipc.c index 2bcc1cad6037..13eacfda64ac 100644 --- a/fs/cifsd/transport_ipc.c +++ b/fs/cifsd/transport_ipc.c @@ -43,11 +43,11 @@ static unsigned int ksmbd_tools_pid; static bool ksmbd_ipc_validate_version(struct genl_info *m) { if (m->genlhdr->version != KSMBD_GENL_VERSION) { - ksmbd_err("%s. ksmbd: %d, kernel module: %d. %s.\n", - "Daemon and kernel module version mismatch", - m->genlhdr->version, - KSMBD_GENL_VERSION, - "User-space ksmbd should terminate"); + pr_err("%s. ksmbd: %d, kernel module: %d. %s.\n", + "Daemon and kernel module version mismatch", + m->genlhdr->version, + KSMBD_GENL_VERSION, + "User-space ksmbd should terminate"); return false; } return true; @@ -267,8 +267,8 @@ static int handle_response(int type, void *payload, size_t sz) * request message type + 1. */ if (entry->type + 1 != type) { - ksmbd_err("Waiting for IPC type %d, got %d. Ignore.\n", - entry->type + 1, type); + pr_err("Waiting for IPC type %d, got %d. Ignore.\n", + entry->type + 1, type); } entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); @@ -313,9 +313,9 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req) ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req), req->ifc_list_sz); if (ret) { - ksmbd_err("Server configuration error: %s %s %s\n", - req->netbios_name, req->server_string, - req->work_group); + pr_err("Server configuration error: %s %s %s\n", + req->netbios_name, req->server_string, + req->work_group); return ret; } @@ -353,7 +353,7 @@ static int handle_startup_event(struct sk_buff *skb, struct genl_info *info) mutex_lock(&startup_lock); if (!ksmbd_server_configurable()) { mutex_unlock(&startup_lock); - ksmbd_err("Server reset is in progress, can't start daemon\n"); + pr_err("Server reset is in progress, can't start daemon\n"); return -EINVAL; } @@ -363,7 +363,7 @@ static int handle_startup_event(struct sk_buff *skb, struct genl_info *info) goto out; } - ksmbd_err("Reconnect to a new user space daemon\n"); + pr_err("Reconnect to a new user space daemon\n"); } else { struct ksmbd_startup_request *req; @@ -384,7 +384,7 @@ out: static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info) { - ksmbd_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd); + pr_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd); return -EINVAL; } @@ -827,7 +827,7 @@ static int __ipc_heartbeat(void) WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); server_conf.ipc_last_active = 0; ksmbd_tools_pid = 0; - ksmbd_err("No IPC daemon response for %lus\n", delta / HZ); + pr_err("No IPC daemon response for %lus\n", delta / HZ); mutex_unlock(&startup_lock); return -EINVAL; } @@ -871,7 +871,7 @@ int ksmbd_ipc_init(void) ret = genl_register_family(&ksmbd_genl_family); if (ret) { - ksmbd_err("Failed to register KSMBD netlink interface %d\n", ret); + pr_err("Failed to register KSMBD netlink interface %d\n", ret); cancel_delayed_work_sync(&ipc_timer_work); } diff --git a/fs/cifsd/transport_rdma.c b/fs/cifsd/transport_rdma.c index 52237f023b66..bd7a090d5350 100644 --- a/fs/cifsd/transport_rdma.c +++ b/fs/cifsd/transport_rdma.c @@ -525,9 +525,9 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { if (wc->status != IB_WC_WR_FLUSH_ERR) { - ksmbd_err("Recv error. status='%s (%d)' opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); + pr_err("Recv error. status='%s (%d)' opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); smb_direct_disconnect_rdma_connection(t); } put_empty_recvmsg(t, recvmsg); @@ -623,7 +623,7 @@ static int smb_direct_post_recv(struct smb_direct_transport *t, ret = ib_post_recv(t->qp, &wr, NULL); if (ret) { - ksmbd_err("Can't post recv: %d\n", ret); + pr_err("Can't post recv: %d\n", ret); ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, recvmsg->sge.length, DMA_FROM_DEVICE); @@ -645,7 +645,7 @@ static int smb_direct_read(struct ksmbd_transport *t, char *buf, again: if (st->status != SMB_DIRECT_CS_CONNECTED) { - ksmbd_err("disconnected\n"); + pr_err("disconnected\n"); return -ENOTCONN; } @@ -794,7 +794,7 @@ static void smb_direct_post_recv_credits(struct work_struct *work) ret = smb_direct_post_recv(t, recvmsg); if (ret) { - ksmbd_err("Can't post recv: %d\n", ret); + pr_err("Can't post recv: %d\n", ret); put_recvmsg(t, recvmsg); break; } @@ -829,9 +829,9 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) wc->opcode); if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { - ksmbd_err("Send error. status='%s (%d)', opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); + pr_err("Send error. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); smb_direct_disconnect_rdma_connection(t); } @@ -880,7 +880,7 @@ static int smb_direct_post_send(struct smb_direct_transport *t, ret = ib_post_send(t->qp, wr, NULL); if (ret) { - ksmbd_err("failed to post send: %d\n", ret); + pr_err("failed to post send: %d\n", ret); if (wr->num_sge > 1) { if (atomic_dec_and_test(&t->send_payload_pending)) wake_up(&t->wait_send_payload_pending); @@ -1158,11 +1158,11 @@ static int smb_direct_post_send_data(struct smb_direct_transport *t, sg, SMB_DIRECT_MAX_SEND_SGES - 1, DMA_TO_DEVICE); if (sg_cnt <= 0) { - ksmbd_err("failed to map buffer\n"); + pr_err("failed to map buffer\n"); ret = -ENOMEM; goto err; } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES - 1) { - ksmbd_err("buffer not fitted into sges\n"); + pr_err("buffer not fitted into sges\n"); ret = -E2BIG; ib_dma_unmap_sg(t->cm_id->device, sg, sg_cnt, DMA_TO_DEVICE); @@ -1290,8 +1290,8 @@ static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, struct smb_direct_transport *t = msg->t; if (wc->status != IB_WC_SUCCESS) { - ksmbd_err("read/write error. opcode = %d, status = %s(%d)\n", - wc->opcode, ib_wc_status_msg(wc->status), wc->status); + pr_err("read/write error. opcode = %d, status = %s(%d)\n", + wc->opcode, ib_wc_status_msg(wc->status), wc->status); smb_direct_disconnect_rdma_connection(t); } @@ -1348,7 +1348,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, ret = get_sg_list(buf, buf_len, msg->sgt.sgl, msg->sgt.orig_nents); if (ret <= 0) { - ksmbd_err("failed to get pages\n"); + pr_err("failed to get pages\n"); goto err; } @@ -1357,7 +1357,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, 0, remote_offset, remote_key, is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); if (ret < 0) { - ksmbd_err("failed to init rdma_rw_ctx: %d\n", ret); + pr_err("failed to init rdma_rw_ctx: %d\n", ret); goto err; } @@ -1369,7 +1369,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, ret = ib_post_send(t->qp, first_wr, NULL); if (ret) { - ksmbd_err("failed to post send wr: %d\n", ret); + pr_err("failed to post send wr: %d\n", ret); goto err; } @@ -1445,9 +1445,9 @@ static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, break; } default: - ksmbd_err("Unexpected RDMA CM event. cm_id=%p, event=%s (%d)\n", - cm_id, rdma_event_msg(event->event), - event->event); + pr_err("Unexpected RDMA CM event. cm_id=%p, event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), + event->event); break; } return 0; @@ -1557,7 +1557,7 @@ static int smb_direct_accept_client(struct smb_direct_transport *t) ret = rdma_accept(t->cm_id, &conn_param); if (ret) { - ksmbd_err("error at rdma_accept: %d\n", ret); + pr_err("error at rdma_accept: %d\n", ret); return ret; } @@ -1581,14 +1581,14 @@ static int smb_direct_negotiate(struct smb_direct_transport *t) ret = smb_direct_post_recv(t, recvmsg); if (ret) { - ksmbd_err("Can't post recv: %d\n", ret); + pr_err("Can't post recv: %d\n", ret); goto out; } t->negotiation_requested = false; ret = smb_direct_accept_client(t); if (ret) { - ksmbd_err("Can't accept client\n"); + pr_err("Can't accept client\n"); goto out; } @@ -1635,7 +1635,7 @@ static int smb_direct_init_params(struct smb_direct_transport *t, t->max_send_size = smb_direct_max_send_size; max_send_sges = DIV_ROUND_UP(t->max_send_size, PAGE_SIZE) + 2; if (max_send_sges > SMB_DIRECT_MAX_SEND_SGES) { - ksmbd_err("max_send_size %d is too large\n", t->max_send_size); + pr_err("max_send_size %d is too large\n", t->max_send_size); return -EINVAL; } @@ -1655,31 +1655,31 @@ static int smb_direct_init_params(struct smb_direct_transport *t, max_send_wrs = smb_direct_send_credit_target + max_rw_wrs; if (max_send_wrs > device->attrs.max_cqe || max_send_wrs > device->attrs.max_qp_wr) { - ksmbd_err("consider lowering send_credit_target = %d, or max_outstanding_rw_ops = %d\n", - smb_direct_send_credit_target, - smb_direct_max_outstanding_rw_ops); - ksmbd_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", - device->attrs.max_cqe, device->attrs.max_qp_wr); + pr_err("consider lowering send_credit_target = %d, or max_outstanding_rw_ops = %d\n", + smb_direct_send_credit_target, + smb_direct_max_outstanding_rw_ops); + pr_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); return -EINVAL; } if (smb_direct_receive_credit_max > device->attrs.max_cqe || smb_direct_receive_credit_max > device->attrs.max_qp_wr) { - ksmbd_err("consider lowering receive_credit_max = %d\n", - smb_direct_receive_credit_max); - ksmbd_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", - device->attrs.max_cqe, device->attrs.max_qp_wr); + pr_err("consider lowering receive_credit_max = %d\n", + smb_direct_receive_credit_max); + pr_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); return -EINVAL; } if (device->attrs.max_send_sge < SMB_DIRECT_MAX_SEND_SGES) { - ksmbd_err("warning: device max_send_sge = %d too small\n", - device->attrs.max_send_sge); + pr_err("warning: device max_send_sge = %d too small\n", + device->attrs.max_send_sge); return -EINVAL; } if (device->attrs.max_recv_sge < SMB_DIRECT_MAX_RECV_SGES) { - ksmbd_err("warning: device max_recv_sge = %d too small\n", - device->attrs.max_recv_sge); + pr_err("warning: device max_recv_sge = %d too small\n", + device->attrs.max_recv_sge); return -EINVAL; } @@ -1788,7 +1788,7 @@ static int smb_direct_create_qpair(struct smb_direct_transport *t, t->pd = ib_alloc_pd(t->cm_id->device, 0); if (IS_ERR(t->pd)) { - ksmbd_err("Can't create RDMA PD\n"); + pr_err("Can't create RDMA PD\n"); ret = PTR_ERR(t->pd); t->pd = NULL; return ret; @@ -1797,7 +1797,7 @@ static int smb_direct_create_qpair(struct smb_direct_transport *t, t->send_cq = ib_alloc_cq(t->cm_id->device, t, t->send_credit_target, 0, IB_POLL_WORKQUEUE); if (IS_ERR(t->send_cq)) { - ksmbd_err("Can't create RDMA send CQ\n"); + pr_err("Can't create RDMA send CQ\n"); ret = PTR_ERR(t->send_cq); t->send_cq = NULL; goto err; @@ -1807,7 +1807,7 @@ static int smb_direct_create_qpair(struct smb_direct_transport *t, cap->max_send_wr + cap->max_rdma_ctxs, 0, IB_POLL_WORKQUEUE); if (IS_ERR(t->recv_cq)) { - ksmbd_err("Can't create RDMA recv CQ\n"); + pr_err("Can't create RDMA recv CQ\n"); ret = PTR_ERR(t->recv_cq); t->recv_cq = NULL; goto err; @@ -1825,7 +1825,7 @@ static int smb_direct_create_qpair(struct smb_direct_transport *t, ret = rdma_create_qp(t->cm_id, t->pd, &qp_attr); if (ret) { - ksmbd_err("Can't create RDMA QP: %d\n", ret); + pr_err("Can't create RDMA QP: %d\n", ret); goto err; } @@ -1861,25 +1861,25 @@ static int smb_direct_prepare(struct ksmbd_transport *t) ret = smb_direct_init_params(st, &qp_cap); if (ret) { - ksmbd_err("Can't configure RDMA parameters\n"); + pr_err("Can't configure RDMA parameters\n"); return ret; } ret = smb_direct_create_pools(st); if (ret) { - ksmbd_err("Can't init RDMA pool: %d\n", ret); + pr_err("Can't init RDMA pool: %d\n", ret); return ret; } ret = smb_direct_create_qpair(st, &qp_cap); if (ret) { - ksmbd_err("Can't accept RDMA client: %d\n", ret); + pr_err("Can't accept RDMA client: %d\n", ret); return ret; } ret = smb_direct_negotiate(st); if (ret) { - ksmbd_err("Can't negotiate: %d\n", ret); + pr_err("Can't negotiate: %d\n", ret); return ret; } @@ -1917,7 +1917,7 @@ static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) if (IS_ERR(KSMBD_TRANS(t)->handler)) { int ret = PTR_ERR(KSMBD_TRANS(t)->handler); - ksmbd_err("Can't start thread\n"); + pr_err("Can't start thread\n"); free_transport(t); return ret; } @@ -1933,7 +1933,7 @@ static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, int ret = smb_direct_handle_connect_request(cm_id); if (ret) { - ksmbd_err("Can't create transport: %d\n", ret); + pr_err("Can't create transport: %d\n", ret); return ret; } @@ -1942,8 +1942,8 @@ static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, break; } default: - ksmbd_err("Unexpected listen event. cm_id=%p, event=%s (%d)\n", - cm_id, rdma_event_msg(event->event), event->event); + pr_err("Unexpected listen event. cm_id=%p, event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), event->event); break; } return 0; @@ -1962,13 +1962,13 @@ static int smb_direct_listen(int port) cm_id = rdma_create_id(&init_net, smb_direct_listen_handler, &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); if (IS_ERR(cm_id)) { - ksmbd_err("Can't create cm id: %ld\n", PTR_ERR(cm_id)); + pr_err("Can't create cm id: %ld\n", PTR_ERR(cm_id)); return PTR_ERR(cm_id); } ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); if (ret) { - ksmbd_err("Can't bind: %d\n", ret); + pr_err("Can't bind: %d\n", ret); goto err; } @@ -1976,7 +1976,7 @@ static int smb_direct_listen(int port) ret = rdma_listen(cm_id, 10); if (ret) { - ksmbd_err("Can't listen: %d\n", ret); + pr_err("Can't listen: %d\n", ret); goto err; } return 0; @@ -2006,7 +2006,7 @@ int ksmbd_rdma_init(void) if (ret) { destroy_workqueue(smb_direct_wq); smb_direct_wq = NULL; - ksmbd_err("Can't listen: %d\n", ret); + pr_err("Can't listen: %d\n", ret); return ret; } diff --git a/fs/cifsd/transport_tcp.c b/fs/cifsd/transport_tcp.c index 16702b7874f4..56ec11ff5a9f 100644 --- a/fs/cifsd/transport_tcp.c +++ b/fs/cifsd/transport_tcp.c @@ -190,7 +190,7 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk) csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn); if (kernel_getpeername(client_sk, csin) < 0) { - ksmbd_err("client ip resolution failed\n"); + pr_err("client ip resolution failed\n"); rc = -EINVAL; goto out_error; } @@ -200,7 +200,7 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk) "ksmbd:%u", ksmbd_tcp_get_port(csin)); if (IS_ERR(KSMBD_TRANS(t)->handler)) { - ksmbd_err("cannot start conn thread\n"); + pr_err("cannot start conn thread\n"); rc = PTR_ERR(KSMBD_TRANS(t)->handler); free_transport(t); } @@ -380,7 +380,7 @@ static void tcp_destroy_socket(struct socket *ksmbd_socket) ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR); if (ret) - ksmbd_err("Failed to shutdown socket: %d\n", ret); + pr_err("Failed to shutdown socket: %d\n", ret); else sock_release(ksmbd_socket); } @@ -400,11 +400,11 @@ static int create_socket(struct interface *iface) ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); if (ret) { - ksmbd_err("Can't create socket for ipv6, try ipv4: %d\n", ret); + pr_err("Can't create socket for ipv6, try ipv4: %d\n", ret); ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); if (ret) { - ksmbd_err("Can't create socket for ipv4: %d\n", ret); + pr_err("Can't create socket for ipv4: %d\n", ret); goto out_error; } @@ -427,7 +427,7 @@ static int create_socket(struct interface *iface) KERNEL_SOCKPTR(iface->name), strlen(iface->name)); if (ret != -ENODEV && ret < 0) { - ksmbd_err("Failed to set SO_BINDTODEVICE: %d\n", ret); + pr_err("Failed to set SO_BINDTODEVICE: %d\n", ret); goto out_error; } @@ -438,7 +438,7 @@ static int create_socket(struct interface *iface) ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6, sizeof(sin6)); if (ret) { - ksmbd_err("Failed to bind socket: %d\n", ret); + pr_err("Failed to bind socket: %d\n", ret); goto out_error; } @@ -447,14 +447,14 @@ static int create_socket(struct interface *iface) ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG); if (ret) { - ksmbd_err("Port listen() error: %d\n", ret); + pr_err("Port listen() error: %d\n", ret); goto out_error; } iface->ksmbd_socket = ksmbd_socket; ret = ksmbd_tcp_run_kthread(iface); if (ret) { - ksmbd_err("Can't start ksmbd main kthread: %d\n", ret); + pr_err("Can't start ksmbd main kthread: %d\n", ret); goto out_error; } iface->state = IFACE_STATE_CONFIGURED; @@ -540,7 +540,7 @@ static void tcp_stop_kthread(struct task_struct *kthread) ret = kthread_stop(kthread); if (ret) - ksmbd_err("failed to stop forker thread\n"); + pr_err("failed to stop forker thread\n"); } void ksmbd_tcp_destroy(void) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index ef74e56cd05f..ad08bad8df4e 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -45,7 +45,7 @@ static char *extract_last_component(char *path) p++; } else { p = NULL; - ksmbd_err("Invalid path %s\n", path); + pr_err("Invalid path %s\n", path); } return p; } @@ -170,8 +170,8 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) if (IS_ERR(dentry)) { err = PTR_ERR(dentry); if (err != -ENOENT) - ksmbd_err("path create failed for %s, err %d\n", - name, err); + pr_err("path create failed for %s, err %d\n", + name, err); return err; } @@ -181,7 +181,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(dentry)); } else { - ksmbd_err("File(%s): creation failed (err:%d)\n", name, err); + pr_err("File(%s): creation failed (err:%d)\n", name, err); } done_path_create(&path, dentry); return err; @@ -235,7 +235,7 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) out: done_path_create(&path, dentry); if (err) - ksmbd_err("mkdir(%s): creation failed (err:%d)\n", name, err); + pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); return err; } @@ -259,7 +259,7 @@ static ssize_t ksmbd_vfs_getcasexattr(struct dentry *dentry, char *attr_name, name, attr_value); if (value_len < 0) - ksmbd_err("failed to get xattr in file\n"); + pr_err("failed to get xattr in file\n"); break; } @@ -324,7 +324,7 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end, if (flock->fl_end >= start && end >= flock->fl_start) { if (flock->fl_type == F_RDLCK) { if (type == WRITE) { - ksmbd_err("not allow write by shared lock\n"); + pr_err("not allow write by shared lock\n"); error = 1; goto out; } @@ -332,7 +332,7 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end, /* check owner in lock */ if (flock->fl_file != filp) { error = 1; - ksmbd_err("not allow rw access by exclusive lock from other opens\n"); + pr_err("not allow rw access by exclusive lock from other opens\n"); goto out; } } @@ -368,7 +368,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, if (work->conn->connection_type) { if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { - ksmbd_err("no right to read(%s)\n", FP_FILENAME(fp)); + pr_err("no right to read(%s)\n", FP_FILENAME(fp)); return -EACCES; } } @@ -381,15 +381,15 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, ret = check_lock_range(filp, *pos, *pos + count - 1, READ); if (ret) { - ksmbd_err("unable to read due to lock\n"); + pr_err("unable to read due to lock\n"); return -EAGAIN; } } nbytes = kernel_read(filp, rbuf, count, pos); if (nbytes < 0) { - ksmbd_err("smb read failed for (%s), err = %zd\n", - fp->filename, nbytes); + pr_err("smb read failed for (%s), err = %zd\n", + fp->filename, nbytes); return nbytes; } @@ -418,7 +418,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, fp->stream.size, &stream_buf); if ((int)v_len < 0) { - ksmbd_err("not found stream in xattr : %zd\n", v_len); + pr_err("not found stream in xattr : %zd\n", v_len); err = (int)v_len; goto out; } @@ -476,7 +476,7 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, if (sess->conn->connection_type) { if (!(fp->daccess & FILE_WRITE_DATA_LE)) { - ksmbd_err("no right to write(%s)\n", FP_FILENAME(fp)); + pr_err("no right to write(%s)\n", FP_FILENAME(fp)); err = -EACCES; goto out; } @@ -494,7 +494,7 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, if (!work->tcon->posix_extensions) { err = check_lock_range(filp, *pos, *pos + count - 1, WRITE); if (err) { - ksmbd_err("unable to write due to lock\n"); + pr_err("unable to write due to lock\n"); err = -EAGAIN; goto out; } @@ -515,8 +515,8 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, if (sync) { err = vfs_fsync_range(filp, offset, offset + *written, 0); if (err < 0) - ksmbd_err("fsync failed for filename = %s, err = %d\n", - FP_FILENAME(fp), err); + pr_err("fsync failed for filename = %s, err = %d\n", + FP_FILENAME(fp), err); } out: @@ -537,7 +537,7 @@ int ksmbd_vfs_getattr(struct path *path, struct kstat *stat) err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT); if (err) - ksmbd_err("getattr failed, err %d\n", err); + pr_err("getattr failed, err %d\n", err); return err; } @@ -555,12 +555,12 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) fp = ksmbd_lookup_fd_slow(work, fid, p_id); if (!fp) { - ksmbd_err("failed to get filp for fid %llu\n", fid); + pr_err("failed to get filp for fid %llu\n", fid); return -ENOENT; } err = vfs_fsync(fp->filp, 0); if (err < 0) - ksmbd_err("smb fsync failed, err = %d\n", err); + pr_err("smb fsync failed, err = %d\n", err); ksmbd_fd_put(work, fp); return err; } @@ -654,8 +654,8 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, err = kern_path(oldname, flags, &oldpath); if (err) { - ksmbd_err("cannot get linux path for %s, err = %d\n", - oldname, err); + pr_err("cannot get linux path for %s, err = %d\n", + oldname, err); goto out1; } @@ -663,13 +663,13 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, flags | LOOKUP_REVAL); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); - ksmbd_err("path create err for %s, err %d\n", newname, err); + pr_err("path create err for %s, err %d\n", newname, err); goto out2; } err = -EXDEV; if (oldpath.mnt != newpath.mnt) { - ksmbd_err("vfs_link failed err %d\n", err); + pr_err("vfs_link failed err %d\n", err); goto out3; } @@ -730,7 +730,7 @@ static int __ksmbd_vfs_rename(struct ksmbd_work *work, dst_dent = lookup_one_len(dst_name, dst_dent_parent, strlen(dst_name)); err = PTR_ERR(dst_dent); if (IS_ERR(dst_dent)) { - ksmbd_err("lookup failed %s [%d]\n", dst_name, err); + pr_err("lookup failed %s [%d]\n", dst_name, err); goto out; } @@ -747,7 +747,7 @@ static int __ksmbd_vfs_rename(struct ksmbd_work *work, err = vfs_rename(&rd); } if (err) - ksmbd_err("vfs_rename failed err %d\n", err); + pr_err("vfs_rename failed err %d\n", err); if (dst_dent) dput(dst_dent); out: @@ -835,14 +835,14 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, if (name) { err = kern_path(name, 0, &path); if (err) { - ksmbd_err("cannot get linux path for %s, err %d\n", - name, err); + pr_err("cannot get linux path for %s, err %d\n", + name, err); return err; } err = vfs_truncate(&path, size); if (err) - ksmbd_err("truncate failed for %s err %d\n", - name, err); + pr_err("truncate failed for %s err %d\n", + name, err); path_put(&path); } else { struct file *filp; @@ -864,15 +864,15 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, } if (err) { - ksmbd_err("failed due to lock\n"); + pr_err("failed due to lock\n"); return -EAGAIN; } } err = vfs_truncate(&filp->f_path, size); if (err) - ksmbd_err("truncate failed for filename : %s err %d\n", - fp->filename, err); + pr_err("truncate failed for filename : %s err %d\n", + fp->filename, err); } return err; @@ -1458,7 +1458,7 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct inode *inode, xa_entry->type = SMB_ACL_MASK; break; default: - ksmbd_err("unknown type : 0x%x\n", pa_entry->e_tag); + pr_err("unknown type : 0x%x\n", pa_entry->e_tag); goto out; } @@ -1502,7 +1502,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash); if (rc) { - ksmbd_err("failed to generate hash for ndr acl\n"); + pr_err("failed to generate hash for ndr acl\n"); return rc; } @@ -1513,27 +1513,27 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, rc = ndr_encode_posix_acl(&acl_ndr, inode, smb_acl, def_smb_acl); if (rc) { - ksmbd_err("failed to encode ndr to posix acl\n"); + pr_err("failed to encode ndr to posix acl\n"); goto out; } rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, acl.posix_acl_hash); if (rc) { - ksmbd_err("failed to generate hash for ndr acl\n"); + pr_err("failed to generate hash for ndr acl\n"); goto out; } rc = ndr_encode_v4_ntacl(&sd_ndr, &acl); if (rc) { - ksmbd_err("failed to encode ndr to posix acl\n"); + pr_err("failed to encode ndr to posix acl\n"); goto out; } rc = ksmbd_vfs_setxattr(dentry, XATTR_NAME_SD, sd_ndr.data, sd_ndr.offset, 0); if (rc < 0) - ksmbd_err("Failed to store XATTR ntacl :%d\n", rc); + pr_err("Failed to store XATTR ntacl :%d\n", rc); kfree(sd_ndr.data); out: @@ -1570,19 +1570,19 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, rc = ndr_encode_posix_acl(&acl_ndr, inode, smb_acl, def_smb_acl); if (rc) { - ksmbd_err("failed to encode ndr to posix acl\n"); + pr_err("failed to encode ndr to posix acl\n"); goto out; } rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, cmp_hash); if (rc) { - ksmbd_err("failed to generate hash for ndr acl\n"); + pr_err("failed to generate hash for ndr acl\n"); goto out; } if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { - ksmbd_err("hash value diff\n"); + pr_err("hash value diff\n"); rc = -EINVAL; goto out; } @@ -1821,11 +1821,11 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, *total_size_written = 0; if (!(src_fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { - ksmbd_err("no right to read(%s)\n", FP_FILENAME(src_fp)); + pr_err("no right to read(%s)\n", FP_FILENAME(src_fp)); return -EACCES; } if (!(dst_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) { - ksmbd_err("no right to write(%s)\n", FP_FILENAME(dst_fp)); + pr_err("no right to write(%s)\n", FP_FILENAME(dst_fp)); return -EACCES; } diff --git a/fs/cifsd/vfs_cache.c b/fs/cifsd/vfs_cache.c index 4cf14c247e9e..c88210b15289 100644 --- a/fs/cifsd/vfs_cache.c +++ b/fs/cifsd/vfs_cache.c @@ -185,7 +185,7 @@ static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp) rc = ksmbd_inode_init(ci, fp); if (rc) { - ksmbd_err("inode initialized failed\n"); + pr_err("inode initialized failed\n"); kfree(ci); return NULL; } @@ -254,8 +254,8 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) err = ksmbd_vfs_remove_xattr(filp->f_path.dentry, fp->stream.name); if (err) - ksmbd_err("remove xattr failed : %s\n", - fp->stream.name); + pr_err("remove xattr failed : %s\n", + fp->stream.name); } if (atomic_dec_and_test(&ci->m_count)) { @@ -542,7 +542,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL); if (!fp) { - ksmbd_err("Failed to allocate memory\n"); + pr_err("Failed to allocate memory\n"); return ERR_PTR(-ENOMEM); } @@ -698,7 +698,7 @@ int ksmbd_init_file_cache(void) return 0; out: - ksmbd_err("failed to allocate file cache\n"); + pr_err("failed to allocate file cache\n"); return -ENOMEM; } From e8c061917133dd77410239bfc0fae151b1955af2 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 22 Jun 2021 11:06:11 +0900 Subject: [PATCH 138/417] ksmbd: opencode to avoid trivial wrappers Opencode to avoid trivial wrappers that just make the code hard to follow. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 19 ++++++++++++------- fs/cifsd/vfs.c | 30 ++---------------------------- fs/cifsd/vfs.h | 4 ---- 3 files changed, 14 insertions(+), 39 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 84f4cd7f545f..96a0cb512882 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "glob.h" #include "smb2pdu.h" @@ -2948,10 +2949,12 @@ int smb2_open(struct ksmbd_work *work) ksmbd_debug(SMB, "request smb2 create allocate size : %llu\n", alloc_size); - err = ksmbd_vfs_alloc_size(work, fp, alloc_size); + smb_break_all_levII_oplock(work, fp, 1); + err = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, + alloc_size); if (err < 0) ksmbd_debug(SMB, - "ksmbd_vfs_alloc_size is failed : %d\n", + "vfs_fallocate is failed : %d\n", err); } @@ -3762,7 +3765,7 @@ int smb2_query_dir(struct ksmbd_work *work) dir_fp->readdir_data.private = &query_dir_private; set_ctx_actor(&dir_fp->readdir_data.ctx, __query_dir); - rc = ksmbd_vfs_readdir(dir_fp->filp, &dir_fp->readdir_data); + rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); if (rc == 0) restart_ctx(&dir_fp->readdir_data.ctx); if (rc == -ENOSPC) @@ -5465,9 +5468,11 @@ static int set_file_allocation_info(struct ksmbd_work *work, inode = file_inode(fp->filp); if (alloc_blks > inode->i_blocks) { - rc = ksmbd_vfs_alloc_size(work, fp, alloc_blks * 512); + smb_break_all_levII_oplock(work, fp, 1); + rc = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, + alloc_blks * 512); if (rc && rc != -EOPNOTSUPP) { - pr_err("ksmbd_vfs_alloc_size is failed : %d\n", rc); + pr_err("vfs_fallocate is failed : %d\n", rc); return rc; } } else if (alloc_blks < inode->i_blocks) { @@ -6672,7 +6677,7 @@ no_check_gl: flock = smb_lock->fl; list_del(&smb_lock->llist); retry: - err = ksmbd_vfs_lock(filp, smb_lock->cmd, flock); + err = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); skip: if (flags & SMB2_LOCKFLAG_UNLOCK) { if (!err) { @@ -6785,7 +6790,7 @@ out: rlock->fl_start = smb_lock->start; rlock->fl_end = smb_lock->end; - err = ksmbd_vfs_lock(filp, 0, rlock); + err = vfs_lock_file(filp, 0, rlock, NULL); if (err) pr_err("rollback unlock fail : %d\n", err); list_del(&smb_lock->llist); diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index ad08bad8df4e..1ba3fd95ba6b 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -1000,32 +1000,6 @@ void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) } } -/** - * ksmbd_vfs_lock() - vfs helper for smb file locking - * @filp: the file to apply the lock to - * @cmd: type of locking operation (F_SETLK, F_GETLK, etc.) - * @flock: The lock to be applied - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_lock(struct file *filp, int cmd, struct file_lock *flock) -{ - ksmbd_debug(VFS, "calling vfs_lock_file\n"); - return vfs_lock_file(filp, cmd, flock, NULL); -} - -int ksmbd_vfs_readdir(struct file *file, struct ksmbd_readdir_data *rdata) -{ - return iterate_dir(file, &rdata->ctx); -} - -int ksmbd_vfs_alloc_size(struct ksmbd_work *work, struct ksmbd_file *fp, - loff_t len) -{ - smb_break_all_levII_oplock(work, fp, 1); - return vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, len); -} - int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, loff_t off, loff_t len) { @@ -1216,7 +1190,7 @@ int ksmbd_vfs_empty_dir(struct ksmbd_file *fp) set_ctx_actor(&readdir_data.ctx, __dir_empty); readdir_data.dirent_count = 0; - err = ksmbd_vfs_readdir(fp->filp, &readdir_data); + err = iterate_dir(fp->filp, &readdir_data.ctx); if (readdir_data.dirent_count > 2) err = -ENOTEMPTY; else @@ -1266,7 +1240,7 @@ static int ksmbd_vfs_lookup_in_dir(struct path *dir, char *name, size_t namelen) if (IS_ERR(dfilp)) return PTR_ERR(dfilp); - ret = ksmbd_vfs_readdir(dfilp, &readdir_data); + ret = iterate_dir(dfilp, &readdir_data.ctx); if (readdir_data.dirent_count > 0) ret = 0; fput(dfilp); diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index 03b877e6520b..e1021f579f37 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -232,10 +232,6 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, bool caseless); int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); -int ksmbd_vfs_lock(struct file *filp, int cmd, struct file_lock *flock); -int ksmbd_vfs_readdir(struct file *file, struct ksmbd_readdir_data *rdata); -int ksmbd_vfs_alloc_size(struct ksmbd_work *work, struct ksmbd_file *fp, - loff_t len); int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, loff_t off, loff_t len); struct file_allocated_range_buffer; From 4b637fc18902600dfe722f9b1a45950bfc8bc7b5 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 18 Jun 2021 10:20:24 +0900 Subject: [PATCH 139/417] ksmbd: factor out a ksmbd_validate_entry_in_use helper from __ksmbd_vfs_rename Factor out a self-contained helper to find sub file/dir in use. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/vfs.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 1ba3fd95ba6b..ca4c6c020a8e 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -687,6 +687,29 @@ out1: return err; } +static int ksmbd_validate_entry_in_use(struct dentry *src_dent) +{ + struct dentry *dst_dent; + + spin_lock(&src_dent->d_lock); + list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { + struct ksmbd_file *child_fp; + + if (d_really_is_negative(dst_dent)) + continue; + + child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); + if (child_fp) { + spin_unlock(&src_dent->d_lock); + ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); + return -EACCES; + } + } + spin_unlock(&src_dent->d_lock); + + return 0; +} + static int __ksmbd_vfs_rename(struct ksmbd_work *work, struct dentry *src_dent_parent, struct dentry *src_dent, @@ -698,21 +721,9 @@ static int __ksmbd_vfs_rename(struct ksmbd_work *work, int err; if (!work->tcon->posix_extensions) { - spin_lock(&src_dent->d_lock); - list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { - struct ksmbd_file *child_fp; - - if (d_really_is_negative(dst_dent)) - continue; - - child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); - if (child_fp) { - spin_unlock(&src_dent->d_lock); - ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); - return -EACCES; - } - } - spin_unlock(&src_dent->d_lock); + err = ksmbd_validate_entry_in_use(src_dent); + if (err) + return err; } if (d_really_is_negative(src_dent_parent)) From 67d1c432994cbf30f63ec35abba493b027f0c910 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 22 Jun 2021 11:42:29 +0900 Subject: [PATCH 140/417] ksmbd: opencode posix acl functions instead of wrappers Add select FS_POSIX_ACL in Kconfig and then opencode posix acl functions instead of wrappers Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Kconfig | 1 + fs/cifsd/smb2pdu.c | 4 ++-- fs/cifsd/smbacl.c | 14 +++++++------- fs/cifsd/vfs.c | 44 +++++++++----------------------------------- fs/cifsd/vfs.h | 4 ---- 5 files changed, 19 insertions(+), 48 deletions(-) diff --git a/fs/cifsd/Kconfig b/fs/cifsd/Kconfig index e6448b04f46e..796f928a7da0 100644 --- a/fs/cifsd/Kconfig +++ b/fs/cifsd/Kconfig @@ -19,6 +19,7 @@ config SMB_SERVER select CRYPTO_GCM select ASN1 select OID_REGISTRY + select FS_POSIX_ACL default n help Choose Y here if you want to allow SMB3 compliant clients diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 96a0cb512882..0d004c6d1c63 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -2327,9 +2327,9 @@ static void ksmbd_acls_fattr(struct smb_fattr *fattr, struct inode *inode) fattr->cf_mode = inode->i_mode; fattr->cf_dacls = NULL; - fattr->cf_acls = ksmbd_vfs_get_acl(inode, ACL_TYPE_ACCESS); + fattr->cf_acls = get_acl(inode, ACL_TYPE_ACCESS); if (S_ISDIR(inode->i_mode)) - fattr->cf_dacls = ksmbd_vfs_get_acl(inode, ACL_TYPE_DEFAULT); + fattr->cf_dacls = get_acl(inode, ACL_TYPE_DEFAULT); } /** diff --git a/fs/cifsd/smbacl.c b/fs/cifsd/smbacl.c index 23c952612db4..958937a548a1 100644 --- a/fs/cifsd/smbacl.c +++ b/fs/cifsd/smbacl.c @@ -532,7 +532,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, if (acl_state.users->n || acl_state.groups->n) { acl_state.mask.allow = 0x07; - fattr->cf_acls = ksmbd_vfs_posix_acl_alloc(acl_state.users->n + + fattr->cf_acls = posix_acl_alloc(acl_state.users->n + acl_state.groups->n + 4, GFP_KERNEL); if (fattr->cf_acls) { cf_pace = fattr->cf_acls->a_entries; @@ -543,7 +543,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, if (default_acl_state.users->n || default_acl_state.groups->n) { default_acl_state.mask.allow = 0x07; fattr->cf_dacls = - ksmbd_vfs_posix_acl_alloc(default_acl_state.users->n + + posix_acl_alloc(default_acl_state.users->n + default_acl_state.groups->n + 4, GFP_KERNEL); if (fattr->cf_dacls) { cf_pdace = fattr->cf_dacls->a_entries; @@ -1202,7 +1202,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, granted = GENERIC_ALL_FLAGS; } - posix_acls = ksmbd_vfs_get_acl(d_inode(dentry), ACL_TYPE_ACCESS); + posix_acls = get_acl(d_inode(dentry), ACL_TYPE_ACCESS); if (posix_acls && !found) { unsigned int id = -1; @@ -1287,11 +1287,11 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, ksmbd_vfs_remove_acl_xattrs(dentry); /* Update posix acls */ if (fattr.cf_dacls) { - rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, - fattr.cf_acls); + rc = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, + fattr.cf_acls); if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) - rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, - fattr.cf_dacls); + rc = set_posix_acl(&init_user_ns, inode, + ACL_TYPE_DEFAULT, fattr.cf_dacls); } /* Check it only calling from SD BUFFER context */ diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index ca4c6c020a8e..e34e536dc9ce 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -1407,7 +1407,7 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct inode *inode, struct xattr_acl_entry *xa_entry; int i; - posix_acls = ksmbd_vfs_get_acl(inode, acl_type); + posix_acls = get_acl(inode, acl_type); if (!posix_acls) return NULL; @@ -1630,34 +1630,6 @@ int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, return err; } -struct posix_acl *ksmbd_vfs_posix_acl_alloc(int count, gfp_t flags) -{ -#if IS_ENABLED(CONFIG_FS_POSIX_ACL) - return posix_acl_alloc(count, flags); -#else - return NULL; -#endif -} - -struct posix_acl *ksmbd_vfs_get_acl(struct inode *inode, int type) -{ -#if IS_ENABLED(CONFIG_FS_POSIX_ACL) - return get_acl(inode, type); -#else - return NULL; -#endif -} - -int ksmbd_vfs_set_posix_acl(struct inode *inode, int type, - struct posix_acl *acl) -{ -#if IS_ENABLED(CONFIG_FS_POSIX_ACL) - return set_posix_acl(&init_user_ns, inode, type, acl); -#else - return -EOPNOTSUPP; -#endif -} - /** * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format * @p: destination buffer @@ -1895,19 +1867,20 @@ int ksmbd_vfs_set_init_posix_acl(struct inode *inode) acl_state.group.allow; acl_state.mask.allow = 0x07; - acls = ksmbd_vfs_posix_acl_alloc(6, GFP_KERNEL); + acls = posix_acl_alloc(6, GFP_KERNEL); if (!acls) { free_acl_state(&acl_state); return -ENOMEM; } posix_state_to_acl(&acl_state, acls->a_entries); - rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, acls); + rc = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", rc); else if (S_ISDIR(inode->i_mode)) { posix_state_to_acl(&acl_state, acls->a_entries); - rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); + rc = set_posix_acl(&init_user_ns, inode, ACL_TYPE_DEFAULT, + acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", rc); @@ -1923,7 +1896,7 @@ int ksmbd_vfs_inherit_posix_acl(struct inode *inode, struct inode *parent_inode) struct posix_acl_entry *pace; int rc, i; - acls = ksmbd_vfs_get_acl(parent_inode, ACL_TYPE_DEFAULT); + acls = get_acl(parent_inode, ACL_TYPE_DEFAULT); if (!acls) return -ENOENT; pace = acls->a_entries; @@ -1935,12 +1908,13 @@ int ksmbd_vfs_inherit_posix_acl(struct inode *inode, struct inode *parent_inode) } } - rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_ACCESS, acls); + rc = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", rc); if (S_ISDIR(inode->i_mode)) { - rc = ksmbd_vfs_set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); + rc = set_posix_acl(&init_user_ns, inode, ACL_TYPE_DEFAULT, + acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", rc); diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index e1021f579f37..29352c227028 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -258,10 +258,6 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, struct xattr_dos_attrib *da); int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, struct xattr_dos_attrib *da); -struct posix_acl *ksmbd_vfs_posix_acl_alloc(int count, gfp_t flags); -struct posix_acl *ksmbd_vfs_get_acl(struct inode *inode, int type); -int ksmbd_vfs_set_posix_acl(struct inode *inode, int type, - struct posix_acl *acl); int ksmbd_vfs_set_init_posix_acl(struct inode *inode); int ksmbd_vfs_inherit_posix_acl(struct inode *inode, struct inode *parent_inode); From ee2033e9c64139c4f052bed52e72eba44a08b40a Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 22 Jun 2021 13:26:24 +0900 Subject: [PATCH 141/417] ksmbd: change stream type macro to enumeration Change stream type macro to enumeration and move it to vfs.h. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/glob.h | 3 --- fs/cifsd/vfs.h | 8 ++++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/cifsd/glob.h b/fs/cifsd/glob.h index 8119cb7ddbed..49a5a3afa118 100644 --- a/fs/cifsd/glob.h +++ b/fs/cifsd/glob.h @@ -16,9 +16,6 @@ extern int ksmbd_debug_types; -#define DATA_STREAM 1 -#define DIR_STREAM 2 - #define KSMBD_DEBUG_SMB BIT(0) #define KSMBD_DEBUG_AUTH BIT(1) #define KSMBD_DEBUG_VFS BIT(2) diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index 29352c227028..a9c14c5dee8d 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -101,6 +101,14 @@ struct xattr_ntacl { #define XATTR_NAME_SD_LEN \ (sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1) +/* + * Enumeration for stream type. + */ +enum { + DATA_STREAM = 1, /* type $DATA */ + DIR_STREAM /* type $INDEX_ALLOCATION */ +}; + /* CreateOptions */ /* Flag is set, it must not be a file , valid for directory only */ #define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) From ee81cae1a6323fa4489313dfd9de436da7ff8519 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 26 Jun 2021 22:32:34 +0900 Subject: [PATCH 142/417] ksmbd: use f_bsize instead of q->limits.logical_block_size Use f_bsize instead of q->limits.logical_block_size. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 25 +++++++------------------ fs/cifsd/vfs.c | 23 ----------------------- fs/cifsd/vfs.h | 1 - 3 files changed, 7 insertions(+), 42 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 0d004c6d1c63..341d51e711a5 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -4739,16 +4739,12 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, case FS_SIZE_INFORMATION: { struct filesystem_info *info; - unsigned short logical_sector_size; info = (struct filesystem_info *)(rsp->Buffer); - logical_sector_size = - ksmbd_vfs_logical_sector_size(d_inode(path.dentry)); - info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); - info->SectorsPerAllocationUnit = cpu_to_le32(stfs.f_bsize >> 9); - info->BytesPerSector = cpu_to_le32(logical_sector_size); + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); rsp->OutputBufferLength = cpu_to_le32(24); inc_rfc1001_len(rsp_org, 24); fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; @@ -4757,19 +4753,15 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, case FS_FULL_SIZE_INFORMATION: { struct smb2_fs_full_size_info *info; - unsigned short logical_sector_size; info = (struct smb2_fs_full_size_info *)(rsp->Buffer); - logical_sector_size = - ksmbd_vfs_logical_sector_size(d_inode(path.dentry)); - info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); info->CallerAvailableAllocationUnits = cpu_to_le64(stfs.f_bavail); info->ActualAvailableAllocationUnits = cpu_to_le64(stfs.f_bfree); - info->SectorsPerAllocationUnit = cpu_to_le32(stfs.f_bsize >> 9); - info->BytesPerSector = cpu_to_le32(logical_sector_size); + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); rsp->OutputBufferLength = cpu_to_le32(32); inc_rfc1001_len(rsp_org, 32); fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; @@ -4846,16 +4838,13 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, case FS_POSIX_INFORMATION: { struct filesystem_posix_info *info; - unsigned short logical_sector_size; if (!work->tcon->posix_extensions) { pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); rc = -EOPNOTSUPP; } else { info = (struct filesystem_posix_info *)(rsp->Buffer); - logical_sector_size = - ksmbd_vfs_logical_sector_size(d_inode(path.dentry)); - info->OptimalTransferSize = cpu_to_le32(logical_sector_size); + info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize); info->BlockSize = cpu_to_le32(stfs.f_bsize); info->TotalBlocks = cpu_to_le64(stfs.f_blocks); info->BlocksAvail = cpu_to_le64(stfs.f_bfree); @@ -5588,13 +5577,13 @@ static int set_file_position_info(struct ksmbd_file *fp, char *buf) { struct smb2_file_pos_info *file_info; loff_t current_byte_offset; - unsigned short sector_size; + unsigned long sector_size; struct inode *inode; inode = file_inode(fp->filp); file_info = (struct smb2_file_pos_info *)buf; current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset); - sector_size = ksmbd_vfs_logical_sector_size(inode); + sector_size = inode->i_sb->s_blocksize; if (current_byte_offset < 0 || (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index e34e536dc9ce..9c594e88b2c7 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -1119,28 +1118,6 @@ out: return err; } -/* - * ksmbd_vfs_get_logical_sector_size() - get logical sector size from inode - * @inode: inode - * - * Return: logical sector size - */ -unsigned short ksmbd_vfs_logical_sector_size(struct inode *inode) -{ - struct request_queue *q; - unsigned short ret_val = 512; - - if (!inode->i_sb->s_bdev) - return ret_val; - - q = inode->i_sb->s_bdev->bd_disk->queue; - - if (q && q->limits.logical_block_size) - ret_val = q->limits.logical_block_size; - - return ret_val; -} - /* * ksmbd_vfs_get_smb2_sector_size() - get fs sector sizes * @inode: inode diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index a9c14c5dee8d..751bb6ea6e12 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -247,7 +247,6 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, struct file_allocated_range_buffer *ranges, int in_count, int *out_count); int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry); -unsigned short ksmbd_vfs_logical_sector_size(struct inode *inode); void ksmbd_vfs_smb2_sector_size(struct inode *inode, struct ksmbd_fs_sector_size *fs_ss); void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); From 560ac05130696de2491881bbc2a5024c94bc3912 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 22 Jun 2021 16:16:45 +0900 Subject: [PATCH 143/417] ksmbd: remove unneeded NULL check in the list iterator Remove unneeded NULL check in the list iterator. And use list_for_each_entry_safe instead of list_for_each_safe. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/mgmt/user_session.c | 13 +++++-------- fs/cifsd/smb2pdu.c | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/fs/cifsd/mgmt/user_session.c b/fs/cifsd/mgmt/user_session.c index 615b46f0762b..c5ba9694e1f1 100644 --- a/fs/cifsd/mgmt/user_session.c +++ b/fs/cifsd/mgmt/user_session.c @@ -30,15 +30,12 @@ struct ksmbd_session_rpc { static void free_channel_list(struct ksmbd_session *sess) { - struct channel *chann; - struct list_head *tmp, *t; + struct channel *chann, *tmp; - list_for_each_safe(tmp, t, &sess->ksmbd_chann_list) { - chann = list_entry(tmp, struct channel, chann_list); - if (chann) { - list_del(&chann->chann_list); - kfree(chann); - } + list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list, + chann_list) { + list_del(&chann->chann_list); + kfree(chann); } } diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 341d51e711a5..bbb35e68abc4 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -76,7 +76,7 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn struct channel *chann; list_for_each_entry(chann, &sess->ksmbd_chann_list, chann_list) { - if (chann && chann->conn == conn) + if (chann->conn == conn) return chann; } From 131bac1ece2e16201674b2f29b64d2044c826b56 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 22 Jun 2021 16:20:47 +0900 Subject: [PATCH 144/417] ksmbd: use f_bsize in FS_SECTOR_SIZE_INFORMATION Use f_bsize in FS_SECTOR_SIZE_INFORMATION to avoid the access the block layer. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 12 ++++-------- fs/cifsd/vfs.c | 31 ------------------------------- fs/cifsd/vfs.h | 8 -------- 3 files changed, 4 insertions(+), 47 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index bbb35e68abc4..1327ae806b17 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -4791,19 +4791,15 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, case FS_SECTOR_SIZE_INFORMATION: { struct smb3_fs_ss_info *info; - struct ksmbd_fs_sector_size fs_ss; info = (struct smb3_fs_ss_info *)(rsp->Buffer); - ksmbd_vfs_smb2_sector_size(d_inode(path.dentry), &fs_ss); - info->LogicalBytesPerSector = - cpu_to_le32(fs_ss.logical_sector_size); + info->LogicalBytesPerSector = cpu_to_le32(stfs.f_bsize); info->PhysicalBytesPerSectorForAtomicity = - cpu_to_le32(fs_ss.physical_sector_size); - info->PhysicalBytesPerSectorForPerf = - cpu_to_le32(fs_ss.optimal_io_size); + cpu_to_le32(stfs.f_bsize); + info->PhysicalBytesPerSectorForPerf = cpu_to_le32(stfs.f_bsize); info->FSEffPhysicalBytesPerSectorForAtomicity = - cpu_to_le32(fs_ss.optimal_io_size); + cpu_to_le32(stfs.f_bsize); info->Flags = cpu_to_le32(SSINFO_FLAGS_ALIGNED_DEVICE | SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE); info->ByteOffsetForSectorAlignment = 0; diff --git a/fs/cifsd/vfs.c b/fs/cifsd/vfs.c index 9c594e88b2c7..fddabb4c7db6 100644 --- a/fs/cifsd/vfs.c +++ b/fs/cifsd/vfs.c @@ -1118,37 +1118,6 @@ out: return err; } -/* - * ksmbd_vfs_get_smb2_sector_size() - get fs sector sizes - * @inode: inode - * @fs_ss: fs sector size struct - */ -void ksmbd_vfs_smb2_sector_size(struct inode *inode, - struct ksmbd_fs_sector_size *fs_ss) -{ - struct request_queue *q; - - fs_ss->logical_sector_size = 512; - fs_ss->physical_sector_size = 512; - fs_ss->optimal_io_size = 512; - - if (!inode->i_sb->s_bdev) - return; - - q = inode->i_sb->s_bdev->bd_disk->queue; - - if (q) { - if (q->limits.logical_block_size) - fs_ss->logical_sector_size = - q->limits.logical_block_size; - if (q->limits.physical_block_size) - fs_ss->physical_sector_size = - q->limits.physical_block_size; - if (q->limits.io_opt) - fs_ss->optimal_io_size = q->limits.io_opt; - } -} - static int __dir_empty(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) { diff --git a/fs/cifsd/vfs.h b/fs/cifsd/vfs.h index 751bb6ea6e12..49f0558ace32 100644 --- a/fs/cifsd/vfs.h +++ b/fs/cifsd/vfs.h @@ -192,12 +192,6 @@ struct ksmbd_kstat { __le32 file_attributes; }; -struct ksmbd_fs_sector_size { - unsigned short logical_sector_size; - unsigned int physical_sector_size; - unsigned int optimal_io_size; -}; - int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete); int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess); @@ -247,8 +241,6 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, struct file_allocated_range_buffer *ranges, int in_count, int *out_count); int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry); -void ksmbd_vfs_smb2_sector_size(struct inode *inode, - struct ksmbd_fs_sector_size *fs_ss); void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, struct ksmbd_kstat *ksmbd_kstat); From 1a93084b9a89818aec0ac7b59a5a51f2112bf203 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 24 Jun 2021 10:34:11 +0900 Subject: [PATCH 145/417] ksmbd: move fs/cifsd to fs/ksmbd Move fs/cifsd to fs/ksmbd and rename the remaining cifsd name to ksmbd. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- Documentation/filesystems/cifs/index.rst | 2 +- .../filesystems/cifs/{cifsd.rst => ksmbd.rst} | 10 +++++----- fs/Kconfig | 2 +- fs/Makefile | 2 +- fs/{cifsd => ksmbd}/Kconfig | 14 +++++++------- fs/{cifsd => ksmbd}/Makefile | 0 fs/{cifsd => ksmbd}/asn1.c | 0 fs/{cifsd => ksmbd}/asn1.h | 0 fs/{cifsd => ksmbd}/auth.c | 0 fs/{cifsd => ksmbd}/auth.h | 0 fs/{cifsd => ksmbd}/connection.c | 0 fs/{cifsd => ksmbd}/connection.h | 0 fs/{cifsd => ksmbd}/crypto_ctx.c | 0 fs/{cifsd => ksmbd}/crypto_ctx.h | 0 fs/{cifsd => ksmbd}/glob.h | 0 fs/{cifsd => ksmbd}/ksmbd_server.h | 0 fs/{cifsd => ksmbd}/ksmbd_spnego_negtokeninit.asn1 | 0 fs/{cifsd => ksmbd}/ksmbd_spnego_negtokentarg.asn1 | 0 fs/{cifsd => ksmbd}/ksmbd_work.c | 0 fs/{cifsd => ksmbd}/ksmbd_work.h | 0 fs/{cifsd => ksmbd}/mgmt/ksmbd_ida.c | 0 fs/{cifsd => ksmbd}/mgmt/ksmbd_ida.h | 0 fs/{cifsd => ksmbd}/mgmt/share_config.c | 0 fs/{cifsd => ksmbd}/mgmt/share_config.h | 0 fs/{cifsd => ksmbd}/mgmt/tree_connect.c | 0 fs/{cifsd => ksmbd}/mgmt/tree_connect.h | 0 fs/{cifsd => ksmbd}/mgmt/user_config.c | 0 fs/{cifsd => ksmbd}/mgmt/user_config.h | 0 fs/{cifsd => ksmbd}/mgmt/user_session.c | 0 fs/{cifsd => ksmbd}/mgmt/user_session.h | 0 fs/{cifsd => ksmbd}/misc.c | 0 fs/{cifsd => ksmbd}/misc.h | 0 fs/{cifsd => ksmbd}/ndr.c | 0 fs/{cifsd => ksmbd}/ndr.h | 0 fs/{cifsd => ksmbd}/nterr.h | 0 fs/{cifsd => ksmbd}/ntlmssp.h | 0 fs/{cifsd => ksmbd}/oplock.c | 0 fs/{cifsd => ksmbd}/oplock.h | 0 fs/{cifsd => ksmbd}/server.c | 0 fs/{cifsd => ksmbd}/server.h | 0 fs/{cifsd => ksmbd}/smb2misc.c | 0 fs/{cifsd => ksmbd}/smb2ops.c | 0 fs/{cifsd => ksmbd}/smb2pdu.c | 0 fs/{cifsd => ksmbd}/smb2pdu.h | 0 fs/{cifsd => ksmbd}/smb_common.c | 0 fs/{cifsd => ksmbd}/smb_common.h | 0 fs/{cifsd => ksmbd}/smbacl.c | 0 fs/{cifsd => ksmbd}/smbacl.h | 0 fs/{cifsd => ksmbd}/smbfsctl.h | 0 fs/{cifsd => ksmbd}/smbstatus.h | 0 fs/{cifsd => ksmbd}/transport_ipc.c | 0 fs/{cifsd => ksmbd}/transport_ipc.h | 0 fs/{cifsd => ksmbd}/transport_rdma.c | 0 fs/{cifsd => ksmbd}/transport_rdma.h | 0 fs/{cifsd => ksmbd}/transport_tcp.c | 0 fs/{cifsd => ksmbd}/transport_tcp.h | 0 fs/{cifsd => ksmbd}/unicode.c | 0 fs/{cifsd => ksmbd}/unicode.h | 0 fs/{cifsd => ksmbd}/uniupr.h | 0 fs/{cifsd => ksmbd}/vfs.c | 0 fs/{cifsd => ksmbd}/vfs.h | 0 fs/{cifsd => ksmbd}/vfs_cache.c | 0 fs/{cifsd => ksmbd}/vfs_cache.h | 0 63 files changed, 15 insertions(+), 15 deletions(-) rename Documentation/filesystems/cifs/{cifsd.rst => ksmbd.rst} (98%) rename fs/{cifsd => ksmbd}/Kconfig (81%) rename fs/{cifsd => ksmbd}/Makefile (100%) rename fs/{cifsd => ksmbd}/asn1.c (100%) rename fs/{cifsd => ksmbd}/asn1.h (100%) rename fs/{cifsd => ksmbd}/auth.c (100%) rename fs/{cifsd => ksmbd}/auth.h (100%) rename fs/{cifsd => ksmbd}/connection.c (100%) rename fs/{cifsd => ksmbd}/connection.h (100%) rename fs/{cifsd => ksmbd}/crypto_ctx.c (100%) rename fs/{cifsd => ksmbd}/crypto_ctx.h (100%) rename fs/{cifsd => ksmbd}/glob.h (100%) rename fs/{cifsd => ksmbd}/ksmbd_server.h (100%) rename fs/{cifsd => ksmbd}/ksmbd_spnego_negtokeninit.asn1 (100%) rename fs/{cifsd => ksmbd}/ksmbd_spnego_negtokentarg.asn1 (100%) rename fs/{cifsd => ksmbd}/ksmbd_work.c (100%) rename fs/{cifsd => ksmbd}/ksmbd_work.h (100%) rename fs/{cifsd => ksmbd}/mgmt/ksmbd_ida.c (100%) rename fs/{cifsd => ksmbd}/mgmt/ksmbd_ida.h (100%) rename fs/{cifsd => ksmbd}/mgmt/share_config.c (100%) rename fs/{cifsd => ksmbd}/mgmt/share_config.h (100%) rename fs/{cifsd => ksmbd}/mgmt/tree_connect.c (100%) rename fs/{cifsd => ksmbd}/mgmt/tree_connect.h (100%) rename fs/{cifsd => ksmbd}/mgmt/user_config.c (100%) rename fs/{cifsd => ksmbd}/mgmt/user_config.h (100%) rename fs/{cifsd => ksmbd}/mgmt/user_session.c (100%) rename fs/{cifsd => ksmbd}/mgmt/user_session.h (100%) rename fs/{cifsd => ksmbd}/misc.c (100%) rename fs/{cifsd => ksmbd}/misc.h (100%) rename fs/{cifsd => ksmbd}/ndr.c (100%) rename fs/{cifsd => ksmbd}/ndr.h (100%) rename fs/{cifsd => ksmbd}/nterr.h (100%) rename fs/{cifsd => ksmbd}/ntlmssp.h (100%) rename fs/{cifsd => ksmbd}/oplock.c (100%) rename fs/{cifsd => ksmbd}/oplock.h (100%) rename fs/{cifsd => ksmbd}/server.c (100%) rename fs/{cifsd => ksmbd}/server.h (100%) rename fs/{cifsd => ksmbd}/smb2misc.c (100%) rename fs/{cifsd => ksmbd}/smb2ops.c (100%) rename fs/{cifsd => ksmbd}/smb2pdu.c (100%) rename fs/{cifsd => ksmbd}/smb2pdu.h (100%) rename fs/{cifsd => ksmbd}/smb_common.c (100%) rename fs/{cifsd => ksmbd}/smb_common.h (100%) rename fs/{cifsd => ksmbd}/smbacl.c (100%) rename fs/{cifsd => ksmbd}/smbacl.h (100%) rename fs/{cifsd => ksmbd}/smbfsctl.h (100%) rename fs/{cifsd => ksmbd}/smbstatus.h (100%) rename fs/{cifsd => ksmbd}/transport_ipc.c (100%) rename fs/{cifsd => ksmbd}/transport_ipc.h (100%) rename fs/{cifsd => ksmbd}/transport_rdma.c (100%) rename fs/{cifsd => ksmbd}/transport_rdma.h (100%) rename fs/{cifsd => ksmbd}/transport_tcp.c (100%) rename fs/{cifsd => ksmbd}/transport_tcp.h (100%) rename fs/{cifsd => ksmbd}/unicode.c (100%) rename fs/{cifsd => ksmbd}/unicode.h (100%) rename fs/{cifsd => ksmbd}/uniupr.h (100%) rename fs/{cifsd => ksmbd}/vfs.c (100%) rename fs/{cifsd => ksmbd}/vfs.h (100%) rename fs/{cifsd => ksmbd}/vfs_cache.c (100%) rename fs/{cifsd => ksmbd}/vfs_cache.h (100%) diff --git a/Documentation/filesystems/cifs/index.rst b/Documentation/filesystems/cifs/index.rst index e762586b5dc7..1c8597a679ab 100644 --- a/Documentation/filesystems/cifs/index.rst +++ b/Documentation/filesystems/cifs/index.rst @@ -6,5 +6,5 @@ CIFS .. toctree:: :maxdepth: 1 - cifsd + ksmbd cifsroot diff --git a/Documentation/filesystems/cifs/cifsd.rst b/Documentation/filesystems/cifs/ksmbd.rst similarity index 98% rename from Documentation/filesystems/cifs/cifsd.rst rename to Documentation/filesystems/cifs/ksmbd.rst index 01a0be272ce6..1e111efecd45 100644 --- a/Documentation/filesystems/cifs/cifsd.rst +++ b/Documentation/filesystems/cifs/ksmbd.rst @@ -1,13 +1,13 @@ .. SPDX-License-Identifier: GPL-2.0 ========================== -CIFSD - SMB3 Kernel Server +KSMBD - SMB3 Kernel Server ========================== -CIFSD is a linux kernel server which implements SMB3 protocol in kernel space +KSMBD is a linux kernel server which implements SMB3 protocol in kernel space for sharing files over network. -CIFSD architecture +KSMBD architecture ================== The subset of performance related operations belong in kernelspace and @@ -60,7 +60,7 @@ NetServerGetInfo. Complete DCE/RPC response is prepared from the user space and passed over to the associated kernel thread for the client. -CIFSD Feature Status +KSMBD Feature Status ==================== ============================== ================================================= @@ -138,7 +138,7 @@ How to run 6. Access share from Windows or Linux using CIFS -Shutdown CIFSD +Shutdown KSMBD ============== 1. kill user and kernel space daemon diff --git a/fs/Kconfig b/fs/Kconfig index 7462761ebd2f..720c38f484c6 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -344,7 +344,7 @@ config NFS_V4_2_SSC_HELPER source "net/sunrpc/Kconfig" source "fs/ceph/Kconfig" source "fs/cifs/Kconfig" -source "fs/cifsd/Kconfig" +source "fs/ksmbd/Kconfig" source "fs/coda/Kconfig" source "fs/afs/Kconfig" source "fs/9p/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 542a77374d12..e03a048b2cd8 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -98,7 +98,7 @@ obj-$(CONFIG_NLS) += nls/ obj-$(CONFIG_UNICODE) += unicode/ obj-$(CONFIG_SYSV_FS) += sysv/ obj-$(CONFIG_CIFS) += cifs/ -obj-$(CONFIG_SMB_SERVER) += cifsd/ +obj-$(CONFIG_SMB_SERVER) += ksmbd/ obj-$(CONFIG_HPFS_FS) += hpfs/ obj-$(CONFIG_NTFS_FS) += ntfs/ obj-$(CONFIG_UFS_FS) += ufs/ diff --git a/fs/cifsd/Kconfig b/fs/ksmbd/Kconfig similarity index 81% rename from fs/cifsd/Kconfig rename to fs/ksmbd/Kconfig index 796f928a7da0..e9a5ac01b6e0 100644 --- a/fs/cifsd/Kconfig +++ b/fs/ksmbd/Kconfig @@ -1,5 +1,5 @@ config SMB_SERVER - tristate "SMB server support (EXPERIMENTAL)" + tristate "SMB3 server support (EXPERIMENTAL)" depends on INET depends on MULTIUSER depends on FILE_LOCKING @@ -31,13 +31,13 @@ config SMB_SERVER case you can choose N here. You also need to install user space programs which can be found - in cifsd-tools, available from - https://github.com/cifsd-team/cifsd-tools. - More detail about how to run the cifsd kernel server is + in ksmbd-tools, available from + https://github.com/cifsd-team/ksmbd-tools. + More detail about how to run the ksmbd kernel server is available via README file - (https://github.com/cifsd-team/cifsd-tools/blob/master/README). + (https://github.com/cifsd-team/ksmbd-tools/blob/master/README). - cifsd kernel server includes support for auto-negotiation, + ksmbd kernel server includes support for auto-negotiation, Secure negotiate, Pre-authentication integrity, oplock/lease, compound requests, multi-credit, packet signing, RDMA(smbdirect), smb3 encryption, copy-offload, secure per-user session @@ -61,7 +61,7 @@ config SMB_SERVER_CHECK_CAP_NET_ADMIN default y help - Prevent unprivileged processes to start the cifsd kernel server. + Prevent unprivileged processes to start the ksmbd kernel server. config SMB_SERVER_KERBEROS5 bool "Support for Kerberos 5" diff --git a/fs/cifsd/Makefile b/fs/ksmbd/Makefile similarity index 100% rename from fs/cifsd/Makefile rename to fs/ksmbd/Makefile diff --git a/fs/cifsd/asn1.c b/fs/ksmbd/asn1.c similarity index 100% rename from fs/cifsd/asn1.c rename to fs/ksmbd/asn1.c diff --git a/fs/cifsd/asn1.h b/fs/ksmbd/asn1.h similarity index 100% rename from fs/cifsd/asn1.h rename to fs/ksmbd/asn1.h diff --git a/fs/cifsd/auth.c b/fs/ksmbd/auth.c similarity index 100% rename from fs/cifsd/auth.c rename to fs/ksmbd/auth.c diff --git a/fs/cifsd/auth.h b/fs/ksmbd/auth.h similarity index 100% rename from fs/cifsd/auth.h rename to fs/ksmbd/auth.h diff --git a/fs/cifsd/connection.c b/fs/ksmbd/connection.c similarity index 100% rename from fs/cifsd/connection.c rename to fs/ksmbd/connection.c diff --git a/fs/cifsd/connection.h b/fs/ksmbd/connection.h similarity index 100% rename from fs/cifsd/connection.h rename to fs/ksmbd/connection.h diff --git a/fs/cifsd/crypto_ctx.c b/fs/ksmbd/crypto_ctx.c similarity index 100% rename from fs/cifsd/crypto_ctx.c rename to fs/ksmbd/crypto_ctx.c diff --git a/fs/cifsd/crypto_ctx.h b/fs/ksmbd/crypto_ctx.h similarity index 100% rename from fs/cifsd/crypto_ctx.h rename to fs/ksmbd/crypto_ctx.h diff --git a/fs/cifsd/glob.h b/fs/ksmbd/glob.h similarity index 100% rename from fs/cifsd/glob.h rename to fs/ksmbd/glob.h diff --git a/fs/cifsd/ksmbd_server.h b/fs/ksmbd/ksmbd_server.h similarity index 100% rename from fs/cifsd/ksmbd_server.h rename to fs/ksmbd/ksmbd_server.h diff --git a/fs/cifsd/ksmbd_spnego_negtokeninit.asn1 b/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 similarity index 100% rename from fs/cifsd/ksmbd_spnego_negtokeninit.asn1 rename to fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 diff --git a/fs/cifsd/ksmbd_spnego_negtokentarg.asn1 b/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 similarity index 100% rename from fs/cifsd/ksmbd_spnego_negtokentarg.asn1 rename to fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 diff --git a/fs/cifsd/ksmbd_work.c b/fs/ksmbd/ksmbd_work.c similarity index 100% rename from fs/cifsd/ksmbd_work.c rename to fs/ksmbd/ksmbd_work.c diff --git a/fs/cifsd/ksmbd_work.h b/fs/ksmbd/ksmbd_work.h similarity index 100% rename from fs/cifsd/ksmbd_work.h rename to fs/ksmbd/ksmbd_work.h diff --git a/fs/cifsd/mgmt/ksmbd_ida.c b/fs/ksmbd/mgmt/ksmbd_ida.c similarity index 100% rename from fs/cifsd/mgmt/ksmbd_ida.c rename to fs/ksmbd/mgmt/ksmbd_ida.c diff --git a/fs/cifsd/mgmt/ksmbd_ida.h b/fs/ksmbd/mgmt/ksmbd_ida.h similarity index 100% rename from fs/cifsd/mgmt/ksmbd_ida.h rename to fs/ksmbd/mgmt/ksmbd_ida.h diff --git a/fs/cifsd/mgmt/share_config.c b/fs/ksmbd/mgmt/share_config.c similarity index 100% rename from fs/cifsd/mgmt/share_config.c rename to fs/ksmbd/mgmt/share_config.c diff --git a/fs/cifsd/mgmt/share_config.h b/fs/ksmbd/mgmt/share_config.h similarity index 100% rename from fs/cifsd/mgmt/share_config.h rename to fs/ksmbd/mgmt/share_config.h diff --git a/fs/cifsd/mgmt/tree_connect.c b/fs/ksmbd/mgmt/tree_connect.c similarity index 100% rename from fs/cifsd/mgmt/tree_connect.c rename to fs/ksmbd/mgmt/tree_connect.c diff --git a/fs/cifsd/mgmt/tree_connect.h b/fs/ksmbd/mgmt/tree_connect.h similarity index 100% rename from fs/cifsd/mgmt/tree_connect.h rename to fs/ksmbd/mgmt/tree_connect.h diff --git a/fs/cifsd/mgmt/user_config.c b/fs/ksmbd/mgmt/user_config.c similarity index 100% rename from fs/cifsd/mgmt/user_config.c rename to fs/ksmbd/mgmt/user_config.c diff --git a/fs/cifsd/mgmt/user_config.h b/fs/ksmbd/mgmt/user_config.h similarity index 100% rename from fs/cifsd/mgmt/user_config.h rename to fs/ksmbd/mgmt/user_config.h diff --git a/fs/cifsd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c similarity index 100% rename from fs/cifsd/mgmt/user_session.c rename to fs/ksmbd/mgmt/user_session.c diff --git a/fs/cifsd/mgmt/user_session.h b/fs/ksmbd/mgmt/user_session.h similarity index 100% rename from fs/cifsd/mgmt/user_session.h rename to fs/ksmbd/mgmt/user_session.h diff --git a/fs/cifsd/misc.c b/fs/ksmbd/misc.c similarity index 100% rename from fs/cifsd/misc.c rename to fs/ksmbd/misc.c diff --git a/fs/cifsd/misc.h b/fs/ksmbd/misc.h similarity index 100% rename from fs/cifsd/misc.h rename to fs/ksmbd/misc.h diff --git a/fs/cifsd/ndr.c b/fs/ksmbd/ndr.c similarity index 100% rename from fs/cifsd/ndr.c rename to fs/ksmbd/ndr.c diff --git a/fs/cifsd/ndr.h b/fs/ksmbd/ndr.h similarity index 100% rename from fs/cifsd/ndr.h rename to fs/ksmbd/ndr.h diff --git a/fs/cifsd/nterr.h b/fs/ksmbd/nterr.h similarity index 100% rename from fs/cifsd/nterr.h rename to fs/ksmbd/nterr.h diff --git a/fs/cifsd/ntlmssp.h b/fs/ksmbd/ntlmssp.h similarity index 100% rename from fs/cifsd/ntlmssp.h rename to fs/ksmbd/ntlmssp.h diff --git a/fs/cifsd/oplock.c b/fs/ksmbd/oplock.c similarity index 100% rename from fs/cifsd/oplock.c rename to fs/ksmbd/oplock.c diff --git a/fs/cifsd/oplock.h b/fs/ksmbd/oplock.h similarity index 100% rename from fs/cifsd/oplock.h rename to fs/ksmbd/oplock.h diff --git a/fs/cifsd/server.c b/fs/ksmbd/server.c similarity index 100% rename from fs/cifsd/server.c rename to fs/ksmbd/server.c diff --git a/fs/cifsd/server.h b/fs/ksmbd/server.h similarity index 100% rename from fs/cifsd/server.h rename to fs/ksmbd/server.h diff --git a/fs/cifsd/smb2misc.c b/fs/ksmbd/smb2misc.c similarity index 100% rename from fs/cifsd/smb2misc.c rename to fs/ksmbd/smb2misc.c diff --git a/fs/cifsd/smb2ops.c b/fs/ksmbd/smb2ops.c similarity index 100% rename from fs/cifsd/smb2ops.c rename to fs/ksmbd/smb2ops.c diff --git a/fs/cifsd/smb2pdu.c b/fs/ksmbd/smb2pdu.c similarity index 100% rename from fs/cifsd/smb2pdu.c rename to fs/ksmbd/smb2pdu.c diff --git a/fs/cifsd/smb2pdu.h b/fs/ksmbd/smb2pdu.h similarity index 100% rename from fs/cifsd/smb2pdu.h rename to fs/ksmbd/smb2pdu.h diff --git a/fs/cifsd/smb_common.c b/fs/ksmbd/smb_common.c similarity index 100% rename from fs/cifsd/smb_common.c rename to fs/ksmbd/smb_common.c diff --git a/fs/cifsd/smb_common.h b/fs/ksmbd/smb_common.h similarity index 100% rename from fs/cifsd/smb_common.h rename to fs/ksmbd/smb_common.h diff --git a/fs/cifsd/smbacl.c b/fs/ksmbd/smbacl.c similarity index 100% rename from fs/cifsd/smbacl.c rename to fs/ksmbd/smbacl.c diff --git a/fs/cifsd/smbacl.h b/fs/ksmbd/smbacl.h similarity index 100% rename from fs/cifsd/smbacl.h rename to fs/ksmbd/smbacl.h diff --git a/fs/cifsd/smbfsctl.h b/fs/ksmbd/smbfsctl.h similarity index 100% rename from fs/cifsd/smbfsctl.h rename to fs/ksmbd/smbfsctl.h diff --git a/fs/cifsd/smbstatus.h b/fs/ksmbd/smbstatus.h similarity index 100% rename from fs/cifsd/smbstatus.h rename to fs/ksmbd/smbstatus.h diff --git a/fs/cifsd/transport_ipc.c b/fs/ksmbd/transport_ipc.c similarity index 100% rename from fs/cifsd/transport_ipc.c rename to fs/ksmbd/transport_ipc.c diff --git a/fs/cifsd/transport_ipc.h b/fs/ksmbd/transport_ipc.h similarity index 100% rename from fs/cifsd/transport_ipc.h rename to fs/ksmbd/transport_ipc.h diff --git a/fs/cifsd/transport_rdma.c b/fs/ksmbd/transport_rdma.c similarity index 100% rename from fs/cifsd/transport_rdma.c rename to fs/ksmbd/transport_rdma.c diff --git a/fs/cifsd/transport_rdma.h b/fs/ksmbd/transport_rdma.h similarity index 100% rename from fs/cifsd/transport_rdma.h rename to fs/ksmbd/transport_rdma.h diff --git a/fs/cifsd/transport_tcp.c b/fs/ksmbd/transport_tcp.c similarity index 100% rename from fs/cifsd/transport_tcp.c rename to fs/ksmbd/transport_tcp.c diff --git a/fs/cifsd/transport_tcp.h b/fs/ksmbd/transport_tcp.h similarity index 100% rename from fs/cifsd/transport_tcp.h rename to fs/ksmbd/transport_tcp.h diff --git a/fs/cifsd/unicode.c b/fs/ksmbd/unicode.c similarity index 100% rename from fs/cifsd/unicode.c rename to fs/ksmbd/unicode.c diff --git a/fs/cifsd/unicode.h b/fs/ksmbd/unicode.h similarity index 100% rename from fs/cifsd/unicode.h rename to fs/ksmbd/unicode.h diff --git a/fs/cifsd/uniupr.h b/fs/ksmbd/uniupr.h similarity index 100% rename from fs/cifsd/uniupr.h rename to fs/ksmbd/uniupr.h diff --git a/fs/cifsd/vfs.c b/fs/ksmbd/vfs.c similarity index 100% rename from fs/cifsd/vfs.c rename to fs/ksmbd/vfs.c diff --git a/fs/cifsd/vfs.h b/fs/ksmbd/vfs.h similarity index 100% rename from fs/cifsd/vfs.h rename to fs/ksmbd/vfs.h diff --git a/fs/cifsd/vfs_cache.c b/fs/ksmbd/vfs_cache.c similarity index 100% rename from fs/cifsd/vfs_cache.c rename to fs/ksmbd/vfs_cache.c diff --git a/fs/cifsd/vfs_cache.h b/fs/ksmbd/vfs_cache.h similarity index 100% rename from fs/cifsd/vfs_cache.h rename to fs/ksmbd/vfs_cache.h From 49be5aa47b69ad09b0045b662d61bad51e6309e7 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 24 Jun 2021 09:20:03 +0900 Subject: [PATCH 146/417] MAINTAINERS: rename cifsd to ksmbd Rename cifsd to ksmbd and update Sergey's mail address. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- MAINTAINERS | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index f23bb5cbfd70..6691ac75fce5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4540,16 +4540,6 @@ T: git git://git.samba.org/sfrench/cifs-2.6.git F: Documentation/admin-guide/cifs/ F: fs/cifs/ -COMMON INTERNET FILE SYSTEM SERVER (CIFSD) -M: Namjae Jeon -M: Sergey Senozhatsky -M: Steve French -M: Hyunchul Lee -L: linux-cifs@vger.kernel.org -L: linux-cifsd-devel@lists.sourceforge.net -S: Maintained -F: fs/cifsd/ - COMPACTPCI HOTPLUG CORE M: Scott Murray L: linux-pci@vger.kernel.org @@ -9948,6 +9938,15 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git F: Documentation/dev-tools/kselftest* F: tools/testing/selftests/ +KERNEL SMB3 SERVER (KSMBD) +M: Namjae Jeon +M: Sergey Senozhatsky +M: Steve French +M: Hyunchul Lee +L: linux-cifs@vger.kernel.org +S: Maintained +F: fs/ksmbd/ + KERNEL UNIT TESTING FRAMEWORK (KUnit) M: Brendan Higgins L: linux-kselftest@vger.kernel.org From 333111a6dc32a2768f581876bdb5ef4231f5084e Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Wed, 23 Jun 2021 11:07:43 +0900 Subject: [PATCH 147/417] ksmbd: factor out a ksmbd_vfs_lock_parent helper Factor out a self-contained helper to get stable parent dentry. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/vfs.c | 125 ++++++++++++++++++++++++------------------------- 1 file changed, 62 insertions(+), 63 deletions(-) diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index fddabb4c7db6..e64eab7a58a8 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -60,6 +60,41 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, i_uid_write(inode, i_uid_read(parent_inode)); } +/** + * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable + * + * the parent dentry got by dget_parent or @parent could be + * unstable, we try to lock a parent inode and lookup the + * child dentry again. + * + * the reference count of @parent isn't incremented. + */ +static int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child) +{ + struct dentry *dentry; + int ret = 0; + + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); + dentry = lookup_one_len(child->d_name.name, parent, + child->d_name.len); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto out_err; + } + + if (dentry != child) { + ret = -ESTALE; + dput(dentry); + goto out_err; + } + + dput(dentry); + return 0; +out_err: + inode_unlock(d_inode(parent)); + return ret; +} + int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete) { int mask, ret = 0; @@ -78,29 +113,18 @@ int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete) return -EACCES; if (delete) { - struct dentry *child, *parent; + struct dentry *parent; parent = dget_parent(dentry); - inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); - child = lookup_one_len(dentry->d_name.name, parent, - dentry->d_name.len); - if (IS_ERR(child)) { - ret = PTR_ERR(child); - goto out_lock; + ret = ksmbd_vfs_lock_parent(parent, dentry); + if (ret) { + dput(parent); + return ret; } - if (child != dentry) { - ret = -ESTALE; - dput(child); - goto out_lock; - } - dput(child); - - if (inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) { + if (inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) ret = -EACCES; - goto out_lock; - } -out_lock: + inode_unlock(d_inode(parent)); dput(parent); } @@ -109,7 +133,7 @@ out_lock: int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) { - struct dentry *parent, *child; + struct dentry *parent; int ret = 0; *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); @@ -127,25 +151,15 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) *daccess |= FILE_EXECUTE_LE; parent = dget_parent(dentry); - inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); - child = lookup_one_len(dentry->d_name.name, parent, - dentry->d_name.len); - if (IS_ERR(child)) { - ret = PTR_ERR(child); - goto out_lock; + ret = ksmbd_vfs_lock_parent(parent, dentry); + if (ret) { + dput(parent); + return ret; } - if (child != dentry) { - ret = -ESTALE; - dput(child); - goto out_lock; - } - dput(child); - if (!inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) *daccess |= FILE_DELETE_LE; -out_lock: inode_unlock(d_inode(parent)); dput(parent); return ret; @@ -573,7 +587,7 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) { struct path path; - struct dentry *dentry, *parent; + struct dentry *parent; int err; int flags = 0; @@ -592,35 +606,32 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) } parent = dget_parent(path.dentry); - inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); - dentry = lookup_one_len(path.dentry->d_name.name, parent, - strlen(path.dentry->d_name.name)); - if (IS_ERR(dentry)) { - err = PTR_ERR(dentry); - ksmbd_debug(VFS, "%s: lookup failed, err %d\n", - path.dentry->d_name.name, err); - goto out_err; + err = ksmbd_vfs_lock_parent(parent, path.dentry); + if (err) { + dput(parent); + path_put(&path); + ksmbd_revert_fsids(work); + return err; } - if (!d_inode(dentry) || !d_inode(dentry)->i_nlink) { - dput(dentry); + if (!d_inode(path.dentry)->i_nlink) { err = -ENOENT; goto out_err; } - if (S_ISDIR(d_inode(dentry)->i_mode)) { - err = vfs_rmdir(&init_user_ns, d_inode(parent), dentry); + if (S_ISDIR(d_inode(path.dentry)->i_mode)) { + err = vfs_rmdir(&init_user_ns, d_inode(parent), path.dentry); if (err && err != -ENOTEMPTY) ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, err); } else { - err = vfs_unlink(&init_user_ns, d_inode(parent), dentry, NULL); + err = vfs_unlink(&init_user_ns, d_inode(parent), path.dentry, + NULL); if (err) ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, err); } - dput(dentry); out_err: inode_unlock(d_inode(parent)); dput(parent); @@ -1086,30 +1097,18 @@ int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name) int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry) { - struct dentry *child; int err = 0; - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + err = ksmbd_vfs_lock_parent(dir, dentry); + if (err) + return err; dget(dentry); - child = lookup_one_len(dentry->d_name.name, dir, dentry->d_name.len); - if (IS_ERR(child)) { - err = PTR_ERR(child); - goto out; - } - - if (child != dentry) { - err = -ESTALE; - dput(child); - goto out; - } - dput(child); if (S_ISDIR(d_inode(dentry)->i_mode)) err = vfs_rmdir(&init_user_ns, d_inode(dir), dentry); else err = vfs_unlink(&init_user_ns, d_inode(dir), dentry, NULL); -out: dput(dentry); inode_unlock(d_inode(dir)); if (err) From 6c5e36d13e2a338ed611d2bcc6c615dd0550b17d Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Wed, 23 Jun 2021 13:48:24 +0900 Subject: [PATCH 148/417] ksmbd: set MAY_* flags together with open flags set MAY_* flags together with open flags and remove ksmbd_vfs_inode_permission(). Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 38 ++++++++++++++++++++++++-------------- fs/ksmbd/vfs.c | 42 +++++++++++++----------------------------- fs/ksmbd/vfs.h | 3 +-- 3 files changed, 38 insertions(+), 45 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 1327ae806b17..25715d57c2bb 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -1836,21 +1836,27 @@ out_err1: * @file_present: is file already present * @access: file access flags * @disposition: file disposition flags + * @may_flags: set with MAY_ flags * * Return: file open flags */ static int smb2_create_open_flags(bool file_present, __le32 access, - __le32 disposition) + __le32 disposition, + int *may_flags) { int oflags = O_NONBLOCK | O_LARGEFILE; if (access & FILE_READ_DESIRED_ACCESS_LE && - access & FILE_WRITE_DESIRE_ACCESS_LE) + access & FILE_WRITE_DESIRE_ACCESS_LE) { oflags |= O_RDWR; - else if (access & FILE_WRITE_DESIRE_ACCESS_LE) + *may_flags = MAY_OPEN | MAY_READ | MAY_WRITE; + } else if (access & FILE_WRITE_DESIRE_ACCESS_LE) { oflags |= O_WRONLY; - else + *may_flags = MAY_OPEN | MAY_WRITE; + } else { oflags |= O_RDONLY; + *may_flags = MAY_OPEN | MAY_READ; + } if (access == FILE_READ_ATTRIBUTES_LE) oflags |= O_PATH; @@ -1884,6 +1890,7 @@ static int smb2_create_open_flags(bool file_present, __le32 access, break; } } + return oflags; } @@ -2355,7 +2362,7 @@ int smb2_open(struct ksmbd_work *work) struct create_ea_buf_req *ea_buf = NULL; struct oplock_info *opinfo; __le32 *next_ptr = NULL; - int req_op_level = 0, open_flags = 0, file_info = 0; + int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; int rc = 0, len = 0; int contxt_cnt = 0, query_disk_id = 0; int maximal_access_ctxt = 0, posix_ctxt = 0; @@ -2696,7 +2703,8 @@ int smb2_open(struct ksmbd_work *work) } open_flags = smb2_create_open_flags(file_present, daccess, - req->CreateDisposition); + req->CreateDisposition, + &may_flags); if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { if (open_flags & O_CREAT) { @@ -2723,21 +2731,23 @@ int smb2_open(struct ksmbd_work *work) goto err_out; } } else if (!already_permitted) { - bool may_delete; - - may_delete = daccess & FILE_DELETE_LE || - req->CreateOptions & FILE_DELETE_ON_CLOSE_LE; - /* FILE_READ_ATTRIBUTE is allowed without inode_permission, * because execute(search) permission on a parent directory, * is already granted. */ if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { - rc = ksmbd_vfs_inode_permission(path.dentry, - open_flags & O_ACCMODE, - may_delete); + rc = inode_permission(&init_user_ns, + d_inode(path.dentry), + may_flags); if (rc) goto err_out; + + if ((daccess & FILE_DELETE_LE) || + (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + rc = ksmbd_vfs_may_delete(path.dentry); + if (rc) + goto err_out; + } } } diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index e64eab7a58a8..6181a58e8a33 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -95,39 +95,23 @@ out_err: return ret; } -int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete) +int ksmbd_vfs_may_delete(struct dentry *dentry) { - int mask, ret = 0; + struct dentry *parent; + int ret; - mask = 0; - acc_mode &= O_ACCMODE; - - if (acc_mode == O_RDONLY) - mask = MAY_READ; - else if (acc_mode == O_WRONLY) - mask = MAY_WRITE; - else if (acc_mode == O_RDWR) - mask = MAY_READ | MAY_WRITE; - - if (inode_permission(&init_user_ns, d_inode(dentry), mask | MAY_OPEN)) - return -EACCES; - - if (delete) { - struct dentry *parent; - - parent = dget_parent(dentry); - ret = ksmbd_vfs_lock_parent(parent, dentry); - if (ret) { - dput(parent); - return ret; - } - - if (inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) - ret = -EACCES; - - inode_unlock(d_inode(parent)); + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(parent, dentry); + if (ret) { dput(parent); + return ret; } + + ret = inode_permission(&init_user_ns, d_inode(parent), + MAY_EXEC | MAY_WRITE); + + inode_unlock(d_inode(parent)); + dput(parent); return ret; } diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h index 49f0558ace32..ae8eff1f0315 100644 --- a/fs/ksmbd/vfs.h +++ b/fs/ksmbd/vfs.h @@ -192,8 +192,7 @@ struct ksmbd_kstat { __le32 file_attributes; }; -int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, - bool delete); +int ksmbd_vfs_may_delete(struct dentry *dentry); int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess); int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); From b622948789a96a8f347c8e77e18d100c7f1a78fa Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 25 Jun 2021 07:02:06 +0900 Subject: [PATCH 149/417] ksmbd: remove macros in transport_ipc.c Remove macros in transport_ipc.c Reviewed-by: Christoph Hellwig Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/transport_ipc.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/fs/ksmbd/transport_ipc.c b/fs/ksmbd/transport_ipc.c index 13eacfda64ac..ca5099118fdf 100644 --- a/fs/ksmbd/transport_ipc.c +++ b/fs/ksmbd/transport_ipc.c @@ -38,8 +38,6 @@ static DEFINE_IDA(ipc_ida); static unsigned int ksmbd_tools_pid; -#define KSMBD_IPC_MSG_HANDLE(m) (*(unsigned int *)m) - static bool ksmbd_ipc_validate_version(struct genl_info *m) { if (m->genlhdr->version != KSMBD_GENL_VERSION) { @@ -56,12 +54,9 @@ static bool ksmbd_ipc_validate_version(struct genl_info *m) struct ksmbd_ipc_msg { unsigned int type; unsigned int sz; - unsigned char ____payload[0]; + unsigned char payload[]; }; -#define KSMBD_IPC_MSG_PAYLOAD(m) \ - ((void *)(((struct ksmbd_ipc_msg *)(m))->____payload)) - struct ipc_msg_table_entry { unsigned int handle; unsigned int type; @@ -251,7 +246,7 @@ static void ipc_msg_handle_free(int handle) static int handle_response(int type, void *payload, size_t sz) { - int handle = KSMBD_IPC_MSG_HANDLE(payload); + unsigned int handle = *(unsigned int *)payload; struct ipc_msg_table_entry *entry; int ret = 0; @@ -432,7 +427,7 @@ static int ipc_msg_send(struct ksmbd_ipc_msg *msg) if (!nlh) goto out; - ret = nla_put(skb, msg->type, msg->sz, KSMBD_IPC_MSG_PAYLOAD(msg)); + ret = nla_put(skb, msg->type, msg->sz, msg->payload); if (ret) { genlmsg_cancel(skb, nlh); goto out; @@ -509,7 +504,7 @@ struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account) return NULL; msg->type = KSMBD_EVENT_LOGIN_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_login_request *)msg->payload; req->handle = ksmbd_acquire_id(&ipc_ida); strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); @@ -532,7 +527,7 @@ ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) return NULL; msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_spnego_authen_request *)msg->payload; req->handle = ksmbd_acquire_id(&ipc_ida); req->spnego_blob_len = blob_len; memcpy(req->spnego_blob, spnego_blob, blob_len); @@ -564,7 +559,7 @@ ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, return NULL; msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_tree_connect_request *)msg->payload; req->handle = ksmbd_acquire_id(&ipc_ida); req->account_flags = sess->user->flags; @@ -597,7 +592,7 @@ int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, return -ENOMEM; msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_tree_disconnect_request *)msg->payload; req->session_id = session_id; req->connect_id = connect_id; @@ -620,7 +615,7 @@ int ksmbd_ipc_logout_request(const char *account) return -ENOMEM; msg->type = KSMBD_EVENT_LOGOUT_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_logout_request *)msg->payload; strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); ret = ipc_msg_send(msg); @@ -643,7 +638,7 @@ ksmbd_ipc_share_config_request(const char *name) return NULL; msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_share_config_request *)msg->payload; req->handle = ksmbd_acquire_id(&ipc_ida); strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME); @@ -664,7 +659,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle) return NULL; msg->type = KSMBD_EVENT_RPC_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_rpc_command *)msg->payload; req->handle = handle; req->flags = ksmbd_session_rpc_method(sess, handle); req->flags |= KSMBD_RPC_OPEN_METHOD; @@ -686,7 +681,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle return NULL; msg->type = KSMBD_EVENT_RPC_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_rpc_command *)msg->payload; req->handle = handle; req->flags = ksmbd_session_rpc_method(sess, handle); req->flags |= KSMBD_RPC_CLOSE_METHOD; @@ -709,7 +704,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle return NULL; msg->type = KSMBD_EVENT_RPC_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_rpc_command *)msg->payload; req->handle = handle; req->flags = ksmbd_session_rpc_method(sess, handle); req->flags |= rpc_context_flags(sess); @@ -733,7 +728,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) return NULL; msg->type = KSMBD_EVENT_RPC_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_rpc_command *)msg->payload; req->handle = handle; req->flags = ksmbd_session_rpc_method(sess, handle); req->flags |= rpc_context_flags(sess); @@ -757,7 +752,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle return NULL; msg->type = KSMBD_EVENT_RPC_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_rpc_command *)req->payload; req->handle = handle; req->flags = ksmbd_session_rpc_method(sess, handle); req->flags |= rpc_context_flags(sess); @@ -782,7 +777,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payloa return NULL; msg->type = KSMBD_EVENT_RPC_REQUEST; - req = KSMBD_IPC_MSG_PAYLOAD(msg); + req = (struct ksmbd_rpc_command *)req->payload; req->handle = ksmbd_acquire_id(&ipc_ida); req->flags = rpc_context_flags(sess); req->flags |= KSMBD_RPC_RAP_METHOD; From 8ad8dc34211742c816d45dd2ce62aa103a82f4c2 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 25 Jun 2021 07:02:07 +0900 Subject: [PATCH 150/417] ksmbd: replace BUFFER_NR_PAGES with inline function Replace BUFFER_NR_PAGES with inline function Reviewed-by: Christoph Hellwig Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/transport_rdma.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c index bd7a090d5350..b3af474d4cad 100644 --- a/fs/ksmbd/transport_rdma.c +++ b/fs/ksmbd/transport_rdma.c @@ -204,9 +204,11 @@ struct smb_direct_rdma_rw_msg { struct scatterlist sg_list[0]; }; -#define BUFFER_NR_PAGES(buf, len) \ - (DIV_ROUND_UP((unsigned long)(buf) + (len), PAGE_SIZE) \ - - (unsigned long)(buf) / PAGE_SIZE) +static inline int get_buf_page_count(void *buf, int size) +{ + return DIV_ROUND_UP((uintptr_t)buf + size, PAGE_SIZE) - + (uintptr_t)buf / PAGE_SIZE; +} static void smb_direct_destroy_pools(struct smb_direct_transport *transport); static void smb_direct_post_recv_credits(struct work_struct *work); @@ -1048,7 +1050,7 @@ static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nen int offset, len; int i = 0; - if (nentries < BUFFER_NR_PAGES(buf, size)) + if (nentries < get_buf_page_count(buf, size)) return -EINVAL; offset = offset_in_page(buf); @@ -1338,7 +1340,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, msg->sgt.sgl = &msg->sg_list[0]; ret = sg_alloc_table_chained(&msg->sgt, - BUFFER_NR_PAGES(buf, buf_len), + get_buf_page_count(buf, buf_len), msg->sg_list, SG_CHUNK_SIZE); if (ret) { atomic_inc(&t->rw_avail_ops); @@ -1353,7 +1355,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, } ret = rdma_rw_ctx_init(&msg->rw_ctx, t->qp, t->qp->port, - msg->sg_list, BUFFER_NR_PAGES(buf, buf_len), + msg->sg_list, get_buf_page_count(buf, buf_len), 0, remote_offset, remote_key, is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); if (ret < 0) { From c2220322b4577fc32ad3b7b4ddb856bd1f8c7461 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 25 Jun 2021 07:02:08 +0900 Subject: [PATCH 151/417] ksmbd: replace KSMBD_ALIGN with kernel ALIGN macro Replace KSMBD_ALIGN with kernel ALIGN macro Reviewed-by: Christoph Hellwig Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/ndr.c | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/fs/ksmbd/ndr.c b/fs/ksmbd/ndr.c index 46cc01475d38..db2ec07e076e 100644 --- a/fs/ksmbd/ndr.c +++ b/fs/ksmbd/ndr.c @@ -11,21 +11,6 @@ #define PAYLOAD_HEAD(d) ((d)->data + (d)->offset) -#define KSMBD_ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) - -#define KSMBD_ALIGN(x, a) \ - ({ \ - typeof(x) ret = (x); \ - if (((x) & ((typeof(x))(a) - 1)) != 0) \ - ret = KSMBD_ALIGN_MASK(x, (typeof(x))(a) - 1); \ - ret; \ - }) - -static void align_offset(struct ndr *ndr, int n) -{ - ndr->offset = KSMBD_ALIGN(ndr->offset, n); -} - static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) { char *data; @@ -85,7 +70,7 @@ static int ndr_write_string(struct ndr *n, void *value, size_t sz) strncpy(PAYLOAD_HEAD(n), value, sz); sz++; n->offset += sz; - align_offset(n, 2); + n->offset = ALIGN(n->offset, 2); return 0; } @@ -96,7 +81,7 @@ static int ndr_read_string(struct ndr *n, void *value, size_t sz) memcpy(value, PAYLOAD_HEAD(n), len); len++; n->offset += len; - align_offset(n, 2); + n->offset = ALIGN(n->offset, 2); return 0; } @@ -210,20 +195,20 @@ static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) int i; ndr_write_int32(n, acl->count); - align_offset(n, 8); + n->offset = ALIGN(n->offset, 8); ndr_write_int32(n, acl->count); ndr_write_int32(n, 0); for (i = 0; i < acl->count; i++) { - align_offset(n, 8); + n->offset = ALIGN(n->offset, 8); ndr_write_int16(n, acl->entries[i].type); ndr_write_int16(n, acl->entries[i].type); if (acl->entries[i].type == SMB_ACL_USER) { - align_offset(n, 8); + n->offset = ALIGN(n->offset, 8); ndr_write_int64(n, acl->entries[i].uid); } else if (acl->entries[i].type == SMB_ACL_GROUP) { - align_offset(n, 8); + n->offset = ALIGN(n->offset, 8); ndr_write_int64(n, acl->entries[i].gid); } From cb5b047f8e14e91774f68625dafb130fb160b4eb Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 25 Jun 2021 07:02:09 +0900 Subject: [PATCH 152/417] ksmbd: replace PAYLOAD_HEAD with inline function Replace PAYLOAD_HEAD with inline function. Reviewed-by: Christoph Hellwig Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/ndr.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/fs/ksmbd/ndr.c b/fs/ksmbd/ndr.c index db2ec07e076e..bcf13a2aa9d4 100644 --- a/fs/ksmbd/ndr.c +++ b/fs/ksmbd/ndr.c @@ -9,7 +9,10 @@ #include "glob.h" #include "ndr.h" -#define PAYLOAD_HEAD(d) ((d)->data + (d)->offset) +static inline char *ndr_get_field(struct ndr *n) +{ + return n->data + n->offset; +} static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) { @@ -30,7 +33,7 @@ static void ndr_write_int16(struct ndr *n, __u16 value) if (n->length <= n->offset + sizeof(value)) try_to_realloc_ndr_blob(n, sizeof(value)); - *(__le16 *)PAYLOAD_HEAD(n) = cpu_to_le16(value); + *(__le16 *)ndr_get_field(n) = cpu_to_le16(value); n->offset += sizeof(value); } @@ -39,7 +42,7 @@ static void ndr_write_int32(struct ndr *n, __u32 value) if (n->length <= n->offset + sizeof(value)) try_to_realloc_ndr_blob(n, sizeof(value)); - *(__le32 *)PAYLOAD_HEAD(n) = cpu_to_le32(value); + *(__le32 *)ndr_get_field(n) = cpu_to_le32(value); n->offset += sizeof(value); } @@ -48,7 +51,7 @@ static void ndr_write_int64(struct ndr *n, __u64 value) if (n->length <= n->offset + sizeof(value)) try_to_realloc_ndr_blob(n, sizeof(value)); - *(__le64 *)PAYLOAD_HEAD(n) = cpu_to_le64(value); + *(__le64 *)ndr_get_field(n) = cpu_to_le64(value); n->offset += sizeof(value); } @@ -57,7 +60,7 @@ static int ndr_write_bytes(struct ndr *n, void *value, size_t sz) if (n->length <= n->offset + sz) try_to_realloc_ndr_blob(n, sz); - memcpy(PAYLOAD_HEAD(n), value, sz); + memcpy(ndr_get_field(n), value, sz); n->offset += sz; return 0; } @@ -67,7 +70,7 @@ static int ndr_write_string(struct ndr *n, void *value, size_t sz) if (n->length <= n->offset + sz) try_to_realloc_ndr_blob(n, sz); - strncpy(PAYLOAD_HEAD(n), value, sz); + strncpy(ndr_get_field(n), value, sz); sz++; n->offset += sz; n->offset = ALIGN(n->offset, 2); @@ -76,9 +79,9 @@ static int ndr_write_string(struct ndr *n, void *value, size_t sz) static int ndr_read_string(struct ndr *n, void *value, size_t sz) { - int len = strnlen(PAYLOAD_HEAD(n), sz); + int len = strnlen(ndr_get_field(n), sz); - memcpy(value, PAYLOAD_HEAD(n), len); + memcpy(value, ndr_get_field(n), len); len++; n->offset += len; n->offset = ALIGN(n->offset, 2); @@ -87,7 +90,7 @@ static int ndr_read_string(struct ndr *n, void *value, size_t sz) static int ndr_read_bytes(struct ndr *n, void *value, size_t sz) { - memcpy(value, PAYLOAD_HEAD(n), sz); + memcpy(value, ndr_get_field(n), sz); n->offset += sz; return 0; } @@ -96,7 +99,7 @@ static __u16 ndr_read_int16(struct ndr *n) { __u16 ret; - ret = le16_to_cpu(*(__le16 *)PAYLOAD_HEAD(n)); + ret = le16_to_cpu(*(__le16 *)ndr_get_field(n)); n->offset += sizeof(__u16); return ret; } @@ -105,7 +108,7 @@ static __u32 ndr_read_int32(struct ndr *n) { __u32 ret; - ret = le32_to_cpu(*(__le32 *)PAYLOAD_HEAD(n)); + ret = le32_to_cpu(*(__le32 *)ndr_get_field(n)); n->offset += sizeof(__u32); return ret; } @@ -114,7 +117,7 @@ static __u64 ndr_read_int64(struct ndr *n) { __u64 ret; - ret = le64_to_cpu(*(__le64 *)PAYLOAD_HEAD(n)); + ret = le64_to_cpu(*(__le64 *)ndr_get_field(n)); n->offset += sizeof(__u64); return ret; } From d4075abbc6b571e9d03d7a742e53fd6085223649 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 25 Jun 2021 07:02:10 +0900 Subject: [PATCH 153/417] ksmbd: remove getting worker state macros Remove getting worker state macros Reviewed-by: Christoph Hellwig Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/ksmbd_work.h | 4 ---- fs/ksmbd/smb2pdu.c | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/ksmbd/ksmbd_work.h b/fs/ksmbd/ksmbd_work.h index 0e2d4f3fc49f..a91abd438a85 100644 --- a/fs/ksmbd/ksmbd_work.h +++ b/fs/ksmbd/ksmbd_work.h @@ -86,10 +86,6 @@ struct ksmbd_work { struct list_head interim_entry; }; -#define WORK_CANCELLED(w) ((w)->state == KSMBD_WORK_CANCELLED) -#define WORK_CLOSED(w) ((w)->state == KSMBD_WORK_CLOSED) -#define WORK_ACTIVE(w) ((w)->state == KSMBD_WORK_ACTIVE) - #define RESPONSE_BUF_NEXT(w) \ (((w)->response_buf + (w)->next_smb2_rsp_hdr_off)) #define REQUEST_BUF_NEXT(w) \ diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 25715d57c2bb..38a36390b64d 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -6716,12 +6716,12 @@ skip: err = ksmbd_vfs_posix_lock_wait(flock); - if (!WORK_ACTIVE(work)) { + if (work->state != KSMBD_WORK_ACTIVE) { list_del(&smb_lock->llist); list_del(&smb_lock->glist); locks_free_lock(flock); - if (WORK_CANCELLED(work)) { + if (work->state == KSMBD_WORK_CANCELLED) { spin_lock(&fp->f_lock); list_del(&work->fp_entry); spin_unlock(&fp->f_lock); From d8fb29980cb5369c4ea520c0b4e1a8893e88f14c Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 25 Jun 2021 11:53:26 +0900 Subject: [PATCH 154/417] ksmbd: remove and replace macros with inline functions in smb_common.h Remove and replace macros with inline functions in smb_common.h Reviewed-by: Christoph Hellwig Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/mgmt/user_session.c | 8 +++----- fs/ksmbd/oplock.c | 6 ++++-- fs/ksmbd/smb2pdu.c | 5 +++-- fs/ksmbd/smb_common.h | 12 ++++++------ 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c index c5ba9694e1f1..8d8ffd8c6f19 100644 --- a/fs/ksmbd/mgmt/user_session.c +++ b/fs/ksmbd/mgmt/user_session.c @@ -154,11 +154,9 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) list_del(&sess->sessions_entry); - if (IS_SMB2(sess->conn)) { - down_write(&sessions_table_lock); - hash_del(&sess->hlist); - up_write(&sessions_table_lock); - } + down_write(&sessions_table_lock); + hash_del(&sess->hlist); + up_write(&sessions_table_lock); if (sess->user) ksmbd_free_user(sess->user); diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index 9027cb7d970f..71e15a591582 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -631,7 +631,8 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) rsp_hdr = work->response_buf; memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->smb2_buf_length = cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn)); + rsp_hdr->smb2_buf_length = + cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->CreditRequest = cpu_to_le16(0); @@ -737,7 +738,8 @@ static void __smb2_lease_break_noti(struct work_struct *wk) rsp_hdr = work->response_buf; memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->smb2_buf_length = cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn)); + rsp_hdr->smb2_buf_length = + cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->CreditRequest = cpu_to_le16(0); diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 38a36390b64d..ece03135127c 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -243,7 +243,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); rsp_hdr->smb2_buf_length = - cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn)); + cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; @@ -497,7 +497,8 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) struct ksmbd_conn *conn = work->conn; memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->smb2_buf_length = cpu_to_be32(HEADER_SIZE_NO_BUF_LEN(conn)); + rsp_hdr->smb2_buf_length = + cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->Command = rcv_hdr->Command; diff --git a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h index 084166ba7654..8489b92229fa 100644 --- a/fs/ksmbd/smb_common.h +++ b/fs/ksmbd/smb_common.h @@ -50,12 +50,6 @@ extern struct list_head global_lock_list; -#define IS_SMB2(x) ((x)->vals->protocol_id != SMB10_PROT_ID) - -#define HEADER_SIZE(conn) ((conn)->vals->header_size) -#define HEADER_SIZE_NO_BUF_LEN(conn) ((conn)->vals->header_size - 4) -#define MAX_HEADER_SIZE(conn) ((conn)->vals->max_header_size) - /* RFC 1002 session packet types */ #define RFC1002_SESSION_MESSAGE 0x00 #define RFC1002_SESSION_REQUEST 0x81 @@ -490,6 +484,12 @@ struct smb_version_cmds { int (*proc)(struct ksmbd_work *swork); }; +static inline size_t +smb2_hdr_size_no_buflen(struct smb_version_values *vals) +{ + return vals->header_size - 4; +} + int ksmbd_min_protocol(void); int ksmbd_max_protocol(void); From 02d4b4aa6d3b135b00f20da9d623d2bbae63768f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 25 Jun 2021 13:43:01 +0900 Subject: [PATCH 155/417] ksmbd: replace SMB_DIRECT_TRANS macro with inline function replace SMB_DIRECT_TRANS macro with inline function. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/transport_rdma.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c index b3af474d4cad..171fb3dd018a 100644 --- a/fs/ksmbd/transport_rdma.c +++ b/fs/ksmbd/transport_rdma.c @@ -158,8 +158,6 @@ struct smb_direct_transport { }; #define KSMBD_TRANS(t) ((struct ksmbd_transport *)&((t)->transport)) -#define SMB_DIRECT_TRANS(t) ((struct smb_direct_transport *)container_of(t, \ - struct smb_direct_transport, transport)) enum { SMB_DIRECT_MSG_NEGOTIATE_REQ = 0, @@ -217,6 +215,12 @@ static int smb_direct_post_send_data(struct smb_direct_transport *t, struct kvec *iov, int niov, int remaining_data_length); +static inline struct smb_direct_transport * +smb_trans_direct_transfort(struct ksmbd_transport *t) +{ + return container_of(t, struct smb_direct_transport, transport); +} + static inline void *smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg) { @@ -643,7 +647,7 @@ static int smb_direct_read(struct ksmbd_transport *t, char *buf, int to_copy, to_read, data_read, offset; u32 data_length, remaining_data_length, data_offset; int rc; - struct smb_direct_transport *st = SMB_DIRECT_TRANS(t); + struct smb_direct_transport *st = smb_trans_direct_transfort(t); again: if (st->status != SMB_DIRECT_CS_CONNECTED) { @@ -1194,7 +1198,7 @@ static int smb_direct_writev(struct ksmbd_transport *t, struct kvec *iov, int niovs, int buflen, bool need_invalidate, unsigned int remote_key) { - struct smb_direct_transport *st = SMB_DIRECT_TRANS(t); + struct smb_direct_transport *st = smb_trans_direct_transfort(t); int remaining_data_length; int start, i, j; int max_iov_size = st->max_send_size - @@ -1393,7 +1397,7 @@ static int smb_direct_rdma_write(struct ksmbd_transport *t, void *buf, unsigned int buflen, u32 remote_key, u64 remote_offset, u32 remote_len) { - return smb_direct_rdma_xmit(SMB_DIRECT_TRANS(t), buf, buflen, + return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, remote_key, remote_offset, remote_len, false); } @@ -1402,14 +1406,14 @@ static int smb_direct_rdma_read(struct ksmbd_transport *t, void *buf, unsigned int buflen, u32 remote_key, u64 remote_offset, u32 remote_len) { - return smb_direct_rdma_xmit(SMB_DIRECT_TRANS(t), buf, buflen, + return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, remote_key, remote_offset, remote_len, true); } static void smb_direct_disconnect(struct ksmbd_transport *t) { - struct smb_direct_transport *st = SMB_DIRECT_TRANS(t); + struct smb_direct_transport *st = smb_trans_direct_transfort(t); ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", st->cm_id); @@ -1857,7 +1861,7 @@ err: static int smb_direct_prepare(struct ksmbd_transport *t) { - struct smb_direct_transport *st = SMB_DIRECT_TRANS(t); + struct smb_direct_transport *st = smb_trans_direct_transfort(t); int ret; struct ib_qp_cap qp_cap; From 8a893315dc06158ce33d1a3292e07170ce2fcd64 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 25 Jun 2021 13:43:37 +0900 Subject: [PATCH 156/417] ksmbd: replace request and respone buffer macro with inline functions replace request and respone buffer macro with inline functions. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/ksmbd_work.h | 21 ++++++++++++++++---- fs/ksmbd/smb2misc.c | 2 +- fs/ksmbd/smb2pdu.c | 46 +++++++++++++++++++++---------------------- 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/fs/ksmbd/ksmbd_work.h b/fs/ksmbd/ksmbd_work.h index a91abd438a85..c655bf371ce5 100644 --- a/fs/ksmbd/ksmbd_work.h +++ b/fs/ksmbd/ksmbd_work.h @@ -86,10 +86,23 @@ struct ksmbd_work { struct list_head interim_entry; }; -#define RESPONSE_BUF_NEXT(w) \ - (((w)->response_buf + (w)->next_smb2_rsp_hdr_off)) -#define REQUEST_BUF_NEXT(w) \ - (((w)->request_buf + (w)->next_smb2_rcv_hdr_off)) +/** + * ksmbd_resp_buf_next - Get next buffer on compound response. + * @work: smb work containing response buffer + */ +static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) +{ + return work->response_buf + work->next_smb2_rsp_hdr_off; +} + +/** + * ksmbd_req_buf_next - Get next buffer on compound request. + * @work: smb work containing response buffer + */ +static inline void *ksmbd_req_buf_next(struct ksmbd_work *work) +{ + return work->request_buf + work->next_smb2_rcv_hdr_off; +} struct ksmbd_work *ksmbd_alloc_work_struct(void); void ksmbd_free_work_struct(struct ksmbd_work *work); diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index e412d69690ed..730d68032c46 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -341,7 +341,7 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) __u32 len = get_rfc1002_len(pdu); if (work->next_smb2_rcv_hdr_off) { - pdu = REQUEST_BUF_NEXT(work); + pdu = ksmbd_req_buf_next(work); hdr = &pdu->hdr; } diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index ece03135127c..42fc3bd2d464 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -40,8 +40,8 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) { if (work->next_smb2_rcv_hdr_off) { - *req = REQUEST_BUF_NEXT(work); - *rsp = RESPONSE_BUF_NEXT(work); + *req = ksmbd_req_buf_next(work); + *rsp = ksmbd_resp_buf_next(work); } else { *req = work->request_buf; *rsp = work->response_buf; @@ -126,7 +126,7 @@ void smb2_set_err_rsp(struct ksmbd_work *work) struct smb2_err_rsp *err_rsp; if (work->next_smb2_rcv_hdr_off) - err_rsp = RESPONSE_BUF_NEXT(work); + err_rsp = ksmbd_resp_buf_next(work); else err_rsp = work->response_buf; @@ -196,7 +196,7 @@ u16 get_smb2_cmd_val(struct ksmbd_work *work) struct smb2_hdr *rcv_hdr; if (work->next_smb2_rcv_hdr_off) - rcv_hdr = REQUEST_BUF_NEXT(work); + rcv_hdr = ksmbd_req_buf_next(work); else rcv_hdr = work->request_buf; return le16_to_cpu(rcv_hdr->Command); @@ -212,7 +212,7 @@ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) struct smb2_hdr *rsp_hdr; if (work->next_smb2_rcv_hdr_off) - rsp_hdr = RESPONSE_BUF_NEXT(work); + rsp_hdr = ksmbd_resp_buf_next(work); else rsp_hdr = work->response_buf; rsp_hdr->Status = err; @@ -315,8 +315,8 @@ static int smb2_consume_credit_charge(struct ksmbd_work *work, */ int smb2_set_rsp_credits(struct ksmbd_work *work) { - struct smb2_hdr *req_hdr = REQUEST_BUF_NEXT(work); - struct smb2_hdr *hdr = RESPONSE_BUF_NEXT(work); + struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); + struct smb2_hdr *hdr = ksmbd_resp_buf_next(work); struct ksmbd_conn *conn = work->conn; unsigned short credits_requested = le16_to_cpu(req_hdr->CreditRequest); unsigned short credit_charge = 1, credits_granted = 0; @@ -383,8 +383,8 @@ out: */ static void init_chained_smb2_rsp(struct ksmbd_work *work) { - struct smb2_hdr *req = REQUEST_BUF_NEXT(work); - struct smb2_hdr *rsp = RESPONSE_BUF_NEXT(work); + struct smb2_hdr *req = ksmbd_req_buf_next(work); + struct smb2_hdr *rsp = ksmbd_resp_buf_next(work); struct smb2_hdr *rsp_hdr; struct smb2_hdr *rcv_hdr; int next_hdr_offset = 0; @@ -422,8 +422,8 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) new_len, work->next_smb2_rcv_hdr_off, work->next_smb2_rsp_hdr_off); - rsp_hdr = RESPONSE_BUF_NEXT(work); - rcv_hdr = REQUEST_BUF_NEXT(work); + rsp_hdr = ksmbd_resp_buf_next(work); + rcv_hdr = ksmbd_req_buf_next(work); if (!(rcv_hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { ksmbd_debug(SMB, "related flag should be set\n"); @@ -462,7 +462,7 @@ bool is_chained_smb2_message(struct ksmbd_work *work) if (hdr->ProtocolId != SMB2_PROTO_NUMBER) return false; - hdr = REQUEST_BUF_NEXT(work); + hdr = ksmbd_req_buf_next(work); if (le32_to_cpu(hdr->NextCommand) > 0) { ksmbd_debug(SMB, "got SMB2 chained command\n"); init_chained_smb2_rsp(work); @@ -5725,8 +5725,8 @@ int smb2_set_info(struct ksmbd_work *work) rsp_org = work->response_buf; if (work->next_smb2_rcv_hdr_off) { - req = REQUEST_BUF_NEXT(work); - rsp = RESPONSE_BUF_NEXT(work); + req = ksmbd_req_buf_next(work); + rsp = ksmbd_resp_buf_next(work); if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { ksmbd_debug(SMB, "Compound request set FID = %u\n", work->compound_fid); @@ -7224,8 +7224,8 @@ int smb2_ioctl(struct ksmbd_work *work) rsp_org = work->response_buf; if (work->next_smb2_rcv_hdr_off) { - req = REQUEST_BUF_NEXT(work); - rsp = RESPONSE_BUF_NEXT(work); + req = ksmbd_req_buf_next(work); + rsp = ksmbd_resp_buf_next(work); if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { ksmbd_debug(SMB, "Compound request set FID = %u\n", work->compound_fid); @@ -7848,7 +7848,7 @@ int smb2_check_sign_req(struct ksmbd_work *work) hdr_org = hdr = work->request_buf; if (work->next_smb2_rcv_hdr_off) - hdr = REQUEST_BUF_NEXT(work); + hdr = ksmbd_req_buf_next(work); if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) len = be32_to_cpu(hdr_org->smb2_buf_length); @@ -7892,9 +7892,9 @@ void smb2_set_sign_rsp(struct ksmbd_work *work) hdr_org = hdr = work->response_buf; if (work->next_smb2_rsp_hdr_off) - hdr = RESPONSE_BUF_NEXT(work); + hdr = ksmbd_resp_buf_next(work); - req_hdr = REQUEST_BUF_NEXT(work); + req_hdr = ksmbd_req_buf_next(work); if (!work->next_smb2_rsp_hdr_off) { len = get_rfc1002_len(hdr_org); @@ -7946,7 +7946,7 @@ int smb3_check_sign_req(struct ksmbd_work *work) hdr_org = hdr = work->request_buf; if (work->next_smb2_rcv_hdr_off) - hdr = REQUEST_BUF_NEXT(work); + hdr = ksmbd_req_buf_next(work); if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) len = be32_to_cpu(hdr_org->smb2_buf_length); @@ -8005,9 +8005,9 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) hdr_org = hdr = work->response_buf; if (work->next_smb2_rsp_hdr_off) - hdr = RESPONSE_BUF_NEXT(work); + hdr = ksmbd_resp_buf_next(work); - req_hdr = REQUEST_BUF_NEXT(work); + req_hdr = ksmbd_req_buf_next(work); if (!work->next_smb2_rsp_hdr_off) { len = get_rfc1002_len(hdr_org); @@ -8217,7 +8217,7 @@ bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) return false; if (work->next_smb2_rcv_hdr_off) - rsp = RESPONSE_BUF_NEXT(work); + rsp = ksmbd_resp_buf_next(work); if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && rsp->Status == STATUS_SUCCESS) From e294f78d34785151cb6d7199ff61d110f9520e65 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 28 Jun 2021 15:26:37 +0900 Subject: [PATCH 157/417] ksmbd: allow PROTECTED_DACL_SECINFO and UNPROTECTED_DACL_SECINFO addition information in smb2 set info security "cifsd: Fix regression in smb2_get_info" patch cause that dacl doesn't work. windows send smb2 set info security with PROTECTED_DACL_SECINFO to control dacl. But previous patch doesn't allow it. This patch add PROTECTED_DACL_SECINFO and UNPROTECTED_DACL_SECINFO addtional information flags in check. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 42fc3bd2d464..7d8bec07630b 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -4888,9 +4888,11 @@ static int smb2_get_info_sec(struct ksmbd_work *work, int addition_info = le32_to_cpu(req->AdditionalInformation); int rc; - if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO)) { - ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", - addition_info); + if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | + PROTECTED_DACL_SECINFO | + UNPROTECTED_DACL_SECINFO)) { + pr_err("Unsupported addition info: 0x%x)\n", + addition_info); pntsd->revision = cpu_to_le16(1); pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); From a5a25a114ab2412831f063361360eb1192ca6151 Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Sat, 26 Jun 2021 22:56:48 +0900 Subject: [PATCH 158/417] ksmbd: Relax credit_charge check in smb2_validate_credit_charge() smb2_validate_credit_charge() checks the CreditCharge field in the request is valid with regards to the payload size. The current implementation rejects requests with CreditCharge = 0 and a payload < 64K, even though they should be accepted. Set CreditCharge to a minimum value of 1 to avoid rejecting such requests. This matches what samba4 does. Fixes share enumeration for jcifs-ng clients. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2misc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index 730d68032c46..4508631c5706 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -317,14 +317,12 @@ static int smb2_validate_credit_charge(struct smb2_hdr *hdr) return 0; } + credit_charge = max(1, credit_charge); max_len = max(req_len, expect_resp_len); calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); - if (!credit_charge && max_len > SMB2_MAX_BUFFER_SIZE) { - pr_err("credit charge is zero and payload size(%d) is bigger than 64K\n", - max_len); - return 1; - } else if (credit_charge < calc_credit_num) { - pr_err("credit charge : %d, calc_credit_num : %d\n", + + if (credit_charge < calc_credit_num) { + pr_err("Insufficient credit charge, given: %d, needed: %d\n", credit_charge, calc_credit_num); return 1; } From 493fa2fbe4597db474e43d38fb8805cbaef654ac Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 29 Jun 2021 09:22:16 +0900 Subject: [PATCH 159/417] ksmbd: fix dentry racy with rename() Using ->d_name can be broken due to races with rename(). So use %pd with ->d_name to print filename and In other cases, use it under ->d_lock. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 19 ++++++++++--------- fs/ksmbd/vfs.c | 14 ++++++++------ fs/ksmbd/vfs_cache.h | 1 - 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 7d8bec07630b..70e6d6e3e84b 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -3711,8 +3711,8 @@ int smb2_query_dir(struct ksmbd_work *work) if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || inode_permission(&init_user_ns, file_inode(dir_fp->filp), MAY_READ | MAY_EXEC)) { - pr_err("no right to enumerate directory (%s)\n", - FP_FILENAME(dir_fp)); + pr_err("no right to enumerate directory (%pd)\n", + dir_fp->filp->f_path.dentry); rc = -EACCES; goto err_out2; } @@ -4266,14 +4266,15 @@ static void get_file_alternate_info(struct ksmbd_work *work, { struct ksmbd_conn *conn = work->conn; struct smb2_file_alt_name_info *file_info; + struct dentry *dentry = fp->filp->f_path.dentry; int conv_len; - char *filename; - filename = (char *)FP_FILENAME(fp); + spin_lock(&dentry->d_lock); file_info = (struct smb2_file_alt_name_info *)rsp->Buffer; conv_len = ksmbd_extract_shortname(conn, - filename, + dentry->d_name.name, file_info->FileName); + 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); @@ -5938,8 +5939,8 @@ int smb2_read(struct ksmbd_work *work) goto out; } - ksmbd_debug(SMB, "filename %s, offset %lld, len %zu\n", FP_FILENAME(fp), - offset, length); + ksmbd_debug(SMB, "filename %pd, offset %lld, len %zu\n", + fp->filp->f_path.dentry, offset, length); work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); if (!work->aux_payload_buf) { @@ -6216,8 +6217,8 @@ int smb2_write(struct ksmbd_work *work) if (le32_to_cpu(req->Flags) & SMB2_WRITEFLAG_WRITE_THROUGH) writethrough = true; - ksmbd_debug(SMB, "filename %s, offset %lld, len %zu\n", - FP_FILENAME(fp), offset, length); + ksmbd_debug(SMB, "filename %pd, offset %lld, len %zu\n", + fp->filp->f_path.dentry, offset, length); err = ksmbd_vfs_write(work, fp, data_buf, length, &offset, writethrough, &nbytes); if (err < 0) diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index 6181a58e8a33..ed1c0626e205 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -365,7 +365,8 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, if (work->conn->connection_type) { if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { - pr_err("no right to read(%s)\n", FP_FILENAME(fp)); + pr_err("no right to read(%pd)\n", + fp->filp->f_path.dentry); return -EACCES; } } @@ -473,7 +474,8 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, if (sess->conn->connection_type) { if (!(fp->daccess & FILE_WRITE_DATA_LE)) { - pr_err("no right to write(%s)\n", FP_FILENAME(fp)); + pr_err("no right to write(%pd)\n", + fp->filp->f_path.dentry); err = -EACCES; goto out; } @@ -512,8 +514,8 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, if (sync) { err = vfs_fsync_range(filp, offset, offset + *written, 0); if (err < 0) - pr_err("fsync failed for filename = %s, err = %d\n", - FP_FILENAME(fp), err); + pr_err("fsync failed for filename = %pd, err = %d\n", + fp->filp->f_path.dentry, err); } out: @@ -1707,11 +1709,11 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, *total_size_written = 0; if (!(src_fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { - pr_err("no right to read(%s)\n", FP_FILENAME(src_fp)); + pr_err("no right to read(%pd)\n", src_fp->filp->f_path.dentry); return -EACCES; } if (!(dst_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) { - pr_err("no right to write(%s)\n", FP_FILENAME(dst_fp)); + pr_err("no right to write(%pd)\n", dst_fp->filp->f_path.dentry); return -EACCES; } diff --git a/fs/ksmbd/vfs_cache.h b/fs/ksmbd/vfs_cache.h index 745855367106..03c36906cab0 100644 --- a/fs/ksmbd/vfs_cache.h +++ b/fs/ksmbd/vfs_cache.h @@ -25,7 +25,6 @@ #define KSMBD_NO_FID (UINT_MAX) #define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) -#define FP_FILENAME(fp) ((fp)->filp->f_path.dentry->d_name.name) #define FP_INODE(fp) d_inode((fp)->filp->f_path.dentry) #define PARENT_INODE(fp) d_inode((fp)->filp->f_path.dentry->d_parent) From ab0b263b749ade964db46b148a965eb88bd644be Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 29 Jun 2021 09:20:13 +0900 Subject: [PATCH 160/417] ksmbd: opencode to remove FP_INODE macro Opencode to remove FP_INODE macro. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/oplock.c | 2 +- fs/ksmbd/smb2pdu.c | 26 +++++++++++++------------- fs/ksmbd/smb_common.c | 2 +- fs/ksmbd/vfs.c | 2 +- fs/ksmbd/vfs_cache.c | 6 +++--- fs/ksmbd/vfs_cache.h | 1 - 6 files changed, 19 insertions(+), 20 deletions(-) diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index 71e15a591582..3f0dd9b35c78 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -1579,7 +1579,7 @@ void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id) void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) { struct create_posix_rsp *buf; - struct inode *inode = FP_INODE(fp); + struct inode *inode = file_inode(fp->filp); buf = (struct create_posix_rsp *)cc; memset(buf, 0, sizeof(struct create_posix_rsp)); diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 70e6d6e3e84b..2d515e44d48e 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2908,7 +2908,7 @@ int smb2_open(struct ksmbd_work *work) 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))) { - if (share_ret < 0 && !S_ISDIR(FP_INODE(fp)->i_mode)) { + if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { rc = share_ret; goto err_out; } @@ -2995,7 +2995,7 @@ int smb2_open(struct ksmbd_work *work) memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); - generic_fillattr(&init_user_ns, FP_INODE(fp), &stat); + generic_fillattr(&init_user_ns, file_inode(fp->filp), &stat); rsp->StructureSize = cpu_to_le16(89); rcu_read_lock(); @@ -4123,7 +4123,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, } basic_info = (struct smb2_file_all_info *)rsp->Buffer; - generic_fillattr(&init_user_ns, FP_INODE(fp), &stat); + generic_fillattr(&init_user_ns, file_inode(fp->filp), &stat); basic_info->CreationTime = cpu_to_le64(fp->create_time); time = ksmbd_UnixTimeToNT(stat.atime); basic_info->LastAccessTime = cpu_to_le64(time); @@ -4163,7 +4163,7 @@ static void get_file_standard_info(struct smb2_query_info_rsp *rsp, struct inode *inode; struct kstat stat; - inode = FP_INODE(fp); + inode = file_inode(fp->filp); generic_fillattr(&init_user_ns, inode, &stat); sinfo = (struct smb2_file_standard_info *)rsp->Buffer; @@ -4218,7 +4218,7 @@ static int get_file_all_info(struct ksmbd_work *work, if (!filename) return -ENOMEM; - inode = FP_INODE(fp); + inode = file_inode(fp->filp); generic_fillattr(&init_user_ns, inode, &stat); ksmbd_debug(SMB, "filename = %s\n", filename); @@ -4294,7 +4294,7 @@ static void get_file_stream_info(struct ksmbd_work *work, ssize_t xattr_list_len; int nbytes = 0, streamlen, stream_name_len, next, idx = 0; - generic_fillattr(&init_user_ns, FP_INODE(fp), &stat); + generic_fillattr(&init_user_ns, file_inode(fp->filp), &stat); file_info = (struct smb2_file_stream_info *)rsp->Buffer; xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); @@ -4373,7 +4373,7 @@ static void get_file_internal_info(struct smb2_query_info_rsp *rsp, struct smb2_file_internal_info *file_info; struct kstat stat; - generic_fillattr(&init_user_ns, FP_INODE(fp), &stat); + generic_fillattr(&init_user_ns, file_inode(fp->filp), &stat); file_info = (struct smb2_file_internal_info *)rsp->Buffer; file_info->IndexNumber = cpu_to_le64(stat.ino); rsp->OutputBufferLength = @@ -4397,7 +4397,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; - inode = FP_INODE(fp); + inode = file_inode(fp->filp); generic_fillattr(&init_user_ns, inode, &stat); file_info->CreationTime = cpu_to_le64(fp->create_time); @@ -4459,7 +4459,7 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp, struct smb2_file_comp_info *file_info; struct kstat stat; - generic_fillattr(&init_user_ns, FP_INODE(fp), &stat); + generic_fillattr(&init_user_ns, file_inode(fp->filp), &stat); file_info = (struct smb2_file_comp_info *)rsp->Buffer; file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9); @@ -4498,7 +4498,7 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, void *rsp_org) { struct smb311_posix_qinfo *file_info; - struct inode *inode = FP_INODE(fp); + struct inode *inode = file_inode(fp->filp); u64 time; file_info = (struct smb311_posix_qinfo *)rsp->Buffer; @@ -4927,7 +4927,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, if (!fp) return -ENOENT; - inode = FP_INODE(fp); + inode = file_inode(fp->filp); ksmbd_acls_fattr(&fattr, inode); if (test_share_config_flag(work->tcon->share_conf, @@ -5109,7 +5109,7 @@ int smb2_close(struct ksmbd_work *work) goto out; } - inode = FP_INODE(fp); + inode = file_inode(fp->filp); rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 : cpu_to_le64(inode->i_blocks << 9); @@ -7397,7 +7397,7 @@ int smb2_ioctl(struct ksmbd_work *work) } reparse_ptr->ReparseTag = - smb2_get_reparse_tag_special_file(FP_INODE(fp)->i_mode); + smb2_get_reparse_tag_special_file(file_inode(fp->filp)->i_mode); reparse_ptr->ReparseDataLength = 0; ksmbd_fd_put(work, fp); nbytes = sizeof(struct reparse_data_buffer); diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c index 5bf644d7e321..b573575a1de5 100644 --- a/fs/ksmbd/smb_common.c +++ b/fs/ksmbd/smb_common.c @@ -488,7 +488,7 @@ int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) */ read_lock(&curr_fp->f_ci->m_lock); list_for_each_entry(prev_fp, &curr_fp->f_ci->m_fp_list, node) { - if (file_inode(filp) != FP_INODE(prev_fp)) + if (file_inode(filp) != file_inode(prev_fp->filp)) continue; if (filp == prev_fp->filp) diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index ed1c0626e205..40783bb414d6 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -1024,7 +1024,7 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, int in_count, int *out_count) { struct file *f = fp->filp; - struct inode *inode = FP_INODE(fp); + struct inode *inode = file_inode(fp->filp); loff_t maxbytes = (u64)inode->i_sb->s_maxbytes, end; loff_t extent_start, extent_end; int ret = 0; diff --git a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c index c88210b15289..5c9efcfaeb5c 100644 --- a/fs/ksmbd/vfs_cache.c +++ b/fs/ksmbd/vfs_cache.c @@ -83,7 +83,7 @@ static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) { - return __ksmbd_inode_lookup(FP_INODE(fp)); + return __ksmbd_inode_lookup(file_inode(fp->filp)); } static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) @@ -156,7 +156,7 @@ static void ksmbd_inode_unhash(struct ksmbd_inode *ci) static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) { - ci->m_inode = FP_INODE(fp); + ci->m_inode = file_inode(fp->filp); atomic_set(&ci->m_count, 1); atomic_set(&ci->op_count, 0); atomic_set(&ci->sop_count, 0); @@ -479,7 +479,7 @@ struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) read_lock(&ci->m_lock); list_for_each_entry(lfp, &ci->m_fp_list, node) { - if (inode == FP_INODE(lfp)) { + if (inode == file_inode(lfp->filp)) { atomic_dec(&ci->m_count); read_unlock(&ci->m_lock); return lfp; diff --git a/fs/ksmbd/vfs_cache.h b/fs/ksmbd/vfs_cache.h index 03c36906cab0..b01192ebd86b 100644 --- a/fs/ksmbd/vfs_cache.h +++ b/fs/ksmbd/vfs_cache.h @@ -25,7 +25,6 @@ #define KSMBD_NO_FID (UINT_MAX) #define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) -#define FP_INODE(fp) d_inode((fp)->filp->f_path.dentry) #define PARENT_INODE(fp) d_inode((fp)->filp->f_path.dentry->d_parent) #define ATTR_FP(fp) ((fp)->attrib_only && \ From 12202c0594b18218e1645fd0fad92cf77a1f3145 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 29 Jun 2021 09:23:56 +0900 Subject: [PATCH 161/417] ksmbd: use ksmbd_vfs_lock_parent to get stable parent dentry Use ksmbd_vfs_lock_parent to get stable parent dentry and remove PARENT_INODE macro. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 15 ++++++++++++++- fs/ksmbd/vfs.c | 2 +- fs/ksmbd/vfs.h | 1 + fs/ksmbd/vfs_cache.h | 2 -- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 2d515e44d48e..bf798ee65b25 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -5538,6 +5538,9 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, char *buf) { struct ksmbd_file *parent_fp; + struct dentry *parent; + struct dentry *dentry = fp->filp->f_path.dentry; + int ret; if (!(fp->daccess & FILE_DELETE_LE)) { pr_err("no right to delete : 0x%x\n", fp->daccess); @@ -5547,7 +5550,17 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, if (ksmbd_stream_fd(fp)) goto next; - parent_fp = ksmbd_lookup_fd_inode(PARENT_INODE(fp)); + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(parent, dentry); + if (ret) { + dput(parent); + return ret; + } + + parent_fp = ksmbd_lookup_fd_inode(d_inode(parent)); + inode_unlock(d_inode(parent)); + dput(parent); + if (parent_fp) { if (parent_fp->daccess & FILE_DELETE_LE) { pr_err("parent dir is opened with delete access\n"); diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index 40783bb414d6..702166266f91 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -69,7 +69,7 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, * * the reference count of @parent isn't incremented. */ -static int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child) +int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child) { struct dentry *dentry; int ret = 0; diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h index ae8eff1f0315..ba12fea004b5 100644 --- a/fs/ksmbd/vfs.h +++ b/fs/ksmbd/vfs.h @@ -192,6 +192,7 @@ struct ksmbd_kstat { __le32 file_attributes; }; +int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child); int ksmbd_vfs_may_delete(struct dentry *dentry); int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess); int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); diff --git a/fs/ksmbd/vfs_cache.h b/fs/ksmbd/vfs_cache.h index b01192ebd86b..752cbdab3522 100644 --- a/fs/ksmbd/vfs_cache.h +++ b/fs/ksmbd/vfs_cache.h @@ -25,8 +25,6 @@ #define KSMBD_NO_FID (UINT_MAX) #define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) -#define PARENT_INODE(fp) d_inode((fp)->filp->f_path.dentry->d_parent) - #define ATTR_FP(fp) ((fp)->attrib_only && \ ((fp)->cdoption != FILE_OVERWRITE_IF_LE && \ (fp)->cdoption != FILE_OVERWRITE_LE && \ From 849fbc549d4cca576d659d7df139c5f04104cb48 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 29 Jun 2021 09:24:31 +0900 Subject: [PATCH 162/417] ksmbd: opencode to remove ATTR_FP macro Opencode to remove ATTR_FP macro. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/oplock.c | 4 +++- fs/ksmbd/vfs_cache.h | 5 ----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index 3f0dd9b35c78..43c8b7ce6095 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -1111,7 +1111,9 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, goto set_lev; /* grant none-oplock if second open is trunc */ - if (ATTR_FP(fp)) { + if (fp->attrib_only && fp->cdoption != FILE_OVERWRITE_IF_LE && + fp->cdoption != FILE_OVERWRITE_LE && + fp->cdoption != FILE_SUPERSEDE_LE) { req_op_level = SMB2_OPLOCK_LEVEL_NONE; goto set_lev; } diff --git a/fs/ksmbd/vfs_cache.h b/fs/ksmbd/vfs_cache.h index 752cbdab3522..543494f664cb 100644 --- a/fs/ksmbd/vfs_cache.h +++ b/fs/ksmbd/vfs_cache.h @@ -25,11 +25,6 @@ #define KSMBD_NO_FID (UINT_MAX) #define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) -#define ATTR_FP(fp) ((fp)->attrib_only && \ - ((fp)->cdoption != FILE_OVERWRITE_IF_LE && \ - (fp)->cdoption != FILE_OVERWRITE_LE && \ - (fp)->cdoption != FILE_SUPERSEDE_LE)) - struct ksmbd_conn; struct ksmbd_session; From 0ae941ef2e481e478a4b6c52a16e73c7bb4b9e3e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jun 2021 09:37:09 +0900 Subject: [PATCH 163/417] ksmbd: remove SMB1 oplock level macros ksmbd does not support SMB1. This patch remove SMB1 oplock level macros. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/oplock.c | 2 +- fs/ksmbd/oplock.h | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index 43c8b7ce6095..a9f171ccf770 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -39,7 +39,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, opinfo->sess = sess; opinfo->conn = sess->conn; - opinfo->level = OPLOCK_NONE; + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; opinfo->op_state = OPLOCK_STATE_NONE; opinfo->pending_break = 0; opinfo->fid = id; diff --git a/fs/ksmbd/oplock.h b/fs/ksmbd/oplock.h index 9fb7ea74e86c..119b8047cfbd 100644 --- a/fs/ksmbd/oplock.h +++ b/fs/ksmbd/oplock.h @@ -11,12 +11,6 @@ #define OPLOCK_WAIT_TIME (35 * HZ) -/* SMB Oplock levels */ -#define OPLOCK_NONE 0 -#define OPLOCK_EXCLUSIVE 1 -#define OPLOCK_BATCH 2 -#define OPLOCK_READ 3 /* level 2 oplock */ - /* SMB2 Oplock levels */ #define SMB2_OPLOCK_LEVEL_NONE 0x00 #define SMB2_OPLOCK_LEVEL_II 0x01 From 6128468da50c790f56d0aed2f604333fb324f897 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jun 2021 09:37:43 +0900 Subject: [PATCH 164/417] ksmbd: change ACE types to enumeration Change ACE types to enumeration. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smbacl.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/ksmbd/smbacl.h b/fs/ksmbd/smbacl.h index fb5480f0aa89..baa9b9b47a07 100644 --- a/fs/ksmbd/smbacl.h +++ b/fs/ksmbd/smbacl.h @@ -17,8 +17,13 @@ #define NUM_AUTHS (6) /* number of authority fields */ #define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ -#define ACCESS_ALLOWED 0 -#define ACCESS_DENIED 1 +/* + * ACE types - see MS-DTYP 2.4.4.1 + */ +enum { + ACCESS_ALLOWED, + ACCESS_DENIED, +}; #define SIDOWNER 1 #define SIDGROUP 2 From 12411ad59d49e415f987719b8f676e2c6b99be37 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jun 2021 09:38:13 +0900 Subject: [PATCH 165/417] ksmbd: change sid types to enumeration Change sid types to enumeration. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smbacl.h | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/fs/ksmbd/smbacl.h b/fs/ksmbd/smbacl.h index baa9b9b47a07..3e1345e9f24f 100644 --- a/fs/ksmbd/smbacl.h +++ b/fs/ksmbd/smbacl.h @@ -25,15 +25,20 @@ enum { ACCESS_DENIED, }; -#define SIDOWNER 1 -#define SIDGROUP 2 -#define SIDCREATOR_OWNER 3 -#define SIDCREATOR_GROUP 4 -#define SIDUNIX_USER 5 -#define SIDUNIX_GROUP 6 -#define SIDNFS_USER 7 -#define SIDNFS_GROUP 8 -#define SIDNFS_MODE 9 +/* + * Security ID types + */ +enum { + SIDOWNER = 1, + SIDGROUP, + SIDCREATOR_OWNER, + SIDCREATOR_GROUP, + SIDUNIX_USER, + SIDUNIX_GROUP, + SIDNFS_USER, + SIDNFS_GROUP, + SIDNFS_MODE, +}; /* Revision for ACLs */ #define SD_REVISION 1 From b9cbfb524d73ca953604dc421098b4a3aa14d095 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jun 2021 09:38:46 +0900 Subject: [PATCH 166/417] ksmbd: change server state type macro to enumeration Change server state type macro to enumeration. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/server.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/ksmbd/server.h b/fs/ksmbd/server.h index b682d28963e8..2fce06e8b833 100644 --- a/fs/ksmbd/server.h +++ b/fs/ksmbd/server.h @@ -8,10 +8,15 @@ #include "smbacl.h" -#define SERVER_STATE_STARTING_UP 0 -#define SERVER_STATE_RUNNING 1 -#define SERVER_STATE_RESETTING 2 -#define SERVER_STATE_SHUTTING_DOWN 3 +/* + * Server state type + */ +enum { + SERVER_STATE_STARTING_UP, + SERVER_STATE_RUNNING, + SERVER_STATE_RESETTING, + SERVER_STATE_SHUTTING_DOWN, +}; #define SERVER_CONF_NETBIOS_NAME 0 #define SERVER_CONF_SERVER_STRING 1 From c63ee4a521e766da6ec5ee1d2058d1ec06216214 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jun 2021 09:39:15 +0900 Subject: [PATCH 167/417] ksmbd: change server config string index to enumeration Change server config string index to enumeration. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/server.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/ksmbd/server.h b/fs/ksmbd/server.h index 2fce06e8b833..ac9d932f8c8a 100644 --- a/fs/ksmbd/server.h +++ b/fs/ksmbd/server.h @@ -18,9 +18,14 @@ enum { SERVER_STATE_SHUTTING_DOWN, }; -#define SERVER_CONF_NETBIOS_NAME 0 -#define SERVER_CONF_SERVER_STRING 1 -#define SERVER_CONF_WORK_GROUP 2 +/* + * Server global config string index + */ +enum { + SERVER_CONF_NETBIOS_NAME, + SERVER_CONF_SERVER_STRING, + SERVER_CONF_WORK_GROUP, +}; struct ksmbd_server_config { unsigned int flags; From 8b758859dfbe9598ba41e8b9b01e44edcc0c2fc1 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 29 Jun 2021 14:52:00 +0900 Subject: [PATCH 168/417] ksmbd: reorder and document on-disk and netlink structures in headers Reorder and document on-disk and netlink structures in headers. This is a userspace ABI to communicate data between ksmbd and user IPC daemon using netlink. This is added to track and cache user account DB and share configuration info from userspace. - KSMBD_EVENT_HEARTBEAT_REQUEST(ksmbd_heartbeat) This event is to check whether user IPC daemon is alive. If user IPC daemon is dead, ksmbd keep existing connection till disconnecting and new connection will be denied. - KSMBD_EVENT_STARTING_UP(ksmbd_startup_request) This event is to receive the information that initializes the ksmbd server from the user IPC daemon and to start the server. The global section parameters are given from smb.conf as initialization information. - KSMBD_EVENT_SHUTTING_DOWN(ksmbd_shutdown_request) This event is to shutdown ksmbd server. - KSMBD_EVENT_LOGIN_REQUEST/RESPONSE(ksmbd_login_request/response) This event is to get user account info to user IPC daemon. - KSMBD_EVENT_SHARE_CONFIG_REQUEST/RESPONSE (ksmbd_share_config_request/response) This event is to get net share configuration info. - KSMBD_EVENT_TREE_CONNECT_REQUEST/RESPONSE (ksmbd_tree_connect_request/response) This event is to get session and tree connect info. - KSMBD_EVENT_TREE_DISCONNECT_REQUEST(ksmbd_tree_disconnect_request) This event is to send tree disconnect info to user IPC daemon. - KSMBD_EVENT_LOGOUT_REQUEST(ksmbd_logout_request) This event is to send logout request to user IPC daemon. - KSMBD_EVENT_RPC_REQUEST/RESPONSE(ksmbd_rpc_command) This event is to make DCE/RPC request like srvsvc, wkssvc, lsarpc, samr to be processed in userspace. - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE (ksmbd_spnego_authen_request/response) This event is to make kerberos authentication to be processed in userspace. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/{ksmbd_server.h => ksmbd_netlink.h} | 177 +++++++++++++++---- fs/ksmbd/ksmbd_work.c | 1 - fs/ksmbd/mgmt/tree_connect.h | 2 +- fs/ksmbd/smb2ops.c | 1 - fs/ksmbd/smbacl.c | 1 - fs/ksmbd/vfs.h | 87 +-------- fs/ksmbd/xattr.h | 122 +++++++++++++ 7 files changed, 269 insertions(+), 122 deletions(-) rename fs/ksmbd/{ksmbd_server.h => ksmbd_netlink.h} (55%) create mode 100644 fs/ksmbd/xattr.h diff --git a/fs/ksmbd/ksmbd_server.h b/fs/ksmbd/ksmbd_netlink.h similarity index 55% rename from fs/ksmbd/ksmbd_server.h rename to fs/ksmbd/ksmbd_netlink.h index 55b7602b79bd..2fbe2bc1e093 100644 --- a/fs/ksmbd/ksmbd_server.h +++ b/fs/ksmbd/ksmbd_netlink.h @@ -10,6 +10,49 @@ #include +/* + * This is a userspace ABI to communicate data between ksmbd and user IPC + * daemon using netlink. This is added to track and cache user account DB + * and share configuration info from userspace. + * + * - KSMBD_EVENT_HEARTBEAT_REQUEST(ksmbd_heartbeat) + * This event is to check whether user IPC daemon is alive. If user IPC + * daemon is dead, ksmbd keep existing connection till disconnecting and + * new connection will be denied. + * + * - KSMBD_EVENT_STARTING_UP(ksmbd_startup_request) + * This event is to receive the information that initializes the ksmbd + * server from the user IPC daemon and to start the server. The global + * section parameters are given from smb.conf as initialization + * information. + * + * - KSMBD_EVENT_SHUTTING_DOWN(ksmbd_shutdown_request) + * This event is to shutdown ksmbd server. + * + * - KSMBD_EVENT_LOGIN_REQUEST/RESPONSE(ksmbd_login_request/response) + * This event is to get user account info to user IPC daemon. + * + * - KSMBD_EVENT_SHARE_CONFIG_REQUEST/RESPONSE(ksmbd_share_config_request/response) + * This event is to get net share configuration info. + * + * - KSMBD_EVENT_TREE_CONNECT_REQUEST/RESPONSE(ksmbd_tree_connect_request/response) + * This event is to get session and tree connect info. + * + * - KSMBD_EVENT_TREE_DISCONNECT_REQUEST(ksmbd_tree_disconnect_request) + * This event is to send tree disconnect info to user IPC daemon. + * + * - KSMBD_EVENT_LOGOUT_REQUEST(ksmbd_logout_request) + * This event is to send logout request to user IPC daemon. + * + * - KSMBD_EVENT_RPC_REQUEST/RESPONSE(ksmbd_rpc_command) + * This event is to make DCE/RPC request like srvsvc, wkssvc, lsarpc, + * samr to be processed in userspace. + * + * - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response) + * This event is to make kerberos authentication to be processed in + * userspace. + */ + #define KSMBD_GENL_NAME "SMBD_GENL" #define KSMBD_GENL_VERSION 0x01 @@ -17,6 +60,9 @@ #define KSMBD_REQ_MAX_HASH_SZ 18 #define KSMBD_REQ_MAX_SHARE_NAME 64 +/* + * IPC heartbeat frame to check whether user IPC daemon is alive. + */ struct ksmbd_heartbeat { __u32 handle; }; @@ -29,53 +75,79 @@ struct ksmbd_heartbeat { #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) #define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) +/* + * IPC request for ksmbd server startup + */ struct ksmbd_startup_request { - __u32 flags; - __s32 signing; - __s8 min_prot[16]; - __s8 max_prot[16]; + __u32 flags; /* Flags for global config */ + __s32 signing; /* Signing enabled */ + __s8 min_prot[16]; /* The minimum SMB protocol version */ + __s8 max_prot[16]; /* The maximum SMB protocol version */ __s8 netbios_name[16]; - __s8 work_group[64]; - __s8 server_string[64]; - __u16 tcp_port; - __u16 ipc_timeout; - __u32 deadtime; - __u32 file_max; - __u32 smb2_max_write; - __u32 smb2_max_read; - __u32 smb2_max_trans; - __u32 share_fake_fscaps; - __u32 sub_auth[3]; - __u32 ifc_list_sz; + __s8 work_group[64]; /* Workgroup */ + __s8 server_string[64]; /* Server string */ + __u16 tcp_port; /* tcp port */ + __u16 ipc_timeout; /* + * specifies the number of seconds + * server will wait for the userspace to + * reply to heartbeat frames. + */ + __u32 deadtime; /* Number of minutes of inactivity */ + __u32 file_max; /* Limits the maximum number of open files */ + __u32 smb2_max_write; /* MAX write size */ + __u32 smb2_max_read; /* MAX read size */ + __u32 smb2_max_trans; /* MAX trans size */ + __u32 share_fake_fscaps; /* + * Support some special application that + * makes QFSINFO calls to check whether + * we set the SPARSE_FILES bit (0x40). + */ + __u32 sub_auth[3]; /* Subauth value for Security ID */ + __u32 ifc_list_sz; /* interfaces list size */ __s8 ____payload[]; }; #define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) +/* + * IPC request to shutdown ksmbd server. + */ struct ksmbd_shutdown_request { __s32 reserved; }; +/* + * IPC user login request. + */ struct ksmbd_login_request { __u32 handle; - __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ }; +/* + * IPC user login response. + */ struct ksmbd_login_response { __u32 handle; - __u32 gid; - __u32 uid; - __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; + __u32 gid; /* group id */ + __u32 uid; /* user id */ + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ __u16 status; - __u16 hash_sz; - __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; + __u16 hash_sz; /* hash size */ + __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; /* password hash */ }; +/* + * IPC request to fetch net share config. + */ struct ksmbd_share_config_request { __u32 handle; - __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; + __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; /* share name */ }; +/* + * IPC response to the net share config request. + */ struct ksmbd_share_config_response { __u32 handle; __u32 flags; @@ -102,6 +174,10 @@ ksmbd_share_config_path(struct ksmbd_share_config_response *sc) return p; } +/* + * IPC request for tree connection. This request include session and tree + * connect info from client. + */ struct ksmbd_tree_connect_request { __u32 handle; __u16 account_flags; @@ -113,21 +189,34 @@ struct ksmbd_tree_connect_request { __s8 peer_addr[64]; }; +/* + * IPC Response structure for tree connection. + */ struct ksmbd_tree_connect_response { __u32 handle; __u16 status; __u16 connection_flags; }; +/* + * IPC Request struture to disconnect tree connection. + */ struct ksmbd_tree_disconnect_request { - __u64 session_id; - __u64 connect_id; + __u64 session_id; /* session id */ + __u64 connect_id; /* tree connection id */ }; +/* + * IPC Response structure to logout user account. + */ struct ksmbd_logout_request { - __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ }; +/* + * RPC command structure to send rpc request like srvsvc or wkssvc to + * IPC user daemon. + */ struct ksmbd_rpc_command { __u32 handle; __u32 flags; @@ -135,18 +224,36 @@ struct ksmbd_rpc_command { __u8 payload[]; }; +/* + * IPC Request Kerberos authentication + */ struct ksmbd_spnego_authen_request { __u32 handle; - __u16 spnego_blob_len; - __u8 spnego_blob[0]; + __u16 spnego_blob_len; /* the length of spnego_blob */ + __u8 spnego_blob[0]; /* + * the GSS token from SecurityBuffer of + * SMB2 SESSION SETUP request + */ }; +/* + * Response data which includes the GSS token and the session key generated by + * user daemon. + */ struct ksmbd_spnego_authen_response { __u32 handle; - struct ksmbd_login_response login_response; - __u16 session_key_len; - __u16 spnego_blob_len; - __u8 payload[]; /* session key + AP_REP */ + struct ksmbd_login_response login_response; /* + * the login response with + * a user identified by the + * GSS token from a client + */ + __u16 session_key_len; /* the length of the session key */ + __u16 spnego_blob_len; /* + * the length of the GSS token which will be + * stored in SecurityBuffer of SMB2 SESSION + * SETUP response + */ + __u8 payload[]; /* session key + AP_REP */ }; /* @@ -185,6 +292,9 @@ enum ksmbd_event { KSMBD_EVENT_MAX }; +/* + * Enumeration for IPC tree connect status. + */ enum KSMBD_TREE_CONN_STATUS { KSMBD_TREE_CONN_STATUS_OK = 0, KSMBD_TREE_CONN_STATUS_NOMEM, @@ -262,6 +372,9 @@ enum KSMBD_TREE_CONN_STATUS { #define KSMBD_RPC_LSARPC_METHOD_INVOKE BIT(11) #define KSMBD_RPC_LSARPC_METHOD_RETURN (KSMBD_RPC_LSARPC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +/* + * RPC status definitions. + */ #define KSMBD_RPC_OK 0 #define KSMBD_RPC_EBAD_FUNC 0x00000001 #define KSMBD_RPC_EACCESS_DENIED 0x00000005 diff --git a/fs/ksmbd/ksmbd_work.c b/fs/ksmbd/ksmbd_work.c index 7c914451bbe1..fd58eb4809f6 100644 --- a/fs/ksmbd/ksmbd_work.c +++ b/fs/ksmbd/ksmbd_work.c @@ -12,7 +12,6 @@ #include "connection.h" #include "ksmbd_work.h" #include "mgmt/ksmbd_ida.h" -#include "ksmbd_server.h" static struct kmem_cache *work_cache; static struct workqueue_struct *ksmbd_wq; diff --git a/fs/ksmbd/mgmt/tree_connect.h b/fs/ksmbd/mgmt/tree_connect.h index 4e40ec3f4774..18e2a996e0aa 100644 --- a/fs/ksmbd/mgmt/tree_connect.h +++ b/fs/ksmbd/mgmt/tree_connect.h @@ -8,7 +8,7 @@ #include -#include "../ksmbd_server.h" +#include "../ksmbd_netlink.h" struct ksmbd_share_config; struct ksmbd_user; diff --git a/fs/ksmbd/smb2ops.c b/fs/ksmbd/smb2ops.c index f7e5f21d4ae2..8262908e467c 100644 --- a/fs/ksmbd/smb2ops.c +++ b/fs/ksmbd/smb2ops.c @@ -12,7 +12,6 @@ #include "connection.h" #include "smb_common.h" #include "server.h" -#include "ksmbd_server.h" static struct smb_version_values smb21_server_values = { .version_string = SMB21_VERSION_STRING, diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c index 958937a548a1..d385c7045cc0 100644 --- a/fs/ksmbd/smbacl.c +++ b/fs/ksmbd/smbacl.c @@ -14,7 +14,6 @@ #include "smb_common.h" #include "server.h" #include "misc.h" -#include "ksmbd_server.h" #include "mgmt/share_config.h" static const struct smb_sid domain = {1, 4, {0, 0, 0, 0, 0, 5}, diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h index ba12fea004b5..e30174a0e5a1 100644 --- a/fs/ksmbd/vfs.h +++ b/fs/ksmbd/vfs.h @@ -14,92 +14,7 @@ #include #include "smbacl.h" - -/* STREAM XATTR PREFIX */ -#define STREAM_PREFIX "DosStream." -#define STREAM_PREFIX_LEN (sizeof(STREAM_PREFIX) - 1) -#define XATTR_NAME_STREAM (XATTR_USER_PREFIX STREAM_PREFIX) -#define XATTR_NAME_STREAM_LEN (sizeof(XATTR_NAME_STREAM) - 1) - -enum { - XATTR_DOSINFO_ATTRIB = 0x00000001, - XATTR_DOSINFO_EA_SIZE = 0x00000002, - XATTR_DOSINFO_SIZE = 0x00000004, - XATTR_DOSINFO_ALLOC_SIZE = 0x00000008, - XATTR_DOSINFO_CREATE_TIME = 0x00000010, - XATTR_DOSINFO_CHANGE_TIME = 0x00000020, - XATTR_DOSINFO_ITIME = 0x00000040 -}; - -struct xattr_dos_attrib { - __u16 version; - __u32 flags; - __u32 attr; - __u32 ea_size; - __u64 size; - __u64 alloc_size; - __u64 create_time; - __u64 change_time; - __u64 itime; -}; - -/* DOS ATTRIBUITE XATTR PREFIX */ -#define DOS_ATTRIBUTE_PREFIX "DOSATTRIB" -#define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1) -#define XATTR_NAME_DOS_ATTRIBUTE \ - (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) -#define XATTR_NAME_DOS_ATTRIBUTE_LEN \ - (sizeof(XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) - 1) - -#define XATTR_SD_HASH_TYPE_SHA256 0x1 -#define XATTR_SD_HASH_SIZE 64 - -#define SMB_ACL_READ 4 -#define SMB_ACL_WRITE 2 -#define SMB_ACL_EXECUTE 1 - -enum { - SMB_ACL_TAG_INVALID = 0, - SMB_ACL_USER, - SMB_ACL_USER_OBJ, - SMB_ACL_GROUP, - SMB_ACL_GROUP_OBJ, - SMB_ACL_OTHER, - SMB_ACL_MASK -}; - -struct xattr_acl_entry { - int type; - uid_t uid; - gid_t gid; - mode_t perm; -}; - -struct xattr_smb_acl { - int count; - int next; - struct xattr_acl_entry entries[0]; -}; - -struct xattr_ntacl { - __u16 version; - void *sd_buf; - __u32 sd_size; - __u16 hash_type; - __u8 desc[10]; - __u16 desc_len; - __u64 current_time; - __u8 hash[XATTR_SD_HASH_SIZE]; - __u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; -}; - -/* SECURITY DESCRIPTOR XATTR PREFIX */ -#define SD_PREFIX "NTACL" -#define SD_PREFIX_LEN (sizeof(SD_PREFIX) - 1) -#define XATTR_NAME_SD \ - (XATTR_SECURITY_PREFIX SD_PREFIX) -#define XATTR_NAME_SD_LEN \ - (sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1) +#include "xattr.h" /* * Enumeration for stream type. diff --git a/fs/ksmbd/xattr.h b/fs/ksmbd/xattr.h new file mode 100644 index 000000000000..8857c01093d9 --- /dev/null +++ b/fs/ksmbd/xattr.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + */ + +#ifndef __XATTR_H__ +#define __XATTR_H__ + +/* + * These are on-disk structures to store additional metadata into xattr to + * reproduce windows filesystem semantics. And they are encoded with NDR to + * compatible with samba's xattr meta format. The compatibility with samba + * is important because it can lose the information(file attribute, + * creation time, acls) about the existing files when switching between + * ksmbd and samba. + */ + +/* + * Dos attribute flags used for what variable is valid. + */ +enum { + XATTR_DOSINFO_ATTRIB = 0x00000001, + XATTR_DOSINFO_EA_SIZE = 0x00000002, + XATTR_DOSINFO_SIZE = 0x00000004, + XATTR_DOSINFO_ALLOC_SIZE = 0x00000008, + XATTR_DOSINFO_CREATE_TIME = 0x00000010, + XATTR_DOSINFO_CHANGE_TIME = 0x00000020, + XATTR_DOSINFO_ITIME = 0x00000040 +}; + +/* + * Dos attribute structure which is compatible with samba's one. + * Storing it into the xattr named "DOSATTRIB" separately from inode + * allows ksmbd to faithfully reproduce windows filesystem semantics + * on top of a POSIX filesystem. + */ +struct xattr_dos_attrib { + __u16 version; /* version 3 or version 4 */ + __u32 flags; /* valid flags */ + __u32 attr; /* Dos attribute */ + __u32 ea_size; /* EA size */ + __u64 size; + __u64 alloc_size; + __u64 create_time; /* File creation time */ + __u64 change_time; /* File change time */ + __u64 itime; /* Invented/Initial time */ +}; + +/* + * Enumeration is used for computing posix acl hash. + */ +enum { + SMB_ACL_TAG_INVALID = 0, + SMB_ACL_USER, + SMB_ACL_USER_OBJ, + SMB_ACL_GROUP, + SMB_ACL_GROUP_OBJ, + SMB_ACL_OTHER, + SMB_ACL_MASK +}; + +#define SMB_ACL_READ 4 +#define SMB_ACL_WRITE 2 +#define SMB_ACL_EXECUTE 1 + +struct xattr_acl_entry { + int type; + uid_t uid; + gid_t gid; + mode_t perm; +}; + +/* + * xattr_smb_acl structure is used for computing posix acl hash. + */ +struct xattr_smb_acl { + int count; + int next; + struct xattr_acl_entry entries[0]; +}; + +/* 64bytes hash in xattr_ntacl is computed with sha256 */ +#define XATTR_SD_HASH_TYPE_SHA256 0x1 +#define XATTR_SD_HASH_SIZE 64 + +/* + * xattr_ntacl is used for storing ntacl and hashes. + * Hash is used for checking valid posix acl and ntacl in xattr. + */ +struct xattr_ntacl { + __u16 version; /* version 4*/ + void *sd_buf; + __u32 sd_size; + __u16 hash_type; /* hash type */ + __u8 desc[10]; /* posix_acl description */ + __u16 desc_len; + __u64 current_time; + __u8 hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for ntacl */ + __u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for posix acl */ +}; + +/* DOS ATTRIBUITE XATTR PREFIX */ +#define DOS_ATTRIBUTE_PREFIX "DOSATTRIB" +#define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1) +#define XATTR_NAME_DOS_ATTRIBUTE (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) +#define XATTR_NAME_DOS_ATTRIBUTE_LEN \ + (sizeof(XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) - 1) + +/* STREAM XATTR PREFIX */ +#define STREAM_PREFIX "DosStream." +#define STREAM_PREFIX_LEN (sizeof(STREAM_PREFIX) - 1) +#define XATTR_NAME_STREAM (XATTR_USER_PREFIX STREAM_PREFIX) +#define XATTR_NAME_STREAM_LEN (sizeof(XATTR_NAME_STREAM) - 1) + +/* SECURITY DESCRIPTOR(NTACL) XATTR PREFIX */ +#define SD_PREFIX "NTACL" +#define SD_PREFIX_LEN (sizeof(SD_PREFIX) - 1) +#define XATTR_NAME_SD (XATTR_SECURITY_PREFIX SD_PREFIX) +#define XATTR_NAME_SD_LEN \ + (sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1) + +#endif /* __XATTR_H__ */ From ef24c962d0f29036041a007a75bcd0f50233c83e Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Wed, 30 Jun 2021 18:25:52 +0900 Subject: [PATCH 169/417] ksmbd: replace struct dentry with struct path in some function's arguments For user namespace support, we need to pass struct user_namespace with struct dentry to some functions. For reducing the number of arguments, replace the struct dentry with struct path in these functions. Reviewed-by: Christoph Hellwig Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 21 +++++++++++---------- fs/ksmbd/smbacl.c | 25 +++++++++++++------------ fs/ksmbd/smbacl.h | 6 +++--- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index bf798ee65b25..d79ea3eb57a7 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2172,13 +2172,13 @@ static noinline int smb2_set_stream_name_xattr(struct path *path, return 0; } -static int smb2_remove_smb_xattrs(struct dentry *dentry) +static int smb2_remove_smb_xattrs(struct path *path) { char *name, *xattr_list = NULL; ssize_t xattr_list_len; int err = 0; - xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); if (xattr_list_len < 0) { goto out; } else if (!xattr_list_len) { @@ -2196,7 +2196,7 @@ static int smb2_remove_smb_xattrs(struct dentry *dentry) strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, STREAM_PREFIX_LEN)) continue; - err = ksmbd_vfs_remove_xattr(dentry, name); + err = ksmbd_vfs_remove_xattr(path->dentry, name); if (err) ksmbd_debug(SMB, "remove xattr failed : %s\n", name); } @@ -2214,7 +2214,7 @@ static int smb2_create_truncate(struct path *path) return rc; } - rc = smb2_remove_smb_xattrs(path->dentry); + rc = smb2_remove_smb_xattrs(path); if (rc == -EOPNOTSUPP) rc = 0; if (rc) @@ -2305,7 +2305,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, static int smb2_create_sd_buffer(struct ksmbd_work *work, struct smb2_create_req *req, - struct dentry *dentry) + struct path *path) { struct create_context *context; int rc = -ENOENT; @@ -2321,7 +2321,8 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work, ksmbd_debug(SMB, "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); sd_buf = (struct create_sd_buf_req *)context; - rc = set_info_sec(work->conn, work->tcon, dentry, &sd_buf->ntsd, + rc = set_info_sec(work->conn, work->tcon, + path, &sd_buf->ntsd, le32_to_cpu(sd_buf->ccontext.DataLength), true); } @@ -2684,7 +2685,7 @@ int smb2_open(struct ksmbd_work *work) daccess = smb_map_generic_desired_access(req->DesiredAccess); if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { - rc = smb_check_perm_dacl(conn, path.dentry, &daccess, + rc = smb_check_perm_dacl(conn, &path, &daccess, sess->user->uid); if (rc) goto err_out; @@ -2814,12 +2815,12 @@ int smb2_open(struct ksmbd_work *work) if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { - rc = smb_inherit_dacl(conn, path.dentry, sess->user->uid, + rc = smb_inherit_dacl(conn, &path, sess->user->uid, sess->user->gid); } if (rc) { - rc = smb2_create_sd_buffer(work, req, path.dentry); + rc = smb2_create_sd_buffer(work, req, &path); if (rc) { if (posix_acl_rc) ksmbd_vfs_set_init_posix_acl(inode); @@ -5719,7 +5720,7 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, fp->saccess |= FILE_SHARE_DELETE_LE; - return set_info_sec(fp->conn, fp->tcon, fp->filp->f_path.dentry, pntsd, + return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, buf_len, false); } diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c index d385c7045cc0..e0825d3771a1 100644 --- a/fs/ksmbd/smbacl.c +++ b/fs/ksmbd/smbacl.c @@ -941,7 +941,8 @@ static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4)); } -int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, +int smb_inherit_dacl(struct ksmbd_conn *conn, + struct path *path, unsigned int uid, unsigned int gid) { const struct smb_sid *psid, *creator = NULL; @@ -949,11 +950,11 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, struct smb_acl *parent_pdacl; struct smb_ntsd *parent_pntsd = NULL; struct smb_sid owner_sid, group_sid; - struct dentry *parent = dentry->d_parent; + struct dentry *parent = path->dentry->d_parent; int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0; int rc = -ENOENT, num_aces, dacloffset, pntsd_type, acl_len; char *aces_base; - bool is_dir = S_ISDIR(d_inode(dentry)->i_mode); + bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); acl_len = ksmbd_vfs_get_sd_xattr(conn, parent, &parent_pntsd); if (acl_len <= 0) @@ -1086,7 +1087,7 @@ pass: pntsd_size += sizeof(struct smb_acl) + nt_size; } - ksmbd_vfs_set_sd_xattr(conn, dentry, pntsd, pntsd_size); + ksmbd_vfs_set_sd_xattr(conn, path->dentry, pntsd, pntsd_size); kfree(pntsd); rc = 0; } @@ -1109,7 +1110,7 @@ bool smb_inherit_flags(int flags, bool is_dir) return false; } -int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, +int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, __le32 *pdaccess, int uid) { struct smb_ntsd *pntsd = NULL; @@ -1127,7 +1128,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, char *end_of_acl; ksmbd_debug(SMB, "check permission using windows acl\n"); - acl_size = ksmbd_vfs_get_sd_xattr(conn, dentry, &pntsd); + acl_size = ksmbd_vfs_get_sd_xattr(conn, path->dentry, &pntsd); if (acl_size <= 0 || !pntsd || !pntsd->dacloffset) { kfree(pntsd); return 0; @@ -1201,7 +1202,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, granted = GENERIC_ALL_FLAGS; } - posix_acls = get_acl(d_inode(dentry), ACL_TYPE_ACCESS); + posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); if (posix_acls && !found) { unsigned int id = -1; @@ -1261,12 +1262,12 @@ err_out: } int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, - struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len, + struct path *path, struct smb_ntsd *pntsd, int ntsd_len, bool type_check) { int rc; struct smb_fattr fattr = {{0}}; - struct inode *inode = d_inode(dentry); + struct inode *inode = d_inode(path->dentry); fattr.cf_uid = INVALID_UID; fattr.cf_gid = INVALID_GID; @@ -1283,7 +1284,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, inode->i_gid = fattr.cf_gid; mark_inode_dirty(inode); - ksmbd_vfs_remove_acl_xattrs(dentry); + ksmbd_vfs_remove_acl_xattrs(path->dentry); /* Update posix acls */ if (fattr.cf_dacls) { rc = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, @@ -1299,8 +1300,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { /* Update WinACL in xattr */ - ksmbd_vfs_remove_sd_xattrs(dentry); - ksmbd_vfs_set_sd_xattr(conn, dentry, pntsd, ntsd_len); + ksmbd_vfs_remove_sd_xattrs(path->dentry); + ksmbd_vfs_set_sd_xattr(conn, path->dentry, pntsd, ntsd_len); } out: diff --git a/fs/ksmbd/smbacl.h b/fs/ksmbd/smbacl.h index 3e1345e9f24f..4ee7bda32e5f 100644 --- a/fs/ksmbd/smbacl.h +++ b/fs/ksmbd/smbacl.h @@ -200,12 +200,12 @@ void posix_state_to_acl(struct posix_acl_state *state, struct posix_acl_entry *pace); int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid); bool smb_inherit_flags(int flags, bool is_dir); -int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *dentry, +int smb_inherit_dacl(struct ksmbd_conn *conn, struct path *path, unsigned int uid, unsigned int gid); -int smb_check_perm_dacl(struct ksmbd_conn *conn, struct dentry *dentry, +int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, __le32 *pdaccess, int uid); int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, - struct dentry *dentry, struct smb_ntsd *pntsd, int ntsd_len, + struct path *path, struct smb_ntsd *pntsd, int ntsd_len, bool type_check); void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); void ksmbd_init_domain(u32 *sub_auth); From af34983e831587472333e47c86a350a2360c6093 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Wed, 30 Jun 2021 18:25:53 +0900 Subject: [PATCH 170/417] ksmbd: add user namespace support For user namespace support, call vfs functions with struct user_namespace got from struct path. This patch have been tested mannually as below. Create an id-mapped mount using the mount-idmapped utility (https://github.com/brauner/mount-idmapped). $ mount-idmapped --map-mount b:1003:1002:1 /home/foo /foo (the user, "foo" is 1003, and the user "bar" is 1002). And mount the export directory using cifs with the user, "bar". succeed to create/delete/stat/read/write files and directory in the /foo. But fail with a bind mount for /home/foo. Reviewed-by: Christoph Hellwig Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/ndr.c | 8 +- fs/ksmbd/ndr.h | 4 +- fs/ksmbd/oplock.c | 4 +- fs/ksmbd/smb2pdu.c | 113 +++++++++++++++++---------- fs/ksmbd/smb_common.c | 5 +- fs/ksmbd/smb_common.h | 1 + fs/ksmbd/smbacl.c | 99 +++++++++++++----------- fs/ksmbd/smbacl.h | 10 +-- fs/ksmbd/vfs.c | 172 ++++++++++++++++++++++++++---------------- fs/ksmbd/vfs.h | 51 +++++++++---- fs/ksmbd/vfs_cache.c | 5 +- 11 files changed, 294 insertions(+), 178 deletions(-) diff --git a/fs/ksmbd/ndr.c b/fs/ksmbd/ndr.c index bcf13a2aa9d4..cf0df78259c9 100644 --- a/fs/ksmbd/ndr.c +++ b/fs/ksmbd/ndr.c @@ -222,7 +222,9 @@ static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) return 0; } -int ndr_encode_posix_acl(struct ndr *n, struct inode *inode, +int ndr_encode_posix_acl(struct ndr *n, + struct user_namespace *user_ns, + struct inode *inode, struct xattr_smb_acl *acl, struct xattr_smb_acl *def_acl) { @@ -250,8 +252,8 @@ int ndr_encode_posix_acl(struct ndr *n, struct inode *inode, ndr_write_int32(n, 0); } - ndr_write_int64(n, from_kuid(&init_user_ns, inode->i_uid)); - ndr_write_int64(n, from_kgid(&init_user_ns, inode->i_gid)); + ndr_write_int64(n, from_kuid(user_ns, inode->i_uid)); + ndr_write_int64(n, from_kgid(user_ns, inode->i_gid)); ndr_write_int32(n, inode->i_mode); if (acl) { diff --git a/fs/ksmbd/ndr.h b/fs/ksmbd/ndr.h index 77b2d1ac93a0..60ca265d1bb0 100644 --- a/fs/ksmbd/ndr.h +++ b/fs/ksmbd/ndr.h @@ -14,8 +14,8 @@ struct ndr { int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); -int ndr_encode_posix_acl(struct ndr *n, struct inode *inode, - struct xattr_smb_acl *acl, +int ndr_encode_posix_acl(struct ndr *n, struct user_namespace *user_ns, + struct inode *inode, struct xattr_smb_acl *acl, struct xattr_smb_acl *def_acl); int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl); diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index a9f171ccf770..5484b5bf75b0 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -1612,9 +1612,9 @@ void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) buf->nlink = cpu_to_le32(inode->i_nlink); buf->reparse_tag = cpu_to_le32(fp->volatile_id); buf->mode = cpu_to_le32(inode->i_mode); - id_to_sid(from_kuid(&init_user_ns, inode->i_uid), + id_to_sid(from_kuid(file_mnt_user_ns(fp->filp), inode->i_uid), SIDNFS_USER, (struct smb_sid *)&buf->SidBuffer[0]); - id_to_sid(from_kgid(&init_user_ns, inode->i_gid), + id_to_sid(from_kgid(file_mnt_user_ns(fp->filp), inode->i_gid), SIDNFS_GROUP, (struct smb_sid *)&buf->SidBuffer[20]); } diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index d79ea3eb57a7..dda90812feef 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2081,14 +2081,16 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) value = (char *)&eabuf->name + eabuf->EaNameLength + 1; if (!eabuf->EaValueLength) { - rc = ksmbd_vfs_casexattr_len(path->dentry, + rc = ksmbd_vfs_casexattr_len(mnt_user_ns(path->mnt), + path->dentry, attr_name, XATTR_USER_PREFIX_LEN + eabuf->EaNameLength); /* delete the EA only when it exits */ if (rc > 0) { - rc = ksmbd_vfs_remove_xattr(path->dentry, + rc = ksmbd_vfs_remove_xattr(mnt_user_ns(path->mnt), + path->dentry, attr_name); if (rc < 0) { @@ -2102,7 +2104,8 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) /* if the EA doesn't exist, just do nothing. */ rc = 0; } else { - rc = ksmbd_vfs_setxattr(path->dentry, attr_name, value, + rc = ksmbd_vfs_setxattr(mnt_user_ns(path->mnt), + path->dentry, attr_name, value, le16_to_cpu(eabuf->EaValueLength), 0); if (rc < 0) { ksmbd_debug(SMB, @@ -2155,7 +2158,8 @@ static noinline int smb2_set_stream_name_xattr(struct path *path, fp->stream.size = xattr_stream_size; /* Check if there is stream prefix in xattr space */ - rc = ksmbd_vfs_casexattr_len(path->dentry, + rc = ksmbd_vfs_casexattr_len(mnt_user_ns(path->mnt), + path->dentry, xattr_stream_name, xattr_stream_size); if (rc >= 0) @@ -2166,7 +2170,8 @@ static noinline int smb2_set_stream_name_xattr(struct path *path, return -EBADF; } - rc = ksmbd_vfs_setxattr(path->dentry, xattr_stream_name, NULL, 0, 0); + rc = ksmbd_vfs_setxattr(mnt_user_ns(path->mnt), + path->dentry, xattr_stream_name, NULL, 0, 0); if (rc < 0) pr_err("Failed to store XATTR stream name :%d\n", rc); return 0; @@ -2196,7 +2201,8 @@ static int smb2_remove_smb_xattrs(struct path *path) strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, STREAM_PREFIX_LEN)) continue; - err = ksmbd_vfs_remove_xattr(path->dentry, name); + err = ksmbd_vfs_remove_xattr(mnt_user_ns(path->mnt), + path->dentry, name); if (err) ksmbd_debug(SMB, "remove xattr failed : %s\n", name); } @@ -2240,7 +2246,8 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, struct path *path, da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | XATTR_DOSINFO_ITIME; - rc = ksmbd_vfs_set_dos_attrib_xattr(path->dentry, &da); + rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), + path->dentry, &da); if (rc) ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); } @@ -2258,7 +2265,8 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) return; - rc = ksmbd_vfs_get_dos_attrib_xattr(path->dentry, &da); + rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path->mnt), + path->dentry, &da); if (rc > 0) { fp->f_ci->m_fattr = cpu_to_le32(da.attr); fp->create_time = da.create_time; @@ -2634,7 +2642,7 @@ int smb2_open(struct ksmbd_work *work) rc = 0; } else { file_present = true; - generic_fillattr(&init_user_ns, d_inode(path.dentry), &stat); + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); } if (stream_name) { if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { @@ -2695,7 +2703,8 @@ int smb2_open(struct ksmbd_work *work) if (!file_present) { daccess = cpu_to_le32(GENERIC_ALL_FLAGS); } else { - rc = ksmbd_vfs_query_maximal_access(path.dentry, + rc = ksmbd_vfs_query_maximal_access(mnt_user_ns(path.mnt), + path.dentry, &daccess); if (rc) goto err_out; @@ -2738,7 +2747,7 @@ int smb2_open(struct ksmbd_work *work) * is already granted. */ if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { - rc = inode_permission(&init_user_ns, + rc = inode_permission(mnt_user_ns(path.mnt), d_inode(path.dentry), may_flags); if (rc) @@ -2746,7 +2755,8 @@ int smb2_open(struct ksmbd_work *work) if ((daccess & FILE_DELETE_LE) || (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { - rc = ksmbd_vfs_may_delete(path.dentry); + rc = ksmbd_vfs_may_delete(mnt_user_ns(path.mnt), + path.dentry); if (rc) goto err_out; } @@ -2809,7 +2819,9 @@ int smb2_open(struct ksmbd_work *work) int posix_acl_rc; struct inode *inode = d_inode(path.dentry); - posix_acl_rc = ksmbd_vfs_inherit_posix_acl(inode, d_inode(path.dentry->d_parent)); + posix_acl_rc = ksmbd_vfs_inherit_posix_acl(mnt_user_ns(path.mnt), + inode, + d_inode(path.dentry->d_parent)); if (posix_acl_rc) ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); @@ -2823,7 +2835,8 @@ int smb2_open(struct ksmbd_work *work) rc = smb2_create_sd_buffer(work, req, &path); if (rc) { if (posix_acl_rc) - ksmbd_vfs_set_init_posix_acl(inode); + ksmbd_vfs_set_init_posix_acl(mnt_user_ns(path.mnt), + inode); if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { @@ -2845,15 +2858,17 @@ int smb2_open(struct ksmbd_work *work) if (!pntsd) goto err_out; - rc = build_sec_desc(pntsd, NULL, + rc = build_sec_desc(mnt_user_ns(path.mnt), + pntsd, NULL, OWNER_SECINFO | - GROUP_SECINFO | - DACL_SECINFO, + GROUP_SECINFO | + DACL_SECINFO, &pntsd_size, &fattr); posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); rc = ksmbd_vfs_set_sd_xattr(conn, + mnt_user_ns(path.mnt), path.dentry, pntsd, pntsd_size); @@ -2895,7 +2910,7 @@ int smb2_open(struct ksmbd_work *work) rc = ksmbd_vfs_getattr(&path, &stat); if (rc) { - generic_fillattr(&init_user_ns, d_inode(path.dentry), &stat); + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); rc = 0; } @@ -2996,7 +3011,8 @@ int smb2_open(struct ksmbd_work *work) memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); - generic_fillattr(&init_user_ns, file_inode(fp->filp), &stat); + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); rsp->StructureSize = cpu_to_le16(89); rcu_read_lock(); @@ -3048,7 +3064,8 @@ int smb2_open(struct ksmbd_work *work) struct create_context *mxac_ccontext; if (maximal_access == 0) - ksmbd_vfs_query_maximal_access(path.dentry, + ksmbd_vfs_query_maximal_access(mnt_user_ns(path.mnt), + path.dentry, &maximal_access); mxac_ccontext = (struct create_context *)(rsp->Buffer + le32_to_cpu(rsp->CreateContextsLength)); @@ -3251,6 +3268,7 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) * @conn: connection instance * @info_level: smb information level * @d_info: structure included variables for query dir + * @user_ns: user namespace * @ksmbd_kstat: ksmbd wrapper of dirent stat information * * if directory has many entries, find first can't read it fully. @@ -3260,6 +3278,7 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) */ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, struct ksmbd_dir_info *d_info, + struct user_namespace *user_ns, struct ksmbd_kstat *ksmbd_kstat) { int next_entry_offset = 0; @@ -3412,9 +3431,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, S_ISDIR(ksmbd_kstat->kstat->mode) ? ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; if (d_info->hide_dot_file && d_info->name[0] == '.') posix_info->DosAttributes |= ATTR_HIDDEN_LE; - id_to_sid(from_kuid(&init_user_ns, ksmbd_kstat->kstat->uid), + id_to_sid(from_kuid(user_ns, ksmbd_kstat->kstat->uid), SIDNFS_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); - id_to_sid(from_kgid(&init_user_ns, ksmbd_kstat->kstat->gid), + id_to_sid(from_kgid(user_ns, ksmbd_kstat->kstat->gid), SIDNFS_GROUP, (struct smb_sid *)&posix_info->SidBuffer[20]); memcpy(posix_info->name, conv_name, conv_len); posix_info->name_len = cpu_to_le32(conv_len); @@ -3496,12 +3515,14 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv) ksmbd_kstat.kstat = &kstat; if (priv->info_level != FILE_NAMES_INFORMATION) ksmbd_vfs_fill_dentry_attrs(priv->work, + file_mnt_user_ns(priv->dir_fp->filp), dent, &ksmbd_kstat); rc = smb2_populate_readdir_entry(priv->work->conn, priv->info_level, priv->d_info, + file_mnt_user_ns(priv->dir_fp->filp), &ksmbd_kstat); dput(dent); if (rc) @@ -3710,7 +3731,8 @@ int smb2_query_dir(struct ksmbd_work *work) } if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || - inode_permission(&init_user_ns, file_inode(dir_fp->filp), + inode_permission(file_mnt_user_ns(dir_fp->filp), + file_inode(dir_fp->filp), MAY_READ | MAY_EXEC)) { pr_err("no right to enumerate directory (%pd)\n", dir_fp->filp->f_path.dentry); @@ -4035,7 +4057,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, buf_free_len -= (offsetof(struct smb2_ea_info, name) + name_len + 1); /* bailout if xattr can't fit in buf_free_len */ - value_len = ksmbd_vfs_getxattr(path->dentry, name, &buf); + value_len = ksmbd_vfs_getxattr(mnt_user_ns(path->mnt), + path->dentry, name, &buf); if (value_len <= 0) { rc = -ENOENT; rsp->hdr.Status = STATUS_INVALID_HANDLE; @@ -4124,7 +4147,8 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, } basic_info = (struct smb2_file_all_info *)rsp->Buffer; - generic_fillattr(&init_user_ns, file_inode(fp->filp), &stat); + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); basic_info->CreationTime = cpu_to_le64(fp->create_time); time = ksmbd_UnixTimeToNT(stat.atime); basic_info->LastAccessTime = cpu_to_le64(time); @@ -4165,7 +4189,7 @@ static void get_file_standard_info(struct smb2_query_info_rsp *rsp, struct kstat stat; inode = file_inode(fp->filp); - generic_fillattr(&init_user_ns, inode, &stat); + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); sinfo = (struct smb2_file_standard_info *)rsp->Buffer; delete_pending = ksmbd_inode_pending_delete(fp); @@ -4220,7 +4244,7 @@ static int get_file_all_info(struct ksmbd_work *work, return -ENOMEM; inode = file_inode(fp->filp); - generic_fillattr(&init_user_ns, inode, &stat); + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); ksmbd_debug(SMB, "filename = %s\n", filename); delete_pending = ksmbd_inode_pending_delete(fp); @@ -4295,7 +4319,8 @@ static void get_file_stream_info(struct ksmbd_work *work, ssize_t xattr_list_len; int nbytes = 0, streamlen, stream_name_len, next, idx = 0; - generic_fillattr(&init_user_ns, file_inode(fp->filp), &stat); + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); file_info = (struct smb2_file_stream_info *)rsp->Buffer; xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); @@ -4374,7 +4399,8 @@ static void get_file_internal_info(struct smb2_query_info_rsp *rsp, struct smb2_file_internal_info *file_info; struct kstat stat; - generic_fillattr(&init_user_ns, file_inode(fp->filp), &stat); + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); file_info = (struct smb2_file_internal_info *)rsp->Buffer; file_info->IndexNumber = cpu_to_le64(stat.ino); rsp->OutputBufferLength = @@ -4399,7 +4425,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; inode = file_inode(fp->filp); - generic_fillattr(&init_user_ns, inode, &stat); + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); file_info->CreationTime = cpu_to_le64(fp->create_time); time = ksmbd_UnixTimeToNT(stat.atime); @@ -4460,7 +4486,8 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp, struct smb2_file_comp_info *file_info; struct kstat stat; - generic_fillattr(&init_user_ns, file_inode(fp->filp), &stat); + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); file_info = (struct smb2_file_comp_info *)rsp->Buffer; file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9); @@ -4933,9 +4960,11 @@ static int smb2_get_info_sec(struct ksmbd_work *work, if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) - ksmbd_vfs_get_sd_xattr(work->conn, fp->filp->f_path.dentry, &ppntsd); + ksmbd_vfs_get_sd_xattr(work->conn, file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, &ppntsd); - rc = build_sec_desc(pntsd, ppntsd, addition_info, &secdesclen, &fattr); + rc = build_sec_desc(file_mnt_user_ns(fp->filp), + pntsd, ppntsd, addition_info, &secdesclen, &fattr); posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); kfree(ppntsd); @@ -5228,7 +5257,8 @@ static int smb2_rename(struct ksmbd_work *work, struct ksmbd_file *fp, if (rc) goto out; - rc = ksmbd_vfs_setxattr(fp->filp->f_path.dentry, + rc = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, xattr_stream_name, NULL, 0, 0); if (rc < 0) { @@ -5412,7 +5442,8 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | XATTR_DOSINFO_ITIME; - rc = ksmbd_vfs_set_dos_attrib_xattr(filp->f_path.dentry, &da); + rc = ksmbd_vfs_set_dos_attrib_xattr(file_mnt_user_ns(filp), + filp->f_path.dentry, &da); if (rc) ksmbd_debug(SMB, "failed to restore file attribute in EA\n"); @@ -5433,14 +5464,14 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) return -EACCES; - rc = setattr_prepare(&init_user_ns, dentry, &attrs); + rc = setattr_prepare(file_mnt_user_ns(filp), dentry, &attrs); if (rc) return -EINVAL; inode_lock(inode); - setattr_copy(&init_user_ns, inode, &attrs); + setattr_copy(file_mnt_user_ns(filp), inode, &attrs); attrs.ia_valid &= ~ATTR_CTIME; - rc = notify_change(&init_user_ns, dentry, &attrs, NULL); + rc = notify_change(file_mnt_user_ns(filp), dentry, &attrs, NULL); inode_unlock(inode); } return 0; @@ -7188,12 +7219,14 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { struct xattr_dos_attrib da; - ret = ksmbd_vfs_get_dos_attrib_xattr(fp->filp->f_path.dentry, &da); + ret = ksmbd_vfs_get_dos_attrib_xattr(file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, &da); if (ret <= 0) goto out; da.attr = le32_to_cpu(fp->f_ci->m_fattr); - ret = ksmbd_vfs_set_dos_attrib_xattr(fp->filp->f_path.dentry, &da); + ret = ksmbd_vfs_set_dos_attrib_xattr(file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, &da); if (ret) fp->f_ci->m_fattr = old_fattr; } diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c index b573575a1de5..f770f3ffb840 100644 --- a/fs/ksmbd/smb_common.c +++ b/fs/ksmbd/smb_common.c @@ -274,6 +274,7 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, char *search_pattern, int (*fn)(struct ksmbd_conn *, int, struct ksmbd_dir_info *, + struct user_namespace *, struct ksmbd_kstat *)) { int i, rc = 0; @@ -300,9 +301,11 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, ksmbd_kstat.kstat = &kstat; ksmbd_vfs_fill_dentry_attrs(work, + file_mnt_user_ns(dir->filp), dir->filp->f_path.dentry->d_parent, &ksmbd_kstat); - rc = fn(conn, info_level, d_info, &ksmbd_kstat); + rc = fn(conn, info_level, d_info, + file_mnt_user_ns(dir->filp), &ksmbd_kstat); if (rc) break; if (d_info->out_buf_len <= 0) diff --git a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h index 8489b92229fa..6ab28aa33024 100644 --- a/fs/ksmbd/smb_common.h +++ b/fs/ksmbd/smb_common.h @@ -514,6 +514,7 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int (*fn)(struct ksmbd_conn *, int, struct ksmbd_dir_info *, + struct user_namespace *, struct ksmbd_kstat *)); int ksmbd_extract_shortname(struct ksmbd_conn *conn, diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c index e0825d3771a1..4ee714b9b351 100644 --- a/fs/ksmbd/smbacl.c +++ b/fs/ksmbd/smbacl.c @@ -253,7 +253,8 @@ void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid) ssid->num_subauth++; } -static int sid_to_id(struct smb_sid *psid, uint sidtype, +static int sid_to_id(struct user_namespace *user_ns, + struct smb_sid *psid, uint sidtype, struct smb_fattr *fattr) { int rc = -EINVAL; @@ -274,8 +275,8 @@ static int sid_to_id(struct smb_sid *psid, uint sidtype, id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); if (id > 0) { - uid = make_kuid(&init_user_ns, id); - if (uid_valid(uid) && kuid_has_mapping(&init_user_ns, uid)) { + uid = make_kuid(user_ns, id); + if (uid_valid(uid) && kuid_has_mapping(user_ns, uid)) { fattr->cf_uid = uid; rc = 0; } @@ -286,8 +287,8 @@ static int sid_to_id(struct smb_sid *psid, uint sidtype, id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); if (id > 0) { - gid = make_kgid(&init_user_ns, id); - if (gid_valid(gid) && kgid_has_mapping(&init_user_ns, gid)) { + gid = make_kgid(user_ns, id); + if (gid_valid(gid) && kgid_has_mapping(user_ns, gid)) { fattr->cf_gid = gid; rc = 0; } @@ -362,7 +363,8 @@ void free_acl_state(struct posix_acl_state *state) kfree(state->groups); } -static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, +static void parse_dacl(struct user_namespace *user_ns, + struct smb_acl *pdacl, char *end_of_acl, struct smb_sid *pownersid, struct smb_sid *pgrpsid, struct smb_fattr *fattr) { @@ -474,7 +476,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, ppace[i]->type); temp_fattr.cf_uid = INVALID_UID; - ret = sid_to_id(&ppace[i]->sid, SIDOWNER, &temp_fattr); + ret = sid_to_id(user_ns, &ppace[i]->sid, SIDOWNER, &temp_fattr); if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { pr_err("%s: Error %d mapping Owner SID to uid\n", __func__, ret); @@ -553,7 +555,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, free_acl_state(&default_acl_state); } -static void set_posix_acl_entries_dacl(struct smb_ace *pndace, +static void set_posix_acl_entries_dacl(struct user_namespace *user_ns, + struct smb_ace *pndace, struct smb_fattr *fattr, u32 *num_aces, u16 *size, u32 nt_aces_num) { @@ -577,14 +580,14 @@ static void set_posix_acl_entries_dacl(struct smb_ace *pndace, uid_t uid; unsigned int sid_type = SIDOWNER; - uid = from_kuid(&init_user_ns, pace->e_uid); + uid = from_kuid(user_ns, pace->e_uid); if (!uid) sid_type = SIDUNIX_USER; id_to_sid(uid, sid_type, sid); } else if (pace->e_tag == ACL_GROUP) { gid_t gid; - gid = from_kgid(&init_user_ns, pace->e_gid); + gid = from_kgid(user_ns, pace->e_gid); id_to_sid(gid, SIDUNIX_GROUP, sid); } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) { smb_copy_sid(sid, &sid_everyone); @@ -643,12 +646,12 @@ posix_default_acl: if (pace->e_tag == ACL_USER) { uid_t uid; - uid = from_kuid(&init_user_ns, pace->e_uid); + uid = from_kuid(user_ns, pace->e_uid); id_to_sid(uid, SIDCREATOR_OWNER, sid); } else if (pace->e_tag == ACL_GROUP) { gid_t gid; - gid = from_kgid(&init_user_ns, pace->e_gid); + gid = from_kgid(user_ns, pace->e_gid); id_to_sid(gid, SIDCREATOR_GROUP, sid); } else { kfree(sid); @@ -666,7 +669,9 @@ posix_default_acl: } } -static void set_ntacl_dacl(struct smb_acl *pndacl, struct smb_acl *nt_dacl, +static void set_ntacl_dacl(struct user_namespace *user_ns, + struct smb_acl *pndacl, + struct smb_acl *nt_dacl, const struct smb_sid *pownersid, const struct smb_sid *pgrpsid, struct smb_fattr *fattr) @@ -687,12 +692,14 @@ static void set_ntacl_dacl(struct smb_acl *pndacl, struct smb_acl *nt_dacl, } } - set_posix_acl_entries_dacl(pndace, fattr, &num_aces, &size, nt_num_aces); + set_posix_acl_entries_dacl(user_ns, pndace, fattr, + &num_aces, &size, nt_num_aces); pndacl->num_aces = cpu_to_le32(num_aces); pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); } -static void set_mode_dacl(struct smb_acl *pndacl, struct smb_fattr *fattr) +static void set_mode_dacl(struct user_namespace *user_ns, + struct smb_acl *pndacl, struct smb_fattr *fattr) { struct smb_ace *pace, *pndace; u32 num_aces = 0; @@ -703,12 +710,13 @@ static void set_mode_dacl(struct smb_acl *pndacl, struct smb_fattr *fattr) pace = pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); if (fattr->cf_acls) { - set_posix_acl_entries_dacl(pndace, fattr, &num_aces, &size, num_aces); + set_posix_acl_entries_dacl(user_ns, pndace, fattr, + &num_aces, &size, num_aces); goto out; } /* owner RID */ - uid = from_kuid(&init_user_ns, fattr->cf_uid); + uid = from_kuid(user_ns, fattr->cf_uid); if (uid) sid = &server_conf.domain_sid; else @@ -725,7 +733,7 @@ static void set_mode_dacl(struct smb_acl *pndacl, struct smb_fattr *fattr) ace_size = fill_ace_for_sid(pace, &sid_unix_groups, ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); pace->sid.sub_auth[pace->sid.num_subauth++] = - cpu_to_le32(from_kgid(&init_user_ns, fattr->cf_gid)); + cpu_to_le32(from_kgid(user_ns, fattr->cf_gid)); pace->size = cpu_to_le16(ace_size + 4); size += le16_to_cpu(pace->size); pace = (struct smb_ace *)((char *)pndace + size); @@ -771,8 +779,8 @@ static int parse_sid(struct smb_sid *psid, char *end_of_acl) } /* Convert CIFS ACL to POSIX form */ -int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, - struct smb_fattr *fattr) +int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + int acl_len, struct smb_fattr *fattr) { int rc = 0; struct smb_sid *owner_sid_ptr, *group_sid_ptr; @@ -811,7 +819,7 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, return rc; } - rc = sid_to_id(owner_sid_ptr, SIDOWNER, fattr); + rc = sid_to_id(user_ns, owner_sid_ptr, SIDOWNER, fattr); if (rc) { pr_err("%s: Error %d mapping Owner SID to uid\n", __func__, rc); @@ -826,7 +834,7 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, __func__, rc); return rc; } - rc = sid_to_id(group_sid_ptr, SIDUNIX_GROUP, fattr); + rc = sid_to_id(user_ns, group_sid_ptr, SIDUNIX_GROUP, fattr); if (rc) { pr_err("%s: Error %d mapping Group SID to gid\n", __func__, rc); @@ -841,15 +849,16 @@ int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, pntsd->type |= cpu_to_le16(DACL_PROTECTED); if (dacloffset) { - parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr, - fattr); + parse_dacl(user_ns, dacl_ptr, end_of_acl, + owner_sid_ptr, group_sid_ptr, fattr); } return 0; } /* Convert permission bits from mode to equivalent CIFS ACL */ -int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, +int build_sec_desc(struct user_namespace *user_ns, + struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, int addition_info, __u32 *secdesclen, struct smb_fattr *fattr) { @@ -866,7 +875,7 @@ int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, if (!nowner_sid_ptr) return -ENOMEM; - uid = from_kuid(&init_user_ns, fattr->cf_uid); + uid = from_kuid(user_ns, fattr->cf_uid); if (!uid) sid_type = SIDUNIX_USER; id_to_sid(uid, sid_type, nowner_sid_ptr); @@ -877,7 +886,7 @@ int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, return -ENOMEM; } - gid = from_kgid(&init_user_ns, fattr->cf_gid); + gid = from_kgid(user_ns, fattr->cf_gid); id_to_sid(gid, SIDUNIX_GROUP, ngroup_sid_ptr); offset = sizeof(struct smb_ntsd); @@ -909,7 +918,7 @@ int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, dacl_ptr->num_aces = 0; if (!ppntsd) { - set_mode_dacl(dacl_ptr, fattr); + set_mode_dacl(user_ns, dacl_ptr, fattr); } else if (!ppntsd->dacloffset) { goto out; } else { @@ -917,8 +926,8 @@ int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + le32_to_cpu(ppntsd->dacloffset)); - set_ntacl_dacl(dacl_ptr, ppdacl_ptr, nowner_sid_ptr, - ngroup_sid_ptr, fattr); + set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr, + nowner_sid_ptr, ngroup_sid_ptr, fattr); } pntsd->dacloffset = cpu_to_le32(offset); offset += le16_to_cpu(dacl_ptr->size); @@ -956,7 +965,8 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, char *aces_base; bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); - acl_len = ksmbd_vfs_get_sd_xattr(conn, parent, &parent_pntsd); + acl_len = ksmbd_vfs_get_sd_xattr(conn, mnt_user_ns(path->mnt), + parent, &parent_pntsd); if (acl_len <= 0) return rc; dacloffset = le32_to_cpu(parent_pntsd->dacloffset); @@ -1087,7 +1097,8 @@ pass: pntsd_size += sizeof(struct smb_acl) + nt_size; } - ksmbd_vfs_set_sd_xattr(conn, path->dentry, pntsd, pntsd_size); + ksmbd_vfs_set_sd_xattr(conn, mnt_user_ns(path->mnt), + path->dentry, pntsd, pntsd_size); kfree(pntsd); rc = 0; } @@ -1128,7 +1139,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, char *end_of_acl; ksmbd_debug(SMB, "check permission using windows acl\n"); - acl_size = ksmbd_vfs_get_sd_xattr(conn, path->dentry, &pntsd); + acl_size = ksmbd_vfs_get_sd_xattr(conn, mnt_user_ns(path->mnt), + path->dentry, &pntsd); if (acl_size <= 0 || !pntsd || !pntsd->dacloffset) { kfree(pntsd); return 0; @@ -1209,9 +1221,11 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, pa_entry = posix_acls->a_entries; for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { if (pa_entry->e_tag == ACL_USER) - id = from_kuid(&init_user_ns, pa_entry->e_uid); + id = from_kuid(mnt_user_ns(path->mnt), + pa_entry->e_uid); else if (pa_entry->e_tag == ACL_GROUP) - id = from_kgid(&init_user_ns, pa_entry->e_gid); + id = from_kgid(mnt_user_ns(path->mnt), + pa_entry->e_gid); else continue; @@ -1273,7 +1287,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, fattr.cf_gid = INVALID_GID; fattr.cf_mode = inode->i_mode; - rc = parse_sec_desc(pntsd, ntsd_len, &fattr); + rc = parse_sec_desc(mnt_user_ns(path->mnt), pntsd, ntsd_len, &fattr); if (rc) goto out; @@ -1284,13 +1298,13 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, inode->i_gid = fattr.cf_gid; mark_inode_dirty(inode); - ksmbd_vfs_remove_acl_xattrs(path->dentry); + ksmbd_vfs_remove_acl_xattrs(mnt_user_ns(path->mnt), path->dentry); /* Update posix acls */ if (fattr.cf_dacls) { - rc = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, - fattr.cf_acls); + rc = set_posix_acl(mnt_user_ns(path->mnt), inode, + ACL_TYPE_ACCESS, fattr.cf_acls); if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) - rc = set_posix_acl(&init_user_ns, inode, + rc = set_posix_acl(mnt_user_ns(path->mnt), inode, ACL_TYPE_DEFAULT, fattr.cf_dacls); } @@ -1300,8 +1314,9 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { /* Update WinACL in xattr */ - ksmbd_vfs_remove_sd_xattrs(path->dentry); - ksmbd_vfs_set_sd_xattr(conn, path->dentry, pntsd, ntsd_len); + ksmbd_vfs_remove_sd_xattrs(mnt_user_ns(path->mnt), path->dentry); + ksmbd_vfs_set_sd_xattr(conn, mnt_user_ns(path->mnt), + path->dentry, pntsd, ntsd_len); } out: diff --git a/fs/ksmbd/smbacl.h b/fs/ksmbd/smbacl.h index 4ee7bda32e5f..940f686a1d95 100644 --- a/fs/ksmbd/smbacl.h +++ b/fs/ksmbd/smbacl.h @@ -189,11 +189,11 @@ struct posix_acl_state { struct posix_ace_state_array *groups; }; -int parse_sec_desc(struct smb_ntsd *pntsd, int acl_len, - struct smb_fattr *fattr); -int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, - int addition_info, __u32 *secdesclen, - struct smb_fattr *fattr); +int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + int acl_len, struct smb_fattr *fattr); +int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + struct smb_ntsd *ppntsd, int addition_info, + __u32 *secdesclen, struct smb_fattr *fattr); int init_acl_state(struct posix_acl_state *state, int cnt); void free_acl_state(struct posix_acl_state *state); void posix_state_to_acl(struct posix_acl_state *state, diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index 702166266f91..0f5a4fb8215f 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -95,7 +95,8 @@ out_err: return ret; } -int ksmbd_vfs_may_delete(struct dentry *dentry) +int ksmbd_vfs_may_delete(struct user_namespace *user_ns, + struct dentry *dentry) { struct dentry *parent; int ret; @@ -107,7 +108,7 @@ int ksmbd_vfs_may_delete(struct dentry *dentry) return ret; } - ret = inode_permission(&init_user_ns, d_inode(parent), + ret = inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE); inode_unlock(d_inode(parent)); @@ -115,23 +116,24 @@ int ksmbd_vfs_may_delete(struct dentry *dentry) return ret; } -int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) +int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess) { struct dentry *parent; int ret = 0; *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); - if (!inode_permission(&init_user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE)) + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE)) *daccess |= cpu_to_le32(WRITE_DAC | WRITE_OWNER | SYNCHRONIZE | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_DELETE_CHILD); - if (!inode_permission(&init_user_ns, d_inode(dentry), MAY_OPEN | MAY_READ)) + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_READ)) *daccess |= FILE_READ_DATA_LE | FILE_READ_EA_LE; - if (!inode_permission(&init_user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC)) + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC)) *daccess |= FILE_EXECUTE_LE; parent = dget_parent(dentry); @@ -141,7 +143,7 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess) return ret; } - if (!inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) + if (!inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) *daccess |= FILE_DELETE_LE; inode_unlock(d_inode(parent)); @@ -173,7 +175,8 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) } mode |= S_IFREG; - err = vfs_create(&init_user_ns, d_inode(path.dentry), dentry, mode, true); + err = vfs_create(mnt_user_ns(path.mnt), d_inode(path.dentry), + dentry, mode, true); if (!err) { ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(dentry)); @@ -208,7 +211,8 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) } mode |= S_IFDIR; - err = vfs_mkdir(&init_user_ns, d_inode(path.dentry), dentry, mode); + err = vfs_mkdir(mnt_user_ns(path.mnt), d_inode(path.dentry), + dentry, mode); if (err) { goto out; } else if (d_unhashed(dentry)) { @@ -236,7 +240,8 @@ out: return err; } -static ssize_t ksmbd_vfs_getcasexattr(struct dentry *dentry, char *attr_name, +static ssize_t ksmbd_vfs_getcasexattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, int attr_name_len, char **attr_value) { char *name, *xattr_list = NULL; @@ -252,7 +257,8 @@ static ssize_t ksmbd_vfs_getcasexattr(struct dentry *dentry, char *attr_name, if (strncasecmp(attr_name, name, attr_name_len)) continue; - value_len = ksmbd_vfs_getxattr(dentry, + value_len = ksmbd_vfs_getxattr(user_ns, + dentry, name, attr_value); if (value_len < 0) @@ -274,7 +280,8 @@ static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", *pos, count); - v_len = ksmbd_vfs_getcasexattr(fp->filp->f_path.dentry, + v_len = ksmbd_vfs_getcasexattr(file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, fp->stream.name, fp->stream.size, &stream_buf); @@ -411,7 +418,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, count = (*pos + count) - XATTR_SIZE_MAX; } - v_len = ksmbd_vfs_getcasexattr(fp->filp->f_path.dentry, + v_len = ksmbd_vfs_getcasexattr(file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, fp->stream.name, fp->stream.size, &stream_buf); @@ -436,7 +444,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, memcpy(&stream_buf[*pos], buf, count); - err = ksmbd_vfs_setxattr(fp->filp->f_path.dentry, + err = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, fp->stream.name, (void *)stream_buf, size, @@ -606,13 +615,14 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) } if (S_ISDIR(d_inode(path.dentry)->i_mode)) { - err = vfs_rmdir(&init_user_ns, d_inode(parent), path.dentry); + err = vfs_rmdir(mnt_user_ns(path.mnt), d_inode(parent), + path.dentry); if (err && err != -ENOTEMPTY) ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, err); } else { - err = vfs_unlink(&init_user_ns, d_inode(parent), path.dentry, - NULL); + err = vfs_unlink(mnt_user_ns(path.mnt), d_inode(parent), + path.dentry, NULL); if (err) ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, err); @@ -669,7 +679,8 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, goto out3; } - err = vfs_link(oldpath.dentry, &init_user_ns, d_inode(newpath.dentry), + err = vfs_link(oldpath.dentry, mnt_user_ns(newpath.mnt), + d_inode(newpath.dentry), dentry, NULL); if (err) ksmbd_debug(VFS, "vfs_link failed err %d\n", err); @@ -707,8 +718,10 @@ static int ksmbd_validate_entry_in_use(struct dentry *src_dent) } static int __ksmbd_vfs_rename(struct ksmbd_work *work, + struct user_namespace *src_user_ns, struct dentry *src_dent_parent, struct dentry *src_dent, + struct user_namespace *dst_user_ns, struct dentry *dst_dent_parent, struct dentry *trap_dent, char *dst_name) @@ -744,10 +757,10 @@ static int __ksmbd_vfs_rename(struct ksmbd_work *work, err = -ENOTEMPTY; if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) { struct renamedata rd = { - .old_mnt_userns = &init_user_ns, + .old_mnt_userns = src_user_ns, .old_dir = d_inode(src_dent_parent), .old_dentry = src_dent, - .new_mnt_userns = &init_user_ns, + .new_mnt_userns = dst_user_ns, .new_dir = d_inode(dst_dent_parent), .new_dentry = dst_dent, }; @@ -809,8 +822,10 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, dput(src_child); err = __ksmbd_vfs_rename(work, + file_mnt_user_ns(fp->filp), src_dent_parent, src_dent, + mnt_user_ns(dst_path.mnt), dst_dent_parent, trap_dent, dst_name); @@ -917,27 +932,30 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) return size; } -static ssize_t ksmbd_vfs_xattr_len(struct dentry *dentry, char *xattr_name) +static ssize_t ksmbd_vfs_xattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *xattr_name) { - return vfs_getxattr(&init_user_ns, dentry, xattr_name, NULL, 0); + return vfs_getxattr(user_ns, dentry, xattr_name, NULL, 0); } /** * ksmbd_vfs_getxattr() - vfs helper for smb get extended attributes value + * @user_ns: user namespace * @dentry: dentry of file for getting xattrs * @xattr_name: name of xattr name to query * @xattr_buf: destination buffer xattr value * * Return: read xattr value length on success, otherwise error */ -ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, - char **xattr_buf) +ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + struct dentry *dentry, + char *xattr_name, char **xattr_buf) { ssize_t xattr_len; char *buf; *xattr_buf = NULL; - xattr_len = ksmbd_vfs_xattr_len(dentry, xattr_name); + xattr_len = ksmbd_vfs_xattr_len(user_ns, dentry, xattr_name); if (xattr_len < 0) return xattr_len; @@ -945,7 +963,7 @@ ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, if (!buf) return -ENOMEM; - xattr_len = vfs_getxattr(&init_user_ns, dentry, xattr_name, + xattr_len = vfs_getxattr(user_ns, dentry, xattr_name, (void *)buf, xattr_len); if (xattr_len > 0) *xattr_buf = buf; @@ -956,6 +974,7 @@ ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, /** * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value + * @user_ns: user namespace * @dentry: dentry to set XATTR at * @name: xattr name for setxattr * @value: xattr value to set @@ -964,12 +983,14 @@ ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, * * Return: 0 on success, otherwise error */ -int ksmbd_vfs_setxattr(struct dentry *dentry, const char *attr_name, +int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + struct dentry *dentry, const char *attr_name, const void *attr_value, size_t attr_size, int flags) { int err; - err = vfs_setxattr(&init_user_ns, dentry, + err = vfs_setxattr(user_ns, + dentry, attr_name, attr_value, attr_size, @@ -1076,12 +1097,14 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, return ret; } -int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name) +int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name) { - return vfs_removexattr(&init_user_ns, dentry, attr_name); + return vfs_removexattr(user_ns, dentry, attr_name); } -int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry) +int ksmbd_vfs_unlink(struct user_namespace *user_ns, + struct dentry *dir, struct dentry *dentry) { int err = 0; @@ -1091,9 +1114,9 @@ int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry) dget(dentry); if (S_ISDIR(d_inode(dentry)->i_mode)) - err = vfs_rmdir(&init_user_ns, d_inode(dir), dentry); + err = vfs_rmdir(user_ns, d_inode(dir), dentry); else - err = vfs_unlink(&init_user_ns, d_inode(dir), dentry, NULL); + err = vfs_unlink(user_ns, d_inode(dir), dentry, NULL); dput(dentry); inode_unlock(d_inode(dir)); @@ -1267,7 +1290,8 @@ out: return err; } -int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry) +int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + struct dentry *dentry) { char *name, *xattr_list = NULL; ssize_t xattr_list_len; @@ -1289,7 +1313,7 @@ int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry) sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) || !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) { - err = ksmbd_vfs_remove_xattr(dentry, name); + err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); if (err) ksmbd_debug(SMB, "remove acl xattr failed : %s\n", name); @@ -1300,7 +1324,8 @@ out: return err; } -int ksmbd_vfs_remove_sd_xattrs(struct dentry *dentry) +int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + struct dentry *dentry) { char *name, *xattr_list = NULL; ssize_t xattr_list_len; @@ -1319,7 +1344,7 @@ int ksmbd_vfs_remove_sd_xattrs(struct dentry *dentry) ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { - err = ksmbd_vfs_remove_xattr(dentry, name); + err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); if (err) ksmbd_debug(SMB, "remove xattr failed : %s\n", name); } @@ -1329,7 +1354,8 @@ out: return err; } -static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct inode *inode, +static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespace *user_ns, + struct inode *inode, int acl_type) { struct xattr_smb_acl *smb_acl = NULL; @@ -1355,14 +1381,14 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct inode *inode, switch (pa_entry->e_tag) { case ACL_USER: xa_entry->type = SMB_ACL_USER; - xa_entry->uid = from_kuid(&init_user_ns, pa_entry->e_uid); + xa_entry->uid = from_kuid(user_ns, pa_entry->e_uid); break; case ACL_USER_OBJ: xa_entry->type = SMB_ACL_USER_OBJ; break; case ACL_GROUP: xa_entry->type = SMB_ACL_GROUP; - xa_entry->gid = from_kgid(&init_user_ns, pa_entry->e_gid); + xa_entry->gid = from_kgid(user_ns, pa_entry->e_gid); break; case ACL_GROUP_OBJ: xa_entry->type = SMB_ACL_GROUP_OBJ; @@ -1390,7 +1416,9 @@ out: return smb_acl; } -int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, struct smb_ntsd *pntsd, int len) { int rc; @@ -1422,12 +1450,14 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, return rc; } - smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, ACL_TYPE_ACCESS); + smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS); if (S_ISDIR(inode->i_mode)) - def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT); - rc = ndr_encode_posix_acl(&acl_ndr, inode, smb_acl, def_smb_acl); + rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, + smb_acl, def_smb_acl); if (rc) { pr_err("failed to encode ndr to posix acl\n"); goto out; @@ -1446,7 +1476,8 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, goto out; } - rc = ksmbd_vfs_setxattr(dentry, XATTR_NAME_SD, sd_ndr.data, + rc = ksmbd_vfs_setxattr(user_ns, dentry, + XATTR_NAME_SD, sd_ndr.data, sd_ndr.offset, 0); if (rc < 0) pr_err("Failed to store XATTR ntacl :%d\n", rc); @@ -1459,13 +1490,15 @@ out: return rc; } -int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, struct smb_ntsd **pntsd) { int rc; struct ndr n; - rc = ksmbd_vfs_getxattr(dentry, XATTR_NAME_SD, &n.data); + rc = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_SD, &n.data); if (rc > 0) { struct inode *inode = d_inode(dentry); struct ndr acl_ndr = {0}; @@ -1478,13 +1511,15 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, if (rc) return rc; - smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, + smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, ACL_TYPE_ACCESS); if (S_ISDIR(inode->i_mode)) - def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(inode, + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, + inode, ACL_TYPE_DEFAULT); - rc = ndr_encode_posix_acl(&acl_ndr, inode, smb_acl, def_smb_acl); + rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, + smb_acl, def_smb_acl); if (rc) { pr_err("failed to encode ndr to posix acl\n"); goto out; @@ -1522,7 +1557,8 @@ out: return rc; } -int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, +int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, struct xattr_dos_attrib *da) { struct ndr n; @@ -1532,7 +1568,7 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, if (err) return err; - err = ksmbd_vfs_setxattr(dentry, XATTR_NAME_DOS_ATTRIBUTE, + err = ksmbd_vfs_setxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, (void *)n.data, n.offset, 0); if (err) ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); @@ -1541,13 +1577,14 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, return err; } -int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, +int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, struct xattr_dos_attrib *da) { struct ndr n; int err; - err = ksmbd_vfs_getxattr(dentry, XATTR_NAME_DOS_ATTRIBUTE, + err = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, (char **)&n.data); if (err > 0) { n.length = err; @@ -1593,13 +1630,15 @@ void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) return info; } -int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct dentry *dentry, struct ksmbd_kstat *ksmbd_kstat) { u64 time; int rc; - generic_fillattr(&init_user_ns, d_inode(dentry), ksmbd_kstat->kstat); + generic_fillattr(user_ns, d_inode(dentry), ksmbd_kstat->kstat); time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); ksmbd_kstat->create_time = time; @@ -1617,7 +1656,7 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { struct xattr_dos_attrib da; - rc = ksmbd_vfs_get_dos_attrib_xattr(dentry, &da); + rc = ksmbd_vfs_get_dos_attrib_xattr(user_ns, dentry, &da); if (rc > 0) { ksmbd_kstat->file_attributes = cpu_to_le32(da.attr); ksmbd_kstat->create_time = da.create_time; @@ -1629,7 +1668,8 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, return 0; } -ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, char *attr_name, +ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, int attr_name_len) { char *name, *xattr_list = NULL; @@ -1645,7 +1685,7 @@ ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, char *attr_name, if (strncasecmp(attr_name, name, attr_name_len)) continue; - value_len = ksmbd_vfs_xattr_len(dentry, name); + value_len = ksmbd_vfs_xattr_len(user_ns, dentry, name); break; } @@ -1775,7 +1815,8 @@ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) locks_delete_block(flock); } -int ksmbd_vfs_set_init_posix_acl(struct inode *inode) +int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + struct inode *inode) { struct posix_acl_state acl_state; struct posix_acl *acls; @@ -1804,13 +1845,13 @@ int ksmbd_vfs_set_init_posix_acl(struct inode *inode) return -ENOMEM; } posix_state_to_acl(&acl_state, acls->a_entries); - rc = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, acls); + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", rc); else if (S_ISDIR(inode->i_mode)) { posix_state_to_acl(&acl_state, acls->a_entries); - rc = set_posix_acl(&init_user_ns, inode, ACL_TYPE_DEFAULT, + rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", @@ -1821,7 +1862,8 @@ int ksmbd_vfs_set_init_posix_acl(struct inode *inode) return rc; } -int ksmbd_vfs_inherit_posix_acl(struct inode *inode, struct inode *parent_inode) +int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + struct inode *inode, struct inode *parent_inode) { struct posix_acl *acls; struct posix_acl_entry *pace; @@ -1839,12 +1881,12 @@ int ksmbd_vfs_inherit_posix_acl(struct inode *inode, struct inode *parent_inode) } } - rc = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, acls); + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", rc); if (S_ISDIR(inode->i_mode)) { - rc = set_posix_acl(&init_user_ns, inode, ACL_TYPE_DEFAULT, + rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h index e30174a0e5a1..b255f90acf8f 100644 --- a/fs/ksmbd/vfs.h +++ b/fs/ksmbd/vfs.h @@ -108,8 +108,9 @@ struct ksmbd_kstat { }; int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child); -int ksmbd_vfs_may_delete(struct dentry *dentry); -int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess); +int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry); +int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess); int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, @@ -136,15 +137,20 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, unsigned int *chunk_size_written, loff_t *total_size_written); ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); -ssize_t ksmbd_vfs_getxattr(struct dentry *dentry, char *xattr_name, +ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + struct dentry *dentry, + char *xattr_name, char **xattr_buf); -ssize_t ksmbd_vfs_casexattr_len(struct dentry *dentry, char *attr_name, +ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, int attr_name_len); -int ksmbd_vfs_setxattr(struct dentry *dentry, const char *attr_name, +int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + struct dentry *dentry, const char *attr_name, const void *attr_value, size_t attr_size, int flags); int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, size_t *xattr_stream_name_size, int s_type); -int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name); +int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name); int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, bool caseless); int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); @@ -155,24 +161,37 @@ struct file_allocated_range_buffer; int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, struct file_allocated_range_buffer *ranges, int in_count, int *out_count); -int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry); +int ksmbd_vfs_unlink(struct user_namespace *user_ns, + struct dentry *dir, struct dentry *dentry); void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); -int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct dentry *dentry, struct ksmbd_kstat *ksmbd_kstat); int ksmbd_vfs_posix_lock_wait(struct file_lock *flock); int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); -int ksmbd_vfs_remove_acl_xattrs(struct dentry *dentry); -int ksmbd_vfs_remove_sd_xattrs(struct dentry *dentry); -int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, +int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + struct dentry *dentry); +int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + struct dentry *dentry); +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, struct smb_ntsd *pntsd, int len); -int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct dentry *dentry, +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, struct smb_ntsd **pntsd); -int ksmbd_vfs_set_dos_attrib_xattr(struct dentry *dentry, +int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, struct xattr_dos_attrib *da); -int ksmbd_vfs_get_dos_attrib_xattr(struct dentry *dentry, +int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, struct xattr_dos_attrib *da); -int ksmbd_vfs_set_init_posix_acl(struct inode *inode); -int ksmbd_vfs_inherit_posix_acl(struct inode *inode, +int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + struct inode *inode); +int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + struct inode *inode, struct inode *parent_inode); #endif /* __KSMBD_VFS_H__ */ diff --git a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c index 5c9efcfaeb5c..1941ad3f5aa5 100644 --- a/fs/ksmbd/vfs_cache.c +++ b/fs/ksmbd/vfs_cache.c @@ -251,7 +251,8 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) filp = fp->filp; if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { ci->m_flags &= ~S_DEL_ON_CLS_STREAM; - err = ksmbd_vfs_remove_xattr(filp->f_path.dentry, + err = ksmbd_vfs_remove_xattr(file_mnt_user_ns(filp), + filp->f_path.dentry, fp->stream.name); if (err) pr_err("remove xattr failed : %s\n", @@ -265,7 +266,7 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) dir = dentry->d_parent; ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); write_unlock(&ci->m_lock); - ksmbd_vfs_unlink(dir, dentry); + ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry); write_lock(&ci->m_lock); } write_unlock(&ci->m_lock); From 690f969705138b235b9fa4c4d19e5129ed54a845 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 3 Jul 2021 08:00:06 +0900 Subject: [PATCH 171/417] ksmbd: fix kernel oops in ksmbd_rpc_ioctl/rap() "ksmbd: remove macros in transport_ipc.c" commit change msg to req in ksmbd_rpc_ioctl/rap(). This will cause kernel oops when running smbclient -L test. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/transport_ipc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ksmbd/transport_ipc.c b/fs/ksmbd/transport_ipc.c index ca5099118fdf..44aea33a67fa 100644 --- a/fs/ksmbd/transport_ipc.c +++ b/fs/ksmbd/transport_ipc.c @@ -752,7 +752,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle return NULL; msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)req->payload; + req = (struct ksmbd_rpc_command *)msg->payload; req->handle = handle; req->flags = ksmbd_session_rpc_method(sess, handle); req->flags |= rpc_context_flags(sess); @@ -777,7 +777,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payloa return NULL; msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)req->payload; + req = (struct ksmbd_rpc_command *)msg->payload; req->handle = ksmbd_acquire_id(&ipc_ida); req->flags = rpc_context_flags(sess); req->flags |= KSMBD_RPC_RAP_METHOD; From 465d720485eff1468503d210b1b966660f5f9b85 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 3 Jul 2021 12:10:36 +0900 Subject: [PATCH 172/417] ksmbd: call mnt_user_ns once in a function Avoid calling mnt_user_ns() many time in a function. Cc: Christoph Hellwig Cc: Christian Brauner Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/oplock.c | 5 +-- fs/ksmbd/smb2pdu.c | 77 +++++++++++++++++++++++++------------------ fs/ksmbd/smb_common.c | 5 +-- fs/ksmbd/smbacl.c | 25 +++++++------- fs/ksmbd/vfs.c | 5 +-- 5 files changed, 68 insertions(+), 49 deletions(-) diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index 5484b5bf75b0..71063568dfee 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -1582,6 +1582,7 @@ void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) { struct create_posix_rsp *buf; struct inode *inode = file_inode(fp->filp); + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); buf = (struct create_posix_rsp *)cc; memset(buf, 0, sizeof(struct create_posix_rsp)); @@ -1612,9 +1613,9 @@ void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) buf->nlink = cpu_to_le32(inode->i_nlink); buf->reparse_tag = cpu_to_le32(fp->volatile_id); buf->mode = cpu_to_le32(inode->i_mode); - id_to_sid(from_kuid(file_mnt_user_ns(fp->filp), inode->i_uid), + id_to_sid(from_kuid(user_ns, inode->i_uid), SIDNFS_USER, (struct smb_sid *)&buf->SidBuffer[0]); - id_to_sid(from_kgid(file_mnt_user_ns(fp->filp), inode->i_gid), + id_to_sid(from_kgid(user_ns, inode->i_gid), SIDNFS_GROUP, (struct smb_sid *)&buf->SidBuffer[20]); } diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index dda90812feef..d4ef8f55fa4b 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2050,6 +2050,7 @@ out: */ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) { + struct user_namespace *user_ns = mnt_user_ns(path->mnt); char *attr_name = NULL, *value; int rc = 0; int next = 0; @@ -2081,7 +2082,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) value = (char *)&eabuf->name + eabuf->EaNameLength + 1; if (!eabuf->EaValueLength) { - rc = ksmbd_vfs_casexattr_len(mnt_user_ns(path->mnt), + rc = ksmbd_vfs_casexattr_len(user_ns, path->dentry, attr_name, XATTR_USER_PREFIX_LEN + @@ -2089,7 +2090,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) /* delete the EA only when it exits */ if (rc > 0) { - rc = ksmbd_vfs_remove_xattr(mnt_user_ns(path->mnt), + rc = ksmbd_vfs_remove_xattr(user_ns, path->dentry, attr_name); @@ -2104,7 +2105,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) /* if the EA doesn't exist, just do nothing. */ rc = 0; } else { - rc = ksmbd_vfs_setxattr(mnt_user_ns(path->mnt), + rc = ksmbd_vfs_setxattr(user_ns, path->dentry, attr_name, value, le16_to_cpu(eabuf->EaValueLength), 0); if (rc < 0) { @@ -2143,6 +2144,7 @@ static noinline int smb2_set_stream_name_xattr(struct path *path, struct ksmbd_file *fp, char *stream_name, int s_type) { + struct user_namespace *user_ns = mnt_user_ns(path->mnt); size_t xattr_stream_size; char *xattr_stream_name; int rc; @@ -2158,7 +2160,7 @@ static noinline int smb2_set_stream_name_xattr(struct path *path, fp->stream.size = xattr_stream_size; /* Check if there is stream prefix in xattr space */ - rc = ksmbd_vfs_casexattr_len(mnt_user_ns(path->mnt), + rc = ksmbd_vfs_casexattr_len(user_ns, path->dentry, xattr_stream_name, xattr_stream_size); @@ -2170,8 +2172,8 @@ static noinline int smb2_set_stream_name_xattr(struct path *path, return -EBADF; } - rc = ksmbd_vfs_setxattr(mnt_user_ns(path->mnt), - path->dentry, xattr_stream_name, NULL, 0, 0); + rc = ksmbd_vfs_setxattr(user_ns, path->dentry, + xattr_stream_name, NULL, 0, 0); if (rc < 0) pr_err("Failed to store XATTR stream name :%d\n", rc); return 0; @@ -2179,6 +2181,7 @@ static noinline int smb2_set_stream_name_xattr(struct path *path, static int smb2_remove_smb_xattrs(struct path *path) { + struct user_namespace *user_ns = mnt_user_ns(path->mnt); char *name, *xattr_list = NULL; ssize_t xattr_list_len; int err = 0; @@ -2201,8 +2204,7 @@ static int smb2_remove_smb_xattrs(struct path *path) strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, STREAM_PREFIX_LEN)) continue; - err = ksmbd_vfs_remove_xattr(mnt_user_ns(path->mnt), - path->dentry, name); + err = ksmbd_vfs_remove_xattr(user_ns, path->dentry, name); if (err) ksmbd_debug(SMB, "remove xattr failed : %s\n", name); } @@ -2366,6 +2368,7 @@ int smb2_open(struct ksmbd_work *work) struct ksmbd_share_config *share = tcon->share_conf; struct ksmbd_file *fp = NULL; struct file *filp = NULL; + struct user_namespace *user_ns = NULL; struct kstat stat; struct create_context *context; struct lease_ctx_info *lc = NULL; @@ -2642,7 +2645,8 @@ int smb2_open(struct ksmbd_work *work) rc = 0; } else { file_present = true; - generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); + user_ns = mnt_user_ns(path.mnt); + generic_fillattr(user_ns, d_inode(path.dentry), &stat); } if (stream_name) { if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { @@ -2703,7 +2707,7 @@ int smb2_open(struct ksmbd_work *work) if (!file_present) { daccess = cpu_to_le32(GENERIC_ALL_FLAGS); } else { - rc = ksmbd_vfs_query_maximal_access(mnt_user_ns(path.mnt), + rc = ksmbd_vfs_query_maximal_access(user_ns, path.dentry, &daccess); if (rc) @@ -2734,6 +2738,7 @@ int smb2_open(struct ksmbd_work *work) goto err_out; created = true; + user_ns = mnt_user_ns(path.mnt); if (ea_buf) { rc = smb2_set_ea(&ea_buf->ea, &path); if (rc == -EOPNOTSUPP) @@ -2747,7 +2752,7 @@ int smb2_open(struct ksmbd_work *work) * is already granted. */ if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { - rc = inode_permission(mnt_user_ns(path.mnt), + rc = inode_permission(user_ns, d_inode(path.dentry), may_flags); if (rc) @@ -2755,7 +2760,7 @@ int smb2_open(struct ksmbd_work *work) if ((daccess & FILE_DELETE_LE) || (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { - rc = ksmbd_vfs_may_delete(mnt_user_ns(path.mnt), + rc = ksmbd_vfs_may_delete(user_ns, path.dentry); if (rc) goto err_out; @@ -2819,7 +2824,7 @@ int smb2_open(struct ksmbd_work *work) int posix_acl_rc; struct inode *inode = d_inode(path.dentry); - posix_acl_rc = ksmbd_vfs_inherit_posix_acl(mnt_user_ns(path.mnt), + posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns, inode, d_inode(path.dentry->d_parent)); if (posix_acl_rc) @@ -2835,7 +2840,7 @@ int smb2_open(struct ksmbd_work *work) rc = smb2_create_sd_buffer(work, req, &path); if (rc) { if (posix_acl_rc) - ksmbd_vfs_set_init_posix_acl(mnt_user_ns(path.mnt), + ksmbd_vfs_set_init_posix_acl(user_ns, inode); if (test_share_config_flag(work->tcon->share_conf, @@ -2858,7 +2863,7 @@ int smb2_open(struct ksmbd_work *work) if (!pntsd) goto err_out; - rc = build_sec_desc(mnt_user_ns(path.mnt), + rc = build_sec_desc(user_ns, pntsd, NULL, OWNER_SECINFO | GROUP_SECINFO | @@ -2868,7 +2873,7 @@ int smb2_open(struct ksmbd_work *work) posix_acl_release(fattr.cf_dacls); rc = ksmbd_vfs_set_sd_xattr(conn, - mnt_user_ns(path.mnt), + user_ns, path.dentry, pntsd, pntsd_size); @@ -2910,7 +2915,7 @@ int smb2_open(struct ksmbd_work *work) rc = ksmbd_vfs_getattr(&path, &stat); if (rc) { - generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); + generic_fillattr(user_ns, d_inode(path.dentry), &stat); rc = 0; } @@ -3011,7 +3016,7 @@ int smb2_open(struct ksmbd_work *work) memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); - generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + generic_fillattr(user_ns, file_inode(fp->filp), &stat); rsp->StructureSize = cpu_to_le16(89); @@ -3064,7 +3069,7 @@ int smb2_open(struct ksmbd_work *work) struct create_context *mxac_ccontext; if (maximal_access == 0) - ksmbd_vfs_query_maximal_access(mnt_user_ns(path.mnt), + ksmbd_vfs_query_maximal_access(user_ns, path.dentry, &maximal_access); mxac_ccontext = (struct create_context *)(rsp->Buffer + @@ -3482,6 +3487,7 @@ static void unlock_dir(struct ksmbd_file *dir_fp) static int process_query_dir_entries(struct smb2_query_dir_private *priv) { + struct user_namespace *user_ns = file_mnt_user_ns(priv->dir_fp->filp); struct kstat kstat; struct ksmbd_kstat ksmbd_kstat; int rc; @@ -3515,14 +3521,14 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv) ksmbd_kstat.kstat = &kstat; if (priv->info_level != FILE_NAMES_INFORMATION) ksmbd_vfs_fill_dentry_attrs(priv->work, - file_mnt_user_ns(priv->dir_fp->filp), + user_ns, dent, &ksmbd_kstat); rc = smb2_populate_readdir_entry(priv->work->conn, priv->info_level, priv->d_info, - file_mnt_user_ns(priv->dir_fp->filp), + user_ns, &ksmbd_kstat); dput(dent); if (rc) @@ -3981,6 +3987,7 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, ssize_t buf_free_len, alignment_bytes, next_offset, rsp_data_cnt = 0; struct smb2_ea_info_req *ea_req = NULL; struct path *path; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); if (!(fp->daccess & FILE_READ_EA_LE)) { pr_err("Not permitted to read ext attr : 0x%x\n", @@ -4057,8 +4064,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, buf_free_len -= (offsetof(struct smb2_ea_info, name) + name_len + 1); /* bailout if xattr can't fit in buf_free_len */ - value_len = ksmbd_vfs_getxattr(mnt_user_ns(path->mnt), - path->dentry, name, &buf); + value_len = ksmbd_vfs_getxattr(user_ns, path->dentry, + name, &buf); if (value_len <= 0) { rc = -ENOENT; rsp->hdr.Status = STATUS_INVALID_HANDLE; @@ -4909,6 +4916,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, struct smb2_query_info_rsp *rsp, void *rsp_org) { struct ksmbd_file *fp; + struct user_namespace *user_ns; struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; struct smb_fattr fattr = {{0}}; struct inode *inode; @@ -4955,16 +4963,17 @@ static int smb2_get_info_sec(struct ksmbd_work *work, if (!fp) return -ENOENT; + user_ns = file_mnt_user_ns(fp->filp); inode = file_inode(fp->filp); ksmbd_acls_fattr(&fattr, inode); if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) - ksmbd_vfs_get_sd_xattr(work->conn, file_mnt_user_ns(fp->filp), + ksmbd_vfs_get_sd_xattr(work->conn, user_ns, fp->filp->f_path.dentry, &ppntsd); - rc = build_sec_desc(file_mnt_user_ns(fp->filp), - pntsd, ppntsd, addition_info, &secdesclen, &fattr); + rc = build_sec_desc(user_ns, pntsd, ppntsd, addition_info, + &secdesclen, &fattr); posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); kfree(ppntsd); @@ -5388,6 +5397,7 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, struct iattr temp_attrs; struct file *filp; struct inode *inode; + struct user_namespace *user_ns; int rc; if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE)) @@ -5397,6 +5407,7 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, attrs.ia_valid = 0; filp = fp->filp; inode = file_inode(filp); + user_ns = file_mnt_user_ns(filp); if (file_info->CreationTime) fp->create_time = le64_to_cpu(file_info->CreationTime); @@ -5442,7 +5453,7 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | XATTR_DOSINFO_ITIME; - rc = ksmbd_vfs_set_dos_attrib_xattr(file_mnt_user_ns(filp), + rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, filp->f_path.dentry, &da); if (rc) ksmbd_debug(SMB, @@ -5464,14 +5475,14 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) return -EACCES; - rc = setattr_prepare(file_mnt_user_ns(filp), dentry, &attrs); + rc = setattr_prepare(user_ns, dentry, &attrs); if (rc) return -EINVAL; inode_lock(inode); - setattr_copy(file_mnt_user_ns(filp), inode, &attrs); + setattr_copy(user_ns, inode, &attrs); attrs.ia_valid &= ~ATTR_CTIME; - rc = notify_change(file_mnt_user_ns(filp), dentry, &attrs, NULL); + rc = notify_change(user_ns, dentry, &attrs, NULL); inode_unlock(inode); } return 0; @@ -7201,12 +7212,14 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, struct file_sparse *sparse) { struct ksmbd_file *fp; + struct user_namespace *user_ns; int ret = 0; __le32 old_fattr; fp = ksmbd_lookup_fd_fast(work, id); if (!fp) return -ENOENT; + user_ns = file_mnt_user_ns(fp->filp); old_fattr = fp->f_ci->m_fattr; if (sparse->SetSparse) @@ -7219,13 +7232,13 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { struct xattr_dos_attrib da; - ret = ksmbd_vfs_get_dos_attrib_xattr(file_mnt_user_ns(fp->filp), + ret = ksmbd_vfs_get_dos_attrib_xattr(user_ns, fp->filp->f_path.dentry, &da); if (ret <= 0) goto out; da.attr = le32_to_cpu(fp->f_ci->m_fattr); - ret = ksmbd_vfs_set_dos_attrib_xattr(file_mnt_user_ns(fp->filp), + ret = ksmbd_vfs_set_dos_attrib_xattr(user_ns, fp->filp->f_path.dentry, &da); if (ret) fp->f_ci->m_fattr = old_fattr; diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c index f770f3ffb840..38026d9bb704 100644 --- a/fs/ksmbd/smb_common.c +++ b/fs/ksmbd/smb_common.c @@ -279,6 +279,7 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, { int i, rc = 0; struct ksmbd_conn *conn = work->conn; + struct user_namespace *user_ns = file_mnt_user_ns(dir->filp); for (i = 0; i < 2; i++) { struct kstat kstat; @@ -301,11 +302,11 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, ksmbd_kstat.kstat = &kstat; ksmbd_vfs_fill_dentry_attrs(work, - file_mnt_user_ns(dir->filp), + user_ns, dir->filp->f_path.dentry->d_parent, &ksmbd_kstat); rc = fn(conn, info_level, d_info, - file_mnt_user_ns(dir->filp), &ksmbd_kstat); + user_ns, &ksmbd_kstat); if (rc) break; if (d_info->out_buf_len <= 0) diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c index 4ee714b9b351..86ef2a701cc2 100644 --- a/fs/ksmbd/smbacl.c +++ b/fs/ksmbd/smbacl.c @@ -960,12 +960,13 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct smb_ntsd *parent_pntsd = NULL; struct smb_sid owner_sid, group_sid; struct dentry *parent = path->dentry->d_parent; + struct user_namespace *user_ns = mnt_user_ns(path->mnt); int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0; int rc = -ENOENT, num_aces, dacloffset, pntsd_type, acl_len; char *aces_base; bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); - acl_len = ksmbd_vfs_get_sd_xattr(conn, mnt_user_ns(path->mnt), + acl_len = ksmbd_vfs_get_sd_xattr(conn, user_ns, parent, &parent_pntsd); if (acl_len <= 0) return rc; @@ -1097,7 +1098,7 @@ pass: pntsd_size += sizeof(struct smb_acl) + nt_size; } - ksmbd_vfs_set_sd_xattr(conn, mnt_user_ns(path->mnt), + ksmbd_vfs_set_sd_xattr(conn, user_ns, path->dentry, pntsd, pntsd_size); kfree(pntsd); rc = 0; @@ -1124,6 +1125,7 @@ bool smb_inherit_flags(int flags, bool is_dir) int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, __le32 *pdaccess, int uid) { + struct user_namespace *user_ns = mnt_user_ns(path->mnt); struct smb_ntsd *pntsd = NULL; struct smb_acl *pdacl; struct posix_acl *posix_acls; @@ -1139,7 +1141,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, char *end_of_acl; ksmbd_debug(SMB, "check permission using windows acl\n"); - acl_size = ksmbd_vfs_get_sd_xattr(conn, mnt_user_ns(path->mnt), + acl_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, path->dentry, &pntsd); if (acl_size <= 0 || !pntsd || !pntsd->dacloffset) { kfree(pntsd); @@ -1221,10 +1223,10 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, pa_entry = posix_acls->a_entries; for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { if (pa_entry->e_tag == ACL_USER) - id = from_kuid(mnt_user_ns(path->mnt), + id = from_kuid(user_ns, pa_entry->e_uid); else if (pa_entry->e_tag == ACL_GROUP) - id = from_kgid(mnt_user_ns(path->mnt), + id = from_kgid(user_ns, pa_entry->e_gid); else continue; @@ -1282,12 +1284,13 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, int rc; struct smb_fattr fattr = {{0}}; struct inode *inode = d_inode(path->dentry); + struct user_namespace *user_ns = mnt_user_ns(path->mnt); fattr.cf_uid = INVALID_UID; fattr.cf_gid = INVALID_GID; fattr.cf_mode = inode->i_mode; - rc = parse_sec_desc(mnt_user_ns(path->mnt), pntsd, ntsd_len, &fattr); + rc = parse_sec_desc(user_ns, pntsd, ntsd_len, &fattr); if (rc) goto out; @@ -1298,13 +1301,13 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, inode->i_gid = fattr.cf_gid; mark_inode_dirty(inode); - ksmbd_vfs_remove_acl_xattrs(mnt_user_ns(path->mnt), path->dentry); + ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); /* Update posix acls */ if (fattr.cf_dacls) { - rc = set_posix_acl(mnt_user_ns(path->mnt), inode, + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, fattr.cf_acls); if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) - rc = set_posix_acl(mnt_user_ns(path->mnt), inode, + rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, fattr.cf_dacls); } @@ -1314,8 +1317,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { /* Update WinACL in xattr */ - ksmbd_vfs_remove_sd_xattrs(mnt_user_ns(path->mnt), path->dentry); - ksmbd_vfs_set_sd_xattr(conn, mnt_user_ns(path->mnt), + ksmbd_vfs_remove_sd_xattrs(user_ns, path->dentry); + ksmbd_vfs_set_sd_xattr(conn, user_ns, path->dentry, pntsd, ntsd_len); } diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index 0f5a4fb8215f..7339d5c74aad 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -406,6 +406,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, size_t count) { char *stream_buf = NULL, *wbuf; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); size_t size, v_len; int err = 0; @@ -418,7 +419,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, count = (*pos + count) - XATTR_SIZE_MAX; } - v_len = ksmbd_vfs_getcasexattr(file_mnt_user_ns(fp->filp), + v_len = ksmbd_vfs_getcasexattr(user_ns, fp->filp->f_path.dentry, fp->stream.name, fp->stream.size, @@ -444,7 +445,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, memcpy(&stream_buf[*pos], buf, count); - err = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp), + err = ksmbd_vfs_setxattr(user_ns, fp->filp->f_path.dentry, fp->stream.name, (void *)stream_buf, From 4951a84f61d6de4ab5aca1d49a6b6ee2ad2d1eec Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 6 Jul 2021 13:05:01 +0100 Subject: [PATCH 173/417] ksmbd: Fix read on the uninitialized pointer sess There is a error handling case that passes control to label out_err without pointer sess being assigned a value. The unassigned pointer may be any garbage value and so the test of rc < 0 && sess maybe true leading to sess being passed to the call to ksmbd_session_destroy. Fix this by setting sess to NULL in this corner case. Addresses-Coverity: ("Uninitialized pointer read") Signed-off-by: Colin Ian King Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index d4ef8f55fa4b..994b95b6b3c2 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -1615,6 +1615,7 @@ int smb2_sess_setup(struct ksmbd_work *work) } else if ((conn->dialect < SMB30_PROT_ID || server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + sess = NULL; rc = -EACCES; goto out_err; } else { From 6cfbcf2f40e371ce36c030addc539597d058b3a9 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 7 Jul 2021 14:55:31 +0900 Subject: [PATCH 174/417] ksmbd: remove unneeded NULL check in for_each_netdev netdev can never be NULL in for_each_netdev loop. This patch remove unneeded NULL check. Reported-by: Coverity Scan Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 994b95b6b3c2..2811dfabfa75 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -7004,11 +7004,6 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, rtnl_lock(); for_each_netdev(&init_net, netdev) { - if (unlikely(!netdev)) { - rtnl_unlock(); - return -EINVAL; - } - if (netdev->type == ARPHRD_LOOPBACK) continue; From b8fc94cdb144467d88f35344076fd3621af93a17 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 7 Jul 2021 14:56:44 +0900 Subject: [PATCH 175/417] ksmbd: fix read on the uninitialized send_ctx If st->status is not SMB_DIRECT_CS_CONNECTED, It will jump done label and accessing the uninitialized send_ctxi by smb_direct_flush_send_list will cause kernel oops. This patch just return -ENOTCONN to avoid it. Reported-by: Coverity Scan Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/transport_rdma.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c index 171fb3dd018a..d5728c84a15a 100644 --- a/fs/ksmbd/transport_rdma.c +++ b/fs/ksmbd/transport_rdma.c @@ -1207,10 +1207,8 @@ static int smb_direct_writev(struct ksmbd_transport *t, struct kvec vec; struct smb_direct_send_ctx send_ctx; - if (st->status != SMB_DIRECT_CS_CONNECTED) { - ret = -ENOTCONN; - goto done; - } + if (st->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; //FIXME: skip RFC1002 header.. buflen -= 4; From dac0ec6e1b4a876abb61b6cd2ec589f8e87e95c9 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 7 Jul 2021 14:57:24 +0900 Subject: [PATCH 176/417] ksmbd: fix memory leak smb2_populate_readdir_entry() Add missing kfree(conv_name) on error path. Reported-by: Coverity Scan Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 2811dfabfa75..ea406ff1a8c1 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -3291,7 +3291,7 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, char *conv_name; int conv_len; void *kstat; - int struct_sz; + int struct_sz, rc = 0; conv_name = ksmbd_convert_dir_info_name(d_info, conn->local_nls, @@ -3301,8 +3301,8 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, /* Somehow the name has only terminating NULL bytes */ if (conv_len < 0) { - kfree(conv_name); - return -EINVAL; + rc = -EINVAL; + goto free_conv_name; } struct_sz = readdir_info_level_struct_sz(info_level); @@ -3311,7 +3311,8 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, if (next_entry_offset > d_info->out_buf_len) { d_info->out_buf_len = 0; - return -ENOSPC; + rc = -ENOSPC; + goto free_conv_name; } kstat = d_info->wptr; @@ -3453,14 +3454,15 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, d_info->data_count += next_entry_offset; d_info->out_buf_len -= next_entry_offset; d_info->wptr += next_entry_offset; - kfree(conv_name); ksmbd_debug(SMB, "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", info_level, d_info->out_buf_len, next_entry_offset, d_info->data_count); - return 0; +free_conv_name: + kfree(conv_name); + return rc; } struct smb2_query_dir_private { From a9071e3c8659d777eb6527e1d377021381d1b5ec Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 7 Jul 2021 15:01:21 +0900 Subject: [PATCH 177/417] ksmbd: fix memory leak in smb_inherit_dacl() Add two labels to fix memory leak in smb_inherit_dacl(). Reported-by: Coverity Scan Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smbacl.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c index 86ef2a701cc2..fa99d950a6f2 100644 --- a/fs/ksmbd/smbacl.c +++ b/fs/ksmbd/smbacl.c @@ -962,25 +962,29 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *parent = path->dentry->d_parent; struct user_namespace *user_ns = mnt_user_ns(path->mnt); int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0; - int rc = -ENOENT, num_aces, dacloffset, pntsd_type, acl_len; + int rc = 0, num_aces, dacloffset, pntsd_type, acl_len; char *aces_base; bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); acl_len = ksmbd_vfs_get_sd_xattr(conn, user_ns, parent, &parent_pntsd); if (acl_len <= 0) - return rc; + return -ENOENT; dacloffset = le32_to_cpu(parent_pntsd->dacloffset); - if (!dacloffset) - goto out; + if (!dacloffset) { + rc = -EINVAL; + goto free_parent_pntsd; + } parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); num_aces = le32_to_cpu(parent_pdacl->num_aces); pntsd_type = le16_to_cpu(parent_pntsd->type); aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); - if (!aces_base) - goto out; + if (!aces_base) { + rc = -ENOMEM; + goto free_parent_pntsd; + } aces = (struct smb_ace *)aces_base; parent_aces = (struct smb_ace *)((char *)parent_pdacl + @@ -1060,7 +1064,7 @@ pass: nt_size, GFP_KERNEL); if (!pntsd) { rc = -ENOMEM; - goto out; + goto free_aces_base; } pntsd->revision = cpu_to_le16(1); @@ -1101,11 +1105,12 @@ pass: ksmbd_vfs_set_sd_xattr(conn, user_ns, path->dentry, pntsd, pntsd_size); kfree(pntsd); - rc = 0; } +free_aces_base: kfree(aces_base); -out: +free_parent_pntsd: + kfree(parent_pntsd); return rc; } From 3867369ef8f760155da684e10d29e0bf9b733b48 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 8 Jul 2021 12:32:27 +0900 Subject: [PATCH 178/417] ksmbd: change data type of volatile/persistent id to u64 This patch change data type of volatile/persistent id to u64 to make issue from idr_find and idr_remove(). !HAS_FILE_ID check will protect integer overflow issue from idr_find and idr_remove(). Reported-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/ksmbd_work.h | 6 +++--- fs/ksmbd/smb2pdu.c | 37 +++++++++++++++++++------------------ fs/ksmbd/vfs_cache.c | 32 ++++++++++++++++---------------- fs/ksmbd/vfs_cache.h | 20 +++++++++----------- 4 files changed, 47 insertions(+), 48 deletions(-) diff --git a/fs/ksmbd/ksmbd_work.h b/fs/ksmbd/ksmbd_work.h index c655bf371ce5..f7156bc50049 100644 --- a/fs/ksmbd/ksmbd_work.h +++ b/fs/ksmbd/ksmbd_work.h @@ -43,9 +43,9 @@ struct ksmbd_work { * Current Local FID assigned compound response if SMB2 CREATE * command is present in compound request */ - unsigned int compound_fid; - unsigned int compound_pfid; - unsigned int compound_sid; + u64 compound_fid; + u64 compound_pfid; + u64 compound_sid; const struct cred *saved_cred; diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index ea406ff1a8c1..18e275abc68f 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2809,7 +2809,7 @@ int smb2_open(struct ksmbd_work *work) /* Get Persistent-ID */ ksmbd_open_durable_fd(fp); - if (!HAS_FILE_ID(fp->persistent_id)) { + if (!has_file_id(fp->persistent_id)) { rc = -ENOMEM; goto err_out; } @@ -4577,15 +4577,15 @@ static int smb2_get_info_file(struct ksmbd_work *work, } if (work->next_smb2_rcv_hdr_off) { - if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { - ksmbd_debug(SMB, "Compound request set FID = %u\n", + if (!has_file_id(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", work->compound_fid); id = work->compound_fid; pid = work->compound_pfid; } } - if (!HAS_FILE_ID(id)) { + if (!has_file_id(id)) { id = le64_to_cpu(req->VolatileFileId); pid = le64_to_cpu(req->PersistentFileId); } @@ -4949,15 +4949,15 @@ static int smb2_get_info_sec(struct ksmbd_work *work, } if (work->next_smb2_rcv_hdr_off) { - if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { - ksmbd_debug(SMB, "Compound request set FID = %u\n", + if (!has_file_id(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", work->compound_fid); id = work->compound_fid; pid = work->compound_pfid; } } - if (!HAS_FILE_ID(id)) { + if (!has_file_id(id)) { id = le64_to_cpu(req->VolatileFileId); pid = le64_to_cpu(req->PersistentFileId); } @@ -5083,7 +5083,7 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work) */ int smb2_close(struct ksmbd_work *work) { - unsigned int volatile_id = KSMBD_NO_FID; + u64 volatile_id = KSMBD_NO_FID; u64 sess_id; struct smb2_close_req *req; struct smb2_close_rsp *rsp; @@ -5119,15 +5119,16 @@ int smb2_close(struct ksmbd_work *work) } if (work->next_smb2_rcv_hdr_off && - !HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { - if (!HAS_FILE_ID(work->compound_fid)) { + !has_file_id(le64_to_cpu(req->VolatileFileId))) { + if (!has_file_id(work->compound_fid)) { /* file already closed, return FILE_CLOSED */ ksmbd_debug(SMB, "file already closed\n"); rsp->hdr.Status = STATUS_FILE_CLOSED; err = -EBADF; goto out; } else { - ksmbd_debug(SMB, "Compound request set FID = %u:%u\n", + ksmbd_debug(SMB, + "Compound request set FID = %llu:%llu\n", work->compound_fid, work->compound_pfid); volatile_id = work->compound_fid; @@ -5139,7 +5140,7 @@ int smb2_close(struct ksmbd_work *work) } else { volatile_id = le64_to_cpu(req->VolatileFileId); } - ksmbd_debug(SMB, "volatile_id = %u\n", volatile_id); + ksmbd_debug(SMB, "volatile_id = %llu\n", volatile_id); rsp->StructureSize = cpu_to_le16(60); rsp->Reserved = 0; @@ -5789,8 +5790,8 @@ int smb2_set_info(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); - if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { - ksmbd_debug(SMB, "Compound request set FID = %u\n", + if (!has_file_id(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", work->compound_fid); id = work->compound_fid; pid = work->compound_pfid; @@ -5800,7 +5801,7 @@ int smb2_set_info(struct ksmbd_work *work) rsp = work->response_buf; } - if (!HAS_FILE_ID(id)) { + if (!has_file_id(id)) { id = le64_to_cpu(req->VolatileFileId); pid = le64_to_cpu(req->PersistentFileId); } @@ -7287,8 +7288,8 @@ int smb2_ioctl(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); - if (!HAS_FILE_ID(le64_to_cpu(req->VolatileFileId))) { - ksmbd_debug(SMB, "Compound request set FID = %u\n", + if (!has_file_id(le64_to_cpu(req->VolatileFileId))) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", work->compound_fid); id = work->compound_fid; } @@ -7297,7 +7298,7 @@ int smb2_ioctl(struct ksmbd_work *work) rsp = work->response_buf; } - if (!HAS_FILE_ID(id)) + if (!has_file_id(id)) id = le64_to_cpu(req->VolatileFileId); if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) { diff --git a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c index 1941ad3f5aa5..c54c605637a0 100644 --- a/fs/ksmbd/vfs_cache.c +++ b/fs/ksmbd/vfs_cache.c @@ -277,7 +277,7 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) { - if (!HAS_FILE_ID(fp->persistent_id)) + if (!has_file_id(fp->persistent_id)) return; write_lock(&global_ft.lock); @@ -287,7 +287,7 @@ static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) { - if (!HAS_FILE_ID(fp->volatile_id)) + if (!has_file_id(fp->volatile_id)) return; write_lock(&fp->f_ci->m_lock); @@ -327,10 +327,13 @@ static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) } static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, - unsigned int id) + u64 id) { struct ksmbd_file *fp; + if (!has_file_id(id)) + return NULL; + read_lock(&ft->lock); fp = idr_find(ft->idr, id); if (fp) @@ -359,12 +362,12 @@ static void set_close_state_blocked_works(struct ksmbd_file *fp) spin_unlock(&fp->f_lock); } -int ksmbd_close_fd(struct ksmbd_work *work, unsigned int id) +int ksmbd_close_fd(struct ksmbd_work *work, u64 id) { struct ksmbd_file *fp; struct ksmbd_file_table *ft; - if (!HAS_FILE_ID(id)) + if (!has_file_id(id)) return 0; ft = &work->sess->file_table; @@ -404,12 +407,12 @@ static bool __sanity_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *f return true; } -struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, unsigned int id) +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id) { return __ksmbd_lookup_fd(&work->sess->file_table, id); } -struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, unsigned int id) +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id) { struct ksmbd_file *fp = __ksmbd_lookup_fd(&work->sess->file_table, id); @@ -420,19 +423,16 @@ struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, unsigned int id return NULL; } -struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, unsigned int id, - unsigned int pid) +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid) { struct ksmbd_file *fp; - if (!HAS_FILE_ID(id)) { + if (!has_file_id(id)) { id = work->compound_fid; pid = work->compound_pfid; } - if (!HAS_FILE_ID(id)) - return NULL; - fp = __ksmbd_lookup_fd(&work->sess->file_table, id); if (!__sanity_check(work->tcon, fp)) { ksmbd_fd_put(work, fp); @@ -494,7 +494,7 @@ struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) #define OPEN_ID_TYPE_VOLATILE_ID (0) #define OPEN_ID_TYPE_PERSISTENT_ID (1) -static void __open_id_set(struct ksmbd_file *fp, unsigned int id, int type) +static void __open_id_set(struct ksmbd_file *fp, u64 id, int type) { if (type == OPEN_ID_TYPE_VOLATILE_ID) fp->volatile_id = id; @@ -505,7 +505,7 @@ static void __open_id_set(struct ksmbd_file *fp, unsigned int id, int type) static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, int type) { - unsigned int id = 0; + u64 id = 0; int ret; if (type == OPEN_ID_TYPE_VOLATILE_ID && fd_limit_depleted()) { @@ -515,7 +515,7 @@ static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, idr_preload(GFP_KERNEL); write_lock(&ft->lock); - ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX, GFP_NOWAIT); + ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); if (ret >= 0) { id = ret; ret = 0; diff --git a/fs/ksmbd/vfs_cache.h b/fs/ksmbd/vfs_cache.h index 543494f664cb..70e987293564 100644 --- a/fs/ksmbd/vfs_cache.h +++ b/fs/ksmbd/vfs_cache.h @@ -22,7 +22,7 @@ #define FILE_GENERIC_EXECUTE 0X1200a0 #define KSMBD_START_FID 0 -#define KSMBD_NO_FID (UINT_MAX) +#define KSMBD_NO_FID (INT_MAX) #define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) struct ksmbd_conn; @@ -62,8 +62,8 @@ struct ksmbd_inode { struct ksmbd_file { struct file *filp; char *filename; - unsigned int persistent_id; - unsigned int volatile_id; + u64 persistent_id; + u64 volatile_id; spinlock_t f_lock; @@ -122,10 +122,8 @@ struct ksmbd_file_table { struct idr *idr; }; -static inline bool HAS_FILE_ID(unsigned long long req) +static inline bool has_file_id(u64 id) { - unsigned int id = (unsigned int)req; - return id < KSMBD_NO_FID; } @@ -136,11 +134,11 @@ static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) int ksmbd_init_file_table(struct ksmbd_file_table *ft); void ksmbd_destroy_file_table(struct ksmbd_file_table *ft); -int ksmbd_close_fd(struct ksmbd_work *work, unsigned int id); -struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, unsigned int id); -struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, unsigned int id); -struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, unsigned int id, - unsigned int pid); +int ksmbd_close_fd(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid); void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); From 0f6619aee86f11cee0c5063777c4febdf18cb28b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 7 Jul 2021 13:15:40 +0300 Subject: [PATCH 179/417] ksmbd: delete some stray tabs These lines are intended one tab too far. Signed-off-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 18e275abc68f..d81768431249 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -6699,7 +6699,7 @@ int smb2_lock(struct ksmbd_work *work) cmp_lock->start < smb_lock->end) { pr_err("previous lock conflict with zero byte lock range\n"); rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; - goto out; + goto out; } if (smb_lock->zero_len && !cmp_lock->zero_len && @@ -6707,7 +6707,7 @@ int smb2_lock(struct ksmbd_work *work) smb_lock->start < cmp_lock->end) { pr_err("current lock conflict with zero byte lock range\n"); rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; - goto out; + goto out; } if (((cmp_lock->start <= smb_lock->start && From 07781de9051859d2f38a9e199384c64bb1924c47 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 7 Jul 2021 13:15:32 +0300 Subject: [PATCH 180/417] ksmbd: use kasprintf() in ksmbd_vfs_xattr_stream_name() Simplify the code by using kasprintf(). This also silences a Smatch warning: fs/ksmbd/vfs.c:1725 ksmbd_vfs_xattr_stream_name() warn: inconsistent indenting Signed-off-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/vfs.c | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index 7339d5c74aad..38677c20d048 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -1698,35 +1698,20 @@ out: int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, size_t *xattr_stream_name_size, int s_type) { - int stream_name_size; - char *xattr_stream_name_buf; - char *type; - int type_len; + char *type, *buf; if (s_type == DIR_STREAM) type = ":$INDEX_ALLOCATION"; else type = ":$DATA"; - type_len = strlen(type); - stream_name_size = strlen(stream_name); - *xattr_stream_name_size = stream_name_size + XATTR_NAME_STREAM_LEN + 1; - xattr_stream_name_buf = kmalloc(*xattr_stream_name_size + type_len, - GFP_KERNEL); - if (!xattr_stream_name_buf) + buf = kasprintf(GFP_KERNEL, "%s%s%s", + XATTR_NAME_STREAM, stream_name, type); + if (!buf) return -ENOMEM; - memcpy(xattr_stream_name_buf, XATTR_NAME_STREAM, XATTR_NAME_STREAM_LEN); - - if (stream_name_size) { - memcpy(&xattr_stream_name_buf[XATTR_NAME_STREAM_LEN], - stream_name, stream_name_size); - } - memcpy(&xattr_stream_name_buf[*xattr_stream_name_size - 1], type, type_len); - *xattr_stream_name_size += type_len; - - xattr_stream_name_buf[*xattr_stream_name_size - 1] = '\0'; - *xattr_stream_name = xattr_stream_name_buf; + *xattr_stream_name = buf; + *xattr_stream_name_size = strlen(buf) + 1; return 0; } From 4b92841ef27b56883fa4491a3d51db3eef68c481 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 10 Jul 2021 09:31:08 +0900 Subject: [PATCH 181/417] ksmbd: fix the running request count decrement decrement the count of running requests after sending the last response for multi-response requests. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/connection.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index 928e22e19def..6e51e08addee 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -120,7 +120,8 @@ int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) list_empty(&work->async_request_entry)) return 0; - atomic_dec(&conn->req_running); + if (!work->multiRsp) + atomic_dec(&conn->req_running); spin_lock(&conn->request_lock); if (!work->multiRsp) { list_del_init(&work->request_entry); From d63528eb0d43c4796c42aad56889dec12cf4e122 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 10 Jul 2021 16:22:41 +0900 Subject: [PATCH 182/417] ksmbd: free ksmbd_lock when file is closed Append ksmbd_lock into the connection's lock list and the ksmbd_file's lock list. And when a file is closed, detach ksmbd_lock from these lists and free it. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/connection.c | 7 +- fs/ksmbd/connection.h | 6 ++ fs/ksmbd/smb2pdu.c | 154 ++++++++++++++++++++++++++---------------- fs/ksmbd/smb_common.c | 2 - fs/ksmbd/smb_common.h | 2 - fs/ksmbd/vfs_cache.c | 16 +++++ fs/ksmbd/vfs_cache.h | 4 +- 7 files changed, 125 insertions(+), 66 deletions(-) diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index 6e51e08addee..8430848bea45 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -19,8 +19,8 @@ static DEFINE_MUTEX(init_lock); static struct ksmbd_conn_ops default_conn_ops; -static LIST_HEAD(conn_list); -static DEFINE_RWLOCK(conn_list_lock); +LIST_HEAD(conn_list); +DEFINE_RWLOCK(conn_list_lock); /** * ksmbd_conn_free() - free resources of the connection instance @@ -70,6 +70,9 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) spin_lock_init(&conn->credits_lock); ida_init(&conn->async_ida); + spin_lock_init(&conn->llist_lock); + INIT_LIST_HEAD(&conn->lock_list); + write_lock(&conn_list_lock); list_add(&conn->conns_list, &conn_list); write_unlock(&conn_list_lock); diff --git a/fs/ksmbd/connection.h b/fs/ksmbd/connection.h index 98108b41f739..487c2024b0d5 100644 --- a/fs/ksmbd/connection.h +++ b/fs/ksmbd/connection.h @@ -79,6 +79,9 @@ struct ksmbd_conn { char *ntlmssp_cryptkey; }; + spinlock_t llist_lock; + struct list_head lock_list; + struct preauth_integrity_info *preauth_info; bool need_neg; @@ -138,6 +141,9 @@ struct ksmbd_transport { #define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) #define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr)) +extern struct list_head conn_list; +extern rwlock_t conn_list_lock; + bool ksmbd_conn_alive(struct ksmbd_conn *conn); void ksmbd_conn_wait_idle(struct ksmbd_conn *conn); struct ksmbd_conn *ksmbd_conn_alloc(void); diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index d81768431249..99e2368ae672 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -6513,8 +6513,9 @@ static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, lock->flags = flags; if (lock->start == lock->end) lock->zero_len = 1; + INIT_LIST_HEAD(&lock->clist); + INIT_LIST_HEAD(&lock->flist); INIT_LIST_HEAD(&lock->llist); - INIT_LIST_HEAD(&lock->glist); list_add_tail(&lock->llist, lock_list); return lock; @@ -6553,7 +6554,8 @@ int smb2_lock(struct ksmbd_work *work) int cmd = 0; int err = 0, i; u64 lock_start, lock_length; - struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp; + struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp, *tmp2; + struct ksmbd_conn *conn; int nolock = 0; LIST_HEAD(lock_list); LIST_HEAD(rollback_list); @@ -6662,72 +6664,89 @@ int smb2_lock(struct ksmbd_work *work) if (!(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) && !(smb_lock->flags & SMB2_LOCKFLAG_FAIL_IMMEDIATELY)) - goto no_check_gl; + goto no_check_cl; nolock = 1; - /* check locks in global list */ - list_for_each_entry(cmp_lock, &global_lock_list, glist) { - if (file_inode(cmp_lock->fl->fl_file) != - file_inode(smb_lock->fl->fl_file)) - continue; + /* check locks in connection list */ + read_lock(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + spin_lock(&conn->llist_lock); + list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { + if (file_inode(cmp_lock->fl->fl_file) != + file_inode(smb_lock->fl->fl_file)) + continue; - if (smb_lock->fl->fl_type == F_UNLCK) { - if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file && - cmp_lock->start == smb_lock->start && - cmp_lock->end == smb_lock->end && - !lock_defer_pending(cmp_lock->fl)) { - nolock = 0; - locks_free_lock(cmp_lock->fl); - list_del(&cmp_lock->glist); - kfree(cmp_lock); - break; + if (smb_lock->fl->fl_type == F_UNLCK) { + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file && + cmp_lock->start == smb_lock->start && + cmp_lock->end == smb_lock->end && + !lock_defer_pending(cmp_lock->fl)) { + nolock = 0; + list_del(&cmp_lock->flist); + list_del(&cmp_lock->clist); + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + + locks_free_lock(cmp_lock->fl); + kfree(cmp_lock); + goto out_check_cl; + } + continue; } - continue; - } - if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) { - if (smb_lock->flags & SMB2_LOCKFLAG_SHARED) - continue; - } else { - if (cmp_lock->flags & SMB2_LOCKFLAG_SHARED) - continue; - } + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) { + if (smb_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } else { + if (cmp_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } - /* check zero byte lock range */ - if (cmp_lock->zero_len && !smb_lock->zero_len && - cmp_lock->start > smb_lock->start && - cmp_lock->start < smb_lock->end) { - pr_err("previous lock conflict with zero byte lock range\n"); - rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; - goto out; - } + /* check zero byte lock range */ + if (cmp_lock->zero_len && !smb_lock->zero_len && + cmp_lock->start > smb_lock->start && + cmp_lock->start < smb_lock->end) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + pr_err("previous lock conflict with zero byte lock range\n"); + rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + goto out; + } - if (smb_lock->zero_len && !cmp_lock->zero_len && - smb_lock->start > cmp_lock->start && - smb_lock->start < cmp_lock->end) { - pr_err("current lock conflict with zero byte lock range\n"); - rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; - goto out; - } + if (smb_lock->zero_len && !cmp_lock->zero_len && + smb_lock->start > cmp_lock->start && + smb_lock->start < cmp_lock->end) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + pr_err("current lock conflict with zero byte lock range\n"); + rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + goto out; + } - if (((cmp_lock->start <= smb_lock->start && - cmp_lock->end > smb_lock->start) || - (cmp_lock->start < smb_lock->end && cmp_lock->end >= smb_lock->end)) && - !cmp_lock->zero_len && !smb_lock->zero_len) { - pr_err("Not allow lock operation on exclusive lock range\n"); - rsp->hdr.Status = - STATUS_LOCK_NOT_GRANTED; - goto out; + if (((cmp_lock->start <= smb_lock->start && + cmp_lock->end > smb_lock->start) || + (cmp_lock->start < smb_lock->end && + cmp_lock->end >= smb_lock->end)) && + !cmp_lock->zero_len && !smb_lock->zero_len) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + pr_err("Not allow lock operation on exclusive lock range\n"); + rsp->hdr.Status = + STATUS_LOCK_NOT_GRANTED; + goto out; + } } + spin_unlock(&conn->llist_lock); } - + read_unlock(&conn_list_lock); +out_check_cl: if (smb_lock->fl->fl_type == F_UNLCK && nolock) { pr_err("Try to unlock nolocked range\n"); rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; goto out; } -no_check_gl: +no_check_cl: if (smb_lock->zero_len) { err = 0; goto skip; @@ -6753,8 +6772,10 @@ skip: ksmbd_debug(SMB, "would have to wait for getting lock\n"); - list_add_tail(&smb_lock->glist, - &global_lock_list); + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + spin_unlock(&work->conn->llist_lock); list_add(&smb_lock->llist, &rollback_list); argv = kmalloc(sizeof(void *), GFP_KERNEL); @@ -6782,7 +6803,9 @@ skip: if (work->state != KSMBD_WORK_ACTIVE) { list_del(&smb_lock->llist); - list_del(&smb_lock->glist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); locks_free_lock(flock); if (work->state == KSMBD_WORK_CANCELLED) { @@ -6806,14 +6829,21 @@ skip: } list_del(&smb_lock->llist); - list_del(&smb_lock->glist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + spin_lock(&fp->f_lock); list_del(&work->fp_entry); spin_unlock(&fp->f_lock); goto retry; } else if (!err) { - list_add_tail(&smb_lock->glist, - &global_lock_list); + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + list_add_tail(&smb_lock->flist, + &fp->lock_list); + spin_unlock(&work->conn->llist_lock); list_add(&smb_lock->llist, &rollback_list); ksmbd_debug(SMB, "successful in taking lock\n"); } else { @@ -6852,8 +6882,14 @@ out: err = vfs_lock_file(filp, 0, rlock, NULL); if (err) pr_err("rollback unlock fail : %d\n", err); + list_del(&smb_lock->llist); - list_del(&smb_lock->glist); + spin_lock(&work->conn->llist_lock); + if (!list_empty(&smb_lock->flist)) + list_del(&smb_lock->flist); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + locks_free_lock(smb_lock->fl); locks_free_lock(rlock); kfree(smb_lock); diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c index 38026d9bb704..24c6bb476f6e 100644 --- a/fs/ksmbd/smb_common.c +++ b/fs/ksmbd/smb_common.c @@ -23,8 +23,6 @@ static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; #define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) #define KSMBD_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr)) -LIST_HEAD(global_lock_list); - struct smb_protocol { int index; char *name; diff --git a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h index 6ab28aa33024..b8c350725905 100644 --- a/fs/ksmbd/smb_common.h +++ b/fs/ksmbd/smb_common.h @@ -48,8 +48,6 @@ #define CIFS_DEFAULT_IOSIZE (64 * 1024) #define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ -extern struct list_head global_lock_list; - /* RFC 1002 session packet types */ #define RFC1002_SESSION_MESSAGE 0x00 #define RFC1002_SESSION_REQUEST 0x81 diff --git a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c index c54c605637a0..92d8c61ffd2a 100644 --- a/fs/ksmbd/vfs_cache.c +++ b/fs/ksmbd/vfs_cache.c @@ -302,6 +302,7 @@ static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) { struct file *filp; + struct ksmbd_lock *smb_lock, *tmp_lock; fd_limit_close(); __ksmbd_remove_durable_fd(fp); @@ -313,6 +314,20 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) __ksmbd_inode_close(fp); if (!IS_ERR_OR_NULL(filp)) fput(filp); + + /* because the reference count of fp is 0, it is guaranteed that + * there are not accesses to fp->lock_list. + */ + list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { + spin_lock(&fp->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&fp->conn->llist_lock); + + list_del(&smb_lock->flist); + locks_free_lock(smb_lock->fl); + kfree(smb_lock); + } + kfree(fp->filename); if (ksmbd_stream_fd(fp)) kfree(fp->stream.name); @@ -549,6 +564,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) INIT_LIST_HEAD(&fp->blocked_works); INIT_LIST_HEAD(&fp->node); + INIT_LIST_HEAD(&fp->lock_list); spin_lock_init(&fp->f_lock); atomic_set(&fp->refcount, 1); diff --git a/fs/ksmbd/vfs_cache.h b/fs/ksmbd/vfs_cache.h index 70e987293564..70dfe6a99f13 100644 --- a/fs/ksmbd/vfs_cache.h +++ b/fs/ksmbd/vfs_cache.h @@ -30,7 +30,8 @@ struct ksmbd_session; struct ksmbd_lock { struct file_lock *fl; - struct list_head glist; + struct list_head clist; + struct list_head flist; struct list_head llist; unsigned int flags; int cmd; @@ -91,6 +92,7 @@ struct ksmbd_file { struct stream stream; struct list_head node; struct list_head blocked_works; + struct list_head lock_list; int durable_timeout; From 45a64e8b08493b768fa029a5508cec8cf2b89f2d Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 10 Jul 2021 09:34:20 +0900 Subject: [PATCH 183/417] ksmbd: uninterruptible wait for a file being unlocked the wait can be canceled by SMB2_CANCEL, SMB2_CLOSE, SMB2_LOGOFF, disconnection or shutdown, we don't have to use wait_event_interruptible. And this remove the warning from Coverity: CID 1502834 (#1 of 1): Unused value (UNUSED_VALUE) returned_value: Assigning value from ksmbd_vfs_posix_lock_wait(flock) to err here, but that stored value is overwritten before it can be used. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 2 +- fs/ksmbd/vfs.c | 4 ++-- fs/ksmbd/vfs.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 99e2368ae672..f73721c3b0e9 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -6799,7 +6799,7 @@ skip: smb2_send_interim_resp(work, STATUS_PENDING); - err = ksmbd_vfs_posix_lock_wait(flock); + ksmbd_vfs_posix_lock_wait(flock); if (work->state != KSMBD_WORK_ACTIVE) { list_del(&smb_lock->llist); diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index 38677c20d048..88e947f69f47 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -1784,9 +1784,9 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, return 0; } -int ksmbd_vfs_posix_lock_wait(struct file_lock *flock) +void ksmbd_vfs_posix_lock_wait(struct file_lock *flock) { - return wait_event_interruptible(flock->fl_wait, !flock->fl_blocker); + wait_event(flock->fl_wait, !flock->fl_blocker); } int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout) diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h index b255f90acf8f..cb0cba0d5d07 100644 --- a/fs/ksmbd/vfs.h +++ b/fs/ksmbd/vfs.h @@ -168,7 +168,7 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct user_namespace *user_ns, struct dentry *dentry, struct ksmbd_kstat *ksmbd_kstat); -int ksmbd_vfs_posix_lock_wait(struct file_lock *flock); +void ksmbd_vfs_posix_lock_wait(struct file_lock *flock); int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, From ce154c32af3c60727171ff28ae97bcceda63b1c6 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 9 Jul 2021 17:06:33 +0900 Subject: [PATCH 184/417] ksmbd: make smb2_find_context_vals return NULL if not found instead of -ENOENT, make smb2_find_context_vals return NULL if the given context cannot be found. Reported-by: Dan Carpenter Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/oplock.c | 2 +- fs/ksmbd/smb2pdu.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index 71063568dfee..8e53815eedc6 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -1472,7 +1472,7 @@ struct create_context *smb2_find_context_vals(void *open_req, const char *tag) next = le32_to_cpu(cc->Next); } while (next != 0); - return ERR_PTR(-ENOENT); + return NULL; } /** diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index f73721c3b0e9..af33d4f95d44 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2131,7 +2131,7 @@ static inline int check_context_err(void *ctx, char *str) int err; err = PTR_ERR(ctx); - ksmbd_debug(SMB, "find context %s err %d\n", str, err); + ksmbd_debug(SMB, "find context %s err %d\n", str, err ? err : -ENOENT); if (err == -EINVAL) { pr_err("bad name length\n"); @@ -2525,7 +2525,7 @@ int smb2_open(struct ksmbd_work *work) if (req->CreateContextsOffset) { /* Parse non-durable handle create contexts */ context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER); - if (IS_ERR(context)) { + if (IS_ERR_OR_NULL(context)) { rc = check_context_err(context, SMB2_CREATE_EA_BUFFER); if (rc < 0) goto err_out1; @@ -2540,7 +2540,7 @@ int smb2_open(struct ksmbd_work *work) context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); - if (IS_ERR(context)) { + if (IS_ERR_OR_NULL(context)) { rc = check_context_err(context, SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); if (rc < 0) @@ -2553,7 +2553,7 @@ int smb2_open(struct ksmbd_work *work) context = smb2_find_context_vals(req, SMB2_CREATE_TIMEWARP_REQUEST); - if (IS_ERR(context)) { + if (IS_ERR_OR_NULL(context)) { rc = check_context_err(context, SMB2_CREATE_TIMEWARP_REQUEST); if (rc < 0) @@ -2567,7 +2567,7 @@ int smb2_open(struct ksmbd_work *work) if (tcon->posix_extensions) { context = smb2_find_context_vals(req, SMB2_CREATE_TAG_POSIX); - if (IS_ERR(context)) { + if (IS_ERR_OR_NULL(context)) { rc = check_context_err(context, SMB2_CREATE_TAG_POSIX); if (rc < 0) @@ -2970,7 +2970,7 @@ int smb2_open(struct ksmbd_work *work) az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, SMB2_CREATE_ALLOCATION_SIZE); - if (IS_ERR(az_req)) { + if (IS_ERR_OR_NULL(az_req)) { rc = check_context_err(az_req, SMB2_CREATE_ALLOCATION_SIZE); if (rc < 0) @@ -2992,7 +2992,7 @@ int smb2_open(struct ksmbd_work *work) } context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID); - if (IS_ERR(context)) { + if (IS_ERR_OR_NULL(context)) { rc = check_context_err(context, SMB2_CREATE_QUERY_ON_DISK_ID); if (rc < 0) goto err_out; From 21dd1fd6d718ac59841c3ee3d0b1d82508ef24dc Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 9 Jul 2021 17:06:34 +0900 Subject: [PATCH 185/417] ksmbd: handle error cases first in smb2_create_sd_buffers For code cleanup, handle error cases first in smb2_create_sd_buffers(). Reported-by: Dan Carpenter Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index af33d4f95d44..2e266a9d3935 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2319,25 +2319,23 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work, struct path *path) { struct create_context *context; - int rc = -ENOENT; + struct create_sd_buf_req *sd_buf; if (!req->CreateContextsOffset) - return rc; + return -ENOENT; /* Parse SD BUFFER create contexts */ context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER); - if (context && !IS_ERR(context)) { - struct create_sd_buf_req *sd_buf; + if (!context) + return -ENOENT; + else if (IS_ERR(context)) + return PTR_ERR(context); - ksmbd_debug(SMB, - "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); - sd_buf = (struct create_sd_buf_req *)context; - rc = set_info_sec(work->conn, work->tcon, - path, &sd_buf->ntsd, - le32_to_cpu(sd_buf->ccontext.DataLength), true); - } - - return rc; + ksmbd_debug(SMB, + "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); + sd_buf = (struct create_sd_buf_req *)context; + return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, + le32_to_cpu(sd_buf->ccontext.DataLength), true); } static void ksmbd_acls_fattr(struct smb_fattr *fattr, struct inode *inode) From f19b3967fb0967aa02b8bfe26ce186ca7525dff7 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 13 Jul 2021 09:59:34 +0900 Subject: [PATCH 186/417] ksmbd: remove unneeded check_context_err Coverity Scan seems to report false alarm. *** CID 1505930: (USE_AFTER_FREE) /fs/ksmbd/smb2pdu.c: 2527 in smb2_open() >>> CID 1505930: (USE_AFTER_FREE) >>> Passing freed pointer "context" as an argument to >>> "check_context_err". This patch remove unneeded check_context_err to make coverity scan happy. Reported-by: Coverity Scan Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/oplock.c | 3 +- fs/ksmbd/smb2pdu.c | 73 +++++++++++++++------------------------------- 2 files changed, 26 insertions(+), 50 deletions(-) diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index 8e53815eedc6..6ace6c2f22dc 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -1446,7 +1446,8 @@ struct lease_ctx_info *parse_lease_state(void *open_req) * @open_req: buffer containing smb2 file open(create) request * @tag: context name to search for * - * Return: pointer to requested context, NULL if @str context not found + * Return: pointer to requested context, NULL if @str context not found + * or error pointer if name length is invalid. */ struct create_context *smb2_find_context_vals(void *open_req, const char *tag) { diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 2e266a9d3935..319e07a28dca 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2126,21 +2126,6 @@ next: return rc; } -static inline int check_context_err(void *ctx, char *str) -{ - int err; - - err = PTR_ERR(ctx); - ksmbd_debug(SMB, "find context %s err %d\n", str, err ? err : -ENOENT); - - if (err == -EINVAL) { - pr_err("bad name length\n"); - return err; - } - - return 0; -} - static noinline int smb2_set_stream_name_xattr(struct path *path, struct ksmbd_file *fp, char *stream_name, int s_type) @@ -2523,11 +2508,10 @@ int smb2_open(struct ksmbd_work *work) if (req->CreateContextsOffset) { /* Parse non-durable handle create contexts */ context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER); - if (IS_ERR_OR_NULL(context)) { - rc = check_context_err(context, SMB2_CREATE_EA_BUFFER); - if (rc < 0) - goto err_out1; - } else { + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { ea_buf = (struct create_ea_buf_req *)context; if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { rsp->hdr.Status = STATUS_ACCESS_DENIED; @@ -2538,12 +2522,10 @@ int smb2_open(struct ksmbd_work *work) context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); - if (IS_ERR_OR_NULL(context)) { - rc = check_context_err(context, - SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); - if (rc < 0) - goto err_out1; - } else { + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { ksmbd_debug(SMB, "get query maximal access context\n"); maximal_access_ctxt = 1; @@ -2551,12 +2533,10 @@ int smb2_open(struct ksmbd_work *work) context = smb2_find_context_vals(req, SMB2_CREATE_TIMEWARP_REQUEST); - if (IS_ERR_OR_NULL(context)) { - rc = check_context_err(context, - SMB2_CREATE_TIMEWARP_REQUEST); - if (rc < 0) - goto err_out1; - } else { + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { ksmbd_debug(SMB, "get timewarp context\n"); rc = -EBADF; goto err_out1; @@ -2565,12 +2545,10 @@ int smb2_open(struct ksmbd_work *work) if (tcon->posix_extensions) { context = smb2_find_context_vals(req, SMB2_CREATE_TAG_POSIX); - if (IS_ERR_OR_NULL(context)) { - rc = check_context_err(context, - SMB2_CREATE_TAG_POSIX); - if (rc < 0) - goto err_out1; - } else { + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { struct create_posix *posix = (struct create_posix *)context; ksmbd_debug(SMB, "get posix context\n"); @@ -2968,12 +2946,10 @@ int smb2_open(struct ksmbd_work *work) az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, SMB2_CREATE_ALLOCATION_SIZE); - if (IS_ERR_OR_NULL(az_req)) { - rc = check_context_err(az_req, - SMB2_CREATE_ALLOCATION_SIZE); - if (rc < 0) - goto err_out; - } else { + if (IS_ERR(az_req)) { + rc = PTR_ERR(az_req); + goto err_out; + } else if (az_req) { loff_t alloc_size = le64_to_cpu(az_req->AllocationSize); int err; @@ -2990,11 +2966,10 @@ int smb2_open(struct ksmbd_work *work) } context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID); - if (IS_ERR_OR_NULL(context)) { - rc = check_context_err(context, SMB2_CREATE_QUERY_ON_DISK_ID); - if (rc < 0) - goto err_out; - } else { + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out; + } else if (context) { ksmbd_debug(SMB, "get query on disk id context\n"); query_disk_id = 1; } From 78ad2c277af4cf503f985fd506fbb1f8576460f2 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 13 Jul 2021 17:12:41 +0900 Subject: [PATCH 187/417] ksmbd: fix memory leak in ksmbd_vfs_get_sd_xattr() Add free acl.sd_buf and n.data on error handling in ksmbd_vfs_get_sd_xattr(). Reported-by: Coverity Scan Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/vfs.c | 103 +++++++++++++++++++++++++------------------------ 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index 88e947f69f47..612c52d7a01b 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -1498,63 +1498,66 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, { int rc; struct ndr n; + struct inode *inode = d_inode(dentry); + struct ndr acl_ndr = {0}; + struct xattr_ntacl acl; + struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; + __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; rc = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_SD, &n.data); - if (rc > 0) { - struct inode *inode = d_inode(dentry); - struct ndr acl_ndr = {0}; - struct xattr_ntacl acl; - struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; - __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; + if (rc <= 0) + return rc; - n.length = rc; - rc = ndr_decode_v4_ntacl(&n, &acl); - if (rc) - return rc; + n.length = rc; + rc = ndr_decode_v4_ntacl(&n, &acl); + if (rc) + goto free_n_data; - smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, - ACL_TYPE_ACCESS); - if (S_ISDIR(inode->i_mode)) - def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, - inode, - ACL_TYPE_DEFAULT); + smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT); - rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, - smb_acl, def_smb_acl); - if (rc) { - pr_err("failed to encode ndr to posix acl\n"); - goto out; - } - - rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, - cmp_hash); - if (rc) { - pr_err("failed to generate hash for ndr acl\n"); - goto out; - } - - if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { - pr_err("hash value diff\n"); - rc = -EINVAL; - goto out; - } - - *pntsd = acl.sd_buf; - (*pntsd)->osidoffset = - cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - NDR_NTSD_OFFSETOF); - (*pntsd)->gsidoffset = - cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - NDR_NTSD_OFFSETOF); - (*pntsd)->dacloffset = - cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) - NDR_NTSD_OFFSETOF); - - rc = acl.sd_size; -out: - kfree(n.data); - kfree(acl_ndr.data); - kfree(smb_acl); - kfree(def_smb_acl); + rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, smb_acl, + def_smb_acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out_free; } + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, cmp_hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + goto out_free; + } + + if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { + pr_err("hash value diff\n"); + rc = -EINVAL; + goto out_free; + } + + *pntsd = acl.sd_buf; + (*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - + NDR_NTSD_OFFSETOF); + (*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - + NDR_NTSD_OFFSETOF); + (*pntsd)->dacloffset = cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) - + NDR_NTSD_OFFSETOF); + + rc = acl.sd_size; +out_free: + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + if (rc < 0) { + kfree(acl.sd_buf); + *pntsd = NULL; + } + +free_n_data: + kfree(n.data); return rc; } From 96ad4ec51c06c6fafc10b4e3a20753e127ce27d4 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 13 Jul 2021 17:17:28 +0900 Subject: [PATCH 188/417] ksmbd: fix unused err value in smb2_lock CID 1502845 (#1 of 1): Unused value (UNUSED_VALUE) value_overwrite: Overwriting previous write to err with value from vfs_lock_file(filp, 0U, rlock, NULL). 6880 err = vfs_lock_file(filp, 0, rlock, NULL); 6881 if (err) 6882 pr_err("rollback unlock fail : %d\n", err); Reported-by: Coverity Scan Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 319e07a28dca..ee91d99dbf9b 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -6835,7 +6835,7 @@ skip: rsp->Reserved = 0; inc_rfc1001_len(rsp, 4); ksmbd_fd_put(work, fp); - return err; + return 0; out: list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { @@ -6846,15 +6846,16 @@ out: list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { struct file_lock *rlock = NULL; + int rc; rlock = smb_flock_init(filp); rlock->fl_type = F_UNLCK; rlock->fl_start = smb_lock->start; rlock->fl_end = smb_lock->end; - err = vfs_lock_file(filp, 0, rlock, NULL); - if (err) - pr_err("rollback unlock fail : %d\n", err); + rc = vfs_lock_file(filp, 0, rlock, NULL); + if (rc) + pr_err("rollback unlock fail : %d\n", rc); list_del(&smb_lock->llist); spin_lock(&work->conn->llist_lock); @@ -6871,7 +6872,7 @@ out2: ksmbd_debug(SMB, "failed in taking lock(flags : %x)\n", flags); smb2_set_err_rsp(work); ksmbd_fd_put(work, fp); - return 0; + return err; } static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, From 03d8d4f1896eba2240aa946ce591e86e538504cd Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Tue, 13 Jul 2021 16:09:34 +0900 Subject: [PATCH 189/417] ksmbd: set RDMA capability for FSCTL_QUERY_NETWORK_INTERFACE_INFO set RDMA capability for FSCTL_QUERY_NETWORK_INTERFACE_INFO. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 9 +++++---- fs/ksmbd/transport_rdma.c | 14 ++++++++++++++ fs/ksmbd/transport_rdma.h | 2 ++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index ee91d99dbf9b..c1a594599431 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -22,6 +22,7 @@ #include "asn1.h" #include "connection.h" #include "transport_ipc.h" +#include "transport_rdma.h" #include "vfs.h" #include "vfs_cache.h" #include "misc.h" @@ -7028,11 +7029,11 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, &rsp->Buffer[nbytes]; nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); - /* TODO: specify the RDMA capabilities */ + nii_rsp->Capability = 0; if (netdev->num_tx_queues > 1) - nii_rsp->Capability = cpu_to_le32(RSS_CAPABLE); - else - nii_rsp->Capability = 0; + nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE); + if (ksmbd_rdma_capable_netdev(netdev)) + nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE); nii_rsp->Next = cpu_to_le32(152); nii_rsp->Reserved = 0; diff --git a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c index d5728c84a15a..f818fe358f31 100644 --- a/fs/ksmbd/transport_rdma.c +++ b/fs/ksmbd/transport_rdma.c @@ -2033,6 +2033,20 @@ int ksmbd_rdma_destroy(void) return 0; } +bool ksmbd_rdma_capable_netdev(struct net_device *netdev) +{ + struct ib_device *ibdev; + bool rdma_capable = false; + + ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_UNKNOWN); + if (ibdev) { + if (rdma_frwr_is_supported(&ibdev->attrs)) + rdma_capable = true; + ib_device_put(ibdev); + } + return rdma_capable; +} + static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { .prepare = smb_direct_prepare, .disconnect = smb_direct_disconnect, diff --git a/fs/ksmbd/transport_rdma.h b/fs/ksmbd/transport_rdma.h index da60fcec3ede..72e2574079f3 100644 --- a/fs/ksmbd/transport_rdma.h +++ b/fs/ksmbd/transport_rdma.h @@ -53,9 +53,11 @@ struct smb_direct_data_transfer { #ifdef CONFIG_SMB_SERVER_SMBDIRECT int ksmbd_rdma_init(void); int ksmbd_rdma_destroy(void); +bool ksmbd_rdma_capable_netdev(struct net_device *netdev); #else static inline int ksmbd_rdma_init(void) { return 0; } static inline int ksmbd_rdma_destroy(void) { return 0; } +static inline bool ksmbd_rdma_capable_netdev(struct net_device *netdev) { return false; } #endif #endif /* __KSMBD_TRANSPORT_RDMA_H__ */ From 0a427cc638ada13a703b044f38f4b01628c4e620 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Tue, 13 Jul 2021 15:38:30 +0900 Subject: [PATCH 190/417] ksmbd: fix an error message in ksmbd_conn_trasnport_init Fix an error message in ksmbd_conn_transport_init(). Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index 8430848bea45..d7ee0bfb5838 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -372,7 +372,7 @@ int ksmbd_conn_transport_init(void) ret = ksmbd_rdma_init(); if (ret) { - pr_err("Failed to init KSMBD subsystem: %d\n", ret); + pr_err("Failed to init RDMA subsystem: %d\n", ret); goto out; } out: From a9c241d01d0a80209cb7dde76a89f450b0d5a78d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 15 Jul 2021 10:22:20 +0900 Subject: [PATCH 191/417] ksmbd: fix typo in comment Fix typo "openning" -> "opening". Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index d7ee0bfb5838..af086d35398a 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -247,7 +247,7 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn) /* * Stop current session if the time that get last request from client - * is bigger than deadtime user configured and openning file count is + * is bigger than deadtime user configured and opening file count is * zero. */ if (server_conf.deadtime > 0 && From e4b60e92d4f878b774eca22fa4c00fa04f6354b4 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 16 Jul 2021 14:51:30 +0900 Subject: [PATCH 192/417] ksmbd: fix wrong compression context size Use smb2_compression_ctx instead of smb2_encryption_neg_context. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index c1a594599431..f9e6e2bd4cbf 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -924,7 +924,7 @@ static int decode_compress_ctxt(struct ksmbd_conn *conn, * Return compression context size in request. * So need to plus extra number of CompressionAlgorithms size. */ - return sizeof(struct smb2_encryption_neg_context) + + return sizeof(struct smb2_compression_ctx) + ((algo_cnt - 1) * 2); } From 58090b175271870842d823622013d4499f462a10 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 16 Jul 2021 14:52:09 +0900 Subject: [PATCH 193/417] ksmbd: fix wrong error status return on session setup When user insert wrong password, ksmbd return STATUS_INVALID_PARAMETER error status to client. It will make user confusing whether it is not password problem. This patch change error status to STATUS_LOGON_FAILURE. and return STATUS_INSUFFICIENT_RESOURCES if memory allocation failed on session setup. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index f9e6e2bd4cbf..77e42a572825 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -1338,8 +1338,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) user = session_user(conn, req); if (!user) { ksmbd_debug(SMB, "Unknown user name or an error\n"); - rsp->hdr.Status = STATUS_LOGON_FAILURE; - return -EINVAL; + return -EPERM; } /* Check for previous session */ @@ -1363,8 +1362,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) if (user_guest(sess->user)) { if (conn->sign) { ksmbd_debug(SMB, "Guest login not allowed when signing enabled\n"); - rsp->hdr.Status = STATUS_LOGON_FAILURE; - return -EACCES; + return -EPERM; } rsp->SessionFlags = SMB2_SESSION_FLAG_IS_GUEST_LE; @@ -1377,8 +1375,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) if (rc) { set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); ksmbd_debug(SMB, "authentication failed\n"); - rsp->hdr.Status = STATUS_LOGON_FAILURE; - return -EINVAL; + return -EPERM; } /* @@ -1403,8 +1400,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) if (rc) { ksmbd_debug(SMB, "SMB3 encryption key generation failed\n"); - rsp->hdr.Status = STATUS_LOGON_FAILURE; - return rc; + return -EINVAL; } sess->enc = true; rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; @@ -1434,16 +1430,14 @@ binding_session: rc = conn->ops->generate_signingkey(sess, conn); if (rc) { ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); - rsp->hdr.Status = STATUS_LOGON_FAILURE; - return rc; + return -EINVAL; } } if (conn->dialect > SMB20_PROT_ID) { if (!ksmbd_conn_lookup_dialect(conn)) { pr_err("fail to verify the dialect\n"); - rsp->hdr.Status = STATUS_USER_SESSION_DELETED; - return -EPERM; + return -ENOENT; } } return 0; @@ -1483,8 +1477,7 @@ static int krb5_authenticate(struct ksmbd_work *work) out_blob, &out_len); if (retval) { ksmbd_debug(SMB, "krb5 authentication failed\n"); - rsp->hdr.Status = STATUS_LOGON_FAILURE; - return retval; + return -EINVAL; } rsp->SecurityBufferLength = cpu_to_le16(out_len); inc_rfc1001_len(rsp, out_len - 1); @@ -1499,8 +1492,7 @@ static int krb5_authenticate(struct ksmbd_work *work) if (retval) { ksmbd_debug(SMB, "SMB3 encryption key generation failed\n"); - rsp->hdr.Status = STATUS_LOGON_FAILURE; - return retval; + return -EINVAL; } sess->enc = true; rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; @@ -1524,16 +1516,14 @@ static int krb5_authenticate(struct ksmbd_work *work) retval = conn->ops->generate_signingkey(sess, conn); if (retval) { ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); - rsp->hdr.Status = STATUS_LOGON_FAILURE; - return retval; + return -EINVAL; } } if (conn->dialect > SMB20_PROT_ID) { if (!ksmbd_conn_lookup_dialect(conn)) { pr_err("fail to verify the dialect\n"); - rsp->hdr.Status = STATUS_USER_SESSION_DELETED; - return -EPERM; + return -ENOENT; } } return 0; @@ -1709,6 +1699,8 @@ out_err: rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; else if (rc == -EFAULT) rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; + else if (rc == -ENOMEM) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; else if (rc) rsp->hdr.Status = STATUS_LOGON_FAILURE; From 67307023d02b1339e0b930b742fe5a9cd81284ca Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 16 Jul 2021 14:52:46 +0900 Subject: [PATCH 194/417] ksmbd: set STATUS_INVALID_PARAMETER error status if credit charge is invalid MS-SMB2 specification describe : If the calculated credit number is greater than the CreditCharge, the server MUST fail the request with the error code STATUS_INVALID_PARAMETER. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/server.c | 20 ++++++++++---------- fs/ksmbd/smb2misc.c | 9 +++++++-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/fs/ksmbd/server.c b/fs/ksmbd/server.c index a8c59e96a2f7..e6a9f6aa47eb 100644 --- a/fs/ksmbd/server.c +++ b/fs/ksmbd/server.c @@ -101,8 +101,8 @@ static inline int check_conn_state(struct ksmbd_work *work) return 0; } -#define TCP_HANDLER_CONTINUE 0 -#define TCP_HANDLER_ABORT 1 +#define SERVER_HANDLER_CONTINUE 0 +#define SERVER_HANDLER_ABORT 1 static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, u16 *cmd) @@ -112,10 +112,10 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, int ret; if (check_conn_state(work)) - return TCP_HANDLER_CONTINUE; + return SERVER_HANDLER_CONTINUE; if (ksmbd_verify_smb_message(work)) - return TCP_HANDLER_ABORT; + return SERVER_HANDLER_ABORT; command = conn->ops->get_cmd_val(work); *cmd = command; @@ -123,21 +123,21 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, andx_again: if (command >= conn->max_cmds) { conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); - return TCP_HANDLER_CONTINUE; + return SERVER_HANDLER_CONTINUE; } cmds = &conn->cmds[command]; if (!cmds->proc) { ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); - return TCP_HANDLER_CONTINUE; + return SERVER_HANDLER_CONTINUE; } if (work->sess && conn->ops->is_sign_req(work, command)) { ret = conn->ops->check_sign_req(work); if (!ret) { conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); - return TCP_HANDLER_CONTINUE; + return SERVER_HANDLER_CONTINUE; } } @@ -153,8 +153,8 @@ andx_again: } if (work->send_no_response) - return TCP_HANDLER_ABORT; - return TCP_HANDLER_CONTINUE; + return SERVER_HANDLER_ABORT; + return SERVER_HANDLER_CONTINUE; } static void __handle_ksmbd_work(struct ksmbd_work *work, @@ -203,7 +203,7 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, do { rc = __process_request(work, conn, &command); - if (rc == TCP_HANDLER_ABORT) + if (rc == SERVER_HANDLER_ABORT) break; /* diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index 4508631c5706..e68aa7d718ed 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -423,8 +423,13 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) return 1; } - return work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU ? - smb2_validate_credit_charge(hdr) : 0; + if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && + smb2_validate_credit_charge(hdr)) { + work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return 1; + } + + return 0; } int smb2_negotiate_request(struct ksmbd_work *work) From d347d745f06c7e6503abc08f68dc3b71da71596d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 16 Jul 2021 16:39:54 +0900 Subject: [PATCH 195/417] ksmbd: move credit charge verification over smb2 request size verification Move credit charge verification over smb2 request size verification to avoid being skipped. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2misc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index e68aa7d718ed..9aa46bb3e10d 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -385,6 +385,12 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) } } + if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && + smb2_validate_credit_charge(hdr)) { + work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return 1; + } + clc_len = smb2_calc_size(hdr); if (len != clc_len) { /* server can return one byte more due to implied bcc[0] */ @@ -423,12 +429,6 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) return 1; } - if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && - smb2_validate_credit_charge(hdr)) { - work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); - return 1; - } - return 0; } From a6579cbfd7216b071008db13360c322a6b21400b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 12 Jul 2021 17:24:30 +0100 Subject: [PATCH 196/417] gfs2: Fix memory leak of object lsi on error return path In the case where IS_ERR(lsi->si_sc_inode) is true the error exit path to free_local does not kfree the allocated object lsi leading to a memory leak. Fix this by kfree'ing lst before taking the error exit path. Addresses-Coverity: ("Resource leak") Fixes: 97fd734ba17e ("gfs2: lookup local statfs inodes prior to journal recovery") Signed-off-by: Colin Ian King Signed-off-by: Andreas Gruenbacher --- fs/gfs2/ops_fstype.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 5f4504dd0875..bd3b3be1a473 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -677,6 +677,7 @@ static int init_statfs(struct gfs2_sbd *sdp) error = PTR_ERR(lsi->si_sc_inode); fs_err(sdp, "can't find local \"sc\" file#%u: %d\n", jd->jd_jid, error); + kfree(lsi); goto free_local; } lsi->si_jid = jd->jd_jid; From 9223958816f9df133ae936c9371378ba1203e0da Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 16 Jul 2021 17:16:11 +0900 Subject: [PATCH 197/417] ksmbd: fix typo of MS-SMBD Fix typo : "MS-KSMBD" => "MS-SMBD". Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/transport_rdma.c | 2 +- fs/ksmbd/transport_rdma.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c index f818fe358f31..f2ae6bae83f1 100644 --- a/fs/ksmbd/transport_rdma.c +++ b/fs/ksmbd/transport_rdma.c @@ -58,7 +58,7 @@ /* * User configurable initial values per SMB_DIRECT transport connection - * as defined in [MS-KSMBD] 3.1.1.1 + * as defined in [MS-SMBD] 3.1.1.1 * Those may change after a SMB_DIRECT negotiation */ /* The local peer's maximum number of credits to grant to the peer */ diff --git a/fs/ksmbd/transport_rdma.h b/fs/ksmbd/transport_rdma.h index 72e2574079f3..0fa8adc0776f 100644 --- a/fs/ksmbd/transport_rdma.h +++ b/fs/ksmbd/transport_rdma.h @@ -9,7 +9,7 @@ #define SMB_DIRECT_PORT 5445 -/* SMB DIRECT negotiation request packet [MS-KSMBD] 2.2.1 */ +/* SMB DIRECT negotiation request packet [MS-SMBD] 2.2.1 */ struct smb_direct_negotiate_req { __le16 min_version; __le16 max_version; @@ -20,7 +20,7 @@ struct smb_direct_negotiate_req { __le32 max_fragmented_size; } __packed; -/* SMB DIRECT negotiation response packet [MS-KSMBD] 2.2.2 */ +/* SMB DIRECT negotiation response packet [MS-SMBD] 2.2.2 */ struct smb_direct_negotiate_resp { __le16 min_version; __le16 max_version; @@ -37,7 +37,7 @@ struct smb_direct_negotiate_resp { #define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 -/* SMB DIRECT data transfer packet with payload [MS-KSMBD] 2.2.3 */ +/* SMB DIRECT data transfer packet with payload [MS-SMBD] 2.2.3 */ struct smb_direct_data_transfer { __le16 credits_requested; __le16 credits_granted; From af320a739029f6f8c5c05e769fadaf88e9b7d34f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 21 Jul 2021 10:03:19 +0900 Subject: [PATCH 198/417] ksmbd: add negotiate context verification This patch add negotiate context verification code to check bounds. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 118 ++++++++++++++++++++++++--------------------- fs/ksmbd/smb2pdu.h | 6 +-- 2 files changed, 65 insertions(+), 59 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 77e42a572825..64a4d66997a3 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -835,10 +835,10 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, conn->cipher_type); rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); - ctxt_size += sizeof(struct smb2_encryption_neg_context); + ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; /* Round to 8 byte boundary */ pneg_ctxt += - round_up(sizeof(struct smb2_encryption_neg_context), + round_up(sizeof(struct smb2_encryption_neg_context) + 2, 8); } @@ -850,9 +850,10 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, build_compression_ctxt((struct smb2_compression_ctx *)pneg_ctxt, conn->compress_algorithm); rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); - ctxt_size += sizeof(struct smb2_compression_ctx); + ctxt_size += sizeof(struct smb2_compression_ctx) + 2; /* Round to 8 byte boundary */ - pneg_ctxt += round_up(sizeof(struct smb2_compression_ctx), 8); + pneg_ctxt += round_up(sizeof(struct smb2_compression_ctx) + 2, + 8); } if (conn->posix_ext_supported) { @@ -881,16 +882,23 @@ static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, return err; } -static int decode_encrypt_ctxt(struct ksmbd_conn *conn, - struct smb2_encryption_neg_context *pneg_ctxt) +static void decode_encrypt_ctxt(struct ksmbd_conn *conn, + struct smb2_encryption_neg_context *pneg_ctxt, + int len_of_ctxts) { - int i; int cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); + int i, cphs_size = cph_cnt * sizeof(__le16); conn->cipher_type = 0; + if (sizeof(struct smb2_encryption_neg_context) + cphs_size > + len_of_ctxts) { + pr_err("Invalid cipher count(%d)\n", cph_cnt); + return; + } + if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)) - goto out; + return; for (i = 0; i < cph_cnt; i++) { if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM || @@ -903,90 +911,88 @@ static int decode_encrypt_ctxt(struct ksmbd_conn *conn, break; } } - -out: - /* - * Return encrypt context size in request. - * So need to plus extra number of ciphers size. - */ - return sizeof(struct smb2_encryption_neg_context) + - ((cph_cnt - 1) * 2); } -static int decode_compress_ctxt(struct ksmbd_conn *conn, - struct smb2_compression_ctx *pneg_ctxt) +static void decode_compress_ctxt(struct ksmbd_conn *conn, + struct smb2_compression_ctx *pneg_ctxt) { - int algo_cnt = le16_to_cpu(pneg_ctxt->CompressionAlgorithmCount); - conn->compress_algorithm = SMB3_COMPRESS_NONE; - - /* - * Return compression context size in request. - * So need to plus extra number of CompressionAlgorithms size. - */ - return sizeof(struct smb2_compression_ctx) + - ((algo_cnt - 1) * 2); } static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, struct smb2_negotiate_req *req) { - int i = 0; - __le32 status = 0; /* +4 is to account for the RFC1001 len field */ - char *pneg_ctxt = (char *)req + - le32_to_cpu(req->NegotiateContextOffset) + 4; - __le16 *ContextType = (__le16 *)pneg_ctxt; + struct smb2_neg_context *pctx = (struct smb2_neg_context *)((char *)req + 4); + int i = 0, len_of_ctxts; + int offset = le32_to_cpu(req->NegotiateContextOffset); int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); - int ctxt_size; + int len_of_smb = be32_to_cpu(req->hdr.smb2_buf_length); + __le32 status = STATUS_INVALID_PARAMETER; + + ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); + if (len_of_smb <= offset) { + ksmbd_debug(SMB, "Invalid response: negotiate context offset\n"); + return status; + } + + len_of_ctxts = len_of_smb - offset; - ksmbd_debug(SMB, "negotiate context count = %d\n", neg_ctxt_cnt); - status = STATUS_INVALID_PARAMETER; while (i++ < neg_ctxt_cnt) { - if (*ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { + int clen; + + /* check that offset is not beyond end of SMB */ + if (len_of_ctxts == 0) + break; + + if (len_of_ctxts < sizeof(struct smb2_neg_context)) + break; + + pctx = (struct smb2_neg_context *)((char *)pctx + offset); + clen = le16_to_cpu(pctx->DataLength); + if (clen + sizeof(struct smb2_neg_context) > len_of_ctxts) + break; + + if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { ksmbd_debug(SMB, "deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); if (conn->preauth_info->Preauth_HashId) break; status = decode_preauth_ctxt(conn, - (struct smb2_preauth_neg_context *)pneg_ctxt); - pneg_ctxt += DIV_ROUND_UP(sizeof(struct smb2_preauth_neg_context), 8) * 8; - } else if (*ContextType == SMB2_ENCRYPTION_CAPABILITIES) { + (struct smb2_preauth_neg_context *)pctx); + if (status != STATUS_SUCCESS) + break; + } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) { ksmbd_debug(SMB, "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); if (conn->cipher_type) break; - ctxt_size = decode_encrypt_ctxt(conn, - (struct smb2_encryption_neg_context *)pneg_ctxt); - pneg_ctxt += DIV_ROUND_UP(ctxt_size, 8) * 8; - } else if (*ContextType == SMB2_COMPRESSION_CAPABILITIES) { + decode_encrypt_ctxt(conn, + (struct smb2_encryption_neg_context *)pctx, + len_of_ctxts); + } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) { ksmbd_debug(SMB, "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); if (conn->compress_algorithm) break; - ctxt_size = decode_compress_ctxt(conn, - (struct smb2_compression_ctx *)pneg_ctxt); - pneg_ctxt += DIV_ROUND_UP(ctxt_size, 8) * 8; - } else if (*ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { + decode_compress_ctxt(conn, + (struct smb2_compression_ctx *)pctx); + } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { ksmbd_debug(SMB, "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); - ctxt_size = sizeof(struct smb2_netname_neg_context); - ctxt_size += DIV_ROUND_UP(le16_to_cpu(((struct smb2_netname_neg_context *) - pneg_ctxt)->DataLength), 8) * 8; - pneg_ctxt += ctxt_size; - } else if (*ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { + } else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { ksmbd_debug(SMB, "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); conn->posix_ext_supported = true; - pneg_ctxt += DIV_ROUND_UP(sizeof(struct smb2_posix_neg_context), 8) * 8; } - ContextType = (__le16 *)pneg_ctxt; - if (status != STATUS_SUCCESS) - break; + /* offsets must be 8 byte aligned */ + clen = (clen + 7) & ~0x7; + offset = clen + sizeof(struct smb2_neg_context); + len_of_ctxts -= clen + sizeof(struct smb2_neg_context); } return status; } diff --git a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h index 0eac40e1ba65..21cb93e771f7 100644 --- a/fs/ksmbd/smb2pdu.h +++ b/fs/ksmbd/smb2pdu.h @@ -299,7 +299,7 @@ struct smb2_encryption_neg_context { __le32 Reserved; /* CipherCount usally 2, but can be 3 when AES256-GCM enabled */ __le16 CipherCount; /* AES-128-GCM and AES-128-CCM by default */ - __le16 Ciphers[1]; + __le16 Ciphers[]; } __packed; #define SMB3_COMPRESS_NONE cpu_to_le16(0x0000) @@ -314,7 +314,7 @@ struct smb2_compression_ctx { __le16 CompressionAlgorithmCount; __u16 Padding; __le32 Reserved1; - __le16 CompressionAlgorithms[1]; + __le16 CompressionAlgorithms[]; } __packed; #define POSIX_CTXT_DATA_LEN 16 @@ -329,7 +329,7 @@ struct smb2_netname_neg_context { __le16 ContextType; /* 0x100 */ __le16 DataLength; __le32 Reserved; - __le16 NetName[0]; /* hostname of target converted to UCS-2 */ + __le16 NetName[]; /* hostname of target converted to UCS-2 */ } __packed; struct smb2_negotiate_rsp { From 378087cd17eea71c4e78e6053597e38429ccee0f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 21 Jul 2021 10:05:53 +0900 Subject: [PATCH 199/417] ksmbd: add support for negotiating signing algorithm Support for faster packet signing (using GMAC instead of CMAC) can now be negotiated to some newer servers, including Windows. See MS-SMB2 section 2.2.3.17. This patch adds support for sending the new negotiate context with two supported signing algorithms(AES-CMAC, HMAC-SHA256). If client add support for AES_GMAC, Server will be supported later depend on it. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/connection.h | 2 ++ fs/ksmbd/smb2ops.c | 4 +++ fs/ksmbd/smb2pdu.c | 58 +++++++++++++++++++++++++++++++++++++++++++ fs/ksmbd/smb2pdu.h | 14 +++++++++++ 4 files changed, 78 insertions(+) diff --git a/fs/ksmbd/connection.h b/fs/ksmbd/connection.h index 487c2024b0d5..e5403c587a58 100644 --- a/fs/ksmbd/connection.h +++ b/fs/ksmbd/connection.h @@ -109,6 +109,8 @@ struct ksmbd_conn { __le16 cipher_type; __le16 compress_algorithm; bool posix_ext_supported; + bool signing_negotiated; + __le16 signing_algorithm; bool binding; }; diff --git a/fs/ksmbd/smb2ops.c b/fs/ksmbd/smb2ops.c index 8262908e467c..197473871aa4 100644 --- a/fs/ksmbd/smb2ops.c +++ b/fs/ksmbd/smb2ops.c @@ -204,6 +204,7 @@ void init_smb2_1_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; + conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; @@ -221,6 +222,7 @@ void init_smb3_0_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; @@ -245,6 +247,7 @@ void init_smb3_02_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; @@ -269,6 +272,7 @@ int init_smb3_11_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 64a4d66997a3..7e6e3d8c20e8 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -786,6 +786,18 @@ static void build_compression_ctxt(struct smb2_compression_ctx *pneg_ctxt, pneg_ctxt->CompressionAlgorithms[0] = comp_algo; } +static void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt, + __le16 sign_algo) +{ + pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16((sizeof(struct smb2_signing_capabilities) + 2) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->SigningAlgorithms[0] = sign_algo; +} + static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) { pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; @@ -863,6 +875,18 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ctxt_size += sizeof(struct smb2_posix_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8); + } + + if (conn->signing_negotiated) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_SIGNING_CAPABILITIES context\n"); + build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt, + conn->signing_algorithm); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; } inc_rfc1001_len(rsp, ctxt_size); @@ -919,6 +943,34 @@ static void decode_compress_ctxt(struct ksmbd_conn *conn, conn->compress_algorithm = SMB3_COMPRESS_NONE; } +static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, + struct smb2_signing_capabilities *pneg_ctxt, + int len_of_ctxts) +{ + int sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); + int i, sign_alos_size = sign_algo_cnt * sizeof(__le16); + + conn->signing_negotiated = false; + + if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > + len_of_ctxts) { + pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); + return; + } + + for (i = 0; i < sign_algo_cnt; i++) { + if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256 || + pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC) { + ksmbd_debug(SMB, "Signing Algorithm ID = 0x%x\n", + pneg_ctxt->SigningAlgorithms[i]); + conn->signing_negotiated = true; + conn->signing_algorithm = + pneg_ctxt->SigningAlgorithms[i]; + break; + } + } +} + static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, struct smb2_negotiate_req *req) { @@ -987,6 +1039,12 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, ksmbd_debug(SMB, "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); conn->posix_ext_supported = true; + } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_SIGNING_CAPABILITIES context\n"); + decode_sign_cap_ctxt(conn, + (struct smb2_signing_capabilities *)pctx, + len_of_ctxts); } /* offsets must be 8 byte aligned */ diff --git a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h index 21cb93e771f7..89019f67234c 100644 --- a/fs/ksmbd/smb2pdu.h +++ b/fs/ksmbd/smb2pdu.h @@ -268,6 +268,7 @@ struct preauth_integrity_info { #define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2) #define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3) #define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5) +#define SMB2_SIGNING_CAPABILITIES cpu_to_le16(8) #define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100) struct smb2_neg_context { @@ -332,6 +333,19 @@ struct smb2_netname_neg_context { __le16 NetName[]; /* hostname of target converted to UCS-2 */ } __packed; +/* Signing algorithms */ +#define SIGNING_ALG_HMAC_SHA256 cpu_to_le16(0) +#define SIGNING_ALG_AES_CMAC cpu_to_le16(1) +#define SIGNING_ALG_AES_GMAC cpu_to_le16(2) + +struct smb2_signing_capabilities { + __le16 ContextType; /* 8 */ + __le16 DataLength; + __le32 Reserved; + __le16 SigningAlgorithmCount; + __le16 SigningAlgorithms[]; +} __packed; + struct smb2_negotiate_rsp { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 65 */ From 654c8876f93677915b1a009bc7f2421ab8750bf1 Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Fri, 23 Jul 2021 12:58:41 +0900 Subject: [PATCH 200/417] ksmbd: Fix potential memory leak in tcp_destroy_socket() ksmbd_socket must be freed even if kernel_sock_shutdown() somehow fails. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/transport_tcp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/ksmbd/transport_tcp.c b/fs/ksmbd/transport_tcp.c index 56ec11ff5a9f..dc15a5ecd2e0 100644 --- a/fs/ksmbd/transport_tcp.c +++ b/fs/ksmbd/transport_tcp.c @@ -381,8 +381,7 @@ static void tcp_destroy_socket(struct socket *ksmbd_socket) ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR); if (ret) pr_err("Failed to shutdown socket: %d\n", ret); - else - sock_release(ksmbd_socket); + sock_release(ksmbd_socket); } /** From 1d904eaf3f99565bdeffbed359e44dd88efbef02 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Fri, 23 Jul 2021 13:01:06 +0900 Subject: [PATCH 201/417] ksmbd: fix -Wstringop-truncation warnings Kernel test bot reports the following warnings: In function 'ndr_write_string', inlined from 'ndr_encode_dos_attr' at fs/ksmbd/ndr.c:136:3: >> fs/ksmbd/ndr.c:70:2: warning: 'strncpy' destination unchanged after copying no bytes [-Wstringop-truncation] 70 | strncpy(PAYLOAD_HEAD(n), value, sz); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In function 'ndr_write_string', inlined from 'ndr_encode_dos_attr' at fs/ksmbd/ndr.c:134:3: >> fs/ksmbd/ndr.c:70:2: warning: 'strncpy' output truncated before terminating nul copying as many bytes from a string as its length [-Wstringop-truncation] 70 | strncpy(PAYLOAD_HEAD(n), value, sz); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ fs/ksmbd/ndr.c: In function 'ndr_encode_dos_attr': fs/ksmbd/ndr.c:134:3: note: length computed here 134 | ndr_write_string(n, hex_attr, strlen(hex_attr)); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Reported-by: kernel test robot Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/ndr.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/ksmbd/ndr.c b/fs/ksmbd/ndr.c index cf0df78259c9..df23dfbaf657 100644 --- a/fs/ksmbd/ndr.c +++ b/fs/ksmbd/ndr.c @@ -65,13 +65,15 @@ static int ndr_write_bytes(struct ndr *n, void *value, size_t sz) return 0; } -static int ndr_write_string(struct ndr *n, void *value, size_t sz) +static int ndr_write_string(struct ndr *n, char *value) { + size_t sz; + + sz = strlen(value) + 1; if (n->length <= n->offset + sz) try_to_realloc_ndr_blob(n, sz); - strncpy(ndr_get_field(n), value, sz); - sz++; + memcpy(ndr_get_field(n), value, sz); n->offset += sz; n->offset = ALIGN(n->offset, 2); return 0; @@ -134,9 +136,9 @@ int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) if (da->version == 3) { snprintf(hex_attr, 10, "0x%x", da->attr); - ndr_write_string(n, hex_attr, strlen(hex_attr)); + ndr_write_string(n, hex_attr); } else { - ndr_write_string(n, "", strlen("")); + ndr_write_string(n, ""); } ndr_write_int16(n, da->version); ndr_write_int32(n, da->version); From d18760560593e5af921f51a8c9b64b6109d634c2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 1 Jul 2021 23:53:46 -0700 Subject: [PATCH 202/417] fscrypt: add fscrypt_symlink_getattr() for computing st_size Add a helper function fscrypt_symlink_getattr() which will be called from the various filesystems' ->getattr() methods to read and decrypt the target of encrypted symlinks in order to report the correct st_size. Detailed explanation: As required by POSIX and as documented in various man pages, st_size for a symlink is supposed to be the length of the symlink target. Unfortunately, st_size has always been wrong for encrypted symlinks because st_size is populated from i_size from disk, which intentionally contains the length of the encrypted symlink target. That's slightly greater than the length of the decrypted symlink target (which is the symlink target that userspace usually sees), and usually won't match the length of the no-key encoded symlink target either. This hadn't been fixed yet because reporting the correct st_size would require reading the symlink target from disk and decrypting or encoding it, which historically has been considered too heavyweight to do in ->getattr(). Also historically, the wrong st_size had only broken a test (LTP lstat03) and there were no known complaints from real users. (This is probably because the st_size of symlinks isn't used too often, and when it is, typically it's for a hint for what buffer size to pass to readlink() -- which a slightly-too-large size still works for.) However, a couple things have changed now. First, there have recently been complaints about the current behavior from real users: - Breakage in rpmbuild: https://github.com/rpm-software-management/rpm/issues/1682 https://github.com/google/fscrypt/issues/305 - Breakage in toybox cpio: https://www.mail-archive.com/toybox@lists.landley.net/msg07193.html - Breakage in libgit2: https://issuetracker.google.com/issues/189629152 (on Android public issue tracker, requires login) Second, we now cache decrypted symlink targets in ->i_link. Therefore, taking the performance hit of reading and decrypting the symlink target in ->getattr() wouldn't be as big a deal as it used to be, since usually it will just save having to do the same thing later. Also note that eCryptfs ended up having to read and decrypt symlink targets in ->getattr() as well, to fix this same issue; see commit 3a60a1686f0d ("eCryptfs: Decrypt symlink target for stat size"). So, let's just bite the bullet, and read and decrypt the symlink target in ->getattr() in order to report the correct st_size. Add a function fscrypt_symlink_getattr() which the filesystems will call to do this. (Alternatively, we could store the decrypted size of symlinks on-disk. But there isn't a great place to do so, and encryption is meant to hide the original size to some extent; that property would be lost.) Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20210702065350.209646-2-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/hooks.c | 44 +++++++++++++++++++++++++++++++++++++++++ include/linux/fscrypt.h | 7 +++++++ 2 files changed, 51 insertions(+) diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index a73b0376e6f3..af74599ae1cf 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -384,3 +384,47 @@ err_kfree: return ERR_PTR(err); } EXPORT_SYMBOL_GPL(fscrypt_get_symlink); + +/** + * fscrypt_symlink_getattr() - set the correct st_size for encrypted symlinks + * @path: the path for the encrypted symlink being queried + * @stat: the struct being filled with the symlink's attributes + * + * Override st_size of encrypted symlinks to be the length of the decrypted + * symlink target (or the no-key encoded symlink target, if the key is + * unavailable) rather than the length of the encrypted symlink target. This is + * necessary for st_size to match the symlink target that userspace actually + * sees. POSIX requires this, and some userspace programs depend on it. + * + * This requires reading the symlink target from disk if needed, setting up the + * inode's encryption key if possible, and then decrypting or encoding the + * symlink target. This makes lstat() more heavyweight than is normally the + * case. However, decrypted symlink targets will be cached in ->i_link, so + * usually the symlink won't have to be read and decrypted again later if/when + * it is actually followed, readlink() is called, or lstat() is called again. + * + * Return: 0 on success, -errno on failure + */ +int fscrypt_symlink_getattr(const struct path *path, struct kstat *stat) +{ + struct dentry *dentry = path->dentry; + struct inode *inode = d_inode(dentry); + const char *link; + DEFINE_DELAYED_CALL(done); + + /* + * To get the symlink target that userspace will see (whether it's the + * decrypted target or the no-key encoded target), we can just get it in + * the same way the VFS does during path resolution and readlink(). + */ + link = READ_ONCE(inode->i_link); + if (!link) { + link = inode->i_op->get_link(dentry, inode, &done); + if (IS_ERR(link)) + return PTR_ERR(link); + } + stat->size = strlen(link); + do_delayed_call(&done); + return 0; +} +EXPORT_SYMBOL_GPL(fscrypt_symlink_getattr); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 2ea1387bb497..b7bfd0cd4f3e 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -253,6 +253,7 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target, const char *fscrypt_get_symlink(struct inode *inode, const void *caddr, unsigned int max_size, struct delayed_call *done); +int fscrypt_symlink_getattr(const struct path *path, struct kstat *stat); static inline void fscrypt_set_ops(struct super_block *sb, const struct fscrypt_operations *s_cop) { @@ -583,6 +584,12 @@ static inline const char *fscrypt_get_symlink(struct inode *inode, return ERR_PTR(-EOPNOTSUPP); } +static inline int fscrypt_symlink_getattr(const struct path *path, + struct kstat *stat) +{ + return -EOPNOTSUPP; +} + static inline void fscrypt_set_ops(struct super_block *sb, const struct fscrypt_operations *s_cop) { From 8c4bca10ceafc43b1ca0a9fab5fa27e13cbce99e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 1 Jul 2021 23:53:47 -0700 Subject: [PATCH 203/417] ext4: report correct st_size for encrypted symlinks The stat() family of syscalls report the wrong size for encrypted symlinks, which has caused breakage in several userspace programs. Fix this by calling fscrypt_symlink_getattr() after ext4_getattr() for encrypted symlinks. This function computes the correct size by reading and decrypting the symlink target (if it's not already cached). For more details, see the commit which added fscrypt_symlink_getattr(). Fixes: f348c252320b ("ext4 crypto: add symlink encryption") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20210702065350.209646-3-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/ext4/symlink.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index dd05af983092..69109746e6e2 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -52,10 +52,20 @@ static const char *ext4_encrypted_get_link(struct dentry *dentry, return paddr; } +static int ext4_encrypted_symlink_getattr(struct user_namespace *mnt_userns, + const struct path *path, + struct kstat *stat, u32 request_mask, + unsigned int query_flags) +{ + ext4_getattr(mnt_userns, path, stat, request_mask, query_flags); + + return fscrypt_symlink_getattr(path, stat); +} + const struct inode_operations ext4_encrypted_symlink_inode_operations = { .get_link = ext4_encrypted_get_link, .setattr = ext4_setattr, - .getattr = ext4_getattr, + .getattr = ext4_encrypted_symlink_getattr, .listxattr = ext4_listxattr, }; From 461b43a8f92e68e96c4424b31e15f2b35f1bbfa9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 1 Jul 2021 23:53:48 -0700 Subject: [PATCH 204/417] f2fs: report correct st_size for encrypted symlinks The stat() family of syscalls report the wrong size for encrypted symlinks, which has caused breakage in several userspace programs. Fix this by calling fscrypt_symlink_getattr() after f2fs_getattr() for encrypted symlinks. This function computes the correct size by reading and decrypting the symlink target (if it's not already cached). For more details, see the commit which added fscrypt_symlink_getattr(). Fixes: cbaf042a3cc6 ("f2fs crypto: add symlink encryption") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20210702065350.209646-4-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/f2fs/namei.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index e149c8c66a71..9c528e583c9d 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -1323,9 +1323,19 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry, return target; } +static int f2fs_encrypted_symlink_getattr(struct user_namespace *mnt_userns, + const struct path *path, + struct kstat *stat, u32 request_mask, + unsigned int query_flags) +{ + f2fs_getattr(mnt_userns, path, stat, request_mask, query_flags); + + return fscrypt_symlink_getattr(path, stat); +} + const struct inode_operations f2fs_encrypted_symlink_inode_operations = { .get_link = f2fs_encrypted_get_link, - .getattr = f2fs_getattr, + .getattr = f2fs_encrypted_symlink_getattr, .setattr = f2fs_setattr, .listxattr = f2fs_listxattr, }; From 064c734986011390b4d111f1a99372b7f26c3850 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 1 Jul 2021 23:53:49 -0700 Subject: [PATCH 205/417] ubifs: report correct st_size for encrypted symlinks The stat() family of syscalls report the wrong size for encrypted symlinks, which has caused breakage in several userspace programs. Fix this by calling fscrypt_symlink_getattr() after ubifs_getattr() for encrypted symlinks. This function computes the correct size by reading and decrypting the symlink target (if it's not already cached). For more details, see the commit which added fscrypt_symlink_getattr(). Fixes: ca7f85be8d6c ("ubifs: Add support for encrypted symlinks") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20210702065350.209646-5-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/ubifs/file.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 2e4e1d159969..5cfa28cd00cd 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1630,6 +1630,17 @@ static const char *ubifs_get_link(struct dentry *dentry, return fscrypt_get_symlink(inode, ui->data, ui->data_len, done); } +static int ubifs_symlink_getattr(struct user_namespace *mnt_userns, + const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags) +{ + ubifs_getattr(mnt_userns, path, stat, request_mask, query_flags); + + if (IS_ENCRYPTED(d_inode(path->dentry))) + return fscrypt_symlink_getattr(path, stat); + return 0; +} + const struct address_space_operations ubifs_file_address_operations = { .readpage = ubifs_readpage, .writepage = ubifs_writepage, @@ -1655,7 +1666,7 @@ const struct inode_operations ubifs_file_inode_operations = { const struct inode_operations ubifs_symlink_inode_operations = { .get_link = ubifs_get_link, .setattr = ubifs_setattr, - .getattr = ubifs_getattr, + .getattr = ubifs_symlink_getattr, .listxattr = ubifs_listxattr, .update_time = ubifs_update_time, }; From e538b0985a05cfe245ada0bb92f177efec6b8a88 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 1 Jul 2021 23:53:50 -0700 Subject: [PATCH 206/417] fscrypt: remove mention of symlink st_size quirk from documentation Now that the correct st_size is reported for encrypted symlinks on all filesystems, update the documentation accordingly. Link: https://lore.kernel.org/r/20210702065350.209646-6-ebiggers@kernel.org Signed-off-by: Eric Biggers --- Documentation/filesystems/fscrypt.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 44b67ebd6e40..02ec57818920 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -1063,11 +1063,6 @@ astute users may notice some differences in behavior: - DAX (Direct Access) is not supported on encrypted files. -- The st_size of an encrypted symlink will not necessarily give the - length of the symlink target as required by POSIX. It will actually - give the length of the ciphertext, which will be slightly longer - than the plaintext due to NUL-padding and an extra 2-byte overhead. - - The maximum length of an encrypted symlink is 2 bytes shorter than the maximum length of an unencrypted symlink. For example, on an EXT4 filesystem with a 4K block size, unencrypted symlinks can be up From ba47b515f59406038a6ad763d4aff1ab50be2038 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 17 Jul 2021 19:01:25 -0500 Subject: [PATCH 207/417] fscrypt: align Base64 encoding with RFC 4648 base64url fscrypt uses a Base64 encoding to encode no-key filenames (the filenames that are presented to userspace when a directory is listed without its encryption key). There are many variants of Base64, but the most common ones are specified by RFC 4648. fscrypt can't use the regular RFC 4648 "base64" variant because "base64" uses the '/' character, which isn't allowed in filenames. However, RFC 4648 also specifies a "base64url" variant for use in URLs and filenames. "base64url" is less common than "base64", but it's still implemented in many programming libraries. Unfortunately, what fscrypt actually uses is a custom Base64 variant that differs from "base64url" in several ways: - The binary data is divided into 6-bit chunks differently. - Values 62 and 63 are encoded with '+' and ',' instead of '-' and '_'. - '='-padding isn't used. This isn't a problem per se, as the padding isn't technically necessary, and RFC 4648 doesn't strictly require it. But it needs to be properly documented. There have been two attempts to copy the fscrypt Base64 code into lib/ (https://lkml.kernel.org/r/20200821182813.52570-6-jlayton@kernel.org and https://lkml.kernel.org/r/20210716110428.9727-5-hare@suse.de), and both have been caught up by the fscrypt Base64 variant being nonstandard and not properly documented. Also, the planned use of the fscrypt Base64 code in the CephFS storage back-end will prevent it from being changed later (whereas currently it can still be changed), so we need to choose an encoding that we're happy with before it's too late. Therefore, switch the fscrypt Base64 variant to base64url, in order to align more closely with RFC 4648 and other implementations and uses of Base64. However, I opted not to implement '='-padding, as '='-padding adds complexity, is unnecessary, and isn't required by the RFC. Link: https://lore.kernel.org/r/20210718000125.59701-1-ebiggers@kernel.org Reviewed-by: Hannes Reinecke Signed-off-by: Eric Biggers --- Documentation/filesystems/fscrypt.rst | 10 +-- fs/crypto/fname.c | 106 ++++++++++++++++---------- 2 files changed, 70 insertions(+), 46 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 02ec57818920..0eb799d9d05a 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -1230,12 +1230,12 @@ the user-supplied name to get the ciphertext. Lookups without the key are more complicated. The raw ciphertext may contain the ``\0`` and ``/`` characters, which are illegal in -filenames. Therefore, readdir() must base64-encode the ciphertext for -presentation. For most filenames, this works fine; on ->lookup(), the -filesystem just base64-decodes the user-supplied name to get back to -the raw ciphertext. +filenames. Therefore, readdir() must base64url-encode the ciphertext +for presentation. For most filenames, this works fine; on ->lookup(), +the filesystem just base64url-decodes the user-supplied name to get +back to the raw ciphertext. -However, for very long filenames, base64 encoding would cause the +However, for very long filenames, base64url encoding would cause the filename length to exceed NAME_MAX. To prevent this, readdir() actually presents long filenames in an abbreviated form which encodes a strong "hash" of the ciphertext filename, along with the optional diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index d00455440d08..eb538c28df94 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -26,7 +26,7 @@ * it to find the directory entry again if requested. Naively, that would just * mean using the ciphertext filenames. However, since the ciphertext filenames * can contain illegal characters ('\0' and '/'), they must be encoded in some - * way. We use base64. But that can cause names to exceed NAME_MAX (255 + * way. We use base64url. But that can cause names to exceed NAME_MAX (255 * bytes), so we also need to use a strong hash to abbreviate long names. * * The filesystem may also need another kind of hash, the "dirhash", to quickly @@ -38,7 +38,7 @@ * casefolded directories use this type of dirhash. At least in these cases, * each no-key name must include the name's dirhash too. * - * To meet all these requirements, we base64-encode the following + * To meet all these requirements, we base64url-encode the following * variable-length structure. It contains the dirhash, or 0's if the filesystem * didn't provide one; up to 149 bytes of the ciphertext name; and for * ciphertexts longer than 149 bytes, also the SHA-256 of the remaining bytes. @@ -52,15 +52,19 @@ struct fscrypt_nokey_name { u32 dirhash[2]; u8 bytes[149]; u8 sha256[SHA256_DIGEST_SIZE]; -}; /* 189 bytes => 252 bytes base64-encoded, which is <= NAME_MAX (255) */ +}; /* 189 bytes => 252 bytes base64url-encoded, which is <= NAME_MAX (255) */ /* - * Decoded size of max-size nokey name, i.e. a name that was abbreviated using + * Decoded size of max-size no-key name, i.e. a name that was abbreviated using * the strong hash and thus includes the 'sha256' field. This isn't simply * sizeof(struct fscrypt_nokey_name), as the padding at the end isn't included. */ #define FSCRYPT_NOKEY_NAME_MAX offsetofend(struct fscrypt_nokey_name, sha256) +/* Encoded size of max-size no-key name */ +#define FSCRYPT_NOKEY_NAME_MAX_ENCODED \ + FSCRYPT_BASE64URL_CHARS(FSCRYPT_NOKEY_NAME_MAX) + static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) { if (str->len == 1 && str->name[0] == '.') @@ -175,62 +179,82 @@ static int fname_decrypt(const struct inode *inode, return 0; } -static const char lookup_table[65] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; +static const char base64url_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -#define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3) +#define FSCRYPT_BASE64URL_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3) /** - * base64_encode() - base64-encode some bytes - * @src: the bytes to encode - * @len: number of bytes to encode - * @dst: (output) the base64-encoded string. Not NUL-terminated. + * fscrypt_base64url_encode() - base64url-encode some binary data + * @src: the binary data to encode + * @srclen: the length of @src in bytes + * @dst: (output) the base64url-encoded string. Not NUL-terminated. * - * Encodes the input string using characters from the set [A-Za-z0-9+,]. - * The encoded string is roughly 4/3 times the size of the input string. + * Encodes data using base64url encoding, i.e. the "Base 64 Encoding with URL + * and Filename Safe Alphabet" specified by RFC 4648. '='-padding isn't used, + * as it's unneeded and not required by the RFC. base64url is used instead of + * base64 to avoid the '/' character, which isn't allowed in filenames. * - * Return: length of the encoded string + * Return: the length of the resulting base64url-encoded string in bytes. + * This will be equal to FSCRYPT_BASE64URL_CHARS(srclen). */ -static int base64_encode(const u8 *src, int len, char *dst) +static int fscrypt_base64url_encode(const u8 *src, int srclen, char *dst) { - int i, bits = 0, ac = 0; + u32 ac = 0; + int bits = 0; + int i; char *cp = dst; - for (i = 0; i < len; i++) { - ac += src[i] << bits; + for (i = 0; i < srclen; i++) { + ac = (ac << 8) | src[i]; bits += 8; do { - *cp++ = lookup_table[ac & 0x3f]; - ac >>= 6; bits -= 6; + *cp++ = base64url_table[(ac >> bits) & 0x3f]; } while (bits >= 6); } if (bits) - *cp++ = lookup_table[ac & 0x3f]; + *cp++ = base64url_table[(ac << (6 - bits)) & 0x3f]; return cp - dst; } -static int base64_decode(const char *src, int len, u8 *dst) +/** + * fscrypt_base64url_decode() - base64url-decode a string + * @src: the string to decode. Doesn't need to be NUL-terminated. + * @srclen: the length of @src in bytes + * @dst: (output) the decoded binary data + * + * Decodes a string using base64url encoding, i.e. the "Base 64 Encoding with + * URL and Filename Safe Alphabet" specified by RFC 4648. '='-padding isn't + * accepted, nor are non-encoding characters such as whitespace. + * + * This implementation hasn't been optimized for performance. + * + * Return: the length of the resulting decoded binary data in bytes, + * or -1 if the string isn't a valid base64url string. + */ +static int fscrypt_base64url_decode(const char *src, int srclen, u8 *dst) { - int i, bits = 0, ac = 0; - const char *p; - u8 *cp = dst; + u32 ac = 0; + int bits = 0; + int i; + u8 *bp = dst; + + for (i = 0; i < srclen; i++) { + const char *p = strchr(base64url_table, src[i]); - for (i = 0; i < len; i++) { - p = strchr(lookup_table, src[i]); if (p == NULL || src[i] == 0) - return -2; - ac += (p - lookup_table) << bits; + return -1; + ac = (ac << 6) | (p - base64url_table); bits += 6; if (bits >= 8) { - *cp++ = ac & 0xff; - ac >>= 8; bits -= 8; + *bp++ = (u8)(ac >> bits); } } - if (ac) + if (ac & ((1 << bits) - 1)) return -1; - return cp - dst; + return bp - dst; } bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, @@ -263,10 +287,8 @@ bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, int fscrypt_fname_alloc_buffer(u32 max_encrypted_len, struct fscrypt_str *crypto_str) { - const u32 max_encoded_len = BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX); - u32 max_presented_len; - - max_presented_len = max(max_encoded_len, max_encrypted_len); + u32 max_presented_len = max_t(u32, FSCRYPT_NOKEY_NAME_MAX_ENCODED, + max_encrypted_len); crypto_str->name = kmalloc(max_presented_len + 1, GFP_NOFS); if (!crypto_str->name) @@ -342,7 +364,7 @@ int fscrypt_fname_disk_to_usr(const struct inode *inode, offsetof(struct fscrypt_nokey_name, bytes)); BUILD_BUG_ON(offsetofend(struct fscrypt_nokey_name, bytes) != offsetof(struct fscrypt_nokey_name, sha256)); - BUILD_BUG_ON(BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX) > NAME_MAX); + BUILD_BUG_ON(FSCRYPT_NOKEY_NAME_MAX_ENCODED > NAME_MAX); nokey_name.dirhash[0] = hash; nokey_name.dirhash[1] = minor_hash; @@ -358,7 +380,8 @@ int fscrypt_fname_disk_to_usr(const struct inode *inode, nokey_name.sha256); size = FSCRYPT_NOKEY_NAME_MAX; } - oname->len = base64_encode((const u8 *)&nokey_name, size, oname->name); + oname->len = fscrypt_base64url_encode((const u8 *)&nokey_name, size, + oname->name); return 0; } EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); @@ -432,14 +455,15 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, * user-supplied name */ - if (iname->len > BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX)) + if (iname->len > FSCRYPT_NOKEY_NAME_MAX_ENCODED) return -ENOENT; fname->crypto_buf.name = kmalloc(FSCRYPT_NOKEY_NAME_MAX, GFP_KERNEL); if (fname->crypto_buf.name == NULL) return -ENOMEM; - ret = base64_decode(iname->name, iname->len, fname->crypto_buf.name); + ret = fscrypt_base64url_decode(iname->name, iname->len, + fname->crypto_buf.name); if (ret < (int)offsetof(struct fscrypt_nokey_name, bytes[1]) || (ret > offsetof(struct fscrypt_nokey_name, sha256) && ret != FSCRYPT_NOKEY_NAME_MAX)) { From d337a44e429e6de23ed3d73fcb81ec44f7b05522 Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Tue, 27 Jul 2021 09:24:51 +0900 Subject: [PATCH 208/417] ksmbd: Return STATUS_OBJECT_PATH_NOT_FOUND if smb2_creat() returns ENOENT Both Windows 10's SMB server and samba return STATUS_OBJECT_PATH_NOT_FOUND when trying to access a nonexistent path. This fixes Windows 10 File History tool. The latter relies on the server returning STATUS_OBJECT_PATH_NOT_FOUND to figure out what part of the target path needs to be created. Returning STATUS_OBJECT_NAME_INVALID will make it stop and display an error to the user. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 7e6e3d8c20e8..1b1f34d9d1a0 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2768,8 +2768,13 @@ int smb2_open(struct ksmbd_work *work) if (!file_present) { rc = smb2_creat(work, &path, name, open_flags, posix_mode, req->CreateOptions & FILE_DIRECTORY_FILE_LE); - if (rc) + if (rc) { + if (rc == -ENOENT) { + rc = -EIO; + rsp->hdr.Status = STATUS_OBJECT_PATH_NOT_FOUND; + } goto err_out; + } created = true; user_ns = mnt_user_ns(path.mnt); From 9fb8fac08f6670c9bba70d6c616ad84dd7a45528 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 27 Jul 2021 09:28:10 +0900 Subject: [PATCH 209/417] ksmbd: don't set RSS capable in FSCTL_QUERY_NETWORK_INTERFACE_INFO ksmbd does not support RSS mode stably. RSS mode enabling will be set later. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 1b1f34d9d1a0..93e98ef94c73 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -7091,8 +7091,6 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); nii_rsp->Capability = 0; - if (netdev->num_tx_queues > 1) - nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE); if (ksmbd_rdma_capable_netdev(netdev)) nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE); From 08bdbc6ef46ad522a24dc6b8e01c039cb0c7e761 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 27 Jul 2021 09:30:29 +0900 Subject: [PATCH 210/417] ksmbd: use channel signingkey for binding SMB2 session setup Windows client disconnect connection by wrong signed SMB2 session setup response on SMB3 multichannel mode. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 93e98ef94c73..7f5ca471b306 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -8150,7 +8150,8 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) len = ALIGN(len, 8); } - if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + if (conn->binding == false && + le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { signing_key = work->sess->smb3signingkey; } else { chann = lookup_chann_list(work->sess, work->conn); From 6c99dfc4c5f6fa1f5a90c068be6201d7a0cebff1 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 27 Jul 2021 09:40:05 +0900 Subject: [PATCH 211/417] ksmbd: fix missing error code in smb2_lock Dan report a warning that is missing error code in smb2_lock from static checker. This patch add error code to avoid static checker warning. Reported-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 66 ++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 7f5ca471b306..b52375ce19e9 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -6587,7 +6587,7 @@ int smb2_lock(struct ksmbd_work *work) int lock_count; int flags = 0; int cmd = 0; - int err = 0, i; + int err = -EIO, i, rc = 0; u64 lock_start, lock_length; struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp, *tmp2; struct ksmbd_conn *conn; @@ -6603,7 +6603,7 @@ int smb2_lock(struct ksmbd_work *work) if (!fp) { ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", le64_to_cpu(req->VolatileFileId)); - rsp->hdr.Status = STATUS_FILE_CLOSED; + err = -ENOENT; goto out2; } @@ -6613,7 +6613,7 @@ int smb2_lock(struct ksmbd_work *work) ksmbd_debug(SMB, "lock count is %d\n", lock_count); if (!lock_count) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; + err = -EINVAL; goto out2; } @@ -6621,10 +6621,8 @@ int smb2_lock(struct ksmbd_work *work) flags = le32_to_cpu(lock_ele[i].Flags); flock = smb_flock_init(filp); - if (!flock) { - rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + if (!flock) goto out; - } cmd = smb2_set_flock_flags(flock, flags); @@ -6662,8 +6660,7 @@ int smb2_lock(struct ksmbd_work *work) if (cmp_lock->fl->fl_type != F_UNLCK && flock->fl_type != F_UNLCK) { pr_err("conflict two locks in one request\n"); - rsp->hdr.Status = - STATUS_INVALID_PARAMETER; + err = -EINVAL; goto out; } } @@ -6671,19 +6668,19 @@ int smb2_lock(struct ksmbd_work *work) smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list); if (!smb_lock) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; + err = -EINVAL; goto out; } } list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { if (smb_lock->cmd < 0) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; + err = -EINVAL; goto out; } if (!(smb_lock->flags & SMB2_LOCKFLAG_MASK)) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; + err = -EINVAL; goto out; } @@ -6691,7 +6688,7 @@ int smb2_lock(struct ksmbd_work *work) smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) || (prior_lock == SMB2_LOCKFLAG_UNLOCK && !(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK))) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; + err = -EINVAL; goto out; } @@ -6744,8 +6741,7 @@ int smb2_lock(struct ksmbd_work *work) spin_unlock(&conn->llist_lock); read_unlock(&conn_list_lock); pr_err("previous lock conflict with zero byte lock range\n"); - rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; - goto out; + goto out; } if (smb_lock->zero_len && !cmp_lock->zero_len && @@ -6754,8 +6750,7 @@ int smb2_lock(struct ksmbd_work *work) spin_unlock(&conn->llist_lock); read_unlock(&conn_list_lock); pr_err("current lock conflict with zero byte lock range\n"); - rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; - goto out; + goto out; } if (((cmp_lock->start <= smb_lock->start && @@ -6766,8 +6761,6 @@ int smb2_lock(struct ksmbd_work *work) spin_unlock(&conn->llist_lock); read_unlock(&conn_list_lock); pr_err("Not allow lock operation on exclusive lock range\n"); - rsp->hdr.Status = - STATUS_LOCK_NOT_GRANTED; goto out; } } @@ -6790,19 +6783,19 @@ no_check_cl: flock = smb_lock->fl; list_del(&smb_lock->llist); retry: - err = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); + rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); skip: if (flags & SMB2_LOCKFLAG_UNLOCK) { - if (!err) { + if (!rc) { ksmbd_debug(SMB, "File unlocked\n"); - } else if (err == -ENOENT) { + } else if (rc == -ENOENT) { rsp->hdr.Status = STATUS_NOT_LOCKED; goto out; } locks_free_lock(flock); kfree(smb_lock); } else { - if (err == FILE_LOCK_DEFERRED) { + if (rc == FILE_LOCK_DEFERRED) { void **argv; ksmbd_debug(SMB, @@ -6820,12 +6813,11 @@ skip: } argv[0] = flock; - err = setup_async_work(work, - smb2_remove_blocked_lock, - argv); - if (err) { - rsp->hdr.Status = - STATUS_INSUFFICIENT_RESOURCES; + rc = setup_async_work(work, + smb2_remove_blocked_lock, + argv); + if (rc) { + err = -ENOMEM; goto out; } spin_lock(&fp->f_lock); @@ -6872,7 +6864,7 @@ skip: list_del(&work->fp_entry); spin_unlock(&fp->f_lock); goto retry; - } else if (!err) { + } else if (!rc) { spin_lock(&work->conn->llist_lock); list_add_tail(&smb_lock->clist, &work->conn->lock_list); @@ -6882,7 +6874,6 @@ skip: list_add(&smb_lock->llist, &rollback_list); ksmbd_debug(SMB, "successful in taking lock\n"); } else { - rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; goto out; } } @@ -6908,7 +6899,6 @@ out: list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { struct file_lock *rlock = NULL; - int rc; rlock = smb_flock_init(filp); rlock->fl_type = F_UNLCK; @@ -6931,7 +6921,19 @@ out: kfree(smb_lock); } out2: - ksmbd_debug(SMB, "failed in taking lock(flags : %x)\n", flags); + ksmbd_debug(SMB, "failed in taking lock(flags : %x), err : %d\n", flags, err); + + if (!rsp->hdr.Status) { + if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (err == -ENOMEM) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else + rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + } + smb2_set_err_rsp(work); ksmbd_fd_put(work, fp); return err; From f1abdb78a1080a49deac6e91e1675525d1d3dfbe Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 27 Jul 2021 13:25:13 +0900 Subject: [PATCH 212/417] ksmbd: add ipv6_addr_v4mapped check to know if connection from client is ipv4 ksmbd create socket with IPv6 to listen both IPv4 and IPv6 connection from client. Server should send IP addresses of NICs through network interface info response. If Client connection is IPv4, Server should fill IPv4 address in response buffer. But ss_family is always PF_INET6 on IPv6 socket. So This patch add ipv6_addr_v4mapped check to know client connection is IPv4. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index b52375ce19e9..209e32e61a75 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -7078,6 +7078,7 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, struct sockaddr_storage_rsp *sockaddr_storage; unsigned int flags; unsigned long long speed; + struct sockaddr_in6 *csin6 = (struct sockaddr_in6 *)&conn->peer_addr; rtnl_lock(); for_each_netdev(&init_net, netdev) { @@ -7117,7 +7118,8 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, nii_rsp->SockAddr_Storage; memset(sockaddr_storage, 0, 128); - if (conn->peer_addr.ss_family == PF_INET) { + if (conn->peer_addr.ss_family == PF_INET || + ipv6_addr_v4mapped(&csin6->sin6_addr)) { struct in_device *idev; sockaddr_storage->Family = cpu_to_le16(INTERNETWORK); From 38ef66b05cfa3560323344a0b3e09e583f1eb974 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 28 Jul 2021 21:37:28 -0700 Subject: [PATCH 213/417] fscrypt: document struct fscrypt_operations Document all fields of struct fscrypt_operations so that it's more clear what filesystems that use (or plan to use) fs/crypto/ need to implement. Link: https://lore.kernel.org/r/20210729043728.18480-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- include/linux/fscrypt.h | 109 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 4 deletions(-) diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index b7bfd0cd4f3e..e912ed9141d9 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -47,27 +47,128 @@ struct fscrypt_name { #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 #ifdef CONFIG_FS_ENCRYPTION + /* - * fscrypt superblock flags + * If set, the fscrypt bounce page pool won't be allocated (unless another + * filesystem needs it). Set this if the filesystem always uses its own bounce + * pages for writes and therefore won't need the fscrypt bounce page pool. */ #define FS_CFLG_OWN_PAGES (1U << 1) -/* - * crypto operations for filesystems - */ +/* Crypto operations for filesystems */ struct fscrypt_operations { + + /* Set of optional flags; see above for allowed flags */ unsigned int flags; + + /* + * If set, this is a filesystem-specific key description prefix that + * will be accepted for "logon" keys for v1 fscrypt policies, in + * addition to the generic prefix "fscrypt:". This functionality is + * deprecated, so new filesystems shouldn't set this field. + */ const char *key_prefix; + + /* + * Get the fscrypt context of the given inode. + * + * @inode: the inode whose context to get + * @ctx: the buffer into which to get the context + * @len: length of the @ctx buffer in bytes + * + * Return: On success, returns the length of the context in bytes; this + * may be less than @len. On failure, returns -ENODATA if the + * inode doesn't have a context, -ERANGE if the context is + * longer than @len, or another -errno code. + */ int (*get_context)(struct inode *inode, void *ctx, size_t len); + + /* + * Set an fscrypt context on the given inode. + * + * @inode: the inode whose context to set. The inode won't already have + * an fscrypt context. + * @ctx: the context to set + * @len: length of @ctx in bytes (at most FSCRYPT_SET_CONTEXT_MAX_SIZE) + * @fs_data: If called from fscrypt_set_context(), this will be the + * value the filesystem passed to fscrypt_set_context(). + * Otherwise (i.e. when called from + * FS_IOC_SET_ENCRYPTION_POLICY) this will be NULL. + * + * i_rwsem will be held for write. + * + * Return: 0 on success, -errno on failure. + */ int (*set_context)(struct inode *inode, const void *ctx, size_t len, void *fs_data); + + /* + * Get the dummy fscrypt policy in use on the filesystem (if any). + * + * Filesystems only need to implement this function if they support the + * test_dummy_encryption mount option. + * + * Return: A pointer to the dummy fscrypt policy, if the filesystem is + * mounted with test_dummy_encryption; otherwise NULL. + */ const union fscrypt_policy *(*get_dummy_policy)(struct super_block *sb); + + /* + * Check whether a directory is empty. i_rwsem will be held for write. + */ bool (*empty_dir)(struct inode *inode); + + /* The filesystem's maximum ciphertext filename length, in bytes */ unsigned int max_namelen; + + /* + * Check whether the filesystem's inode numbers and UUID are stable, + * meaning that they will never be changed even by offline operations + * such as filesystem shrinking and therefore can be used in the + * encryption without the possibility of files becoming unreadable. + * + * Filesystems only need to implement this function if they want to + * support the FSCRYPT_POLICY_FLAG_IV_INO_LBLK_{32,64} flags. These + * flags are designed to work around the limitations of UFS and eMMC + * inline crypto hardware, and they shouldn't be used in scenarios where + * such hardware isn't being used. + * + * Leaving this NULL is equivalent to always returning false. + */ bool (*has_stable_inodes)(struct super_block *sb); + + /* + * Get the number of bits that the filesystem uses to represent inode + * numbers and file logical block numbers. + * + * By default, both of these are assumed to be 64-bit. This function + * can be implemented to declare that either or both of these numbers is + * shorter, which may allow the use of the + * FSCRYPT_POLICY_FLAG_IV_INO_LBLK_{32,64} flags and/or the use of + * inline crypto hardware whose maximum DUN length is less than 64 bits + * (e.g., eMMC v5.2 spec compliant hardware). This function only needs + * to be implemented if support for one of these features is needed. + */ void (*get_ino_and_lblk_bits)(struct super_block *sb, int *ino_bits_ret, int *lblk_bits_ret); + + /* + * Return the number of block devices to which the filesystem may write + * encrypted file contents. + * + * If the filesystem can use multiple block devices (other than block + * devices that aren't used for encrypted file contents, such as + * external journal devices), and wants to support inline encryption, + * then it must implement this function. Otherwise it's not needed. + */ int (*get_num_devices)(struct super_block *sb); + + /* + * If ->get_num_devices() returns a value greater than 1, then this + * function is called to get the array of request_queues that the + * filesystem is using -- one per block device. (There may be duplicate + * entries in this array, as block devices can share a request_queue.) + */ void (*get_devices)(struct super_block *sb, struct request_queue **devs); }; From 8b99f3504b688e3b55380521b6bf68c3d0c485d6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 2 Aug 2021 08:14:03 +0900 Subject: [PATCH 214/417] ksmbd: fix an oops in error handling in smb2_open() If smb2_get_name() then name is an error pointer. In the clean up code, we try to kfree() it and that will lead to an Oops. Set it to NULL instead. Signed-off-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 209e32e61a75..636570ecfa31 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2462,6 +2462,7 @@ int smb2_open(struct ksmbd_work *work) rc = PTR_ERR(name); if (rc != -ENOMEM) rc = -ENOENT; + name = NULL; goto err_out1; } From d03ef4daf33a33da8d7c397102fff8ae87d04a93 Mon Sep 17 00:00:00 2001 From: Wang Shilong Date: Tue, 3 Aug 2021 09:48:04 -0700 Subject: [PATCH 215/417] fs: forbid invalid project ID fileattr_set_prepare() should check if project ID is valid, otherwise dqget() will return NULL for such project ID quota. Signed-off-by: Wang Shilong Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/ioctl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/ioctl.c b/fs/ioctl.c index 1e2204fa9963..d4fabb5421cd 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -817,6 +817,14 @@ static int fileattr_set_prepare(struct inode *inode, if ((old_ma->fsx_xflags ^ fa->fsx_xflags) & FS_XFLAG_PROJINHERIT) return -EINVAL; + } else { + /* + * Caller is allowed to change the project ID. If it is being + * changed, make sure that the new value is valid. + */ + if (old_ma->fsx_projid != fa->fsx_projid && + !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid))) + return -EINVAL; } /* Check extent size hints. */ From 9d9b16054b7d357afde69a027514c695092b0d22 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Tue, 1 Jun 2021 09:41:40 -0500 Subject: [PATCH 216/417] gfs2: Fix glock recursion in freeze_go_xmote_bh We must not call gfs2_consist (which does a file system withdraw) from the freeze glock's freeze_go_xmote_bh function because the withdraw will try to use the freeze glock, thus causing a glock recursion error. This patch changes freeze_go_xmote_bh to call function gfs2_assert_withdraw_delayed instead of gfs2_consist to avoid recursion. Signed-off-by: Bob Peterson --- fs/gfs2/glops.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 54d3fbeb3002..384565d63eea 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -610,16 +610,13 @@ static int freeze_go_xmote_bh(struct gfs2_glock *gl) j_gl->gl_ops->go_inval(j_gl, DIO_METADATA); error = gfs2_find_jhead(sdp->sd_jdesc, &head, false); - if (error) - gfs2_consist(sdp); - if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) - gfs2_consist(sdp); - - /* Initialize some head of the log stuff */ - if (!gfs2_withdrawn(sdp)) { - sdp->sd_log_sequence = head.lh_sequence + 1; - gfs2_log_pointers_init(sdp, head.lh_blkno); - } + if (gfs2_assert_withdraw_delayed(sdp, !error)) + return error; + if (gfs2_assert_withdraw_delayed(sdp, head.lh_flags & + GFS2_LOG_HEAD_UNMOUNT)) + return -EIO; + sdp->sd_log_sequence = head.lh_sequence + 1; + gfs2_log_pointers_init(sdp, head.lh_blkno); } return 0; } From eebff916f07775b2ecd9186439e69a70af24630b Mon Sep 17 00:00:00 2001 From: Marios Makassikis Date: Thu, 12 Aug 2021 10:12:35 +0900 Subject: [PATCH 217/417] ksmbd: Fix multi-protocol negotiation To negotiate either the SMB2 protocol or SMB protocol, a client must send a SMB_COM_NEGOTIATE message containing the list of dialects it supports, to which the server will respond with either a SMB_COM_NEGOTIATE or a SMB2_NEGOTIATE response. The current implementation responds with the highest common dialect, rather than looking explicitly for "SMB 2.???" and "SMB 2.002", as indicated in [MS-SMB2]: [MS-SMB2] 3.3.5.3.1: If the server does not implement the SMB 2.1 or 3.x dialect family, processing MUST continue as specified in 3.3.5.3.2. Otherwise, the server MUST scan the dialects provided for the dialect string "SMB 2.???". If the string is not present, continue to section 3.3.5.3.2. If the string is present, the server MUST respond with an SMB2 NEGOTIATE Response as specified in 2.2.4. [MS-SMB2] 3.3.5.3.2: The server MUST scan the dialects provided for the dialect string "SMB 2.002". If the string is present, the client understands SMB2, and the server MUST respond with an SMB2 NEGOTIATE Response. This is an issue if a client attempts to negotiate SMB3.1.1 using a SMB_COM_NEGOTIATE, as it will trigger the following NULL pointer dereference: 8<--- cut here --- Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = 1917455e [00000000] *pgd=00000000 Internal error: Oops: 17 [#1] ARM CPU: 0 PID: 60 Comm: kworker/0:1 Not tainted 5.4.60-00027-g0518c02b5c5b #35 Hardware name: Marvell Kirkwood (Flattened Device Tree) Workqueue: ksmbd-io handle_ksmbd_work PC is at ksmbd_gen_preauth_integrity_hash+0x24/0x190 LR is at smb3_preauth_hash_rsp+0x50/0xa0 pc : [<802b7044>] lr : [<802d6ac0>] psr: 40000013 sp : bf199ed8 ip : 00000000 fp : 80d1edb0 r10: 80a3471b r9 : 8091af16 r8 : 80d70640 r7 : 00000072 r6 : be95e198 r5 : ca000000 r4 : b97fee00 r3 : 00000000 r2 : 00000002 r1 : b97fea00 r0 : b97fee00 Flags: nZcv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user Control: 0005317f Table: 3e7f4000 DAC: 00000055 Process kworker/0:1 (pid: 60, stack limit = 0x3dd1fdb4) Stack: (0xbf199ed8 to 0xbf19a000) 9ec0: b97fee00 00000000 9ee0: be95e198 00000072 80d70640 802d6ac0 b3da2680 b97fea00 424d53ff be95e140 9f00: b97fee00 802bd7b0 bf10fa58 80128a78 00000000 000001c8 b6220000 bf0b7720 9f20: be95e198 80d0c410 bf7e2a00 00000000 00000000 be95e19c 80d0c370 80123b90 9f40: bf0b7720 be95e198 bf0b7720 bf0b7734 80d0c410 bf198000 80d0c424 80d116e0 9f60: bf10fa58 801240c0 00000000 bf10fa40 bf1463a0 bf198000 bf0b7720 80123ed0 9f80: bf077ee4 bf10fa58 00000000 80127f80 bf1463a0 80127e88 00000000 00000000 9fa0: 00000000 00000000 00000000 801010d0 00000000 00000000 00000000 00000000 9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9fe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000 [<802b7044>] (ksmbd_gen_preauth_integrity_hash) from [<802d6ac0>] (smb3_preauth_hash_rsp+0x50/0xa0) [<802d6ac0>] (smb3_preauth_hash_rsp) from [<802bd7b0>] (handle_ksmbd_work+0x348/0x3f8) [<802bd7b0>] (handle_ksmbd_work) from [<80123b90>] (process_one_work+0x160/0x200) [<80123b90>] (process_one_work) from [<801240c0>] (worker_thread+0x1f0/0x2e4) [<801240c0>] (worker_thread) from [<80127f80>] (kthread+0xf8/0x10c) [<80127f80>] (kthread) from [<801010d0>] (ret_from_fork+0x14/0x24) Exception stack(0xbf199fb0 to 0xbf199ff8) 9fa0: 00000000 00000000 00000000 00000000 9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9fe0: 00000000 00000000 00000000 00000000 00000013 00000000 Code: e1855803 e5d13003 e1855c03 e5903094 (e1d330b0) ---[ end trace 8d03be3ed09e5699 ]--- Kernel panic - not syncing: Fatal exception smb3_preauth_hash_rsp() panics because conn->preauth_info is only allocated when processing a SMB2 NEGOTIATE request. Fix this by splitting the smb_protos array into two, each containing only SMB1 and SMB2 dialects respectively. While here, make ksmbd_negotiate_smb_dialect() static as it not called from anywhere else. Signed-off-by: Marios Makassikis Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb_common.c | 53 +++++++++++++++++++++++++++++-------------- fs/ksmbd/smb_common.h | 1 - 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c index 24c6bb476f6e..b108b918ec84 100644 --- a/fs/ksmbd/smb_common.c +++ b/fs/ksmbd/smb_common.c @@ -30,7 +30,7 @@ struct smb_protocol { __u16 prot_id; }; -static struct smb_protocol smb_protos[] = { +static struct smb_protocol smb1_protos[] = { { SMB21_PROT, "\2SMB 2.1", @@ -43,6 +43,15 @@ static struct smb_protocol smb_protos[] = { "SMB2_22", SMB2X_PROT_ID }, +}; + +static struct smb_protocol smb2_protos[] = { + { + SMB21_PROT, + "\2SMB 2.1", + "SMB2_10", + SMB21_PROT_ID + }, { SMB30_PROT, "\2SMB 3.0", @@ -90,14 +99,24 @@ inline int ksmbd_max_protocol(void) int ksmbd_lookup_protocol_idx(char *str) { - int offt = ARRAY_SIZE(smb_protos) - 1; + int offt = ARRAY_SIZE(smb1_protos) - 1; int len = strlen(str); while (offt >= 0) { - if (!strncmp(str, smb_protos[offt].prot, len)) { + if (!strncmp(str, smb1_protos[offt].prot, len)) { ksmbd_debug(SMB, "selected %s dialect idx = %d\n", - smb_protos[offt].prot, offt); - return smb_protos[offt].index; + smb1_protos[offt].prot, offt); + return smb1_protos[offt].index; + } + offt--; + } + + offt = ARRAY_SIZE(smb2_protos) - 1; + while (offt >= 0) { + if (!strncmp(str, smb2_protos[offt].prot, len)) { + ksmbd_debug(SMB, "selected %s dialect idx = %d\n", + smb2_protos[offt].prot, offt); + return smb2_protos[offt].index; } offt--; } @@ -169,7 +188,7 @@ static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) int i, seq_num, bcount, next; char *dialect; - for (i = ARRAY_SIZE(smb_protos) - 1; i >= 0; i--) { + for (i = ARRAY_SIZE(smb1_protos) - 1; i >= 0; i--) { seq_num = 0; next = 0; dialect = cli_dialects; @@ -178,14 +197,14 @@ static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) dialect = next_dialect(dialect, &next); ksmbd_debug(SMB, "client requested dialect %s\n", dialect); - if (!strcmp(dialect, smb_protos[i].name)) { - if (supported_protocol(smb_protos[i].index)) { + if (!strcmp(dialect, smb1_protos[i].name)) { + if (supported_protocol(smb1_protos[i].index)) { ksmbd_debug(SMB, "selected %s dialect\n", - smb_protos[i].name); - if (smb_protos[i].index == SMB1_PROT) + smb1_protos[i].name); + if (smb1_protos[i].index == SMB1_PROT) return seq_num; - return smb_protos[i].prot_id; + return smb1_protos[i].prot_id; } } seq_num++; @@ -201,19 +220,19 @@ int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) int i; int count; - for (i = ARRAY_SIZE(smb_protos) - 1; i >= 0; i--) { + for (i = ARRAY_SIZE(smb2_protos) - 1; i >= 0; i--) { count = le16_to_cpu(dialects_count); while (--count >= 0) { ksmbd_debug(SMB, "client requested dialect 0x%x\n", le16_to_cpu(cli_dialects[count])); if (le16_to_cpu(cli_dialects[count]) != - smb_protos[i].prot_id) + smb2_protos[i].prot_id) continue; - if (supported_protocol(smb_protos[i].index)) { + if (supported_protocol(smb2_protos[i].index)) { ksmbd_debug(SMB, "selected %s dialect\n", - smb_protos[i].name); - return smb_protos[i].prot_id; + smb2_protos[i].name); + return smb2_protos[i].prot_id; } } } @@ -221,7 +240,7 @@ int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) return BAD_PROT_ID; } -int ksmbd_negotiate_smb_dialect(void *buf) +static int ksmbd_negotiate_smb_dialect(void *buf) { __le32 proto; diff --git a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h index b8c350725905..c4219c3432e2 100644 --- a/fs/ksmbd/smb_common.h +++ b/fs/ksmbd/smb_common.h @@ -498,7 +498,6 @@ bool ksmbd_smb_request(struct ksmbd_conn *conn); int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count); -int ksmbd_negotiate_smb_dialect(void *buf); int ksmbd_init_smb_server(struct ksmbd_work *work); bool ksmbd_pdu_size_has_room(unsigned int pdu); From f4228b678b410a401148f9ad9911d0013fa0f24e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 12 Aug 2021 10:16:40 +0900 Subject: [PATCH 218/417] ksmbd: change int data type to boolean Change data type of function that return only 0 or 1 to boolean. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 32 ++++++++++++++++---------------- fs/ksmbd/smb2pdu.h | 6 +++--- fs/ksmbd/smb_common.h | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 636570ecfa31..0de4163978ce 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -58,18 +58,18 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) * * Return: 1 if valid session id, otherwise 0 */ -static inline int check_session_id(struct ksmbd_conn *conn, u64 id) +static inline bool check_session_id(struct ksmbd_conn *conn, u64 id) { struct ksmbd_session *sess; if (id == 0 || id == -1) - return 0; + return false; sess = ksmbd_session_lookup_all(conn, id); if (sess) - return 1; + return true; pr_err("Invalid user session id: %llu\n", id); - return 0; + return false; } struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) @@ -145,45 +145,45 @@ void smb2_set_err_rsp(struct ksmbd_work *work) * is_smb2_neg_cmd() - is it smb2 negotiation command * @work: smb work containing smb header * - * Return: 1 if smb2 negotiation command, otherwise 0 + * Return: true if smb2 negotiation command, otherwise false */ -int is_smb2_neg_cmd(struct ksmbd_work *work) +bool is_smb2_neg_cmd(struct ksmbd_work *work) { struct smb2_hdr *hdr = work->request_buf; /* is it SMB2 header ? */ if (hdr->ProtocolId != SMB2_PROTO_NUMBER) - return 0; + return false; /* make sure it is request not response message */ if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) - return 0; + return false; if (hdr->Command != SMB2_NEGOTIATE) - return 0; + return false; - return 1; + return true; } /** * is_smb2_rsp() - is it smb2 response * @work: smb work containing smb response buffer * - * Return: 1 if smb2 response, otherwise 0 + * Return: true if smb2 response, otherwise false */ -int is_smb2_rsp(struct ksmbd_work *work) +bool is_smb2_rsp(struct ksmbd_work *work) { struct smb2_hdr *hdr = work->response_buf; /* is it SMB2 header ? */ if (hdr->ProtocolId != SMB2_PROTO_NUMBER) - return 0; + return false; /* make sure it is response not request message */ if (!(hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)) - return 0; + return false; - return 1; + return true; } /** @@ -8291,7 +8291,7 @@ int smb3_encrypt_resp(struct ksmbd_work *work) return rc; } -int smb3_is_transform_hdr(void *buf) +bool smb3_is_transform_hdr(void *buf) { struct smb2_transform_hdr *trhdr = buf; diff --git a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h index 89019f67234c..bcec845b03f3 100644 --- a/fs/ksmbd/smb2pdu.h +++ b/fs/ksmbd/smb2pdu.h @@ -1638,8 +1638,8 @@ void init_smb2_max_read_size(unsigned int sz); void init_smb2_max_write_size(unsigned int sz); void init_smb2_max_trans_size(unsigned int sz); -int is_smb2_neg_cmd(struct ksmbd_work *work); -int is_smb2_rsp(struct ksmbd_work *work); +bool is_smb2_neg_cmd(struct ksmbd_work *work); +bool is_smb2_rsp(struct ksmbd_work *work); u16 get_smb2_cmd_val(struct ksmbd_work *work); void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err); @@ -1664,7 +1664,7 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn); void smb3_preauth_hash_rsp(struct ksmbd_work *work); -int smb3_is_transform_hdr(void *buf); +bool smb3_is_transform_hdr(void *buf); int smb3_decrypt_req(struct ksmbd_work *work); int smb3_encrypt_resp(struct ksmbd_work *work); bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work); diff --git a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h index c4219c3432e2..eb667d85558e 100644 --- a/fs/ksmbd/smb_common.h +++ b/fs/ksmbd/smb_common.h @@ -473,7 +473,7 @@ struct smb_version_ops { void (*set_sign_rsp)(struct ksmbd_work *work); int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn); int (*generate_encryptionkey)(struct ksmbd_session *sess); - int (*is_transform_hdr)(void *buf); + bool (*is_transform_hdr)(void *buf); int (*decrypt_req)(struct ksmbd_work *work); int (*encrypt_resp)(struct ksmbd_work *work); }; From 5ec3df8e98f51e21fe1f46633b6085897f9b040e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 12 Aug 2021 10:17:39 +0900 Subject: [PATCH 219/417] ksmbd: update the comment for smb2_get_ksmbd_tcon() Update the comment for smb2_get_ksmbd_tcon(). Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 0de4163978ce..4ac4fe22edde 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -85,10 +85,11 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn } /** - * smb2_get_ksmbd_tcon() - get tree connection information for a tree id + * smb2_get_ksmbd_tcon() - get tree connection information using a tree id. * @work: smb work * - * Return: matching tree connection on success, otherwise error + * Return: 0 if there is a tree connection matched or these are + * skipable commands, otherwise error */ int smb2_get_ksmbd_tcon(struct ksmbd_work *work) { From c6ce2b5716b04cc6ec36fa7e3c3b851368e6ee7c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 12 Aug 2021 10:18:18 +0900 Subject: [PATCH 220/417] ksmbd: use proper errno instead of -1 in smb2_get_ksmbd_tcon() Use proper errno instead of -1 in smb2_get_ksmbd_tcon(). Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 4ac4fe22edde..8f6ffa427ebf 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -106,14 +106,14 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work) if (xa_empty(&work->sess->tree_conns)) { ksmbd_debug(SMB, "NO tree connected\n"); - return -1; + return -ENOENT; } tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId); work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); if (!work->tcon) { pr_err("Invalid tid %d\n", tree_id); - return -1; + return -EINVAL; } return 1; From 777cad1604d68ed4379ec899d1f7d2f6a29f01f0 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 13 Aug 2021 08:15:33 +0900 Subject: [PATCH 221/417] ksmbd: remove select FS_POSIX_ACL in Kconfig ksmbd is forcing to turn on FS_POSIX_ACL in Kconfig to use vfs acl functions(posix_acl_alloc, get_acl, set_posix_acl). OpenWRT and other platform doesn't use acl and this config is disable by default in kernel. This patch use IS_ENABLED() to know acl config is enable and use acl function if it is enable. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/Kconfig | 1 - fs/ksmbd/smb2pdu.c | 9 ++++-- fs/ksmbd/smbacl.c | 76 +++++++++++++++++++++++++++------------------- fs/ksmbd/vfs.c | 9 ++++++ 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/fs/ksmbd/Kconfig b/fs/ksmbd/Kconfig index e9a5ac01b6e0..b83cbd756ae5 100644 --- a/fs/ksmbd/Kconfig +++ b/fs/ksmbd/Kconfig @@ -19,7 +19,6 @@ config SMB_SERVER select CRYPTO_GCM select ASN1 select OID_REGISTRY - select FS_POSIX_ACL default n help Choose Y here if you want to allow SMB3 compliant clients diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 8f6ffa427ebf..0131997c2177 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2386,11 +2386,14 @@ static void ksmbd_acls_fattr(struct smb_fattr *fattr, struct inode *inode) fattr->cf_uid = inode->i_uid; fattr->cf_gid = inode->i_gid; fattr->cf_mode = inode->i_mode; + fattr->cf_acls = NULL; fattr->cf_dacls = NULL; - fattr->cf_acls = get_acl(inode, ACL_TYPE_ACCESS); - if (S_ISDIR(inode->i_mode)) - fattr->cf_dacls = get_acl(inode, ACL_TYPE_DEFAULT); + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_acls = get_acl(inode, ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + fattr->cf_dacls = get_acl(inode, ACL_TYPE_DEFAULT); + } } /** diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c index fa99d950a6f2..2ca3714c518e 100644 --- a/fs/ksmbd/smbacl.c +++ b/fs/ksmbd/smbacl.c @@ -533,22 +533,29 @@ static void parse_dacl(struct user_namespace *user_ns, if (acl_state.users->n || acl_state.groups->n) { acl_state.mask.allow = 0x07; - fattr->cf_acls = posix_acl_alloc(acl_state.users->n + - acl_state.groups->n + 4, GFP_KERNEL); - if (fattr->cf_acls) { - cf_pace = fattr->cf_acls->a_entries; - posix_state_to_acl(&acl_state, cf_pace); + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_acls = + posix_acl_alloc(acl_state.users->n + + acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_acls) { + cf_pace = fattr->cf_acls->a_entries; + posix_state_to_acl(&acl_state, cf_pace); + } } } if (default_acl_state.users->n || default_acl_state.groups->n) { default_acl_state.mask.allow = 0x07; - fattr->cf_dacls = - posix_acl_alloc(default_acl_state.users->n + - default_acl_state.groups->n + 4, GFP_KERNEL); - if (fattr->cf_dacls) { - cf_pdace = fattr->cf_dacls->a_entries; - posix_state_to_acl(&default_acl_state, cf_pdace); + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_dacls = + posix_acl_alloc(default_acl_state.users->n + + default_acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_dacls) { + cf_pdace = fattr->cf_dacls->a_entries; + posix_state_to_acl(&default_acl_state, cf_pdace); + } } } free_acl_state(&acl_state); @@ -1221,31 +1228,36 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, granted = GENERIC_ALL_FLAGS; } - posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); - if (posix_acls && !found) { - unsigned int id = -1; + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); + if (posix_acls && !found) { + unsigned int id = -1; - pa_entry = posix_acls->a_entries; - for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { - if (pa_entry->e_tag == ACL_USER) - id = from_kuid(user_ns, - pa_entry->e_uid); - else if (pa_entry->e_tag == ACL_GROUP) - id = from_kgid(user_ns, - pa_entry->e_gid); - else - continue; + pa_entry = posix_acls->a_entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { + if (pa_entry->e_tag == ACL_USER) + id = from_kuid(user_ns, + pa_entry->e_uid); + else if (pa_entry->e_tag == ACL_GROUP) + id = from_kgid(user_ns, + pa_entry->e_gid); + else + continue; - if (id == uid) { - mode_to_access_flags(pa_entry->e_perm, 0777, &access_bits); - if (!access_bits) - access_bits = SET_MINIMUM_RIGHTS; - goto check_access_bits; + if (id == uid) { + mode_to_access_flags(pa_entry->e_perm, + 0777, + &access_bits); + if (!access_bits) + access_bits = + SET_MINIMUM_RIGHTS; + goto check_access_bits; + } } } + if (posix_acls) + posix_acl_release(posix_acls); } - if (posix_acls) - posix_acl_release(posix_acls); if (!found) { if (others_ace) { @@ -1308,7 +1320,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); /* Update posix acls */ - if (fattr.cf_dacls) { + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) { rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, fattr.cf_acls); if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index 612c52d7a01b..aee28ee6b19c 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -1365,6 +1365,9 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespac struct xattr_acl_entry *xa_entry; int i; + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return NULL; + posix_acls = get_acl(inode, acl_type); if (!posix_acls) return NULL; @@ -1811,6 +1814,9 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, struct posix_acl *acls; int rc; + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return -EOPNOTSUPP; + ksmbd_debug(SMB, "Set posix acls\n"); rc = init_acl_state(&acl_state, 1); if (rc) @@ -1858,6 +1864,9 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, struct posix_acl_entry *pace; int rc, i; + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return -EOPNOTSUPP; + acls = get_acl(parent_inode, ACL_TYPE_DEFAULT); if (!acls) return -ENOENT; From 323b1ea10263e5f11c9fb12e25f6d8beb327228c Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 12 Aug 2021 10:23:08 +0900 Subject: [PATCH 222/417] ksmbd: smbd: fix kernel oops during server shutdown if server shutdown happens in the situation that there are connections, workqueue could be destroyed before queueing disconnect work. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/transport_rdma.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c index f2ae6bae83f1..58f530056ac0 100644 --- a/fs/ksmbd/transport_rdma.c +++ b/fs/ksmbd/transport_rdma.c @@ -329,7 +329,8 @@ static void smb_direct_disconnect_rdma_work(struct work_struct *work) static void smb_direct_disconnect_rdma_connection(struct smb_direct_transport *t) { - queue_work(smb_direct_wq, &t->disconnect_work); + if (t->status == SMB_DIRECT_CS_CONNECTED) + queue_work(smb_direct_wq, &t->disconnect_work); } static void smb_direct_send_immediate_work(struct work_struct *work) @@ -1415,7 +1416,7 @@ static void smb_direct_disconnect(struct ksmbd_transport *t) ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", st->cm_id); - smb_direct_disconnect_rdma_connection(st); + smb_direct_disconnect_rdma_work(&st->disconnect_work); wait_event_interruptible(st->wait_status, st->status == SMB_DIRECT_CS_DISCONNECTED); free_transport(st); From 668fff017233ed7d1bc684a23cdf2875be1b5aea Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 12 Aug 2021 11:32:28 +0900 Subject: [PATCH 223/417] ksmbd: update SMB3 multi-channel support in ksmbd.rst ksmbd start supporting SMB3 Multi-channel feature. Mark it as Partially supported till replay/retry mechanisms are implemented. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- Documentation/filesystems/cifs/ksmbd.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/filesystems/cifs/ksmbd.rst b/Documentation/filesystems/cifs/ksmbd.rst index 1e111efecd45..a1326157d53f 100644 --- a/Documentation/filesystems/cifs/ksmbd.rst +++ b/Documentation/filesystems/cifs/ksmbd.rst @@ -84,7 +84,8 @@ SMB3 encryption(CCM, GCM) Supported. (CCM and GCM128 supported, GCM256 in progress) SMB direct(RDMA) Partially Supported. SMB3 Multi-channel is required to connect to Windows client. -SMB3 Multi-channel In Progress. +SMB3 Multi-channel Partially Supported. Planned to implement + replay/retry mechanisms for future. SMB3.1.1 POSIX extension Supported. ACLs Partially Supported. only DACLs available, SACLs (auditing) is planned for the future. For From 29668d7e9d842be722a4c0416bb577828026ce4f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 17 Aug 2021 13:06:38 +0900 Subject: [PATCH 224/417] MAINTAINERS: add git adddress of ksmbd Add missing git address of ksmbd. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6691ac75fce5..85af3ce6a28e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9945,6 +9945,7 @@ M: Steve French M: Hyunchul Lee L: linux-cifs@vger.kernel.org S: Maintained +T: git git://git.samba.org/ksmbd.git F: fs/ksmbd/ KERNEL UNIT TESTING FRAMEWORK (KUnit) From c7e0b781b73c2e26e442ed71397cc2bc5945a732 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 28 Jun 2021 16:34:20 -0400 Subject: [PATCH 225/417] NFSD: Clean up splice actor A few useful observations: - The value in @size is never modified. - splice_desc.len is an unsigned int, and so is xdr_buf.page_len. An implicit cast to size_t is unnecessary. - The computation of .page_len is the same in all three arms of the "if" statement, so hoist it out to make it clear that the operation is an unconditional invariant. The resulting function is 18 bytes shorter on my system (-Os). Signed-off-by: Chuck Lever Reviewed-by: NeilBrown --- fs/nfsd/vfs.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index a224a5e23cc1..46a6d9fce3d2 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -847,26 +847,21 @@ nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, struct svc_rqst *rqstp = sd->u.data; struct page **pp = rqstp->rq_next_page; struct page *page = buf->page; - size_t size; - - size = sd->len; if (rqstp->rq_res.page_len == 0) { get_page(page); put_page(*rqstp->rq_next_page); *(rqstp->rq_next_page++) = page; rqstp->rq_res.page_base = buf->offset; - rqstp->rq_res.page_len = size; } else if (page != pp[-1]) { get_page(page); if (*rqstp->rq_next_page) put_page(*rqstp->rq_next_page); *(rqstp->rq_next_page++) = page; - rqstp->rq_res.page_len += size; - } else - rqstp->rq_res.page_len += size; + } + rqstp->rq_res.page_len += sd->len; - return size; + return sd->len; } static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe, From 2f0f88f42f2eab0421ed37d7494de9124fdf0d34 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 1 Jul 2021 10:03:10 -0400 Subject: [PATCH 226/417] SUNRPC: Add svc_rqst_replace_page() API Replacing a page in rq_pages[] requires a get_page(), which is a bus-locked operation, and a put_page(), which can be even more costly. To reduce the cost of replacing a page in rq_pages[], batch the put_page() operations by collecting "freed" pages in a pagevec, and then release those pages when the pagevec is full. This pagevec is also emptied when each RPC completes. Signed-off-by: Chuck Lever --- include/linux/sunrpc/svc.h | 4 ++++ net/sunrpc/svc.c | 21 +++++++++++++++++++++ net/sunrpc/svc_xprt.c | 3 +++ 3 files changed, 28 insertions(+) diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index e91d51ea028b..ab9afbf0a0d8 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -19,6 +19,7 @@ #include #include #include +#include /* statistics for svc_pool structures */ struct svc_pool_stats { @@ -256,6 +257,7 @@ struct svc_rqst { struct page * *rq_next_page; /* next reply page to use */ struct page * *rq_page_end; /* one past the last page */ + struct pagevec rq_pvec; struct kvec rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */ struct bio_vec rq_bvec[RPCSVC_MAXPAGES]; @@ -502,6 +504,8 @@ struct svc_rqst *svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node); struct svc_rqst *svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node); +void svc_rqst_replace_page(struct svc_rqst *rqstp, + struct page *page); void svc_rqst_free(struct svc_rqst *); void svc_exit_thread(struct svc_rqst *); unsigned int svc_pool_map_get(void); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 0de918cb3d90..d2d412d43827 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -838,6 +838,27 @@ svc_set_num_threads_sync(struct svc_serv *serv, struct svc_pool *pool, int nrser } EXPORT_SYMBOL_GPL(svc_set_num_threads_sync); +/** + * svc_rqst_replace_page - Replace one page in rq_pages[] + * @rqstp: svc_rqst with pages to replace + * @page: replacement page + * + * When replacing a page in rq_pages, batch the release of the + * replaced pages to avoid hammering the page allocator. + */ +void svc_rqst_replace_page(struct svc_rqst *rqstp, struct page *page) +{ + if (*rqstp->rq_next_page) { + if (!pagevec_space(&rqstp->rq_pvec)) + __pagevec_release(&rqstp->rq_pvec); + pagevec_add(&rqstp->rq_pvec, *rqstp->rq_next_page); + } + + get_page(page); + *(rqstp->rq_next_page++) = page; +} +EXPORT_SYMBOL_GPL(svc_rqst_replace_page); + /* * Called from a server thread as it's exiting. Caller must hold the "service * mutex" for the service. diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index d66a8e44a1ae..682058a5ec13 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -539,6 +539,7 @@ static void svc_xprt_release(struct svc_rqst *rqstp) kfree(rqstp->rq_deferred); rqstp->rq_deferred = NULL; + pagevec_release(&rqstp->rq_pvec); svc_free_res_pages(rqstp); rqstp->rq_res.page_len = 0; rqstp->rq_res.page_base = 0; @@ -664,6 +665,8 @@ static int svc_alloc_arg(struct svc_rqst *rqstp) struct xdr_buf *arg = &rqstp->rq_arg; unsigned long pages, filled; + pagevec_init(&rqstp->rq_pvec); + pages = (serv->sv_max_mesg + 2 * PAGE_SIZE) >> PAGE_SHIFT; if (pages > RPCSVC_MAXPAGES) { pr_warn_once("svc: warning: pages=%lu > RPCSVC_MAXPAGES=%lu\n", From 496d83cf0f2fa70cfe256c2499e2d3523d3868f3 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 28 Jun 2021 17:24:27 -0400 Subject: [PATCH 227/417] NFSD: Batch release pages during splice read Large splice reads call put_page() repeatedly. put_page() is relatively expensive to call, so replace it with the new svc_rqst_replace_page() helper to help amortize that cost. Signed-off-by: Chuck Lever Reviewed-by: NeilBrown --- fs/nfsd/vfs.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 46a6d9fce3d2..7732a384f949 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -849,15 +849,10 @@ nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, struct page *page = buf->page; if (rqstp->rq_res.page_len == 0) { - get_page(page); - put_page(*rqstp->rq_next_page); - *(rqstp->rq_next_page++) = page; + svc_rqst_replace_page(rqstp, page); rqstp->rq_res.page_base = buf->offset; } else if (page != pp[-1]) { - get_page(page); - if (*rqstp->rq_next_page) - put_page(*rqstp->rq_next_page); - *(rqstp->rq_next_page++) = page; + svc_rqst_replace_page(rqstp, page); } rqstp->rq_res.page_len += sd->len; From 883b4aee4dec64bc807a7dda4651c6a5efe9a74d Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Fri, 16 Jul 2021 20:55:10 -0400 Subject: [PATCH 228/417] tracing: Add trace_event helper macros __string_len() and __assign_str_len() There's a few cases that a string that is to be recorded in a trace event, does not have a terminating 'nul' character, and instead, the tracepoint passes in the length of the string to record. Add two helper macros to the trace event code that lets this work easier, than tricks with "%.*s" logic. __string_len() which is similar to __string() for declaration, but takes a length argument. __assign_str_len() which is similar to __assign_str() for assiging the string, but it too takes a length argument. Note, the TRACE_EVENT() macro will allocate the location on the ring buffer to 'len + 1', that will be used to store the string into. It is a requirement that the 'len' used for this is a most the length of the string being recorded. This string can still use __get_str() just like strings created with __string() can use to retrieve the string. Link: https://lore.kernel.org/linux-nfs/20210513105018.7539996a@gandalf.local.home/ Tested-by: Chuck Lever Signed-off-by: Steven Rostedt (VMware) Signed-off-by: Chuck Lever --- include/trace/trace_events.h | 22 ++++++++++++++++++ samples/trace_events/trace-events-sample.h | 27 ++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/include/trace/trace_events.h b/include/trace/trace_events.h index acc17194c160..08810a463880 100644 --- a/include/trace/trace_events.h +++ b/include/trace/trace_events.h @@ -102,6 +102,9 @@ TRACE_MAKE_SYSTEM_STR(); #undef __string #define __string(item, src) __dynamic_array(char, item, -1) +#undef __string_len +#define __string_len(item, src, len) __dynamic_array(char, item, -1) + #undef __bitmask #define __bitmask(item, nr_bits) __dynamic_array(char, item, -1) @@ -197,6 +200,9 @@ TRACE_MAKE_SYSTEM_STR(); #undef __string #define __string(item, src) __dynamic_array(char, item, -1) +#undef __string_len +#define __string_len(item, src, len) __dynamic_array(char, item, -1) + #undef __bitmask #define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1) @@ -459,6 +465,9 @@ static struct trace_event_functions trace_event_type_funcs_##call = { \ #undef __string #define __string(item, src) __dynamic_array(char, item, -1) +#undef __string_len +#define __string_len(item, src, len) __dynamic_array(char, item, -1) + #undef __bitmask #define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1) @@ -507,6 +516,9 @@ static struct trace_event_fields trace_event_fields_##call[] = { \ #define __string(item, src) __dynamic_array(char, item, \ strlen((src) ? (const char *)(src) : "(null)") + 1) +#undef __string_len +#define __string_len(item, src, len) __dynamic_array(char, item, (len) + 1) + /* * __bitmask_size_in_bytes_raw is the number of bytes needed to hold * num_possible_cpus(). @@ -670,10 +682,20 @@ static inline notrace int trace_event_get_offsets_##call( \ #undef __string #define __string(item, src) __dynamic_array(char, item, -1) +#undef __string_len +#define __string_len(item, src, len) __dynamic_array(char, item, -1) + #undef __assign_str #define __assign_str(dst, src) \ strcpy(__get_str(dst), (src) ? (const char *)(src) : "(null)"); +#undef __assign_str_len +#define __assign_str_len(dst, src, len) \ + do { \ + memcpy(__get_str(dst), (src), (len)); \ + __get_str(dst)[len] = '\0'; \ + } while(0) + #undef __bitmask #define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1) diff --git a/samples/trace_events/trace-events-sample.h b/samples/trace_events/trace-events-sample.h index 13a35f7cbe66..e61471ab7d14 100644 --- a/samples/trace_events/trace-events-sample.h +++ b/samples/trace_events/trace-events-sample.h @@ -141,6 +141,33 @@ * In most cases, the __assign_str() macro will take the same * parameters as the __string() macro had to declare the string. * + * __string_len: This is a helper to a __dynamic_array, but it understands + * that the array has characters in it, and with the combined + * use of __assign_str_len(), it will allocate 'len' + 1 bytes + * in the ring buffer and add a '\0' to the string. This is + * useful if the string being saved has no terminating '\0' byte. + * It requires that the length of the string is known as it acts + * like a memcpy(). + * + * Declared with: + * + * __string_len(foo, bar, len) + * + * To assign this string, use the helper macro __assign_str_len(). + * + * __assign_str(foo, bar, len); + * + * Then len + 1 is allocated to the ring buffer, and a nul terminating + * byte is added. This is similar to: + * + * memcpy(__get_str(foo), bar, len); + * __get_str(foo)[len] = 0; + * + * The advantage of using this over __dynamic_array, is that it + * takes care of allocating the extra byte on the ring buffer + * for the '\0' terminating byte, and __get_str(foo) can be used + * in the TP_printk(). + * * __bitmask: This is another kind of __dynamic_array, but it expects * an array of longs, and the number of bits to parse. It takes * two parameters (name, nr_bits), where name is the name of the From 408c0de706186bb11aaed87cf86d96d7776d3b6f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 12 May 2021 09:39:06 -0400 Subject: [PATCH 229/417] NFSD: Use new __string_len C macros for the nfs_dirent tracepoint Clean up. Signed-off-by: Chuck Lever --- fs/nfsd/trace.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index adaec43548d1..52a43acd546c 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -400,18 +400,16 @@ TRACE_EVENT(nfsd_dirent, TP_STRUCT__entry( __field(u32, fh_hash) __field(u64, ino) - __field(int, len) - __dynamic_array(unsigned char, name, namlen) + __string_len(name, name, namlen) ), TP_fast_assign( __entry->fh_hash = fhp ? knfsd_fh_hash(&fhp->fh_handle) : 0; __entry->ino = ino; - __entry->len = namlen; - memcpy(__get_str(name), name, namlen); + __assign_str_len(name, name, namlen) ), - TP_printk("fh_hash=0x%08x ino=%llu name=%.*s", - __entry->fh_hash, __entry->ino, - __entry->len, __get_str(name)) + TP_printk("fh_hash=0x%08x ino=%llu name=%s", + __entry->fh_hash, __entry->ino, __get_str(name) + ) ) #include "state.h" From d27b74a8675ca34dfd54c4bc4b3a11b7aa87e1a3 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 14 May 2021 15:34:57 -0400 Subject: [PATCH 230/417] NFSD: Use new __string_len C macros for nfsd_clid_class Clean up. Signed-off-by: Chuck Lever --- fs/nfsd/trace.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index 52a43acd546c..538520957a81 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -606,7 +606,7 @@ DECLARE_EVENT_CLASS(nfsd_clid_class, __array(unsigned char, addr, sizeof(struct sockaddr_in6)) __field(unsigned long, flavor) __array(unsigned char, verifier, NFS4_VERIFIER_SIZE) - __dynamic_array(char, name, clp->cl_name.len + 1) + __string_len(name, name, clp->cl_name.len) ), TP_fast_assign( __entry->cl_boot = clp->cl_clientid.cl_boot; @@ -616,8 +616,7 @@ DECLARE_EVENT_CLASS(nfsd_clid_class, __entry->flavor = clp->cl_cred.cr_flavor; memcpy(__entry->verifier, (void *)&clp->cl_verifier, NFS4_VERIFIER_SIZE); - memcpy(__get_str(name), clp->cl_name.data, clp->cl_name.len); - __get_str(name)[clp->cl_name.len] = '\0'; + __assign_str_len(name, clp->cl_name.data, clp->cl_name.len); ), TP_printk("addr=%pISpc name='%s' verifier=0x%s flavor=%s client=%08x:%08x", __entry->addr, __get_str(name), From cd2d644ddba183ec7b451b7c20d5c7cc06fcf0d7 Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Mon, 26 Jul 2021 09:33:28 -0400 Subject: [PATCH 231/417] lockd: Fix invalid lockowner cast after vfs_test_lock After calling vfs_test_lock() the pointer to a conflicting lock can be returned, and that lock is not guarunteed to be owned by nlm. In that case, we cannot cast it to struct nlm_lockowner. Instead return the pid of that conflicting lock. Fixes: 646d73e91b42 ("lockd: Show pid of lockd for remote locks") Signed-off-by: Benjamin Coddington Signed-off-by: Chuck Lever --- fs/lockd/svclock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 61d3cc2283dc..498cb70c2c0d 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -634,7 +634,7 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, conflock->caller = "somehost"; /* FIXME */ conflock->len = strlen(conflock->caller); conflock->oh.len = 0; /* don't return OH info */ - conflock->svid = ((struct nlm_lockowner *)lock->fl.fl_owner)->pid; + conflock->svid = lock->fl.fl_pid; conflock->fl.fl_type = lock->fl.fl_type; conflock->fl.fl_start = lock->fl.fl_start; conflock->fl.fl_end = lock->fl.fl_end; From 6c8c84f525100a1cade5698320b4abe43062e159 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 7 Jul 2021 14:57:28 -0400 Subject: [PATCH 232/417] svcrdma: Fewer calls to wake_up() in Send completion handler Because wake_up() takes an IRQ-safe lock, it can be expensive, especially to call inside of a single-threaded completion handler. What's more, the Send wait queue almost never has waiters, so most of the time, this is an expensive no-op. As always, the goal is to reduce the average overhead of each completion, because a transport's completion handlers are single- threaded on one CPU core. This change reduces CPU utilization of the Send completion thread by 2-3% on my server. Signed-off-by: Chuck Lever Reviewed-By: Tom Talpey --- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma_rw.c | 7 ++----- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 18 +++++++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 3184465de3a0..57c60ffe76dd 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -207,6 +207,7 @@ extern void svc_rdma_send_error_msg(struct svcxprt_rdma *rdma, struct svc_rdma_send_ctxt *sctxt, struct svc_rdma_recv_ctxt *rctxt, int status); +extern void svc_rdma_wake_send_waiters(struct svcxprt_rdma *rdma, int avail); extern int svc_rdma_sendto(struct svc_rqst *); extern int svc_rdma_result_payload(struct svc_rqst *rqstp, unsigned int offset, unsigned int length); diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index 1e651447dc4e..3d1b119f6e3e 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -248,8 +248,7 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc) trace_svcrdma_wc_write(wc, &cc->cc_cid); - atomic_add(cc->cc_sqecount, &rdma->sc_sq_avail); - wake_up(&rdma->sc_send_wait); + svc_rdma_wake_send_waiters(rdma, cc->cc_sqecount); if (unlikely(wc->status != IB_WC_SUCCESS)) svc_xprt_deferred_close(&rdma->sc_xprt); @@ -304,9 +303,7 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc) trace_svcrdma_wc_read(wc, &cc->cc_cid); - atomic_add(cc->cc_sqecount, &rdma->sc_sq_avail); - wake_up(&rdma->sc_send_wait); - + svc_rdma_wake_send_waiters(rdma, cc->cc_sqecount); cc->cc_status = wc->status; complete(&cc->cc_done); return; diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index d6bbafb773e1..fba2ee4eb607 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -258,6 +258,20 @@ void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma, spin_unlock(&rdma->sc_send_lock); } +/** + * svc_rdma_wake_send_waiters - manage Send Queue accounting + * @rdma: controlling transport + * @avail: Number of additional SQEs that are now available + * + */ +void svc_rdma_wake_send_waiters(struct svcxprt_rdma *rdma, int avail) +{ + atomic_add(avail, &rdma->sc_sq_avail); + smp_mb__after_atomic(); + if (unlikely(waitqueue_active(&rdma->sc_send_wait))) + wake_up(&rdma->sc_send_wait); +} + /** * svc_rdma_wc_send - Invoked by RDMA provider for each polled Send WC * @cq: Completion Queue context @@ -275,11 +289,9 @@ static void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc) trace_svcrdma_wc_send(wc, &ctxt->sc_cid); + svc_rdma_wake_send_waiters(rdma, 1); complete(&ctxt->sc_done); - atomic_inc(&rdma->sc_sq_avail); - wake_up(&rdma->sc_send_wait); - if (unlikely(wc->status != IB_WC_SUCCESS)) svc_xprt_deferred_close(&rdma->sc_xprt); } From b6c2bfea096ba22583f1071c10ce0745804b9b95 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 9 Feb 2021 10:32:20 -0500 Subject: [PATCH 233/417] svcrdma: Relieve contention on sc_send_lock. /proc/lock_stat indicates the the sc_send_lock is heavily contended when the server is under load from a single client. To address this, convert the send_ctxt free list to an llist. Returning an item to the send_ctxt cache is now waitless, which reduces the instruction path length in the single-threaded Send handler (svc_rdma_wc_send). The goal is to enable the ib_comp_wq worker to handle a higher RPC/RDMA Send completion rate given the same CPU resources. This change reduces CPU utilization of Send completion by 2-3% on my server. Signed-off-by: Chuck Lever Reviewed-By: Tom Talpey --- include/linux/sunrpc/svc_rdma.h | 4 ++-- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 23 ++++++++--------------- net/sunrpc/xprtrdma/svc_rdma_transport.c | 2 +- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 57c60ffe76dd..5f8d5af6556c 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -90,7 +90,7 @@ struct svcxprt_rdma { struct ib_pd *sc_pd; spinlock_t sc_send_lock; - struct list_head sc_send_ctxts; + struct llist_head sc_send_ctxts; spinlock_t sc_rw_ctxt_lock; struct list_head sc_rw_ctxts; @@ -150,7 +150,7 @@ struct svc_rdma_recv_ctxt { }; struct svc_rdma_send_ctxt { - struct list_head sc_list; + struct llist_node sc_node; struct rpc_rdma_cid sc_cid; struct ib_send_wr sc_send_wr; diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index fba2ee4eb607..599021b2391d 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -113,13 +113,6 @@ static void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc); -static inline struct svc_rdma_send_ctxt * -svc_rdma_next_send_ctxt(struct list_head *list) -{ - return list_first_entry_or_null(list, struct svc_rdma_send_ctxt, - sc_list); -} - static void svc_rdma_send_cid_init(struct svcxprt_rdma *rdma, struct rpc_rdma_cid *cid) { @@ -182,9 +175,10 @@ fail0: void svc_rdma_send_ctxts_destroy(struct svcxprt_rdma *rdma) { struct svc_rdma_send_ctxt *ctxt; + struct llist_node *node; - while ((ctxt = svc_rdma_next_send_ctxt(&rdma->sc_send_ctxts))) { - list_del(&ctxt->sc_list); + while ((node = llist_del_first(&rdma->sc_send_ctxts)) != NULL) { + ctxt = llist_entry(node, struct svc_rdma_send_ctxt, sc_node); ib_dma_unmap_single(rdma->sc_pd->device, ctxt->sc_sges[0].addr, rdma->sc_max_req_size, @@ -204,12 +198,13 @@ void svc_rdma_send_ctxts_destroy(struct svcxprt_rdma *rdma) struct svc_rdma_send_ctxt *svc_rdma_send_ctxt_get(struct svcxprt_rdma *rdma) { struct svc_rdma_send_ctxt *ctxt; + struct llist_node *node; spin_lock(&rdma->sc_send_lock); - ctxt = svc_rdma_next_send_ctxt(&rdma->sc_send_ctxts); - if (!ctxt) + node = llist_del_first(&rdma->sc_send_ctxts); + if (!node) goto out_empty; - list_del(&ctxt->sc_list); + ctxt = llist_entry(node, struct svc_rdma_send_ctxt, sc_node); spin_unlock(&rdma->sc_send_lock); out: @@ -253,9 +248,7 @@ void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma, ctxt->sc_sges[i].length); } - spin_lock(&rdma->sc_send_lock); - list_add(&ctxt->sc_list, &rdma->sc_send_ctxts); - spin_unlock(&rdma->sc_send_lock); + llist_add(&ctxt->sc_node, &rdma->sc_send_ctxts); } /** diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index d94b7759ada1..99474078c304 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -136,7 +136,7 @@ static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv, svc_xprt_init(net, &svc_rdma_class, &cma_xprt->sc_xprt, serv); INIT_LIST_HEAD(&cma_xprt->sc_accept_q); INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q); - INIT_LIST_HEAD(&cma_xprt->sc_send_ctxts); + init_llist_head(&cma_xprt->sc_send_ctxts); init_llist_head(&cma_xprt->sc_recv_ctxts); INIT_LIST_HEAD(&cma_xprt->sc_rw_ctxts); init_waitqueue_head(&cma_xprt->sc_send_wait); From 07a92d009f0b1557d3d58905ce18821a483be2e1 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 8 Feb 2021 15:33:16 -0500 Subject: [PATCH 234/417] svcrdma: Convert rdma->sc_rw_ctxts to llist Relieve contention on sc_rw_ctxt_lock by converting rdma->sc_rw_ctxts to an llist. The goal is to reduce the average overhead of Send completions, because a transport's completion handlers are single-threaded on one CPU core. This change reduces CPU utilization of each Send completion by 2-3% on my server. Signed-off-by: Chuck Lever Reviewed-By: Tom Talpey --- include/linux/sunrpc/svc_rdma.h | 2 +- net/sunrpc/xprtrdma/svc_rdma_rw.c | 49 +++++++++++++++++------- net/sunrpc/xprtrdma/svc_rdma_transport.c | 2 +- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 5f8d5af6556c..24aa159d29a7 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -92,7 +92,7 @@ struct svcxprt_rdma { spinlock_t sc_send_lock; struct llist_head sc_send_ctxts; spinlock_t sc_rw_ctxt_lock; - struct list_head sc_rw_ctxts; + struct llist_head sc_rw_ctxts; u32 sc_pending_recvs; u32 sc_recv_batch; diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index 3d1b119f6e3e..e27433f08ca7 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -35,6 +35,7 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc); * controlling svcxprt_rdma is destroyed. */ struct svc_rdma_rw_ctxt { + struct llist_node rw_node; struct list_head rw_list; struct rdma_rw_ctx rw_ctx; unsigned int rw_nents; @@ -53,19 +54,19 @@ static struct svc_rdma_rw_ctxt * svc_rdma_get_rw_ctxt(struct svcxprt_rdma *rdma, unsigned int sges) { struct svc_rdma_rw_ctxt *ctxt; + struct llist_node *node; spin_lock(&rdma->sc_rw_ctxt_lock); - - ctxt = svc_rdma_next_ctxt(&rdma->sc_rw_ctxts); - if (ctxt) { - list_del(&ctxt->rw_list); - spin_unlock(&rdma->sc_rw_ctxt_lock); + node = llist_del_first(&rdma->sc_rw_ctxts); + spin_unlock(&rdma->sc_rw_ctxt_lock); + if (node) { + ctxt = llist_entry(node, struct svc_rdma_rw_ctxt, rw_node); } else { - spin_unlock(&rdma->sc_rw_ctxt_lock); ctxt = kmalloc(struct_size(ctxt, rw_first_sgl, SG_CHUNK_SIZE), GFP_KERNEL); if (!ctxt) goto out_noctx; + INIT_LIST_HEAD(&ctxt->rw_list); } @@ -83,14 +84,18 @@ out_noctx: return NULL; } +static void __svc_rdma_put_rw_ctxt(struct svcxprt_rdma *rdma, + struct svc_rdma_rw_ctxt *ctxt, + struct llist_head *list) +{ + sg_free_table_chained(&ctxt->rw_sg_table, SG_CHUNK_SIZE); + llist_add(&ctxt->rw_node, list); +} + static void svc_rdma_put_rw_ctxt(struct svcxprt_rdma *rdma, struct svc_rdma_rw_ctxt *ctxt) { - sg_free_table_chained(&ctxt->rw_sg_table, SG_CHUNK_SIZE); - - spin_lock(&rdma->sc_rw_ctxt_lock); - list_add(&ctxt->rw_list, &rdma->sc_rw_ctxts); - spin_unlock(&rdma->sc_rw_ctxt_lock); + __svc_rdma_put_rw_ctxt(rdma, ctxt, &rdma->sc_rw_ctxts); } /** @@ -101,9 +106,10 @@ static void svc_rdma_put_rw_ctxt(struct svcxprt_rdma *rdma, void svc_rdma_destroy_rw_ctxts(struct svcxprt_rdma *rdma) { struct svc_rdma_rw_ctxt *ctxt; + struct llist_node *node; - while ((ctxt = svc_rdma_next_ctxt(&rdma->sc_rw_ctxts)) != NULL) { - list_del(&ctxt->rw_list); + while ((node = llist_del_first(&rdma->sc_rw_ctxts)) != NULL) { + ctxt = llist_entry(node, struct svc_rdma_rw_ctxt, rw_node); kfree(ctxt); } } @@ -171,20 +177,35 @@ static void svc_rdma_cc_init(struct svcxprt_rdma *rdma, cc->cc_sqecount = 0; } +/* + * The consumed rw_ctx's are cleaned and placed on a local llist so + * that only one atomic llist operation is needed to put them all + * back on the free list. + */ static void svc_rdma_cc_release(struct svc_rdma_chunk_ctxt *cc, enum dma_data_direction dir) { struct svcxprt_rdma *rdma = cc->cc_rdma; + struct llist_node *first, *last; struct svc_rdma_rw_ctxt *ctxt; + LLIST_HEAD(free); + first = last = NULL; while ((ctxt = svc_rdma_next_ctxt(&cc->cc_rwctxts)) != NULL) { list_del(&ctxt->rw_list); rdma_rw_ctx_destroy(&ctxt->rw_ctx, rdma->sc_qp, rdma->sc_port_num, ctxt->rw_sg_table.sgl, ctxt->rw_nents, dir); - svc_rdma_put_rw_ctxt(rdma, ctxt); + __svc_rdma_put_rw_ctxt(rdma, ctxt, &free); + + ctxt->rw_node.next = first; + first = &ctxt->rw_node; + if (!last) + last = first; } + if (first) + llist_add_batch(first, last, &rdma->sc_rw_ctxts); } /* State for sending a Write or Reply chunk. diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 99474078c304..d1faa522c3dd 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -138,7 +138,7 @@ static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv, INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q); init_llist_head(&cma_xprt->sc_send_ctxts); init_llist_head(&cma_xprt->sc_recv_ctxts); - INIT_LIST_HEAD(&cma_xprt->sc_rw_ctxts); + init_llist_head(&cma_xprt->sc_rw_ctxts); init_waitqueue_head(&cma_xprt->sc_send_wait); spin_lock_init(&cma_xprt->sc_lock); From ea49dc79002c416a9003f3204bc14f846a0dbcae Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 28 Jul 2021 08:56:09 +1000 Subject: [PATCH 235/417] NFSD: remove vanity comments Including one's name in copyright claims is appropriate. Including it in random comments is just vanity. After 2 decades, it is time for these to be gone. Signed-off-by: NeilBrown Signed-off-by: Chuck Lever --- fs/nfsd/vfs.c | 1 - include/uapi/linux/nfsd/nfsfh.h | 1 - 2 files changed, 2 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 7732a384f949..7851cf30a75d 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -244,7 +244,6 @@ out_nfserr: * returned. Otherwise the covered directory is returned. * NOTE: this mountpoint crossing is not supported properly by all * clients and is explicitly disallowed for NFSv3 - * NeilBrown */ __be32 nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, diff --git a/include/uapi/linux/nfsd/nfsfh.h b/include/uapi/linux/nfsd/nfsfh.h index 427294dd56a1..e29e8accc4f4 100644 --- a/include/uapi/linux/nfsd/nfsfh.h +++ b/include/uapi/linux/nfsd/nfsfh.h @@ -33,7 +33,6 @@ struct nfs_fhbase_old { /* * This is the new flexible, extensible style NFSv2/v3/v4 file handle. - * by Neil Brown - March 2000 * * The file handle starts with a sequence of four-byte words. * The first word contains a version number (1) and three descriptor bytes From 5c11720767f70d34357d00a15ba5a0ad052c40fe Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 5 Aug 2021 15:11:24 -0400 Subject: [PATCH 236/417] SUNRPC: Fix a NULL pointer deref in trace_svc_stats_latency() Some paths through svc_process() leave rqst->rq_procinfo set to NULL, which triggers a crash if tracing happens to be enabled. Fixes: 89ff87494c6e ("SUNRPC: Display RPC procedure names instead of proc numbers") Signed-off-by: Chuck Lever --- include/linux/sunrpc/svc.h | 1 + include/trace/events/sunrpc.h | 8 ++++---- net/sunrpc/svc.c | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index ab9afbf0a0d8..f0f846fa396e 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -527,6 +527,7 @@ void svc_wake_up(struct svc_serv *); void svc_reserve(struct svc_rqst *rqstp, int space); struct svc_pool * svc_pool_for_cpu(struct svc_serv *serv, int cpu); char * svc_print_addr(struct svc_rqst *, char *, size_t); +const char * svc_proc_name(const struct svc_rqst *rqstp); int svc_encode_result_payload(struct svc_rqst *rqstp, unsigned int offset, unsigned int length); diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index 861f199896c6..d323f5a049c8 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -1642,7 +1642,7 @@ TRACE_EVENT(svc_process, __field(u32, vers) __field(u32, proc) __string(service, name) - __string(procedure, rqst->rq_procinfo->pc_name) + __string(procedure, svc_proc_name(rqst)) __string(addr, rqst->rq_xprt ? rqst->rq_xprt->xpt_remotebuf : "(null)") ), @@ -1652,7 +1652,7 @@ TRACE_EVENT(svc_process, __entry->vers = rqst->rq_vers; __entry->proc = rqst->rq_proc; __assign_str(service, name); - __assign_str(procedure, rqst->rq_procinfo->pc_name); + __assign_str(procedure, svc_proc_name(rqst)); __assign_str(addr, rqst->rq_xprt ? rqst->rq_xprt->xpt_remotebuf : "(null)"); ), @@ -1918,7 +1918,7 @@ TRACE_EVENT(svc_stats_latency, TP_STRUCT__entry( __field(u32, xid) __field(unsigned long, execute) - __string(procedure, rqst->rq_procinfo->pc_name) + __string(procedure, svc_proc_name(rqst)) __string(addr, rqst->rq_xprt->xpt_remotebuf) ), @@ -1926,7 +1926,7 @@ TRACE_EVENT(svc_stats_latency, __entry->xid = be32_to_cpu(rqst->rq_xid); __entry->execute = ktime_to_us(ktime_sub(ktime_get(), rqst->rq_stime)); - __assign_str(procedure, rqst->rq_procinfo->pc_name); + __assign_str(procedure, svc_proc_name(rqst)); __assign_str(addr, rqst->rq_xprt->xpt_remotebuf); ), diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index d2d412d43827..5aa263326b6a 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1650,6 +1650,21 @@ u32 svc_max_payload(const struct svc_rqst *rqstp) } EXPORT_SYMBOL_GPL(svc_max_payload); +/** + * svc_proc_name - Return RPC procedure name in string form + * @rqstp: svc_rqst to operate on + * + * Return value: + * Pointer to a NUL-terminated string + */ +const char *svc_proc_name(const struct svc_rqst *rqstp) +{ + if (rqstp && rqstp->rq_procinfo) + return rqstp->rq_procinfo->pc_name; + return "unknown"; +} + + /** * svc_encode_result_payload - mark a range of bytes as a result payload * @rqstp: svc_rqst to operate on From a2071573d6346819cc4e5787b4206f2184985160 Mon Sep 17 00:00:00 2001 From: Jia He Date: Tue, 3 Aug 2021 12:59:36 +0200 Subject: [PATCH 237/417] sysctl: introduce new proc handler proc_dobool This is to let bool variable could be correctly displayed in big/little endian sysctl procfs. sizeof(bool) is arch dependent, proc_dobool should work in all arches. Suggested-by: Pan Xinhui Signed-off-by: Jia He [thuth: rebased the patch to the current kernel version] Signed-off-by: Thomas Huth Signed-off-by: Chuck Lever --- include/linux/sysctl.h | 2 ++ kernel/sysctl.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index d99ca99837de..1fa2b69c6fc3 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -48,6 +48,8 @@ typedef int proc_handler(struct ctl_table *ctl, int write, void *buffer, size_t *lenp, loff_t *ppos); int proc_dostring(struct ctl_table *, int, void *, size_t *, loff_t *); +int proc_dobool(struct ctl_table *table, int write, void *buffer, + size_t *lenp, loff_t *ppos); int proc_dointvec(struct ctl_table *, int, void *, size_t *, loff_t *); int proc_douintvec(struct ctl_table *, int, void *, size_t *, loff_t *); int proc_dointvec_minmax(struct ctl_table *, int, void *, size_t *, loff_t *); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 272f4a272f8c..25e49b4d8049 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -536,6 +536,21 @@ static void proc_put_char(void **buf, size_t *size, char c) } } +static int do_proc_dobool_conv(bool *negp, unsigned long *lvalp, + int *valp, + int write, void *data) +{ + if (write) { + *(bool *)valp = *lvalp; + } else { + int val = *(bool *)valp; + + *lvalp = (unsigned long)val; + *negp = false; + } + return 0; +} + static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp, int *valp, int write, void *data) @@ -798,6 +813,26 @@ static int do_proc_douintvec(struct ctl_table *table, int write, buffer, lenp, ppos, conv, data); } +/** + * proc_dobool - read/write a bool + * @table: the sysctl table + * @write: %TRUE if this is a write to the sysctl file + * @buffer: the user buffer + * @lenp: the size of the user buffer + * @ppos: file position + * + * Reads/writes up to table->maxlen/sizeof(unsigned int) integer + * values from/to the user buffer, treated as an ASCII string. + * + * Returns 0 on success. + */ +int proc_dobool(struct ctl_table *table, int write, void *buffer, + size_t *lenp, loff_t *ppos) +{ + return do_proc_dointvec(table, write, buffer, lenp, ppos, + do_proc_dobool_conv, NULL); +} + /** * proc_dointvec - read a vector of integers * @table: the sysctl table @@ -1630,6 +1665,12 @@ int proc_dostring(struct ctl_table *table, int write, return -ENOSYS; } +int proc_dobool(struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + return -ENOSYS; +} + int proc_dointvec(struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { @@ -3425,6 +3466,7 @@ int __init sysctl_init(void) * No sense putting this after each symbol definition, twice, * exception granted :-) */ +EXPORT_SYMBOL(proc_dobool); EXPORT_SYMBOL(proc_dointvec); EXPORT_SYMBOL(proc_douintvec); EXPORT_SYMBOL(proc_dointvec_jiffies); From d02a3a2cb25d384005a6e3446a445013342024b7 Mon Sep 17 00:00:00 2001 From: Jia He Date: Tue, 3 Aug 2021 12:59:37 +0200 Subject: [PATCH 238/417] lockd: change the proc_handler for nsm_use_hostnames nsm_use_hostnames is a module parameter and it will be exported to sysctl procfs. This is to let user sometimes change it from userspace. But the minimal unit for sysctl procfs read/write it sizeof(int). In big endian system, the converting from/to bool to/from int will cause error for proc items. This patch use a new proc_handler proc_dobool to fix it. Signed-off-by: Jia He Reviewed-by: Pan Xinhui [thuth: Fix typo in commit message] Signed-off-by: Thomas Huth Signed-off-by: Chuck Lever --- fs/lockd/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 2de048f80eb8..0ab9756ed235 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -584,7 +584,7 @@ static struct ctl_table nlm_sysctls[] = { .data = &nsm_use_hostnames, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dobool, }, { .procname = "nsm_local_state", From b4ab2fea7c797b0b8b92332c7e315703c12d37d8 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 30 Jul 2021 16:07:36 -0400 Subject: [PATCH 239/417] SUNRPC: Add RPC_AUTH_TLS protocol numbers Shared by client and server. See: https://www.iana.org/assignments/rpc-authentication-numbers/rpc-authentication-numbers.xhtml Signed-off-by: Chuck Lever --- include/linux/sunrpc/msg_prot.h | 1 + include/linux/sunrpc/xdr.h | 1 + 2 files changed, 2 insertions(+) diff --git a/include/linux/sunrpc/msg_prot.h b/include/linux/sunrpc/msg_prot.h index 938c2bf29db8..02117ed0fa2e 100644 --- a/include/linux/sunrpc/msg_prot.h +++ b/include/linux/sunrpc/msg_prot.h @@ -20,6 +20,7 @@ enum rpc_auth_flavors { RPC_AUTH_DES = 3, RPC_AUTH_KRB = 4, RPC_AUTH_GSS = 6, + RPC_AUTH_TLS = 7, RPC_AUTH_MAXFLAVOR = 8, /* pseudoflavors: */ RPC_AUTH_GSS_KRB5 = 390003, diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index a965cbc136ad..b519609af1d0 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -95,6 +95,7 @@ xdr_buf_init(struct xdr_buf *buf, void *start, size_t len) #define rpc_auth_unix cpu_to_be32(RPC_AUTH_UNIX) #define rpc_auth_short cpu_to_be32(RPC_AUTH_SHORT) #define rpc_auth_gss cpu_to_be32(RPC_AUTH_GSS) +#define rpc_auth_tls cpu_to_be32(RPC_AUTH_TLS) #define rpc_call cpu_to_be32(RPC_CALL) #define rpc_reply cpu_to_be32(RPC_REPLY) From 5a4753446253a427c0ff1e433b9c4933e5af207c Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 12 Aug 2021 16:41:42 -0400 Subject: [PATCH 240/417] rpc: fix gss_svc_init cleanup on failure The failure case here should be rare, but it's obviously wrong. Signed-off-by: J. Bruce Fields Signed-off-by: Chuck Lever --- net/sunrpc/auth_gss/svcauth_gss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index a81be45f40d9..3d685fe328fa 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1980,7 +1980,7 @@ gss_svc_init_net(struct net *net) goto out2; return 0; out2: - destroy_use_gss_proxy_proc_entry(net); + rsi_cache_destroy_net(net); out1: rsc_cache_destroy_net(net); return rv; From f7104cc1a9159cd0d3e8526cb638ae0301de4b61 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 12 Aug 2021 16:41:43 -0400 Subject: [PATCH 241/417] nfsd4: Fix forced-expiry locking This should use the network-namespace-wide client_lock, not the per-client cl_lock. You shouldn't see any bugs unless you're actually using the forced-expiry interface introduced by 89c905beccbb. Fixes: 89c905beccbb "nfsd: allow forced expiration of NFSv4 clients" Signed-off-by: J. Bruce Fields Signed-off-by: Chuck Lever --- fs/nfsd/nfs4state.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index fa67ecd5fe63..2bedc7839ec5 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2687,9 +2687,9 @@ static void force_expire_client(struct nfs4_client *clp) trace_nfsd_clid_admin_expired(&clp->cl_clientid); - spin_lock(&clp->cl_lock); + spin_lock(&nn->client_lock); clp->cl_time = 0; - spin_unlock(&clp->cl_lock); + spin_unlock(&nn->client_lock); wait_event(expiry_wq, atomic_read(&clp->cl_rpc_users) == 0); spin_lock(&nn->client_lock); From 729580ddc53efd8093371788721487024c9b2f71 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 18 Aug 2021 17:12:26 -0400 Subject: [PATCH 242/417] svcrdma: xpt_bc_xprt is already clear in __svc_rdma_free() svc_xprt_free() already "puts" the bc_xprt before calling the transport's "free" method. No need to do it twice. Signed-off-by: Chuck Lever --- net/sunrpc/xprtrdma/svc_rdma_transport.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index d1faa522c3dd..94b20fb47135 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -545,7 +545,6 @@ static void __svc_rdma_free(struct work_struct *work) { struct svcxprt_rdma *rdma = container_of(work, struct svcxprt_rdma, sc_work); - struct svc_xprt *xprt = &rdma->sc_xprt; /* This blocks until the Completion Queues are empty */ if (rdma->sc_qp && !IS_ERR(rdma->sc_qp)) @@ -553,12 +552,6 @@ static void __svc_rdma_free(struct work_struct *work) svc_rdma_flush_recv_queues(rdma); - /* Final put of backchannel client transport */ - if (xprt->xpt_bc_xprt) { - xprt_put(xprt->xpt_bc_xprt); - xprt->xpt_bc_xprt = NULL; - } - svc_rdma_destroy_rw_ctxts(rdma); svc_rdma_send_ctxts_destroy(rdma); svc_rdma_recv_ctxts_destroy(rdma); From c37453cb87e38623cb47437fdbf54ffc1262cc45 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Mon, 22 Feb 2021 10:29:46 -0500 Subject: [PATCH 243/417] gfs2: be more verbose replaying invalid rgrp blocks This patch adds some crucial information when journal replay detects a replay of an obsolete rgrp block. For example, it wasn't printing the journal id or the generation number played. This just supplements what is logged in this unusual case. The function that actually complains about the replaying of an obsolete rgrp block has been split off to avoid long lines and sparse warnings. Signed-off-by: Bob Peterson --- fs/gfs2/lops.c | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index 8ee05d25dfa6..ca0bb3a73912 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -761,6 +761,32 @@ static void buf_lo_before_scan(struct gfs2_jdesc *jd, jd->jd_replayed_blocks = 0; } +#define obsolete_rgrp_replay \ +"Replaying 0x%llx from jid=%d/0x%llx but we already have a bh!\n" +#define obsolete_rgrp_replay2 \ +"busy:%d, pinned:%d rg_gen:0x%llx, j_gen:0x%llx\n" + +static void obsolete_rgrp(struct gfs2_jdesc *jd, struct buffer_head *bh_log, + u64 blkno) +{ + struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); + struct gfs2_rgrpd *rgd; + struct gfs2_rgrp *jrgd = (struct gfs2_rgrp *)bh_log->b_data; + + rgd = gfs2_blk2rgrpd(sdp, blkno, false); + if (rgd && rgd->rd_addr == blkno && + rgd->rd_bits && rgd->rd_bits->bi_bh) { + fs_info(sdp, obsolete_rgrp_replay, (unsigned long long)blkno, + jd->jd_jid, bh_log->b_blocknr); + fs_info(sdp, obsolete_rgrp_replay2, + buffer_busy(rgd->rd_bits->bi_bh) ? 1 : 0, + buffer_pinned(rgd->rd_bits->bi_bh), + rgd->rd_igeneration, + be64_to_cpu(jrgd->rg_igeneration)); + gfs2_dump_glock(NULL, rgd->rd_gl, true); + } +} + static int buf_lo_scan_elements(struct gfs2_jdesc *jd, u32 start, struct gfs2_log_descriptor *ld, __be64 *ptr, int pass) @@ -799,21 +825,9 @@ static int buf_lo_scan_elements(struct gfs2_jdesc *jd, u32 start, struct gfs2_meta_header *mh = (struct gfs2_meta_header *)bh_ip->b_data; - if (mh->mh_type == cpu_to_be32(GFS2_METATYPE_RG)) { - struct gfs2_rgrpd *rgd; + if (mh->mh_type == cpu_to_be32(GFS2_METATYPE_RG)) + obsolete_rgrp(jd, bh_log, blkno); - rgd = gfs2_blk2rgrpd(sdp, blkno, false); - if (rgd && rgd->rd_addr == blkno && - rgd->rd_bits && rgd->rd_bits->bi_bh) { - fs_info(sdp, "Replaying 0x%llx but we " - "already have a bh!\n", - (unsigned long long)blkno); - fs_info(sdp, "busy:%d, pinned:%d\n", - buffer_busy(rgd->rd_bits->bi_bh) ? 1 : 0, - buffer_pinned(rgd->rd_bits->bi_bh)); - gfs2_dump_glock(NULL, rgd->rd_gl, true); - } - } mark_buffer_dirty(bh_ip); } brelse(bh_log); From 69a61144f32b590650af8b5f1e1262f1a731f9c5 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Mon, 24 May 2021 11:51:26 -0500 Subject: [PATCH 244/417] gfs2: trivial clean up of gfs2_ail_error This patch does not change function. It adds variable sdp to clean up function gfs2_ail_error and make it more readable. Signed-off-by: Bob Peterson --- fs/gfs2/glops.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 384565d63eea..5fbdc7e9f4ff 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -33,16 +33,18 @@ extern struct workqueue_struct *gfs2_control_wq; static void gfs2_ail_error(struct gfs2_glock *gl, const struct buffer_head *bh) { - fs_err(gl->gl_name.ln_sbd, + struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + + fs_err(sdp, "AIL buffer %p: blocknr %llu state 0x%08lx mapping %p page " "state 0x%lx\n", bh, (unsigned long long)bh->b_blocknr, bh->b_state, bh->b_page->mapping, bh->b_page->flags); - fs_err(gl->gl_name.ln_sbd, "AIL glock %u:%llu mapping %p\n", + fs_err(sdp, "AIL glock %u:%llu mapping %p\n", gl->gl_name.ln_type, gl->gl_name.ln_number, gfs2_glock2aspace(gl)); - gfs2_lm(gl->gl_name.ln_sbd, "AIL error\n"); - gfs2_withdraw(gl->gl_name.ln_sbd); + gfs2_lm(sdp, "AIL error\n"); + gfs2_withdraw(sdp); } /** From dc7674eda002037d7a2d551e272037574507c2db Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 30 Jun 2021 12:49:59 -0500 Subject: [PATCH 245/417] gfs2: tiny cleanup in gfs2_log_reserve Function gfs2_log_reserve was setting revoke_blks to 0. There's no need because it calculates it shortly thereafter. This patch removes the unnecessary set. Signed-off-by: Bob Peterson --- fs/gfs2/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 42c15cfc0821..f0ee3ff6f9a8 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -594,7 +594,7 @@ void gfs2_log_reserve(struct gfs2_sbd *sdp, struct gfs2_trans *tr, { unsigned int blks = tr->tr_reserved; unsigned int revokes = tr->tr_revokes; - unsigned int revoke_blks = 0; + unsigned int revoke_blks; *extra_revokes = 0; if (revokes) { From a9a27d4ab3de2a6a81bad4b158c74a554d78e89b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 20 Aug 2021 15:38:06 +0900 Subject: [PATCH 246/417] ksmbd: don't set FILE DELETE and FILE_DELETE_CHILD in access mask by default When there is no dacl in request, ksmbd send dacl that coverted by using file permission. This patch don't set FILE DELETE and FILE_DELETE_CHILD in access mask by default. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smbacl.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c index 2ca3714c518e..20455d810523 100644 --- a/fs/ksmbd/smbacl.c +++ b/fs/ksmbd/smbacl.c @@ -731,7 +731,6 @@ static void set_mode_dacl(struct user_namespace *user_ns, ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0, fattr->cf_mode, 0700); pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); - pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; pace->size = cpu_to_le16(ace_size + 4); size += le16_to_cpu(pace->size); pace = (struct smb_ace *)((char *)pndace + size); @@ -752,7 +751,6 @@ static void set_mode_dacl(struct user_namespace *user_ns, /* creator owner */ size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED, 0x0b, fattr->cf_mode, 0700); - pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; pace = (struct smb_ace *)((char *)pndace + size); /* creator group */ From a28dc123fa66ba7f3eca7cffc4b01d96bfd35c27 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 14 May 2021 07:42:33 -0500 Subject: [PATCH 247/417] gfs2: init system threads before freeze lock Patch 96b1454f2e ("gfs2: move freeze glock outside the make_fs_rw and _ro functions") changed the gfs2 mount sequence so that it holds the freeze lock before calling gfs2_make_fs_rw. Before this patch, gfs2_make_fs_rw called init_threads to initialize the quotad and logd threads. That is a problem if the system needs to withdraw due to IO errors early in the mount sequence, for example, while initializing the system statfs inode: 1. An IO error causes the statfs glock to not sync properly after recovery, and leaves items on the ail list. 2. The leftover items on the ail list causes its do_xmote call to fail, which makes it want to withdraw. But since the glock code cannot withdraw (because the withdraw sequence uses glocks) it relies upon the logd daemon to initiate the withdraw. 3. The withdraw can never be performed by the logd daemon because all this takes place before the logd daemon is started. This patch moves function init_threads from super.c to ops_fstype.c and it changes gfs2_fill_super to start its threads before holding the freeze lock, and if there's an error, stop its threads after releasing it. This allows the logd to run unblocked by the freeze lock. Thus, the logd daemon can perform its withdraw sequence properly. Fixes: 96b1454f2e8e ("gfs2: move freeze glock outside the make_fs_rw and _ro functions") Signed-off-by: Bob Peterson --- fs/gfs2/ops_fstype.c | 42 ++++++++++++++++++++++++++++++ fs/gfs2/super.c | 61 +++++--------------------------------------- 2 files changed, 48 insertions(+), 55 deletions(-) diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index bd3b3be1a473..ca76e3b8792c 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1089,6 +1089,34 @@ void gfs2_online_uevent(struct gfs2_sbd *sdp) kobject_uevent_env(&sdp->sd_kobj, KOBJ_ONLINE, envp); } +static int init_threads(struct gfs2_sbd *sdp) +{ + struct task_struct *p; + int error = 0; + + p = kthread_run(gfs2_logd, sdp, "gfs2_logd"); + if (IS_ERR(p)) { + error = PTR_ERR(p); + fs_err(sdp, "can't start logd thread: %d\n", error); + return error; + } + sdp->sd_logd_process = p; + + p = kthread_run(gfs2_quotad, sdp, "gfs2_quotad"); + if (IS_ERR(p)) { + error = PTR_ERR(p); + fs_err(sdp, "can't start quotad thread: %d\n", error); + goto fail; + } + sdp->sd_quotad_process = p; + return 0; + +fail: + kthread_stop(sdp->sd_logd_process); + sdp->sd_logd_process = NULL; + return error; +} + /** * gfs2_fill_super - Read in superblock * @sb: The VFS superblock @@ -1217,6 +1245,14 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) goto fail_per_node; } + if (!sb_rdonly(sb)) { + error = init_threads(sdp); + if (error) { + gfs2_withdraw_delayed(sdp); + goto fail_per_node; + } + } + error = gfs2_freeze_lock(sdp, &freeze_gh, 0); if (error) goto fail_per_node; @@ -1226,6 +1262,12 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) gfs2_freeze_unlock(&freeze_gh); if (error) { + if (sdp->sd_quotad_process) + kthread_stop(sdp->sd_quotad_process); + sdp->sd_quotad_process = NULL; + if (sdp->sd_logd_process) + kthread_stop(sdp->sd_logd_process); + sdp->sd_logd_process = NULL; fs_err(sdp, "can't make FS RW: %d\n", error); goto fail_per_node; } diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 4d4ceb0b6903..2bdbba5ea8d7 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -119,34 +119,6 @@ int gfs2_jdesc_check(struct gfs2_jdesc *jd) return 0; } -static int init_threads(struct gfs2_sbd *sdp) -{ - struct task_struct *p; - int error = 0; - - p = kthread_run(gfs2_logd, sdp, "gfs2_logd"); - if (IS_ERR(p)) { - error = PTR_ERR(p); - fs_err(sdp, "can't start logd thread: %d\n", error); - return error; - } - sdp->sd_logd_process = p; - - p = kthread_run(gfs2_quotad, sdp, "gfs2_quotad"); - if (IS_ERR(p)) { - error = PTR_ERR(p); - fs_err(sdp, "can't start quotad thread: %d\n", error); - goto fail; - } - sdp->sd_quotad_process = p; - return 0; - -fail: - kthread_stop(sdp->sd_logd_process); - sdp->sd_logd_process = NULL; - return error; -} - /** * gfs2_make_fs_rw - Turn a Read-Only FS into a Read-Write one * @sdp: the filesystem @@ -161,26 +133,17 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp) struct gfs2_log_header_host head; int error; - error = init_threads(sdp); - if (error) { - gfs2_withdraw_delayed(sdp); - return error; - } - j_gl->gl_ops->go_inval(j_gl, DIO_METADATA); - if (gfs2_withdrawn(sdp)) { - error = -EIO; - goto fail; - } + if (gfs2_withdrawn(sdp)) + return -EIO; error = gfs2_find_jhead(sdp->sd_jdesc, &head, false); if (error || gfs2_withdrawn(sdp)) - goto fail; + return error; if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) { gfs2_consist(sdp); - error = -EIO; - goto fail; + return -EIO; } /* Initialize some head of the log stuff */ @@ -188,20 +151,8 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp) gfs2_log_pointers_init(sdp, head.lh_blkno); error = gfs2_quota_init(sdp); - if (error || gfs2_withdrawn(sdp)) - goto fail; - - set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); - - return 0; - -fail: - if (sdp->sd_quotad_process) - kthread_stop(sdp->sd_quotad_process); - sdp->sd_quotad_process = NULL; - if (sdp->sd_logd_process) - kthread_stop(sdp->sd_logd_process); - sdp->sd_logd_process = NULL; + if (!error && !gfs2_withdrawn(sdp)) + set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); return error; } From 70c11ba8f2dc6ff216477a8dd7ec0ad8568c410e Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 30 Jun 2021 11:46:17 -0500 Subject: [PATCH 248/417] gfs2: Don't release and reacquire local statfs bh Before this patch, several functions in gfs2 related to the updating of the statfs file used a newly acquired/read buffer_head for the local statfs file. This is completely unnecessary, because other nodes should never update it. Recreating the buffer is a waste of time. This patch allows gfs2 to read in the local statefs buffer_head at mount time and keep it around until unmount time. Signed-off-by: Bob Peterson --- fs/gfs2/aops.c | 9 ++------- fs/gfs2/incore.h | 1 + fs/gfs2/ops_fstype.c | 9 +++++++++ fs/gfs2/super.c | 44 ++++++++++++-------------------------------- fs/gfs2/super.h | 3 +-- 5 files changed, 25 insertions(+), 41 deletions(-) diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 81d8f064126e..005e920f5d4a 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -574,10 +574,9 @@ void adjust_fs_space(struct inode *inode) { struct gfs2_sbd *sdp = GFS2_SB(inode); struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); - struct gfs2_inode *l_ip = GFS2_I(sdp->sd_sc_inode); struct gfs2_statfs_change_host *m_sc = &sdp->sd_statfs_master; struct gfs2_statfs_change_host *l_sc = &sdp->sd_statfs_local; - struct buffer_head *m_bh, *l_bh; + struct buffer_head *m_bh; u64 fs_total, new_free; if (gfs2_trans_begin(sdp, 2 * RES_STATFS, 0) != 0) @@ -600,11 +599,7 @@ void adjust_fs_space(struct inode *inode) (unsigned long long)new_free); gfs2_statfs_change(sdp, new_free, new_free, 0); - if (gfs2_meta_inode_buffer(l_ip, &l_bh) != 0) - goto out2; - update_statfs(sdp, m_bh, l_bh); - brelse(l_bh); -out2: + update_statfs(sdp, m_bh); brelse(m_bh); out: sdp->sd_rindex_uptodate = 0; diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index e6f820f146cb..397b091cbed1 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -768,6 +768,7 @@ struct gfs2_sbd { struct gfs2_glock *sd_jinode_gl; struct gfs2_holder sd_sc_gh; + struct buffer_head *sd_sc_bh; struct gfs2_holder sd_qc_gh; struct completion sd_journal_ready; diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index ca76e3b8792c..5b90d2b7db47 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -696,8 +696,16 @@ static int init_statfs(struct gfs2_sbd *sdp) fs_err(sdp, "can't lock local \"sc\" file: %d\n", error); goto free_local; } + /* read in the local statfs buffer - other nodes don't change it. */ + error = gfs2_meta_inode_buffer(ip, &sdp->sd_sc_bh); + if (error) { + fs_err(sdp, "Cannot read in local statfs: %d\n", error); + goto unlock_sd_gh; + } return 0; +unlock_sd_gh: + gfs2_glock_dq_uninit(&sdp->sd_sc_gh); free_local: free_local_statfs_inodes(sdp); iput(pn); @@ -711,6 +719,7 @@ out: static void uninit_statfs(struct gfs2_sbd *sdp) { if (!sdp->sd_args.ar_spectator) { + brelse(sdp->sd_sc_bh); gfs2_glock_dq_uninit(&sdp->sd_sc_gh); free_local_statfs_inodes(sdp); } diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 2bdbba5ea8d7..0a5b7dfa7a45 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -178,9 +178,8 @@ int gfs2_statfs_init(struct gfs2_sbd *sdp) { struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); struct gfs2_statfs_change_host *m_sc = &sdp->sd_statfs_master; - struct gfs2_inode *l_ip = GFS2_I(sdp->sd_sc_inode); struct gfs2_statfs_change_host *l_sc = &sdp->sd_statfs_local; - struct buffer_head *m_bh, *l_bh; + struct buffer_head *m_bh; struct gfs2_holder gh; int error; @@ -199,21 +198,15 @@ int gfs2_statfs_init(struct gfs2_sbd *sdp) sizeof(struct gfs2_dinode)); spin_unlock(&sdp->sd_statfs_spin); } else { - error = gfs2_meta_inode_buffer(l_ip, &l_bh); - if (error) - goto out_m_bh; - spin_lock(&sdp->sd_statfs_spin); gfs2_statfs_change_in(m_sc, m_bh->b_data + sizeof(struct gfs2_dinode)); - gfs2_statfs_change_in(l_sc, l_bh->b_data + + gfs2_statfs_change_in(l_sc, sdp->sd_sc_bh->b_data + sizeof(struct gfs2_dinode)); spin_unlock(&sdp->sd_statfs_spin); - brelse(l_bh); } -out_m_bh: brelse(m_bh); out: gfs2_glock_dq_uninit(&gh); @@ -226,22 +219,17 @@ void gfs2_statfs_change(struct gfs2_sbd *sdp, s64 total, s64 free, struct gfs2_inode *l_ip = GFS2_I(sdp->sd_sc_inode); struct gfs2_statfs_change_host *l_sc = &sdp->sd_statfs_local; struct gfs2_statfs_change_host *m_sc = &sdp->sd_statfs_master; - struct buffer_head *l_bh; s64 x, y; int need_sync = 0; - int error; - error = gfs2_meta_inode_buffer(l_ip, &l_bh); - if (error) - return; - - gfs2_trans_add_meta(l_ip->i_gl, l_bh); + gfs2_trans_add_meta(l_ip->i_gl, sdp->sd_sc_bh); spin_lock(&sdp->sd_statfs_spin); l_sc->sc_total += total; l_sc->sc_free += free; l_sc->sc_dinodes += dinodes; - gfs2_statfs_change_out(l_sc, l_bh->b_data + sizeof(struct gfs2_dinode)); + gfs2_statfs_change_out(l_sc, sdp->sd_sc_bh->b_data + + sizeof(struct gfs2_dinode)); if (sdp->sd_args.ar_statfs_percent) { x = 100 * l_sc->sc_free; y = m_sc->sc_free * sdp->sd_args.ar_statfs_percent; @@ -250,20 +238,18 @@ void gfs2_statfs_change(struct gfs2_sbd *sdp, s64 total, s64 free, } spin_unlock(&sdp->sd_statfs_spin); - brelse(l_bh); if (need_sync) gfs2_wake_up_statfs(sdp); } -void update_statfs(struct gfs2_sbd *sdp, struct buffer_head *m_bh, - struct buffer_head *l_bh) +void update_statfs(struct gfs2_sbd *sdp, struct buffer_head *m_bh) { struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); struct gfs2_inode *l_ip = GFS2_I(sdp->sd_sc_inode); struct gfs2_statfs_change_host *m_sc = &sdp->sd_statfs_master; struct gfs2_statfs_change_host *l_sc = &sdp->sd_statfs_local; - gfs2_trans_add_meta(l_ip->i_gl, l_bh); + gfs2_trans_add_meta(l_ip->i_gl, sdp->sd_sc_bh); gfs2_trans_add_meta(m_ip->i_gl, m_bh); spin_lock(&sdp->sd_statfs_spin); @@ -271,7 +257,7 @@ void update_statfs(struct gfs2_sbd *sdp, struct buffer_head *m_bh, m_sc->sc_free += l_sc->sc_free; m_sc->sc_dinodes += l_sc->sc_dinodes; memset(l_sc, 0, sizeof(struct gfs2_statfs_change)); - memset(l_bh->b_data + sizeof(struct gfs2_dinode), + memset(sdp->sd_sc_bh->b_data + sizeof(struct gfs2_dinode), 0, sizeof(struct gfs2_statfs_change)); gfs2_statfs_change_out(m_sc, m_bh->b_data + sizeof(struct gfs2_dinode)); spin_unlock(&sdp->sd_statfs_spin); @@ -281,11 +267,10 @@ int gfs2_statfs_sync(struct super_block *sb, int type) { struct gfs2_sbd *sdp = sb->s_fs_info; struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); - struct gfs2_inode *l_ip = GFS2_I(sdp->sd_sc_inode); struct gfs2_statfs_change_host *m_sc = &sdp->sd_statfs_master; struct gfs2_statfs_change_host *l_sc = &sdp->sd_statfs_local; struct gfs2_holder gh; - struct buffer_head *m_bh, *l_bh; + struct buffer_head *m_bh; int error; error = gfs2_glock_nq_init(m_ip->i_gl, LM_ST_EXCLUSIVE, GL_NOCACHE, @@ -306,21 +291,15 @@ int gfs2_statfs_sync(struct super_block *sb, int type) } spin_unlock(&sdp->sd_statfs_spin); - error = gfs2_meta_inode_buffer(l_ip, &l_bh); + error = gfs2_trans_begin(sdp, 2 * RES_DINODE, 0); if (error) goto out_bh; - error = gfs2_trans_begin(sdp, 2 * RES_DINODE, 0); - if (error) - goto out_bh2; - - update_statfs(sdp, m_bh, l_bh); + update_statfs(sdp, m_bh); sdp->sd_statfs_force_sync = 0; gfs2_trans_end(sdp); -out_bh2: - brelse(l_bh); out_bh: brelse(m_bh); out_unlock: @@ -626,6 +605,7 @@ restart: gfs2_glock_dq_uninit(&sdp->sd_journal_gh); if (gfs2_holder_initialized(&sdp->sd_jinode_gh)) gfs2_glock_dq_uninit(&sdp->sd_jinode_gh); + brelse(sdp->sd_sc_bh); gfs2_glock_dq_uninit(&sdp->sd_sc_gh); gfs2_glock_dq_uninit(&sdp->sd_qc_gh); free_local_statfs_inodes(sdp); diff --git a/fs/gfs2/super.h b/fs/gfs2/super.h index ec4affb33ed5..58d13fd77aed 100644 --- a/fs/gfs2/super.h +++ b/fs/gfs2/super.h @@ -43,8 +43,7 @@ extern void gfs2_statfs_change_in(struct gfs2_statfs_change_host *sc, const void *buf); extern void gfs2_statfs_change_out(const struct gfs2_statfs_change_host *sc, void *buf); -extern void update_statfs(struct gfs2_sbd *sdp, struct buffer_head *m_bh, - struct buffer_head *l_bh); +extern void update_statfs(struct gfs2_sbd *sdp, struct buffer_head *m_bh); extern int gfs2_statfs_sync(struct super_block *sb, int type); extern void gfs2_freeze_func(struct work_struct *work); From 7392fbb0a402ec4e4342a5e26a4bd6c359e67165 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Mon, 26 Jul 2021 12:53:12 -0500 Subject: [PATCH 249/417] gfs2: Make recovery error more readable Before this patch, withdraws could cause an error that looked like: Journal recovery skipped for 0 until next mount. This patch changes it to a more readable: Journal recovery skipped for jid 0 until next mount. Signed-off-by: Bob Peterson --- fs/gfs2/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c index f4325b44956d..34087bba88ee 100644 --- a/fs/gfs2/util.c +++ b/fs/gfs2/util.c @@ -295,7 +295,7 @@ skip_recovery: fs_warn(sdp, "Journal recovery complete for jid %d.\n", sdp->sd_lockstruct.ls_jid); else - fs_warn(sdp, "Journal recovery skipped for %d until next " + fs_warn(sdp, "Journal recovery skipped for jid %d until next " "mount.\n", sdp->sd_lockstruct.ls_jid); fs_warn(sdp, "Glock dequeues delayed: %lu\n", sdp->sd_glock_dqs_held); sdp->sd_glock_dqs_held = 0; From a8f1d32d0f04354ee4dddb83072413f2c299a192 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Tue, 27 Jul 2021 13:52:42 -0500 Subject: [PATCH 250/417] gfs2: Eliminate vestigial HIF_FIRST Holder flag HIF_FIRST is no longer used or needed, so remove it. Signed-off-by: Bob Peterson --- fs/gfs2/glock.c | 2 -- fs/gfs2/incore.h | 1 - 2 files changed, 3 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 1f3902ecdded..ff8d6a79fc6e 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -2077,8 +2077,6 @@ static const char *hflags2str(char *buf, u16 flags, unsigned long iflags) *p++ = 'H'; if (test_bit(HIF_WAIT, &iflags)) *p++ = 'W'; - if (test_bit(HIF_FIRST, &iflags)) - *p++ = 'F'; *p = 0; return buf; } diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 397b091cbed1..0fe49770166e 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -253,7 +253,6 @@ struct gfs2_lkstats { enum { /* States */ HIF_HOLDER = 6, /* Set for gh that "holds" the glock */ - HIF_FIRST = 7, HIF_WAIT = 10, }; From ba3ca2bcf4aa20670849f621f059b3657fd7614a Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Thu, 29 Jul 2021 07:34:39 -0500 Subject: [PATCH 251/417] gfs2: nit: gfs2_drop_inode shouldn't return bool Today, gfs2_drop_inode can return "false" for an int value. I'm sure this was just an oversight. Change to int value. Signed-off-by: Bob Peterson --- fs/gfs2/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 0a5b7dfa7a45..6e00d15ef0a8 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -947,7 +947,7 @@ static int gfs2_drop_inode(struct inode *inode) gfs2_glock_hold(gl); if (!gfs2_queue_delete_work(gl, 0)) gfs2_glock_queue_put(gl); - return false; + return 0; } return generic_drop_inode(inode); From 1b8550b5de7610027609ef605f85dc29f1d9da82 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 30 Jul 2021 12:40:25 -0500 Subject: [PATCH 252/417] gfs2: Mark journal inodes as "don't cache" Before this patch, journal inodes were considered regular inodes, which meant that instead of evicting them, function iput_final would just put them on the lru for later processing. If the file system withdrew for whatever reason, the withdraw would never be seen until the inode was evicted, which could be indefinitely. This patch marks all journal inodes as "don't cache" which means function iput_final will evict them immediately, allowing us to properly recover the journal on other cluster nodes. Signed-off-by: Bob Peterson --- fs/gfs2/ops_fstype.c | 1 + fs/gfs2/util.c | 1 + 2 files changed, 2 insertions(+) diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 5b90d2b7db47..7f8410d8fdc1 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -614,6 +614,7 @@ static int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh) break; } + d_mark_dontcache(jd->jd_inode); spin_lock(&sdp->sd_jindex_spin); jd->jd_jid = sdp->sd_journals++; jip = GFS2_I(jd->jd_inode); diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c index 34087bba88ee..cf345a86ef67 100644 --- a/fs/gfs2/util.c +++ b/fs/gfs2/util.c @@ -278,6 +278,7 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp) goto skip_recovery; } sdp->sd_jdesc->jd_inode = inode; + d_mark_dontcache(inode); /* * Now wait until recovery is complete. From 8cc67f704f4b61384343629feb1f1c30d64188c6 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 30 Jul 2021 12:41:16 -0500 Subject: [PATCH 253/417] gfs2: don't stop reads while withdraw in progress When gfs2 withdraws a file system, it calls signal_our_withdraw which triggers another node to replay the withdrawing node's journal. Then it waits until it knows the journal has been replayed. Part of this wait is to repeatedly call check_journal_clean which calls gfs2_jdesc_check, which checks to see if the journal is sane. As part of its sanity checks it needs to re-read its journal's metadata. But with today's code, any attempt to re-read the metadata results in -EIO because of a check for the file system withdraw in function gfs2_meta_wait. This patch adds an additional check for SDF_WITHDRAW_IN_PROG, to tell if the read is done while the withdraw is in progress. In that case we allow the metadata read to not be rejected. Therefore the metadata check is done properly, so the withdraw sequence can finish normally. Signed-off-by: Bob Peterson --- fs/gfs2/meta_io.c | 7 +++---- fs/gfs2/util.h | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index 7c9619997355..72d30a682ece 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -258,8 +258,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags, struct buffer_head *bh, *bhs[2]; int num = 0; - if (unlikely(gfs2_withdrawn(sdp)) && - (!sdp->sd_jdesc || gl != sdp->sd_jinode_gl)) { + if (unlikely(gfs2_withdrawn(sdp)) && !gfs2_withdraw_in_prog(sdp)) { *bhp = NULL; return -EIO; } @@ -317,7 +316,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags, int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh) { - if (unlikely(gfs2_withdrawn(sdp))) + if (unlikely(gfs2_withdrawn(sdp)) && !gfs2_withdraw_in_prog(sdp)) return -EIO; wait_on_buffer(bh); @@ -328,7 +327,7 @@ int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh) gfs2_io_error_bh_wd(sdp, bh); return -EIO; } - if (unlikely(gfs2_withdrawn(sdp))) + if (unlikely(gfs2_withdrawn(sdp)) && !gfs2_withdraw_in_prog(sdp)) return -EIO; return 0; diff --git a/fs/gfs2/util.h b/fs/gfs2/util.h index 69e1a0ae5a4d..78ec190f4155 100644 --- a/fs/gfs2/util.h +++ b/fs/gfs2/util.h @@ -218,6 +218,11 @@ static inline bool gfs2_withdrawing(struct gfs2_sbd *sdp) !test_bit(SDF_WITHDRAWN, &sdp->sd_flags); } +static inline bool gfs2_withdraw_in_prog(struct gfs2_sbd *sdp) +{ + return test_bit(SDF_WITHDRAW_IN_PROG, &sdp->sd_flags); +} + #define gfs2_tune_get(sdp, field) \ gfs2_tune_get_i(&(sdp)->sd_tune, &(sdp)->sd_tune.field) From d1340f80f0b8066321b499a376780da00560e857 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 30 Jul 2021 12:41:49 -0500 Subject: [PATCH 254/417] gfs2: Don't call dlm after protocol is unmounted In the gfs2 withdraw sequence, the dlm protocol is unmounted with a call to lm_unmount. After a withdraw, users are allowed to unmount the withdrawn file system. But at that point we may still have glocks left over that we need to free via unmount's call to gfs2_gl_hash_clear. These glocks may have never been completed because of whatever problem caused the withdraw (IO errors or whatever). Before this patch, function gdlm_put_lock would still try to call into dlm to unlock these leftover glocks, which resulted in dlm returning -EINVAL because the lock space was abandoned. These glocks were never freed because there was no mechanism after that to free them. This patch adds a check to gdlm_put_lock to see if the locking protocol was inactive (DFL_UNMOUNT flag) and if so, free the glock and not make the invalid call into dlm. I could have combined this "if" with the one that follows, related to leftover glock LVBs, but I felt the code was more readable with its own if clause. Signed-off-by: Bob Peterson --- fs/gfs2/lock_dlm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index dac040162ecc..50578f881e6d 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -299,6 +299,11 @@ static void gdlm_put_lock(struct gfs2_glock *gl) gfs2_sbstats_inc(gl, GFS2_LKS_DCOUNT); gfs2_update_request_times(gl); + /* don't want to call dlm if we've unmounted the lock protocol */ + if (test_bit(DFL_UNMOUNT, &ls->ls_recover_flags)) { + gfs2_glock_free(gl); + return; + } /* don't want to skip dlm_unlock writing the lvb when lock has one */ if (test_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags) && From fffe9bee14b0e04ef632b96279fa44cb3df80812 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 30 Jul 2021 13:23:49 -0500 Subject: [PATCH 255/417] gfs2: Delay withdraw from atomic context Before this patch, if function __gfs2_ail_flush detected an error syncing the ail list, it call gfs2_ail_error which called gfs2_withdraw. Since __gfs2_ail_flush deals with a specific glock, we shouldn't withdraw immediately because the withdraw code (signal_our_withdraw) uses glocks in its processing. This patch changes the call from gfs2_withdraw to gfs2_withdraw_delayed which defers the withdraw until a more appropriate context, such as the logd daemon, discovers the intent to withdraw. Reported-by: Dan Carpenter Signed-off-by: Bob Peterson --- fs/gfs2/glops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 5fbdc7e9f4ff..79c621c7863d 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -44,7 +44,7 @@ static void gfs2_ail_error(struct gfs2_glock *gl, const struct buffer_head *bh) gl->gl_name.ln_type, gl->gl_name.ln_number, gfs2_glock2aspace(gl)); gfs2_lm(sdp, "AIL error\n"); - gfs2_withdraw(sdp); + gfs2_withdraw_delayed(sdp); } /** From 08d736667185dca2751cf47eabb0830cecdeb160 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Mon, 2 Aug 2021 09:08:51 -0500 Subject: [PATCH 256/417] gfs2: Remove redundant check from gfs2_glock_dq In function gfs2_glock_dq, it checks to see if this is the fast path. Before this patch, it checked both "find_first_holder(gl) == NULL" and list_empty(&gl->gl_holders), which is redundant. If gl_holders is empty then find_first_holder must return NULL. This patch removes the redundancy. Signed-off-by: Bob Peterson --- fs/gfs2/glock.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index ff8d6a79fc6e..e0eaa9cf9fb6 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1494,12 +1494,11 @@ void gfs2_glock_dq(struct gfs2_holder *gh) list_del_init(&gh->gh_list); clear_bit(HIF_HOLDER, &gh->gh_iflags); - if (find_first_holder(gl) == NULL) { - if (list_empty(&gl->gl_holders) && - !test_bit(GLF_PENDING_DEMOTE, &gl->gl_flags) && - !test_bit(GLF_DEMOTE, &gl->gl_flags)) - fast_path = 1; - } + if (list_empty(&gl->gl_holders) && + !test_bit(GLF_PENDING_DEMOTE, &gl->gl_flags) && + !test_bit(GLF_DEMOTE, &gl->gl_flags)) + fast_path = 1; + if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl)) gfs2_glock_add_to_lru(gl); From c782af250083f69ba810e79b60a552252e777416 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 3 Aug 2021 15:45:18 -0400 Subject: [PATCH 257/417] SUNRPC: Add a /sys/kernel/debug/fail_sunrpc/ directory This directory will contain a set of administrative controls for enabling error injection for kernel RPC consumers. Signed-off-by: Chuck Lever --- lib/Kconfig.debug | 7 +++++++ net/sunrpc/debugfs.c | 14 ++++++++++++++ net/sunrpc/fail.h | 21 +++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 net/sunrpc/fail.h diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 5ddd575159fb..cd78bb0a7dd9 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1971,6 +1971,13 @@ config FAIL_MMC_REQUEST and to test how the mmc host driver handles retries from the block device. +config FAIL_SUNRPC + bool "Fault-injection capability for SunRPC" + depends on FAULT_INJECTION_DEBUG_FS && SUNRPC_DEBUG + help + Provide fault-injection capability for SunRPC and + its consumers. + config FAULT_INJECTION_STACKTRACE_FILTER bool "stacktrace filter for fault-injection capabilities" depends on FAULT_INJECTION_DEBUG_FS && STACKTRACE_SUPPORT diff --git a/net/sunrpc/debugfs.c b/net/sunrpc/debugfs.c index 56029e3af6ff..eaeb51f83abd 100644 --- a/net/sunrpc/debugfs.c +++ b/net/sunrpc/debugfs.c @@ -8,7 +8,9 @@ #include #include #include + #include "netns.h" +#include "fail.h" static struct dentry *topdir; static struct dentry *rpc_clnt_dir; @@ -297,6 +299,13 @@ static const struct file_operations fault_disconnect_fops = { .release = fault_release, }; +#if IS_ENABLED(CONFIG_FAIL_SUNRPC) +struct fail_sunrpc_attr fail_sunrpc = { + .attr = FAULT_ATTR_INITIALIZER, +}; +EXPORT_SYMBOL_GPL(fail_sunrpc); +#endif + void __exit sunrpc_debugfs_exit(void) { @@ -321,4 +330,9 @@ sunrpc_debugfs_init(void) debugfs_create_file("disconnect", S_IFREG | 0400, rpc_fault_dir, NULL, &fault_disconnect_fops); + +#if IS_ENABLED(CONFIG_FAIL_SUNRPC) + fault_create_debugfs_attr("fail_sunrpc", NULL, + &fail_sunrpc.attr); +#endif } diff --git a/net/sunrpc/fail.h b/net/sunrpc/fail.h new file mode 100644 index 000000000000..1d402b0d3453 --- /dev/null +++ b/net/sunrpc/fail.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021, Oracle. All rights reserved. + */ + +#ifndef _NET_SUNRPC_FAIL_H_ +#define _NET_SUNRPC_FAIL_H_ + +#include + +#if IS_ENABLED(CONFIG_FAULT_INJECTION) + +struct fail_sunrpc_attr { + struct fault_attr attr; +}; + +extern struct fail_sunrpc_attr fail_sunrpc; + +#endif /* CONFIG_FAULT_INJECTION */ + +#endif /* _NET_SUNRPC_FAIL_H_ */ From a4ae308143961bf688e1c8a62f6604e62b491120 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 5 Aug 2021 10:25:49 -0400 Subject: [PATCH 258/417] SUNRPC: Move client-side disconnect injection Disconnect injection stress-tests the ability for both client and server implementations to behave resiliently in the face of network instability. Convert the existing client-side disconnect injection infrastructure to use the kernel's generic error injection facility. The generic facility has a richer set of injection criteria. Signed-off-by: Chuck Lever --- include/linux/sunrpc/xprt.h | 18 -------- net/sunrpc/debugfs.c | 82 ++++++++----------------------------- net/sunrpc/fail.h | 2 + net/sunrpc/xprt.c | 14 +++++++ 4 files changed, 32 insertions(+), 84 deletions(-) diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index c8c39f22d3b1..b15c1f07162d 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -288,7 +288,6 @@ struct rpc_xprt { const char *address_strings[RPC_DISPLAY_MAX]; #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) struct dentry *debugfs; /* debugfs directory */ - atomic_t inject_disconnect; #endif struct rcu_head rcu; const struct xprt_class *xprt_class; @@ -502,21 +501,4 @@ static inline int xprt_test_and_set_binding(struct rpc_xprt *xprt) return test_and_set_bit(XPRT_BINDING, &xprt->state); } -#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) -extern unsigned int rpc_inject_disconnect; -static inline void xprt_inject_disconnect(struct rpc_xprt *xprt) -{ - if (!rpc_inject_disconnect) - return; - if (atomic_dec_return(&xprt->inject_disconnect)) - return; - atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect); - xprt->ops->inject_disconnect(xprt); -} -#else -static inline void xprt_inject_disconnect(struct rpc_xprt *xprt) -{ -} -#endif - #endif /* _LINUX_SUNRPC_XPRT_H */ diff --git a/net/sunrpc/debugfs.c b/net/sunrpc/debugfs.c index eaeb51f83abd..04e453ad3508 100644 --- a/net/sunrpc/debugfs.c +++ b/net/sunrpc/debugfs.c @@ -16,8 +16,6 @@ static struct dentry *topdir; static struct dentry *rpc_clnt_dir; static struct dentry *rpc_xprt_dir; -unsigned int rpc_inject_disconnect; - static int tasks_show(struct seq_file *f, void *v) { @@ -237,8 +235,6 @@ rpc_xprt_debugfs_register(struct rpc_xprt *xprt) /* make tasks file */ debugfs_create_file("info", S_IFREG | 0400, xprt->debugfs, xprt, &xprt_info_fops); - - atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect); } void @@ -248,62 +244,26 @@ rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt) xprt->debugfs = NULL; } -static int -fault_open(struct inode *inode, struct file *filp) -{ - filp->private_data = kmalloc(128, GFP_KERNEL); - if (!filp->private_data) - return -ENOMEM; - return 0; -} - -static int -fault_release(struct inode *inode, struct file *filp) -{ - kfree(filp->private_data); - return 0; -} - -static ssize_t -fault_disconnect_read(struct file *filp, char __user *user_buf, - size_t len, loff_t *offset) -{ - char *buffer = (char *)filp->private_data; - size_t size; - - size = sprintf(buffer, "%u\n", rpc_inject_disconnect); - return simple_read_from_buffer(user_buf, len, offset, buffer, size); -} - -static ssize_t -fault_disconnect_write(struct file *filp, const char __user *user_buf, - size_t len, loff_t *offset) -{ - char buffer[16]; - - if (len >= sizeof(buffer)) - len = sizeof(buffer) - 1; - if (copy_from_user(buffer, user_buf, len)) - return -EFAULT; - buffer[len] = '\0'; - if (kstrtouint(buffer, 10, &rpc_inject_disconnect)) - return -EINVAL; - return len; -} - -static const struct file_operations fault_disconnect_fops = { - .owner = THIS_MODULE, - .open = fault_open, - .read = fault_disconnect_read, - .write = fault_disconnect_write, - .release = fault_release, -}; - #if IS_ENABLED(CONFIG_FAIL_SUNRPC) struct fail_sunrpc_attr fail_sunrpc = { .attr = FAULT_ATTR_INITIALIZER, }; EXPORT_SYMBOL_GPL(fail_sunrpc); + +static void fail_sunrpc_init(void) +{ + struct dentry *dir; + + dir = fault_create_debugfs_attr("fail_sunrpc", NULL, + &fail_sunrpc.attr); + + debugfs_create_bool("ignore-client-disconnect", S_IFREG | 0600, dir, + &fail_sunrpc.ignore_client_disconnect); +} +#else +static void fail_sunrpc_init(void) +{ +} #endif void __exit @@ -318,21 +278,11 @@ sunrpc_debugfs_exit(void) void __init sunrpc_debugfs_init(void) { - struct dentry *rpc_fault_dir; - topdir = debugfs_create_dir("sunrpc", NULL); rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir); rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir); - rpc_fault_dir = debugfs_create_dir("inject_fault", topdir); - - debugfs_create_file("disconnect", S_IFREG | 0400, rpc_fault_dir, NULL, - &fault_disconnect_fops); - -#if IS_ENABLED(CONFIG_FAIL_SUNRPC) - fault_create_debugfs_attr("fail_sunrpc", NULL, - &fail_sunrpc.attr); -#endif + fail_sunrpc_init(); } diff --git a/net/sunrpc/fail.h b/net/sunrpc/fail.h index 1d402b0d3453..62c1b9fd59e2 100644 --- a/net/sunrpc/fail.h +++ b/net/sunrpc/fail.h @@ -12,6 +12,8 @@ struct fail_sunrpc_attr { struct fault_attr attr; + + bool ignore_client_disconnect; }; extern struct fail_sunrpc_attr fail_sunrpc; diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index fb6db09725c7..05abe344a269 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -56,6 +56,7 @@ #include "sunrpc.h" #include "sysfs.h" +#include "fail.h" /* * Local variables @@ -855,6 +856,19 @@ xprt_init_autodisconnect(struct timer_list *t) queue_work(xprtiod_workqueue, &xprt->task_cleanup); } +#if IS_ENABLED(CONFIG_FAIL_SUNRPC) +static void xprt_inject_disconnect(struct rpc_xprt *xprt) +{ + if (!fail_sunrpc.ignore_client_disconnect && + should_fail(&fail_sunrpc.attr, 1)) + xprt->ops->inject_disconnect(xprt); +} +#else +static inline void xprt_inject_disconnect(struct rpc_xprt *xprt) +{ +} +#endif + bool xprt_lock_connect(struct rpc_xprt *xprt, struct rpc_task *task, void *cookie) From 3a1261805940d0ff1dbbb9c705dddbc018c0423f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 3 Aug 2021 15:55:58 -0400 Subject: [PATCH 259/417] SUNRPC: Server-side disconnect injection Disconnect injection stress-tests the ability for both client and server implementations to behave resiliently in the face of network instability. A file called /sys/kernel/debug/fail_sunrpc/ignore-server-disconnect enables administrators to turn off server-side disconnect injection while allowing other types of sunrpc errors to be injected. The default setting is that server-side disconnect injection is enabled (ignore=false). Signed-off-by: Chuck Lever --- net/sunrpc/debugfs.c | 3 +++ net/sunrpc/fail.h | 2 ++ net/sunrpc/svc.c | 8 ++++++++ 3 files changed, 13 insertions(+) diff --git a/net/sunrpc/debugfs.c b/net/sunrpc/debugfs.c index 04e453ad3508..827bf3a28178 100644 --- a/net/sunrpc/debugfs.c +++ b/net/sunrpc/debugfs.c @@ -259,6 +259,9 @@ static void fail_sunrpc_init(void) debugfs_create_bool("ignore-client-disconnect", S_IFREG | 0600, dir, &fail_sunrpc.ignore_client_disconnect); + + debugfs_create_bool("ignore-server-disconnect", S_IFREG | 0600, dir, + &fail_sunrpc.ignore_server_disconnect); } #else static void fail_sunrpc_init(void) diff --git a/net/sunrpc/fail.h b/net/sunrpc/fail.h index 62c1b9fd59e2..69dc30cc44b8 100644 --- a/net/sunrpc/fail.h +++ b/net/sunrpc/fail.h @@ -14,6 +14,8 @@ struct fail_sunrpc_attr { struct fault_attr attr; bool ignore_client_disconnect; + + bool ignore_server_disconnect; }; extern struct fail_sunrpc_attr fail_sunrpc; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 5aa263326b6a..bfcbaf7b3822 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -31,6 +31,8 @@ #include +#include "fail.h" + #define RPCDBG_FACILITY RPCDBG_SVCDSP static void svc_unregister(const struct svc_serv *serv, struct net *net); @@ -1524,6 +1526,12 @@ svc_process(struct svc_rqst *rqstp) struct svc_serv *serv = rqstp->rq_server; u32 dir; +#if IS_ENABLED(CONFIG_FAIL_SUNRPC) + if (!fail_sunrpc.ignore_server_disconnect && + should_fail(&fail_sunrpc.attr, 1)) + svc_xprt_deferred_close(rqstp->rq_xprt); +#endif + /* * Setup response xdr_buf. * Initially it has just one page From 400edd8c0455b9de91d079a4141ff20ba2d221f2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 9 Aug 2021 14:01:50 -0400 Subject: [PATCH 260/417] SUNRPC: Add documentation for the fail_sunrpc/ directory Signed-off-by: Chuck Lever --- .../fault-injection/fault-injection.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/fault-injection/fault-injection.rst b/Documentation/fault-injection/fault-injection.rst index f47d05ed0d94..4a25c5eb6f07 100644 --- a/Documentation/fault-injection/fault-injection.rst +++ b/Documentation/fault-injection/fault-injection.rst @@ -24,6 +24,10 @@ Available fault injection capabilities injects futex deadlock and uaddr fault errors. +- fail_sunrpc + + injects kernel RPC client and server failures. + - fail_make_request injects disk IO errors on devices permitted by setting @@ -151,6 +155,20 @@ configuration of fault-injection capabilities. default is 'N', setting it to 'Y' will disable failure injections when dealing with private (address space) futexes. +- /sys/kernel/debug/fail_sunrpc/ignore-client-disconnect: + + Format: { 'Y' | 'N' } + + default is 'N', setting it to 'Y' will disable disconnect + injection on the RPC client. + +- /sys/kernel/debug/fail_sunrpc/ignore-server-disconnect: + + Format: { 'Y' | 'N' } + + default is 'N', setting it to 'Y' will disable disconnect + injection on the RPC server. + - /sys/kernel/debug/fail_function/inject: Format: { 'function-name' | '!function-name' | '' } From e70e392fa768d46ca59f2f8c0e7374099c980622 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 21 Aug 2021 23:26:01 +0900 Subject: [PATCH 261/417] ksmbd: fix permission check issue on chown and chmod When commanding chmod and chown on cifs&ksmbd, ksmbd allows it without file permissions check. There is code to check it in settattr_prepare. Instead of setting the inode directly, update the mode and uid/gid through notify_change. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/smb2pdu.c | 5 +++++ fs/ksmbd/smbacl.c | 24 ++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 0131997c2177..d329ea49fa14 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -5861,10 +5861,15 @@ int smb2_set_info(struct ksmbd_work *work) break; case SMB2_O_INFO_SECURITY: ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out; + } rc = smb2_set_info_sec(fp, le32_to_cpu(req->AdditionalInformation), req->Buffer, le32_to_cpu(req->BufferLength)); + ksmbd_revert_fsids(work); break; default: rc = -EOPNOTSUPP; diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c index 20455d810523..5456e3ad943e 100644 --- a/fs/ksmbd/smbacl.c +++ b/fs/ksmbd/smbacl.c @@ -1300,6 +1300,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, struct smb_fattr fattr = {{0}}; struct inode *inode = d_inode(path->dentry); struct user_namespace *user_ns = mnt_user_ns(path->mnt); + struct iattr newattrs; fattr.cf_uid = INVALID_UID; fattr.cf_gid = INVALID_GID; @@ -1309,12 +1310,23 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, if (rc) goto out; - inode->i_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); - if (!uid_eq(fattr.cf_uid, INVALID_UID)) - inode->i_uid = fattr.cf_uid; - if (!gid_eq(fattr.cf_gid, INVALID_GID)) - inode->i_gid = fattr.cf_gid; - mark_inode_dirty(inode); + newattrs.ia_valid = ATTR_CTIME; + if (!uid_eq(fattr.cf_uid, INVALID_UID)) { + newattrs.ia_valid |= ATTR_UID; + newattrs.ia_uid = fattr.cf_uid; + } + if (!gid_eq(fattr.cf_gid, INVALID_GID)) { + newattrs.ia_valid |= ATTR_GID; + newattrs.ia_gid = fattr.cf_gid; + } + newattrs.ia_valid |= ATTR_MODE; + newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); + + inode_lock(inode); + rc = notify_change(user_ns, path->dentry, &newattrs, NULL); + inode_unlock(inode); + if (rc) + goto out; ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); /* Update posix acls */ From 7de875b231edb807387a81cde288aa9e1015ef9e Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 20 Aug 2021 17:01:59 -0400 Subject: [PATCH 262/417] lockd: lockd server-side shouldn't set fl_ops Locks have two sets of op arrays, fl_lmops for the lock manager (lockd or nfsd), fl_ops for the filesystem. The server-side lockd code has been setting its own fl_ops, which leads to confusion (and crashes) in the reexport case, where the filesystem expects to be the only one setting fl_ops. And there's no reason for it that I can see-the lm_get/put_owner ops do the same job. Reported-by: Daire Byrne Tested-by: Daire Byrne Signed-off-by: J. Bruce Fields Signed-off-by: Chuck Lever --- fs/lockd/svclock.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 498cb70c2c0d..273a81971ed5 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -395,28 +395,10 @@ nlmsvc_release_lockowner(struct nlm_lock *lock) nlmsvc_put_lockowner(lock->fl.fl_owner); } -static void nlmsvc_locks_copy_lock(struct file_lock *new, struct file_lock *fl) -{ - struct nlm_lockowner *nlm_lo = (struct nlm_lockowner *)fl->fl_owner; - new->fl_owner = nlmsvc_get_lockowner(nlm_lo); -} - -static void nlmsvc_locks_release_private(struct file_lock *fl) -{ - nlmsvc_put_lockowner((struct nlm_lockowner *)fl->fl_owner); -} - -static const struct file_lock_operations nlmsvc_lock_ops = { - .fl_copy_lock = nlmsvc_locks_copy_lock, - .fl_release_private = nlmsvc_locks_release_private, -}; - void nlmsvc_locks_init_private(struct file_lock *fl, struct nlm_host *host, pid_t pid) { fl->fl_owner = nlmsvc_find_lockowner(host, pid); - if (fl->fl_owner != NULL) - fl->fl_ops = &nlmsvc_lock_ops; } /* @@ -788,9 +770,21 @@ nlmsvc_notify_blocked(struct file_lock *fl) printk(KERN_WARNING "lockd: notification for unknown block!\n"); } +static fl_owner_t nlmsvc_get_owner(fl_owner_t owner) +{ + return nlmsvc_get_lockowner(owner); +} + +static void nlmsvc_put_owner(fl_owner_t owner) +{ + nlmsvc_put_lockowner(owner); +} + const struct lock_manager_operations nlmsvc_lock_operations = { .lm_notify = nlmsvc_notify_blocked, .lm_grant = nlmsvc_grant_deferred, + .lm_get_owner = nlmsvc_get_owner, + .lm_put_owner = nlmsvc_put_owner, }; /* From 5a80d1c6a2704d880c1df30315a1d9b0dc1f2cd8 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Tue, 29 Jun 2021 03:36:45 +0900 Subject: [PATCH 263/417] btrfs: zoned: remove max_zone_append_size logic There used to be a patch in the original series for zoned support which limited the extent size to max_zone_append_size, but this patch has been dropped somewhere around v9. We've decided to go the opposite direction, instead of limiting extents in the first place we split them before submission to comply with the device's limits. Remove the related code, btrfs_fs_info::max_zone_append_size and btrfs_zoned_device_info::max_zone_append_size. This also removes the workaround for dm-crypt introduced in 1d68128c107a ("btrfs: zoned: fail mount if the device does not support zone append") because the fix has been merged as f34ee1dce642 ("dm crypt: Fix zoned block device support"). Reviewed-by: Anand Jain Signed-off-by: Johannes Thumshirn Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 2 -- fs/btrfs/extent_io.c | 1 - fs/btrfs/zoned.c | 20 -------------------- fs/btrfs/zoned.h | 1 - 4 files changed, 24 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index e5e53e592d4f..4a69aa604ac5 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1012,8 +1012,6 @@ struct btrfs_fs_info { u64 zoned; }; - /* Max size to emit ZONE_APPEND write command */ - u64 max_zone_append_size; struct mutex zoned_meta_io_lock; spinlock_t treelog_bg_lock; u64 treelog_bg; diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 9e81d25dea70..1f947e24091a 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3266,7 +3266,6 @@ static int calc_bio_boundaries(struct btrfs_bio_ctrl *bio_ctrl, return 0; } - ASSERT(fs_info->max_zone_append_size > 0); /* Ordered extent not yet created, so we're good */ ordered = btrfs_lookup_ordered_extent(inode, logical); if (!ordered) { diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 907c2cc45c9c..4f8bfceda095 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -296,7 +296,6 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device) struct btrfs_fs_info *fs_info = device->fs_info; struct btrfs_zoned_device_info *zone_info = NULL; struct block_device *bdev = device->bdev; - struct request_queue *queue = bdev_get_queue(bdev); sector_t nr_sectors; sector_t sector = 0; struct blk_zone *zones = NULL; @@ -348,19 +347,10 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device) nr_sectors = bdev_nr_sectors(bdev); zone_info->zone_size_shift = ilog2(zone_info->zone_size); - zone_info->max_zone_append_size = - (u64)queue_max_zone_append_sectors(queue) << SECTOR_SHIFT; zone_info->nr_zones = nr_sectors >> ilog2(zone_sectors); if (!IS_ALIGNED(nr_sectors, zone_sectors)) zone_info->nr_zones++; - if (bdev_is_zoned(bdev) && zone_info->max_zone_append_size == 0) { - btrfs_err(fs_info, "zoned: device %pg does not support zone append", - bdev); - ret = -EINVAL; - goto out; - } - zone_info->seq_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL); if (!zone_info->seq_zones) { ret = -ENOMEM; @@ -529,7 +519,6 @@ int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info) u64 zoned_devices = 0; u64 nr_devices = 0; u64 zone_size = 0; - u64 max_zone_append_size = 0; const bool incompat_zoned = btrfs_fs_incompat(fs_info, ZONED); int ret = 0; @@ -565,11 +554,6 @@ int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info) ret = -EINVAL; goto out; } - if (!max_zone_append_size || - (zone_info->max_zone_append_size && - zone_info->max_zone_append_size < max_zone_append_size)) - max_zone_append_size = - zone_info->max_zone_append_size; } nr_devices++; } @@ -619,7 +603,6 @@ int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info) } fs_info->zone_size = zone_size; - fs_info->max_zone_append_size = max_zone_append_size; fs_info->fs_devices->chunk_alloc_policy = BTRFS_CHUNK_ALLOC_ZONED; /* @@ -1318,9 +1301,6 @@ bool btrfs_use_zone_append(struct btrfs_inode *inode, u64 start) if (!btrfs_is_zoned(fs_info)) return false; - if (!fs_info->max_zone_append_size) - return false; - if (!is_data_inode(&inode->vfs_inode)) return false; diff --git a/fs/btrfs/zoned.h b/fs/btrfs/zoned.h index b0ae2608cb6b..4b299705bb12 100644 --- a/fs/btrfs/zoned.h +++ b/fs/btrfs/zoned.h @@ -22,7 +22,6 @@ struct btrfs_zoned_device_info { */ u64 zone_size; u8 zone_size_shift; - u64 max_zone_append_size; u32 nr_zones; unsigned long *seq_zones; unsigned long *empty_zones; From b3b7e1d0b4c2d80e7be8248305f6f47e46329e7e Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 23 Jun 2021 15:48:53 +0200 Subject: [PATCH 264/417] btrfs: add special case to setget helpers for 64k pages On 64K pages the size of the extent_buffer::pages array is 1 and compilation with -Warray-bounds warns due to kaddr = page_address(eb->pages[idx + 1]); when reading byte range crossing page boundary. This does never actually overflow the array because on 64K because all the data fit in one page and bounds are checked by check_setget_bounds. To fix the reported overflows and warnings add a compile-time condition that will allow compiler to eliminate the dead code that reads from the idx + 1 page. Link: https://lore.kernel.org/lkml/20210623083901.1d49d19d@canb.auug.org.au/ CC: Gustavo A. R. Silva Signed-off-by: David Sterba --- fs/btrfs/struct-funcs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/struct-funcs.c b/fs/btrfs/struct-funcs.c index 8260f8bb3ff0..f429256f56db 100644 --- a/fs/btrfs/struct-funcs.c +++ b/fs/btrfs/struct-funcs.c @@ -73,7 +73,7 @@ u##bits btrfs_get_token_##bits(struct btrfs_map_token *token, \ } \ token->kaddr = page_address(token->eb->pages[idx]); \ token->offset = idx << PAGE_SHIFT; \ - if (oip + size <= PAGE_SIZE) \ + if (INLINE_EXTENT_BUFFER_PAGES == 1 || oip + size <= PAGE_SIZE ) \ return get_unaligned_le##bits(token->kaddr + oip); \ \ memcpy(lebytes, token->kaddr + oip, part); \ @@ -94,7 +94,7 @@ u##bits btrfs_get_##bits(const struct extent_buffer *eb, \ u8 lebytes[sizeof(u##bits)]; \ \ ASSERT(check_setget_bounds(eb, ptr, off, size)); \ - if (oip + size <= PAGE_SIZE) \ + if (INLINE_EXTENT_BUFFER_PAGES == 1 || oip + size <= PAGE_SIZE) \ return get_unaligned_le##bits(kaddr + oip); \ \ memcpy(lebytes, kaddr + oip, part); \ @@ -124,7 +124,7 @@ void btrfs_set_token_##bits(struct btrfs_map_token *token, \ } \ token->kaddr = page_address(token->eb->pages[idx]); \ token->offset = idx << PAGE_SHIFT; \ - if (oip + size <= PAGE_SIZE) { \ + if (INLINE_EXTENT_BUFFER_PAGES == 1 || oip + size <= PAGE_SIZE) { \ put_unaligned_le##bits(val, token->kaddr + oip); \ return; \ } \ @@ -146,7 +146,7 @@ void btrfs_set_##bits(const struct extent_buffer *eb, void *ptr, \ u8 lebytes[sizeof(u##bits)]; \ \ ASSERT(check_setget_bounds(eb, ptr, off, size)); \ - if (oip + size <= PAGE_SIZE) { \ + if (INLINE_EXTENT_BUFFER_PAGES == 1 || oip + size <= PAGE_SIZE) { \ put_unaligned_le##bits(val, kaddr + oip); \ return; \ } \ From 4a9531cf89d29de82ef157513e593e58f49ef8f4 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Mon, 5 Jul 2021 17:06:18 +0800 Subject: [PATCH 265/417] btrfs: check-integrity: drop unnecessary function prototypes The function prototypes below aren't necessary as the functions are first defined before called. Remove them. Signed-off-by: Anand Jain Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/check-integrity.c | 49 -------------------------------------- 1 file changed, 49 deletions(-) diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c index 169508609324..9cd88dfc5f8a 100644 --- a/fs/btrfs/check-integrity.c +++ b/fs/btrfs/check-integrity.c @@ -243,47 +243,6 @@ struct btrfsic_state { u32 datablock_size; }; -static void btrfsic_block_init(struct btrfsic_block *b); -static struct btrfsic_block *btrfsic_block_alloc(void); -static void btrfsic_block_free(struct btrfsic_block *b); -static void btrfsic_block_link_init(struct btrfsic_block_link *n); -static struct btrfsic_block_link *btrfsic_block_link_alloc(void); -static void btrfsic_block_link_free(struct btrfsic_block_link *n); -static void btrfsic_dev_state_init(struct btrfsic_dev_state *ds); -static struct btrfsic_dev_state *btrfsic_dev_state_alloc(void); -static void btrfsic_dev_state_free(struct btrfsic_dev_state *ds); -static void btrfsic_block_hashtable_init(struct btrfsic_block_hashtable *h); -static void btrfsic_block_hashtable_add(struct btrfsic_block *b, - struct btrfsic_block_hashtable *h); -static void btrfsic_block_hashtable_remove(struct btrfsic_block *b); -static struct btrfsic_block *btrfsic_block_hashtable_lookup( - struct block_device *bdev, - u64 dev_bytenr, - struct btrfsic_block_hashtable *h); -static void btrfsic_block_link_hashtable_init( - struct btrfsic_block_link_hashtable *h); -static void btrfsic_block_link_hashtable_add( - struct btrfsic_block_link *l, - struct btrfsic_block_link_hashtable *h); -static void btrfsic_block_link_hashtable_remove(struct btrfsic_block_link *l); -static struct btrfsic_block_link *btrfsic_block_link_hashtable_lookup( - struct block_device *bdev_ref_to, - u64 dev_bytenr_ref_to, - struct block_device *bdev_ref_from, - u64 dev_bytenr_ref_from, - struct btrfsic_block_link_hashtable *h); -static void btrfsic_dev_state_hashtable_init( - struct btrfsic_dev_state_hashtable *h); -static void btrfsic_dev_state_hashtable_add( - struct btrfsic_dev_state *ds, - struct btrfsic_dev_state_hashtable *h); -static void btrfsic_dev_state_hashtable_remove(struct btrfsic_dev_state *ds); -static struct btrfsic_dev_state *btrfsic_dev_state_hashtable_lookup(dev_t dev, - struct btrfsic_dev_state_hashtable *h); -static struct btrfsic_stack_frame *btrfsic_stack_frame_alloc(void); -static void btrfsic_stack_frame_free(struct btrfsic_stack_frame *sf); -static int btrfsic_process_superblock(struct btrfsic_state *state, - struct btrfs_fs_devices *fs_devices); static int btrfsic_process_metablock(struct btrfsic_state *state, struct btrfsic_block *block, struct btrfsic_block_data_ctx *block_ctx, @@ -313,14 +272,6 @@ static int btrfsic_map_block(struct btrfsic_state *state, u64 bytenr, u32 len, static void btrfsic_release_block_ctx(struct btrfsic_block_data_ctx *block_ctx); static int btrfsic_read_block(struct btrfsic_state *state, struct btrfsic_block_data_ctx *block_ctx); -static void btrfsic_dump_database(struct btrfsic_state *state); -static int btrfsic_test_for_metadata(struct btrfsic_state *state, - char **datav, unsigned int num_pages); -static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state, - u64 dev_bytenr, char **mapped_datav, - unsigned int num_pages, - struct bio *bio, int *bio_is_patched, - int submit_bio_bh_rw); static int btrfsic_process_written_superblock( struct btrfsic_state *state, struct btrfsic_block *const block, From 2eadb9e75e8e65eaf3e17628e24798a3c5374f90 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Mon, 5 Jul 2021 12:29:19 +0300 Subject: [PATCH 266/417] btrfs: make btrfs_finish_chunk_alloc private to block-group.c One of the final things that must be done to add a new chunk is inserting its device extent items in the device tree. They describe the portion of allocated device physical space during phase 1 of chunk allocation. This is currently done in btrfs_finish_chunk_alloc whose name isn't very informative. What's more, this function is only used in block-group.c but is defined as public. There isn't anything special about it that would warrant it being defined in volumes.c. Just move btrfs_finish_chunk_alloc and alloc_chunk_dev_extent to block-group.c, make the former static and rename both functions to insert_dev_extents and insert_dev_extent respectively. Reviewed-by: Filipe Manana Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/block-group.c | 93 +++++++++++++++++++++++++++++++++++++++++- fs/btrfs/volumes.c | 92 ----------------------------------------- fs/btrfs/volumes.h | 2 - 3 files changed, 91 insertions(+), 96 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 9e7d9d0c763d..5bd76a45037e 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -2244,6 +2244,95 @@ static int insert_block_group_item(struct btrfs_trans_handle *trans, return btrfs_insert_item(trans, root, &key, &bgi, sizeof(bgi)); } +static int insert_dev_extent(struct btrfs_trans_handle *trans, + struct btrfs_device *device, u64 chunk_offset, + u64 start, u64 num_bytes) +{ + struct btrfs_fs_info *fs_info = device->fs_info; + struct btrfs_root *root = fs_info->dev_root; + struct btrfs_path *path; + struct btrfs_dev_extent *extent; + struct extent_buffer *leaf; + struct btrfs_key key; + int ret; + + WARN_ON(!test_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state)); + WARN_ON(test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)); + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + key.objectid = device->devid; + key.type = BTRFS_DEV_EXTENT_KEY; + key.offset = start; + ret = btrfs_insert_empty_item(trans, root, path, &key, sizeof(*extent)); + if (ret) + goto out; + + leaf = path->nodes[0]; + extent = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dev_extent); + btrfs_set_dev_extent_chunk_tree(leaf, extent, BTRFS_CHUNK_TREE_OBJECTID); + btrfs_set_dev_extent_chunk_objectid(leaf, extent, + BTRFS_FIRST_CHUNK_TREE_OBJECTID); + btrfs_set_dev_extent_chunk_offset(leaf, extent, chunk_offset); + + btrfs_set_dev_extent_length(leaf, extent, num_bytes); + btrfs_mark_buffer_dirty(leaf); +out: + btrfs_free_path(path); + return ret; +} + +/* + * This function belongs to phase 2. + * + * See the comment at btrfs_chunk_alloc() for details about the chunk allocation + * phases. + */ +static int insert_dev_extents(struct btrfs_trans_handle *trans, + u64 chunk_offset, u64 chunk_size) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_device *device; + struct extent_map *em; + struct map_lookup *map; + u64 dev_offset; + u64 stripe_size; + int i; + int ret = 0; + + em = btrfs_get_chunk_map(fs_info, chunk_offset, chunk_size); + if (IS_ERR(em)) + return PTR_ERR(em); + + map = em->map_lookup; + stripe_size = em->orig_block_len; + + /* + * Take the device list mutex to prevent races with the final phase of + * a device replace operation that replaces the device object associated + * with the map's stripes, because the device object's id can change + * at any time during that final phase of the device replace operation + * (dev-replace.c:btrfs_dev_replace_finishing()), so we could grab the + * replaced device and then see it with an ID of BTRFS_DEV_REPLACE_DEVID, + * resulting in persisting a device extent item with such ID. + */ + mutex_lock(&fs_info->fs_devices->device_list_mutex); + for (i = 0; i < map->num_stripes; i++) { + device = map->stripes[i].dev; + dev_offset = map->stripes[i].physical; + + ret = insert_dev_extent(trans, device, chunk_offset, dev_offset, + stripe_size); + if (ret) + break; + } + mutex_unlock(&fs_info->fs_devices->device_list_mutex); + + free_extent_map(em); + return ret; +} + /* * This function, btrfs_create_pending_block_groups(), belongs to the phase 2 of * chunk allocation. @@ -2278,8 +2367,8 @@ void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans) if (ret) btrfs_abort_transaction(trans, ret); } - ret = btrfs_finish_chunk_alloc(trans, block_group->start, - block_group->length); + ret = insert_dev_extents(trans, block_group->start, + block_group->length); if (ret) btrfs_abort_transaction(trans, ret); add_block_group_free_space(trans, block_group); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 70f94b75f25a..eb734099ccba 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1759,48 +1759,6 @@ out: return ret; } -static int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans, - struct btrfs_device *device, - u64 chunk_offset, u64 start, u64 num_bytes) -{ - int ret; - struct btrfs_path *path; - struct btrfs_fs_info *fs_info = device->fs_info; - struct btrfs_root *root = fs_info->dev_root; - struct btrfs_dev_extent *extent; - struct extent_buffer *leaf; - struct btrfs_key key; - - WARN_ON(!test_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state)); - WARN_ON(test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)); - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - key.objectid = device->devid; - key.offset = start; - key.type = BTRFS_DEV_EXTENT_KEY; - ret = btrfs_insert_empty_item(trans, root, path, &key, - sizeof(*extent)); - if (ret) - goto out; - - leaf = path->nodes[0]; - extent = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_dev_extent); - btrfs_set_dev_extent_chunk_tree(leaf, extent, - BTRFS_CHUNK_TREE_OBJECTID); - btrfs_set_dev_extent_chunk_objectid(leaf, extent, - BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_dev_extent_chunk_offset(leaf, extent, chunk_offset); - - btrfs_set_dev_extent_length(leaf, extent, num_bytes); - btrfs_mark_buffer_dirty(leaf); -out: - btrfs_free_path(path); - return ret; -} - static u64 find_next_chunk(struct btrfs_fs_info *fs_info) { struct extent_map_tree *em_tree; @@ -5463,56 +5421,6 @@ out: return block_group; } -/* - * This function, btrfs_finish_chunk_alloc(), belongs to phase 2. - * - * See the comment at btrfs_chunk_alloc() for details about the chunk allocation - * phases. - */ -int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans, - u64 chunk_offset, u64 chunk_size) -{ - struct btrfs_fs_info *fs_info = trans->fs_info; - struct btrfs_device *device; - struct extent_map *em; - struct map_lookup *map; - u64 dev_offset; - u64 stripe_size; - int i; - int ret = 0; - - em = btrfs_get_chunk_map(fs_info, chunk_offset, chunk_size); - if (IS_ERR(em)) - return PTR_ERR(em); - - map = em->map_lookup; - stripe_size = em->orig_block_len; - - /* - * Take the device list mutex to prevent races with the final phase of - * a device replace operation that replaces the device object associated - * with the map's stripes, because the device object's id can change - * at any time during that final phase of the device replace operation - * (dev-replace.c:btrfs_dev_replace_finishing()), so we could grab the - * replaced device and then see it with an ID of BTRFS_DEV_REPLACE_DEVID, - * resulting in persisting a device extent item with such ID. - */ - mutex_lock(&fs_info->fs_devices->device_list_mutex); - for (i = 0; i < map->num_stripes; i++) { - device = map->stripes[i].dev; - dev_offset = map->stripes[i].physical; - - ret = btrfs_alloc_dev_extent(trans, device, chunk_offset, - dev_offset, stripe_size); - if (ret) - break; - } - mutex_unlock(&fs_info->fs_devices->device_list_mutex); - - free_extent_map(em); - return ret; -} - /* * This function, btrfs_chunk_alloc_add_chunk_item(), typically belongs to the * phase 1 of chunk allocation. It belongs to phase 2 only when allocating system diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 55a8ba244716..70c749eee3ad 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -508,8 +508,6 @@ int btrfs_is_parity_mirror(struct btrfs_fs_info *fs_info, u64 logical, u64 len); unsigned long btrfs_full_stripe_len(struct btrfs_fs_info *fs_info, u64 logical); -int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans, - u64 chunk_offset, u64 chunk_size); int btrfs_chunk_alloc_add_chunk_item(struct btrfs_trans_handle *trans, struct btrfs_block_group *bg); int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset); From 67d5e289a193c643a70ceda437c625e2bc876dbc Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Tue, 6 Jul 2021 15:13:25 -0300 Subject: [PATCH 267/417] btrfs: remove max argument from generic_bin_search Both callers use btrfs_header_nritems to feed the max argument. Remove the argument and let generic_bin_search call it itself. Reviewed-by: Nikolay Borisov Signed-off-by: Marcos Paulo de Souza Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index c5c08c87e130..c212f1218fdd 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -726,21 +726,21 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, /* * search for key in the extent_buffer. The items start at offset p, - * and they are item_size apart. There are 'max' items in p. + * and they are item_size apart. * * the slot in the array is returned via slot, and it points to * the place where you would insert key if it is not found in * the array. * - * slot may point to max if the key is bigger than all of the keys + * Slot may point to total number of items if the key is bigger than + * all of the keys */ static noinline int generic_bin_search(struct extent_buffer *eb, unsigned long p, int item_size, - const struct btrfs_key *key, - int max, int *slot) + const struct btrfs_key *key, int *slot) { int low = 0; - int high = max; + int high = btrfs_header_nritems(eb); int ret; const int key_size = sizeof(struct btrfs_disk_key); @@ -799,15 +799,11 @@ int btrfs_bin_search(struct extent_buffer *eb, const struct btrfs_key *key, if (btrfs_header_level(eb) == 0) return generic_bin_search(eb, offsetof(struct btrfs_leaf, items), - sizeof(struct btrfs_item), - key, btrfs_header_nritems(eb), - slot); + sizeof(struct btrfs_item), key, slot); else return generic_bin_search(eb, offsetof(struct btrfs_node, ptrs), - sizeof(struct btrfs_key_ptr), - key, btrfs_header_nritems(eb), - slot); + sizeof(struct btrfs_key_ptr), key, slot); } static void root_add_used(struct btrfs_root *root, u32 size) From 23608d51a3b2a0e1e884eba7b1d1eadefe4aadcc Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Sun, 4 Jul 2021 20:04:57 +0800 Subject: [PATCH 268/417] btrfs: cleanup fs_devices pointer usage in btrfs_trim_fs Drop variable 'devices' (used only once) and add new variable for the fs_devices, so it is used at two locations within btrfs_trim_fs() function and also helps to access fs_devices->devices. Signed-off-by: Anand Jain Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 268ce58d4569..d5925bebd379 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -5950,9 +5950,9 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed) */ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range) { + struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; struct btrfs_block_group *cache = NULL; struct btrfs_device *device; - struct list_head *devices; u64 group_trimmed; u64 range_end = U64_MAX; u64 start; @@ -6016,9 +6016,9 @@ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range) btrfs_warn(fs_info, "failed to trim %llu block group(s), last error %d", bg_failed, bg_ret); - mutex_lock(&fs_info->fs_devices->device_list_mutex); - devices = &fs_info->fs_devices->devices; - list_for_each_entry(device, devices, dev_list) { + + mutex_lock(&fs_devices->device_list_mutex); + list_for_each_entry(device, &fs_devices->devices, dev_list) { if (test_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state)) continue; @@ -6031,7 +6031,7 @@ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range) trimmed += group_trimmed; } - mutex_unlock(&fs_info->fs_devices->device_list_mutex); + mutex_unlock(&fs_devices->device_list_mutex); if (dev_failed) btrfs_warn(fs_info, From b0ee5e1ec44afda53aaa37f8c41ad00d170506cb Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 14 Jun 2021 22:22:22 +0200 Subject: [PATCH 269/417] btrfs: drop from __GFP_HIGHMEM all allocations The highmem flag is used for allocating pages for compression and for raid56 pages. The high memory makes sense on 32bit systems but is not without problems. On 64bit system's it's just another layer of wrappers. The time the pages are allocated for compression or raid56 is relatively short (about a transaction commit), so the pages are not blocked indefinitely. As the number of pages depends on the amount of data being written/read, there's a theoretical problem. A fast device on a 32bit system could use most of the low memory pool, while with the highmem allocation that would not happen. This was possibly the original idea long time ago, but nowadays we optimize for 64bit systems. This patch removes all usage of the __GFP_HIGHMEM flag for page allocation, the kmap/kunmap are still in place and will be removed in followup patches. Remaining is masking out the bit in alloc_extent_state and __lookup_free_space_inode, that can safely stay. Signed-off-by: David Sterba --- fs/btrfs/compression.c | 3 +-- fs/btrfs/lzo.c | 4 ++-- fs/btrfs/raid56.c | 10 +++++----- fs/btrfs/zlib.c | 6 +++--- fs/btrfs/zstd.c | 6 +++--- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 30d82cdf128c..49fdec423092 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -721,8 +721,7 @@ blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, goto fail1; for (pg_index = 0; pg_index < nr_pages; pg_index++) { - cb->compressed_pages[pg_index] = alloc_page(GFP_NOFS | - __GFP_HIGHMEM); + cb->compressed_pages[pg_index] = alloc_page(GFP_NOFS); if (!cb->compressed_pages[pg_index]) { faili = pg_index - 1; ret = BLK_STS_RESOURCE; diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index cd042c7567a4..2bebb60c5830 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -146,7 +146,7 @@ int lzo_compress_pages(struct list_head *ws, struct address_space *mapping, * store the size of all chunks of compressed data in * the first 4 bytes */ - out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + out_page = alloc_page(GFP_NOFS); if (out_page == NULL) { ret = -ENOMEM; goto out; @@ -216,7 +216,7 @@ int lzo_compress_pages(struct list_head *ws, struct address_space *mapping, goto out; } - out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + out_page = alloc_page(GFP_NOFS); if (out_page == NULL) { ret = -ENOMEM; goto out; diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index 244d499ebc72..a40a45a007d4 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -1035,7 +1035,7 @@ static int alloc_rbio_pages(struct btrfs_raid_bio *rbio) for (i = 0; i < rbio->nr_pages; i++) { if (rbio->stripe_pages[i]) continue; - page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + page = alloc_page(GFP_NOFS); if (!page) return -ENOMEM; rbio->stripe_pages[i] = page; @@ -1054,7 +1054,7 @@ static int alloc_rbio_parity_pages(struct btrfs_raid_bio *rbio) for (; i < rbio->nr_pages; i++) { if (rbio->stripe_pages[i]) continue; - page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + page = alloc_page(GFP_NOFS); if (!page) return -ENOMEM; rbio->stripe_pages[i] = page; @@ -2300,7 +2300,7 @@ static int alloc_rbio_essential_pages(struct btrfs_raid_bio *rbio) if (rbio->stripe_pages[index]) continue; - page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + page = alloc_page(GFP_NOFS); if (!page) return -ENOMEM; rbio->stripe_pages[index] = page; @@ -2350,14 +2350,14 @@ static noinline void finish_parity_scrub(struct btrfs_raid_bio *rbio, if (!need_check) goto writeback; - p_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + p_page = alloc_page(GFP_NOFS); if (!p_page) goto cleanup; SetPageUptodate(p_page); if (has_qstripe) { /* RAID6, allocate and map temp space for the Q stripe */ - q_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + q_page = alloc_page(GFP_NOFS); if (!q_page) { __free_page(p_page); goto cleanup; diff --git a/fs/btrfs/zlib.c b/fs/btrfs/zlib.c index c3fa7d3fa770..2c792bc5a987 100644 --- a/fs/btrfs/zlib.c +++ b/fs/btrfs/zlib.c @@ -121,7 +121,7 @@ int zlib_compress_pages(struct list_head *ws, struct address_space *mapping, workspace->strm.total_in = 0; workspace->strm.total_out = 0; - out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + out_page = alloc_page(GFP_NOFS); if (out_page == NULL) { ret = -ENOMEM; goto out; @@ -202,7 +202,7 @@ int zlib_compress_pages(struct list_head *ws, struct address_space *mapping, ret = -E2BIG; goto out; } - out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + out_page = alloc_page(GFP_NOFS); if (out_page == NULL) { ret = -ENOMEM; goto out; @@ -240,7 +240,7 @@ int zlib_compress_pages(struct list_head *ws, struct address_space *mapping, ret = -E2BIG; goto out; } - out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + out_page = alloc_page(GFP_NOFS); if (out_page == NULL) { ret = -ENOMEM; goto out; diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c index 3e26b466476a..9451d2bb984e 100644 --- a/fs/btrfs/zstd.c +++ b/fs/btrfs/zstd.c @@ -405,7 +405,7 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, /* Allocate and map in the output buffer */ - out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + out_page = alloc_page(GFP_NOFS); if (out_page == NULL) { ret = -ENOMEM; goto out; @@ -452,7 +452,7 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, ret = -E2BIG; goto out; } - out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + out_page = alloc_page(GFP_NOFS); if (out_page == NULL) { ret = -ENOMEM; goto out; @@ -512,7 +512,7 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, ret = -E2BIG; goto out; } - out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + out_page = alloc_page(GFP_NOFS); if (out_page == NULL) { ret = -ENOMEM; goto out; From 8c945d32e60427cbc0859cf7045bbe6196bb03d8 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 14 Jun 2021 22:25:53 +0200 Subject: [PATCH 270/417] btrfs: compression: drop kmap/kunmap from lzo As we don't use highmem pages anymore, drop the kmap/kunmap. The kmap is simply page_address and kunmap is a no-op. Signed-off-by: David Sterba --- fs/btrfs/lzo.c | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 2bebb60c5830..576a0e6142ad 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -140,7 +140,7 @@ int lzo_compress_pages(struct list_head *ws, struct address_space *mapping, *total_in = 0; in_page = find_get_page(mapping, start >> PAGE_SHIFT); - data_in = kmap(in_page); + data_in = page_address(in_page); /* * store the size of all chunks of compressed data in @@ -151,7 +151,7 @@ int lzo_compress_pages(struct list_head *ws, struct address_space *mapping, ret = -ENOMEM; goto out; } - cpage_out = kmap(out_page); + cpage_out = page_address(out_page); out_offset = LZO_LEN; tot_out = LZO_LEN; pages[0] = out_page; @@ -209,7 +209,6 @@ int lzo_compress_pages(struct list_head *ws, struct address_space *mapping, if (out_len == 0 && tot_in >= len) break; - kunmap(out_page); if (nr_pages == nr_dest_pages) { out_page = NULL; ret = -E2BIG; @@ -221,7 +220,7 @@ int lzo_compress_pages(struct list_head *ws, struct address_space *mapping, ret = -ENOMEM; goto out; } - cpage_out = kmap(out_page); + cpage_out = page_address(out_page); pages[nr_pages++] = out_page; pg_bytes_left = PAGE_SIZE; @@ -243,12 +242,11 @@ int lzo_compress_pages(struct list_head *ws, struct address_space *mapping, break; bytes_left = len - tot_in; - kunmap(in_page); put_page(in_page); start += PAGE_SIZE; in_page = find_get_page(mapping, start >> PAGE_SHIFT); - data_in = kmap(in_page); + data_in = page_address(in_page); in_len = min(bytes_left, PAGE_SIZE); } @@ -258,22 +256,17 @@ int lzo_compress_pages(struct list_head *ws, struct address_space *mapping, } /* store the size of all chunks of compressed data */ - sizes_ptr = kmap_local_page(pages[0]); + sizes_ptr = page_address(pages[0]); write_compress_length(sizes_ptr, tot_out); - kunmap_local(sizes_ptr); ret = 0; *total_out = tot_out; *total_in = tot_in; out: *out_pages = nr_pages; - if (out_page) - kunmap(out_page); - if (in_page) { - kunmap(in_page); + if (in_page) put_page(in_page); - } return ret; } @@ -299,12 +292,11 @@ int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) unsigned long tot_out; unsigned long tot_len; char *buf; - bool may_late_unmap, need_unmap; struct page **pages_in = cb->compressed_pages; u64 disk_start = cb->start; struct bio *orig_bio = cb->orig_bio; - data_in = kmap(pages_in[0]); + data_in = page_address(pages_in[0]); tot_len = read_compress_length(data_in); /* * Compressed data header check. @@ -345,13 +337,11 @@ int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) tot_in += in_len; working_bytes = in_len; - may_late_unmap = need_unmap = false; /* fast path: avoid using the working buffer */ if (in_page_bytes_left >= in_len) { buf = data_in + in_offset; bytes = in_len; - may_late_unmap = true; goto cont; } @@ -381,12 +371,8 @@ cont: goto done; } - if (may_late_unmap) - need_unmap = true; - else - kunmap(pages_in[page_in_index]); - - data_in = kmap(pages_in[++page_in_index]); + page_in_index++; + data_in = page_address(pages_in[page_in_index]); in_page_bytes_left = PAGE_SIZE; in_offset = 0; @@ -396,8 +382,6 @@ cont: out_len = max_segment_len; ret = lzo1x_decompress_safe(buf, in_len, workspace->buf, &out_len); - if (need_unmap) - kunmap(pages_in[page_in_index - 1]); if (ret != LZO_E_OK) { pr_warn("BTRFS: decompress failed\n"); ret = -EIO; @@ -413,7 +397,6 @@ cont: break; } done: - kunmap(pages_in[page_in_index]); if (!ret) zero_fill_bio(orig_bio); return ret; @@ -466,7 +449,7 @@ int lzo_decompress(struct list_head *ws, unsigned char *data_in, destlen = min_t(unsigned long, destlen, PAGE_SIZE); bytes = min_t(unsigned long, destlen, out_len - start_byte); - kaddr = kmap_local_page(dest_page); + kaddr = page_address(dest_page); memcpy(kaddr, workspace->buf + start_byte, bytes); /* @@ -476,7 +459,6 @@ int lzo_decompress(struct list_head *ws, unsigned char *data_in, */ if (bytes < destlen) memset(kaddr+bytes, 0, destlen-bytes); - kunmap_local(kaddr); out: return ret; } From 696ab562e6df9fbafd6052d8ce4aafcb2ed16069 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 15 Jun 2021 01:00:05 +0200 Subject: [PATCH 271/417] btrfs: compression: drop kmap/kunmap from zlib As we don't use highmem pages anymore, drop the kmap/kunmap. The kmap is simply page_address and kunmap is a no-op. Signed-off-by: David Sterba --- fs/btrfs/zlib.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/fs/btrfs/zlib.c b/fs/btrfs/zlib.c index 2c792bc5a987..5e18d7ad75a4 100644 --- a/fs/btrfs/zlib.c +++ b/fs/btrfs/zlib.c @@ -126,7 +126,7 @@ int zlib_compress_pages(struct list_head *ws, struct address_space *mapping, ret = -ENOMEM; goto out; } - cpage_out = kmap(out_page); + cpage_out = page_address(out_page); pages[0] = out_page; nr_pages = 1; @@ -148,26 +148,22 @@ int zlib_compress_pages(struct list_head *ws, struct address_space *mapping, int i; for (i = 0; i < in_buf_pages; i++) { - if (in_page) { - kunmap(in_page); + if (in_page) put_page(in_page); - } in_page = find_get_page(mapping, start >> PAGE_SHIFT); - data_in = kmap(in_page); + data_in = page_address(in_page); memcpy(workspace->buf + i * PAGE_SIZE, data_in, PAGE_SIZE); start += PAGE_SIZE; } workspace->strm.next_in = workspace->buf; } else { - if (in_page) { - kunmap(in_page); + if (in_page) put_page(in_page); - } in_page = find_get_page(mapping, start >> PAGE_SHIFT); - data_in = kmap(in_page); + data_in = page_address(in_page); start += PAGE_SIZE; workspace->strm.next_in = data_in; } @@ -196,7 +192,6 @@ int zlib_compress_pages(struct list_head *ws, struct address_space *mapping, * the stream end if required */ if (workspace->strm.avail_out == 0) { - kunmap(out_page); if (nr_pages == nr_dest_pages) { out_page = NULL; ret = -E2BIG; @@ -207,7 +202,7 @@ int zlib_compress_pages(struct list_head *ws, struct address_space *mapping, ret = -ENOMEM; goto out; } - cpage_out = kmap(out_page); + cpage_out = page_address(out_page); pages[nr_pages] = out_page; nr_pages++; workspace->strm.avail_out = PAGE_SIZE; @@ -234,7 +229,6 @@ int zlib_compress_pages(struct list_head *ws, struct address_space *mapping, goto out; } else if (workspace->strm.avail_out == 0) { /* get another page for the stream end */ - kunmap(out_page); if (nr_pages == nr_dest_pages) { out_page = NULL; ret = -E2BIG; @@ -245,7 +239,7 @@ int zlib_compress_pages(struct list_head *ws, struct address_space *mapping, ret = -ENOMEM; goto out; } - cpage_out = kmap(out_page); + cpage_out = page_address(out_page); pages[nr_pages] = out_page; nr_pages++; workspace->strm.avail_out = PAGE_SIZE; @@ -264,13 +258,8 @@ int zlib_compress_pages(struct list_head *ws, struct address_space *mapping, *total_in = workspace->strm.total_in; out: *out_pages = nr_pages; - if (out_page) - kunmap(out_page); - - if (in_page) { - kunmap(in_page); + if (in_page) put_page(in_page); - } return ret; } @@ -289,7 +278,7 @@ int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) u64 disk_start = cb->start; struct bio *orig_bio = cb->orig_bio; - data_in = kmap(pages_in[page_in_index]); + data_in = page_address(pages_in[page_in_index]); workspace->strm.next_in = data_in; workspace->strm.avail_in = min_t(size_t, srclen, PAGE_SIZE); workspace->strm.total_in = 0; @@ -311,7 +300,6 @@ int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) { pr_warn("BTRFS: inflateInit failed\n"); - kunmap(pages_in[page_in_index]); return -EIO; } while (workspace->strm.total_in < srclen) { @@ -339,13 +327,13 @@ int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) if (workspace->strm.avail_in == 0) { unsigned long tmp; - kunmap(pages_in[page_in_index]); + page_in_index++; if (page_in_index >= total_pages_in) { data_in = NULL; break; } - data_in = kmap(pages_in[page_in_index]); + data_in = page_address(pages_in[page_in_index]); workspace->strm.next_in = data_in; tmp = srclen - workspace->strm.total_in; workspace->strm.avail_in = min(tmp, @@ -358,8 +346,6 @@ int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) ret = 0; done: zlib_inflateEnd(&workspace->strm); - if (data_in) - kunmap(pages_in[page_in_index]); if (!ret) zero_fill_bio(orig_bio); return ret; From bbaf9715f3f5b5ff0de71da91fcc34ee9c198ed8 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 15 Jun 2021 01:00:05 +0200 Subject: [PATCH 272/417] btrfs: compression: drop kmap/kunmap from zstd As we don't use highmem pages anymore, drop the kmap/kunmap. The kmap is simply page_address and kunmap is a no-op. Signed-off-by: David Sterba --- fs/btrfs/zstd.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c index 9451d2bb984e..200ba08bfae6 100644 --- a/fs/btrfs/zstd.c +++ b/fs/btrfs/zstd.c @@ -399,7 +399,7 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, /* map in the first page of input data */ in_page = find_get_page(mapping, start >> PAGE_SHIFT); - workspace->in_buf.src = kmap(in_page); + workspace->in_buf.src = page_address(in_page); workspace->in_buf.pos = 0; workspace->in_buf.size = min_t(size_t, len, PAGE_SIZE); @@ -411,7 +411,7 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, goto out; } pages[nr_pages++] = out_page; - workspace->out_buf.dst = kmap(out_page); + workspace->out_buf.dst = page_address(out_page); workspace->out_buf.pos = 0; workspace->out_buf.size = min_t(size_t, max_out, PAGE_SIZE); @@ -446,7 +446,6 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, if (workspace->out_buf.pos == workspace->out_buf.size) { tot_out += PAGE_SIZE; max_out -= PAGE_SIZE; - kunmap(out_page); if (nr_pages == nr_dest_pages) { out_page = NULL; ret = -E2BIG; @@ -458,7 +457,7 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, goto out; } pages[nr_pages++] = out_page; - workspace->out_buf.dst = kmap(out_page); + workspace->out_buf.dst = page_address(out_page); workspace->out_buf.pos = 0; workspace->out_buf.size = min_t(size_t, max_out, PAGE_SIZE); @@ -473,13 +472,12 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, /* Check if we need more input */ if (workspace->in_buf.pos == workspace->in_buf.size) { tot_in += PAGE_SIZE; - kunmap(in_page); put_page(in_page); start += PAGE_SIZE; len -= PAGE_SIZE; in_page = find_get_page(mapping, start >> PAGE_SHIFT); - workspace->in_buf.src = kmap(in_page); + workspace->in_buf.src = page_address(in_page); workspace->in_buf.pos = 0; workspace->in_buf.size = min_t(size_t, len, PAGE_SIZE); } @@ -506,7 +504,6 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, tot_out += PAGE_SIZE; max_out -= PAGE_SIZE; - kunmap(out_page); if (nr_pages == nr_dest_pages) { out_page = NULL; ret = -E2BIG; @@ -518,7 +515,7 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, goto out; } pages[nr_pages++] = out_page; - workspace->out_buf.dst = kmap(out_page); + workspace->out_buf.dst = page_address(out_page); workspace->out_buf.pos = 0; workspace->out_buf.size = min_t(size_t, max_out, PAGE_SIZE); } @@ -534,12 +531,8 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping, out: *out_pages = nr_pages; /* Cleanup */ - if (in_page) { - kunmap(in_page); + if (in_page) put_page(in_page); - } - if (out_page) - kunmap(out_page); return ret; } @@ -565,7 +558,7 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) goto done; } - workspace->in_buf.src = kmap(pages_in[page_in_index]); + workspace->in_buf.src = page_address(pages_in[page_in_index]); workspace->in_buf.pos = 0; workspace->in_buf.size = min_t(size_t, srclen, PAGE_SIZE); @@ -601,14 +594,14 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) break; if (workspace->in_buf.pos == workspace->in_buf.size) { - kunmap(pages_in[page_in_index++]); + page_in_index++; if (page_in_index >= total_pages_in) { workspace->in_buf.src = NULL; ret = -EIO; goto done; } srclen -= PAGE_SIZE; - workspace->in_buf.src = kmap(pages_in[page_in_index]); + workspace->in_buf.src = page_address(pages_in[page_in_index]); workspace->in_buf.pos = 0; workspace->in_buf.size = min_t(size_t, srclen, PAGE_SIZE); } @@ -616,8 +609,6 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) ret = 0; zero_fill_bio(orig_bio); done: - if (workspace->in_buf.src) - kunmap(pages_in[page_in_index]); return ret; } From 4c2bf276b56d8d27ddbafcdf056ef3fc60ae50b0 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 15 Jun 2021 17:15:38 +0200 Subject: [PATCH 273/417] btrfs: compression: drop kmap/kunmap from generic helpers The pages in compressed_pages are not from highmem anymore so we can drop the mapping for checksum calculation and inline extent. Signed-off-by: David Sterba --- fs/btrfs/compression.c | 3 +-- fs/btrfs/inode.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 49fdec423092..aeda426b6121 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -172,10 +172,9 @@ static int check_compressed_csum(struct btrfs_inode *inode, struct bio *bio, /* Hash through the page sector by sector */ for (pg_offset = 0; pg_offset < bytes_left; pg_offset += sectorsize) { - kaddr = kmap_atomic(page); + kaddr = page_address(page); crypto_shash_digest(shash, kaddr + pg_offset, sectorsize, csum); - kunmap_atomic(kaddr); if (memcmp(&csum, cb_sum, csum_size) != 0) { btrfs_print_data_csum_error(inode, disk_start, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 06f9f167222b..c84d42a290c6 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -286,9 +286,8 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans, cur_size = min_t(unsigned long, compressed_size, PAGE_SIZE); - kaddr = kmap_atomic(cpage); + kaddr = page_address(cpage); write_extent_buffer(leaf, kaddr, ptr, cur_size); - kunmap_atomic(kaddr); i++; ptr += cur_size; From 5da384799278afe0d2557e4d4482240840c208b8 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 15 Jun 2021 17:15:38 +0200 Subject: [PATCH 274/417] btrfs: check-integrity: drop kmap/kunmap for block pages The pages in block_ctx have never been allocated from highmem (in btrfsic_read_block) so the mapping is pointless and can be removed. Signed-off-by: David Sterba --- fs/btrfs/check-integrity.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c index 9cd88dfc5f8a..86816088927f 100644 --- a/fs/btrfs/check-integrity.c +++ b/fs/btrfs/check-integrity.c @@ -1509,10 +1509,8 @@ static void btrfsic_release_block_ctx(struct btrfsic_block_data_ctx *block_ctx) /* Pages must be unmapped in reverse order */ while (num_pages > 0) { num_pages--; - if (block_ctx->datav[num_pages]) { - kunmap_local(block_ctx->datav[num_pages]); + if (block_ctx->datav[num_pages]) block_ctx->datav[num_pages] = NULL; - } if (block_ctx->pagev[num_pages]) { __free_page(block_ctx->pagev[num_pages]); block_ctx->pagev[num_pages] = NULL; @@ -1589,7 +1587,7 @@ static int btrfsic_read_block(struct btrfsic_state *state, i = j; } for (i = 0; i < num_pages; i++) - block_ctx->datav[i] = kmap_local_page(block_ctx->pagev[i]); + block_ctx->datav[i] = page_address(block_ctx->pagev[i]); return block_ctx->len; } @@ -2654,7 +2652,7 @@ static void __btrfsic_submit_bio(struct bio *bio) bio_for_each_segment(bvec, bio, iter) { BUG_ON(bvec.bv_len != PAGE_SIZE); - mapped_datav[i] = kmap_local_page(bvec.bv_page); + mapped_datav[i] = page_address(bvec.bv_page); i++; if (dev_state->state->print_mask & @@ -2667,9 +2665,6 @@ static void __btrfsic_submit_bio(struct bio *bio) mapped_datav, segs, bio, &bio_is_patched, bio->bi_opf); - /* Unmap in reverse order */ - for (--i; i >= 0; i--) - kunmap_local(mapped_datav[i]); kfree(mapped_datav); } else if (NULL != dev_state && (bio->bi_opf & REQ_PREFLUSH)) { if (dev_state->state->print_mask & From 069a2e37789a9adb236d8f7a5f65a1390b51f184 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 20 Jul 2021 16:03:03 +0100 Subject: [PATCH 275/417] btrfs: continue readahead of siblings even if target node is in memory At reada_for_search(), when attempting to readahead a node or leaf's siblings, we skip the readahead of the siblings if the node/leaf is already in memory. That is probably fine for the READA_FORWARD and READA_BACK readahead types, as they are used on contexts where we end up reading some consecutive leaves, but usually not the whole btree. However for a READA_FORWARD_ALWAYS mode, currently only used for full send operations, it does not make sense to skip the readahead if the target node or leaf is already loaded in memory, since we know the caller is visiting every node and leaf of the btree in ascending order. So change the behaviour to not skip the readahead when the target node is already in memory and the readahead mode is READA_FORWARD_ALWAYS. The following test script was used to measure the improvement on a box using an average, consumer grade, spinning disk, with 32GiB of RAM and using a non-debug kernel config (Debian's default config). $ cat test.sh #!/bin/bash DEV=/dev/sdj MNT=/mnt/sdj MKFS_OPTIONS="--nodesize 16384" # default, just to be explicit MOUNT_OPTIONS="-o max_inline=2048" # default, just to be explicit mkfs.btrfs -f $MKFS_OPTIONS $DEV > /dev/null mount $MOUNT_OPTIONS $DEV $MNT # Create files with inline data to make it easier and faster to create # large btrees. add_files() { local total=$1 local start_offset=$2 local number_jobs=$3 local total_per_job=$(($total / $number_jobs)) echo "Creating $total new files using $number_jobs jobs" for ((n = 0; n < $number_jobs; n++)); do ( local start_num=$(($start_offset + $n * $total_per_job)) for ((i = 1; i <= $total_per_job; i++)); do local file_num=$((start_num + $i)) local file_path="$MNT/file_${file_num}" xfs_io -f -c "pwrite -S 0xab 0 2000" $file_path > /dev/null if [ $? -ne 0 ]; then echo "Failed creating file $file_path" break fi done ) & worker_pids[$n]=$! done wait ${worker_pids[@]} sync echo echo "btree node/leaf count: $(btrfs inspect-internal dump-tree -t 5 $DEV | egrep '^(node|leaf) ' | wc -l)" } file_count=2000000 add_files $file_count 0 4 echo echo "Creating snapshot..." btrfs subvolume snapshot -r $MNT $MNT/snap1 umount $MNT echo 3 > /proc/sys/vm/drop_caches blockdev --flushbufs $DEV &> /dev/null hdparm -F $DEV &> /dev/null mount $MOUNT_OPTIONS $DEV $MNT echo echo "Testing full send..." start=$(date +%s) btrfs send $MNT/snap1 > /dev/null end=$(date +%s) echo echo "Full send took $((end - start)) seconds" umount $MNT The duration of the full send operations, in seconds, were the following: Before this change: 85 seconds After this change: 76 seconds (-11.2%) Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index c212f1218fdd..63c026495193 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -1233,7 +1233,6 @@ static void reada_for_search(struct btrfs_fs_info *fs_info, u64 target; u64 nread = 0; u64 nread_max; - struct extent_buffer *eb; u32 nr; u32 blocksize; u32 nscan = 0; @@ -1262,10 +1261,14 @@ static void reada_for_search(struct btrfs_fs_info *fs_info, search = btrfs_node_blockptr(node, slot); blocksize = fs_info->nodesize; - eb = find_extent_buffer(fs_info, search); - if (eb) { - free_extent_buffer(eb); - return; + if (path->reada != READA_FORWARD_ALWAYS) { + struct extent_buffer *eb; + + eb = find_extent_buffer(fs_info, search); + if (eb) { + free_extent_buffer(eb); + return; + } } target = search; From 6534c0c99dddafc47bd4152949751ccd6a5681fc Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Tue, 20 Jul 2021 15:02:47 -0300 Subject: [PATCH 276/417] btrfs: pass NULL as trans to btrfs_search_slot if we only want to search Using a transaction in btrfs_search_slot is only useful when we are searching to add or modify the tree. When the function is used for searching, insert length and mod arguments are 0, there is no need to use a transaction. No functional changes, changing for consistency. Reviewed-by: Nikolay Borisov Signed-off-by: Marcos Paulo de Souza Signed-off-by: David Sterba --- fs/btrfs/backref.c | 2 +- fs/btrfs/extent-tree.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 78b202d198b8..728c728d03f3 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -1211,7 +1211,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans, again: head = NULL; - ret = btrfs_search_slot(trans, fs_info->extent_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, fs_info->extent_root, &key, path, 0, 0); if (ret < 0) goto out; BUG_ON(ret == 0); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index d5925bebd379..fc3da7585fb7 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -153,7 +153,7 @@ search_again: else key.type = BTRFS_EXTENT_ITEM_KEY; - ret = btrfs_search_slot(trans, fs_info->extent_root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, fs_info->extent_root, &key, path, 0, 0); if (ret < 0) goto out_free; From 2b29726c473b3f7d1b8f22d138ed12b2776bb5d2 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 19 Jul 2021 13:43:04 +0800 Subject: [PATCH 277/417] btrfs: rescue: allow ibadroots to skip bad extent tree when reading block group items When extent tree gets corrupted, normally it's not extent tree root, but one toasted tree leaf/node. In that case, rescue=ibadroots mount option won't help as it can only handle the extent tree root corruption. This patch will enhance the behavior by: - Allow fill_dummy_bgs() to ignore -EEXIST error This means we may have some block group items read from disk, but then hit some error halfway. - Fallback to fill_dummy_bgs() if any error gets hit in btrfs_read_block_groups() Of course, this still needs rescue=ibadroots mount option. With that, rescue=ibadroots can handle extent tree corruption more gracefully and allow a better recover chance. Reported-by: Zhenyu Wu Link: https://www.spinics.net/lists/linux-btrfs/msg114424.html Reviewed-by: Su Yue Reviewed-by: Anand Jain Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/block-group.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 5bd76a45037e..d5421ee0d366 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -2105,11 +2105,22 @@ static int fill_dummy_bgs(struct btrfs_fs_info *fs_info) bg->used = em->len; bg->flags = map->type; ret = btrfs_add_block_group_cache(fs_info, bg); + /* + * We may have some valid block group cache added already, in + * that case we skip to the next one. + */ + if (ret == -EEXIST) { + ret = 0; + btrfs_put_block_group(bg); + continue; + } + if (ret) { btrfs_remove_free_space_cache(bg); btrfs_put_block_group(bg); break; } + btrfs_update_space_info(fs_info, bg->flags, em->len, em->len, 0, 0, &space_info); bg->space_info = space_info; @@ -2212,6 +2223,14 @@ int btrfs_read_block_groups(struct btrfs_fs_info *info) ret = check_chunk_block_group_mappings(info); error: btrfs_free_path(path); + /* + * We've hit some error while reading the extent tree, and have + * rescue=ibadroots mount option. + * Try to fill the tree using dummy block groups so that the user can + * continue to mount and grab their data. + */ + if (ret && btrfs_test_opt(info, IGNOREBADROOTS)) + ret = fill_dummy_bgs(info); return ret; } From 506650dcb3a716ad98681f7091ba2f8e748c04b8 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 20 Jul 2021 16:05:22 +0100 Subject: [PATCH 278/417] btrfs: improve the batch insertion of delayed items When we insert the delayed items of an inode, which corresponds to the directory index keys for a directory (key type BTRFS_DIR_INDEX_KEY), we do the following: 1) Pick the first delayed item from the rbtree and insert it into the fs/subvolume btree, using btrfs_insert_empty_item() for that; 2) Without releasing the path returned by btrfs_insert_empty_item(), keep collecting as many consecutive delayed items from the rbtree as possible, as long as each one's BTRFS_DIR_INDEX_KEY key is the immediate successor of the previously picked item and as long as they fit in the available space of the leaf the path points to; 3) Then insert all the collected items into the leaf; 4) Release the reserve metadata space for each collected item and release each item (implies deleting from the rbtree); 5) Unlock the path. While this is much better than inserting items one by one, it can be improved in a few aspects: 1) Instead of adding items based on the remaining free space of the leaf, collect as many items that can fit in a leaf and bulk insert them. This results in less and larger batches, reducing the total amount of time to insert the delayed items. For example when adding 100K files to a directory, we ended up creating 1658 batches with very variable sizes ranging from 1 item to 118 items, on a filesystem with a node/leaf size of 16K. After this change, we end up with 839 batches, with the vast majority of them having exactly 120 items; 2) We do the search for more items to batch, by iterating the rbtree, while holding a write lock on the leaf; 3) While still holding the leaf locked, we are releasing the reserved metadata for each item and then deleting each item, keeping a write lock on the leaf for longer than necessary. Releasing the delayed items one by one can take a significant amount of time, because deleting them from the rbtree can often be a bit slow when the deletion results in rebalancing the rbtree. So change this so that we try to create larger batches, with a total item size up to the maximum a leaf can support, and by unlocking the leaf immediately after inserting the items, releasing the reserved metadata space of each item and releasing each item without holding the write lock on the leaf. The following script that runs fs_mark was used to test this change: $ cat test.sh #!/bin/bash DEV=/dev/nvme0n1 MNT=/mnt/nvme0n1 MOUNT_OPTIONS="-o ssd" MKFS_OPTIONS="-m single -d single" FILES=1000000 THREADS=16 FILE_SIZE=0 echo "performance" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor umount $DEV &> /dev/null mkfs.btrfs -f $MKFS_OPTIONS $DEV mount $MOUNT_OPTIONS $DEV $MNT OPTS="-S 0 -L 5 -n $FILES -s $FILE_SIZE -t 16" for ((i = 1; i <= $THREADS; i++)); do OPTS="$OPTS -d $MNT/d$i" done fs_mark $OPTS umount $MNT It was run on machine with 12 cores, 64G of ram, using a NVMe device and using a non-debug kernel config (Debian's default config). Results before this change: FSUse% Count Size Files/sec App Overhead 1 16000000 0 76182.1 72223046 3 32000000 0 62746.9 80776528 5 48000000 0 77029.0 93022381 6 64000000 0 73691.6 95251075 8 80000000 0 66288.0 85089634 Results after this change: FSUse% Count Size Files/sec App Overhead 1 16000000 0 79049.5 (+3.7%) 69700824 3 32000000 0 65248.9 (+3.9%) 80583693 5 48000000 0 77991.4 (+1.2%) 90040908 6 64000000 0 75096.8 (+1.9%) 89862241 8 80000000 0 66926.8 (+1.0%) 84429169 Reviewed-by: Josef Bacik Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/delayed-inode.c | 234 +++++++++++++++------------------------ 1 file changed, 90 insertions(+), 144 deletions(-) diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 257c1e18abd4..20f3e748027e 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -672,176 +672,122 @@ static void btrfs_delayed_inode_release_metadata(struct btrfs_fs_info *fs_info, } /* - * This helper will insert some continuous items into the same leaf according - * to the free space of the leaf. - */ -static int btrfs_batch_insert_items(struct btrfs_root *root, - struct btrfs_path *path, - struct btrfs_delayed_item *item) -{ - struct btrfs_delayed_item *curr, *next; - int free_space; - int total_size = 0; - struct extent_buffer *leaf; - char *data_ptr; - struct btrfs_key *keys; - u32 *data_size; - struct list_head head; - int slot; - int nitems; - int i; - int ret = 0; - - BUG_ON(!path->nodes[0]); - - leaf = path->nodes[0]; - free_space = btrfs_leaf_free_space(leaf); - INIT_LIST_HEAD(&head); - - next = item; - nitems = 0; - - /* - * count the number of the continuous items that we can insert in batch - */ - while (total_size + next->data_len + sizeof(struct btrfs_item) <= - free_space) { - total_size += next->data_len + sizeof(struct btrfs_item); - list_add_tail(&next->tree_list, &head); - nitems++; - - curr = next; - next = __btrfs_next_delayed_item(curr); - if (!next) - break; - - if (!btrfs_is_continuous_delayed_item(curr, next)) - break; - } - - if (!nitems) { - ret = 0; - goto out; - } - - keys = kmalloc_array(nitems, sizeof(struct btrfs_key), GFP_NOFS); - if (!keys) { - ret = -ENOMEM; - goto out; - } - - data_size = kmalloc_array(nitems, sizeof(u32), GFP_NOFS); - if (!data_size) { - ret = -ENOMEM; - goto error; - } - - /* get keys of all the delayed items */ - i = 0; - list_for_each_entry(next, &head, tree_list) { - keys[i] = next->key; - data_size[i] = next->data_len; - i++; - } - - /* insert the keys of the items */ - setup_items_for_insert(root, path, keys, data_size, nitems); - - /* insert the dir index items */ - slot = path->slots[0]; - list_for_each_entry_safe(curr, next, &head, tree_list) { - data_ptr = btrfs_item_ptr(leaf, slot, char); - write_extent_buffer(leaf, &curr->data, - (unsigned long)data_ptr, - curr->data_len); - slot++; - - btrfs_delayed_item_release_metadata(root, curr); - - list_del(&curr->tree_list); - btrfs_release_delayed_item(curr); - } - -error: - kfree(data_size); - kfree(keys); -out: - return ret; -} - -/* - * This helper can just do simple insertion that needn't extend item for new - * data, such as directory name index insertion, inode insertion. + * Insert a single delayed item or a batch of delayed items that have consecutive + * keys if they exist. */ static int btrfs_insert_delayed_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, - struct btrfs_delayed_item *delayed_item) + struct btrfs_delayed_item *first_item) { - struct extent_buffer *leaf; + LIST_HEAD(batch); + struct btrfs_delayed_item *curr; + struct btrfs_delayed_item *next; + const int max_size = BTRFS_LEAF_DATA_SIZE(root->fs_info); + int total_size; + int nitems; unsigned int nofs_flag; - char *ptr; + char *ins_data = NULL; + struct btrfs_key *ins_keys; + u32 *ins_sizes; int ret; + list_add_tail(&first_item->tree_list, &batch); + nitems = 1; + total_size = first_item->data_len + sizeof(struct btrfs_item); + curr = first_item; + + while (true) { + int next_size; + + next = __btrfs_next_delayed_item(curr); + if (!next || !btrfs_is_continuous_delayed_item(curr, next)) + break; + + next_size = next->data_len + sizeof(struct btrfs_item); + if (total_size + next_size > max_size) + break; + + list_add_tail(&next->tree_list, &batch); + nitems++; + total_size += next_size; + curr = next; + } + + if (nitems == 1) { + ins_keys = &first_item->key; + ins_sizes = &first_item->data_len; + } else { + int i = 0; + + ins_data = kmalloc(nitems * sizeof(u32) + + nitems * sizeof(struct btrfs_key), GFP_NOFS); + if (!ins_data) { + ret = -ENOMEM; + goto out; + } + ins_sizes = (u32 *)ins_data; + ins_keys = (struct btrfs_key *)(ins_data + nitems * sizeof(u32)); + list_for_each_entry(curr, &batch, tree_list) { + ins_keys[i] = curr->key; + ins_sizes[i] = curr->data_len; + i++; + } + } + nofs_flag = memalloc_nofs_save(); - ret = btrfs_insert_empty_item(trans, root, path, &delayed_item->key, - delayed_item->data_len); + ret = btrfs_insert_empty_items(trans, root, path, ins_keys, ins_sizes, + nitems); memalloc_nofs_restore(nofs_flag); - if (ret < 0 && ret != -EEXIST) - return ret; + if (ret) + goto out; - leaf = path->nodes[0]; + list_for_each_entry(curr, &batch, tree_list) { + char *data_ptr; - ptr = btrfs_item_ptr(leaf, path->slots[0], char); + data_ptr = btrfs_item_ptr(path->nodes[0], path->slots[0], char); + write_extent_buffer(path->nodes[0], &curr->data, + (unsigned long)data_ptr, curr->data_len); + path->slots[0]++; + } - write_extent_buffer(leaf, delayed_item->data, (unsigned long)ptr, - delayed_item->data_len); - btrfs_mark_buffer_dirty(leaf); + /* + * Now release our path before releasing the delayed items and their + * metadata reservations, so that we don't block other tasks for more + * time than needed. + */ + btrfs_release_path(path); - btrfs_delayed_item_release_metadata(root, delayed_item); - return 0; + list_for_each_entry_safe(curr, next, &batch, tree_list) { + list_del(&curr->tree_list); + btrfs_delayed_item_release_metadata(root, curr); + btrfs_release_delayed_item(curr); + } +out: + kfree(ins_data); + return ret; } -/* - * we insert an item first, then if there are some continuous items, we try - * to insert those items into the same leaf. - */ static int btrfs_insert_delayed_items(struct btrfs_trans_handle *trans, struct btrfs_path *path, struct btrfs_root *root, struct btrfs_delayed_node *node) { - struct btrfs_delayed_item *curr, *prev; int ret = 0; -do_again: - mutex_lock(&node->mutex); - curr = __btrfs_first_delayed_insertion_item(node); - if (!curr) - goto insert_end; + while (ret == 0) { + struct btrfs_delayed_item *curr; - ret = btrfs_insert_delayed_item(trans, root, path, curr); - if (ret < 0) { - btrfs_release_path(path); - goto insert_end; + mutex_lock(&node->mutex); + curr = __btrfs_first_delayed_insertion_item(node); + if (!curr) { + mutex_unlock(&node->mutex); + break; + } + ret = btrfs_insert_delayed_item(trans, root, path, curr); + mutex_unlock(&node->mutex); } - prev = curr; - curr = __btrfs_next_delayed_item(prev); - if (curr && btrfs_is_continuous_delayed_item(prev, curr)) { - /* insert the continuous items into the same leaf */ - path->slots[0]++; - btrfs_batch_insert_items(root, path, curr); - } - btrfs_release_delayed_item(prev); - btrfs_mark_buffer_dirty(path->nodes[0]); - - btrfs_release_path(path); - mutex_unlock(&node->mutex); - goto do_again; - -insert_end: - mutex_unlock(&node->mutex); return ret; } From 5a656c3628b241443fd07cda60f3b0587bb8c328 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 20 Jul 2021 16:05:23 +0100 Subject: [PATCH 279/417] btrfs: stop doing GFP_KERNEL memory allocations in the ref verify tool In commit 351cbf6e4410e7 ("btrfs: use nofs allocations for running delayed items") we wrapped all btree updates when running delayed items with memalloc_nofs_save() and memalloc_nofs_restore(), due to a lock inversion detected by lockdep involving reclaim and the mutex of delayed nodes. The problem is because the ref verify tool does some memory allocations with GFP_KERNEL, which can trigger reclaim and reclaim can trigger inode eviction, which requires locking the mutex of an inode's delayed node. On the other hand the ref verify tool is called when allocating metadata extents as part of operations that modify a btree, which is a problem when running delayed nodes, where we do btree updates while holding the mutex of a delayed node. This is what caused the lockdep warning. Instead of wrapping every btree update when running delayed nodes, change the ref verify tool to never do GFP_KERNEL allocations, because: 1) We get less repeated code, which at the moment does not even have a comment mentioning why we need to setup the NOFS context, which is a recommended good practice as mentioned at Documentation/core-api/gfp_mask-from-fs-io.rst 2) The ref verify tool is something meant only for debugging and not something that should be enabled on non-debug / non-development kernels; 3) We may have yet more places outside delayed-inode.c where we have similar problem: doing btree updates while holding some lock and then having the GFP_KERNEL memory allocations, from the ref verify tool, trigger reclaim and trying again to acquire the same lock through the reclaim path. Or we could get more such cases in the future, therefore this change prevents getting into similar cases when using the ref verify tool. Curiously most of the memory allocations done by the ref verify tool were already using GFP_NOFS, except a few ones for no apparent reason. Reviewed-by: Josef Bacik Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-inode.c | 12 ------------ fs/btrfs/ref-verify.c | 10 +++++----- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 20f3e748027e..61452f04181a 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -6,7 +6,6 @@ #include #include -#include #include "misc.h" #include "delayed-inode.h" #include "disk-io.h" @@ -686,7 +685,6 @@ static int btrfs_insert_delayed_item(struct btrfs_trans_handle *trans, const int max_size = BTRFS_LEAF_DATA_SIZE(root->fs_info); int total_size; int nitems; - unsigned int nofs_flag; char *ins_data = NULL; struct btrfs_key *ins_keys; u32 *ins_sizes; @@ -735,10 +733,8 @@ static int btrfs_insert_delayed_item(struct btrfs_trans_handle *trans, } } - nofs_flag = memalloc_nofs_save(); ret = btrfs_insert_empty_items(trans, root, path, ins_keys, ins_sizes, nitems); - memalloc_nofs_restore(nofs_flag); if (ret) goto out; @@ -860,7 +856,6 @@ static int btrfs_delete_delayed_items(struct btrfs_trans_handle *trans, struct btrfs_delayed_node *node) { struct btrfs_delayed_item *curr, *prev; - unsigned int nofs_flag; int ret = 0; do_again: @@ -869,9 +864,7 @@ do_again: if (!curr) goto delete_fail; - nofs_flag = memalloc_nofs_save(); ret = btrfs_search_slot(trans, root, &curr->key, path, -1, 1); - memalloc_nofs_restore(nofs_flag); if (ret < 0) goto delete_fail; else if (ret > 0) { @@ -940,7 +933,6 @@ static int __btrfs_update_delayed_inode(struct btrfs_trans_handle *trans, struct btrfs_key key; struct btrfs_inode_item *inode_item; struct extent_buffer *leaf; - unsigned int nofs_flag; int mod; int ret; @@ -953,9 +945,7 @@ static int __btrfs_update_delayed_inode(struct btrfs_trans_handle *trans, else mod = 1; - nofs_flag = memalloc_nofs_save(); ret = btrfs_lookup_inode(trans, root, path, &key, mod); - memalloc_nofs_restore(nofs_flag); if (ret > 0) ret = -ENOENT; if (ret < 0) @@ -1012,9 +1002,7 @@ search: key.type = BTRFS_INODE_EXTREF_KEY; key.offset = -1; - nofs_flag = memalloc_nofs_save(); ret = btrfs_search_slot(trans, root, &key, path, -1, 1); - memalloc_nofs_restore(nofs_flag); if (ret < 0) goto err_out; ASSERT(ret); diff --git a/fs/btrfs/ref-verify.c b/fs/btrfs/ref-verify.c index 8e026de74c44..d2062d5f71dd 100644 --- a/fs/btrfs/ref-verify.c +++ b/fs/btrfs/ref-verify.c @@ -264,8 +264,8 @@ static struct block_entry *add_block_entry(struct btrfs_fs_info *fs_info, struct block_entry *be = NULL, *exist; struct root_entry *re = NULL; - re = kzalloc(sizeof(struct root_entry), GFP_KERNEL); - be = kzalloc(sizeof(struct block_entry), GFP_KERNEL); + re = kzalloc(sizeof(struct root_entry), GFP_NOFS); + be = kzalloc(sizeof(struct block_entry), GFP_NOFS); if (!be || !re) { kfree(re); kfree(be); @@ -313,7 +313,7 @@ static int add_tree_block(struct btrfs_fs_info *fs_info, u64 ref_root, struct root_entry *re; struct ref_entry *ref = NULL, *exist; - ref = kmalloc(sizeof(struct ref_entry), GFP_KERNEL); + ref = kmalloc(sizeof(struct ref_entry), GFP_NOFS); if (!ref) return -ENOMEM; @@ -358,7 +358,7 @@ static int add_shared_data_ref(struct btrfs_fs_info *fs_info, struct block_entry *be; struct ref_entry *ref; - ref = kzalloc(sizeof(struct ref_entry), GFP_KERNEL); + ref = kzalloc(sizeof(struct ref_entry), GFP_NOFS); if (!ref) return -ENOMEM; be = add_block_entry(fs_info, bytenr, num_bytes, 0); @@ -393,7 +393,7 @@ static int add_extent_data_ref(struct btrfs_fs_info *fs_info, u64 offset = btrfs_extent_data_ref_offset(leaf, dref); u32 num_refs = btrfs_extent_data_ref_count(leaf, dref); - ref = kzalloc(sizeof(struct ref_entry), GFP_KERNEL); + ref = kzalloc(sizeof(struct ref_entry), GFP_NOFS); if (!ref) return -ENOMEM; be = add_block_entry(fs_info, bytenr, num_bytes, ref_root); From cceaa89f02f15f232391ae4be214137b0a0285c0 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 20 Jul 2021 16:03:40 +0100 Subject: [PATCH 280/417] btrfs: remove racy and unnecessary inode transaction update when using no-holes When using the NO_HOLES feature and expanding the size of an inode, we update the inode's last_trans, last_sub_trans and last_log_commit fields at maybe_insert_hole() so that a fsync does know that the inode needs to be logged (by making sure that btrfs_inode_in_log() returns false). This happens for expanding truncate operations, buffered writes, direct IO writes and when cloning extents to an offset greater than the inode's i_size. However the way we do it is racy, because in between setting the inode's last_sub_trans and last_log_commit fields, the log transaction ID that was assigned to last_sub_trans might be committed before we read the root's last_log_commit and assign that value to last_log_commit. If that happens it would make a future call to btrfs_inode_in_log() return true. This is a race that should be extremely unlikely to be hit in practice, and it is the same that was described by commit bc0939fcfab0d7 ("btrfs: fix race between marking inode needs to be logged and log syncing"). The fix would simply be to set last_log_commit to the value we assigned to last_sub_trans minus 1, like it was done in that commit. However updating these two fields plus the last_trans field is pointless here because all the callers of btrfs_cont_expand() (which is the only caller of maybe_insert_hole()) always call btrfs_set_inode_last_trans() or btrfs_update_inode() after calling btrfs_cont_expand(). Calling either btrfs_set_inode_last_trans() or btrfs_update_inode() guarantees that the next fsync will log the inode, as it makes btrfs_inode_in_log() return false. So just remove the code that explicitly sets the inode's last_trans, last_sub_trans and last_log_commit fields. Reviewed-by: Josef Bacik Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/inode.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c84d42a290c6..deec968f937f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5087,15 +5087,13 @@ static int maybe_insert_hole(struct btrfs_root *root, struct btrfs_inode *inode, int ret; /* - * Still need to make sure the inode looks like it's been updated so - * that any holes get logged if we fsync. + * If NO_HOLES is enabled, we don't need to do anything. + * Later, up in the call chain, either btrfs_set_inode_last_sub_trans() + * or btrfs_update_inode() will be called, which guarantee that the next + * fsync will know this inode was changed and needs to be logged. */ - if (btrfs_fs_incompat(fs_info, NO_HOLES)) { - inode->last_trans = fs_info->generation; - inode->last_sub_trans = root->log_transid; - inode->last_log_commit = root->last_log_commit; + if (btrfs_fs_incompat(fs_info, NO_HOLES)) return 0; - } /* * 1 - for the one we're dropping From e1a6d2648300ef4cfdcfd4838224fe5cefe3caaa Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 20 Jul 2021 16:03:41 +0100 Subject: [PATCH 281/417] btrfs: avoid unnecessary log mutex contention when syncing log When syncing the log we acquire the root's log mutex just to update the root's last_log_commit. This is unnecessary because: 1) At this point there can only be one task updating this value, which is the task committing the current log transaction. Any task that enters btrfs_sync_log() has to wait for the previous log transaction to commit and wait for the current log transaction to commit if someone else already started it (in this case it never reaches to the point of updating last_log_commit, as that is done by the committing task); 2) All readers of the root's last_log_commit don't acquire the root's log mutex. This is to avoid blocking the readers, potentially for too long and because getting a stale value of last_log_commit does not cause any functional problem, in the worst case getting a stale value results in logging an inode unnecessarily. Plus it's actually very rare to get a stale value that results in unnecessarily logging the inode. So in order to avoid unnecessary contention on the root's log mutex, which is used for several different purposes, like starting/joining a log transaction and starting writeback of a log transaction, stop acquiring the log mutex for updating the root's last_log_commit. Reviewed-by: Josef Bacik Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/tree-log.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index e6430ac9bbe8..a7ce23b9d2ee 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -3328,10 +3328,16 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, goto out_wake_log_root; } - mutex_lock(&root->log_mutex); - if (root->last_log_commit < log_transid) - root->last_log_commit = log_transid; - mutex_unlock(&root->log_mutex); + /* + * We know there can only be one task here, since we have not yet set + * root->log_commit[index1] to 0 and any task attempting to sync the + * log must wait for the previous log transaction to commit if it's + * still in progress or wait for the current log transaction commit if + * someone else already started it. We use <= and not < because the + * first log transaction has an ID of 0. + */ + ASSERT(root->last_log_commit <= log_transid); + root->last_log_commit = log_transid; out_wake_log_root: mutex_lock(&log_root_tree->log_mutex); From e68107e51f8466e1fae40d64b873d0a11398a628 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 20 Jul 2021 16:03:42 +0100 Subject: [PATCH 282/417] btrfs: remove unnecessary list head initialization when syncing log One of the last steps of syncing the log is to remove all log contexts from the root's list of contexts, done at btrfs_remove_all_log_ctxs(). There we iterate over all the contexts in the list and delete each one from the list, and after that we call INIT_LIST_HEAD() on the list. That is unnecessary since at that point the list is empty. So just remove the INIT_LIST_HEAD() call. It's not needed, increases code size (bloat-o-meter reported a delta of -122 for btrfs_sync_log() after this change) and increases two critical sections delimited by log mutexes. Reviewed-by: Josef Bacik Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/tree-log.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index a7ce23b9d2ee..3e6c8f8a2909 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -3039,8 +3039,6 @@ static inline void btrfs_remove_all_log_ctxs(struct btrfs_root *root, list_del_init(&ctx->list); ctx->log_ret = error; } - - INIT_LIST_HEAD(&root->log_ctxs[index]); } /* From 2ac691d8b3b1dd300a48b1763fa3a1434863070b Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 20 Jul 2021 16:03:43 +0100 Subject: [PATCH 283/417] btrfs: avoid unnecessary lock and leaf splits when updating inode in the log During a fast fsync, if we have already fsynced the file before and in the current transaction, we can make the inode item update more efficient and avoid acquiring a write lock on the leaf's parent. To update the inode item we are always using btrfs_insert_empty_item() to get a path pointing to the inode item, which calls btrfs_search_slot() with an "ins_len" argument of 'sizeof(struct btrfs_inode_item) + sizeof(struct btrfs_item)', and that always results in the search taking a write lock on the level 1 node that is the parent of the leaf that contains the inode item. This adds unnecessary lock contention on log trees when we have multiple fsyncs in parallel against inodes in the same subvolume, which has a very significant impact due to the fact that log trees are short lived and their height very rarely goes beyond level 2. Also, by using btrfs_insert_empty_item() when we need to update the inode item, we also end up splitting the leaf of the existing inode item when the leaf has an amount of free space smaller than the size of an inode item. Improve this by using btrfs_seach_slot(), with a 0 "ins_len" argument, when we know the inode item already exists in the log. This avoids these two inefficiencies. The following script, using fio, was used to perform the tests: $ cat fio-test.sh #!/bin/bash DEV=/dev/nvme0n1 MNT=/mnt/nvme0n1 MOUNT_OPTIONS="-o ssd" MKFS_OPTIONS="-d single -m single" if [ $# -ne 4 ]; then echo "Use $0 NUM_JOBS FILE_SIZE FSYNC_FREQ BLOCK_SIZE" exit 1 fi NUM_JOBS=$1 FILE_SIZE=$2 FSYNC_FREQ=$3 BLOCK_SIZE=$4 cat < /tmp/fio-job.ini [writers] rw=randwrite fsync=$FSYNC_FREQ fallocate=none group_reporting=1 direct=0 bs=$BLOCK_SIZE ioengine=sync size=$FILE_SIZE directory=$MNT numjobs=$NUM_JOBS EOF echo "performance" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor echo echo "Using config:" echo cat /tmp/fio-job.ini echo echo "mount options: $MOUNT_OPTIONS" echo umount $MNT &> /dev/null mkfs.btrfs -f $MKFS_OPTIONS $DEV mount $MOUNT_OPTIONS $DEV $MNT fio /tmp/fio-job.ini umount $MNT The tests were done on a physical machine, with 12 cores, 64G of RAM, using a NVMEe device and using a non-debug kernel config (the default one from Debian). The summary line from fio is provided below for each test run. With 8 jobs, file size 256M, fsync frequency of 4 and a block size of 4K: Before: WRITE: bw=28.3MiB/s (29.7MB/s), 28.3MiB/s-28.3MiB/s (29.7MB/s-29.7MB/s), io=2048MiB (2147MB), run=72297-72297msec After: WRITE: bw=28.7MiB/s (30.1MB/s), 28.7MiB/s-28.7MiB/s (30.1MB/s-30.1MB/s), io=2048MiB (2147MB), run=71411-71411msec +1.4% throughput, -1.2% runtime With 16 jobs, file size 256M, fsync frequency of 4 and a block size of 4K: Before: WRITE: bw=40.0MiB/s (42.0MB/s), 40.0MiB/s-40.0MiB/s (42.0MB/s-42.0MB/s), io=4096MiB (4295MB), run=99980-99980msec After: WRITE: bw=40.9MiB/s (42.9MB/s), 40.9MiB/s-40.9MiB/s (42.9MB/s-42.9MB/s), io=4096MiB (4295MB), run=97933-97933msec +2.2% throughput, -2.1% runtime The changes are small but it's possible to be better on faster hardware as in the test machine used disk utilization was pretty much 100% during the whole time the tests were running (observed with 'iostat -xz 1'). The tests also included the previous patch with the subject of: "btrfs: avoid unnecessary log mutex contention when syncing log". So they compared a branch without that patch and without this patch versus a branch with these two patches applied. Reviewed-by: Josef Bacik Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/tree-log.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 3e6c8f8a2909..8dde5c08a48f 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -3972,14 +3972,41 @@ static void fill_inode_item(struct btrfs_trans_handle *trans, static int log_inode_item(struct btrfs_trans_handle *trans, struct btrfs_root *log, struct btrfs_path *path, - struct btrfs_inode *inode) + struct btrfs_inode *inode, bool inode_item_dropped) { struct btrfs_inode_item *inode_item; int ret; - ret = btrfs_insert_empty_item(trans, log, path, - &inode->location, sizeof(*inode_item)); - if (ret && ret != -EEXIST) + /* + * If we are doing a fast fsync and the inode was logged before in the + * current transaction, then we know the inode was previously logged and + * it exists in the log tree. For performance reasons, in this case use + * btrfs_search_slot() directly with ins_len set to 0 so that we never + * attempt a write lock on the leaf's parent, which adds unnecessary lock + * contention in case there are concurrent fsyncs for other inodes of the + * same subvolume. Using btrfs_insert_empty_item() when the inode item + * already exists can also result in unnecessarily splitting a leaf. + */ + if (!inode_item_dropped && inode->logged_trans == trans->transid) { + ret = btrfs_search_slot(trans, log, &inode->location, path, 0, 1); + ASSERT(ret <= 0); + if (ret > 0) + ret = -ENOENT; + } else { + /* + * This means it is the first fsync in the current transaction, + * so the inode item is not in the log and we need to insert it. + * We can never get -EEXIST because we are only called for a fast + * fsync and in case an inode eviction happens after the inode was + * logged before in the current transaction, when we load again + * the inode, we set BTRFS_INODE_NEEDS_FULL_SYNC on its runtime + * flags and set ->logged_trans to 0. + */ + ret = btrfs_insert_empty_item(trans, log, path, &inode->location, + sizeof(*inode_item)); + ASSERT(ret != -EEXIST); + } + if (ret) return ret; inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); @@ -5303,6 +5330,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, bool need_log_inode_item = true; bool xattrs_logged = false; bool recursive_logging = false; + bool inode_item_dropped = true; path = btrfs_alloc_path(); if (!path) @@ -5437,6 +5465,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, } else { if (inode_only == LOG_INODE_ALL) fast_search = true; + inode_item_dropped = false; goto log_extents; } @@ -5470,7 +5499,7 @@ log_extents: btrfs_release_path(path); btrfs_release_path(dst_path); if (need_log_inode_item) { - err = log_inode_item(trans, log, dst_path, inode); + err = log_inode_item(trans, log, dst_path, inode, inode_item_dropped); if (err) goto out_unlock; /* From c7bcbb2120cb74ce8757e310e5ceea1f3a139597 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 22 Jul 2021 15:58:10 +0100 Subject: [PATCH 284/417] btrfs: remove ignore_offset argument from btrfs_find_all_roots() Currently all the callers of btrfs_find_all_roots() pass a value of false for its ignore_offset argument. This makes the argument pointless and we can remove it and make btrfs_find_all_roots() always pass false as the ignore_offset argument for btrfs_find_all_roots_safe(). So just do that. Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/backref.c | 4 ++-- fs/btrfs/backref.h | 2 +- fs/btrfs/qgroup.c | 8 ++++---- fs/btrfs/tests/qgroup-tests.c | 30 ++++++++++-------------------- 4 files changed, 17 insertions(+), 27 deletions(-) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 728c728d03f3..f735b8798ba1 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -1488,14 +1488,14 @@ static int btrfs_find_all_roots_safe(struct btrfs_trans_handle *trans, int btrfs_find_all_roots(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 bytenr, u64 time_seq, struct ulist **roots, - bool ignore_offset, bool skip_commit_root_sem) + bool skip_commit_root_sem) { int ret; if (!trans && !skip_commit_root_sem) down_read(&fs_info->commit_root_sem); ret = btrfs_find_all_roots_safe(trans, fs_info, bytenr, - time_seq, roots, ignore_offset); + time_seq, roots, false); if (!trans && !skip_commit_root_sem) up_read(&fs_info->commit_root_sem); return ret; diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h index ff5f07f9940b..ba454032dbe2 100644 --- a/fs/btrfs/backref.h +++ b/fs/btrfs/backref.h @@ -47,7 +47,7 @@ int btrfs_find_all_leafs(struct btrfs_trans_handle *trans, const u64 *extent_item_pos, bool ignore_offset); int btrfs_find_all_roots(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 bytenr, - u64 time_seq, struct ulist **roots, bool ignore_offset, + u64 time_seq, struct ulist **roots, bool skip_commit_root_sem); char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, u32 name_len, unsigned long name_off, diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 0fa121171ca1..db680f5be745 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -1733,7 +1733,7 @@ int btrfs_qgroup_trace_extent_post(struct btrfs_trans_handle *trans, ASSERT(trans != NULL); ret = btrfs_find_all_roots(NULL, trans->fs_info, bytenr, 0, &old_root, - false, true); + true); if (ret < 0) { trans->fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT; btrfs_warn(trans->fs_info, @@ -2651,7 +2651,7 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans) /* Search commit root to find old_roots */ ret = btrfs_find_all_roots(NULL, fs_info, record->bytenr, 0, - &record->old_roots, false, false); + &record->old_roots, false); if (ret < 0) goto cleanup; } @@ -2667,7 +2667,7 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans) * current root. It's safe inside commit_transaction(). */ ret = btrfs_find_all_roots(trans, fs_info, - record->bytenr, BTRFS_SEQ_LAST, &new_roots, false, false); + record->bytenr, BTRFS_SEQ_LAST, &new_roots, false); if (ret < 0) goto cleanup; if (qgroup_to_skip) { @@ -3201,7 +3201,7 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans, num_bytes = found.offset; ret = btrfs_find_all_roots(NULL, fs_info, found.objectid, 0, - &roots, false, false); + &roots, false); if (ret < 0) goto out; /* For rescan, just pass old_roots as NULL */ diff --git a/fs/btrfs/tests/qgroup-tests.c b/fs/btrfs/tests/qgroup-tests.c index 98b5aaba46f1..19ba7d5b7d8f 100644 --- a/fs/btrfs/tests/qgroup-tests.c +++ b/fs/btrfs/tests/qgroup-tests.c @@ -223,8 +223,7 @@ static int test_no_shared_qgroup(struct btrfs_root *root, * we can only call btrfs_qgroup_account_extent() directly to test * quota. */ - ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, - false, false); + ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, false); if (ret) { ulist_free(old_roots); test_err("couldn't find old roots: %d", ret); @@ -236,8 +235,7 @@ static int test_no_shared_qgroup(struct btrfs_root *root, if (ret) return ret; - ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, - false, false); + ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, false); if (ret) { ulist_free(old_roots); ulist_free(new_roots); @@ -260,8 +258,7 @@ static int test_no_shared_qgroup(struct btrfs_root *root, old_roots = NULL; new_roots = NULL; - ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, - false, false); + ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, false); if (ret) { ulist_free(old_roots); test_err("couldn't find old roots: %d", ret); @@ -272,8 +269,7 @@ static int test_no_shared_qgroup(struct btrfs_root *root, if (ret) return -EINVAL; - ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, - false, false); + ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, false); if (ret) { ulist_free(old_roots); ulist_free(new_roots); @@ -324,8 +320,7 @@ static int test_multiple_refs(struct btrfs_root *root, return ret; } - ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, - false, false); + ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, false); if (ret) { ulist_free(old_roots); test_err("couldn't find old roots: %d", ret); @@ -337,8 +332,7 @@ static int test_multiple_refs(struct btrfs_root *root, if (ret) return ret; - ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, - false, false); + ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, false); if (ret) { ulist_free(old_roots); ulist_free(new_roots); @@ -359,8 +353,7 @@ static int test_multiple_refs(struct btrfs_root *root, return -EINVAL; } - ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, - false, false); + ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, false); if (ret) { ulist_free(old_roots); test_err("couldn't find old roots: %d", ret); @@ -372,8 +365,7 @@ static int test_multiple_refs(struct btrfs_root *root, if (ret) return ret; - ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, - false, false); + ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, false); if (ret) { ulist_free(old_roots); ulist_free(new_roots); @@ -400,8 +392,7 @@ static int test_multiple_refs(struct btrfs_root *root, return -EINVAL; } - ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, - false, false); + ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &old_roots, false); if (ret) { ulist_free(old_roots); test_err("couldn't find old roots: %d", ret); @@ -413,8 +404,7 @@ static int test_multiple_refs(struct btrfs_root *root, if (ret) return ret; - ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, - false, false); + ret = btrfs_find_all_roots(&trans, fs_info, nodesize, 0, &new_roots, false); if (ret) { ulist_free(old_roots); ulist_free(new_roots); From ad9a9378502d5a9da3a47666878246b9404a3391 Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Tue, 13 Jul 2021 10:58:03 -0300 Subject: [PATCH 285/417] btrfs: use btrfs_next_leaf instead of btrfs_next_item when slots > nritems After calling btrfs_search_slot is a common practice to check if the slot found isn't bigger than number of slots in the current leaf, and if so, search for the same key in the next leaf by calling btrfs_next_leaf, which calls btrfs_next_old_leaf to do the job. Calling btrfs_next_item in the same situation would end up in the same code flow, since * btrfs_next_item * btrfs_next_old_item * if slot >= nritems(curr_leaf) btrfs_next_old_leaf Change btrfs_verify_dev_extents and calculate_emulated_zone_size functions to use btrfs_next_leaf in the same situation. Signed-off-by: Marcos Paulo de Souza Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 2 +- fs/btrfs/zoned.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index eb734099ccba..01127a4dfe91 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -8052,7 +8052,7 @@ int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info) goto out; if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_item(root, path); + ret = btrfs_next_leaf(root, path); if (ret < 0) goto out; /* No dev extents at all? Not good */ diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 4f8bfceda095..47af1ab3bf12 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -245,7 +245,7 @@ static int calculate_emulated_zone_size(struct btrfs_fs_info *fs_info) goto out; if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { - ret = btrfs_next_item(root, path); + ret = btrfs_next_leaf(root, path); if (ret < 0) goto out; /* No dev extents at all? Not good */ From f8ee80de7bcf57bada19df887d8a7f87fd179cfa Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Mon, 26 Jul 2021 15:51:07 -0300 Subject: [PATCH 286/417] btrfs: remove unneeded return variable in btrfs_lookup_file_extent We can return from btrfs_search_slot directly which also shows that it follows the same return value convention. Signed-off-by: Marcos Paulo de Souza Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/file-item.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index df6631eefc65..2673c6ba7a4e 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -233,7 +233,6 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, struct btrfs_path *path, u64 objectid, u64 offset, int mod) { - int ret; struct btrfs_key file_key; int ins_len = mod < 0 ? -1 : 0; int cow = mod != 0; @@ -241,8 +240,8 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, file_key.objectid = objectid; file_key.offset = offset; file_key.type = BTRFS_EXTENT_DATA_KEY; - ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow); - return ret; + + return btrfs_search_slot(trans, root, &file_key, path, ins_len, cow); } /* From a7d1c5dc8632e9b370ad26478c468d4e4e29f263 Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Mon, 26 Jul 2021 16:19:09 -0300 Subject: [PATCH 287/417] btrfs: introduce btrfs_lookup_match_dir btrfs_search_slot is called in multiple places in dir-item.c to search for a dir entry, and then calling btrfs_match_dir_name to return a btrfs_dir_item. In order to reduce the number of callers of btrfs_search_slot, create a common function that looks for the dir key, and if found call btrfs_match_dir_item_name. Signed-off-by: Marcos Paulo de Souza Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/dir-item.c | 76 +++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index 98b63ebed539..f1274d5c3805 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -170,6 +170,25 @@ out_free: return 0; } +static struct btrfs_dir_item *btrfs_lookup_match_dir( + struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct btrfs_path *path, + struct btrfs_key *key, const char *name, + int name_len, int mod) +{ + const int ins_len = (mod < 0 ? -1 : 0); + const int cow = (mod != 0); + int ret; + + ret = btrfs_search_slot(trans, root, key, path, ins_len, cow); + if (ret < 0) + return ERR_PTR(ret); + if (ret > 0) + return ERR_PTR(-ENOENT); + + return btrfs_match_dir_item_name(root->fs_info, path, name, name_len); +} + /* * lookup a directory item based on name. 'dir' is the objectid * we're searching in, and 'mod' tells us if you plan on deleting the @@ -181,23 +200,18 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, const char *name, int name_len, int mod) { - int ret; struct btrfs_key key; - int ins_len = mod < 0 ? -1 : 0; - int cow = mod != 0; + struct btrfs_dir_item *di; key.objectid = dir; key.type = BTRFS_DIR_ITEM_KEY; - key.offset = btrfs_name_hash(name, name_len); - ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); - if (ret < 0) - return ERR_PTR(ret); - if (ret > 0) + di = btrfs_lookup_match_dir(trans, root, path, &key, name, name_len, mod); + if (IS_ERR(di) && PTR_ERR(di) == -ENOENT) return NULL; - return btrfs_match_dir_item_name(root->fs_info, path, name, name_len); + return di; } int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir, @@ -211,7 +225,6 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir, int slot; struct btrfs_path *path; - path = btrfs_alloc_path(); if (!path) return -ENOMEM; @@ -220,20 +233,20 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir, key.type = BTRFS_DIR_ITEM_KEY; key.offset = btrfs_name_hash(name, name_len); - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + di = btrfs_lookup_match_dir(NULL, root, path, &key, name, name_len, 0); + if (IS_ERR(di)) { + ret = PTR_ERR(di); + /* Nothing found, we're safe */ + if (ret == -ENOENT) { + ret = 0; + goto out; + } - /* return back any errors */ - if (ret < 0) - goto out; - - /* nothing found, we're safe */ - if (ret > 0) { - ret = 0; - goto out; + if (ret < 0) + goto out; } /* we found an item, look for our name in the item */ - di = btrfs_match_dir_item_name(root->fs_info, path, name, name_len); if (di) { /* our exact name was found */ ret = -EEXIST; @@ -274,21 +287,13 @@ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans, u64 objectid, const char *name, int name_len, int mod) { - int ret; struct btrfs_key key; - int ins_len = mod < 0 ? -1 : 0; - int cow = mod != 0; key.objectid = dir; key.type = BTRFS_DIR_INDEX_KEY; key.offset = objectid; - ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); - if (ret < 0) - return ERR_PTR(ret); - if (ret > 0) - return ERR_PTR(-ENOENT); - return btrfs_match_dir_item_name(root->fs_info, path, name, name_len); + return btrfs_lookup_match_dir(trans, root, path, &key, name, name_len, mod); } struct btrfs_dir_item * @@ -345,21 +350,18 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans, const char *name, u16 name_len, int mod) { - int ret; struct btrfs_key key; - int ins_len = mod < 0 ? -1 : 0; - int cow = mod != 0; + struct btrfs_dir_item *di; key.objectid = dir; key.type = BTRFS_XATTR_ITEM_KEY; key.offset = btrfs_name_hash(name, name_len); - ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); - if (ret < 0) - return ERR_PTR(ret); - if (ret > 0) + + di = btrfs_lookup_match_dir(trans, root, path, &key, name, name_len, mod); + if (IS_ERR(di) && PTR_ERR(di) == -ENOENT) return NULL; - return btrfs_match_dir_item_name(root->fs_info, path, name, name_len); + return di; } /* From a129ffb8166a5a87162f79b0dd013044df68e497 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 27 Jul 2021 13:41:32 +0800 Subject: [PATCH 288/417] btrfs: remove unused start and end parameters from btrfs_run_delalloc_range() Since commit d75855b4518b ("btrfs: Remove extent_io_ops::writepage_start_hook") removes the writepage_start_hook() and adds btrfs_writepage_cow_fixup() function, there is no need to follow the old hook parameters. Remove the @start and @end hook, since currently the fixup check is full page check, it doesn't need @start and @end hook. Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 2 +- fs/btrfs/extent_io.c | 7 +++---- fs/btrfs/inode.c | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 4a69aa604ac5..f265fb4f417f 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3192,7 +3192,7 @@ int btrfs_prealloc_file_range_trans(struct inode *inode, int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct page *locked_page, u64 start, u64 end, int *page_started, unsigned long *nr_written, struct writeback_control *wbc); -int btrfs_writepage_cow_fixup(struct page *page, u64 start, u64 end); +int btrfs_writepage_cow_fixup(struct page *page); void btrfs_writepage_endio_finish_ordered(struct btrfs_inode *inode, struct page *page, u64 start, u64 end, int uptodate); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 1f947e24091a..05637ef22b9e 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3828,9 +3828,8 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode, int *nr_ret) { struct btrfs_fs_info *fs_info = inode->root->fs_info; - u64 start = page_offset(page); - u64 end = start + PAGE_SIZE - 1; - u64 cur = start; + u64 cur = page_offset(page); + u64 end = cur + PAGE_SIZE - 1; u64 extent_offset; u64 block_start; struct extent_map *em; @@ -3840,7 +3839,7 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode, const unsigned int write_flags = wbc_to_write_flags(wbc); bool compressed; - ret = btrfs_writepage_cow_fixup(page, start, end); + ret = btrfs_writepage_cow_fixup(page); if (ret) { /* Fixup worker will requeue */ redirty_page_for_writepage(wbc, page); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index deec968f937f..30f2dc421cef 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2769,7 +2769,7 @@ out_page: * to fix it up. The async helper will wait for ordered extents, set * the delalloc bit and make it safe to write the page. */ -int btrfs_writepage_cow_fixup(struct page *page, u64 start, u64 end) +int btrfs_writepage_cow_fixup(struct page *page) { struct inode *inode = page->mapping->host; struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); From 25c1252a026c6c34ff99c86f31856701b2192c0e Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Jul 2021 14:15:08 +0200 Subject: [PATCH 289/417] btrfs: switch uptodate to bool in btrfs_writepage_endio_finish_ordered The uptodate parameter should be bool, change the type. Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 2 +- fs/btrfs/extent_io.c | 8 ++++---- fs/btrfs/inode.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index f265fb4f417f..194002f4e611 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3195,7 +3195,7 @@ int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct page *locked_page int btrfs_writepage_cow_fixup(struct page *page); void btrfs_writepage_endio_finish_ordered(struct btrfs_inode *inode, struct page *page, u64 start, - u64 end, int uptodate); + u64 end, bool uptodate); extern const struct dentry_operations btrfs_dentry_operations; extern const struct iomap_ops btrfs_dio_iomap_ops; extern const struct iomap_dio_ops btrfs_dio_ops; diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 05637ef22b9e..19ba5b03b2df 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2779,7 +2779,7 @@ next: void end_extent_writepage(struct page *page, int err, u64 start, u64 end) { struct btrfs_inode *inode; - int uptodate = (err == 0); + const bool uptodate = (err == 0); int ret = 0; ASSERT(page && page->mapping); @@ -3863,7 +3863,7 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode, if (cur >= i_size) { btrfs_writepage_endio_finish_ordered(inode, page, cur, - end, 1); + end, true); break; } @@ -3913,7 +3913,7 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode, nr++; else btrfs_writepage_endio_finish_ordered(inode, - page, cur, cur + iosize - 1, 1); + page, cur, cur + iosize - 1, true); cur += iosize; continue; } @@ -4982,7 +4982,7 @@ int extent_write_locked_range(struct inode *inode, u64 start, u64 end, ret = __extent_writepage(page, &wbc_writepages, &epd); else { btrfs_writepage_endio_finish_ordered(BTRFS_I(inode), - page, start, start + PAGE_SIZE - 1, 1); + page, start, start + PAGE_SIZE - 1, true); unlock_page(page); } put_page(page); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 30f2dc421cef..d41aa8e5d0cc 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -972,7 +972,7 @@ retry: p->mapping = inode->vfs_inode.i_mapping; btrfs_writepage_endio_finish_ordered(inode, p, start, - end, 0); + end, false); p->mapping = NULL; extent_clear_unlock_delalloc(inode, start, end, NULL, 0, @@ -3170,7 +3170,7 @@ static void finish_ordered_fn(struct btrfs_work *work) void btrfs_writepage_endio_finish_ordered(struct btrfs_inode *inode, struct page *page, u64 start, - u64 end, int uptodate) + u64 end, bool uptodate) { trace_btrfs_writepage_end_io_hook(inode, start, end, uptodate); From f41b6ba93d8ef990c4acc70987bbc138c1926ebb Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Jul 2021 14:15:10 +0200 Subject: [PATCH 290/417] btrfs: remove uptodate parameter from btrfs_dec_test_first_ordered_pending In commit e65f152e4348 ("btrfs: refactor how we finish ordered extent io for endio functions") there was last caller not using 1 for the uptodate parameter. Now there's only one, passing 1, so we can remove it and simplify the code. Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/inode.c | 2 +- fs/btrfs/ordered-data.c | 5 +---- fs/btrfs/ordered-data.h | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d41aa8e5d0cc..83caf69295f7 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8554,7 +8554,7 @@ static void btrfs_invalidatepage(struct page *page, unsigned int offset, spin_unlock_irq(&inode->ordered_tree.lock); if (btrfs_dec_test_ordered_pending(inode, &ordered, - cur, range_end + 1 - cur, 1)) { + cur, range_end + 1 - cur)) { btrfs_finish_ordered_io(ordered); /* * The ordered extent has finished, now we're again diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 5c0f8481e25e..edb65abf0393 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -446,7 +446,6 @@ void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode, * Will be also used to store the finished ordered extent. * @file_offset: File offset for the finished IO * @io_size: Length of the finish IO range - * @uptodate: If the IO finishes without problem * * Return true if the ordered extent is finished in the range, and update * @cached. @@ -457,7 +456,7 @@ void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode, */ bool btrfs_dec_test_ordered_pending(struct btrfs_inode *inode, struct btrfs_ordered_extent **cached, - u64 file_offset, u64 io_size, int uptodate) + u64 file_offset, u64 io_size) { struct btrfs_ordered_inode_tree *tree = &inode->ordered_tree; struct rb_node *node; @@ -486,8 +485,6 @@ have_entry: entry->bytes_left, io_size); entry->bytes_left -= io_size; - if (!uptodate) - set_bit(BTRFS_ORDERED_IOERR, &entry->flags); if (entry->bytes_left == 0) { /* diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index b2d88aba8420..4194e960ff61 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -177,7 +177,7 @@ void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode, bool uptodate); bool btrfs_dec_test_ordered_pending(struct btrfs_inode *inode, struct btrfs_ordered_extent **cached, - u64 file_offset, u64 io_size, int uptodate); + u64 file_offset, u64 io_size); int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset, u64 disk_bytenr, u64 num_bytes, u64 disk_num_bytes, int type); From 809d6902b3b05fd6b4494ff1460c227b99fcb4c3 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Jul 2021 14:15:12 +0200 Subject: [PATCH 291/417] btrfs: make btrfs_next_leaf static inline btrfs_next_leaf is a simple wrapper for btrfs_next_old_leaf so move it to header to avoid the function call overhead. Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 10 ---------- fs/btrfs/ctree.h | 13 ++++++++++++- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 63c026495193..99b33a5b33c8 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -4357,16 +4357,6 @@ next: return 1; } -/* - * search the tree again to find a leaf with greater keys - * returns 0 if it found something or 1 if there are no greater leaves. - * returns < 0 on io errors. - */ -int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path) -{ - return btrfs_next_old_leaf(root, path, 0); -} - int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path, u64 time_seq) { diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 194002f4e611..3cccf0f05666 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2899,7 +2899,6 @@ static inline int btrfs_insert_empty_item(struct btrfs_trans_handle *trans, return btrfs_insert_empty_items(trans, root, path, key, &data_size, 1); } -int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path); int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path); int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path, u64 time_seq); @@ -2911,6 +2910,18 @@ static inline int btrfs_next_old_item(struct btrfs_root *root, return btrfs_next_old_leaf(root, p, time_seq); return 0; } + +/* + * Search the tree again to find a leaf with greater keys. + * + * Returns 0 if it found something or 1 if there are no greater leaves. + * Returns < 0 on error. + */ +static inline int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path) +{ + return btrfs_next_old_leaf(root, path, 0); +} + static inline int btrfs_next_item(struct btrfs_root *root, struct btrfs_path *p) { return btrfs_next_old_item(root, p, 0); From 0ac6e06b6c137e18d95070fdd3c6cbd319005ffb Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Jul 2021 14:15:15 +0200 Subject: [PATCH 292/417] btrfs: tree-checker: use table values for stripe checks There are hardcoded values in several checks regarding chunks and stripe constraints. We have that defined in the raid table and ought to use it. Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/tree-checker.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index a8b2e0d2c025..ac9416cb4496 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -873,13 +873,18 @@ int btrfs_check_chunk_valid(struct extent_buffer *leaf, } } - if (unlikely((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes != 2) || - (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes != 2) || - (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) || - (type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) || - (type & BTRFS_BLOCK_GROUP_DUP && num_stripes != 2) || + if (unlikely((type & BTRFS_BLOCK_GROUP_RAID10 && + sub_stripes != btrfs_raid_array[BTRFS_RAID_RAID10].sub_stripes) || + (type & BTRFS_BLOCK_GROUP_RAID1 && + num_stripes != btrfs_raid_array[BTRFS_RAID_RAID1].devs_min) || + (type & BTRFS_BLOCK_GROUP_RAID5 && + num_stripes < btrfs_raid_array[BTRFS_RAID_RAID5].devs_min) || + (type & BTRFS_BLOCK_GROUP_RAID6 && + num_stripes < btrfs_raid_array[BTRFS_RAID_RAID6].devs_min) || + (type & BTRFS_BLOCK_GROUP_DUP && + num_stripes != btrfs_raid_array[BTRFS_RAID_DUP].dev_stripes) || ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 && - num_stripes != 1))) { + num_stripes != btrfs_raid_array[BTRFS_RAID_SINGLE].dev_stripes))) { chunk_err(leaf, chunk, logical, "invalid num_stripes:sub_stripes %u:%u for profile %llu", num_stripes, sub_stripes, From 6c154ba41bd0b925428e73571df2f80dc8d082ba Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Jul 2021 14:15:17 +0200 Subject: [PATCH 293/417] btrfs: tree-checker: add missing stripe checks for raid1c3/4 profiles The stripe checks for raid1c3/raid1c4 are missing in the sequence in btrfs_check_chunk_valid. Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/tree-checker.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index ac9416cb4496..7ba94b683ee3 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -877,6 +877,10 @@ int btrfs_check_chunk_valid(struct extent_buffer *leaf, sub_stripes != btrfs_raid_array[BTRFS_RAID_RAID10].sub_stripes) || (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes != btrfs_raid_array[BTRFS_RAID_RAID1].devs_min) || + (type & BTRFS_BLOCK_GROUP_RAID1C3 && + num_stripes != btrfs_raid_array[BTRFS_RAID_RAID1C3].devs_min) || + (type & BTRFS_BLOCK_GROUP_RAID1C4 && + num_stripes != btrfs_raid_array[BTRFS_RAID_RAID1C4].devs_min) || (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < btrfs_raid_array[BTRFS_RAID_RAID5].devs_min) || (type & BTRFS_BLOCK_GROUP_RAID6 && From 500a44c9b301ae1844e38606c4bff4f15c174fb0 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Jul 2021 14:15:19 +0200 Subject: [PATCH 294/417] btrfs: uninline btrfs_bg_flags_to_raid_index The helper does a simple translation from block group flags to index to the btrfs_raid_array table. There's no apparent reason to inline the function, the translation happens usually once per function and is not called in a loop. Making it a proper function saves quite some binary code (x86_64, release config): text data bss dec hex filename 1164011 19253 14912 1198176 124860 pre/btrfs.ko 1161559 19253 14912 1195724 123ecc post/btrfs.ko DELTA: -2451 Also add the const attribute as there are no side effects, this could help compiler to optimize a few things without the function body. Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 26 ++++++++++++++++++++++++++ fs/btrfs/volumes.h | 27 +-------------------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 01127a4dfe91..40d850a7beba 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -153,6 +153,32 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { }, }; +/* + * Convert block group flags (BTRFS_BLOCK_GROUP_*) to btrfs_raid_types, which + * can be used as index to access btrfs_raid_array[]. + */ +enum btrfs_raid_types __attribute_const__ btrfs_bg_flags_to_raid_index(u64 flags) +{ + if (flags & BTRFS_BLOCK_GROUP_RAID10) + return BTRFS_RAID_RAID10; + else if (flags & BTRFS_BLOCK_GROUP_RAID1) + return BTRFS_RAID_RAID1; + else if (flags & BTRFS_BLOCK_GROUP_RAID1C3) + return BTRFS_RAID_RAID1C3; + else if (flags & BTRFS_BLOCK_GROUP_RAID1C4) + return BTRFS_RAID_RAID1C4; + else if (flags & BTRFS_BLOCK_GROUP_DUP) + return BTRFS_RAID_DUP; + else if (flags & BTRFS_BLOCK_GROUP_RAID0) + return BTRFS_RAID_RAID0; + else if (flags & BTRFS_BLOCK_GROUP_RAID5) + return BTRFS_RAID_RAID5; + else if (flags & BTRFS_BLOCK_GROUP_RAID6) + return BTRFS_RAID_RAID6; + + return BTRFS_RAID_SINGLE; /* BTRFS_BLOCK_GROUP_SINGLE */ +} + const char *btrfs_bg_type_to_raid_name(u64 flags) { const int index = btrfs_bg_flags_to_raid_index(flags); diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 70c749eee3ad..b082250b42e0 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -566,32 +566,6 @@ static inline void btrfs_dev_stat_set(struct btrfs_device *dev, atomic_inc(&dev->dev_stats_ccnt); } -/* - * Convert block group flags (BTRFS_BLOCK_GROUP_*) to btrfs_raid_types, which - * can be used as index to access btrfs_raid_array[]. - */ -static inline enum btrfs_raid_types btrfs_bg_flags_to_raid_index(u64 flags) -{ - if (flags & BTRFS_BLOCK_GROUP_RAID10) - return BTRFS_RAID_RAID10; - else if (flags & BTRFS_BLOCK_GROUP_RAID1) - return BTRFS_RAID_RAID1; - else if (flags & BTRFS_BLOCK_GROUP_RAID1C3) - return BTRFS_RAID_RAID1C3; - else if (flags & BTRFS_BLOCK_GROUP_RAID1C4) - return BTRFS_RAID_RAID1C4; - else if (flags & BTRFS_BLOCK_GROUP_DUP) - return BTRFS_RAID_DUP; - else if (flags & BTRFS_BLOCK_GROUP_RAID0) - return BTRFS_RAID_RAID0; - else if (flags & BTRFS_BLOCK_GROUP_RAID5) - return BTRFS_RAID_RAID5; - else if (flags & BTRFS_BLOCK_GROUP_RAID6) - return BTRFS_RAID_RAID6; - - return BTRFS_RAID_SINGLE; /* BTRFS_BLOCK_GROUP_SINGLE */ -} - void btrfs_commit_device_sizes(struct btrfs_transaction *trans); struct list_head * __attribute_const__ btrfs_get_fs_uuids(void); @@ -601,6 +575,7 @@ void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info, struct block_device *bdev, const char *device_path); +enum btrfs_raid_types __attribute_const__ btrfs_bg_flags_to_raid_index(u64 flags); int btrfs_bg_type_to_factor(u64 flags); const char *btrfs_bg_type_to_raid_name(u64 flags); int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info); From fe4f46d40c1c2ff78a8a7280e455f115c32e6b41 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Jul 2021 14:15:21 +0200 Subject: [PATCH 295/417] btrfs: merge alloc_device helpers The device allocation is split to two functions, but one just calls the other and they're very far in the file. Merge them together. Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 66 ++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 40d850a7beba..23159bd29c6d 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -430,44 +430,6 @@ void __exit btrfs_cleanup_fs_uuids(void) } } -/* - * Returns a pointer to a new btrfs_device on success; ERR_PTR() on error. - * Returned struct is not linked onto any lists and must be destroyed using - * btrfs_free_device. - */ -static struct btrfs_device *__alloc_device(struct btrfs_fs_info *fs_info) -{ - struct btrfs_device *dev; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return ERR_PTR(-ENOMEM); - - /* - * Preallocate a bio that's always going to be used for flushing device - * barriers and matches the device lifespan - */ - dev->flush_bio = bio_kmalloc(GFP_KERNEL, 0); - if (!dev->flush_bio) { - kfree(dev); - return ERR_PTR(-ENOMEM); - } - - INIT_LIST_HEAD(&dev->dev_list); - INIT_LIST_HEAD(&dev->dev_alloc_list); - INIT_LIST_HEAD(&dev->post_commit_list); - - atomic_set(&dev->reada_in_flight, 0); - atomic_set(&dev->dev_stats_ccnt, 0); - btrfs_device_data_ordered_init(dev); - INIT_RADIX_TREE(&dev->reada_zones, GFP_NOFS & ~__GFP_DIRECT_RECLAIM); - INIT_RADIX_TREE(&dev->reada_extents, GFP_NOFS & ~__GFP_DIRECT_RECLAIM); - extent_io_tree_init(fs_info, &dev->alloc_state, - IO_TREE_DEVICE_ALLOC_STATE, NULL); - - return dev; -} - static noinline struct btrfs_fs_devices *find_fsid( const u8 *fsid, const u8 *metadata_fsid) { @@ -6857,9 +6819,31 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, if (WARN_ON(!devid && !fs_info)) return ERR_PTR(-EINVAL); - dev = __alloc_device(fs_info); - if (IS_ERR(dev)) - return dev; + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + /* + * Preallocate a bio that's always going to be used for flushing device + * barriers and matches the device lifespan + */ + dev->flush_bio = bio_kmalloc(GFP_KERNEL, 0); + if (!dev->flush_bio) { + kfree(dev); + return ERR_PTR(-ENOMEM); + } + + INIT_LIST_HEAD(&dev->dev_list); + INIT_LIST_HEAD(&dev->dev_alloc_list); + INIT_LIST_HEAD(&dev->post_commit_list); + + atomic_set(&dev->reada_in_flight, 0); + atomic_set(&dev->dev_stats_ccnt, 0); + btrfs_device_data_ordered_init(dev); + INIT_RADIX_TREE(&dev->reada_zones, GFP_NOFS & ~__GFP_DIRECT_RECLAIM); + INIT_RADIX_TREE(&dev->reada_extents, GFP_NOFS & ~__GFP_DIRECT_RECLAIM); + extent_io_tree_init(fs_info, &dev->alloc_state, + IO_TREE_DEVICE_ALLOC_STATE, NULL); if (devid) tmp = *devid; From d58ede8d1d9fb0f70d6aa51fa6550d2d580f8c17 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Jul 2021 14:15:24 +0200 Subject: [PATCH 296/417] btrfs: simplify data stripe calculation helpers There are two helpers doing the same calculations based on nparity and ncopies. calc_data_stripes can be simplified into one expression, so far we don't have profile with both copies and parity, so there's no effective change. calc_stripe_length should reuse the helper and not repeat the same calculation. Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 23159bd29c6d..c7339f163bda 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3568,10 +3568,7 @@ static u64 calc_data_stripes(u64 type, int num_stripes) const int ncopies = btrfs_raid_array[index].ncopies; const int nparity = btrfs_raid_array[index].nparity; - if (nparity) - return num_stripes - nparity; - else - return num_stripes / ncopies; + return (num_stripes - nparity) / ncopies; } /* [pstart, pend) */ @@ -6879,15 +6876,7 @@ static void btrfs_report_missing_device(struct btrfs_fs_info *fs_info, static u64 calc_stripe_length(u64 type, u64 chunk_len, int num_stripes) { - int index = btrfs_bg_flags_to_raid_index(type); - int ncopies = btrfs_raid_array[index].ncopies; - const int nparity = btrfs_raid_array[index].nparity; - int data_stripes; - - if (nparity) - data_stripes = num_stripes - nparity; - else - data_stripes = num_stripes / ncopies; + const int data_stripes = calc_data_stripes(type, num_stripes); return div_u64(chunk_len, data_stripes); } From 214cc184321743327c84c4a13ad08d088dfb3c4a Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Jul 2021 14:15:26 +0200 Subject: [PATCH 297/417] btrfs: constify and cleanup variables in comparators Comparators just read the data and thus get const parameters. This should be also preserved by the local variables, update all comparators passed to sort or bsearch. Cleanups: - unnecessary casts are dropped - btrfs_cmp_device_free_bytes is cleaned up to follow the common pattern and 'inline' is dropped as the function address is taken Signed-off-by: David Sterba --- fs/btrfs/raid56.c | 8 ++++---- fs/btrfs/send.c | 6 +++--- fs/btrfs/super.c | 13 ++++++------- fs/btrfs/tree-log.c | 2 +- fs/btrfs/volumes.c | 2 +- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index a40a45a007d4..d8d268ca8aa7 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -1636,10 +1636,10 @@ struct btrfs_plug_cb { static int plug_cmp(void *priv, const struct list_head *a, const struct list_head *b) { - struct btrfs_raid_bio *ra = container_of(a, struct btrfs_raid_bio, - plug_list); - struct btrfs_raid_bio *rb = container_of(b, struct btrfs_raid_bio, - plug_list); + const struct btrfs_raid_bio *ra = container_of(a, struct btrfs_raid_bio, + plug_list); + const struct btrfs_raid_bio *rb = container_of(b, struct btrfs_raid_bio, + plug_list); u64 a_sector = ra->bio_list.head->bi_iter.bi_sector; u64 b_sector = rb->bio_list.head->bi_iter.bi_sector; diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 6ac37ae6c811..75cff564dedf 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -1198,7 +1198,7 @@ struct backref_ctx { static int __clone_root_cmp_bsearch(const void *key, const void *elt) { u64 root = (u64)(uintptr_t)key; - struct clone_root *cr = (struct clone_root *)elt; + const struct clone_root *cr = elt; if (root < cr->root->root_key.objectid) return -1; @@ -1209,8 +1209,8 @@ static int __clone_root_cmp_bsearch(const void *key, const void *elt) static int __clone_root_cmp_sort(const void *e1, const void *e2) { - struct clone_root *cr1 = (struct clone_root *)e1; - struct clone_root *cr2 = (struct clone_root *)e2; + const struct clone_root *cr1 = e1; + const struct clone_root *cr2 = e2; if (cr1->root->root_key.objectid < cr2->root->root_key.objectid) return -1; diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index d07b18b2b250..35ff142ad242 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2096,16 +2096,15 @@ restore: } /* Used to sort the devices by max_avail(descending sort) */ -static inline int btrfs_cmp_device_free_bytes(const void *dev_info1, - const void *dev_info2) +static int btrfs_cmp_device_free_bytes(const void *a, const void *b) { - if (((struct btrfs_device_info *)dev_info1)->max_avail > - ((struct btrfs_device_info *)dev_info2)->max_avail) + const struct btrfs_device_info *dev_info1 = a; + const struct btrfs_device_info *dev_info2 = b; + + if (dev_info1->max_avail > dev_info2->max_avail) return -1; - else if (((struct btrfs_device_info *)dev_info1)->max_avail < - ((struct btrfs_device_info *)dev_info2)->max_avail) + else if (dev_info1->max_avail < dev_info2->max_avail) return 1; - else return 0; } diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 8dde5c08a48f..191dea1d2416 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4191,7 +4191,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, static int extent_cmp(void *priv, const struct list_head *a, const struct list_head *b) { - struct extent_map *em1, *em2; + const struct extent_map *em1, *em2; em1 = list_entry(a, struct extent_map, list); em2 = list_entry(b, struct extent_map, list); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index c7339f163bda..d42fb61aadc3 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1216,7 +1216,7 @@ static int open_fs_devices(struct btrfs_fs_devices *fs_devices, static int devid_cmp(void *priv, const struct list_head *a, const struct list_head *b) { - struct btrfs_device *dev1, *dev2; + const struct btrfs_device *dev1, *dev2; dev1 = list_entry(a, struct btrfs_device, dev_list); dev2 = list_entry(b, struct btrfs_device, dev_list); From 4c37a7938496756649096a7ec26320eb8b0d90fb Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:34:50 +0800 Subject: [PATCH 298/417] btrfs: reset this_bio_flag to avoid inheriting old flags In btrfs_do_readpage(), we never reset @this_bio_flag after we hit a compressed extent. This is fine, as for PAGE_SIZE == sectorsize case, we can only have one sector for one page, thus @this_bio_flag will only be set at most once. But for subpage case, after hitting a compressed extent, @this_bio_flag will always have EXTENT_BIO_COMPRESSED bit, even we're reading a regular extent. This will lead to various read errors, and causing new ASSERT() in incoming subpage patches, which adds more strict check in btrfs_submit_compressed_read(). Fix it by declaring @this_bio_flag inside the main loop and reset its value for each iteration. Reviewed-by: Anand Jain Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 19ba5b03b2df..4ee10669ed6b 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3487,7 +3487,6 @@ int btrfs_do_readpage(struct page *page, struct extent_map **em_cached, size_t pg_offset = 0; size_t iosize; size_t blocksize = inode->i_sb->s_blocksize; - unsigned long this_bio_flag = 0; struct extent_io_tree *tree = &BTRFS_I(inode)->io_tree; ret = set_page_extent_mapped(page); @@ -3518,6 +3517,7 @@ int btrfs_do_readpage(struct page *page, struct extent_map **em_cached, } begin_page_read(fs_info, page); while (cur <= end) { + unsigned long this_bio_flag = 0; bool force_bio_submit = false; u64 disk_bytenr; From 3670e6451bc9c39ab3a46f1da19360219e4319f3 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:34:51 +0800 Subject: [PATCH 299/417] btrfs: subpage: check if there are compressed extents inside one page [BUG] When testing experimental subpage compressed write support, it hits a NULL pointer dereference inside read path: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000018 pc : __pi_memcmp+0x28/0x1ec lr : check_data_csum+0xd0/0x274 [btrfs] Call trace: __pi_memcmp+0x28/0x1ec btrfs_verify_data_csum+0xf4/0x244 [btrfs] end_bio_extent_readpage+0x1d0/0x6b0 [btrfs] bio_endio+0x15c/0x1dc end_workqueue_fn+0x44/0x64 [btrfs] btrfs_work_helper+0x74/0x250 [btrfs] process_one_work+0x1d4/0x47c worker_thread+0x180/0x400 kthread+0x11c/0x120 ret_from_fork+0x10/0x30 Code: 54000261 d100044c d343fd8c f8408403 (f8408424) ---[ end trace 9e2c59f33ea40866 ]--- [CAUSE] When reading two compressed extents inside the same page, like the following layout, we trigger above crash: 0 32K 64K |-------|\\\\\\\| | \- Compressed extent (A) \--------- Compressed extent (B) For compressed read, we don't need to populate its io_bio->csum, as we rely on compressed_bio->csum to verify the compressed data, and then copy the decompressed to inode pages. Normally btrfs_verify_data_csum() skip such page by checking and clearing its PageChecked flag But since that flag is still for the full page, when endio for inode page range [0, 32K) gets executed, it clears PageChecked flag for the full page. Then when endio for inode page range [32K, 64K) gets executed, since the page no longer has PageChecked flag, it just continues checking, even though io_bio->csum is NULL. [FIX] Thankfully there are only two users of PageChecked bit: - Cow fixup Since subpage has its own way to trace page dirty (dirty_bitmap) and ordered bit (ordered_bitmap), it should never trigger cow fixup. - Compressed read We can distinguish such read by just checking io_bio->csum. So just check io_bio->csum before doing the verification to avoid such NULL pointer dereference. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/inode.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 83caf69295f7..ae9e4ad1949a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3256,6 +3256,20 @@ unsigned int btrfs_verify_data_csum(struct btrfs_io_bio *io_bio, u32 bio_offset, return 0; } + /* + * For subpage case, above PageChecked is not safe as it's not subpage + * compatible. + * But for now only cow fixup and compressed read utilize PageChecked + * flag, while in this context we can easily use io_bio->csum to + * determine if we really need to do csum verification. + * + * So for now, just exit if io_bio->csum is NULL, as it means it's + * compressed read, and its compressed data csum has already been + * verified. + */ + if (io_bio->csum == NULL) + return 0; + if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM) return 0; From ca62e85ded2c02f5e5d469103f2a5b6b09883dcc Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:34:52 +0800 Subject: [PATCH 300/417] btrfs: disable compressed readahead for subpage For current subpage support, we only support 64K page size with 4K sector size. This makes compressed readahead less effective, as maximum compressed extent size is only 128K, 2x the page size. On the other hand, the function add_ra_bio_pages() is still assuming sectorsize == PAGE_SIZE, and code change may affect 4K page size systems. So for now, let's disable subpage compressed readahead for now. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/compression.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index aeda426b6121..18579b904d5c 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -564,6 +564,16 @@ static noinline int add_ra_bio_pages(struct inode *inode, if (isize == 0) return 0; + /* + * For current subpage support, we only support 64K page size, + * which means maximum compressed extent size (128K) is just 2x page + * size. + * This makes readahead less effective, so here disable readahead for + * subpage for now, until full compressed write is supported. + */ + if (btrfs_sb(inode->i_sb)->sectorsize < PAGE_SIZE) + return 0; + end_index = (i_size_read(inode) - 1) >> PAGE_SHIFT; while (last_offset < compressed_end) { From 557023ea9f06baf2659b232b08b8e8711f7001a6 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 5 Jul 2021 10:00:56 +0800 Subject: [PATCH 301/417] btrfs: grab correct extent map for subpage compressed extent read [BUG] When subpage compressed read write support is enabled, btrfs/038 always fails with EIO. A simplified script can easily trigger the problem: mkfs.btrfs -f -s 4k $dev mount $dev $mnt -o compress=lzo xfs_io -f -c "truncate 118811" $mnt/foo xfs_io -c "pwrite -S 0x0d -b 39987 92267 39987" $mnt/foo > /dev/null sync btrfs subvolume snapshot -r $mnt $mnt/mysnap1 xfs_io -c "pwrite -S 0x3e -b 80000 200000 80000" $mnt/foo > /dev/null sync xfs_io -c "pwrite -S 0xdc -b 10000 250000 10000" $mnt/foo > /dev/null xfs_io -c "pwrite -S 0xff -b 10000 300000 10000" $mnt/foo > /dev/null sync btrfs subvolume snapshot -r $mnt $mnt/mysnap2 cat $mnt/mysnap2/foo # Above cat will fail due to EIO [CAUSE] The problem is in btrfs_submit_compressed_read(). When it tries to grab the extent map of the read range, it uses the following call: em = lookup_extent_mapping(em_tree, page_offset(bio_first_page_all(bio)), fs_info->sectorsize); The problem is in the page_offset(bio_first_page_all(bio)) part. The offending inode has the following file extent layout item 10 key (257 EXTENT_DATA 131072) itemoff 15639 itemsize 53 generation 8 type 1 (regular) extent data disk byte 13680640 nr 4096 extent data offset 0 nr 4096 ram 4096 extent compression 0 (none) item 11 key (257 EXTENT_DATA 135168) itemoff 15586 itemsize 53 generation 8 type 1 (regular) extent data disk byte 0 nr 0 item 12 key (257 EXTENT_DATA 196608) itemoff 15533 itemsize 53 generation 8 type 1 (regular) extent data disk byte 13676544 nr 4096 extent data offset 0 nr 53248 ram 86016 extent compression 2 (lzo) And the bio passed in has the following parameters: page_offset(bio_first_page_all(bio)) = 131072 bio_first_bvec_all(bio)->bv_offset = 65536 If we use page_offset(bio_first_page_all(bio) without adding bv_offset, we will get an extent map for file offset 131072, not 196608. This means we read uncompressed data from disk, and later decompression will definitely fail. [FIX] Take bv_offset into consideration when trying to grab an extent map. And add an ASSERT() to ensure we're really getting a compressed extent. Thankfully this won't affect anything but subpage, thus we only need to ensure this patch get merged before we enabled basic subpage support. Reviewed-by: Anand Jain Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/compression.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 18579b904d5c..41ee862470b0 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -682,6 +682,7 @@ blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, struct page *page; struct bio *comp_bio; u64 cur_disk_byte = bio->bi_iter.bi_sector << 9; + u64 file_offset; u64 em_len; u64 em_start; struct extent_map *em; @@ -691,15 +692,17 @@ blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, em_tree = &BTRFS_I(inode)->extent_tree; + file_offset = bio_first_bvec_all(bio)->bv_offset + + page_offset(bio_first_page_all(bio)); + /* we need the actual starting offset of this extent in the file */ read_lock(&em_tree->lock); - em = lookup_extent_mapping(em_tree, - page_offset(bio_first_page_all(bio)), - fs_info->sectorsize); + em = lookup_extent_mapping(em_tree, file_offset, fs_info->sectorsize); read_unlock(&em_tree->lock); if (!em) return BLK_STS_IOERR; + ASSERT(em->compress_type != BTRFS_COMPRESS_NONE); compressed_len = em->block_len; cb = kmalloc(compressed_bio_size(fs_info, compressed_len), GFP_NOFS); if (!cb) From 1c3dc1731ed2b3757b25533c5245926ffc94f7dc Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 5 Jul 2021 10:00:58 +0800 Subject: [PATCH 302/417] btrfs: rework btrfs_decompress_buf2page() There are several bugs inside the function btrfs_decompress_buf2page() - @start_byte doesn't take bvec.bv_offset into consideration Thus it can't handle case where the target range is not page aligned. - Too many helper variables There are tons of helper variables, @buf_offset, @current_buf_start, @start_byte, @prev_start_byte, @working_bytes, @bytes. This hurts anyone who wants to read the function. - No obvious main cursor for the iteartion A new problem caused by previous problem. - Comments for parameter list makes no sense Like @buf_start is the offset to @buf, or offset inside the full decompressed extent? (Spoiler alert, the later case) And @total_out acts more like @buf_start + @size_of_buf. The worst is @disk_start. The real meaning of it is the file offset of the full decompressed extent. This patch will rework the whole function by: - Add a proper comment with ASCII art to explain the parameter list - Rework parameter list The old @buf_start is renamed to @decompressed, to show how many bytes are already decompressed inside the full decompressed extent. The old @total_out is replaced by @buf_len, which is the decompressed data size. For old @disk_start and @bio, just pass @compressed_bio in. - Use single main cursor The main cursor will be @cur_file_offset, to show what's the current file offset. Other helper variables will be declared inside the main loop, and only minimal amount of helper variables: * offset_inside_decompressed_buf: The only real helper * copy_start_file_offset: File offset we start memcpy * bvec_file_offset: File offset of current bvec Even with all these extensive comments, the final function is still smaller than the original function, which is definitely a win. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/compression.c | 140 +++++++++++++++++++---------------------- fs/btrfs/compression.h | 5 +- fs/btrfs/lzo.c | 8 +-- fs/btrfs/zlib.c | 12 ++-- fs/btrfs/zstd.c | 6 +- 5 files changed, 74 insertions(+), 97 deletions(-) diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 41ee862470b0..7869ad12bc6e 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -1272,96 +1272,82 @@ void __cold btrfs_exit_compress(void) } /* - * Copy uncompressed data from working buffer to pages. + * Copy decompressed data from working buffer to pages. * - * buf_start is the byte offset we're of the start of our workspace buffer. + * @buf: The decompressed data buffer + * @buf_len: The decompressed data length + * @decompressed: Number of bytes that are already decompressed inside the + * compressed extent + * @cb: The compressed extent descriptor + * @orig_bio: The original bio that the caller wants to read for * - * total_out is the last byte of the buffer + * An easier to understand graph is like below: + * + * |<- orig_bio ->| |<- orig_bio->| + * |<------- full decompressed extent ----->| + * |<----------- @cb range ---->| + * | |<-- @buf_len -->| + * |<--- @decompressed --->| + * + * Note that, @cb can be a subpage of the full decompressed extent, but + * @cb->start always has the same as the orig_file_offset value of the full + * decompressed extent. + * + * When reading compressed extent, we have to read the full compressed extent, + * while @orig_bio may only want part of the range. + * Thus this function will ensure only data covered by @orig_bio will be copied + * to. + * + * Return 0 if we have copied all needed contents for @orig_bio. + * Return >0 if we need continue decompress. */ -int btrfs_decompress_buf2page(const char *buf, unsigned long buf_start, - unsigned long total_out, u64 disk_start, - struct bio *bio) +int btrfs_decompress_buf2page(const char *buf, u32 buf_len, + struct compressed_bio *cb, u32 decompressed) { - unsigned long buf_offset; - unsigned long current_buf_start; - unsigned long start_byte; - unsigned long prev_start_byte; - unsigned long working_bytes = total_out - buf_start; - unsigned long bytes; - struct bio_vec bvec = bio_iter_iovec(bio, bio->bi_iter); + struct bio *orig_bio = cb->orig_bio; + /* Offset inside the full decompressed extent */ + u32 cur_offset; - /* - * start byte is the first byte of the page we're currently - * copying into relative to the start of the compressed data. - */ - start_byte = page_offset(bvec.bv_page) - disk_start; + cur_offset = decompressed; + /* The main loop to do the copy */ + while (cur_offset < decompressed + buf_len) { + struct bio_vec bvec; + size_t copy_len; + u32 copy_start; + /* Offset inside the full decompressed extent */ + u32 bvec_offset; - /* we haven't yet hit data corresponding to this page */ - if (total_out <= start_byte) - return 1; + bvec = bio_iter_iovec(orig_bio, orig_bio->bi_iter); + /* + * cb->start may underflow, but subtracting that value can still + * give us correct offset inside the full decompressed extent. + */ + bvec_offset = page_offset(bvec.bv_page) + bvec.bv_offset - cb->start; - /* - * the start of the data we care about is offset into - * the middle of our working buffer - */ - if (total_out > start_byte && buf_start < start_byte) { - buf_offset = start_byte - buf_start; - working_bytes -= buf_offset; - } else { - buf_offset = 0; - } - current_buf_start = buf_start; + /* Haven't reached the bvec range, exit */ + if (decompressed + buf_len <= bvec_offset) + return 1; - /* copy bytes from the working buffer into the pages */ - while (working_bytes > 0) { - bytes = min_t(unsigned long, bvec.bv_len, - PAGE_SIZE - (buf_offset % PAGE_SIZE)); - bytes = min(bytes, working_bytes); - - memcpy_to_page(bvec.bv_page, bvec.bv_offset, buf + buf_offset, - bytes); - flush_dcache_page(bvec.bv_page); - - buf_offset += bytes; - working_bytes -= bytes; - current_buf_start += bytes; - - /* check if we need to pick another page */ - bio_advance(bio, bytes); - if (!bio->bi_iter.bi_size) - return 0; - bvec = bio_iter_iovec(bio, bio->bi_iter); - prev_start_byte = start_byte; - start_byte = page_offset(bvec.bv_page) - disk_start; + copy_start = max(cur_offset, bvec_offset); + copy_len = min(bvec_offset + bvec.bv_len, + decompressed + buf_len) - copy_start; + ASSERT(copy_len); /* - * We need to make sure we're only adjusting - * our offset into compression working buffer when - * we're switching pages. Otherwise we can incorrectly - * keep copying when we were actually done. + * Extra range check to ensure we didn't go beyond + * @buf + @buf_len. */ - if (start_byte != prev_start_byte) { - /* - * make sure our new page is covered by this - * working buffer - */ - if (total_out <= start_byte) - return 1; + ASSERT(copy_start - decompressed < buf_len); + memcpy_to_page(bvec.bv_page, bvec.bv_offset, + buf + copy_start - decompressed, copy_len); + flush_dcache_page(bvec.bv_page); + cur_offset += copy_len; - /* - * the next page in the biovec might not be adjacent - * to the last page, but it might still be found - * inside this working buffer. bump our offset pointer - */ - if (total_out > start_byte && - current_buf_start < start_byte) { - buf_offset = start_byte - buf_start; - working_bytes = total_out - start_byte; - current_buf_start = buf_start + buf_offset; - } - } + bio_advance(orig_bio, copy_len); + /* Finished the bio */ + if (!orig_bio->bi_iter.bi_size) + return 0; } - return 1; } diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h index c359f20920d0..399be0b435bf 100644 --- a/fs/btrfs/compression.h +++ b/fs/btrfs/compression.h @@ -86,9 +86,8 @@ int btrfs_compress_pages(unsigned int type_level, struct address_space *mapping, unsigned long *total_out); int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page, unsigned long start_byte, size_t srclen, size_t destlen); -int btrfs_decompress_buf2page(const char *buf, unsigned long buf_start, - unsigned long total_out, u64 disk_start, - struct bio *bio); +int btrfs_decompress_buf2page(const char *buf, u32 buf_len, + struct compressed_bio *cb, u32 decompressed); blk_status_t btrfs_submit_compressed_write(struct btrfs_inode *inode, u64 start, unsigned int len, u64 disk_start, diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 576a0e6142ad..6cab15e52cec 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -293,8 +293,6 @@ int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) unsigned long tot_len; char *buf; struct page **pages_in = cb->compressed_pages; - u64 disk_start = cb->start; - struct bio *orig_bio = cb->orig_bio; data_in = page_address(pages_in[0]); tot_len = read_compress_length(data_in); @@ -391,14 +389,14 @@ cont: buf_start = tot_out; tot_out += out_len; - ret2 = btrfs_decompress_buf2page(workspace->buf, buf_start, - tot_out, disk_start, orig_bio); + ret2 = btrfs_decompress_buf2page(workspace->buf, out_len, + cb, buf_start); if (ret2 == 0) break; } done: if (!ret) - zero_fill_bio(orig_bio); + zero_fill_bio(cb->orig_bio); return ret; } diff --git a/fs/btrfs/zlib.c b/fs/btrfs/zlib.c index 5e18d7ad75a4..8afa90074891 100644 --- a/fs/btrfs/zlib.c +++ b/fs/btrfs/zlib.c @@ -275,8 +275,6 @@ int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_SIZE); unsigned long buf_start; struct page **pages_in = cb->compressed_pages; - u64 disk_start = cb->start; - struct bio *orig_bio = cb->orig_bio; data_in = page_address(pages_in[page_in_index]); workspace->strm.next_in = data_in; @@ -314,9 +312,8 @@ int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) if (buf_start == total_out) break; - ret2 = btrfs_decompress_buf2page(workspace->buf, buf_start, - total_out, disk_start, - orig_bio); + ret2 = btrfs_decompress_buf2page(workspace->buf, + total_out - buf_start, cb, buf_start); if (ret2 == 0) { ret = 0; goto done; @@ -336,8 +333,7 @@ int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) data_in = page_address(pages_in[page_in_index]); workspace->strm.next_in = data_in; tmp = srclen - workspace->strm.total_in; - workspace->strm.avail_in = min(tmp, - PAGE_SIZE); + workspace->strm.avail_in = min(tmp, PAGE_SIZE); } } if (ret != Z_STREAM_END) @@ -347,7 +343,7 @@ int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) done: zlib_inflateEnd(&workspace->strm); if (!ret) - zero_fill_bio(orig_bio); + zero_fill_bio(cb->orig_bio); return ret; } diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c index 200ba08bfae6..56dce9f00988 100644 --- a/fs/btrfs/zstd.c +++ b/fs/btrfs/zstd.c @@ -540,8 +540,6 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) { struct workspace *workspace = list_entry(ws, struct workspace, list); struct page **pages_in = cb->compressed_pages; - u64 disk_start = cb->start; - struct bio *orig_bio = cb->orig_bio; size_t srclen = cb->compressed_len; ZSTD_DStream *stream; int ret = 0; @@ -582,7 +580,7 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) workspace->out_buf.pos = 0; ret = btrfs_decompress_buf2page(workspace->out_buf.dst, - buf_start, total_out, disk_start, orig_bio); + total_out - buf_start, cb, buf_start); if (ret == 0) break; @@ -607,7 +605,7 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) } } ret = 0; - zero_fill_bio(orig_bio); + zero_fill_bio(cb->orig_bio); done: return ret; } From a6e66e6f8c1b685e11b778bef614480a9c1a5278 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:34:55 +0800 Subject: [PATCH 303/417] btrfs: rework lzo_decompress_bio() to make it subpage compatible For the initial subpage support, although we won't support compressed write, we still need to support compressed read. But for lzo_decompress_bio() it has several problems: - The abuse of PAGE_SIZE for boundary detection For subpage case, we should follow sectorsize to detect the padding zeros. Using PAGE_SIZE will cause subpage compress read to skip certain bytes, and causing read error. - Too many helper variables There are half a dozen helper variables, which is only making things harder to read This patch will rework lzo_decompress_bio() to make it work for subpage: - Use sectorsize to do boundary check, while still use PAGE_SIZE for page switching This allows us to have the same on-disk format for 4K sectorsize fs, while take advantage of larger page size. - Use two main cursors Only @cur_in and @cur_out is utilized as the main cursor. The helper variables will only be declared inside the loop, and only 2 helper variables needed. - Introduce a helper function to copy compressed segment payload Introduce a new helper, copy_compressed_segment(), to copy a compressed segment to workspace buffer. This function will handle the page switching. Now the net result is, with all the excessive comments and new helper function, the refactored code is still smaller, and easier to read. For other decompression code, they have no special padding rule, thus no need to bother for initial subpage support, but will be refactored to the same style later. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/lzo.c | 192 +++++++++++++++++++++++-------------------------- 1 file changed, 88 insertions(+), 104 deletions(-) diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 6cab15e52cec..c25dfd1a8a54 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -14,6 +14,7 @@ #include #include #include "compression.h" +#include "ctree.h" #define LZO_LEN 4 @@ -271,130 +272,113 @@ out: return ret; } +/* + * Copy the compressed segment payload into @dest. + * + * For the payload there will be no padding, just need to do page switching. + */ +static void copy_compressed_segment(struct compressed_bio *cb, + char *dest, u32 len, u32 *cur_in) +{ + u32 orig_in = *cur_in; + + while (*cur_in < orig_in + len) { + struct page *cur_page; + u32 copy_len = min_t(u32, PAGE_SIZE - offset_in_page(*cur_in), + orig_in + len - *cur_in); + + ASSERT(copy_len); + cur_page = cb->compressed_pages[*cur_in / PAGE_SIZE]; + + memcpy(dest + *cur_in - orig_in, + page_address(cur_page) + offset_in_page(*cur_in), + copy_len); + + *cur_in += copy_len; + } +} + int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) { struct workspace *workspace = list_entry(ws, struct workspace, list); - int ret = 0, ret2; - char *data_in; - unsigned long page_in_index = 0; - size_t srclen = cb->compressed_len; - unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_SIZE); - unsigned long buf_start; - unsigned long buf_offset = 0; - unsigned long bytes; - unsigned long working_bytes; - size_t in_len; - size_t out_len; - const size_t max_segment_len = lzo1x_worst_compress(PAGE_SIZE); - unsigned long in_offset; - unsigned long in_page_bytes_left; - unsigned long tot_in; - unsigned long tot_out; - unsigned long tot_len; - char *buf; - struct page **pages_in = cb->compressed_pages; + const struct btrfs_fs_info *fs_info = btrfs_sb(cb->inode->i_sb); + const u32 sectorsize = fs_info->sectorsize; + int ret; + /* Compressed data length, can be unaligned */ + u32 len_in; + /* Offset inside the compressed data */ + u32 cur_in = 0; + /* Bytes decompressed so far */ + u32 cur_out = 0; + + len_in = read_compress_length(page_address(cb->compressed_pages[0])); + cur_in += LZO_LEN; - data_in = page_address(pages_in[0]); - tot_len = read_compress_length(data_in); /* - * Compressed data header check. + * LZO header length check * - * The real compressed size can't exceed the maximum extent length, and - * all pages should be used (whole unused page with just the segment - * header is not possible). If this happens it means the compressed - * extent is corrupted. + * The total length should not exceed the maximum extent length, + * and all sectors should be used. + * If this happens, it means the compressed extent is corrupted. */ - if (tot_len > min_t(size_t, BTRFS_MAX_COMPRESSED, srclen) || - tot_len < srclen - PAGE_SIZE) { - ret = -EUCLEAN; - goto done; + if (len_in > min_t(size_t, BTRFS_MAX_COMPRESSED, cb->compressed_len) || + round_up(len_in, sectorsize) < cb->compressed_len) { + btrfs_err(fs_info, + "invalid lzo header, lzo len %u compressed len %u", + len_in, cb->compressed_len); + return -EUCLEAN; } - tot_in = LZO_LEN; - in_offset = LZO_LEN; - in_page_bytes_left = PAGE_SIZE - LZO_LEN; - - tot_out = 0; - - while (tot_in < tot_len) { - in_len = read_compress_length(data_in + in_offset); - in_page_bytes_left -= LZO_LEN; - in_offset += LZO_LEN; - tot_in += LZO_LEN; + /* Go through each lzo segment */ + while (cur_in < len_in) { + struct page *cur_page; + /* Length of the compressed segment */ + u32 seg_len; + u32 sector_bytes_left; + size_t out_len = lzo1x_worst_compress(sectorsize); /* - * Segment header check. - * - * The segment length must not exceed the maximum LZO - * compression size, nor the total compressed size. + * We should always have enough space for one segment header + * inside current sector. */ - if (in_len > max_segment_len || tot_in + in_len > tot_len) { - ret = -EUCLEAN; - goto done; - } + ASSERT(cur_in / sectorsize == + (cur_in + LZO_LEN - 1) / sectorsize); + cur_page = cb->compressed_pages[cur_in / PAGE_SIZE]; + ASSERT(cur_page); + seg_len = read_compress_length(page_address(cur_page) + + offset_in_page(cur_in)); + cur_in += LZO_LEN; - tot_in += in_len; - working_bytes = in_len; + /* Copy the compressed segment payload into workspace */ + copy_compressed_segment(cb, workspace->cbuf, seg_len, &cur_in); - /* fast path: avoid using the working buffer */ - if (in_page_bytes_left >= in_len) { - buf = data_in + in_offset; - bytes = in_len; - goto cont; - } - - /* copy bytes from the pages into the working buffer */ - buf = workspace->cbuf; - buf_offset = 0; - while (working_bytes) { - bytes = min(working_bytes, in_page_bytes_left); - - memcpy(buf + buf_offset, data_in + in_offset, bytes); - buf_offset += bytes; -cont: - working_bytes -= bytes; - in_page_bytes_left -= bytes; - in_offset += bytes; - - /* check if we need to pick another page */ - if ((working_bytes == 0 && in_page_bytes_left < LZO_LEN) - || in_page_bytes_left == 0) { - tot_in += in_page_bytes_left; - - if (working_bytes == 0 && tot_in >= tot_len) - break; - - if (page_in_index + 1 >= total_pages_in) { - ret = -EIO; - goto done; - } - - page_in_index++; - data_in = page_address(pages_in[page_in_index]); - - in_page_bytes_left = PAGE_SIZE; - in_offset = 0; - } - } - - out_len = max_segment_len; - ret = lzo1x_decompress_safe(buf, in_len, workspace->buf, - &out_len); + /* Decompress the data */ + ret = lzo1x_decompress_safe(workspace->cbuf, seg_len, + workspace->buf, &out_len); if (ret != LZO_E_OK) { - pr_warn("BTRFS: decompress failed\n"); + btrfs_err(fs_info, "failed to decompress"); ret = -EIO; - break; + goto out; } - buf_start = tot_out; - tot_out += out_len; + /* Copy the data into inode pages */ + ret = btrfs_decompress_buf2page(workspace->buf, out_len, cb, cur_out); + cur_out += out_len; - ret2 = btrfs_decompress_buf2page(workspace->buf, out_len, - cb, buf_start); - if (ret2 == 0) - break; + /* All data read, exit */ + if (ret == 0) + goto out; + ret = 0; + + /* Check if the sector has enough space for a segment header */ + sector_bytes_left = sectorsize - (cur_in % sectorsize); + if (sector_bytes_left >= LZO_LEN) + continue; + + /* Skip the padding zeros */ + cur_in += sector_bytes_left; } -done: +out: if (!ret) zero_fill_bio(cb->orig_bio); return ret; From f47960f49e59b9d77bd2919c3513dbbe088c3908 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:34:56 +0800 Subject: [PATCH 304/417] btrfs: reloc: factor out relocation page read and dirty part In function relocate_file_extent_cluster(), we have a big loop for marking all involved page delalloc. That part is long enough to be contained in one function, so this patch will move that code chunk into a new function, relocate_one_page(). This also provides enough space for later subpage work. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/relocation.c | 198 ++++++++++++++++++++---------------------- 1 file changed, 93 insertions(+), 105 deletions(-) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index fc831597cb22..e28c3b936937 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -2886,19 +2886,102 @@ noinline int btrfs_should_cancel_balance(struct btrfs_fs_info *fs_info) } ALLOW_ERROR_INJECTION(btrfs_should_cancel_balance, TRUE); +static int relocate_one_page(struct inode *inode, struct file_ra_state *ra, + struct file_extent_cluster *cluster, + int *cluster_nr, unsigned long page_index) +{ + struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); + u64 offset = BTRFS_I(inode)->index_cnt; + const unsigned long last_index = (cluster->end - offset) >> PAGE_SHIFT; + gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping); + struct page *page; + u64 page_start; + u64 page_end; + int ret; + + ASSERT(page_index <= last_index); + ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode), PAGE_SIZE); + if (ret) + return ret; + + page = find_lock_page(inode->i_mapping, page_index); + if (!page) { + page_cache_sync_readahead(inode->i_mapping, ra, NULL, + page_index, last_index + 1 - page_index); + page = find_or_create_page(inode->i_mapping, page_index, mask); + if (!page) { + ret = -ENOMEM; + goto release_delalloc; + } + } + ret = set_page_extent_mapped(page); + if (ret < 0) + goto release_page; + + if (PageReadahead(page)) + page_cache_async_readahead(inode->i_mapping, ra, NULL, page, + page_index, last_index + 1 - page_index); + + if (!PageUptodate(page)) { + btrfs_readpage(NULL, page); + lock_page(page); + if (!PageUptodate(page)) { + ret = -EIO; + goto release_page; + } + } + + page_start = page_offset(page); + page_end = page_start + PAGE_SIZE - 1; + + lock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end); + + if (*cluster_nr < cluster->nr && + page_start + offset == cluster->boundary[*cluster_nr]) { + set_extent_bits(&BTRFS_I(inode)->io_tree, page_start, page_end, + EXTENT_BOUNDARY); + (*cluster_nr)++; + } + + ret = btrfs_set_extent_delalloc(BTRFS_I(inode), page_start, page_end, + 0, NULL); + if (ret) { + clear_extent_bits(&BTRFS_I(inode)->io_tree, page_start, + page_end, EXTENT_LOCKED | EXTENT_BOUNDARY); + goto release_page; + + } + set_page_dirty(page); + + unlock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end); + unlock_page(page); + put_page(page); + + btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE); + balance_dirty_pages_ratelimited(inode->i_mapping); + btrfs_throttle(fs_info); + if (btrfs_should_cancel_balance(fs_info)) + ret = -ECANCELED; + return ret; + +release_page: + unlock_page(page); + put_page(page); +release_delalloc: + btrfs_delalloc_release_metadata(BTRFS_I(inode), PAGE_SIZE, true); + btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE); + return ret; +} + static int relocate_file_extent_cluster(struct inode *inode, struct file_extent_cluster *cluster) { struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); - u64 page_start; - u64 page_end; u64 offset = BTRFS_I(inode)->index_cnt; unsigned long index; unsigned long last_index; - struct page *page; struct file_ra_state *ra; - gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping); - int nr = 0; + int cluster_nr = 0; int ret = 0; if (!cluster->nr) @@ -2919,109 +3002,14 @@ static int relocate_file_extent_cluster(struct inode *inode, if (ret) goto out; - index = (cluster->start - offset) >> PAGE_SHIFT; last_index = (cluster->end - offset) >> PAGE_SHIFT; - while (index <= last_index) { - ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode), - PAGE_SIZE); - if (ret) - goto out; - - page = find_lock_page(inode->i_mapping, index); - if (!page) { - page_cache_sync_readahead(inode->i_mapping, - ra, NULL, index, - last_index + 1 - index); - page = find_or_create_page(inode->i_mapping, index, - mask); - if (!page) { - btrfs_delalloc_release_metadata(BTRFS_I(inode), - PAGE_SIZE, true); - btrfs_delalloc_release_extents(BTRFS_I(inode), - PAGE_SIZE); - ret = -ENOMEM; - goto out; - } - } - ret = set_page_extent_mapped(page); - if (ret < 0) { - btrfs_delalloc_release_metadata(BTRFS_I(inode), - PAGE_SIZE, true); - btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE); - unlock_page(page); - put_page(page); - goto out; - } - - if (PageReadahead(page)) { - page_cache_async_readahead(inode->i_mapping, - ra, NULL, page, index, - last_index + 1 - index); - } - - if (!PageUptodate(page)) { - btrfs_readpage(NULL, page); - lock_page(page); - if (!PageUptodate(page)) { - unlock_page(page); - put_page(page); - btrfs_delalloc_release_metadata(BTRFS_I(inode), - PAGE_SIZE, true); - btrfs_delalloc_release_extents(BTRFS_I(inode), - PAGE_SIZE); - ret = -EIO; - goto out; - } - } - - page_start = page_offset(page); - page_end = page_start + PAGE_SIZE - 1; - - lock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end); - - if (nr < cluster->nr && - page_start + offset == cluster->boundary[nr]) { - set_extent_bits(&BTRFS_I(inode)->io_tree, - page_start, page_end, - EXTENT_BOUNDARY); - nr++; - } - - ret = btrfs_set_extent_delalloc(BTRFS_I(inode), page_start, - page_end, 0, NULL); - if (ret) { - unlock_page(page); - put_page(page); - btrfs_delalloc_release_metadata(BTRFS_I(inode), - PAGE_SIZE, true); - btrfs_delalloc_release_extents(BTRFS_I(inode), - PAGE_SIZE); - - clear_extent_bits(&BTRFS_I(inode)->io_tree, - page_start, page_end, - EXTENT_LOCKED | EXTENT_BOUNDARY); - goto out; - - } - set_page_dirty(page); - - unlock_extent(&BTRFS_I(inode)->io_tree, - page_start, page_end); - unlock_page(page); - put_page(page); - - index++; - btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE); - balance_dirty_pages_ratelimited(inode->i_mapping); - btrfs_throttle(fs_info); - if (btrfs_should_cancel_balance(fs_info)) { - ret = -ECANCELED; - goto out; - } - } - WARN_ON(nr != cluster->nr); + for (index = (cluster->start - offset) >> PAGE_SHIFT; + index <= last_index && !ret; index++) + ret = relocate_one_page(inode, ra, cluster, &cluster_nr, index); if (btrfs_is_zoned(fs_info) && !ret) ret = btrfs_wait_ordered_range(inode, 0, (u64)-1); + if (ret == 0) + WARN_ON(cluster_nr != cluster->nr); out: kfree(ra); return ret; From c2832898126fc320a0e2915b07bf8924cf54770e Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:34:57 +0800 Subject: [PATCH 305/417] btrfs: make relocate_one_page() handle subpage case For subpage case, one page of data reloc inode can contain several file extents, like this: |<--- File extent A --->| FE B | FE C |<--- File extent D -->| |<--------- Page --------->| We can no longer use PAGE_SIZE directly for various operations. This patch will relocate_one_page() to handle subpage case by: - Iterating through all extents of a cluster when marking pages When marking pages dirty and delalloc, we need to check the cluster extent boundary. Now we introduce a loop to go extent by extent of a page, until we either finished the last extent, or reach the page end. By this, regular sectorsize == PAGE_SIZE can still work as usual, since we will do that loop only once. - Iteration start from max(page_start, extent_start) Since we can have the following case: | FE B | FE C |<--- File extent D -->| |<--------- Page --------->| Thus we can't always start from page_start, but do a max(page_start, extent_start) - Iteration end when the cluster is exhausted Similar to previous case, the last file extent can end before the page end: |<--- File extent A --->| FE B | FE C | |<--------- Page --------->| In this case, we need to manually exit the loop after we have finished the last extent of the cluster. - Reserve metadata space for each extent range Since now we can hit multiple ranges in one page, we should reserve metadata for each range, not simply PAGE_SIZE. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/relocation.c | 108 ++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 30 deletions(-) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index e28c3b936937..9c8cea5cabe4 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -24,6 +24,7 @@ #include "block-group.h" #include "backref.h" #include "misc.h" +#include "subpage.h" /* * Relocation overview @@ -2886,6 +2887,17 @@ noinline int btrfs_should_cancel_balance(struct btrfs_fs_info *fs_info) } ALLOW_ERROR_INJECTION(btrfs_should_cancel_balance, TRUE); +static u64 get_cluster_boundary_end(struct file_extent_cluster *cluster, + int cluster_nr) +{ + /* Last extent, use cluster end directly */ + if (cluster_nr >= cluster->nr - 1) + return cluster->end; + + /* Use next boundary start*/ + return cluster->boundary[cluster_nr + 1] - 1; +} + static int relocate_one_page(struct inode *inode, struct file_ra_state *ra, struct file_extent_cluster *cluster, int *cluster_nr, unsigned long page_index) @@ -2897,22 +2909,17 @@ static int relocate_one_page(struct inode *inode, struct file_ra_state *ra, struct page *page; u64 page_start; u64 page_end; + u64 cur; int ret; ASSERT(page_index <= last_index); - ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode), PAGE_SIZE); - if (ret) - return ret; - page = find_lock_page(inode->i_mapping, page_index); if (!page) { page_cache_sync_readahead(inode->i_mapping, ra, NULL, page_index, last_index + 1 - page_index); page = find_or_create_page(inode->i_mapping, page_index, mask); - if (!page) { - ret = -ENOMEM; - goto release_delalloc; - } + if (!page) + return -ENOMEM; } ret = set_page_extent_mapped(page); if (ret < 0) @@ -2934,30 +2941,74 @@ static int relocate_one_page(struct inode *inode, struct file_ra_state *ra, page_start = page_offset(page); page_end = page_start + PAGE_SIZE - 1; - lock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end); + /* + * Start from the cluster, as for subpage case, the cluster can start + * inside the page. + */ + cur = max(page_start, cluster->boundary[*cluster_nr] - offset); + while (cur <= page_end) { + u64 extent_start = cluster->boundary[*cluster_nr] - offset; + u64 extent_end = get_cluster_boundary_end(cluster, + *cluster_nr) - offset; + u64 clamped_start = max(page_start, extent_start); + u64 clamped_end = min(page_end, extent_end); + u32 clamped_len = clamped_end + 1 - clamped_start; - if (*cluster_nr < cluster->nr && - page_start + offset == cluster->boundary[*cluster_nr]) { - set_extent_bits(&BTRFS_I(inode)->io_tree, page_start, page_end, - EXTENT_BOUNDARY); - (*cluster_nr)++; + /* Reserve metadata for this range */ + ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode), + clamped_len); + if (ret) + goto release_page; + + /* Mark the range delalloc and dirty for later writeback */ + lock_extent(&BTRFS_I(inode)->io_tree, clamped_start, clamped_end); + ret = btrfs_set_extent_delalloc(BTRFS_I(inode), clamped_start, + clamped_end, 0, NULL); + if (ret) { + clear_extent_bits(&BTRFS_I(inode)->io_tree, + clamped_start, clamped_end, + EXTENT_LOCKED | EXTENT_BOUNDARY); + btrfs_delalloc_release_metadata(BTRFS_I(inode), + clamped_len, true); + btrfs_delalloc_release_extents(BTRFS_I(inode), + clamped_len); + goto release_page; + } + btrfs_page_set_dirty(fs_info, page, clamped_start, clamped_len); + + /* + * Set the boundary if it's inside the page. + * Data relocation requires the destination extents to have the + * same size as the source. + * EXTENT_BOUNDARY bit prevents current extent from being merged + * with previous extent. + */ + if (in_range(cluster->boundary[*cluster_nr] - offset, + page_start, PAGE_SIZE)) { + u64 boundary_start = cluster->boundary[*cluster_nr] - + offset; + u64 boundary_end = boundary_start + + fs_info->sectorsize - 1; + + set_extent_bits(&BTRFS_I(inode)->io_tree, + boundary_start, boundary_end, + EXTENT_BOUNDARY); + } + unlock_extent(&BTRFS_I(inode)->io_tree, clamped_start, clamped_end); + btrfs_delalloc_release_extents(BTRFS_I(inode), clamped_len); + cur += clamped_len; + + /* Crossed extent end, go to next extent */ + if (cur >= extent_end) { + (*cluster_nr)++; + /* Just finished the last extent of the cluster, exit. */ + if (*cluster_nr >= cluster->nr) + break; + } } - - ret = btrfs_set_extent_delalloc(BTRFS_I(inode), page_start, page_end, - 0, NULL); - if (ret) { - clear_extent_bits(&BTRFS_I(inode)->io_tree, page_start, - page_end, EXTENT_LOCKED | EXTENT_BOUNDARY); - goto release_page; - - } - set_page_dirty(page); - - unlock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end); unlock_page(page); put_page(page); - btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE); balance_dirty_pages_ratelimited(inode->i_mapping); btrfs_throttle(fs_info); if (btrfs_should_cancel_balance(fs_info)) @@ -2967,9 +3018,6 @@ static int relocate_one_page(struct inode *inode, struct file_ra_state *ra, release_page: unlock_page(page); put_page(page); -release_delalloc: - btrfs_delalloc_release_metadata(BTRFS_I(inode), PAGE_SIZE, true); - btrfs_delalloc_release_extents(BTRFS_I(inode), PAGE_SIZE); return ret; } From cc1d0d93d55ac12e7faee9acfcd7c28c8b86cf89 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:34:58 +0800 Subject: [PATCH 306/417] btrfs: subpage: fix writeback which does not have ordered extent [BUG] When running fsstress with subpage RW support, there are random BUG_ON()s triggered with the following trace: kernel BUG at fs/btrfs/file-item.c:667! Internal error: Oops - BUG: 0 [#1] SMP CPU: 1 PID: 3486 Comm: kworker/u13:2 5.11.0-rc4-custom+ #43 Hardware name: Radxa ROCK Pi 4B (DT) Workqueue: btrfs-worker-high btrfs_work_helper [btrfs] pstate: 60000005 (nZCv daif -PAN -UAO -TCO BTYPE=--) pc : btrfs_csum_one_bio+0x420/0x4e0 [btrfs] lr : btrfs_csum_one_bio+0x400/0x4e0 [btrfs] Call trace: btrfs_csum_one_bio+0x420/0x4e0 [btrfs] btrfs_submit_bio_start+0x20/0x30 [btrfs] run_one_async_start+0x28/0x44 [btrfs] btrfs_work_helper+0x128/0x1b4 [btrfs] process_one_work+0x22c/0x430 worker_thread+0x70/0x3a0 kthread+0x13c/0x140 ret_from_fork+0x10/0x30 [CAUSE] Above BUG_ON() means there is some bio range which doesn't have ordered extent, which indeed is worth a BUG_ON(). Unlike regular sectorsize == PAGE_SIZE case, in subpage we have extra subpage dirty bitmap to record which range is dirty and should be written back. This means, if we submit bio for a subpage range, we do not only need to clear page dirty, but also need to clear subpage dirty bits. In __extent_writepage_io(), we will call btrfs_page_clear_dirty() for any range we submit a bio. But there is loophole, if we hit a range which is beyond i_size, we just call btrfs_writepage_endio_finish_ordered() to finish the ordered io, then break out, without clearing the subpage dirty. This means, if we hit above branch, the subpage dirty bits are still there, if other range of the page get dirtied and we need to writeback that page again, we will submit bio for the old range, leaving a wild bio range which doesn't have ordered extent. [FIX] Fix it by always calling btrfs_page_clear_dirty() in __extent_writepage_io(). Also to avoid such problem from happening again, add a new assert, btrfs_page_assert_not_dirty(), to make sure both page dirty and subpage dirty bits are cleared before exiting __extent_writepage_io(). Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 16 ++++++++++++++++ fs/btrfs/subpage.c | 20 ++++++++++++++++++++ fs/btrfs/subpage.h | 3 +++ 3 files changed, 39 insertions(+) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 4ee10669ed6b..e1a464f6b42f 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3864,6 +3864,15 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode, if (cur >= i_size) { btrfs_writepage_endio_finish_ordered(inode, page, cur, end, true); + /* + * This range is beyond i_size, thus we don't need to + * bother writing back. + * But we still need to clear the dirty subpage bit, or + * the next time the page gets dirtied, we will try to + * writeback the sectors with subpage dirty bits, + * causing writeback without ordered extent. + */ + btrfs_page_clear_dirty(fs_info, page, cur, end + 1 - cur); break; } @@ -3914,6 +3923,7 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode, else btrfs_writepage_endio_finish_ordered(inode, page, cur, cur + iosize - 1, true); + btrfs_page_clear_dirty(fs_info, page, cur, iosize); cur += iosize; continue; } @@ -3949,6 +3959,12 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode, cur += iosize; nr++; } + /* + * If we finish without problem, we should not only clear page dirty, + * but also empty subpage dirty bits + */ + if (!ret) + btrfs_page_assert_not_dirty(fs_info, page); *nr_ret = nr; return ret; } diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c index 640bcd21bf28..3c311841cdee 100644 --- a/fs/btrfs/subpage.c +++ b/fs/btrfs/subpage.c @@ -559,3 +559,23 @@ IMPLEMENT_BTRFS_PAGE_OPS(writeback, set_page_writeback, end_page_writeback, PageWriteback); IMPLEMENT_BTRFS_PAGE_OPS(ordered, SetPageOrdered, ClearPageOrdered, PageOrdered); + +/* + * Make sure not only the page dirty bit is cleared, but also subpage dirty bit + * is cleared. + */ +void btrfs_page_assert_not_dirty(const struct btrfs_fs_info *fs_info, + struct page *page) +{ + struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; + + if (!IS_ENABLED(CONFIG_BTRFS_ASSERT)) + return; + + ASSERT(!PageDirty(page)); + if (fs_info->sectorsize == PAGE_SIZE) + return; + + ASSERT(PagePrivate(page) && page->private); + ASSERT(subpage->dirty_bitmap == 0); +} diff --git a/fs/btrfs/subpage.h b/fs/btrfs/subpage.h index 4d7aca85d915..0120948f37a1 100644 --- a/fs/btrfs/subpage.h +++ b/fs/btrfs/subpage.h @@ -126,4 +126,7 @@ DECLARE_BTRFS_SUBPAGE_OPS(ordered); bool btrfs_subpage_clear_and_test_dirty(const struct btrfs_fs_info *fs_info, struct page *page, u64 start, u32 len); +void btrfs_page_assert_not_dirty(const struct btrfs_fs_info *fs_info, + struct page *page); + #endif From 7367253a351ef7202d215d3145d7e83e1472be7d Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:34:59 +0800 Subject: [PATCH 307/417] btrfs: subpage: disable inline extent creation [BUG] When running the following fsx command (extracted from generic/127) on subpage filesystem, it can create inline extent with regular extents: fsx -q -l 262144 -o 65536 -S 191110531 -N 9057 -R -W $mnt/file > /tmp/fsx The offending extent would look like: item 9 key (257 INODE_REF 256) itemoff 15703 itemsize 14 index 2 namelen 4 name: file item 10 key (257 EXTENT_DATA 0) itemoff 14975 itemsize 728 generation 7 type 0 (inline) inline extent data size 707 ram_bytes 707 compression 0 (none) item 11 key (257 EXTENT_DATA 4096) itemoff 14922 itemsize 53 generation 7 type 2 (prealloc) prealloc data disk byte 102346752 nr 4096 prealloc data offset 0 nr 4096 [CAUSE] For subpage filesystem, the writeback is triggered in page units, which means, even if we just want to writeback range [16K, 20K) for 64K page system, we will still try to writeback any dirty sector of range [0, 64K). This is never a problem if sectorsize == PAGE_SIZE, but for subpage, this can cause unexpected problems. For above test case, the last several operations from fsx are: 9055 trunc from 0x40000 to 0x2c3 9057 falloc from 0x164c to 0x19d2 (0x386 bytes) In operation 9055, we dirtied sector [0, 4096), then in falloc, we call btrfs_wait_ordered_range(inode, start=4096, len=4096), only expecting to writeback any dirty data in [4096, 8192), but nothing else. Unfortunately, in subpage case, above btrfs_wait_ordered_range() will trigger writeback of the range [0, 64K), which includes the data at [0, 4096). And since at the call site, we haven't yet increased i_size, which is still 707, this means cow_file_range() can insert an inline extent. Resulting above inline + regular extent. [WORKAROUND] I don't really have any good short-term solution yet, as this means all operations that would trigger writeback need to be reviewed for any i_size change. So here I choose to disable inline extent creation for subpage case as a workaround. We have done tons of work just to avoid such extent, so I don't to create an exception just for subpage. This only affects inline extent creation, subpage has no problem reading existing inline extents at all. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/inode.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index ae9e4ad1949a..cade9a2d561e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -681,7 +681,11 @@ again: } } cont: - if (start == 0) { + /* + * Check cow_file_range() for why we don't even try to create inline + * extent for subpage case. + */ + if (start == 0 && fs_info->sectorsize == PAGE_SIZE) { /* lets try to make an inline extent */ if (ret || total_in < actual_end) { /* we didn't compress the entire range, try @@ -1079,7 +1083,17 @@ static noinline int cow_file_range(struct btrfs_inode *inode, inode_should_defrag(inode, start, end, num_bytes, SZ_64K); - if (start == 0) { + /* + * Due to the page size limit, for subpage we can only trigger the + * writeback for the dirty sectors of page, that means data writeback + * is doing more writeback than what we want. + * + * This is especially unexpected for some call sites like fallocate, + * where we only increase i_size after everything is done. + * This means we can trigger inline extent even if we didn't want to. + * So here we skip inline extent creation completely. + */ + if (start == 0 && fs_info->sectorsize == PAGE_SIZE) { /* lets try to make an inline extent */ ret = cow_file_range_inline(inode, start, end, 0, BTRFS_COMPRESS_NONE, NULL); From e0eefe07f89516f57dac3fbb759b75a5f0a19ae4 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:35:00 +0800 Subject: [PATCH 308/417] btrfs: subpage: allow submit_extent_page() to do bio split Current submit_extent_page() just checks if the current page range can be fitted into current bio, and if not, submit then re-add. But this behavior can't handle subpage case at all. For subpage case, the problem is in the page size, 64K, which is also the same size as stripe size. This means, if we can't fit a full 64K into a bio, due to stripe limit, then it won't fit into next bio without crossing stripe either. The proper way to handle it is: - Check how many bytes we can be put into current bio - Put as many bytes as possible into current bio first - Submit current bio - Create a new bio - Add the remaining bytes into the new bio Refactor submit_extent_page() so that it does the above iteration. The main loop inside submit_extent_page() will look like this: cur = pg_offset; while (cur < pg_offset + size) { u32 offset = cur - pg_offset; int added; if (!bio_ctrl->bio) { /* Allocate new bio if needed */ } /* Add as many bytes into the bio */ added = btrfs_bio_add_page(); if (added < size - offset) { /* The current bio is full, submit it */ } cur += added; } Also, since we're doing new bio allocation deep inside the main loop, extract that code into a new helper, alloc_new_bio(). Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 190 +++++++++++++++++++++++++++++-------------- 1 file changed, 131 insertions(+), 59 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index e1a464f6b42f..5843f4948431 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -172,6 +172,8 @@ int __must_check submit_one_bio(struct bio *bio, int mirror_num, bio->bi_private = NULL; + /* Caller should ensure the bio has at least some range added */ + ASSERT(bio->bi_iter.bi_size); if (is_data_inode(tree->private_data)) ret = btrfs_submit_data_bio(tree->private_data, bio, mirror_num, bio_flags); @@ -3181,20 +3183,22 @@ struct bio *btrfs_bio_clone_partial(struct bio *orig, int offset, int size) * @size: portion of page that we want to write * @prev_bio_flags: flags of previous bio to see if we can merge the current one * @bio_flags: flags of the current bio to see if we can merge them - * @return: true if page was added, false otherwise * * Attempt to add a page to bio considering stripe alignment etc. * - * Return true if successfully page added. Otherwise, return false. + * Return >= 0 for the number of bytes added to the bio. + * Can return 0 if the current bio is already at stripe/zone boundary. + * Return <0 for error. */ -static bool btrfs_bio_add_page(struct btrfs_bio_ctrl *bio_ctrl, - struct page *page, - u64 disk_bytenr, unsigned int size, - unsigned int pg_offset, - unsigned long bio_flags) +static int btrfs_bio_add_page(struct btrfs_bio_ctrl *bio_ctrl, + struct page *page, + u64 disk_bytenr, unsigned int size, + unsigned int pg_offset, + unsigned long bio_flags) { struct bio *bio = bio_ctrl->bio; u32 bio_size = bio->bi_iter.bi_size; + u32 real_size; const sector_t sector = disk_bytenr >> SECTOR_SHIFT; bool contig; int ret; @@ -3203,25 +3207,32 @@ static bool btrfs_bio_add_page(struct btrfs_bio_ctrl *bio_ctrl, /* The limit should be calculated when bio_ctrl->bio is allocated */ ASSERT(bio_ctrl->len_to_oe_boundary && bio_ctrl->len_to_stripe_boundary); if (bio_ctrl->bio_flags != bio_flags) - return false; + return 0; if (bio_ctrl->bio_flags & EXTENT_BIO_COMPRESSED) contig = bio->bi_iter.bi_sector == sector; else contig = bio_end_sector(bio) == sector; if (!contig) - return false; + return 0; - if (bio_size + size > bio_ctrl->len_to_oe_boundary || - bio_size + size > bio_ctrl->len_to_stripe_boundary) - return false; + real_size = min(bio_ctrl->len_to_oe_boundary, + bio_ctrl->len_to_stripe_boundary) - bio_size; + real_size = min(real_size, size); + + /* + * If real_size is 0, never call bio_add_*_page(), as even size is 0, + * bio will still execute its endio function on the page! + */ + if (real_size == 0) + return 0; if (bio_op(bio) == REQ_OP_ZONE_APPEND) - ret = bio_add_zone_append_page(bio, page, size, pg_offset); + ret = bio_add_zone_append_page(bio, page, real_size, pg_offset); else - ret = bio_add_page(bio, page, size, pg_offset); + ret = bio_add_page(bio, page, real_size, pg_offset); - return ret == size; + return ret; } static int calc_bio_boundaries(struct btrfs_bio_ctrl *bio_ctrl, @@ -3279,6 +3290,62 @@ static int calc_bio_boundaries(struct btrfs_bio_ctrl *bio_ctrl, return 0; } +static int alloc_new_bio(struct btrfs_inode *inode, + struct btrfs_bio_ctrl *bio_ctrl, + struct writeback_control *wbc, + unsigned int opf, + bio_end_io_t end_io_func, + u64 disk_bytenr, u32 offset, + unsigned long bio_flags) +{ + struct btrfs_fs_info *fs_info = inode->root->fs_info; + struct bio *bio; + int ret; + + /* + * For compressed page range, its disk_bytenr is always @disk_bytenr + * passed in, no matter if we have added any range into previous bio. + */ + if (bio_flags & EXTENT_BIO_COMPRESSED) + bio = btrfs_bio_alloc(disk_bytenr); + else + bio = btrfs_bio_alloc(disk_bytenr + offset); + bio_ctrl->bio = bio; + bio_ctrl->bio_flags = bio_flags; + ret = calc_bio_boundaries(bio_ctrl, inode); + if (ret < 0) + goto error; + bio->bi_end_io = end_io_func; + bio->bi_private = &inode->io_tree; + bio->bi_write_hint = inode->vfs_inode.i_write_hint; + bio->bi_opf = opf; + if (wbc) { + struct block_device *bdev; + + bdev = fs_info->fs_devices->latest_bdev; + bio_set_dev(bio, bdev); + wbc_init_bio(wbc, bio); + } + if (btrfs_is_zoned(fs_info) && bio_op(bio) == REQ_OP_ZONE_APPEND) { + struct btrfs_device *device; + + device = btrfs_zoned_get_device(fs_info, disk_bytenr, + fs_info->sectorsize); + if (IS_ERR(device)) { + ret = PTR_ERR(device); + goto error; + } + + btrfs_io_bio(bio)->device = device; + } + return 0; +error: + bio_ctrl->bio = NULL; + bio->bi_status = errno_to_blk_status(ret); + bio_endio(bio); + return ret; +} + /* * @opf: bio REQ_OP_* and REQ_* flags as one value * @wbc: optional writeback control for io accounting @@ -3304,61 +3371,66 @@ static int submit_extent_page(unsigned int opf, bool force_bio_submit) { int ret = 0; - struct bio *bio; - size_t io_size = min_t(size_t, size, PAGE_SIZE); struct btrfs_inode *inode = BTRFS_I(page->mapping->host); - struct extent_io_tree *tree = &inode->io_tree; - struct btrfs_fs_info *fs_info = inode->root->fs_info; + unsigned int cur = pg_offset; ASSERT(bio_ctrl); ASSERT(pg_offset < PAGE_SIZE && size <= PAGE_SIZE && pg_offset + size <= PAGE_SIZE); - if (bio_ctrl->bio) { - bio = bio_ctrl->bio; - if (force_bio_submit || - !btrfs_bio_add_page(bio_ctrl, page, disk_bytenr, io_size, - pg_offset, bio_flags)) { - ret = submit_one_bio(bio, mirror_num, bio_ctrl->bio_flags); + if (force_bio_submit && bio_ctrl->bio) { + ret = submit_one_bio(bio_ctrl->bio, mirror_num, bio_ctrl->bio_flags); + bio_ctrl->bio = NULL; + if (ret < 0) + return ret; + } + + while (cur < pg_offset + size) { + u32 offset = cur - pg_offset; + int added; + + /* Allocate new bio if needed */ + if (!bio_ctrl->bio) { + ret = alloc_new_bio(inode, bio_ctrl, wbc, opf, + end_io_func, disk_bytenr, offset, + bio_flags); + if (ret < 0) + return ret; + } + /* + * We must go through btrfs_bio_add_page() to ensure each + * page range won't cross various boundaries. + */ + if (bio_flags & EXTENT_BIO_COMPRESSED) + added = btrfs_bio_add_page(bio_ctrl, page, disk_bytenr, + size - offset, pg_offset + offset, + bio_flags); + else + added = btrfs_bio_add_page(bio_ctrl, page, + disk_bytenr + offset, size - offset, + pg_offset + offset, bio_flags); + + /* Metadata page range should never be split */ + if (!is_data_inode(&inode->vfs_inode)) + ASSERT(added == 0 || added == size - offset); + + /* At least we added some page, update the account */ + if (wbc && added) + wbc_account_cgroup_owner(wbc, page, added); + + /* We have reached boundary, submit right now */ + if (added < size - offset) { + /* The bio should contain some page(s) */ + ASSERT(bio_ctrl->bio->bi_iter.bi_size); + ret = submit_one_bio(bio_ctrl->bio, mirror_num, + bio_ctrl->bio_flags); bio_ctrl->bio = NULL; if (ret < 0) return ret; - } else { - if (wbc) - wbc_account_cgroup_owner(wbc, page, io_size); - return 0; } + cur += added; } - - bio = btrfs_bio_alloc(disk_bytenr); - bio_add_page(bio, page, io_size, pg_offset); - bio->bi_end_io = end_io_func; - bio->bi_private = tree; - bio->bi_write_hint = page->mapping->host->i_write_hint; - bio->bi_opf = opf; - if (wbc) { - struct block_device *bdev; - - bdev = fs_info->fs_devices->latest_bdev; - bio_set_dev(bio, bdev); - wbc_init_bio(wbc, bio); - wbc_account_cgroup_owner(wbc, page, io_size); - } - if (btrfs_is_zoned(fs_info) && bio_op(bio) == REQ_OP_ZONE_APPEND) { - struct btrfs_device *device; - - device = btrfs_zoned_get_device(fs_info, disk_bytenr, io_size); - if (IS_ERR(device)) - return PTR_ERR(device); - - btrfs_io_bio(bio)->device = device; - } - - bio_ctrl->bio = bio; - bio_ctrl->bio_flags = bio_flags; - ret = calc_bio_boundaries(bio_ctrl, inode); - - return ret; + return 0; } static int attach_extent_buffer_page(struct extent_buffer *eb, From c8050b3b7f76586945003a8a2aeb2c8157f26645 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:35:01 +0800 Subject: [PATCH 309/417] btrfs: subpage: reject raid56 filesystem and profile conversion RAID56 is not only unsafe due to its write-hole problem, but also has tons of hardcoded PAGE_SIZE. Disable it for subpage support for now. Reviewed-by: Anand Jain Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 10 ++++++++++ fs/btrfs/volumes.c | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index a59ab7b9aea0..b9ba244de1d1 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3402,6 +3402,16 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device goto fail_alloc; } } + if (sectorsize != PAGE_SIZE) { + if (btrfs_super_incompat_flags(fs_info->super_copy) & + BTRFS_FEATURE_INCOMPAT_RAID56) { + btrfs_err(fs_info, + "RAID56 is not yet supported for sector size %u with page size %lu", + sectorsize, PAGE_SIZE); + err = -EINVAL; + goto fail_alloc; + } + } ret = btrfs_init_workqueues(fs_info, fs_devices); if (ret) { diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index d42fb61aadc3..8e61307ffad2 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3968,6 +3968,13 @@ static inline int validate_convert_profile(struct btrfs_fs_info *fs_info, if (!(bargs->flags & BTRFS_BALANCE_ARGS_CONVERT)) return true; + if (fs_info->sectorsize < PAGE_SIZE && + bargs->target & BTRFS_BLOCK_GROUP_RAID56_MASK) { + btrfs_err(fs_info, + "RAID56 is not yet supported for sectorsize %u with page size %lu", + fs_info->sectorsize, PAGE_SIZE); + return false; + } /* Profile is valid and does not have bits outside of the allowed set */ if (alloc_profile_is_valid(bargs->target, 1) && (bargs->target & ~allowed) == 0) From e0467866198f7f536806f39e5d0d91ae8018de08 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:35:02 +0800 Subject: [PATCH 310/417] btrfs: subpage: fix race between prepare_pages() and btrfs_releasepage() [BUG] When running generic/095, there is a high chance to crash with subpage data RW support: assertion failed: PagePrivate(page) && page->private ------------[ cut here ]------------ kernel BUG at fs/btrfs/ctree.h:3403! Internal error: Oops - BUG: 0 [#1] SMP CPU: 1 PID: 3567 Comm: fio Tainted: 5.12.0-rc7-custom+ #17 Hardware name: Khadas VIM3 (DT) Call trace: assertfail.constprop.0+0x28/0x2c [btrfs] btrfs_subpage_assert+0x80/0xa0 [btrfs] btrfs_subpage_set_uptodate+0x34/0xec [btrfs] btrfs_page_clamp_set_uptodate+0x74/0xa4 [btrfs] btrfs_dirty_pages+0x160/0x270 [btrfs] btrfs_buffered_write+0x444/0x630 [btrfs] btrfs_direct_write+0x1cc/0x2d0 [btrfs] btrfs_file_write_iter+0xc0/0x160 [btrfs] new_sync_write+0xe8/0x180 vfs_write+0x1b4/0x210 ksys_pwrite64+0x7c/0xc0 __arm64_sys_pwrite64+0x24/0x30 el0_svc_common.constprop.0+0x70/0x140 do_el0_svc+0x28/0x90 el0_svc+0x2c/0x54 el0_sync_handler+0x1a8/0x1ac el0_sync+0x170/0x180 Code: f0000160 913be042 913c4000 955444bc (d4210000) ---[ end trace 3fdd39f4cccedd68 ]--- [CAUSE] Although prepare_pages() calls find_or_create_page(), which returns the page locked, but in later prepare_uptodate_page() calls, we may call btrfs_readpage() which will unlock the page before it returns. This leaves a window where btrfs_releasepage() can sneak in and release the page, clearing page->private and causing above ASSERT(). [FIX] In prepare_uptodate_page(), we should not only check page->mapping, but also PagePrivate() to ensure we are still holding the correct page which has proper fs context setup. Reported-by: Ritesh Harjani Tested-by: Ritesh Harjani Reviewed-by: Filipe Manana Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/file.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index ee34497500e1..8c57af3702fa 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1340,7 +1340,18 @@ static int prepare_uptodate_page(struct inode *inode, unlock_page(page); return -EIO; } - if (page->mapping != inode->i_mapping) { + + /* + * Since btrfs_readpage() will unlock the page before it + * returns, there is a window where btrfs_releasepage() can + * be called to release the page. + * Here we check both inode mapping and PagePrivate() to + * make sure the page was not released. + * + * The private flag check is essential for subpage as we need + * to store extra bitmap using page->private. + */ + if (page->mapping != inode->i_mapping || !PagePrivate(page)) { unlock_page(page); return -EAGAIN; } From 7c11d0ae439565b4560b0c0f36bf05171ed1a146 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:35:03 +0800 Subject: [PATCH 311/417] btrfs: subpage: fix a potential use-after-free in writeback helper [BUG] There is a possible use-after-free bug when running generic/095. BUG: Unable to handle kernel data access on write at 0x6b6b6b6b6b6b725b Faulting instruction address: 0xc000000000283654 c000000000283078 do_raw_spin_unlock+0x88/0x230 c0000000012b1e14 _raw_spin_unlock_irqrestore+0x44/0x90 c000000000a918dc btrfs_subpage_clear_writeback+0xac/0xe0 c0000000009e0458 end_bio_extent_writepage+0x158/0x270 c000000000b6fd14 bio_endio+0x254/0x270 c0000000009fc0f0 btrfs_end_bio+0x1a0/0x200 c000000000b6fd14 bio_endio+0x254/0x270 c000000000b781fc blk_update_request+0x46c/0x670 c000000000b8b394 blk_mq_end_request+0x34/0x1d0 c000000000d82d1c lo_complete_rq+0x11c/0x140 c000000000b880a4 blk_complete_reqs+0x84/0xb0 c0000000012b2ca4 __do_softirq+0x334/0x680 c0000000001dd878 irq_exit+0x148/0x1d0 c000000000016f4c do_IRQ+0x20c/0x240 c000000000009240 hardware_interrupt_common_virt+0x1b0/0x1c0 [CAUSE] There is very small race window like the following in generic/095. Thread 1 | Thread 2 --------------------------------+------------------------------------ end_bio_extent_writepage() | btrfs_releasepage() |- spin_lock_irqsave() | | |- end_page_writeback() | | | | |- if (PageWriteback() ||...) | | |- clear_page_extent_mapped() | | |- kfree(subpage); |- spin_unlock_irqrestore(). The race can also happen between writeback and btrfs_invalidatepage(), although that would be much harder as btrfs_invalidatepage() has much more work to do before the clear_page_extent_mapped() call. [FIX] Here we "wait" for the subapge spinlock to be released before we detach subpage structure. So this patch will introduce a new function, wait_subpage_spinlock(), to do the "wait" by acquiring the spinlock and release it. Since the caller has ensured the page is not dirty nor writeback, and page is already locked, the only way to hold the subpage spinlock is from endio function. Thus we only need to acquire the spinlock to wait for any existing holder. Reported-by: Ritesh Harjani Tested-by: Ritesh Harjani Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/file.c | 8 ++++---- fs/btrfs/inode.c | 39 ++++++++++++++++++++++++++++++++++++++- fs/btrfs/subpage.c | 4 +++- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 8c57af3702fa..d3f2623a2af0 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1343,10 +1343,10 @@ static int prepare_uptodate_page(struct inode *inode, /* * Since btrfs_readpage() will unlock the page before it - * returns, there is a window where btrfs_releasepage() can - * be called to release the page. - * Here we check both inode mapping and PagePrivate() to - * make sure the page was not released. + * returns, there is a window where btrfs_releasepage() can be + * called to release the page. Here we check both inode + * mapping and PagePrivate() to make sure the page was not + * released. * * The private flag check is essential for subpage as we need * to store extra bitmap using page->private. diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index cade9a2d561e..034fe81db5c1 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8426,11 +8426,47 @@ static void btrfs_readahead(struct readahead_control *rac) extent_readahead(rac); } +/* + * For releasepage() and invalidatepage() we have a race window where + * end_page_writeback() is called but the subpage spinlock is not yet released. + * If we continue to release/invalidate the page, we could cause use-after-free + * for subpage spinlock. So this function is to spin and wait for subpage + * spinlock. + */ +static void wait_subpage_spinlock(struct page *page) +{ + struct btrfs_fs_info *fs_info = btrfs_sb(page->mapping->host->i_sb); + struct btrfs_subpage *subpage; + + if (fs_info->sectorsize == PAGE_SIZE) + return; + + ASSERT(PagePrivate(page) && page->private); + subpage = (struct btrfs_subpage *)page->private; + + /* + * This may look insane as we just acquire the spinlock and release it, + * without doing anything. But we just want to make sure no one is + * still holding the subpage spinlock. + * And since the page is not dirty nor writeback, and we have page + * locked, the only possible way to hold a spinlock is from the endio + * function to clear page writeback. + * + * Here we just acquire the spinlock so that all existing callers + * should exit and we're safe to release/invalidate the page. + */ + spin_lock_irq(&subpage->lock); + spin_unlock_irq(&subpage->lock); +} + static int __btrfs_releasepage(struct page *page, gfp_t gfp_flags) { int ret = try_release_extent_mapping(page, gfp_flags); - if (ret == 1) + + if (ret == 1) { + wait_subpage_spinlock(page); clear_page_extent_mapped(page); + } return ret; } @@ -8494,6 +8530,7 @@ static void btrfs_invalidatepage(struct page *page, unsigned int offset, * do double ordered extent accounting on the same page. */ wait_on_page_writeback(page); + wait_subpage_spinlock(page); /* * For subpage case, we have call sites like diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c index 3c311841cdee..cb10e56ee31e 100644 --- a/fs/btrfs/subpage.c +++ b/fs/btrfs/subpage.c @@ -435,8 +435,10 @@ void btrfs_subpage_clear_writeback(const struct btrfs_fs_info *fs_info, spin_lock_irqsave(&subpage->lock, flags); subpage->writeback_bitmap &= ~tmp; - if (subpage->writeback_bitmap == 0) + if (subpage->writeback_bitmap == 0) { + ASSERT(PageWriteback(page)); end_page_writeback(page); + } spin_unlock_irqrestore(&subpage->lock, flags); } From e3c62324e470c0a89df966603156b34fccd01708 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:35:04 +0800 Subject: [PATCH 312/417] btrfs: subpage: fix false alert when relocating partial preallocated data extents [BUG] When relocating partial preallocated data extents (part of the preallocated extent is written) for subpage, it can cause the following false alert and make the relocation to fail: BTRFS info (device dm-3): balance: start -d BTRFS info (device dm-3): relocating block group 13631488 flags data BTRFS warning (device dm-3): csum failed root -9 ino 257 off 4096 csum 0x98757625 expected csum 0x00000000 mirror 1 BTRFS error (device dm-3): bdev /dev/mapper/arm_nvme-test errs: wr 0, rd 0, flush 0, corrupt 1, gen 0 BTRFS warning (device dm-3): csum failed root -9 ino 257 off 4096 csum 0x98757625 expected csum 0x00000000 mirror 1 BTRFS error (device dm-3): bdev /dev/mapper/arm_nvme-test errs: wr 0, rd 0, flush 0, corrupt 2, gen 0 BTRFS info (device dm-3): balance: ended with status: -5 The minimal script to reproduce looks like this: mkfs.btrfs -f -s 4k $dev mount $dev -o nospace_cache $mnt xfs_io -f -c "falloc 0 8k" $mnt/file xfs_io -f -c "pwrite 0 4k" $mnt/file btrfs balance start -d $mnt [CAUSE] Function btrfs_verify_data_csum() checks if the full range has EXTENT_NODATASUM bit for data reloc inode, if *all* bytes of the range have EXTENT_NODATASUM bit, then it skip the range. This works pretty well for regular sectorsize, as in that case btrfs_verify_data_csum() is called for each sector, thus no problem at all. But for subpage case, btrfs_verify_data_csum() is called on each bvec, which can contain several sectors, and since it checks *all* bytes for EXTENT_NODATASUM bit, if we have some range with csum, then we will continue checking all the sectors. For the preallocated sectors, it doesn't have any csum, thus obviously the csum won't match and cause the false alert. [FIX] Move the EXTENT_NODATASUM check into the main loop, so that we can check each sector for EXTENT_NODATASUM bit for subpage case. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/inode.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 034fe81db5c1..915f11462c7c 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3290,19 +3290,24 @@ unsigned int btrfs_verify_data_csum(struct btrfs_io_bio *io_bio, u32 bio_offset, if (!root->fs_info->csum_root) return 0; - if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID && - test_range_bit(io_tree, start, end, EXTENT_NODATASUM, 1, NULL)) { - clear_extent_bits(io_tree, start, end, EXTENT_NODATASUM); - return 0; - } - ASSERT(page_offset(page) <= start && end <= page_offset(page) + PAGE_SIZE - 1); for (pg_off = offset_in_page(start); pg_off < offset_in_page(end); pg_off += sectorsize, bio_offset += sectorsize) { + u64 file_offset = pg_off + page_offset(page); int ret; + if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID && + test_range_bit(io_tree, file_offset, + file_offset + sectorsize - 1, + EXTENT_NODATASUM, 1, NULL)) { + /* Skip the range without csum for data reloc inode */ + clear_extent_bits(io_tree, file_offset, + file_offset + sectorsize - 1, + EXTENT_NODATASUM); + continue; + } ret = check_data_csum(inode, io_bio, bio_offset, page, pg_off, page_offset(page) + pg_off); if (ret < 0) { From 9d9ea1e68a05ef852d612f0c49d274c86e1e710a Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:35:05 +0800 Subject: [PATCH 313/417] btrfs: subpage: fix relocation potentially overwriting last page data [BUG] When using the following script, btrfs will report data corruption after one data balance with subpage support: mkfs.btrfs -f -s 4k $dev mount $dev -o nospace_cache $mnt $fsstress -w -n 8 -s 1620948986 -d $mnt/ -v > /tmp/fsstress sync btrfs balance start -d $mnt btrfs scrub start -B $mnt Similar problem can be easily observed in btrfs/028 test case, there will be tons of balance failure with -EIO. [CAUSE] Above fsstress will result the following data extents layout in extent tree: item 10 key (13631488 EXTENT_ITEM 98304) itemoff 15889 itemsize 82 refs 2 gen 7 flags DATA extent data backref root FS_TREE objectid 259 offset 1339392 count 1 extent data backref root FS_TREE objectid 259 offset 647168 count 1 item 11 key (13631488 BLOCK_GROUP_ITEM 8388608) itemoff 15865 itemsize 24 block group used 102400 chunk_objectid 256 flags DATA item 12 key (13733888 EXTENT_ITEM 4096) itemoff 15812 itemsize 53 refs 1 gen 7 flags DATA extent data backref root FS_TREE objectid 259 offset 729088 count 1 Then when creating the data reloc inode, the data reloc inode will look like this: 0 32K 64K 96K 100K 104K |<------ Extent A ----->| |<- Ext B ->| Then when we first try to relocate extent A, we setup the data reloc inode with i_size 96K, then read both page [0, 64K) and page [64K, 128K). For page 64K, since the i_size is just 96K, we fill range [96K, 128K) with 0 and set it uptodate. Then when we come to extent B, we update i_size to 104K, then try to read page [64K, 128K). Then we find the page is already uptodate, so we skip the read. But range [96K, 128K) is filled with 0, not the real data. Then we writeback the data reloc inode to disk, with 0 filling range [96K, 128K), corrupting the content of extent B. The behavior is caused by the fact that we still do full page read for subpage case. The bug won't really happen for regular sectorsize, as one page only contains one sector. [FIX] This patch will fix the problem by invalidating range [i_size, PAGE_END] in prealloc_file_extent_cluster(). So that if above example happens, when we preallocate the file extent for extent B, we will clear the uptodate bits for range [96K, 128K), allowing later relocate_one_page() to re-read the needed range. There is a special note for the invalidating part. Since we're not calling real btrfs_invalidatepage(), but just clearing the subpage and page uptodate bits, we can leave a page half dirty and half out of date. Reading such page can cause a deadlock, as we normally expect a dirty page to be fully uptodate. Thus here we flush and wait the data reloc inode before doing the hacked invalidating. This won't cause extra overhead, as we're going to writeback the data later anyway. Reported-by: Ritesh Harjani Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/relocation.c | 60 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 9c8cea5cabe4..914d403b4415 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -2782,10 +2782,70 @@ static noinline_for_stack int prealloc_file_extent_cluster( u64 num_bytes; int nr; int ret = 0; + u64 i_size = i_size_read(&inode->vfs_inode); u64 prealloc_start = cluster->start - offset; u64 prealloc_end = cluster->end - offset; u64 cur_offset = prealloc_start; + /* + * For subpage case, previous i_size may not be aligned to PAGE_SIZE. + * This means the range [i_size, PAGE_END + 1) is filled with zeros by + * btrfs_do_readpage() call of previously relocated file cluster. + * + * If the current cluster starts in the above range, btrfs_do_readpage() + * will skip the read, and relocate_one_page() will later writeback + * the padding zeros as new data, causing data corruption. + * + * Here we have to manually invalidate the range (i_size, PAGE_END + 1). + */ + if (!IS_ALIGNED(i_size, PAGE_SIZE)) { + struct address_space *mapping = inode->vfs_inode.i_mapping; + struct btrfs_fs_info *fs_info = inode->root->fs_info; + const u32 sectorsize = fs_info->sectorsize; + struct page *page; + + ASSERT(sectorsize < PAGE_SIZE); + ASSERT(IS_ALIGNED(i_size, sectorsize)); + + /* + * Subpage can't handle page with DIRTY but without UPTODATE + * bit as it can lead to the following deadlock: + * + * btrfs_readpage() + * | Page already *locked* + * |- btrfs_lock_and_flush_ordered_range() + * |- btrfs_start_ordered_extent() + * |- extent_write_cache_pages() + * |- lock_page() + * We try to lock the page we already hold. + * + * Here we just writeback the whole data reloc inode, so that + * we will be ensured to have no dirty range in the page, and + * are safe to clear the uptodate bits. + * + * This shouldn't cause too much overhead, as we need to write + * the data back anyway. + */ + ret = filemap_write_and_wait(mapping); + if (ret < 0) + return ret; + + clear_extent_bits(&inode->io_tree, i_size, + round_up(i_size, PAGE_SIZE) - 1, + EXTENT_UPTODATE); + page = find_lock_page(mapping, i_size >> PAGE_SHIFT); + /* + * If page is freed we don't need to do anything then, as we + * will re-read the whole page anyway. + */ + if (page) { + btrfs_subpage_clear_uptodate(fs_info, page, i_size, + round_up(i_size, PAGE_SIZE) - i_size); + unlock_page(page); + put_page(page); + } + } + BUG_ON(cluster->start != cluster->boundary[0]); ret = btrfs_alloc_data_chunk_ondemand(inode, prealloc_end + 1 - prealloc_start); From 95ea0486b20e4de9011d04b05ed667201940b532 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:35:06 +0800 Subject: [PATCH 314/417] btrfs: allow read-write for 4K sectorsize on 64K page size systems Since now we support data and metadata read-write for subpage, remove the RO requirement for subpage mount. There are some extra limitations though: - For now, subpage RW mount is still considered experimental Thus that mount warning will still be there. - No compression support There are still quite some PAGE_SIZE hard coded and quite some call sites use extent_clear_unlock_delalloc() to unlock locked_page. This will screw up subpage helpers. Now for subpage RW mount, no matter what mount option or inode attr is set, all writes will not be compressed. Although reading compressed data has no problem. - No defrag for subpage case The defrag support for subpage case will come in later patches, which will also rework the defrag workflow. - No inline extent will be created This is mostly due to the fact that filemap_fdatawrite_range() will trigger more write than the range specified. In fallocate calls, this behavior can make us to writeback which can be inlined, before we enlarge the i_size. This is a very special corner case, and even current btrfs check won't report error on such inline extent + regular extent. But considering how much effort has been put to prevent such inline + regular, I'd prefer to cut off inline extent completely until we have a good solution. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 13 ++++--------- fs/btrfs/inode.c | 3 +++ fs/btrfs/ioctl.c | 6 ++++++ fs/btrfs/super.c | 7 ------- fs/btrfs/sysfs.c | 4 ++++ 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index b9ba244de1d1..2f9515dccce0 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3392,15 +3392,10 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device goto fail_alloc; } - /* For 4K sector size support, it's only read-only */ - if (PAGE_SIZE == SZ_64K && sectorsize == SZ_4K) { - if (!sb_rdonly(sb) || btrfs_super_log_root(disk_super)) { - btrfs_err(fs_info, - "subpage sectorsize %u only supported read-only for page size %lu", - sectorsize, PAGE_SIZE); - err = -EINVAL; - goto fail_alloc; - } + if (sectorsize != PAGE_SIZE) { + btrfs_warn(fs_info, + "read-write for sector size %u with page size %lu is experimental", + sectorsize, PAGE_SIZE); } if (sectorsize != PAGE_SIZE) { if (btrfs_super_incompat_flags(fs_info->super_copy) & diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 915f11462c7c..7e63b5c0a1dc 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -489,6 +489,9 @@ static noinline int add_async_extent(struct async_chunk *cow, */ static inline bool inode_can_compress(struct btrfs_inode *inode) { + /* Subpage doesn't support compression yet */ + if (inode->root->fs_info->sectorsize < PAGE_SIZE) + return false; if (inode->flags & BTRFS_INODE_NODATACOW || inode->flags & BTRFS_INODE_NODATASUM) return false; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 0ba98e08a029..4d809899c076 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3115,6 +3115,12 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp) goto out; } + /* Subpage defrag will be supported in later commits */ + if (root->fs_info->sectorsize < PAGE_SIZE) { + ret = -ENOTTY; + goto out; + } + switch (inode->i_mode & S_IFMT) { case S_IFDIR: if (!capable(CAP_SYS_ADMIN)) { diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 35ff142ad242..0d2e3ab2fc31 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2041,13 +2041,6 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) ret = -EINVAL; goto restore; } - if (fs_info->sectorsize < PAGE_SIZE) { - btrfs_warn(fs_info, - "read-write mount is not yet allowed for sectorsize %u page size %lu", - fs_info->sectorsize, PAGE_SIZE); - ret = -EINVAL; - goto restore; - } /* * NOTE: when remounting with a change that does writes, don't diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 9d1d140118ff..d9d53a255ef9 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -366,6 +366,10 @@ static ssize_t supported_sectorsizes_show(struct kobject *kobj, { ssize_t ret = 0; + /* 4K sector size is also supported with 64K page size */ + if (PAGE_SIZE == SZ_64K) + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%u ", SZ_4K); + /* Only sectorsize == PAGE_SIZE is now supported */ ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%lu\n", PAGE_SIZE); From 963e4db83e2832ee5e760f3c7f92d68bd66156f6 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 26 Jul 2021 14:35:07 +0800 Subject: [PATCH 315/417] btrfs: unify regular and subpage error paths in __extent_writepage() [BUG] When running btrfs/160 in a loop for subpage with experimental compression support, it has a high chance to crash (~20%): BTRFS critical (device dm-7): panic in __btrfs_add_ordered_extent:238: inconsistency in ordered tree at offset 0 (errno=-17 Object already exists) ------------[ cut here ]------------ kernel BUG at fs/btrfs/ordered-data.c:238! Internal error: Oops - BUG: 0 [#1] SMP pc : __btrfs_add_ordered_extent+0x550/0x670 [btrfs] lr : __btrfs_add_ordered_extent+0x550/0x670 [btrfs] Call trace: __btrfs_add_ordered_extent+0x550/0x670 [btrfs] btrfs_add_ordered_extent+0x2c/0x50 [btrfs] run_delalloc_nocow+0x81c/0x8fc [btrfs] btrfs_run_delalloc_range+0xa4/0x390 [btrfs] writepage_delalloc+0xc0/0x1ac [btrfs] __extent_writepage+0xf4/0x370 [btrfs] extent_write_cache_pages+0x288/0x4f4 [btrfs] extent_writepages+0x58/0xe0 [btrfs] btrfs_writepages+0x1c/0x30 [btrfs] do_writepages+0x60/0x110 __filemap_fdatawrite_range+0x108/0x170 filemap_fdatawrite_range+0x20/0x30 btrfs_fdatawrite_range+0x34/0x4dc [btrfs] __btrfs_write_out_cache+0x34c/0x480 [btrfs] btrfs_write_out_cache+0x144/0x220 [btrfs] btrfs_start_dirty_block_groups+0x3ac/0x6b0 [btrfs] btrfs_commit_transaction+0xd0/0xbb4 [btrfs] btrfs_sync_fs+0x64/0x1cc [btrfs] sync_fs_one_sb+0x3c/0x50 iterate_supers+0xcc/0x1d4 ksys_sync+0x6c/0xd0 __arm64_sys_sync+0x1c/0x30 invoke_syscall+0x50/0x120 el0_svc_common.constprop.0+0x4c/0xd4 do_el0_svc+0x30/0x9c el0_svc+0x2c/0x54 el0_sync_handler+0x1a8/0x1b0 el0_sync+0x198/0x1c0 ---[ end trace 336f67369ae6e0af ]--- [CAUSE] For subpage case, we can have multiple sectors inside a page, this makes it possible for __extent_writepage() to have part of its page submitted before returning. In btrfs/160, we are using dm-dust to emulate write error, this means for certain pages, we could have everything running fine, but at the end of __extent_writepage(), one of the submitted bios fails due to dm-dust. Then the page is marked Error, and we change @ret from 0 to -EIO. This makes the caller extent_write_cache_pages() to error out, without submitting the remaining pages. Furthermore, since we're erroring out for free space cache, it doesn't really care about the error and will update the inode and retry the writeback. Then we re-run the delalloc range, and will try to insert the same delalloc range while previous delalloc range is still hanging there, triggering the above error. [FIX] The proper fix is to handle errors from __extent_writepage() properly, by ending the remaining ordered extent. But that fix needs the following changes: - Know at exactly which sector the error happened Currently __extent_writepage_io() works for the full page, can't return at which sector we hit the error. - Grab the ordered extent covering the failed sector As a hotfix for subpage case, here we unify the error paths in __extent_writepage(). In fact, the "if (PageError(page))" branch never get executed if @ret is still 0 for non-subpage cases. As for non-subpage case, we never submit current page in __extent_writepage(), but only add current page into bio. The bio can only get submitted in next page. Thus we never get PageError() set due to IO failure, thus when we hit the branch, @ret is never 0. By simply removing that @ret assignment, we let subpage case ignore the IO failure, thus only error out for fatal errors just like regular sectorsize. So that IO error won't be treated as fatal error not trigger the hanging OE problem. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 51 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 5843f4948431..378ae860b3f3 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2789,8 +2789,14 @@ void end_extent_writepage(struct page *page, int err, u64 start, u64 end) btrfs_writepage_endio_finish_ordered(inode, page, start, end, uptodate); if (!uptodate) { - ClearPageUptodate(page); - SetPageError(page); + const struct btrfs_fs_info *fs_info = inode->root->fs_info; + u32 len; + + ASSERT(end + 1 - start <= U32_MAX); + len = end + 1 - start; + + btrfs_page_clear_uptodate(fs_info, page, start, len); + btrfs_page_set_error(fs_info, page, start, len); ret = err < 0 ? err : -EIO; mapping_set_error(page->mapping, ret); } @@ -3793,7 +3799,8 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode, ret = btrfs_run_delalloc_range(inode, page, delalloc_start, delalloc_end, &page_started, nr_written, wbc); if (ret) { - SetPageError(page); + btrfs_page_set_error(inode->root->fs_info, page, + page_offset(page), PAGE_SIZE); /* * btrfs_run_delalloc_range should return < 0 for error * but just in case, we use > 0 here meaning the IO is @@ -4067,7 +4074,8 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, WARN_ON(!PageLocked(page)); - ClearPageError(page); + btrfs_page_clear_error(btrfs_sb(inode->i_sb), page, + page_offset(page), PAGE_SIZE); pg_offset = offset_in_page(i_size); if (page->index > end_index || @@ -4108,10 +4116,39 @@ done: set_page_writeback(page); end_page_writeback(page); } - if (PageError(page)) { - ret = ret < 0 ? ret : -EIO; + /* + * Here we used to have a check for PageError() and then set @ret and + * call end_extent_writepage(). + * + * But in fact setting @ret here will cause different error paths + * between subpage and regular sectorsize. + * + * For regular page size, we never submit current page, but only add + * current page to current bio. + * The bio submission can only happen in next page. + * Thus if we hit the PageError() branch, @ret is already set to + * non-zero value and will not get updated for regular sectorsize. + * + * But for subpage case, it's possible we submit part of current page, + * thus can get PageError() set by submitted bio of the same page, + * while our @ret is still 0. + * + * So here we unify the behavior and don't set @ret. + * Error can still be properly passed to higher layer as page will + * be set error, here we just don't handle the IO failure. + * + * NOTE: This is just a hotfix for subpage. + * The root fix will be properly ending ordered extent when we hit + * an error during writeback. + * + * But that needs a bigger refactoring, as we not only need to grab the + * submitted OE, but also need to know exactly at which bytenr we hit + * the error. + * Currently the full page based __extent_writepage_io() is not + * capable of that. + */ + if (PageError(page)) end_extent_writepage(page, ret, start, page_end); - } unlock_page(page); ASSERT(ret <= 0); return ret; From ac98141d140444fe93e26471d3074c603b70e2ca Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 14 Jul 2021 14:47:17 -0400 Subject: [PATCH 316/417] btrfs: wake up async_delalloc_pages waiters after submit We use the async_delalloc_pages mechanism to make sure that we've completed our async work before trying to continue our delalloc flushing. The reason for this is we need to see any ordered extents that were created by our delalloc flushing. However we're waking up before we do the submit work, which is before we create the ordered extents. This is a pretty wide race window where we could potentially think there are no ordered extents and thus exit shrink_delalloc prematurely. Fix this by waking us up after we've done the work to create ordered extents. CC: stable@vger.kernel.org # 5.4+ Reviewed-by: Nikolay Borisov Signed-off-by: Josef Bacik Signed-off-by: David Sterba --- fs/btrfs/inode.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 7e63b5c0a1dc..73b6413cc9af 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1306,11 +1306,6 @@ static noinline void async_cow_submit(struct btrfs_work *work) nr_pages = (async_chunk->end - async_chunk->start + PAGE_SIZE) >> PAGE_SHIFT; - /* atomic_sub_return implies a barrier */ - if (atomic_sub_return(nr_pages, &fs_info->async_delalloc_pages) < - 5 * SZ_1M) - cond_wake_up_nomb(&fs_info->async_submit_wait); - /* * ->inode could be NULL if async_chunk_start has failed to compress, * in which case we don't have anything to submit, yet we need to @@ -1319,6 +1314,11 @@ static noinline void async_cow_submit(struct btrfs_work *work) */ if (async_chunk->inode) submit_compressed_extents(async_chunk); + + /* atomic_sub_return implies a barrier */ + if (atomic_sub_return(nr_pages, &fs_info->async_delalloc_pages) < + 5 * SZ_1M) + cond_wake_up_nomb(&fs_info->async_submit_wait); } static noinline void async_cow_free(struct btrfs_work *work) From 8197766d806f02d69d2f54563e0b4672bddcc535 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 14 Jul 2021 14:47:18 -0400 Subject: [PATCH 317/417] btrfs: include delalloc related info in dump space info tracepoint In order to debug delalloc flushing issues I added delalloc_bytes and ordered_bytes to this tracepoint to see if they were non-zero when we were going ENOSPC. This was valuable for me and showed me cases where we weren't waiting on ordered extents properly. In order to add this to the tracepoint we need to take away the const modifier for fs_info, as percpu_sum_counter_positive() will change the counter when it adds up the percpu buckets. This is needed to make sure we're getting accurate information at these tracepoints, as the wrong information could send us down the wrong path when debugging problems. Reviewed-by: Nikolay Borisov Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- include/trace/events/btrfs.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index b671b1f2ce0f..97e16a34405b 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -2037,7 +2037,7 @@ TRACE_EVENT(btrfs_convert_extent_bit, ); DECLARE_EVENT_CLASS(btrfs_dump_space_info, - TP_PROTO(const struct btrfs_fs_info *fs_info, + TP_PROTO(struct btrfs_fs_info *fs_info, const struct btrfs_space_info *sinfo), TP_ARGS(fs_info, sinfo), @@ -2057,6 +2057,8 @@ DECLARE_EVENT_CLASS(btrfs_dump_space_info, __field( u64, delayed_refs_reserved ) __field( u64, delayed_reserved ) __field( u64, free_chunk_space ) + __field( u64, delalloc_bytes ) + __field( u64, ordered_bytes ) ), TP_fast_assign_btrfs(fs_info, @@ -2074,6 +2076,8 @@ DECLARE_EVENT_CLASS(btrfs_dump_space_info, __entry->delayed_refs_reserved = fs_info->delayed_refs_rsv.reserved; __entry->delayed_reserved = fs_info->delayed_block_rsv.reserved; __entry->free_chunk_space = atomic64_read(&fs_info->free_chunk_space); + __entry->delalloc_bytes = percpu_counter_sum_positive(&fs_info->delalloc_bytes); + __entry->ordered_bytes = percpu_counter_sum_positive(&fs_info->ordered_bytes); ), TP_printk_btrfs("flags=%s total_bytes=%llu bytes_used=%llu " @@ -2081,7 +2085,8 @@ DECLARE_EVENT_CLASS(btrfs_dump_space_info, "bytes_may_use=%llu bytes_readonly=%llu " "reclaim_size=%llu clamp=%d global_reserved=%llu " "trans_reserved=%llu delayed_refs_reserved=%llu " - "delayed_reserved=%llu chunk_free_space=%llu", + "delayed_reserved=%llu chunk_free_space=%llu " + "delalloc_bytes=%llu ordered_bytes=%llu", __print_flags(__entry->flags, "|", BTRFS_GROUP_FLAGS), __entry->total_bytes, __entry->bytes_used, __entry->bytes_pinned, __entry->bytes_reserved, @@ -2089,11 +2094,12 @@ DECLARE_EVENT_CLASS(btrfs_dump_space_info, __entry->reclaim_size, __entry->clamp, __entry->global_reserved, __entry->trans_reserved, __entry->delayed_refs_reserved, - __entry->delayed_reserved, __entry->free_chunk_space) + __entry->delayed_reserved, __entry->free_chunk_space, + __entry->delalloc_bytes, __entry->ordered_bytes) ); DEFINE_EVENT(btrfs_dump_space_info, btrfs_done_preemptive_reclaim, - TP_PROTO(const struct btrfs_fs_info *fs_info, + TP_PROTO(struct btrfs_fs_info *fs_info, const struct btrfs_space_info *sinfo), TP_ARGS(fs_info, sinfo) ); From fcdef39c03c5beb2a7bcb627addb0b259b9c5164 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 14 Jul 2021 14:47:19 -0400 Subject: [PATCH 318/417] btrfs: enable a tracepoint when we fail tickets When debugging early enospc problems it was useful to have a tracepoint where we failed all tickets so I could check the state of the enospc counters at failure time to validate my fixes. This adds the tracpoint so you can easily get that information. Reviewed-by: Nikolay Borisov Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/space-info.c | 2 ++ include/trace/events/btrfs.h | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index f79bf85f2439..fbd492fe87f9 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -824,6 +824,8 @@ static bool maybe_fail_all_tickets(struct btrfs_fs_info *fs_info, struct reserve_ticket *ticket; u64 tickets_id = space_info->tickets_id; + trace_btrfs_fail_all_tickets(fs_info, space_info); + if (btrfs_test_opt(fs_info, ENOSPC_DEBUG)) { btrfs_info(fs_info, "cannot satisfy tickets, dumping space info"); __btrfs_dump_space_info(fs_info, space_info); diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 97e16a34405b..c7d19eadecc5 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -2104,6 +2104,12 @@ DEFINE_EVENT(btrfs_dump_space_info, btrfs_done_preemptive_reclaim, TP_ARGS(fs_info, sinfo) ); +DEFINE_EVENT(btrfs_dump_space_info, btrfs_fail_all_tickets, + TP_PROTO(struct btrfs_fs_info *fs_info, + const struct btrfs_space_info *sinfo), + TP_ARGS(fs_info, sinfo) +); + TRACE_EVENT(btrfs_reserve_ticket, TP_PROTO(const struct btrfs_fs_info *fs_info, u64 flags, u64 bytes, u64 start_ns, int flush, int error), From 03fe78cc2942c55cc13be5ca42578750f17204a1 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 14 Jul 2021 14:47:20 -0400 Subject: [PATCH 319/417] btrfs: use delalloc_bytes to determine flush amount for shrink_delalloc We have been hitting some early ENOSPC issues in production with more recent kernels, and I tracked it down to us simply not flushing delalloc as aggressively as we should be. With tracing I was seeing us failing all tickets with all of the block rsvs at or around 0, with very little pinned space, but still around 120MiB of outstanding bytes_may_used. Upon further investigation I saw that we were flushing around 14 pages per shrink call for delalloc, despite having around 2GiB of delalloc outstanding. Consider the example of a 8 way machine, all CPUs trying to create a file in parallel, which at the time of this commit requires 5 items to do. Assuming a 16k leaf size, we have 10MiB of total metadata reclaim size waiting on reservations. Now assume we have 128MiB of delalloc outstanding. With our current math we would set items to 20, and then set to_reclaim to 20 * 256k, or 5MiB. Assuming that we went through this loop all 3 times, for both FLUSH_DELALLOC and FLUSH_DELALLOC_WAIT, and then did the full loop twice, we'd only flush 60MiB of the 128MiB delalloc space. This could leave a fair bit of delalloc reservations still hanging around by the time we go to ENOSPC out all the remaining tickets. Fix this two ways. First, change the calculations to be a fraction of the total delalloc bytes on the system. Prior to this change we were calculating based on dirty inodes so our math made more sense, now it's just completely unrelated to what we're actually doing. Second add a FLUSH_DELALLOC_FULL state, that we hold off until we've gone through the flush states at least once. This will empty the system of all delalloc so we're sure to be truly out of space when we start failing tickets. I'm tagging stable 5.10 and forward, because this is where we started using the page stuff heavily again. This affects earlier kernel versions as well, but would be a pain to backport to them as the flushing mechanisms aren't the same. CC: stable@vger.kernel.org # 5.10+ Reviewed-by: Nikolay Borisov Signed-off-by: Josef Bacik Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 9 ++++---- fs/btrfs/space-info.c | 40 +++++++++++++++++++++++++----------- include/trace/events/btrfs.h | 1 + 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 3cccf0f05666..fd3084feb4b5 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2779,10 +2779,11 @@ enum btrfs_flush_state { FLUSH_DELAYED_REFS = 4, FLUSH_DELALLOC = 5, FLUSH_DELALLOC_WAIT = 6, - ALLOC_CHUNK = 7, - ALLOC_CHUNK_FORCE = 8, - RUN_DELAYED_IPUTS = 9, - COMMIT_TRANS = 10, + FLUSH_DELALLOC_FULL = 7, + ALLOC_CHUNK = 8, + ALLOC_CHUNK_FORCE = 9, + RUN_DELAYED_IPUTS = 10, + COMMIT_TRANS = 11, }; int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index fbd492fe87f9..eb90a262563f 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -493,6 +493,11 @@ static void shrink_delalloc(struct btrfs_fs_info *fs_info, long time_left; int loops; + delalloc_bytes = percpu_counter_sum_positive(&fs_info->delalloc_bytes); + ordered_bytes = percpu_counter_sum_positive(&fs_info->ordered_bytes); + if (delalloc_bytes == 0 && ordered_bytes == 0) + return; + /* Calc the number of the pages we need flush for space reservation */ if (to_reclaim == U64_MAX) { items = U64_MAX; @@ -500,22 +505,21 @@ static void shrink_delalloc(struct btrfs_fs_info *fs_info, /* * to_reclaim is set to however much metadata we need to * reclaim, but reclaiming that much data doesn't really track - * exactly, so increase the amount to reclaim by 2x in order to - * make sure we're flushing enough delalloc to hopefully reclaim - * some metadata reservations. + * exactly. What we really want to do is reclaim full inode's + * worth of reservations, however that's not available to us + * here. We will take a fraction of the delalloc bytes for our + * flushing loops and hope for the best. Delalloc will expand + * the amount we write to cover an entire dirty extent, which + * will reclaim the metadata reservation for that range. If + * it's not enough subsequent flush stages will be more + * aggressive. */ + to_reclaim = max(to_reclaim, delalloc_bytes >> 3); items = calc_reclaim_items_nr(fs_info, to_reclaim) * 2; - to_reclaim = items * EXTENT_SIZE_PER_ITEM; } trans = (struct btrfs_trans_handle *)current->journal_info; - delalloc_bytes = percpu_counter_sum_positive( - &fs_info->delalloc_bytes); - ordered_bytes = percpu_counter_sum_positive(&fs_info->ordered_bytes); - if (delalloc_bytes == 0 && ordered_bytes == 0) - return; - /* * If we are doing more ordered than delalloc we need to just wait on * ordered extents, otherwise we'll waste time trying to flush delalloc @@ -595,8 +599,11 @@ static void flush_space(struct btrfs_fs_info *fs_info, break; case FLUSH_DELALLOC: case FLUSH_DELALLOC_WAIT: + case FLUSH_DELALLOC_FULL: + if (state == FLUSH_DELALLOC_FULL) + num_bytes = U64_MAX; shrink_delalloc(fs_info, space_info, num_bytes, - state == FLUSH_DELALLOC_WAIT, for_preempt); + state != FLUSH_DELALLOC, for_preempt); break; case FLUSH_DELAYED_REFS_NR: case FLUSH_DELAYED_REFS: @@ -906,6 +913,14 @@ static void btrfs_async_reclaim_metadata_space(struct work_struct *work) commit_cycles--; } + /* + * We do not want to empty the system of delalloc unless we're + * under heavy pressure, so allow one trip through the flushing + * logic before we start doing a FLUSH_DELALLOC_FULL. + */ + if (flush_state == FLUSH_DELALLOC_FULL && !commit_cycles) + flush_state++; + /* * We don't want to force a chunk allocation until we've tried * pretty hard to reclaim space. Think of the case where we @@ -1069,7 +1084,7 @@ static void btrfs_preempt_reclaim_metadata_space(struct work_struct *work) * so if we now have space to allocate do the force chunk allocation. */ static const enum btrfs_flush_state data_flush_states[] = { - FLUSH_DELALLOC_WAIT, + FLUSH_DELALLOC_FULL, RUN_DELAYED_IPUTS, COMMIT_TRANS, ALLOC_CHUNK_FORCE, @@ -1158,6 +1173,7 @@ static const enum btrfs_flush_state evict_flush_states[] = { FLUSH_DELAYED_REFS, FLUSH_DELALLOC, FLUSH_DELALLOC_WAIT, + FLUSH_DELALLOC_FULL, ALLOC_CHUNK, COMMIT_TRANS, }; diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index c7d19eadecc5..8f58fd95efc7 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -94,6 +94,7 @@ struct btrfs_space_info; EM( FLUSH_DELAYED_ITEMS, "FLUSH_DELAYED_ITEMS") \ EM( FLUSH_DELALLOC, "FLUSH_DELALLOC") \ EM( FLUSH_DELALLOC_WAIT, "FLUSH_DELALLOC_WAIT") \ + EM( FLUSH_DELALLOC_FULL, "FLUSH_DELALLOC_FULL") \ EM( FLUSH_DELAYED_REFS_NR, "FLUSH_DELAYED_REFS_NR") \ EM( FLUSH_DELAYED_REFS, "FLUSH_ELAYED_REFS") \ EM( ALLOC_CHUNK, "ALLOC_CHUNK") \ From e16460707e94c3d4c1b5418cb68b28b8efa903b2 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 14 Jul 2021 14:47:21 -0400 Subject: [PATCH 320/417] btrfs: wait on async extents when flushing delalloc I've been debugging an early ENOSPC problem in production and finally root caused it to this problem. When we switched to the per-inode in 38d715f494f2 ("btrfs: use btrfs_start_delalloc_roots in shrink_delalloc") I pulled out the async extent handling, because we were doing the correct thing by calling filemap_flush() if we had async extents set. This would properly wait on any async extents by locking the page in the second flush, thus making sure our ordered extents were properly set up. However when I switched us back to page based flushing, I used sync_inode(), which allows us to pass in our own wbc. The problem here is that sync_inode() is smarter than the filemap_* helpers, it tries to avoid calling writepages at all. This means that our second call could skip calling do_writepages altogether, and thus not wait on the pagelock for the async helpers. This means we could come back before any ordered extents were created and then simply continue on in our flushing mechanisms and ENOSPC out when we have plenty of space to use. Fix this by putting back the async pages logic in shrink_delalloc. This allows us to bulk write out everything that we need to, and then we can wait in one place for the async helpers to catch up, and then wait on any ordered extents that are created. Fixes: e076ab2a2ca7 ("btrfs: shrink delalloc pages instead of full inodes") CC: stable@vger.kernel.org # 5.10+ Reviewed-by: Nikolay Borisov Signed-off-by: Josef Bacik Signed-off-by: David Sterba --- fs/btrfs/inode.c | 4 ---- fs/btrfs/space-info.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 73b6413cc9af..25eb214f56ac 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9879,10 +9879,6 @@ static int start_delalloc_inodes(struct btrfs_root *root, &work->work); } else { ret = sync_inode(inode, wbc); - if (!ret && - test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT, - &BTRFS_I(inode)->runtime_flags)) - ret = sync_inode(inode, wbc); btrfs_add_delayed_iput(inode); if (ret || wbc->nr_to_write <= 0) goto out; diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index eb90a262563f..d9c8d738678f 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -532,9 +532,49 @@ static void shrink_delalloc(struct btrfs_fs_info *fs_info, while ((delalloc_bytes || ordered_bytes) && loops < 3) { u64 temp = min(delalloc_bytes, to_reclaim) >> PAGE_SHIFT; long nr_pages = min_t(u64, temp, LONG_MAX); + int async_pages; btrfs_start_delalloc_roots(fs_info, nr_pages, true); + /* + * We need to make sure any outstanding async pages are now + * processed before we continue. This is because things like + * sync_inode() try to be smart and skip writing if the inode is + * marked clean. We don't use filemap_fwrite for flushing + * because we want to control how many pages we write out at a + * time, thus this is the only safe way to make sure we've + * waited for outstanding compressed workers to have started + * their jobs and thus have ordered extents set up properly. + * + * This exists because we do not want to wait for each + * individual inode to finish its async work, we simply want to + * start the IO on everybody, and then come back here and wait + * for all of the async work to catch up. Once we're done with + * that we know we'll have ordered extents for everything and we + * can decide if we wait for that or not. + * + * If we choose to replace this in the future, make absolutely + * sure that the proper waiting is being done in the async case, + * as there have been bugs in that area before. + */ + async_pages = atomic_read(&fs_info->async_delalloc_pages); + if (!async_pages) + goto skip_async; + + /* + * We don't want to wait forever, if we wrote less pages in this + * loop than we have outstanding, only wait for that number of + * pages, otherwise we can wait for all async pages to finish + * before continuing. + */ + if (async_pages > nr_pages) + async_pages -= nr_pages; + else + async_pages = 0; + wait_event(fs_info->async_submit_wait, + atomic_read(&fs_info->async_delalloc_pages) <= + async_pages); +skip_async: loops++; if (wait_ordered && !trans) { btrfs_wait_ordered_roots(fs_info, items, 0, (u64)-1); From 5a798493b8f30121363359bba834392f044c169b Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 14 Jul 2021 14:47:22 -0400 Subject: [PATCH 321/417] fs: add a filemap_fdatawrite_wbc helper Btrfs sometimes needs to flush dirty pages on a bunch of dirty inodes in order to reclaim metadata reservations. Unfortunately most helpers in this area are too smart for us: 1) The normal filemap_fdata* helpers only take range and sync modes, and don't give any indication of how much was written, so we can only flush full inodes, which isn't what we want in most cases. 2) The normal writeback path requires us to have the s_umount sem held, but we can't unconditionally take it in this path because we could deadlock. 3) The normal writeback path also skips inodes with I_SYNC set if we write with WB_SYNC_NONE. This isn't the behavior we want under heavy ENOSPC pressure, we want to actually make sure the pages are under writeback before returning, and if another thread is in the middle of writing the file we may return before they're under writeback and miss our ordered extents and not properly wait for completion. 4) sync_inode() uses the normal writeback path and has the same problem as #3. What we really want is to call do_writepages() with our wbc. This way we can make sure that writeback is actually started on the pages, and we can control how many pages are written as a whole as we write many inodes using the same wbc. Accomplish this with a new helper that does just that so we can use it for our ENOSPC flushing infrastructure. Reviewed-by: Nikolay Borisov Reviewed-by: Christoph Hellwig Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- include/linux/fs.h | 2 ++ mm/filemap.c | 36 +++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index 640574294216..452cd4843843 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2891,6 +2891,8 @@ extern int filemap_fdatawrite_range(struct address_space *mapping, loff_t start, loff_t end); extern int filemap_check_errors(struct address_space *mapping); extern void __filemap_set_wb_err(struct address_space *mapping, int err); +int filemap_fdatawrite_wbc(struct address_space *mapping, + struct writeback_control *wbc); static inline int filemap_write_and_wait(struct address_space *mapping) { diff --git a/mm/filemap.c b/mm/filemap.c index d1458ecf2f51..034d370d4ebb 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -377,6 +377,32 @@ static int filemap_check_and_keep_errors(struct address_space *mapping) return 0; } +/** + * filemap_fdatawrite_wbc - start writeback on mapping dirty pages in range + * @mapping: address space structure to write + * @wbc: the writeback_control controlling the writeout + * + * Call writepages on the mapping using the provided wbc to control the + * writeout. + * + * Return: %0 on success, negative error code otherwise. + */ +int filemap_fdatawrite_wbc(struct address_space *mapping, + struct writeback_control *wbc) +{ + int ret; + + if (!mapping_can_writeback(mapping) || + !mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) + return 0; + + wbc_attach_fdatawrite_inode(wbc, mapping->host); + ret = do_writepages(mapping, wbc); + wbc_detach_inode(wbc); + return ret; +} +EXPORT_SYMBOL(filemap_fdatawrite_wbc); + /** * __filemap_fdatawrite_range - start writeback on mapping dirty pages in range * @mapping: address space structure to write @@ -397,7 +423,6 @@ static int filemap_check_and_keep_errors(struct address_space *mapping) int __filemap_fdatawrite_range(struct address_space *mapping, loff_t start, loff_t end, int sync_mode) { - int ret; struct writeback_control wbc = { .sync_mode = sync_mode, .nr_to_write = LONG_MAX, @@ -405,14 +430,7 @@ int __filemap_fdatawrite_range(struct address_space *mapping, loff_t start, .range_end = end, }; - if (!mapping_can_writeback(mapping) || - !mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) - return 0; - - wbc_attach_fdatawrite_inode(&wbc, mapping->host); - ret = do_writepages(mapping, &wbc); - wbc_detach_inode(&wbc); - return ret; + return filemap_fdatawrite_wbc(mapping, &wbc); } static inline int __filemap_fdatawrite(struct address_space *mapping, From b3776305278e5937366f512d3e655ace13b8b027 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 14 Jul 2021 14:47:23 -0400 Subject: [PATCH 322/417] btrfs: use the filemap_fdatawrite_wbc helper for delalloc shrinking sync_inode() has some holes that can cause problems if we're under heavy ENOSPC pressure. If there's writeback running on a separate thread sync_inode() will skip writing the inode altogether. What we really want is to make sure writeback has been started on all the pages to make sure we can see the ordered extents and wait on them if appropriate. Switch to this new helper which will allow us to accomplish this and avoid ENOSPC'ing early. Reviewed-by: Nikolay Borisov Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 25eb214f56ac..c917e3117842 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9878,7 +9878,7 @@ static int start_delalloc_inodes(struct btrfs_root *root, btrfs_queue_work(root->fs_info->flush_workers, &work->work); } else { - ret = sync_inode(inode, wbc); + ret = filemap_fdatawrite_wbc(inode->i_mapping, wbc); btrfs_add_delayed_iput(inode); if (ret || wbc->nr_to_write <= 0) goto out; From 25d23cd01621b740ce3e8f4f0fab40e24d163462 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 14 Jul 2021 14:47:24 -0400 Subject: [PATCH 323/417] 9p: migrate from sync_inode to filemap_fdatawrite_wbc We're going to remove sync_inode, so migrate to filemap_fdatawrite_wbc instead. Reviewed-by: Christoph Hellwig Reviewed-by: Nikolay Borisov Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/9p/vfs_file.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 59c32c9b799f..6b64e8391f30 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -625,12 +625,7 @@ static void v9fs_mmap_vm_close(struct vm_area_struct *vma) p9_debug(P9_DEBUG_VFS, "9p VMA close, %p, flushing", vma); inode = file_inode(vma->vm_file); - - if (!mapping_can_writeback(inode->i_mapping)) - wbc.nr_to_write = 0; - - might_sleep(); - sync_inode(inode, &wbc); + filemap_fdatawrite_wbc(inode->i_mapping, &wbc); } From 5662c967c69dfd162a0667d69bad776939bedf85 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 14 Jul 2021 14:47:25 -0400 Subject: [PATCH 324/417] fs: kill sync_inode Now that all users of sync_inode() have been deleted, remove sync_inode(). Reviewed-by: Christoph Hellwig Reviewed-by: Nikolay Borisov Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/fs-writeback.c | 19 +------------------ include/linux/fs.h | 1 - 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 4c3370548982..eb57dade6076 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -2729,23 +2729,6 @@ int write_inode_now(struct inode *inode, int sync) } EXPORT_SYMBOL(write_inode_now); -/** - * sync_inode - write an inode and its pages to disk. - * @inode: the inode to sync - * @wbc: controls the writeback mode - * - * sync_inode() will write an inode and its pages to disk. It will also - * correctly update the inode on its superblock's dirty inode lists and will - * update inode->i_state. - * - * The caller must have a ref on the inode. - */ -int sync_inode(struct inode *inode, struct writeback_control *wbc) -{ - return writeback_single_inode(inode, wbc); -} -EXPORT_SYMBOL(sync_inode); - /** * sync_inode_metadata - write an inode to disk * @inode: the inode to sync @@ -2762,6 +2745,6 @@ int sync_inode_metadata(struct inode *inode, int wait) .nr_to_write = 0, /* metadata-only */ }; - return sync_inode(inode, &wbc); + return writeback_single_inode(inode, &wbc); } EXPORT_SYMBOL(sync_inode_metadata); diff --git a/include/linux/fs.h b/include/linux/fs.h index 452cd4843843..1751addcb36e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2457,7 +2457,6 @@ static inline void file_accessed(struct file *file) extern int file_modified(struct file *file); -int sync_inode(struct inode *inode, struct writeback_control *wbc); int sync_inode_metadata(struct inode *inode, int wait); struct file_system_type { From e83502ca5f1e1f03fb1393008ec22d17e7dc9882 Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Wed, 21 Jul 2021 21:43:32 +0900 Subject: [PATCH 325/417] block: fix argument type of bio_trim() The function bio_trim has offset and size arguments that are declared as int. The callers of this function use sector_t type when passing the offset and size, e.g. drivers/md/raid1.c:narrow_write_error() and drivers/md/raid1.c:narrow_write_error(). Change offset and size arguments to sector_t type for bio_trim(). Also, add WARN_ON_ONCE() to catch their overflow. Reviewed-by: Christoph Hellwig Signed-off-by: Chaitanya Kulkarni Signed-off-by: Naohiro Aota Reviewed-by: David Sterba Signed-off-by: David Sterba --- block/bio.c | 12 +++++++----- include/linux/bio.h | 2 +- include/linux/blk_types.h | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/block/bio.c b/block/bio.c index 1fab762e079b..77cadcba93b9 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1463,12 +1463,15 @@ EXPORT_SYMBOL(bio_split); * @bio: bio to trim * @offset: number of sectors to trim from the front of @bio * @size: size we want to trim @bio to, in sectors + * + * This function is typically used for bios that are cloned and submitted + * to the underlying device in parts. */ -void bio_trim(struct bio *bio, int offset, int size) +void bio_trim(struct bio *bio, sector_t offset, sector_t size) { - /* 'bio' is a cloned bio which we need to trim to match - * the given offset and size. - */ + if (WARN_ON_ONCE(offset > BIO_MAX_SECTORS || size > BIO_MAX_SECTORS || + offset + size > bio->bi_iter.bi_size)) + return; size <<= 9; if (offset == 0 && size == bio->bi_iter.bi_size) @@ -1479,7 +1482,6 @@ void bio_trim(struct bio *bio, int offset, int size) if (bio_integrity(bio)) bio_integrity_trim(bio); - } EXPORT_SYMBOL_GPL(bio_trim); diff --git a/include/linux/bio.h b/include/linux/bio.h index 2203b686e1f0..8a451d77b573 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -375,7 +375,7 @@ static inline void bip_set_seed(struct bio_integrity_payload *bip, #endif /* CONFIG_BLK_DEV_INTEGRITY */ -extern void bio_trim(struct bio *bio, int offset, int size); +void bio_trim(struct bio *bio, sector_t offset, sector_t size); extern struct bio *bio_split(struct bio *bio, int sectors, gfp_t gfp, struct bio_set *bs); diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 290f9061b29a..bca4d33876d4 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -281,6 +281,7 @@ struct bio { }; #define BIO_RESET_BYTES offsetof(struct bio, bi_max_vecs) +#define BIO_MAX_SECTORS (UINT_MAX >> SECTOR_SHIFT) /* * bio flags From 21dda654d4808833668b380e5f0b9befff8640ae Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Wed, 21 Jul 2021 21:43:33 +0900 Subject: [PATCH 326/417] btrfs: fix argument type of btrfs_bio_clone_partial() The offset and can never be negative use unsigned int instead of int type for them. Reviewed-by: Christoph Hellwig Signed-off-by: Chaitanya Kulkarni Signed-off-by: Naohiro Aota Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 4 +++- fs/btrfs/extent_io.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 378ae860b3f3..3abb9365fbe9 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3161,11 +3161,13 @@ struct bio *btrfs_io_bio_alloc(unsigned int nr_iovecs) return bio; } -struct bio *btrfs_bio_clone_partial(struct bio *orig, int offset, int size) +struct bio *btrfs_bio_clone_partial(struct bio *orig, u64 offset, u64 size) { struct bio *bio; struct btrfs_io_bio *btrfs_bio; + ASSERT(offset <= UINT_MAX && size <= UINT_MAX); + /* this will never fail when it's backed by a bioset */ bio = bio_clone_fast(orig, GFP_NOFS, &btrfs_bioset); ASSERT(bio); diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 62027f551b44..53abdc280451 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -280,7 +280,7 @@ void extent_clear_unlock_delalloc(struct btrfs_inode *inode, u64 start, u64 end, struct bio *btrfs_bio_alloc(u64 first_byte); struct bio *btrfs_io_bio_alloc(unsigned int nr_iovecs); struct bio *btrfs_bio_clone(struct bio *bio); -struct bio *btrfs_bio_clone_partial(struct bio *orig, int offset, int size); +struct bio *btrfs_bio_clone_partial(struct bio *orig, u64 offset, u64 size); int repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 start, u64 length, u64 logical, struct page *page, From 42b5d73b5d231bbe38639c6dec913505d7f55372 Mon Sep 17 00:00:00 2001 From: Naohiro Aota Date: Wed, 21 Jul 2021 21:43:34 +0900 Subject: [PATCH 327/417] btrfs: drop unnecessary ASSERT from btrfs_submit_direct() When on SINGLE block group, btrfs_get_io_geometry() will return "the size of the block group - the offset of the logical address within the block group" as geom.len. Since we allow up to 8 GiB zone size on zoned filesystem, we can have up to 8 GiB block group, so can have up to 8 GiB geom.len as well. With this setup, we easily hit the "ASSERT(geom.len <= INT_MAX);". The ASSERT looks like to guard btrfs_bio_clone_partial() and bio_trim() which both take "int" (now u64 due to the previous patch). So to be precise the ASSERT should check if clone_len <= UINT_MAX. But actually, clone_len is already capped by bio.bi_iter.bi_size which is unsigned int. So the ASSERT is not necessary. Drop the ASSERT and properly compare submit_len and geom.len in u64. Then, let the implicit casting to convert it to u64. Signed-off-by: Naohiro Aota Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c917e3117842..3ed88f2ac393 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8239,8 +8239,8 @@ static blk_qc_t btrfs_submit_direct(struct inode *inode, struct iomap *iomap, u64 start_sector; int async_submit = 0; u64 submit_len; - int clone_offset = 0; - int clone_len; + u64 clone_offset = 0; + u64 clone_len; u64 logical; int ret; blk_status_t status; @@ -8288,9 +8288,9 @@ static blk_qc_t btrfs_submit_direct(struct inode *inode, struct iomap *iomap, status = errno_to_blk_status(ret); goto out_err_em; } - ASSERT(geom.len <= INT_MAX); - clone_len = min_t(int, submit_len, geom.len); + clone_len = min(submit_len, geom.len); + ASSERT(clone_len <= UINT_MAX); /* * This will never fail as it's passing GPF_NOFS and From 6e8e777deb5cbff76bcd34b1f45bc747f48e8abe Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 27 Jul 2021 11:24:44 +0100 Subject: [PATCH 328/417] btrfs: eliminate some false positives when checking if inode was logged When checking if an inode was previously logged in the current transaction through the helper inode_logged(), we can return some false positives that can be easily eliminated. These correspond to the cases where an inode has a ->logged_trans value that is not zero and its value is smaller then the ID of the current transaction. This means we know exactly that the inode was never logged before in the current transaction, so we can return false and avoid the callers to do extra work: 1) Having btrfs_del_dir_entries_in_log() and btrfs_del_inode_ref_in_log() unnecessarily join a log transaction and do deletion searches in a log tree that will not find anything. This just adds unnecessary contention on extent buffer locks; 2) Having btrfs_log_new_name() unnecessarily log an inode when it is not needed. If the inode was not logged before, we don't need to log it in LOG_INODE_EXISTS mode. So just make sure that any false positive only happens when ->logged_trans has a value of 0. Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/tree-log.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 191dea1d2416..d09202e0c9df 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -3421,14 +3421,10 @@ int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans, } /* - * Check if an inode was logged in the current transaction. We can't always rely - * on an inode's logged_trans value, because it's an in-memory only field and - * therefore not persisted. This means that its value is lost if the inode gets - * evicted and loaded again from disk (in which case it has a value of 0, and - * certainly it is smaller then any possible transaction ID), when that happens - * the full_sync flag is set in the inode's runtime flags, so on that case we - * assume eviction happened and ignore the logged_trans value, assuming the - * worst case, that the inode was logged before in the current transaction. + * Check if an inode was logged in the current transaction. This may often + * return some false positives, because logged_trans is an in memory only field, + * not persisted anywhere. This is meant to be used in contexts where a false + * positive has no functional consequences. */ static bool inode_logged(struct btrfs_trans_handle *trans, struct btrfs_inode *inode) @@ -3436,7 +3432,18 @@ static bool inode_logged(struct btrfs_trans_handle *trans, if (inode->logged_trans == trans->transid) return true; - if (inode->last_trans == trans->transid && + /* + * The inode's logged_trans is always 0 when we load it (because it is + * not persisted in the inode item or elsewhere). So if it is 0, the + * inode was last modified in the current transaction and has the + * full_sync flag set, then the inode may have been logged before in + * the current transaction, then evicted and loaded again in the current + * transaction - or may have never been logged in the current transaction, + * but since we can not be sure, we have to assume it was, otherwise our + * callers can leave an inconsistent log. + */ + if (inode->logged_trans == 0 && + inode->last_trans == trans->transid && test_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &inode->runtime_flags) && !test_bit(BTRFS_FS_LOG_RECOVERING, &trans->fs_info->flags)) return true; From bd54f381a12ac695593271a663d36d14220215b2 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 27 Jul 2021 11:24:45 +0100 Subject: [PATCH 329/417] btrfs: do not pin logs too early during renames During renames we pin the logs of the roots a bit too early, before the calls to btrfs_insert_inode_ref(). We can pin the logs after those calls, since those will not change anything in a log tree. In a scenario where we have multiple and diverse filesystem operations running in parallel, those calls can take a significant amount of time, due to lock contention on extent buffers, and delay log commits from other tasks for longer than necessary. So just pin logs after calls to btrfs_insert_inode_ref() and right before the first operation that can update a log tree. The following script that uses dbench was used for testing: $ cat dbench-test.sh #!/bin/bash DEV=/dev/nvme0n1 MNT=/mnt/nvme0n1 MOUNT_OPTIONS="-o ssd" MKFS_OPTIONS="-m single -d single" echo "performance" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor umount $DEV &> /dev/null mkfs.btrfs -f $MKFS_OPTIONS $DEV mount $MOUNT_OPTIONS $DEV $MNT dbench -D $MNT -t 120 16 umount $MNT The tests were run on a machine with 12 cores, 64G of RAN, a NVMe device and using a non-debug kernel config (Debian's default config). The results compare a branch without this patch and without the previous patch in the series, that has the subject: "btrfs: eliminate some false positives when checking if inode was logged" Versus the same branch with these two patches applied. dbench with 8 clients, results before: Operation Count AvgLat MaxLat ---------------------------------------- NTCreateX 4391359 0.009 249.745 Close 3225882 0.001 3.243 Rename 185953 0.065 240.643 Unlink 886669 0.049 249.906 Deltree 112 2.455 217.433 Mkdir 56 0.002 0.004 Qpathinfo 3980281 0.004 3.109 Qfileinfo 697579 0.001 0.187 Qfsinfo 729780 0.002 2.424 Sfileinfo 357764 0.004 1.415 Find 1538861 0.016 4.863 WriteX 2189666 0.010 3.327 ReadX 6883443 0.002 0.729 LockX 14298 0.002 0.073 UnlockX 14298 0.001 0.042 Flush 307777 2.447 303.663 Throughput 1149.6 MB/sec 8 clients 8 procs max_latency=303.666 ms dbench with 8 clients, results after: Operation Count AvgLat MaxLat ---------------------------------------- NTCreateX 4269920 0.009 213.532 Close 3136653 0.001 0.690 Rename 180805 0.082 213.858 Unlink 862189 0.050 172.893 Deltree 112 2.998 218.328 Mkdir 56 0.002 0.003 Qpathinfo 3870158 0.004 5.072 Qfileinfo 678375 0.001 0.194 Qfsinfo 709604 0.002 0.485 Sfileinfo 347850 0.004 1.304 Find 1496310 0.017 5.504 WriteX 2129613 0.010 2.882 ReadX 6693066 0.002 1.517 LockX 13902 0.002 0.075 UnlockX 13902 0.001 0.055 Flush 299276 2.511 220.189 Throughput 1187.33 MB/sec 8 clients 8 procs max_latency=220.194 ms +3.2% throughput, -31.8% max latency dbench with 16 clients, results before: Operation Count AvgLat MaxLat ---------------------------------------- NTCreateX 5978334 0.028 156.507 Close 4391598 0.001 1.345 Rename 253136 0.241 155.057 Unlink 1207220 0.182 257.344 Deltree 160 6.123 36.277 Mkdir 80 0.003 0.005 Qpathinfo 5418817 0.012 6.867 Qfileinfo 949929 0.001 0.941 Qfsinfo 993560 0.002 1.386 Sfileinfo 486904 0.004 2.829 Find 2095088 0.059 8.164 WriteX 2982319 0.017 9.029 ReadX 9371484 0.002 4.052 LockX 19470 0.002 0.461 UnlockX 19470 0.001 0.990 Flush 418936 2.740 347.902 Throughput 1495.31 MB/sec 16 clients 16 procs max_latency=347.909 ms dbench with 16 clients, results after: Operation Count AvgLat MaxLat ---------------------------------------- NTCreateX 5711833 0.029 131.240 Close 4195897 0.001 1.732 Rename 241849 0.204 147.831 Unlink 1153341 0.184 231.322 Deltree 160 6.086 30.198 Mkdir 80 0.003 0.021 Qpathinfo 5177011 0.012 7.150 Qfileinfo 907768 0.001 0.793 Qfsinfo 949205 0.002 1.431 Sfileinfo 465317 0.004 2.454 Find 2001541 0.058 7.819 WriteX 2850661 0.017 9.110 ReadX 8952289 0.002 3.991 LockX 18596 0.002 0.655 UnlockX 18596 0.001 0.179 Flush 400342 2.879 293.607 Throughput 1565.73 MB/sec 16 clients 16 procs max_latency=293.611 ms +4.6% throughput, -16.9% max latency Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/inode.c | 48 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 3ed88f2ac393..bfa3991e476b 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9350,8 +9350,6 @@ static int btrfs_rename_exchange(struct inode *old_dir, /* force full log commit if subvolume involved. */ btrfs_set_log_full_commit(trans); } else { - btrfs_pin_log_trans(root); - root_log_pinned = true; ret = btrfs_insert_inode_ref(trans, dest, new_dentry->d_name.name, new_dentry->d_name.len, @@ -9368,8 +9366,6 @@ static int btrfs_rename_exchange(struct inode *old_dir, /* force full log commit if subvolume involved. */ btrfs_set_log_full_commit(trans); } else { - btrfs_pin_log_trans(dest); - dest_log_pinned = true; ret = btrfs_insert_inode_ref(trans, root, old_dentry->d_name.name, old_dentry->d_name.len, @@ -9400,6 +9396,29 @@ static int btrfs_rename_exchange(struct inode *old_dir, BTRFS_I(new_inode), 1); } + /* + * Now pin the logs of the roots. We do it to ensure that no other task + * can sync the logs while we are in progress with the rename, because + * that could result in an inconsistency in case any of the inodes that + * are part of this rename operation were logged before. + * + * We pin the logs even if at this precise moment none of the inodes was + * logged before. This is because right after we checked for that, some + * other task fsyncing some other inode not involved with this rename + * operation could log that one of our inodes exists. + * + * We don't need to pin the logs before the above calls to + * btrfs_insert_inode_ref(), since those don't ever need to change a log. + */ + if (old_ino != BTRFS_FIRST_FREE_OBJECTID) { + btrfs_pin_log_trans(root); + root_log_pinned = true; + } + if (new_ino != BTRFS_FIRST_FREE_OBJECTID) { + btrfs_pin_log_trans(dest); + dest_log_pinned = true; + } + /* src is a subvolume */ if (old_ino == BTRFS_FIRST_FREE_OBJECTID) { ret = btrfs_unlink_subvol(trans, old_dir, old_dentry); @@ -9652,8 +9671,6 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, /* force full log commit if subvolume involved. */ btrfs_set_log_full_commit(trans); } else { - btrfs_pin_log_trans(root); - log_pinned = true; ret = btrfs_insert_inode_ref(trans, dest, new_dentry->d_name.name, new_dentry->d_name.len, @@ -9677,6 +9694,25 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (unlikely(old_ino == BTRFS_FIRST_FREE_OBJECTID)) { ret = btrfs_unlink_subvol(trans, old_dir, old_dentry); } else { + /* + * Now pin the log. We do it to ensure that no other task can + * sync the log while we are in progress with the rename, as + * that could result in an inconsistency in case any of the + * inodes that are part of this rename operation were logged + * before. + * + * We pin the log even if at this precise moment none of the + * inodes was logged before. This is because right after we + * checked for that, some other task fsyncing some other inode + * not involved with this rename operation could log that one of + * our inodes exists. + * + * We don't need to pin the logs before the above call to + * btrfs_insert_inode_ref(), since that does not need to change + * a log. + */ + btrfs_pin_log_trans(root); + log_pinned = true; ret = __btrfs_unlink_inode(trans, root, BTRFS_I(old_dir), BTRFS_I(d_inode(old_dentry)), old_dentry->d_name.name, From b2f78e88052bc0bee56bbf646d245fcfb431a873 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 22 Jul 2021 20:54:37 +0200 Subject: [PATCH 330/417] btrfs: allow degenerate raid0/raid10 The data on raid0 and raid10 are supposed to be spread over multiple devices, so the minimum constraints are set to 2 and 4 respectively. This is an artificial limit and there's some interest to remove it. Change this to allow raid0 on one device and raid10 on two devices. This works as expected eg. when converting or removing devices. The only difference is when raid0 on two devices gets one device removed. Unpatched would silently create a single profile, while newly it would be raid0. The motivation is to allow to preserve the profile type as long as it possible for some intermediate state (device removal, conversion), or when there are disks of different size, with raid0 the otherwise unusable space of the last device will be used too. Similarly for raid10, though the two largest devices would need to be the same. Unpatched kernel will mount and use the degenerate profiles just fine but won't allow any operation that would not satisfy the stricter device number constraints, eg. not allowing to go from 3 to 2 devices for raid10 or various profile conversions. Example output: # btrfs fi us -T . Overall: Device size: 10.00GiB Device allocated: 1.01GiB Device unallocated: 8.99GiB Device missing: 0.00B Used: 200.61MiB Free (estimated): 9.79GiB (min: 9.79GiB) Free (statfs, df): 9.79GiB Data ratio: 1.00 Metadata ratio: 1.00 Global reserve: 3.25MiB (used: 0.00B) Multiple profiles: no Data Metadata System Id Path RAID0 single single Unallocated -- ---------- --------- --------- -------- ----------- 1 /dev/sda10 1.00GiB 8.00MiB 1.00MiB 8.99GiB -- ---------- --------- --------- -------- ----------- Total 1.00GiB 8.00MiB 1.00MiB 8.99GiB Used 200.25MiB 352.00KiB 16.00KiB # btrfs dev us . /dev/sda10, ID: 1 Device size: 10.00GiB Device slack: 0.00B Data,RAID0/1: 1.00GiB Metadata,single: 8.00MiB System,single: 1.00MiB Unallocated: 8.99GiB Note "Data,RAID0/1", with btrfs-progs 5.13+ the number of devices per profile is printed. Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 8e61307ffad2..8526cc39faa9 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -38,7 +38,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .sub_stripes = 2, .dev_stripes = 1, .devs_max = 0, /* 0 == as many as possible */ - .devs_min = 4, + .devs_min = 2, .tolerated_failures = 1, .devs_increment = 2, .ncopies = 2, @@ -103,7 +103,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .sub_stripes = 1, .dev_stripes = 1, .devs_max = 0, - .devs_min = 2, + .devs_min = 1, .tolerated_failures = 0, .devs_increment = 1, .ncopies = 1, From 7361b4ae03d9f3325852eec1a7395ec5f1ff802d Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 28 Jul 2021 14:05:05 +0800 Subject: [PATCH 331/417] btrfs: remove the dead comment in writepage_delalloc() When btrfs_run_delalloc_range() failed, we will error out. But there is a strange comment mentioning that btrfs_run_delalloc_range() could have returned value >0 to indicate the IO has already started. Commit 40f765805f08 ("Btrfs: split up __extent_writepage to lower stack usage") introduced the comment, but unfortunately at that time, we were already using @page_started to indicate that case, and still return 0. Furthermore, even if that comment was right (which is not), we would return -EIO if the IO had already started. By all means the comment is incorrect, just remove the comment along with the dead check. Just to be extra safe, add an ASSERT() in btrfs_run_delalloc_range() to make sure we either return 0 or error, no positive return value. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 8 +------- fs/btrfs/inode.c | 1 + 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 3abb9365fbe9..66888b10b00d 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3803,13 +3803,7 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode, if (ret) { btrfs_page_set_error(inode->root->fs_info, page, page_offset(page), PAGE_SIZE); - /* - * btrfs_run_delalloc_range should return < 0 for error - * but just in case, we use > 0 here meaning the IO is - * started, so we don't want to return > 0 unless - * things are going well. - */ - return ret < 0 ? ret : -EIO; + return ret; } /* * delalloc_end is already one less than the total length, so diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index bfa3991e476b..949f6a6c616a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1962,6 +1962,7 @@ int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct page *locked_page ret = cow_file_range_async(inode, wbc, locked_page, start, end, page_started, nr_written); } + ASSERT(ret <= 0); if (ret) btrfs_cleanup_ordered_extents(inode, locked_page, start, end - start + 1); From efc222f8d79c0431c92ae8c72bc62d2e6bcf0a00 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Wed, 28 Jul 2021 07:03:05 +0800 Subject: [PATCH 332/417] btrfs: simplify return values in btrfs_check_raid_min_devices Function btrfs_check_raid_min_devices() returns error code from the enum btrfs_err_code and it starts from 1. So there is no need to check if ret is > 0. So drop this check and also drop the local variable ret. Signed-off-by: Anand Jain Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 8526cc39faa9..230192d097c4 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1949,12 +1949,8 @@ static int btrfs_check_raid_min_devices(struct btrfs_fs_info *fs_info, if (!(all_avail & btrfs_raid_array[i].bg_flag)) continue; - if (num_devices < btrfs_raid_array[i].devs_min) { - int ret = btrfs_raid_array[i].mindev_error; - - if (ret) - return ret; - } + if (num_devices < btrfs_raid_array[i].devs_min) + return btrfs_raid_array[i].mindev_error; } return 0; From 77eea05e7851d910b7992c8c237a6b5d462050da Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Wed, 30 Jun 2021 13:01:48 -0700 Subject: [PATCH 333/417] btrfs: add ro compat flags to inodes Currently, inode flags are fully backwards incompatible in btrfs. If we introduce a new inode flag, then tree-checker will detect it and fail. This can even cause us to fail to mount entirely. To make it possible to introduce new flags which can be read-only compatible, like VERITY, we add new ro flags to btrfs without treating them quite so harshly in tree-checker. A read-only file system can survive an unexpected flag, and can be mounted. As for the implementation, it unfortunately gets a little complicated. The on-disk representation of the inode, btrfs_inode_item, has an __le64 for flags but the in-memory representation, btrfs_inode, uses a u32. David Sterba had the nice idea that we could reclaim those wasted 32 bits on disk and use them for the new ro_compat flags. It turns out that the tree-checker code which checks for unknown flags is broken, and ignores the upper 32 bits we are hoping to use. The issue is that the flags use the literal 1 rather than 1ULL, so the flags are signed ints, and one of them is specifically (1 << 31). As a result, the mask which ORs the flags is a negative integer on machines where int is 32 bit twos complement. When tree-checker evaluates the expression: btrfs_inode_flags(leaf, iitem) & ~BTRFS_INODE_FLAG_MASK) The mask is something like 0x80000abc, which gets promoted to u64 with sign extension to 0xffffffff80000abc. Negating that 64 bit mask leaves all the upper bits zeroed, and we can't detect unexpected flags. This suggests that we can't use those bits after all. Luckily, we have good reason to believe that they are zero anyway. Inode flags are metadata, which is always checksummed, so any bit flips that would introduce 1s would cause a checksum failure anyway (excluding the improbable case of the checksum getting corrupted exactly badly). Further, unless the 1 << 31 flag is used, the cast to u64 of the 32 bit inode flag should preserve its value and not add leading zeroes (at least for twos complement). The only place that flag (BTRFS_INODE_ROOT_ITEM_INIT) is used is in a special inode embedded in the root item, and indeed for that inode we see 0xffffffff80000000 as the flags on disk. However, that inode is never seen by tree checker, nor is it used in a context where verity might be meaningful. Theoretically, a future ro flag might cause trouble on that inode, so we should proactively clean up that mess before it does. With the introduction of the new ro flags, keep two separate unsigned masks and check them against the appropriate u32. Since we no longer run afoul of sign extension, this also stops writing out 0xffffffff80000000 in root_item inodes going forward. Signed-off-by: Boris Burkov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/btrfs_inode.h | 20 +++++++++++++++++++- fs/btrfs/ctree.h | 28 +++++++++++++++------------- fs/btrfs/delayed-inode.c | 9 +++++++-- fs/btrfs/inode.c | 9 +++++++-- fs/btrfs/ioctl.c | 7 ++++--- fs/btrfs/tree-checker.c | 17 +++++++++++++---- fs/btrfs/tree-log.c | 5 ++++- 7 files changed, 69 insertions(+), 26 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index c652e19ad74e..1093b00130be 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -189,8 +189,10 @@ struct btrfs_inode { */ u64 csum_bytes; - /* flags field from the on disk inode */ + /* Backwards incompatible flags, lower half of inode_item::flags */ u32 flags; + /* Read-only compatibility flags, upper half of inode_item::flags */ + u32 ro_flags; /* * Counters to keep track of the number of extent item's we may use due @@ -348,6 +350,22 @@ struct btrfs_dio_private { u8 csums[]; }; +/* + * btrfs_inode_item stores flags in a u64, btrfs_inode stores them in two + * separate u32s. These two functions convert between the two representations. + */ +static inline u64 btrfs_inode_combine_flags(u32 flags, u32 ro_flags) +{ + return (flags | ((u64)ro_flags << 32)); +} + +static inline void btrfs_inode_split_flags(u64 inode_item_flags, + u32 *flags, u32 *ro_flags) +{ + *flags = (u32)inode_item_flags; + *ro_flags = (u32)(inode_item_flags >> 32); +} + /* Array of bytes with variable length, hexadecimal format 0x1234 */ #define CSUM_FMT "0x%*phN" #define CSUM_FMT_VALUE(size, bytes) size, bytes diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index fd3084feb4b5..9e3b7a56a78f 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1482,20 +1482,20 @@ do { \ /* * Inode flags */ -#define BTRFS_INODE_NODATASUM (1 << 0) -#define BTRFS_INODE_NODATACOW (1 << 1) -#define BTRFS_INODE_READONLY (1 << 2) -#define BTRFS_INODE_NOCOMPRESS (1 << 3) -#define BTRFS_INODE_PREALLOC (1 << 4) -#define BTRFS_INODE_SYNC (1 << 5) -#define BTRFS_INODE_IMMUTABLE (1 << 6) -#define BTRFS_INODE_APPEND (1 << 7) -#define BTRFS_INODE_NODUMP (1 << 8) -#define BTRFS_INODE_NOATIME (1 << 9) -#define BTRFS_INODE_DIRSYNC (1 << 10) -#define BTRFS_INODE_COMPRESS (1 << 11) +#define BTRFS_INODE_NODATASUM (1U << 0) +#define BTRFS_INODE_NODATACOW (1U << 1) +#define BTRFS_INODE_READONLY (1U << 2) +#define BTRFS_INODE_NOCOMPRESS (1U << 3) +#define BTRFS_INODE_PREALLOC (1U << 4) +#define BTRFS_INODE_SYNC (1U << 5) +#define BTRFS_INODE_IMMUTABLE (1U << 6) +#define BTRFS_INODE_APPEND (1U << 7) +#define BTRFS_INODE_NODUMP (1U << 8) +#define BTRFS_INODE_NOATIME (1U << 9) +#define BTRFS_INODE_DIRSYNC (1U << 10) +#define BTRFS_INODE_COMPRESS (1U << 11) -#define BTRFS_INODE_ROOT_ITEM_INIT (1 << 31) +#define BTRFS_INODE_ROOT_ITEM_INIT (1U << 31) #define BTRFS_INODE_FLAG_MASK \ (BTRFS_INODE_NODATASUM | \ @@ -1512,6 +1512,8 @@ do { \ BTRFS_INODE_COMPRESS | \ BTRFS_INODE_ROOT_ITEM_INIT) +#define BTRFS_INODE_RO_FLAG_MASK (0) + struct btrfs_map_token { struct extent_buffer *eb; char *kaddr; diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 61452f04181a..1e08eb2b27f0 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1645,6 +1645,8 @@ static void fill_stack_inode_item(struct btrfs_trans_handle *trans, struct btrfs_inode_item *inode_item, struct inode *inode) { + u64 flags; + btrfs_set_stack_inode_uid(inode_item, i_uid_read(inode)); btrfs_set_stack_inode_gid(inode_item, i_gid_read(inode)); btrfs_set_stack_inode_size(inode_item, BTRFS_I(inode)->disk_i_size); @@ -1657,7 +1659,9 @@ static void fill_stack_inode_item(struct btrfs_trans_handle *trans, inode_peek_iversion(inode)); btrfs_set_stack_inode_transid(inode_item, trans->transid); btrfs_set_stack_inode_rdev(inode_item, inode->i_rdev); - btrfs_set_stack_inode_flags(inode_item, BTRFS_I(inode)->flags); + flags = btrfs_inode_combine_flags(BTRFS_I(inode)->flags, + BTRFS_I(inode)->ro_flags); + btrfs_set_stack_inode_flags(inode_item, flags); btrfs_set_stack_inode_block_group(inode_item, 0); btrfs_set_stack_timespec_sec(&inode_item->atime, @@ -1715,7 +1719,8 @@ int btrfs_fill_inode(struct inode *inode, u32 *rdev) btrfs_stack_inode_sequence(inode_item)); inode->i_rdev = 0; *rdev = btrfs_stack_inode_rdev(inode_item); - BTRFS_I(inode)->flags = btrfs_stack_inode_flags(inode_item); + btrfs_inode_split_flags(btrfs_stack_inode_flags(inode_item), + &BTRFS_I(inode)->flags, &BTRFS_I(inode)->ro_flags); inode->i_atime.tv_sec = btrfs_stack_timespec_sec(&inode_item->atime); inode->i_atime.tv_nsec = btrfs_stack_timespec_nsec(&inode_item->atime); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 949f6a6c616a..cd5a67ba7e71 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3764,7 +3764,8 @@ static int btrfs_read_locked_inode(struct inode *inode, rdev = btrfs_inode_rdev(leaf, inode_item); BTRFS_I(inode)->index_cnt = (u64)-1; - BTRFS_I(inode)->flags = btrfs_inode_flags(leaf, inode_item); + btrfs_inode_split_flags(btrfs_inode_flags(leaf, inode_item), + &BTRFS_I(inode)->flags, &BTRFS_I(inode)->ro_flags); cache_index: /* @@ -3895,6 +3896,7 @@ static void fill_inode_item(struct btrfs_trans_handle *trans, struct inode *inode) { struct btrfs_map_token token; + u64 flags; btrfs_init_map_token(&token, leaf); @@ -3930,7 +3932,9 @@ static void fill_inode_item(struct btrfs_trans_handle *trans, btrfs_set_token_inode_sequence(&token, item, inode_peek_iversion(inode)); btrfs_set_token_inode_transid(&token, item, trans->transid); btrfs_set_token_inode_rdev(&token, item, inode->i_rdev); - btrfs_set_token_inode_flags(&token, item, BTRFS_I(inode)->flags); + flags = btrfs_inode_combine_flags(BTRFS_I(inode)->flags, + BTRFS_I(inode)->ro_flags); + btrfs_set_token_inode_flags(&token, item, flags); btrfs_set_token_inode_block_group(&token, item, 0); } @@ -9064,6 +9068,7 @@ struct inode *btrfs_alloc_inode(struct super_block *sb) ei->defrag_bytes = 0; ei->disk_i_size = 0; ei->flags = 0; + ei->ro_flags = 0; ei->csum_bytes = 0; ei->index_cnt = (u64)-1; ei->dir_index = 0; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 4d809899c076..17aefb5f08ea 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -103,9 +103,10 @@ static unsigned int btrfs_mask_fsflags_for_type(struct inode *inode, * Export internal inode flags to the format expected by the FS_IOC_GETFLAGS * ioctl. */ -static unsigned int btrfs_inode_flags_to_fsflags(unsigned int flags) +static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode) { unsigned int iflags = 0; + u32 flags = binode->flags; if (flags & BTRFS_INODE_SYNC) iflags |= FS_SYNC_FL; @@ -200,7 +201,7 @@ int btrfs_fileattr_get(struct dentry *dentry, struct fileattr *fa) { struct btrfs_inode *binode = BTRFS_I(d_inode(dentry)); - fileattr_fill_flags(fa, btrfs_inode_flags_to_fsflags(binode->flags)); + fileattr_fill_flags(fa, btrfs_inode_flags_to_fsflags(binode)); return 0; } @@ -224,7 +225,7 @@ int btrfs_fileattr_set(struct user_namespace *mnt_userns, return -EOPNOTSUPP; fsflags = btrfs_mask_fsflags_for_type(inode, fa->flags); - old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags); + old_fsflags = btrfs_inode_flags_to_fsflags(binode); ret = check_fsflags(old_fsflags, fsflags); if (ret) return ret; diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 7ba94b683ee3..7733e8ac0a69 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -24,6 +24,7 @@ #include "compression.h" #include "volumes.h" #include "misc.h" +#include "btrfs_inode.h" /* * Error message should follow the following format: @@ -1008,6 +1009,8 @@ static int check_inode_item(struct extent_buffer *leaf, u32 valid_mask = (S_IFMT | S_ISUID | S_ISGID | S_ISVTX | 0777); u32 mode; int ret; + u32 flags; + u32 ro_flags; ret = check_inode_key(leaf, key, slot); if (unlikely(ret < 0)) @@ -1063,11 +1066,17 @@ static int check_inode_item(struct extent_buffer *leaf, btrfs_inode_nlink(leaf, iitem)); return -EUCLEAN; } - if (unlikely(btrfs_inode_flags(leaf, iitem) & ~BTRFS_INODE_FLAG_MASK)) { + btrfs_inode_split_flags(btrfs_inode_flags(leaf, iitem), &flags, &ro_flags); + if (unlikely(flags & ~BTRFS_INODE_FLAG_MASK)) { inode_item_err(leaf, slot, - "unknown flags detected: 0x%llx", - btrfs_inode_flags(leaf, iitem) & - ~BTRFS_INODE_FLAG_MASK); + "unknown incompat flags detected: 0x%x", flags); + return -EUCLEAN; + } + if (unlikely(!sb_rdonly(fs_info->sb) && + (ro_flags & ~BTRFS_INODE_RO_FLAG_MASK))) { + inode_item_err(leaf, slot, + "unknown ro-compat flags detected on writeable mount: 0x%x", + ro_flags); return -EUCLEAN; } return 0; diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index d09202e0c9df..567adc3de11a 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -3924,6 +3924,7 @@ static void fill_inode_item(struct btrfs_trans_handle *trans, u64 logged_isize) { struct btrfs_map_token token; + u64 flags; btrfs_init_map_token(&token, leaf); @@ -3973,7 +3974,9 @@ static void fill_inode_item(struct btrfs_trans_handle *trans, btrfs_set_token_inode_sequence(&token, item, inode_peek_iversion(inode)); btrfs_set_token_inode_transid(&token, item, trans->transid); btrfs_set_token_inode_rdev(&token, item, inode->i_rdev); - btrfs_set_token_inode_flags(&token, item, BTRFS_I(inode)->flags); + flags = btrfs_inode_combine_flags(BTRFS_I(inode)->flags, + BTRFS_I(inode)->ro_flags); + btrfs_set_token_inode_flags(&token, item, flags); btrfs_set_token_inode_block_group(&token, item, 0); } From 146054090b0859b28fc39015c7704ccc3c3a347f Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Wed, 30 Jun 2021 13:01:49 -0700 Subject: [PATCH 334/417] btrfs: initial fsverity support Add support for fsverity in btrfs. To support the generic interface in fs/verity, we add two new item types in the fs tree for inodes with verity enabled. One stores the per-file verity descriptor and btrfs verity item and the other stores the Merkle tree data itself. Verity checking is done in end_page_read just before a page is marked uptodate. This naturally handles a variety of edge cases like holes, preallocated extents, and inline extents. Some care needs to be taken to not try to verity pages past the end of the file, which are accessed by the generic buffered file reading code under some circumstances like reading to the end of the last page and trying to read again. Direct IO on a verity file falls back to buffered reads. Verity relies on PageChecked for the Merkle tree data itself to avoid re-walking up shared paths in the tree. For this reason, we need to cache the Merkle tree data. Since the file is immutable after verity is turned on, we can cache it at an index past EOF. Use the new inode ro_flags to store verity on the inode item, so that we can enable verity on a file, then rollback to an older kernel and still mount the file system and read the file. Since we can't safely write the file anymore without ruining the invariants of the Merkle tree, we mark a ro_compat flag on the file system when a file has verity enabled. Acked-by: Eric Biggers Co-developed-by: Chris Mason Signed-off-by: Chris Mason Signed-off-by: Boris Burkov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/Makefile | 1 + fs/btrfs/btrfs_inode.h | 7 + fs/btrfs/ctree.h | 31 +- fs/btrfs/extent_io.c | 26 +- fs/btrfs/file.c | 10 + fs/btrfs/inode.c | 6 + fs/btrfs/ioctl.c | 14 +- fs/btrfs/super.c | 3 + fs/btrfs/sysfs.c | 6 + fs/btrfs/verity.c | 738 ++++++++++++++++++++++++++++++++ include/uapi/linux/btrfs.h | 1 + include/uapi/linux/btrfs_tree.h | 35 ++ 12 files changed, 859 insertions(+), 19 deletions(-) create mode 100644 fs/btrfs/verity.c diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index cec88a66bd6c..3dcf9bcc2326 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -36,6 +36,7 @@ btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o btrfs-$(CONFIG_BTRFS_FS_REF_VERIFY) += ref-verify.o btrfs-$(CONFIG_BLK_DEV_ZONED) += zoned.o +btrfs-$(CONFIG_FS_VERITY) += verity.o btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \ tests/extent-buffer-tests.o tests/btrfs-tests.o \ diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 1093b00130be..76ee1452c57b 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -51,6 +51,13 @@ enum { * the file range, inode's io_tree). */ BTRFS_INODE_NO_DELALLOC_FLUSH, + /* + * Set when we are working on enabling verity for a file. Computing and + * writing the whole Merkle tree can take a while so we want to prevent + * races where two separate tasks attempt to simultaneously start verity + * on the same file. + */ + BTRFS_INODE_VERITY_IN_PROGRESS, }; /* in memory btrfs inode */ diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 9e3b7a56a78f..f17be4b023cb 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -281,7 +281,8 @@ struct btrfs_super_block { #define BTRFS_FEATURE_COMPAT_RO_SUPP \ (BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE | \ - BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID) + BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID | \ + BTRFS_FEATURE_COMPAT_RO_VERITY) #define BTRFS_FEATURE_COMPAT_RO_SAFE_SET 0ULL #define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR 0ULL @@ -1512,7 +1513,9 @@ do { \ BTRFS_INODE_COMPRESS | \ BTRFS_INODE_ROOT_ITEM_INIT) -#define BTRFS_INODE_RO_FLAG_MASK (0) +#define BTRFS_INODE_RO_VERITY (1U << 0) + +#define BTRFS_INODE_RO_FLAG_MASK (BTRFS_INODE_RO_VERITY) struct btrfs_map_token { struct extent_buffer *eb; @@ -3791,6 +3794,30 @@ static inline int btrfs_defrag_cancelled(struct btrfs_fs_info *fs_info) return signal_pending(current); } +/* verity.c */ +#ifdef CONFIG_FS_VERITY + +extern const struct fsverity_operations btrfs_verityops; +int btrfs_drop_verity_items(struct btrfs_inode *inode); + +BTRFS_SETGET_FUNCS(verity_descriptor_encryption, struct btrfs_verity_descriptor_item, + encryption, 8); +BTRFS_SETGET_FUNCS(verity_descriptor_size, struct btrfs_verity_descriptor_item, + size, 64); +BTRFS_SETGET_STACK_FUNCS(stack_verity_descriptor_encryption, + struct btrfs_verity_descriptor_item, encryption, 8); +BTRFS_SETGET_STACK_FUNCS(stack_verity_descriptor_size, + struct btrfs_verity_descriptor_item, size, 64); + +#else + +static inline int btrfs_drop_verity_items(struct btrfs_inode *inode) +{ + return 0; +} + +#endif + /* Sanity test specific functions */ #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS void btrfs_test_destroy_inode(struct inode *inode); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 66888b10b00d..96de6e70d06c 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "misc.h" #include "extent_io.h" #include "extent-io-tree.h" @@ -2247,18 +2248,6 @@ int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end, return bitset; } -/* - * helper function to set a given page up to date if all the - * extents in the tree for that page are up to date - */ -static void check_page_uptodate(struct extent_io_tree *tree, struct page *page) -{ - u64 start = page_offset(page); - u64 end = start + PAGE_SIZE - 1; - if (test_range_bit(tree, start, end, EXTENT_UPTODATE, 1, NULL)) - SetPageUptodate(page); -} - int free_io_failure(struct extent_io_tree *failure_tree, struct extent_io_tree *io_tree, struct io_failure_record *rec) @@ -2690,7 +2679,15 @@ static void end_page_read(struct page *page, bool uptodate, u64 start, u32 len) start + len <= page_offset(page) + PAGE_SIZE); if (uptodate) { - btrfs_page_set_uptodate(fs_info, page, start, len); + if (fsverity_active(page->mapping->host) && + !PageError(page) && + !PageUptodate(page) && + start < i_size_read(page->mapping->host) && + !fsverity_verify_page(page)) { + btrfs_page_set_error(fs_info, page, start, len); + } else { + btrfs_page_set_uptodate(fs_info, page, start, len); + } } else { btrfs_page_clear_uptodate(fs_info, page, start, len); btrfs_page_set_error(fs_info, page, start, len); @@ -3105,7 +3102,7 @@ readpage_ok: /* Update page status and unlock */ end_page_read(page, uptodate, start, len); endio_readpage_release_extent(&processed, BTRFS_I(inode), - start, end, uptodate); + start, end, PageUptodate(page)); } /* Release the last extent */ endio_readpage_release_extent(&processed, NULL, 0, 0, false); @@ -3706,7 +3703,6 @@ int btrfs_do_readpage(struct page *page, struct extent_map **em_cached, /* the get_extent function already copied into the page */ if (test_range_bit(tree, cur, cur_end, EXTENT_UPTODATE, 1, NULL)) { - check_page_uptodate(tree, page); unlock_extent(tree, cur, cur + iosize - 1); end_page_read(page, true, cur, iosize); cur = cur + iosize; diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index d3f2623a2af0..7ff577005d0f 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "ctree.h" #include "disk-io.h" #include "transaction.h" @@ -3615,7 +3616,13 @@ static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int whence) static int btrfs_file_open(struct inode *inode, struct file *filp) { + int ret; + filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC; + + ret = fsverity_file_open(inode, filp); + if (ret) + return ret; return generic_file_open(inode, filp); } @@ -3644,6 +3651,9 @@ static ssize_t btrfs_direct_read(struct kiocb *iocb, struct iov_iter *to) struct inode *inode = file_inode(iocb->ki_filp); ssize_t ret; + if (fsverity_active(inode)) + return 0; + if (check_direct_read(btrfs_sb(inode->i_sb), to, iocb->ki_pos)) return 0; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index cd5a67ba7e71..766cd35be33d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "misc.h" #include "ctree.h" #include "disk-io.h" @@ -5560,6 +5561,7 @@ void btrfs_evict_inode(struct inode *inode) trace_btrfs_inode_evict(inode); if (!root) { + fsverity_cleanup_inode(inode); clear_inode(inode); return; } @@ -5642,6 +5644,7 @@ no_delete: * to retry these periodically in the future. */ btrfs_remove_delayed_node(BTRFS_I(inode)); + fsverity_cleanup_inode(inode); clear_inode(inode); } @@ -9250,6 +9253,7 @@ static int btrfs_getattr(struct user_namespace *mnt_userns, struct inode *inode = d_inode(path->dentry); u32 blocksize = inode->i_sb->s_blocksize; u32 bi_flags = BTRFS_I(inode)->flags; + u32 bi_ro_flags = BTRFS_I(inode)->ro_flags; stat->result_mask |= STATX_BTIME; stat->btime.tv_sec = BTRFS_I(inode)->i_otime.tv_sec; @@ -9262,6 +9266,8 @@ static int btrfs_getattr(struct user_namespace *mnt_userns, stat->attributes |= STATX_ATTR_IMMUTABLE; if (bi_flags & BTRFS_INODE_NODUMP) stat->attributes |= STATX_ATTR_NODUMP; + if (bi_ro_flags & BTRFS_INODE_RO_VERITY) + stat->attributes |= STATX_ATTR_VERITY; stat->attributes_mask |= (STATX_ATTR_APPEND | STATX_ATTR_COMPRESSED | diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 17aefb5f08ea..85c8b5a87a6a 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "ctree.h" #include "disk-io.h" #include "export.h" @@ -107,6 +108,7 @@ static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode) { unsigned int iflags = 0; u32 flags = binode->flags; + u32 ro_flags = binode->ro_flags; if (flags & BTRFS_INODE_SYNC) iflags |= FS_SYNC_FL; @@ -122,6 +124,8 @@ static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode) iflags |= FS_DIRSYNC_FL; if (flags & BTRFS_INODE_NODATACOW) iflags |= FS_NOCOW_FL; + if (ro_flags & BTRFS_INODE_RO_VERITY) + iflags |= FS_VERITY_FL; if (flags & BTRFS_INODE_NOCOMPRESS) iflags |= FS_NOCOMP_FL; @@ -149,10 +153,12 @@ void btrfs_sync_inode_flags_to_i_flags(struct inode *inode) new_fl |= S_NOATIME; if (binode->flags & BTRFS_INODE_DIRSYNC) new_fl |= S_DIRSYNC; + if (binode->ro_flags & BTRFS_INODE_RO_VERITY) + new_fl |= S_VERITY; set_mask_bits(&inode->i_flags, - S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC, - new_fl); + S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC | + S_VERITY, new_fl); } /* @@ -5020,6 +5026,10 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_get_subvol_rootref(file, argp); case BTRFS_IOC_INO_LOOKUP_USER: return btrfs_ioctl_ino_lookup_user(file, argp); + case FS_IOC_ENABLE_VERITY: + return fsverity_ioctl_enable(file, (const void __user *)argp); + case FS_IOC_MEASURE_VERITY: + return fsverity_ioctl_measure(file, argp); } return -ENOTTY; diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 0d2e3ab2fc31..2bdc544b4c95 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1353,6 +1353,9 @@ static int btrfs_fill_super(struct super_block *sb, sb->s_op = &btrfs_super_ops; sb->s_d_op = &btrfs_dentry_operations; sb->s_export_op = &btrfs_export_ops; +#ifdef CONFIG_FS_VERITY + sb->s_vop = &btrfs_verityops; +#endif sb->s_xattr = btrfs_xattr_handlers; sb->s_time_gran = 1; #ifdef CONFIG_BTRFS_FS_POSIX_ACL diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index d9d53a255ef9..bfe5e27617b0 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -267,6 +267,9 @@ BTRFS_FEAT_ATTR_INCOMPAT(raid1c34, RAID1C34); #ifdef CONFIG_BTRFS_DEBUG BTRFS_FEAT_ATTR_INCOMPAT(zoned, ZONED); #endif +#ifdef CONFIG_FS_VERITY +BTRFS_FEAT_ATTR_COMPAT_RO(verity, VERITY); +#endif static struct attribute *btrfs_supported_feature_attrs[] = { BTRFS_FEAT_ATTR_PTR(mixed_backref), @@ -284,6 +287,9 @@ static struct attribute *btrfs_supported_feature_attrs[] = { BTRFS_FEAT_ATTR_PTR(raid1c34), #ifdef CONFIG_BTRFS_DEBUG BTRFS_FEAT_ATTR_PTR(zoned), +#endif +#ifdef CONFIG_FS_VERITY + BTRFS_FEAT_ATTR_PTR(verity), #endif NULL }; diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c new file mode 100644 index 000000000000..ac4c2ca45925 --- /dev/null +++ b/fs/btrfs/verity.c @@ -0,0 +1,738 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ctree.h" +#include "btrfs_inode.h" +#include "transaction.h" +#include "disk-io.h" +#include "locking.h" + +/* + * Implementation of the interface defined in struct fsverity_operations. + * + * The main question is how and where to store the verity descriptor and the + * Merkle tree. We store both in dedicated btree items in the filesystem tree, + * together with the rest of the inode metadata. This means we'll need to do + * extra work to encrypt them once encryption is supported in btrfs, but btrfs + * has a lot of careful code around i_size and it seems better to make a new key + * type than try and adjust all of our expectations for i_size. + * + * Note that this differs from the implementation in ext4 and f2fs, where + * this data is stored as if it were in the file, but past EOF. However, btrfs + * does not have a widespread mechanism for caching opaque metadata pages, so we + * do pretend that the Merkle tree pages themselves are past EOF for the + * purposes of caching them (as opposed to creating a virtual inode). + * + * fs verity items are stored under two different key types on disk. + * The descriptor items: + * [ inode objectid, BTRFS_VERITY_DESC_ITEM_KEY, offset ] + * + * At offset 0, we store a btrfs_verity_descriptor_item which tracks the + * size of the descriptor item and some extra data for encryption. + * Starting at offset 1, these hold the generic fs verity descriptor. + * The latter are opaque to btrfs, we just read and write them as a blob for + * the higher level verity code. The most common descriptor size is 256 bytes. + * + * The merkle tree items: + * [ inode objectid, BTRFS_VERITY_MERKLE_ITEM_KEY, offset ] + * + * These also start at offset 0, and correspond to the merkle tree bytes. + * So when fsverity asks for page 0 of the merkle tree, we pull up one page + * starting at offset 0 for this key type. These are also opaque to btrfs, + * we're blindly storing whatever fsverity sends down. + */ + +#define MERKLE_START_ALIGN 65536 + +/* + * Compute the logical file offset where we cache the Merkle tree. + * + * @inode: inode of the verity file + * + * For the purposes of caching the Merkle tree pages, as required by + * fs-verity, it is convenient to do size computations in terms of a file + * offset, rather than in terms of page indices. + * + * Use 64K to be sure it's past the last page in the file, even with 64K pages. + * That rounding operation itself can overflow loff_t, so we do it in u64 and + * check. + * + * Returns the file offset on success, negative error code on failure. + */ +static loff_t merkle_file_pos(const struct inode *inode) +{ + u64 sz = inode->i_size; + u64 rounded = round_up(sz, MERKLE_START_ALIGN); + + if (rounded > inode->i_sb->s_maxbytes) + return -EFBIG; + + return rounded; +} + +/* + * Drop all the items for this inode with this key_type. + * + * @inode: inode to drop items for + * @key_type: type of items to drop (BTRFS_VERITY_DESC_ITEM or + * BTRFS_VERITY_MERKLE_ITEM) + * + * Before doing a verity enable we cleanup any existing verity items. + * This is also used to clean up if a verity enable failed half way through. + * + * Returns number of dropped items on success, negative error code on failure. + */ +static int drop_verity_items(struct btrfs_inode *inode, u8 key_type) +{ + struct btrfs_trans_handle *trans; + struct btrfs_root *root = inode->root; + struct btrfs_path *path; + struct btrfs_key key; + int count = 0; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + while (1) { + /* 1 for the item being dropped */ + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out; + } + + /* + * Walk backwards through all the items until we find one that + * isn't from our key type or objectid + */ + key.objectid = btrfs_ino(inode); + key.type = key_type; + key.offset = (u64)-1; + + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret > 0) { + ret = 0; + /* No more keys of this type, we're done */ + if (path->slots[0] == 0) + break; + path->slots[0]--; + } else if (ret < 0) { + btrfs_end_transaction(trans); + goto out; + } + + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + + /* No more keys of this type, we're done */ + if (key.objectid != btrfs_ino(inode) || key.type != key_type) + break; + + /* + * This shouldn't be a performance sensitive function because + * it's not used as part of truncate. If it ever becomes + * perf sensitive, change this to walk forward and bulk delete + * items + */ + ret = btrfs_del_items(trans, root, path, path->slots[0], 1); + if (ret) { + btrfs_end_transaction(trans); + goto out; + } + count++; + btrfs_release_path(path); + btrfs_end_transaction(trans); + } + ret = count; + btrfs_end_transaction(trans); +out: + btrfs_free_path(path); + return ret; +} + +/* + * Drop all verity items + * + * @inode: inode to drop verity items for + * + * In most contexts where we are dropping verity items, we want to do it for all + * the types of verity items, not a particular one. + * + * Returns: 0 on success, negative error code on failure. + */ +int btrfs_drop_verity_items(struct btrfs_inode *inode) +{ + int ret; + + ret = drop_verity_items(inode, BTRFS_VERITY_DESC_ITEM_KEY); + if (ret < 0) + return ret; + ret = drop_verity_items(inode, BTRFS_VERITY_MERKLE_ITEM_KEY); + if (ret < 0) + return ret; + + return 0; +} + +/* + * Insert and write inode items with a given key type and offset. + * + * @inode: inode to insert for + * @key_type: key type to insert + * @offset: item offset to insert at + * @src: source data to write + * @len: length of source data to write + * + * Write len bytes from src into items of up to 2K length. + * The inserted items will have key (ino, key_type, offset + off) where off is + * consecutively increasing from 0 up to the last item ending at offset + len. + * + * Returns 0 on success and a negative error code on failure. + */ +static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset, + const char *src, u64 len) +{ + struct btrfs_trans_handle *trans; + struct btrfs_path *path; + struct btrfs_root *root = inode->root; + struct extent_buffer *leaf; + struct btrfs_key key; + unsigned long copy_bytes; + unsigned long src_offset = 0; + void *data; + int ret = 0; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + while (len > 0) { + /* 1 for the new item being inserted */ + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + break; + } + + key.objectid = btrfs_ino(inode); + key.type = key_type; + key.offset = offset; + + /* + * Insert 2K at a time mostly to be friendly for smaller leaf + * size filesystems + */ + copy_bytes = min_t(u64, len, 2048); + + ret = btrfs_insert_empty_item(trans, root, path, &key, copy_bytes); + if (ret) { + btrfs_end_transaction(trans); + break; + } + + leaf = path->nodes[0]; + + data = btrfs_item_ptr(leaf, path->slots[0], void); + write_extent_buffer(leaf, src + src_offset, + (unsigned long)data, copy_bytes); + offset += copy_bytes; + src_offset += copy_bytes; + len -= copy_bytes; + + btrfs_release_path(path); + btrfs_end_transaction(trans); + } + + btrfs_free_path(path); + return ret; +} + +/* + * Read inode items of the given key type and offset from the btree. + * + * @inode: inode to read items of + * @key_type: key type to read + * @offset: item offset to read from + * @dest: Buffer to read into. This parameter has slightly tricky + * semantics. If it is NULL, the function will not do any copying + * and will just return the size of all the items up to len bytes. + * If dest_page is passed, then the function will kmap_local the + * page and ignore dest, but it must still be non-NULL to avoid the + * counting-only behavior. + * @len: length in bytes to read + * @dest_page: copy into this page instead of the dest buffer + * + * Helper function to read items from the btree. This returns the number of + * bytes read or < 0 for errors. We can return short reads if the items don't + * exist on disk or aren't big enough to fill the desired length. Supports + * reading into a provided buffer (dest) or into the page cache + * + * Returns number of bytes read or a negative error code on failure. + */ +static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset, + char *dest, u64 len, struct page *dest_page) +{ + struct btrfs_path *path; + struct btrfs_root *root = inode->root; + struct extent_buffer *leaf; + struct btrfs_key key; + u64 item_end; + u64 copy_end; + int copied = 0; + u32 copy_offset; + unsigned long copy_bytes; + unsigned long dest_offset = 0; + void *data; + char *kaddr = dest; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + if (dest_page) + path->reada = READA_FORWARD; + + key.objectid = btrfs_ino(inode); + key.type = key_type; + key.offset = offset; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) { + goto out; + } else if (ret > 0) { + ret = 0; + if (path->slots[0] == 0) + goto out; + path->slots[0]--; + } + + while (len > 0) { + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + + if (key.objectid != btrfs_ino(inode) || key.type != key_type) + break; + + item_end = btrfs_item_size_nr(leaf, path->slots[0]) + key.offset; + + if (copied > 0) { + /* + * Once we've copied something, we want all of the items + * to be sequential + */ + if (key.offset != offset) + break; + } else { + /* + * Our initial offset might be in the middle of an + * item. Make sure it all makes sense. + */ + if (key.offset > offset) + break; + if (item_end <= offset) + break; + } + + /* desc = NULL to just sum all the item lengths */ + if (!dest) + copy_end = item_end; + else + copy_end = min(offset + len, item_end); + + /* Number of bytes in this item we want to copy */ + copy_bytes = copy_end - offset; + + /* Offset from the start of item for copying */ + copy_offset = offset - key.offset; + + if (dest) { + if (dest_page) + kaddr = kmap_local_page(dest_page); + + data = btrfs_item_ptr(leaf, path->slots[0], void); + read_extent_buffer(leaf, kaddr + dest_offset, + (unsigned long)data + copy_offset, + copy_bytes); + + if (dest_page) + kunmap_local(kaddr); + } + + offset += copy_bytes; + dest_offset += copy_bytes; + len -= copy_bytes; + copied += copy_bytes; + + path->slots[0]++; + if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { + /* + * We've reached the last slot in this leaf and we need + * to go to the next leaf. + */ + ret = btrfs_next_leaf(root, path); + if (ret < 0) { + break; + } else if (ret > 0) { + ret = 0; + break; + } + } + } +out: + btrfs_free_path(path); + if (!ret) + ret = copied; + return ret; +} + +/* + * Rollback in-progress verity if we encounter an error. + * + * @inode: inode verity had an error for + * + * We try to handle recoverable errors while enabling verity by rolling it back + * and just failing the operation, rather than having an fs level error no + * matter what. However, any error in rollback is unrecoverable. + * + * Returns 0 on success, negative error code on failure. + */ +static int rollback_verity(struct btrfs_inode *inode) +{ + struct btrfs_trans_handle *trans; + struct btrfs_root *root = inode->root; + int ret; + + ASSERT(inode_is_locked(&inode->vfs_inode)); + truncate_inode_pages(inode->vfs_inode.i_mapping, inode->vfs_inode.i_size); + clear_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &inode->runtime_flags); + ret = btrfs_drop_verity_items(inode); + if (ret) { + btrfs_handle_fs_error(root->fs_info, ret, + "failed to drop verity items in rollback %llu", + (u64)inode->vfs_inode.i_ino); + goto out; + } + + /* 1 for updating the inode flag */ + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + btrfs_handle_fs_error(root->fs_info, ret, + "failed to start transaction in verity rollback %llu", + (u64)inode->vfs_inode.i_ino); + goto out; + } + inode->ro_flags &= ~BTRFS_INODE_RO_VERITY; + btrfs_sync_inode_flags_to_i_flags(&inode->vfs_inode); + ret = btrfs_update_inode(trans, root, inode); + if (ret) { + btrfs_abort_transaction(trans, ret); + goto out; + } + btrfs_end_transaction(trans); +out: + return ret; +} + +/* + * Finalize making the file a valid verity file + * + * @inode: inode to be marked as verity + * @desc: contents of the verity descriptor to write (not NULL) + * @desc_size: size of the verity descriptor + * + * Do the actual work of finalizing verity after successfully writing the Merkle + * tree: + * + * - write out the descriptor items + * - mark the inode with the verity flag + * - mark the ro compat bit + * - clear the in progress bit + * + * Returns 0 on success, negative error code on failure. + */ +static int finish_verity(struct btrfs_inode *inode, const void *desc, + size_t desc_size) +{ + struct btrfs_trans_handle *trans = NULL; + struct btrfs_root *root = inode->root; + struct btrfs_verity_descriptor_item item; + int ret; + + /* Write out the descriptor item */ + memset(&item, 0, sizeof(item)); + btrfs_set_stack_verity_descriptor_size(&item, desc_size); + ret = write_key_bytes(inode, BTRFS_VERITY_DESC_ITEM_KEY, 0, + (const char *)&item, sizeof(item)); + if (ret) + goto out; + + /* Write out the descriptor itself */ + ret = write_key_bytes(inode, BTRFS_VERITY_DESC_ITEM_KEY, 1, + desc, desc_size); + if (ret) + goto out; + + /* 1 for updating the inode flag */ + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out; + } + inode->ro_flags |= BTRFS_INODE_RO_VERITY; + btrfs_sync_inode_flags_to_i_flags(&inode->vfs_inode); + ret = btrfs_update_inode(trans, root, inode); + if (ret) + goto end_trans; + clear_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &inode->runtime_flags); + btrfs_set_fs_compat_ro(root->fs_info, VERITY); +end_trans: + btrfs_end_transaction(trans); +out: + return ret; + +} + +/* + * fsverity op that begins enabling verity. + * + * @filp: file to enable verity on + * + * Begin enabling fsverity for the file. We drop any existing verity items + * and set the in progress bit. + * + * Returns 0 on success, negative error code on failure. + */ +static int btrfs_begin_enable_verity(struct file *filp) +{ + struct btrfs_inode *inode = BTRFS_I(file_inode(filp)); + int ret; + + ASSERT(inode_is_locked(file_inode(filp))); + + if (test_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &inode->runtime_flags)) + return -EBUSY; + + ret = btrfs_drop_verity_items(inode); + if (ret) + return ret; + + set_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &inode->runtime_flags); + + return 0; +} + +/* + * fsverity op that ends enabling verity. + * + * @filp: file we are finishing enabling verity on + * @desc: verity descriptor to write out (NULL in error conditions) + * @desc_size: size of the verity descriptor (variable with signatures) + * @merkle_tree_size: size of the merkle tree in bytes + * + * If desc is null, then VFS is signaling an error occurred during verity + * enable, and we should try to rollback. Otherwise, attempt to finish verity. + * + * Returns 0 on success, negative error code on error. + */ +static int btrfs_end_enable_verity(struct file *filp, const void *desc, + size_t desc_size, u64 merkle_tree_size) +{ + struct btrfs_inode *inode = BTRFS_I(file_inode(filp)); + int ret = 0; + int rollback_ret; + + ASSERT(inode_is_locked(file_inode(filp))); + + if (desc == NULL) + goto rollback; + + ret = finish_verity(inode, desc, desc_size); + if (ret) + goto rollback; + return ret; + +rollback: + rollback_ret = rollback_verity(inode); + if (rollback_ret) + btrfs_err(inode->root->fs_info, + "failed to rollback verity items: %d", rollback_ret); + return ret; +} + +/* + * fsverity op that gets the struct fsverity_descriptor. + * + * @inode: inode to get the descriptor of + * @buf: output buffer for the descriptor contents + * @buf_size: size of the output buffer. 0 to query the size + * + * fsverity does a two pass setup for reading the descriptor, in the first pass + * it calls with buf_size = 0 to query the size of the descriptor, and then in + * the second pass it actually reads the descriptor off disk. + * + * Returns the size on success or a negative error code on failure. + */ +static int btrfs_get_verity_descriptor(struct inode *inode, void *buf, + size_t buf_size) +{ + u64 true_size; + int ret = 0; + struct btrfs_verity_descriptor_item item; + + memset(&item, 0, sizeof(item)); + ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_DESC_ITEM_KEY, 0, + (char *)&item, sizeof(item), NULL); + if (ret < 0) + return ret; + + if (item.reserved[0] != 0 || item.reserved[1] != 0) + return -EUCLEAN; + + true_size = btrfs_stack_verity_descriptor_size(&item); + if (true_size > INT_MAX) + return -EUCLEAN; + + if (buf_size == 0) + return true_size; + if (buf_size < true_size) + return -ERANGE; + + ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_DESC_ITEM_KEY, 1, + buf, buf_size, NULL); + if (ret < 0) + return ret; + if (ret != true_size) + return -EIO; + + return true_size; +} + +/* + * fsverity op that reads and caches a merkle tree page. + * + * @inode: inode to read a merkle tree page for + * @index: page index relative to the start of the merkle tree + * @num_ra_pages: number of pages to readahead. Optional, we ignore it + * + * The Merkle tree is stored in the filesystem btree, but its pages are cached + * with a logical position past EOF in the inode's mapping. + * + * Returns the page we read, or an ERR_PTR on error. + */ +static struct page *btrfs_read_merkle_tree_page(struct inode *inode, + pgoff_t index, + unsigned long num_ra_pages) +{ + struct page *page; + u64 off = (u64)index << PAGE_SHIFT; + loff_t merkle_pos = merkle_file_pos(inode); + int ret; + + if (merkle_pos < 0) + return ERR_PTR(merkle_pos); + if (merkle_pos > inode->i_sb->s_maxbytes - off - PAGE_SIZE) + return ERR_PTR(-EFBIG); + index += merkle_pos >> PAGE_SHIFT; +again: + page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED); + if (page) { + if (PageUptodate(page)) + return page; + + lock_page(page); + /* + * We only insert uptodate pages, so !Uptodate has to be + * an error + */ + if (!PageUptodate(page)) { + unlock_page(page); + put_page(page); + return ERR_PTR(-EIO); + } + unlock_page(page); + return page; + } + + page = __page_cache_alloc(mapping_gfp_constraint(inode->i_mapping, ~__GFP_FS)); + if (!page) + return ERR_PTR(-ENOMEM); + + /* + * Merkle item keys are indexed from byte 0 in the merkle tree. + * They have the form: + * + * [ inode objectid, BTRFS_MERKLE_ITEM_KEY, offset in bytes ] + */ + ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_MERKLE_ITEM_KEY, off, + page_address(page), PAGE_SIZE, page); + if (ret < 0) { + put_page(page); + return ERR_PTR(ret); + } + if (ret < PAGE_SIZE) + memzero_page(page, ret, PAGE_SIZE - ret); + + SetPageUptodate(page); + ret = add_to_page_cache_lru(page, inode->i_mapping, index, GFP_NOFS); + + if (!ret) { + /* Inserted and ready for fsverity */ + unlock_page(page); + } else { + put_page(page); + /* Did someone race us into inserting this page? */ + if (ret == -EEXIST) + goto again; + page = ERR_PTR(ret); + } + return page; +} + +/* + * fsverity op that writes a Merkle tree block into the btree. + * + * @inode: inode to write a Merkle tree block for + * @buf: Merkle tree data block to write + * @index: index of the block in the Merkle tree + * @log_blocksize: log base 2 of the Merkle tree block size + * + * Note that the block size could be different from the page size, so it is not + * safe to assume that index is a page index. + * + * Returns 0 on success or negative error code on failure + */ +static int btrfs_write_merkle_tree_block(struct inode *inode, const void *buf, + u64 index, int log_blocksize) +{ + u64 off = index << log_blocksize; + u64 len = 1ULL << log_blocksize; + loff_t merkle_pos = merkle_file_pos(inode); + + if (merkle_pos < 0) + return merkle_pos; + if (merkle_pos > inode->i_sb->s_maxbytes - off - len) + return -EFBIG; + + return write_key_bytes(BTRFS_I(inode), BTRFS_VERITY_MERKLE_ITEM_KEY, + off, buf, len); +} + +const struct fsverity_operations btrfs_verityops = { + .begin_enable_verity = btrfs_begin_enable_verity, + .end_enable_verity = btrfs_end_enable_verity, + .get_verity_descriptor = btrfs_get_verity_descriptor, + .read_merkle_tree_page = btrfs_read_merkle_tree_page, + .write_merkle_tree_block = btrfs_write_merkle_tree_block, +}; diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 22cd037123fa..d7d3cfead056 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -288,6 +288,7 @@ struct btrfs_ioctl_fs_info_args { * first mount when booting older kernel versions. */ #define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID (1ULL << 1) +#define BTRFS_FEATURE_COMPAT_RO_VERITY (1ULL << 2) #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0) #define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1) diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index ccdb40fe40dc..e1c4c732aaba 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -118,6 +118,29 @@ #define BTRFS_INODE_REF_KEY 12 #define BTRFS_INODE_EXTREF_KEY 13 #define BTRFS_XATTR_ITEM_KEY 24 + +/* + * fs verity items are stored under two different key types on disk. + * The descriptor items: + * [ inode objectid, BTRFS_VERITY_DESC_ITEM_KEY, offset ] + * + * At offset 0, we store a btrfs_verity_descriptor_item which tracks the size + * of the descriptor item and some extra data for encryption. + * Starting at offset 1, these hold the generic fs verity descriptor. The + * latter are opaque to btrfs, we just read and write them as a blob for the + * higher level verity code. The most common descriptor size is 256 bytes. + * + * The merkle tree items: + * [ inode objectid, BTRFS_VERITY_MERKLE_ITEM_KEY, offset ] + * + * These also start at offset 0, and correspond to the merkle tree bytes. When + * fsverity asks for page 0 of the merkle tree, we pull up one page starting at + * offset 0 for this key type. These are also opaque to btrfs, we're blindly + * storing whatever fsverity sends down. + */ +#define BTRFS_VERITY_DESC_ITEM_KEY 36 +#define BTRFS_VERITY_MERKLE_ITEM_KEY 37 + #define BTRFS_ORPHAN_ITEM_KEY 48 /* reserve 2-15 close to the inode for later flexibility */ @@ -991,4 +1014,16 @@ struct btrfs_qgroup_limit_item { __le64 rsv_excl; } __attribute__ ((__packed__)); +struct btrfs_verity_descriptor_item { + /* Size of the verity descriptor in bytes */ + __le64 size; + /* + * When we implement support for fscrypt, we will need to encrypt the + * Merkle tree for encrypted verity files. These 128 bits are for the + * eventual storage of an fscrypt initialization vector. + */ + __le64 reserved[2]; + __u8 encryption; +} __attribute__ ((__packed__)); + #endif /* _BTRFS_CTREE_H_ */ From 705242538ff348874e642f2ce953e19702af411d Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Wed, 30 Jun 2021 13:01:50 -0700 Subject: [PATCH 335/417] btrfs: verity metadata orphan items Writing out the verity data is too large of an operation to do in a single transaction. If we are interrupted before we finish creating fsverity metadata for a file, or fail to clean up already created metadata after a failure, we could leak the verity items that we already committed. To address this issue, we use the orphan mechanism. When we start enabling verity on a file, we also add an orphan item for that inode. When we are finished, we delete the orphan. However, if we are interrupted midway, the orphan will be present at mount and we can cleanup the half-formed verity state. There is a possible race with a normal unlink operation: if unlink and verity run on the same file in parallel, it is possible for verity to succeed and delete the still legitimate orphan added by unlink. Then, if we are interrupted and mount in that state, we will never clean up the inode properly. This is also possible for a file created with O_TMPFILE. Check nlink==0 before deleting to avoid this race. A final thing to note is that this is a resurrection of using orphans to signal an operation besides "delete this inode". The old case was to signal the need to do a truncate. That case still technically applies for mounting very old file systems, so we need to take some care to not clobber it. To that end, we just have to be careful that verity orphan cleanup is a no-op for non-verity files. Signed-off-by: Boris Burkov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 15 ++++++-- fs/btrfs/verity.c | 87 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 766cd35be33d..bc41d6c8d8d8 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3557,7 +3557,14 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) /* * If we have an inode with links, there are a couple of - * possibilities. Old kernels (before v3.12) used to create an + * possibilities: + * + * 1. We were halfway through creating fsverity metadata for the + * file. In that case, the orphan item represents incomplete + * fsverity metadata which must be cleaned up with + * btrfs_drop_verity_items and deleting the orphan item. + + * 2. Old kernels (before v3.12) used to create an * orphan item for truncate indicating that there were possibly * extent items past i_size that needed to be deleted. In v3.12, * truncate was changed to update i_size in sync with the extent @@ -3575,8 +3582,12 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) * but either way, we can delete the orphan item. */ if (ret == -ENOENT || inode->i_nlink) { - if (!ret) + if (!ret) { + ret = btrfs_drop_verity_items(BTRFS_I(inode)); iput(inode); + if (ret) + goto out; + } trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) { ret = PTR_ERR(trans); diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c index ac4c2ca45925..28d443d3ef93 100644 --- a/fs/btrfs/verity.c +++ b/fs/btrfs/verity.c @@ -49,6 +49,15 @@ * So when fsverity asks for page 0 of the merkle tree, we pull up one page * starting at offset 0 for this key type. These are also opaque to btrfs, * we're blindly storing whatever fsverity sends down. + * + * Another important consideration is the fact that the Merkle tree data scales + * linearly with the size of the file (with 4K pages/blocks and SHA-256, it's + * ~1/127th the size) so for large files, writing the tree can be a lengthy + * operation. For that reason, we guard the whole enable verity operation + * (between begin_enable_verity and end_enable_verity) with an orphan item. + * Again, because the data can be pretty large, it's quite possible that we + * could run out of space writing it, so we try our best to handle errors by + * stopping and rolling back rather than aborting the victim transaction. */ #define MERKLE_START_ALIGN 65536 @@ -396,6 +405,39 @@ out: return ret; } +/* + * Delete an fsverity orphan + * + * @trans: transaction to do the delete in + * @inode: inode to orphan + * + * Capture verity orphan specific logic that is repeated in the couple places + * we delete verity orphans. Specifically, handling ENOENT and ignoring inodes + * with 0 links. + * + * Returns zero on success or a negative error code on failure. + */ +static int del_orphan(struct btrfs_trans_handle *trans, struct btrfs_inode *inode) +{ + struct btrfs_root *root = inode->root; + int ret; + + /* + * If the inode has no links, it is either already unlinked, or was + * created with O_TMPFILE. In either case, it should have an orphan from + * that other operation. Rather than reference count the orphans, we + * simply ignore them here, because we only invoke the verity path in + * the orphan logic when i_nlink is 1. + */ + if (!inode->vfs_inode.i_nlink) + return 0; + + ret = btrfs_del_orphan_item(trans, root, btrfs_ino(inode)); + if (ret == -ENOENT) + ret = 0; + return ret; +} + /* * Rollback in-progress verity if we encounter an error. * @@ -424,8 +466,11 @@ static int rollback_verity(struct btrfs_inode *inode) goto out; } - /* 1 for updating the inode flag */ - trans = btrfs_start_transaction(root, 1); + /* + * 1 for updating the inode flag + * 1 for deleting the orphan + */ + trans = btrfs_start_transaction(root, 2); if (IS_ERR(trans)) { ret = PTR_ERR(trans); btrfs_handle_fs_error(root->fs_info, ret, @@ -440,6 +485,11 @@ static int rollback_verity(struct btrfs_inode *inode) btrfs_abort_transaction(trans, ret); goto out; } + ret = del_orphan(trans, inode); + if (ret) { + btrfs_abort_transaction(trans, ret); + goto out; + } btrfs_end_transaction(trans); out: return ret; @@ -457,6 +507,7 @@ out: * * - write out the descriptor items * - mark the inode with the verity flag + * - delete the orphan item * - mark the ro compat bit * - clear the in progress bit * @@ -484,8 +535,11 @@ static int finish_verity(struct btrfs_inode *inode, const void *desc, if (ret) goto out; - /* 1 for updating the inode flag */ - trans = btrfs_start_transaction(root, 1); + /* + * 1 for updating the inode flag + * 1 for deleting the orphan + */ + trans = btrfs_start_transaction(root, 2); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; @@ -493,6 +547,9 @@ static int finish_verity(struct btrfs_inode *inode, const void *desc, inode->ro_flags |= BTRFS_INODE_RO_VERITY; btrfs_sync_inode_flags_to_i_flags(&inode->vfs_inode); ret = btrfs_update_inode(trans, root, inode); + if (ret) + goto end_trans; + ret = del_orphan(trans, inode); if (ret) goto end_trans; clear_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &inode->runtime_flags); @@ -509,14 +566,16 @@ out: * * @filp: file to enable verity on * - * Begin enabling fsverity for the file. We drop any existing verity items - * and set the in progress bit. + * Begin enabling fsverity for the file. We drop any existing verity items, add + * an orphan and set the in progress bit. * * Returns 0 on success, negative error code on failure. */ static int btrfs_begin_enable_verity(struct file *filp) { struct btrfs_inode *inode = BTRFS_I(file_inode(filp)); + struct btrfs_root *root = inode->root; + struct btrfs_trans_handle *trans; int ret; ASSERT(inode_is_locked(file_inode(filp))); @@ -524,11 +583,25 @@ static int btrfs_begin_enable_verity(struct file *filp) if (test_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &inode->runtime_flags)) return -EBUSY; + /* + * This should almost never do anything, but theoretically, it's + * possible that we failed to enable verity on a file, then were + * interrupted or failed while rolling back, failed to cleanup the + * orphan, and finally attempt to enable verity again. + */ ret = btrfs_drop_verity_items(inode); if (ret) return ret; - set_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &inode->runtime_flags); + /* 1 for the orphan item */ + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + ret = btrfs_orphan_add(trans, inode); + if (!ret) + set_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &inode->runtime_flags); + btrfs_end_transaction(trans); return 0; } From ea3dc7d2d1f524eb2a34b6501be38c82be5c7ff1 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 28 Jul 2021 18:10:39 +0200 Subject: [PATCH 336/417] btrfs: print if fsverity support is built in when loading module As fsverity support depends on a config option, print that at module load time like we do for similar features. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/super.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 2bdc544b4c95..d444338db3c6 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2566,6 +2566,11 @@ static void __init btrfs_print_mod_info(void) ", zoned=yes" #else ", zoned=no" +#endif +#ifdef CONFIG_FS_VERITY + ", fsverity=yes" +#else + ", fsverity=no" #endif ; pr_info("Btrfs loaded, crc32c=%s%s\n", crc32c_impl(), options); From 0ff40a910f5649dfacc4fb5daa7e73692196342d Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Thu, 29 Jul 2021 05:22:16 -0300 Subject: [PATCH 337/417] btrfs: introduce btrfs_search_backwards function It's a common practice to start a search using offset (u64)-1, which is the u64 maximum value, meaning that we want the search_slot function to be set in the last item with the same objectid and type. Once we are in this position, it's a matter to start a search backwards by calling btrfs_previous_item, which will check if we'll need to go to a previous leaf and other necessary checks, only to be sure that we are in last offset of the same object and type. The new btrfs_search_backwards function does the all these steps when necessary, and can be used to avoid code duplication. Signed-off-by: Marcos Paulo de Souza Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 21 +++++++++++++++++++++ fs/btrfs/ctree.h | 4 ++++ fs/btrfs/ioctl.c | 30 ++++++++---------------------- fs/btrfs/super.c | 26 ++++++-------------------- fs/btrfs/volumes.c | 7 +------ 5 files changed, 40 insertions(+), 48 deletions(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 99b33a5b33c8..84627cbd5b5b 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2101,6 +2101,27 @@ again: return 0; } +/* + * Execute search and call btrfs_previous_item to traverse backwards if the item + * was not found. + * + * Return 0 if found, 1 if not found and < 0 if error. + */ +int btrfs_search_backwards(struct btrfs_root *root, struct btrfs_key *key, + struct btrfs_path *path) +{ + int ret; + + ret = btrfs_search_slot(NULL, root, key, path, 0, 0); + if (ret > 0) + ret = btrfs_previous_item(root, path, key->objectid, key->type); + + if (ret == 0) + btrfs_item_key_to_cpu(path->nodes[0], key, path->slots[0]); + + return ret; +} + /* * adjust the pointers going up the tree, starting at level * making sure the right key of each node is points to 'key'. diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index f17be4b023cb..a898257ad2b5 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2908,6 +2908,10 @@ static inline int btrfs_insert_empty_item(struct btrfs_trans_handle *trans, int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path); int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path, u64 time_seq); + +int btrfs_search_backwards(struct btrfs_root *root, struct btrfs_key *key, + struct btrfs_path *path); + static inline int btrfs_next_old_item(struct btrfs_root *root, struct btrfs_path *p, u64 time_seq) { diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 85c8b5a87a6a..ba1dab6a5012 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2389,23 +2389,16 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, key.offset = (u64)-1; while (1) { - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_backwards(root, &key, path); if (ret < 0) goto out; else if (ret > 0) { - ret = btrfs_previous_item(root, path, dirid, - BTRFS_INODE_REF_KEY); - if (ret < 0) - goto out; - else if (ret > 0) { - ret = -ENOENT; - goto out; - } + ret = -ENOENT; + goto out; } l = path->nodes[0]; slot = path->slots[0]; - btrfs_item_key_to_cpu(l, &key, slot); iref = btrfs_item_ptr(l, slot, struct btrfs_inode_ref); len = btrfs_inode_ref_name_len(l, iref); @@ -2480,23 +2473,16 @@ static int btrfs_search_path_in_tree_user(struct inode *inode, key.type = BTRFS_INODE_REF_KEY; key.offset = (u64)-1; while (1) { - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (ret < 0) { + ret = btrfs_search_backwards(root, &key, path); + if (ret < 0) + goto out_put; + else if (ret > 0) { + ret = -ENOENT; goto out_put; - } else if (ret > 0) { - ret = btrfs_previous_item(root, path, dirid, - BTRFS_INODE_REF_KEY); - if (ret < 0) { - goto out_put; - } else if (ret > 0) { - ret = -ENOENT; - goto out_put; - } } leaf = path->nodes[0]; slot = path->slots[0]; - btrfs_item_key_to_cpu(leaf, &key, slot); iref = btrfs_item_ptr(leaf, slot, struct btrfs_inode_ref); len = btrfs_inode_ref_name_len(leaf, iref); diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index d444338db3c6..409bee3e7587 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1201,21 +1201,14 @@ char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info, key.type = BTRFS_ROOT_BACKREF_KEY; key.offset = (u64)-1; - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_backwards(root, &key, path); if (ret < 0) { goto err; } else if (ret > 0) { - ret = btrfs_previous_item(root, path, subvol_objectid, - BTRFS_ROOT_BACKREF_KEY); - if (ret < 0) { - goto err; - } else if (ret > 0) { - ret = -ENOENT; - goto err; - } + ret = -ENOENT; + goto err; } - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); subvol_objectid = key.offset; root_ref = btrfs_item_ptr(path->nodes[0], path->slots[0], @@ -1248,21 +1241,14 @@ char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info, key.type = BTRFS_INODE_REF_KEY; key.offset = (u64)-1; - ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0); + ret = btrfs_search_backwards(fs_root, &key, path); if (ret < 0) { goto err; } else if (ret > 0) { - ret = btrfs_previous_item(fs_root, path, dirid, - BTRFS_INODE_REF_KEY); - if (ret < 0) { - goto err; - } else if (ret > 0) { - ret = -ENOENT; - goto err; - } + ret = -ENOENT; + goto err; } - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); dirid = key.offset; inode_ref = btrfs_item_ptr(path->nodes[0], diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 230192d097c4..536e60c6ade3 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1586,14 +1586,9 @@ again: key.offset = search_start; key.type = BTRFS_DEV_EXTENT_KEY; - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ret = btrfs_search_backwards(root, &key, path); if (ret < 0) goto out; - if (ret > 0) { - ret = btrfs_previous_item(root, path, key.objectid, key.type); - if (ret < 0) - goto out; - } while (1) { l = path->nodes[0]; From 98caf9531e1de8104b45fabbab4b6c2f290068fa Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Tue, 27 Jul 2021 16:17:26 -0500 Subject: [PATCH 338/417] btrfs: allocate file_ra_state on stack in readahead_cache Instead of allocating file_ra_state using kmalloc, allocate on stack. sizeof(struct readahead) = 32 bytes. Reviewed-by: Anand Jain Signed-off-by: Goldwyn Rodrigues Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-cache.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 2131ae5b9ed7..8eeb65278ac0 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -344,19 +344,13 @@ fail: static void readahead_cache(struct inode *inode) { - struct file_ra_state *ra; + struct file_ra_state ra; unsigned long last_index; - ra = kzalloc(sizeof(*ra), GFP_NOFS); - if (!ra) - return; - - file_ra_state_init(ra, inode->i_mapping); + file_ra_state_init(&ra, inode->i_mapping); last_index = (i_size_read(inode) - 1) >> PAGE_SHIFT; - page_cache_sync_readahead(inode->i_mapping, ra, NULL, 0, last_index); - - kfree(ra); + page_cache_sync_readahead(inode->i_mapping, &ra, NULL, 0, last_index); } static int io_ctl_init(struct btrfs_io_ctl *io_ctl, struct inode *inode, From 0afb603afc3e3dd15c99dd34d5e18b46f9f5c0e4 Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Tue, 27 Jul 2021 16:17:29 -0500 Subject: [PATCH 339/417] btrfs: allocate btrfs_ioctl_quota_rescan_args on stack Instead of using kmalloc() to allocate btrfs_ioctl_quota_rescan_args, allocate btrfs_ioctl_quota_rescan_args on stack, the size is reasonably small and ioctls are called in process context. sizeof(btrfs_ioctl_quota_rescan_args) = 64 Reviewed-by: Anand Jain Signed-off-by: Goldwyn Rodrigues Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index ba1dab6a5012..3d0ae797cd8d 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4403,25 +4403,20 @@ drop_write: static long btrfs_ioctl_quota_rescan_status(struct btrfs_fs_info *fs_info, void __user *arg) { - struct btrfs_ioctl_quota_rescan_args *qsa; + struct btrfs_ioctl_quota_rescan_args qsa = {0}; int ret = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - qsa = kzalloc(sizeof(*qsa), GFP_KERNEL); - if (!qsa) - return -ENOMEM; - if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) { - qsa->flags = 1; - qsa->progress = fs_info->qgroup_rescan_progress.objectid; + qsa.flags = 1; + qsa.progress = fs_info->qgroup_rescan_progress.objectid; } - if (copy_to_user(arg, qsa, sizeof(*qsa))) + if (copy_to_user(arg, &qsa, sizeof(qsa))) ret = -EFAULT; - kfree(qsa); return ret; } From c853a5783ebe123847886d432354931874367292 Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Tue, 27 Jul 2021 16:17:30 -0500 Subject: [PATCH 340/417] btrfs: allocate btrfs_ioctl_defrag_range_args on stack Instead of using kmalloc() to allocate btrfs_ioctl_defrag_range_args, allocate btrfs_ioctl_defrag_range_args on stack, the size is reasonably small and ioctls are called in process context. sizeof(btrfs_ioctl_defrag_range_args) = 48 Reviewed-by: Anand Jain Signed-off-by: Goldwyn Rodrigues Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 3d0ae797cd8d..d09eaa83b5d2 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3096,7 +3096,7 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp) { struct inode *inode = file_inode(file); struct btrfs_root *root = BTRFS_I(inode)->root; - struct btrfs_ioctl_defrag_range_args *range; + struct btrfs_ioctl_defrag_range_args range = {0}; int ret; ret = mnt_want_write_file(file); @@ -3134,33 +3134,24 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp) goto out; } - range = kzalloc(sizeof(*range), GFP_KERNEL); - if (!range) { - ret = -ENOMEM; - goto out; - } - if (argp) { - if (copy_from_user(range, argp, - sizeof(*range))) { + if (copy_from_user(&range, argp, sizeof(range))) { ret = -EFAULT; - kfree(range); goto out; } /* compression requires us to start the IO */ - if ((range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)) { - range->flags |= BTRFS_DEFRAG_RANGE_START_IO; - range->extent_thresh = (u32)-1; + if ((range.flags & BTRFS_DEFRAG_RANGE_COMPRESS)) { + range.flags |= BTRFS_DEFRAG_RANGE_START_IO; + range.extent_thresh = (u32)-1; } } else { /* the rest are all set to zero by kzalloc */ - range->len = (u64)-1; + range.len = (u64)-1; } ret = btrfs_defrag_file(file_inode(file), file, - range, BTRFS_OLDEST_GENERATION, 0); + &range, BTRFS_OLDEST_GENERATION, 0); if (ret > 0) ret = 0; - kfree(range); break; default: ret = -EINVAL; From dce2815039061116c41da1db24b9282e6e5e1734 Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Tue, 27 Jul 2021 16:17:31 -0500 Subject: [PATCH 341/417] btrfs: allocate backref_ctx on stack in find_extent_clone Instead of using kmalloc() to allocate backref_ctx, allocate backref_ctx on stack. The size is reasonably small. sizeof(backref_ctx) = 48 Reviewed-by: Anand Jain Signed-off-by: Goldwyn Rodrigues Signed-off-by: David Sterba --- fs/btrfs/send.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 75cff564dedf..72f9b865e847 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -1307,7 +1307,7 @@ static int find_extent_clone(struct send_ctx *sctx, u64 flags = 0; struct btrfs_file_extent_item *fi; struct extent_buffer *eb = path->nodes[0]; - struct backref_ctx *backref_ctx = NULL; + struct backref_ctx backref_ctx = {0}; struct clone_root *cur_clone_root; struct btrfs_key found_key; struct btrfs_path *tmp_path; @@ -1322,12 +1322,6 @@ static int find_extent_clone(struct send_ctx *sctx, /* We only use this path under the commit sem */ tmp_path->need_commit_sem = 0; - backref_ctx = kmalloc(sizeof(*backref_ctx), GFP_KERNEL); - if (!backref_ctx) { - ret = -ENOMEM; - goto out; - } - if (data_offset >= ino_size) { /* * There may be extents that lie behind the file's size. @@ -1392,12 +1386,12 @@ static int find_extent_clone(struct send_ctx *sctx, cur_clone_root->found_refs = 0; } - backref_ctx->sctx = sctx; - backref_ctx->found = 0; - backref_ctx->cur_objectid = ino; - backref_ctx->cur_offset = data_offset; - backref_ctx->found_itself = 0; - backref_ctx->extent_len = num_bytes; + backref_ctx.sctx = sctx; + backref_ctx.found = 0; + backref_ctx.cur_objectid = ino; + backref_ctx.cur_offset = data_offset; + backref_ctx.found_itself = 0; + backref_ctx.extent_len = num_bytes; /* * The last extent of a file may be too large due to page alignment. @@ -1405,7 +1399,7 @@ static int find_extent_clone(struct send_ctx *sctx, * __iterate_backrefs work. */ if (data_offset + num_bytes >= ino_size) - backref_ctx->extent_len = ino_size - data_offset; + backref_ctx.extent_len = ino_size - data_offset; /* * Now collect all backrefs. @@ -1416,12 +1410,12 @@ static int find_extent_clone(struct send_ctx *sctx, extent_item_pos = 0; ret = iterate_extent_inodes(fs_info, found_key.objectid, extent_item_pos, 1, __iterate_backrefs, - backref_ctx, false); + &backref_ctx, false); if (ret < 0) goto out; - if (!backref_ctx->found_itself) { + if (!backref_ctx.found_itself) { /* found a bug in backref code? */ ret = -EIO; btrfs_err(fs_info, @@ -1434,7 +1428,7 @@ static int find_extent_clone(struct send_ctx *sctx, "find_extent_clone: data_offset=%llu, ino=%llu, num_bytes=%llu, logical=%llu", data_offset, ino, num_bytes, logical); - if (!backref_ctx->found) + if (!backref_ctx.found) btrfs_debug(fs_info, "no clones found"); cur_clone_root = NULL; @@ -1458,7 +1452,6 @@ static int find_extent_clone(struct send_ctx *sctx, out: btrfs_free_path(tmp_path); - kfree(backref_ctx); return ret; } From 1c167b87f4f9c89e33dcffb92a45e30f937f04d6 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 29 Jul 2021 15:28:34 +0100 Subject: [PATCH 342/417] btrfs: remove unnecessary NULL check for the new inode during rename exchange At the very end of btrfs_rename_exchange(), in case an error happened, we are checking if 'new_inode' is NULL, but that is not needed since during a rename exchange, unlike regular renames, 'new_inode' can never be NULL, and if it were, we would have a crashed much earlier when we dereference it multiple times. So remove the check because it is not necessary and because it is causing static checkers to emit a warning. I probably introduced the check by copy-pasting similar code from btrfs_rename(), where 'new_inode' can be NULL, in commit 86e8aa0e772cab ("Btrfs: unpin logs if rename exchange operation fails"). Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index bc41d6c8d8d8..d8a1e58b4bc8 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9523,8 +9523,7 @@ out_fail: if (btrfs_inode_in_log(BTRFS_I(old_dir), fs_info->generation) || btrfs_inode_in_log(BTRFS_I(new_dir), fs_info->generation) || btrfs_inode_in_log(BTRFS_I(old_inode), fs_info->generation) || - (new_inode && - btrfs_inode_in_log(BTRFS_I(new_inode), fs_info->generation))) + btrfs_inode_in_log(BTRFS_I(new_inode), fs_info->generation)) btrfs_set_log_full_commit(trans); if (root_log_pinned) { From d135a5339611352047462ef5943aee3a1202aa37 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 29 Jul 2021 15:29:01 +0100 Subject: [PATCH 343/417] btrfs: remove no longer needed full sync flag check at inode_logged() Now that we are checking if the inode's logged_trans is 0 to detect the possibility of the inode having been evicted and reloaded, the test for the full sync flag (BTRFS_INODE_NEEDS_FULL_SYNC) is no longer needed at tree-log.c:inode_logged(). Its purpose was to detect the possibility of a previous eviction as well, since when an inode is loaded the full sync flag is always set on it (and only cleared after the inode is logged). So just remove the check and update the comment. The check for the inode's logged_trans being 0 was added recently by the patch with the subject "btrfs: eliminate some false positives when checking if inode was logged". Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/tree-log.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 567adc3de11a..5debb8c3663c 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -3435,16 +3435,14 @@ static bool inode_logged(struct btrfs_trans_handle *trans, /* * The inode's logged_trans is always 0 when we load it (because it is * not persisted in the inode item or elsewhere). So if it is 0, the - * inode was last modified in the current transaction and has the - * full_sync flag set, then the inode may have been logged before in - * the current transaction, then evicted and loaded again in the current - * transaction - or may have never been logged in the current transaction, - * but since we can not be sure, we have to assume it was, otherwise our - * callers can leave an inconsistent log. + * inode was last modified in the current transaction then the inode may + * have been logged before in the current transaction, then evicted and + * loaded again in the current transaction - or may have never been logged + * in the current transaction, but since we can not be sure, we have to + * assume it was, otherwise our callers can leave an inconsistent log. */ if (inode->logged_trans == 0 && inode->last_trans == trans->transid && - test_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &inode->runtime_flags) && !test_bit(BTRFS_FS_LOG_RECOVERING, &trans->fs_info->flags)) return true; From 1f295373022e84683bc5768caca46bdba3a376c1 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 29 Jul 2021 15:30:21 +0100 Subject: [PATCH 344/417] btrfs: update comment at log_conflicting_inodes() A comment at log_conflicting_inodes() mentions that we check the inode's logged_trans field instead of using btrfs_inode_in_log() because the field last_log_commit is not updated when we log that an inode exists and the inode has the full sync flag (BTRFS_INODE_NEEDS_FULL_SYNC) set. The part about the full sync flag is not true anymore since commit 9acc8103ab594f ("btrfs: fix unpersisted i_size on fsync after expanding truncate"), so update the comment to not mention that part anymore. Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/tree-log.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 5debb8c3663c..1ce7e4480256 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -5092,8 +5092,8 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans, /* * Check the inode's logged_trans only instead of * btrfs_inode_in_log(). This is because the last_log_commit of - * the inode is not updated when we only log that it exists and - * it has the full sync bit set (see btrfs_log_inode()). + * the inode is not updated when we only log that it exists (see + * btrfs_log_inode()). */ if (BTRFS_I(inode)->logged_trans == trans->transid) { spin_unlock(&BTRFS_I(inode)->lock); From 77233c2d2ec95030afcaf9fd90e4bdd6125e5c15 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Mon, 9 Aug 2021 20:41:17 +0900 Subject: [PATCH 345/417] btrfs: zoned: allow disabling of zone auto reclaim Automatically reclaiming dirty zones might not always be desired for all workloads, especially as there are currently still some rough edges with the relocation code on zoned filesystems. Allow disabling zone auto reclaim on a per filesystem basis by writing 0 as the threshold value. Reviewed-by: Naohiro Aota Signed-off-by: Johannes Thumshirn Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-cache.c | 7 ++++--- fs/btrfs/sysfs.c | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 8eeb65278ac0..e91440bd0794 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -2538,6 +2538,7 @@ static int __btrfs_add_free_space_zoned(struct btrfs_block_group *block_group, struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; u64 offset = bytenr - block_group->start; u64 to_free, to_unusable; + const int bg_reclaim_threshold = READ_ONCE(fs_info->bg_reclaim_threshold); spin_lock(&ctl->tree_lock); if (!used) @@ -2567,9 +2568,9 @@ static int __btrfs_add_free_space_zoned(struct btrfs_block_group *block_group, /* All the region is now unusable. Mark it as unused and reclaim */ if (block_group->zone_unusable == block_group->length) { btrfs_mark_bg_unused(block_group); - } else if (block_group->zone_unusable >= - div_factor_fine(block_group->length, - fs_info->bg_reclaim_threshold)) { + } else if (bg_reclaim_threshold && + block_group->zone_unusable >= + div_factor_fine(block_group->length, bg_reclaim_threshold)) { btrfs_mark_bg_to_reclaim(block_group); } diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index bfe5e27617b0..c1261309a817 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -984,7 +984,8 @@ static ssize_t btrfs_bg_reclaim_threshold_show(struct kobject *kobj, struct btrfs_fs_info *fs_info = to_fs_info(kobj); ssize_t ret; - ret = scnprintf(buf, PAGE_SIZE, "%d\n", fs_info->bg_reclaim_threshold); + ret = scnprintf(buf, PAGE_SIZE, "%d\n", + READ_ONCE(fs_info->bg_reclaim_threshold)); return ret; } @@ -1001,10 +1002,10 @@ static ssize_t btrfs_bg_reclaim_threshold_store(struct kobject *kobj, if (ret) return ret; - if (thresh <= 50 || thresh > 100) + if (thresh != 0 && (thresh <= 50 || thresh > 100)) return -EINVAL; - fs_info->bg_reclaim_threshold = thresh; + WRITE_ONCE(fs_info->bg_reclaim_threshold, thresh); return len; } From ba86dd9fe60e5853fbff96f2658212908b83f271 Mon Sep 17 00:00:00 2001 From: Naohiro Aota Date: Mon, 9 Aug 2021 13:32:30 +0900 Subject: [PATCH 346/417] btrfs: zoned: suppress reclaim error message on EAGAIN btrfs_relocate_chunk() can fail with -EAGAIN when e.g. send operations are running. The message can fail btrfs/187 and it's unnecessary because we anyway add it back to the reclaim list. btrfs_reclaim_bgs_work() `-> btrfs_relocate_chunk() `-> btrfs_relocate_block_group() `-> reloc_chunk_start() `-> if (fs_info->send_in_progress) `-> return -EAGAIN CC: stable@vger.kernel.org # 5.13+ Fixes: 18bb8bbf13c1 ("btrfs: zoned: automatically reclaim zones") Reviewed-by: Johannes Thumshirn Signed-off-by: Naohiro Aota Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/block-group.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index d5421ee0d366..a3b830b8410a 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -1561,7 +1561,7 @@ void btrfs_reclaim_bgs_work(struct work_struct *work) div64_u64(zone_unusable * 100, bg->length)); trace_btrfs_reclaim_block_group(bg); ret = btrfs_relocate_chunk(fs_info, bg->start); - if (ret) + if (ret && ret != -EAGAIN) btrfs_err(fs_info, "error relocating chunk %llu", bg->start); From 0ae79c6fe70d5c5c645733b7ed39d5e6021d8c9a Mon Sep 17 00:00:00 2001 From: Naohiro Aota Date: Mon, 9 Aug 2021 13:13:44 +0900 Subject: [PATCH 347/417] btrfs: zoned: fix block group alloc_offset calculation alloc_offset is offset from the start of a block group and @offset is actually an address in logical space. Thus, we need to consider block_group->start when calculating them. Fixes: 011b41bffa3d ("btrfs: zoned: advance allocation pointer after tree log node") CC: stable@vger.kernel.org # 5.12+ Signed-off-by: Naohiro Aota Signed-off-by: David Sterba --- fs/btrfs/free-space-cache.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index e91440bd0794..da0eee7c9e5f 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -2647,8 +2647,11 @@ int btrfs_remove_free_space(struct btrfs_block_group *block_group, * btrfs_pin_extent_for_log_replay() when replaying the log. * Advance the pointer not to overwrite the tree-log nodes. */ - if (block_group->alloc_offset < offset + bytes) - block_group->alloc_offset = offset + bytes; + if (block_group->start + block_group->alloc_offset < + offset + bytes) { + block_group->alloc_offset = + offset + bytes - block_group->start; + } return 0; } From 63fb5879db7ca94fefac12cf7a5a051cee889c12 Mon Sep 17 00:00:00 2001 From: Naohiro Aota Date: Mon, 9 Aug 2021 09:29:18 +0900 Subject: [PATCH 348/417] btrfs: zoned: add asserts on splitting extent_map We call split_zoned_em() on an extent_map on submitting a bio for it. Thus, we can assume the extent_map is PINNED, not LOGGING, and in the modified list. Add ASSERT()s to ensure the extent_maps after the split also has the proper flags set and are in the modified list. Suggested-by: Filipe Manana Reviewed-by: Johannes Thumshirn Signed-off-by: Naohiro Aota Signed-off-by: David Sterba --- fs/btrfs/inode.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d8a1e58b4bc8..09bd7c11c99b 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2303,7 +2303,6 @@ static int split_zoned_em(struct btrfs_inode *inode, u64 start, u64 len, struct extent_map *split_mid = NULL; struct extent_map *split_post = NULL; int ret = 0; - int modified; unsigned long flags; /* Sanity check */ @@ -2333,11 +2332,12 @@ static int split_zoned_em(struct btrfs_inode *inode, u64 start, u64 len, ASSERT(em->len == len); ASSERT(!test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)); ASSERT(em->block_start < EXTENT_MAP_LAST_BYTE); + ASSERT(test_bit(EXTENT_FLAG_PINNED, &em->flags)); + ASSERT(!test_bit(EXTENT_FLAG_LOGGING, &em->flags)); + ASSERT(!list_empty(&em->list)); flags = em->flags; clear_bit(EXTENT_FLAG_PINNED, &em->flags); - clear_bit(EXTENT_FLAG_LOGGING, &flags); - modified = !list_empty(&em->list); /* First, replace the em with a new extent_map starting from * em->start */ split_pre->start = em->start; @@ -2351,7 +2351,7 @@ static int split_zoned_em(struct btrfs_inode *inode, u64 start, u64 len, split_pre->compress_type = em->compress_type; split_pre->generation = em->generation; - replace_extent_mapping(em_tree, em, split_pre, modified); + replace_extent_mapping(em_tree, em, split_pre, 1); /* * Now we only have an extent_map at: @@ -2371,7 +2371,7 @@ static int split_zoned_em(struct btrfs_inode *inode, u64 start, u64 len, split_mid->flags = flags; split_mid->compress_type = em->compress_type; split_mid->generation = em->generation; - add_extent_mapping(em_tree, split_mid, modified); + add_extent_mapping(em_tree, split_mid, 1); } if (post) { @@ -2385,7 +2385,7 @@ static int split_zoned_em(struct btrfs_inode *inode, u64 start, u64 len, split_post->flags = flags; split_post->compress_type = em->compress_type; split_post->generation = em->generation; - add_extent_mapping(em_tree, split_post, modified); + add_extent_mapping(em_tree, split_post, 1); } /* Once for us */ From e4571b8c5e9ffa1e85c0c671995bd4dcc5c75091 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Fri, 6 Aug 2021 18:24:15 +0800 Subject: [PATCH 349/417] btrfs: fix NULL pointer dereference when deleting device by invalid id [BUG] It's easy to trigger NULL pointer dereference, just by removing a non-existing device id: # mkfs.btrfs -f -m single -d single /dev/test/scratch1 \ /dev/test/scratch2 # mount /dev/test/scratch1 /mnt/btrfs # btrfs device remove 3 /mnt/btrfs Then we have the following kernel NULL pointer dereference: BUG: kernel NULL pointer dereference, address: 0000000000000000 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: 0000 [#1] PREEMPT SMP NOPTI CPU: 9 PID: 649 Comm: btrfs Not tainted 5.14.0-rc3-custom+ #35 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:btrfs_rm_device+0x4de/0x6b0 [btrfs] btrfs_ioctl+0x18bb/0x3190 [btrfs] ? lock_is_held_type+0xa5/0x120 ? find_held_lock.constprop.0+0x2b/0x80 ? do_user_addr_fault+0x201/0x6a0 ? lock_release+0xd2/0x2d0 ? __x64_sys_ioctl+0x83/0xb0 __x64_sys_ioctl+0x83/0xb0 do_syscall_64+0x3b/0x90 entry_SYSCALL_64_after_hwframe+0x44/0xae [CAUSE] Commit a27a94c2b0c7 ("btrfs: Make btrfs_find_device_by_devspec return btrfs_device directly") moves the "missing" device path check into btrfs_rm_device(). But btrfs_rm_device() itself can have case where it only receives @devid, with NULL as @device_path. In that case, calling strcmp() on NULL will trigger the NULL pointer dereference. Before that commit, we handle the "missing" case inside btrfs_find_device_by_devspec(), which will not check @device_path at all if @devid is provided, thus no way to trigger the bug. [FIX] Before calling strcmp(), also make sure @device_path is not NULL. Fixes: a27a94c2b0c7 ("btrfs: Make btrfs_find_device_by_devspec return btrfs_device directly") CC: stable@vger.kernel.org # 5.4+ Reported-by: butt3rflyh4ck Reviewed-by: Anand Jain Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 536e60c6ade3..7fec0c68b744 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2074,7 +2074,7 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path, if (IS_ERR(device)) { if (PTR_ERR(device) == -ENOENT && - strcmp(device_path, "missing") == 0) + device_path && strcmp(device_path, "missing") == 0) ret = BTRFS_ERROR_DEV_MISSING_NOT_FOUND; else ret = PTR_ERR(device); From e7849e33cf5d785568b181e3c15236e32c7dfdb2 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Tue, 10 Aug 2021 21:55:59 +0800 Subject: [PATCH 350/417] btrfs: sysfs: document structures and their associated files Sysfs file has grown big. It takes some time to locate the correct struct attribute to add new files. Create a table and map the struct attribute to its sysfs path. Also, fix the comment about the debug sysfs path. And add the comments to the attributes instead of attribute group, where sysfs file names are defined. Signed-off-by: Anand Jain Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/sysfs.c | 91 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 75 insertions(+), 16 deletions(-) diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index c1261309a817..25a6f587852b 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -22,6 +22,26 @@ #include "block-group.h" #include "qgroup.h" +/* + * Structure name Path + * -------------------------------------------------------------------------- + * btrfs_supported_static_feature_attrs /sys/fs/btrfs/features + * btrfs_supported_feature_attrs /sys/fs/btrfs/features and + * /sys/fs/btrfs//features + * btrfs_attrs /sys/fs/btrfs/ + * devid_attrs /sys/fs/btrfs//devinfo/ + * allocation_attrs /sys/fs/btrfs//allocation + * qgroup_attrs /sys/fs/btrfs//qgroups/_ + * space_info_attrs /sys/fs/btrfs//allocation/ + * raid_attrs /sys/fs/btrfs//allocation// + * + * When built with BTRFS_CONFIG_DEBUG: + * + * btrfs_debug_feature_attrs /sys/fs/btrfs/debug + * btrfs_debug_mount_attrs /sys/fs/btrfs//debug + * discard_debug_attrs /sys/fs/btrfs//debug/discard + */ + struct btrfs_feature_attr { struct kobj_attribute kobj_attr; enum btrfs_feature_set feature_set; @@ -271,6 +291,13 @@ BTRFS_FEAT_ATTR_INCOMPAT(zoned, ZONED); BTRFS_FEAT_ATTR_COMPAT_RO(verity, VERITY); #endif +/* + * Features which depend on feature bits and may differ between each fs. + * + * /sys/fs/btrfs/features - all available features implemeted by this version + * /sys/fs/btrfs/UUID/features - features of the fs which are enabled or + * can be changed on a mounted filesystem. + */ static struct attribute *btrfs_supported_feature_attrs[] = { BTRFS_FEAT_ATTR_PTR(mixed_backref), BTRFS_FEAT_ATTR_PTR(default_subvol), @@ -294,13 +321,6 @@ static struct attribute *btrfs_supported_feature_attrs[] = { NULL }; -/* - * Features which depend on feature bits and may differ between each fs. - * - * /sys/fs/btrfs/features lists all available features of this kernel while - * /sys/fs/btrfs/UUID/features shows features of the fs which are enabled or - * can be changed online. - */ static const struct attribute_group btrfs_feature_attr_group = { .name = "features", .is_visible = btrfs_feature_visible, @@ -384,6 +404,12 @@ static ssize_t supported_sectorsizes_show(struct kobject *kobj, BTRFS_ATTR(static_feature, supported_sectorsizes, supported_sectorsizes_show); +/* + * Features which only depend on kernel version. + * + * These are listed in /sys/fs/btrfs/features along with + * btrfs_supported_feature_attrs. + */ static struct attribute *btrfs_supported_static_feature_attrs[] = { BTRFS_ATTR_PTR(static_feature, rmdir_subvol), BTRFS_ATTR_PTR(static_feature, supported_checksums), @@ -393,12 +419,6 @@ static struct attribute *btrfs_supported_static_feature_attrs[] = { NULL }; -/* - * Features which only depend on kernel version. - * - * These are listed in /sys/fs/btrfs/features along with - * btrfs_feature_attr_group - */ static const struct attribute_group btrfs_static_feature_attr_group = { .name = "features", .attrs = btrfs_supported_static_feature_attrs, @@ -557,6 +577,11 @@ static ssize_t btrfs_discard_max_discard_size_store(struct kobject *kobj, BTRFS_ATTR_RW(discard, max_discard_size, btrfs_discard_max_discard_size_show, btrfs_discard_max_discard_size_store); +/* + * Per-filesystem debugging of discard (when mounted with discard=async). + * + * Path: /sys/fs/btrfs//debug/discard/ + */ static const struct attribute *discard_debug_attrs[] = { BTRFS_ATTR_PTR(discard, discardable_bytes), BTRFS_ATTR_PTR(discard, discardable_extents), @@ -570,15 +595,19 @@ static const struct attribute *discard_debug_attrs[] = { }; /* - * Runtime debugging exported via sysfs + * Per-filesystem runtime debugging exported via sysfs. * - * /sys/fs/btrfs/debug - applies to module or all filesystems - * /sys/fs/btrfs/UUID - applies only to the given filesystem + * Path: /sys/fs/btrfs/UUID/debug/ */ static const struct attribute *btrfs_debug_mount_attrs[] = { NULL, }; +/* + * Runtime debugging exported via sysfs, applies to all mounted filesystems. + * + * Path: /sys/fs/btrfs/debug + */ static struct attribute *btrfs_debug_feature_attrs[] = { NULL }; @@ -647,6 +676,11 @@ static ssize_t raid_bytes_show(struct kobject *kobj, return scnprintf(buf, PAGE_SIZE, "%llu\n", val); } +/* + * Allocation information about block group profiles. + * + * Path: /sys/fs/btrfs//allocation/// + */ static struct attribute *raid_attrs[] = { BTRFS_ATTR_PTR(raid, total_bytes), BTRFS_ATTR_PTR(raid, used_bytes), @@ -686,6 +720,11 @@ SPACE_INFO_ATTR(bytes_zone_unusable); SPACE_INFO_ATTR(disk_used); SPACE_INFO_ATTR(disk_total); +/* + * Allocation information about block group types. + * + * Path: /sys/fs/btrfs//allocation// + */ static struct attribute *space_info_attrs[] = { BTRFS_ATTR_PTR(space_info, flags), BTRFS_ATTR_PTR(space_info, total_bytes), @@ -713,6 +752,11 @@ static struct kobj_type space_info_ktype = { .default_groups = space_info_groups, }; +/* + * Allocation information about block groups. + * + * Path: /sys/fs/btrfs//allocation/ + */ static const struct attribute *allocation_attrs[] = { BTRFS_ATTR_PTR(allocation, global_rsv_reserved), BTRFS_ATTR_PTR(allocation, global_rsv_size), @@ -1012,6 +1056,11 @@ static ssize_t btrfs_bg_reclaim_threshold_store(struct kobject *kobj, BTRFS_ATTR_RW(, bg_reclaim_threshold, btrfs_bg_reclaim_threshold_show, btrfs_bg_reclaim_threshold_store); +/* + * Per-filesystem information and stats. + * + * Path: /sys/fs/btrfs// + */ static const struct attribute *btrfs_attrs[] = { BTRFS_ATTR_PTR(, label), BTRFS_ATTR_PTR(, nodesize), @@ -1521,6 +1570,11 @@ static ssize_t btrfs_devinfo_error_stats_show(struct kobject *kobj, } BTRFS_ATTR(devid, error_stats, btrfs_devinfo_error_stats_show); +/* + * Information about one device. + * + * Path: /sys/fs/btrfs//devinfo// + */ static struct attribute *devid_attrs[] = { BTRFS_ATTR_PTR(devid, error_stats), BTRFS_ATTR_PTR(devid, in_fs_metadata), @@ -1810,6 +1864,11 @@ QGROUP_RSV_ATTR(data, BTRFS_QGROUP_RSV_DATA); QGROUP_RSV_ATTR(meta_pertrans, BTRFS_QGROUP_RSV_META_PERTRANS); QGROUP_RSV_ATTR(meta_prealloc, BTRFS_QGROUP_RSV_META_PREALLOC); +/* + * Qgroup information. + * + * Path: /sys/fs/btrfs//qgroups/_/ + */ static struct attribute *qgroup_attrs[] = { BTRFS_ATTR_PTR(qgroup, referenced), BTRFS_ATTR_PTR(qgroup, exclusive), From c2fd68b6b2b00f0a6280b5971028c10c8f0ba70f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:40 +0200 Subject: [PATCH 351/417] namei: add mapping aware lookup helper Various filesystems rely on the lookup_one_len() helper to lookup a single path component relative to a well-known starting point. Allow such filesystems to support idmapped mounts by adding a version of this helper to take the idmap into account when calling inode_permission(). This change is a required to let btrfs (and other filesystems) support idmapped mounts. Cc: Christoph Hellwig Cc: Al Viro Cc: Matthew Wilcox (Oracle) Cc: linux-fsdevel@vger.kernel.org Reviewed-by: Josef Bacik Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner Signed-off-by: David Sterba --- fs/namei.c | 43 +++++++++++++++++++++++++++++++++++++------ include/linux/namei.h | 1 + 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index bf6d8a738c59..902df46e7dd3 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2575,8 +2575,9 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, } EXPORT_SYMBOL(vfs_path_lookup); -static int lookup_one_len_common(const char *name, struct dentry *base, - int len, struct qstr *this) +static int lookup_one_common(struct user_namespace *mnt_userns, + const char *name, struct dentry *base, int len, + struct qstr *this) { this->name = name; this->len = len; @@ -2604,7 +2605,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base, return err; } - return inode_permission(&init_user_ns, base->d_inode, MAY_EXEC); + return inode_permission(mnt_userns, base->d_inode, MAY_EXEC); } /** @@ -2628,7 +2629,7 @@ struct dentry *try_lookup_one_len(const char *name, struct dentry *base, int len WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -2655,7 +2656,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -2664,6 +2665,36 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) } EXPORT_SYMBOL(lookup_one_len); +/** + * lookup_one - filesystem helper to lookup single pathname component + * @mnt_userns: user namespace of the mount the lookup is performed from + * @name: pathname component to lookup + * @base: base directory to lookup from + * @len: maximum length @len should be interpreted to + * + * Note that this routine is purely a helper for filesystem usage and should + * not be called by generic code. + * + * The caller must hold base->i_mutex. + */ +struct dentry *lookup_one(struct user_namespace *mnt_userns, const char *name, + struct dentry *base, int len) +{ + struct dentry *dentry; + struct qstr this; + int err; + + WARN_ON_ONCE(!inode_is_locked(base->d_inode)); + + err = lookup_one_common(mnt_userns, name, base, len, &this); + if (err) + return ERR_PTR(err); + + dentry = lookup_dcache(&this, base, 0); + return dentry ? dentry : __lookup_slow(&this, base, 0); +} +EXPORT_SYMBOL(lookup_one); + /** * lookup_one_len_unlocked - filesystem helper to lookup single pathname component * @name: pathname component to lookup @@ -2683,7 +2714,7 @@ struct dentry *lookup_one_len_unlocked(const char *name, int err; struct dentry *ret; - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); diff --git a/include/linux/namei.h b/include/linux/namei.h index be9a2b349ca7..e89329bb3134 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -68,6 +68,7 @@ extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int); extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int); +struct dentry *lookup_one(struct user_namespace *, const char *, struct dentry *, int); extern int follow_down_one(struct path *); extern int follow_down(struct path *); From b3b6f5b9225506abc2e8d1f393761a6e509b791f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:41 +0200 Subject: [PATCH 352/417] btrfs: handle idmaps in btrfs_new_inode() Extend btrfs_new_inode() to take the idmapped mount into account when initializing a new inode. This is just a matter of passing down the mount's userns. The rest is taken care of in inode_init_owner(). This is a preliminary patch to make the individual btrfs inode operations idmapped mount aware. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 09bd7c11c99b..0f9017a207ab 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6422,6 +6422,7 @@ static void btrfs_inherit_iflags(struct inode *inode, struct inode *dir) static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct user_namespace *mnt_userns, struct inode *dir, const char *name, int name_len, u64 ref_objectid, u64 objectid, @@ -6531,7 +6532,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, if (ret != 0) goto fail_unlock; - inode_init_owner(&init_user_ns, inode, dir, mode); + inode_init_owner(mnt_userns, inode, dir, mode); inode_set_bytes(inode, 0); inode->i_mtime = current_time(inode); @@ -6716,9 +6717,9 @@ static int btrfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name, - dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, - mode, &index); + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + dentry->d_name.name, dentry->d_name.len, + btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { err = PTR_ERR(inode); inode = NULL; @@ -6780,9 +6781,9 @@ static int btrfs_create(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name, - dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, - mode, &index); + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + dentry->d_name.name, dentry->d_name.len, + btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { err = PTR_ERR(inode); inode = NULL; @@ -6925,8 +6926,9 @@ static int btrfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_fail; - inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name, - dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + dentry->d_name.name, dentry->d_name.len, + btrfs_ino(BTRFS_I(dir)), objectid, S_IFDIR | mode, &index); if (IS_ERR(inode)) { err = PTR_ERR(inode); @@ -9038,7 +9040,8 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, if (err < 0) return err; - inode = btrfs_new_inode(trans, new_root, NULL, "..", 2, ino, ino, + inode = btrfs_new_inode(trans, new_root, &init_user_ns, NULL, "..", 2, + ino, ino, S_IFDIR | (~current_umask() & S_IRWXUGO), &index); if (IS_ERR(inode)) @@ -9559,7 +9562,7 @@ static int btrfs_whiteout_for_rename(struct btrfs_trans_handle *trans, if (ret) return ret; - inode = btrfs_new_inode(trans, root, dir, + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), @@ -10071,9 +10074,10 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name, - dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), - objectid, S_IFLNK|S_IRWXUGO, &index); + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + dentry->d_name.name, dentry->d_name.len, + btrfs_ino(BTRFS_I(dir)), objectid, + S_IFLNK | S_IRWXUGO, &index); if (IS_ERR(inode)) { err = PTR_ERR(inode); inode = NULL; @@ -10422,7 +10426,7 @@ static int btrfs_tmpfile(struct user_namespace *mnt_userns, struct inode *dir, if (ret) goto out; - inode = btrfs_new_inode(trans, root, dir, NULL, 0, + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, NULL, 0, btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { ret = PTR_ERR(inode); From ca07274c3da901c7daa2c9280679c282b7c03bef Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:42 +0200 Subject: [PATCH 353/417] btrfs: allow idmapped rename inode op Enable btrfs_rename() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 0f9017a207ab..04a37b750a17 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9550,6 +9550,7 @@ out_notrans: static int btrfs_whiteout_for_rename(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry) { @@ -9562,7 +9563,7 @@ static int btrfs_whiteout_for_rename(struct btrfs_trans_handle *trans, if (ret) return ret; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), @@ -9599,9 +9600,10 @@ out: return ret; } -static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry, - unsigned int flags) +static int btrfs_rename(struct user_namespace *mnt_userns, + struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) { struct btrfs_fs_info *fs_info = btrfs_sb(old_dir->i_sb); struct btrfs_trans_handle *trans; @@ -9791,8 +9793,8 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, } if (flags & RENAME_WHITEOUT) { - ret = btrfs_whiteout_for_rename(trans, root, old_dir, - old_dentry); + ret = btrfs_whiteout_for_rename(trans, root, mnt_userns, + old_dir, old_dentry); if (ret) { btrfs_abort_transaction(trans, ret); @@ -9842,7 +9844,8 @@ static int btrfs_rename2(struct user_namespace *mnt_userns, struct inode *old_di return btrfs_rename_exchange(old_dir, old_dentry, new_dir, new_dentry); - return btrfs_rename(old_dir, old_dentry, new_dir, new_dentry, flags); + return btrfs_rename(mnt_userns, old_dir, old_dentry, new_dir, + new_dentry, flags); } struct btrfs_delalloc_work { From c020d2eaf1a84ba8611fe2a232f4951faa98e0e0 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:43 +0200 Subject: [PATCH 354/417] btrfs: allow idmapped getattr inode op Enable btrfs_getattr() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 04a37b750a17..04a30cab799d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9288,7 +9288,7 @@ static int btrfs_getattr(struct user_namespace *mnt_userns, STATX_ATTR_IMMUTABLE | STATX_ATTR_NODUMP); - generic_fillattr(&init_user_ns, inode, stat); + generic_fillattr(mnt_userns, inode, stat); stat->dev = BTRFS_I(inode)->root->anon_dev; spin_lock(&BTRFS_I(inode)->lock); From 72105277dcfca69175cea713f5edda4132839e14 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:44 +0200 Subject: [PATCH 355/417] btrfs: allow idmapped mknod inode op Enable btrfs_mknod() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 04a30cab799d..4a1cc106ab1f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6717,7 +6717,7 @@ static int btrfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { From e93ca491d03fda28db54a3d6ddc15f03a61364d7 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:45 +0200 Subject: [PATCH 356/417] btrfs: allow idmapped create inode op Enable btrfs_create() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 4a1cc106ab1f..a75661cd68ec 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6781,7 +6781,7 @@ static int btrfs_create(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { From b0b3e44d346c91dde3899d37eddf867b9b36ffdc Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:46 +0200 Subject: [PATCH 357/417] btrfs: allow idmapped mkdir inode op Enable btrfs_mkdir() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a75661cd68ec..16bd56edb8e1 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6926,7 +6926,7 @@ static int btrfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_fail; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, S_IFDIR | mode, &index); From 5a0521086e5fc5eb51690d4fc63fd26fdb5ae881 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:47 +0200 Subject: [PATCH 358/417] btrfs: allow idmapped symlink inode op Enable btrfs_symlink() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 16bd56edb8e1..0ffd48f9b685 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10077,7 +10077,7 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, S_IFLNK | S_IRWXUGO, &index); From 98b6ab5fc0988242114a4f0e02ed225685d9cc2b Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:48 +0200 Subject: [PATCH 359/417] btrfs: allow idmapped tmpfile inode op Enable btrfs_tmpfile() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 0ffd48f9b685..c040d9925077 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10429,7 +10429,7 @@ static int btrfs_tmpfile(struct user_namespace *mnt_userns, struct inode *dir, if (ret) goto out; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, NULL, 0, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, NULL, 0, btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { ret = PTR_ERR(inode); From d4d09464614227a6cc9ae42cd1d761e4320e4ebc Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:49 +0200 Subject: [PATCH 360/417] btrfs: allow idmapped setattr inode op Enable btrfs_setattr() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c040d9925077..88ac63f3b57b 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5392,7 +5392,7 @@ static int btrfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentr if (btrfs_root_readonly(root)) return -EROFS; - err = setattr_prepare(&init_user_ns, dentry, attr); + err = setattr_prepare(mnt_userns, dentry, attr); if (err) return err; @@ -5403,13 +5403,12 @@ static int btrfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentr } if (attr->ia_valid) { - setattr_copy(&init_user_ns, inode, attr); + setattr_copy(mnt_userns, inode, attr); inode_inc_iversion(inode); err = btrfs_dirty_inode(inode); if (!err && attr->ia_valid & ATTR_MODE) - err = posix_acl_chmod(&init_user_ns, inode, - inode->i_mode); + err = posix_acl_chmod(mnt_userns, inode, inode->i_mode); } return err; From 3bc71ba02cf5376b390289bef8c9f5d6049f1866 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:50 +0200 Subject: [PATCH 361/417] btrfs: allow idmapped permission inode op Enable btrfs_permission() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 88ac63f3b57b..6ef2448a9937 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10403,7 +10403,7 @@ static int btrfs_permission(struct user_namespace *mnt_userns, if (BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) return -EACCES; } - return generic_permission(&init_user_ns, inode, mask); + return generic_permission(mnt_userns, inode, mask); } static int btrfs_tmpfile(struct user_namespace *mnt_userns, struct inode *dir, From 5474bf400f16bd1f930627ea65b698bca09dcfc6 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:51 +0200 Subject: [PATCH 362/417] btrfs: check whether fsgid/fsuid are mapped during subvolume creation When a new subvolume is created btrfs currently doesn't check whether the fsgid/fsuid of the caller actually have a mapping in the user namespace attached to the filesystem. The VFS always checks this to make sure that the caller's fsgid/fsuid can be represented on-disk. This is most relevant for filesystems that can be mounted inside user namespaces but it is in general a good hardening measure to prevent unrepresentable gid/uid from being written to disk. Since we want to support idmapped mounts for btrfs ioctls to create subvolumes in follow-up patches this becomes important since we want to make sure the fsgid/fsuid of the caller as mapped according to the idmapped mount can be represented on-disk. Simply add the missing fsuidgid_has_mapping() line from the VFS may_create() version to btrfs_may_create(). Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index d09eaa83b5d2..3661d2ce8ef6 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -877,6 +877,8 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; + if (!fsuidgid_has_mapping(dir->i_sb, &init_user_ns)) + return -EOVERFLOW; return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); } From 4d4340c912ccc351da5578f73c68f1109dcc8e2d Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:52 +0200 Subject: [PATCH 363/417] btrfs: allow idmapped SNAP_CREATE/SUBVOL_CREATE ioctls Creating subvolumes and snapshots is one of the core features of btrfs and is even available to unprivileged users. Make it possible to use subvolume and snapshot creation on idmapped mounts. This is a fairly straightforward operation since all the permission checking helpers are already capable of handling idmapped mounts. So we just need to pass down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 3 ++- fs/btrfs/inode.c | 5 +++-- fs/btrfs/ioctl.c | 47 +++++++++++++++++++++++++++-------------------- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index a898257ad2b5..f07c82fafa04 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3164,7 +3164,8 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, struct extent_state **cached_state); int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, struct btrfs_root *new_root, - struct btrfs_root *parent_root); + struct btrfs_root *parent_root, + struct user_namespace *mnt_userns); void btrfs_set_delalloc_extent(struct inode *inode, struct extent_state *state, unsigned *bits); void btrfs_clear_delalloc_extent(struct inode *inode, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 6ef2448a9937..2aa9646bce56 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9028,7 +9028,8 @@ out: */ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, struct btrfs_root *new_root, - struct btrfs_root *parent_root) + struct btrfs_root *parent_root, + struct user_namespace *mnt_userns) { struct inode *inode; int err; @@ -9039,7 +9040,7 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, if (err < 0) return err; - inode = btrfs_new_inode(trans, new_root, &init_user_ns, NULL, "..", 2, + inode = btrfs_new_inode(trans, new_root, mnt_userns, NULL, "..", 2, ino, ino, S_IFDIR | (~current_umask() & S_IRWXUGO), &index); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 3661d2ce8ef6..910b5142c8c5 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -499,8 +499,8 @@ int __pure btrfs_is_empty_uuid(u8 *uuid) return 1; } -static noinline int create_subvol(struct inode *dir, - struct dentry *dentry, +static noinline int create_subvol(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *dentry, const char *name, int namelen, struct btrfs_qgroup_inherit *inherit) { @@ -645,7 +645,7 @@ static noinline int create_subvol(struct inode *dir, goto fail; } - ret = btrfs_create_subvol_root(trans, new_root, root); + ret = btrfs_create_subvol_root(trans, new_root, root, mnt_userns); btrfs_put_root(new_root); if (ret) { /* We potentially lose an unused inode item here */ @@ -871,15 +871,16 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) } /* copy of may_create in fs/namei.c() */ -static inline int btrfs_may_create(struct inode *dir, struct dentry *child) +static inline int btrfs_may_create(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *child) { if (d_really_is_positive(child)) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; - if (!fsuidgid_has_mapping(dir->i_sb, &init_user_ns)) + if (!fsuidgid_has_mapping(dir->i_sb, mnt_userns)) return -EOVERFLOW; - return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); + return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC); } /* @@ -888,6 +889,7 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child) * inside this filesystem so it's quite a bit simpler. */ static noinline int btrfs_mksubvol(const struct path *parent, + struct user_namespace *mnt_userns, const char *name, int namelen, struct btrfs_root *snap_src, bool readonly, @@ -902,12 +904,12 @@ static noinline int btrfs_mksubvol(const struct path *parent, if (error == -EINTR) return error; - dentry = lookup_one_len(name, parent->dentry, namelen); + dentry = lookup_one(mnt_userns, name, parent->dentry, namelen); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_unlock; - error = btrfs_may_create(dir, dentry); + error = btrfs_may_create(mnt_userns, dir, dentry); if (error) goto out_dput; @@ -929,7 +931,7 @@ static noinline int btrfs_mksubvol(const struct path *parent, if (snap_src) error = create_snapshot(snap_src, dir, dentry, readonly, inherit); else - error = create_subvol(dir, dentry, name, namelen, inherit); + error = create_subvol(mnt_userns, dir, dentry, name, namelen, inherit); if (!error) fsnotify_mkdir(dir, dentry); @@ -943,6 +945,7 @@ out_unlock: } static noinline int btrfs_mksnapshot(const struct path *parent, + struct user_namespace *mnt_userns, const char *name, int namelen, struct btrfs_root *root, bool readonly, @@ -972,7 +975,7 @@ static noinline int btrfs_mksnapshot(const struct path *parent, btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); - ret = btrfs_mksubvol(parent, name, namelen, + ret = btrfs_mksubvol(parent, mnt_userns, name, namelen, root, readonly, inherit); out: if (snapshot_force_cow) @@ -1801,6 +1804,7 @@ out_drop: } static noinline int __btrfs_ioctl_snap_create(struct file *file, + struct user_namespace *mnt_userns, const char *name, unsigned long fd, int subvol, bool readonly, struct btrfs_qgroup_inherit *inherit) @@ -1828,8 +1832,8 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, } if (subvol) { - ret = btrfs_mksubvol(&file->f_path, name, namelen, - NULL, readonly, inherit); + ret = btrfs_mksubvol(&file->f_path, mnt_userns, name, + namelen, NULL, readonly, inherit); } else { struct fd src = fdget(fd); struct inode *src_inode; @@ -1843,16 +1847,17 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, btrfs_info(BTRFS_I(file_inode(file))->root->fs_info, "Snapshot src from another FS"); ret = -EXDEV; - } else if (!inode_owner_or_capable(&init_user_ns, src_inode)) { + } else if (!inode_owner_or_capable(mnt_userns, src_inode)) { /* * Subvolume creation is not restricted, but snapshots * are limited to own subvolumes only */ ret = -EPERM; } else { - ret = btrfs_mksnapshot(&file->f_path, name, namelen, - BTRFS_I(src_inode)->root, - readonly, inherit); + ret = btrfs_mksnapshot(&file->f_path, mnt_userns, + name, namelen, + BTRFS_I(src_inode)->root, + readonly, inherit); } fdput(src); } @@ -1876,8 +1881,9 @@ static noinline int btrfs_ioctl_snap_create(struct file *file, return PTR_ERR(vol_args); vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; - ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, - subvol, false, NULL); + ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file), + vol_args->name, vol_args->fd, subvol, + false, NULL); kfree(vol_args); return ret; @@ -1935,8 +1941,9 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file, } } - ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, - subvol, readonly, inherit); + ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file), + vol_args->name, vol_args->fd, subvol, + readonly, inherit); if (ret) goto free_inherit; free_inherit: From c4ed533bdc7960873ab0258a4d18569061b4b0b4 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:53 +0200 Subject: [PATCH 364/417] btrfs: allow idmapped SNAP_DESTROY ioctls Destroying subvolumes and snapshots are important features of btrfs. Both operations are available to unprivileged users if the filesystem has been mounted with the "user_subvol_rm_allowed" mount option. Allow subvolume and snapshot deletion on idmapped mounts. This is a fairly straightforward operation since all the permission checking helpers are already capable of handling idmapped mounts. So we just need to pass down the mount's userns. Subvolumes and snapshots can either be deleted by specifying their name or - if BTRFS_IOC_SNAP_DESTROY_V2 is used - by their subvolume or snapshot id if the BTRFS_SUBVOL_SPEC_BY_ID is set. This feature is blocked on idmapped mounts as this allows filesystem wide subvolume deletions and thus can escape the scope of what's exposed under the mount identified by the fd passed with the ioctl. This means that even the root or CAP_SYS_ADMIN capable user can't delete a subvolume via BTRFS_SUBVOL_SPEC_BY_ID. This is intentional. The root user is currently already subject to permission checks in btrfs_may_delete() including whether the inode's i_uid/i_gid of the directory the subvolume is located in have a mapping in the caller's idmapping. For this to fail isn't currently possible since a btrfs filesystem can't be mounted with a non-initial idmapping but it shows that even the root user would fail to delete a subvolume if the relevant inode isn't mapped in their idmapping. The idmapped mount case is the same in principle. This isn't a huge problem a root user wanting to delete arbitrary subvolumes can just always create another (even detached) mount without an idmapping attached. In addition, we will allow BTRFS_SUBVOL_SPEC_BY_ID for cases where the subvolume to delete is directly located under inode referenced by the fd passed for the ioctl() in a follow-up commit. Here is an example where a btrfs subvolume is deleted through a subvolume mount that does not expose the subvolume to be delete but it can still be deleted by using the subvolume id: /* Compile the following program as "delete_by_spec". */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include static int rm_subvolume_by_id(int fd, uint64_t subvolid) { struct btrfs_ioctl_vol_args_v2 args = {}; int ret; args.flags = BTRFS_SUBVOL_SPEC_BY_ID; args.subvolid = subvolid; ret = ioctl(fd, BTRFS_IOC_SNAP_DESTROY_V2, &args); if (ret < 0) return -1; return 0; } int main(int argc, char *argv[]) { int subvolid = 0; if (argc < 3) exit(1); fprintf(stderr, "Opening %s\n", argv[1]); int fd = open(argv[1], O_CLOEXEC | O_DIRECTORY); if (fd < 0) exit(2); subvolid = atoi(argv[2]); fprintf(stderr, "Deleting subvolume with subvolid %d\n", subvolid); int ret = rm_subvolume_by_id(fd, subvolid); if (ret < 0) exit(3); exit(0); } #include " #include " #include sudo umount /mnt sudo mount ${LOOPDEV} -o subvol=B/C,user_subvol_rm_allowed /mnt ./delete_by_spec /mnt ${SUBVOLID} With idmapped mounts this can potentially be used by users to delete subvolumes/snapshots they would otherwise not have access to as the idmapping would be applied to an inode that is not exposed in the mount of the subvolume. The fact that this is a filesystem wide operation suggests it might be a good idea to expose this under a separate ioctl that clearly indicates this. In essence, the file descriptor passed with the ioctl is merely used to identify the filesystem on which to operate when BTRFS_SUBVOL_SPEC_BY_ID is used. Reviewed-by: Josef Bacik Reviewed-by: Qu Wenruo Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 910b5142c8c5..6ec30e11ad22 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -837,7 +837,8 @@ free_pending: * nfs_async_unlink(). */ -static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) +static int btrfs_may_delete(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *victim, int isdir) { int error; @@ -847,12 +848,12 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) BUG_ON(d_inode(victim->d_parent) != dir); audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); - error = inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); + error = inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC); if (error) return error; if (IS_APPEND(dir)) return -EPERM; - if (check_sticky(&init_user_ns, dir, d_inode(victim)) || + if (check_sticky(mnt_userns, dir, d_inode(victim)) || IS_APPEND(d_inode(victim)) || IS_IMMUTABLE(d_inode(victim)) || IS_SWAPFILE(d_inode(victim))) return -EPERM; @@ -2907,6 +2908,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, struct btrfs_root *dest = NULL; struct btrfs_ioctl_vol_args *vol_args = NULL; struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL; + struct user_namespace *mnt_userns = file_mnt_user_ns(file); char *subvol_name, *subvol_name_ptr = NULL; int subvol_namelen; int err = 0; @@ -2934,6 +2936,18 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (err) goto out; } else { + /* + * Deleting by subvolume id can be used to delete + * subvolumes/snapshots anywhere in the filesystem. + * Ensure that users can't abuse idmapped mounts of + * btrfs subvolumes/snapshots to perform operations in + * the whole filesystem. + */ + if (mnt_userns != &init_user_ns) { + err = -EOPNOTSUPP; + goto out; + } + if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) { err = -EINVAL; goto out; @@ -3018,7 +3032,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, err = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT); if (err == -EINTR) goto free_subvol_name; - dentry = lookup_one_len(subvol_name, parent, subvol_namelen); + dentry = lookup_one(mnt_userns, subvol_name, parent, subvol_namelen); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); goto out_unlock_dir; @@ -3060,14 +3074,13 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (root == dest) goto out_dput; - err = inode_permission(&init_user_ns, inode, - MAY_WRITE | MAY_EXEC); + err = inode_permission(mnt_userns, inode, MAY_WRITE | MAY_EXEC); if (err) goto out_dput; } /* check if subvolume may be deleted by a user */ - err = btrfs_may_delete(dir, dentry, 1); + err = btrfs_may_delete(mnt_userns, dir, dentry, 1); if (err) goto out_dput; From aabb34e7a31c608dd7c00db9ad320e05941a39d0 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:54 +0200 Subject: [PATCH 365/417] btrfs: relax restrictions for SNAP_DESTROY_V2 with subvolids So far we prevented the deletion of subvolumes and snapshots using subvolume ids possible with the BTRFS_SUBVOL_SPEC_BY_ID flag. This restriction is necessary on idmapped mounts as this allows filesystem wide subvolume and snapshot deletions and thus can escape the scope of what's exposed under the mount identified by the fd passed with the ioctl. Deletion by subvolume id works by looking for an alias of the parent of the subvolume or snapshot to be deleted. The parent alias can be anywhere in the filesystem. However, as long as the alias of the parent that is found is the same as the one identified by the file descriptor passed through the ioctl we can allow the deletion. Reviewed-by: Josef Bacik Reviewed-by: Qu Wenruo Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 6ec30e11ad22..63e7b2616302 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2936,17 +2936,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (err) goto out; } else { - /* - * Deleting by subvolume id can be used to delete - * subvolumes/snapshots anywhere in the filesystem. - * Ensure that users can't abuse idmapped mounts of - * btrfs subvolumes/snapshots to perform operations in - * the whole filesystem. - */ - if (mnt_userns != &init_user_ns) { - err = -EOPNOTSUPP; - goto out; - } + struct inode *old_dir; if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) { err = -EINVAL; @@ -2984,6 +2974,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, err = PTR_ERR(parent); goto out_drop_write; } + old_dir = dir; dir = d_inode(parent); /* @@ -2994,6 +2985,20 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, */ destroy_parent = true; + /* + * On idmapped mounts, deletion via subvolid is + * restricted to subvolumes that are immediate + * ancestors of the inode referenced by the file + * descriptor in the ioctl. Otherwise the idmapping + * could potentially be abused to delete subvolumes + * anywhere in the filesystem the user wouldn't be able + * to delete without an idmapped mount. + */ + if (old_dir != dir && mnt_userns != &init_user_ns) { + err = -EOPNOTSUPP; + goto free_parent; + } + subvol_name_ptr = btrfs_get_subvol_name_from_objectid( fs_info, vol_args2->subvolid); if (IS_ERR(subvol_name_ptr)) { From e4fed17a32b6b1017ff2fb4cd73938abeeadd907 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:55 +0200 Subject: [PATCH 366/417] btrfs: allow idmapped SET_RECEIVED_SUBVOL ioctls The SET_RECEIVED_SUBVOL ioctls are used to set information about a received subvolume. Make it possible to set information about a received subvolume on idmapped mounts. This is a fairly straightforward operation since all the permission checking helpers are already capable of handling idmapped mounts. So we just need to pass down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 63e7b2616302..aa60a9578747 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4448,6 +4448,7 @@ static long btrfs_ioctl_quota_rescan_wait(struct btrfs_fs_info *fs_info, } static long _btrfs_ioctl_set_received_subvol(struct file *file, + struct user_namespace *mnt_userns, struct btrfs_ioctl_received_subvol_args *sa) { struct inode *inode = file_inode(file); @@ -4459,7 +4460,7 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file, int ret = 0; int received_uuid_changed; - if (!inode_owner_or_capable(&init_user_ns, inode)) + if (!inode_owner_or_capable(mnt_userns, inode)) return -EPERM; ret = mnt_want_write_file(file); @@ -4564,7 +4565,7 @@ static long btrfs_ioctl_set_received_subvol_32(struct file *file, args64->rtime.nsec = args32->rtime.nsec; args64->flags = args32->flags; - ret = _btrfs_ioctl_set_received_subvol(file, args64); + ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_user_ns(file), args64); if (ret) goto out; @@ -4598,7 +4599,7 @@ static long btrfs_ioctl_set_received_subvol(struct file *file, if (IS_ERR(sa)) return PTR_ERR(sa); - ret = _btrfs_ioctl_set_received_subvol(file, sa); + ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_user_ns(file), sa); if (ret) goto out; From 39e1674ff0351f6a47d3105e51bb1f9c72b3f20e Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:56 +0200 Subject: [PATCH 367/417] btrfs: allow idmapped SUBVOL_SETFLAGS ioctl Setting flags on subvolumes or snapshots are core features of btrfs. The SUBVOL_SETFLAGS ioctl is especially important as it allows to make subvolumes and snapshots read-only or read-write. Allow setting flags on btrfs subvolumes and snapshots on idmapped mounts. This is a fairly straightforward operation since all the permission checking helpers are already capable of handling idmapped mounts. So we just need to pass down the mount's userns. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index aa60a9578747..9d54149bad6e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1988,7 +1988,7 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, u64 flags; int ret = 0; - if (!inode_owner_or_capable(&init_user_ns, inode)) + if (!inode_owner_or_capable(file_mnt_user_ns(file), inode)) return -EPERM; ret = mnt_want_write_file(file); From 6623d9a0b0ce340d3e4dc4b18705ad212a49677a Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:57 +0200 Subject: [PATCH 368/417] btrfs: allow idmapped INO_LOOKUP_USER ioctl The INO_LOOKUP_USER is an unprivileged version of the INO_LOOKUP ioctl and has the following restrictions. The main difference between the two is that INO_LOOKUP is filesystem wide operation wheres INO_LOOKUP_USER is scoped beneath the file descriptor passed with the ioctl. Specifically, INO_LOOKUP_USER must adhere to the following restrictions: - The caller must be privileged over each inode of each path component for the path they are trying to lookup. - The path for the subvolume the caller is trying to lookup must be reachable from the inode associated with the file descriptor passed with the ioctl. The second condition makes it possible to scope the lookup of the path to the mount identified by the file descriptor passed with the ioctl. This allows us to enable this ioctl on idmapped mounts. Specifically, this is possible because all child subvolumes of a parent subvolume are reachable when the parent subvolume is mounted. So if the user had access to open the parent subvolume or has been given the fd then they can lookup the path if they had access to it provided they were privileged over each path component. Note, the INO_LOOKUP_USER ioctl allows a user to learn the path and name of a subvolume even though they would otherwise be restricted from doing so via regular VFS-based lookup. So think about a parent subvolume with multiple child subvolumes. Someone could mount he parent subvolume and restrict access to the child subvolumes by overmounting them with empty directories. At this point the user can't traverse the child subvolumes and they can't open files in the child subvolumes. However, they can still learn the path of child subvolumes as long as they have access to the parent subvolume by using the INO_LOOKUP_USER ioctl. The underlying assumption here is that it's ok that the lookup ioctls can't really take mounts into account other than the original mount the fd belongs to during lookup. Since this assumption is baked into the original INO_LOOKUP_USER ioctl we can extend it to idmapped mounts. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9d54149bad6e..41524f9aeac3 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2439,7 +2439,8 @@ out: return ret; } -static int btrfs_search_path_in_tree_user(struct inode *inode, +static int btrfs_search_path_in_tree_user(struct user_namespace *mnt_userns, + struct inode *inode, struct btrfs_ioctl_ino_lookup_user_args *args) { struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; @@ -2530,7 +2531,7 @@ static int btrfs_search_path_in_tree_user(struct inode *inode, ret = PTR_ERR(temp_inode); goto out_put; } - ret = inode_permission(&init_user_ns, temp_inode, + ret = inode_permission(mnt_userns, temp_inode, MAY_READ | MAY_EXEC); iput(temp_inode); if (ret) { @@ -2672,7 +2673,7 @@ static int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp) return -EACCES; } - ret = btrfs_search_path_in_tree_user(inode, args); + ret = btrfs_search_path_in_tree_user(file_mnt_user_ns(file), inode, args); if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) ret = -EFAULT; From 4a8b34afa9c94c180d16999e405d380cc0477369 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:58 +0200 Subject: [PATCH 369/417] btrfs: handle ACLs on idmapped mounts Make the ACL code idmapped mount aware. The POSIX default and POSIX access ACLs are the only ACLs other than some specific xattrs that take DAC permissions into account. On an idmapped mount they need to be translated according to the mount's userns. The main change is done to __btrfs_set_acl() which is responsible for translating POSIX ACLs to their final on-disk representation. The btrfs_init_acl() helper does not need to take the idmapped mount into account since it is called in the context of file creation operations (mknod, create, mkdir, symlink, tmpfile) and is used for btrfs_init_inode_security() to copy POSIX default and POSIX access permissions from the parent directory. These ACLs need to be inherited unmodified from the parent directory. This is identical to what we do for ext4 and xfs. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/acl.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index d95eb5c8cb37..c9f9789e828f 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -53,7 +53,8 @@ struct posix_acl *btrfs_get_acl(struct inode *inode, int type) } static int __btrfs_set_acl(struct btrfs_trans_handle *trans, - struct inode *inode, struct posix_acl *acl, int type) + struct user_namespace *mnt_userns, + struct inode *inode, struct posix_acl *acl, int type) { int ret, size = 0; const char *name; @@ -114,12 +115,12 @@ int btrfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, umode_t old_mode = inode->i_mode; if (type == ACL_TYPE_ACCESS && acl) { - ret = posix_acl_update_mode(&init_user_ns, inode, + ret = posix_acl_update_mode(mnt_userns, inode, &inode->i_mode, &acl); if (ret) return ret; } - ret = __btrfs_set_acl(NULL, inode, acl, type); + ret = __btrfs_set_acl(NULL, mnt_userns, inode, acl, type); if (ret) inode->i_mode = old_mode; return ret; @@ -140,14 +141,14 @@ int btrfs_init_acl(struct btrfs_trans_handle *trans, return ret; if (default_acl) { - ret = __btrfs_set_acl(trans, inode, default_acl, + ret = __btrfs_set_acl(trans, &init_user_ns, inode, default_acl, ACL_TYPE_DEFAULT); posix_acl_release(default_acl); } if (acl) { if (!ret) - ret = __btrfs_set_acl(trans, inode, acl, + ret = __btrfs_set_acl(trans, &init_user_ns, inode, acl, ACL_TYPE_ACCESS); posix_acl_release(acl); } From 5b9b26f5d0b88b74001dcfe4ab8a8f2f4e744112 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 27 Jul 2021 12:48:59 +0200 Subject: [PATCH 370/417] btrfs: allow idmapped mount Now that we converted btrfs internally to account for idmapped mounts allow the creation of idmapped mounts on by setting the FS_ALLOW_IDMAP flag. We only need to raise this flag on the btrfs_root_fs_type filesystem since btrfs_mount_root() is ultimately responsible for allocating the superblock and is called into from btrfs_mount() associated with btrfs_fs_type. The conversion of the btrfs inode operations was straightforward. Regarding btrfs specific ioctls that perform checks based on inode permissions only those have been allowed that are not filesystem wide operations and hence can be reasonably charged against a specific mount. Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 409bee3e7587..537d90bf5d84 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2362,7 +2362,7 @@ static struct file_system_type btrfs_root_fs_type = { .name = "btrfs", .mount = btrfs_mount_root, .kill_sb = btrfs_kill_super, - .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA, + .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA | FS_ALLOW_IDMAP, }; MODULE_ALIAS_FS("btrfs"); From 8be2ba2e0e11ade6ab96d8887dbb12abbd3540f4 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 29 Jul 2021 18:52:46 +0100 Subject: [PATCH 371/417] btrfs: avoid unnecessarily logging directories that had no changes There are several cases where when logging an inode we need to log its parent directories or logging subdirectories when logging a directory. There are cases however where we end up logging a directory even if it was not changed in the current transaction, no dentries added or removed since the last transaction. While this is harmless from a functional point of view, it is a waste time as it brings no advantage. One example where this is triggered is the following: $ mkfs.btrfs -f /dev/sdc $ mount /dev/sdc /mnt $ mkdir /mnt/A $ mkdir /mnt/B $ mkdir /mnt/C $ touch /mnt/A/foo $ ln /mnt/A/foo /mnt/B/bar $ ln /mnt/A/foo /mnt/C/baz $ sync $ rm -f /mnt/A/foo $ xfs_io -c "fsync" /mnt/B/bar This last fsync ends up logging directories A, B and C, however we only need to log directory A, as B and C were not changed since the last transaction commit. So fix this by changing need_log_inode(), to return false in case the given inode is a directory and has a ->last_trans value smaller than the current transaction's ID. Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/tree-log.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 1ce7e4480256..a1aaa1b8bd5c 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -5613,6 +5613,13 @@ out_unlock: static bool need_log_inode(struct btrfs_trans_handle *trans, struct btrfs_inode *inode) { + /* + * If a directory was not modified, no dentries added or removed, we can + * and should avoid logging it. + */ + if (S_ISDIR(inode->vfs_inode.i_mode) && inode->last_trans < trans->transid) + return false; + /* * If this inode does not have new/updated/deleted xattrs since the last * time it was logged and is flagged as logged in the current transaction, From 3736127a3aa805602b7a2ad60ec9cfce68065fbb Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Mon, 2 Aug 2021 09:34:00 -0300 Subject: [PATCH 372/417] btrfs: tree-log: check btrfs_lookup_data_extent return value Function btrfs_lookup_data_extent calls btrfs_search_slot to verify if the EXTENT_ITEM exists in the extent tree. btrfs_search_slot can return values bellow zero if an error happened. Function replay_one_extent currently checks if the search found something (0 returned) and increments the reference, and if not, it seems to evaluate as 'not found'. Fix the condition by checking if the value was bellow zero and return early. Reviewed-by: Filipe Manana Signed-off-by: Marcos Paulo de Souza Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/tree-log.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index a1aaa1b8bd5c..f7efc26aa82a 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -753,7 +753,9 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans, */ ret = btrfs_lookup_data_extent(fs_info, ins.objectid, ins.offset); - if (ret == 0) { + if (ret < 0) { + goto out; + } else if (ret == 0) { btrfs_init_generic_ref(&ref, BTRFS_ADD_DELAYED_REF, ins.objectid, ins.offset, 0); From 93c60b17f2b5fca2c5931d7944788d1ef5f25528 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 11 Aug 2021 14:37:15 -0400 Subject: [PATCH 373/417] btrfs: reduce the preemptive flushing threshold to 90% The preemptive flushing code was added in order to avoid needing to synchronously wait for ENOSPC flushing to recover space. Once we're almost full however we can essentially flush constantly. We were using 98% as a threshold to determine if we were simply full, however in practice this is a really high bar to hit. For example reports of systems running into this problem had around 94% usage and thus continued to flush. Fix this by lowering the threshold to 90%, which is a more sane value, especially for smaller file systems. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=212185 CC: stable@vger.kernel.org # 5.12+ Fixes: 576fa34830af ("btrfs: improve preemptive background space flushing") Signed-off-by: Josef Bacik Signed-off-by: David Sterba --- fs/btrfs/space-info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index d9c8d738678f..cab532a48bcd 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -733,7 +733,7 @@ static bool need_preemptive_reclaim(struct btrfs_fs_info *fs_info, { u64 global_rsv_size = fs_info->global_block_rsv.reserved; u64 ordered, delalloc; - u64 thresh = div_factor_fine(space_info->total_bytes, 98); + u64 thresh = div_factor_fine(space_info->total_bytes, 90); u64 used; /* If we're just plain full then async reclaim just slows us down. */ From 114623979405abf0b143f9c6688b3ff00ee48338 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 11 Aug 2021 14:37:16 -0400 Subject: [PATCH 374/417] btrfs: do not do preemptive flushing if the majority is global rsv A common characteristic of the bug report where preemptive flushing was going full tilt was the fact that the vast majority of the free metadata space was used up by the global reserve. The hard 90% threshold would cover the majority of these cases, but to be even smarter we should take into account how much of the outstanding reservations are covered by the global block reserve. If the global block reserve accounts for the vast majority of outstanding reservations, skip preemptive flushing, as it will likely just cause churn and pain. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=212185 Signed-off-by: Josef Bacik Signed-off-by: David Sterba --- fs/btrfs/space-info.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index cab532a48bcd..5ada02e0e629 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -741,6 +741,20 @@ static bool need_preemptive_reclaim(struct btrfs_fs_info *fs_info, global_rsv_size) >= thresh) return false; + used = space_info->bytes_may_use + space_info->bytes_pinned; + + /* The total flushable belongs to the global rsv, don't flush. */ + if (global_rsv_size >= used) + return false; + + /* + * 128MiB is 1/4 of the maximum global rsv size. If we have less than + * that devoted to other reservations then there's no sense in flushing, + * we don't have a lot of things that need flushing. + */ + if (used - global_rsv_size <= SZ_128M) + return false; + /* * We have tickets queued, bail so we don't compete with the async * flushers. From 939c7feb19217c752a4b368d35aae1ed98f40b61 Mon Sep 17 00:00:00 2001 From: Naohiro Aota Date: Wed, 11 Aug 2021 15:37:08 +0900 Subject: [PATCH 375/417] btrfs: zoned: fix ordered extent boundary calculation btrfs_lookup_ordered_extent() is supposed to query the offset in a file instead of the logical address. Pass the file offset from submit_extent_page() to calc_bio_boundaries(). Also, calc_bio_boundaries() relies on the bio's operation flag, so move the call site after setting it. Fixes: 390ed29b817e ("btrfs: refactor submit_extent_page() to make bio and its flag tracing easier") Reviewed-by: Qu Wenruo Signed-off-by: Naohiro Aota Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 96de6e70d06c..aaddd7225348 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3241,7 +3241,7 @@ static int btrfs_bio_add_page(struct btrfs_bio_ctrl *bio_ctrl, } static int calc_bio_boundaries(struct btrfs_bio_ctrl *bio_ctrl, - struct btrfs_inode *inode) + struct btrfs_inode *inode, u64 file_offset) { struct btrfs_fs_info *fs_info = inode->root->fs_info; struct btrfs_io_geometry geom; @@ -3283,7 +3283,7 @@ static int calc_bio_boundaries(struct btrfs_bio_ctrl *bio_ctrl, } /* Ordered extent not yet created, so we're good */ - ordered = btrfs_lookup_ordered_extent(inode, logical); + ordered = btrfs_lookup_ordered_extent(inode, file_offset); if (!ordered) { bio_ctrl->len_to_oe_boundary = U32_MAX; return 0; @@ -3300,7 +3300,7 @@ static int alloc_new_bio(struct btrfs_inode *inode, struct writeback_control *wbc, unsigned int opf, bio_end_io_t end_io_func, - u64 disk_bytenr, u32 offset, + u64 disk_bytenr, u32 offset, u64 file_offset, unsigned long bio_flags) { struct btrfs_fs_info *fs_info = inode->root->fs_info; @@ -3317,13 +3317,13 @@ static int alloc_new_bio(struct btrfs_inode *inode, bio = btrfs_bio_alloc(disk_bytenr + offset); bio_ctrl->bio = bio; bio_ctrl->bio_flags = bio_flags; - ret = calc_bio_boundaries(bio_ctrl, inode); - if (ret < 0) - goto error; bio->bi_end_io = end_io_func; bio->bi_private = &inode->io_tree; bio->bi_write_hint = inode->vfs_inode.i_write_hint; bio->bi_opf = opf; + ret = calc_bio_boundaries(bio_ctrl, inode, file_offset); + if (ret < 0) + goto error; if (wbc) { struct block_device *bdev; @@ -3398,6 +3398,7 @@ static int submit_extent_page(unsigned int opf, if (!bio_ctrl->bio) { ret = alloc_new_bio(inode, bio_ctrl, wbc, opf, end_io_func, disk_bytenr, offset, + page_offset(page) + cur, bio_flags); if (ret < 0) return ret; From 0d977e0eba234e01a60bdde27314dc21374201b3 Mon Sep 17 00:00:00 2001 From: Desmond Cheong Zhi Xi Date: Sat, 21 Aug 2021 01:50:40 +0800 Subject: [PATCH 376/417] btrfs: reset replace target device to allocation state on close This crash was observed with a failed assertion on device close: BTRFS: Transaction aborted (error -28) WARNING: CPU: 1 PID: 3902 at fs/btrfs/extent-tree.c:2150 btrfs_run_delayed_refs+0x1d2/0x1e0 [btrfs] Modules linked in: btrfs blake2b_generic libcrc32c crc32c_intel xor zstd_decompress zstd_compress xxhash lzo_compress lzo_decompress raid6_pq loop CPU: 1 PID: 3902 Comm: kworker/u8:4 Not tainted 5.14.0-rc5-default+ #1532 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 Workqueue: events_unbound btrfs_async_reclaim_metadata_space [btrfs] RIP: 0010:btrfs_run_delayed_refs+0x1d2/0x1e0 [btrfs] RSP: 0018:ffffb7a5452d7d80 EFLAGS: 00010282 RAX: 0000000000000000 RBX: 0000000000000003 RCX: 0000000000000000 RDX: 0000000000000001 RSI: ffffffffabee13c4 RDI: 00000000ffffffff RBP: ffff97834176a378 R08: 0000000000000001 R09: 0000000000000001 R10: 0000000000000000 R11: 0000000000000001 R12: ffff97835195d388 R13: 0000000005b08000 R14: ffff978385484000 R15: 000000000000016c FS: 0000000000000000(0000) GS:ffff9783bd800000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000056190d003fe8 CR3: 000000002a81e005 CR4: 0000000000170ea0 Call Trace: flush_space+0x197/0x2f0 [btrfs] btrfs_async_reclaim_metadata_space+0x139/0x300 [btrfs] process_one_work+0x262/0x5e0 worker_thread+0x4c/0x320 ? process_one_work+0x5e0/0x5e0 kthread+0x144/0x170 ? set_kthread_struct+0x40/0x40 ret_from_fork+0x1f/0x30 irq event stamp: 19334989 hardirqs last enabled at (19334997): [] console_unlock+0x2b7/0x400 hardirqs last disabled at (19335006): [] console_unlock+0x33d/0x400 softirqs last enabled at (19334900): [] __do_softirq+0x30d/0x574 softirqs last disabled at (19334893): [] irq_exit_rcu+0x12c/0x140 ---[ end trace 45939e308e0dd3c7 ]--- BTRFS: error (device vdd) in btrfs_run_delayed_refs:2150: errno=-28 No space left BTRFS info (device vdd): forced readonly BTRFS warning (device vdd): failed setting block group ro: -30 BTRFS info (device vdd): suspending dev_replace for unmount assertion failed: !test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state), in fs/btrfs/volumes.c:1150 ------------[ cut here ]------------ kernel BUG at fs/btrfs/ctree.h:3431! invalid opcode: 0000 [#1] PREEMPT SMP CPU: 1 PID: 3982 Comm: umount Tainted: G W 5.14.0-rc5-default+ #1532 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 RIP: 0010:assertfail.constprop.0+0x18/0x1a [btrfs] RSP: 0018:ffffb7a5454c7db8 EFLAGS: 00010246 RAX: 0000000000000068 RBX: ffff978364b91c00 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffffffffabee13c4 RDI: 00000000ffffffff RBP: ffff9783523a4c00 R08: 0000000000000001 R09: 0000000000000001 R10: 0000000000000000 R11: 0000000000000001 R12: ffff9783523a4d18 R13: 0000000000000000 R14: 0000000000000004 R15: 0000000000000003 FS: 00007f61c8f42800(0000) GS:ffff9783bd800000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000056190cffa810 CR3: 0000000030b96002 CR4: 0000000000170ea0 Call Trace: btrfs_close_one_device.cold+0x11/0x55 [btrfs] close_fs_devices+0x44/0xb0 [btrfs] btrfs_close_devices+0x48/0x160 [btrfs] generic_shutdown_super+0x69/0x100 kill_anon_super+0x14/0x30 btrfs_kill_super+0x12/0x20 [btrfs] deactivate_locked_super+0x2c/0xa0 cleanup_mnt+0x144/0x1b0 task_work_run+0x59/0xa0 exit_to_user_mode_loop+0xe7/0xf0 exit_to_user_mode_prepare+0xaf/0xf0 syscall_exit_to_user_mode+0x19/0x50 do_syscall_64+0x4a/0x90 entry_SYSCALL_64_after_hwframe+0x44/0xae This happens when close_ctree is called while a dev_replace hasn't completed. In close_ctree, we suspend the dev_replace, but keep the replace target around so that we can resume the dev_replace procedure when we mount the root again. This is the call trace: close_ctree(): btrfs_dev_replace_suspend_for_unmount(); btrfs_close_devices(): btrfs_close_fs_devices(): btrfs_close_one_device(): ASSERT(!test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)); However, since the replace target sticks around, there is a device with BTRFS_DEV_STATE_REPLACE_TGT set on close, and we fail the assertion in btrfs_close_one_device. To fix this, if we come across the replace target device when closing, we should properly reset it back to allocation state. This fix also ensures that if a non-target device has a corrupted state and has the BTRFS_DEV_STATE_REPLACE_TGT bit set, the assertion will still catch the error. Reported-by: David Sterba Fixes: b2a616676839 ("btrfs: fix rw device counting in __btrfs_free_extra_devids") CC: stable@vger.kernel.org # 4.19+ Reviewed-by: Anand Jain Signed-off-by: Desmond Cheong Zhi Xi Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 7fec0c68b744..ec3a874165de 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1118,6 +1118,9 @@ static void btrfs_close_one_device(struct btrfs_device *device) fs_devices->rw_devices--; } + if (device->devid == BTRFS_DEV_REPLACE_DEVID) + clear_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state); + if (test_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state)) fs_devices->missing_devices--; From 2dc6f19e4f438d4c14987cb17aee38aaf7304e7f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 23 Aug 2021 12:01:18 -0400 Subject: [PATCH 377/417] nlm: minor nlm_lookup_file argument change It'll come in handy to get the whole nlm_lock. Signed-off-by: J. Bruce Fields Signed-off-by: Chuck Lever --- fs/lockd/svc4proc.c | 3 ++- fs/lockd/svcproc.c | 2 +- fs/lockd/svcsubs.c | 15 ++++++++------- include/linux/lockd/lockd.h | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 4c10fb5138f1..bc496bbd696b 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -40,7 +40,8 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, /* Obtain file pointer. Not used by FREE_ALL call. */ if (filp != NULL) { - if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0) + error = nlm_lookup_file(rqstp, &file, lock); + if (error) goto no_locks; *filp = file; diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 4ae4b63b5392..f4e5e0eb30fd 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -69,7 +69,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, /* Obtain file pointer. Not used by FREE_ALL call. */ if (filp != NULL) { - error = cast_status(nlm_lookup_file(rqstp, &file, &lock->fh)); + error = cast_status(nlm_lookup_file(rqstp, &file, lock)); if (error != 0) goto no_locks; *filp = file; diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 028fc152da22..2d62633b39e5 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -82,31 +82,31 @@ static inline unsigned int file_hash(struct nfs_fh *f) */ __be32 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, - struct nfs_fh *f) + struct nlm_lock *lock) { struct nlm_file *file; unsigned int hash; __be32 nfserr; - nlm_debug_print_fh("nlm_lookup_file", f); + nlm_debug_print_fh("nlm_lookup_file", &lock->fh); - hash = file_hash(f); + hash = file_hash(&lock->fh); /* Lock file table */ mutex_lock(&nlm_file_mutex); hlist_for_each_entry(file, &nlm_files[hash], f_list) - if (!nfs_compare_fh(&file->f_handle, f)) + if (!nfs_compare_fh(&file->f_handle, &lock->fh)) goto found; - nlm_debug_print_fh("creating file for", f); + nlm_debug_print_fh("creating file for", &lock->fh); nfserr = nlm_lck_denied_nolocks; file = kzalloc(sizeof(*file), GFP_KERNEL); if (!file) goto out_unlock; - memcpy(&file->f_handle, f, sizeof(struct nfs_fh)); + memcpy(&file->f_handle, &lock->fh, sizeof(struct nfs_fh)); mutex_init(&file->f_mutex); INIT_HLIST_NODE(&file->f_list); INIT_LIST_HEAD(&file->f_blocks); @@ -117,7 +117,8 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, * We have to make sure we have the right credential to open * the file. */ - if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) { + nfserr = nlmsvc_ops->fopen(rqstp, &lock->fh, &file->f_file); + if (nfserr) { dprintk("lockd: open failed (error %d)\n", nfserr); goto out_free; } diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 666f5f310a04..81b71ad2040a 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -286,7 +286,7 @@ void nlmsvc_locks_init_private(struct file_lock *, struct nlm_host *, pid_t); * File handling for the server personality */ __be32 nlm_lookup_file(struct svc_rqst *, struct nlm_file **, - struct nfs_fh *); + struct nlm_lock *); void nlm_release_file(struct nlm_file *); void nlmsvc_release_lockowner(struct nlm_lock *); void nlmsvc_mark_resources(struct net *); From a81041b7d8f08c4e1014173c5483a0f18724a576 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 23 Aug 2021 11:26:39 -0400 Subject: [PATCH 378/417] nlm: minor refactoring Make this lookup slightly more concise, and prepare for changing how we look this up in a following patch. Signed-off-by: J. Bruce Fields Signed-off-by: Chuck Lever --- fs/lockd/svclock.c | 16 ++++++++-------- fs/lockd/svcsubs.c | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 273a81971ed5..bcd180ba9957 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -474,8 +474,8 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, __be32 ret; dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n", - locks_inode(file->f_file)->i_sb->s_id, - locks_inode(file->f_file)->i_ino, + nlmsvc_file_inode(file)->i_sb->s_id, + nlmsvc_file_inode(file)->i_ino, lock->fl.fl_type, lock->fl.fl_pid, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end, @@ -581,8 +581,8 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_lockowner *test_owner; dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n", - locks_inode(file->f_file)->i_sb->s_id, - locks_inode(file->f_file)->i_ino, + nlmsvc_file_inode(file)->i_sb->s_id, + nlmsvc_file_inode(file)->i_ino, lock->fl.fl_type, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); @@ -644,8 +644,8 @@ nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock) int error; dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)\n", - locks_inode(file->f_file)->i_sb->s_id, - locks_inode(file->f_file)->i_ino, + nlmsvc_file_inode(file)->i_sb->s_id, + nlmsvc_file_inode(file)->i_ino, lock->fl.fl_pid, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); @@ -673,8 +673,8 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l int status = 0; dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n", - locks_inode(file->f_file)->i_sb->s_id, - locks_inode(file->f_file)->i_ino, + nlmsvc_file_inode(file)->i_sb->s_id, + nlmsvc_file_inode(file)->i_ino, lock->fl.fl_pid, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 2d62633b39e5..e02951f8a28f 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -45,7 +45,7 @@ static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f) static inline void nlm_debug_print_file(char *msg, struct nlm_file *file) { - struct inode *inode = locks_inode(file->f_file); + struct inode *inode = nlmsvc_file_inode(file); dprintk("lockd: %s %s/%ld\n", msg, inode->i_sb->s_id, inode->i_ino); @@ -416,7 +416,7 @@ nlmsvc_match_sb(void *datap, struct nlm_file *file) { struct super_block *sb = datap; - return sb == locks_inode(file->f_file)->i_sb; + return sb == nlmsvc_file_inode(file)->i_sb; } /** From b661601a9fdf1af8516e1100de8bba84bd41cca4 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 20 Aug 2021 17:02:02 -0400 Subject: [PATCH 379/417] lockd: update nlm_lookup_file reexport comment Update comment to reflect that we *do* allow reexport, whether it's a good idea or not.... Signed-off-by: J. Bruce Fields Signed-off-by: Chuck Lever --- fs/lockd/svcsubs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index e02951f8a28f..13e6ffc219ec 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -111,8 +111,9 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, INIT_HLIST_NODE(&file->f_list); INIT_LIST_HEAD(&file->f_blocks); - /* Open the file. Note that this must not sleep for too long, else - * we would lock up lockd:-) So no NFS re-exports, folks. + /* + * Open the file. Note that if we're reexporting, for example, + * this could block the lockd thread for a while. * * We have to make sure we have the right credential to open * the file. From 91ef658fb8b82837f94ea0d45d14b5b2d2541e70 Mon Sep 17 00:00:00 2001 From: Dmitry Kadashev Date: Thu, 8 Jul 2021 13:34:37 +0700 Subject: [PATCH 380/417] namei: ignore ERR/NULL names in putname() Supporting ERR/NULL names in putname() makes callers code cleaner, and is what some other path walking functions already support for the same reason. This also removes a few existing IS_ERR checks before putname(). Suggested-by: Linus Torvalds Link: https://lore.kernel.org/io-uring/CAHk-=wgCac9hBsYzKMpHk0EbLgQaXR=OUAjHaBtaY+G8A9KhFg@mail.gmail.com/ Acked-by: Linus Torvalds Cc: Al Viro Cc: Christian Brauner Signed-off-by: Dmitry Kadashev Link: https://lore.kernel.org/r/20210708063447.3556403-2-dkadashev@gmail.com Signed-off-by: Jens Axboe --- fs/namei.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index bf6d8a738c59..dc36bda5c2e7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -247,6 +247,9 @@ getname_kernel(const char * filename) void putname(struct filename *name) { + if (IS_ERR_OR_NULL(name)) + return; + BUG_ON(name->refcnt <= 0); if (--name->refcnt > 0) @@ -4728,11 +4731,9 @@ exit1: goto retry; } put_both: - if (!IS_ERR(from)) - putname(from); + putname(from); put_new: - if (!IS_ERR(to)) - putname(to); + putname(to); return error; } From 0ee50b47532a81ab36046241822d1ecb4e08e76d Mon Sep 17 00:00:00 2001 From: Dmitry Kadashev Date: Thu, 8 Jul 2021 13:34:38 +0700 Subject: [PATCH 381/417] namei: change filename_parentat() calling conventions Since commit 5c31b6cedb675 ("namei: saner calling conventions for filename_parentat()") filename_parentat() had the following behavior WRT the passed in struct filename *: * On error the name is consumed (putname() is called on it); * On success the name is returned back as the return value; Now there is a need for filename_create() and filename_lookup() variants that do not consume the passed filename, and following the same "consume the name only on error" semantics is proven to be hard to reason about and result in confusing code. Hence this preparation change splits filename_parentat() into two: one that always consumes the name and another that never consumes the name. This will allow to implement two filename_create() variants in the same way, and is a consistent and hopefully easier to reason about approach. Link: https://lore.kernel.org/io-uring/CAOKbgA7MiqZAq3t-HDCpSGUFfco4hMA9ArAE-74fTpU+EkvKPw@mail.gmail.com/ Cc: Al Viro Cc: Christian Brauner Acked-by: Linus Torvalds Signed-off-by: Dmitry Kadashev Link: https://lore.kernel.org/r/20210708063447.3556403-3-dkadashev@gmail.com Signed-off-by: Jens Axboe --- fs/namei.c | 108 ++++++++++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 55 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index dc36bda5c2e7..aaba6b42b222 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2498,7 +2498,7 @@ static int path_parentat(struct nameidata *nd, unsigned flags, return err; } -static struct filename *filename_parentat(int dfd, struct filename *name, +static int __filename_parentat(int dfd, struct filename *name, unsigned int flags, struct path *parent, struct qstr *last, int *type) { @@ -2506,7 +2506,7 @@ static struct filename *filename_parentat(int dfd, struct filename *name, struct nameidata nd; if (IS_ERR(name)) - return name; + return PTR_ERR(name); set_nameidata(&nd, dfd, name, NULL); retval = path_parentat(&nd, flags | LOOKUP_RCU, parent); if (unlikely(retval == -ECHILD)) @@ -2517,29 +2517,34 @@ static struct filename *filename_parentat(int dfd, struct filename *name, *last = nd.last; *type = nd.last_type; audit_inode(name, parent->dentry, AUDIT_INODE_PARENT); - } else { - putname(name); - name = ERR_PTR(retval); } restore_nameidata(); - return name; + return retval; +} + +static int filename_parentat(int dfd, struct filename *name, + unsigned int flags, struct path *parent, + struct qstr *last, int *type) +{ + int retval = __filename_parentat(dfd, name, flags, parent, last, type); + + putname(name); + return retval; } /* does lookup, returns the object with parent locked */ struct dentry *kern_path_locked(const char *name, struct path *path) { - struct filename *filename; struct dentry *d; struct qstr last; - int type; + int type, error; - filename = filename_parentat(AT_FDCWD, getname_kernel(name), 0, path, + error = filename_parentat(AT_FDCWD, getname_kernel(name), 0, path, &last, &type); - if (IS_ERR(filename)) - return ERR_CAST(filename); + if (error) + return ERR_PTR(error); if (unlikely(type != LAST_NORM)) { path_put(path); - putname(filename); return ERR_PTR(-EINVAL); } inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); @@ -2548,7 +2553,6 @@ struct dentry *kern_path_locked(const char *name, struct path *path) inode_unlock(path->dentry->d_inode); path_put(path); } - putname(filename); return d; } @@ -3585,9 +3589,9 @@ static struct dentry *filename_create(int dfd, struct filename *name, */ lookup_flags &= LOOKUP_REVAL; - name = filename_parentat(dfd, name, lookup_flags, path, &last, &type); - if (IS_ERR(name)) - return ERR_CAST(name); + error = filename_parentat(dfd, name, lookup_flags, path, &last, &type); + if (error) + return ERR_PTR(error); /* * Yucky last component or no last component at all? @@ -3625,7 +3629,6 @@ static struct dentry *filename_create(int dfd, struct filename *name, error = err2; goto fail; } - putname(name); return dentry; fail: dput(dentry); @@ -3636,7 +3639,6 @@ unlock: mnt_drop_write(path->mnt); out: path_put(path); - putname(name); return dentry; } @@ -3927,59 +3929,59 @@ EXPORT_SYMBOL(vfs_rmdir); long do_rmdir(int dfd, struct filename *name) { struct user_namespace *mnt_userns; - int error = 0; + int error; struct dentry *dentry; struct path path; struct qstr last; int type; unsigned int lookup_flags = 0; retry: - name = filename_parentat(dfd, name, lookup_flags, - &path, &last, &type); - if (IS_ERR(name)) - return PTR_ERR(name); + error = __filename_parentat(dfd, name, lookup_flags, &path, &last, &type); + if (error) + goto exit1; switch (type) { case LAST_DOTDOT: error = -ENOTEMPTY; - goto exit1; + goto exit2; case LAST_DOT: error = -EINVAL; - goto exit1; + goto exit2; case LAST_ROOT: error = -EBUSY; - goto exit1; + goto exit2; } error = mnt_want_write(path.mnt); if (error) - goto exit1; + goto exit2; inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); dentry = __lookup_hash(&last, path.dentry, lookup_flags); error = PTR_ERR(dentry); if (IS_ERR(dentry)) - goto exit2; + goto exit3; if (!dentry->d_inode) { error = -ENOENT; - goto exit3; + goto exit4; } error = security_path_rmdir(&path, dentry); if (error) - goto exit3; + goto exit4; mnt_userns = mnt_user_ns(path.mnt); error = vfs_rmdir(mnt_userns, path.dentry->d_inode, dentry); -exit3: +exit4: dput(dentry); -exit2: +exit3: inode_unlock(path.dentry->d_inode); mnt_drop_write(path.mnt); -exit1: +exit2: path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } +exit1: putname(name); return error; } @@ -4073,17 +4075,17 @@ long do_unlinkat(int dfd, struct filename *name) struct inode *delegated_inode = NULL; unsigned int lookup_flags = 0; retry: - name = filename_parentat(dfd, name, lookup_flags, &path, &last, &type); - if (IS_ERR(name)) - return PTR_ERR(name); + error = __filename_parentat(dfd, name, lookup_flags, &path, &last, &type); + if (error) + goto exit1; error = -EISDIR; if (type != LAST_NORM) - goto exit1; + goto exit2; error = mnt_want_write(path.mnt); if (error) - goto exit1; + goto exit2; retry_deleg: inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); dentry = __lookup_hash(&last, path.dentry, lookup_flags); @@ -4100,11 +4102,11 @@ retry_deleg: ihold(inode); error = security_path_unlink(&path, dentry); if (error) - goto exit2; + goto exit3; mnt_userns = mnt_user_ns(path.mnt); error = vfs_unlink(mnt_userns, path.dentry->d_inode, dentry, &delegated_inode); -exit2: +exit3: dput(dentry); } inode_unlock(path.dentry->d_inode); @@ -4117,13 +4119,14 @@ exit2: goto retry_deleg; } mnt_drop_write(path.mnt); -exit1: +exit2: path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; inode = NULL; goto retry; } +exit1: putname(name); return error; @@ -4134,7 +4137,7 @@ slashes: error = -EISDIR; else error = -ENOTDIR; - goto exit2; + goto exit3; } SYSCALL_DEFINE3(unlinkat, int, dfd, const char __user *, pathname, int, flag) @@ -4605,29 +4608,25 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, int error = -EINVAL; if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) - goto put_both; + goto put_names; if ((flags & (RENAME_NOREPLACE | RENAME_WHITEOUT)) && (flags & RENAME_EXCHANGE)) - goto put_both; + goto put_names; if (flags & RENAME_EXCHANGE) target_flags = 0; retry: - from = filename_parentat(olddfd, from, lookup_flags, &old_path, + error = __filename_parentat(olddfd, from, lookup_flags, &old_path, &old_last, &old_type); - if (IS_ERR(from)) { - error = PTR_ERR(from); - goto put_new; - } + if (error) + goto put_names; - to = filename_parentat(newdfd, to, lookup_flags, &new_path, &new_last, + error = __filename_parentat(newdfd, to, lookup_flags, &new_path, &new_last, &new_type); - if (IS_ERR(to)) { - error = PTR_ERR(to); + if (error) goto exit1; - } error = -EXDEV; if (old_path.mnt != new_path.mnt) @@ -4730,9 +4729,8 @@ exit1: lookup_flags |= LOOKUP_REVAL; goto retry; } -put_both: +put_names: putname(from); -put_new: putname(to); return error; } From 584d3226d665214dc1c498045c253529acdd3134 Mon Sep 17 00:00:00 2001 From: Dmitry Kadashev Date: Thu, 8 Jul 2021 13:34:39 +0700 Subject: [PATCH 382/417] namei: make do_mkdirat() take struct filename Pass in the struct filename pointers instead of the user string, and update the three callers to do the same. This is heavily based on commit dbea8d345177 ("fs: make do_renameat2() take struct filename"). This behaves like do_unlinkat() and do_renameat2(). Cc: Al Viro Acked-by: Linus Torvalds Signed-off-by: Dmitry Kadashev Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20210708063447.3556403-4-dkadashev@gmail.com Signed-off-by: Jens Axboe --- fs/internal.h | 1 + fs/namei.c | 26 +++++++++++++++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index 82e8eb32ff3d..fa0b107e828c 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -76,6 +76,7 @@ long do_unlinkat(int dfd, struct filename *name); int may_linkat(struct user_namespace *mnt_userns, struct path *link); int do_renameat2(int olddfd, struct filename *oldname, int newdfd, struct filename *newname, unsigned int flags); +long do_mkdirat(int dfd, struct filename *name, umode_t mode); /* * namespace.c diff --git a/fs/namei.c b/fs/namei.c index aaba6b42b222..e9358e61fce2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3573,7 +3573,7 @@ struct file *do_file_open_root(const struct path *root, return file; } -static struct dentry *filename_create(int dfd, struct filename *name, +static struct dentry *__filename_create(int dfd, struct filename *name, struct path *path, unsigned int lookup_flags) { struct dentry *dentry = ERR_PTR(-EEXIST); @@ -3589,7 +3589,7 @@ static struct dentry *filename_create(int dfd, struct filename *name, */ lookup_flags &= LOOKUP_REVAL; - error = filename_parentat(dfd, name, lookup_flags, path, &last, &type); + error = __filename_parentat(dfd, name, lookup_flags, path, &last, &type); if (error) return ERR_PTR(error); @@ -3642,6 +3642,15 @@ out: return dentry; } +static inline struct dentry *filename_create(int dfd, struct filename *name, + struct path *path, unsigned int lookup_flags) +{ + struct dentry *res = __filename_create(dfd, name, path, lookup_flags); + + putname(name); + return res; +} + struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path, unsigned int lookup_flags) { @@ -3832,7 +3841,7 @@ int vfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, } EXPORT_SYMBOL(vfs_mkdir); -static long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) +long do_mkdirat(int dfd, struct filename *name, umode_t mode) { struct dentry *dentry; struct path path; @@ -3840,9 +3849,10 @@ static long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) unsigned int lookup_flags = LOOKUP_DIRECTORY; retry: - dentry = user_path_create(dfd, pathname, &path, lookup_flags); + dentry = __filename_create(dfd, name, &path, lookup_flags); + error = PTR_ERR(dentry); if (IS_ERR(dentry)) - return PTR_ERR(dentry); + goto out_putname; if (!IS_POSIXACL(path.dentry->d_inode)) mode &= ~current_umask(); @@ -3858,17 +3868,19 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } +out_putname: + putname(name); return error; } SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode) { - return do_mkdirat(dfd, pathname, mode); + return do_mkdirat(dfd, getname(pathname), mode); } SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) { - return do_mkdirat(AT_FDCWD, pathname, mode); + return do_mkdirat(AT_FDCWD, getname(pathname), mode); } /** From 7797251bb5ab7f184dafdfebd05f469ff6a67b77 Mon Sep 17 00:00:00 2001 From: Dmitry Kadashev Date: Thu, 8 Jul 2021 13:34:40 +0700 Subject: [PATCH 383/417] namei: make do_mknodat() take struct filename Pass in the struct filename pointers instead of the user string, for uniformity with the recently converted do_unlinkat(), do_renameat(), do_mkdirat(). Cc: Al Viro Cc: Christian Brauner Acked-by: Linus Torvalds Link: https://lore.kernel.org/io-uring/20210330071700.kpjoyp5zlni7uejm@wittgenstein/ Signed-off-by: Dmitry Kadashev Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20210708063447.3556403-5-dkadashev@gmail.com Signed-off-by: Jens Axboe --- fs/namei.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index e9358e61fce2..ea575dd788b7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3739,7 +3739,7 @@ static int may_mknod(umode_t mode) } } -static long do_mknodat(int dfd, const char __user *filename, umode_t mode, +static long do_mknodat(int dfd, struct filename *name, umode_t mode, unsigned int dev) { struct user_namespace *mnt_userns; @@ -3750,17 +3750,18 @@ static long do_mknodat(int dfd, const char __user *filename, umode_t mode, error = may_mknod(mode); if (error) - return error; + goto out1; retry: - dentry = user_path_create(dfd, filename, &path, lookup_flags); + dentry = __filename_create(dfd, name, &path, lookup_flags); + error = PTR_ERR(dentry); if (IS_ERR(dentry)) - return PTR_ERR(dentry); + goto out1; if (!IS_POSIXACL(path.dentry->d_inode)) mode &= ~current_umask(); error = security_path_mknod(&path, dentry, mode, dev); if (error) - goto out; + goto out2; mnt_userns = mnt_user_ns(path.mnt); switch (mode & S_IFMT) { @@ -3779,24 +3780,26 @@ retry: dentry, mode, 0); break; } -out: +out2: done_path_create(&path, dentry); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } +out1: + putname(name); return error; } SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode, unsigned int, dev) { - return do_mknodat(dfd, filename, mode, dev); + return do_mknodat(dfd, getname(filename), mode, dev); } SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, dev) { - return do_mknodat(AT_FDCWD, filename, mode, dev); + return do_mknodat(AT_FDCWD, getname(filename), mode, dev); } /** From da2d0cede330192879e8e16ddb3158aa76ba5ec2 Mon Sep 17 00:00:00 2001 From: Dmitry Kadashev Date: Thu, 8 Jul 2021 13:34:41 +0700 Subject: [PATCH 384/417] namei: make do_symlinkat() take struct filename Pass in the struct filename pointers instead of the user string, for uniformity with the recently converted do_mkdnodat(), do_unlinkat(), do_renameat(), do_mkdirat(). Cc: Al Viro Cc: Christian Brauner Acked-by: Linus Torvalds Link: https://lore.kernel.org/io-uring/20210330071700.kpjoyp5zlni7uejm@wittgenstein/ Signed-off-by: Dmitry Kadashev Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20210708063447.3556403-6-dkadashev@gmail.com Signed-off-by: Jens Axboe --- fs/namei.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index ea575dd788b7..522c35b33fea 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4207,23 +4207,23 @@ int vfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, } EXPORT_SYMBOL(vfs_symlink); -static long do_symlinkat(const char __user *oldname, int newdfd, - const char __user *newname) +static long do_symlinkat(struct filename *from, int newdfd, + struct filename *to) { int error; - struct filename *from; struct dentry *dentry; struct path path; unsigned int lookup_flags = 0; - from = getname(oldname); - if (IS_ERR(from)) - return PTR_ERR(from); + if (IS_ERR(from)) { + error = PTR_ERR(from); + goto out_putnames; + } retry: - dentry = user_path_create(newdfd, newname, &path, lookup_flags); + dentry = __filename_create(newdfd, to, &path, lookup_flags); error = PTR_ERR(dentry); if (IS_ERR(dentry)) - goto out_putname; + goto out_putnames; error = security_path_symlink(&path, dentry, from->name); if (!error) { @@ -4238,7 +4238,8 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } -out_putname: +out_putnames: + putname(to); putname(from); return error; } @@ -4246,12 +4247,12 @@ out_putname: SYSCALL_DEFINE3(symlinkat, const char __user *, oldname, int, newdfd, const char __user *, newname) { - return do_symlinkat(oldname, newdfd, newname); + return do_symlinkat(getname(oldname), newdfd, getname(newname)); } SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname) { - return do_symlinkat(oldname, AT_FDCWD, newname); + return do_symlinkat(getname(oldname), AT_FDCWD, getname(newname)); } /** From 8228e2c313194f13f1d1806ed5734a26c38d49ac Mon Sep 17 00:00:00 2001 From: Dmitry Kadashev Date: Thu, 8 Jul 2021 13:34:42 +0700 Subject: [PATCH 385/417] namei: add getname_uflags() There are a couple of places where we already open-code the (flags & AT_EMPTY_PATH) check and io_uring will likely add another one in the future. Let's just add a simple helper getname_uflags() that handles this directly and use it. Cc: Al Viro Cc: Christian Brauner Acked-by: Linus Torvalds Link: https://lore.kernel.org/io-uring/20210415100815.edrn4a7cy26wkowe@wittgenstein/ Signed-off-by: Christian Brauner Signed-off-by: Dmitry Kadashev Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20210708063447.3556403-7-dkadashev@gmail.com Signed-off-by: Jens Axboe --- fs/exec.c | 8 ++------ fs/namei.c | 8 ++++++++ include/linux/fs.h | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 38f63451b928..3b78b22addfb 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -2070,10 +2070,8 @@ SYSCALL_DEFINE5(execveat, const char __user *const __user *, envp, int, flags) { - int lookup_flags = (flags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; - return do_execveat(fd, - getname_flags(filename, lookup_flags, NULL), + getname_uflags(filename, flags), argv, envp, flags); } @@ -2091,10 +2089,8 @@ COMPAT_SYSCALL_DEFINE5(execveat, int, fd, const compat_uptr_t __user *, envp, int, flags) { - int lookup_flags = (flags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; - return compat_do_execveat(fd, - getname_flags(filename, lookup_flags, NULL), + getname_uflags(filename, flags), argv, envp, flags); } #endif diff --git a/fs/namei.c b/fs/namei.c index 522c35b33fea..41f58dabe84c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -203,6 +203,14 @@ getname_flags(const char __user *filename, int flags, int *empty) return result; } +struct filename * +getname_uflags(const char __user *filename, int uflags) +{ + int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; + + return getname_flags(filename, flags, NULL); +} + struct filename * getname(const char __user * filename) { diff --git a/include/linux/fs.h b/include/linux/fs.h index 640574294216..26d41a445e81 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2786,6 +2786,7 @@ static inline struct file *file_clone_open(struct file *file) extern int filp_close(struct file *, fl_owner_t id); extern struct filename *getname_flags(const char __user *, int, int *); +extern struct filename *getname_uflags(const char __user *, int); extern struct filename *getname(const char __user *); extern struct filename *getname_kernel(const char *); extern void putname(struct filename *name); From 020250f31c4c75ac7687a673e29c00786582a5f4 Mon Sep 17 00:00:00 2001 From: Dmitry Kadashev Date: Thu, 8 Jul 2021 13:34:43 +0700 Subject: [PATCH 386/417] namei: make do_linkat() take struct filename Pass in the struct filename pointers instead of the user string, for uniformity with do_renameat2, do_unlinkat, do_mknodat, etc. Cc: Al Viro Cc: Christian Brauner Acked-by: Linus Torvalds Link: https://lore.kernel.org/io-uring/20210330071700.kpjoyp5zlni7uejm@wittgenstein/ Signed-off-by: Dmitry Kadashev Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20210708063447.3556403-8-dkadashev@gmail.com Signed-off-by: Jens Axboe --- fs/namei.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 41f58dabe84c..4e359766febf 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2467,7 +2467,7 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path return err; } -int filename_lookup(int dfd, struct filename *name, unsigned flags, +static int __filename_lookup(int dfd, struct filename *name, unsigned flags, struct path *path, struct path *root) { int retval; @@ -2485,6 +2485,14 @@ int filename_lookup(int dfd, struct filename *name, unsigned flags, audit_inode(name, path->dentry, flags & LOOKUP_MOUNTPOINT ? AUDIT_INODE_NOEVAL : 0); restore_nameidata(); + return retval; +} + +int filename_lookup(int dfd, struct filename *name, unsigned flags, + struct path *path, struct path *root) +{ + int retval = __filename_lookup(dfd, name, flags, path, root); + putname(name); return retval; } @@ -4361,8 +4369,8 @@ EXPORT_SYMBOL(vfs_link); * with linux 2.0, and to avoid hard-linking to directories * and other special files. --ADM */ -static int do_linkat(int olddfd, const char __user *oldname, int newdfd, - const char __user *newname, int flags) +static int do_linkat(int olddfd, struct filename *old, int newdfd, + struct filename *new, int flags) { struct user_namespace *mnt_userns; struct dentry *new_dentry; @@ -4371,31 +4379,32 @@ static int do_linkat(int olddfd, const char __user *oldname, int newdfd, int how = 0; int error; - if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) - return -EINVAL; + if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) { + error = -EINVAL; + goto out_putnames; + } /* * To use null names we require CAP_DAC_READ_SEARCH * This ensures that not everyone will be able to create * handlink using the passed filedescriptor. */ - if (flags & AT_EMPTY_PATH) { - if (!capable(CAP_DAC_READ_SEARCH)) - return -ENOENT; - how = LOOKUP_EMPTY; + if (flags & AT_EMPTY_PATH && !capable(CAP_DAC_READ_SEARCH)) { + error = -ENOENT; + goto out_putnames; } if (flags & AT_SYMLINK_FOLLOW) how |= LOOKUP_FOLLOW; retry: - error = user_path_at(olddfd, oldname, how, &old_path); + error = __filename_lookup(olddfd, old, how, &old_path, NULL); if (error) - return error; + goto out_putnames; - new_dentry = user_path_create(newdfd, newname, &new_path, + new_dentry = __filename_create(newdfd, new, &new_path, (how & LOOKUP_REVAL)); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) - goto out; + goto out_putpath; error = -EXDEV; if (old_path.mnt != new_path.mnt) @@ -4423,8 +4432,11 @@ out_dput: how |= LOOKUP_REVAL; goto retry; } -out: +out_putpath: path_put(&old_path); +out_putnames: + putname(old); + putname(new); return error; } @@ -4432,12 +4444,13 @@ out: SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, int, flags) { - return do_linkat(olddfd, oldname, newdfd, newname, flags); + return do_linkat(olddfd, getname_uflags(oldname, flags), + newdfd, getname(newname), flags); } SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname) { - return do_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0); + return do_linkat(AT_FDCWD, getname(oldname), AT_FDCWD, getname(newname), 0); } /** From 45f30dab395730aa3b3da14d9f19ea0d7d43db53 Mon Sep 17 00:00:00 2001 From: Dmitry Kadashev Date: Thu, 8 Jul 2021 13:34:44 +0700 Subject: [PATCH 387/417] namei: update do_*() helpers to return ints Update the following to return int rather than long, for uniformity with the rest of the do_* helpers in namei.c: * do_rmdir() * do_unlinkat() * do_mkdirat() * do_mknodat() * do_symlinkat() Cc: Al Viro Cc: Christian Brauner Acked-by: Linus Torvalds Link: https://lore.kernel.org/io-uring/20210514143202.dmzfcgz5hnauy7ze@wittgenstein/ Signed-off-by: Dmitry Kadashev Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20210708063447.3556403-9-dkadashev@gmail.com Signed-off-by: Jens Axboe --- fs/internal.h | 6 +++--- fs/namei.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index fa0b107e828c..d6b15dad1310 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -71,12 +71,12 @@ extern int filename_lookup(int dfd, struct filename *name, unsigned flags, struct path *path, struct path *root); extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct path *); -long do_rmdir(int dfd, struct filename *name); -long do_unlinkat(int dfd, struct filename *name); +int do_rmdir(int dfd, struct filename *name); +int do_unlinkat(int dfd, struct filename *name); int may_linkat(struct user_namespace *mnt_userns, struct path *link); int do_renameat2(int olddfd, struct filename *oldname, int newdfd, struct filename *newname, unsigned int flags); -long do_mkdirat(int dfd, struct filename *name, umode_t mode); +int do_mkdirat(int dfd, struct filename *name, umode_t mode); /* * namespace.c diff --git a/fs/namei.c b/fs/namei.c index 4e359766febf..e2425c0a406e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3755,7 +3755,7 @@ static int may_mknod(umode_t mode) } } -static long do_mknodat(int dfd, struct filename *name, umode_t mode, +static int do_mknodat(int dfd, struct filename *name, umode_t mode, unsigned int dev) { struct user_namespace *mnt_userns; @@ -3860,7 +3860,7 @@ int vfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, } EXPORT_SYMBOL(vfs_mkdir); -long do_mkdirat(int dfd, struct filename *name, umode_t mode) +int do_mkdirat(int dfd, struct filename *name, umode_t mode) { struct dentry *dentry; struct path path; @@ -3957,7 +3957,7 @@ out: } EXPORT_SYMBOL(vfs_rmdir); -long do_rmdir(int dfd, struct filename *name) +int do_rmdir(int dfd, struct filename *name) { struct user_namespace *mnt_userns; int error; @@ -4095,7 +4095,7 @@ EXPORT_SYMBOL(vfs_unlink); * writeout happening, and we don't want to prevent access to the directory * while waiting on the I/O. */ -long do_unlinkat(int dfd, struct filename *name) +int do_unlinkat(int dfd, struct filename *name) { int error; struct dentry *dentry; @@ -4223,7 +4223,7 @@ int vfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, } EXPORT_SYMBOL(vfs_symlink); -static long do_symlinkat(struct filename *from, int newdfd, +static int do_symlinkat(struct filename *from, int newdfd, struct filename *to) { int error; From e34a02dc40c95d126bb6486dcf802bbb8d1624a0 Mon Sep 17 00:00:00 2001 From: Dmitry Kadashev Date: Thu, 8 Jul 2021 13:34:45 +0700 Subject: [PATCH 388/417] io_uring: add support for IORING_OP_MKDIRAT IORING_OP_MKDIRAT behaves like mkdirat(2) and takes the same flags and arguments. Acked-by: Linus Torvalds Signed-off-by: Dmitry Kadashev Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20210708063447.3556403-10-dkadashev@gmail.com [axboe: add splice_fd_in check] Signed-off-by: Jens Axboe --- fs/io_uring.c | 60 +++++++++++++++++++++++++++++++++++ include/uapi/linux/io_uring.h | 1 + 2 files changed, 61 insertions(+) diff --git a/fs/io_uring.c b/fs/io_uring.c index 3898f7ab14f6..8e14e71bf6ac 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -662,6 +662,13 @@ struct io_unlink { struct filename *filename; }; +struct io_mkdir { + struct file *file; + int dfd; + umode_t mode; + struct filename *filename; +}; + struct io_completion { struct file *file; u32 cflags; @@ -821,6 +828,7 @@ struct io_kiocb { struct io_shutdown shutdown; struct io_rename rename; struct io_unlink unlink; + struct io_mkdir mkdir; /* use only after cleaning per-op data, see io_clean_op() */ struct io_completion compl; }; @@ -1032,6 +1040,7 @@ static const struct io_op_def io_op_defs[] = { }, [IORING_OP_RENAMEAT] = {}, [IORING_OP_UNLINKAT] = {}, + [IORING_OP_MKDIRAT] = {}, }; /* requests with any of those set should undergo io_disarm_next() */ @@ -3603,6 +3612,49 @@ static int io_unlinkat(struct io_kiocb *req, unsigned int issue_flags) return 0; } +static int io_mkdirat_prep(struct io_kiocb *req, + const struct io_uring_sqe *sqe) +{ + struct io_mkdir *mkd = &req->mkdir; + const char __user *fname; + + if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) + return -EINVAL; + if (sqe->ioprio || sqe->off || sqe->rw_flags || sqe->buf_index || + sqe->splice_fd_in) + return -EINVAL; + if (unlikely(req->flags & REQ_F_FIXED_FILE)) + return -EBADF; + + mkd->dfd = READ_ONCE(sqe->fd); + mkd->mode = READ_ONCE(sqe->len); + + fname = u64_to_user_ptr(READ_ONCE(sqe->addr)); + mkd->filename = getname(fname); + if (IS_ERR(mkd->filename)) + return PTR_ERR(mkd->filename); + + req->flags |= REQ_F_NEED_CLEANUP; + return 0; +} + +static int io_mkdirat(struct io_kiocb *req, int issue_flags) +{ + struct io_mkdir *mkd = &req->mkdir; + int ret; + + if (issue_flags & IO_URING_F_NONBLOCK) + return -EAGAIN; + + ret = do_mkdirat(mkd->dfd, mkd->filename, mkd->mode); + + req->flags &= ~REQ_F_NEED_CLEANUP; + if (ret < 0) + req_set_fail(req); + io_req_complete(req, ret); + return 0; +} + static int io_shutdown_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { @@ -6000,6 +6052,8 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) return io_renameat_prep(req, sqe); case IORING_OP_UNLINKAT: return io_unlinkat_prep(req, sqe); + case IORING_OP_MKDIRAT: + return io_mkdirat_prep(req, sqe); } printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", @@ -6163,6 +6217,9 @@ static void io_clean_op(struct io_kiocb *req) case IORING_OP_UNLINKAT: putname(req->unlink.filename); break; + case IORING_OP_MKDIRAT: + putname(req->mkdir.filename); + break; } } if ((req->flags & REQ_F_POLLED) && req->apoll) { @@ -6291,6 +6348,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags) case IORING_OP_UNLINKAT: ret = io_unlinkat(req, issue_flags); break; + case IORING_OP_MKDIRAT: + ret = io_mkdirat(req, issue_flags); + break; default: ret = -EINVAL; break; diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 79126d5cd289..a926407c230e 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -133,6 +133,7 @@ enum { IORING_OP_SHUTDOWN, IORING_OP_RENAMEAT, IORING_OP_UNLINKAT, + IORING_OP_MKDIRAT, /* this goes last, obviously */ IORING_OP_LAST, From da521626ac620d8719d674a48b8ec3620eefd42a Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 11 Aug 2021 09:20:04 -0600 Subject: [PATCH 389/417] bio: optimize initialization of a bio The memset() used is measurably slower in targeted benchmarks, wasting about 1% of the total runtime, or 50% of the (later) hot path cached bio alloc. Get rid of it and fill in the bio manually. Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/bio.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/block/bio.c b/block/bio.c index 1fab762e079b..6fa5c653283b 100644 --- a/block/bio.c +++ b/block/bio.c @@ -246,12 +246,40 @@ static void bio_free(struct bio *bio) void bio_init(struct bio *bio, struct bio_vec *table, unsigned short max_vecs) { - memset(bio, 0, sizeof(*bio)); + bio->bi_next = NULL; + bio->bi_bdev = NULL; + bio->bi_opf = 0; + bio->bi_flags = 0; + bio->bi_ioprio = 0; + bio->bi_write_hint = 0; + bio->bi_status = 0; + bio->bi_iter.bi_sector = 0; + bio->bi_iter.bi_size = 0; + bio->bi_iter.bi_idx = 0; + bio->bi_iter.bi_bvec_done = 0; + bio->bi_end_io = NULL; + bio->bi_private = NULL; +#ifdef CONFIG_BLK_CGROUP + bio->bi_blkg = NULL; + bio->bi_issue.value = 0; +#ifdef CONFIG_BLK_CGROUP_IOCOST + bio->bi_iocost_cost = 0; +#endif +#endif +#ifdef CONFIG_BLK_INLINE_ENCRYPTION + bio->bi_crypt_context = NULL; +#endif +#ifdef CONFIG_BLK_DEV_INTEGRITY + bio->bi_integrity = NULL; +#endif + bio->bi_vcnt = 0; + atomic_set(&bio->__bi_remaining, 1); atomic_set(&bio->__bi_cnt, 1); - bio->bi_io_vec = table; bio->bi_max_vecs = max_vecs; + bio->bi_io_vec = table; + bio->bi_pool = NULL; } EXPORT_SYMBOL(bio_init); From 6c7ef543df909dbdcd8cb24ef30627cba62a4e91 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 10 Aug 2021 09:29:55 -0600 Subject: [PATCH 390/417] fs: add kiocb alloc cache flag If this kiocb can safely use the polled bio allocation cache, then this flag must be set. Generally this can be set for polled IO, where we will not see IRQ completions of the request. Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- include/linux/fs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/fs.h b/include/linux/fs.h index 640574294216..0dcc5de779c9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -319,6 +319,8 @@ enum rw_hint { /* iocb->ki_waitq is valid */ #define IOCB_WAITQ (1 << 19) #define IOCB_NOIO (1 << 20) +/* can use bio alloc cache */ +#define IOCB_ALLOC_CACHE (1 << 21) struct kiocb { struct file *ki_filp; From be4d234d7aebbfe0c233bc20b9cdef7ab3408ff4 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 8 Mar 2021 11:37:47 -0700 Subject: [PATCH 391/417] bio: add allocation cache abstraction Add a per-cpu bio_set cache for bio allocations, enabling us to quickly recycle them instead of going through the slab allocator. This cache isn't IRQ safe, and hence is only really suitable for polled IO. Very simple - keeps a count of bio's in the cache, and maintains a max of 512 with a slack of 64. If we get above max + slack, we drop slack number of bio's. Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/bio.c | 134 +++++++++++++++++++++++++++++++++---- include/linux/bio.h | 13 ++++ include/linux/blk_types.h | 1 + include/linux/cpuhotplug.h | 1 + 4 files changed, 135 insertions(+), 14 deletions(-) diff --git a/block/bio.c b/block/bio.c index 6fa5c653283b..dbb0bc8e1ef7 100644 --- a/block/bio.c +++ b/block/bio.c @@ -25,6 +25,11 @@ #include "blk.h" #include "blk-rq-qos.h" +struct bio_alloc_cache { + struct bio_list free_list; + unsigned int nr; +}; + static struct biovec_slab { int nr_vecs; char *name; @@ -619,6 +624,53 @@ void guard_bio_eod(struct bio *bio) bio_truncate(bio, maxsector << 9); } +#define ALLOC_CACHE_MAX 512 +#define ALLOC_CACHE_SLACK 64 + +static void bio_alloc_cache_prune(struct bio_alloc_cache *cache, + unsigned int nr) +{ + unsigned int i = 0; + struct bio *bio; + + while ((bio = bio_list_pop(&cache->free_list)) != NULL) { + cache->nr--; + bio_free(bio); + if (++i == nr) + break; + } +} + +static int bio_cpu_dead(unsigned int cpu, struct hlist_node *node) +{ + struct bio_set *bs; + + bs = hlist_entry_safe(node, struct bio_set, cpuhp_dead); + if (bs->cache) { + struct bio_alloc_cache *cache = per_cpu_ptr(bs->cache, cpu); + + bio_alloc_cache_prune(cache, -1U); + } + return 0; +} + +static void bio_alloc_cache_destroy(struct bio_set *bs) +{ + int cpu; + + if (!bs->cache) + return; + + cpuhp_state_remove_instance_nocalls(CPUHP_BIO_DEAD, &bs->cpuhp_dead); + for_each_possible_cpu(cpu) { + struct bio_alloc_cache *cache; + + cache = per_cpu_ptr(bs->cache, cpu); + bio_alloc_cache_prune(cache, -1U); + } + free_percpu(bs->cache); +} + /** * bio_put - release a reference to a bio * @bio: bio to release reference to @@ -629,16 +681,23 @@ void guard_bio_eod(struct bio *bio) **/ void bio_put(struct bio *bio) { - if (!bio_flagged(bio, BIO_REFFED)) - bio_free(bio); - else { + if (unlikely(bio_flagged(bio, BIO_REFFED))) { BIO_BUG_ON(!atomic_read(&bio->__bi_cnt)); + if (!atomic_dec_and_test(&bio->__bi_cnt)) + return; + } - /* - * last put frees it - */ - if (atomic_dec_and_test(&bio->__bi_cnt)) - bio_free(bio); + if (bio_flagged(bio, BIO_PERCPU_CACHE)) { + struct bio_alloc_cache *cache; + + bio_uninit(bio); + cache = per_cpu_ptr(bio->bi_pool->cache, get_cpu()); + bio_list_add_head(&cache->free_list, bio); + if (++cache->nr > ALLOC_CACHE_MAX + ALLOC_CACHE_SLACK) + bio_alloc_cache_prune(cache, ALLOC_CACHE_SLACK); + put_cpu(); + } else { + bio_free(bio); } } EXPORT_SYMBOL(bio_put); @@ -1530,6 +1589,7 @@ int biovec_init_pool(mempool_t *pool, int pool_entries) */ void bioset_exit(struct bio_set *bs) { + bio_alloc_cache_destroy(bs); if (bs->rescue_workqueue) destroy_workqueue(bs->rescue_workqueue); bs->rescue_workqueue = NULL; @@ -1591,12 +1651,18 @@ int bioset_init(struct bio_set *bs, biovec_init_pool(&bs->bvec_pool, pool_size)) goto bad; - if (!(flags & BIOSET_NEED_RESCUER)) - return 0; - - bs->rescue_workqueue = alloc_workqueue("bioset", WQ_MEM_RECLAIM, 0); - if (!bs->rescue_workqueue) - goto bad; + if (flags & BIOSET_NEED_RESCUER) { + bs->rescue_workqueue = alloc_workqueue("bioset", + WQ_MEM_RECLAIM, 0); + if (!bs->rescue_workqueue) + goto bad; + } + if (flags & BIOSET_PERCPU_CACHE) { + bs->cache = alloc_percpu(struct bio_alloc_cache); + if (!bs->cache) + goto bad; + cpuhp_state_add_instance_nocalls(CPUHP_BIO_DEAD, &bs->cpuhp_dead); + } return 0; bad: @@ -1623,6 +1689,43 @@ int bioset_init_from_src(struct bio_set *bs, struct bio_set *src) } EXPORT_SYMBOL(bioset_init_from_src); +/** + * bio_alloc_kiocb - Allocate a bio from bio_set based on kiocb + * @kiocb: kiocb describing the IO + * @bs: bio_set to allocate from + * + * Description: + * Like @bio_alloc_bioset, but pass in the kiocb. The kiocb is only + * used to check if we should dip into the per-cpu bio_set allocation + * cache. The allocation uses GFP_KERNEL internally. + * + */ +struct bio *bio_alloc_kiocb(struct kiocb *kiocb, unsigned short nr_vecs, + struct bio_set *bs) +{ + struct bio_alloc_cache *cache; + struct bio *bio; + + if (!(kiocb->ki_flags & IOCB_ALLOC_CACHE) || nr_vecs > BIO_INLINE_VECS) + return bio_alloc_bioset(GFP_KERNEL, nr_vecs, bs); + + cache = per_cpu_ptr(bs->cache, get_cpu()); + bio = bio_list_pop(&cache->free_list); + if (bio) { + cache->nr--; + put_cpu(); + bio_init(bio, nr_vecs ? bio->bi_inline_vecs : NULL, nr_vecs); + bio->bi_pool = bs; + bio_set_flag(bio, BIO_PERCPU_CACHE); + return bio; + } + put_cpu(); + bio = bio_alloc_bioset(GFP_KERNEL, nr_vecs, bs); + bio_set_flag(bio, BIO_PERCPU_CACHE); + return bio; +} +EXPORT_SYMBOL_GPL(bio_alloc_kiocb); + static int __init init_bio(void) { int i; @@ -1637,6 +1740,9 @@ static int __init init_bio(void) SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); } + cpuhp_setup_state_multi(CPUHP_BIO_DEAD, "block/bio:dead", NULL, + bio_cpu_dead); + if (bioset_init(&fs_bio_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS)) panic("bio: can't allocate bios\n"); diff --git a/include/linux/bio.h b/include/linux/bio.h index 2203b686e1f0..89ad28213b1d 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -401,6 +401,7 @@ static inline struct bio *bio_next_split(struct bio *bio, int sectors, enum { BIOSET_NEED_BVECS = BIT(0), BIOSET_NEED_RESCUER = BIT(1), + BIOSET_PERCPU_CACHE = BIT(2), }; extern int bioset_init(struct bio_set *, unsigned int, unsigned int, int flags); extern void bioset_exit(struct bio_set *); @@ -409,6 +410,8 @@ extern int bioset_init_from_src(struct bio_set *bs, struct bio_set *src); struct bio *bio_alloc_bioset(gfp_t gfp, unsigned short nr_iovecs, struct bio_set *bs); +struct bio *bio_alloc_kiocb(struct kiocb *kiocb, unsigned short nr_vecs, + struct bio_set *bs); struct bio *bio_kmalloc(gfp_t gfp_mask, unsigned short nr_iovecs); extern void bio_put(struct bio *); @@ -699,6 +702,11 @@ struct bio_set { struct kmem_cache *bio_slab; unsigned int front_pad; + /* + * per-cpu bio alloc cache + */ + struct bio_alloc_cache __percpu *cache; + mempool_t bio_pool; mempool_t bvec_pool; #if defined(CONFIG_BLK_DEV_INTEGRITY) @@ -715,6 +723,11 @@ struct bio_set { struct bio_list rescue_list; struct work_struct rescue_work; struct workqueue_struct *rescue_workqueue; + + /* + * Hot un-plug notifier for the per-cpu cache, if used + */ + struct hlist_node cpuhp_dead; }; static inline bool bioset_initialized(struct bio_set *bs) diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 290f9061b29a..f68d4e8c775e 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -301,6 +301,7 @@ enum { BIO_TRACKED, /* set if bio goes through the rq_qos path */ BIO_REMAPPED, BIO_ZONE_WRITE_LOCKED, /* Owns a zoned device zone write lock */ + BIO_PERCPU_CACHE, /* can participate in per-cpu alloc cache */ BIO_FLAG_LAST }; diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index f39b34b13871..fe72c8d6c980 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -46,6 +46,7 @@ enum cpuhp_state { CPUHP_ARM_OMAP_WAKE_DEAD, CPUHP_IRQ_POLL_DEAD, CPUHP_BLOCK_SOFTIRQ_DEAD, + CPUHP_BIO_DEAD, CPUHP_ACPI_CPUDRV_DEAD, CPUHP_S390_PFAULT_DEAD, CPUHP_BLK_MQ_DEAD, From be863b9e4348a791e360d25611a1bdde2c9595ed Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 11 Aug 2021 10:19:06 -0600 Subject: [PATCH 392/417] block: clear BIO_PERCPU_CACHE flag if polling isn't supported The bio alloc cache relies on the fact that a polled bio will complete in process context, clear the cacheable flag if we disable polling for a given bio. Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/block/blk-core.c b/block/blk-core.c index 4f8449b29b21..0d4d6b1e5d25 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -832,8 +832,11 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio) } } - if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags)) + if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags)) { + /* can't support alloc cache if we turn off polling */ + bio_clear_flag(bio, BIO_PERCPU_CACHE); bio->bi_opf &= ~REQ_HIPRI; + } switch (bio_op(bio)) { case REQ_OP_DISCARD: From 394918ebb889f99d89db6843bcc93279b2b745f9 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 8 Mar 2021 11:40:23 -0700 Subject: [PATCH 393/417] io_uring: enable use of bio alloc cache Mark polled IO as being safe for dipping into the bio allocation cache, in case the targeted bio_set has it enabled. This brings an IOPOLL gen2 Optane QD=128 workload from ~3.2M IOPS to ~3.5M IOPS. Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/io_uring.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 4f5a00707644..504aede8ca47 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -2737,7 +2737,7 @@ static int io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe) !kiocb->ki_filp->f_op->iopoll) return -EOPNOTSUPP; - kiocb->ki_flags |= IOCB_HIPRI; + kiocb->ki_flags |= IOCB_HIPRI | IOCB_ALLOC_CACHE; kiocb->ki_complete = io_complete_rw_iopoll; req->iopoll_completed = 0; } else { From 01cfa28af486c9df3775232f10c3dd7ba2e88318 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 12 Aug 2021 09:05:52 -0600 Subject: [PATCH 394/417] block: use the percpu bio cache in __blkdev_direct_IO Use bio_alloc_kiocb to dip into the percpu cache of bios when the caller asks for it. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/block_dev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/block_dev.c b/fs/block_dev.c index 9ef4f1fc2cb0..3c7fb7106713 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -385,7 +385,7 @@ static ssize_t __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, (bdev_logical_block_size(bdev) - 1)) return -EINVAL; - bio = bio_alloc_bioset(GFP_KERNEL, nr_pages, &blkdev_dio_pool); + bio = bio_alloc_kiocb(iocb, nr_pages, &blkdev_dio_pool); dio = container_of(bio, struct blkdev_dio, bio); dio->is_sync = is_sync = is_sync_kiocb(iocb); @@ -513,7 +513,9 @@ blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter) static __init int blkdev_init(void) { - return bioset_init(&blkdev_dio_pool, 4, offsetof(struct blkdev_dio, bio), BIOSET_NEED_BVECS); + return bioset_init(&blkdev_dio_pool, 4, + offsetof(struct blkdev_dio, bio), + BIOSET_NEED_BVECS|BIOSET_PERCPU_CACHE); } module_init(blkdev_init); From 270a1c913ebd745ebee716af5f7215e1c2b30cc0 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 12 Aug 2021 11:42:53 -0600 Subject: [PATCH 395/417] block: provide bio_clear_hipri() helper Any case that turns off REQ_HIPRI must also clear BIO_PERCPU_CACHE, as non-polled IO may complete through hard/soft IRQ and hence isn't safe for our polled bio alloc cache. Provide a helper that does just that, and use it in the merging code as well if we split a bio and turn off polling. Fixes: be863b9e4348 ("block: clear BIO_PERCPU_CACHE flag if polling isn't supported") Reported-by: Keith Busch Signed-off-by: Jens Axboe --- block/blk-core.c | 7 ++----- block/blk-merge.c | 2 +- block/blk.h | 7 +++++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index 0d4d6b1e5d25..f35d401e65f8 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -832,11 +832,8 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio) } } - if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags)) { - /* can't support alloc cache if we turn off polling */ - bio_clear_flag(bio, BIO_PERCPU_CACHE); - bio->bi_opf &= ~REQ_HIPRI; - } + if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags)) + bio_clear_hipri(bio); switch (bio_op(bio)) { case REQ_OP_DISCARD: diff --git a/block/blk-merge.c b/block/blk-merge.c index a11b3b53717e..bc25ad409fc1 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -285,7 +285,7 @@ split: * iopoll in direct IO routine. Given performance gain of iopoll for * big IO can be trival, disable iopoll when split needed. */ - bio->bi_opf &= ~REQ_HIPRI; + bio_clear_hipri(bio); return bio_split(bio, sectors, GFP_NOIO, bs); } diff --git a/block/blk.h b/block/blk.h index cb01429c162c..5a4652a10931 100644 --- a/block/blk.h +++ b/block/blk.h @@ -364,4 +364,11 @@ extern struct device_attribute dev_attr_events; extern struct device_attribute dev_attr_events_async; extern struct device_attribute dev_attr_events_poll_msecs; +static inline void bio_clear_hipri(struct bio *bio) +{ + /* can't support alloc cache if we turn off polling */ + bio_clear_flag(bio, BIO_PERCPU_CACHE); + bio->bi_opf &= ~REQ_HIPRI; +} + #endif /* BLK_INTERNAL_H */ From 3d5b3fbedad65088ec079a4c4d1a2f47e11ae1e7 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 13 Aug 2021 07:53:09 -0600 Subject: [PATCH 396/417] bio: improve kerneldoc documentation for bio_alloc_kiocb() We're missing a description for the 'nr_vecs' parameter. While in there, clarify that freeing a bio allocated through this function must be done from process context. Fixes: 1cbbd31c4ada ("bio: add allocation cache abstraction") Reported-by: Stephen Rothwell Signed-off-by: Jens Axboe --- block/bio.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/block/bio.c b/block/bio.c index dbb0bc8e1ef7..ef88fa3afe4c 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1692,12 +1692,15 @@ EXPORT_SYMBOL(bioset_init_from_src); /** * bio_alloc_kiocb - Allocate a bio from bio_set based on kiocb * @kiocb: kiocb describing the IO + * @nr_iovecs: number of iovecs to pre-allocate * @bs: bio_set to allocate from * * Description: * Like @bio_alloc_bioset, but pass in the kiocb. The kiocb is only * used to check if we should dip into the per-cpu bio_set allocation - * cache. The allocation uses GFP_KERNEL internally. + * cache. The allocation uses GFP_KERNEL internally. On return, the + * bio is marked BIO_PERCPU_CACHEABLE, and the final put of the bio + * MUST be done from process context, not hard/soft IRQ. * */ struct bio *bio_alloc_kiocb(struct kiocb *kiocb, unsigned short nr_vecs, From 7a8721f84fcb3b2946a92380b6fc311e017ff02c Mon Sep 17 00:00:00 2001 From: Dmitry Kadashev Date: Thu, 8 Jul 2021 13:34:46 +0700 Subject: [PATCH 397/417] io_uring: add support for IORING_OP_SYMLINKAT IORING_OP_SYMLINKAT behaves like symlinkat(2) and takes the same flags and arguments. Acked-by: Linus Torvalds Suggested-by: Christian Brauner Link: https://lore.kernel.org/io-uring/20210514145259.wtl4xcsp52woi6ab@wittgenstein/ Signed-off-by: Dmitry Kadashev Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20210708063447.3556403-11-dkadashev@gmail.com [axboe: add splice_fd_in check] Signed-off-by: Jens Axboe --- fs/internal.h | 1 + fs/io_uring.c | 67 +++++++++++++++++++++++++++++++++++ fs/namei.c | 3 +- include/uapi/linux/io_uring.h | 1 + 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index d6b15dad1310..2f9750aefbd6 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -77,6 +77,7 @@ int may_linkat(struct user_namespace *mnt_userns, struct path *link); int do_renameat2(int olddfd, struct filename *oldname, int newdfd, struct filename *newname, unsigned int flags); int do_mkdirat(int dfd, struct filename *name, umode_t mode); +int do_symlinkat(struct filename *from, int newdfd, struct filename *to); /* * namespace.c diff --git a/fs/io_uring.c b/fs/io_uring.c index 8e14e71bf6ac..33941df9084b 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -669,6 +669,13 @@ struct io_mkdir { struct filename *filename; }; +struct io_symlink { + struct file *file; + int new_dfd; + struct filename *oldpath; + struct filename *newpath; +}; + struct io_completion { struct file *file; u32 cflags; @@ -829,6 +836,7 @@ struct io_kiocb { struct io_rename rename; struct io_unlink unlink; struct io_mkdir mkdir; + struct io_symlink symlink; /* use only after cleaning per-op data, see io_clean_op() */ struct io_completion compl; }; @@ -1041,6 +1049,7 @@ static const struct io_op_def io_op_defs[] = { [IORING_OP_RENAMEAT] = {}, [IORING_OP_UNLINKAT] = {}, [IORING_OP_MKDIRAT] = {}, + [IORING_OP_SYMLINKAT] = {}, }; /* requests with any of those set should undergo io_disarm_next() */ @@ -3655,6 +3664,55 @@ static int io_mkdirat(struct io_kiocb *req, int issue_flags) return 0; } +static int io_symlinkat_prep(struct io_kiocb *req, + const struct io_uring_sqe *sqe) +{ + struct io_symlink *sl = &req->symlink; + const char __user *oldpath, *newpath; + + if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) + return -EINVAL; + if (sqe->ioprio || sqe->len || sqe->rw_flags || sqe->buf_index || + sqe->splice_fd_in) + return -EINVAL; + if (unlikely(req->flags & REQ_F_FIXED_FILE)) + return -EBADF; + + sl->new_dfd = READ_ONCE(sqe->fd); + oldpath = u64_to_user_ptr(READ_ONCE(sqe->addr)); + newpath = u64_to_user_ptr(READ_ONCE(sqe->addr2)); + + sl->oldpath = getname(oldpath); + if (IS_ERR(sl->oldpath)) + return PTR_ERR(sl->oldpath); + + sl->newpath = getname(newpath); + if (IS_ERR(sl->newpath)) { + putname(sl->oldpath); + return PTR_ERR(sl->newpath); + } + + req->flags |= REQ_F_NEED_CLEANUP; + return 0; +} + +static int io_symlinkat(struct io_kiocb *req, int issue_flags) +{ + struct io_symlink *sl = &req->symlink; + int ret; + + if (issue_flags & IO_URING_F_NONBLOCK) + return -EAGAIN; + + ret = do_symlinkat(sl->oldpath, sl->new_dfd, sl->newpath); + + req->flags &= ~REQ_F_NEED_CLEANUP; + if (ret < 0) + req_set_fail(req); + io_req_complete(req, ret); + return 0; +} + static int io_shutdown_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { @@ -6054,6 +6112,8 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) return io_unlinkat_prep(req, sqe); case IORING_OP_MKDIRAT: return io_mkdirat_prep(req, sqe); + case IORING_OP_SYMLINKAT: + return io_symlinkat_prep(req, sqe); } printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", @@ -6220,6 +6280,10 @@ static void io_clean_op(struct io_kiocb *req) case IORING_OP_MKDIRAT: putname(req->mkdir.filename); break; + case IORING_OP_SYMLINKAT: + putname(req->symlink.oldpath); + putname(req->symlink.newpath); + break; } } if ((req->flags & REQ_F_POLLED) && req->apoll) { @@ -6351,6 +6415,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags) case IORING_OP_MKDIRAT: ret = io_mkdirat(req, issue_flags); break; + case IORING_OP_SYMLINKAT: + ret = io_symlinkat(req, issue_flags); + break; default: ret = -EINVAL; break; diff --git a/fs/namei.c b/fs/namei.c index e2425c0a406e..803fc95b7658 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4223,8 +4223,7 @@ int vfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, } EXPORT_SYMBOL(vfs_symlink); -static int do_symlinkat(struct filename *from, int newdfd, - struct filename *to) +int do_symlinkat(struct filename *from, int newdfd, struct filename *to) { int error; struct dentry *dentry; diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index a926407c230e..61fd347ab176 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -134,6 +134,7 @@ enum { IORING_OP_RENAMEAT, IORING_OP_UNLINKAT, IORING_OP_MKDIRAT, + IORING_OP_SYMLINKAT, /* this goes last, obviously */ IORING_OP_LAST, From cf30da90bc3a26911d369f199411f38b701394de Mon Sep 17 00:00:00 2001 From: Dmitry Kadashev Date: Thu, 8 Jul 2021 13:34:47 +0700 Subject: [PATCH 398/417] io_uring: add support for IORING_OP_LINKAT IORING_OP_LINKAT behaves like linkat(2) and takes the same flags and arguments. In some internal places 'hardlink' is used instead of 'link' to avoid confusion with the SQE links. Name 'link' conflicts with the existing 'link' member of io_kiocb. Acked-by: Linus Torvalds Suggested-by: Christian Brauner Link: https://lore.kernel.org/io-uring/20210514145259.wtl4xcsp52woi6ab@wittgenstein/ Signed-off-by: Dmitry Kadashev Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20210708063447.3556403-12-dkadashev@gmail.com [axboe: add splice_fd_in check] Signed-off-by: Jens Axboe --- fs/internal.h | 2 + fs/io_uring.c | 71 +++++++++++++++++++++++++++++++++++ fs/namei.c | 2 +- include/uapi/linux/io_uring.h | 2 + 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/fs/internal.h b/fs/internal.h index 2f9750aefbd6..2bb444600852 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -78,6 +78,8 @@ int do_renameat2(int olddfd, struct filename *oldname, int newdfd, struct filename *newname, unsigned int flags); int do_mkdirat(int dfd, struct filename *name, umode_t mode); int do_symlinkat(struct filename *from, int newdfd, struct filename *to); +int do_linkat(int olddfd, struct filename *old, int newdfd, + struct filename *new, int flags); /* * namespace.c diff --git a/fs/io_uring.c b/fs/io_uring.c index 33941df9084b..a89bbffbe042 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -676,6 +676,15 @@ struct io_symlink { struct filename *newpath; }; +struct io_hardlink { + struct file *file; + int old_dfd; + int new_dfd; + struct filename *oldpath; + struct filename *newpath; + int flags; +}; + struct io_completion { struct file *file; u32 cflags; @@ -837,6 +846,7 @@ struct io_kiocb { struct io_unlink unlink; struct io_mkdir mkdir; struct io_symlink symlink; + struct io_hardlink hardlink; /* use only after cleaning per-op data, see io_clean_op() */ struct io_completion compl; }; @@ -1050,6 +1060,7 @@ static const struct io_op_def io_op_defs[] = { [IORING_OP_UNLINKAT] = {}, [IORING_OP_MKDIRAT] = {}, [IORING_OP_SYMLINKAT] = {}, + [IORING_OP_LINKAT] = {}, }; /* requests with any of those set should undergo io_disarm_next() */ @@ -3713,6 +3724,57 @@ static int io_symlinkat(struct io_kiocb *req, int issue_flags) return 0; } +static int io_linkat_prep(struct io_kiocb *req, + const struct io_uring_sqe *sqe) +{ + struct io_hardlink *lnk = &req->hardlink; + const char __user *oldf, *newf; + + if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) + return -EINVAL; + if (sqe->ioprio || sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in) + return -EINVAL; + if (unlikely(req->flags & REQ_F_FIXED_FILE)) + return -EBADF; + + lnk->old_dfd = READ_ONCE(sqe->fd); + lnk->new_dfd = READ_ONCE(sqe->len); + oldf = u64_to_user_ptr(READ_ONCE(sqe->addr)); + newf = u64_to_user_ptr(READ_ONCE(sqe->addr2)); + lnk->flags = READ_ONCE(sqe->hardlink_flags); + + lnk->oldpath = getname(oldf); + if (IS_ERR(lnk->oldpath)) + return PTR_ERR(lnk->oldpath); + + lnk->newpath = getname(newf); + if (IS_ERR(lnk->newpath)) { + putname(lnk->oldpath); + return PTR_ERR(lnk->newpath); + } + + req->flags |= REQ_F_NEED_CLEANUP; + return 0; +} + +static int io_linkat(struct io_kiocb *req, int issue_flags) +{ + struct io_hardlink *lnk = &req->hardlink; + int ret; + + if (issue_flags & IO_URING_F_NONBLOCK) + return -EAGAIN; + + ret = do_linkat(lnk->old_dfd, lnk->oldpath, lnk->new_dfd, + lnk->newpath, lnk->flags); + + req->flags &= ~REQ_F_NEED_CLEANUP; + if (ret < 0) + req_set_fail(req); + io_req_complete(req, ret); + return 0; +} + static int io_shutdown_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { @@ -6114,6 +6176,8 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) return io_mkdirat_prep(req, sqe); case IORING_OP_SYMLINKAT: return io_symlinkat_prep(req, sqe); + case IORING_OP_LINKAT: + return io_linkat_prep(req, sqe); } printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", @@ -6284,6 +6348,10 @@ static void io_clean_op(struct io_kiocb *req) putname(req->symlink.oldpath); putname(req->symlink.newpath); break; + case IORING_OP_LINKAT: + putname(req->hardlink.oldpath); + putname(req->hardlink.newpath); + break; } } if ((req->flags & REQ_F_POLLED) && req->apoll) { @@ -6418,6 +6486,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags) case IORING_OP_SYMLINKAT: ret = io_symlinkat(req, issue_flags); break; + case IORING_OP_LINKAT: + ret = io_linkat(req, issue_flags); + break; default: ret = -EINVAL; break; diff --git a/fs/namei.c b/fs/namei.c index 803fc95b7658..0718e1e87eb4 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4368,7 +4368,7 @@ EXPORT_SYMBOL(vfs_link); * with linux 2.0, and to avoid hard-linking to directories * and other special files. --ADM */ -static int do_linkat(int olddfd, struct filename *old, int newdfd, +int do_linkat(int olddfd, struct filename *old, int newdfd, struct filename *new, int flags) { struct user_namespace *mnt_userns; diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 61fd347ab176..10eb38d2864f 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -44,6 +44,7 @@ struct io_uring_sqe { __u32 splice_flags; __u32 rename_flags; __u32 unlink_flags; + __u32 hardlink_flags; }; __u64 user_data; /* data to be passed back at completion time */ /* pack this to avoid bogus arm OABI complaints */ @@ -135,6 +136,7 @@ enum { IORING_OP_UNLINKAT, IORING_OP_MKDIRAT, IORING_OP_SYMLINKAT, + IORING_OP_LINKAT, /* this goes last, obviously */ IORING_OP_LAST, From 7f024fcd5c97dc70bb9121c80407cf3cf9be7159 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 23 Aug 2021 16:44:00 -0400 Subject: [PATCH 399/417] Keep read and write fds with each nlm_file We shouldn't really be using a read-only file descriptor to take a write lock. Most filesystems will put up with it. But NFS, for example, won't. Signed-off-by: J. Bruce Fields Signed-off-by: Chuck Lever --- fs/lockd/svc4proc.c | 4 +- fs/lockd/svclock.c | 25 ++++++--- fs/lockd/svcproc.c | 4 +- fs/lockd/svcsubs.c | 102 +++++++++++++++++++++++++----------- fs/nfsd/lockd.c | 8 ++- include/linux/lockd/bind.h | 3 +- include/linux/lockd/lockd.h | 9 +++- 7 files changed, 111 insertions(+), 44 deletions(-) diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index bc496bbd696b..e10ae2c41279 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -40,13 +40,15 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, /* Obtain file pointer. Not used by FREE_ALL call. */ if (filp != NULL) { + int mode = lock_to_openmode(&lock->fl); + error = nlm_lookup_file(rqstp, &file, lock); if (error) goto no_locks; *filp = file; /* Set up the missing parts of the file_lock structure */ - lock->fl.fl_file = file->f_file; + lock->fl.fl_file = file->f_file[mode]; lock->fl.fl_pid = current->tgid; lock->fl.fl_lmops = &nlmsvc_lock_operations; nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index bcd180ba9957..a7b4c51667ad 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -471,6 +471,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, { struct nlm_block *block = NULL; int error; + int mode; __be32 ret; dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n", @@ -524,7 +525,8 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, if (!wait) lock->fl.fl_flags &= ~FL_SLEEP; - error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); + mode = lock_to_openmode(&lock->fl); + error = vfs_lock_file(file->f_file[mode], F_SETLK, &lock->fl, NULL); lock->fl.fl_flags &= ~FL_SLEEP; dprintk("lockd: vfs_lock_file returned %d\n", error); @@ -577,6 +579,7 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_lock *conflock, struct nlm_cookie *cookie) { int error; + int mode; __be32 ret; struct nlm_lockowner *test_owner; @@ -595,7 +598,8 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, /* If there's a conflicting lock, remember to clean up the test lock */ test_owner = (struct nlm_lockowner *)lock->fl.fl_owner; - error = vfs_test_lock(file->f_file, &lock->fl); + mode = lock_to_openmode(&lock->fl); + error = vfs_test_lock(file->f_file[mode], &lock->fl); if (error) { /* We can't currently deal with deferred test requests */ if (error == FILE_LOCK_DEFERRED) @@ -641,7 +645,7 @@ out: __be32 nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock) { - int error; + int error = 0; dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)\n", nlmsvc_file_inode(file)->i_sb->s_id, @@ -654,7 +658,12 @@ nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock) nlmsvc_cancel_blocked(net, file, lock); lock->fl.fl_type = F_UNLCK; - error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); + if (file->f_file[O_RDONLY]) + error = vfs_lock_file(file->f_file[O_RDONLY], F_SETLK, + &lock->fl, NULL); + if (file->f_file[O_WRONLY]) + error = vfs_lock_file(file->f_file[O_WRONLY], F_SETLK, + &lock->fl, NULL); return (error < 0)? nlm_lck_denied_nolocks : nlm_granted; } @@ -671,6 +680,7 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l { struct nlm_block *block; int status = 0; + int mode; dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n", nlmsvc_file_inode(file)->i_sb->s_id, @@ -686,7 +696,8 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l block = nlmsvc_lookup_block(file, lock); mutex_unlock(&file->f_mutex); if (block != NULL) { - vfs_cancel_lock(block->b_file->f_file, + mode = lock_to_openmode(&lock->fl); + vfs_cancel_lock(block->b_file->f_file[mode], &block->b_call->a_args.lock.fl); status = nlmsvc_unlink_block(block); nlmsvc_release_block(block); @@ -803,6 +814,7 @@ nlmsvc_grant_blocked(struct nlm_block *block) { struct nlm_file *file = block->b_file; struct nlm_lock *lock = &block->b_call->a_args.lock; + int mode; int error; loff_t fl_start, fl_end; @@ -828,7 +840,8 @@ nlmsvc_grant_blocked(struct nlm_block *block) lock->fl.fl_flags |= FL_SLEEP; fl_start = lock->fl.fl_start; fl_end = lock->fl.fl_end; - error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); + mode = lock_to_openmode(&lock->fl); + error = vfs_lock_file(file->f_file[mode], F_SETLK, &lock->fl, NULL); lock->fl.fl_flags &= ~FL_SLEEP; lock->fl.fl_start = fl_start; lock->fl.fl_end = fl_end; diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index f4e5e0eb30fd..99696d3f6dd6 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -55,6 +55,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, struct nlm_host *host = NULL; struct nlm_file *file = NULL; struct nlm_lock *lock = &argp->lock; + int mode; __be32 error = 0; /* nfsd callbacks must have been installed for this procedure */ @@ -75,7 +76,8 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, *filp = file; /* Set up the missing parts of the file_lock structure */ - lock->fl.fl_file = file->f_file; + mode = lock_to_openmode(&lock->fl); + lock->fl.fl_file = file->f_file[mode]; lock->fl.fl_pid = current->tgid; lock->fl.fl_lmops = &nlmsvc_lock_operations; nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid); diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 13e6ffc219ec..cb3a7512c33e 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -71,14 +71,35 @@ static inline unsigned int file_hash(struct nfs_fh *f) return tmp & (FILE_NRHASH - 1); } +int lock_to_openmode(struct file_lock *lock) +{ + return (lock->fl_type == F_WRLCK) ? O_WRONLY : O_RDONLY; +} + +/* + * Open the file. Note that if we're reexporting, for example, + * this could block the lockd thread for a while. + * + * We have to make sure we have the right credential to open + * the file. + */ +static __be32 nlm_do_fopen(struct svc_rqst *rqstp, + struct nlm_file *file, int mode) +{ + struct file **fp = &file->f_file[mode]; + __be32 nfserr; + + if (*fp) + return 0; + nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode); + if (nfserr) + dprintk("lockd: open failed (error %d)\n", nfserr); + return nfserr; +} + /* * Lookup file info. If it doesn't exist, create a file info struct * and open a (VFS) file for the given inode. - * - * FIXME: - * Note that we open the file O_RDONLY even when creating write locks. - * This is not quite right, but for now, we assume the client performs - * the proper R/W checking. */ __be32 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, @@ -87,42 +108,38 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, struct nlm_file *file; unsigned int hash; __be32 nfserr; + int mode; nlm_debug_print_fh("nlm_lookup_file", &lock->fh); hash = file_hash(&lock->fh); + mode = lock_to_openmode(&lock->fl); /* Lock file table */ mutex_lock(&nlm_file_mutex); hlist_for_each_entry(file, &nlm_files[hash], f_list) - if (!nfs_compare_fh(&file->f_handle, &lock->fh)) + if (!nfs_compare_fh(&file->f_handle, &lock->fh)) { + mutex_lock(&file->f_mutex); + nfserr = nlm_do_fopen(rqstp, file, mode); + mutex_unlock(&file->f_mutex); goto found; - + } nlm_debug_print_fh("creating file for", &lock->fh); nfserr = nlm_lck_denied_nolocks; file = kzalloc(sizeof(*file), GFP_KERNEL); if (!file) - goto out_unlock; + goto out_free; memcpy(&file->f_handle, &lock->fh, sizeof(struct nfs_fh)); mutex_init(&file->f_mutex); INIT_HLIST_NODE(&file->f_list); INIT_LIST_HEAD(&file->f_blocks); - /* - * Open the file. Note that if we're reexporting, for example, - * this could block the lockd thread for a while. - * - * We have to make sure we have the right credential to open - * the file. - */ - nfserr = nlmsvc_ops->fopen(rqstp, &lock->fh, &file->f_file); - if (nfserr) { - dprintk("lockd: open failed (error %d)\n", nfserr); - goto out_free; - } + nfserr = nlm_do_fopen(rqstp, file, mode); + if (nfserr) + goto out_unlock; hlist_add_head(&file->f_list, &nlm_files[hash]); @@ -130,7 +147,6 @@ found: dprintk("lockd: found file %p (count %d)\n", file, file->f_count); *result = file; file->f_count++; - nfserr = 0; out_unlock: mutex_unlock(&nlm_file_mutex); @@ -150,13 +166,34 @@ nlm_delete_file(struct nlm_file *file) nlm_debug_print_file("closing file", file); if (!hlist_unhashed(&file->f_list)) { hlist_del(&file->f_list); - nlmsvc_ops->fclose(file->f_file); + if (file->f_file[O_RDONLY]) + nlmsvc_ops->fclose(file->f_file[O_RDONLY]); + if (file->f_file[O_WRONLY]) + nlmsvc_ops->fclose(file->f_file[O_WRONLY]); kfree(file); } else { printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); } } +static int nlm_unlock_files(struct nlm_file *file) +{ + struct file_lock lock; + struct file *f; + + lock.fl_type = F_UNLCK; + lock.fl_start = 0; + lock.fl_end = OFFSET_MAX; + for (f = file->f_file[0]; f <= file->f_file[1]; f++) { + if (f && vfs_lock_file(f, F_SETLK, &lock, NULL) < 0) { + pr_warn("lockd: unlock failure in %s:%d\n", + __FILE__, __LINE__); + return 1; + } + } + return 0; +} + /* * Loop over all locks on the given file and perform the specified * action. @@ -184,17 +221,10 @@ again: lockhost = ((struct nlm_lockowner *)fl->fl_owner)->host; if (match(lockhost, host)) { - struct file_lock lock = *fl; spin_unlock(&flctx->flc_lock); - lock.fl_type = F_UNLCK; - lock.fl_start = 0; - lock.fl_end = OFFSET_MAX; - if (vfs_lock_file(file->f_file, F_SETLK, &lock, NULL) < 0) { - printk("lockd: unlock failure in %s:%d\n", - __FILE__, __LINE__); + if (nlm_unlock_files(file)) return 1; - } goto again; } } @@ -248,6 +278,15 @@ nlm_file_inuse(struct nlm_file *file) return 0; } +static void nlm_close_files(struct nlm_file *file) +{ + struct file *f; + + for (f = file->f_file[0]; f <= file->f_file[1]; f++) + if (f) + nlmsvc_ops->fclose(f); +} + /* * Loop over all files in the file table. */ @@ -278,7 +317,7 @@ nlm_traverse_files(void *data, nlm_host_match_fn_t match, if (list_empty(&file->f_blocks) && !file->f_locks && !file->f_shares && !file->f_count) { hlist_del(&file->f_list); - nlmsvc_ops->fclose(file->f_file); + nlm_close_files(file); kfree(file); } } @@ -412,6 +451,7 @@ nlmsvc_invalidate_all(void) nlm_traverse_files(NULL, nlmsvc_is_client, NULL); } + static int nlmsvc_match_sb(void *datap, struct nlm_file *file) { diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 3f5b3d7b62b7..606fa155c28a 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -25,9 +25,11 @@ * Note: we hold the dentry use count while the file is open. */ static __be32 -nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp) +nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp, + int mode) { __be32 nfserr; + int access; struct svc_fh fh; /* must initialize before using! but maxsize doesn't matter */ @@ -36,7 +38,9 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp) memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size); fh.fh_export = NULL; - nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp); + access = (mode == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ; + access |= NFSD_MAY_LOCK; + nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp); fh_put(&fh); /* We return nlm error codes as nlm doesn't know * about nfsd, but nfsd does know about nlm.. diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h index 0520c0cd73f4..3bc9f7410e21 100644 --- a/include/linux/lockd/bind.h +++ b/include/linux/lockd/bind.h @@ -27,7 +27,8 @@ struct rpc_task; struct nlmsvc_binding { __be32 (*fopen)(struct svc_rqst *, struct nfs_fh *, - struct file **); + struct file **, + int mode); void (*fclose)(struct file *); }; diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 81b71ad2040a..c4ae6506b8b3 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -10,6 +10,8 @@ #ifndef LINUX_LOCKD_LOCKD_H #define LINUX_LOCKD_LOCKD_H +/* XXX: a lot of this should really be under fs/lockd. */ + #include #include #include @@ -154,7 +156,8 @@ struct nlm_rqst { struct nlm_file { struct hlist_node f_list; /* linked list */ struct nfs_fh f_handle; /* NFS file handle */ - struct file * f_file; /* VFS file pointer */ + struct file * f_file[2]; /* VFS file pointers, + indexed by O_ flags */ struct nlm_share * f_shares; /* DOS shares */ struct list_head f_blocks; /* blocked locks */ unsigned int f_locks; /* guesstimate # of locks */ @@ -267,6 +270,7 @@ typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref); /* * Server-side lock handling */ +int lock_to_openmode(struct file_lock *); __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, struct nlm_host *, struct nlm_lock *, int, struct nlm_cookie *, int); @@ -301,7 +305,8 @@ int nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr); static inline struct inode *nlmsvc_file_inode(struct nlm_file *file) { - return locks_inode(file->f_file); + return locks_inode(file->f_file[O_RDONLY] ? + file->f_file[O_RDONLY] : file->f_file[O_WRONLY]); } static inline int __nlm_privileged_request4(const struct sockaddr *sap) From 1923b544bf6094f7c3a86692de8a026b9d52efc1 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 25 Aug 2021 09:42:22 +0900 Subject: [PATCH 400/417] MAINTAINERS: ksmbd: update my email address My email address in ksmbd entry will be not available in a few days. Update it to my own kernel.org address. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 85af3ce6a28e..4e75f5ec0aba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9939,7 +9939,7 @@ F: Documentation/dev-tools/kselftest* F: tools/testing/selftests/ KERNEL SMB3 SERVER (KSMBD) -M: Namjae Jeon +M: Namjae Jeon M: Sergey Senozhatsky M: Steve French M: Hyunchul Lee From e9e3d5f9e34c00b57dbec0d422cf2ad7318c0155 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 25 Aug 2021 09:57:09 +0900 Subject: [PATCH 401/417] MAINTAINERS: ksmbd: add cifs_common directory to ksmbd entry The codes that shared between cifs and ksmbd will move into the cifs_common directory. This patch add it to the ksmbd entry in the MAINTAINERS file. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4e75f5ec0aba..29b678b0c9b4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9946,6 +9946,7 @@ M: Hyunchul Lee L: linux-cifs@vger.kernel.org S: Maintained T: git git://git.samba.org/ksmbd.git +F: fs/cifs_common/ F: fs/ksmbd/ KERNEL UNIT TESTING FRAMEWORK (KUnit) From f980d055a0f858d73d9467bb0b570721bbfcdfb8 Mon Sep 17 00:00:00 2001 From: Len Baker Date: Tue, 17 Aug 2021 12:27:09 +0200 Subject: [PATCH 402/417] CIFS: Fix a potencially linear read overflow strlcpy() reads the entire source buffer first. This read may exceed the destination size limit. This is both inefficient and can lead to linear read overflows if a source string is not NUL-terminated. Also, the strnlen() call does not avoid the read overflow in the strlcpy function when a not NUL-terminated string is passed. So, replace this block by a call to kstrndup() that avoids this type of overflow and does the same. Fixes: 066ce6899484d ("cifs: rename cifs_strlcpy_to_host and make it use new functions") Signed-off-by: Len Baker Reviewed-by: Paulo Alcantara (SUSE) Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_unicode.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 9bd03a231032..171ad8b42107 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -358,14 +358,9 @@ cifs_strndup_from_utf16(const char *src, const int maxlen, if (!dst) return NULL; cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage, - NO_MAP_UNI_RSVD); + NO_MAP_UNI_RSVD); } else { - len = strnlen(src, maxlen); - len++; - dst = kmalloc(len, GFP_KERNEL); - if (!dst) - return NULL; - strlcpy(dst, src, len); + dst = kstrndup(src, maxlen, GFP_KERNEL); } return dst; From d72c74197b70bc3c95152f351a568007bffa3e11 Mon Sep 17 00:00:00 2001 From: Ding Hui Date: Tue, 17 Aug 2021 22:55:10 +0800 Subject: [PATCH 403/417] cifs: fix wrong release in sess_alloc_buffer() failed path smb_buf is allocated by small_smb_init_no_tc(), and buf type is CIFS_SMALL_BUFFER, so we should use cifs_small_buf_release() to release it in failed path. Signed-off-by: Ding Hui Reviewed-by: Paulo Alcantara (SUSE) Signed-off-by: Steve French --- fs/cifs/sess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index c5785fd3f52e..606fd7d6cb71 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -877,7 +877,7 @@ sess_alloc_buffer(struct sess_data *sess_data, int wct) return 0; out_free_smb_buf: - kfree(smb_buf); + cifs_small_buf_release(smb_buf); sess_data->iov[0].iov_base = NULL; sess_data->iov[0].iov_len = 0; sess_data->buf0_type = CIFS_NO_BUFFER; From 7321be2663da5922343cc121f1ff04924cee2e76 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 23 Aug 2021 13:52:12 -0500 Subject: [PATCH 404/417] smb3: fix posix extensions mount option We were incorrectly initializing the posix extensions in the conversion to the new mount API. CC: # 5.11+ Reported-by: Christian Brauner Acked-by: Christian Brauner Suggested-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifs/fs_context.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/cifs/fs_context.c b/fs/cifs/fs_context.c index eed59bc1d913..727c8835b222 100644 --- a/fs/cifs/fs_context.c +++ b/fs/cifs/fs_context.c @@ -1266,10 +1266,17 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, ctx->posix_paths = 1; break; case Opt_unix: - if (result.negated) + if (result.negated) { + if (ctx->linux_ext == 1) + pr_warn_once("conflicting posix mount options specified\n"); ctx->linux_ext = 0; - else ctx->no_linux_ext = 1; + } else { + if (ctx->no_linux_ext == 1) + pr_warn_once("conflicting posix mount options specified\n"); + ctx->linux_ext = 1; + ctx->no_linux_ext = 0; + } break; case Opt_nocase: ctx->nocase = 1; From 3d2b50e0e7682b2453ccfac775ad7c2c1d5ceb45 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 20 Aug 2021 18:10:36 -0500 Subject: [PATCH 405/417] oid_registry: Add OIDs for missing Spnego auth mechanisms to Macs In testing mounts to Macs, noticed that the OIDS for some GSSAPI/SPNEGO auth mechanisms sent by the server were not recognized and were missing from the header. Reviewed-by: Paulo Alcantara (SUSE) Signed-off-by: Steve French --- include/linux/oid_registry.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 3d8db1f6a5db..0f4a8903922a 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -70,6 +70,9 @@ enum OID { OID_spnego, /* 1.3.6.1.5.5.2 */ + OID_IAKerb, /* 1.3.6.1.5.2.5 */ + OID_PKU2U, /* 1.3.5.1.5.2.7 */ + OID_Scram, /* 1.3.6.1.5.5.14 */ OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ OID_sha1, /* 1.3.14.3.2.26 */ OID_id_ansip384r1, /* 1.3.132.0.34 */ @@ -104,6 +107,10 @@ enum OID { OID_authorityKeyIdentifier, /* 2.5.29.35 */ OID_extKeyUsage, /* 2.5.29.37 */ + /* Heimdal mechanisms */ + OID_NetlogonMechanism, /* 1.2.752.43.14.2 */ + OID_appleLocalKdcSupported, /* 1.2.752.43.14.3 */ + /* EC-RDSA */ OID_gostCPSignA, /* 1.2.643.2.2.35.1 */ OID_gostCPSignB, /* 1.2.643.2.2.35.2 */ From 18d04062f83b3eedb64e9f64ede26ee83ae7f152 Mon Sep 17 00:00:00 2001 From: Shyam Prasad N Date: Tue, 10 Aug 2021 10:22:28 +0000 Subject: [PATCH 406/417] cifs: enable fscache usage even for files opened as rw So far, the fscache implementation we had supports only a small set of use cases. Particularly for files opened with O_RDONLY. This commit enables it even for rw based file opens. It also enables the reuse of cached data in case of mount option (cache=singleclient) where it is guaranteed that this is the only client (and server) which operates on the files. There's also a single line change in fscache.c to get around a bug seen in fscache. Signed-off-by: Shyam Prasad N Acked-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 1 - fs/cifs/cifssmb.c | 1 + fs/cifs/file.c | 15 +++++++++++++-- fs/cifs/fscache.c | 41 +++++++++++++++++++++++++++++++++-------- fs/cifs/fscache.h | 23 +++++++++++++++++++++++ fs/cifs/inode.c | 6 ++++++ 6 files changed, 76 insertions(+), 11 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 64b71c4e2a9d..6e4fc4221418 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -399,7 +399,6 @@ cifs_evict_inode(struct inode *inode) { truncate_inode_pages_final(&inode->i_data); clear_inode(inode); - cifs_fscache_release_inode_cookie(inode); } static void diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 65d1a65bfc37..f207de803629 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2101,6 +2101,7 @@ cifs_writev_complete(struct work_struct *work) else if (wdata->result < 0) SetPageError(page); end_page_writeback(page); + cifs_readpage_to_fscache(inode, page); put_page(page); } if (wdata->result != -EAGAIN) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index bb98fbdd22a9..d0216472f1c6 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -377,6 +377,8 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file) struct cifsLockInfo *li, *tmp; struct super_block *sb = inode->i_sb; + cifs_fscache_release_inode_cookie(inode); + /* * Delete any outstanding lock records. We'll lose them when the file * is closed anyway. @@ -882,8 +884,10 @@ int cifs_close(struct inode *inode, struct file *file) if ((cinode->oplock == CIFS_CACHE_RHW_FLG) && cinode->lease_granted && dclose) { - if (test_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) + if (test_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) { inode->i_ctime = inode->i_mtime = current_time(inode); + cifs_fscache_update_inode_cookie(inode); + } spin_lock(&cinode->deferred_lock); cifs_add_deferred_close(cfile, dclose); if (cfile->deferred_close_scheduled && @@ -4170,6 +4174,10 @@ static vm_fault_t cifs_page_mkwrite(struct vm_fault *vmf) { struct page *page = vmf->page; + struct file *file = vmf->vma->vm_file; + struct inode *inode = file_inode(file); + + cifs_fscache_wait_on_page_write(inode, page); lock_page(page); return VM_FAULT_LOCKED; @@ -4235,13 +4243,16 @@ cifs_readv_complete(struct work_struct *work) (rdata->result == -EAGAIN && got_bytes)) { flush_dcache_page(page); SetPageUptodate(page); - } + } else + SetPageError(page); unlock_page(page); if (rdata->result == 0 || (rdata->result == -EAGAIN && got_bytes)) cifs_readpage_to_fscache(rdata->mapping->host, page); + else + cifs_fscache_uncache_page(rdata->mapping->host, page); got_bytes -= min_t(unsigned int, PAGE_SIZE, got_bytes); diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index dd625033cd6b..fab47fa7df74 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -176,29 +176,34 @@ void cifs_fscache_release_inode_cookie(struct inode *inode) auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec; cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache); + /* fscache_relinquish_cookie does not seem to update auxdata */ + fscache_update_cookie(cifsi->fscache, &auxdata); fscache_relinquish_cookie(cifsi->fscache, &auxdata, false); cifsi->fscache = NULL; } } -static void cifs_fscache_disable_inode_cookie(struct inode *inode) +void cifs_fscache_update_inode_cookie(struct inode *inode) { + struct cifs_fscache_inode_auxdata auxdata; struct cifsInodeInfo *cifsi = CIFS_I(inode); if (cifsi->fscache) { + memset(&auxdata, 0, sizeof(auxdata)); + auxdata.eof = cifsi->server_eof; + auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec; + auxdata.last_change_time_sec = cifsi->vfs_inode.i_ctime.tv_sec; + auxdata.last_write_time_nsec = cifsi->vfs_inode.i_mtime.tv_nsec; + auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec; + cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache); - fscache_uncache_all_inode_pages(cifsi->fscache, inode); - fscache_relinquish_cookie(cifsi->fscache, NULL, true); - cifsi->fscache = NULL; + fscache_update_cookie(cifsi->fscache, &auxdata); } } void cifs_fscache_set_inode_cookie(struct inode *inode, struct file *filp) { - if ((filp->f_flags & O_ACCMODE) != O_RDONLY) - cifs_fscache_disable_inode_cookie(inode); - else - cifs_fscache_enable_inode_cookie(inode); + cifs_fscache_enable_inode_cookie(inode); } void cifs_fscache_reset_inode_cookie(struct inode *inode) @@ -310,6 +315,8 @@ void __cifs_readpage_to_fscache(struct inode *inode, struct page *page) struct cifsInodeInfo *cifsi = CIFS_I(inode); int ret; + WARN_ON(!cifsi->fscache); + cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n", __func__, cifsi->fscache, page, inode); ret = fscache_write_page(cifsi->fscache, page, @@ -334,3 +341,21 @@ void __cifs_fscache_invalidate_page(struct page *page, struct inode *inode) fscache_wait_on_page_write(cookie, page); fscache_uncache_page(cookie, page); } + +void __cifs_fscache_wait_on_page_write(struct inode *inode, struct page *page) +{ + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct fscache_cookie *cookie = cifsi->fscache; + + cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", __func__, page, cookie); + fscache_wait_on_page_write(cookie, page); +} + +void __cifs_fscache_uncache_page(struct inode *inode, struct page *page) +{ + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct fscache_cookie *cookie = cifsi->fscache; + + cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", __func__, page, cookie); + fscache_uncache_page(cookie, page); +} diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 3d55cb2ef055..82e856b9cf89 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -55,10 +55,13 @@ extern void cifs_fscache_get_super_cookie(struct cifs_tcon *); extern void cifs_fscache_release_super_cookie(struct cifs_tcon *); extern void cifs_fscache_release_inode_cookie(struct inode *); +extern void cifs_fscache_update_inode_cookie(struct inode *inode); extern void cifs_fscache_set_inode_cookie(struct inode *, struct file *); extern void cifs_fscache_reset_inode_cookie(struct inode *); extern void __cifs_fscache_invalidate_page(struct page *, struct inode *); +extern void __cifs_fscache_wait_on_page_write(struct inode *inode, struct page *page); +extern void __cifs_fscache_uncache_page(struct inode *inode, struct page *page); extern int cifs_fscache_release_page(struct page *page, gfp_t gfp); extern int __cifs_readpage_from_fscache(struct inode *, struct page *); extern int __cifs_readpages_from_fscache(struct inode *, @@ -76,6 +79,20 @@ static inline void cifs_fscache_invalidate_page(struct page *page, __cifs_fscache_invalidate_page(page, inode); } +static inline void cifs_fscache_wait_on_page_write(struct inode *inode, + struct page *page) +{ + if (PageFsCache(page)) + __cifs_fscache_wait_on_page_write(inode, page); +} + +static inline void cifs_fscache_uncache_page(struct inode *inode, + struct page *page) +{ + if (PageFsCache(page)) + __cifs_fscache_uncache_page(inode, page); +} + static inline int cifs_readpage_from_fscache(struct inode *inode, struct page *page) { @@ -123,6 +140,7 @@ static inline void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) {} static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {} +static inline void cifs_fscache_update_inode_cookie(struct inode *inode) {} static inline void cifs_fscache_set_inode_cookie(struct inode *inode, struct file *filp) {} static inline void cifs_fscache_reset_inode_cookie(struct inode *inode) {} @@ -133,6 +151,11 @@ static inline int cifs_fscache_release_page(struct page *page, gfp_t gfp) static inline void cifs_fscache_invalidate_page(struct page *page, struct inode *inode) {} +static inline void cifs_fscache_wait_on_page_write(struct inode *inode, + struct page *page) {} +static inline void cifs_fscache_uncache_page(struct inode *inode, + struct page *page) {} + static inline int cifs_readpage_from_fscache(struct inode *inode, struct page *page) { diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 65f8a70cece3..50c01cff4c84 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -2297,6 +2297,7 @@ cifs_revalidate_mapping(struct inode *inode) { int rc; unsigned long *flags = &CIFS_I(inode)->flags; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); /* swapfiles are not supposed to be shared */ if (IS_SWAPFILE(inode)) @@ -2308,11 +2309,16 @@ cifs_revalidate_mapping(struct inode *inode) return rc; if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) { + /* for cache=singleclient, do not invalidate */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) + goto skip_invalidate; + rc = cifs_invalidate_mapping(inode); if (rc) set_bit(CIFS_INO_INVALID_MAPPING, flags); } +skip_invalidate: clear_bit_unlock(CIFS_INO_LOCK, flags); smp_mb__after_atomic(); wake_up_bit(flags, CIFS_INO_LOCK); From 76a3c92ec9e0668e4cd0e9ff1782eb68f61a179c Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 19 Aug 2021 20:34:58 +1000 Subject: [PATCH 407/417] cifs: remove support for NTLM and weaker authentication algorithms for SMB1. This removes the dependency to DES. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/Kconfig | 28 ----- fs/cifs/cifs_debug.c | 11 -- fs/cifs/cifs_swn.c | 2 - fs/cifs/cifsencrypt.c | 81 -------------- fs/cifs/cifsfs.c | 6 - fs/cifs/cifsglob.h | 32 +----- fs/cifs/cifspdu.h | 28 ----- fs/cifs/cifsproto.h | 10 -- fs/cifs/cifssmb.c | 106 +----------------- fs/cifs/connect.c | 32 ------ fs/cifs/fs_context.c | 14 --- fs/cifs/fs_context.h | 3 - fs/cifs/sess.c | 255 +----------------------------------------- fs/cifs/smbencrypt.c | 117 +------------------ 14 files changed, 5 insertions(+), 720 deletions(-) diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 7364950a9ef4..2e8b132efdbc 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -16,7 +16,6 @@ config CIFS select CRYPTO_GCM select CRYPTO_ECB select CRYPTO_AES - select CRYPTO_LIB_DES select KEYS select DNS_RESOLVER select ASN1 @@ -85,33 +84,6 @@ config CIFS_ALLOW_INSECURE_LEGACY If unsure, say Y. -config CIFS_WEAK_PW_HASH - bool "Support legacy servers which use weaker LANMAN security" - depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY - help - Modern CIFS servers including Samba and most Windows versions - (since 1997) support stronger NTLM (and even NTLMv2 and Kerberos) - security mechanisms. These hash the password more securely - than the mechanisms used in the older LANMAN version of the - SMB protocol but LANMAN based authentication is needed to - establish sessions with some old SMB servers. - - Enabling this option allows the cifs module to mount to older - LANMAN based servers such as OS/2 and Windows 95, but such - mounts may be less secure than mounts using NTLM or more recent - security mechanisms if you are on a public network. Unless you - have a need to access old SMB servers (and are on a private - network) you probably want to say N. Even if this support - is enabled in the kernel build, LANMAN authentication will not be - used automatically. At runtime LANMAN mounts are disabled but - can be set to required (or optional) either in - /proc/fs/cifs (see Documentation/admin-guide/cifs/usage.rst for - more detail) or via an option on the mount command. This support - is disabled by default in order to reduce the possibility of a - downgrade attack. - - If unsure, say N. - config CIFS_UPCALL bool "Kerberos/SPNEGO advanced session setup" depends on CIFS diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 8857ac7e7a14..51a824fc926a 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -250,9 +250,6 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY seq_printf(m, ",ALLOW_INSECURE_LEGACY"); #endif -#ifdef CONFIG_CIFS_WEAK_PW_HASH - seq_printf(m, ",WEAK_PW_HASH"); -#endif #ifdef CONFIG_CIFS_POSIX seq_printf(m, ",CIFS_POSIX"); #endif @@ -929,14 +926,6 @@ cifs_security_flags_handle_must_flags(unsigned int *flags) *flags = CIFSSEC_MUST_NTLMSSP; else if ((*flags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) *flags = CIFSSEC_MUST_NTLMV2; - else if ((*flags & CIFSSEC_MUST_NTLM) == CIFSSEC_MUST_NTLM) - *flags = CIFSSEC_MUST_NTLM; - else if (CIFSSEC_MUST_LANMAN && - (*flags & CIFSSEC_MUST_LANMAN) == CIFSSEC_MUST_LANMAN) - *flags = CIFSSEC_MUST_LANMAN; - else if (CIFSSEC_MUST_PLNTXT && - (*flags & CIFSSEC_MUST_PLNTXT) == CIFSSEC_MUST_PLNTXT) - *flags = CIFSSEC_MUST_PLNTXT; *flags |= signflags; } diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c index 93b47818c6c2..12bde7bfda86 100644 --- a/fs/cifs/cifs_swn.c +++ b/fs/cifs/cifs_swn.c @@ -147,8 +147,6 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) goto nlmsg_fail; } break; - case LANMAN: - case NTLM: case NTLMv2: case RawNTLMSSP: ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb); diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index ecf15d845dbd..7680e0a9bea3 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -250,87 +250,6 @@ int cifs_verify_signature(struct smb_rqst *rqst, } -/* first calculate 24 bytes ntlm response and then 16 byte session key */ -int setup_ntlm_response(struct cifs_ses *ses, const struct nls_table *nls_cp) -{ - int rc = 0; - unsigned int temp_len = CIFS_SESS_KEY_SIZE + CIFS_AUTH_RESP_SIZE; - char temp_key[CIFS_SESS_KEY_SIZE]; - - if (!ses) - return -EINVAL; - - ses->auth_key.response = kmalloc(temp_len, GFP_KERNEL); - if (!ses->auth_key.response) - return -ENOMEM; - - ses->auth_key.len = temp_len; - - rc = SMBNTencrypt(ses->password, ses->server->cryptkey, - ses->auth_key.response + CIFS_SESS_KEY_SIZE, nls_cp); - if (rc) { - cifs_dbg(FYI, "%s Can't generate NTLM response, error: %d\n", - __func__, rc); - return rc; - } - - rc = E_md4hash(ses->password, temp_key, nls_cp); - if (rc) { - cifs_dbg(FYI, "%s Can't generate NT hash, error: %d\n", - __func__, rc); - return rc; - } - - rc = mdfour(ses->auth_key.response, temp_key, CIFS_SESS_KEY_SIZE); - if (rc) - cifs_dbg(FYI, "%s Can't generate NTLM session key, error: %d\n", - __func__, rc); - - return rc; -} - -#ifdef CONFIG_CIFS_WEAK_PW_HASH -int calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, - char *lnm_session_key) -{ - int i, len; - int rc; - char password_with_pad[CIFS_ENCPWD_SIZE] = {0}; - - if (password) { - for (len = 0; len < CIFS_ENCPWD_SIZE; len++) - if (!password[len]) - break; - - memcpy(password_with_pad, password, len); - } - - if (!encrypt && global_secflags & CIFSSEC_MAY_PLNTXT) { - memcpy(lnm_session_key, password_with_pad, - CIFS_ENCPWD_SIZE); - return 0; - } - - /* calculate old style session key */ - /* calling toupper is less broken than repeatedly - calling nls_toupper would be since that will never - work for UTF8, but neither handles multibyte code pages - but the only alternative would be converting to UCS-16 (Unicode) - (using a routine something like UniStrupr) then - uppercasing and then converting back from Unicode - which - would only worth doing it if we knew it were utf8. Basically - utf8 and other multibyte codepages each need their own strupper - function since a byte at a time will ont work. */ - - for (i = 0; i < CIFS_ENCPWD_SIZE; i++) - password_with_pad[i] = toupper(password_with_pad[i]); - - rc = SMBencrypt(password_with_pad, cryptkey, lnm_session_key); - - return rc; -} -#endif /* CIFS_WEAK_PW_HASH */ - /* Build a proper attribute value/target info pairs blob. * Fill in netbios and dns domain name and workstation name * and client time (total five av pairs and + one end of fields indicator. diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 6e4fc4221418..b1b175f229ee 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -437,15 +437,9 @@ cifs_show_security(struct seq_file *s, struct cifs_ses *ses) seq_puts(s, ",sec="); switch (ses->sectype) { - case LANMAN: - seq_puts(s, "lanman"); - break; case NTLMv2: seq_puts(s, "ntlmv2"); break; - case NTLM: - seq_puts(s, "ntlm"); - break; case Kerberos: seq_puts(s, "krb5"); break; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c6a9542ca281..c068f7d8d879 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -114,8 +114,6 @@ enum statusEnum { enum securityEnum { Unspecified = 0, /* not specified */ - LANMAN, /* Legacy LANMAN auth */ - NTLM, /* Legacy NTLM012 auth with NTLM hash */ NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */ RawNTLMSSP, /* NTLMSSP without SPNEGO, NTLMv2 hash */ Kerberos, /* Kerberos via SPNEGO */ @@ -634,7 +632,6 @@ struct TCP_Server_Info { struct session_key session_key; unsigned long lstrp; /* when we got last response from this server */ struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ -#define CIFS_NEGFLAVOR_LANMAN 0 /* wct == 13, LANMAN */ #define CIFS_NEGFLAVOR_UNENCAP 1 /* wct == 17, but no ext_sec */ #define CIFS_NEGFLAVOR_EXTENDED 2 /* wct == 17, ext_sec bit set */ char negflavor; /* NEGOTIATE response flavor */ @@ -1734,16 +1731,8 @@ static inline bool is_retryable_error(int error) /* Security Flags: indicate type of session setup needed */ #define CIFSSEC_MAY_SIGN 0x00001 -#define CIFSSEC_MAY_NTLM 0x00002 #define CIFSSEC_MAY_NTLMV2 0x00004 #define CIFSSEC_MAY_KRB5 0x00008 -#ifdef CONFIG_CIFS_WEAK_PW_HASH -#define CIFSSEC_MAY_LANMAN 0x00010 -#define CIFSSEC_MAY_PLNTXT 0x00020 -#else -#define CIFSSEC_MAY_LANMAN 0 -#define CIFSSEC_MAY_PLNTXT 0 -#endif /* weak passwords */ #define CIFSSEC_MAY_SEAL 0x00040 /* not supported yet */ #define CIFSSEC_MAY_NTLMSSP 0x00080 /* raw ntlmssp with ntlmv2 */ @@ -1751,32 +1740,19 @@ static inline bool is_retryable_error(int error) /* note that only one of the following can be set so the result of setting MUST flags more than once will be to require use of the stronger protocol */ -#define CIFSSEC_MUST_NTLM 0x02002 #define CIFSSEC_MUST_NTLMV2 0x04004 #define CIFSSEC_MUST_KRB5 0x08008 -#ifdef CONFIG_CIFS_WEAK_PW_HASH -#define CIFSSEC_MUST_LANMAN 0x10010 -#define CIFSSEC_MUST_PLNTXT 0x20020 -#ifdef CONFIG_CIFS_UPCALL -#define CIFSSEC_MASK 0xBF0BF /* allows weak security but also krb5 */ -#else -#define CIFSSEC_MASK 0xB70B7 /* current flags supported if weak */ -#endif /* UPCALL */ -#else /* do not allow weak pw hash */ -#define CIFSSEC_MUST_LANMAN 0 -#define CIFSSEC_MUST_PLNTXT 0 #ifdef CONFIG_CIFS_UPCALL #define CIFSSEC_MASK 0x8F08F /* flags supported if no weak allowed */ #else #define CIFSSEC_MASK 0x87087 /* flags supported if no weak allowed */ #endif /* UPCALL */ -#endif /* WEAK_PW_HASH */ #define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */ #define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */ #define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP) -#define CIFSSEC_MAX (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2) -#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP) +#define CIFSSEC_MAX (CIFSSEC_MUST_NTLMV2) +#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP) /* ***************************************************************** * All constants go here @@ -1940,10 +1916,6 @@ static inline char *get_security_type_str(enum securityEnum sectype) return "Kerberos"; case NTLMv2: return "NTLMv2"; - case NTLM: - return "NTLM"; - case LANMAN: - return "LANMAN"; default: return "Unknown"; } diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index f6e235001358..dc920e206336 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -14,13 +14,7 @@ #include #include "smbfsctl.h" -#ifdef CONFIG_CIFS_WEAK_PW_HASH -#define LANMAN_PROT 0 -#define LANMAN2_PROT 1 -#define CIFS_PROT 2 -#else #define CIFS_PROT 0 -#endif #define POSIX_PROT (CIFS_PROT+1) #define BAD_PROT 0xFFFF @@ -505,30 +499,8 @@ typedef struct negotiate_req { unsigned char DialectsArray[1]; } __attribute__((packed)) NEGOTIATE_REQ; -/* Dialect index is 13 for LANMAN */ - #define MIN_TZ_ADJ (15 * 60) /* minimum grid for timezones in seconds */ -typedef struct lanman_neg_rsp { - struct smb_hdr hdr; /* wct = 13 */ - __le16 DialectIndex; - __le16 SecurityMode; - __le16 MaxBufSize; - __le16 MaxMpxCount; - __le16 MaxNumberVcs; - __le16 RawMode; - __le32 SessionKey; - struct { - __le16 Time; - __le16 Date; - } __attribute__((packed)) SrvTime; - __le16 ServerTimeZone; - __le16 EncryptionKeyLength; - __le16 Reserved; - __u16 ByteCount; - unsigned char EncryptionKey[1]; -} __attribute__((packed)) LANMAN_NEG_RSP; - #define READ_RAW_ENABLE 1 #define WRITE_RAW_ENABLE 2 #define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index e0def0f0714b..f9740c21ca3d 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -498,19 +498,12 @@ extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *); extern int cifs_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, __u32 expected_sequence_number); -extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *, - const struct nls_table *); -extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *); extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server); extern int calc_seckey(struct cifs_ses *); extern int generate_smb30signingkey(struct cifs_ses *); extern int generate_smb311signingkey(struct cifs_ses *); -#ifdef CONFIG_CIFS_WEAK_PW_HASH -extern int calc_lanman_hash(const char *password, const char *cryptkey, - bool encrypt, char *lnm_session_key); -#endif /* CIFS_WEAK_PW_HASH */ extern int CIFSSMBCopy(unsigned int xid, struct cifs_tcon *source_tcon, const char *fromName, @@ -547,11 +540,8 @@ extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, const unsigned char *path); -extern int mdfour(unsigned char *, unsigned char *, int); extern int E_md4hash(const unsigned char *passwd, unsigned char *p16, const struct nls_table *codepage); -extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, - unsigned char *p24); extern int cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f207de803629..a8e41c1e80ca 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -42,10 +42,6 @@ static struct { int index; char *name; } protocols[] = { -#ifdef CONFIG_CIFS_WEAK_PW_HASH - {LANMAN_PROT, "\2LM1.2X002"}, - {LANMAN2_PROT, "\2LANMAN2.1"}, -#endif /* weak password hashing for legacy clients */ {CIFS_PROT, "\2NT LM 0.12"}, {POSIX_PROT, "\2POSIX 2"}, {BAD_PROT, "\2"} @@ -55,10 +51,6 @@ static struct { int index; char *name; } protocols[] = { -#ifdef CONFIG_CIFS_WEAK_PW_HASH - {LANMAN_PROT, "\2LM1.2X002"}, - {LANMAN2_PROT, "\2LANMAN2.1"}, -#endif /* weak password hashing for legacy clients */ {CIFS_PROT, "\2NT LM 0.12"}, {BAD_PROT, "\2"} }; @@ -66,17 +58,9 @@ static struct { /* define the number of elements in the cifs dialect array */ #ifdef CONFIG_CIFS_POSIX -#ifdef CONFIG_CIFS_WEAK_PW_HASH -#define CIFS_NUM_PROT 4 -#else #define CIFS_NUM_PROT 2 -#endif /* CIFS_WEAK_PW_HASH */ #else /* not posix */ -#ifdef CONFIG_CIFS_WEAK_PW_HASH -#define CIFS_NUM_PROT 3 -#else #define CIFS_NUM_PROT 1 -#endif /* CONFIG_CIFS_WEAK_PW_HASH */ #endif /* CIFS_POSIX */ /* @@ -475,89 +459,6 @@ cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required) return 0; } -#ifdef CONFIG_CIFS_WEAK_PW_HASH -static int -decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) -{ - __s16 tmp; - struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr; - - if (server->dialect != LANMAN_PROT && server->dialect != LANMAN2_PROT) - return -EOPNOTSUPP; - - server->sec_mode = le16_to_cpu(rsp->SecurityMode); - server->maxReq = min_t(unsigned int, - le16_to_cpu(rsp->MaxMpxCount), - cifs_max_pending); - set_credits(server, server->maxReq); - server->maxBuf = le16_to_cpu(rsp->MaxBufSize); - /* set up max_read for readpages check */ - server->max_read = server->maxBuf; - /* even though we do not use raw we might as well set this - accurately, in case we ever find a need for it */ - if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { - server->max_rw = 0xFF00; - server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; - } else { - server->max_rw = 0;/* do not need to use raw anyway */ - server->capabilities = CAP_MPX_MODE; - } - tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone); - if (tmp == -1) { - /* OS/2 often does not set timezone therefore - * we must use server time to calc time zone. - * Could deviate slightly from the right zone. - * Smallest defined timezone difference is 15 minutes - * (i.e. Nepal). Rounding up/down is done to match - * this requirement. - */ - int val, seconds, remain, result; - struct timespec64 ts; - time64_t utc = ktime_get_real_seconds(); - ts = cnvrtDosUnixTm(rsp->SrvTime.Date, - rsp->SrvTime.Time, 0); - cifs_dbg(FYI, "SrvTime %lld sec since 1970 (utc: %lld) diff: %lld\n", - ts.tv_sec, utc, - utc - ts.tv_sec); - val = (int)(utc - ts.tv_sec); - seconds = abs(val); - result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ; - remain = seconds % MIN_TZ_ADJ; - if (remain >= (MIN_TZ_ADJ / 2)) - result += MIN_TZ_ADJ; - if (val < 0) - result = -result; - server->timeAdj = result; - } else { - server->timeAdj = (int)tmp; - server->timeAdj *= 60; /* also in seconds */ - } - cifs_dbg(FYI, "server->timeAdj: %d seconds\n", server->timeAdj); - - - /* BB get server time for time conversions and add - code to use it and timezone since this is not UTC */ - - if (rsp->EncryptionKeyLength == - cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { - memcpy(server->cryptkey, rsp->EncryptionKey, - CIFS_CRYPTO_KEY_SIZE); - } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { - return -EIO; /* need cryptkey unless plain text */ - } - - cifs_dbg(FYI, "LANMAN negotiated\n"); - return 0; -} -#else -static inline int -decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) -{ - cifs_dbg(VFS, "mount failed, cifs module not built with CIFS_WEAK_PW_HASH support\n"); - return -EOPNOTSUPP; -} -#endif - static bool should_set_ext_sec_flag(enum securityEnum sectype) { @@ -626,16 +527,12 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) server->dialect = le16_to_cpu(pSMBr->DialectIndex); cifs_dbg(FYI, "Dialect: %d\n", server->dialect); /* Check wct = 1 error case */ - if ((pSMBr->hdr.WordCount < 13) || (server->dialect == BAD_PROT)) { + if ((pSMBr->hdr.WordCount <= 13) || (server->dialect == BAD_PROT)) { /* core returns wct = 1, but we do not ask for core - otherwise small wct just comes when dialect index is -1 indicating we could not negotiate a common dialect */ rc = -EOPNOTSUPP; goto neg_err_exit; - } else if (pSMBr->hdr.WordCount == 13) { - server->negflavor = CIFS_NEGFLAVOR_LANMAN; - rc = decode_lanman_negprot_rsp(server, pSMBr); - goto signing_check; } else if (pSMBr->hdr.WordCount != 17) { /* unknown wct */ rc = -EOPNOTSUPP; @@ -677,7 +574,6 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) server->capabilities &= ~CAP_EXTENDED_SECURITY; } -signing_check: if (!rc) rc = cifs_enable_signing(server, ses->sign); neg_err_exit: diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 3781eee9360a..0db344807ef1 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3684,38 +3684,6 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, *bcc_ptr = 0; /* password is null byte */ bcc_ptr++; /* skip password */ /* already aligned so no need to do it below */ - } else { - pSMB->PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE); - /* BB FIXME add code to fail this if NTLMv2 or Kerberos - specified as required (when that support is added to - the vfs in the future) as only NTLM or the much - weaker LANMAN (which we do not send by default) is accepted - by Samba (not sure whether other servers allow - NTLMv2 password here) */ -#ifdef CONFIG_CIFS_WEAK_PW_HASH - if ((global_secflags & CIFSSEC_MAY_LANMAN) && - (ses->sectype == LANMAN)) - calc_lanman_hash(tcon->password, ses->server->cryptkey, - ses->server->sec_mode & - SECMODE_PW_ENCRYPT ? true : false, - bcc_ptr); - else -#endif /* CIFS_WEAK_PW_HASH */ - rc = SMBNTencrypt(tcon->password, ses->server->cryptkey, - bcc_ptr, nls_codepage); - if (rc) { - cifs_dbg(FYI, "%s Can't generate NTLM rsp. Error: %d\n", - __func__, rc); - cifs_buf_release(smb_buffer); - return rc; - } - - bcc_ptr += CIFS_AUTH_RESP_SIZE; - if (ses->capabilities & CAP_UNICODE) { - /* must align unicode strings */ - *bcc_ptr = 0; /* null byte password */ - bcc_ptr++; - } } if (ses->server->sign) diff --git a/fs/cifs/fs_context.c b/fs/cifs/fs_context.c index 727c8835b222..3109def8e199 100644 --- a/fs/cifs/fs_context.c +++ b/fs/cifs/fs_context.c @@ -57,12 +57,9 @@ static const match_table_t cifs_secflavor_tokens = { { Opt_sec_krb5p, "krb5p" }, { Opt_sec_ntlmsspi, "ntlmsspi" }, { Opt_sec_ntlmssp, "ntlmssp" }, - { Opt_ntlm, "ntlm" }, - { Opt_sec_ntlmi, "ntlmi" }, { Opt_sec_ntlmv2, "nontlm" }, { Opt_sec_ntlmv2, "ntlmv2" }, { Opt_sec_ntlmv2i, "ntlmv2i" }, - { Opt_sec_lanman, "lanman" }, { Opt_sec_none, "none" }, { Opt_sec_err, NULL } @@ -221,23 +218,12 @@ cifs_parse_security_flavors(struct fs_context *fc, char *value, struct smb3_fs_c case Opt_sec_ntlmssp: ctx->sectype = RawNTLMSSP; break; - case Opt_sec_ntlmi: - ctx->sign = true; - fallthrough; - case Opt_ntlm: - ctx->sectype = NTLM; - break; case Opt_sec_ntlmv2i: ctx->sign = true; fallthrough; case Opt_sec_ntlmv2: ctx->sectype = NTLMv2; break; -#ifdef CONFIG_CIFS_WEAK_PW_HASH - case Opt_sec_lanman: - ctx->sectype = LANMAN; - break; -#endif case Opt_sec_none: ctx->nullauth = 1; break; diff --git a/fs/cifs/fs_context.h b/fs/cifs/fs_context.h index b6243972edf3..a42ba71d7a81 100644 --- a/fs/cifs/fs_context.h +++ b/fs/cifs/fs_context.h @@ -47,11 +47,8 @@ enum cifs_sec_param { Opt_sec_krb5p, Opt_sec_ntlmsspi, Opt_sec_ntlmssp, - Opt_ntlm, - Opt_sec_ntlmi, Opt_sec_ntlmv2, Opt_sec_ntlmv2i, - Opt_sec_lanman, Opt_sec_none, Opt_sec_err diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 606fd7d6cb71..118403fbeda2 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -799,30 +799,16 @@ cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) } case CIFS_NEGFLAVOR_UNENCAP: switch (requested) { - case NTLM: case NTLMv2: return requested; case Unspecified: if (global_secflags & CIFSSEC_MAY_NTLMV2) return NTLMv2; - if (global_secflags & CIFSSEC_MAY_NTLM) - return NTLM; break; default: break; } - fallthrough; /* to attempt LANMAN authentication next */ - case CIFS_NEGFLAVOR_LANMAN: - switch (requested) { - case LANMAN: - return requested; - case Unspecified: - if (global_secflags & CIFSSEC_MAY_LANMAN) - return LANMAN; - fallthrough; - default: - return Unspecified; - } + fallthrough; default: return Unspecified; } @@ -947,230 +933,6 @@ sess_sendreceive(struct sess_data *sess_data) return rc; } -/* - * LANMAN and plaintext are less secure and off by default. - * So we make this explicitly be turned on in kconfig (in the - * build) and turned on at runtime (changed from the default) - * in proc/fs/cifs or via mount parm. Unfortunately this is - * needed for old Win (e.g. Win95), some obscure NAS and OS/2 - */ -#ifdef CONFIG_CIFS_WEAK_PW_HASH -static void -sess_auth_lanman(struct sess_data *sess_data) -{ - int rc = 0; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - char *bcc_ptr; - struct cifs_ses *ses = sess_data->ses; - char lnm_session_key[CIFS_AUTH_RESP_SIZE]; - __u16 bytes_remaining; - - /* lanman 2 style sessionsetup */ - /* wct = 10 */ - rc = sess_alloc_buffer(sess_data, 10); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - bcc_ptr = sess_data->iov[2].iov_base; - (void)cifs_ssetup_hdr(ses, pSMB); - - pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE; - - if (ses->user_name != NULL) { - /* no capabilities flags in old lanman negotiation */ - pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE); - - /* Calculate hash with password and copy into bcc_ptr. - * Encryption Key (stored as in cryptkey) gets used if the - * security mode bit in Negotiate Protocol response states - * to use challenge/response method (i.e. Password bit is 1). - */ - rc = calc_lanman_hash(ses->password, ses->server->cryptkey, - ses->server->sec_mode & SECMODE_PW_ENCRYPT ? - true : false, lnm_session_key); - if (rc) - goto out; - - memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE); - bcc_ptr += CIFS_AUTH_RESP_SIZE; - } else { - pSMB->old_req.PasswordLength = 0; - } - - /* - * can not sign if LANMAN negotiated so no need - * to calculate signing key? but what if server - * changed to do higher than lanman dialect and - * we reconnected would we ever calc signing_key? - */ - - cifs_dbg(FYI, "Negotiating LANMAN setting up strings\n"); - /* Unicode not allowed for LANMAN dialects */ - ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); - - sess_data->iov[2].iov_len = (long) bcc_ptr - - (long) sess_data->iov[2].iov_base; - - rc = sess_sendreceive(sess_data); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - - /* lanman response has a word count of 3 */ - if (smb_buf->WordCount != 3) { - rc = -EIO; - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out; - } - - if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) - cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ - - ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ - cifs_dbg(FYI, "UID = %llu\n", ses->Suid); - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - - /* BB check if Unicode and decode strings */ - if (bytes_remaining == 0) { - /* no string area to decode, do nothing */ - } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { - /* unicode string area must be word-aligned */ - if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) { - ++bcc_ptr; - --bytes_remaining; - } - decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } else { - decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } - - rc = sess_establish_session(sess_data); -out: - sess_data->result = rc; - sess_data->func = NULL; - sess_free_buffer(sess_data); -} - -#endif - -static void -sess_auth_ntlm(struct sess_data *sess_data) -{ - int rc = 0; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - char *bcc_ptr; - struct cifs_ses *ses = sess_data->ses; - __u32 capabilities; - __u16 bytes_remaining; - - /* old style NTLM sessionsetup */ - /* wct = 13 */ - rc = sess_alloc_buffer(sess_data, 13); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - bcc_ptr = sess_data->iov[2].iov_base; - capabilities = cifs_ssetup_hdr(ses, pSMB); - - pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); - if (ses->user_name != NULL) { - pSMB->req_no_secext.CaseInsensitivePasswordLength = - cpu_to_le16(CIFS_AUTH_RESP_SIZE); - pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(CIFS_AUTH_RESP_SIZE); - - /* calculate ntlm response and session key */ - rc = setup_ntlm_response(ses, sess_data->nls_cp); - if (rc) { - cifs_dbg(VFS, "Error %d during NTLM authentication\n", - rc); - goto out; - } - - /* copy ntlm response */ - memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, - CIFS_AUTH_RESP_SIZE); - bcc_ptr += CIFS_AUTH_RESP_SIZE; - memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, - CIFS_AUTH_RESP_SIZE); - bcc_ptr += CIFS_AUTH_RESP_SIZE; - } else { - pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; - pSMB->req_no_secext.CaseSensitivePasswordLength = 0; - } - - if (ses->capabilities & CAP_UNICODE) { - /* unicode strings must be word aligned */ - if (sess_data->iov[0].iov_len % 2) { - *bcc_ptr = 0; - bcc_ptr++; - } - unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); - } else { - ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); - } - - - sess_data->iov[2].iov_len = (long) bcc_ptr - - (long) sess_data->iov[2].iov_base; - - rc = sess_sendreceive(sess_data); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - - if (smb_buf->WordCount != 3) { - rc = -EIO; - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out; - } - - if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) - cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ - - ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ - cifs_dbg(FYI, "UID = %llu\n", ses->Suid); - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - - /* BB check if Unicode and decode strings */ - if (bytes_remaining == 0) { - /* no string area to decode, do nothing */ - } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { - /* unicode string area must be word-aligned */ - if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) { - ++bcc_ptr; - --bytes_remaining; - } - decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } else { - decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } - - rc = sess_establish_session(sess_data); -out: - sess_data->result = rc; - sess_data->func = NULL; - sess_free_buffer(sess_data); - kfree(ses->auth_key.response); - ses->auth_key.response = NULL; -} - static void sess_auth_ntlmv2(struct sess_data *sess_data) { @@ -1675,21 +1437,6 @@ static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data) } switch (type) { - case LANMAN: - /* LANMAN and plaintext are less secure and off by default. - * So we make this explicitly be turned on in kconfig (in the - * build) and turned on at runtime (changed from the default) - * in proc/fs/cifs or via mount parm. Unfortunately this is - * needed for old Win (e.g. Win95), some obscure NAS and OS/2 */ -#ifdef CONFIG_CIFS_WEAK_PW_HASH - sess_data->func = sess_auth_lanman; - break; -#else - return -EOPNOTSUPP; -#endif - case NTLM: - sess_data->func = sess_auth_ntlm; - break; case NTLMv2: sess_data->func = sess_auth_ntlmv2; break; diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c index 39a938443e3e..5da7eea3323f 100644 --- a/fs/cifs/smbencrypt.c +++ b/fs/cifs/smbencrypt.c @@ -18,7 +18,6 @@ #include #include #include -#include #include "cifs_fs_sb.h" #include "cifs_unicode.h" #include "cifspdu.h" @@ -38,74 +37,8 @@ #define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8) #define SSVAL(buf,pos,val) SSVALX((buf),(pos),((__u16)(val))) -static void -str_to_key(unsigned char *str, unsigned char *key) -{ - int i; - - key[0] = str[0] >> 1; - key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2); - key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3); - key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4); - key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5); - key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6); - key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7); - key[7] = str[6] & 0x7F; - for (i = 0; i < 8; i++) - key[i] = (key[i] << 1); -} - -static int -smbhash(unsigned char *out, const unsigned char *in, unsigned char *key) -{ - unsigned char key2[8]; - struct des_ctx ctx; - - str_to_key(key, key2); - - if (fips_enabled) { - cifs_dbg(VFS, "FIPS compliance enabled: DES not permitted\n"); - return -ENOENT; - } - - des_expand_key(&ctx, key2, DES_KEY_SIZE); - des_encrypt(&ctx, out, in); - memzero_explicit(&ctx, sizeof(ctx)); - - return 0; -} - -static int -E_P16(unsigned char *p14, unsigned char *p16) -{ - int rc; - unsigned char sp8[8] = - { 0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 }; - - rc = smbhash(p16, sp8, p14); - if (rc) - return rc; - rc = smbhash(p16 + 8, sp8, p14 + 7); - return rc; -} - -static int -E_P24(unsigned char *p21, const unsigned char *c8, unsigned char *p24) -{ - int rc; - - rc = smbhash(p24, c8, p21); - if (rc) - return rc; - rc = smbhash(p24 + 8, c8, p21 + 7); - if (rc) - return rc; - rc = smbhash(p24 + 16, c8, p21 + 14); - return rc; -} - /* produce a md4 message digest from data of length n bytes */ -int +static int mdfour(unsigned char *md4_hash, unsigned char *link_str, int link_len) { int rc; @@ -135,32 +68,6 @@ mdfour_err: return rc; } -/* - This implements the X/Open SMB password encryption - It takes a password, a 8 byte "crypt key" and puts 24 bytes of - encrypted password into p24 */ -/* Note that password must be uppercased and null terminated */ -int -SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24) -{ - int rc; - unsigned char p14[14], p16[16], p21[21]; - - memset(p14, '\0', 14); - memset(p16, '\0', 16); - memset(p21, '\0', 21); - - memcpy(p14, passwd, 14); - rc = E_P16(p14, p16); - if (rc) - return rc; - - memcpy(p21, p16, 16); - rc = E_P24(p21, c8, p24); - - return rc; -} - /* * Creates the MD4 Hash of the users password in NT UNICODE. */ @@ -186,25 +93,3 @@ E_md4hash(const unsigned char *passwd, unsigned char *p16, return rc; } - -/* Does the NT MD4 hash then des encryption. */ -int -SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24, - const struct nls_table *codepage) -{ - int rc; - unsigned char p16[16], p21[21]; - - memset(p16, '\0', 16); - memset(p21, '\0', 21); - - rc = E_md4hash(passwd, p16, codepage); - if (rc) { - cifs_dbg(FYI, "%s Can't generate NT hash, error: %d\n", - __func__, rc); - return rc; - } - memcpy(p21, p16, 16); - rc = E_P24(p21, c8, p24); - return rc; -} From 71c02863246167b3d1639b8278681ca8ebedcb4e Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 19 Aug 2021 20:34:59 +1000 Subject: [PATCH 408/417] cifs: fork arc4 and create a separate module for it for cifs and other users We can not drop ARC4 and basically destroy CIFS connectivity for almost all CIFS users so create a new forked ARC4 module that CIFS and other subsystems that have a hard dependency on ARC4 can use. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/Kconfig | 7 +++ fs/Makefile | 1 + fs/cifs/Kconfig | 1 - fs/cifs/cifsencrypt.c | 8 ++-- fs/cifs_common/Makefile | 6 +++ fs/cifs_common/arc4.h | 23 ++++++++++ fs/cifs_common/cifs_arc4.c | 87 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 fs/cifs_common/Makefile create mode 100644 fs/cifs_common/arc4.h create mode 100644 fs/cifs_common/cifs_arc4.c diff --git a/fs/Kconfig b/fs/Kconfig index a7749c126b8e..6d719f2c5828 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -358,7 +358,14 @@ config NFS_V4_2_SSC_HELPER source "net/sunrpc/Kconfig" source "fs/ceph/Kconfig" + source "fs/cifs/Kconfig" + +config CIFS_COMMON + tristate + default y if CIFS=y + default m if CIFS=m + source "fs/coda/Kconfig" source "fs/afs/Kconfig" source "fs/9p/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index f98f3e691c37..77b0e79c8b4f 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -96,6 +96,7 @@ obj-$(CONFIG_LOCKD) += lockd/ obj-$(CONFIG_NLS) += nls/ obj-$(CONFIG_UNICODE) += unicode/ obj-$(CONFIG_SYSV_FS) += sysv/ +obj-$(CONFIG_CIFS_COMMON) += cifs_common/ obj-$(CONFIG_CIFS) += cifs/ obj-$(CONFIG_HPFS_FS) += hpfs/ obj-$(CONFIG_NTFS_FS) += ntfs/ diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 2e8b132efdbc..aa4457d72392 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -10,7 +10,6 @@ config CIFS select CRYPTO_SHA512 select CRYPTO_CMAC select CRYPTO_HMAC - select CRYPTO_LIB_ARC4 select CRYPTO_AEAD2 select CRYPTO_CCM select CRYPTO_GCM diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 7680e0a9bea3..6679e07e533e 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include "../cifs_common/arc4.h" #include int __cifs_calc_signature(struct smb_rqst *rqst, @@ -699,9 +699,9 @@ calc_seckey(struct cifs_ses *ses) return -ENOMEM; } - arc4_setkey(ctx_arc4, ses->auth_key.response, CIFS_SESS_KEY_SIZE); - arc4_crypt(ctx_arc4, ses->ntlmssp->ciphertext, sec_key, - CIFS_CPHTXT_SIZE); + cifs_arc4_setkey(ctx_arc4, ses->auth_key.response, CIFS_SESS_KEY_SIZE); + cifs_arc4_crypt(ctx_arc4, ses->ntlmssp->ciphertext, sec_key, + CIFS_CPHTXT_SIZE); /* make secondary_key/nonce as session key */ memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE); diff --git a/fs/cifs_common/Makefile b/fs/cifs_common/Makefile new file mode 100644 index 000000000000..2fc9b40345c4 --- /dev/null +++ b/fs/cifs_common/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for Linux filesystem routines that are shared by client and server. +# + +obj-$(CONFIG_CIFS_COMMON) += cifs_arc4.o diff --git a/fs/cifs_common/arc4.h b/fs/cifs_common/arc4.h new file mode 100644 index 000000000000..12e71ec033a1 --- /dev/null +++ b/fs/cifs_common/arc4.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Common values for ARC4 Cipher Algorithm + */ + +#ifndef _CRYPTO_ARC4_H +#define _CRYPTO_ARC4_H + +#include + +#define ARC4_MIN_KEY_SIZE 1 +#define ARC4_MAX_KEY_SIZE 256 +#define ARC4_BLOCK_SIZE 1 + +struct arc4_ctx { + u32 S[256]; + u32 x, y; +}; + +int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len); +void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len); + +#endif /* _CRYPTO_ARC4_H */ diff --git a/fs/cifs_common/cifs_arc4.c b/fs/cifs_common/cifs_arc4.c new file mode 100644 index 000000000000..b964cc682944 --- /dev/null +++ b/fs/cifs_common/cifs_arc4.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Cryptographic API + * + * ARC4 Cipher Algorithm + * + * Jon Oberheide + */ + +#include +#include "arc4.h" + +MODULE_LICENSE("GPL"); + +int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len) +{ + int i, j = 0, k = 0; + + ctx->x = 1; + ctx->y = 0; + + for (i = 0; i < 256; i++) + ctx->S[i] = i; + + for (i = 0; i < 256; i++) { + u32 a = ctx->S[i]; + + j = (j + in_key[k] + a) & 0xff; + ctx->S[i] = ctx->S[j]; + ctx->S[j] = a; + if (++k >= key_len) + k = 0; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_arc4_setkey); + +void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len) +{ + u32 *const S = ctx->S; + u32 x, y, a, b; + u32 ty, ta, tb; + + if (len == 0) + return; + + x = ctx->x; + y = ctx->y; + + a = S[x]; + y = (y + a) & 0xff; + b = S[y]; + + do { + S[y] = a; + a = (a + b) & 0xff; + S[x] = b; + x = (x + 1) & 0xff; + ta = S[x]; + ty = (y + ta) & 0xff; + tb = S[ty]; + *out++ = *in++ ^ S[a]; + if (--len == 0) + break; + y = ty; + a = ta; + b = tb; + } while (true); + + ctx->x = x; + ctx->y = y; +} +EXPORT_SYMBOL_GPL(cifs_arc4_crypt); + +static int __init +init_cifs_common(void) +{ + return 0; +} +static void __init +exit_cifs_common(void) +{ +} + +module_init(init_cifs_common) +module_exit(exit_cifs_common) From 42c21973fa3c0f4898330fa30d327fbab67b5460 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Fri, 20 Aug 2021 09:32:56 +1000 Subject: [PATCH 409/417] cifs: create a MD4 module and switch cifs.ko to use it MD4 support will likely be removed from the crypto directory, but is needed for compression of NTLMSSP in SMB3 mounts. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/Kconfig | 1 - fs/cifs/cifsfs.c | 1 - fs/cifs/smbencrypt.c | 22 ++--- fs/cifs_common/Makefile | 1 + fs/cifs_common/cifs_md4.c | 201 ++++++++++++++++++++++++++++++++++++++ fs/cifs_common/md4.h | 27 +++++ 6 files changed, 238 insertions(+), 15 deletions(-) create mode 100644 fs/cifs_common/cifs_md4.c create mode 100644 fs/cifs_common/md4.h diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index aa4457d72392..3b7e3b9e4fd2 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -4,7 +4,6 @@ config CIFS depends on INET select NLS select CRYPTO - select CRYPTO_MD4 select CRYPTO_MD5 select CRYPTO_SHA256 select CRYPTO_SHA512 diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index b1b175f229ee..8c20bfa187ac 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -1748,7 +1748,6 @@ MODULE_DESCRIPTION MODULE_VERSION(CIFS_VERSION); MODULE_SOFTDEP("ecb"); MODULE_SOFTDEP("hmac"); -MODULE_SOFTDEP("md4"); MODULE_SOFTDEP("md5"); MODULE_SOFTDEP("nls"); MODULE_SOFTDEP("aes"); diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c index 5da7eea3323f..10047cc55286 100644 --- a/fs/cifs/smbencrypt.c +++ b/fs/cifs/smbencrypt.c @@ -24,6 +24,7 @@ #include "cifsglob.h" #include "cifs_debug.h" #include "cifsproto.h" +#include "../cifs_common/md4.h" #ifndef false #define false 0 @@ -42,29 +43,24 @@ static int mdfour(unsigned char *md4_hash, unsigned char *link_str, int link_len) { int rc; - struct crypto_shash *md4 = NULL; - struct sdesc *sdescmd4 = NULL; + struct md4_ctx mctx; - rc = cifs_alloc_hash("md4", &md4, &sdescmd4); - if (rc) - goto mdfour_err; - - rc = crypto_shash_init(&sdescmd4->shash); + rc = cifs_md4_init(&mctx); if (rc) { - cifs_dbg(VFS, "%s: Could not init md4 shash\n", __func__); + cifs_dbg(VFS, "%s: Could not init MD4\n", __func__); goto mdfour_err; } - rc = crypto_shash_update(&sdescmd4->shash, link_str, link_len); + rc = cifs_md4_update(&mctx, link_str, link_len); if (rc) { - cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__); + cifs_dbg(VFS, "%s: Could not update MD4\n", __func__); goto mdfour_err; } - rc = crypto_shash_final(&sdescmd4->shash, md4_hash); + rc = cifs_md4_final(&mctx, md4_hash); if (rc) - cifs_dbg(VFS, "%s: Could not generate md4 hash\n", __func__); + cifs_dbg(VFS, "%s: Could not finalize MD4\n", __func__); + mdfour_err: - cifs_free_hash(&md4, &sdescmd4); return rc; } diff --git a/fs/cifs_common/Makefile b/fs/cifs_common/Makefile index 2fc9b40345c4..6fedd2f88a25 100644 --- a/fs/cifs_common/Makefile +++ b/fs/cifs_common/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_CIFS_COMMON) += cifs_arc4.o +obj-$(CONFIG_CIFS_COMMON) += cifs_md4.o diff --git a/fs/cifs_common/cifs_md4.c b/fs/cifs_common/cifs_md4.c new file mode 100644 index 000000000000..dbf9113b8600 --- /dev/null +++ b/fs/cifs_common/cifs_md4.c @@ -0,0 +1,201 @@ +/* + * Cryptographic API. + * + * MD4 Message Digest Algorithm (RFC1320). + * + * Implementation derived from Andrew Tridgell and Steve French's + * CIFS MD4 implementation, and the cryptoapi implementation + * originally based on the public domain implementation written + * by Colin Plumb in 1993. + * + * Copyright (c) Andrew Tridgell 1997-1998. + * Modified by Steve French (sfrench@us.ibm.com) 2002 + * Copyright (c) Cryptoapi developers. + * Copyright (c) 2002 David S. Miller (davem@redhat.com) + * Copyright (c) 2002 James Morris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include "md4.h" + +MODULE_LICENSE("GPL"); + +static inline u32 lshift(u32 x, unsigned int s) +{ + x &= 0xFFFFFFFF; + return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s)); +} + +static inline u32 F(u32 x, u32 y, u32 z) +{ + return (x & y) | ((~x) & z); +} + +static inline u32 G(u32 x, u32 y, u32 z) +{ + return (x & y) | (x & z) | (y & z); +} + +static inline u32 H(u32 x, u32 y, u32 z) +{ + return x ^ y ^ z; +} + +#define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s)) +#define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (u32)0x5A827999,s)) +#define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (u32)0x6ED9EBA1,s)) + +static void md4_transform(u32 *hash, u32 const *in) +{ + u32 a, b, c, d; + + a = hash[0]; + b = hash[1]; + c = hash[2]; + d = hash[3]; + + ROUND1(a, b, c, d, in[0], 3); + ROUND1(d, a, b, c, in[1], 7); + ROUND1(c, d, a, b, in[2], 11); + ROUND1(b, c, d, a, in[3], 19); + ROUND1(a, b, c, d, in[4], 3); + ROUND1(d, a, b, c, in[5], 7); + ROUND1(c, d, a, b, in[6], 11); + ROUND1(b, c, d, a, in[7], 19); + ROUND1(a, b, c, d, in[8], 3); + ROUND1(d, a, b, c, in[9], 7); + ROUND1(c, d, a, b, in[10], 11); + ROUND1(b, c, d, a, in[11], 19); + ROUND1(a, b, c, d, in[12], 3); + ROUND1(d, a, b, c, in[13], 7); + ROUND1(c, d, a, b, in[14], 11); + ROUND1(b, c, d, a, in[15], 19); + + ROUND2(a, b, c, d, in[0], 3); + ROUND2(d, a, b, c, in[4], 5); + ROUND2(c, d, a, b, in[8], 9); + ROUND2(b, c, d, a, in[12], 13); + ROUND2(a, b, c, d, in[1], 3); + ROUND2(d, a, b, c, in[5], 5); + ROUND2(c, d, a, b, in[9], 9); + ROUND2(b, c, d, a, in[13], 13); + ROUND2(a, b, c, d, in[2], 3); + ROUND2(d, a, b, c, in[6], 5); + ROUND2(c, d, a, b, in[10], 9); + ROUND2(b, c, d, a, in[14], 13); + ROUND2(a, b, c, d, in[3], 3); + ROUND2(d, a, b, c, in[7], 5); + ROUND2(c, d, a, b, in[11], 9); + ROUND2(b, c, d, a, in[15], 13); + + ROUND3(a, b, c, d, in[0], 3); + ROUND3(d, a, b, c, in[8], 9); + ROUND3(c, d, a, b, in[4], 11); + ROUND3(b, c, d, a, in[12], 15); + ROUND3(a, b, c, d, in[2], 3); + ROUND3(d, a, b, c, in[10], 9); + ROUND3(c, d, a, b, in[6], 11); + ROUND3(b, c, d, a, in[14], 15); + ROUND3(a, b, c, d, in[1], 3); + ROUND3(d, a, b, c, in[9], 9); + ROUND3(c, d, a, b, in[5], 11); + ROUND3(b, c, d, a, in[13], 15); + ROUND3(a, b, c, d, in[3], 3); + ROUND3(d, a, b, c, in[11], 9); + ROUND3(c, d, a, b, in[7], 11); + ROUND3(b, c, d, a, in[15], 15); + + hash[0] += a; + hash[1] += b; + hash[2] += c; + hash[3] += d; +} + +static inline void md4_transform_helper(struct md4_ctx *ctx) +{ + le32_to_cpu_array(ctx->block, ARRAY_SIZE(ctx->block)); + md4_transform(ctx->hash, ctx->block); +} + +int cifs_md4_init(struct md4_ctx *mctx) +{ + memset(mctx, 0, sizeof(struct md4_ctx)); + mctx->hash[0] = 0x67452301; + mctx->hash[1] = 0xefcdab89; + mctx->hash[2] = 0x98badcfe; + mctx->hash[3] = 0x10325476; + mctx->byte_count = 0; + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_md4_init); + +int cifs_md4_update(struct md4_ctx *mctx, const u8 *data, unsigned int len) +{ + const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f); + + mctx->byte_count += len; + + if (avail > len) { + memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), + data, len); + return 0; + } + + memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), + data, avail); + + md4_transform_helper(mctx); + data += avail; + len -= avail; + + while (len >= sizeof(mctx->block)) { + memcpy(mctx->block, data, sizeof(mctx->block)); + md4_transform_helper(mctx); + data += sizeof(mctx->block); + len -= sizeof(mctx->block); + } + + memcpy(mctx->block, data, len); + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_md4_update); + +int cifs_md4_final(struct md4_ctx *mctx, u8 *out) +{ + const unsigned int offset = mctx->byte_count & 0x3f; + char *p = (char *)mctx->block + offset; + int padding = 56 - (offset + 1); + + *p++ = 0x80; + if (padding < 0) { + memset(p, 0x00, padding + sizeof(u64)); + md4_transform_helper(mctx); + p = (char *)mctx->block; + padding = 56; + } + + memset(p, 0, padding); + mctx->block[14] = mctx->byte_count << 3; + mctx->block[15] = mctx->byte_count >> 29; + le32_to_cpu_array(mctx->block, (sizeof(mctx->block) - + sizeof(u64)) / sizeof(u32)); + md4_transform(mctx->hash, mctx->block); + cpu_to_le32_array(mctx->hash, ARRAY_SIZE(mctx->hash)); + memcpy(out, mctx->hash, sizeof(mctx->hash)); + memset(mctx, 0, sizeof(*mctx)); + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_md4_final); diff --git a/fs/cifs_common/md4.h b/fs/cifs_common/md4.h new file mode 100644 index 000000000000..5337becc699a --- /dev/null +++ b/fs/cifs_common/md4.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Common values for ARC4 Cipher Algorithm + */ + +#ifndef _CIFS_MD4_H +#define _CIFS_MD4_H + +#include + +#define MD4_DIGEST_SIZE 16 +#define MD4_HMAC_BLOCK_SIZE 64 +#define MD4_BLOCK_WORDS 16 +#define MD4_HASH_WORDS 4 + +struct md4_ctx { + u32 hash[MD4_HASH_WORDS]; + u32 block[MD4_BLOCK_WORDS]; + u64 byte_count; +}; + + +int cifs_md4_init(struct md4_ctx *mctx); +int cifs_md4_update(struct md4_ctx *mctx, const u8 *data, unsigned int len); +int cifs_md4_final(struct md4_ctx *mctx, u8 *out); + +#endif /* _CIFS_MD4_H */ From 38f4910b8b26d3a940167f207bddfcc589310c8a Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 24 Aug 2021 12:07:46 -0500 Subject: [PATCH 410/417] cifs: cifs_md4 convert to SPDX identifier Add SPDX license identifier and replace license boilerplate for cifs_md4.c Signed-off-by: Steve French --- fs/cifs/smb2maperror.c | 1 - fs/cifs_common/cifs_md4.c | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c index cea39bcecbab..181514b8770d 100644 --- a/fs/cifs/smb2maperror.c +++ b/fs/cifs/smb2maperror.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LGPL-2.1 /* - * fs/smb2/smb2maperror.c * * Functions which do error mapping of SMB2 status codes to POSIX errors * diff --git a/fs/cifs_common/cifs_md4.c b/fs/cifs_common/cifs_md4.c index dbf9113b8600..50f78cfc6ce9 100644 --- a/fs/cifs_common/cifs_md4.c +++ b/fs/cifs_common/cifs_md4.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Cryptographic API. * @@ -14,11 +15,6 @@ * Copyright (c) 2002 David S. Miller (davem@redhat.com) * Copyright (c) 2002 James Morris * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * */ #include #include From 332c404a55ef3b39837e958284275622a2a4849d Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 24 Aug 2021 16:14:53 -0500 Subject: [PATCH 411/417] cifs: add cifs_common directory to MAINTAINERS file With some files moving into the cifs_common directory, we need to add it to the CIFS entry in the MAINTAINERS file. Suggested-by: Paulo Alcantara (SUSE) Signed-off-by: Steve French --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index c6b8a720c0bc..c1d80232f949 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4629,6 +4629,7 @@ W: http://linux-cifs.samba.org/ T: git git://git.samba.org/sfrench/cifs-2.6.git F: Documentation/admin-guide/cifs/ F: fs/cifs/ +F: fs/cifs_common/ COMPACTPCI HOTPLUG CORE M: Scott Murray From 3998f0b8bc49ec784990971dc1f16bf367b19078 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 25 Aug 2021 21:16:56 +1000 Subject: [PATCH 412/417] cifs: Do not leak EDEADLK to dgetents64 for STATUS_USER_SESSION_DELETED RHBZ: 1994393 If we hit a STATUS_USER_SESSION_DELETED for the Create part in the Create/QueryDirectory compound that starts a directory scan we will leak EDEADLK back to userspace and surprise glibc and the application. Pick this up initiate_cifs_search() and retry a small number of tries before we return an error to userspace. Cc: stable@vger.kernel.org Reported-by: Xiaoli Feng Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/readdir.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index bfee176b901d..54d77c99e21c 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -369,7 +369,7 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, */ static int -initiate_cifs_search(const unsigned int xid, struct file *file, +_initiate_cifs_search(const unsigned int xid, struct file *file, const char *full_path) { __u16 search_flags; @@ -451,6 +451,27 @@ error_exit: return rc; } +static int +initiate_cifs_search(const unsigned int xid, struct file *file, + const char *full_path) +{ + int rc, retry_count = 0; + + do { + rc = _initiate_cifs_search(xid, file, full_path); + /* + * If we don't have enough credits to start reading the + * directory just try again after short wait. + */ + if (rc != -EDEADLK) + break; + + usleep_range(512, 2048); + } while (retry_count++ < 5); + + return rc; +} + /* return length of unicode string in bytes */ static int cifs_unicode_bytelen(const char *str) { From f657f8eef3ff870552c9fd2839e0061046f44618 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 20 Aug 2021 17:02:04 -0400 Subject: [PATCH 413/417] nfs: don't atempt blocking locks on nfs reexports NFS implements blocking locks by blocking inside its lock method. In the reexport case, this blocks the nfs server thread, which could lead to deadlocks since an nfs server thread might be required to unlock the conflicting lock. It also causes a crash, since the nfs server thread assumes it can free the lock when its lm_notify lock callback is called. Ideal would be to make the nfs lock method return without blocking in this case, but for now it works just not to attempt blocking locks. The difference is just that the original client will have to poll (as it does in the v4.0 case) instead of getting a callback when the lock's available. Signed-off-by: J. Bruce Fields Acked-by: Anna Schumaker Signed-off-by: Chuck Lever --- fs/nfs/export.c | 2 +- fs/nfsd/nfs4state.c | 8 ++++++-- include/linux/exportfs.h | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/nfs/export.c b/fs/nfs/export.c index 37a1a88df771..d772c20bbfd1 100644 --- a/fs/nfs/export.c +++ b/fs/nfs/export.c @@ -180,5 +180,5 @@ const struct export_operations nfs_export_ops = { .fetch_iversion = nfs_fetch_iversion, .flags = EXPORT_OP_NOWCC|EXPORT_OP_NOSUBTREECHK| EXPORT_OP_CLOSE_BEFORE_UNLINK|EXPORT_OP_REMOTE_FS| - EXPORT_OP_NOATOMIC_ATTR, + EXPORT_OP_NOATOMIC_ATTR|EXPORT_OP_SYNC_LOCKS, }; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 2bedc7839ec5..d0b2041c4d75 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -6835,6 +6835,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_blocked_lock *nbl = NULL; struct file_lock *file_lock = NULL; struct file_lock *conflock = NULL; + struct super_block *sb; __be32 status = 0; int lkflg; int err; @@ -6856,6 +6857,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, dprintk("NFSD: nfsd4_lock: permission denied!\n"); return status; } + sb = cstate->current_fh.fh_dentry->d_sb; if (lock->lk_is_new) { if (nfsd4_has_session(cstate)) @@ -6904,7 +6906,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, fp = lock_stp->st_stid.sc_file; switch (lock->lk_type) { case NFS4_READW_LT: - if (nfsd4_has_session(cstate)) + if (nfsd4_has_session(cstate) && + !(sb->s_export_op->flags & EXPORT_OP_SYNC_LOCKS)) fl_flags |= FL_SLEEP; fallthrough; case NFS4_READ_LT: @@ -6916,7 +6919,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, fl_type = F_RDLCK; break; case NFS4_WRITEW_LT: - if (nfsd4_has_session(cstate)) + if (nfsd4_has_session(cstate) && + !(sb->s_export_op->flags & EXPORT_OP_SYNC_LOCKS)) fl_flags |= FL_SLEEP; fallthrough; case NFS4_WRITE_LT: diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h index fe848901fcc3..3260fe714846 100644 --- a/include/linux/exportfs.h +++ b/include/linux/exportfs.h @@ -221,6 +221,8 @@ struct export_operations { #define EXPORT_OP_NOATOMIC_ATTR (0x10) /* Filesystem cannot supply atomic attribute updates */ +#define EXPORT_OP_SYNC_LOCKS (0x20) /* Filesystem can't do + asychronous blocking locks */ unsigned long flags; }; From b840be2f00c0bc00d993f8f76e251052b83e4382 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 20 Aug 2021 17:02:05 -0400 Subject: [PATCH 414/417] lockd: don't attempt blocking locks on nfs reexports As in the v4 case, it doesn't work well to block waiting for a lock on an nfs filesystem. As in the v4 case, that means we're depending on the client to poll. It's probably incorrect to depend on that, but I *think* clients do poll in practice. In any case, it's an improvement over hanging the lockd thread indefinitely as we currently are. Signed-off-by: J. Bruce Fields Acked-by: Anna Schumaker Signed-off-by: Chuck Lever --- fs/lockd/svclock.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index a7b4c51667ad..e9b85d8fd5fe 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -31,6 +31,7 @@ #include #include #include +#include #define NLMDBG_FACILITY NLMDBG_SVCLOCK @@ -470,18 +471,24 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_cookie *cookie, int reclaim) { struct nlm_block *block = NULL; + struct inode *inode = nlmsvc_file_inode(file); int error; int mode; + int async_block = 0; __be32 ret; dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n", - nlmsvc_file_inode(file)->i_sb->s_id, - nlmsvc_file_inode(file)->i_ino, + inode->i_sb->s_id, inode->i_ino, lock->fl.fl_type, lock->fl.fl_pid, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end, wait); + if (inode->i_sb->s_export_op->flags & EXPORT_OP_SYNC_LOCKS) { + async_block = wait; + wait = 0; + } + /* Lock file against concurrent access */ mutex_lock(&file->f_mutex); /* Get existing block (in case client is busy-waiting) @@ -542,7 +549,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, */ if (wait) break; - ret = nlm_lck_denied; + ret = async_block ? nlm_lck_blocked : nlm_lck_denied; goto out; case FILE_LOCK_DEFERRED: if (wait) From bb0a55bb7148a49e549ee992200860e7a040d3a5 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 20 Aug 2021 17:02:06 -0400 Subject: [PATCH 415/417] nfs: don't allow reexport reclaims In the reexport case, nfsd is currently passing along locks with the reclaim bit set. The client sends a new lock request, which is granted if there's currently no conflict--even if it's possible a conflicting lock could have been briefly held in the interim. We don't currently have any way to safely grant reclaim, so for now let's just deny them all. I'm doing this by passing the reclaim bit to nfs and letting it fail the call, with the idea that eventually the client might be able to do something more forgiving here. Signed-off-by: J. Bruce Fields Acked-by: Anna Schumaker Signed-off-by: Chuck Lever --- fs/nfs/file.c | 3 +++ fs/nfsd/nfs4state.c | 3 +++ fs/nfsd/nfsproc.c | 1 + include/linux/errno.h | 1 + include/linux/fs.h | 1 + 5 files changed, 9 insertions(+) diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 1fef107961bc..7411658f8b05 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -806,6 +806,9 @@ int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) nfs_inc_stats(inode, NFSIOS_VFSLOCK); + if (fl->fl_flags & FL_RECLAIM) + return -ENOGRACE; + /* No mandatory locks over NFS */ if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) goto out_err; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d0b2041c4d75..1b6a7f48982e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -6903,6 +6903,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!locks_in_grace(net) && lock->lk_reclaim) goto out; + if (lock->lk_reclaim) + fl_flags |= FL_RECLAIM; + fp = lock_stp->st_stid.sc_file; switch (lock->lk_type) { case NFS4_READW_LT: diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 60d7c59e7935..90fcd6178823 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -881,6 +881,7 @@ nfserrno (int errno) { nfserr_serverfault, -ENFILE }, { nfserr_io, -EUCLEAN }, { nfserr_perm, -ENOKEY }, + { nfserr_no_grace, -ENOGRACE}, }; int i; diff --git a/include/linux/errno.h b/include/linux/errno.h index d73f597a2484..8b0c754bab02 100644 --- a/include/linux/errno.h +++ b/include/linux/errno.h @@ -31,5 +31,6 @@ #define EJUKEBOX 528 /* Request initiated, but will not complete before timeout */ #define EIOCBQUEUED 529 /* iocb queued, will get completion event */ #define ERECALLCONFLICT 530 /* conflict with recalled state */ +#define ENOGRACE 531 /* NFS file lock reclaim refused */ #endif diff --git a/include/linux/fs.h b/include/linux/fs.h index 640574294216..1f5c3dbce1da 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -997,6 +997,7 @@ static inline struct file *get_file(struct file *f) #define FL_UNLOCK_PENDING 512 /* Lease is being broken */ #define FL_OFDLCK 1024 /* lock is "owned" by struct file */ #define FL_LAYOUT 2048 /* outstanding pNFS layout */ +#define FL_RECLAIM 4096 /* reclaiming from a reboot server */ #define FL_CLOSE_POSIX (FL_POSIX | FL_CLOSE) From 0bcc7ca40bd823193224e9f38bafbd8325aaf566 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 24 Aug 2021 22:36:03 -0400 Subject: [PATCH 416/417] nfsd: fix crash on LOCKT on reexported NFSv3 Unlike other filesystems, NFSv3 tries to use fl_file in the GETLK case. Signed-off-by: J. Bruce Fields Signed-off-by: Chuck Lever --- fs/nfsd/nfs4state.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 1b6a7f48982e..4b6d60b46b0a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -7043,8 +7043,7 @@ out: /* * The NFSv4 spec allows a client to do a LOCKT without holding an OPEN, * so we do a temporary open here just to get an open file to pass to - * vfs_test_lock. (Arguably perhaps test_lock should be done with an - * inode operation.) + * vfs_test_lock. */ static __be32 nfsd_test_lock(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file_lock *lock) { @@ -7059,7 +7058,9 @@ static __be32 nfsd_test_lock(struct svc_rqst *rqstp, struct svc_fh *fhp, struct NFSD_MAY_READ)); if (err) goto out; + lock->fl_file = nf->nf_file; err = nfserrno(vfs_test_lock(nf->nf_file, lock)); + lock->fl_file = NULL; out: fh_unlock(fhp); nfsd_file_put(nf); From 7d5d8d7156892f82cf40b63228ce788248cc57a3 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 27 Aug 2021 10:18:05 +0900 Subject: [PATCH 417/417] ksmbd: fix __write_overflow warning in ndr_read_string Dan reported __write_overflow warning in ndr_read_string. CC [M] fs/ksmbd/ndr.o In file included from ./include/linux/string.h:253, from ./include/linux/bitmap.h:11, from ./include/linux/cpumask.h:12, from ./arch/x86/include/asm/cpumask.h:5, from ./arch/x86/include/asm/msr.h:11, from ./arch/x86/include/asm/processor.h:22, from ./arch/x86/include/asm/cpufeature.h:5, from ./arch/x86/include/asm/thread_info.h:53, from ./include/linux/thread_info.h:60, from ./arch/x86/include/asm/preempt.h:7, from ./include/linux/preempt.h:78, from ./include/linux/spinlock.h:55, from ./include/linux/wait.h:9, from ./include/linux/wait_bit.h:8, from ./include/linux/fs.h:6, from fs/ksmbd/ndr.c:7: In function memcpy, inlined from ndr_read_string at fs/ksmbd/ndr.c:86:2, inlined from ndr_decode_dos_attr at fs/ksmbd/ndr.c:167:2: ./include/linux/fortify-string.h:219:4: error: call to __write_overflow declared with attribute error: detected write beyond size of object __write_overflow(); ^~~~~~~~~~~~~~~~~~ This seems to be a false alarm because hex_attr size is always smaller than n->length. This patch fix this warning by allocation hex_attr with n->length. Reported-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/ksmbd/ndr.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/ksmbd/ndr.c b/fs/ksmbd/ndr.c index df23dfbaf657..2243a2c64b37 100644 --- a/fs/ksmbd/ndr.c +++ b/fs/ksmbd/ndr.c @@ -160,11 +160,16 @@ int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) { - char hex_attr[12] = {0}; + char *hex_attr; int version2; + hex_attr = kzalloc(n->length, GFP_KERNEL); + if (!hex_attr) + return -ENOMEM; + n->offset = 0; - ndr_read_string(n, hex_attr, n->length - n->offset); + ndr_read_string(n, hex_attr, n->length); + kfree(hex_attr); da->version = ndr_read_int16(n); if (da->version != 3 && da->version != 4) {