summaryrefslogtreecommitdiff
path: root/fs/smb/client
diff options
context:
space:
mode:
Diffstat (limited to 'fs/smb/client')
-rw-r--r--fs/smb/client/.gitignore3
-rw-r--r--fs/smb/client/Kconfig18
-rw-r--r--fs/smb/client/Makefile34
-rw-r--r--fs/smb/client/cached_dir.c13
-rw-r--r--fs/smb/client/cached_dir.h2
-rw-r--r--fs/smb/client/cifs_debug.c125
-rw-r--r--fs/smb/client/cifs_fs_sb.h1
-rw-r--r--fs/smb/client/cifs_spnego.c16
-rw-r--r--fs/smb/client/cifs_swn.c320
-rw-r--r--fs/smb/client/cifs_unicode.c1
-rw-r--r--fs/smb/client/cifsacl.c468
-rw-r--r--fs/smb/client/cifsencrypt.c74
-rw-r--r--fs/smb/client/cifsfs.c242
-rw-r--r--fs/smb/client/cifsfs.h32
-rw-r--r--fs/smb/client/cifsglob.h70
-rw-r--r--fs/smb/client/cifsproto.h39
-rw-r--r--fs/smb/client/cifssmb.c113
-rw-r--r--fs/smb/client/compress.c16
-rw-r--r--fs/smb/client/compress.h28
-rw-r--r--fs/smb/client/compress/lz77.c235
-rw-r--r--fs/smb/client/compress/lz77.h15
-rw-r--r--fs/smb/client/connect.c86
-rw-r--r--fs/smb/client/dfs_cache.c63
-rw-r--r--fs/smb/client/dir.c375
-rw-r--r--fs/smb/client/file.c481
-rw-r--r--fs/smb/client/fs_context.c321
-rw-r--r--fs/smb/client/fs_context.h2
-rw-r--r--fs/smb/client/fscache.c6
-rw-r--r--fs/smb/client/gen_smb1_mapping124
-rw-r--r--fs/smb/client/inode.c238
-rw-r--r--fs/smb/client/ioctl.c154
-rw-r--r--fs/smb/client/link.c18
-rw-r--r--fs/smb/client/misc.c206
-rw-r--r--fs/smb/client/namespace.c1
-rw-r--r--fs/smb/client/netlink.c6
-rw-r--r--fs/smb/client/nterr.c704
-rw-r--r--fs/smb/client/nterr.h1082
-rw-r--r--fs/smb/client/readdir.c38
-rw-r--r--fs/smb/client/reparse.c96
-rw-r--r--fs/smb/client/reparse.h24
-rw-r--r--fs/smb/client/sess.c117
-rw-r--r--fs/smb/client/smb1encrypt.c20
-rw-r--r--fs/smb/client/smb1maperror.c869
-rw-r--r--fs/smb/client/smb1maperror_test.c78
-rw-r--r--fs/smb/client/smb1misc.c3
-rw-r--r--fs/smb/client/smb1ops.c64
-rw-r--r--fs/smb/client/smb1pdu.h14
-rw-r--r--fs/smb/client/smb1proto.h17
-rw-r--r--fs/smb/client/smb1transport.c65
-rw-r--r--fs/smb/client/smb2file.c68
-rw-r--r--fs/smb/client/smb2glob.h12
-rw-r--r--fs/smb/client/smb2inode.c474
-rw-r--r--fs/smb/client/smb2maperror.c28
-rw-r--r--fs/smb/client/smb2maperror_test.c13
-rw-r--r--fs/smb/client/smb2misc.c124
-rw-r--r--fs/smb/client/smb2ops.c636
-rw-r--r--fs/smb/client/smb2pdu.c268
-rw-r--r--fs/smb/client/smb2pdu.h35
-rw-r--r--fs/smb/client/smb2proto.h19
-rw-r--r--fs/smb/client/smb2transport.c144
-rw-r--r--fs/smb/client/smbdirect.c3107
-rw-r--r--fs/smb/client/smbdirect.h21
-rw-r--r--fs/smb/client/smberr.h415
-rw-r--r--fs/smb/client/trace.h169
-rw-r--r--fs/smb/client/transport.c26
-rw-r--r--fs/smb/client/xattr.c4
66 files changed, 5392 insertions, 7308 deletions
diff --git a/fs/smb/client/.gitignore b/fs/smb/client/.gitignore
index 8a6e04ab62b9..66e6e2ade0bd 100644
--- a/fs/smb/client/.gitignore
+++ b/fs/smb/client/.gitignore
@@ -1 +1,4 @@
+smb1_mapping_table.c
+smb1_err_dos_map.c
+smb1_err_srv_map.c
smb2_mapping_table.c
diff --git a/fs/smb/client/Kconfig b/fs/smb/client/Kconfig
index 17bd368574e9..2b7db5fb0fd9 100644
--- a/fs/smb/client/Kconfig
+++ b/fs/smb/client/Kconfig
@@ -5,12 +5,11 @@ config CIFS
select NLS
select NLS_UCS2_UTILS
select CRYPTO
- select CRYPTO_CMAC
select CRYPTO_AEAD2
select CRYPTO_CCM
select CRYPTO_GCM
- select CRYPTO_ECB
select CRYPTO_AES
+ select CRYPTO_LIB_AES_CBC_MACS
select CRYPTO_LIB_ARC4
select CRYPTO_LIB_MD5
select CRYPTO_LIB_SHA256
@@ -181,7 +180,9 @@ if CIFS
config CIFS_SMB_DIRECT
bool "SMB Direct support"
- depends on CIFS=m && INFINIBAND && INFINIBAND_ADDR_TRANS || CIFS=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y
+ depends on CIFS && INFINIBAND && INFINIBAND_ADDR_TRANS
+ depends on CIFS=m || INFINIBAND=y
+ select SMBDIRECT
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,
@@ -217,4 +218,15 @@ config CIFS_COMPRESSION
Say Y here if you want SMB traffic to be compressed.
If unsure, say N.
+config SMB1_KUNIT_TESTS
+ tristate "KUnit tests for SMB1"
+ depends on SMB_KUNIT_TESTS && CIFS_ALLOW_INSECURE_LEGACY
+ default SMB_KUNIT_TESTS
+ help
+ This builds the SMB1-specific KUnit tests.
+
+ These tests are only enabled when legacy insecure SMB1 support
+ (CIFS_ALLOW_INSECURE_LEGACY) is enabled.
+
+ If unsure, say N.
endif
diff --git a/fs/smb/client/Makefile b/fs/smb/client/Makefile
index 3abd357d6df6..fc6b9d35c962 100644
--- a/fs/smb/client/Makefile
+++ b/fs/smb/client/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_CIFS) += cifs.o
cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \
inode.o link.o misc.o netmisc.o smbencrypt.o transport.o \
- cached_dir.o cifs_unicode.o nterr.o cifsencrypt.o \
+ cached_dir.o cifs_unicode.o cifsencrypt.o \
readdir.o ioctl.o sess.o export.o unc.o winucase.o \
smb2ops.o smb2maperror.o smb2transport.o \
smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \
@@ -42,18 +42,42 @@ cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += \
smb1session.o \
smb1transport.o
-cifs-$(CONFIG_CIFS_COMPRESSION) += compress.o compress/lz77.o
+cifs-$(CONFIG_CIFS_COMPRESSION) += compress.o
+
+ifneq ($(CONFIG_CIFS_ALLOW_INSECURE_LEGACY),)
+#
+# Build the SMB1 error mapping tables from nterr.h and smberr.h
+#
+smb1-gen-y := smb1_mapping_table.c \
+ smb1_err_dos_map.c \
+ smb1_err_srv_map.c
+
+$(obj)/smb1_mapping_table.c: $(src)/nterr.h $(src)/gen_smb1_mapping FORCE
+ $(call if_changed,gen_smb1_mapping)
+
+$(obj)/smb1_err_%.c: $(src)/smberr.h $(src)/gen_smb1_mapping FORCE
+ $(call if_changed,gen_smb1_mapping)
+
+$(obj)/smb1maperror.o: $(addprefix $(obj)/, $(smb1-gen-y))
+
+quiet_cmd_gen_smb1_mapping = GEN $@
+ cmd_gen_smb1_mapping = perl $(src)/gen_smb1_mapping $< $@
+endif
#
# Build the SMB2 error mapping table from smb2status.h
#
$(obj)/smb2_mapping_table.c: $(src)/../common/smb2status.h \
- $(src)/gen_smb2_mapping
- $(call cmd,gen_smb2_mapping)
+ $(src)/gen_smb2_mapping FORCE
+ $(call if_changed,gen_smb2_mapping)
$(obj)/smb2maperror.o: $(obj)/smb2_mapping_table.c
quiet_cmd_gen_smb2_mapping = GEN $@
cmd_gen_smb2_mapping = perl $(src)/gen_smb2_mapping $< $@
-clean-files += smb2_mapping_table.c
+obj-$(CONFIG_SMB1_KUNIT_TESTS) += smb1maperror_test.o
+obj-$(CONFIG_SMB_KUNIT_TESTS) += smb2maperror_test.o
+
+# Let Kbuild handle tracking and cleaning
+targets += smb2_mapping_table.c $(smb1-gen-y)
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index 04bb95091f49..88d5e9a32f28 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -286,6 +286,14 @@ replay_again:
&rqst[0], &oplock, &oparms, utf16_path);
if (rc)
goto oshr_free;
+
+ if (oplock != SMB2_OPLOCK_LEVEL_II) {
+ rc = -EINVAL;
+ cifs_dbg(FYI, "%s: Oplock level %d not suitable for cached directory\n",
+ __func__, oplock);
+ goto oshr_free;
+ }
+
smb2_set_next_command(tcon, &rqst[0]);
memset(&qi_iov, 0, sizeof(qi_iov));
@@ -593,7 +601,7 @@ done:
* Invalidate all cached dirs when a TCON has been reset
* due to a session loss.
*/
-void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
+void invalidate_all_cached_dirs(struct cifs_tcon *tcon, bool sync)
{
struct cached_fids *cfids = tcon->cfids;
struct cached_fid *cfid, *q;
@@ -625,7 +633,8 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
/* run laundromat unconditionally now as there might have been previously queued work */
mod_delayed_work(cfid_put_wq, &cfids->laundromat_work, 0);
- flush_delayed_work(&cfids->laundromat_work);
+ if (sync)
+ flush_delayed_work(&cfids->laundromat_work);
}
static void
diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h
index 19d5592512e4..fc756836da95 100644
--- a/fs/smb/client/cached_dir.h
+++ b/fs/smb/client/cached_dir.h
@@ -90,7 +90,7 @@ void close_cached_dir(struct cached_fid *cfid);
void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon,
const char *name, struct cifs_sb_info *cifs_sb);
void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
-void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
+void invalidate_all_cached_dirs(struct cifs_tcon *tcon, bool sync);
bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
#endif /* _CACHED_DIR_H */
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index 217444e3e6d0..3761d3ad6088 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -23,7 +23,6 @@
#endif
#ifdef CONFIG_CIFS_SMB_DIRECT
#include "smbdirect.h"
-#include "../common/smbdirect/smbdirect_pdu.h"
#endif
#include "cifs_swn.h"
#include "cached_dir.h"
@@ -307,6 +306,9 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
LIST_HEAD(entry);
seq_puts(m, "# Version:1\n");
+#ifdef CONFIG_CIFS_DEBUG
+ seq_puts(m, "# Write 0 to this file to drop all cached directory entries\n");
+#endif /* CONFIG_CIFS_DEBUG */
seq_puts(m, "# Format:\n");
seq_puts(m, "# <tree id> <sess id> <persistent fid> <lease-key> <path>\n");
@@ -354,6 +356,51 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
return 0;
}
+#ifdef CONFIG_CIFS_DEBUG
+static int cifs_debug_dirs_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_debug_dirs_proc_show, NULL);
+}
+
+/* Drop all cached directory entries across all CIFS mounts. */
+static ssize_t cifs_debug_dirs_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ int rc, v;
+
+ rc = kstrtoint_from_user(buffer, count, 10, &v);
+ if (rc)
+ return rc;
+
+ if (v == 0) {
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ if (cifs_ses_exiting(ses))
+ continue;
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list)
+ invalidate_all_cached_dirs(tcon, false);
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ }
+
+ return count;
+}
+
+static const struct proc_ops cifs_debug_dirs_proc_ops = {
+ .proc_open = cifs_debug_dirs_proc_open,
+ .proc_read = seq_read,
+ .proc_lseek = seq_lseek,
+ .proc_release = single_release,
+ .proc_write = cifs_debug_dirs_proc_write,
+};
+#endif /* CONFIG_CIFS_DEBUG */
+
static __always_inline const char *compression_alg_str(__le16 alg)
{
switch (alg) {
@@ -452,11 +499,6 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
c = 0;
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
-#ifdef CONFIG_CIFS_SMB_DIRECT
- struct smbdirect_socket *sc;
- struct smbdirect_socket_parameters *sp;
-#endif
-
/* channel info will be printed as a part of sessions below */
if (SERVER_IS_CHAN(server))
continue;
@@ -471,66 +513,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\nClientGUID: %pUL", server->client_guid);
spin_unlock(&server->srv_lock);
#ifdef CONFIG_CIFS_SMB_DIRECT
- if (!server->rdma)
- goto skip_rdma;
-
- if (!server->smbd_conn) {
- seq_printf(m, "\nSMBDirect transport not available");
- goto skip_rdma;
- }
- sc = &server->smbd_conn->socket;
- sp = &sc->parameters;
-
- seq_printf(m, "\nSMBDirect protocol version: 0x%x "
- "transport status: %s (%u)",
- SMBDIRECT_V1,
- smbdirect_socket_status_string(sc->status),
- sc->status);
- seq_printf(m, "\nConn receive_credit_max: %u "
- "send_credit_target: %u max_send_size: %u",
- sp->recv_credit_max,
- sp->send_credit_target,
- sp->max_send_size);
- seq_printf(m, "\nConn max_fragmented_recv_size: %u "
- "max_fragmented_send_size: %u max_receive_size:%u",
- sp->max_fragmented_recv_size,
- sp->max_fragmented_send_size,
- sp->max_recv_size);
- seq_printf(m, "\nConn keep_alive_interval: %u "
- "max_readwrite_size: %u rdma_readwrite_threshold: %u",
- sp->keepalive_interval_msec * 1000,
- sp->max_read_write_size,
- server->rdma_readwrite_threshold);
- seq_printf(m, "\nDebug count_get_receive_buffer: %llu "
- "count_put_receive_buffer: %llu count_send_empty: %llu",
- sc->statistics.get_receive_buffer,
- sc->statistics.put_receive_buffer,
- sc->statistics.send_empty);
- seq_printf(m, "\nRead Queue "
- "count_enqueue_reassembly_queue: %llu "
- "count_dequeue_reassembly_queue: %llu "
- "reassembly_data_length: %u "
- "reassembly_queue_length: %u",
- sc->statistics.enqueue_reassembly_queue,
- sc->statistics.dequeue_reassembly_queue,
- sc->recv_io.reassembly.data_length,
- sc->recv_io.reassembly.queue_length);
- seq_printf(m, "\nCurrent Credits send_credits: %u "
- "receive_credits: %u receive_credit_target: %u",
- atomic_read(&sc->send_io.credits.count),
- atomic_read(&sc->recv_io.credits.count),
- sc->recv_io.credits.target);
- seq_printf(m, "\nPending send_pending: %u ",
- atomic_read(&sc->send_io.pending.count));
- seq_printf(m, "\nMR responder_resources: %u "
- "max_frmr_depth: %u mr_type: 0x%x",
- sp->responder_resources,
- sp->max_frmr_depth,
- sc->mr_io.type);
- seq_printf(m, "\nMR mr_ready_count: %u mr_used_count: %u",
- atomic_read(&sc->mr_io.ready.count),
- atomic_read(&sc->mr_io.used.count));
-skip_rdma:
+ smbd_debug_proc_show(server, m);
#endif
seq_printf(m, "\nNumber of credits: %d,%d,%d Dialect 0x%x",
server->credits,
@@ -771,7 +754,7 @@ static ssize_t cifs_stats_proc_write(struct file *file,
atomic_set(&server->smb2slowcmd[i], 0);
server->time_per_cmd[i] = 0;
server->slowest_cmd[i] = 0;
- server->fastest_cmd[0] = 0;
+ server->fastest_cmd[i] = 0;
}
#endif /* CONFIG_CIFS_STATS2 */
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
@@ -950,9 +933,11 @@ cifs_proc_init(void)
proc_create_single("open_files", 0400, proc_fs_cifs,
cifs_debug_files_proc_show);
- proc_create_single("open_dirs", 0400, proc_fs_cifs,
- cifs_debug_dirs_proc_show);
-
+#ifdef CONFIG_CIFS_DEBUG
+ proc_create("open_dirs", 0600, proc_fs_cifs, &cifs_debug_dirs_proc_ops);
+#else /* CONFIG_CIFS_DEBUG */
+ proc_create_single("open_dirs", 0400, proc_fs_cifs, cifs_debug_dirs_proc_show);
+#endif /* !CONFIG_CIFS_DEBUG */
proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_ops);
proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_ops);
proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_ops);
diff --git a/fs/smb/client/cifs_fs_sb.h b/fs/smb/client/cifs_fs_sb.h
index 84e7e366b0ff..d6494e1d93cc 100644
--- a/fs/smb/client/cifs_fs_sb.h
+++ b/fs/smb/client/cifs_fs_sb.h
@@ -56,6 +56,7 @@ struct cifs_sb_info {
struct smb3_fs_context *ctx;
atomic_t active;
atomic_t mnt_cifs_flags;
+ atomic_t outstanding_rreq; /* nr of rreqs not yet fully deinitialized */
struct delayed_work prune_tlinks;
struct rcu_head rcu;
diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c
index 3a41bbada04c..44c407275680 100644
--- a/fs/smb/client/cifs_spnego.c
+++ b/fs/smb/client/cifs_spnego.c
@@ -8,6 +8,7 @@
*/
#include <linux/list.h>
+#include <linux/cred.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <keys/user-type.h>
@@ -40,12 +41,27 @@ cifs_spnego_key_destroy(struct key *key)
kfree(key->payload.data[0]);
}
+static int
+cifs_spnego_key_vet_description(const char *description)
+{
+ /*
+ * cifs.spnego descriptions are authority-bearing inputs to cifs.upcall.
+ * They are only valid when produced by CIFS while using the private
+ * spnego_cred installed below. Do not let userspace create this type
+ * of key through request_key(2)/add_key(2), since the helper treats
+ * pid/uid/creduid/upcall_target as kernel-originating fields.
+ */
+ if (current_cred() != spnego_cred)
+ return -EPERM;
+ return 0;
+}
/*
* keytype for CIFS spnego keys
*/
struct key_type cifs_spnego_key_type = {
.name = "cifs.spnego",
+ .vet_description = cifs_spnego_key_vet_description,
.instantiate = cifs_spnego_key_instantiate,
.destroy = cifs_spnego_key_destroy,
.describe = user_describe,
diff --git a/fs/smb/client/cifs_swn.c b/fs/smb/client/cifs_swn.c
index 9753a432d099..c49ecddf4a33 100644
--- a/fs/smb/client/cifs_swn.c
+++ b/fs/smb/client/cifs_swn.c
@@ -28,10 +28,54 @@ struct cifs_swn_reg {
bool net_name_notify;
bool share_name_notify;
bool ip_notify;
+};
- struct cifs_tcon *tcon;
+struct cifs_swn_reg_info {
+ int id;
+ unsigned int ref_count;
+ const char *net_name;
+ const char *share_name;
+ bool net_name_notify;
+ bool share_name_notify;
+ bool ip_notify;
};
+static void cifs_swn_snapshot_reg(struct cifs_swn_reg *swnreg,
+ struct cifs_swn_reg_info *info)
+{
+ info->id = swnreg->id;
+ info->ref_count = kref_read(&swnreg->ref_count);
+ info->net_name = swnreg->net_name;
+ info->share_name = swnreg->share_name;
+ info->net_name_notify = swnreg->net_name_notify;
+ info->share_name_notify = swnreg->share_name_notify;
+ info->ip_notify = swnreg->ip_notify;
+}
+
+static int cifs_swn_dup_reg(struct cifs_swn_reg *swnreg,
+ struct cifs_swn_reg_info *info)
+{
+ cifs_swn_snapshot_reg(swnreg, info);
+
+ info->net_name = kstrdup(swnreg->net_name, GFP_KERNEL);
+ if (!info->net_name)
+ return -ENOMEM;
+
+ info->share_name = kstrdup(swnreg->share_name, GFP_KERNEL);
+ if (!info->share_name) {
+ kfree(info->net_name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void cifs_swn_free_reg_info(struct cifs_swn_reg_info *info)
+{
+ kfree(info->net_name);
+ kfree(info->share_name);
+}
+
static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb)
{
int ret;
@@ -73,7 +117,8 @@ static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb)
* The authentication information to connect to the witness service is bundled
* into the message.
*/
-static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
+static int cifs_swn_send_register_message(struct cifs_swn_reg_info *swnreg,
+ struct cifs_tcon *tcon)
{
struct sk_buff *skb;
struct genlmsghdr *hdr;
@@ -109,10 +154,10 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
* told to switch to it (client move message). In these cases we unregister from the
* server address and register to the new address when we receive the notification.
*/
- if (swnreg->tcon->ses->server->use_swn_dstaddr)
- addr = &swnreg->tcon->ses->server->swn_dstaddr;
+ if (tcon->ses->server->use_swn_dstaddr)
+ addr = &tcon->ses->server->swn_dstaddr;
else
- addr = &swnreg->tcon->ses->server->dstaddr;
+ addr = &tcon->ses->server->dstaddr;
ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr);
if (ret < 0)
@@ -136,10 +181,10 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
goto nlmsg_fail;
}
- authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype);
+ authtype = cifs_select_sectype(tcon->ses->server, tcon->ses->sectype);
switch (authtype) {
case Kerberos:
- ret = cifs_swn_auth_info_krb(swnreg->tcon, skb);
+ ret = cifs_swn_auth_info_krb(tcon, skb);
if (ret < 0) {
cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret);
goto nlmsg_fail;
@@ -147,7 +192,7 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
break;
case NTLMv2:
case RawNTLMSSP:
- ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb);
+ ret = cifs_swn_auth_info_ntlm(tcon, skb);
if (ret < 0) {
cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret);
goto nlmsg_fail;
@@ -176,7 +221,8 @@ nlmsg_fail:
/*
* Sends an uregister message to the userspace daemon based on the registration
*/
-static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
+static int cifs_swn_send_unregister_message(struct cifs_swn_reg_info *swnreg,
+ struct cifs_tcon *tcon)
{
struct sk_buff *skb;
struct genlmsghdr *hdr;
@@ -205,7 +251,7 @@ static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
goto nlmsg_fail;
ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
- &swnreg->tcon->ses->server->dstaddr);
+ &tcon->ses->server->dstaddr);
if (ret < 0)
goto nlmsg_fail;
@@ -242,6 +288,88 @@ nlmsg_fail:
}
/*
+ * Allocation-free mirror of extract_hostname() + extract_sharename() from
+ * fs/smb/client/unc.c. Those helpers kmalloc(GFP_KERNEL); this runs under
+ * cifs_tcp_ses_lock and tcon->tc_lock, both spinlocks, so we mirror their
+ * parsing in place against the caller's stable net_name/share_name strings.
+ * Keep in sync with unc.c.
+ */
+static bool cifs_swn_tcon_matches(struct cifs_tcon *tcon,
+ const char *net_name,
+ const char *share_name)
+{
+ const char *unc = tcon->tree_name;
+ const char *host, *share, *delim;
+ size_t host_len, share_len;
+
+ if (!tcon->use_witness)
+ return false;
+
+ /* extract_hostname: require strlen(unc) >= 3 */
+ if (strnlen(unc, 3) < 3)
+ return false;
+ /* extract_hostname: skip all leading '\' characters */
+ for (host = unc; *host == '\\'; host++)
+ ;
+ if (!*host)
+ return false;
+ delim = strchr(host, '\\');
+ if (!delim)
+ return false;
+ host_len = delim - host;
+ if (strlen(net_name) != host_len ||
+ strncasecmp(host, net_name, host_len))
+ return false;
+
+ /* extract_sharename: start at unc + 2, then first '\' onward */
+ share = unc + 2;
+ delim = strchr(share, '\\');
+ if (!delim)
+ return false;
+ share = delim + 1;
+ share_len = strlen(share);
+
+ return strlen(share_name) == share_len &&
+ !strncasecmp(share, share_name, share_len);
+}
+
+/*
+ * One SWN registration id represents one net/share name pair. Multiple
+ * mounted tcons can therefore share the id. Pick a live representative at
+ * use time instead of caching the first tcon pointer in the registration.
+ */
+static struct cifs_tcon *cifs_swn_get_tcon(struct cifs_swn_reg_info *swnreg)
+{
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+ spin_lock(&tcon->tc_lock);
+ if (tcon->status == TID_EXITING ||
+ !cifs_swn_tcon_matches(tcon, swnreg->net_name,
+ swnreg->share_name)) {
+ spin_unlock(&tcon->tc_lock);
+ continue;
+ }
+ ++tcon->tc_count;
+ trace_smb3_tcon_ref(tcon->debug_id,
+ tcon->tc_count,
+ netfs_trace_tcon_ref_get_swn_notify);
+ spin_unlock(&tcon->tc_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return tcon;
+ }
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ return NULL;
+}
+
+/*
* Try to find a matching registration for the tcon's server name and share name.
* Calls to this function must be protected by cifs_swnreg_idr_mutex.
* TODO Try to avoid memory allocations
@@ -297,7 +425,7 @@ static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon)
/*
* Get a registration for the tcon's server and share name, allocating a new one if it does not
- * exists
+ * exist.
*/
static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
{
@@ -315,7 +443,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
goto unlock;
}
- reg = kmalloc_obj(struct cifs_swn_reg, GFP_ATOMIC);
+ reg = kmalloc_obj(struct cifs_swn_reg);
if (reg == NULL) {
ret = -ENOMEM;
goto fail_unlock;
@@ -323,7 +451,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
kref_init(&reg->ref_count);
- reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC);
+ reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_KERNEL);
if (reg->id < 0) {
cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__);
ret = reg->id;
@@ -347,8 +475,6 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
reg->net_name_notify = true;
reg->share_name_notify = true;
reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
-
- reg->tcon = tcon;
unlock:
mutex_unlock(&cifs_swnreg_idr_mutex);
@@ -368,11 +494,6 @@ fail_unlock:
static void cifs_swn_reg_release(struct kref *ref)
{
struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count);
- int ret;
-
- ret = cifs_swn_send_unregister_message(swnreg);
- if (ret < 0)
- cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret);
idr_remove(&cifs_swnreg_idr, swnreg->id);
kfree(swnreg->net_name);
@@ -380,23 +501,33 @@ static void cifs_swn_reg_release(struct kref *ref)
kfree(swnreg);
}
-static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
+static void cifs_put_swn_reg_locked(struct cifs_swn_reg *swnreg,
+ struct cifs_tcon *tcon)
{
- mutex_lock(&cifs_swnreg_idr_mutex);
+ if (kref_read(&swnreg->ref_count) == 1) {
+ struct cifs_swn_reg_info swnreg_info;
+ int ret;
+
+ cifs_swn_snapshot_reg(swnreg, &swnreg_info);
+ ret = cifs_swn_send_unregister_message(&swnreg_info, tcon);
+ if (ret < 0)
+ cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n",
+ __func__, ret);
+ }
+
kref_put(&swnreg->ref_count, cifs_swn_reg_release);
- mutex_unlock(&cifs_swnreg_idr_mutex);
}
-static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state)
+static int cifs_swn_resource_state_changed(struct cifs_tcon *tcon, const char *name, int state)
{
switch (state) {
case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE:
cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name);
- cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true);
+ cifs_signal_cifsd_for_reconnect(tcon->ses->server, true);
break;
case CIFS_SWN_RESOURCE_STATE_AVAILABLE:
cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name);
- cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true);
+ cifs_signal_cifsd_for_reconnect(tcon->ses->server, true);
break;
case CIFS_SWN_RESOURCE_STATE_UNKNOWN:
cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name);
@@ -502,7 +633,7 @@ unlock:
return ret;
}
-static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr)
+static int cifs_swn_client_move(struct cifs_tcon *tcon, struct sockaddr_storage *addr)
{
struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr;
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr;
@@ -512,14 +643,17 @@ static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_sto
else if (addr->ss_family == AF_INET6)
cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr);
- return cifs_swn_reconnect(swnreg->tcon, addr);
+ return cifs_swn_reconnect(tcon, addr);
}
int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
{
struct cifs_swn_reg *swnreg;
+ struct cifs_swn_reg_info swnreg_info;
+ struct cifs_tcon *tcon;
char name[256];
int type;
+ int ret = 0;
if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) {
int swnreg_id;
@@ -527,21 +661,34 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]);
mutex_lock(&cifs_swnreg_idr_mutex);
swnreg = idr_find(&cifs_swnreg_idr, swnreg_id);
- mutex_unlock(&cifs_swnreg_idr_mutex);
if (swnreg == NULL) {
+ mutex_unlock(&cifs_swnreg_idr_mutex);
cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id);
return -EINVAL;
}
+ ret = cifs_swn_dup_reg(swnreg, &swnreg_info);
+ mutex_unlock(&cifs_swnreg_idr_mutex);
+ if (ret)
+ return ret;
} else {
cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__);
return -EINVAL;
}
+ tcon = cifs_swn_get_tcon(&swnreg_info);
+ if (!tcon) {
+ cifs_dbg(FYI, "%s: registration id %d has no live tcon\n",
+ __func__, swnreg_info.id);
+ ret = -ENODEV;
+ goto free_info;
+ }
+
if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) {
type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]);
} else {
cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
switch (type) {
@@ -553,15 +700,18 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
sizeof(name));
} else {
cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) {
state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]);
} else {
cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
- return cifs_swn_resource_state_changed(swnreg, name, state);
+ ret = cifs_swn_resource_state_changed(tcon, name, state);
+ break;
}
case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: {
struct sockaddr_storage addr;
@@ -570,28 +720,36 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr));
} else {
cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
- return cifs_swn_client_move(swnreg, &addr);
+ ret = cifs_swn_client_move(tcon, &addr);
+ break;
}
default:
cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type);
break;
}
- return 0;
+out:
+ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
+free_info:
+ cifs_swn_free_reg_info(&swnreg_info);
+ return ret;
}
int cifs_swn_register(struct cifs_tcon *tcon)
{
struct cifs_swn_reg *swnreg;
+ struct cifs_swn_reg_info swnreg_info;
int ret;
swnreg = cifs_get_swn_reg(tcon);
if (IS_ERR(swnreg))
return PTR_ERR(swnreg);
- ret = cifs_swn_send_register_message(swnreg);
+ cifs_swn_snapshot_reg(swnreg, &swnreg_info);
+ ret = cifs_swn_send_register_message(&swnreg_info, tcon);
if (ret < 0) {
cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret);
/* Do not put the swnreg or return error, the echo task will retry */
@@ -612,35 +770,68 @@ int cifs_swn_unregister(struct cifs_tcon *tcon)
return PTR_ERR(swnreg);
}
+ cifs_put_swn_reg_locked(swnreg, tcon);
mutex_unlock(&cifs_swnreg_idr_mutex);
- cifs_put_swn_reg(swnreg);
-
return 0;
}
-void cifs_swn_dump(struct seq_file *m)
+/*
+ * Snapshot one registration under cifs_swnreg_idr_mutex and return. Callers
+ * intentionally do the per-registration network/genlmsg work without the
+ * mutex held, both to keep the critical section short and to avoid nesting
+ * cifs_swnreg_idr_mutex inside the higher tc_lock when a live tcon is then
+ * pinned for the send.
+ */
+static int cifs_swn_get_next_reg_info(int *id, struct cifs_swn_reg_info *info)
{
struct cifs_swn_reg *swnreg;
+ int ret = 0;
+
+ mutex_lock(&cifs_swnreg_idr_mutex);
+ swnreg = idr_get_next(&cifs_swnreg_idr, id);
+ if (swnreg) {
+ ret = cifs_swn_dup_reg(swnreg, info);
+ if (!ret) {
+ *id = swnreg->id + 1;
+ ret = 1;
+ }
+ }
+ mutex_unlock(&cifs_swnreg_idr_mutex);
+
+ return ret;
+}
+
+void cifs_swn_dump(struct seq_file *m)
+{
+ struct cifs_swn_reg_info swnreg_info;
+ struct cifs_tcon *tcon;
struct sockaddr_in *sa;
struct sockaddr_in6 *sa6;
- int id;
+ int id = 0;
+ int ret;
seq_puts(m, "Witness registrations:");
- mutex_lock(&cifs_swnreg_idr_mutex);
- idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
+ while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) {
seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ",
- id, kref_read(&swnreg->ref_count),
- swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)",
- swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)");
- switch (swnreg->tcon->ses->server->dstaddr.ss_family) {
+ swnreg_info.id, swnreg_info.ref_count,
+ swnreg_info.net_name, swnreg_info.net_name_notify ? "(y)" : "(n)",
+ swnreg_info.share_name, swnreg_info.share_name_notify ? "(y)" : "(n)");
+
+ tcon = cifs_swn_get_tcon(&swnreg_info);
+ if (!tcon) {
+ seq_puts(m, "(no live tcon)");
+ goto next;
+ }
+
+ switch (tcon->ses->server->dstaddr.ss_family) {
case AF_INET:
- sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr;
+ sa = (struct sockaddr_in *)&tcon->ses->server->dstaddr;
seq_printf(m, "%pI4", &sa->sin_addr.s_addr);
break;
case AF_INET6:
- sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr;
+ sa6 = (struct sockaddr_in6 *)&tcon->ses->server->dstaddr;
seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr);
if (sa6->sin6_scope_id)
seq_printf(m, "%%%u", sa6->sin6_scope_id);
@@ -648,23 +839,38 @@ void cifs_swn_dump(struct seq_file *m)
default:
seq_puts(m, "(unknown)");
}
- seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)");
+ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
+next:
+ seq_printf(m, "%s", swnreg_info.ip_notify ? "(y)" : "(n)");
+ cifs_swn_free_reg_info(&swnreg_info);
}
- mutex_unlock(&cifs_swnreg_idr_mutex);
+ if (ret < 0)
+ seq_printf(m, "\nFailed to snapshot witness registration: %d", ret);
seq_puts(m, "\n");
}
void cifs_swn_check(void)
{
- struct cifs_swn_reg *swnreg;
- int id;
+ struct cifs_swn_reg_info swnreg_info;
+ struct cifs_tcon *tcon;
+ int id = 0;
int ret;
- mutex_lock(&cifs_swnreg_idr_mutex);
- idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
- ret = cifs_swn_send_register_message(swnreg);
+ while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) {
+ tcon = cifs_swn_get_tcon(&swnreg_info);
+ if (!tcon) {
+ cifs_dbg(FYI, "%s: registration id %d has no live tcon\n",
+ __func__, swnreg_info.id);
+ goto free_info;
+ }
+
+ ret = cifs_swn_send_register_message(&swnreg_info, tcon);
if (ret < 0)
cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret);
+ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
+free_info:
+ cifs_swn_free_reg_info(&swnreg_info);
}
- mutex_unlock(&cifs_swnreg_idr_mutex);
+ if (ret < 0)
+ cifs_dbg(FYI, "%s: Failed to snapshot registration: %d\n", __func__, ret);
}
diff --git a/fs/smb/client/cifs_unicode.c b/fs/smb/client/cifs_unicode.c
index e2edc207cef2..4a8a591f4bca 100644
--- a/fs/smb/client/cifs_unicode.c
+++ b/fs/smb/client/cifs_unicode.c
@@ -6,6 +6,7 @@
*/
#include <linux/fs.h>
#include <linux/slab.h>
+#include <linux/unaligned.h>
#include "cifs_fs_sb.h"
#include "cifs_unicode.h"
#include "cifsglob.h"
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
index f4cb3018a358..c5e47a835f99 100644
--- a/fs/smb/client/cifsacl.c
+++ b/fs/smb/client/cifsacl.c
@@ -68,6 +68,9 @@ cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
char *payload;
+ if (prep->datalen > U16_MAX)
+ return -EINVAL;
+
/*
* If the payload is less than or equal to the size of a pointer, then
* an allocation here is wasteful. Just copy the data directly to the
@@ -97,8 +100,23 @@ cifs_idmap_key_destroy(struct key *key)
kfree(key->payload.data[0]);
}
+static int
+cifs_idmap_key_vet_description(const char *description)
+{
+ /*
+ * cifs.idmap descriptions are authority-bearing inputs to the
+ * cifs.idmap upcall helper. Only allow the kernel to create this
+ * type of key using the private root_cred installed in
+ * init_cifs_idmap; reject userspace request_key(2)/add_key(2).
+ */
+ if (current_cred() != root_cred)
+ return -EPERM;
+ return 0;
+}
+
static struct key_type cifs_idmap_key_type = {
.name = "cifs.idmap",
+ .vet_description = cifs_idmap_key_vet_description,
.instantiate = cifs_idmap_key_instantiate,
.destroy = cifs_idmap_key_destroy,
.describe = user_describe,
@@ -275,6 +293,73 @@ cifs_copy_sid(struct smb_sid *dst, const struct smb_sid *src)
return size;
}
+static int parse_sid(const struct smb_sid *psid, const char *end_of_acl)
+{
+ unsigned int sid_len;
+
+ /* SID must contain the fixed header before num_subauth is trusted. */
+ if (end_of_acl < (const char *)psid + CIFS_SID_BASE_SIZE) {
+ cifs_dbg(VFS, "ACL too small to parse SID %p\n", psid);
+ return -EINVAL;
+ }
+ if (psid->num_subauth > SID_MAX_SUB_AUTHORITIES) {
+ cifs_dbg(VFS, "SID contains too many subauthorities %u\n",
+ psid->num_subauth);
+ return -EINVAL;
+ }
+
+ sid_len = CIFS_SID_BASE_SIZE + psid->num_subauth * sizeof(__le32);
+ if (end_of_acl < (const char *)psid + sid_len) {
+ cifs_dbg(VFS, "ACL too small to parse SID %p\n", psid);
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_CIFS_DEBUG2
+ if (psid->num_subauth) {
+ int i;
+
+ cifs_dbg(FYI, "SID revision %d num_auth %d\n",
+ psid->revision, psid->num_subauth);
+
+ for (i = 0; i < psid->num_subauth; i++) {
+ cifs_dbg(FYI, "SID sub_auth[%d]: 0x%x\n",
+ i, le32_to_cpu(psid->sub_auth[i]));
+ }
+
+ cifs_dbg(FYI, "RID 0x%x\n",
+ le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]));
+ }
+#endif
+
+ return 0;
+}
+
+static int sid_from_sd(const struct smb_ntsd *pntsd, __u32 secdesclen,
+ __u32 sid_offset, struct smb_sid **sid)
+{
+ struct smb_sid *psid;
+ char *end_of_acl;
+
+ if (secdesclen < sizeof(struct smb_ntsd)) {
+ cifs_dbg(VFS, "ACL too small to parse security descriptor\n");
+ return -EINVAL;
+ }
+ end_of_acl = (char *)pntsd + secdesclen;
+
+ if (sid_offset < sizeof(struct smb_ntsd) ||
+ sid_offset > secdesclen - CIFS_SID_BASE_SIZE) {
+ cifs_dbg(VFS, "Server returned illegal SID offset\n");
+ return -EINVAL;
+ }
+
+ psid = (struct smb_sid *)((char *)pntsd + sid_offset);
+ if (parse_sid(psid, end_of_acl))
+ return -EINVAL;
+
+ *sid = psid;
+ return 0;
+}
+
static int
id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid)
{
@@ -515,14 +600,14 @@ exit_cifs_idmap(void)
}
/* copy ntsd, owner sid, and group sid from a security descriptor to another */
-static __u32 copy_sec_desc(const struct smb_ntsd *pntsd,
- struct smb_ntsd *pnntsd,
- __u32 sidsoffset,
- struct smb_sid *pownersid,
- struct smb_sid *pgrpsid)
+static int copy_sec_desc(const struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
+ __u32 sidsoffset, __u32 secdesclen,
+ __u32 *pnsecdesclen, struct smb_sid *pownersid,
+ struct smb_sid *pgrpsid)
{
struct smb_sid *owner_sid_ptr, *group_sid_ptr;
struct smb_sid *nowner_sid_ptr, *ngroup_sid_ptr;
+ int rc;
/* copy security descriptor control portion */
pnntsd->revision = pntsd->revision;
@@ -533,28 +618,34 @@ static __u32 copy_sec_desc(const struct smb_ntsd *pntsd,
pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct smb_sid));
/* copy owner sid */
- if (pownersid)
+ if (pownersid) {
owner_sid_ptr = pownersid;
- else
- owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
- le32_to_cpu(pntsd->osidoffset));
+ } else {
+ rc = sid_from_sd(pntsd, secdesclen,
+ le32_to_cpu(pntsd->osidoffset), &owner_sid_ptr);
+ if (rc)
+ return rc;
+ }
nowner_sid_ptr = (struct smb_sid *)((char *)pnntsd + sidsoffset);
cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr);
/* copy group sid */
- if (pgrpsid)
+ if (pgrpsid) {
group_sid_ptr = pgrpsid;
- else
- group_sid_ptr = (struct smb_sid *)((char *)pntsd +
- le32_to_cpu(pntsd->gsidoffset));
+ } else {
+ rc = sid_from_sd(pntsd, secdesclen,
+ le32_to_cpu(pntsd->gsidoffset), &group_sid_ptr);
+ if (rc)
+ return rc;
+ }
ngroup_sid_ptr = (struct smb_sid *)((char *)pnntsd + sidsoffset +
sizeof(struct smb_sid));
cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr);
- return sidsoffset + (2 * sizeof(struct smb_sid));
+ *pnsecdesclen = sidsoffset + (2 * sizeof(struct smb_sid));
+ return 0;
}
-
/*
change posix mode to reflect permissions
pmode is the existing mode (we only want to overwrite part of this
@@ -758,6 +849,77 @@ static void dump_ace(struct smb_ace *pace, char *end_of_acl)
}
#endif
+static int validate_dacl(struct smb_acl *pdacl, char *end_of_acl)
+{
+ int i, ace_hdr_size, ace_size, min_ace_size;
+ u16 dacl_size, num_aces;
+ char *acl_base, *end_of_dacl;
+ struct smb_ace *pace;
+
+ if (!pdacl)
+ return 0;
+
+ if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl)) {
+ cifs_dbg(VFS, "ACL too small to parse DACL\n");
+ return -EINVAL;
+ }
+
+ dacl_size = le16_to_cpu(pdacl->size);
+ if (dacl_size < sizeof(struct smb_acl) ||
+ end_of_acl < (char *)pdacl + dacl_size) {
+ cifs_dbg(VFS, "ACL too small to parse DACL\n");
+ return -EINVAL;
+ }
+
+ num_aces = le16_to_cpu(pdacl->num_aces);
+ if (!num_aces)
+ return 0;
+
+ ace_hdr_size = offsetof(struct smb_ace, sid) +
+ offsetof(struct smb_sid, sub_auth);
+ min_ace_size = ace_hdr_size + sizeof(__le32);
+ if (num_aces > (dacl_size - sizeof(struct smb_acl)) / min_ace_size) {
+ cifs_dbg(VFS, "ACL too small to parse DACL\n");
+ return -EINVAL;
+ }
+
+ end_of_dacl = (char *)pdacl + dacl_size;
+ acl_base = (char *)pdacl;
+ ace_size = sizeof(struct smb_acl);
+
+ for (i = 0; i < num_aces; ++i) {
+ if (end_of_dacl - acl_base < ace_size) {
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
+ return -EINVAL;
+ }
+
+ pace = (struct smb_ace *)(acl_base + ace_size);
+ acl_base = (char *)pace;
+
+ if (end_of_dacl - acl_base < ace_hdr_size ||
+ pace->sid.num_subauth == 0 ||
+ pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) {
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
+ return -EINVAL;
+ }
+
+ ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth;
+ if (end_of_dacl - acl_base < ace_size ||
+ le16_to_cpu(pace->size) < ace_size) {
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
+ return -EINVAL;
+ }
+
+ ace_size = le16_to_cpu(pace->size);
+ if (end_of_dacl - acl_base < ace_size) {
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
struct smb_sid *pownersid, struct smb_sid *pgrpsid,
struct cifs_fattr *fattr, bool mode_from_special_sid)
@@ -777,12 +939,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 + sizeof(struct smb_acl) ||
- end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
- cifs_dbg(VFS, "ACL too small to parse DACL\n");
+ if (validate_dacl(pdacl, end_of_acl))
return;
- }
cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n",
le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
@@ -800,37 +958,19 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
if (num_aces > 0) {
umode_t denied_mode = 0;
- if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) /
- (offsetof(struct smb_ace, sid) +
- offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
- return;
-
ppace = kmalloc_objs(struct smb_ace *, num_aces);
if (!ppace)
return;
for (i = 0; i < num_aces; ++i) {
- if (end_of_acl - acl_base < acl_size)
- break;
-
ppace[i] = (struct smb_ace *) (acl_base + acl_size);
- acl_base = (char *)ppace[i];
- acl_size = offsetof(struct smb_ace, sid) +
- offsetof(struct smb_sid, sub_auth);
-
- if (end_of_acl - acl_base < acl_size ||
- ppace[i]->sid.num_subauth == 0 ||
- ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
- (end_of_acl - acl_base <
- acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) ||
- (le16_to_cpu(ppace[i]->size) <
- acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth))
- break;
#ifdef CONFIG_CIFS_DEBUG2
- dump_ace(ppace[i], end_of_acl);
+ dump_ace(ppace[i],
+ (char *)pdacl + le16_to_cpu(pdacl->size));
#endif
if (mode_from_special_sid &&
+ ppace[i]->sid.num_subauth >= 3 &&
(compare_sids(&(ppace[i]->sid),
&sid_unix_NFS_mode) == 0)) {
/*
@@ -840,7 +980,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
*/
fattr->cf_mode &= ~07777;
fattr->cf_mode |=
- le32_to_cpu(ppace[i]->sid.sub_auth[2]);
+ le32_to_cpu(ppace[i]->sid.sub_auth[2]) & 07777;
break;
} else {
if (compare_sids(&(ppace[i]->sid), pownersid) == 0) {
@@ -870,6 +1010,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
(void *)ppace[i],
sizeof(struct smb_ace)); */
+ acl_base = (char *)ppace[i];
acl_size = le16_to_cpu(ppace[i]->size);
}
@@ -955,13 +1096,13 @@ unsigned int setup_special_user_owner_ACE(struct smb_ace *pntace)
static void populate_new_aces(char *nacl_base,
struct smb_sid *pownersid,
struct smb_sid *pgrpsid,
- __u64 *pnmode, u16 *pnum_aces, u16 *pnsize,
+ __u64 *pnmode, u16 *pnum_aces, u32 *pnsize,
bool modefromsid,
bool posix)
{
__u64 nmode;
u16 num_aces = 0;
- u16 nsize = 0;
+ u32 nsize = 0;
__u64 user_mode;
__u64 group_mode;
__u64 other_mode;
@@ -1060,16 +1201,17 @@ set_size:
*pnsize = nsize;
}
-static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *pndacl,
- struct smb_sid *pownersid, struct smb_sid *pgrpsid,
- struct smb_sid *pnownersid, struct smb_sid *pngrpsid)
+static int replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *pndacl,
+ struct smb_sid *pownersid, struct smb_sid *pgrpsid,
+ struct smb_sid *pnownersid, struct smb_sid *pngrpsid,
+ int *aclflag, u16 *pnsize)
{
int i;
u16 size = 0;
struct smb_ace *pntace = NULL;
char *acl_base = NULL;
u16 src_num_aces = 0;
- u16 nsize = 0;
+ u32 nsize = 0;
struct smb_ace *pnntace = NULL;
char *nacl_base = NULL;
u16 ace_size = 0;
@@ -1086,18 +1228,24 @@ static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *p
pntace = (struct smb_ace *) (acl_base + size);
pnntace = (struct smb_ace *) (nacl_base + nsize);
- if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0)
+ if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0) {
ace_size = cifs_copy_ace(pnntace, pntace, pnownersid);
- else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0)
+ *aclflag |= CIFS_ACL_DACL;
+ } else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0) {
ace_size = cifs_copy_ace(pnntace, pntace, pngrpsid);
- else
+ *aclflag |= CIFS_ACL_DACL;
+ } else {
ace_size = cifs_copy_ace(pnntace, pntace, NULL);
+ }
size += le16_to_cpu(pntace->size);
nsize += ace_size;
+ if (nsize > U16_MAX)
+ return -EOVERFLOW;
}
- return nsize;
+ *pnsize = nsize;
+ return 0;
}
static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
@@ -1109,7 +1257,7 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
struct smb_ace *pntace = NULL;
char *acl_base = NULL;
u16 src_num_aces = 0;
- u16 nsize = 0;
+ u32 nsize = 0;
struct smb_ace *pnntace = NULL;
char *nacl_base = NULL;
u16 num_aces = 0;
@@ -1160,6 +1308,8 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
nsize += cifs_copy_ace(pnntace, pntace, NULL);
num_aces++;
+ if (nsize > U16_MAX)
+ return -EOVERFLOW;
next_ace:
size += le16_to_cpu(pntace->size);
@@ -1176,42 +1326,25 @@ next_ace:
}
finalize_dacl:
+ /* The DACL size field is 16-bit on the wire, see MS-DTYP 2.4.5 */
+ if (nsize > U16_MAX)
+ return -EOVERFLOW;
+
pndacl->num_aces = cpu_to_le16(num_aces);
pndacl->size = cpu_to_le16(nsize);
return 0;
}
-static int parse_sid(struct smb_sid *psid, char *end_of_acl)
+static bool dacl_offset_valid(unsigned int acl_len, __u32 dacloffset)
{
- /* BB need to add parm so we can store the SID BB */
-
- /* 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) {
- cifs_dbg(VFS, "ACL too small to parse SID %p\n", psid);
- return -EINVAL;
- }
-
-#ifdef CONFIG_CIFS_DEBUG2
- if (psid->num_subauth) {
- int i;
- cifs_dbg(FYI, "SID revision %d num_auth %d\n",
- psid->revision, psid->num_subauth);
-
- for (i = 0; i < psid->num_subauth; i++) {
- cifs_dbg(FYI, "SID sub_auth[%d]: 0x%x\n",
- i, le32_to_cpu(psid->sub_auth[i]));
- }
+ if (acl_len < sizeof(struct smb_acl))
+ return false;
- /* BB add length check to make sure that we do not have huge
- num auths and therefore go off the end */
- cifs_dbg(FYI, "RID 0x%x\n",
- le32_to_cpu(psid->sub_auth[psid->num_subauth-1]));
- }
-#endif
+ if (dacloffset < sizeof(struct smb_ntsd))
+ return false;
- return 0;
+ return dacloffset <= acl_len - sizeof(struct smb_acl);
}
@@ -1222,54 +1355,69 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
{
int rc = 0;
struct smb_sid *owner_sid_ptr, *group_sid_ptr;
+ unsigned int sbflags = cifs_sb_flags(cifs_sb);
struct smb_acl *dacl_ptr; /* no need for SACL ptr */
- char *end_of_acl = ((char *)pntsd) + acl_len;
- __u32 dacloffset;
+ char *end_of_acl;
+ __u32 dacloffset, osidoffset, gsidoffset;
if (pntsd == NULL)
return smb_EIO(smb_eio_trace_null_pointers);
+ if (acl_len < (int)sizeof(struct smb_ntsd)) {
+ cifs_dbg(VFS, "ACL too small to parse security descriptor\n");
+ return -EINVAL;
+ }
+ end_of_acl = ((char *)pntsd) + acl_len;
- 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));
+ osidoffset = le32_to_cpu(pntsd->osidoffset);
+ gsidoffset = le32_to_cpu(pntsd->gsidoffset);
dacloffset = le32_to_cpu(pntsd->dacloffset);
- dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
cifs_dbg(NOISY, "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),
+ pntsd->revision, pntsd->type, osidoffset, gsidoffset,
le32_to_cpu(pntsd->sacloffset), dacloffset);
-/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */
- rc = parse_sid(owner_sid_ptr, end_of_acl);
+ fattr->cf_uid = cifs_sb->ctx->linux_uid;
+ fattr->cf_gid = cifs_sb->ctx->linux_gid;
+
+ rc = sid_from_sd(pntsd, acl_len, osidoffset, &owner_sid_ptr);
if (rc) {
cifs_dbg(FYI, "%s: Error %d parsing Owner SID\n", __func__, rc);
return rc;
}
- rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER);
- if (rc) {
- cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n",
- __func__, rc);
- return rc;
+ if (!(sbflags & CIFS_MOUNT_OVERR_UID)) {
+ rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n",
+ __func__, rc);
+ return rc;
+ }
}
- rc = parse_sid(group_sid_ptr, end_of_acl);
+ rc = sid_from_sd(pntsd, acl_len, gsidoffset, &group_sid_ptr);
if (rc) {
- cifs_dbg(FYI, "%s: Error %d mapping Owner SID to gid\n",
+ cifs_dbg(FYI, "%s: Error %d parsing Group SID\n",
__func__, rc);
return rc;
}
- rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP);
- if (rc) {
- cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n",
- __func__, rc);
- return rc;
+ if (!(sbflags & CIFS_MOUNT_OVERR_GID)) {
+ rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n",
+ __func__, rc);
+ return rc;
+ }
}
- if (dacloffset)
+ if (dacloffset) {
+ if (!dacl_offset_valid(acl_len, dacloffset)) {
+ cifs_dbg(VFS, "Server returned illegal DACL offset\n");
+ return -EINVAL;
+ }
+
+ dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
group_sid_ptr, fattr, get_mode_from_special_sid);
- else
+ } else {
cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */
+ }
return rc;
}
@@ -1287,22 +1435,41 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
struct smb_sid *nowner_sid_ptr = NULL, *ngroup_sid_ptr = NULL;
struct smb_acl *dacl_ptr = NULL; /* no need for SACL ptr */
struct smb_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
- char *end_of_acl = ((char *)pntsd) + secdesclen;
+ char *end_of_acl;
u16 size = 0;
+ __u32 osidoffset, gsidoffset;
+
+ if (secdesclen < sizeof(struct smb_ntsd)) {
+ cifs_dbg(VFS, "ACL too small to parse security descriptor\n");
+ return -EINVAL;
+ }
+ end_of_acl = ((char *)pntsd) + secdesclen;
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
- dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
- if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
- cifs_dbg(VFS, "Server returned illegal ACL size\n");
+ if (!dacl_offset_valid(secdesclen, dacloffset)) {
+ cifs_dbg(VFS, "Server returned illegal DACL offset\n");
return -EINVAL;
}
+
+ dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
+ rc = validate_dacl(dacl_ptr, end_of_acl);
+ if (rc)
+ return rc;
}
- 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));
+ osidoffset = le32_to_cpu(pntsd->osidoffset);
+ gsidoffset = le32_to_cpu(pntsd->gsidoffset);
+ rc = sid_from_sd(pntsd, secdesclen, osidoffset, &owner_sid_ptr);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Error %d parsing Owner SID\n", __func__, rc);
+ return rc;
+ }
+ rc = sid_from_sd(pntsd, secdesclen, gsidoffset, &group_sid_ptr);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Error %d parsing Group SID\n", __func__, rc);
+ return rc;
+ }
if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
ndacloffset = sizeof(struct smb_ntsd);
@@ -1315,11 +1482,15 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr,
pnmode, mode_from_sid, posix);
+ if (rc)
+ return rc;
sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
/* copy the non-dacl portion of secdesc */
- *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset,
- NULL, NULL);
+ rc = copy_sec_desc(pntsd, pnntsd, sidsoffset, secdesclen,
+ pnsecdesclen, NULL, NULL);
+ if (rc)
+ return rc;
*aclflag |= CIFS_ACL_DACL;
} else {
@@ -1388,16 +1559,21 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
if (dacloffset) {
/* Replace ACEs for old owner with new one */
- size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr,
- owner_sid_ptr, group_sid_ptr,
- nowner_sid_ptr, ngroup_sid_ptr);
+ rc = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr,
+ owner_sid_ptr, group_sid_ptr,
+ nowner_sid_ptr, ngroup_sid_ptr,
+ aclflag, &size);
+ if (rc)
+ goto chown_chgrp_exit;
ndacl_ptr->size = cpu_to_le16(size);
}
sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
/* copy the non-dacl portion of secdesc */
- *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset,
- nowner_sid_ptr, ngroup_sid_ptr);
+ rc = copy_sec_desc(pntsd, pnntsd, sidsoffset, secdesclen,
+ pnsecdesclen, nowner_sid_ptr, ngroup_sid_ptr);
+ if (rc)
+ goto chown_chgrp_exit;
chown_chgrp_exit:
/* errors could jump here. So make sure we return soon after this */
@@ -1489,7 +1665,7 @@ struct smb_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
struct cifsFileInfo *open_file = NULL;
if (inode)
- open_file = find_readable_file(CIFS_I(inode), true);
+ open_file = find_readable_file(CIFS_I(inode), FIND_FSUID_ONLY);
if (!open_file)
return get_cifs_acl_by_path(cifs_sb, path, pacllen, info);
@@ -1605,7 +1781,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
kuid_t uid, kgid_t gid)
{
int rc = 0;
- int aclflag = CIFS_ACL_DACL; /* default flag to set */
+ int aclflag = 0;
__u32 secdesclen = 0;
__u32 nsecdesclen = 0;
__u32 dacloffset = 0;
@@ -1661,12 +1837,26 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
nsecdesclen = sizeof(struct smb_ntsd) + (sizeof(struct smb_sid) * 2);
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
+ if (!dacl_offset_valid(secdesclen, dacloffset)) {
+ cifs_dbg(VFS, "Server returned illegal DACL offset\n");
+ rc = -EINVAL;
+ goto id_mode_to_cifs_acl_exit;
+ }
+
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
- if (mode_from_sid)
- nsecdesclen +=
- le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
- else /* cifsacl */
- nsecdesclen += le16_to_cpu(dacl_ptr->size);
+ rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen);
+ if (rc) {
+ kfree(pntsd);
+ cifs_put_tlink(tlink);
+ return rc;
+ }
+ /*
+ * Worst case: every ACE is rewritten with a new SID of
+ * SID_MAX_SUB_AUTHORITIES sub-auths -> sizeof(smb_ace) each,
+ * plus the smb_acl header replace_sids_and_copy_aces() emits.
+ */
+ nsecdesclen += sizeof(struct smb_acl) +
+ le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
}
}
@@ -1677,7 +1867,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
* descriptor parameters, and security descriptor itself
*/
nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN);
- pnntsd = kmalloc(nsecdesclen, GFP_KERNEL);
+ pnntsd = kzalloc(nsecdesclen, GFP_KERNEL);
if (!pnntsd) {
kfree(pntsd);
cifs_put_tlink(tlink);
@@ -1689,14 +1879,24 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc);
- if (ops->set_acl == NULL)
- rc = -EOPNOTSUPP;
+ if (rc != 0)
+ goto id_mode_to_cifs_acl_exit;
- if (!rc) {
- /* Set the security descriptor */
- rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag);
- cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
+ if (aclflag == 0) {
+ cifs_dbg(FYI, "set_cifs_acl aclflag=0, no change mapped\n");
+ goto id_mode_to_cifs_acl_exit;
}
+
+ if (ops->set_acl == NULL) {
+ rc = -EOPNOTSUPP;
+ goto id_mode_to_cifs_acl_exit;
+ }
+
+ /* Set the security descriptor */
+ rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag);
+ cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
+
+id_mode_to_cifs_acl_exit:
cifs_put_tlink(tlink);
kfree(pnntsd);
diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c
index 3d731f3af235..71a2a59123f5 100644
--- a/fs/smb/client/cifsencrypt.c
+++ b/fs/smb/client/cifsencrypt.c
@@ -22,49 +22,33 @@
#include <linux/fips.h>
#include <linux/iov_iter.h>
#include <crypto/aead.h>
+#include <crypto/aes-cbc-macs.h>
#include <crypto/arc4.h>
#include <crypto/md5.h>
#include <crypto/sha2.h>
-static int cifs_sig_update(struct cifs_calc_sig_ctx *ctx,
- const u8 *data, size_t len)
+static size_t cifs_sig_step(void *iter_base, size_t progress, size_t len,
+ void *priv, void *priv2)
{
- if (ctx->md5) {
- md5_update(ctx->md5, data, len);
- return 0;
- }
- if (ctx->hmac) {
- hmac_sha256_update(ctx->hmac, data, len);
- return 0;
- }
- return crypto_shash_update(ctx->shash, data, len);
+ struct cifs_calc_sig_ctx *ctx = priv;
+
+ if (ctx->md5)
+ md5_update(ctx->md5, iter_base, len);
+ else if (ctx->hmac)
+ hmac_sha256_update(ctx->hmac, iter_base, len);
+ else
+ aes_cmac_update(ctx->cmac, iter_base, len);
+ return 0; /* Return value is length *not* processed, i.e. 0. */
}
-static int cifs_sig_final(struct cifs_calc_sig_ctx *ctx, u8 *out)
+static void cifs_sig_final(struct cifs_calc_sig_ctx *ctx, u8 *out)
{
- if (ctx->md5) {
+ if (ctx->md5)
md5_final(ctx->md5, out);
- return 0;
- }
- if (ctx->hmac) {
+ else if (ctx->hmac)
hmac_sha256_final(ctx->hmac, out);
- return 0;
- }
- return crypto_shash_final(ctx->shash, out);
-}
-
-static size_t cifs_sig_step(void *iter_base, size_t progress, size_t len,
- void *priv, void *priv2)
-{
- struct cifs_calc_sig_ctx *ctx = priv;
- int ret, *pret = priv2;
-
- ret = cifs_sig_update(ctx, iter_base, len);
- if (ret < 0) {
- *pret = ret;
- return len;
- }
- return 0;
+ else
+ aes_cmac_final(ctx->cmac, out);
}
/*
@@ -75,9 +59,8 @@ static int cifs_sig_iter(const struct iov_iter *iter, size_t maxsize,
{
struct iov_iter tmp_iter = *iter;
size_t did;
- int err;
- did = iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, &err,
+ did = iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, NULL,
cifs_sig_step);
if (did != maxsize)
return smb_EIO2(smb_eio_trace_sig_iter, did, maxsize);
@@ -108,11 +91,8 @@ int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
if (rc < 0)
return rc;
- rc = cifs_sig_final(ctx, signature);
- if (rc)
- cifs_dbg(VFS, "%s: Could not generate hash\n", __func__);
-
- return rc;
+ cifs_sig_final(ctx, signature);
+ return 0;
}
/* Build a proper attribute value/target info pairs blob.
@@ -269,12 +249,13 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
E_md4hash(ses->password, nt_hash, nls_cp);
hmac_md5_init_usingrawkey(&hmac_ctx, nt_hash, CIFS_NTHASH_SIZE);
+ memzero_explicit(nt_hash, sizeof(nt_hash));
/* convert ses->user_name to unicode */
len = ses->user_name ? strlen(ses->user_name) : 0;
user = kmalloc(2 + (len * 2), GFP_KERNEL);
if (user == NULL)
- return -ENOMEM;
+ goto out_nomem;
if (len) {
len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp);
@@ -292,7 +273,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
domain = kmalloc(2 + (len * 2), GFP_KERNEL);
if (domain == NULL)
- return -ENOMEM;
+ goto out_nomem;
len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len,
nls_cp);
@@ -304,7 +285,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
server = kmalloc(2 + (len * 2), GFP_KERNEL);
if (server == NULL)
- return -ENOMEM;
+ goto out_nomem;
len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len, nls_cp);
hmac_md5_update(&hmac_ctx, (const u8 *)server, 2 * len);
@@ -313,6 +294,10 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
hmac_md5_final(&hmac_ctx, ntlmv2_hash);
return 0;
+
+out_nomem:
+ memzero_explicit(&hmac_ctx, sizeof(hmac_ctx));
+ return -ENOMEM;
}
static void CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
@@ -483,6 +468,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
rc = 0;
unlock:
cifs_server_unlock(ses->server);
+ memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash));
setup_ntlmv2_rsp_ret:
kfree_sensitive(tiblob);
@@ -523,8 +509,6 @@ calc_seckey(struct cifs_ses *ses)
void
cifs_crypto_secmech_release(struct TCP_Server_Info *server)
{
- cifs_free_hash(&server->secmech.aes_cmac);
-
if (server->secmech.enc) {
crypto_free_aead(server->secmech.enc);
server->secmech.enc = NULL;
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 427558404aa5..7ecd70efdfea 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/fs.h>
+#include <linux/fs_context.h>
#include <linux/filelock.h>
#include <linux/mount.h>
#include <linux/slab.h>
@@ -30,6 +31,7 @@
#include <linux/xattr.h>
#include <linux/mm.h>
#include <linux/key-type.h>
+#include <linux/fileattr.h>
#include <uapi/linux/magic.h>
#include <net/ipv6.h>
#include "cifsfs.h"
@@ -124,42 +126,8 @@ MODULE_PARM_DESC(dir_cache_timeout, "Number of seconds to cache directory conten
/* Module-wide total cached dirents (in bytes) across all tcons */
atomic64_t cifs_dircache_bytes_used = ATOMIC64_INIT(0);
-/*
- * Write-only module parameter to drop all cached directory entries across
- * all CIFS mounts. Echo a non-zero value to trigger.
- */
-static void cifs_drop_all_dir_caches(void)
-{
- struct TCP_Server_Info *server;
- struct cifs_ses *ses;
- struct cifs_tcon *tcon;
-
- spin_lock(&cifs_tcp_ses_lock);
- list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
- list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
- if (cifs_ses_exiting(ses))
- continue;
- list_for_each_entry(tcon, &ses->tcon_list, tcon_list)
- invalidate_all_cached_dirs(tcon);
- }
- }
- spin_unlock(&cifs_tcp_ses_lock);
-}
-
-static int cifs_param_set_drop_dir_cache(const char *val, const struct kernel_param *kp)
-{
- bool bv;
- int rc = kstrtobool(val, &bv);
-
- if (rc)
- return rc;
- if (bv)
- cifs_drop_all_dir_caches();
- return 0;
-}
-
-module_param_call(drop_dir_cache, cifs_param_set_drop_dir_cache, NULL, NULL, 0200);
-MODULE_PARM_DESC(drop_dir_cache, "Write 1 to drop all cached directory entries across all CIFS mounts");
+atomic_t cifs_sillycounter;
+atomic_t cifs_tmpcounter;
#ifdef CONFIG_CIFS_STATS2
unsigned int slow_rsp_threshold = 1;
@@ -332,10 +300,28 @@ static void cifs_kill_sb(struct super_block *sb)
/*
* We need to release all dentries for the cached directories
- * before we kill the sb.
+ * and close all deferred file handles before we kill the sb.
*/
if (cifs_sb->root) {
close_all_cached_dirs(cifs_sb);
+ cifs_close_all_deferred_files_sb(cifs_sb);
+
+ /* Wait for all pending oplock breaks to complete */
+ flush_workqueue(cifsoplockd_wq);
+ /* Wait for all opened files to release */
+ flush_workqueue(deferredclose_wq);
+
+ /*
+ * Wait for all in-flight netfs I/O requests to finish their
+ * cleanup_work so that any cifsFileInfo final puts they queue
+ * to fileinfo_put_wq/serverclose_wq have been queued, then
+ * drain the workqueue so the cfile dentry refs are dropped to
+ * avoid the busy dentry warning.
+ */
+ wait_var_event(&cifs_sb->outstanding_rreq,
+ !atomic_read(&cifs_sb->outstanding_rreq));
+ flush_workqueue(serverclose_wq);
+ flush_workqueue(fileinfo_put_wq);
/* finally release root dentry */
dput(cifs_sb->root);
@@ -454,6 +440,7 @@ cifs_alloc_inode(struct super_block *sb)
return NULL;
cifs_inode->cifsAttrs = ATTR_ARCHIVE; /* default */
cifs_inode->time = 0;
+ cifs_inode->time_last_write = 0;
/*
* Until the file is open and we have gotten oplock info back from the
* server, can not assume caching of file data or metadata.
@@ -464,7 +451,8 @@ cifs_alloc_inode(struct super_block *sb)
spin_lock_init(&cifs_inode->writers_lock);
cifs_inode->writers = 0;
cifs_inode->netfs.inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */
- cifs_inode->netfs.remote_i_size = 0;
+ cifs_inode->netfs._remote_i_size = 0;
+ cifs_inode->netfs._zero_point = 0;
cifs_inode->uniqueid = 0;
cifs_inode->createtime = 0;
cifs_inode->epoch = 0;
@@ -704,6 +692,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
seq_puts(s, ",seal");
else if (tcon->ses->server->ignore_signature)
seq_puts(s, ",signloosely");
+ if (cifs_sb->ctx->compress)
+ seq_puts(s, ",compress");
if (tcon->nocase)
seq_puts(s, ",nocase");
if (tcon->nodelete)
@@ -868,7 +858,6 @@ static void cifs_umount_begin(struct super_block *sb)
spin_unlock(&tcon->tc_lock);
spin_unlock(&cifs_tcp_ses_lock);
- cifs_close_all_deferred_files(tcon);
/* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
/* cancel_notify_requests(tcon); */
if (tcon->ses && tcon->ses->server) {
@@ -997,26 +986,19 @@ cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb)
return dentry;
}
-static int cifs_set_super(struct super_block *sb, void *data)
-{
- struct cifs_mnt_data *mnt_data = data;
- sb->s_fs_info = mnt_data->cifs_sb;
- return set_anon_super(sb, NULL);
-}
-
struct dentry *
-cifs_smb3_do_mount(struct file_system_type *fs_type,
- int flags, struct smb3_fs_context *old_ctx)
+cifs_smb3_do_mount(struct fs_context *fc, struct smb3_fs_context *old_ctx)
{
struct cifs_mnt_data mnt_data;
struct cifs_sb_info *cifs_sb;
struct super_block *sb;
struct dentry *root;
+ unsigned int saved_sb_flags;
int rc;
if (cifsFYI) {
- cifs_dbg(FYI, "%s: devname=%s flags=0x%x\n", __func__,
- old_ctx->source, flags);
+ cifs_dbg(FYI, "%s: devname=%s sb_flags=0x%x\n", __func__,
+ old_ctx->source, fc->sb_flags);
} else {
cifs_info("Attempting to mount %s\n", old_ctx->source);
}
@@ -1043,7 +1025,7 @@ cifs_smb3_do_mount(struct file_system_type *fs_type,
rc = cifs_mount(cifs_sb, cifs_sb->ctx);
if (rc) {
- if (!(flags & SB_SILENT))
+ if (!(fc->sb_flags & SB_SILENT))
cifs_dbg(VFS, "cifs_mount failed w/return code = %d\n",
rc);
root = ERR_PTR(rc);
@@ -1052,12 +1034,27 @@ cifs_smb3_do_mount(struct file_system_type *fs_type,
mnt_data.ctx = cifs_sb->ctx;
mnt_data.cifs_sb = cifs_sb;
- mnt_data.flags = flags;
-
- /* BB should we make this contingent on mount parm? */
- flags |= SB_NODIRATIME | SB_NOATIME;
+ mnt_data.flags = 0;
- sb = sget(fs_type, cifs_match_super, cifs_set_super, flags, &mnt_data);
+ /*
+ * sb->s_flags is set from fc->sb_flags by alloc_super(). CIFS has
+ * historically forced SB_NODIRATIME | SB_NOATIME on every mount and
+ * ignored the caller-supplied SB_* flags. Preserve that behaviour by
+ * overriding fc->sb_flags around the sget_fc() call.
+ *
+ * Hand cifs_sb to sget_fc() via fc->s_fs_info; sget_fc() copies it
+ * onto sb->s_fs_info before running set() and clears fc->s_fs_info
+ * on successful publish. Pass the rest of the per-mount context to
+ * cifs_match_super() through fc->sget_key.
+ */
+ saved_sb_flags = fc->sb_flags;
+ fc->sb_flags = SB_NODIRATIME | SB_NOATIME;
+ fc->s_fs_info = cifs_sb;
+ fc->sget_key = &mnt_data;
+ sb = sget_fc(fc, cifs_match_super, set_anon_super_fc);
+ fc->sget_key = NULL;
+ fc->s_fs_info = NULL;
+ fc->sb_flags = saved_sb_flags;
if (IS_ERR(sb)) {
cifs_umount(cifs_sb);
return ERR_CAST(sb);
@@ -1193,9 +1190,60 @@ struct file_system_type smb3_fs_type = {
MODULE_ALIAS_FS("smb3");
MODULE_ALIAS("smb3");
+int cifs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ struct inode *inode = d_inode(dentry);
+ u32 attrs;
+
+ /* Preserve FS_COMPR_FL previously reported by cifs_ioctl(). */
+ if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED)
+ fa->flags |= FS_COMPR_FL;
+
+ /*
+ * FS_CASEFOLD_FL is defined by UAPI as a folder attribute,
+ * and userspace tools (e.g., lsattr) display it only on
+ * directories. Confine the case-handling bits to directories
+ * to match that convention; for non-directories the share's
+ * case semantics are still discoverable through the parent.
+ */
+ if (!S_ISDIR(inode->i_mode))
+ return 0;
+
+ /*
+ * The server's FS_ATTRIBUTE_INFORMATION response, cached on
+ * the tcon at mount, reflects the share's case-handling
+ * semantics after any POSIX extensions negotiation. Prefer
+ * it over the client-local nocase mount option, which only
+ * governs dentry comparison on this superblock.
+ *
+ * QueryFSInfo is best-effort at mount; when it did not
+ * populate fsAttrInfo, MaxPathNameComponentLength remains
+ * zero. In that case fall back to nocase so the reporting
+ * matches the comparison behavior installed on the sb.
+ */
+ if (le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength) == 0) {
+ if (tcon->nocase) {
+ fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
+ fa->flags |= FS_CASEFOLD_FL;
+ }
+ return 0;
+ }
+ attrs = le32_to_cpu(tcon->fsAttrInfo.Attributes);
+ if (!(attrs & FILE_CASE_SENSITIVE_SEARCH)) {
+ fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
+ fa->flags |= FS_CASEFOLD_FL;
+ }
+ if (!(attrs & FILE_CASE_PRESERVED_NAMES))
+ fa->fsx_xflags |= FS_XFLAG_CASENONPRESERVING;
+ return 0;
+}
+
const struct inode_operations cifs_dir_inode_ops = {
.create = cifs_create,
.atomic_open = cifs_atomic_open,
+ .tmpfile = cifs_tmpfile,
.lookup = cifs_lookup,
.getattr = cifs_getattr,
.unlink = cifs_unlink,
@@ -1210,6 +1258,7 @@ const struct inode_operations cifs_dir_inode_ops = {
.listxattr = cifs_listxattr,
.get_acl = cifs_get_acl,
.set_acl = cifs_set_acl,
+ .fileattr_get = cifs_fileattr_get,
};
const struct inode_operations cifs_file_inode_ops = {
@@ -1220,6 +1269,7 @@ const struct inode_operations cifs_file_inode_ops = {
.fiemap = cifs_fiemap,
.get_acl = cifs_get_acl,
.set_acl = cifs_set_acl,
+ .fileattr_get = cifs_fileattr_get,
};
const char *cifs_get_link(struct dentry *dentry, struct inode *inode,
@@ -1266,7 +1316,7 @@ static int cifs_precopy_set_eof(struct inode *src_inode, struct cifsInodeInfo *s
struct cifsFileInfo *writeable_srcfile;
int rc = -EINVAL;
- writeable_srcfile = find_writable_file(src_cifsi, FIND_WR_FSUID_ONLY);
+ writeable_srcfile = find_writable_file(src_cifsi, FIND_FSUID_ONLY);
if (writeable_srcfile) {
if (src_tcon->ses->server->ops->set_file_size)
rc = src_tcon->ses->server->ops->set_file_size(
@@ -1333,7 +1383,8 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
struct cifsFileInfo *smb_file_src = src_file->private_data;
struct cifsFileInfo *smb_file_target = dst_file->private_data;
struct cifs_tcon *target_tcon, *src_tcon;
- unsigned long long destend, fstart, fend, old_size, new_size;
+ unsigned long long i_size, new_size;
+ unsigned long long destend, fstart, fend;
unsigned int xid;
int rc;
@@ -1362,8 +1413,19 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
*/
lock_two_nondirectories(target_inode, src_inode);
- if (len == 0)
- len = src_inode->i_size - off;
+ if (len == 0) {
+ loff_t src_size = i_size_read(src_inode);
+
+ if (off > src_size) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+ len = src_size - off;
+ if (!len) {
+ rc = 0;
+ goto unlock;
+ }
+ }
cifs_dbg(FYI, "clone range\n");
@@ -1377,7 +1439,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
* Advance the EOF marker after the flush above to the end of the range
* if it's short of that.
*/
- if (src_cifsi->netfs.remote_i_size < off + len) {
+ if (netfs_read_remote_i_size(src_inode) < off + len) {
rc = cifs_precopy_set_eof(src_inode, src_cifsi, src_tcon, xid, off + len);
if (rc < 0)
goto unlock;
@@ -1398,26 +1460,24 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
rc = cifs_flush_folio(target_inode, destend, &fstart, &fend, false);
if (rc)
goto unlock;
- if (fend > target_cifsi->netfs.zero_point)
- target_cifsi->netfs.zero_point = fend + 1;
- old_size = target_cifsi->netfs.remote_i_size;
+
+ spin_lock(&target_inode->i_lock);
+ if (fend > target_cifsi->netfs._zero_point)
+ netfs_write_zero_point(target_inode, fend + 1);
+ i_size = target_inode->i_size;
+ spin_unlock(&target_inode->i_lock);
/* Discard all the folios that overlap the destination region. */
cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend);
truncate_inode_pages_range(&target_inode->i_data, fstart, fend);
- fscache_invalidate(cifs_inode_cookie(target_inode), NULL,
- i_size_read(target_inode), 0);
+ fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size, 0);
rc = -EOPNOTSUPP;
if (target_tcon->ses->server->ops->duplicate_extents) {
rc = target_tcon->ses->server->ops->duplicate_extents(xid,
smb_file_src, smb_file_target, off, len, destoff);
- if (rc == 0 && new_size > old_size) {
- truncate_setsize(target_inode, new_size);
- fscache_resize_cookie(cifs_inode_cookie(target_inode),
- new_size);
- } else if (rc == -EOPNOTSUPP) {
+ if (rc == -EOPNOTSUPP) {
/*
* copy_file_range syscall man page indicates EINVAL
* is returned e.g when "fd_in and fd_out refer to the
@@ -1432,13 +1492,21 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
rc = -EINVAL;
}
}
- if (rc == 0 && new_size > target_cifsi->netfs.zero_point)
- target_cifsi->netfs.zero_point = new_size;
+ if (rc == 0) {
+ spin_lock(&target_inode->i_lock);
+ if (new_size > target_cifsi->netfs._zero_point)
+ netfs_write_zero_point(target_inode, new_size);
+ spin_unlock(&target_inode->i_lock);
+ }
}
- /* force revalidate of size and timestamps of target file now
- that target is updated on the server */
- CIFS_I(target_inode)->time = 0;
+ /*
+ * On success, duplicate_extents already updated the target inode attrs
+ * or marked them stale if the refresh failed. On failure, mark attrs
+ * stale because EOF may have changed before the clone failed.
+ */
+ if (rc)
+ CIFS_I(target_inode)->time = 0;
unlock:
/* although unlocking in the reverse order from locking is not
strictly necessary here it is a little cleaner to be consistent */
@@ -1504,7 +1572,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
* Advance the EOF marker after the flush above to the end of the range
* if it's short of that.
*/
- if (src_cifsi->netfs.remote_i_size < off + len) {
+ if (netfs_read_remote_i_size(src_inode) < off + len) {
rc = cifs_precopy_set_eof(src_inode, src_cifsi, src_tcon, xid, off + len);
if (rc < 0)
goto unlock;
@@ -1532,8 +1600,12 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
fscache_resize_cookie(cifs_inode_cookie(target_inode),
i_size_read(target_inode));
}
- if (rc > 0 && destoff + rc > target_cifsi->netfs.zero_point)
- target_cifsi->netfs.zero_point = destoff + rc;
+ if (rc > 0) {
+ spin_lock(&target_inode->i_lock);
+ if (destoff + rc > target_cifsi->netfs._zero_point)
+ netfs_write_zero_point(target_inode, destoff + rc);
+ spin_unlock(&target_inode->i_lock);
+ }
}
file_accessed(src_file);
@@ -1631,7 +1703,7 @@ const struct file_operations cifs_file_strict_ops = {
const struct file_operations cifs_file_direct_ops = {
.read_iter = netfs_unbuffered_read_iter,
- .write_iter = netfs_file_write_iter,
+ .write_iter = cifs_direct_write_iter,
.open = cifs_open,
.release = cifs_close,
.lock = cifs_lock,
@@ -1687,7 +1759,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
const struct file_operations cifs_file_direct_nobrl_ops = {
.read_iter = netfs_unbuffered_read_iter,
- .write_iter = netfs_file_write_iter,
+ .write_iter = cifs_direct_write_iter,
.open = cifs_open,
.release = cifs_close,
.fsync = cifs_fsync,
@@ -1908,6 +1980,12 @@ init_cifs(void)
{
int rc = 0;
+#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
+ rc = smb1_init_maperror();
+ if (rc)
+ return rc;
+#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
+
rc = smb2_init_maperror();
if (rc)
return rc;
@@ -2145,10 +2223,8 @@ MODULE_DESCRIPTION
("VFS to access SMB3 servers e.g. Samba, Macs, Azure and Windows (and "
"also older servers complying with the SNIA CIFS Specification)");
MODULE_VERSION(CIFS_VERSION);
-MODULE_SOFTDEP("ecb");
MODULE_SOFTDEP("nls");
MODULE_SOFTDEP("aes");
-MODULE_SOFTDEP("cmac");
MODULE_SOFTDEP("aead2");
MODULE_SOFTDEP("ccm");
MODULE_SOFTDEP("gcm");
diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h
index e320d39b01f5..0c85daa8386e 100644
--- a/fs/smb/client/cifsfs.h
+++ b/fs/smb/client/cifsfs.h
@@ -10,9 +10,13 @@
#define _CIFSFS_H
#include <linux/hash.h>
+#include <linux/dcache.h>
#define ROOT_I 2
+extern atomic_t cifs_sillycounter;
+extern atomic_t cifs_tmpcounter;
+
/*
* ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down
* so that it will fit. We use hash_64 to convert the value to 31 bits, and
@@ -49,10 +53,12 @@ void cifs_sb_deactive(struct super_block *sb);
/* Functions related to inodes */
extern const struct inode_operations cifs_dir_inode_ops;
struct inode *cifs_root_iget(struct super_block *sb);
-int cifs_create(struct mnt_idmap *idmap, struct inode *inode,
- struct dentry *direntry, umode_t mode, bool excl);
-int cifs_atomic_open(struct inode *inode, struct dentry *direntry,
+int cifs_create(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *direntry, umode_t mode);
+int cifs_atomic_open(struct inode *dir, struct dentry *direntry,
struct file *file, unsigned int oflags, umode_t mode);
+int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
+ struct file *file, umode_t mode);
struct dentry *cifs_lookup(struct inode *parent_dir_inode,
struct dentry *direntry, unsigned int flags);
int cifs_unlink(struct inode *dir, struct dentry *dentry);
@@ -83,6 +89,9 @@ extern const struct inode_operations cifs_file_inode_ops;
extern const struct inode_operations cifs_symlink_inode_ops;
extern const struct inode_operations cifs_namespace_inode_operations;
+struct file_kattr;
+int cifs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
+
/* Functions related to files and directories */
extern const struct netfs_request_ops cifs_req_ops;
@@ -98,6 +107,7 @@ int cifs_closedir(struct inode *inode, struct file *file);
ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to);
ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from);
ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from);
+ssize_t cifs_direct_write_iter(struct kiocb *iocb, struct iov_iter *from);
ssize_t cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter);
int cifs_flock(struct file *file, int cmd, struct file_lock *fl);
int cifs_lock(struct file *file, int cmd, struct file_lock *flock);
@@ -137,16 +147,26 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, struct file *src_file,
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg);
void cifs_setsize(struct inode *inode, loff_t offset);
+void cifs_resize_file_locked(struct inode *inode, loff_t offset);
+struct fs_context;
struct smb3_fs_context;
-struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type, int flags,
+struct dentry *cifs_smb3_do_mount(struct fs_context *fc,
struct smb3_fs_context *old_ctx);
+char *cifs_silly_fullpath(struct dentry *dentry);
+
+#define CIFS_TMPNAME_PREFIX ".__smbfile_tmp"
+#define CIFS_TMPNAME_LEN (DNAME_INLINE_LEN - 1)
+
+#define CIFS_SILLYNAME_PREFIX ".__smbfile_silly"
+#define CIFS_SILLYNAME_LEN (DNAME_INLINE_LEN - 1)
+
#ifdef CONFIG_CIFS_NFSD_EXPORT
extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */
/* when changing internal version - update following two lines at same time */
-#define SMB3_PRODUCT_BUILD 59
-#define CIFS_VERSION "2.59"
+#define SMB3_PRODUCT_BUILD 61
+#define CIFS_VERSION "2.61"
#endif /* _CIFSFS_H */
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 6f9b6c72962b..79e4e84f8985 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -20,9 +20,9 @@
#include <linux/utsname.h>
#include <linux/sched/mm.h>
#include <linux/netfs.h>
+#include <linux/fcntl.h>
#include "cifs_fs_sb.h"
#include "cifsacl.h"
-#include <crypto/internal/hash.h>
#include <uapi/linux/cifs/cifs_mount.h>
#include "../common/smbglob.h"
#include "../common/smb2pdu.h"
@@ -220,10 +220,8 @@ struct session_key {
char *response;
};
-/* crypto hashing related structure/fields, not specific to a sec mech */
+/* encryption related structure/fields, not specific to a sec mech */
struct cifs_secmech {
- struct shash_desc *aes_cmac; /* block-cipher based MAC function, for SMB3 signatures */
-
struct crypto_aead *enc; /* smb3 encryption AEAD TFM (AES-CCM and AES-GCM) */
struct crypto_aead *dec; /* smb3 decryption AEAD TFM (AES-CCM and AES-GCM) */
};
@@ -252,6 +250,7 @@ struct cifs_open_info_data {
bool adjust_tz;
bool reparse_point;
bool contains_posix_file_info;
+ bool unknown_nlink;
struct {
/* ioctl response buffer */
struct {
@@ -427,7 +426,7 @@ struct smb_version_operations {
int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *,
const unsigned int);
int (*set_compression)(const unsigned int, struct cifs_tcon *,
- struct cifsFileInfo *);
+ struct cifsFileInfo *, __u16);
/* check if we can send an echo or nor */
bool (*can_echo)(struct TCP_Server_Info *);
/* send echo request */
@@ -791,6 +790,8 @@ struct TCP_Server_Info {
struct {
bool requested; /* "compress" mount option set*/
bool enabled; /* actually negotiated with server */
+ bool chained; /* chained transforms were negotiated */
+ bool pattern; /* Pattern_V1 chained payloads were negotiated */
__le16 alg; /* preferred alg negotiated with server */
} compression;
__u16 signing_algorithm;
@@ -1393,6 +1394,7 @@ struct cifs_search_info {
bool emptyDir:1;
bool unicode:1;
bool smallBuf:1; /* so we know which buf_release function to call */
+ bool is_dynamic_buf:1; /* dynamically allocated buffer - can be variable size */
};
#define ACL_NO_MODE ((umode_t)(-1))
@@ -1533,9 +1535,16 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
#define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG)
#define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG)
-/*
- * One of these for each file inode
- */
+enum cifs_inode_flags {
+ CIFS_INODE_PENDING_OPLOCK_BREAK, /* oplock break in progress */
+ CIFS_INODE_PENDING_WRITERS, /* Writes in progress */
+ CIFS_INODE_FLAG_UNUSED, /* Unused flag */
+ CIFS_INO_DELETE_PENDING, /* delete pending on server */
+ CIFS_INO_INVALID_MAPPING, /* pagecache is invalid */
+ CIFS_INO_LOCK, /* lock bit for synchronization */
+ CIFS_INO_TMPFILE, /* for O_TMPFILE inodes */
+ CIFS_INO_CLOSE_ON_LOCK, /* Not to defer the close when lock is set */
+};
struct cifsInodeInfo {
struct netfs_inode netfs; /* Netfslib context and vfs inode */
@@ -1553,17 +1562,11 @@ struct cifsInodeInfo {
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
unsigned int oplock; /* oplock/lease level we have */
__u16 epoch; /* used to track lease state changes */
-#define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */
-#define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */
-#define CIFS_INODE_FLAG_UNUSED (2) /* Unused flag */
-#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */
-#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */
-#define CIFS_INO_LOCK (5) /* lock bit for synchronization */
-#define CIFS_INO_CLOSE_ON_LOCK (7) /* Not to defer the close when lock is set */
unsigned long flags;
spinlock_t writers_lock;
unsigned int writers; /* Number of writers on this inode */
unsigned long time; /* jiffies of last update of inode */
+ unsigned long time_last_write; /* jiffies of last writable close or truncate */
u64 uniqueid; /* server inode number */
u64 createtime; /* creation time on server */
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */
@@ -1884,12 +1887,12 @@ static inline bool is_replayable_error(int error)
}
-/* cifs_get_writable_file() flags */
-enum cifs_writable_file_flags {
- FIND_WR_ANY = 0U,
- FIND_WR_FSUID_ONLY = (1U << 0),
- FIND_WR_WITH_DELETE = (1U << 1),
- FIND_WR_NO_PENDING_DELETE = (1U << 2),
+enum cifs_find_flags {
+ FIND_ANY = 0U,
+ FIND_FSUID_ONLY = (1U << 0),
+ FIND_WITH_DELETE = (1U << 1),
+ FIND_NO_PENDING_DELETE = (1U << 2),
+ FIND_OPEN_FLAGS = (1U << 3),
};
#define MID_FREE 0
@@ -1906,6 +1909,7 @@ enum cifs_writable_file_flags {
#define CIFS_NO_BUFFER 0 /* Response buffer not returned */
#define CIFS_SMALL_BUFFER 1
#define CIFS_LARGE_BUFFER 2
+#define CIFS_DYNAMIC_BUFFER 3 /* Dynamically allocated buffer */
#define CIFS_IOVEC 4 /* array of response buffers */
/* Type of Request to SendReceive2 */
@@ -2258,6 +2262,7 @@ struct smb2_compound_vars {
struct kvec qi_iov;
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
+ struct kvec hl_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec unlink_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec rename_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec close_iov;
@@ -2322,7 +2327,7 @@ static inline void mid_execute_callback(struct TCP_Server_Info *server,
struct cifs_calc_sig_ctx {
struct md5_ctx *md5;
struct hmac_sha256_ctx *hmac;
- struct shash_desc *shash;
+ struct aes_cmac_ctx *cmac;
};
#define CIFS_RECONN_DELAY_SECS 30
@@ -2375,4 +2380,25 @@ static inline bool cifs_forced_shutdown(const struct cifs_sb_info *sbi)
return cifs_sb_flags(sbi) & CIFS_MOUNT_SHUTDOWN;
}
+static inline int cifs_open_create_options(unsigned int oflags, int opts)
+{
+ /* O_SYNC also has bit for O_DSYNC so following check picks up either */
+ if (oflags & O_SYNC)
+ opts |= CREATE_WRITE_THROUGH;
+ if (oflags & O_DIRECT)
+ opts |= CREATE_NO_BUFFER;
+ if (oflags & O_TMPFILE)
+ opts |= CREATE_DELETE_ON_CLOSE;
+ return opts;
+}
+
+/*
+ * inode->i_blocks is counted in 512-byte units, independent of
+ * inode->i_blksize.
+ */
+#define CIFS_INO_BLOCK_SIZE 512ULL
+#define CIFS_INO_BLOCKS(size) \
+ DIV_ROUND_UP_ULL((u64)(size), CIFS_INO_BLOCK_SIZE)
+#define CIFS_INO_BYTES(blocks) ((u64)(blocks) * CIFS_INO_BLOCK_SIZE)
+
#endif /* _CIFS_GLOB_H */
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index 96d6b5325aa3..00168839c123 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -19,6 +19,7 @@
struct statfs;
struct smb_rqst;
struct smb3_fs_context;
+struct fs_context;
/*
*****************************************************************
@@ -89,7 +90,6 @@ int cifs_handle_standard(struct TCP_Server_Info *server,
struct mid_q_entry *mid);
char *smb3_fs_context_fullpath(const struct smb3_fs_context *ctx, char dirsep);
int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx);
-int smb3_parse_opt(const char *options, const char *key, char **val);
int cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs);
bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs);
int cifs_discard_remaining_data(struct TCP_Server_Info *server);
@@ -138,12 +138,15 @@ void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata,
ssize_t result);
struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
int flags);
-int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags,
+int __cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
+ unsigned int find_flags, unsigned int open_flags,
+ struct cifsFileInfo **ret_file);
+int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
+ struct inode *inode, int flags,
struct cifsFileInfo **ret_file);
-int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, int flags,
- struct cifsFileInfo **ret_file);
-struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
- bool fsuid_only);
+struct cifsFileInfo *__find_readable_file(struct cifsInodeInfo *cifs_inode,
+ unsigned int find_flags,
+ unsigned int open_flags);
int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
struct cifsFileInfo **ret_file);
int cifs_get_hardlink_path(struct cifs_tcon *tcon, struct inode *inode,
@@ -233,7 +236,7 @@ void cifs_mount_put_conns(struct cifs_mount_ctx *mnt_ctx);
int cifs_mount_get_session(struct cifs_mount_ctx *mnt_ctx);
int cifs_is_path_remote(struct cifs_mount_ctx *mnt_ctx);
int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx);
-int cifs_match_super(struct super_block *sb, void *data);
+int cifs_match_super(struct super_block *sb, struct fs_context *fc);
int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx);
void cifs_umount(struct cifs_sb_info *cifs_sb);
void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
@@ -261,6 +264,7 @@ void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode);
void cifs_close_all_deferred_files(struct cifs_tcon *tcon);
+void cifs_close_all_deferred_files_sb(struct cifs_sb_info *cifs_sb);
void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
struct dentry *dentry);
@@ -313,7 +317,7 @@ int generate_smb311signingkey(struct cifs_ses *ses,
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
-void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb);
+void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb, const char *reason, int rc);
bool couldbe_mf_symlink(const struct cifs_fattr *fattr);
int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
@@ -347,9 +351,6 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *server,
enum securityEnum requested);
-int cifs_alloc_hash(const char *name, struct shash_desc **sdesc);
-void cifs_free_hash(struct shash_desc **sdesc);
-
int cifs_try_adding_channels(struct cifs_ses *ses);
int smb3_update_ses_channels(struct cifs_ses *ses,
struct TCP_Server_Info *server,
@@ -595,4 +596,20 @@ static inline void cifs_sg_set_buf(struct sg_table *sgtable,
}
}
+static inline int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
+ unsigned int find_flags,
+ struct cifsFileInfo **ret_file)
+{
+ find_flags &= ~FIND_OPEN_FLAGS;
+ return __cifs_get_writable_file(cifs_inode, find_flags, 0, ret_file);
+}
+
+static inline struct cifsFileInfo *
+find_readable_file(struct cifsInodeInfo *cinode, unsigned int find_flags)
+{
+ find_flags &= ~FIND_OPEN_FLAGS;
+ find_flags |= FIND_NO_PENDING_DELETE;
+ return __find_readable_file(cinode, find_flags, 0);
+}
+
#endif /* _CIFSPROTO_H */
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index 3990a9012264..6dddbd84b93b 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -615,6 +615,11 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
tcon->tid = smb_buffer_response->Tid;
bcc_ptr = pByteArea(smb_buffer_response);
bytes_left = get_bcc(smb_buffer_response);
+ if (bytes_left < 2) {
+ rc = smb_EIO2(smb_eio_trace_tcon_bcc_too_small,
+ bytes_left, 2);
+ goto out;
+ }
length = strnlen(bcc_ptr, bytes_left - 2);
if (smb_buffer->Flags2 & SMBFLG2_UNICODE)
is_unicode = true;
@@ -670,6 +675,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
reset_cifs_unix_caps(xid, tcon, NULL, NULL);
}
}
+out:
cifs_buf_release(smb_buffer);
return rc;
}
@@ -1465,6 +1471,7 @@ cifs_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
struct cifs_io_subrequest *rdata = mid->callback_data;
struct netfs_inode *ictx = netfs_inode(rdata->rreq->inode);
struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink);
+ struct inode *inode = &ictx->inode;
struct smb_rqst rqst = { .rq_iov = rdata->iov,
.rq_nvec = 1,
.rq_iter = rdata->subreq.io_iter };
@@ -1538,7 +1545,7 @@ do_retry:
} else {
size_t trans = rdata->subreq.transferred + rdata->got_bytes;
if (trans < rdata->subreq.len &&
- rdata->subreq.start + trans >= ictx->remote_i_size) {
+ rdata->subreq.start + trans >= netfs_read_remote_i_size(inode)) {
rdata->result = 0;
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
} else if (rdata->got_bytes > 0) {
@@ -1680,8 +1687,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
/* tcon and ses pointer are checked in smb_init */
- if (tcon->ses->server == NULL)
+ if (!tcon->ses->server) {
+ cifs_small_buf_release(pSMB);
return -ECONNABORTED;
+ }
pSMB->AndXCommand = 0xFF; /* none */
pSMB->Fid = netfid;
@@ -1710,8 +1719,17 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
pSMBr = (READ_RSP *)rsp_iov.iov_base;
if (rc) {
cifs_dbg(VFS, "Send error in read = %d\n", rc);
+ } else if (rsp_iov.iov_len < tcon->ses->server->vals->read_rsp_size) {
+ /* check that the received response can hold a whole READ_RSP */
+ cifs_dbg(FYI, "%s: server returned short header. got=%zu expected=%zu\n",
+ __func__, rsp_iov.iov_len,
+ tcon->ses->server->vals->read_rsp_size);
+ rc = smb_EIO2(smb_eio_trace_read_rsp_short,
+ rsp_iov.iov_len, tcon->ses->server->vals->read_rsp_size);
+ *nbytes = 0;
} else {
- int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
+ unsigned int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
+ __u16 data_offset = le16_to_cpu(pSMBr->DataOffset);
data_length = data_length << 16;
data_length += le16_to_cpu(pSMBr->DataLength);
*nbytes = data_length;
@@ -1719,14 +1737,21 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
/*check that DataLength would not go beyond end of SMB */
if ((data_length > CIFSMaxBufSize)
|| (data_length > count)) {
- cifs_dbg(FYI, "bad length %d for count %d\n",
- data_length, count);
+ cifs_dbg(FYI, "%s: bad length %u for count %u\n",
+ __func__, data_length, count);
rc = smb_EIO2(smb_eio_trace_read_overlarge,
data_length, count);
*nbytes = 0;
+ } else if (data_offset < sizeof(*pSMBr) ||
+ (size_t)data_offset + data_length > rsp_iov.iov_len) {
+ /* check that the data lies within the received response */
+ cifs_dbg(FYI, "%s: bad data offset %u length %u for response of %zu\n",
+ __func__, data_offset, data_length, rsp_iov.iov_len);
+ rc = smb_EIO2(smb_eio_trace_read_bad_offset,
+ data_offset, data_length);
+ *nbytes = 0;
} else {
- pReadData = (char *) (&pSMBr->hdr.Protocol) +
- le16_to_cpu(pSMBr->DataOffset);
+ pReadData = (char *) (&pSMBr->hdr.Protocol) + data_offset;
/* if (rc = copy_to_user(buf, pReadData, data_length)) {
cifs_dbg(VFS, "Faulting on read rc = %d\n",rc);
rc = -EFAULT;
@@ -1795,8 +1820,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
/* tcon and ses pointer are checked in smb_init */
- if (tcon->ses->server == NULL)
+ if (!tcon->ses->server) {
+ cifs_buf_release(pSMB);
return -ECONNABORTED;
+ }
pSMB->AndXCommand = 0xFF; /* none */
pSMB->Fid = netfid;
@@ -2076,8 +2103,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
/* tcon and ses pointer are checked in smb_init */
- if (tcon->ses->server == NULL)
+ if (!tcon->ses->server) {
+ cifs_small_buf_release(pSMB);
return -ECONNABORTED;
+ }
pSMB->AndXCommand = 0xFF; /* none */
pSMB->Fid = netfid;
@@ -3051,7 +3080,7 @@ int cifs_query_reparse_point(const unsigned int xid,
end = 2 + get_bcc(&io_rsp->hdr) + (__u8 *)&io_rsp->ByteCount;
start = (__u8 *)&io_rsp->hdr.Protocol + data_offset;
- if (start >= end) {
+ if (start >= end || (size_t)(end - start) < sizeof(*buf)) {
rc = smb_EIO2(smb_eio_trace_qreparse_data_area,
(unsigned long)start - (unsigned long)io_rsp,
(unsigned long)end - (unsigned long)io_rsp);
@@ -3206,7 +3235,7 @@ out_close:
int
CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
- __u16 fid)
+ __u16 fid, __u16 compression_state)
{
int rc = 0;
int bytes_returned;
@@ -3221,7 +3250,7 @@ CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
in_len = rc;
- pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
+ pSMB->compression_state = cpu_to_le16(compression_state);
pSMB->TotalParameterCount = 0;
pSMB->TotalDataCount = cpu_to_le32(2);
@@ -3542,6 +3571,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count, data_count, param_offset, offset;
+ size_t cifs_acl_size, bytes_available;
cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
setAclRetry:
@@ -3561,8 +3591,7 @@ setAclRetry:
}
params = 6 + name_len;
pSMB->MaxParameterCount = cpu_to_le16(2);
- /* BB find max SMB size from sess */
- pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX));
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
@@ -3574,6 +3603,15 @@ setAclRetry:
parm_data = ((char *)pSMB) + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ /* make sure we can fit the larger cifs_posix_aces in the buffer */
+ cifs_acl_size = sizeof(struct cifs_posix_acl) +
+ (acl->a_count * sizeof(struct cifs_posix_ace));
+ bytes_available = (CIFSMaxBufSize + MAX_HEADER_SIZE(tcon->ses->server)) - offset;
+ if (cifs_acl_size > bytes_available || cifs_acl_size > USHRT_MAX) {
+ rc = -E2BIG;
+ goto setACLerrorExit;
+ }
+
/* convert to on the wire format for POSIX ACL */
data_count = posix_acl_to_cifs(parm_data, acl, acl_type);
@@ -5828,38 +5866,6 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
}
-static int
-CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon,
- const char *fileName, const FILE_BASIC_INFO *data,
- const struct nls_table *nls_codepage,
- struct cifs_sb_info *cifs_sb)
-{
- int oplock = 0;
- struct cifs_open_parms oparms;
- struct cifs_fid fid;
- int rc;
-
- oparms = (struct cifs_open_parms) {
- .tcon = tcon,
- .cifs_sb = cifs_sb,
- .desired_access = GENERIC_WRITE,
- .create_options = cifs_create_options(cifs_sb, 0),
- .disposition = FILE_OPEN,
- .path = fileName,
- .fid = &fid,
- };
-
- rc = CIFS_open(xid, &oparms, &oplock, NULL);
- if (rc)
- goto out;
-
- rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid);
- CIFSSMBClose(xid, tcon, fid.netfid);
-out:
-
- return rc;
-}
-
int
CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const FILE_BASIC_INFO *data,
@@ -5938,10 +5944,6 @@ SetTimesRetry:
if (rc == -EAGAIN)
goto SetTimesRetry;
- if (rc == -EOPNOTSUPP)
- return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data,
- nls_codepage, cifs_sb);
-
return rc;
}
@@ -6348,8 +6350,10 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
int name_len;
int rc = 0;
int bytes_returned = 0;
- __u16 params, param_offset, byte_count, offset, count;
+ __u16 params, param_offset;
+ unsigned int byte_count, offset, count;
int remap = cifs_remap(cifs_sb);
+ unsigned int total_len;
cifs_dbg(FYI, "In SetEA\n");
SetEARetry:
@@ -6401,6 +6405,13 @@ SetEARetry:
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
byte_count = 3 /* pad */ + params + count;
+ if (check_add_overflow(in_len, byte_count, &total_len) ||
+ byte_count > U16_MAX ||
+ total_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cifs_dbg(VFS, "EA request too large: %u bytes\n", total_len);
+ cifs_buf_release(pSMB);
+ return -E2BIG;
+ }
pSMB->DataCount = cpu_to_le16(count);
parm_data->list_len = cpu_to_le32(count);
parm_data->list.EA_flags = 0;
diff --git a/fs/smb/client/compress.c b/fs/smb/client/compress.c
index 3d1e73f5d9af..8f0860970741 100644
--- a/fs/smb/client/compress.c
+++ b/fs/smb/client/compress.c
@@ -22,7 +22,7 @@
#include "cifsproto.h"
#include "smb2proto.h"
-#include "compress/lz77.h"
+#include "../common/compress/lz77.h"
#include "compress.h"
/*
@@ -44,6 +44,11 @@ struct bucket {
unsigned int count;
};
+static inline size_t pow4(size_t n)
+{
+ return n * n * n * n;
+}
+
/*
* has_low_entropy() - Compute Shannon entropy of the sampled data.
* @bkt: Bytes counts of the sample.
@@ -65,7 +70,6 @@ static bool has_low_entropy(struct bucket *bkt, size_t slen)
const size_t threshold = 65, max_entropy = 8 * ilog2(16);
size_t i, p, p2, len, sum = 0;
-#define pow4(n) (n * n * n * n)
len = ilog2(pow4(slen));
for (i = 0; i < 256 && bkt[i].count > 0; i++) {
@@ -329,18 +333,14 @@ int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, compress_s
goto err_free;
}
- /*
- * This is just overprovisioning, as the algorithm will error out if @dst reaches 7/8
- * of @slen.
- */
- dlen = slen;
+ dlen = smb_lz77_compressed_alloc_size(slen);
dst = kvzalloc(dlen, GFP_KERNEL);
if (!dst) {
ret = -ENOMEM;
goto err_free;
}
- ret = lz77_compress(src, slen, dst, &dlen);
+ ret = smb_lz77_compress(src, slen, dst, &dlen);
if (!ret) {
struct smb2_compression_hdr hdr = { 0 };
struct smb_rqst comp_rq = { .rq_nvec = 3, };
diff --git a/fs/smb/client/compress.h b/fs/smb/client/compress.h
index 2679baca129b..e08e6d339d21 100644
--- a/fs/smb/client/compress.h
+++ b/fs/smb/client/compress.h
@@ -18,6 +18,7 @@
#include <linux/uio.h>
#include <linux/kernel.h>
#include "../common/smb2pdu.h"
+#include "../common/compress/compress.h"
#include "cifsglob.h"
/* sizeof(smb2_compression_hdr) - sizeof(OriginalPayloadSize) */
@@ -34,29 +35,6 @@ int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq,
compress_send_fn send_fn);
bool should_compress(const struct cifs_tcon *tcon, const struct smb_rqst *rq);
-/*
- * smb_compress_alg_valid() - Validate a compression algorithm.
- * @alg: Compression algorithm to check.
- * @valid_none: Conditional check whether NONE algorithm should be
- * considered valid or not.
- *
- * If @alg is SMB3_COMPRESS_NONE, this function returns @valid_none.
- *
- * Note that 'NONE' (0) compressor type is considered invalid in protocol
- * negotiation, as it's never requested to/returned from the server.
- *
- * Return: true if @alg is valid/supported, false otherwise.
- */
-static __always_inline int smb_compress_alg_valid(__le16 alg, bool valid_none)
-{
- if (alg == SMB3_COMPRESS_NONE)
- return valid_none;
-
- if (alg == SMB3_COMPRESS_LZ77 || alg == SMB3_COMPRESS_PATTERN)
- return true;
-
- return false;
-}
#else /* !CONFIG_CIFS_COMPRESSION */
static inline int smb_compress(void *unused1, void *unused2, void *unused3)
{
@@ -68,9 +46,5 @@ static inline bool should_compress(void *unused1, void *unused2)
return false;
}
-static inline int smb_compress_alg_valid(__le16 unused1, bool unused2)
-{
- return -EOPNOTSUPP;
-}
#endif /* !CONFIG_CIFS_COMPRESSION */
#endif /* _SMB_COMPRESS_H */
diff --git a/fs/smb/client/compress/lz77.c b/fs/smb/client/compress/lz77.c
deleted file mode 100644
index 96e8a8057a77..000000000000
--- a/fs/smb/client/compress/lz77.c
+++ /dev/null
@@ -1,235 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2024, SUSE LLC
- *
- * Authors: Enzo Matsumiya <ematsumiya@suse.de>
- *
- * Implementation of the LZ77 "plain" compression algorithm, as per MS-XCA spec.
- */
-#include <linux/slab.h>
-#include <linux/sizes.h>
-#include <linux/count_zeros.h>
-#include <linux/unaligned.h>
-
-#include "lz77.h"
-
-/*
- * Compression parameters.
- */
-#define LZ77_MATCH_MIN_LEN 4
-#define LZ77_MATCH_MIN_DIST 1
-#define LZ77_MATCH_MAX_DIST SZ_1K
-#define LZ77_HASH_LOG 15
-#define LZ77_HASH_SIZE (1 << LZ77_HASH_LOG)
-#define LZ77_STEP_SIZE sizeof(u64)
-
-static __always_inline u8 lz77_read8(const u8 *ptr)
-{
- return get_unaligned(ptr);
-}
-
-static __always_inline u64 lz77_read64(const u64 *ptr)
-{
- return get_unaligned(ptr);
-}
-
-static __always_inline void lz77_write8(u8 *ptr, u8 v)
-{
- put_unaligned(v, ptr);
-}
-
-static __always_inline void lz77_write16(u16 *ptr, u16 v)
-{
- put_unaligned_le16(v, ptr);
-}
-
-static __always_inline void lz77_write32(u32 *ptr, u32 v)
-{
- put_unaligned_le32(v, ptr);
-}
-
-static __always_inline u32 lz77_match_len(const void *wnd, const void *cur, const void *end)
-{
- const void *start = cur;
- u64 diff;
-
- /* Safe for a do/while because otherwise we wouldn't reach here from the main loop. */
- do {
- diff = lz77_read64(cur) ^ lz77_read64(wnd);
- if (!diff) {
- cur += LZ77_STEP_SIZE;
- wnd += LZ77_STEP_SIZE;
-
- continue;
- }
-
- /* This computes the number of common bytes in @diff. */
- cur += count_trailing_zeros(diff) >> 3;
-
- return (cur - start);
- } while (likely(cur + LZ77_STEP_SIZE < end));
-
- while (cur < end && lz77_read8(cur++) == lz77_read8(wnd++))
- ;
-
- return (cur - start);
-}
-
-static __always_inline void *lz77_write_match(void *dst, void **nib, u32 dist, u32 len)
-{
- len -= 3;
- dist--;
- dist <<= 3;
-
- if (len < 7) {
- lz77_write16(dst, dist + len);
-
- return dst + 2;
- }
-
- dist |= 7;
- lz77_write16(dst, dist);
- dst += 2;
- len -= 7;
-
- if (!*nib) {
- lz77_write8(dst, umin(len, 15));
- *nib = dst;
- dst++;
- } else {
- u8 *b = *nib;
-
- lz77_write8(b, *b | umin(len, 15) << 4);
- *nib = NULL;
- }
-
- if (len < 15)
- return dst;
-
- len -= 15;
- if (len < 255) {
- lz77_write8(dst, len);
-
- return dst + 1;
- }
-
- lz77_write8(dst, 0xff);
- dst++;
- len += 7 + 15;
- if (len <= 0xffff) {
- lz77_write16(dst, len);
-
- return dst + 2;
- }
-
- lz77_write16(dst, 0);
- dst += 2;
- lz77_write32(dst, len);
-
- return dst + 4;
-}
-
-noinline int lz77_compress(const void *src, u32 slen, void *dst, u32 *dlen)
-{
- const void *srcp, *end;
- void *dstp, *nib, *flag_pos;
- u32 flag_count = 0;
- long flag = 0;
- u64 *htable;
-
- srcp = src;
- end = src + slen;
- dstp = dst;
- nib = NULL;
- flag_pos = dstp;
- dstp += 4;
-
- htable = kvcalloc(LZ77_HASH_SIZE, sizeof(*htable), GFP_KERNEL);
- if (!htable)
- return -ENOMEM;
-
- /* Main loop. */
- do {
- u32 dist, len = 0;
- const void *wnd;
- u64 hash;
-
- hash = ((lz77_read64(srcp) << 24) * 889523592379ULL) >> (64 - LZ77_HASH_LOG);
- wnd = src + htable[hash];
- htable[hash] = srcp - src;
- dist = srcp - wnd;
-
- if (dist && dist < LZ77_MATCH_MAX_DIST)
- len = lz77_match_len(wnd, srcp, end);
-
- if (len < LZ77_MATCH_MIN_LEN) {
- lz77_write8(dstp, lz77_read8(srcp));
-
- dstp++;
- srcp++;
-
- flag <<= 1;
- flag_count++;
- if (flag_count == 32) {
- lz77_write32(flag_pos, flag);
- flag_count = 0;
- flag_pos = dstp;
- dstp += 4;
- }
-
- continue;
- }
-
- /*
- * Bail out if @dstp reached >= 7/8 of @slen -- already compressed badly, not worth
- * going further.
- */
- if (unlikely(dstp - dst >= slen - (slen >> 3))) {
- *dlen = slen;
- goto out;
- }
-
- dstp = lz77_write_match(dstp, &nib, dist, len);
- srcp += len;
-
- flag = (flag << 1) | 1;
- flag_count++;
- if (flag_count == 32) {
- lz77_write32(flag_pos, flag);
- flag_count = 0;
- flag_pos = dstp;
- dstp += 4;
- }
- } while (likely(srcp + LZ77_STEP_SIZE < end));
-
- while (srcp < end) {
- u32 c = umin(end - srcp, 32 - flag_count);
-
- memcpy(dstp, srcp, c);
-
- dstp += c;
- srcp += c;
-
- flag <<= c;
- flag_count += c;
- if (flag_count == 32) {
- lz77_write32(flag_pos, flag);
- flag_count = 0;
- flag_pos = dstp;
- dstp += 4;
- }
- }
-
- flag <<= (32 - flag_count);
- flag |= (1 << (32 - flag_count)) - 1;
- lz77_write32(flag_pos, flag);
-
- *dlen = dstp - dst;
-out:
- kvfree(htable);
-
- if (*dlen < slen)
- return 0;
-
- return -EMSGSIZE;
-}
diff --git a/fs/smb/client/compress/lz77.h b/fs/smb/client/compress/lz77.h
deleted file mode 100644
index cdcb191b48a2..000000000000
--- a/fs/smb/client/compress/lz77.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) 2024, SUSE LLC
- *
- * Authors: Enzo Matsumiya <ematsumiya@suse.de>
- *
- * Implementation of the LZ77 "plain" compression algorithm, as per MS-XCA spec.
- */
-#ifndef _SMB_COMPRESS_LZ77_H
-#define _SMB_COMPRESS_LZ77_H
-
-#include <linux/kernel.h>
-
-int lz77_compress(const void *src, u32 slen, void *dst, u32 *dlen);
-#endif /* _SMB_COMPRESS_LZ77_H */
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index 3bad2c5c523d..28e1ddeb6182 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -6,6 +6,7 @@
*
*/
#include <linux/fs.h>
+#include <linux/fs_context.h>
#include <linux/net.h>
#include <linux/string.h>
#include <linux/sched/mm.h>
@@ -20,7 +21,6 @@
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/kthread.h>
-#include <linux/pagevec.h>
#include <linux/freezer.h>
#include <linux/namei.h>
#include <linux/uuid.h>
@@ -174,6 +174,8 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
nserver = ses->chans[i].server;
if (!nserver)
continue;
+ if (!list_empty(&nserver->rlist))
+ continue;
nserver->srv_count++;
list_add(&nserver->rlist, &reco);
}
@@ -182,11 +184,15 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
}
}
+ spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry_safe(server, nserver, &reco, rlist) {
list_del_init(&server->rlist);
set_need_reco(server);
+ spin_unlock(&cifs_tcp_ses_lock);
cifs_put_tcp_session(server, 0);
+ spin_lock(&cifs_tcp_ses_lock);
}
+ spin_unlock(&cifs_tcp_ses_lock);
}
/*
@@ -457,8 +463,8 @@ static int __reconnect_target_locked(struct TCP_Server_Info *server,
server->hostname = hostname;
spin_unlock(&server->srv_lock);
} else {
- cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n",
- __func__, PTR_ERR(hostname));
+ cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %pe\n",
+ __func__, hostname);
cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__,
server->hostname);
}
@@ -1067,6 +1073,7 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
spin_unlock(&server->srv_lock);
cancel_delayed_work_sync(&server->echo);
+ cancel_delayed_work_sync(&server->reconnect);
spin_lock(&server->srv_lock);
server->tcpStatus = CifsExiting;
@@ -1143,7 +1150,7 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
put_net(cifs_net_ns(server));
kfree(server->leaf_fullpath);
kfree(server->hostname);
- kfree(server);
+ kfree_sensitive(server);
length = atomic_dec_return(&tcpSesAllocCount);
if (length > 0)
@@ -1823,6 +1830,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
spin_lock_init(&tcp_ses->mid_counter_lock);
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
+ INIT_LIST_HEAD(&tcp_ses->rlist);
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
mutex_init(&tcp_ses->reconnect_mutex);
@@ -1872,14 +1880,6 @@ smbd_connected:
* this will succeed. No need for try_module_get().
*/
__module_get(THIS_MODULE);
- tcp_ses->tsk = kthread_run(cifs_demultiplex_thread,
- tcp_ses, "cifsd");
- if (IS_ERR(tcp_ses->tsk)) {
- rc = PTR_ERR(tcp_ses->tsk);
- cifs_dbg(VFS, "error %d create cifsd thread\n", rc);
- module_put(THIS_MODULE);
- goto out_err_crypto_release;
- }
tcp_ses->min_offload = ctx->min_offload;
tcp_ses->retrans = ctx->retrans;
/*
@@ -1887,9 +1887,7 @@ smbd_connected:
* to the struct since the kernel thread not created yet
* no need to spinlock this update of tcpStatus
*/
- spin_lock(&tcp_ses->srv_lock);
tcp_ses->tcpStatus = CifsNeedNegotiate;
- spin_unlock(&tcp_ses->srv_lock);
if ((ctx->max_credits < 20) || (ctx->max_credits > 60000))
tcp_ses->max_credits = SMB2_MAX_CREDITS_AVAILABLE;
@@ -1898,7 +1896,16 @@ smbd_connected:
tcp_ses->nr_targets = 1;
tcp_ses->ignore_signature = ctx->ignore_signature;
- /* thread spawned, put it on the list */
+
+ tcp_ses->tsk = kthread_create(cifs_demultiplex_thread,
+ tcp_ses, "cifsd");
+ if (IS_ERR(tcp_ses->tsk)) {
+ rc = PTR_ERR(tcp_ses->tsk);
+ cifs_dbg(VFS, "error %d create cifsd thread\n", rc);
+ module_put(THIS_MODULE);
+ goto out_err_crypto_release;
+ }
+ /* thread created, put it on the list */
spin_lock(&cifs_tcp_ses_lock);
list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);
spin_unlock(&cifs_tcp_ses_lock);
@@ -1906,6 +1913,12 @@ smbd_connected:
/* queue echo request delayed work */
queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
+ /*
+ * Use split create/wake logic to ensure that tcp_ses is fully populated
+ * and tcp_ses->tsk is valid
+ */
+ wake_up_process(tcp_ses->tsk);
+
return tcp_ses;
out_err_crypto_release:
@@ -1921,6 +1934,7 @@ out_err:
kfree(tcp_ses->leaf_fullpath);
if (tcp_ses->ssocket)
sock_release(tcp_ses->ssocket);
+ smbd_destroy(tcp_ses);
kfree(tcp_ses);
}
return ERR_PTR(rc);
@@ -1955,6 +1969,10 @@ static int match_session(struct cifs_ses *ses,
case Kerberos:
if (!uid_eq(ctx->cred_uid, ses->cred_uid))
return 0;
+ if (strncmp(ses->user_name ?: "",
+ ctx->username ?: "",
+ CIFS_MAX_USERNAME_LEN))
+ return 0;
break;
case NTLMv2:
case RawNTLMSSP:
@@ -2988,9 +3006,9 @@ static int match_prepath(struct super_block *sb,
}
int
-cifs_match_super(struct super_block *sb, void *data)
+cifs_match_super(struct super_block *sb, struct fs_context *fc)
{
- struct cifs_mnt_data *mnt_data = data;
+ struct cifs_mnt_data *mnt_data = fc->sget_key;
struct smb3_fs_context *ctx;
struct cifs_sb_info *cifs_sb;
struct TCP_Server_Info *tcp_srv;
@@ -3476,6 +3494,7 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb)
spin_lock_init(&cifs_sb->tlink_tree_lock);
cifs_sb->tlink_tree = RB_ROOT;
+ atomic_set(&cifs_sb->outstanding_rreq, 0);
cifs_dbg(FYI, "file mode: %04ho dir mode: %04ho\n",
ctx->file_mode, ctx->dir_mode);
@@ -3606,7 +3625,6 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
server = mnt_ctx->server;
ctx = mnt_ctx->fs_ctx;
cifs_sb = mnt_ctx->cifs_sb;
- sbflags = cifs_sb_flags(cifs_sb);
/* search for existing tcon to this server share */
tcon = cifs_get_tcon(mnt_ctx->ses, ctx);
@@ -3621,9 +3639,10 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
* path (i.e., do not remap / and \ and do not map any special characters)
*/
if (tcon->posix_extensions) {
- sbflags |= CIFS_MOUNT_POSIX_PATHS;
- sbflags &= ~(CIFS_MOUNT_MAP_SFM_CHR |
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ atomic_or(CIFS_MOUNT_POSIX_PATHS, &cifs_sb->mnt_cifs_flags);
+ atomic_andnot(CIFS_MOUNT_MAP_SFM_CHR |
+ CIFS_MOUNT_MAP_SPECIAL_CHR,
+ &cifs_sb->mnt_cifs_flags);
}
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
@@ -3647,6 +3666,7 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
tcon->unix_ext = 0; /* server does not support them */
+ sbflags = cifs_sb_flags(cifs_sb);
/* do not care if a following call succeed - informational */
if (!tcon->pipe && server->ops->qfs_tcon) {
server->ops->qfs_tcon(mnt_ctx->xid, tcon, cifs_sb);
@@ -3671,7 +3691,6 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
out:
mnt_ctx->tcon = tcon;
- atomic_set(&cifs_sb->mnt_cifs_flags, sbflags);
return rc;
}
@@ -3866,7 +3885,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
* After reconnecting to a different server, unique ids won't match anymore, so we disable
* serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).
*/
- cifs_autodisable_serverino(cifs_sb);
+ cifs_autodisable_serverino(cifs_sb, "DFS failover may potentially connect to a different server, inode numbers won't match anymore", 0);
/*
* Force the use of prefix path to support failover on DFS paths that resolve to targets
* that have different prefix paths.
@@ -4179,14 +4198,25 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
return rc;
}
-static int
-cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses)
+static int set_fs_context_auth(struct smb3_fs_context *ctx,
+ struct cifs_ses *ses)
{
ctx->sectype = ses->sectype;
- /* krb5 is special, since we don't need username or pw */
- if (ctx->sectype == Kerberos)
+ /*
+ * krb5 is special as we might need to pass username (passwordless) down
+ * to cifs.upcall(8) for keytab.
+ */
+ if (ctx->sectype == Kerberos) {
+ if (ses->user_name && ses->user_name[0]) {
+ ctx->username = kstrndup(ses->user_name,
+ CIFS_MAX_USERNAME_LEN,
+ GFP_KERNEL);
+ if (!ctx->username)
+ return -ENOMEM;
+ }
return 0;
+ }
return cifs_set_cifscreds(ctx, ses);
}
@@ -4226,7 +4256,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
ctx->dfs_root_ses = master_tcon->ses->dfs_root_ses;
ctx->unicode = master_tcon->ses->unicode;
- rc = cifs_set_vol_auth(ctx, master_tcon->ses);
+ rc = set_fs_context_auth(ctx, master_tcon->ses);
if (rc) {
tcon = ERR_PTR(rc);
goto out;
diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c
index 83f8cf2f8d2b..29dfd7595941 100644
--- a/fs/smb/client/dfs_cache.c
+++ b/fs/smb/client/dfs_cache.c
@@ -122,6 +122,9 @@ static inline void free_tgts(struct cache_entry *ce)
kfree(t->name);
kfree(t);
}
+
+ ce->numtgts = 0;
+ WRITE_ONCE(ce->tgthint, NULL);
}
static inline void flush_cache_ent(struct cache_entry *ce)
@@ -363,10 +366,10 @@ static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed)
{
struct cache_dfs_tgt *t;
- t = kmalloc_obj(*t, GFP_ATOMIC);
+ t = kmalloc_obj(*t);
if (!t)
return ERR_PTR(-ENOMEM);
- t->name = kstrdup(name, GFP_ATOMIC);
+ t->name = kstrdup(name, GFP_KERNEL);
if (!t->name) {
kfree(t);
return ERR_PTR(-ENOMEM);
@@ -386,13 +389,6 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
struct cache_dfs_tgt *target;
int i;
- ce->ttl = max_t(int, refs[0].ttl, CACHE_MIN_TTL);
- ce->etime = get_expire_time(ce->ttl);
- ce->srvtype = refs[0].server_type;
- ce->hdr_flags = refs[0].flags;
- ce->ref_flags = refs[0].ref_flag;
- ce->path_consumed = refs[0].path_consumed;
-
for (i = 0; i < numrefs; i++) {
struct cache_dfs_tgt *t;
@@ -407,12 +403,19 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
} else {
list_add_tail(&t->list, &ce->tlist);
}
- ce->numtgts++;
}
target = list_first_entry_or_null(&ce->tlist, struct cache_dfs_tgt,
list);
+
WRITE_ONCE(ce->tgthint, target);
+ ce->ttl = max_t(int, refs[0].ttl, CACHE_MIN_TTL);
+ ce->etime = get_expire_time(ce->ttl);
+ ce->srvtype = refs[0].server_type;
+ ce->hdr_flags = refs[0].flags;
+ ce->ref_flags = refs[0].ref_flag;
+ ce->path_consumed = refs[0].path_consumed;
+ ce->numtgts = numrefs;
return 0;
}
@@ -626,13 +629,12 @@ static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_in
target = READ_ONCE(ce->tgthint);
if (target) {
- th = kstrdup(target->name, GFP_ATOMIC);
+ th = kstrdup(target->name, GFP_KERNEL);
if (!th)
return -ENOMEM;
}
free_tgts(ce);
- ce->numtgts = 0;
rc = copy_ref_data(refs, numrefs, ce, th);
@@ -760,11 +762,11 @@ static int setup_referral(const char *path, struct cache_entry *ce,
memset(ref, 0, sizeof(*ref));
- ref->path_name = kstrdup(path, GFP_ATOMIC);
+ ref->path_name = kstrdup(path, GFP_KERNEL);
if (!ref->path_name)
return -ENOMEM;
- ref->node_name = kstrdup(target, GFP_ATOMIC);
+ ref->node_name = kstrdup(target, GFP_KERNEL);
if (!ref->node_name) {
rc = -ENOMEM;
goto err_free_path;
@@ -869,13 +871,22 @@ int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nl
goto out_free_path;
}
- if (ref)
- rc = setup_referral(path, ce, ref, get_tgt_name(ce));
- else
+ if (ref) {
+ char *target = get_tgt_name(ce);
+
+ if (IS_ERR(target)) {
+ rc = PTR_ERR(target);
+ goto out_unlock;
+ }
+ rc = setup_referral(path, ce, ref, target);
+ } else {
rc = 0;
+ }
+
if (!rc && tgt_list)
rc = get_targets(ce, tgt_list);
+out_unlock:
up_read(&htable_rw_lock);
out_free_path:
@@ -915,10 +926,17 @@ int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
goto out_unlock;
}
- if (ref)
- rc = setup_referral(path, ce, ref, get_tgt_name(ce));
- else
+ if (ref) {
+ char *target = get_tgt_name(ce);
+
+ if (IS_ERR(target)) {
+ rc = PTR_ERR(target);
+ goto out_unlock;
+ }
+ rc = setup_referral(path, ce, ref, target);
+ } else {
rc = 0;
+ }
if (!rc && tgt_list)
rc = get_targets(ce, tgt_list);
@@ -959,7 +977,8 @@ void dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt
t = READ_ONCE(ce->tgthint);
- if (unlikely(!strcasecmp(it->it_name, t->name)))
+ /* Check 't' in case ce->tgthint was cleared by free_tgts() */
+ if (t && unlikely(!strcasecmp(it->it_name, t->name)))
goto out_unlock;
list_for_each_entry(t, &ce->tlist, list) {
@@ -1328,7 +1347,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
* After reconnecting to a different server, unique ids won't match anymore, so we disable
* serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).
*/
- cifs_autodisable_serverino(cifs_sb);
+ cifs_autodisable_serverino(cifs_sb, "DFS failover may potentially connect to a different server, inode numbers won't match anymore", 0);
/*
* Force the use of prefix path to support failover on DFS paths that resolve to targets
* that have different prefix paths.
diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c
index 953f1fee8cb8..6fa6d48fdfd3 100644
--- a/fs/smb/client/dir.c
+++ b/fs/smb/client/dir.c
@@ -115,11 +115,7 @@ char *__build_path_from_dentry_optional_prefix(struct dentry *direntry, void *pa
}
if (dirsep != '/') {
/* BB test paths to Windows with '/' in the midst of prepath */
- char *p;
-
- for (p = s; *p; p++)
- if (*p == '/')
- *p = dirsep;
+ strreplace(s, '/', dirsep);
}
if (dfsplen) {
s -= dfsplen;
@@ -172,22 +168,46 @@ check_name(struct dentry *direntry, struct cifs_tcon *tcon)
return 0;
}
+static char *alloc_parent_path(struct dentry *dentry, size_t namelen)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dentry);
+ void *page = alloc_dentry_path();
+ const char *path;
+ size_t size;
+ char *npath;
-/* Inode operations in similar order to how they appear in Linux file fs.h */
+ path = build_path_from_dentry(dentry->d_parent, page);
+ if (IS_ERR(path)) {
+ npath = ERR_CAST(path);
+ goto out;
+ }
+
+ size = strlen(path) + namelen + 2;
+ npath = kmalloc(size, GFP_KERNEL);
+ if (!npath)
+ npath = ERR_PTR(-ENOMEM);
+ else
+ scnprintf(npath, size, "%s%c", path, CIFS_DIR_SEP(cifs_sb));
+out:
+ free_dentry_path(page);
+ return npath;
+}
-static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
- struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock,
- struct cifs_fid *fid, struct cifs_open_info_data *buf)
+/* Inode operations in similar order to how they appear in Linux file fs.h */
+static int __cifs_do_create(struct inode *dir, struct dentry *direntry,
+ const char *full_path, unsigned int xid,
+ struct tcon_link *tlink, unsigned int oflags,
+ umode_t mode, __u32 *oplock, struct cifs_fid *fid,
+ struct cifs_open_info_data *buf,
+ struct inode **inode)
{
int rc = -ENOENT;
int create_options = CREATE_NOT_DIR;
int desired_access;
- struct cifs_sb_info *cifs_sb = CIFS_SB(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dir);
struct cifs_tcon *tcon = tlink_tcon(tlink);
- const char *full_path;
- void *page = alloc_dentry_path();
struct inode *newinode = NULL;
- unsigned int sbflags;
+ unsigned int sbflags = cifs_sb_flags(cifs_sb);
int disposition;
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms;
@@ -195,25 +215,20 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
int rdwr_for_fscache = 0;
__le32 lease_flags = 0;
+ *inode = NULL;
*oplock = 0;
if (tcon->ses->server->oplocks)
*oplock = REQ_OPLOCK;
- full_path = build_path_from_dentry(direntry, page);
- if (IS_ERR(full_path)) {
- rc = PTR_ERR(full_path);
- goto out;
- }
-
/* If we're caching, we need to be able to fill in around partial writes. */
- if (cifs_fscache_enabled(inode) && (oflags & O_ACCMODE) == O_WRONLY)
+ if (cifs_fscache_enabled(dir) && (oflags & O_ACCMODE) == O_WRONLY)
rdwr_for_fscache = 1;
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
- rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode,
+ rc = cifs_posix_open(full_path, &newinode, dir->i_sb, mode,
oflags, oplock, &fid->netfid, xid);
switch (rc) {
case 0:
@@ -222,11 +237,16 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
goto cifs_create_get_file_info;
}
+ if ((oflags & __O_REGULAR) && !S_ISREG(newinode->i_mode)) {
+ CIFSSMBClose(xid, tcon, fid->netfid);
+ iput(newinode);
+ return -EFTYPE;
+ }
+
if (S_ISDIR(newinode->i_mode)) {
CIFSSMBClose(xid, tcon, fid->netfid);
iput(newinode);
- rc = -EISDIR;
- goto out;
+ return -EISDIR;
}
if (!S_ISREG(newinode->i_mode)) {
@@ -269,7 +289,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
break;
default:
- goto out;
+ return rc;
}
/*
* fallthrough to retry, using older open call, this is case
@@ -287,27 +307,32 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
desired_access |= GENERIC_WRITE;
if (rdwr_for_fscache == 1)
desired_access |= GENERIC_READ;
+ if (oflags & O_TMPFILE)
+ desired_access |= DELETE;
disposition = FILE_OVERWRITE_IF;
- if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ if (oflags & O_CREAT) {
+ if (oflags & O_EXCL)
+ disposition = FILE_CREATE;
+ else if (oflags & O_TRUNC)
+ disposition = FILE_OVERWRITE_IF;
+ else
+ disposition = FILE_OPEN_IF;
+ } else if (oflags & O_TMPFILE) {
disposition = FILE_CREATE;
- else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
- disposition = FILE_OVERWRITE_IF;
- else if ((oflags & O_CREAT) == O_CREAT)
- disposition = FILE_OPEN_IF;
- else
+ } else {
cifs_dbg(FYI, "Create flag not set in create function\n");
+ }
/*
* BB add processing to set equivalent of mode - e.g. via CreateX with
* ACLs
*/
- if (!server->ops->open) {
- rc = -ENOSYS;
- goto out;
- }
+ if (!server->ops->open)
+ return -EOPNOTSUPP;
+ create_options |= cifs_open_create_options(oflags, create_options);
/*
* if we're not using unix extensions, see if we need to set
* ATTR_READONLY on the create call
@@ -357,17 +382,16 @@ retry_open:
rdwr_for_fscache = 2;
goto retry_open;
}
- goto out;
+ return rc;
}
if (rdwr_for_fscache == 2)
- cifs_invalidate_cache(inode, FSCACHE_INVAL_DIO_WRITE);
+ cifs_invalidate_cache(dir, FSCACHE_INVAL_DIO_WRITE);
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
/*
* If Open reported that we actually created a file then we now have to
* set the mode if possible.
*/
- sbflags = cifs_sb_flags(cifs_sb);
if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) {
struct cifs_unix_set_info_args args = {
.mode = mode,
@@ -379,8 +403,8 @@ retry_open:
if (sbflags & CIFS_MOUNT_SET_UID) {
args.uid = current_fsuid();
- if (inode->i_mode & S_ISGID)
- args.gid = inode->i_gid;
+ if (dir->i_mode & S_ISGID)
+ args.gid = dir->i_gid;
else
args.gid = current_fsgid();
} else {
@@ -402,14 +426,14 @@ retry_open:
cifs_create_get_file_info:
/* server might mask mode so we have to query for it */
if (tcon->unix_ext)
- rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb,
+ rc = cifs_get_inode_info_unix(&newinode, full_path, dir->i_sb,
xid);
else {
#else
{
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
/* TODO: Add support for calling POSIX query info here, but passing in fid */
- rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid);
+ rc = cifs_get_inode_info(&newinode, full_path, buf, dir->i_sb, xid, fid);
if (newinode) {
if (server->ops->set_lease_key)
server->ops->set_lease_key(newinode, fid);
@@ -418,8 +442,8 @@ cifs_create_get_file_info:
newinode->i_mode = mode;
if (sbflags & CIFS_MOUNT_SET_UID) {
newinode->i_uid = current_fsuid();
- if (inode->i_mode & S_ISGID)
- newinode->i_gid = inode->i_gid;
+ if (dir->i_mode & S_ISGID)
+ newinode->i_gid = dir->i_gid;
else
newinode->i_gid = current_fsgid();
}
@@ -436,17 +460,18 @@ cifs_create_set_dentry:
goto out_err;
}
- if (newinode)
+ if (newinode) {
+ if ((oflags & __O_REGULAR) && !S_ISREG(newinode->i_mode)) {
+ rc = -EFTYPE;
+ goto out_err;
+ }
if (S_ISDIR(newinode->i_mode)) {
rc = -EISDIR;
goto out_err;
}
+ }
- d_drop(direntry);
- d_add(direntry, newinode);
-
-out:
- free_dentry_path(page);
+ *inode = newinode;
return rc;
out_err:
@@ -454,14 +479,44 @@ out_err:
server->ops->close(xid, tcon, fid);
if (newinode)
iput(newinode);
- goto out;
+ return rc;
+}
+
+static int cifs_do_create(struct inode *dir, struct dentry *direntry,
+ unsigned int xid, struct tcon_link *tlink,
+ unsigned int oflags, umode_t mode,
+ __u32 *oplock, struct cifs_fid *fid,
+ struct cifs_open_info_data *buf,
+ struct inode **inode)
+{
+ void *page = alloc_dentry_path();
+ const char *full_path;
+ int rc;
+
+ full_path = build_path_from_dentry(direntry, page);
+ if (IS_ERR(full_path)) {
+ rc = PTR_ERR(full_path);
+ } else {
+ rc = __cifs_do_create(dir, direntry, full_path, xid,
+ tlink, oflags, mode, oplock,
+ fid, buf, inode);
+ }
+ free_dentry_path(page);
+ return rc;
}
-int
-cifs_atomic_open(struct inode *inode, struct dentry *direntry,
- struct file *file, unsigned int oflags, umode_t mode)
+
+/*
+ * Look up, create and open a CIFS file.
+ *
+ * The initial dentry state is in-lookup or hashed-negative. On success, dentry
+ * will become hashed-positive by calling d_splice_alias() if in-lookup,
+ * otherwise d_instantiate().
+ */
+int cifs_atomic_open(struct inode *dir, struct dentry *direntry,
+ struct file *file, unsigned int oflags, umode_t mode)
{
- struct cifs_sb_info *cifs_sb = CIFS_SB(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dir);
struct cifs_open_info_data buf = {};
struct TCP_Server_Info *server;
struct cifsFileInfo *file_info;
@@ -470,6 +525,8 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
struct tcon_link *tlink;
struct cifs_tcon *tcon;
unsigned int sbflags;
+ struct dentry *alias;
+ struct inode *inode;
unsigned int xid;
__u32 oplock;
int rc;
@@ -496,13 +553,13 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
if (!d_in_lookup(direntry))
return -ENOENT;
- return finish_no_open(file, cifs_lookup(inode, direntry, 0));
+ return finish_no_open(file, cifs_lookup(dir, direntry, 0));
}
xid = get_xid();
cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
- inode, direntry, direntry);
+ dir, direntry, direntry);
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
@@ -523,13 +580,21 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
cifs_add_pending_open(&fid, tlink, &open);
- rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
- &oplock, &fid, &buf);
+ rc = cifs_do_create(dir, direntry, xid, tlink, oflags, mode,
+ &oplock, &fid, &buf, &inode);
if (rc) {
cifs_del_pending_open(&open);
goto out;
}
+ if (d_in_lookup(direntry)) {
+ alias = d_splice_alias(inode, direntry);
+ if (!IS_ERR_OR_NULL(alias))
+ direntry = alias;
+ } else {
+ d_instantiate(direntry, inode);
+ }
+
if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
file->f_mode |= FMODE_CREATED;
@@ -569,9 +634,16 @@ out_free_xid:
return rc;
}
-int cifs_create(struct mnt_idmap *idmap, struct inode *inode,
- struct dentry *direntry, umode_t mode, bool excl)
+/*
+ * Create a CIFS file.
+ *
+ * The initial dentry state is hashed-negative. On success, dentry will become
+ * hashed-positive by calling d_instantiate().
+ */
+int cifs_create(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *direntry, umode_t mode)
{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dir);
int rc;
unsigned int xid = get_xid();
/*
@@ -585,19 +657,20 @@ int cifs_create(struct mnt_idmap *idmap, struct inode *inode,
struct tcon_link *tlink;
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
+ struct inode *inode;
struct cifs_fid fid;
__u32 oplock;
struct cifs_open_info_data buf = {};
cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
- inode, direntry, direntry);
+ dir, direntry, direntry);
- if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) {
+ if (unlikely(cifs_forced_shutdown(cifs_sb))) {
rc = smb_EIO(smb_eio_trace_forced_shutdown);
goto out_free_xid;
}
- tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb));
+ tlink = cifs_sb_tlink(cifs_sb);
rc = PTR_ERR(tlink);
if (IS_ERR(tlink))
goto out_free_xid;
@@ -608,9 +681,13 @@ int cifs_create(struct mnt_idmap *idmap, struct inode *inode,
if (server->ops->new_lease_key)
server->ops->new_lease_key(&fid);
- rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, &oplock, &fid, &buf);
- if (!rc && server->ops->close)
- server->ops->close(xid, tcon, &fid);
+ rc = cifs_do_create(dir, direntry, xid, tlink, oflags,
+ mode, &oplock, &fid, &buf, &inode);
+ if (!rc) {
+ d_instantiate(direntry, inode);
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &fid);
+ }
cifs_free_open_info(&buf);
cifs_put_tlink(tlink);
@@ -959,6 +1036,172 @@ static int cifs_ci_compare(const struct dentry *dentry,
return 0;
}
+static int set_tmpfile_attr(const unsigned int xid, unsigned int oflags,
+ struct inode *inode, const char *full_path,
+ struct TCP_Server_Info *server)
+{
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ FILE_BASIC_INFO fi;
+
+ cinode->cifsAttrs |= ATTR_HIDDEN;
+ if (oflags & O_EXCL)
+ cinode->cifsAttrs |= ATTR_TEMPORARY;
+
+ fi = (FILE_BASIC_INFO) {
+ .Attributes = cpu_to_le32(cinode->cifsAttrs),
+ };
+ return server->ops->set_file_info(inode, full_path, &fi, xid);
+}
+
+/*
+ * Create a hidden temporary CIFS file with delete-on-close bit set.
+ *
+ * The initial dentry state is unhashed-negative. On success, dentry will
+ * become unhashed-positive by calling d_instantiate().
+ */
+int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
+ struct file *file, umode_t mode)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dir);
+ size_t namesize = CIFS_TMPNAME_LEN + 1;
+ char *path __free(kfree) = NULL, *name;
+ unsigned int oflags = file->f_flags;
+ int retries = 0, max_retries = 16;
+ struct TCP_Server_Info *server;
+ struct cifs_pending_open open;
+ struct cifsFileInfo *cfile;
+ struct cifs_fid fid = {};
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ unsigned int sbflags;
+ struct inode *inode;
+ unsigned int xid;
+ __u32 oplock;
+ int namelen;
+ int rc;
+
+ if (unlikely(cifs_forced_shutdown(cifs_sb)))
+ return smb_EIO(smb_eio_trace_forced_shutdown);
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ xid = get_xid();
+
+ if (server->vals->protocol_id < SMB20_PROT_ID) {
+ cifs_dbg(VFS | ONCE, "O_TMPFILE is supported only in SMB2+\n");
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (server->ops->new_lease_key)
+ server->ops->new_lease_key(&fid);
+ cifs_add_pending_open(&fid, tlink, &open);
+
+ path = alloc_parent_path(dentry, namesize - 1);
+ if (IS_ERR(path)) {
+ cifs_del_pending_open(&open);
+ rc = PTR_ERR(path);
+ path = NULL;
+ goto out;
+ }
+
+ name = path + strlen(path);
+ do {
+ /* Append tmpfile name to @path */
+ namelen = scnprintf(name, namesize, CIFS_TMPNAME_PREFIX "%x",
+ atomic_inc_return(&cifs_tmpcounter));
+ rc = __cifs_do_create(dir, dentry, path, xid, tlink, oflags,
+ mode, &oplock, &fid, NULL, &inode);
+ if (!rc) {
+ rc = d_mark_tmpfile_name(file, &QSTR_LEN(name, namelen));
+ if (rc) {
+ cifs_dbg(VFS | ONCE, "%s: failed to set filename in dentry: %d\n",
+ __func__, rc);
+ rc = -EISDIR;
+ iput(inode);
+ goto err_open;
+ }
+ set_nlink(inode, 0);
+ mark_inode_dirty(inode);
+ d_instantiate(dentry, inode);
+ break;
+ }
+ } while (unlikely(rc == -EEXIST) && ++retries < max_retries);
+
+ if (rc) {
+ if (rc == -ENOENT)
+ rc = -EOPNOTSUPP;
+ cifs_del_pending_open(&open);
+ goto out;
+ }
+
+ rc = finish_open(file, dentry, generic_file_open);
+ if (rc)
+ goto err_open;
+
+ sbflags = cifs_sb_flags(cifs_sb);
+ if ((file->f_flags & O_DIRECT) && (sbflags & CIFS_MOUNT_STRICT_IO)) {
+ if (sbflags & CIFS_MOUNT_NO_BRL)
+ file->f_op = &cifs_file_direct_nobrl_ops;
+ else
+ file->f_op = &cifs_file_direct_ops;
+ }
+
+ cfile = cifs_new_fileinfo(&fid, file, tlink, oplock, NULL);
+ if (!cfile) {
+ rc = -ENOMEM;
+ goto err_open;
+ }
+
+ rc = set_tmpfile_attr(xid, oflags, inode, path, server);
+ if (rc)
+ goto out;
+
+ fscache_use_cookie(cifs_inode_cookie(file_inode(file)),
+ file->f_mode & FMODE_WRITE);
+out:
+ cifs_put_tlink(tlink);
+ free_xid(xid);
+ return rc;
+err_open:
+ cifs_del_pending_open(&open);
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &fid);
+ goto out;
+}
+
+char *cifs_silly_fullpath(struct dentry *dentry)
+{
+ unsigned char name[CIFS_SILLYNAME_LEN + 1];
+ int retries = 0, max_retries = 16;
+ size_t namesize = sizeof(name);
+ struct dentry *sdentry = NULL;
+ char *path;
+
+ do {
+ dput(sdentry);
+ scnprintf(name, namesize, CIFS_SILLYNAME_PREFIX "%x",
+ atomic_inc_return(&cifs_sillycounter));
+ sdentry = lookup_noperm(&QSTR(name), dentry->d_parent);
+ if (IS_ERR(sdentry))
+ return ERR_CAST(sdentry);
+ if (d_is_negative(sdentry)) {
+ dput(sdentry);
+ path = alloc_parent_path(dentry, CIFS_SILLYNAME_LEN);
+ if (!IS_ERR(path))
+ strcat(path, name);
+ return path;
+ }
+ } while (++retries < max_retries);
+ dput(sdentry);
+ return ERR_PTR(-EBUSY);
+}
+
const struct dentry_operations cifs_ci_dentry_ops = {
.d_revalidate = cifs_d_revalidate,
.d_hash = cifs_ci_hash,
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index f3ddcdf406c8..0d428517f454 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -15,13 +15,12 @@
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/pagemap.h>
-#include <linux/pagevec.h>
#include <linux/writeback.h>
#include <linux/task_io_accounting_ops.h>
#include <linux/delay.h>
#include <linux/mount.h>
#include <linux/slab.h>
-#include <linux/swap.h>
+#include <linux/swap_ops.h>
#include <linux/mm.h>
#include <asm/div64.h>
#include "cifsfs.h"
@@ -242,6 +241,7 @@ static void cifs_issue_read(struct netfs_io_subrequest *subreq)
return;
failed:
+ add_credits_and_wake_if(rdata->server, &rdata->credits, 0);
subreq->error = rc;
netfs_read_subreq_terminated(subreq);
}
@@ -255,7 +255,7 @@ static void cifs_begin_writeback(struct netfs_io_request *wreq)
struct cifs_io_request *req = container_of(wreq, struct cifs_io_request, rreq);
int ret;
- ret = cifs_get_writable_file(CIFS_I(wreq->inode), FIND_WR_ANY, &req->cfile);
+ ret = cifs_get_writable_file(CIFS_I(wreq->inode), FIND_ANY, &req->cfile);
if (ret) {
cifs_dbg(VFS, "No writable handle in writepages ret=%d\n", ret);
return;
@@ -288,6 +288,7 @@ static int cifs_init_request(struct netfs_io_request *rreq, struct file *file)
return smb_EIO1(smb_eio_trace_not_netfs_writeback, rreq->origin);
}
+ atomic_inc(&cifs_sb->outstanding_rreq);
return 0;
}
@@ -302,16 +303,20 @@ static void cifs_rreq_done(struct netfs_io_request *rreq)
/* we do not want atime to be less than mtime, it broke some apps */
atime = inode_set_atime_to_ts(inode, current_time(inode));
mtime = inode_get_mtime(inode);
- if (timespec64_compare(&atime, &mtime))
+ if (timespec64_compare(&atime, &mtime) < 0)
inode_set_atime_to_ts(inode, inode_get_mtime(inode));
}
static void cifs_free_request(struct netfs_io_request *rreq)
{
struct cifs_io_request *req = container_of(rreq, struct cifs_io_request, rreq);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb);
if (req->cfile)
cifsFileInfo_put(req->cfile);
+
+ if (atomic_dec_and_test(&cifs_sb->outstanding_rreq))
+ wake_up_var(&cifs_sb->outstanding_rreq);
}
static void cifs_free_subrequest(struct netfs_io_subrequest *subreq)
@@ -394,7 +399,7 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
}
spin_unlock(&tcon->open_file_lock);
- invalidate_all_cached_dirs(tcon);
+ invalidate_all_cached_dirs(tcon, true);
spin_lock(&tcon->tc_lock);
if (tcon->status == TID_IN_FILES_INVALIDATE)
tcon->status = TID_NEED_TCON;
@@ -406,22 +411,29 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
*/
}
-static inline int cifs_convert_flags(unsigned int flags, int rdwr_for_fscache)
+static inline int cifs_convert_flags(unsigned int oflags, int rdwr_for_fscache)
{
- if ((flags & O_ACCMODE) == O_RDONLY)
- return GENERIC_READ;
- else if ((flags & O_ACCMODE) == O_WRONLY)
- return rdwr_for_fscache == 1 ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_WRITE;
- else if ((flags & O_ACCMODE) == O_RDWR) {
+ int flags = 0;
+
+ if (oflags & O_TMPFILE)
+ flags |= DELETE;
+
+ if ((oflags & O_ACCMODE) == O_RDONLY)
+ return flags | GENERIC_READ;
+ if ((oflags & O_ACCMODE) == O_WRONLY) {
+ return flags | (rdwr_for_fscache == 1 ?
+ (GENERIC_READ | GENERIC_WRITE) : GENERIC_WRITE);
+ }
+ if ((oflags & O_ACCMODE) == O_RDWR) {
/* GENERIC_ALL is too much permission to request
can cause unnecessary access denied on create */
/* return GENERIC_ALL; */
- return (GENERIC_READ | GENERIC_WRITE);
+ return flags | GENERIC_READ | GENERIC_WRITE;
}
- return (READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES |
- FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA |
- FILE_READ_DATA);
+ return flags | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
+ FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA |
+ FILE_WRITE_DATA | FILE_READ_DATA;
}
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
@@ -584,15 +596,8 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_
*********************************************************************/
disposition = cifs_get_disposition(f_flags);
-
/* BB pass O_SYNC flag through on file attributes .. BB */
-
- /* O_SYNC also has bit for O_DSYNC so following check picks up either */
- if (f_flags & O_SYNC)
- create_options |= CREATE_WRITE_THROUGH;
-
- if (f_flags & O_DIRECT)
- create_options |= CREATE_NO_BUFFER;
+ create_options |= cifs_open_create_options(f_flags, create_options);
retry_open:
oparms = (struct cifs_open_parms) {
@@ -703,6 +708,7 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
cfile->f_flags = file->f_flags;
cfile->invalidHandle = false;
cfile->deferred_close_scheduled = false;
+ cfile->status_file_deleted = file->f_flags & O_TMPFILE;
cfile->tlink = cifs_get_tlink(tlink);
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
INIT_WORK(&cfile->put, cifsFileInfo_put_work);
@@ -711,8 +717,6 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
mutex_init(&cfile->fh_mutex);
spin_lock_init(&cfile->file_info_lock);
- cifs_sb_active(inode->i_sb);
-
/*
* If the server returned a read oplock and we have mandatory brlocks,
* set oplock level to None.
@@ -736,6 +740,8 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
/* if readable file instance put first in list*/
spin_lock(&cinode->open_file_lock);
+ if (file->f_flags & O_TMPFILE)
+ set_bit(CIFS_INO_TMPFILE, &cinode->flags);
fid->purge_cache = false;
server->ops->set_fid(cfile, fid, oplock);
@@ -767,7 +773,6 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file)
struct inode *inode = d_inode(cifs_file->dentry);
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifsLockInfo *li, *tmp;
- struct super_block *sb = inode->i_sb;
/*
* Delete any outstanding lock records. We'll lose them when the file
@@ -785,7 +790,6 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file)
cifs_put_tlink(cifs_file->tlink);
dput(cifs_file->dentry);
- cifs_sb_deactive(sb);
kfree(cifs_file->symlink_target);
kfree(cifs_file);
}
@@ -911,6 +915,14 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
cifs_set_oplock_level(cifsi, 0);
}
+ if (OPEN_FMODE(cifs_file->f_flags) & FMODE_WRITE) {
+ /* Stamp while open_file_lock is held; covers all close paths
+ * including background I/O. Pairs with smp_load_acquire() in
+ * is_size_safe_to_change().
+ */
+ smp_store_release(&cifsi->time_last_write, jiffies);
+ }
+
spin_unlock(&cifsi->open_file_lock);
spin_unlock(&tcon->open_file_lock);
@@ -967,7 +979,7 @@ int cifs_file_flush(const unsigned int xid, struct inode *inode,
return tcon->ses->server->ops->flush(xid, tcon,
&cfile->fid);
}
- rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile);
+ rc = cifs_get_writable_file(CIFS_I(inode), FIND_ANY, &cfile);
if (!rc) {
tcon = tlink_tcon(cfile->tlink);
rc = tcon->ses->server->ops->flush(xid, tcon, &cfile->fid);
@@ -987,26 +999,50 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
struct cifs_tcon *tcon;
int rc;
- rc = filemap_write_and_wait(inode->i_mapping);
- if (is_interrupt_error(rc))
+ rc = inode_lock_killable(inode);
+ if (rc)
return -ERESTARTSYS;
+
+ filemap_invalidate_lock(inode->i_mapping);
+
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (is_interrupt_error(rc)) {
+ rc = -ERESTARTSYS;
+ goto out;
+ }
mapping_set_error(inode->i_mapping, rc);
- cfile = find_writable_file(cinode, FIND_WR_FSUID_ONLY);
+ cfile = find_writable_file(cinode, FIND_FSUID_ONLY);
rc = cifs_file_flush(xid, inode, cfile);
if (!rc) {
if (cfile) {
+ struct netfs_inode *ictx = netfs_inode(inode);
+
tcon = tlink_tcon(cfile->tlink);
server = tcon->ses->server;
+ netfs_wb_begin(ictx, false);
rc = server->ops->set_file_size(xid, tcon,
cfile, 0, false);
- }
- if (!rc) {
- netfs_resize_file(&cinode->netfs, 0, true);
- cifs_setsize(inode, 0);
- inode->i_blocks = 0;
+ if (!rc) {
+ netfs_resize_file(&cinode->netfs, 0, true);
+ cifs_setsize(inode, 0);
+ cifs_invalidate_cache(inode, 0);
+ }
+ netfs_wb_end(ictx);
+ } else {
+ /*
+ * No cached handle; evict stale pages so they can't
+ * be served after the file is later extended; let
+ * the server's O_TRUNC open response set the i_size
+ */
+ truncate_inode_pages(inode->i_mapping, 0);
+ cifs_invalidate_cache(inode, 0);
}
}
+
+out:
+ filemap_invalidate_unlock(inode->i_mapping);
+ inode_unlock(inode);
if (cfile)
cifsFileInfo_put(cfile);
return rc;
@@ -1072,32 +1108,32 @@ int cifs_open(struct inode *inode, struct file *file)
/* Get the cached handle as SMB2 close is deferred */
if (OPEN_FMODE(file->f_flags) & FMODE_WRITE) {
- rc = cifs_get_writable_path(tcon, full_path,
- FIND_WR_FSUID_ONLY |
- FIND_WR_NO_PENDING_DELETE,
- &cfile);
+ rc = __cifs_get_writable_file(CIFS_I(inode),
+ FIND_FSUID_ONLY |
+ FIND_NO_PENDING_DELETE |
+ FIND_OPEN_FLAGS,
+ file->f_flags, &cfile);
} else {
- rc = cifs_get_readable_path(tcon, full_path, &cfile);
+ cfile = __find_readable_file(CIFS_I(inode),
+ FIND_NO_PENDING_DELETE |
+ FIND_OPEN_FLAGS,
+ file->f_flags);
+ rc = cfile ? 0 : -ENOENT;
}
if (rc == 0) {
- unsigned int oflags = file->f_flags & ~(O_CREAT|O_EXCL|O_TRUNC);
- unsigned int cflags = cfile->f_flags & ~(O_CREAT|O_EXCL|O_TRUNC);
-
- if (cifs_convert_flags(oflags, 0) == cifs_convert_flags(cflags, 0) &&
- (oflags & (O_SYNC|O_DIRECT)) == (cflags & (O_SYNC|O_DIRECT))) {
- file->private_data = cfile;
- spin_lock(&CIFS_I(inode)->deferred_lock);
- cifs_del_deferred_close(cfile);
- spin_unlock(&CIFS_I(inode)->deferred_lock);
- goto use_cache;
- }
- _cifsFileInfo_put(cfile, true, false);
- } else {
- /* hard link on the defeered close file */
- rc = cifs_get_hardlink_path(tcon, inode, file);
- if (rc)
- cifs_close_deferred_file(CIFS_I(inode));
- }
+ trace_smb3_open_cached(xid, tcon->tid, tcon->ses->Suid,
+ cfile->fid.persistent_fid,
+ file->f_flags, cfile->f_flags);
+ file->private_data = cfile;
+ spin_lock(&CIFS_I(inode)->deferred_lock);
+ cifs_del_deferred_close(cfile);
+ spin_unlock(&CIFS_I(inode)->deferred_lock);
+ goto use_cache;
+ }
+ /* hard link on the deferred close file */
+ rc = cifs_get_hardlink_path(tcon, inode, file);
+ if (rc)
+ cifs_close_deferred_file(CIFS_I(inode));
if (server->oplocks)
oplock = REQ_OPLOCK;
@@ -1318,13 +1354,8 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
rdwr_for_fscache = 1;
desired_access = cifs_convert_flags(cfile->f_flags, rdwr_for_fscache);
-
- /* O_SYNC also has bit for O_DSYNC so following check picks up either */
- if (cfile->f_flags & O_SYNC)
- create_options |= CREATE_WRITE_THROUGH;
-
- if (cfile->f_flags & O_DIRECT)
- create_options |= CREATE_NO_BUFFER;
+ create_options |= cifs_open_create_options(cfile->f_flags,
+ create_options);
if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &cfile->fid);
@@ -1425,11 +1456,12 @@ void smb2_deferred_work_close(struct work_struct *work)
{
struct cifsFileInfo *cfile = container_of(work,
struct cifsFileInfo, deferred.work);
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
- spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
+ spin_lock(&cinode->deferred_lock);
cifs_del_deferred_close(cfile);
cfile->deferred_close_scheduled = false;
- spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
+ spin_unlock(&cinode->deferred_lock);
_cifsFileInfo_put(cfile, true, false);
}
@@ -1452,6 +1484,7 @@ int cifs_close(struct inode *inode, struct file *file)
struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_deferred_close *dclose;
+ struct cifs_tcon *tcon;
cifs_fscache_unuse_inode_cookie(inode, file->f_mode & FMODE_WRITE);
@@ -1478,11 +1511,22 @@ int cifs_close(struct inode *inode, struct file *file)
cifsFileInfo_get(cfile);
} else {
/* Deferred close for files */
- queue_delayed_work(deferredclose_wq,
- &cfile->deferred, cifs_sb->ctx->closetimeo);
- cfile->deferred_close_scheduled = true;
- spin_unlock(&cinode->deferred_lock);
- return 0;
+ tcon = tlink_tcon(cfile->tlink);
+ trace_smb3_close_cached(tcon->tid, tcon->ses->Suid,
+ cfile->fid.persistent_fid,
+ cifs_sb->ctx->closetimeo);
+ /*
+ * Each queued execution owns one reference.
+ * If nothing was queued, the reference of
+ * the closing file is dropped below.
+ */
+ if (queue_delayed_work(deferredclose_wq,
+ &cfile->deferred,
+ cifs_sb->ctx->closetimeo)) {
+ cfile->deferred_close_scheduled = true;
+ spin_unlock(&cinode->deferred_lock);
+ return 0;
+ }
}
spin_unlock(&cinode->deferred_lock);
_cifsFileInfo_put(cfile, true, false);
@@ -1566,6 +1610,8 @@ int cifs_closedir(struct inode *inode, struct file *file)
cfile->srch_inf.ntwrk_buf_start = NULL;
if (cfile->srch_inf.smallBuf)
cifs_small_buf_release(buf);
+ else if (cfile->srch_inf.is_dynamic_buf)
+ kfree(buf);
else
cifs_buf_release(buf);
}
@@ -1641,6 +1687,9 @@ cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
continue;
if (conf_lock)
*conf_lock = li;
+ trace_smb3_lock_conflict(cfile->fid.persistent_fid,
+ offset, length, type,
+ li->offset, li->length, li->type, li->pid);
return true;
}
return false;
@@ -1722,7 +1771,7 @@ cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock)
*/
static int
cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
- bool wait)
+ bool wait, unsigned int xid)
{
struct cifsLockInfo *conf_lock;
struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
@@ -1737,7 +1786,13 @@ try_again:
lock->type, lock->flags, &conf_lock,
CIFS_LOCK_OP);
if (!exist && cinode->can_cache_brlcks) {
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+
list_add_tail(&lock->llist, &cfile->llist->locks);
+ trace_smb3_lock_cached(xid, cfile->fid.persistent_fid,
+ tcon->tid, tcon->ses->Suid,
+ lock->offset, lock->length,
+ lock->type, 1, 0);
up_write(&cinode->lock_sem);
return rc;
}
@@ -2352,7 +2407,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
if (!lock)
return -ENOMEM;
- rc = cifs_lock_add_if(cfile, lock, wait_flag);
+ rc = cifs_lock_add_if(cfile, lock, wait_flag, xid);
if (rc < 0) {
kfree(lock);
return rc;
@@ -2508,30 +2563,96 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
return rc;
}
+static void cifs_update_i_blocks_for_write(struct inode *inode, loff_t start,
+ loff_t end)
+{
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ u64 allocated_end = CIFS_INO_BYTES(inode->i_blocks);
+ u64 blocks;
+
+ if (cinode->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)
+ return;
+
+ /*
+ * Grow the local estimate only across the currently known allocated
+ * prefix. A write beyond that may leave a hole.
+ */
+ if ((u64)start > allocated_end)
+ return;
+
+ blocks = CIFS_INO_BLOCKS(end);
+ if ((u64)inode->i_blocks < blocks)
+ inode->i_blocks = blocks;
+}
+
+static void cifs_update_i_blocks_after_write(struct kiocb *iocb,
+ ssize_t written)
+{
+ struct inode *inode = file_inode(iocb->ki_filp);
+ loff_t end = iocb->ki_pos;
+
+ if (written <= 0)
+ return;
+
+ spin_lock(&inode->i_lock);
+ cifs_update_i_blocks_for_write(inode, end - written, end);
+ spin_unlock(&inode->i_lock);
+}
+
void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t result)
{
struct netfs_io_request *wreq = wdata->rreq;
- struct netfs_inode *ictx = netfs_inode(wreq->inode);
+ struct inode *inode = wreq->inode;
+ struct netfs_inode *ictx = netfs_inode(inode);
loff_t wrend;
if (result > 0) {
+ spin_lock(&inode->i_lock);
+
wrend = wdata->subreq.start + wdata->subreq.transferred + result;
- if (wrend > ictx->zero_point &&
+ if (wrend > ictx->_zero_point &&
(wdata->rreq->origin == NETFS_UNBUFFERED_WRITE ||
wdata->rreq->origin == NETFS_DIO_WRITE))
- ictx->zero_point = wrend;
- if (wrend > ictx->remote_i_size)
+ netfs_write_zero_point(inode, wrend);
+ if (wrend > ictx->_remote_i_size)
netfs_resize_file(ictx, wrend, true);
+ cifs_update_i_blocks_for_write(inode, wdata->subreq.start,
+ wrend);
+
+ spin_unlock(&inode->i_lock);
}
netfs_write_subrequest_terminated(&wdata->subreq, result);
}
-struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
- bool fsuid_only)
+static bool open_flags_match(struct cifsInodeInfo *cinode,
+ unsigned int oflags, unsigned int cflags)
+{
+ struct inode *inode = &cinode->netfs.inode;
+ int crw = 0, orw = 0;
+
+ oflags &= ~(O_CREAT | O_EXCL | O_TRUNC);
+ cflags &= ~(O_CREAT | O_EXCL | O_TRUNC);
+
+ if (cifs_fscache_enabled(inode)) {
+ if (OPEN_FMODE(cflags) & FMODE_WRITE)
+ crw = 1;
+ if (OPEN_FMODE(oflags) & FMODE_WRITE)
+ orw = 1;
+ }
+ if (cifs_convert_flags(oflags, orw) != cifs_convert_flags(cflags, crw))
+ return false;
+
+ return (oflags & (O_SYNC | O_DIRECT)) == (cflags & (O_SYNC | O_DIRECT));
+}
+
+struct cifsFileInfo *__find_readable_file(struct cifsInodeInfo *cifs_inode,
+ unsigned int find_flags,
+ unsigned int open_flags)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode);
+ bool fsuid_only = find_flags & FIND_FSUID_ONLY;
struct cifsFileInfo *open_file = NULL;
/* only filter by fsuid on multiuser mounts */
@@ -2545,6 +2666,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))
continue;
+ if ((find_flags & FIND_NO_PENDING_DELETE) &&
+ open_file->status_file_deleted)
+ continue;
+ if ((find_flags & FIND_OPEN_FLAGS) &&
+ !open_flags_match(cifs_inode, open_flags,
+ open_file->f_flags))
+ continue;
if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) {
if ((!open_file->invalidHandle)) {
/* found a good file */
@@ -2563,18 +2691,17 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
}
/* Return -EBADF if no handle is found and general rc otherwise */
-int
-cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags,
- struct cifsFileInfo **ret_file)
+int __cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
+ unsigned int find_flags, unsigned int open_flags,
+ struct cifsFileInfo **ret_file)
{
struct cifsFileInfo *open_file, *inv_file = NULL;
+ bool fsuid_only, with_delete;
struct cifs_sb_info *cifs_sb;
bool any_available = false;
- int rc = -EBADF;
unsigned int refind = 0;
- bool fsuid_only = flags & FIND_WR_FSUID_ONLY;
- bool with_delete = flags & FIND_WR_WITH_DELETE;
*ret_file = NULL;
+ int rc = -EBADF;
/*
* Having a null inode here (because mapping->host was set to zero by
@@ -2588,8 +2715,13 @@ cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags,
return rc;
}
+ if (test_bit(CIFS_INO_TMPFILE, &cifs_inode->flags))
+ find_flags = FIND_ANY;
+
cifs_sb = CIFS_SB(cifs_inode);
+ with_delete = find_flags & FIND_WITH_DELETE;
+ fsuid_only = find_flags & FIND_FSUID_ONLY;
/* only filter by fsuid on multiuser mounts */
if (!(cifs_sb_flags(cifs_sb) & CIFS_MOUNT_MULTIUSER))
fsuid_only = false;
@@ -2607,9 +2739,13 @@ refind_writable:
continue;
if (with_delete && !(open_file->fid.access & DELETE))
continue;
- if ((flags & FIND_WR_NO_PENDING_DELETE) &&
+ if ((find_flags & FIND_NO_PENDING_DELETE) &&
open_file->status_file_deleted)
continue;
+ if ((find_flags & FIND_OPEN_FLAGS) &&
+ !open_flags_match(cifs_inode, open_flags,
+ open_file->f_flags))
+ continue;
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
if (!open_file->invalidHandle) {
/* found a good writable file */
@@ -2669,16 +2805,19 @@ find_writable_file(struct cifsInodeInfo *cifs_inode, int flags)
return cfile;
}
-int
-cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
- int flags,
- struct cifsFileInfo **ret_file)
+int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
+ struct inode *inode, int flags,
+ struct cifsFileInfo **ret_file)
{
struct cifsFileInfo *cfile;
- void *page = alloc_dentry_path();
+ void *page;
*ret_file = NULL;
+ if (inode)
+ return cifs_get_writable_file(CIFS_I(inode), flags, ret_file);
+
+ page = alloc_dentry_path();
spin_lock(&tcon->open_file_lock);
list_for_each_entry(cfile, &tcon->openFileList, tlist) {
struct cifsInodeInfo *cinode;
@@ -2726,17 +2865,7 @@ cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
cinode = CIFS_I(d_inode(cfile->dentry));
spin_unlock(&tcon->open_file_lock);
free_dentry_path(page);
- *ret_file = find_readable_file(cinode, 0);
- if (*ret_file) {
- spin_lock(&cinode->open_file_lock);
- if ((*ret_file)->status_file_deleted) {
- spin_unlock(&cinode->open_file_lock);
- cifsFileInfo_put(*ret_file);
- *ret_file = NULL;
- } else {
- spin_unlock(&cinode->open_file_lock);
- }
- }
+ *ret_file = find_readable_file(cinode, FIND_ANY);
return *ret_file ? 0 : -ENOENT;
}
@@ -2808,7 +2937,7 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
}
if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) {
- smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY);
+ smbfile = find_writable_file(CIFS_I(inode), FIND_ANY);
if (smbfile) {
rc = server->ops->flush(xid, tcon, &smbfile->fid);
cifsFileInfo_put(smbfile);
@@ -2878,6 +3007,7 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from)
}
rc = netfs_buffered_write_iter_locked(iocb, from, NULL);
+ cifs_update_i_blocks_after_write(iocb, rc);
out:
up_read(&cinode->lock_sem);
@@ -2907,6 +3037,7 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)
(CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
((cifs_sb_flags(cifs_sb) & CIFS_MOUNT_NOPOSIXBRL) == 0)) {
written = netfs_file_write_iter(iocb, from);
+ cifs_update_i_blocks_after_write(iocb, written);
goto out;
}
written = cifs_writev(iocb, from);
@@ -2919,6 +3050,7 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)
* these pages but not on the region from pos to ppos+len-1.
*/
written = netfs_file_write_iter(iocb, from);
+ cifs_update_i_blocks_after_write(iocb, written);
if (CIFS_CACHE_READ(cinode)) {
/*
* We have read level caching and we have just sent a write
@@ -2937,6 +3069,15 @@ out:
return written;
}
+ssize_t cifs_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+ ssize_t written;
+
+ written = netfs_file_write_iter(iocb, from);
+ cifs_update_i_blocks_after_write(iocb, written);
+ return written;
+}
+
ssize_t cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
ssize_t rc;
@@ -2961,6 +3102,7 @@ ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (iocb->ki_filp->f_flags & O_DIRECT) {
written = netfs_unbuffered_write_iter(iocb, from);
+ cifs_update_i_blocks_after_write(iocb, written);
if (written > 0 && CIFS_CACHE_READ(cinode)) {
cifs_zap_mapping(inode);
cifs_dbg(FYI,
@@ -2976,6 +3118,7 @@ ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
return written;
written = netfs_file_write_iter(iocb, from);
+ cifs_update_i_blocks_after_write(iocb, written);
if (!CIFS_CACHE_WRITE(CIFS_I(inode))) {
rc = filemap_fdatawrite(inode->i_mapping);
@@ -3123,13 +3266,26 @@ static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file,
bool from_readdir)
{
+ struct cifs_sb_info *cifs_sb;
+ unsigned long tlw;
+
if (!cifsInode)
return true;
+ cifs_sb = CIFS_SB(cifsInode);
+
if (is_inode_writable(cifsInode) ||
((cifsInode->oplock & CIFS_CACHE_RW_FLG) != 0 && from_readdir)) {
/* This inode is open for write at least once */
- struct cifs_sb_info *cifs_sb = CIFS_SB(cifsInode);
+
+ /*
+ * Readdir data is unreliable when we have writable handles or
+ * an exclusive lease -- never allow it to change i_size, even
+ * on direct-IO mounts where the server's directory metadata
+ * can still lag behind the actual file state.
+ */
+ if (from_readdir)
+ return false;
if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_DIRECT_IO) {
/* since no page cache to corrupt on directio
@@ -3141,8 +3297,40 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file,
return true;
return false;
- } else
- return true;
+ }
+
+ /*
+ * No writable handles open. Check whether we are within the attribute
+ * cache validity window of a recent local modification.
+ *
+ * For the close() path: _cifsFileInfo_put() stamps time_last_write
+ * (via smp_store_release()) before releasing open_file_lock. That
+ * spin_unlock() is a store-release that pairs with the spin_lock()
+ * (load-acquire) in is_inode_writable() above, so if
+ * is_inode_writable() returned false the smp_load_acquire() below is
+ * guaranteed to observe any time_last_write update from a concurrent
+ * close(), covering all close paths including background I/O.
+ *
+ * For the setattr/truncate paths: those callers use smp_store_release()
+ * directly; the smp_load_acquire() below pairs with that store. There
+ * is no shared lock between setattr and readdir, so this relies on
+ * acquire-release semantics alone. The store propagation latency on
+ * weakly-ordered architectures (nanoseconds) is negligible relative to
+ * the acregmax window (seconds) and the readdir RPC round-trip
+ * (milliseconds), making this a sound design choice in practice.
+ *
+ * time_last_write == 0 means the inode has never been written locally;
+ * skip the window check to avoid false positives near boot time when
+ * jiffies is still close to INITIAL_JIFFIES on 32-bit systems.
+ */
+ if (from_readdir) {
+ /* Pairs with smp_store_release() in _cifsFileInfo_put() and setattr. */
+ tlw = smp_load_acquire(&cifsInode->time_last_write);
+ if (tlw && time_before(jiffies, tlw + cifs_sb->ctx->acregmax))
+ return false;
+ }
+
+ return true;
}
void cifs_oplock_break(struct work_struct *work)
@@ -3163,18 +3351,15 @@ void cifs_oplock_break(struct work_struct *work)
__u64 persistent_fid, volatile_fid;
__u16 net_fid;
- /*
- * Hold a reference to the superblock to prevent it and its inodes from
- * being freed while we are accessing cinode. Otherwise, _cifsFileInfo_put()
- * may release the last reference to the sb and trigger inode eviction.
- */
- cifs_sb_active(sb);
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
TASK_UNINTERRUPTIBLE);
- tlink = cifs_sb_tlink(cifs_sb);
- if (IS_ERR(tlink))
+ tlink = cifs_get_tlink(cfile->tlink);
+ if (IS_ERR_OR_NULL(tlink)) {
+ /* drop the reference taken when the break was queued */
+ _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
goto out;
+ }
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
@@ -3253,9 +3438,40 @@ oplock_break_ack:
cifs_put_tlink(tlink);
out:
cifs_done_oplock_break(cinode);
- cifs_sb_deactive(sb);
}
+#ifdef CONFIG_SWAP
+static void cifs_swap_submit_write(struct swap_io_ctx *ctx)
+{
+ struct swap_iocb *sio = ctx->sio;
+ struct iov_iter iter;
+ int ret;
+
+ swap_fs_prepare_rw(ctx, WRITE, &iter);
+ ret = netfs_unbuffered_write_iter_locked(&sio->iocb, &iter, NULL);
+ if (ret != -EIOCBQUEUED)
+ sio->iocb.ki_complete(&sio->iocb, ret);
+}
+
+static void cifs_swap_submit_read(struct swap_io_ctx *ctx)
+{
+ struct swap_iocb *sio = ctx->sio;
+ struct iov_iter iter;
+ int ret;
+
+ swap_fs_prepare_rw(ctx, READ, &iter);
+ ret = netfs_unbuffered_read_iter_locked(&sio->iocb, &iter);
+ if (ret != -EIOCBQUEUED)
+ sio->iocb.ki_complete(&sio->iocb, ret);
+}
+
+static const struct swap_ops cifs_swap_ops = {
+ .flags = SWAP_OPS_F_REQUIRE_NOFS,
+ .submit_write = cifs_swap_submit_write,
+ .submit_read = cifs_swap_submit_read,
+ .can_merge = swap_fs_can_merge,
+};
+
static int cifs_swap_activate(struct swap_info_struct *sis,
struct file *swap_file, sector_t *span)
{
@@ -3266,7 +3482,7 @@ static int cifs_swap_activate(struct swap_info_struct *sis,
cifs_dbg(FYI, "swap activate\n");
- if (!swap_file->f_mapping->a_ops->swap_rw)
+ if (swap_file->f_mapping->a_ops != &cifs_addr_ops)
/* Cannot support swap */
return -EINVAL;
@@ -3297,9 +3513,7 @@ static int cifs_swap_activate(struct swap_info_struct *sis,
* but we could add call to grab a byte range lock to prevent others
* from reading or writing the file
*/
-
- sis->flags |= SWP_FS_OPS;
- return add_swap_extent(sis, 0, sis->max, 0);
+ return swap_fs_activate(sis, &cifs_swap_ops);
}
static void cifs_swap_deactivate(struct file *file)
@@ -3315,26 +3529,10 @@ static void cifs_swap_deactivate(struct file *file)
/* do we need to unpin (or unlock) the file */
}
-
-/**
- * cifs_swap_rw - SMB3 address space operation for swap I/O
- * @iocb: target I/O control block
- * @iter: I/O buffer
- *
- * Perform IO to the swap-file. This is much like direct IO.
- */
-static int cifs_swap_rw(struct kiocb *iocb, struct iov_iter *iter)
-{
- ssize_t ret;
-
- if (iov_iter_rw(iter) == READ)
- ret = netfs_unbuffered_read_iter_locked(iocb, iter);
- else
- ret = netfs_unbuffered_write_iter_locked(iocb, iter, NULL);
- if (ret < 0)
- return ret;
- return 0;
-}
+#else
+#define cifs_swap_activate NULL
+#define cifs_swap_deactivate NULL
+#endif /* CONFIG_SWAP */
const struct address_space_operations cifs_addr_ops = {
.read_folio = netfs_read_folio,
@@ -3351,7 +3549,6 @@ const struct address_space_operations cifs_addr_ops = {
*/
.swap_activate = cifs_swap_activate,
.swap_deactivate = cifs_swap_deactivate,
- .swap_rw = cifs_swap_rw,
};
/*
diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index 54090739535f..abc29500a664 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -80,7 +80,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
fsparam_flag_no("forcegid", Opt_forcegid),
fsparam_flag("noblocksend", Opt_noblocksend),
fsparam_flag("noautotune", Opt_noautotune),
- fsparam_flag("nolease", Opt_nolease),
+ fsparam_flag_no("lease", Opt_lease),
fsparam_flag_no("hard", Opt_hard),
fsparam_flag_no("soft", Opt_soft),
fsparam_flag_no("perm", Opt_perm),
@@ -420,7 +420,7 @@ static int parse_symlink_flavor(struct fs_context *fc, char *value,
#define DUP_CTX_STR(field) \
do { \
if (ctx->field) { \
- new_ctx->field = kstrdup(ctx->field, GFP_ATOMIC); \
+ new_ctx->field = kstrdup(ctx->field, GFP_KERNEL); \
if (new_ctx->field == NULL) { \
smb3_cleanup_fs_context_contents(new_ctx); \
return -ENOMEM; \
@@ -536,37 +536,6 @@ cifs_parse_smb_version(struct fs_context *fc, char *value, struct smb3_fs_contex
return 0;
}
-int smb3_parse_opt(const char *options, const char *key, char **val)
-{
- int rc = -ENOENT;
- char *opts, *orig, *p;
-
- orig = opts = kstrdup(options, GFP_KERNEL);
- if (!opts)
- return -ENOMEM;
-
- while ((p = strsep(&opts, ","))) {
- char *nval;
-
- if (!*p)
- continue;
- if (strncasecmp(p, key, strlen(key)))
- continue;
- nval = strchr(p, '=');
- if (nval) {
- if (nval == p)
- continue;
- *nval++ = 0;
- *val = kstrdup(nval, GFP_KERNEL);
- rc = !*val ? -ENOMEM : 0;
- goto out;
- }
- }
-out:
- kfree(orig);
- return rc;
-}
-
/*
* Remove duplicate path delimiters. Windows is supposed to do that
* but there are some bugs that prevent rename from working if there are
@@ -588,6 +557,10 @@ char *cifs_sanitize_prepath(char *prepath, gfp_t gfp)
while (IS_DELIM(*cursor1))
cursor1++;
+ /* exit in case of only delimiters */
+ if (!*cursor1)
+ return NULL;
+
/* copy the first letter */
*cursor2 = *cursor1;
@@ -658,13 +631,17 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
/* make sure we have a valid UNC double delimiter prefix */
len = strspn(devname, delims);
- if (len != 2)
+ if (len != 2) {
+ cifs_dbg(VFS, "UNC: path must begin with // or \\\\\n");
return -EINVAL;
+ }
/* find delimiter between host and sharename */
pos = strpbrk(devname + 2, delims);
- if (!pos)
+ if (!pos) {
+ cifs_dbg(VFS, "UNC: missing delimiter between hostname and share name\n");
return -EINVAL;
+ }
/* record the server hostname */
kfree(ctx->server_hostname);
@@ -677,8 +654,10 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
/* now go until next delimiter or end of string */
len = strcspn(pos, delims);
- if (!len)
+ if (!len) {
+ cifs_dbg(VFS, "UNC: missing share name\n");
return -EINVAL;
+ }
/* move "pos" up to delimiter or NULL */
pos += len;
@@ -714,6 +693,41 @@ static int smb3_handle_conflicting_options(struct fs_context *fc)
{
struct smb3_fs_context *ctx = smb3_fc2context(fc);
+ if (ctx->rdma && ctx->vals->protocol_id < SMB30_PROT_ID) {
+ cifs_errorf(fc, "SMB Direct requires Version >=3.0\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (ctx->multiuser && !IS_ENABLED(CONFIG_KEYS)) {
+ cifs_errorf(fc, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (ctx->multiuser && ctx->upcall_target == UPTARGET_MOUNT) {
+ cifs_errorf(fc, "multiuser mount option not supported with upcalltarget set as 'mount'\n");
+ return -EINVAL;
+ }
+
+ if (ctx->uid_specified && !ctx->forceuid_specified) {
+ ctx->override_uid = 1;
+ pr_notice("enabling forceuid mount option implicitly because uid= option is specified\n");
+ }
+
+ if (ctx->gid_specified && !ctx->forcegid_specified) {
+ ctx->override_gid = 1;
+ pr_notice("enabling forcegid mount option implicitly because gid= option is specified\n");
+ }
+
+ if (ctx->override_uid && !ctx->uid_specified) {
+ ctx->override_uid = 0;
+ pr_notice("ignoring forceuid mount option specified with no uid= option\n");
+ }
+
+ if (ctx->override_gid && !ctx->gid_specified) {
+ ctx->override_gid = 0;
+ pr_notice("ignoring forcegid mount option specified with no gid= option\n");
+ }
+
if (ctx->multichannel_specified) {
if (ctx->multichannel) {
if (!ctx->max_channels_specified) {
@@ -732,19 +746,14 @@ static int smb3_handle_conflicting_options(struct fs_context *fc)
return -EINVAL;
}
}
- } else {
- if (ctx->max_channels_specified) {
- if (ctx->max_channels > 1)
- ctx->multichannel = true;
- else
- ctx->multichannel = false;
- } else {
+ } else if (ctx->max_channels_specified) {
+ if (ctx->max_channels > 1)
+ ctx->multichannel = true;
+ else
ctx->multichannel = false;
- ctx->max_channels = 1;
- }
}
- //resetting default values as remount doesn't initialize fs_context again
+ /* clear parse-time latches so they don't persist across remounts */
ctx->multichannel_specified = false;
ctx->max_channels_specified = false;
@@ -757,7 +766,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
static int smb3_fs_context_parse_monolithic(struct fs_context *fc,
void *data);
static int smb3_get_tree(struct fs_context *fc);
-static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels);
+static void smb3_sync_ses_chan_max(struct cifs_ses *ses, size_t max_channels);
static int smb3_reconfigure(struct fs_context *fc);
static const struct fs_context_operations smb3_fs_context_ops = {
@@ -825,28 +834,23 @@ static int smb3_fs_context_parse_monolithic(struct fs_context *fc,
if (ret < 0)
break;
}
- return ret ?: smb3_handle_conflicting_options(fc);
+ return ret;
}
/*
- * Validate the preparsed information in the config.
+ * smb3_fs_context_validate - check initial-mount-only constraints:
+ * UNC presence, address resolution, dialect warnings
+ *
+ * @fc: generic mount context
*/
static int smb3_fs_context_validate(struct fs_context *fc)
{
struct smb3_fs_context *ctx = smb3_fc2context(fc);
+ int rc;
- if (ctx->rdma && ctx->vals->protocol_id < SMB30_PROT_ID) {
- cifs_errorf(fc, "SMB Direct requires Version >=3.0\n");
- return -EOPNOTSUPP;
- }
-
-#ifndef CONFIG_KEYS
- /* Muliuser mounts require CONFIG_KEYS support */
- if (ctx->multiuser) {
- cifs_errorf(fc, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n");
- return -1;
- }
-#endif
+ rc = smb3_handle_conflicting_options(fc);
+ if (rc)
+ return rc;
if (ctx->got_version == false)
pr_warn_once("No dialect specified on mount. Default has changed to a more secure dialect, SMB2.1 or later (e.g. SMB3.1.1), from CIFS (SMB1). To use the less secure SMB1 dialect to access old servers which do not support SMB3.1.1 (or even SMB3 or SMB2.1) specify vers=1.0 on mount.\n");
@@ -881,26 +885,6 @@ static int smb3_fs_context_validate(struct fs_context *fc)
/* set the port that we got earlier */
cifs_set_port((struct sockaddr *)&ctx->dstaddr, ctx->port);
- if (ctx->uid_specified && !ctx->forceuid_specified) {
- ctx->override_uid = 1;
- pr_notice("enabling forceuid mount option implicitly because uid= option is specified\n");
- }
-
- if (ctx->gid_specified && !ctx->forcegid_specified) {
- ctx->override_gid = 1;
- pr_notice("enabling forcegid mount option implicitly because gid= option is specified\n");
- }
-
- if (ctx->override_uid && !ctx->uid_specified) {
- ctx->override_uid = 0;
- pr_notice("ignoring forceuid mount option specified with no uid= option\n");
- }
-
- if (ctx->override_gid && !ctx->gid_specified) {
- ctx->override_gid = 0;
- pr_notice("ignoring forcegid mount option specified with no gid= option\n");
- }
-
return 0;
}
@@ -910,7 +894,7 @@ static int smb3_get_tree_common(struct fs_context *fc)
struct dentry *root;
int rc = 0;
- root = cifs_smb3_do_mount(fc->fs_type, 0, ctx);
+ root = cifs_smb3_do_mount(fc, ctx);
if (IS_ERR(root))
return PTR_ERR(root);
@@ -1031,25 +1015,34 @@ do { \
int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
{
+ char *password = NULL, *password2 = NULL;
+
if (ses->password &&
cifs_sb->ctx->password &&
strcmp(ses->password, cifs_sb->ctx->password)) {
- kfree_sensitive(cifs_sb->ctx->password);
- cifs_sb->ctx->password = kstrdup(ses->password, GFP_KERNEL);
- if (!cifs_sb->ctx->password)
+ password = kstrdup(ses->password, GFP_KERNEL);
+ if (!password)
return -ENOMEM;
}
if (ses->password2 &&
cifs_sb->ctx->password2 &&
strcmp(ses->password2, cifs_sb->ctx->password2)) {
- kfree_sensitive(cifs_sb->ctx->password2);
- cifs_sb->ctx->password2 = kstrdup(ses->password2, GFP_KERNEL);
- if (!cifs_sb->ctx->password2) {
- kfree_sensitive(cifs_sb->ctx->password);
- cifs_sb->ctx->password = NULL;
+ password2 = kstrdup(ses->password2, GFP_KERNEL);
+ if (!password2) {
+ kfree_sensitive(password);
return -ENOMEM;
}
}
+
+ if (password) {
+ kfree_sensitive(cifs_sb->ctx->password);
+ cifs_sb->ctx->password = password;
+ }
+ if (password2) {
+ kfree_sensitive(cifs_sb->ctx->password2);
+ cifs_sb->ctx->password2 = password2;
+ }
+
return 0;
}
@@ -1062,7 +1055,7 @@ int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_se
* with the session's channel lock. This should be called whenever the maximum
* allowed channels for a session changes (e.g., after a remount or reconfigure).
*/
-static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels)
+static void smb3_sync_ses_chan_max(struct cifs_ses *ses, size_t max_channels)
{
spin_lock(&ses->chan_lock);
ses->chan_max = max_channels;
@@ -1072,12 +1065,15 @@ static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channe
static int smb3_reconfigure(struct fs_context *fc)
{
struct smb3_fs_context *ctx = smb3_fc2context(fc);
+ struct smb3_fs_context *new_ctx = NULL;
+ struct smb3_fs_context *old_ctx = NULL;
struct dentry *root = fc->root;
struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses;
unsigned int rsize = ctx->rsize, wsize = ctx->wsize;
char *new_password = NULL, *new_password2 = NULL;
bool need_recon = false;
+ bool need_mchan_update;
int rc;
if (ses->expired_pwd)
@@ -1087,6 +1083,18 @@ static int smb3_reconfigure(struct fs_context *fc)
if (rc)
return rc;
+ rc = smb3_handle_conflicting_options(fc);
+ if (rc)
+ return rc;
+
+ old_ctx = kzalloc_obj(*old_ctx);
+ if (!old_ctx)
+ return -ENOMEM;
+
+ rc = smb3_fs_context_dup(old_ctx, cifs_sb->ctx);
+ if (rc)
+ goto free_old_ctx;
+
/*
* We can not change UNC/username/password/domainname/
* workstation_name/nodename/iocharset
@@ -1096,16 +1104,22 @@ static int smb3_reconfigure(struct fs_context *fc)
STEAL_STRING(cifs_sb, ctx, UNC);
STEAL_STRING(cifs_sb, ctx, source);
STEAL_STRING(cifs_sb, ctx, username);
+ STEAL_STRING(cifs_sb, ctx, domainname);
+ STEAL_STRING(cifs_sb, ctx, nodename);
+ STEAL_STRING(cifs_sb, ctx, iocharset);
- if (need_recon == false)
+ if (!need_recon) {
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
- else {
+ } else {
if (ctx->password) {
new_password = kstrdup(ctx->password, GFP_KERNEL);
- if (!new_password)
- return -ENOMEM;
- } else
+ if (!new_password) {
+ rc = -ENOMEM;
+ goto restore_ctx;
+ }
+ } else {
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
+ }
}
/*
@@ -1115,11 +1129,29 @@ static int smb3_reconfigure(struct fs_context *fc)
if (ctx->password2) {
new_password2 = kstrdup(ctx->password2, GFP_KERNEL);
if (!new_password2) {
- kfree_sensitive(new_password);
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto restore_ctx;
}
- } else
+ } else {
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password2);
+ }
+
+ /* if rsize or wsize not passed in on remount, use previous values */
+ ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize;
+ ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize;
+
+ new_ctx = kzalloc_obj(*new_ctx);
+ if (!new_ctx) {
+ rc = -ENOMEM;
+ goto restore_ctx;
+ }
+
+ rc = smb3_fs_context_dup(new_ctx, ctx);
+ if (rc)
+ goto restore_ctx;
+
+ need_mchan_update = ctx->multichannel != cifs_sb->ctx->multichannel ||
+ ctx->max_channels != cifs_sb->ctx->max_channels;
/*
* we may update the passwords in the ses struct below. Make sure we do
@@ -1130,54 +1162,55 @@ static int smb3_reconfigure(struct fs_context *fc)
/*
* smb2_reconnect may swap password and password2 in case session setup
* failed. First get ctx passwords in sync with ses passwords. It should
- * be okay to do this even if this function were to return an error at a
- * later stage
+ * be done before committing new passwords.
*/
rc = smb3_sync_session_ctx_passwords(cifs_sb, ses);
if (rc) {
mutex_unlock(&ses->session_mutex);
- kfree_sensitive(new_password);
- kfree_sensitive(new_password2);
- return rc;
+ goto cleanup_new_ctx;
+ }
+
+ /*
+ * If multichannel or max_channels has changed, update the session's channels accordingly.
+ * This may add or remove channels to match the new configuration.
+ */
+ if (need_mchan_update) {
+ /* Prevent concurrent scaling operations */
+ spin_lock(&ses->ses_lock);
+ if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) {
+ spin_unlock(&ses->ses_lock);
+ mutex_unlock(&ses->session_mutex);
+ rc = -EINVAL;
+ goto cleanup_new_ctx;
+ }
+ ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS;
+ spin_unlock(&ses->ses_lock);
}
/*
- * now that allocations for passwords are done, commit them
+ * Commit session passwords before any channel work so newly added
+ * channels authenticate with the new credentials.
*/
if (new_password) {
kfree_sensitive(ses->password);
ses->password = new_password;
+ new_password = NULL;
}
if (new_password2) {
kfree_sensitive(ses->password2);
ses->password2 = new_password2;
+ new_password2 = NULL;
}
- /*
- * If multichannel or max_channels has changed, update the session's channels accordingly.
- * This may add or remove channels to match the new configuration.
- */
- if ((ctx->multichannel != cifs_sb->ctx->multichannel) ||
- (ctx->max_channels != cifs_sb->ctx->max_channels)) {
-
+ if (need_mchan_update) {
/* Synchronize ses->chan_max with the new mount context */
smb3_sync_ses_chan_max(ses, ctx->max_channels);
- /* Now update the session's channels to match the new configuration */
- /* Prevent concurrent scaling operations */
- spin_lock(&ses->ses_lock);
- if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) {
- spin_unlock(&ses->ses_lock);
- mutex_unlock(&ses->session_mutex);
- return -EINVAL;
- }
- ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS;
- spin_unlock(&ses->ses_lock);
mutex_unlock(&ses->session_mutex);
- rc = smb3_update_ses_channels(ses, ses->server,
- false /* from_reconnect */,
- false /* disable_mchan */);
+ smb3_update_ses_channels(ses, ses->server,
+ false /* from_reconnect */,
+ false /* disable_mchan */);
/* Clear scaling flag after operation */
spin_lock(&ses->ses_lock);
@@ -1187,16 +1220,12 @@ static int smb3_reconfigure(struct fs_context *fc)
mutex_unlock(&ses->session_mutex);
}
- STEAL_STRING(cifs_sb, ctx, domainname);
- STEAL_STRING(cifs_sb, ctx, nodename);
- STEAL_STRING(cifs_sb, ctx, iocharset);
-
- /* if rsize or wsize not passed in on remount, use previous values */
- ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize;
- ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize;
-
smb3_cleanup_fs_context_contents(cifs_sb->ctx);
- rc = smb3_fs_context_dup(cifs_sb->ctx, ctx);
+ memcpy(cifs_sb->ctx, new_ctx, sizeof(*new_ctx));
+ kfree(new_ctx);
+ new_ctx = NULL;
+ smb3_cleanup_fs_context(old_ctx);
+ old_ctx = NULL;
smb3_update_mnt_flags(cifs_sb);
#ifdef CONFIG_CIFS_DFS_UPCALL
if (!rc)
@@ -1204,6 +1233,19 @@ static int smb3_reconfigure(struct fs_context *fc)
#endif
return rc;
+
+cleanup_new_ctx:
+ smb3_cleanup_fs_context_contents(new_ctx);
+restore_ctx:
+ kfree(new_ctx);
+ kfree_sensitive(new_password);
+ kfree_sensitive(new_password2);
+ smb3_cleanup_fs_context_contents(cifs_sb->ctx);
+ memcpy(cifs_sb->ctx, old_ctx, sizeof(*old_ctx));
+free_old_ctx:
+ kfree(old_ctx);
+
+ return rc;
}
static int smb3_fs_context_parse_param(struct fs_context *fc,
@@ -1330,8 +1372,8 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
case Opt_noautotune:
ctx->noautotune = 1;
break;
- case Opt_nolease:
- ctx->no_lease = 1;
+ case Opt_lease:
+ ctx->no_lease = result.negated;
break;
case Opt_nosparse:
ctx->no_sparse = 1;
@@ -1899,11 +1941,6 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
}
/* case Opt_ignore: - is ignored as expected ... */
- if (ctx->multiuser && ctx->upcall_target == UPTARGET_MOUNT) {
- cifs_errorf(fc, "multiuser mount option not supported with upcalltarget set as 'mount'\n");
- goto cifs_parse_mount_err;
- }
-
return 0;
cifs_parse_mount_err:
@@ -1997,7 +2034,7 @@ int smb3_init_fs_context(struct fs_context *fc)
ctx->backupuid_specified = false; /* no backup intent for a user */
ctx->backupgid_specified = false; /* no backup intent for a group */
- ctx->retrans = 1;
+ ctx->retrans = 0;
ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT;
ctx->symlink_type = CIFS_SYMLINK_TYPE_DEFAULT;
ctx->nonativesocket = 0;
diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h
index 0b64fcb5d302..a80a5caff23c 100644
--- a/fs/smb/client/fs_context.h
+++ b/fs/smb/client/fs_context.h
@@ -102,7 +102,7 @@ enum cifs_param {
Opt_forcegid,
Opt_noblocksend,
Opt_noautotune,
- Opt_nolease,
+ Opt_lease,
Opt_nosparse,
Opt_hard,
Opt_soft,
diff --git a/fs/smb/client/fscache.c b/fs/smb/client/fscache.c
index 01424a5cdb99..9f66bbcd3801 100644
--- a/fs/smb/client/fscache.c
+++ b/fs/smb/client/fscache.c
@@ -38,7 +38,6 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
struct TCP_Server_Info *server = tcon->ses->server;
struct fscache_volume *vcookie;
const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr;
- size_t slen, i;
char *sharename;
char *key;
int ret = -ENOMEM;
@@ -73,10 +72,7 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
return PTR_ERR(sharename);
}
- slen = strlen(sharename);
- for (i = 0; i < slen; i++)
- if (sharename[i] == '/')
- sharename[i] = ';';
+ strreplace(sharename, '/', ';');
key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename);
if (!key)
diff --git a/fs/smb/client/gen_smb1_mapping b/fs/smb/client/gen_smb1_mapping
new file mode 100644
index 000000000000..c2b2939a83c6
--- /dev/null
+++ b/fs/smb/client/gen_smb1_mapping
@@ -0,0 +1,124 @@
+#!/usr/bin/perl -w
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Script to generate SMB1 error mapping tables.
+#
+# Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
+# Author(s): Huiwen He <hehuiwen@kylinos.cn>
+# ChenXiaoSong <chenxiaosong@kylinos.cn>
+#
+use strict;
+
+if ($#ARGV != 1) {
+ print STDERR "Usage: $0 <in-file> <out-file>\n";
+ exit(2);
+}
+
+# Parse input parameters and extract filenames
+my $in_file = $ARGV[0];
+my $out_file = $ARGV[1];
+my $input_name = (split m|/|, $in_file)[-1];
+my $output_name = (split m|/|, $out_file)[-1];
+my $script_name = (split m|/|, $0)[-1];
+my @list = ();
+my %seen = ();
+my $current_class = "";
+
+# Parse annotated entries from the input file
+open(my $in, "<", $in_file) or die "Cannot open $in_file: $!";
+if ($in_file =~ /nterr\.h$/) {
+ while (<$in>) {
+ # Handle backslash line continuation
+ $_ .= <$in> while s/\\\s*\n//;
+
+ # Match #define NT_STATUS_... followed by // CLASS, CODE or /* CLASS, CODE */
+ my $re = qr{^\s*#define\s+(NT_STATUS_[A-Za-z0-9_]+)\s+(.+?)\s*} .
+ qr{(?://\s*|/\*\s*)([A-Z0-9_]+)\s*,\s*([A-Za-z0-9_]+)};
+
+ if (/$re/) {
+ my ($name, $val_str, $class, $code) = ($1, $2, $3, $4);
+
+ # Skip duplicate macro names
+ next if $seen{$name}++;
+
+ # Clean up value string (remove parens, spaces)
+ $val_str =~ s/[\s\(\)]//g;
+ my $val = 0;
+ foreach my $part (split(/\|/, $val_str)) {
+ $val |= hex($part);
+ }
+ push @list, { val => $val, name => $name, class => $class, code => $code };
+ } elsif (/^\s*#define\s+NT_STATUS_.*(?:\/\/|\/\*)/) {
+ # Error if macro has a comment (// or /*) but fails mapping format
+ die "Error: Invalid mapping comment format in $in_file: $_";
+ }
+ }
+} elsif ($in_file =~ /smberr\.h$/) {
+ while (<$in>) {
+ # Handle backslash line continuation
+ $_ .= <$in> while s/\\\s*\n//;
+
+ # Detect current error class from header comments (ERRDOS or ERRSRV)
+ if (/generated with the (\w+) error class/) {
+ $current_class = $1;
+ }
+
+ # Match #define ERR/Err_... <value> followed by // -POSIX_ERR or /* -POSIX_ERR */
+ if (/^\s*#define\s+((?:ERR|Err)[A-Za-z0-9_]+)\s+([0-9a-fA-FxX]+)\s*(?:\/\/|\/\*)\s*(-[A-Z0-9_]+)/) {
+ my ($name, $val_str, $error) = ($1, $2, $3);
+ my $val = ($val_str =~ /^0x/i) ? hex($val_str) : $val_str;
+ push @list, { val => $val, name => $name, error => $error, class => $current_class };
+ } elsif ($current_class && /^\s*#define\s+(?:ERR|Err).*?(?:\/\/|\/\*)/) {
+ # Error if macro has a comment (// or /*) but fails mapping format
+ die "Error: Invalid mapping comment format in $in_file: $_";
+ }
+ }
+}
+close($in);
+
+# Fail if no entries were found to avoid broken builds
+die "Error: No mapping entries found in $in_file\n" unless @list;
+
+# Sort entries numerically by value
+@list = sort { $a->{val} <=> $b->{val} } @list;
+
+# Generate the C mapping table output file
+open(my $out, ">", $out_file) or die "Cannot open $out_file: $!";
+print $out "/* Autogenerated from $input_name by $script_name */\n\n";
+
+if ($output_name eq "smb1_mapping_table.c") {
+ # Generate NT status -> DOS error mapping file
+
+ my $count = scalar @list;
+ my $full_names = "";
+
+ for (my $i = 0; $i < $count; $i++) {
+ my $e = $list[$i];
+ my $val = $e->{val};
+
+ $full_names .= $e->{name};
+
+ # Merge synonyms
+ if ($i < $count - 1 && $list[$i + 1]->{val} == $val) {
+ $full_names .= " or ";
+ next;
+ }
+
+ printf $out "\t{ %s, %s, 0x%08x, \"%s\" },\n", $e->{class}, $e->{code}, $val, $full_names;
+
+ $full_names = "";
+ }
+} elsif ($output_name eq "smb1_err_dos_map.c" || $output_name eq "smb1_err_srv_map.c") {
+ # Generate SMB1 error -> POSIX error mapping file
+
+ # Filtered by exact output filename
+ my $filter = ($output_name eq "smb1_err_dos_map.c") ? "ERRDOS" : "ERRSRV";
+ foreach my $e (@list) {
+ if (!$filter || $e->{class} eq $filter) {
+ printf $out "\t{%s, %s},\n", $e->{name}, $e->{error};
+ }
+ }
+} else {
+ die "Error: Unsupported output target: $output_name\n";
+}
+close($out);
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 3e844c55ab8a..1fe0ef0a95db 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -28,6 +28,23 @@
#include "cached_dir.h"
#include "reparse.h"
+static void cifs_invalidate_cached_dir(struct cifs_tcon *tcon,
+ struct dentry *parent)
+{
+ struct cached_fid *parent_cfid = NULL;
+
+ if (!tcon || !parent)
+ return;
+
+ if (!open_cached_dir_by_dentry(tcon, parent, &parent_cfid)) {
+ mutex_lock(&parent_cfid->dirents.de_mutex);
+ parent_cfid->dirents.is_valid = false;
+ parent_cfid->dirents.is_failed = true;
+ mutex_unlock(&parent_cfid->dirents.de_mutex);
+ close_cached_dir(parent_cfid);
+ }
+}
+
/*
* Set parameters for the netfs library
*/
@@ -119,7 +136,7 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode);
mtime = inode_get_mtime(inode);
if (timespec64_equal(&mtime, &fattr->cf_mtime) &&
- cifs_i->netfs.remote_i_size == fattr->cf_eof) {
+ netfs_read_remote_i_size(inode) == fattr->cf_eof) {
cifs_dbg(FYI, "%s: inode %llu is unchanged\n",
__func__, cifs_i->uniqueid);
return;
@@ -173,12 +190,12 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
CIFS_I(inode)->time = 0; /* force reval */
return -ESTALE;
}
- if (inode_state_read_once(inode) & I_NEW)
- CIFS_I(inode)->netfs.zero_point = fattr->cf_eof;
-
cifs_revalidate_cache(inode, fattr);
spin_lock(&inode->i_lock);
+ if (inode_state_read_once(inode) & I_NEW)
+ netfs_write_zero_point(inode, fattr->cf_eof);
+
fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode);
fattr->cf_atime = timestamp_truncate(fattr->cf_atime, inode);
fattr->cf_ctime = timestamp_truncate(fattr->cf_ctime, inode);
@@ -212,20 +229,16 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
else
clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags);
- cifs_i->netfs.remote_i_size = fattr->cf_eof;
+ netfs_write_remote_i_size(inode, fattr->cf_eof);
/*
* Can't safely change the file size here if the client is writing to
* it due to potential races.
*/
if (is_size_safe_to_change(cifs_i, fattr->cf_eof, from_readdir)) {
i_size_write(inode, fattr->cf_eof);
-
- /*
- * i_blocks is not related to (i_size / i_blksize),
- * but instead 512 byte (2**9) size is required for
- * calculating num blocks.
- */
- inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9;
+ inode->i_blocks = CIFS_INO_BLOCKS(fattr->cf_bytes);
+ } else if (from_readdir && i_size_read(inode) != fattr->cf_eof) {
+ cifs_i->time = 0;
}
if (S_ISLNK(fattr->cf_mode) && fattr->cf_symlink_target) {
@@ -838,6 +851,7 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
struct smb311_posix_qinfo *info = &data->posix_fi;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ unsigned int sbflags = cifs_sb_flags(cifs_sb);
memset(fattr, 0, sizeof(*fattr));
@@ -882,8 +896,12 @@ out_reparse:
fattr->cf_symlink_target = data->symlink_target;
data->symlink_target = NULL;
}
- sid_to_id(cifs_sb, &data->posix_owner, fattr, SIDOWNER);
- sid_to_id(cifs_sb, &data->posix_group, fattr, SIDGROUP);
+ fattr->cf_uid = cifs_sb->ctx->linux_uid;
+ fattr->cf_gid = cifs_sb->ctx->linux_gid;
+ if (!(sbflags & CIFS_MOUNT_OVERR_UID))
+ sid_to_id(cifs_sb, &data->posix_owner, fattr, SIDOWNER);
+ if (!(sbflags & CIFS_MOUNT_OVERR_GID))
+ sid_to_id(cifs_sb, &data->posix_group, fattr, SIDGROUP);
cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n",
fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
@@ -898,6 +916,8 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
memset(fattr, 0, sizeof(*fattr));
+ if (data->unknown_nlink)
+ fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
fattr->cf_cifsattrs = le32_to_cpu(info->Attributes);
if (info->DeletePending)
fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING;
@@ -1134,7 +1154,7 @@ static void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_blo
fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
else {
fattr->cf_uniqueid = iunique(sb, ROOT_I);
- cifs_autodisable_serverino(cifs_sb);
+ cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number via get_srv_inum", rc);
}
return;
}
@@ -1200,7 +1220,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
break;
case IO_REPARSE_TAG_INTERNAL:
rc = 0;
- if (le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY) {
+ if (cifs_open_data_attrs(data) & ATTR_DIRECTORY) {
cifs_create_junction_fattr(fattr, sb);
goto out;
}
@@ -1224,7 +1244,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
*/
if (rc == -EOPNOTSUPP &&
IS_REPARSE_TAG_NAME_SURROGATE(data->reparse.tag) &&
- (le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY)) {
+ (cifs_open_data_attrs(data) & ATTR_DIRECTORY)) {
rc = 0;
cifs_create_junction_fattr(fattr, sb);
goto out;
@@ -1242,13 +1262,14 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
}
if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) {
- bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
+ bool directory = cifs_open_data_attrs(data) & ATTR_DIRECTORY;
+
rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
}
break;
}
- if (tcon->posix_extensions)
+ if (data->contains_posix_file_info)
smb311_posix_info_to_fattr(fattr, data, sb);
else
cifs_open_info_to_fattr(fattr, data, sb);
@@ -1601,7 +1622,7 @@ inode_has_hashed_dentries(struct inode *inode)
struct dentry *dentry;
spin_lock(&inode->i_lock);
- hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) {
+ for_each_alias(dentry, inode) {
if (!d_unhashed(dentry) || IS_ROOT(dentry)) {
spin_unlock(&inode->i_lock);
return true;
@@ -1631,7 +1652,7 @@ retry_iget5_locked:
fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION;
if (inode_has_hashed_dentries(inode)) {
- cifs_autodisable_serverino(CIFS_SB(sb));
+ cifs_autodisable_serverino(CIFS_SB(sb), "Inode number collision detected", 0);
iput(inode);
fattr->cf_uniqueid = iunique(sb, ROOT_I);
goto retry_iget5_locked;
@@ -1697,8 +1718,9 @@ struct inode *cifs_root_iget(struct super_block *sb)
iget_root:
if (!rc) {
if (fattr.cf_flags & CIFS_FATTR_JUNCTION) {
+ cifs_dbg(VFS, "Removing junction mark and disabling 'serverino' to prevent inode collisions\n");
fattr.cf_flags &= ~CIFS_FATTR_JUNCTION;
- cifs_autodisable_serverino(cifs_sb);
+ cifs_autodisable_serverino(cifs_sb, "Cannot retrieve attributes for junction point", rc);
}
inode = cifs_iget(sb, &fattr);
}
@@ -2073,6 +2095,9 @@ psx_del_no_retry:
cifs_set_file_info(inode, attrs, xid, full_path, origattr);
out_reval:
+ if (!rc && dentry->d_parent)
+ cifs_invalidate_cached_dir(tcon, dentry->d_parent);
+
if (inode) {
cifs_inode = CIFS_I(inode);
cifs_inode->time = 0; /* will force revalidate to get info
@@ -2268,6 +2293,13 @@ struct dentry *cifs_mkdir(struct mnt_idmap *idmap, struct inode *inode,
const char *full_path;
void *page;
+ /*
+ * vfs_mkdir() now passes S_IFDIR in @mode, but @mode is forwarded
+ * verbatim to the server and in the past only contained permission
+ * bits. Strip the type bit until SMB is verified to deal with it.
+ */
+ mode &= ~S_IFDIR;
+
cifs_dbg(FYI, "In cifs_mkdir, mode = %04ho inode = 0x%p\n",
mode, inode);
@@ -2384,7 +2416,6 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
}
rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb);
- cifs_put_tlink(tlink);
cifsInode = CIFS_I(d_inode(direntry));
@@ -2394,6 +2425,8 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
i_size_write(d_inode(direntry), 0);
clear_nlink(d_inode(direntry));
spin_unlock(&d_inode(direntry)->i_lock);
+ if (direntry->d_parent)
+ cifs_invalidate_cached_dir(tcon, direntry->d_parent);
}
/* force revalidate to go get info when needed */
@@ -2408,6 +2441,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
inode_set_ctime_current(d_inode(direntry));
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+ cifs_put_tlink(tlink);
rmdir_exit:
free_dentry_path(page);
@@ -2627,11 +2661,8 @@ unlink_target:
if (d_really_is_positive(target_dentry)) {
if (!rc) {
struct inode *inode = d_inode(target_dentry);
- /*
- * Samba and ksmbd servers allow renaming a target
- * directory that is open, so make sure to update
- * ->i_nlink and then mark it as delete pending.
- */
+
+ /* Update the target link count after rename. */
if (S_ISDIR(inode->i_mode)) {
drop_cached_dir_by_name(xid, tcon, to_name, cifs_sb);
spin_lock(&inode->i_lock);
@@ -2642,6 +2673,10 @@ unlink_target:
CIFS_I(inode)->time = 0; /* force reval */
inode_set_ctime_current(inode);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+ } else {
+ cifs_mark_open_handles_for_deleted_file(inode, to_name);
+ cifs_drop_nlink(inode);
+ inode_set_ctime_current(inode);
}
} else if (rc == -EACCES || rc == -EEXIST) {
/*
@@ -2674,6 +2709,12 @@ unlink_target:
}
/* force revalidate to go get info when needed */
+ if (!rc) {
+ cifs_invalidate_cached_dir(tcon, source_dentry->d_parent);
+ if (target_dentry->d_parent != source_dentry->d_parent)
+ cifs_invalidate_cached_dir(tcon, target_dentry->d_parent);
+ }
+
CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0;
cifs_rename_exit:
@@ -2696,7 +2737,8 @@ cifs_dentry_needs_reval(struct dentry *dentry)
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
struct cached_fid *cfid = NULL;
- if (test_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags))
+ if (test_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags) ||
+ test_bit(CIFS_INO_TMPFILE, &cifs_i->flags))
return false;
if (cifs_i->time == 0)
return true;
@@ -2777,7 +2819,9 @@ cifs_revalidate_mapping(struct inode *inode)
if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_RW_CACHE)
goto skip_invalidate;
- cifs_inode->netfs.zero_point = cifs_inode->netfs.remote_i_size;
+ spin_lock(&inode->i_lock);
+ netfs_write_zero_point(inode, netfs_inode(inode)->_remote_i_size);
+ spin_unlock(&inode->i_lock);
rc = filemap_invalidate_inode(inode, true, 0, LLONG_MAX);
if (rc) {
cifs_dbg(VFS, "%s: invalidate inode %p failed with rc %d\n",
@@ -2787,9 +2831,7 @@ cifs_revalidate_mapping(struct inode *inode)
}
skip_invalidate:
- clear_bit_unlock(CIFS_INO_LOCK, flags);
- smp_mb__after_atomic();
- wake_up_bit(flags, CIFS_INO_LOCK);
+ clear_and_wake_up_bit(CIFS_INO_LOCK, flags);
return rc;
}
@@ -2848,7 +2890,7 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry)
}
cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld\n",
- full_path, inode, icount_read(inode),
+ full_path, inode, icount_read_once(inode),
dentry, cifs_get_time(dentry), jiffies);
again:
@@ -2955,14 +2997,14 @@ int cifs_getattr(struct mnt_idmap *idmap, const struct path *path,
stat->attributes |= STATX_ATTR_ENCRYPTED;
/*
- * If on a multiuser mount without unix extensions or cifsacl being
- * enabled, and the admin hasn't overridden them, set the ownership
- * to the fsuid/fsgid of the current process.
+ * If on a multiuser mount without unix extensions, posix extensions
+ * or cifsacl being enabled, and the admin hasn't overridden them,
+ * set the ownership to the fsuid/fsgid of the current process.
*/
sbflags = cifs_sb_flags(cifs_sb);
if ((sbflags & CIFS_MOUNT_MULTIUSER) &&
!(sbflags & CIFS_MOUNT_CIFS_ACL) &&
- !tcon->unix_ext) {
+ !tcon->unix_ext && !tcon->posix_extensions) {
if (!(sbflags & CIFS_MOUNT_OVERR_UID))
stat->uid = current_fsuid();
if (!(sbflags & CIFS_MOUNT_OVERR_GID))
@@ -2997,7 +3039,7 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
}
}
- cfile = find_readable_file(cifs_i, false);
+ cfile = find_readable_file(cifs_i, FIND_ANY);
if (cfile == NULL)
return -EINVAL;
@@ -3013,14 +3055,45 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
void cifs_setsize(struct inode *inode, loff_t offset)
{
+ loff_t old_size;
+ u64 blocks = CIFS_INO_BLOCKS(offset);
+
spin_lock(&inode->i_lock);
+ old_size = i_size_read(inode);
i_size_write(inode, offset);
+
+ /*
+ * Extending EOF does not allocate the intervening range. Only clamp
+ * i_blocks on shrink; allocation growth comes from writes or from the
+ * server-reported AllocationSize.
+ */
+ if (offset < old_size && (u64)inode->i_blocks > blocks)
+ inode->i_blocks = blocks;
spin_unlock(&inode->i_lock);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+ if (offset > old_size)
+ pagecache_isize_extended(inode, old_size, offset);
truncate_pagecache(inode, offset);
netfs_wait_for_outstanding_io(inode);
}
+void cifs_resize_file_locked(struct inode *inode, loff_t offset)
+{
+ struct fscache_cookie *cookie = cifs_inode_cookie(inode);
+
+ lockdep_assert_held_write(&inode->i_rwsem);
+
+ netfs_resize_file(netfs_inode(inode), offset, true);
+ cifs_setsize(inode, offset);
+
+ if (!cookie)
+ return;
+
+ fscache_use_cookie(cookie, true);
+ fscache_resize_cookie(cookie, offset);
+ cifs_fscache_unuse_inode_cookie(inode, true);
+}
+
int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
const char *full_path, struct cifsFileInfo *open_file,
loff_t size)
@@ -3050,7 +3123,7 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
size, false);
cifs_dbg(FYI, "%s: set_file_size: rc = %d\n", __func__, rc);
} else {
- open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY);
+ open_file = find_writable_file(cifsInode, FIND_FSUID_ONLY);
if (open_file) {
tcon = tlink_tcon(open_file->tlink);
server = tcon->ses->server;
@@ -3059,6 +3132,7 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
size, false);
cifs_dbg(FYI, "%s: set_file_size: rc = %d\n", __func__, rc);
cifsFileInfo_put(open_file);
+ tcon = NULL;
}
}
@@ -3084,18 +3158,8 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
cifs_put_tlink(tlink);
set_size_out:
- if (rc == 0) {
- netfs_resize_file(&cifsInode->netfs, size, true);
- cifs_setsize(inode, size);
- /*
- * i_blocks is not related to (i_size / i_blksize), but instead
- * 512 byte (2**9) size is required for calculating num blocks.
- * Until we can query the server for actual allocation size,
- * this is best estimate we have for blocks allocated for a file
- * Number of blocks must be rounded up so size 1 is not 0 blocks
- */
- inode->i_blocks = (512 - 1 + size) >> 9;
- }
+ if (rc == 0)
+ cifs_resize_file_locked(inode, size);
return rc;
}
@@ -3158,6 +3222,17 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
rc = 0;
if (attrs->ia_valid & ATTR_SIZE) {
+ if (attrs->ia_size != i_size_read(inode)) {
+ /* Stamp before RPC. On failure the stamp remains: restoring a
+ * stale snapshot could silently erase a concurrent
+ * _cifsFileInfo_put() close stamp. readdir is suppressed
+ * until the stamp expires; stat() bypasses this via the
+ * from_readdir=false path in is_size_safe_to_change() and
+ * always returns an authoritative QUERY_INFO result.
+ * Pairs with smp_load_acquire() in is_size_safe_to_change().
+ */
+ smp_store_release(&cifsInode->time_last_write, jiffies);
+ }
rc = cifs_file_set_size(xid, direntry, full_path,
open_file, attrs->ia_size);
if (rc != 0)
@@ -3170,9 +3245,13 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
attrs->ia_valid &= ~(ATTR_CTIME | ATTR_MTIME);
}
- /* skip mode change if it's just for clearing setuid/setgid */
- if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
- attrs->ia_valid &= ~ATTR_MODE;
+ /*
+ * This function is only called when Unix extensions are in effect,
+ * so the mode is always sent to and stored on the server. Do not
+ * skip the mode change when clearing setuid/setgid bits: dropping
+ * ATTR_MODE here would leave those bits set on the server after a
+ * write, which is a security issue.
+ */
args = kmalloc_obj(*args);
if (args == NULL) {
@@ -3219,7 +3298,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
open_file->fid.netfid,
open_file->pid);
} else {
- open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY);
+ open_file = find_writable_file(cifsInode, FIND_FSUID_ONLY);
if (open_file) {
pTcon = tlink_tcon(open_file->tlink);
rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args,
@@ -3245,13 +3324,6 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
if (rc)
goto out;
- if ((attrs->ia_valid & ATTR_SIZE) &&
- attrs->ia_size != i_size_read(inode)) {
- truncate_setsize(inode, attrs->ia_size);
- netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
- fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
- }
-
setattr_copy(&nop_mnt_idmap, inode, attrs);
mark_inode_dirty(inode);
@@ -3336,6 +3408,17 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
}
if (attrs->ia_valid & ATTR_SIZE) {
+ if (attrs->ia_size != i_size_read(inode)) {
+ /* Stamp before RPC. On failure the stamp remains: restoring a
+ * stale snapshot could silently erase a concurrent
+ * _cifsFileInfo_put() close stamp. readdir is suppressed
+ * until the stamp expires; stat() bypasses this via the
+ * from_readdir=false path in is_size_safe_to_change() and
+ * always returns an authoritative QUERY_INFO result.
+ * Pairs with smp_load_acquire() in is_size_safe_to_change().
+ */
+ smp_store_release(&cifsInode->time_last_write, jiffies);
+ }
rc = cifs_file_set_size(xid, direntry, full_path,
cfile, attrs->ia_size);
if (rc != 0)
@@ -3354,7 +3437,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
if (attrs->ia_valid & ATTR_GID)
gid = attrs->ia_gid;
- if (sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) {
+ if ((sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) ||
+ cifs_sb_master_tcon(cifs_sb)->posix_extensions) {
if (uid_valid(uid) || gid_valid(gid)) {
mode = NO_CHANGE_64;
rc = id_mode_to_cifs_acl(inode, full_path, &mode,
@@ -3369,8 +3453,23 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
attrs->ia_valid &= ~(ATTR_UID | ATTR_GID);
}
- /* skip mode change if it's just for clearing setuid/setgid */
- if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
+ /*
+ * Skip the mode change if it is only being done to clear the
+ * setuid/setgid bits *and* the mode is emulated via the DOS
+ * read-only attribute (the default, non-ACL case), which cannot
+ * represent the setuid/setgid bits anyway.
+ *
+ * When the mode is instead stored on the server - i.e. with the
+ * cifsacl or modefromsid mount options (via an ACL) or with the
+ * SMB3.1.1 POSIX extensions - the cleared mode must be pushed to
+ * the server. Dropping ATTR_MODE here would leave the setuid/
+ * setgid bit set on the server after a write, which is a security
+ * issue (the bits are not stripped as they are on local
+ * filesystems).
+ */
+ if ((attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) &&
+ !((sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) ||
+ cifs_sb_master_tcon(cifs_sb)->posix_extensions))
attrs->ia_valid &= ~ATTR_MODE;
if (attrs->ia_valid & ATTR_MODE) {
@@ -3445,13 +3544,6 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
if (rc)
goto cifs_setattr_exit;
- if ((attrs->ia_valid & ATTR_SIZE) &&
- attrs->ia_size != i_size_read(inode)) {
- truncate_setsize(inode, attrs->ia_size);
- netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
- fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
- }
-
setattr_copy(&nop_mnt_idmap, inode, attrs);
mark_inode_dirty(inode);
diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c
index 9afab3237e54..2f152d76be7d 100644
--- a/fs/smb/client/ioctl.c
+++ b/fs/smb/client/ioctl.c
@@ -67,6 +67,115 @@ static long cifs_ioctl_query_info(unsigned int xid, struct file *filep,
return rc;
}
+static int cifs_set_compression_by_path(unsigned int xid, struct file *filep,
+ struct cifs_tcon *tcon,
+ __u16 compression_state)
+{
+ struct inode *inode = file_inode(filep);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct cifs_open_parms oparms;
+ struct cifs_open_info_data data = {};
+ struct cifsFileInfo *tmp_cfile = NULL;
+ struct cifs_fid fid = {};
+ const char *full_path;
+ __u32 oplock = 0;
+ u64 uniqueid;
+ void *page;
+ int rc;
+
+ if (!server->ops->open || !server->ops->close ||
+ !server->ops->query_file_info)
+ return -EOPNOTSUPP;
+
+ if (!(cifs_sb_flags(cifs_sb) & CIFS_MOUNT_SERVER_INUM) ||
+ cifs_sb->mnt_cifs_serverino_autodisabled)
+ return -EOPNOTSUPP;
+
+ if (d_unhashed(filep->f_path.dentry))
+ return -ESTALE;
+
+ page = alloc_dentry_path();
+ full_path = build_path_from_dentry(filep->f_path.dentry, page);
+ if (IS_ERR(full_path)) {
+ free_dentry_path(page);
+ return PTR_ERR(full_path);
+ }
+
+ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_DATA |
+ FILE_READ_ATTRIBUTES,
+ FILE_OPEN, 0, ACL_NO_MODE);
+ oparms.fid = &fid;
+
+ rc = server->ops->open(xid, &oparms, &oplock, NULL);
+ if (rc)
+ goto out;
+
+ tmp_cfile = kzalloc_obj(*tmp_cfile);
+ if (!tmp_cfile) {
+ rc = -ENOMEM;
+ goto close;
+ }
+
+ tmp_cfile->fid = fid;
+ rc = server->ops->query_file_info(xid, tcon, tmp_cfile, &data);
+ if (rc)
+ goto close;
+
+ uniqueid = le64_to_cpu(data.fi.IndexNumber);
+ if (uniqueid != CIFS_I(inode)->uniqueid) {
+ rc = -ESTALE;
+ goto close;
+ }
+
+ rc = server->ops->set_compression(xid, tcon, tmp_cfile,
+ compression_state);
+
+close:
+ server->ops->close(xid, tcon, &fid);
+ kfree(tmp_cfile);
+ cifs_free_open_info(&data);
+out:
+ free_dentry_path(page);
+ return rc;
+}
+
+static int cifs_ioctl_set_compression(unsigned int xid, struct file *filep,
+ struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile,
+ __u16 compression_state)
+{
+ struct cifsFileInfo *wfile;
+ struct cifs_tcon *wtcon;
+ struct inode *inode = file_inode(filep);
+ int rc;
+
+ if (!tcon->ses->server->ops->set_compression)
+ return -EOPNOTSUPP;
+
+ if (cfile && (cfile->fid.access & FILE_WRITE_DATA)) {
+ rc = tcon->ses->server->ops->set_compression(xid, tcon, cfile,
+ compression_state);
+ if (rc != -EACCES)
+ return rc;
+ }
+
+ rc = cifs_get_writable_file(CIFS_I(inode), FIND_FSUID_ONLY, &wfile);
+ if (!rc) {
+ wtcon = tlink_tcon(wfile->tlink);
+ rc = wtcon->ses->server->ops->set_compression(xid, wtcon, wfile,
+ compression_state);
+ cifsFileInfo_put(wfile);
+ if (rc != -EACCES)
+ return rc;
+ } else if (rc != -EBADF) {
+ return rc;
+ }
+
+ return cifs_set_compression_by_path(xid, filep, tcon,
+ compression_state);
+}
+
static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
unsigned long srcfd)
{
@@ -296,7 +405,7 @@ search_end:
break;
case SMB2_ENCRYPTION_AES256_CCM:
case SMB2_ENCRYPTION_AES256_GCM:
- out.session_key_length = CIFS_SESS_KEY_SIZE;
+ out.session_key_length = ses->auth_key.len;
out.server_in_key_length = out.server_out_key_length = SMB3_GCM256_CRYPTKEY_SIZE;
break;
default:
@@ -356,6 +465,8 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
struct tcon_link *tlink;
struct cifs_sb_info *cifs_sb;
__u64 ExtAttrBits = 0;
+ bool enable_compression;
+ __u16 compression_state;
#ifdef CONFIG_CIFS_POSIX
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
__u64 caps;
@@ -392,13 +503,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
}
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
#endif /* CONFIG_CIFS_POSIX */
- rc = 0;
- if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) {
- /* add in the compressed bit */
- ExtAttrBits = FS_COMPR_FL;
- rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,
- (int __user *)arg);
- }
+ if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED)
+ ExtAttrBits |= FS_COMPR_FL;
+
+ rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,
+ (int __user *)arg);
break;
case FS_IOC_SETFLAGS:
if (pSMBFile == NULL)
@@ -421,16 +530,31 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
* break;
*/
- /* Currently only flag we can set is compressed flag */
- if ((ExtAttrBits & FS_COMPR_FL) == 0)
+ /* Currently only flag we can set or clear is compressed. */
+ if (ExtAttrBits & ~FS_COMPR_FL) {
+ rc = -EOPNOTSUPP;
break;
+ }
- /* Try to set compress flag */
- if (tcon->ses->server->ops->set_compression) {
- rc = tcon->ses->server->ops->set_compression(
- xid, tcon, pSMBFile);
- cifs_dbg(FYI, "set compress flag rc %d\n", rc);
+ enable_compression = ExtAttrBits & FS_COMPR_FL;
+ compression_state = enable_compression ?
+ COMPRESSION_FORMAT_DEFAULT :
+ COMPRESSION_FORMAT_NONE;
+
+ rc = cifs_ioctl_set_compression(xid, filep, tcon,
+ pSMBFile,
+ compression_state);
+ if (rc == 0) {
+ spin_lock(&inode->i_lock);
+ if (enable_compression)
+ CIFS_I(inode)->cifsAttrs |=
+ FILE_ATTRIBUTE_COMPRESSED;
+ else
+ CIFS_I(inode)->cifsAttrs &=
+ ~FILE_ATTRIBUTE_COMPRESSED;
+ spin_unlock(&inode->i_lock);
}
+ cifs_dbg(FYI, "set compress flag rc %d\n", rc);
break;
case CIFS_IOC_COPYCHUNK_FILE:
rc = cifs_ioctl_copychunk(xid, filep, arg);
diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c
index 434e8fe74080..8d5d6aca742a 100644
--- a/fs/smb/client/link.c
+++ b/fs/smb/client/link.c
@@ -234,7 +234,7 @@ cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_open_parms oparms;
struct cifs_io_parms io_parms = {0};
int buf_type = CIFS_NO_BUFFER;
- struct cifs_open_info_data query_data;
+ struct cifs_open_info_data query_data = {};
oparms = (struct cifs_open_parms) {
.tcon = tcon,
@@ -320,7 +320,7 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
int buf_type = CIFS_NO_BUFFER;
__le16 *utf16_path;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
- struct smb2_file_all_info *pfile_info = NULL;
+ struct cifs_open_info_data data = {};
oparms = (struct cifs_open_parms) {
.tcon = tcon,
@@ -336,20 +336,12 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
if (utf16_path == NULL)
return -ENOMEM;
- pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
- GFP_KERNEL);
-
- if (pfile_info == NULL) {
- kfree(utf16_path);
- return -ENOMEM;
- }
-
- rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL,
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, &data, NULL,
NULL, NULL);
if (rc)
goto qmf_out_open_fail;
- if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
+ if (data.fi.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
/* it's not a symlink */
rc = -ENOENT; /* Is there a better rc to return? */
goto qmf_out;
@@ -367,7 +359,6 @@ qmf_out:
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
qmf_out_open_fail:
kfree(utf16_path);
- kfree(pfile_info);
return rc;
}
@@ -503,6 +494,7 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
if (d_really_is_positive(old_file)) {
cifsInode = CIFS_I(d_inode(old_file));
if (rc == 0) {
+ clear_bit(CIFS_INO_TMPFILE, &cifsInode->flags);
spin_lock(&d_inode(old_file)->i_lock);
inc_nlink(d_inode(old_file));
spin_unlock(&d_inode(old_file)->i_lock);
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index bc24c92b8b95..05168284f205 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -28,6 +28,11 @@
#include "fs_context.h"
#include "cached_dir.h"
+struct tcon_list {
+ struct list_head entry;
+ struct cifs_tcon *tcon;
+};
+
/* The xid serves as a useful identifier for each incoming vfs request,
in a similar way to the mid which is useful to track each sent smb,
and CurrentXid can also provide a running counter (although it
@@ -273,7 +278,7 @@ dump_smb(void *buf, int smb_buf_length)
}
void
-cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
+cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb, const char *reason, int rc)
{
unsigned int sbflags = cifs_sb_flags(cifs_sb);
@@ -285,6 +290,10 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
atomic_andnot(CIFS_MOUNT_SERVER_INUM, &cifs_sb->mnt_cifs_flags);
cifs_sb->mnt_cifs_serverino_autodisabled = true;
+ if (rc)
+ cifs_dbg(VFS, "%s: %d\n", reason, rc);
+ else
+ cifs_dbg(VFS, "%s\n", reason);
cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s\n",
tcon ? tcon->tree_name : "new server");
cifs_dbg(VFS, "The server doesn't seem to support them properly or the files might be on different servers (DFS)\n");
@@ -369,10 +378,11 @@ void cifs_queue_oplock_break(struct cifsFileInfo *cfile)
* open_file_lock to enforce the validity of it for the oplock
* break handler. The matching put is done at the end of the
* handler.
+ *
+ * Only take a reference if the work is actually queued.
*/
- cifsFileInfo_get(cfile);
-
- queue_work(cifsoplockd_wq, &cfile->oplock_break);
+ if (queue_work(cifsoplockd_wq, &cfile->oplock_break))
+ cifsFileInfo_get(cfile);
}
void cifs_done_oplock_break(struct cifsInodeInfo *cinode)
@@ -488,7 +498,7 @@ cifs_del_deferred_close(struct cifsFileInfo *cfile)
void
cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
{
- struct cifsFileInfo *cfile = NULL;
+ struct cifsFileInfo *cfile = NULL, *failed_cfile = NULL;
struct file_list *tmp_list, *tmp_next_list;
LIST_HEAD(file_head);
@@ -505,8 +515,10 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
tmp_list = kmalloc_obj(struct file_list,
GFP_ATOMIC);
- if (tmp_list == NULL)
+ if (tmp_list == NULL) {
+ failed_cfile = cfile;
break;
+ }
tmp_list->cfile = cfile;
list_add_tail(&tmp_list->list, &file_head);
}
@@ -514,6 +526,9 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
}
spin_unlock(&cifs_inode->open_file_lock);
+ if (failed_cfile)
+ _cifsFileInfo_put(failed_cfile, false, false);
+
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
_cifsFileInfo_put(tmp_list->cfile, false, false);
list_del(&tmp_list->list);
@@ -524,7 +539,7 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
void
cifs_close_all_deferred_files(struct cifs_tcon *tcon)
{
- struct cifsFileInfo *cfile;
+ struct cifsFileInfo *cfile, *failed_cfile = NULL;
struct file_list *tmp_list, *tmp_next_list;
LIST_HEAD(file_head);
@@ -538,8 +553,10 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
tmp_list = kmalloc_obj(struct file_list,
GFP_ATOMIC);
- if (tmp_list == NULL)
+ if (tmp_list == NULL) {
+ failed_cfile = cfile;
break;
+ }
tmp_list->cfile = cfile;
list_add_tail(&tmp_list->list, &file_head);
}
@@ -547,6 +564,9 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
}
spin_unlock(&tcon->open_file_lock);
+ if (failed_cfile)
+ _cifsFileInfo_put(failed_cfile, true, false);
+
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
_cifsFileInfo_put(tmp_list->cfile, true, false);
list_del(&tmp_list->list);
@@ -554,11 +574,48 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
}
}
+void cifs_close_all_deferred_files_sb(struct cifs_sb_info *cifs_sb)
+{
+ struct rb_root *root = &cifs_sb->tlink_tree;
+ struct rb_node *node;
+ struct cifs_tcon *tcon;
+ struct tcon_link *tlink;
+ struct tcon_list *tmp_list, *q;
+ LIST_HEAD(tcon_head);
+
+ spin_lock(&cifs_sb->tlink_tree_lock);
+ for (node = rb_first(root); node; node = rb_next(node)) {
+ tlink = rb_entry(node, struct tcon_link, tl_rbnode);
+ tcon = tlink_tcon(tlink);
+ if (IS_ERR(tcon))
+ continue;
+ tmp_list = kmalloc_obj(struct tcon_list, GFP_ATOMIC);
+ if (tmp_list == NULL)
+ break;
+ tmp_list->tcon = tcon;
+ /* Take a reference on tcon to prevent it from being freed */
+ spin_lock(&tcon->tc_lock);
+ ++tcon->tc_count;
+ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+ netfs_trace_tcon_ref_get_close_defer_files);
+ spin_unlock(&tcon->tc_lock);
+ list_add_tail(&tmp_list->entry, &tcon_head);
+ }
+ spin_unlock(&cifs_sb->tlink_tree_lock);
+
+ list_for_each_entry_safe(tmp_list, q, &tcon_head, entry) {
+ cifs_close_all_deferred_files(tmp_list->tcon);
+ list_del(&tmp_list->entry);
+ cifs_put_tcon(tmp_list->tcon, netfs_trace_tcon_ref_put_close_defer_files);
+ kfree(tmp_list);
+ }
+}
+
void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
struct dentry *dentry)
{
struct file_list *tmp_list, *tmp_next_list;
- struct cifsFileInfo *cfile;
+ struct cifsFileInfo *cfile, *failed_cfile = NULL;
LIST_HEAD(file_head);
spin_lock(&tcon->open_file_lock);
@@ -571,14 +628,19 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
tmp_list = kmalloc_obj(struct file_list, GFP_ATOMIC);
- if (tmp_list == NULL)
+ if (tmp_list == NULL) {
+ failed_cfile = cfile;
break;
+ }
tmp_list->cfile = cfile;
list_add_tail(&tmp_list->list, &file_head);
}
}
spin_unlock(&tcon->open_file_lock);
+ if (failed_cfile)
+ _cifsFileInfo_put(failed_cfile, true, false);
+
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
_cifsFileInfo_put(tmp_list->cfile, true, false);
list_del(&tmp_list->list);
@@ -636,6 +698,8 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
int i, rc = 0;
char *data_end;
struct dfs_referral_level_3 *ref;
+ unsigned int path_consumed;
+ size_t search_name_len;
if (rsp_size < sizeof(*rsp)) {
cifs_dbg(VFS | ONCE,
@@ -682,6 +746,7 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
rc = -ENOMEM;
goto parse_DFS_referrals_exit;
}
+ search_name_len = strlen(searchName);
/* collect necessary data from referrals */
for (i = 0; i < *num_of_nodes; i++) {
@@ -690,26 +755,47 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
struct dfs_info3_param *node = (*target_nodes)+i;
node->flags = le32_to_cpu(rsp->DFSFlags);
+ path_consumed = le16_to_cpu(rsp->PathConsumed);
if (is_unicode) {
- __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
- GFP_KERNEL);
- if (tmp == NULL) {
+ size_t search_name_utf16_len = search_name_len * 2 + 2;
+ __le16 *tmp;
+
+ if (path_consumed > search_name_utf16_len) {
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
+
+ tmp = kmalloc(search_name_utf16_len, GFP_KERNEL);
+ if (!tmp) {
rc = -ENOMEM;
goto parse_DFS_referrals_exit;
}
- cifsConvertToUTF16((__le16 *) tmp, searchName,
+ cifsConvertToUTF16((__le16 *)tmp, searchName,
PATH_MAX, nls_codepage, remap);
- node->path_consumed = cifs_utf16_bytes(tmp,
- le16_to_cpu(rsp->PathConsumed),
- nls_codepage);
+ node->path_consumed = cifs_utf16_bytes(tmp, path_consumed,
+ nls_codepage);
kfree(tmp);
- } else
- node->path_consumed = le16_to_cpu(rsp->PathConsumed);
+ } else {
+ if (path_consumed > search_name_len) {
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
+
+ node->path_consumed = path_consumed;
+ }
node->server_type = le16_to_cpu(ref->ServerType);
node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
/* copy DfsPath */
+ if (le16_to_cpu(ref->DfsPathOffset) < sizeof(*ref) ||
+ le16_to_cpu(ref->DfsPathOffset) > data_end - (char *)ref) {
+ cifs_dbg(VFS, "%s: DfsPathOffset %u out of range [%zu, %td]\n",
+ __func__, le16_to_cpu(ref->DfsPathOffset),
+ sizeof(*ref), data_end - (char *)ref);
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
max_len = data_end - temp;
node->path_name = cifs_strndup_from_utf16(temp, max_len,
@@ -720,6 +806,14 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
}
/* copy link target UNC */
+ if (le16_to_cpu(ref->NetworkAddressOffset) < sizeof(*ref) ||
+ le16_to_cpu(ref->NetworkAddressOffset) > data_end - (char *)ref) {
+ cifs_dbg(VFS, "%s: NetworkAddressOffset %u out of range [%zu, %td]\n",
+ __func__, le16_to_cpu(ref->NetworkAddressOffset),
+ sizeof(*ref), data_end - (char *)ref);
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
max_len = data_end - temp;
node->node_name = cifs_strndup_from_utf16(temp, max_len,
@@ -743,63 +837,6 @@ parse_DFS_referrals_exit:
return rc;
}
-/**
- * cifs_alloc_hash - allocate hash and hash context together
- * @name: The name of the crypto hash algo
- * @sdesc: SHASH descriptor where to put the pointer to the hash TFM
- *
- * The caller has to make sure @sdesc is initialized to either NULL or
- * a valid context. It can be freed via cifs_free_hash().
- */
-int
-cifs_alloc_hash(const char *name, struct shash_desc **sdesc)
-{
- int rc = 0;
- struct crypto_shash *alg = NULL;
-
- if (*sdesc)
- return 0;
-
- alg = crypto_alloc_shash(name, 0, 0);
- if (IS_ERR(alg)) {
- cifs_dbg(VFS, "Could not allocate shash TFM '%s'\n", name);
- rc = PTR_ERR(alg);
- *sdesc = NULL;
- return rc;
- }
-
- *sdesc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(alg), GFP_KERNEL);
- if (*sdesc == NULL) {
- cifs_dbg(VFS, "no memory left to allocate shash TFM '%s'\n", name);
- crypto_free_shash(alg);
- return -ENOMEM;
- }
-
- (*sdesc)->tfm = alg;
- return 0;
-}
-
-/**
- * cifs_free_hash - free hash and hash context together
- * @sdesc: Where to find the pointer to the hash TFM
- *
- * Freeing a NULL descriptor is safe.
- */
-void
-cifs_free_hash(struct shash_desc **sdesc)
-{
- if (unlikely(!sdesc) || !*sdesc)
- return;
-
- if ((*sdesc)->tfm) {
- crypto_free_shash((*sdesc)->tfm);
- (*sdesc)->tfm = NULL;
- }
-
- kfree_sensitive(*sdesc);
- *sdesc = NULL;
-}
-
void extract_unc_hostname(const char *unc, const char **h, size_t *len)
{
const char *end;
@@ -863,8 +900,14 @@ static void tcon_super_cb(struct super_block *sb, void *arg)
t1->ses->dfs_root_ses == t2->ses->dfs_root_ses) &&
t1->ses->server == t2->ses->server &&
t2->origin_fullpath &&
- dfs_src_pathname_equal(t2->origin_fullpath, t1->origin_fullpath))
+ dfs_src_pathname_equal(t2->origin_fullpath, t1->origin_fullpath)) {
+ /*
+ * Take the active reference while iterate_supers_type() still
+ * holds s_umount shared.
+ */
+ cifs_sb_active(sb);
sd->sb = sb;
+ }
spin_unlock(&t2->tc_lock);
}
@@ -881,15 +924,8 @@ static struct super_block *__cifs_get_super(void (*f)(struct super_block *, void
for (; *fs_type; fs_type++) {
iterate_supers_type(*fs_type, f, &sd);
- if (sd.sb) {
- /*
- * Grab an active reference in order to prevent automounts (DFS links)
- * of expiring and then freeing up our cifs superblock pointer while
- * we're doing failover.
- */
- cifs_sb_active(sd.sb);
+ if (sd.sb)
return sd.sb;
- }
}
pr_warn_once("%s: could not find dfs superblock\n", __func__);
return ERR_PTR(-EINVAL);
diff --git a/fs/smb/client/namespace.c b/fs/smb/client/namespace.c
index 52a520349cb7..52a51b032fae 100644
--- a/fs/smb/client/namespace.c
+++ b/fs/smb/client/namespace.c
@@ -294,4 +294,5 @@ struct vfsmount *cifs_d_automount(struct path *path)
}
const struct inode_operations cifs_namespace_inode_operations = {
+ .fileattr_get = cifs_fileattr_get,
};
diff --git a/fs/smb/client/netlink.c b/fs/smb/client/netlink.c
index 147d9409252c..0dd10913c37a 100644
--- a/fs/smb/client/netlink.c
+++ b/fs/smb/client/netlink.c
@@ -33,13 +33,17 @@ static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
static const struct genl_ops cifs_genl_ops[] = {
{
.cmd = CIFS_GENL_CMD_SWN_NOTIFY,
+ .flags = GENL_ADMIN_PERM,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = cifs_swn_notify,
},
};
static const struct genl_multicast_group cifs_genl_mcgrps[] = {
- [CIFS_GENL_MCGRP_SWN] = { .name = CIFS_GENL_MCGRP_SWN_NAME },
+ [CIFS_GENL_MCGRP_SWN] = {
+ .name = CIFS_GENL_MCGRP_SWN_NAME,
+ .flags = GENL_MCAST_CAP_NET_ADMIN,
+ },
};
struct genl_family cifs_genl_family = {
diff --git a/fs/smb/client/nterr.c b/fs/smb/client/nterr.c
deleted file mode 100644
index 279566cac435..000000000000
--- a/fs/smb/client/nterr.c
+++ /dev/null
@@ -1,704 +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 <linux/types.h>
-#include <linux/fs.h>
-#include "nterr.h"
-
-const struct nt_err_code_struct nt_errs[] = {
- {"NT_STATUS_OK", NT_STATUS_OK},
- {"NT_STATUS_PENDING", NT_STATUS_PENDING},
- {"NT_STATUS_NOTIFY_ENUM_DIR", NT_STATUS_NOTIFY_ENUM_DIR},
- {"NT_STATUS_MEDIA_CHANGED", NT_STATUS_MEDIA_CHANGED},
- {"NT_STATUS_END_OF_MEDIA", NT_STATUS_END_OF_MEDIA},
- {"NT_STATUS_MEDIA_CHECK", NT_STATUS_MEDIA_CHECK},
- {"NT_STATUS_NO_DATA_DETECTED", NT_STATUS_NO_DATA_DETECTED},
- {"NT_STATUS_STOPPED_ON_SYMLINK", NT_STATUS_STOPPED_ON_SYMLINK},
- {"NT_STATUS_DEVICE_REQUIRES_CLEANING", NT_STATUS_DEVICE_REQUIRES_CLEANING},
- {"NT_STATUS_DEVICE_DOOR_OPEN", NT_STATUS_DEVICE_DOOR_OPEN},
- {"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_VARIABLE_NOT_FOUND", NT_STATUS_VARIABLE_NOT_FOUND},
- {"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_BIOS_FAILED_TO_CONNECT_INTERRUPT",
- NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT},
- {"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_INVALID_LOCK_RANGE", NT_STATUS_INVALID_LOCK_RANGE},
- {"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_VOLUME_DISMOUNTED", NT_STATUS_VOLUME_DISMOUNTED},
- {"NT_STATUS_NOT_A_REPARSE_POINT", NT_STATUS_NOT_A_REPARSE_POINT},
- {"NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT",
- NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT},
- {"NT_STATUS_ENCRYPTION_FAILED", NT_STATUS_ENCRYPTION_FAILED},
- {"NT_STATUS_DECRYPTION_FAILED", NT_STATUS_DECRYPTION_FAILED},
- {"NT_STATUS_RANGE_NOT_FOUND", NT_STATUS_RANGE_NOT_FOUND},
- {"NT_STATUS_NO_RECOVERY_POLICY", NT_STATUS_NO_RECOVERY_POLICY},
- {"NT_STATUS_NO_EFS", NT_STATUS_NO_EFS},
- {"NT_STATUS_WRONG_EFS", NT_STATUS_WRONG_EFS},
- {"NT_STATUS_NO_USER_KEYS", NT_STATUS_NO_USER_KEYS},
- {"NT_STATUS_VOLUME_NOT_UPGRADED", NT_STATUS_VOLUME_NOT_UPGRADED},
- {"NT_STATUS_NETWORK_SESSION_EXPIRED", NT_STATUS_NETWORK_SESSION_EXPIRED},
- {"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES},
- {"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES},
- {"NT_STATUS_SOME_NOT_MAPPED", NT_STATUS_SOME_NOT_MAPPED},
- {"NT_STATUS_NO_SUCH_JOB", NT_STATUS_NO_SUCH_JOB},
- {"NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP",
- NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP},
- {"NT_STATUS_OS2_INVALID_LEVEL", NT_STATUS_OS2_INVALID_LEVEL},
- {NULL, 0}
-};
diff --git a/fs/smb/client/nterr.h b/fs/smb/client/nterr.h
index 9a4a450c6571..a542029c6d34 100644
--- a/fs/smb/client/nterr.h
+++ b/fs/smb/client/nterr.h
@@ -15,13 +15,14 @@
#ifndef _NTERR_H
#define _NTERR_H
-struct nt_err_code_struct {
- char *nt_errstr;
- __u32 nt_errcode;
+/* NT status -> dos error map */
+struct ntstatus_to_dos_err {
+ __u8 dos_class;
+ __u16 dos_code;
+ __u32 ntstatus;
+ const char *nt_errstr;
};
-extern const struct nt_err_code_struct nt_errs[];
-
/* Win32 Error Codes. */
#define NT_ERROR_INVALID_PARAMETER 0x0057
#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a
@@ -30,540 +31,545 @@ extern const struct nt_err_code_struct nt_errs[];
/*
* NTSTATUS Values extracted using a loop in smbclient then printing a netmon
* sniff to a file.
+ *
+ * The comment at the end of each definition indicates `dos_class`
+ * and `dos_code` fields of the `ntstatus_to_dos_map` array; it is
+ * used to generate the `smb1_mapping_table.c` file.
*/
-#define NT_STATUS_OK 0x0000
-#define NT_STATUS_PENDING 0x0103
-#define NT_STATUS_MORE_ENTRIES 0x0105
-#define NT_STATUS_SOME_NOT_MAPPED 0x0107
-#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c
-#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 0x80000022
-#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d
-#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288
-#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000289
-#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 (0xC0000000 | 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_VARIABLE_NOT_FOUND (0xC0000000 | 0x0100)
-#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_BIOS_FAILED_TO_CONNECT_INTERRUPT (0xC0000000 | 0x016e)
-#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_INVALID_LOCK_RANGE (0xC0000000 | 0x01a1)
-#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_VOLUME_DISMOUNTED (0xC0000000 | 0x026e)
-#define NT_STATUS_NOT_A_REPARSE_POINT (0xC0000000 | 0x0275)
-#define NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT (0xC0000000 | 0x0281)
-#define NT_STATUS_ENCRYPTION_FAILED (0xC0000000 | 0x028a)
-#define NT_STATUS_DECRYPTION_FAILED (0xC0000000 | 0x028b)
-#define NT_STATUS_RANGE_NOT_FOUND (0xC0000000 | 0x028c)
-#define NT_STATUS_NO_RECOVERY_POLICY (0xC0000000 | 0x028d)
-#define NT_STATUS_NO_EFS (0xC0000000 | 0x028e)
-#define NT_STATUS_WRONG_EFS (0xC0000000 | 0x028f)
-#define NT_STATUS_NO_USER_KEYS (0xC0000000 | 0x0290)
-#define NT_STATUS_VOLUME_NOT_UPGRADED (0xC0000000 | 0x029c)
-#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_OS2_INVALID_LEVEL 0x007c0001
+#define NT_STATUS_OK 0x0000 // SUCCESS, 0
+#define NT_STATUS_PENDING 0x0103 // ERRHRD, ERRgeneral
+#define NT_STATUS_MORE_ENTRIES 0x0105 // ERRHRD, ERRgeneral
+#define NT_STATUS_SOME_NOT_MAPPED 0x0107 // ERRHRD, ERRgeneral
+#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c // ERRSRV, ERR_NOTIFY_ENUM_DIR
+#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 // ERRDOS, ERRmoredata
+#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a // ERRHRD, ERRgeneral
+#define NT_STATUS_MEDIA_CHANGED 0x8000001c // ERRHRD, ERRgeneral
+#define NT_STATUS_END_OF_MEDIA 0x8000001e // ERRHRD, ERRgeneral
+#define NT_STATUS_MEDIA_CHECK 0x80000020 // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_DATA_DETECTED 0x80000022 // ERRHRD, ERRgeneral
+#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d // ERRDOS, ERRsymlink
+#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 // ERRHRD, ERRgeneral
+#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000289 // ERRHRD, ERRgeneral
+#define NT_STATUS_UNSUCCESSFUL (0xC0000000 | 0x0001) // ERRDOS, ERRgeneral
+#define NT_STATUS_NOT_IMPLEMENTED (0xC0000000 | 0x0002) // ERRDOS, ERRbadfunc
+#define NT_STATUS_INVALID_INFO_CLASS (0xC0000000 | 0x0003) // ERRDOS, ERRbadpipe
+#define NT_STATUS_INFO_LENGTH_MISMATCH (0xC0000000 | 0x0004) // ERRDOS, 24
+#define NT_STATUS_ACCESS_VIOLATION (0xC0000000 | 0x0005) // ERRHRD, ERRgeneral
+#define NT_STATUS_IN_PAGE_ERROR (0xC0000000 | 0x0006) // ERRHRD, ERRgeneral
+#define NT_STATUS_PAGEFILE_QUOTA (0xC0000000 | 0x0007) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_HANDLE (0xC0000000 | 0x0008) // ERRDOS, ERRbadfid
+#define NT_STATUS_BAD_INITIAL_STACK (0xC0000000 | 0x0009) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_INITIAL_PC (0xC0000000 | 0x000a) // ERRDOS, 193
+#define NT_STATUS_INVALID_CID (0xC0000000 | 0x000b) // ERRDOS, 87
+#define NT_STATUS_TIMER_NOT_CANCELED (0xC0000000 | 0x000c) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_PARAMETER (0xC0000000 | 0x000d) // ERRDOS, 87
+#define NT_STATUS_NO_SUCH_DEVICE (0xC0000000 | 0x000e) // ERRDOS, ERRbadfile
+#define NT_STATUS_NO_SUCH_FILE (0xC0000000 | 0x000f) // ERRDOS, ERRbadfile
+#define NT_STATUS_INVALID_DEVICE_REQUEST (0xC0000000 | 0x0010) // ERRDOS, ERRbadfunc
+#define NT_STATUS_END_OF_FILE (0xC0000000 | 0x0011) // ERRDOS, 38
+#define NT_STATUS_WRONG_VOLUME (0xC0000000 | 0x0012) // ERRDOS, 34
+#define NT_STATUS_NO_MEDIA_IN_DEVICE (0xC0000000 | 0x0013) // ERRDOS, 21
+#define NT_STATUS_UNRECOGNIZED_MEDIA (0xC0000000 | 0x0014) // ERRHRD, ERRgeneral
+#define NT_STATUS_NONEXISTENT_SECTOR (0xC0000000 | 0x0015) // ERRDOS, 27
+#define NT_STATUS_MORE_PROCESSING_REQUIRED (0xC0000000 | 0x0016) // ERRDOS, ERRmoredata
+#define NT_STATUS_NO_MEMORY (0xC0000000 | 0x0017) // ERRDOS, ERRnomem
+#define NT_STATUS_CONFLICTING_ADDRESSES (0xC0000000 | 0x0018) // ERRDOS, 487
+#define NT_STATUS_NOT_MAPPED_VIEW (0xC0000000 | 0x0019) // ERRDOS, 487
+#define NT_STATUS_UNABLE_TO_FREE_VM (0xC0000000 | 0x001a) // ERRDOS, 87
+#define NT_STATUS_UNABLE_TO_DELETE_SECTION (0xC0000000 | 0x001b) // ERRDOS, 87
+#define NT_STATUS_INVALID_SYSTEM_SERVICE (0xC0000000 | 0x001c) // ERRDOS, 2142
+#define NT_STATUS_ILLEGAL_INSTRUCTION (0xC0000000 | 0x001d) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_LOCK_SEQUENCE (0xC0000000 | 0x001e) // ERRDOS, ERRnoaccess
+#define NT_STATUS_INVALID_VIEW_SIZE (0xC0000000 | 0x001f) // ERRDOS, ERRnoaccess
+#define NT_STATUS_INVALID_FILE_FOR_SECTION (0xC0000000 | 0x0020) // ERRDOS, 193
+#define NT_STATUS_ALREADY_COMMITTED (0xC0000000 | 0x0021) // ERRDOS, ERRnoaccess
+#define NT_STATUS_ACCESS_DENIED (0xC0000000 | 0x0022) // ERRDOS, ERRnoaccess
+#define NT_STATUS_BUFFER_TOO_SMALL (0xC0000000 | 0x0023) // ERRDOS, 111
+#define NT_STATUS_OBJECT_TYPE_MISMATCH (0xC0000000 | 0x0024) // ERRDOS, ERRbadfid
+#define NT_STATUS_NONCONTINUABLE_EXCEPTION (0xC0000000 | 0x0025) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_DISPOSITION (0xC0000000 | 0x0026) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNWIND (0xC0000000 | 0x0027) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_STACK (0xC0000000 | 0x0028) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_UNWIND_TARGET (0xC0000000 | 0x0029) // ERRHRD, ERRgeneral
+#define NT_STATUS_NOT_LOCKED (0xC0000000 | 0x002a) // ERRDOS, 158
+#define NT_STATUS_PARITY_ERROR (0xC0000000 | 0x002b) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNABLE_TO_DECOMMIT_VM (0xC0000000 | 0x002c) // ERRDOS, 487
+#define NT_STATUS_NOT_COMMITTED (0xC0000000 | 0x002d) // ERRDOS, 487
+#define NT_STATUS_INVALID_PORT_ATTRIBUTES (0xC0000000 | 0x002e) // ERRHRD, ERRgeneral
+#define NT_STATUS_PORT_MESSAGE_TOO_LONG (0xC0000000 | 0x002f) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_PARAMETER_MIX (0xC0000000 | 0x0030) // ERRDOS, 87
+#define NT_STATUS_INVALID_QUOTA_LOWER (0xC0000000 | 0x0031) // ERRHRD, ERRgeneral
+#define NT_STATUS_DISK_CORRUPT_ERROR (0xC0000000 | 0x0032) // ERRHRD, ERRgeneral
+#define NT_STATUS_OBJECT_NAME_INVALID (0xC0000000 | 0x0033) // ERRDOS, ERRbadfile
+#define NT_STATUS_OBJECT_NAME_NOT_FOUND (0xC0000000 | 0x0034) // ERRDOS, ERRbadfile
+#define NT_STATUS_OBJECT_NAME_COLLISION (0xC0000000 | 0x0035) // ERRDOS, ERRalreadyexists
+#define NT_STATUS_HANDLE_NOT_WAITABLE (0xC0000000 | 0x0036) // ERRHRD, ERRgeneral
+#define NT_STATUS_PORT_DISCONNECTED (0xC0000000 | 0x0037) // ERRDOS, ERRbadfid
+#define NT_STATUS_DEVICE_ALREADY_ATTACHED (0xC0000000 | 0x0038) // ERRHRD, ERRgeneral
+#define NT_STATUS_OBJECT_PATH_INVALID (0xC0000000 | 0x0039) // ERRDOS, 161
+#define NT_STATUS_OBJECT_PATH_NOT_FOUND (0xC0000000 | 0x003a) // ERRDOS, ERRbadpath
+#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD (0xC0000000 | 0x003b) // ERRDOS, 161
+#define NT_STATUS_DATA_OVERRUN (0xC0000000 | 0x003c) // ERRHRD, ERRgeneral
+#define NT_STATUS_DATA_LATE_ERROR (0xC0000000 | 0x003d) // ERRHRD, ERRgeneral
+#define NT_STATUS_DATA_ERROR (0xC0000000 | 0x003e) // ERRDOS, 23
+#define NT_STATUS_CRC_ERROR (0xC0000000 | 0x003f) // ERRDOS, 23
+#define NT_STATUS_SECTION_TOO_BIG (0xC0000000 | 0x0040) // ERRDOS, ERRnomem
+#define NT_STATUS_PORT_CONNECTION_REFUSED (0xC0000000 | 0x0041) // ERRDOS, ERRnoaccess
+#define NT_STATUS_INVALID_PORT_HANDLE (0xC0000000 | 0x0042) // ERRDOS, ERRbadfid
+#define NT_STATUS_SHARING_VIOLATION (0xC0000000 | 0x0043) // ERRDOS, ERRbadshare
+#define NT_STATUS_QUOTA_EXCEEDED (0xC0000000 | 0x0044) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_PAGE_PROTECTION (0xC0000000 | 0x0045) // ERRDOS, 87
+#define NT_STATUS_MUTANT_NOT_OWNED (0xC0000000 | 0x0046) // ERRDOS, 288
+#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED (0xC0000000 | 0x0047) // ERRDOS, 298
+#define NT_STATUS_PORT_ALREADY_SET (0xC0000000 | 0x0048) // ERRDOS, 87
+#define NT_STATUS_SECTION_NOT_IMAGE (0xC0000000 | 0x0049) // ERRDOS, 87
+#define NT_STATUS_SUSPEND_COUNT_EXCEEDED (0xC0000000 | 0x004a) // ERRDOS, 156
+#define NT_STATUS_THREAD_IS_TERMINATING (0xC0000000 | 0x004b) // ERRDOS, ERRnoaccess
+#define NT_STATUS_BAD_WORKING_SET_LIMIT (0xC0000000 | 0x004c) // ERRDOS, 87
+#define NT_STATUS_INCOMPATIBLE_FILE_MAP (0xC0000000 | 0x004d) // ERRDOS, 87
+#define NT_STATUS_SECTION_PROTECTION (0xC0000000 | 0x004e) // ERRDOS, 87
+#define NT_STATUS_EAS_NOT_SUPPORTED (0xC0000000 | 0x004f) // ERRDOS, ERReasnotsupported
+#define NT_STATUS_EA_TOO_LARGE (0xC0000000 | 0x0050) // ERRDOS, 255
+#define NT_STATUS_NONEXISTENT_EA_ENTRY (0xC0000000 | 0x0051) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_EAS_ON_FILE (0xC0000000 | 0x0052) // ERRHRD, ERRgeneral
+#define NT_STATUS_EA_CORRUPT_ERROR (0xC0000000 | 0x0053) // ERRHRD, ERRgeneral
+#define NT_STATUS_FILE_LOCK_CONFLICT (0xC0000000 | 0x0054) // ERRDOS, ERRlock
+#define NT_STATUS_LOCK_NOT_GRANTED (0xC0000000 | 0x0055) // ERRDOS, ERRlock
+#define NT_STATUS_DELETE_PENDING (0xC0000000 | 0x0056) // ERRDOS, ERRbadfile
+#define NT_STATUS_CTL_FILE_NOT_SUPPORTED (0xC0000000 | 0x0057) // ERRDOS, ERRunsup
+#define NT_STATUS_UNKNOWN_REVISION (0xC0000000 | 0x0058) // ERRHRD, ERRgeneral
+#define NT_STATUS_REVISION_MISMATCH (0xC0000000 | 0x0059) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_OWNER (0xC0000000 | 0x005a) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_PRIMARY_GROUP (0xC0000000 | 0x005b) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_IMPERSONATION_TOKEN (0xC0000000 | 0x005c) // ERRHRD, ERRgeneral
+#define NT_STATUS_CANT_DISABLE_MANDATORY (0xC0000000 | 0x005d) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_LOGON_SERVERS (0xC0000000 | 0x005e) // ERRDOS, 2215
+#define NT_STATUS_NO_SUCH_LOGON_SESSION (0xC0000000 | 0x005f) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_SUCH_PRIVILEGE (0xC0000000 | 0x0060) // ERRHRD, ERRgeneral
+#define NT_STATUS_PRIVILEGE_NOT_HELD (0xC0000000 | 0x0061) // ERRDOS, ERRnoaccess
+#define NT_STATUS_INVALID_ACCOUNT_NAME (0xC0000000 | 0x0062) // ERRHRD, ERRgeneral
+#define NT_STATUS_USER_EXISTS (0xC0000000 | 0x0063) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_SUCH_USER (0xC0000000 | 0x0064) // ERRDOS, ERRnoaccess
+#define NT_STATUS_GROUP_EXISTS (0xC0000000 | 0x0065) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_SUCH_GROUP (0xC0000000 | 0x0066) // ERRHRD, ERRgeneral
+#define NT_STATUS_MEMBER_IN_GROUP (0xC0000000 | 0x0067) // ERRHRD, ERRgeneral
+#define NT_STATUS_MEMBER_NOT_IN_GROUP (0xC0000000 | 0x0068) // ERRHRD, ERRgeneral
+#define NT_STATUS_LAST_ADMIN (0xC0000000 | 0x0069) // ERRHRD, ERRgeneral
+#define NT_STATUS_WRONG_PASSWORD (0xC0000000 | 0x006a) // ERRSRV, ERRbadpw
+#define NT_STATUS_ILL_FORMED_PASSWORD (0xC0000000 | 0x006b) // ERRHRD, ERRgeneral
+#define NT_STATUS_PASSWORD_RESTRICTION (0xC0000000 | 0x006c) // ERRHRD, ERRgeneral
+#define NT_STATUS_LOGON_FAILURE (0xC0000000 | 0x006d) // ERRDOS, ERRnoaccess
+#define NT_STATUS_ACCOUNT_RESTRICTION (0xC0000000 | 0x006e) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_LOGON_HOURS (0xC0000000 | 0x006f) // ERRSRV, ERRbadLogonTime
+#define NT_STATUS_INVALID_WORKSTATION (0xC0000000 | 0x0070) // ERRSRV, ERRbadclient
+#define NT_STATUS_PASSWORD_EXPIRED (0xC0000000 | 0x0071) // ERRSRV, ERRpasswordExpired
+#define NT_STATUS_ACCOUNT_DISABLED (0xC0000000 | 0x0072) // ERRSRV, ERRaccountexpired
+#define NT_STATUS_NONE_MAPPED (0xC0000000 | 0x0073) // ERRHRD, ERRgeneral
+#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED (0xC0000000 | 0x0074) // ERRHRD, ERRgeneral
+#define NT_STATUS_LUIDS_EXHAUSTED (0xC0000000 | 0x0075) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_SUB_AUTHORITY (0xC0000000 | 0x0076) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_ACL (0xC0000000 | 0x0077) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_SID (0xC0000000 | 0x0078) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_SECURITY_DESCR (0xC0000000 | 0x0079) // ERRHRD, ERRgeneral
+#define NT_STATUS_PROCEDURE_NOT_FOUND (0xC0000000 | 0x007a) // ERRDOS, 127
+#define NT_STATUS_INVALID_IMAGE_FORMAT (0xC0000000 | 0x007b) // ERRDOS, 193
+#define NT_STATUS_NO_TOKEN (0xC0000000 | 0x007c) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_INHERITANCE_ACL (0xC0000000 | 0x007d) // ERRHRD, ERRgeneral
+#define NT_STATUS_RANGE_NOT_LOCKED (0xC0000000 | 0x007e) // ERRDOS, 158
+#define NT_STATUS_DISK_FULL (0xC0000000 | 0x007f) // ERRDOS, 112
+#define NT_STATUS_SERVER_DISABLED (0xC0000000 | 0x0080) // ERRHRD, ERRgeneral
+#define NT_STATUS_SERVER_NOT_DISABLED (0xC0000000 | 0x0081) // ERRHRD, ERRgeneral
+#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED (0xC0000000 | 0x0082) // ERRDOS, 68
+#define NT_STATUS_GUIDS_EXHAUSTED (0xC0000000 | 0x0083) // ERRDOS, 259
+#define NT_STATUS_INVALID_ID_AUTHORITY (0xC0000000 | 0x0084) // ERRHRD, ERRgeneral
+#define NT_STATUS_AGENTS_EXHAUSTED (0xC0000000 | 0x0085) // ERRDOS, 259
+#define NT_STATUS_INVALID_VOLUME_LABEL (0xC0000000 | 0x0086) // ERRDOS, 154
+#define NT_STATUS_SECTION_NOT_EXTENDED (0xC0000000 | 0x0087) // ERRDOS, 14
+#define NT_STATUS_NOT_MAPPED_DATA (0xC0000000 | 0x0088) // ERRDOS, 487
+#define NT_STATUS_RESOURCE_DATA_NOT_FOUND (0xC0000000 | 0x0089) // ERRHRD, ERRgeneral
+#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND (0xC0000000 | 0x008a) // ERRHRD, ERRgeneral
+#define NT_STATUS_RESOURCE_NAME_NOT_FOUND (0xC0000000 | 0x008b) // ERRHRD, ERRgeneral
+#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED (0xC0000000 | 0x008c) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOAT_DENORMAL_OPERAND (0xC0000000 | 0x008d) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO (0xC0000000 | 0x008e) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOAT_INEXACT_RESULT (0xC0000000 | 0x008f) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOAT_INVALID_OPERATION (0xC0000000 | 0x0090) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOAT_OVERFLOW (0xC0000000 | 0x0091) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOAT_STACK_CHECK (0xC0000000 | 0x0092) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOAT_UNDERFLOW (0xC0000000 | 0x0093) // ERRHRD, ERRgeneral
+#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO (0xC0000000 | 0x0094) // ERRHRD, ERRgeneral
+#define NT_STATUS_INTEGER_OVERFLOW (0xC0000000 | 0x0095) // ERRDOS, 534
+#define NT_STATUS_PRIVILEGED_INSTRUCTION (0xC0000000 | 0x0096) // ERRHRD, ERRgeneral
+#define NT_STATUS_TOO_MANY_PAGING_FILES (0xC0000000 | 0x0097) // ERRDOS, ERRnomem
+#define NT_STATUS_FILE_INVALID (0xC0000000 | 0x0098) // ERRHRD, ERRgeneral
+#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED (0xC0000000 | 0x0099) // ERRHRD, ERRgeneral
+#define NT_STATUS_INSUFFICIENT_RESOURCES (0xC0000000 | 0x009a) // ERRDOS, ERRnoresource
+#define NT_STATUS_DFS_EXIT_PATH_FOUND (0xC0000000 | 0x009b) // ERRDOS, ERRbadpath
+#define NT_STATUS_DEVICE_DATA_ERROR (0xC0000000 | 0x009c) // ERRDOS, 23
+#define NT_STATUS_DEVICE_NOT_CONNECTED (0xC0000000 | 0x009d) // ERRHRD, ERRgeneral
+#define NT_STATUS_DEVICE_POWER_FAILURE (0xC0000000 | 0x009e) // ERRDOS, 21
+#define NT_STATUS_FREE_VM_NOT_AT_BASE (0xC0000000 | 0x009f) // ERRDOS, 487
+#define NT_STATUS_MEMORY_NOT_ALLOCATED (0xC0000000 | 0x00a0) // ERRDOS, 487
+#define NT_STATUS_WORKING_SET_QUOTA (0xC0000000 | 0x00a1) // ERRHRD, ERRgeneral
+#define NT_STATUS_MEDIA_WRITE_PROTECTED (0xC0000000 | 0x00a2) // ERRDOS, 19
+#define NT_STATUS_DEVICE_NOT_READY (0xC0000000 | 0x00a3) // ERRDOS, 21
+#define NT_STATUS_INVALID_GROUP_ATTRIBUTES (0xC0000000 | 0x00a4) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_IMPERSONATION_LEVEL (0xC0000000 | 0x00a5) // ERRHRD, ERRgeneral
+#define NT_STATUS_CANT_OPEN_ANONYMOUS (0xC0000000 | 0x00a6) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_VALIDATION_CLASS (0xC0000000 | 0x00a7) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_TOKEN_TYPE (0xC0000000 | 0x00a8) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_MASTER_BOOT_RECORD (0xC0000000 | 0x00a9) // ERRDOS, 87
+#define NT_STATUS_INSTRUCTION_MISALIGNMENT (0xC0000000 | 0x00aa) // ERRHRD, ERRgeneral
+#define NT_STATUS_INSTANCE_NOT_AVAILABLE (0xC0000000 | 0x00ab) // ERRDOS, ERRpipebusy
+#define NT_STATUS_PIPE_NOT_AVAILABLE (0xC0000000 | 0x00ac) // ERRDOS, ERRpipebusy
+#define NT_STATUS_INVALID_PIPE_STATE (0xC0000000 | 0x00ad) // ERRDOS, ERRbadpipe
+#define NT_STATUS_PIPE_BUSY (0xC0000000 | 0x00ae) // ERRDOS, ERRpipebusy
+#define NT_STATUS_ILLEGAL_FUNCTION (0xC0000000 | 0x00af) // ERRDOS, ERRbadfunc
+#define NT_STATUS_PIPE_DISCONNECTED (0xC0000000 | 0x00b0) // ERRDOS, ERRnotconnected
+#define NT_STATUS_PIPE_CLOSING (0xC0000000 | 0x00b1) // ERRDOS, ERRpipeclosing
+#define NT_STATUS_PIPE_CONNECTED (0xC0000000 | 0x00b2) // ERRHRD, ERRgeneral
+#define NT_STATUS_PIPE_LISTENING (0xC0000000 | 0x00b3) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_READ_MODE (0xC0000000 | 0x00b4) // ERRDOS, ERRbadpipe
+#define NT_STATUS_IO_TIMEOUT (0xC0000000 | 0x00b5) // ERRDOS, 121
+#define NT_STATUS_FILE_FORCED_CLOSED (0xC0000000 | 0x00b6) // ERRDOS, 38
+#define NT_STATUS_PROFILING_NOT_STARTED (0xC0000000 | 0x00b7) // ERRHRD, ERRgeneral
+#define NT_STATUS_PROFILING_NOT_STOPPED (0xC0000000 | 0x00b8) // ERRHRD, ERRgeneral
+#define NT_STATUS_COULD_NOT_INTERPRET (0xC0000000 | 0x00b9) // ERRHRD, ERRgeneral
+#define NT_STATUS_FILE_IS_A_DIRECTORY (0xC0000000 | 0x00ba) // ERRDOS, ERRnoaccess
+#define NT_STATUS_NOT_SUPPORTED (0xC0000000 | 0x00bb) // ERRDOS, ERRunsup
+#define NT_STATUS_REMOTE_NOT_LISTENING (0xC0000000 | 0x00bc) // ERRDOS, 51
+#define NT_STATUS_DUPLICATE_NAME (0xC0000000 | 0x00bd) // ERRDOS, 52
+#define NT_STATUS_BAD_NETWORK_PATH (0xC0000000 | 0x00be) // ERRDOS, 53
+#define NT_STATUS_NETWORK_BUSY (0xC0000000 | 0x00bf) // ERRDOS, 54
+#define NT_STATUS_DEVICE_DOES_NOT_EXIST (0xC0000000 | 0x00c0) // ERRDOS, 55
+#define NT_STATUS_TOO_MANY_COMMANDS (0xC0000000 | 0x00c1) // ERRDOS, 56
+#define NT_STATUS_ADAPTER_HARDWARE_ERROR (0xC0000000 | 0x00c2) // ERRDOS, 57
+#define NT_STATUS_INVALID_NETWORK_RESPONSE (0xC0000000 | 0x00c3) // ERRDOS, 58
+#define NT_STATUS_UNEXPECTED_NETWORK_ERROR (0xC0000000 | 0x00c4) // ERRDOS, 59
+#define NT_STATUS_BAD_REMOTE_ADAPTER (0xC0000000 | 0x00c5) // ERRDOS, 60
+#define NT_STATUS_PRINT_QUEUE_FULL (0xC0000000 | 0x00c6) // ERRDOS, 61
+#define NT_STATUS_NO_SPOOL_SPACE (0xC0000000 | 0x00c7) // ERRDOS, 62
+#define NT_STATUS_PRINT_CANCELLED (0xC0000000 | 0x00c8) // ERRDOS, 63
+#define NT_STATUS_NETWORK_NAME_DELETED (0xC0000000 | 0x00c9) // ERRDOS, 64
+#define NT_STATUS_NETWORK_ACCESS_DENIED (0xC0000000 | 0x00ca) // ERRDOS, 65
+#define NT_STATUS_BAD_DEVICE_TYPE (0xC0000000 | 0x00cb) // ERRDOS, 66
+#define NT_STATUS_BAD_NETWORK_NAME (0xC0000000 | 0x00cc) // ERRDOS, ERRnosuchshare
+#define NT_STATUS_TOO_MANY_NAMES (0xC0000000 | 0x00cd) // ERRDOS, 68
+#define NT_STATUS_TOO_MANY_SESSIONS (0xC0000000 | 0x00ce) // ERRDOS, 69
+#define NT_STATUS_SHARING_PAUSED (0xC0000000 | 0x00cf) // ERRDOS, 70
+#define NT_STATUS_REQUEST_NOT_ACCEPTED (0xC0000000 | 0x00d0) // ERRDOS, 71
+#define NT_STATUS_REDIRECTOR_PAUSED (0xC0000000 | 0x00d1) // ERRDOS, 72
+#define NT_STATUS_NET_WRITE_FAULT (0xC0000000 | 0x00d2) // ERRDOS, 88
+#define NT_STATUS_PROFILING_AT_LIMIT (0xC0000000 | 0x00d3) // ERRHRD, ERRgeneral
+#define NT_STATUS_NOT_SAME_DEVICE (0xC0000000 | 0x00d4) // ERRDOS, ERRdiffdevice
+#define NT_STATUS_FILE_RENAMED (0xC0000000 | 0x00d5) // ERRDOS, ERRnoaccess
+#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED (0xC0000000 | 0x00d6) // ERRDOS, 240
+#define NT_STATUS_NO_SECURITY_ON_OBJECT (0xC0000000 | 0x00d7) // ERRHRD, ERRgeneral
+#define NT_STATUS_CANT_WAIT (0xC0000000 | 0x00d8) // ERRHRD, ERRgeneral
+#define NT_STATUS_PIPE_EMPTY (0xC0000000 | 0x00d9) // ERRDOS, ERRpipeclosing
+#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO (0xC0000000 | 0x00da) // ERRHRD, ERRgeneral
+#define NT_STATUS_CANT_TERMINATE_SELF (0xC0000000 | 0x00db) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_SERVER_STATE (0xC0000000 | 0x00dc) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_DOMAIN_STATE (0xC0000000 | 0x00dd) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_DOMAIN_ROLE (0xC0000000 | 0x00de) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_SUCH_DOMAIN (0xC0000000 | 0x00df) // ERRHRD, ERRgeneral
+#define NT_STATUS_DOMAIN_EXISTS (0xC0000000 | 0x00e0) // ERRHRD, ERRgeneral
+#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED (0xC0000000 | 0x00e1) // ERRHRD, ERRgeneral
+#define NT_STATUS_OPLOCK_NOT_GRANTED (0xC0000000 | 0x00e2) // ERRDOS, 300
+#define NT_STATUS_INVALID_OPLOCK_PROTOCOL (0xC0000000 | 0x00e3) // ERRDOS, 301
+#define NT_STATUS_INTERNAL_DB_CORRUPTION (0xC0000000 | 0x00e4) // ERRHRD, ERRgeneral
+#define NT_STATUS_INTERNAL_ERROR (0xC0000000 | 0x00e5) // ERRHRD, ERRgeneral
+#define NT_STATUS_GENERIC_NOT_MAPPED (0xC0000000 | 0x00e6) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_DESCRIPTOR_FORMAT (0xC0000000 | 0x00e7) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_USER_BUFFER (0xC0000000 | 0x00e8) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNEXPECTED_IO_ERROR (0xC0000000 | 0x00e9) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR (0xC0000000 | 0x00ea) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR (0xC0000000 | 0x00eb) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR (0xC0000000 | 0x00ec) // ERRHRD, ERRgeneral
+#define NT_STATUS_NOT_LOGON_PROCESS (0xC0000000 | 0x00ed) // ERRHRD, ERRgeneral
+#define NT_STATUS_LOGON_SESSION_EXISTS (0xC0000000 | 0x00ee) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_PARAMETER_1 (0xC0000000 | 0x00ef) // ERRDOS, 87
+#define NT_STATUS_INVALID_PARAMETER_2 (0xC0000000 | 0x00f0) // ERRDOS, 87
+#define NT_STATUS_INVALID_PARAMETER_3 (0xC0000000 | 0x00f1) // ERRDOS, 87
+#define NT_STATUS_INVALID_PARAMETER_4 (0xC0000000 | 0x00f2) // ERRDOS, 87
+#define NT_STATUS_INVALID_PARAMETER_5 (0xC0000000 | 0x00f3) // ERRDOS, 87
+#define NT_STATUS_INVALID_PARAMETER_6 (0xC0000000 | 0x00f4) // ERRDOS, 87
+#define NT_STATUS_INVALID_PARAMETER_7 (0xC0000000 | 0x00f5) // ERRDOS, 87
+#define NT_STATUS_INVALID_PARAMETER_8 (0xC0000000 | 0x00f6) // ERRDOS, 87
+#define NT_STATUS_INVALID_PARAMETER_9 (0xC0000000 | 0x00f7) // ERRDOS, 87
+#define NT_STATUS_INVALID_PARAMETER_10 (0xC0000000 | 0x00f8) // ERRDOS, 87
+#define NT_STATUS_INVALID_PARAMETER_11 (0xC0000000 | 0x00f9) // ERRDOS, 87
+#define NT_STATUS_INVALID_PARAMETER_12 (0xC0000000 | 0x00fa) // ERRDOS, 87
+#define NT_STATUS_REDIRECTOR_NOT_STARTED (0xC0000000 | 0x00fb) // ERRDOS, ERRbadpath
+#define NT_STATUS_REDIRECTOR_STARTED (0xC0000000 | 0x00fc) // ERRHRD, ERRgeneral
+#define NT_STATUS_STACK_OVERFLOW (0xC0000000 | 0x00fd) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_SUCH_PACKAGE (0xC0000000 | 0x00fe) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_FUNCTION_TABLE (0xC0000000 | 0x00ff) // ERRHRD, ERRgeneral
+#define NT_STATUS_VARIABLE_NOT_FOUND (0xC0000000 | 0x0100) // ERRDOS, 203
+#define NT_STATUS_DIRECTORY_NOT_EMPTY (0xC0000000 | 0x0101) // ERRDOS, 145
+#define NT_STATUS_FILE_CORRUPT_ERROR (0xC0000000 | 0x0102) // ERRHRD, ERRgeneral
+#define NT_STATUS_NOT_A_DIRECTORY (0xC0000000 | 0x0103) // ERRDOS, 267
+#define NT_STATUS_BAD_LOGON_SESSION_STATE (0xC0000000 | 0x0104) // ERRHRD, ERRgeneral
+#define NT_STATUS_LOGON_SESSION_COLLISION (0xC0000000 | 0x0105) // ERRHRD, ERRgeneral
+#define NT_STATUS_NAME_TOO_LONG (0xC0000000 | 0x0106) // ERRDOS, 206
+#define NT_STATUS_FILES_OPEN (0xC0000000 | 0x0107) // ERRDOS, 2401
+#define NT_STATUS_CONNECTION_IN_USE (0xC0000000 | 0x0108) // ERRDOS, 2404
+#define NT_STATUS_MESSAGE_NOT_FOUND (0xC0000000 | 0x0109) // ERRHRD, ERRgeneral
+#define NT_STATUS_PROCESS_IS_TERMINATING (0xC0000000 | 0x010a) // ERRDOS, ERRnoaccess
+#define NT_STATUS_INVALID_LOGON_TYPE (0xC0000000 | 0x010b) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_GUID_TRANSLATION (0xC0000000 | 0x010c) // ERRHRD, ERRgeneral
+#define NT_STATUS_CANNOT_IMPERSONATE (0xC0000000 | 0x010d) // ERRHRD, ERRgeneral
+#define NT_STATUS_IMAGE_ALREADY_LOADED (0xC0000000 | 0x010e) // ERRHRD, ERRgeneral
+#define NT_STATUS_ABIOS_NOT_PRESENT (0xC0000000 | 0x010f) // ERRHRD, ERRgeneral
+#define NT_STATUS_ABIOS_LID_NOT_EXIST (0xC0000000 | 0x0110) // ERRHRD, ERRgeneral
+#define NT_STATUS_ABIOS_LID_ALREADY_OWNED (0xC0000000 | 0x0111) // ERRHRD, ERRgeneral
+#define NT_STATUS_ABIOS_NOT_LID_OWNER (0xC0000000 | 0x0112) // ERRHRD, ERRgeneral
+#define NT_STATUS_ABIOS_INVALID_COMMAND (0xC0000000 | 0x0113) // ERRHRD, ERRgeneral
+#define NT_STATUS_ABIOS_INVALID_LID (0xC0000000 | 0x0114) // ERRHRD, ERRgeneral
+#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE (0xC0000000 | 0x0115) // ERRHRD, ERRgeneral
+#define NT_STATUS_ABIOS_INVALID_SELECTOR (0xC0000000 | 0x0116) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_LDT (0xC0000000 | 0x0117) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_LDT_SIZE (0xC0000000 | 0x0118) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_LDT_OFFSET (0xC0000000 | 0x0119) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_LDT_DESCRIPTOR (0xC0000000 | 0x011a) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_IMAGE_NE_FORMAT (0xC0000000 | 0x011b) // ERRDOS, 193
+#define NT_STATUS_RXACT_INVALID_STATE (0xC0000000 | 0x011c) // ERRHRD, ERRgeneral
+#define NT_STATUS_RXACT_COMMIT_FAILURE (0xC0000000 | 0x011d) // ERRHRD, ERRgeneral
+#define NT_STATUS_MAPPED_FILE_SIZE_ZERO (0xC0000000 | 0x011e) // ERRHRD, ERRgeneral
+#define NT_STATUS_TOO_MANY_OPENED_FILES (0xC0000000 | 0x011f) // ERRDOS, ERRnofids
+#define NT_STATUS_CANCELLED (0xC0000000 | 0x0120) // ERRHRD, ERRgeneral
+#define NT_STATUS_CANNOT_DELETE (0xC0000000 | 0x0121) // ERRDOS, ERRnoaccess
+#define NT_STATUS_INVALID_COMPUTER_NAME (0xC0000000 | 0x0122) // ERRHRD, ERRgeneral
+#define NT_STATUS_FILE_DELETED (0xC0000000 | 0x0123) // ERRDOS, ERRnoaccess
+#define NT_STATUS_SPECIAL_ACCOUNT (0xC0000000 | 0x0124) // ERRHRD, ERRgeneral
+#define NT_STATUS_SPECIAL_GROUP (0xC0000000 | 0x0125) // ERRHRD, ERRgeneral
+#define NT_STATUS_SPECIAL_USER (0xC0000000 | 0x0126) // ERRHRD, ERRgeneral
+#define NT_STATUS_MEMBERS_PRIMARY_GROUP (0xC0000000 | 0x0127) // ERRHRD, ERRgeneral
+#define NT_STATUS_FILE_CLOSED (0xC0000000 | 0x0128) // ERRDOS, ERRbadfid
+#define NT_STATUS_TOO_MANY_THREADS (0xC0000000 | 0x0129) // ERRHRD, ERRgeneral
+#define NT_STATUS_THREAD_NOT_IN_PROCESS (0xC0000000 | 0x012a) // ERRHRD, ERRgeneral
+#define NT_STATUS_TOKEN_ALREADY_IN_USE (0xC0000000 | 0x012b) // ERRHRD, ERRgeneral
+#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED (0xC0000000 | 0x012c) // ERRHRD, ERRgeneral
+#define NT_STATUS_COMMITMENT_LIMIT (0xC0000000 | 0x012d) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_IMAGE_LE_FORMAT (0xC0000000 | 0x012e) // ERRDOS, 193
+#define NT_STATUS_INVALID_IMAGE_NOT_MZ (0xC0000000 | 0x012f) // ERRDOS, 193
+#define NT_STATUS_INVALID_IMAGE_PROTECT (0xC0000000 | 0x0130) // ERRDOS, 193
+#define NT_STATUS_INVALID_IMAGE_WIN_16 (0xC0000000 | 0x0131) // ERRDOS, 193
+#define NT_STATUS_LOGON_SERVER_CONFLICT (0xC0000000 | 0x0132) // ERRHRD, ERRgeneral
+#define NT_STATUS_TIME_DIFFERENCE_AT_DC (0xC0000000 | 0x0133) // ERRHRD, ERRgeneral
+#define NT_STATUS_SYNCHRONIZATION_REQUIRED (0xC0000000 | 0x0134) // ERRHRD, ERRgeneral
+#define NT_STATUS_DLL_NOT_FOUND (0xC0000000 | 0x0135) // ERRDOS, 126
+#define NT_STATUS_OPEN_FAILED (0xC0000000 | 0x0136) // ERRHRD, ERRgeneral
+#define NT_STATUS_IO_PRIVILEGE_FAILED (0xC0000000 | 0x0137) // ERRHRD, ERRgeneral
+#define NT_STATUS_ORDINAL_NOT_FOUND (0xC0000000 | 0x0138) // ERRDOS, 182
+#define NT_STATUS_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0139) // ERRDOS, 127
+#define NT_STATUS_CONTROL_C_EXIT (0xC0000000 | 0x013a) // ERRHRD, ERRgeneral
+#define NT_STATUS_LOCAL_DISCONNECT (0xC0000000 | 0x013b) // ERRDOS, 64
+#define NT_STATUS_REMOTE_DISCONNECT (0xC0000000 | 0x013c) // ERRDOS, 64
+#define NT_STATUS_REMOTE_RESOURCES (0xC0000000 | 0x013d) // ERRDOS, 51
+#define NT_STATUS_LINK_FAILED (0xC0000000 | 0x013e) // ERRDOS, 59
+#define NT_STATUS_LINK_TIMEOUT (0xC0000000 | 0x013f) // ERRDOS, 59
+#define NT_STATUS_INVALID_CONNECTION (0xC0000000 | 0x0140) // ERRDOS, 59
+#define NT_STATUS_INVALID_ADDRESS (0xC0000000 | 0x0141) // ERRDOS, 59
+#define NT_STATUS_DLL_INIT_FAILED (0xC0000000 | 0x0142) // ERRHRD, ERRgeneral
+#define NT_STATUS_MISSING_SYSTEMFILE (0xC0000000 | 0x0143) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNHANDLED_EXCEPTION (0xC0000000 | 0x0144) // ERRHRD, ERRgeneral
+#define NT_STATUS_APP_INIT_FAILURE (0xC0000000 | 0x0145) // ERRHRD, ERRgeneral
+#define NT_STATUS_PAGEFILE_CREATE_FAILED (0xC0000000 | 0x0146) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_PAGEFILE (0xC0000000 | 0x0147) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_LEVEL (0xC0000000 | 0x0148) // ERRDOS, 124
+#define NT_STATUS_WRONG_PASSWORD_CORE (0xC0000000 | 0x0149) // ERRDOS, 86
+#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT (0xC0000000 | 0x014a) // ERRHRD, ERRgeneral
+#define NT_STATUS_PIPE_BROKEN (0xC0000000 | 0x014b) // ERRDOS, 109
+#define NT_STATUS_REGISTRY_CORRUPT (0xC0000000 | 0x014c) // ERRHRD, ERRgeneral
+#define NT_STATUS_REGISTRY_IO_FAILED (0xC0000000 | 0x014d) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_EVENT_PAIR (0xC0000000 | 0x014e) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNRECOGNIZED_VOLUME (0xC0000000 | 0x014f) // ERRHRD, ERRgeneral
+#define NT_STATUS_SERIAL_NO_DEVICE_INITED (0xC0000000 | 0x0150) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_SUCH_ALIAS (0xC0000000 | 0x0151) // ERRHRD, ERRgeneral
+#define NT_STATUS_MEMBER_NOT_IN_ALIAS (0xC0000000 | 0x0152) // ERRHRD, ERRgeneral
+#define NT_STATUS_MEMBER_IN_ALIAS (0xC0000000 | 0x0153) // ERRHRD, ERRgeneral
+#define NT_STATUS_ALIAS_EXISTS (0xC0000000 | 0x0154) // ERRHRD, ERRgeneral
+#define NT_STATUS_LOGON_NOT_GRANTED (0xC0000000 | 0x0155) // ERRHRD, ERRgeneral
+#define NT_STATUS_TOO_MANY_SECRETS (0xC0000000 | 0x0156) // ERRHRD, ERRgeneral
+#define NT_STATUS_SECRET_TOO_LONG (0xC0000000 | 0x0157) // ERRHRD, ERRgeneral
+#define NT_STATUS_INTERNAL_DB_ERROR (0xC0000000 | 0x0158) // ERRHRD, ERRgeneral
+#define NT_STATUS_FULLSCREEN_MODE (0xC0000000 | 0x0159) // ERRHRD, ERRgeneral
+#define NT_STATUS_TOO_MANY_CONTEXT_IDS (0xC0000000 | 0x015a) // ERRHRD, ERRgeneral
+#define NT_STATUS_LOGON_TYPE_NOT_GRANTED (0xC0000000 | 0x015b) // ERRDOS, ERRnoaccess
+#define NT_STATUS_NOT_REGISTRY_FILE (0xC0000000 | 0x015c) // ERRHRD, ERRgeneral
+#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x015d) // ERRHRD, ERRgeneral
+#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR (0xC0000000 | 0x015e) // ERRHRD, ERRgeneral
+#define NT_STATUS_FT_MISSING_MEMBER (0xC0000000 | 0x015f) // ERRHRD, ERRgeneral
+#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY (0xC0000000 | 0x0160) // ERRHRD, ERRgeneral
+#define NT_STATUS_ILLEGAL_CHARACTER (0xC0000000 | 0x0161) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNMAPPABLE_CHARACTER (0xC0000000 | 0x0162) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNDEFINED_CHARACTER (0xC0000000 | 0x0163) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOPPY_VOLUME (0xC0000000 | 0x0164) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND (0xC0000000 | 0x0165) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOPPY_WRONG_CYLINDER (0xC0000000 | 0x0166) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOPPY_UNKNOWN_ERROR (0xC0000000 | 0x0167) // ERRHRD, ERRgeneral
+#define NT_STATUS_FLOPPY_BAD_REGISTERS (0xC0000000 | 0x0168) // ERRHRD, ERRgeneral
+#define NT_STATUS_DISK_RECALIBRATE_FAILED (0xC0000000 | 0x0169) // ERRHRD, ERRgeneral
+#define NT_STATUS_DISK_OPERATION_FAILED (0xC0000000 | 0x016a) // ERRHRD, ERRgeneral
+#define NT_STATUS_DISK_RESET_FAILED (0xC0000000 | 0x016b) // ERRHRD, ERRgeneral
+#define NT_STATUS_SHARED_IRQ_BUSY (0xC0000000 | 0x016c) // ERRHRD, ERRgeneral
+#define NT_STATUS_FT_ORPHANING (0xC0000000 | 0x016d) // ERRHRD, ERRgeneral
+#define NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT (0xC0000000 | 0x016e) // ERRHRD, ERRgeneral
+#define NT_STATUS_PARTITION_FAILURE (0xC0000000 | 0x0172) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_BLOCK_LENGTH (0xC0000000 | 0x0173) // ERRHRD, ERRgeneral
+#define NT_STATUS_DEVICE_NOT_PARTITIONED (0xC0000000 | 0x0174) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNABLE_TO_LOCK_MEDIA (0xC0000000 | 0x0175) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA (0xC0000000 | 0x0176) // ERRHRD, ERRgeneral
+#define NT_STATUS_EOM_OVERFLOW (0xC0000000 | 0x0177) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_MEDIA (0xC0000000 | 0x0178) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_SUCH_MEMBER (0xC0000000 | 0x017a) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_MEMBER (0xC0000000 | 0x017b) // ERRHRD, ERRgeneral
+#define NT_STATUS_KEY_DELETED (0xC0000000 | 0x017c) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_LOG_SPACE (0xC0000000 | 0x017d) // ERRHRD, ERRgeneral
+#define NT_STATUS_TOO_MANY_SIDS (0xC0000000 | 0x017e) // ERRHRD, ERRgeneral
+#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x017f) // ERRHRD, ERRgeneral
+#define NT_STATUS_KEY_HAS_CHILDREN (0xC0000000 | 0x0180) // ERRHRD, ERRgeneral
+#define NT_STATUS_CHILD_MUST_BE_VOLATILE (0xC0000000 | 0x0181) // ERRHRD, ERRgeneral
+#define NT_STATUS_DEVICE_CONFIGURATION_ERROR (0xC0000000 | 0x0182) // ERRDOS, 87
+#define NT_STATUS_DRIVER_INTERNAL_ERROR (0xC0000000 | 0x0183) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_DEVICE_STATE (0xC0000000 | 0x0184) // ERRDOS, 22
+#define NT_STATUS_IO_DEVICE_ERROR (0xC0000000 | 0x0185) // ERRHRD, ERRgeneral
+#define NT_STATUS_DEVICE_PROTOCOL_ERROR (0xC0000000 | 0x0186) // ERRHRD, ERRgeneral
+#define NT_STATUS_BACKUP_CONTROLLER (0xC0000000 | 0x0187) // ERRHRD, ERRgeneral
+#define NT_STATUS_LOG_FILE_FULL (0xC0000000 | 0x0188) // ERRHRD, ERRgeneral
+#define NT_STATUS_TOO_LATE (0xC0000000 | 0x0189) // ERRDOS, 19
+#define NT_STATUS_NO_TRUST_LSA_SECRET (0xC0000000 | 0x018a) // ERRDOS, ERRnoaccess
+#define NT_STATUS_NO_TRUST_SAM_ACCOUNT (0xC0000000 | 0x018b) // ERRDOS, ERRnoaccess
+#define NT_STATUS_TRUSTED_DOMAIN_FAILURE (0xC0000000 | 0x018c) // ERRDOS, ERRnoaccess
+#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE (0xC0000000 | 0x018d) // ERRDOS, ERRnoaccess
+#define NT_STATUS_EVENTLOG_FILE_CORRUPT (0xC0000000 | 0x018e) // ERRHRD, ERRgeneral
+#define NT_STATUS_EVENTLOG_CANT_START (0xC0000000 | 0x018f) // ERRHRD, ERRgeneral
+#define NT_STATUS_TRUST_FAILURE (0xC0000000 | 0x0190) // ERRDOS, ERRnoaccess
+#define NT_STATUS_MUTANT_LIMIT_EXCEEDED (0xC0000000 | 0x0191) // ERRHRD, ERRgeneral
+#define NT_STATUS_NETLOGON_NOT_STARTED (0xC0000000 | 0x0192) // ERRDOS, ERRnetlogonNotStarted
+#define NT_STATUS_ACCOUNT_EXPIRED (0xC0000000 | 0x0193) // ERRSRV, ERRaccountexpired
+#define NT_STATUS_POSSIBLE_DEADLOCK (0xC0000000 | 0x0194) // ERRHRD, ERRgeneral
+#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT (0xC0000000 | 0x0195) // ERRHRD, ERRgeneral
+#define NT_STATUS_REMOTE_SESSION_LIMIT (0xC0000000 | 0x0196) // ERRHRD, ERRgeneral
+#define NT_STATUS_EVENTLOG_FILE_CHANGED (0xC0000000 | 0x0197) // ERRHRD, ERRgeneral
+#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT (0xC0000000 | 0x0198) // ERRDOS, ERRnoaccess
+#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT (0xC0000000 | 0x0199) // ERRDOS, ERRnoaccess
+#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT (0xC0000000 | 0x019a) // ERRDOS, ERRnoaccess
+#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT (0xC0000000 | 0x019b) // ERRDOS, ERRnoaccess
+#define NT_STATUS_FS_DRIVER_REQUIRED (0xC0000000 | 0x019c) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_LOCK_RANGE (0xC0000000 | 0x01a1) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_USER_SESSION_KEY (0xC0000000 | 0x0202) // ERRHRD, ERRgeneral
+#define NT_STATUS_USER_SESSION_DELETED (0xC0000000 | 0x0203) // ERRDOS, 59
+#define NT_STATUS_RESOURCE_LANG_NOT_FOUND (0xC0000000 | 0x0204) // ERRHRD, ERRgeneral
+#define NT_STATUS_INSUFF_SERVER_RESOURCES (0xC0000000 | 0x0205) // ERRDOS, ERRnoresource
+#define NT_STATUS_INVALID_BUFFER_SIZE (0xC0000000 | 0x0206) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_ADDRESS_COMPONENT (0xC0000000 | 0x0207) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_ADDRESS_WILDCARD (0xC0000000 | 0x0208) // ERRHRD, ERRgeneral
+#define NT_STATUS_TOO_MANY_ADDRESSES (0xC0000000 | 0x0209) // ERRDOS, 68
+#define NT_STATUS_ADDRESS_ALREADY_EXISTS (0xC0000000 | 0x020a) // ERRDOS, 52
+#define NT_STATUS_ADDRESS_CLOSED (0xC0000000 | 0x020b) // ERRDOS, 64
+#define NT_STATUS_CONNECTION_DISCONNECTED (0xC0000000 | 0x020c) // ERRDOS, 64
+#define NT_STATUS_CONNECTION_RESET (0xC0000000 | 0x020d) // ERRDOS, 64
+#define NT_STATUS_TOO_MANY_NODES (0xC0000000 | 0x020e) // ERRDOS, 68
+#define NT_STATUS_TRANSACTION_ABORTED (0xC0000000 | 0x020f) // ERRDOS, 59
+#define NT_STATUS_TRANSACTION_TIMED_OUT (0xC0000000 | 0x0210) // ERRDOS, 59
+#define NT_STATUS_TRANSACTION_NO_RELEASE (0xC0000000 | 0x0211) // ERRDOS, 59
+#define NT_STATUS_TRANSACTION_NO_MATCH (0xC0000000 | 0x0212) // ERRDOS, 59
+#define NT_STATUS_TRANSACTION_RESPONDED (0xC0000000 | 0x0213) // ERRDOS, 59
+#define NT_STATUS_TRANSACTION_INVALID_ID (0xC0000000 | 0x0214) // ERRDOS, 59
+#define NT_STATUS_TRANSACTION_INVALID_TYPE (0xC0000000 | 0x0215) // ERRDOS, 59
+#define NT_STATUS_NOT_SERVER_SESSION (0xC0000000 | 0x0216) // ERRDOS, ERRunsup
+#define NT_STATUS_NOT_CLIENT_SESSION (0xC0000000 | 0x0217) // ERRDOS, ERRunsup
+#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE (0xC0000000 | 0x0218) // ERRHRD, ERRgeneral
+#define NT_STATUS_DEBUG_ATTACH_FAILED (0xC0000000 | 0x0219) // ERRHRD, ERRgeneral
+#define NT_STATUS_SYSTEM_PROCESS_TERMINATED (0xC0000000 | 0x021a) // ERRHRD, ERRgeneral
+#define NT_STATUS_DATA_NOT_ACCEPTED (0xC0000000 | 0x021b) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_BROWSER_SERVERS_FOUND (0xC0000000 | 0x021c) // ERRHRD, ERRgeneral
+#define NT_STATUS_VDM_HARD_ERROR (0xC0000000 | 0x021d) // ERRHRD, ERRgeneral
+#define NT_STATUS_DRIVER_CANCEL_TIMEOUT (0xC0000000 | 0x021e) // ERRHRD, ERRgeneral
+#define NT_STATUS_REPLY_MESSAGE_MISMATCH (0xC0000000 | 0x021f) // ERRHRD, ERRgeneral
+#define NT_STATUS_MAPPED_ALIGNMENT (0xC0000000 | 0x0220) // ERRHRD, ERRgeneral
+#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH (0xC0000000 | 0x0221) // ERRDOS, 193
+#define NT_STATUS_LOST_WRITEBEHIND_DATA (0xC0000000 | 0x0222) // ERRHRD, ERRgeneral
+#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID (0xC0000000 | 0x0223) // ERRHRD, ERRgeneral
+#define NT_STATUS_PASSWORD_MUST_CHANGE (0xC0000000 | 0x0224) // ERRSRV, ERRpasswordExpired
+#define NT_STATUS_NOT_FOUND (0xC0000000 | 0x0225) // ERRHRD, ERRgeneral
+#define NT_STATUS_NOT_TINY_STREAM (0xC0000000 | 0x0226) // ERRHRD, ERRgeneral
+#define NT_STATUS_RECOVERY_FAILURE (0xC0000000 | 0x0227) // ERRHRD, ERRgeneral
+#define NT_STATUS_STACK_OVERFLOW_READ (0xC0000000 | 0x0228) // ERRHRD, ERRgeneral
+#define NT_STATUS_FAIL_CHECK (0xC0000000 | 0x0229) // ERRHRD, ERRgeneral
+#define NT_STATUS_DUPLICATE_OBJECTID (0xC0000000 | 0x022a) // ERRHRD, ERRgeneral
+#define NT_STATUS_OBJECTID_EXISTS (0xC0000000 | 0x022b) // ERRHRD, ERRgeneral
+#define NT_STATUS_CONVERT_TO_LARGE (0xC0000000 | 0x022c) // ERRHRD, ERRgeneral
+#define NT_STATUS_RETRY (0xC0000000 | 0x022d) // ERRHRD, ERRgeneral
+#define NT_STATUS_FOUND_OUT_OF_SCOPE (0xC0000000 | 0x022e) // ERRHRD, ERRgeneral
+#define NT_STATUS_ALLOCATE_BUCKET (0xC0000000 | 0x022f) // ERRHRD, ERRgeneral
+#define NT_STATUS_PROPSET_NOT_FOUND (0xC0000000 | 0x0230) // ERRHRD, ERRgeneral
+#define NT_STATUS_MARSHALL_OVERFLOW (0xC0000000 | 0x0231) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_VARIANT (0xC0000000 | 0x0232) // ERRHRD, ERRgeneral
+#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND (0xC0000000 | 0x0233) // ERRHRD, ERRgeneral
+#define NT_STATUS_ACCOUNT_LOCKED_OUT (0xC0000000 | 0x0234) // ERRDOS, ERRnoaccess
+#define NT_STATUS_HANDLE_NOT_CLOSABLE (0xC0000000 | 0x0235) // ERRDOS, ERRbadfid
+#define NT_STATUS_CONNECTION_REFUSED (0xC0000000 | 0x0236) // ERRHRD, ERRgeneral
+#define NT_STATUS_GRACEFUL_DISCONNECT (0xC0000000 | 0x0237) // ERRHRD, ERRgeneral
+#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED (0xC0000000 | 0x0238) // ERRHRD, ERRgeneral
+#define NT_STATUS_ADDRESS_NOT_ASSOCIATED (0xC0000000 | 0x0239) // ERRHRD, ERRgeneral
+#define NT_STATUS_CONNECTION_INVALID (0xC0000000 | 0x023a) // ERRHRD, ERRgeneral
+#define NT_STATUS_CONNECTION_ACTIVE (0xC0000000 | 0x023b) // ERRHRD, ERRgeneral
+#define NT_STATUS_NETWORK_UNREACHABLE (0xC0000000 | 0x023c) // ERRHRD, ERRgeneral
+#define NT_STATUS_HOST_UNREACHABLE (0xC0000000 | 0x023d) // ERRHRD, ERRgeneral
+#define NT_STATUS_PROTOCOL_UNREACHABLE (0xC0000000 | 0x023e) // ERRHRD, ERRgeneral
+#define NT_STATUS_PORT_UNREACHABLE (0xC0000000 | 0x023f) // ERRHRD, ERRgeneral
+#define NT_STATUS_REQUEST_ABORTED (0xC0000000 | 0x0240) // ERRHRD, ERRgeneral
+#define NT_STATUS_CONNECTION_ABORTED (0xC0000000 | 0x0241) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_COMPRESSION_BUFFER (0xC0000000 | 0x0242) // ERRHRD, ERRgeneral
+#define NT_STATUS_USER_MAPPED_FILE (0xC0000000 | 0x0243) // ERRHRD, ERRgeneral
+#define NT_STATUS_AUDIT_FAILED (0xC0000000 | 0x0244) // ERRHRD, ERRgeneral
+#define NT_STATUS_TIMER_RESOLUTION_NOT_SET (0xC0000000 | 0x0245) // ERRHRD, ERRgeneral
+#define NT_STATUS_CONNECTION_COUNT_LIMIT (0xC0000000 | 0x0246) // ERRHRD, ERRgeneral
+#define NT_STATUS_LOGIN_TIME_RESTRICTION (0xC0000000 | 0x0247) // ERRHRD, ERRgeneral
+#define NT_STATUS_LOGIN_WKSTA_RESTRICTION (0xC0000000 | 0x0248) // ERRHRD, ERRgeneral
+#define NT_STATUS_IMAGE_MP_UP_MISMATCH (0xC0000000 | 0x0249) // ERRDOS, 193
+#define NT_STATUS_INSUFFICIENT_LOGON_INFO (0xC0000000 | 0x0250) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_DLL_ENTRYPOINT (0xC0000000 | 0x0251) // ERRHRD, ERRgeneral
+#define NT_STATUS_BAD_SERVICE_ENTRYPOINT (0xC0000000 | 0x0252) // ERRHRD, ERRgeneral
+#define NT_STATUS_LPC_REPLY_LOST (0xC0000000 | 0x0253) // ERRHRD, ERRgeneral
+#define NT_STATUS_IP_ADDRESS_CONFLICT1 (0xC0000000 | 0x0254) // ERRHRD, ERRgeneral
+#define NT_STATUS_IP_ADDRESS_CONFLICT2 (0xC0000000 | 0x0255) // ERRHRD, ERRgeneral
+#define NT_STATUS_REGISTRY_QUOTA_LIMIT (0xC0000000 | 0x0256) // ERRHRD, ERRgeneral
+#define NT_STATUS_PATH_NOT_COVERED (0xC0000000 | 0x0257) // ERRSRV, 3
+#define NT_STATUS_NO_CALLBACK_ACTIVE (0xC0000000 | 0x0258) // ERRHRD, ERRgeneral
+#define NT_STATUS_LICENSE_QUOTA_EXCEEDED (0xC0000000 | 0x0259) // ERRHRD, ERRgeneral
+#define NT_STATUS_PWD_TOO_SHORT (0xC0000000 | 0x025a) // ERRHRD, ERRgeneral
+#define NT_STATUS_PWD_TOO_RECENT (0xC0000000 | 0x025b) // ERRHRD, ERRgeneral
+#define NT_STATUS_PWD_HISTORY_CONFLICT (0xC0000000 | 0x025c) // ERRHRD, ERRgeneral
+#define NT_STATUS_PLUGPLAY_NO_DEVICE (0xC0000000 | 0x025e) // ERRHRD, ERRgeneral
+#define NT_STATUS_UNSUPPORTED_COMPRESSION (0xC0000000 | 0x025f) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_HW_PROFILE (0xC0000000 | 0x0260) // ERRHRD, ERRgeneral
+#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH (0xC0000000 | 0x0261) // ERRHRD, ERRgeneral
+#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND (0xC0000000 | 0x0262) // ERRDOS, 182
+#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0263) // ERRDOS, 127
+#define NT_STATUS_RESOURCE_NOT_OWNED (0xC0000000 | 0x0264) // ERRDOS, 288
+#define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 0x0265) // ERRDOS, ErrTooManyLinks
+#define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 0x0266) // ERRHRD, ERRgeneral
+#define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 0x0267) // ERRHRD, ERRgeneral
+#define NT_STATUS_VOLUME_DISMOUNTED (0xC0000000 | 0x026e) // ERRDOS, 21
+#define NT_STATUS_NOT_A_REPARSE_POINT (0xC0000000 | 0x0275) // ERRHRD, ERRgeneral
+#define NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT (0xC0000000 | 0x0281) // ERRDOS, 161
+#define NT_STATUS_ENCRYPTION_FAILED (0xC0000000 | 0x028a) // ERRDOS, ERRnoaccess
+#define NT_STATUS_DECRYPTION_FAILED (0xC0000000 | 0x028b) // ERRDOS, ERRnoaccess
+#define NT_STATUS_RANGE_NOT_FOUND (0xC0000000 | 0x028c) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_RECOVERY_POLICY (0xC0000000 | 0x028d) // ERRDOS, ERRnoaccess
+#define NT_STATUS_NO_EFS (0xC0000000 | 0x028e) // ERRDOS, ERRnoaccess
+#define NT_STATUS_WRONG_EFS (0xC0000000 | 0x028f) // ERRDOS, ERRnoaccess
+#define NT_STATUS_NO_USER_KEYS (0xC0000000 | 0x0290) // ERRDOS, ERRnoaccess
+#define NT_STATUS_VOLUME_NOT_UPGRADED (0xC0000000 | 0x029c) // ERRDOS, ERRbadfunc
+#define NT_STATUS_NETWORK_SESSION_EXPIRED (0xC0000000 | 0x035c) // ERRHRD, ERRgeneral
+/* scheduler */
+#define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) // ERRHRD, ERRgeneral
+#define NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP (0xC0000000 | 0x5D0000) // ERRHRD, ERRgeneral
+#define NT_STATUS_OS2_INVALID_LEVEL 0x007c0001 // ERRDOS, ERRunknownlevel
#endif /* _NTERR_H */
diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
index be22bbc4a65a..9530e5b01564 100644
--- a/fs/smb/client/readdir.c
+++ b/fs/smb/client/readdir.c
@@ -73,7 +73,6 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
bool posix = cifs_sb_master_tcon(cifs_sb)->posix_extensions;
bool reparse_need_reval = false;
- DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
int rc;
cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
@@ -105,7 +104,7 @@ retry:
(fattr->cf_flags & CIFS_FATTR_NEED_REVAL))
return;
- dentry = d_alloc_parallel(parent, name, &wq);
+ dentry = d_alloc_parallel(parent, name);
}
if (IS_ERR(dentry))
return;
@@ -143,7 +142,8 @@ retry:
fattr->cf_rdev = inode->i_rdev;
fattr->cf_uid = inode->i_uid;
fattr->cf_gid = inode->i_gid;
- fattr->cf_eof = CIFS_I(inode)->netfs.remote_i_size;
+ fattr->cf_eof =
+ netfs_read_remote_i_size(inode);
fattr->cf_symlink_target = NULL;
} else {
CIFS_I(inode)->time = 0;
@@ -242,9 +242,11 @@ static void
cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
struct cifs_sb_info *cifs_sb)
{
+ unsigned int sbflags = cifs_sb_flags(cifs_sb);
struct smb2_posix_info_parsed parsed;
+ int rc;
- posix_info_parse(info, NULL, &parsed);
+ rc = posix_info_parse(info, NULL, &parsed);
memset(fattr, 0, sizeof(*fattr));
fattr->cf_uniqueid = le64_to_cpu(info->Inode);
@@ -281,8 +283,17 @@ cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
le32_to_cpu(info->ReparseTag),
le32_to_cpu(info->Mode));
- sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER);
- sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP);
+ fattr->cf_uid = cifs_sb->ctx->linux_uid;
+ fattr->cf_gid = cifs_sb->ctx->linux_gid;
+ if (rc < 0) {
+ cifs_dbg(VFS, "%s: failed to parse SIDs: %d\n",
+ __func__, rc);
+ } else {
+ if (!(sbflags & CIFS_MOUNT_OVERR_UID))
+ sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER);
+ if (!(sbflags & CIFS_MOUNT_OVERR_GID))
+ sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP);
+ }
}
static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info)
@@ -415,7 +426,7 @@ ffirst_retry:
if (rc == 0) {
cifsFile->invalidHandle = false;
} else if (rc == -EOPNOTSUPP && (sbflags & CIFS_MOUNT_SERVER_INUM)) {
- cifs_autodisable_serverino(cifs_sb);
+ cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number via query_dir_first", rc);
goto ffirst_retry;
}
error_exit:
@@ -732,6 +743,8 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
if (cfile->srch_inf.smallBuf)
cifs_small_buf_release(cfile->srch_inf.
ntwrk_buf_start);
+ else if (cfile->srch_inf.is_dynamic_buf)
+ kfree(cfile->srch_inf.ntwrk_buf_start);
else
cifs_buf_release(cfile->srch_inf.
ntwrk_buf_start);
@@ -950,7 +963,7 @@ static bool cifs_dir_emit(struct dir_context *ctx,
static int cifs_filldir(char *find_entry, struct file *file,
struct dir_context *ctx,
char *scratch_buf, unsigned int max_len,
- struct cached_fid *cfid)
+ char *end_of_smb, struct cached_fid *cfid)
{
struct cifsFileInfo *file_info = file->private_data;
struct super_block *sb = file_inode(file)->i_sb;
@@ -972,6 +985,11 @@ static int cifs_filldir(char *find_entry, struct file *file,
return -EINVAL;
}
+ if (de.name + de.namelen > end_of_smb) {
+ cifs_dbg(VFS, "search entry name extends past end of SMB\n");
+ return -EINVAL;
+ }
+
/* skip . and .. since we added them first */
if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode))
return 0;
@@ -1027,7 +1045,7 @@ static int cifs_filldir(char *find_entry, struct file *file,
fattr.cf_uniqueid = de.ino;
} else {
fattr.cf_uniqueid = iunique(sb, ROOT_I);
- cifs_autodisable_serverino(cifs_sb);
+ cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number from readdir", 0);
}
if ((sbflags & CIFS_MOUNT_MF_SYMLINKS) && couldbe_mf_symlink(&fattr))
@@ -1192,7 +1210,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
*/
*tmp_buf = 0;
rc = cifs_filldir(current_entry, file, ctx,
- tmp_buf, max_len, cfid);
+ tmp_buf, max_len, end_of_smb, cfid);
if (rc) {
if (rc > 0)
rc = 0;
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index cd1e1eaee67a..3a27773186ae 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -3,6 +3,7 @@
* Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com>
*/
+#include <linux/ctype.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/slab.h>
@@ -67,6 +68,7 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode,
char *sym = NULL;
struct kvec iov;
bool directory;
+ int path_len;
int rc = 0;
if (strlen(symname) > REPARSE_SYM_PATH_MAX)
@@ -158,17 +160,40 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode,
convert_delimiter(sym, sep);
/*
- * For absolute NT symlinks it is required to pass also leading
- * backslash and to not mangle NT object prefix "\\??\\" and not to
- * mangle colon in drive letter. But cifs_convert_path_to_utf16()
- * removes leading backslash and replaces '?' and ':'. So temporary
- * mask these characters in NT object prefix by '_' and then change
- * them back.
+ * Absolute NT symlinks must retain the leading backslash, "\\??\\"
+ * prefix and drive-letter colon. cifs_convert_path_to_utf16() strips
+ * the leading backslash and maps '?' and ':', so temporarily mask
+ * these characters with '_' and restore them after conversion.
+ *
+ * When symlinkroot is unset, sym comes directly from the caller.
+ * Validate the complete "\\??\\X:" prefix before using fixed offsets
+ * or subtracting the NT prefix length below. Require an ASCII drive
+ * letter so the prefix occupies six characters in UTF-16 too.
*/
- if (!(sbflags & CIFS_MOUNT_POSIX_PATHS) && symname[0] == '/')
+ if (!(sbflags & CIFS_MOUNT_POSIX_PATHS) && symname[0] == '/') {
+ if (!strstarts(sym, "\\??\\") || !isascii(sym[4]) ||
+ !isalpha(sym[4]) || sym[5] != ':') {
+ rc = -EINVAL;
+ goto out;
+ }
sym[0] = sym[1] = sym[2] = sym[5] = '_';
+ }
+
+ /*
+ * On a POSIX paths mount the symlink target is stored verbatim, so
+ * convert it with cifs_strndup_to_utf16(). cifs_convert_path_to_utf16()
+ * must not be used here: it strips a leading path separator (it is
+ * meant for share-relative SMB paths), which would corrupt an absolute
+ * POSIX symlink target such as "/foo/bar". Using NO_MAP_UNI_RSVD also
+ * matches the readback path in smb2_parse_native_symlink().
+ */
+ if (sbflags & CIFS_MOUNT_POSIX_PATHS)
+ path = cifs_strndup_to_utf16(sym, strlen(sym), &path_len,
+ cifs_sb->local_nls,
+ NO_MAP_UNI_RSVD);
+ else
+ path = cifs_convert_path_to_utf16(sym, cifs_sb);
- path = cifs_convert_path_to_utf16(sym, cifs_sb);
if (!path) {
rc = -ENOMEM;
goto out;
@@ -956,7 +981,8 @@ globalroot:
linux_target[i*3 + 1] = '.';
linux_target[i*3 + 2] = sep;
}
- memcpy(linux_target + levels*3, smb_target+1, smb_target_len); /* +1 to skip leading sep */
+ /* +1 to skip leading sep */
+ memcpy(linux_target + levels*3, smb_target+1, smb_target_len-1);
} else {
/*
* This is either an absolute symlink in POSIX-style format
@@ -1122,25 +1148,31 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
struct cifs_sb_info *cifs_sb,
u32 tag, struct cifs_fattr *fattr)
{
+ unsigned int sbflags = cifs_sb_flags(cifs_sb);
+ kuid_t uid = cifs_sb->ctx->linux_uid;
+ kgid_t gid = cifs_sb->ctx->linux_gid;
struct smb2_file_full_ea_info *ea;
bool have_xattr_dev = false;
+ dev_t rdev = 0;
+ umode_t mode;
u32 next = 0;
+ mode = fattr->cf_mode & ~S_IFMT;
switch (tag) {
case IO_REPARSE_TAG_LX_SYMLINK:
- fattr->cf_mode |= S_IFLNK;
+ mode |= S_IFLNK;
break;
case IO_REPARSE_TAG_LX_FIFO:
- fattr->cf_mode |= S_IFIFO;
+ mode |= S_IFIFO;
break;
case IO_REPARSE_TAG_AF_UNIX:
- fattr->cf_mode |= S_IFSOCK;
+ mode |= S_IFSOCK;
break;
case IO_REPARSE_TAG_LX_CHR:
- fattr->cf_mode |= S_IFCHR;
+ mode |= S_IFCHR;
break;
case IO_REPARSE_TAG_LX_BLK:
- fattr->cf_mode |= S_IFBLK;
+ mode |= S_IFBLK;
break;
}
@@ -1162,26 +1194,31 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
nlen = ea->ea_name_length;
v = (void *)((u8 *)ea->ea_data + ea->ea_name_length + 1);
- if (!strncmp(name, SMB2_WSL_XATTR_UID, nlen))
- fattr->cf_uid = wsl_make_kuid(cifs_sb, v);
- else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen))
- fattr->cf_gid = wsl_make_kgid(cifs_sb, v);
- else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen)) {
+ if (!strncmp(name, SMB2_WSL_XATTR_UID, nlen)) {
+ if (!(sbflags & CIFS_MOUNT_OVERR_UID))
+ uid = wsl_make_kuid(cifs_sb, v);
+ } else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen)) {
+ if (!(sbflags & CIFS_MOUNT_OVERR_GID))
+ gid = wsl_make_kgid(cifs_sb, v);
+ } else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen)) {
/* File type in reparse point tag and in xattr mode must match. */
- if (S_DT(fattr->cf_mode) != S_DT(le32_to_cpu(*(__le32 *)v)))
+ if (S_DT(mode) != S_DT(get_unaligned_le32(v)))
return false;
- fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
+ mode = get_unaligned_le32(v);
} else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) {
- fattr->cf_rdev = reparse_mkdev(v);
+ rdev = reparse_mkdev(v);
have_xattr_dev = true;
}
} while (next);
out:
-
/* Major and minor numbers for char and block devices are mandatory. */
if (!have_xattr_dev && (tag == IO_REPARSE_TAG_LX_CHR || tag == IO_REPARSE_TAG_LX_BLK))
return false;
+ fattr->cf_uid = uid;
+ fattr->cf_gid = gid;
+ fattr->cf_mode = mode;
+ fattr->cf_rdev = rdev;
return true;
}
@@ -1190,6 +1227,7 @@ static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_open_info_data *data)
{
struct reparse_nfs_data_buffer *buf = (struct reparse_nfs_data_buffer *)data->reparse.buf;
+ umode_t ftype;
if (buf == NULL)
return true;
@@ -1205,7 +1243,7 @@ static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb,
WARN_ON_ONCE(1);
return false;
}
- fattr->cf_mode |= S_IFCHR;
+ ftype = S_IFCHR;
fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
break;
case NFS_SPECFILE_BLK:
@@ -1213,22 +1251,23 @@ static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb,
WARN_ON_ONCE(1);
return false;
}
- fattr->cf_mode |= S_IFBLK;
+ ftype = S_IFBLK;
fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
break;
case NFS_SPECFILE_FIFO:
- fattr->cf_mode |= S_IFIFO;
+ ftype = S_IFIFO;
break;
case NFS_SPECFILE_SOCK:
- fattr->cf_mode |= S_IFSOCK;
+ ftype = S_IFSOCK;
break;
case NFS_SPECFILE_LNK:
- fattr->cf_mode |= S_IFLNK;
+ ftype = S_IFLNK;
break;
default:
WARN_ON_ONCE(1);
return false;
}
+ fattr->cf_mode = (fattr->cf_mode & ~S_IFMT) | ftype;
return true;
}
@@ -1256,6 +1295,7 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
break;
case 0: /* SMB1 symlink */
case IO_REPARSE_TAG_SYMLINK:
+ fattr->cf_mode &= ~S_IFMT;
fattr->cf_mode |= S_IFLNK;
break;
default:
diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h
index 0164dc47bdfd..05b2cecb4495 100644
--- a/fs/smb/client/reparse.h
+++ b/fs/smb/client/reparse.h
@@ -9,6 +9,7 @@
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/uidgid.h>
+#include <linux/unaligned.h>
#include "fs_context.h"
#include "cifsglob.h"
#include "../common/smbfsctl.h"
@@ -23,7 +24,7 @@
static inline dev_t reparse_mkdev(void *ptr)
{
- u64 v = le64_to_cpu(*(__le64 *)ptr);
+ u64 v = get_unaligned_le64(ptr);
return MKDEV(v & 0xffffffff, v >> 32);
}
@@ -31,7 +32,7 @@ static inline dev_t reparse_mkdev(void *ptr)
static inline kuid_t wsl_make_kuid(struct cifs_sb_info *cifs_sb,
void *ptr)
{
- u32 uid = le32_to_cpu(*(__le32 *)ptr);
+ u32 uid = get_unaligned_le32(ptr);
if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_OVERR_UID)
return cifs_sb->ctx->linux_uid;
@@ -41,7 +42,7 @@ static inline kuid_t wsl_make_kuid(struct cifs_sb_info *cifs_sb,
static inline kgid_t wsl_make_kgid(struct cifs_sb_info *cifs_sb,
void *ptr)
{
- u32 gid = le32_to_cpu(*(__le32 *)ptr);
+ u32 gid = get_unaligned_le32(ptr);
if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_OVERR_GID)
return cifs_sb->ctx->linux_gid;
@@ -98,15 +99,21 @@ static inline bool reparse_inode_match(struct inode *inode,
timespec64_equal(&ctime, &fattr->cf_ctime);
}
+static inline u32 cifs_open_data_attrs(const struct cifs_open_info_data *data)
+{
+ if (data->contains_posix_file_info)
+ return le32_to_cpu(data->posix_fi.DosAttributes);
+
+ return le32_to_cpu(data->fi.Attributes);
+}
+
static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
{
- u32 attrs;
- bool ret;
+ u32 attrs = cifs_open_data_attrs(data);
if (data->contains_posix_file_info) {
struct smb311_posix_qinfo *fi = &data->posix_fi;
- attrs = le32_to_cpu(fi->DosAttributes);
if (data->reparse_point) {
attrs |= ATTR_REPARSE_POINT;
fi->DosAttributes = cpu_to_le32(attrs);
@@ -115,16 +122,13 @@ static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
} else {
struct smb2_file_all_info *fi = &data->fi;
- attrs = le32_to_cpu(fi->Attributes);
if (data->reparse_point) {
attrs |= ATTR_REPARSE_POINT;
fi->Attributes = cpu_to_le32(attrs);
}
}
- ret = attrs & ATTR_REPARSE_POINT;
-
- return ret;
+ return attrs & ATTR_REPARSE_POINT;
}
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
index 698bd27119ae..e095f41b5882 100644
--- a/fs/smb/client/sess.c
+++ b/fs/smb/client/sess.c
@@ -149,9 +149,9 @@ int cifs_try_adding_channels(struct cifs_ses *ses)
int old_chan_count, new_chan_count;
int left;
int rc = 0;
- int tries = 0;
+ int tries = 0, attempts;
size_t iface_weight = 0, iface_min_speed = 0;
- struct cifs_server_iface *iface = NULL, *niface = NULL;
+ struct cifs_server_iface *iface = NULL, *candidate = NULL;
struct cifs_server_iface *last_iface = NULL;
spin_lock(&ses->chan_lock);
@@ -197,67 +197,89 @@ int cifs_try_adding_channels(struct cifs_ses *ses)
break;
}
- if (!iface)
- iface = list_first_entry(&ses->iface_list, struct cifs_server_iface,
- iface_head);
last_iface = list_last_entry(&ses->iface_list, struct cifs_server_iface,
iface_head);
iface_min_speed = last_iface->speed;
+ spin_unlock(&ses->iface_lock);
- list_for_each_entry_safe_from(iface, niface, &ses->iface_list,
- iface_head) {
- /* do not mix rdma and non-rdma interfaces */
- if (iface->rdma_capable != ses->server->rdma)
- continue;
-
- /* skip ifaces that are unusable */
- if (!iface->is_active ||
- (is_ses_using_iface(ses, iface) &&
- !iface->rss_capable))
- continue;
+ attempts = 0;
+ while (left > 0) {
+ spin_lock(&ses->iface_lock);
- /* check if we already allocated enough channels */
- iface_weight = iface->speed / iface_min_speed;
+ /*
+ * iface_lock must be dropped while opening a channel,
+ * and a concurrent interface refresh may remove and
+ * free entries during that window, so no list entry
+ * may be kept across it without a reference. Scan
+ * the list from the beginning each time and only pass
+ * a referenced candidate to cifs_ses_add_channel();
+ * weight_fulfilled tracks the progress so that no
+ * iface is selected beyond its weight.
+ */
+ candidate = NULL;
+ list_for_each_entry(iface, &ses->iface_list, iface_head) {
+ /* do not mix rdma and non-rdma interfaces */
+ if (iface->rdma_capable != ses->server->rdma)
+ continue;
+
+ /* skip ifaces that are unusable */
+ if (!iface->is_active ||
+ (is_ses_using_iface(ses, iface) &&
+ !iface->rss_capable))
+ continue;
+
+ /* check if we already allocated enough channels */
+ iface_weight = iface->speed / iface_min_speed;
+
+ if (iface->weight_fulfilled >= iface_weight)
+ continue;
+
+ /* take ref before unlock */
+ kref_get(&iface->refcount);
+ candidate = iface;
+ break;
+ }
- if (iface->weight_fulfilled >= iface_weight)
- continue;
+ if (!candidate) {
+ /* no usable iface. reset weight_fulfilled and start over */
+ list_for_each_entry(iface, &ses->iface_list, iface_head)
+ iface->weight_fulfilled = 0;
+ spin_unlock(&ses->iface_lock);
+ break;
+ }
- /* take ref before unlock */
- kref_get(&iface->refcount);
+ attempts++;
+ if (attempts > 3 * ses->chan_max) {
+ kref_put(&candidate->refcount, release_iface);
+ spin_unlock(&ses->iface_lock);
+ break;
+ }
spin_unlock(&ses->iface_lock);
- rc = cifs_ses_add_channel(ses, iface);
+ rc = cifs_ses_add_channel(ses, candidate);
spin_lock(&ses->iface_lock);
if (rc) {
cifs_dbg(VFS, "failed to open extra channel on iface:%pIS rc=%d\n",
- &iface->sockaddr,
+ &candidate->sockaddr,
rc);
- kref_put(&iface->refcount, release_iface);
/* failure to add chan should increase weight */
- iface->weight_fulfilled++;
+ candidate->weight_fulfilled++;
+ kref_put(&candidate->refcount, release_iface);
+ spin_unlock(&ses->iface_lock);
continue;
}
- iface->num_channels++;
- iface->weight_fulfilled++;
+ candidate->num_channels++;
+ candidate->weight_fulfilled++;
cifs_info("successfully opened new channel on iface:%pIS\n",
- &iface->sockaddr);
- break;
- }
-
- /* reached end of list. reset weight_fulfilled and start over */
- if (list_entry_is_head(iface, &ses->iface_list, iface_head)) {
- list_for_each_entry(iface, &ses->iface_list, iface_head)
- iface->weight_fulfilled = 0;
+ &candidate->sockaddr);
spin_unlock(&ses->iface_lock);
- iface = NULL;
- continue;
- }
- spin_unlock(&ses->iface_lock);
- left--;
- new_chan_count++;
+ left--;
+ new_chan_count++;
+ break;
+ }
}
return new_chan_count - old_chan_count;
@@ -595,17 +617,6 @@ cifs_ses_add_channel(struct cifs_ses *ses,
spin_unlock(&ses->chan_lock);
mutex_lock(&ses->session_mutex);
- /*
- * We need to allocate the server crypto now as we will need
- * to sign packets before we generate the channel signing key
- * (we sign with the session key)
- */
- rc = smb3_crypto_shash_allocate(chan->server);
- if (rc) {
- cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
- mutex_unlock(&ses->session_mutex);
- goto out;
- }
rc = cifs_negotiate_protocol(xid, ses, chan->server);
if (!rc)
diff --git a/fs/smb/client/smb1encrypt.c b/fs/smb/client/smb1encrypt.c
index 0dbbce2431ff..c9eb68f04e7b 100644
--- a/fs/smb/client/smb1encrypt.c
+++ b/fs/smb/client/smb1encrypt.c
@@ -11,6 +11,7 @@
#include <linux/fips.h>
#include <crypto/md5.h>
+#include <crypto/utils.h>
#include "cifsproto.h"
#include "smb1proto.h"
#include "cifs_debug.h"
@@ -80,6 +81,7 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
else
memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);
+ memzero_explicit(smb_signature, sizeof(smb_signature));
return rc;
}
@@ -125,15 +127,13 @@ int cifs_verify_signature(struct smb_rqst *rqst,
rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be);
cifs_server_unlock(server);
- if (rc)
- return rc;
-
-/* cifs_dump_mem("what we think it should be: ",
- what_we_think_sig_should_be, 16); */
-
- if (memcmp(server_response_sig, what_we_think_sig_should_be, 8))
- return -EACCES;
- else
- return 0;
+ if (!rc) {
+ if (crypto_memneq(server_response_sig,
+ what_we_think_sig_should_be, 8))
+ rc = -EACCES;
+ }
+ memzero_explicit(what_we_think_sig_should_be,
+ sizeof(what_we_think_sig_should_be));
+ return rc;
}
diff --git a/fs/smb/client/smb1maperror.c b/fs/smb/client/smb1maperror.c
index e1964fd2b85e..395299f9121b 100644
--- a/fs/smb/client/smb1maperror.c
+++ b/fs/smb/client/smb1maperror.c
@@ -6,709 +6,107 @@
*
* Error mapping routines from Samba libsmb/errormap.c
* Copyright (C) Andrew Tridgell 2001
+ * Copyright (C) Luke Kenneth Casson Leighton 1997-2001.
*/
+#include <linux/bsearch.h>
+
+#include <kunit/visibility.h>
+
#include "cifsproto.h"
#include "smb1proto.h"
#include "smberr.h"
#include "nterr.h"
#include "cifs_debug.h"
-struct smb_to_posix_error {
- __u16 smb_err;
- int posix_code;
-};
+static __always_inline int smb1_posix_error_cmp(const void *_key, const void *_pivot)
+{
+ __u16 key = *(__u16 *)_key;
+ const struct smb_to_posix_error *pivot = _pivot;
+
+ if (key < pivot->smb_err)
+ return -1;
+ if (key > pivot->smb_err)
+ return 1;
+ return 0;
+}
static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
- {ERRbadfunc, -EINVAL},
- {ERRbadfile, -ENOENT},
- {ERRbadpath, -ENOTDIR},
- {ERRnofids, -EMFILE},
- {ERRnoaccess, -EACCES},
- {ERRbadfid, -EBADF},
- {ERRbadmcb, -EIO},
- {ERRnomem, -EREMOTEIO},
- {ERRbadmem, -EFAULT},
- {ERRbadenv, -EFAULT},
- {ERRbadformat, -EINVAL},
- {ERRbadaccess, -EACCES},
- {ERRbaddata, -EIO},
- {ERRbaddrive, -ENXIO},
- {ERRremcd, -EACCES},
- {ERRdiffdevice, -EXDEV},
- {ERRnofiles, -ENOENT},
- {ERRwriteprot, -EROFS},
- {ERRbadshare, -EBUSY},
- {ERRlock, -EACCES},
- {ERRunsup, -EINVAL},
- {ERRnosuchshare, -ENXIO},
- {ERRfilexists, -EEXIST},
- {ERRinvparm, -EINVAL},
- {ERRdiskfull, -ENOSPC},
- {ERRinvname, -ENOENT},
- {ERRunknownlevel, -EOPNOTSUPP},
- {ERRdirnotempty, -ENOTEMPTY},
- {ERRnotlocked, -ENOLCK},
- {ERRcancelviolation, -ENOLCK},
- {ERRalreadyexists, -EEXIST},
- {ERRmoredata, -EOVERFLOW},
- {ERReasnotsupported, -EOPNOTSUPP},
- {ErrQuota, -EDQUOT},
- {ErrNotALink, -ENOLINK},
- {ERRnetlogonNotStarted, -ENOPROTOOPT},
- {ERRsymlink, -EOPNOTSUPP},
- {ErrTooManyLinks, -EMLINK},
- {0, 0}
+/*
+ * Automatically generated by the `gen_smb1_mapping` script,
+ * sorted by DOS error code (ascending).
+ */
+#include "smb1_err_dos_map.c"
};
static const struct smb_to_posix_error mapping_table_ERRSRV[] = {
- {ERRerror, -EIO},
- {ERRbadpw, -EACCES}, /* was EPERM */
- {ERRbadtype, -EREMOTE},
- {ERRaccess, -EACCES},
- {ERRinvtid, -ENXIO},
- {ERRinvnetname, -ENXIO},
- {ERRinvdevice, -ENXIO},
- {ERRqfull, -ENOSPC},
- {ERRqtoobig, -ENOSPC},
- {ERRqeof, -EIO},
- {ERRinvpfid, -EBADF},
- {ERRsmbcmd, -EBADRQC},
- {ERRsrverror, -EIO},
- {ERRbadBID, -EIO},
- {ERRfilespecs, -EINVAL},
- {ERRbadLink, -EIO},
- {ERRbadpermits, -EINVAL},
- {ERRbadPID, -ESRCH},
- {ERRsetattrmode, -EINVAL},
- {ERRpaused, -EHOSTDOWN},
- {ERRmsgoff, -EHOSTDOWN},
- {ERRnoroom, -ENOSPC},
- {ERRrmuns, -EUSERS},
- {ERRtimeout, -ETIME},
- {ERRnoresource, -EREMOTEIO},
- {ERRtoomanyuids, -EUSERS},
- {ERRbaduid, -EACCES},
- {ERRusempx, -EIO},
- {ERRusestd, -EIO},
- {ERR_NOTIFY_ENUM_DIR, -ENOBUFS},
- {ERRnoSuchUser, -EACCES},
- {ERRaccountexpired, -EKEYEXPIRED},
- {ERRbadclient, -EACCES},
- {ERRbadLogonTime, -EACCES},
- {ERRpasswordExpired, -EKEYEXPIRED},
-
- {ERRnosupport, -EINVAL},
- {0, 0}
+/*
+ * Automatically generated by the `gen_smb1_mapping` script,
+ * sorted by SRV error code (ascending).
+ */
+#include "smb1_err_srv_map.c"
};
/*****************************************************************************
*convert a NT status code to a dos class/code
*****************************************************************************/
+
+static __always_inline int ntstatus_to_dos_cmp(const void *_key, const void *_pivot)
+{
+ __u32 key = *(__u32 *)_key;
+ const struct ntstatus_to_dos_err *pivot = _pivot;
+
+ if (key < pivot->ntstatus)
+ return -1;
+ if (key > pivot->ntstatus)
+ return 1;
+ return 0;
+}
+
/* NT status -> dos error map */
-static const struct {
- __u8 dos_class;
- __u16 dos_code;
- __u32 ntstatus;
-} ntstatus_to_dos_map[] = {
- {
- ERRSRV, ERR_NOTIFY_ENUM_DIR, NT_STATUS_NOTIFY_ENUM_DIR}, {
- ERRDOS, ERRmoredata, NT_STATUS_BUFFER_OVERFLOW}, {
- ERRDOS, ERRmoredata, NT_STATUS_MORE_PROCESSING_REQUIRED}, {
- ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, {
- ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, {
- ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, {
- ERRDOS, ERRbadpipe, NT_STATUS_INVALID_INFO_CLASS}, {
- ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, {
- ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, {
- ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, {
- ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, {
- ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, {
- ERRDOS, 87, NT_STATUS_INVALID_CID}, {
- ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, {
- ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, {
- ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, {
- ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, {
- ERRDOS, 38, NT_STATUS_END_OF_FILE}, {
- ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, {
- ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, {
- ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR},
-/* { This NT error code was 'sqashed'
- from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK
- during the session setup } */
- {
- ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, {
- ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, {
- ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, {
- ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, {
- ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, {
- ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, {
- ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, {
- ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, {
- ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, {
- ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, {
- ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED},
-/* { This NT error code was 'sqashed'
- from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
- during the session setup } */
- {
- ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, {
- ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, {
- ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, {
- ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, {
- ERRDOS, 158, NT_STATUS_NOT_LOCKED}, {
- ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, {
- ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, {
- ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, {
- ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, {
- ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, {
- /* mapping changed since shell does lookup on * expects FileNotFound */
- ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, {
- ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, {
- ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, {
- ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, {
- ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, {
- ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, {
- ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, {
- ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, {
- ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, {
- ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, {
- ERRDOS, 23, NT_STATUS_DATA_ERROR}, {
- ERRDOS, 23, NT_STATUS_CRC_ERROR}, {
- ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, {
- ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, {
- ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, {
- ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, {
- ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, {
- ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, {
- ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, {
- ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, {
- ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, {
- ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, {
- ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, {
- ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, {
- ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, {
- ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, {
- ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, {
- ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, {
- ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, {
- ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, {
- ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, {
- ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, {
- ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, {
- ERRDOS, ERRbadfile, NT_STATUS_DELETE_PENDING}, {
- ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, {
- ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, {
- ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, {
- ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, {
- ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS},
-/* { This NT error code was 'sqashed'
- from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE
- during the session setup } */
- {
- ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */
- ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, {
- ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, {
- ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, {
- ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN},
-/* { This NT error code was 'sqashed'
- from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE
- during the session setup } */
- {
- ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, {
- ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, {
- ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, {
- ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, {
- ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, {
- ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, {
- ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, {
- ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, {
- ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, {
- ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, {
- ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, {
- ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, {
- ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, {
- ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, {
- ERRDOS, 112, NT_STATUS_DISK_FULL}, {
- ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, {
- ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, {
- ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, {
- ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, {
- ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, {
- ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, {
- ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, {
- ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, {
- ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, {
- ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, {
- ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, {
- ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, {
- ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, {
- ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, {
- ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, {
- ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, {
- ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, {
- ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
-/* { This NT error code was 'sqashed'
- from NT_STATUS_INSUFFICIENT_RESOURCES to
- NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */
- {
- ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, {
- ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, {
- ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, {
- ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, {
- ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, {
- ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, {
- ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, {
- ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, {
- ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, {
- ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, {
- ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, {
- ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, {
- ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, {
- ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, {
- ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, {
- ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, {
- ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, {
- ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, {
- ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, {
- ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, {
- ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, {
- ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, {
- ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, {
- ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, {
- ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, {
- ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, {
- ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, {
- ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, {
- ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, {
- ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, {
- ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, {
- ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, {
- ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, {
- ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, {
- ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, {
- ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, {
- ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, {
- ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, {
- ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, {
- ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, {
- ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, {
- ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, {
- ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, {
- ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, {
- ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, {
- ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, {
- ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, {
- ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, {
- ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, {
- ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, {
- ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, {
- ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, {
- ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, {
- ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, {
- ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, {
- ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, {
- ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, {
- ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, {
- ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, {
- ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, {
- ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, {
- ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, {
- ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, {
- ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, {
- ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, {
- ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, {
- ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, {
- ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, {
- ERRDOS, 203, NT_STATUS_VARIABLE_NOT_FOUND}, {
- ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, {
- ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, {
- ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, {
- ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, {
- ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, {
- ERRDOS, 2401, NT_STATUS_FILES_OPEN}, {
- ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, {
- ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, {
- ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, {
- ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, {
- ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, {
- ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, {
- ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, {
- ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, {
- ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, {
- ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, {
- ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, {
- ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, {
- ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, {
- ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, {
- ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, {
- ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, {
- ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, {
- ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, {
- ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, {
- ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, {
- ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, {
- ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, {
- ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, {
- ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, {
- ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, {
- ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, {
- ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, {
- ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, {
- ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, {
- ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, {
- ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, {
- ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, {
- ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, {
- ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, {
- ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, {
- ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, {
- ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, {
- ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, {
- ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, {
- ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, {
- ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, {
- ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, {
- ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, {
- ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, {
- ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, {
- ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, {
- ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, {
- ERRDOS, 59, NT_STATUS_LINK_FAILED}, {
- ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, {
- ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, {
- ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, {
- ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, {
- ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, {
- ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, {
- ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, {
- ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, {
- ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, {
- ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, {
- ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, {
- ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, {
- ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, {
- ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, {
- ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, {
- ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, {
- ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, {
- ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, {
- ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, {
- ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, {
- ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, {
- ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, {
- ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, {
- ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, {
- ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, {
- ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, {
- ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, {
- ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, {
- ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, {
- ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, {
- ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, {
- ERRHRD, ERRgeneral, NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT}, {
- ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, {
- ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, {
- ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, {
- ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, {
- ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, {
- ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, {
- ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, {
- ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, {
- ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, {
- ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, {
- ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, {
- ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, {
- ERRDOS, 19, NT_STATUS_TOO_LATE}, {
- ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET},
-/* { This NT error code was 'sqashed'
- from NT_STATUS_NO_TRUST_SAM_ACCOUNT to
- NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */
- {
- ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, {
- ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, {
- ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, {
- ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, {
- ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, {
- ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, {
- ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, {
- ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, {
- ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, {
- ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, {
- ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, {
- ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, {
- ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, {
- ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, {
- ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, {
- ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
-/* { This NT error code was 'sqashed'
- from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE
- during the session setup } */
- {
- ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, {
- ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, {
- ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, {
- ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, {
- ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, {
- ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, {
- ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, {
- ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, {
- ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, {
- ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, {
- ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, {
- ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, {
- ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, {
- ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, {
- ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, {
- ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, {
- ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, {
- ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, {
- ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, {
- ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, {
- ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, {
- ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, {
- ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, {
- ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, {
- ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, {
- ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, {
- ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, {
- ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, {
- ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, {
- ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, {
- ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, {
- ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, {
- ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, {
- ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, {
- ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, {
- ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, {
- ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, {
- ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, {
- ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, {
- ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, {
- ERRHRD, ERRgeneral, NT_STATUS_RETRY}, {
- ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, {
- ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, {
- ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, {
- ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, {
- ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, {
- ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, {
- ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, {
- ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, {
- ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, {
- ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, {
- ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, {
- ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, {
- ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, {
- ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, {
- ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, {
- ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, {
- ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, {
- ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, {
- ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, {
- ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, {
- ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, {
- ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, {
- ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, {
- ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, {
- ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, {
- ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, {
- ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, {
- ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, {
- ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, {
- ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, {
- ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, {
- ERRSRV, 3, NT_STATUS_PATH_NOT_COVERED}, {
- ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, {
- ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, {
- ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, {
- ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, {
- ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, {
- ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, {
- ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, {
- ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, {
- ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, {
- ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, {
- ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, {
- ERRDOS, ErrTooManyLinks, NT_STATUS_TOO_MANY_LINKS}, {
- ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, {
- ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, {
- ERRDOS, 21, NT_STATUS_VOLUME_DISMOUNTED}, {
- ERRDOS, 161, NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT}, {
- ERRDOS, ERRnoaccess, NT_STATUS_ENCRYPTION_FAILED}, {
- ERRDOS, ERRnoaccess, NT_STATUS_DECRYPTION_FAILED}, {
- ERRHRD, ERRgeneral, NT_STATUS_RANGE_NOT_FOUND}, {
- ERRDOS, ERRnoaccess, NT_STATUS_NO_RECOVERY_POLICY}, {
- ERRDOS, ERRnoaccess, NT_STATUS_NO_EFS}, {
- ERRDOS, ERRnoaccess, NT_STATUS_WRONG_EFS}, {
- ERRDOS, ERRnoaccess, NT_STATUS_NO_USER_KEYS}, {
- ERRDOS, ERRbadfunc, NT_STATUS_VOLUME_NOT_UPGRADED}, {
- ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, {
- ERRDOS, ERRunknownlevel, NT_STATUS_OS2_INVALID_LEVEL}, {
- 0, 0, 0 }
+static const struct ntstatus_to_dos_err ntstatus_to_dos_map[] = {
+/*
+ * Automatically generated by the `gen_smb1_mapping` script,
+ * sorted by NT status code (ascending).
+ */
+#include "smb1_mapping_table.c"
};
-/*****************************************************************************
- Print an error message from the status code
- *****************************************************************************/
-static void
-cifs_print_status(__u32 status_code)
+static const struct ntstatus_to_dos_err *
+search_ntstatus_to_dos_map(__u32 ntstatus)
{
- int idx = 0;
-
- while (nt_errs[idx].nt_errstr != NULL) {
- if (nt_errs[idx].nt_errcode == status_code) {
- pr_notice("Status code returned 0x%08x %s\n",
- status_code, nt_errs[idx].nt_errstr);
- return;
- }
- idx++;
- }
- return;
+ return __inline_bsearch(&ntstatus, ntstatus_to_dos_map,
+ ARRAY_SIZE(ntstatus_to_dos_map),
+ sizeof(struct ntstatus_to_dos_err),
+ ntstatus_to_dos_cmp);
}
+static const struct smb_to_posix_error *
+search_mapping_table_ERRDOS(__u16 smb_err)
+{
+ return __inline_bsearch(&smb_err, mapping_table_ERRDOS,
+ ARRAY_SIZE(mapping_table_ERRDOS),
+ sizeof(struct smb_to_posix_error),
+ smb1_posix_error_cmp);
+}
-static void
-ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode)
+static const struct smb_to_posix_error *
+search_mapping_table_ERRSRV(__u16 smb_err)
{
- int i;
- if (ntstatus == 0) {
- *eclass = 0;
- *ecode = 0;
- return;
- }
- for (i = 0; ntstatus_to_dos_map[i].ntstatus; i++) {
- if (ntstatus == ntstatus_to_dos_map[i].ntstatus) {
- *eclass = ntstatus_to_dos_map[i].dos_class;
- *ecode = ntstatus_to_dos_map[i].dos_code;
- return;
- }
- }
- *eclass = ERRHRD;
- *ecode = ERRgeneral;
+ return __inline_bsearch(&smb_err, mapping_table_ERRSRV,
+ ARRAY_SIZE(mapping_table_ERRSRV),
+ sizeof(struct smb_to_posix_error),
+ smb1_posix_error_cmp);
}
int
map_smb_to_linux_error(char *buf, bool logErr)
{
struct smb_hdr *smb = (struct smb_hdr *)buf;
- unsigned int i;
int rc = -EIO; /* if transport error smb error may not be set */
__u8 smberrclass;
__u16 smberrcode;
+ const struct smb_to_posix_error *err_map = NULL;
/* BB if NT Status codes - map NT BB */
@@ -720,11 +118,20 @@ map_smb_to_linux_error(char *buf, bool logErr)
/* translate the newer STATUS codes to old style SMB errors
* and then to POSIX errors */
__u32 err = le32_to_cpu(smb->Status.CifsError);
- if (logErr && (err != (NT_STATUS_MORE_PROCESSING_REQUIRED)))
- cifs_print_status(err);
- else if (cifsFYI & CIFS_RC)
- cifs_print_status(err);
- ntstatus_to_dos(err, &smberrclass, &smberrcode);
+ const struct ntstatus_to_dos_err *map = search_ntstatus_to_dos_map(err);
+
+ if (map) {
+ if ((logErr && err != NT_STATUS_MORE_PROCESSING_REQUIRED) ||
+ (cifsFYI & CIFS_RC))
+ pr_notice("Status code returned 0x%08x %s\n",
+ map->ntstatus, map->nt_errstr);
+
+ smberrclass = map->dos_class;
+ smberrcode = map->dos_code;
+ } else {
+ smberrclass = ERRHRD;
+ smberrcode = ERRgeneral;
+ }
} else {
smberrclass = smb->Status.DosError.ErrorClass;
smberrcode = le16_to_cpu(smb->Status.DosError.Error);
@@ -732,38 +139,16 @@ map_smb_to_linux_error(char *buf, bool logErr)
/* old style errors */
- /* DOS class smb error codes - map DOS */
if (smberrclass == ERRDOS) {
+ /* DOS class smb error codes - map DOS */
/* 1 byte field no need to byte reverse */
- for (i = 0;
- i <
- sizeof(mapping_table_ERRDOS) /
- sizeof(struct smb_to_posix_error); i++) {
- if (mapping_table_ERRDOS[i].smb_err == 0)
- break;
- else if (mapping_table_ERRDOS[i].smb_err ==
- smberrcode) {
- rc = mapping_table_ERRDOS[i].posix_code;
- break;
- }
- /* else try next error mapping one to see if match */
- }
+ err_map = search_mapping_table_ERRDOS(smberrcode);
} else if (smberrclass == ERRSRV) {
/* server class of error codes */
- for (i = 0;
- i <
- sizeof(mapping_table_ERRSRV) /
- sizeof(struct smb_to_posix_error); i++) {
- if (mapping_table_ERRSRV[i].smb_err == 0)
- break;
- else if (mapping_table_ERRSRV[i].smb_err ==
- smberrcode) {
- rc = mapping_table_ERRSRV[i].posix_code;
- break;
- }
- /* else try next error mapping to see if match */
- }
+ err_map = search_mapping_table_ERRSRV(smberrcode);
}
+ if (err_map)
+ rc = err_map->posix_code;
/* else ERRHRD class errors or junk - return EIO */
/* special cases for NT status codes which cannot be translated to DOS codes */
@@ -811,3 +196,87 @@ map_and_check_smb_error(struct TCP_Server_Info *server,
return rc;
}
+
+#define DEFINE_CHECK_SORT_FUNC(__array, __field) \
+static int __init __array ## _is_sorted(void) \
+{ \
+ unsigned int i; \
+ \
+ /* Check whether the array is sorted in ascending order */ \
+ for (i = 1; i < ARRAY_SIZE(__array); i++) { \
+ if (__array[i].__field >= \
+ __array[i - 1].__field) \
+ continue; \
+ \
+ pr_err(#__array " array order is incorrect\n"); \
+ return -EINVAL; \
+ } \
+ \
+ return 0; \
+}
+
+/* ntstatus_to_dos_map_is_sorted */
+DEFINE_CHECK_SORT_FUNC(ntstatus_to_dos_map, ntstatus);
+/* mapping_table_ERRDOS_is_sorted */
+DEFINE_CHECK_SORT_FUNC(mapping_table_ERRDOS, smb_err);
+/* mapping_table_ERRSRV_is_sorted */
+DEFINE_CHECK_SORT_FUNC(mapping_table_ERRSRV, smb_err);
+
+int __init smb1_init_maperror(void)
+{
+ int rc;
+
+ rc = ntstatus_to_dos_map_is_sorted();
+ if (rc)
+ return rc;
+
+ rc = mapping_table_ERRDOS_is_sorted();
+ if (rc)
+ return rc;
+
+ return mapping_table_ERRSRV_is_sorted();
+}
+
+#if IS_ENABLED(CONFIG_SMB1_KUNIT_TESTS)
+const struct ntstatus_to_dos_err *
+search_ntstatus_to_dos_map_test(__u32 ntstatus)
+{
+ return search_ntstatus_to_dos_map(ntstatus);
+}
+EXPORT_SYMBOL_IF_KUNIT(search_ntstatus_to_dos_map_test);
+
+const struct ntstatus_to_dos_err *
+ntstatus_to_dos_map_test = ntstatus_to_dos_map;
+EXPORT_SYMBOL_IF_KUNIT(ntstatus_to_dos_map_test);
+
+unsigned int ntstatus_to_dos_num = ARRAY_SIZE(ntstatus_to_dos_map);
+EXPORT_SYMBOL_IF_KUNIT(ntstatus_to_dos_num);
+
+const struct smb_to_posix_error *
+search_mapping_table_ERRDOS_test(__u16 smb_err)
+{
+ return search_mapping_table_ERRDOS(smb_err);
+}
+EXPORT_SYMBOL_IF_KUNIT(search_mapping_table_ERRDOS_test);
+
+const struct smb_to_posix_error *
+mapping_table_ERRDOS_test = mapping_table_ERRDOS;
+EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRDOS_test);
+
+unsigned int mapping_table_ERRDOS_num = ARRAY_SIZE(mapping_table_ERRDOS);
+EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRDOS_num);
+
+const struct smb_to_posix_error *
+search_mapping_table_ERRSRV_test(__u16 smb_err)
+{
+ return search_mapping_table_ERRSRV(smb_err);
+}
+EXPORT_SYMBOL_IF_KUNIT(search_mapping_table_ERRSRV_test);
+
+const struct smb_to_posix_error *
+mapping_table_ERRSRV_test = mapping_table_ERRSRV;
+EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRSRV_test);
+
+unsigned int mapping_table_ERRSRV_num = ARRAY_SIZE(mapping_table_ERRSRV);
+EXPORT_SYMBOL_IF_KUNIT(mapping_table_ERRSRV_num);
+#endif
diff --git a/fs/smb/client/smb1maperror_test.c b/fs/smb/client/smb1maperror_test.c
new file mode 100644
index 000000000000..903c46f71291
--- /dev/null
+++ b/fs/smb/client/smb1maperror_test.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * KUnit tests of SMB1 maperror
+ *
+ * Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
+ * Author(s): Youling Tang <tangyouling@kylinos.cn>
+ * ChenXiaoSong <chenxiaosong@kylinos.cn>
+ *
+ */
+
+#include <kunit/test.h>
+#include "smb1proto.h"
+#include "nterr.h"
+#include "smberr.h"
+
+#define DEFINE_CHECK_SEARCH_FUNC(__struct_name, __field, \
+ __array, __num) \
+static void check_search_ ## __array(struct kunit *test) \
+{ \
+ unsigned int i; \
+ const struct __struct_name *expect, *result; \
+ \
+ for (i = 0; i < __num; i++) { \
+ expect = &__array ## _test[i]; \
+ result = search_ ## __array ## _test(expect->__field); \
+ KUNIT_ASSERT_NOT_NULL(test, result); \
+ test_cmp_ ## __struct_name(test, expect, result); \
+ } \
+}
+
+static void
+test_cmp_ntstatus_to_dos_err(struct kunit *test,
+ const struct ntstatus_to_dos_err *expect,
+ const struct ntstatus_to_dos_err *result)
+{
+ KUNIT_EXPECT_EQ(test, expect->dos_class, result->dos_class);
+ KUNIT_EXPECT_EQ(test, expect->dos_code, result->dos_code);
+ KUNIT_EXPECT_EQ(test, expect->ntstatus, result->ntstatus);
+ KUNIT_EXPECT_STREQ(test, expect->nt_errstr, result->nt_errstr);
+}
+
+static void
+test_cmp_smb_to_posix_error(struct kunit *test,
+ const struct smb_to_posix_error *expect,
+ const struct smb_to_posix_error *result)
+{
+ KUNIT_EXPECT_EQ(test, expect->smb_err, result->smb_err);
+ KUNIT_EXPECT_EQ(test, expect->posix_code, result->posix_code);
+}
+
+/* check_search_ntstatus_to_dos_map */
+DEFINE_CHECK_SEARCH_FUNC(ntstatus_to_dos_err, ntstatus, ntstatus_to_dos_map,
+ ntstatus_to_dos_num);
+/* check_search_mapping_table_ERRDOS */
+DEFINE_CHECK_SEARCH_FUNC(smb_to_posix_error, smb_err, mapping_table_ERRDOS,
+ mapping_table_ERRDOS_num);
+/* check_search_mapping_table_ERRSRV */
+DEFINE_CHECK_SEARCH_FUNC(smb_to_posix_error, smb_err, mapping_table_ERRSRV,
+ mapping_table_ERRSRV_num);
+
+static struct kunit_case maperror_test_cases[] = {
+ KUNIT_CASE(check_search_ntstatus_to_dos_map),
+ KUNIT_CASE(check_search_mapping_table_ERRDOS),
+ KUNIT_CASE(check_search_mapping_table_ERRSRV),
+ {}
+};
+
+static struct kunit_suite maperror_suite = {
+ .name = "smb1_maperror",
+ .test_cases = maperror_test_cases,
+};
+
+kunit_test_suite(maperror_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit tests of SMB1 maperror");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
diff --git a/fs/smb/client/smb1misc.c b/fs/smb/client/smb1misc.c
index ba56023010d8..cdfbbff24b72 100644
--- a/fs/smb/client/smb1misc.c
+++ b/fs/smb/client/smb1misc.c
@@ -80,7 +80,8 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
(struct smb_com_transaction_change_notify_rsp *)buf;
struct file_notify_information *pnotify;
__u32 data_offset = 0;
- size_t len = srv->total_read - srv->pdu_size;
+ /* total_read excludes the RFC1002 preamble */
+ size_t len = srv->total_read;
if (get_bcc(buf) > sizeof(struct file_notify_information)) {
data_offset = le32_to_cpu(pSMBr->DataOffset);
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index 9643eca0cb70..3ac4126267f6 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -49,7 +49,6 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
__u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
- unsigned int sbflags;
cifs_dbg(FYI, "unix caps which server supports %lld\n", cap);
/*
@@ -76,29 +75,27 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)
cifs_dbg(VFS, "per-share encryption not supported yet\n");
- if (cifs_sb)
- sbflags = cifs_sb_flags(cifs_sb);
-
cap &= CIFS_UNIX_CAP_MASK;
if (ctx && ctx->no_psx_acl)
cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
else if (CIFS_UNIX_POSIX_ACL_CAP & cap) {
cifs_dbg(FYI, "negotiated posix acl support\n");
- if (cifs_sb)
- sbflags |= CIFS_MOUNT_POSIXACL;
+ if (cifs_sb) {
+ atomic_or(CIFS_MOUNT_POSIXACL,
+ &cifs_sb->mnt_cifs_flags);
+ }
}
if (ctx && ctx->posix_paths == 0)
cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
cifs_dbg(FYI, "negotiate posix pathnames\n");
- if (cifs_sb)
- sbflags |= CIFS_MOUNT_POSIX_PATHS;
+ if (cifs_sb) {
+ atomic_or(CIFS_MOUNT_POSIX_PATHS,
+ &cifs_sb->mnt_cifs_flags);
+ }
}
- if (cifs_sb)
- atomic_set(&cifs_sb->mnt_cifs_flags, sbflags);
-
cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap);
#ifdef CONFIG_CIFS_DEBUG2
if (cap & CIFS_UNIX_FCNTL_CAP)
@@ -508,21 +505,27 @@ static int
cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path)
{
- int rc;
- FILE_ALL_INFO *file_info;
+ int rc = -EOPNOTSUPP;
+ FILE_ALL_INFO file_info;
- file_info = kmalloc_obj(FILE_ALL_INFO);
- if (file_info == NULL)
- return -ENOMEM;
+ if (tcon->ses->capabilities & CAP_NT_SMBS)
+ rc = CIFSSMBQPathInfo(xid, tcon, full_path, &file_info,
+ 0 /* not legacy */, cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
- rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info,
- 0 /* not legacy */, cifs_sb->local_nls,
- cifs_remap(cifs_sb));
+ /*
+ * Non-UNICODE variant of fallback functions below expands wildcards,
+ * so they cannot be used for querying paths with wildcard characters.
+ * Therefore for such paths returns -ENOENT as they cannot exist.
+ */
+ if ((rc == -EOPNOTSUPP || rc == -EINVAL) &&
+ !(tcon->ses->capabilities & CAP_UNICODE) &&
+ strpbrk(full_path, "*?\"><"))
+ rc = -ENOENT;
if (rc == -EOPNOTSUPP || rc == -EINVAL)
- rc = SMBQueryInformation(xid, tcon, full_path, file_info,
+ rc = SMBQueryInformation(xid, tcon, full_path, &file_info,
cifs_sb->local_nls, cifs_remap(cifs_sb));
- kfree(file_info);
return rc;
}
@@ -539,6 +542,7 @@ static int cifs_query_path_info(const unsigned int xid,
data->reparse_point = false;
data->adjust_tz = false;
+ data->unknown_nlink = false;
/*
* First try CIFSSMBQPathInfo() function which returns more info
@@ -605,6 +609,7 @@ static int cifs_query_path_info(const unsigned int xid,
fi.EASize = di->EaSize;
}
fi.NumberOfLinks = cpu_to_le32(1);
+ data->unknown_nlink = true;
fi.DeletePending = 0;
fi.Directory = !!(le32_to_cpu(fi.Attributes) & ATTR_DIRECTORY);
cifs_buf_release(search_info.ntwrk_buf_start);
@@ -627,6 +632,8 @@ static int cifs_query_path_info(const unsigned int xid,
rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls,
cifs_remap(cifs_sb));
data->adjust_tz = true;
+ if (!rc)
+ data->unknown_nlink = true;
} else if ((rc == -EOPNOTSUPP || rc == -EINVAL) && non_unicode_wildcard) {
/* Path with non-UNICODE wildcard character cannot exist. */
rc = -ENOENT;
@@ -714,7 +721,7 @@ static int cifs_query_path_info(const unsigned int xid,
ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE);
memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1);
data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
- SMB2_WSL_XATTR_MODE_SIZE, 4);
+ SMB2_WSL_XATTR_DEV_SIZE, 4);
rc = 0;
} else if (rc >= 0) {
/* It is an error if EA $LXDEV has wrong size. */
@@ -890,8 +897,10 @@ static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms
else
rc = CIFS_open(xid, oparms, oplock, &fi);
- if (!rc && data)
+ if (!rc && data) {
move_cifs_info_to_smb2(&data->fi, &fi);
+ data->unknown_nlink = true;
+ }
return rc;
}
@@ -952,7 +961,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,
struct cifs_open_parms oparms;
struct cifsFileInfo *open_file;
FILE_BASIC_INFO new_buf;
- struct cifs_open_info_data query_data;
+ struct cifs_open_info_data query_data = {};
__le64 write_time = buf->LastWriteTime;
struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
@@ -960,7 +969,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,
struct cifs_tcon *tcon;
/* if the file is already open for write, just use that fileid */
- open_file = find_writable_file(cinode, FIND_WR_FSUID_ONLY);
+ open_file = find_writable_file(cinode, FIND_FSUID_ONLY);
if (open_file) {
fid.netfid = open_file->fid.netfid;
@@ -1113,9 +1122,10 @@ out:
static int
cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
- struct cifsFileInfo *cfile)
+ struct cifsFileInfo *cfile, __u16 compression_state)
{
- return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid);
+ return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid,
+ compression_state);
}
static int
diff --git a/fs/smb/client/smb1pdu.h b/fs/smb/client/smb1pdu.h
index 97f7e1244a8b..0870949144ab 100644
--- a/fs/smb/client/smb1pdu.h
+++ b/fs/smb/client/smb1pdu.h
@@ -1211,11 +1211,6 @@ typedef struct smb_com_transaction_compr_ioctl_req {
__le16 compression_state; /* See below for valid flags */
} __packed TRANSACT_COMPR_IOCTL_REQ;
-/* compression state flags */
-#define COMPRESSION_FORMAT_NONE 0x0000
-#define COMPRESSION_FORMAT_DEFAULT 0x0001
-#define COMPRESSION_FORMAT_LZNT1 0x0002
-
typedef struct smb_com_transaction_ioctl_rsp {
struct smb_hdr hdr; /* wct = 19 */
__u8 Reserved[3];
@@ -2061,15 +2056,6 @@ typedef struct {
__le32 EASize;
} __packed FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */
-typedef struct {
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le32 Attributes;
- __u32 Pad;
-} __packed FILE_BASIC_INFO; /* size info, level 0x101 */
-
struct file_allocation_info {
__le64 AllocationSize; /* Note old Samba srvr rounds this up too much */
} __packed; /* size used on disk, for level 0x103 for set, 0x105 for query */
diff --git a/fs/smb/client/smb1proto.h b/fs/smb/client/smb1proto.h
index 42569bbcf6fd..80eaeb3dd2ec 100644
--- a/fs/smb/client/smb1proto.h
+++ b/fs/smb/client/smb1proto.h
@@ -117,7 +117,7 @@ struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
struct kvec *reparse_iov,
struct kvec *xattr_iov);
int CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
- __u16 fid);
+ __u16 fid, __u16 compression_state);
int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon,
const unsigned char *searchName, struct posix_acl **acl,
const int acl_type, const struct nls_table *nls_codepage,
@@ -234,8 +234,23 @@ int cifs_verify_signature(struct smb_rqst *rqst,
* smb1maperror.c
*/
int map_smb_to_linux_error(char *buf, bool logErr);
+int smb1_init_maperror(void);
int map_and_check_smb_error(struct TCP_Server_Info *server,
struct mid_q_entry *mid, bool logErr);
+#if IS_ENABLED(CONFIG_SMB1_KUNIT_TESTS)
+extern const struct ntstatus_to_dos_err *ntstatus_to_dos_map_test;
+extern unsigned int ntstatus_to_dos_num;
+const struct ntstatus_to_dos_err *
+search_ntstatus_to_dos_map_test(__u32 ntstatus);
+extern const struct smb_to_posix_error *mapping_table_ERRDOS_test;
+extern unsigned int mapping_table_ERRDOS_num;
+const struct smb_to_posix_error *
+search_mapping_table_ERRDOS_test(__u16 smb_err);
+extern const struct smb_to_posix_error *mapping_table_ERRSRV_test;
+extern unsigned int mapping_table_ERRSRV_num;
+const struct smb_to_posix_error *
+search_mapping_table_ERRSRV_test(__u16 smb_err);
+#endif
/*
* smb1misc.c
diff --git a/fs/smb/client/smb1transport.c b/fs/smb/client/smb1transport.c
index 38d6d5538b96..42e95cc1bd89 100644
--- a/fs/smb/client/smb1transport.c
+++ b/fs/smb/client/smb1transport.c
@@ -260,9 +260,23 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
goto out;
if (out_buf) {
- *pbytes_returned = resp_iov.iov_len;
- if (resp_iov.iov_len)
- memcpy(out_buf, resp_iov.iov_base, resp_iov.iov_len);
+ /* Use smbCalcSize() for both single- and multi-part T2 responses,
+ * both here and in coalesce_t2().
+ */
+ unsigned int copy_len;
+ if (WARN_ON_ONCE(!resp_iov.iov_base)) {
+ rc = -EIO;
+ goto out;
+ }
+ copy_len = smbCalcSize(resp_iov.iov_base);
+ if (copy_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cifs_dbg(VFS, "response size %u exceeds buffer\n",
+ copy_len);
+ rc = -ENOBUFS;
+ goto out;
+ }
+ *pbytes_returned = copy_len;
+ memcpy(out_buf, resp_iov.iov_base, copy_len);
}
out:
@@ -361,12 +375,31 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len)
data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +
get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
- /* validate target area */
data_area_of_src = (char *)&pSMBs->hdr.Protocol +
get_unaligned_le16(&pSMBs->t2_rsp.DataOffset);
data_area_of_tgt += total_in_tgt;
+ /*
+ * DataOffset fields are server-supplied and not validated against
+ * buffer bounds; check both data pointers before mutating the
+ * target header.
+ */
+ if (data_area_of_tgt < (char *)target_hdr +
+ sizeof(struct smb_t2_rsp) + sizeof(__le16) ||
+ data_area_of_tgt + total_in_src >
+ (char *)target_hdr + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cifs_dbg(VFS, "%s: target data area out of bounds\n", __func__);
+ return -EPROTO;
+ }
+ if (data_area_of_src < second_buf +
+ sizeof(struct smb_t2_rsp) + sizeof(__le16) ||
+ data_area_of_src + total_in_src >
+ second_buf + smbCalcSize((struct smb_hdr *)second_buf)) {
+ cifs_dbg(VFS, "%s: secondary data area out of bounds\n", __func__);
+ return -EPROTO;
+ }
+
total_in_tgt += total_in_src;
/* is the result too big for the field? */
if (total_in_tgt > USHRT_MAX) {
@@ -386,11 +419,13 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len)
}
put_bcc(byte_count, target_hdr);
- byte_count = *pdu_len;
- byte_count += total_in_src;
+ /* use smbCalcSize() rather than *pdu_len: the demux loop resets
+ * *pdu_len to each secondary's pdu_length, making it unreliable.
+ */
+ byte_count = smbCalcSize(target_hdr);
/* don't allow buffer to overflow */
if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
- cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n",
+ cifs_dbg(FYI, "coalesced size exceeds buffer size (%u)\n",
byte_count);
return -ENOBUFS;
}
@@ -414,10 +449,18 @@ bool
cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
char *buf, int malformed)
{
- if (malformed)
- return false;
- if (check2ndT2(buf) <= 0)
+ if (malformed || check2ndT2(buf) <= 0) {
+ /* mid->multiRsp blocks the server buf detach in handle_mid();
+ * returning false here would leak resp_buf and leave a dangling
+ * server->smallbuf/bigbuf after the user thread frees resp_buf.
+ */
+ if (mid->multiRsp) {
+ mid->multiEnd = true;
+ dequeue_mid(server, mid, true);
+ return true;
+ }
return false;
+ }
mid->multiRsp = true;
if (mid->resp_buf) {
/* merge response - fix up 1st*/
@@ -460,7 +503,7 @@ check_smb_hdr(struct smb_hdr *smb)
return 0;
/*
- * Windows NT server returns error resposne (e.g. STATUS_DELETE_PENDING
+ * Windows NT server returns error response (e.g. STATUS_DELETE_PENDING
* or STATUS_OBJECT_NAME_NOT_FOUND or ERRDOS/ERRbadfile or any other)
* for some TRANS2 requests without the RESPONSE flag set in header.
*/
diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
index ed651c946251..fb2fccbe8667 100644
--- a/fs/smb/client/smb2file.c
+++ b/fs/smb/client/smb2file.c
@@ -27,10 +27,24 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
{
struct smb2_err_rsp *err = iov->iov_base;
struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL);
+ u8 *end = (u8 *)err + iov->iov_len;
u32 len;
+ /*
+ * Per [MS-SMB2] section 2.2.2, a STATUS_STOPPED_ON_SYMLINK response has to
+ * carry a Symbolic Link Error Response, so ByteCount cannot be zero. Some
+ * servers (e.g. the macOS built-in SMB server) violate this and return an
+ * empty error response, with both ErrorContextCount and ByteCount set to
+ * zero, i.e. without the symlink target. Detect this and return -ENODATA
+ * so that callers can tell "server did not send the target" apart from a
+ * malformed response, and retrieve the target with FSCTL_GET_REPARSE_POINT
+ * instead.
+ */
+ if (!err->ErrorContextCount && !le32_to_cpu(err->ByteCount))
+ return ERR_PTR(-ENODATA);
+
if (err->ErrorContextCount) {
- struct smb2_error_context_rsp *p, *end;
+ struct smb2_error_context_rsp *p;
len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp,
ErrorContextData) +
@@ -39,8 +53,7 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
return ERR_PTR(-EINVAL);
p = (struct smb2_error_context_rsp *)err->ErrorData;
- end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len);
- do {
+ while ((u8 *)p + sizeof(*p) <= end) {
if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) {
sym = (struct smb2_symlink_err_rsp *)p->ErrorContextData;
break;
@@ -48,16 +61,24 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n",
__func__, le32_to_cpu(p->ErrorId));
- len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
+ len = le32_to_cpu(p->ErrorDataLength);
+ if (len > end - ((u8 *)p + sizeof(*p)))
+ return ERR_PTR(-EINVAL);
+ len = ALIGN(len, 8);
+ if (len > end - ((u8 *)p + sizeof(*p)))
+ return ERR_PTR(-EINVAL);
+
p = (struct smb2_error_context_rsp *)(p->ErrorContextData + len);
- } while (p < end);
+ }
} else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) &&
iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) {
sym = (struct smb2_symlink_err_rsp *)err->ErrorData;
}
- if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
- le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
+ if (!IS_ERR(sym) &&
+ ((u8 *)sym + sizeof(*sym) > end ||
+ le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
+ le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
sym = ERR_PTR(-EINVAL);
return sym;
@@ -128,8 +149,10 @@ int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec
print_len = le16_to_cpu(sym->PrintNameLength);
print_offs = le16_to_cpu(sym->PrintNameOffset);
- if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len ||
- iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len)
+ if ((char *)sym->PathBuffer + sub_offs + sub_len >
+ (char *)iov->iov_base + iov->iov_len ||
+ (char *)sym->PathBuffer + print_offs + print_len >
+ (char *)iov->iov_base + iov->iov_len)
return -EINVAL;
return smb2_parse_native_symlink(path,
@@ -147,8 +170,6 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
__le16 *smb2_path;
__u8 smb2_oplock;
struct cifs_open_info_data *data = buf;
- struct smb2_file_all_info file_info = {};
- struct smb2_file_all_info *smb2_data = data ? &file_info : NULL;
struct kvec err_iov = {};
int err_buftype = CIFS_NO_BUFFER;
struct cifs_fid *fid = oparms->fid;
@@ -175,14 +196,14 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
}
smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
- rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
+ rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data, NULL, &err_iov,
&err_buftype);
if (rc == -EACCES && retry_without_read_attributes) {
free_rsp_buf(err_buftype, err_iov.iov_base);
memset(&err_iov, 0, sizeof(err_iov));
err_buftype = CIFS_NO_BUFFER;
oparms->desired_access &= ~FILE_READ_ATTRIBUTES;
- rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
+ rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data, NULL, &err_iov,
&err_buftype);
}
if (rc && data) {
@@ -194,10 +215,18 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov,
oparms->path,
&data->symlink_target);
+ /*
+ * If smb2_parse_symlink_response returned -ENODATA then the
+ * symlink_target was not sent. Treat this as if the SMB2_open()
+ * failed with STATUS_IO_REPARSE_TAG_NOT_HANDLED status, which is
+ * indicated by the -EIO errno.
+ */
+ if (rc == -ENODATA)
+ rc = -EIO;
if (!rc) {
- memset(smb2_data, 0, sizeof(*smb2_data));
+ memset(&data->fi, 0, sizeof(data->fi));
oparms->create_options |= OPEN_REPARSE_POINT;
- rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data,
+ rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, data,
NULL, NULL, NULL);
oparms->create_options &= ~OPEN_REPARSE_POINT;
}
@@ -231,23 +260,22 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
rc = 0;
}
- if (smb2_data) {
+ if (data) {
/* if open response does not have IndexNumber field - get it */
- if (smb2_data->IndexNumber == 0) {
+ if (data->fi.IndexNumber == 0) {
rc = SMB2_get_srv_num(xid, oparms->tcon,
fid->persistent_fid,
fid->volatile_fid,
- &smb2_data->IndexNumber);
+ &data->fi.IndexNumber);
if (rc) {
/*
* let get_inode_info disable server inode
* numbers
*/
- smb2_data->IndexNumber = 0;
+ data->fi.IndexNumber = 0;
rc = 0;
}
}
- memcpy(&data->fi, smb2_data, sizeof(data->fi));
}
*oplock = smb2_oplock;
diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h
index e56e4d402f13..19da74b1edab 100644
--- a/fs/smb/client/smb2glob.h
+++ b/fs/smb/client/smb2glob.h
@@ -46,4 +46,16 @@ enum smb2_compound_ops {
#define END_OF_CHAIN 4
#define RELATED_REQUEST 8
+/*
+ *****************************************************************
+ * Struct definitions go here
+ *****************************************************************
+ */
+
+struct status_to_posix_error {
+ __u32 smb2_status;
+ int posix_error;
+ char *status_string;
+};
+
#endif /* _SMB2_GLOB_H */
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 195a38fd61e8..13fe8e3b48f3 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -22,6 +22,7 @@
#include "smb2glob.h"
#include "smb2proto.h"
#include "cached_dir.h"
+#include "reparse.h"
#include "../common/smb2status.h"
#include "../common/smbfsctl.h"
@@ -40,9 +41,11 @@ static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)
buf = (struct reparse_data_buffer *)((u8 *)io + off);
len = sizeof(*buf);
- rdlen = le16_to_cpu(buf->ReparseDataLength);
+ if (count < len)
+ return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, 0));
- if (count < len || count < rdlen + len)
+ rdlen = le16_to_cpu(buf->ReparseDataLength);
+ if (count < rdlen + len)
return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, rdlen));
return buf;
}
@@ -74,6 +77,17 @@ static int parse_posix_sids(struct cifs_open_info_data *data,
sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len;
sidsbuf_end = sidsbuf + out_len - qi_len;
+ if (sidsbuf_end < sidsbuf) {
+ cifs_dbg(VFS, "%s: server-supplied out_len %u caused pointer wraparound\n",
+ __func__, out_len);
+ return -EINVAL;
+ }
+ if (sidsbuf_end > (u8 *)rsp_iov->iov_base + rsp_iov->iov_len) {
+ cifs_dbg(VFS, "%s: server-supplied out_len %u overruns iov by %td bytes\n",
+ __func__, out_len,
+ sidsbuf_end - ((u8 *)rsp_iov->iov_base + rsp_iov->iov_len));
+ return -EINVAL;
+ }
owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end);
if (owner_len == -1)
@@ -111,7 +125,7 @@ static int check_wsl_eas(struct kvec *rsp_iov)
u32 outlen, next;
u16 vlen;
u8 nlen;
- u8 *end;
+ u8 *ea_end, *iov_end;
outlen = le32_to_cpu(rsp->OutputBufferLength);
if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||
@@ -120,15 +134,19 @@ static int check_wsl_eas(struct kvec *rsp_iov)
ea = (void *)((u8 *)rsp_iov->iov_base +
le16_to_cpu(rsp->OutputBufferOffset));
- end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
+ ea_end = (u8 *)ea + outlen;
+ iov_end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
+ if (ea_end > iov_end)
+ return -EINVAL;
+
for (;;) {
- if ((u8 *)ea > end - sizeof(*ea))
+ if ((u8 *)ea > ea_end - sizeof(*ea))
return -EINVAL;
nlen = ea->ea_name_length;
vlen = le16_to_cpu(ea->ea_value_length);
if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
- (u8 *)ea + nlen + 1 + vlen > end)
+ (u8 *)ea->ea_data + nlen + 1 + vlen > ea_end)
return -EINVAL;
switch (vlen) {
@@ -165,6 +183,27 @@ static int check_wsl_eas(struct kvec *rsp_iov)
}
/*
+ * If @cfile is NULL, then need to account for trailing CLOSE request in the
+ * compound chain.
+ */
+static void set_next_compound(struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile,
+ int i, int num_cmds,
+ struct smb_rqst *rqst, int *num_rqst)
+{
+ int k = !cfile ? 1 : 0;
+
+ if (i + 1 < num_cmds + k)
+ smb2_set_next_command(tcon, &rqst[*num_rqst]);
+ if (i + k > 0)
+ smb2_set_related(&rqst[*num_rqst]);
+ (*num_rqst)++;
+}
+
+#define COMP_PID(cfile) ((cfile) ? (cfile)->fid.persistent_fid : COMPOUND_FID)
+#define COMP_VID(cfile) ((cfile) ? (cfile)->fid.volatile_fid : COMPOUND_FID)
+
+/*
* note: If cfile is passed, the reference to it is dropped here.
* So make sure that you do not reuse cfile after return from this func.
*
@@ -209,7 +248,7 @@ replay_again:
num_rqst = 0;
server = cifs_pick_channel(ses);
- vars = kzalloc_obj(*vars, GFP_ATOMIC);
+ vars = kzalloc_obj(*vars);
if (vars == NULL) {
rc = -ENOMEM;
goto out;
@@ -284,32 +323,16 @@ replay_again:
rqst[num_rqst].rq_iov = &vars->qi_iov;
rqst[num_rqst].rq_nvec = 1;
- if (cfile) {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FILE_ALL_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb2_file_all_info) +
- PATH_MAX * 2, 0, NULL);
- } else {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- FILE_ALL_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb2_file_all_info) +
- PATH_MAX * 2, 0, NULL);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_query_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ FILE_ALL_INFORMATION,
+ SMB2_O_INFO_FILE, 0,
+ sizeof(struct smb2_file_all_info) +
+ PATH_MAX * 2, 0, NULL);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_query_info_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -317,35 +340,18 @@ replay_again:
rqst[num_rqst].rq_iov = &vars->qi_iov;
rqst[num_rqst].rq_nvec = 1;
- if (cfile) {
- /* TBD: fix following to allow for longer SIDs */
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- SMB_FIND_FILE_POSIX_INFO,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb311_posix_qinfo *) +
- (PATH_MAX * 2) +
- (sizeof(struct smb_sid) * 2), 0, NULL);
- } else {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- SMB_FIND_FILE_POSIX_INFO,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb311_posix_qinfo *) +
- (PATH_MAX * 2) +
- (sizeof(struct smb_sid) * 2), 0, NULL);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ /* TBD: fix following to allow for longer SIDs */
+ rc = SMB2_query_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ SMB_FIND_FILE_POSIX_INFO,
+ SMB2_O_INFO_FILE, 0,
+ sizeof(struct smb311_posix_qinfo) +
+ (PATH_MAX * 2) +
+ (sizeof(struct smb_sid) * 2), 0, NULL);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_posix_query_info_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -363,32 +369,15 @@ replay_again:
size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
data[0] = &delete_pending[0];
- if (cfile) {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- current->tgid,
- FILE_DISPOSITION_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- data, size);
- } else {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- current->tgid,
- FILE_DISPOSITION_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- data, size);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_set_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ current->tgid, FILE_DISPOSITION_INFORMATION,
+ SMB2_O_INFO_FILE, 0,
+ data, size);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_unlink_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_EOF:
@@ -398,32 +387,15 @@ replay_again:
size[0] = in_iov[i].iov_len;
data[0] = in_iov[i].iov_base;
- if (cfile) {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- current->tgid,
- FILE_END_OF_FILE_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- data, size);
- } else {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- current->tgid,
- FILE_END_OF_FILE_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- data, size);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_set_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ current->tgid, FILE_END_OF_FILE_INFORMATION,
+ SMB2_O_INFO_FILE, 0,
+ data, size);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_set_eof_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_INFO:
@@ -433,28 +405,14 @@ replay_again:
size[0] = in_iov[i].iov_len;
data[0] = in_iov[i].iov_base;
- if (cfile) {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid, current->tgid,
- FILE_BASIC_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- } else {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID, current->tgid,
- FILE_BASIC_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_set_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ current->tgid, FILE_BASIC_INFORMATION,
+ SMB2_O_INFO_FILE, 0, data, size);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_set_info_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -474,31 +432,19 @@ replay_again:
size[1] = len + 2 /* null */;
data[1] = in_iov[i].iov_base;
- if (cfile) {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- current->tgid, FILE_RENAME_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- } else {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID, COMPOUND_FID,
- current->tgid, FILE_RENAME_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_set_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ current->tgid, FILE_RENAME_INFORMATION,
+ SMB2_O_INFO_FILE, 0, data, size);
+
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_rename_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_HARDLINK:
- rqst[num_rqst].rq_iov = &vars->si_iov[0];
+ rqst[num_rqst].rq_iov = vars->hl_iov;
rqst[num_rqst].rq_nvec = 2;
len = in_iov[i].iov_len;
@@ -514,41 +460,27 @@ replay_again:
data[1] = in_iov[i].iov_base;
rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst], COMPOUND_FID,
- COMPOUND_FID, current->tgid,
- FILE_LINK_INFORMATION,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ current->tgid, FILE_LINK_INFORMATION,
SMB2_O_INFO_FILE, 0, data, size);
if (rc)
goto finished;
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst++]);
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_hardlink_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_REPARSE:
rqst[num_rqst].rq_iov = vars->io_iov;
rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
- if (cfile) {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FSCTL_SET_REPARSE_POINT,
- in_iov[i].iov_base,
- in_iov[i].iov_len, 0);
- } else {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- COMPOUND_FID, COMPOUND_FID,
- FSCTL_SET_REPARSE_POINT,
- in_iov[i].iov_base,
- in_iov[i].iov_len, 0);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ FSCTL_SET_REPARSE_POINT,
+ in_iov[i].iov_base,
+ in_iov[i].iov_len, 0);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_set_reparse_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -556,25 +488,13 @@ replay_again:
rqst[num_rqst].rq_iov = vars->io_iov;
rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
- if (cfile) {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FSCTL_GET_REPARSE_POINT,
- NULL, 0, CIFSMaxBufSize);
- } else {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- COMPOUND_FID, COMPOUND_FID,
- FSCTL_GET_REPARSE_POINT,
- NULL, 0, CIFSMaxBufSize);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ FSCTL_GET_REPARSE_POINT,
+ NULL, 0, CIFSMaxBufSize);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_get_reparse_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -582,34 +502,17 @@ replay_again:
rqst[num_rqst].rq_iov = &vars->ea_iov;
rqst[num_rqst].rq_nvec = 1;
- if (cfile) {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FILE_FULL_EA_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
- sizeof(wsl_query_eas),
- (void *)wsl_query_eas);
- } else {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- FILE_FULL_EA_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
- sizeof(wsl_query_eas),
- (void *)wsl_query_eas);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_query_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ FILE_FULL_EA_INFORMATION,
+ SMB2_O_INFO_FILE, 0,
+ SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
+ sizeof(wsl_query_eas),
+ (void *)wsl_query_eas);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_query_wsl_ea_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -685,8 +588,10 @@ finished:
idata->fi.Attributes = create_rsp->FileAttributes;
idata->fi.AllocationSize = create_rsp->AllocationSize;
idata->fi.EndOfFile = create_rsp->EndofFile;
+ idata->contains_posix_file_info = false;
if (le32_to_cpu(idata->fi.NumberOfLinks) == 0)
idata->fi.NumberOfLinks = cpu_to_le32(1); /* dummy value */
+ idata->unknown_nlink = true;
idata->fi.DeletePending = 0; /* successful open = not delete pending */
idata->fi.Directory = !!(le32_to_cpu(create_rsp->FileAttributes) & ATTR_DIRECTORY);
@@ -707,7 +612,6 @@ finished:
switch (cmds[i]) {
case SMB2_OP_QUERY_INFO:
idata = in_iov[i].iov_base;
- idata->contains_posix_file_info = false;
if (rc == 0 && cfile && cfile->symlink_target) {
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!idata->symlink_target)
@@ -720,6 +624,8 @@ finished:
le16_to_cpu(qi_rsp->OutputBufferOffset),
le32_to_cpu(qi_rsp->OutputBufferLength),
&rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi);
+ if (!rc)
+ idata->contains_posix_file_info = false;
}
SMB2_query_info_free(&rqst[num_rqst++]);
if (rc)
@@ -731,7 +637,6 @@ finished:
break;
case SMB2_OP_POSIX_QUERY_INFO:
idata = in_iov[i].iov_base;
- idata->contains_posix_file_info = true;
if (rc == 0 && cfile && cfile->symlink_target) {
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!idata->symlink_target)
@@ -745,6 +650,8 @@ finished:
le32_to_cpu(qi_rsp->OutputBufferLength),
&rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */,
(char *)&idata->posix_fi);
+ if (!rc)
+ idata->contains_posix_file_info = true;
}
if (rc == 0)
rc = parse_posix_sids(idata, &rsp_iov[i + 1]);
@@ -816,7 +723,6 @@ finished:
idata = in_iov[i].iov_base;
idata->reparse.io.iov = *iov;
idata->reparse.io.buftype = resp_buftype[i + 1];
- idata->contains_posix_file_info = false; /* BB VERIFY */
rbuf = reparse_buf_ptr(iov);
if (IS_ERR(rbuf)) {
rc = PTR_ERR(rbuf);
@@ -838,7 +744,6 @@ finished:
case SMB2_OP_QUERY_WSL_EA:
if (!rc) {
idata = in_iov[i].iov_base;
- idata->contains_posix_file_info = false;
qi_rsp = rsp_iov[i + 1].iov_base;
data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
@@ -903,9 +808,19 @@ static int parse_create_response(struct cifs_open_info_data *data,
rc = smb2_parse_symlink_response(cifs_sb, iov,
full_path,
&data->symlink_target);
- if (rc)
+ if (rc != 0 && rc != -ENODATA)
return rc;
- tag = IO_REPARSE_TAG_SYMLINK;
+ /*
+ * -ENODATA means that the response was parsed but did not contain
+ * the symlink target at all (see symlink_data()). Treat it like
+ * STATUS_IO_REPARSE_TAG_NOT_HANDLED, which does not contain it
+ * either: leave the tag unset and clear rc, so that the caller
+ * retrieves the target with SMB2_OP_GET_REPARSE.
+ */
+ if (rc == -ENODATA)
+ rc = 0;
+ else
+ tag = IO_REPARSE_TAG_SYMLINK;
reparse_point = true;
break;
case STATUS_SUCCESS:
@@ -1098,8 +1013,16 @@ int smb2_query_path_info(const unsigned int xid,
rc = -EOPNOTSUPP;
}
- if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) {
- bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
+ /*
+ * If the symlink was already parsed in create response then it is needed to fix
+ * its type now (after the second call with OPEN_REPARSE_POINT which filled the
+ * metadata attributes). If the symlink was not parsed in create response then
+ * the data->symlink_target was not filled yet and then the type will be fixed
+ * later after data->symlink_target is filled.
+ */
+ if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc && data->symlink_target) {
+ bool directory = cifs_open_data_attrs(data) & ATTR_DIRECTORY;
+
rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
}
break;
@@ -1156,7 +1079,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
cifs_i = CIFS_I(inode);
dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
data.Attributes = cpu_to_le32(dosattrs);
- cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
+ cifs_get_writable_path(tcon, name, inode, FIND_ANY, &cfile);
oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES,
FILE_CREATE, CREATE_NOT_FILE, ACL_NO_MODE);
tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
@@ -1216,6 +1139,7 @@ again:
memset(resp_buftype, 0, sizeof(resp_buftype));
memset(rsp_iov, 0, sizeof(rsp_iov));
+ memset(open_iov, 0, sizeof(open_iov));
rqst[0].rq_iov = open_iov;
rqst[0].rq_nvec = ARRAY_SIZE(open_iov);
@@ -1240,14 +1164,15 @@ again:
creq = rqst[0].rq_iov[0].iov_base;
creq->ShareAccess = FILE_SHARE_DELETE_LE;
+ memset(&close_iov, 0, sizeof(close_iov));
rqst[1].rq_iov = &close_iov;
rqst[1].rq_nvec = 1;
rc = SMB2_close_init(tcon, server, &rqst[1],
COMPOUND_FID, COMPOUND_FID, false);
- smb2_set_related(&rqst[1]);
if (rc)
goto err_free;
+ smb2_set_related(&rqst[1]);
if (retries) {
/* Back-off before retry */
@@ -1330,35 +1255,62 @@ int smb2_rename_path(const unsigned int xid,
const char *from_name, const char *to_name,
struct cifs_sb_info *cifs_sb)
{
+ struct inode *inode = source_dentry ? d_inode(source_dentry) : NULL;
struct cifsFileInfo *cfile;
__u32 co = file_create_options(source_dentry);
drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb);
- cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
+ cifs_get_writable_path(tcon, from_name, inode,
+ FIND_WITH_DELETE, &cfile);
int rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
co, DELETE, SMB2_OP_RENAME, cfile, source_dentry);
if (rc == -EINVAL) {
cifs_dbg(FYI, "invalid lease key, resending request without lease");
- cifs_get_writable_path(tcon, from_name,
- FIND_WR_WITH_DELETE, &cfile);
+ cifs_get_writable_path(tcon, from_name, inode,
+ FIND_WITH_DELETE, &cfile);
rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
co, DELETE, SMB2_OP_RENAME, cfile, NULL);
}
return rc;
}
+static int clear_tmpfile_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ struct inode *inode, const char *full_path)
+{
+ struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ FILE_BASIC_INFO fi;
+
+ cinode->cifsAttrs &= ~(ATTR_TEMPORARY | ATTR_HIDDEN);
+ fi = (FILE_BASIC_INFO) {
+ .Attributes = cpu_to_le32(cinode->cifsAttrs),
+ };
+ return server->ops->set_file_info(inode, full_path, &fi, xid);
+}
+
int smb2_create_hardlink(const unsigned int xid,
struct cifs_tcon *tcon,
struct dentry *source_dentry,
const char *from_name, const char *to_name,
struct cifs_sb_info *cifs_sb)
{
+ struct inode *inode = source_dentry ? d_inode(source_dentry) : NULL;
__u32 co = file_create_options(source_dentry);
+ struct cifsFileInfo *cfile;
+ int rc;
+
+ if (inode && test_bit(CIFS_INO_TMPFILE, &CIFS_I(inode)->flags)) {
+ rc = clear_tmpfile_attr(xid, tcon, inode, from_name);
+ if (rc)
+ return rc;
+ }
+ cifs_get_writable_path(tcon, from_name, inode,
+ FIND_WITH_DELETE, &cfile);
return smb2_set_path_attr(xid, tcon, from_name, to_name,
cifs_sb, co, FILE_READ_ATTRIBUTES,
- SMB2_OP_HARDLINK, NULL, NULL);
+ SMB2_OP_HARDLINK, cfile, NULL);
}
int
@@ -1367,15 +1319,16 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, bool set_alloc,
struct dentry *dentry)
{
+ struct inode *inode = dentry ? d_inode(dentry) : NULL;
+ __le64 eof = cpu_to_le64(size);
struct cifs_open_parms oparms;
struct cifsFileInfo *cfile;
struct kvec in_iov;
- __le64 eof = cpu_to_le64(size);
int rc;
in_iov.iov_base = &eof;
in_iov.iov_len = sizeof(eof);
- cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ cifs_get_writable_path(tcon, full_path, inode, FIND_ANY, &cfile);
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_DATA,
FILE_OPEN, 0, ACL_NO_MODE);
@@ -1385,7 +1338,8 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
cfile, NULL, NULL, dentry);
if (rc == -EINVAL) {
cifs_dbg(FYI, "invalid lease key, resending request without lease");
- cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ cifs_get_writable_path(tcon, full_path,
+ inode, FIND_ANY, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb,
full_path, &oparms, &in_iov,
&(int){SMB2_OP_SET_EOF}, 1,
@@ -1415,7 +1369,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
(buf->LastWriteTime == 0) && (buf->ChangeTime == 0)) {
if (buf->Attributes == 0)
goto out; /* would be a no op, no sense sending this */
- cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ cifs_get_writable_path(tcon, full_path,
+ inode, FIND_ANY, &cfile);
}
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_ATTRIBUTES,
@@ -1474,7 +1429,7 @@ struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data,
if (tcon->posix_extensions) {
cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
- cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ cifs_get_writable_path(tcon, full_path, NULL, FIND_ANY, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
if (!rc) {
@@ -1483,7 +1438,7 @@ struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data,
}
} else {
cmds[1] = SMB2_OP_QUERY_INFO;
- cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ cifs_get_writable_path(tcon, full_path, NULL, FIND_ANY, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
if (!rc) {
@@ -1565,8 +1520,8 @@ int smb2_rename_pending_delete(const char *full_path,
struct dentry *dentry,
const unsigned int xid)
{
- struct cifs_sb_info *cifs_sb = CIFS_SB(d_inode(dentry)->i_sb);
struct cifsInodeInfo *cinode = CIFS_I(d_inode(dentry));
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dentry);
__le16 *utf16_path __free(kfree) = NULL;
__u32 co = file_create_options(dentry);
int cmds[] = {
@@ -1578,14 +1533,10 @@ int smb2_rename_pending_delete(const char *full_path,
char *to_name __free(kfree) = NULL;
__u32 attrs = cinode->cifsAttrs;
struct cifs_open_parms oparms;
- static atomic_t sillycounter;
struct cifsFileInfo *cfile;
struct tcon_link *tlink;
struct cifs_tcon *tcon;
struct kvec iov[2];
- const char *ppath;
- void *page;
- size_t len;
int rc;
tlink = cifs_sb_tlink(cifs_sb);
@@ -1593,25 +1544,14 @@ int smb2_rename_pending_delete(const char *full_path,
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
- page = alloc_dentry_path();
-
- ppath = build_path_from_dentry(dentry->d_parent, page);
- if (IS_ERR(ppath)) {
- rc = PTR_ERR(ppath);
+ to_name = cifs_silly_fullpath(dentry);
+ if (IS_ERR(to_name)) {
+ rc = PTR_ERR(to_name);
+ to_name = NULL;
goto out;
}
- len = strlen(ppath) + strlen("/.__smb1234") + 1;
- to_name = kmalloc(len, GFP_KERNEL);
- if (!to_name) {
- rc = -ENOMEM;
- goto out;
- }
-
- scnprintf(to_name, len, "%s%c.__smb%04X", ppath, CIFS_DIR_SEP(cifs_sb),
- atomic_inc_return(&sillycounter) & 0xffff);
-
- utf16_path = utf16_smb2_path(cifs_sb, to_name, len);
+ utf16_path = utf16_smb2_path(cifs_sb, to_name, strlen(to_name));
if (!utf16_path) {
rc = -ENOMEM;
goto out;
@@ -1634,13 +1574,14 @@ int smb2_rename_pending_delete(const char *full_path,
iov[1].iov_base = utf16_path;
iov[1].iov_len = sizeof(*utf16_path) * UniStrlen((wchar_t *)utf16_path);
- cifs_get_writable_path(tcon, full_path, FIND_WR_WITH_DELETE, &cfile);
+ cifs_get_writable_path(tcon, full_path, d_inode(dentry),
+ FIND_WITH_DELETE, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms, iov,
cmds, num_cmds, cfile, NULL, NULL, dentry);
if (rc == -EINVAL) {
cifs_dbg(FYI, "invalid lease key, resending request without lease\n");
- cifs_get_writable_path(tcon, full_path,
- FIND_WR_WITH_DELETE, &cfile);
+ cifs_get_writable_path(tcon, full_path, d_inode(dentry),
+ FIND_WITH_DELETE, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms, iov,
cmds, num_cmds, cfile, NULL, NULL, NULL);
}
@@ -1653,6 +1594,5 @@ int smb2_rename_pending_delete(const char *full_path,
}
out:
cifs_put_tlink(tlink);
- free_dentry_path(page);
return rc;
}
diff --git a/fs/smb/client/smb2maperror.c b/fs/smb/client/smb2maperror.c
index cd036365201f..d86f2460d0e5 100644
--- a/fs/smb/client/smb2maperror.c
+++ b/fs/smb/client/smb2maperror.c
@@ -8,7 +8,9 @@
*
*/
#include <linux/errno.h>
-#include "cifsglob.h"
+
+#include <kunit/visibility.h>
+
#include "cifsproto.h"
#include "cifs_debug.h"
#include "smb2proto.h"
@@ -16,12 +18,6 @@
#include "../common/smb2status.h"
#include "trace.h"
-struct status_to_posix_error {
- __u32 smb2_status;
- int posix_error;
- char *status_string;
-};
-
static const struct status_to_posix_error smb2_error_map_table[] = {
/*
* Automatically generated by the `gen_smb2_mapping` script,
@@ -115,10 +111,16 @@ int __init smb2_init_maperror(void)
return 0;
}
-#define SMB_CLIENT_KUNIT_AVAILABLE \
- ((IS_MODULE(CONFIG_CIFS) && IS_ENABLED(CONFIG_KUNIT)) || \
- (IS_BUILTIN(CONFIG_CIFS) && IS_BUILTIN(CONFIG_KUNIT)))
+#if IS_ENABLED(CONFIG_SMB_KUNIT_TESTS)
+const struct status_to_posix_error *smb2_get_err_map_test(__u32 smb2_status)
+{
+ return smb2_get_err_map(smb2_status);
+}
+EXPORT_SYMBOL_IF_KUNIT(smb2_get_err_map_test);
+
+const struct status_to_posix_error *smb2_error_map_table_test = smb2_error_map_table;
+EXPORT_SYMBOL_IF_KUNIT(smb2_error_map_table_test);
-#if SMB_CLIENT_KUNIT_AVAILABLE && IS_ENABLED(CONFIG_SMB_KUNIT_TESTS)
-#include "smb2maperror_test.c"
-#endif /* CONFIG_SMB_KUNIT_TESTS */
+unsigned int smb2_error_map_num = ARRAY_SIZE(smb2_error_map_table);
+EXPORT_SYMBOL_IF_KUNIT(smb2_error_map_num);
+#endif
diff --git a/fs/smb/client/smb2maperror_test.c b/fs/smb/client/smb2maperror_test.c
index 38ea6b846a99..44dc5e899cad 100644
--- a/fs/smb/client/smb2maperror_test.c
+++ b/fs/smb/client/smb2maperror_test.c
@@ -9,14 +9,17 @@
*/
#include <kunit/test.h>
+#include "cifsglob.h"
+#include "smb2glob.h"
+#include "smb2proto.h"
static void
test_cmp_map(struct kunit *test, const struct status_to_posix_error *expect)
{
const struct status_to_posix_error *result;
- result = smb2_get_err_map(expect->smb2_status);
- KUNIT_EXPECT_PTR_NE(test, NULL, result);
+ result = smb2_get_err_map_test(expect->smb2_status);
+ KUNIT_ASSERT_NOT_NULL(test, result);
KUNIT_EXPECT_EQ(test, expect->smb2_status, result->smb2_status);
KUNIT_EXPECT_EQ(test, expect->posix_error, result->posix_error);
KUNIT_EXPECT_STREQ(test, expect->status_string, result->status_string);
@@ -26,8 +29,8 @@ static void maperror_test_check_search(struct kunit *test)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(smb2_error_map_table); i++)
- test_cmp_map(test, &smb2_error_map_table[i]);
+ for (i = 0; i < smb2_error_map_num; i++)
+ test_cmp_map(test, &smb2_error_map_table_test[i]);
}
static struct kunit_case maperror_test_cases[] = {
@@ -43,3 +46,5 @@ static struct kunit_suite maperror_suite = {
kunit_test_suite(maperror_suite);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit tests of SMB2 maperror");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c
index 973fce3c959c..0cfe60ae42c3 100644
--- a/fs/smb/client/smb2misc.c
+++ b/fs/smb/client/smb2misc.c
@@ -19,6 +19,9 @@
#include "nterr.h"
#include "cached_dir.h"
+static unsigned int __smb2_calc_size(void *buf, bool *have_data,
+ bool *data_area_overlap);
+
static int
check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid)
{
@@ -82,6 +85,36 @@ static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
/* SMB2_OPLOCK_BREAK */ cpu_to_le16(24)
};
+/*
+ * Minimum received PDU size for commands whose response carries a
+ * variable-length data area. A non-zero entry marks the command as
+ * having one, and gives the length smb2_check_message() requires
+ * before smb2_get_data_area_len() reads the offset and length fields
+ * out of the fixed response struct.
+ */
+static const size_t smb2_min_pdu_len[NUMBER_OF_SMB2_COMMANDS] = {
+ /* SMB2_NEGOTIATE */ sizeof(struct smb2_negotiate_rsp),
+ /* SMB2_SESSION_SETUP */ sizeof(struct smb2_sess_setup_rsp),
+ /* SMB2_LOGOFF */ 0,
+ /* SMB2_TREE_CONNECT */ 0,
+ /* SMB2_TREE_DISCONNECT */ 0,
+ /* SMB2_CREATE */ sizeof(struct smb2_create_rsp),
+ /* SMB2_CLOSE */ 0,
+ /* SMB2_FLUSH */ 0,
+ /* SMB2_READ */ sizeof(struct smb2_read_rsp),
+ /* SMB2_WRITE */ 0,
+ /* SMB2_LOCK */ 0,
+ /* SMB2_IOCTL */ sizeof(struct smb2_ioctl_rsp),
+ /* SMB2_CANCEL */ 0,
+ /* SMB2_ECHO */ 0,
+ /* SMB2_QUERY_DIRECTORY */ sizeof(struct smb2_query_directory_rsp),
+ /* SMB2_CHANGE_NOTIFY */ sizeof(struct smb2_change_notify_rsp),
+ /* SMB2_QUERY_INFO */ sizeof(struct smb2_query_info_rsp),
+ /* SMB2_SET_INFO */ 0,
+ /* SMB2_OPLOCK_BREAK */ 0,
+};
+
+#define smb2_has_data_area(cmd) (smb2_min_pdu_len[cmd] != 0)
#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_hdr) + sizeof(struct smb2_negotiate_rsp))
static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len,
@@ -145,6 +178,8 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
int command;
__u32 calc_len; /* calculated length */
__u64 mid;
+ bool have_data;
+ bool data_area_overlap;
/* If server is a channel, select the primary channel */
pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
@@ -228,7 +263,23 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
}
}
- calc_len = smb2_calc_size(buf);
+ if ((shdr->Status == STATUS_SUCCESS ||
+ shdr->Status == STATUS_MORE_PROCESSING_REQUIRED ||
+ pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE) &&
+ smb2_has_data_area(command) &&
+ len < smb2_min_pdu_len[command]) {
+ cifs_server_dbg(VFS, "SMB2 command %d response too short: %u < %zu\n",
+ command, len, smb2_min_pdu_len[command]);
+ return 1;
+ }
+
+ have_data = false;
+ data_area_overlap = false;
+ calc_len = __smb2_calc_size(buf, &have_data, &data_area_overlap);
+
+ /* Reject responses whose data area overlaps the fixed area. */
+ if (data_area_overlap)
+ return 1;
/* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might
* be 0, and not a real miscalculation */
@@ -241,13 +292,19 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
if (len != calc_len) {
/* create failed on symlink */
if (command == SMB2_CREATE_HE &&
- shdr->Status == STATUS_STOPPED_ON_SYMLINK)
+ shdr->Status == STATUS_STOPPED_ON_SYMLINK &&
+ len > calc_len)
return 0;
/* Windows 7 server returns 24 bytes more */
if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE)
return 0;
- /* server can return one byte more due to implied bcc[0] */
- if (calc_len == len + 1)
+ /*
+ * Server can return one byte more due to implied bcc[0].
+ * Allow it only when there is no data area; if data_length > 0
+ * the +1 gap indicates an overreported data length rather than
+ * the bcc[0] omission.
+ */
+ if (calc_len == len + 1 && !have_data)
return 0;
/*
@@ -282,33 +339,6 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
}
/*
- * The size of the variable area depends on the offset and length fields
- * located in different fields for various SMB2 responses. SMB2 responses
- * 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 */ false,
- /* SMB2_TREE_DISCONNECT */ false,
- /* SMB2_CREATE */ true,
- /* SMB2_CLOSE */ false,
- /* SMB2_FLUSH */ false,
- /* SMB2_READ */ true,
- /* SMB2_WRITE */ false,
- /* SMB2_LOCK */ false,
- /* SMB2_IOCTL */ true,
- /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */
- /* SMB2_ECHO */ false,
- /* SMB2_QUERY_DIRECTORY */ true,
- /* SMB2_CHANGE_NOTIFY */ true,
- /* SMB2_QUERY_INFO */ true,
- /* SMB2_SET_INFO */ false,
- /* 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.
*/
@@ -406,26 +436,35 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr)
}
/*
- * 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.
+ * Calculate the size of the SMB message based on the fixed header, fixed
+ * parameter area, and variable data area.
+ *
+ * If have_data is not NULL, it is set when a non-empty data area is found.
+ * If data_area_overlap is not NULL, it is set when the data area overlaps
+ * the fixed area.
*/
-unsigned int
-smb2_calc_size(void *buf)
+static unsigned int
+__smb2_calc_size(void *buf, bool *have_data, bool *data_area_overlap)
{
struct smb2_pdu *pdu = buf;
struct smb2_hdr *shdr = &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 */
+ int data_length = 0; /* 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(shdr->StructureSize);
+ if (have_data)
+ *have_data = false;
+ if (data_area_overlap)
+ *data_area_overlap = false;
+
/*
* 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(shdr->Command)] == false)
+ if (!smb2_has_data_area(le16_to_cpu(shdr->Command)))
goto calc_size_exit;
smb2_get_data_area_len(&offset, &data_length, shdr);
@@ -441,16 +480,27 @@ smb2_calc_size(void *buf)
if (offset + 1 < len) {
cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n",
offset + 1, len);
+ if (data_area_overlap)
+ *data_area_overlap = true;
data_length = 0;
+ goto calc_size_exit;
} else {
len = offset + data_length;
}
}
calc_size_exit:
cifs_dbg(FYI, "SMB2 len %d\n", len);
+ if (have_data)
+ *have_data = (data_length > 0);
return len;
}
+unsigned int
+smb2_calc_size(void *buf)
+{
+ return __smb2_calc_size(buf, NULL, NULL);
+}
+
/* Note: caller must free return buffer */
__le16 *
cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 7f2d3459cbf9..3464470d3297 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -111,10 +111,21 @@ smb2_add_credits(struct TCP_Server_Info *server,
cifs_trace_rw_credits_zero_in_flight);
}
server->in_flight--;
+
+ /*
+ * Rebalance credits when an op drains in_flight. For session setup,
+ * do this only when the total accumulated credits are high enough (>2)
+ * so that a newly established secondary channel can reserve credits for
+ * echoes and oplocks. We expect this to happen at the end of the final
+ * session setup response.
+ */
if (server->in_flight == 0 &&
((optype & CIFS_OP_MASK) != CIFS_NEG_OP) &&
((optype & CIFS_OP_MASK) != CIFS_SESS_OP))
rc = change_conf(server);
+ else if (server->in_flight == 0 &&
+ ((optype & CIFS_OP_MASK) == CIFS_SESS_OP) && *val > 2)
+ rc = change_conf(server);
/*
* Sometimes server returns 0 credits on oplock break ack - we need to
* rebalance credits in this case.
@@ -628,6 +639,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
struct smb_sockaddr_in6 *p6;
struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL;
struct cifs_server_iface tmp_iface;
+ __be16 port;
ssize_t bytes_left;
size_t next = 0;
int nb_iface = 0;
@@ -662,6 +674,15 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
goto out;
}
+ spin_lock(&ses->server->srv_lock);
+ if (ses->server->dstaddr.ss_family == AF_INET)
+ port = ((struct sockaddr_in *)&ses->server->dstaddr)->sin_port;
+ else if (ses->server->dstaddr.ss_family == AF_INET6)
+ port = ((struct sockaddr_in6 *)&ses->server->dstaddr)->sin6_port;
+ else
+ port = cpu_to_be16(CIFS_PORT);
+ spin_unlock(&ses->server->srv_lock);
+
while (bytes_left >= (ssize_t)sizeof(*p)) {
memset(&tmp_iface, 0, sizeof(tmp_iface));
/* default to 1Gbps when link speed is unset */
@@ -682,7 +703,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
memcpy(&addr4->sin_addr, &p4->IPv4Address, 4);
/* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */
- addr4->sin_port = cpu_to_be16(CIFS_PORT);
+ addr4->sin_port = port;
cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__,
&addr4->sin_addr);
@@ -696,7 +717,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
/* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */
addr6->sin6_flowinfo = 0;
addr6->sin6_scope_id = 0;
- addr6->sin6_port = cpu_to_be16(CIFS_PORT);
+ addr6->sin6_port = port;
cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__,
&addr6->sin6_addr);
@@ -764,9 +785,9 @@ next_iface:
break;
}
/* Validate that Next doesn't point beyond the buffer */
- if (next > bytes_left) {
- cifs_dbg(VFS, "%s: invalid Next pointer %zu > %zd\n",
- __func__, next, bytes_left);
+ if (next < sizeof(*p) || next > bytes_left) {
+ cifs_dbg(VFS, "%s: invalid Next pointer %zu out of range [%zu, %zd]\n",
+ __func__, next, sizeof(*p), bytes_left);
rc = -EINVAL;
goto out;
}
@@ -1032,8 +1053,9 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size,
char *name, *value;
size_t buf_size = dst_size;
size_t name_len, value_len, user_name_len;
+ u32 next_off;
- while (src_size > 0) {
+ while (src_size >= sizeof(*src)) {
name_len = (size_t)src->ea_name_length;
value_len = (size_t)le16_to_cpu(src->ea_value_length);
@@ -1089,14 +1111,22 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size,
if (!src->next_entry_offset)
break;
- if (src_size < le32_to_cpu(src->next_entry_offset)) {
- /* stop before overrun buffer */
- rc = -ERANGE;
- break;
+ next_off = le32_to_cpu(src->next_entry_offset);
+ if (next_off < sizeof(*src) || src_size < next_off) {
+ cifs_dbg(FYI, "EA next_entry_offset %u out of range [%zu, %zu]\n",
+ next_off, sizeof(*src), src_size);
+ rc = smb_EIO2(smb_eio_trace_ea_next_offset,
+ next_off, src_size);
+ goto out;
+ }
+ src_size -= next_off;
+ src = (void *)((char *)src + next_off);
+ if (src_size > 0 && src_size < sizeof(*src)) {
+ cifs_dbg(FYI, "EA next_entry_offset %u left truncated entry (%zu bytes)\n",
+ next_off, src_size);
+ rc = smb_EIO2(smb_eio_trace_ea_next_offset, next_off, src_size);
+ goto out;
}
- src_size -= le32_to_cpu(src->next_entry_offset);
- src = (void *)((char *)src +
- le32_to_cpu(src->next_entry_offset));
}
/* didn't find the named attribute */
@@ -1487,6 +1517,7 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
{
struct smb2_file_network_open_info file_inf;
struct inode *inode;
+ u64 asize;
int rc;
rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid,
@@ -1510,14 +1541,9 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
inode_set_atime_to_ts(inode,
cifs_NTtimeToUnix(file_inf.LastAccessTime));
- /*
- * i_blocks is not related to (i_size / i_blksize),
- * but instead 512 byte (2**9) size is required for
- * calculating num blocks.
- */
- if (le64_to_cpu(file_inf.AllocationSize) > 4096)
- inode->i_blocks =
- (512 - 1 + le64_to_cpu(file_inf.AllocationSize)) >> 9;
+ asize = le64_to_cpu(file_inf.AllocationSize);
+ if (asize > 4096)
+ inode->i_blocks = CIFS_INO_BLOCKS(asize);
/* End of file and Attributes should not have to be updated on close */
spin_unlock(&inode->i_lock);
@@ -1552,7 +1578,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE);
req_res_key_exit:
- kfree(res_key);
+ kfree_sensitive(res_key);
return rc;
}
@@ -1755,8 +1781,8 @@ replay_again:
if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length)
qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
if (qi.input_buffer_length > 0 &&
- le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length
- > rsp_iov[1].iov_len) {
+ size_add(le32_to_cpu(io_rsp->OutputOffset),
+ qi.input_buffer_length) > rsp_iov[1].iov_len) {
rc = -EFAULT;
goto out;
}
@@ -1777,6 +1803,12 @@ replay_again:
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length)
qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength);
+ if (qi.input_buffer_length > 0 &&
+ struct_size(qi_rsp, Buffer, qi.input_buffer_length) >
+ rsp_iov[1].iov_len) {
+ rc = -EFAULT;
+ goto out;
+ }
if (copy_to_user(&pqi->input_buffer_length,
&qi.input_buffer_length,
sizeof(qi.input_buffer_length))) {
@@ -1816,31 +1848,31 @@ free_vars:
*
* @tcon: destination file tcon
* @bytes_left: how many bytes are left to copy
+ * @chunk_size: maximum size of a single chunk
*
* Return: maximum number of chunks with which Chunks[] can be filled.
*/
static inline u32
-calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)
+calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left, u32 chunk_size)
{
u32 max_chunks = READ_ONCE(tcon->max_chunks);
u32 max_bytes_copy = READ_ONCE(tcon->max_bytes_copy);
- u32 max_bytes_chunk = READ_ONCE(tcon->max_bytes_chunk);
u64 need;
u32 allowed;
- if (!max_bytes_chunk || !max_bytes_copy || !max_chunks)
+ if (!chunk_size || !max_bytes_copy || !max_chunks)
return 0;
/* chunks needed for the remaining bytes */
- need = DIV_ROUND_UP_ULL(bytes_left, max_bytes_chunk);
+ need = DIV_ROUND_UP_ULL(bytes_left, chunk_size);
/* chunks allowed per cc request */
- allowed = DIV_ROUND_UP(max_bytes_copy, max_bytes_chunk);
+ allowed = DIV_ROUND_UP(max_bytes_copy, chunk_size);
return (u32)umin(need, umin(max_chunks, allowed));
}
/**
- * smb2_copychunk_range - server-side copy of data range
+ * __smb2_copychunk_range - server-side copy of data range
*
* @xid: transaction id
* @src_file: source file
@@ -1852,15 +1884,15 @@ calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)
* Obtains a resume key for @src_file and issues FSCTL_SRV_COPYCHUNK_WRITE
* IOCTLs, splitting the request into chunks limited by tcon->max_*.
*
- * Return: @len on success; negative errno on failure.
+ * Return: 0 on success; negative errno on failure.
*/
-static ssize_t
-smb2_copychunk_range(const unsigned int xid,
- struct cifsFileInfo *src_file,
- struct cifsFileInfo *dst_file,
- u64 src_off,
- u64 len,
- u64 dst_off)
+static int
+__smb2_copychunk_range(const unsigned int xid,
+ struct cifsFileInfo *src_file,
+ struct cifsFileInfo *dst_file,
+ u64 src_off,
+ u64 len,
+ u64 dst_off)
{
int rc = 0;
unsigned int ret_data_len = 0;
@@ -1868,12 +1900,14 @@ smb2_copychunk_range(const unsigned int xid,
struct copychunk_ioctl_rsp *cc_rsp = NULL;
struct cifs_tcon *tcon;
struct srv_copychunk *chunk;
- u32 chunks, chunk_count, chunk_bytes;
+ u32 chunks, chunk_count, chunk_bytes, chunk_size;
u32 copy_bytes, copy_bytes_left;
u32 chunks_written, bytes_written;
u64 total_bytes_left = len;
u64 src_off_prev, dst_off_prev;
+ u64 max_chunk = 0;
u32 retries = 0;
+ bool reverse = false;
tcon = tlink_tcon(dst_file->tlink);
@@ -1881,8 +1915,50 @@ smb2_copychunk_range(const unsigned int xid,
dst_file->fid.volatile_fid, tcon->tid,
tcon->ses->Suid, src_off, dst_off, len);
+ /*
+ * Same-file left shifts are safe in forward order. For a right shift,
+ * let L be the copy length, delta the distance between the source and
+ * destination, and C the normal chunk size:
+ *
+ * delta >= L: copy forwards using C
+ * delta < L:
+ * delta >= C: copy backwards using C
+ * delta < C: copy backwards with chunks limited to delta
+ *
+ * Copying backwards prevents one chunk from overwriting data needed by
+ * a later chunk. Limiting the chunk size to delta prevents an individual
+ * chunk from overlapping itself.
+ * This limit can be removed once all supported servers handle overlapping
+ * descriptors safely.
+ *
+ * A small right shift over a large range may therefore require many
+ * chunks.
+ */
+ if (src_file == dst_file && dst_off > src_off) {
+ u64 delta = dst_off - src_off;
+
+ if (delta < len) {
+ reverse = true;
+ max_chunk = delta;
+ }
+ }
+
+ /*
+ * A backward copy walks the offsets down from the end of the range.
+ * Do this once, outside the retry loop, so a retry does not move the
+ * offsets again.
+ */
+ if (reverse) {
+ src_off += len;
+ dst_off += len;
+ }
+
retry:
- chunk_count = calc_chunk_count(tcon, total_bytes_left);
+ chunk_size = READ_ONCE(tcon->max_bytes_chunk);
+ if (max_chunk && max_chunk < chunk_size)
+ chunk_size = (u32)max_chunk;
+
+ chunk_count = calc_chunk_count(tcon, total_bytes_left, chunk_size);
if (!chunk_count) {
rc = -EOPNOTSUPP;
goto out;
@@ -1923,16 +1999,21 @@ retry:
while (copy_bytes_left > 0 && chunks < chunk_count) {
chunk = &cc_req->Chunks[chunks++];
+ chunk_bytes = umin(copy_bytes_left, chunk_size);
+ if (reverse) {
+ src_off -= chunk_bytes;
+ dst_off -= chunk_bytes;
+ }
+
chunk->SourceOffset = cpu_to_le64(src_off);
chunk->TargetOffset = cpu_to_le64(dst_off);
-
- chunk_bytes = umin(copy_bytes_left, tcon->max_bytes_chunk);
-
chunk->Length = cpu_to_le32(chunk_bytes);
/* Buffer is zeroed, no need to set chunk->Reserved = 0 */
- src_off += chunk_bytes;
- dst_off += chunk_bytes;
+ if (!reverse) {
+ src_off += chunk_bytes;
+ dst_off += chunk_bytes;
+ }
copy_bytes_left -= chunk_bytes;
copy_bytes += chunk_bytes;
@@ -1980,6 +2061,18 @@ retry:
goto out;
}
+ /*
+ * A successful COPYCHUNK should copy every descriptor (MS-SMB2
+ * 3.3.5.15.6). Reject a short backward copy because the rewind
+ * below only supports forward copying.
+ */
+ if (unlikely(reverse && bytes_written < copy_bytes)) {
+ cifs_tcon_dbg(VFS, "Copychunk short write %u/%u (reverse)\n",
+ bytes_written, copy_bytes);
+ rc = -EIO;
+ goto out;
+ }
+
/* Partial write: rewind */
if (bytes_written < copy_bytes) {
u32 delta = copy_bytes - bytes_written;
@@ -2041,10 +2134,27 @@ out:
trace_smb3_copychunk_done(xid, src_file->fid.volatile_fid,
dst_file->fid.volatile_fid, tcon->tid,
tcon->ses->Suid, src_off, dst_off, len);
- return len;
+ return 0;
}
}
+static ssize_t
+smb2_copychunk_range(const unsigned int xid,
+ struct cifsFileInfo *src_file,
+ struct cifsFileInfo *dst_file,
+ u64 src_off,
+ u64 len,
+ u64 dst_off)
+{
+ int rc;
+
+ rc = __smb2_copychunk_range(xid, src_file, dst_file, src_off, len,
+ dst_off);
+ if (rc)
+ return rc;
+ return len;
+}
+
static int
smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid *fid)
@@ -2094,8 +2204,9 @@ smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid,
}
/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */
-static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
- struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse)
+static int smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile, struct inode *inode,
+ __u8 setsparse)
{
struct cifsInodeInfo *cifsi;
int rc;
@@ -2104,31 +2215,31 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
/* if file already sparse don't bother setting sparse again */
if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse)
- return true; /* already sparse */
+ return 0; /* already sparse */
if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse)
- return true; /* already not sparse */
+ return 0; /* already not sparse */
/*
* Can't check for sparse support on share the usual way via the
* FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share
* since Samba server doesn't set the flag on the share, yet
* supports the set sparse FSCTL and returns sparse correctly
- * in the file attributes. If we fail setting sparse though we
- * mark that server does not support sparse files for this share
- * to avoid repeatedly sending the unsupported fsctl to server
- * if the file is repeatedly extended.
+ * in the file attributes. If the server returns EOPNOTSUPP, mark
+ * that sparse files are not supported on this share to avoid
+ * repeatedly sending the unsupported FSCTL.
*/
if (tcon->broken_sparse_sup)
- return false;
+ return -EOPNOTSUPP;
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
&setsparse, 1, CIFSMaxBufSize, NULL, NULL);
if (rc) {
- tcon->broken_sparse_sup = true;
+ if (rc == -EOPNOTSUPP)
+ tcon->broken_sparse_sup = true;
cifs_dbg(FYI, "set sparse rc = %d\n", rc);
- return false;
+ return rc;
}
if (setsparse)
@@ -2136,7 +2247,7 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
else
cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE);
- return true;
+ return 0;
}
static int
@@ -2169,10 +2280,14 @@ smb2_duplicate_extents(const unsigned int xid,
u64 len, u64 dest_off)
{
int rc;
+ int qrc;
unsigned int ret_data_len;
struct inode *inode;
+ struct smb2_file_all_info file_inf;
struct duplicate_extents_to_file dup_ext_buf;
+ struct timespec64 ts;
struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
+ u64 asize;
/* server fileays advertise duplicate extent support with this flag */
if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) &
@@ -2190,20 +2305,11 @@ smb2_duplicate_extents(const unsigned int xid,
trgtfile->fid.volatile_fid, tcon->tid,
tcon->ses->Suid, src_off, dest_off, len);
inode = d_inode(trgtfile->dentry);
- if (inode->i_size < dest_off + len) {
+ if (i_size_read(inode) < dest_off + len) {
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
if (rc)
goto duplicate_extents_out;
-
- /*
- * Although also could set plausible allocation size (i_blocks)
- * here in addition to setting the file size, in reflink
- * it is likely that the target file is sparse. Its allocation
- * size will be queried on next revalidate, but it is important
- * to make sure that file's cached size is updated immediately
- */
- netfs_resize_file(netfs_inode(inode), dest_off + len, true);
- cifs_setsize(inode, dest_off + len);
+ cifs_resize_file_locked(inode, dest_off + len);
}
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
trgtfile->fid.volatile_fid,
@@ -2216,6 +2322,35 @@ smb2_duplicate_extents(const unsigned int xid,
if (ret_data_len > 0)
cifs_dbg(FYI, "Non-zero response length in duplicate extents\n");
+ if (rc) {
+ CIFS_I(inode)->time = 0; /* force reval */
+ cifs_invalidate_cache(inode, 0);
+ } else {
+ qrc = SMB2_query_info(xid, tcon, trgtfile->fid.persistent_fid,
+ trgtfile->fid.volatile_fid, &file_inf);
+ spin_lock(&inode->i_lock);
+ if (qrc == 0) {
+ asize = le64_to_cpu(file_inf.AllocationSize);
+ CIFS_I(inode)->time = jiffies;
+ if (file_inf.LastWriteTime) {
+ ts = cifs_NTtimeToUnix(file_inf.LastWriteTime);
+ inode_set_mtime_to_ts(inode, ts);
+ }
+ if (file_inf.ChangeTime) {
+ ts = cifs_NTtimeToUnix(file_inf.ChangeTime);
+ inode_set_ctime_to_ts(inode, ts);
+ }
+ if (file_inf.LastAccessTime) {
+ ts = cifs_NTtimeToUnix(file_inf.LastAccessTime);
+ inode_set_atime_to_ts(inode, ts);
+ }
+ inode->i_blocks = CIFS_INO_BLOCKS(asize);
+ } else {
+ CIFS_I(inode)->time = 0; /* force reval */
+ }
+ spin_unlock(&inode->i_lock);
+ }
+
duplicate_extents_out:
if (rc)
trace_smb3_clone_err(xid, srcfile->fid.volatile_fid,
@@ -2231,10 +2366,10 @@ duplicate_extents_out:
static int
smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
- struct cifsFileInfo *cfile)
+ struct cifsFileInfo *cfile, __u16 compression_state)
{
return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid,
- cfile->fid.volatile_fid);
+ cfile->fid.volatile_fid, compression_state);
}
static int
@@ -2328,8 +2463,14 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
* and retry the ioctl again with larger array size sufficient
* to hold all of the snapshot GMT tokens on the second try.
*/
- if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE)
+ if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE) {
+ if (ret_data_len < sizeof(struct smb_snapshot_array)) {
+ rc = -EIO;
+ kfree(retbuf);
+ return rc;
+ }
ret_data_len = sizeof(struct smb_snapshot_array);
+ }
/*
* We return struct SRV_SNAPSHOT_ARRAY, followed by
@@ -3352,7 +3493,7 @@ get_smb2_acl(struct cifs_sb_info *cifs_sb,
struct cifsFileInfo *open_file = NULL;
if (inode && !(info & SACL_SECINFO))
- open_file = find_readable_file(CIFS_I(inode), true);
+ open_file = find_readable_file(CIFS_I(inode), FIND_FSUID_ONLY);
if (!open_file || (info & SACL_SECINFO))
return get_smb2_acl_by_path(cifs_sb, path, pacllen, info);
@@ -3387,8 +3528,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
struct inode *inode = file_inode(file);
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifsFileInfo *cfile = file->private_data;
- struct netfs_inode *ictx = netfs_inode(inode);
- unsigned long long i_size, new_size, remote_size;
+ unsigned long long i_size, new_size, remote_i_size, zero_point;
long rc;
unsigned int xid;
@@ -3397,11 +3537,17 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
ses->Suid, offset, len);
+ new_size = offset + len;
+ if (!keep_size && i_size_read(inode) < new_size) {
+ rc = inode_newsize_ok(inode, new_size);
+ if (rc)
+ goto out;
+ }
+
filemap_invalidate_lock(inode->i_mapping);
- i_size = i_size_read(inode);
- remote_size = ictx->remote_i_size;
- if (offset + len >= remote_size && offset < i_size) {
+ netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point);
+ if (offset + len >= remote_i_size && offset < i_size) {
unsigned long long top = umin(offset + len, i_size);
rc = filemap_write_and_wait_range(inode->i_mapping, offset, top - 1);
@@ -3421,6 +3567,9 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
if (keep_size == false && !CIFS_CACHE_READ(cifsi))
goto zero_range_exit;
+ fscache_invalidate(cifs_inode_cookie(inode), NULL,
+ i_size_read(inode), 0);
+
rc = smb3_zero_data(file, tcon, offset, len, xid);
if (rc < 0)
goto zero_range_exit;
@@ -3428,21 +3577,23 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
/*
* do we also need to change the size of the file?
*/
- new_size = offset + len;
if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) {
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, new_size);
if (rc >= 0) {
truncate_setsize(inode, new_size);
+ spin_lock(&inode->i_lock);
netfs_resize_file(&cifsi->netfs, new_size, true);
- if (offset < cifsi->netfs.zero_point)
- cifsi->netfs.zero_point = offset;
+ if (offset < cifsi->netfs._zero_point)
+ netfs_write_zero_point(inode, offset);
+ spin_unlock(&inode->i_lock);
fscache_resize_cookie(cifs_inode_cookie(inode), new_size);
}
}
zero_range_exit:
filemap_invalidate_unlock(inode->i_mapping);
+ out:
free_xid(xid);
if (rc)
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
@@ -3459,7 +3610,7 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
struct inode *inode = file_inode(file);
struct cifsFileInfo *cfile = file->private_data;
struct file_zero_data_information fsctl_buf;
- unsigned long long end = offset + len, i_size, remote_i_size;
+ unsigned long long end = offset + len, i_size, remote_i_size, zero_point;
long rc;
unsigned int xid;
__u8 set_sparse = 1;
@@ -3468,18 +3619,28 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
/* Need to make file sparse, if not already, before freeing range. */
/* Consider adding equivalent for compressed since it could also work */
- if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) {
- rc = -EOPNOTSUPP;
+ rc = smb2_set_sparse(xid, tcon, cfile, inode, set_sparse);
+ if (rc)
goto out;
- }
filemap_invalidate_lock(inode->i_mapping);
/*
+ * Flush dirty data first, otherwise a dirty folio spanning the punched
+ * range may be written back after the ioctl and refill the hole.
+ */
+ rc = filemap_write_and_wait_range(inode->i_mapping, offset,
+ offset + len - 1);
+ if (rc < 0)
+ goto unlock;
+
+ /*
* We implement the punch hole through ioctl, so we need remove the page
* caches first, otherwise the data may be inconsistent with the server.
*/
truncate_pagecache_range(inode, offset, offset + len - 1);
netfs_wait_for_outstanding_io(inode);
+ fscache_invalidate(cifs_inode_cookie(inode), NULL,
+ i_size_read(inode), 0);
cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);
@@ -3501,14 +3662,17 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
* that we locally hole-punch the tail of the dirty data, the proposed
* EOF update will end up in the wrong place.
*/
- i_size = i_size_read(inode);
- remote_i_size = netfs_inode(inode)->remote_i_size;
+ netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point);
+
if (end > remote_i_size && i_size > remote_i_size) {
unsigned long long extend_to = umin(end, i_size);
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, extend_to);
- if (rc >= 0)
- netfs_inode(inode)->remote_i_size = extend_to;
+ if (rc >= 0) {
+ spin_lock(&inode->i_lock);
+ netfs_write_remote_i_size(inode, extend_to);
+ spin_unlock(&inode->i_lock);
+ }
}
unlock:
@@ -3525,7 +3689,7 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
char *buf)
{
struct cifs_io_parms io_parms = {0};
- int nbytes;
+ unsigned int nbytes;
int rc = 0;
struct kvec iov[2];
@@ -3546,9 +3710,10 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1);
if (rc)
break;
+ if (!nbytes)
+ return -EIO;
if (nbytes > len)
return -EINVAL;
- buf += nbytes;
off += nbytes;
len -= nbytes;
}
@@ -3561,11 +3726,25 @@ static int smb3_simple_fallocate_range(unsigned int xid,
loff_t off, loff_t len)
{
struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data;
+ struct inode *inode = d_inode(cfile->dentry);
u32 out_data_len;
char *buf = NULL;
+ u64 range_start, range_len, range_end;
loff_t l;
int rc;
+ buf = kvzalloc(min_t(loff_t, len, SMB2_MAX_BUFFER_SIZE), GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (off >= i_size_read(inode)) {
+ rc = smb3_simple_fallocate_write_range(xid, tcon, cfile,
+ off, len, buf);
+ goto out;
+ }
+
in_data.file_offset = cpu_to_le64(off);
in_data.length = cpu_to_le64(len);
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
@@ -3577,12 +3756,6 @@ static int smb3_simple_fallocate_range(unsigned int xid,
if (rc)
goto out;
- buf = kzalloc(1024 * 1024, GFP_KERNEL);
- if (buf == NULL) {
- rc = -ENOMEM;
- goto out;
- }
-
tmp_data = out_data;
while (len) {
/*
@@ -3599,13 +3772,21 @@ static int smb3_simple_fallocate_range(unsigned int xid,
goto out;
}
- if (off < le64_to_cpu(tmp_data->file_offset)) {
+ range_start = le64_to_cpu(tmp_data->file_offset);
+ range_len = le64_to_cpu(tmp_data->length);
+ if (check_add_overflow(range_start, range_len, &range_end) ||
+ range_end > S64_MAX) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (off < range_start) {
/*
* We are at a hole. Write until the end of the region
* or until the next allocated data,
* whichever comes next.
*/
- l = le64_to_cpu(tmp_data->file_offset) - off;
+ l = range_start - off;
if (len < l)
l = len;
rc = smb3_simple_fallocate_write_range(xid, tcon,
@@ -3622,11 +3803,13 @@ static int smb3_simple_fallocate_range(unsigned int xid,
* until the end of the data or the end of the region
* we are supposed to fallocate, whichever comes first.
*/
- l = le64_to_cpu(tmp_data->length);
- if (len < l)
- l = len;
- off += l;
- len -= l;
+ if (off < range_end) {
+ l = range_end - off;
+ if (len < l)
+ l = len;
+ off += l;
+ len -= l;
+ }
tmp_data = &tmp_data[1];
out_data_len -= sizeof(struct file_allocated_range_buffer);
@@ -3634,7 +3817,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
out:
kfree(out_data);
- kfree(buf);
+ kvfree(buf);
return rc;
}
@@ -3647,18 +3830,22 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile = file->private_data;
long rc = -EOPNOTSUPP;
unsigned int xid;
- loff_t new_eof;
+ loff_t old_eof, new_eof;
+ struct smb2_file_all_info file_inf;
+ u64 asize;
+ int qrc;
xid = get_xid();
inode = d_inode(cfile->dentry);
cifsi = CIFS_I(inode);
+ old_eof = i_size_read(inode);
trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid,
tcon->ses->Suid, off, len);
/* if file not oplocked can't be sure whether asking to extend size */
if (!CIFS_CACHE_READ(cifsi))
- if (keep_size == false) {
+ if (!keep_size) {
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, off, len, rc);
free_xid(xid);
@@ -3668,21 +3855,96 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
/*
* Extending the file
*/
- if ((keep_size == false) && i_size_read(inode) < off + len) {
+ if (!keep_size && old_eof < off + len) {
rc = inode_newsize_ok(inode, off + len);
if (rc)
goto out;
+ /*
+ * A small range at or beyond EOF can be allocated by writing
+ * zeroes. For off > old_eof, this preserves the intervening
+ * hole instead of allocating from offset 0.
+ */
+ if (off > old_eof ||
+ (off == old_eof && old_eof != 0 &&
+ (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE))) {
+ if (len > 1024 * 1024) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ rc = smb3_simple_fallocate_range(xid, tcon, cfile,
+ off, len);
+ if (rc) {
+ spin_lock(&inode->i_lock);
+ cifsi->time = 0;
+ spin_unlock(&inode->i_lock);
+ goto out;
+ }
+
+ new_eof = off + len;
+ cifs_resize_file_locked(inode, new_eof);
+
+ qrc = SMB2_query_info(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, &file_inf);
+ spin_lock(&inode->i_lock);
+ if (qrc == 0) {
+ asize = le64_to_cpu(file_inf.AllocationSize);
+ inode->i_blocks = CIFS_INO_BLOCKS(asize);
+ } else {
+ cifsi->time = 0;
+ }
+ spin_unlock(&inode->i_lock);
+ goto out;
+ }
+
if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)
smb2_set_sparse(xid, tcon, cfile, inode, false);
new_eof = off + len;
+
+ qrc = SMB2_query_info(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, &file_inf);
+ if (qrc == 0)
+ asize = le64_to_cpu(file_inf.AllocationSize);
+
+ /*
+ * FILE_ALLOCATION_INFORMATION can only describe allocation up to
+ * new_eof. Some servers may accept it without allocating blocks,
+ * so refresh AllocationSize before updating i_blocks.
+ */
+ if (off == 0 || off == old_eof) {
+ if (qrc || asize < new_eof) {
+ rc = SMB2_set_allocation(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ cfile->pid, new_eof);
+ if (rc)
+ goto out;
+ }
+ }
+
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, new_eof);
- if (rc == 0) {
- netfs_resize_file(&cifsi->netfs, new_eof, true);
- cifs_setsize(inode, new_eof);
+ if (rc)
+ goto out;
+
+ cifs_resize_file_locked(inode, new_eof);
+
+ qrc = SMB2_query_info(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, &file_inf);
+ spin_lock(&inode->i_lock);
+ if (qrc == 0) {
+ asize = le64_to_cpu(file_inf.AllocationSize);
+ if (asize >= new_eof)
+ inode->i_blocks = CIFS_INO_BLOCKS(asize);
+ } else {
+ cifsi->time = 0;
}
+ spin_unlock(&inode->i_lock);
goto out;
}
@@ -3772,7 +4034,6 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
struct inode *inode = file_inode(file);
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifsFileInfo *cfile = file->private_data;
- struct netfs_inode *ictx = &cifsi->netfs;
loff_t old_eof, new_eof;
xid = get_xid();
@@ -3785,16 +4046,26 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
}
filemap_invalidate_lock(inode->i_mapping);
- rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1);
+ rc = filemap_write_and_wait_range(inode->i_mapping,
+ round_down(off, PAGE_SIZE),
+ old_eof - 1);
if (rc < 0)
goto out_2;
- truncate_pagecache_range(inode, off, old_eof);
- ictx->zero_point = old_eof;
netfs_wait_for_outstanding_io(inode);
+ /*
+ * Invalidate cached folios from the page containing off to EOF before
+ * moving data on the server, so subsequent reads do not see stale data.
+ */
+ truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1);
+ fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0);
- rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
- old_eof - off - len, off);
+ spin_lock(&inode->i_lock);
+ netfs_write_zero_point(inode, old_eof);
+ spin_unlock(&inode->i_lock);
+
+ rc = __smb2_copychunk_range(xid, cfile, cfile, off + len,
+ old_eof - off - len, off);
if (rc < 0)
goto out_2;
@@ -3807,8 +4078,10 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
rc = 0;
truncate_setsize(inode, new_eof);
+ spin_lock(&inode->i_lock);
netfs_resize_file(&cifsi->netfs, new_eof, true);
- ictx->zero_point = new_eof;
+ netfs_write_zero_point(inode, new_eof);
+ spin_unlock(&inode->i_lock);
fscache_resize_cookie(cifs_inode_cookie(inode), new_eof);
out_2:
filemap_invalidate_unlock(inode->i_mapping);
@@ -3825,7 +4098,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile = file->private_data;
struct inode *inode = file_inode(file);
struct cifsInodeInfo *cifsi = CIFS_I(inode);
- __u64 count, old_eof, new_eof;
+ loff_t old_eof, new_eof;
xid = get_xid();
@@ -3835,15 +4108,32 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
goto out;
}
- count = old_eof - off;
- new_eof = old_eof + len;
+ if (check_add_overflow(old_eof, len, &new_eof)) {
+ rc = -EFBIG;
+ goto out;
+ }
+ rc = inode_newsize_ok(inode, new_eof);
+ if (rc)
+ goto out;
+
+ /* SET_ZERO_DATA creates a hole only in a sparse file. */
+ rc = smb2_set_sparse(xid, tcon, cfile, inode, true);
+ if (rc)
+ goto out;
filemap_invalidate_lock(inode->i_mapping);
- rc = filemap_write_and_wait_range(inode->i_mapping, off, new_eof - 1);
+ rc = filemap_write_and_wait_range(inode->i_mapping,
+ round_down(off, PAGE_SIZE),
+ old_eof - 1);
if (rc < 0)
goto out_2;
- truncate_pagecache_range(inode, off, old_eof);
netfs_wait_for_outstanding_io(inode);
+ /*
+ * Invalidate cached folios from the page containing off to EOF before
+ * moving data on the server, so subsequent reads do not see stale data.
+ */
+ truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1);
+ fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0);
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, new_eof);
@@ -3851,13 +4141,22 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
goto out_2;
truncate_setsize(inode, new_eof);
+ spin_lock(&inode->i_lock);
netfs_resize_file(&cifsi->netfs, i_size_read(inode), true);
+ spin_unlock(&inode->i_lock);
fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode));
- rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len);
+ /*
+ * Move [off, old_eof) right by len. The helper copies backwards if the
+ * source and destination ranges overlap.
+ */
+ rc = __smb2_copychunk_range(xid, cfile, cfile, off, old_eof - off,
+ off + len);
if (rc < 0)
goto out_2;
- cifsi->netfs.zero_point = new_eof;
+ spin_lock(&inode->i_lock);
+ netfs_write_zero_point(inode, new_eof);
+ spin_unlock(&inode->i_lock);
rc = smb3_zero_data(file, tcon, off, len, xid);
if (rc < 0)
@@ -3898,7 +4197,7 @@ static loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offs
* some servers (Windows2016) will not reflect recent writes in
* QUERY_ALLOCATED_RANGES until SMB2_flush is called.
*/
- wrcfile = find_writable_file(cifsi, FIND_WR_ANY);
+ wrcfile = find_writable_file(cifsi, FIND_ANY);
if (wrcfile) {
filemap_write_and_wait(inode->i_mapping);
smb2_flush_file(xid, tcon, &wrcfile->fid);
@@ -4046,7 +4345,7 @@ static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
return smb3_collapse_range(file, tcon, off, len);
else if (mode == FALLOC_FL_INSERT_RANGE)
return smb3_insert_range(file, tcon, off, len);
- else if (mode == 0)
+ else if (mode == FALLOC_FL_ALLOCATE_RANGE)
return smb3_simple_falloc(file, tcon, off, len, false);
return -EOPNOTSUPP;
@@ -4334,11 +4633,13 @@ static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst
unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm);
unsigned int iv_size = crypto_aead_ivsize(tfm);
unsigned int len;
+ int ret;
u8 *p;
- *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig);
- if (IS_ERR_VALUE((long)(int)*num_sgs))
- return ERR_PTR(*num_sgs);
+ ret = cifs_get_num_sgs(rqst, num_rqst, sig);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ *num_sgs = ret;
len = iv_size;
len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1);
@@ -4470,7 +4771,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE);
else
rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE);
-
+ memzero_explicit(key, sizeof(key));
if (rc) {
cifs_server_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc);
return rc;
@@ -4579,10 +4880,10 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst,
size_t cur_size = 0;
rc = netfs_alloc_folioq_buffer(NULL, &buffer, &cur_size,
size, GFP_NOFS);
+ new->rq_buffer = buffer;
if (rc < 0)
goto err_free;
- new->rq_buffer = buffer;
iov_iter_folio_queue(&new->rq_iter, ITER_SOURCE,
buffer, 0, 0, size);
@@ -4681,9 +4982,15 @@ cifs_copy_folioq_to_iter(struct folio_queue *folioq, size_t data_size,
{
for (; folioq; folioq = folioq->next) {
for (int s = 0; s < folioq_count(folioq); s++) {
- struct folio *folio = folioq_folio(folioq, s);
- size_t fsize = folio_size(folio);
- size_t n, len = umin(fsize - skip, data_size);
+ struct folio *folio;
+ size_t fsize, n, len;
+
+ if (data_size == 0)
+ return 0;
+
+ folio = folioq_folio(folioq, s);
+ fsize = folio_size(folio);
+ len = umin(fsize - skip, data_size);
n = copy_folio_to_iter(folio, skip, len, iter);
if (n != len) {
@@ -4696,6 +5003,12 @@ cifs_copy_folioq_to_iter(struct folio_queue *folioq, size_t data_size,
}
}
+ if (data_size != 0) {
+ cifs_dbg(VFS, "%s: short copy, %zu bytes missing\n",
+ __func__, data_size);
+ return smb_EIO2(smb_eio_trace_rx_copy_to_iter, 0, data_size);
+ }
+
return 0;
}
@@ -4706,6 +5019,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
{
unsigned int data_offset;
unsigned int data_len;
+ unsigned int end_off;
unsigned int cur_off;
unsigned int cur_page_idx;
unsigned int pad_len;
@@ -4810,7 +5124,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
}
/* Copy the data to the output I/O iterator. */
- rdata->result = cifs_copy_folioq_to_iter(buffer, buffer_len,
+ rdata->result = cifs_copy_folioq_to_iter(buffer, data_len,
cur_off, &rdata->subreq.io_iter);
if (rdata->result != 0) {
if (is_offloaded)
@@ -4819,9 +5133,10 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
dequeue_mid(server, mid, rdata->result);
return 0;
}
- rdata->got_bytes = buffer_len;
+ rdata->got_bytes = data_len;
- } else if (buf_len >= data_offset + data_len) {
+ } else if (!check_add_overflow(data_offset, data_len, &end_off) &&
+ buf_len >= end_off) {
/* read response payload is in buf */
WARN_ONCE(buffer, "read data can be either in buf or in buffer");
copied = copy_to_iter(buf + data_offset, data_len, &rdata->subreq.io_iter);
@@ -4945,6 +5260,14 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
goto free_dw;
server->total_read += rc;
+ if (le32_to_cpu(tr_hdr->OriginalMessageSize) <
+ server->vals->read_rsp_size) {
+ cifs_server_dbg(VFS, "OriginalMessageSize %u too small for read response (%zu)\n",
+ le32_to_cpu(tr_hdr->OriginalMessageSize),
+ server->vals->read_rsp_size);
+ rc = -EINVAL;
+ goto discard_data;
+ }
len = le32_to_cpu(tr_hdr->OriginalMessageSize) -
server->vals->read_rsp_size;
dw->len = len;
@@ -5057,14 +5380,29 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
length = decrypt_raw_data(server, buf, buf_size, NULL, false);
if (length)
return length;
+ pdu_length = buf_size;
next_is_large = server->large_buf;
one_more:
shdr = (struct smb2_hdr *)buf;
next_cmd = le32_to_cpu(shdr->NextCommand);
+ server->total_read = next_cmd ? next_cmd : pdu_length;
+
+ if (*num_mids >= MAX_COMPOUND) {
+ cifs_server_dbg(VFS, "too many PDUs in compound\n");
+ return -1;
+ }
+
if (next_cmd) {
- if (WARN_ON_ONCE(next_cmd > pdu_length))
+ if (next_cmd < MID_HEADER_SIZE(server) ||
+ next_cmd > pdu_length ||
+ pdu_length - next_cmd < MID_HEADER_SIZE(server)) {
+ unsigned int max_next = pdu_length > (unsigned int)MID_HEADER_SIZE(server) ?
+ pdu_length - (unsigned int)MID_HEADER_SIZE(server) : 0;
+ cifs_server_dbg(VFS, "invalid NextCommand offset %u out of range [%zu, %u]\n",
+ next_cmd, MID_HEADER_SIZE(server), max_next);
return -1;
+ }
if (next_is_large)
next_buffer = (char *)cifs_buf_get();
else
@@ -5085,10 +5423,6 @@ one_more:
mid_entry->resp_buf_size = server->pdu_size;
}
- if (*num_mids >= MAX_COMPOUND) {
- cifs_server_dbg(VFS, "too many PDUs in compound\n");
- return -1;
- }
bufs[*num_mids] = buf;
mids[(*num_mids)++] = mid_entry;
@@ -5104,6 +5438,7 @@ one_more:
server->bigbuf = buf = next_buffer;
else
server->smallbuf = buf = next_buffer;
+ next_buffer = NULL;
goto one_more;
} else if (ret != 0) {
/*
@@ -5186,7 +5521,7 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
{
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms;
- struct cifs_open_info_data idata;
+ struct cifs_open_info_data idata = {};
struct cifs_io_parms io_parms = {};
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_fid fid;
@@ -5235,16 +5570,13 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
data = (u8 *)symname_utf16;
break;
case S_IFSOCK:
- type_len = 8;
- strscpy(type, "LnxSOCK");
- data = (u8 *)&pdev;
- data_len = sizeof(pdev);
+ /* SFU socket is system file with one zero byte */
+ type_len = 1;
+ type[0] = '\0';
break;
case S_IFIFO:
- type_len = 8;
- strscpy(type, "LnxFIFO");
- data = (u8 *)&pdev;
- data_len = sizeof(pdev);
+ /* SFU fifo is system file which is empty */
+ type_len = 0;
break;
default:
rc = -EPERM;
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 04e361ed2356..880ce12f50c4 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -36,7 +36,6 @@
#include "../common/smb2status.h"
#include "smb2glob.h"
#include "cifs_spnego.h"
-#include "../common/smbdirect/smbdirect.h"
#include "smbdirect.h"
#include "trace.h"
#ifdef CONFIG_CIFS_DFS_UPCALL
@@ -190,18 +189,19 @@ cifs_chan_skip_or_disable(struct cifs_ses *ses,
spin_unlock(&ses->chan_lock);
/*
- * the above reference of server by channel
- * needs to be dropped without holding chan_lock
- * as cifs_put_tcp_session takes a higher lock
- * i.e. cifs_tcp_ses_lock
+ * signal the channel and its primary server to
+ * reconnect before dropping the above reference of
+ * server by channel, which is done without holding
+ * chan_lock as cifs_put_tcp_session takes a higher
+ * lock i.e. cifs_tcp_ses_lock
*/
- cifs_put_tcp_session(server, from_reconnect);
-
cifs_signal_cifsd_for_reconnect(server, false);
/* mark primary server as needing reconnect */
pserver = server->primary_server;
cifs_signal_cifsd_for_reconnect(pserver, false);
+
+ cifs_put_tcp_session(server, from_reconnect);
skip_terminate:
return -EHOSTDOWN;
}
@@ -637,10 +637,16 @@ build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt)
pneg_ctxt->DataLength =
cpu_to_le16(sizeof(struct smb2_compression_capabilities_context)
- sizeof(struct smb2_neg_context));
- pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3);
+ /*
+ * Pattern_V1 is useful only as part of a chained transform. LZ77 remains
+ * the preferred general-purpose algorithm selected by this client.
+ */
+ pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(4);
+ pneg_ctxt->Flags = SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED;
pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77;
pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF;
pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1;
+ pneg_ctxt->CompressionAlgorithms[3] = SMB3_COMPRESS_PATTERN;
}
static unsigned int
@@ -828,9 +834,12 @@ static void decode_compress_ctx(struct TCP_Server_Info *server,
struct smb2_compression_capabilities_context *ctxt)
{
unsigned int len = le16_to_cpu(ctxt->DataLength);
- __le16 alg;
+ unsigned int count, i;
server->compression.enabled = false;
+ server->compression.chained = false;
+ server->compression.pattern = false;
+ server->compression.alg = SMB3_COMPRESS_NONE;
/*
* Caller checked that DataLength remains within SMB boundary. We still
@@ -842,20 +851,37 @@ static void decode_compress_ctx(struct TCP_Server_Info *server,
return;
}
- if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) {
+ count = le16_to_cpu(ctxt->CompressionAlgorithmCount);
+ if (!count || count > ARRAY_SIZE(ctxt->CompressionAlgorithms) ||
+ len < 8 + count * sizeof(__le16)) {
pr_warn_once("invalid SMB3 compress algorithm count\n");
return;
}
- alg = ctxt->CompressionAlgorithms[0];
-
- /* 'NONE' (0) compressor type is never negotiated */
- if (alg == 0 || le16_to_cpu(alg) > 3) {
- pr_warn_once("invalid compression algorithm '%u'\n", alg);
+ if (ctxt->Flags != SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE &&
+ ctxt->Flags != SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED) {
+ pr_warn_once("invalid SMB3 compression flags\n");
return;
}
- server->compression.alg = alg;
+ for (i = 0; i < count; i++) {
+ /* Record the intersection supported by the shared SMB codec. */
+ if (ctxt->CompressionAlgorithms[i] == SMB3_COMPRESS_LZ77)
+ server->compression.alg = SMB3_COMPRESS_LZ77;
+ else if (ctxt->CompressionAlgorithms[i] == SMB3_COMPRESS_PATTERN)
+ server->compression.pattern = true;
+ }
+ if (server->compression.alg != SMB3_COMPRESS_LZ77)
+ return;
+
+ /*
+ * Pattern_V1 cannot appear in an unchained transform even if a broken
+ * peer lists it in the algorithm array.
+ */
+ server->compression.chained =
+ ctxt->Flags == SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED;
+ if (!server->compression.chained)
+ server->compression.pattern = false;
server->compression.enabled = true;
}
@@ -1714,17 +1740,30 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
is_binding = (ses->ses_status == SES_GOOD);
spin_unlock(&ses->ses_lock);
+ /*
+ * Per MS-SMB2 3.2.5.3, Session.SessionKey is the first 16 bytes of the
+ * GSS cryptographic key, right-padded with zero bytes if shorter.
+ * Allocate at least SMB2_NTLMV2_SESSKEY_SIZE bytes (zeroed) so the KDF
+ * input buffer is always valid for HMAC-SHA256 even with deprecated
+ * Kerberos enctypes that return a short session key.
+ */
+ if (unlikely(msg->sesskey_len < SMB2_NTLMV2_SESSKEY_SIZE))
+ cifs_dbg(VFS,
+ "short GSS session key (%u bytes); zero-padding per MS-SMB2 3.2.5.3\n",
+ msg->sesskey_len);
+
kfree_sensitive(ses->auth_key.response);
- ses->auth_key.response = kmemdup(msg->data,
- msg->sesskey_len,
- GFP_KERNEL);
+ ses->auth_key.len = max_t(unsigned int, msg->sesskey_len,
+ SMB2_NTLMV2_SESSKEY_SIZE);
+ ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL);
if (!ses->auth_key.response) {
cifs_dbg(VFS, "%s: can't allocate (%u bytes) memory\n",
- __func__, msg->sesskey_len);
+ __func__, ses->auth_key.len);
+ ses->auth_key.len = 0;
rc = -ENOMEM;
goto out_put_spnego_key;
}
- ses->auth_key.len = msg->sesskey_len;
+ memcpy(ses->auth_key.response, msg->data, msg->sesskey_len);
sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
sess_data->iov[1].iov_len = msg->secblob_len;
@@ -2117,8 +2156,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp);
if (unc_path_len <= 0) {
- kfree(unc_path);
- return -EINVAL;
+ rc = -EINVAL;
+ goto free_unc_path;
}
unc_path_len *= 2;
@@ -2127,10 +2166,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
atomic_set(&tcon->num_remote_opens, 0);
rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, server,
(void **) &req, &total_len);
- if (rc) {
- kfree(unc_path);
- return rc;
- }
+ if (rc)
+ goto free_unc_path;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
@@ -2221,6 +2258,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
tcon_exit:
free_rsp_buf(resp_buftype, rsp);
+free_unc_path:
kfree(unc_path);
return rc;
@@ -2258,7 +2296,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
}
spin_unlock(&ses->chan_lock);
- invalidate_all_cached_dirs(tcon);
+ invalidate_all_cached_dirs(tcon, true);
rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, server,
(void **) &req,
@@ -2359,9 +2397,9 @@ parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info,
memset(posix, 0, sizeof(*posix));
- posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0));
- posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4));
- posix->mode = le32_to_cpu(*(__le32 *)(beg + 8));
+ posix->nlink = get_unaligned_le32(beg);
+ posix->reparse_tag = get_unaligned_le32(beg + 4);
+ posix->mode = get_unaligned_le32(beg + 8);
sid = beg + 12;
sid_len = posix_info_sid_size(sid, end);
@@ -3044,7 +3082,8 @@ replay_again:
}
trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid,
- CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES);
+ CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES,
+ rsp->OplockLevel);
SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId);
@@ -3249,7 +3288,7 @@ SMB2_open_free(struct smb_rqst *rqst)
int
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
- __u8 *oplock, struct smb2_file_all_info *buf,
+ __u8 *oplock, struct cifs_open_info_data *buf,
struct create_posix_rsp *posix,
struct kvec *err_iov, int *buftype)
{
@@ -3264,9 +3303,12 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
int rc = 0;
int flags = 0;
int retries = 0, cur_sleep = 0;
+ struct smb2_file_all_info *file_info = buf ? &buf->fi : NULL;
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
oparms->replay = !!(retries);
@@ -3321,9 +3363,6 @@ replay_again:
goto creat_exit;
} else if (rsp == NULL) /* unlikely to happen, but safer to check */
goto creat_exit;
- else
- trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid,
- oparms->create_options, oparms->desired_access);
atomic_inc(&tcon->num_remote_opens);
oparms->fid->persistent_fid = rsp->PersistentFileId;
@@ -3333,21 +3372,27 @@ replay_again:
oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId);
#endif /* CIFS_DEBUG2 */
- if (buf) {
- buf->CreationTime = rsp->CreationTime;
- buf->LastAccessTime = rsp->LastAccessTime;
- buf->LastWriteTime = rsp->LastWriteTime;
- buf->ChangeTime = rsp->ChangeTime;
- buf->AllocationSize = rsp->AllocationSize;
- buf->EndOfFile = rsp->EndofFile;
- buf->Attributes = rsp->FileAttributes;
- buf->NumberOfLinks = cpu_to_le32(1);
- buf->DeletePending = 0; /* successful open = not delete pending */
+ if (file_info) {
+ buf->contains_posix_file_info = false;
+ file_info->CreationTime = rsp->CreationTime;
+ file_info->LastAccessTime = rsp->LastAccessTime;
+ file_info->LastWriteTime = rsp->LastWriteTime;
+ file_info->ChangeTime = rsp->ChangeTime;
+ file_info->AllocationSize = rsp->AllocationSize;
+ file_info->EndOfFile = rsp->EndofFile;
+ file_info->Attributes = rsp->FileAttributes;
+ file_info->NumberOfLinks = cpu_to_le32(1);
+ buf->unknown_nlink = true;
+ file_info->DeletePending = 0; /* successful open = not delete pending */
}
rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch,
- oparms->fid->lease_key, oplock, buf, posix);
+ oparms->fid->lease_key, oplock, file_info, posix);
+
+ trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid,
+ oparms->create_options, oparms->desired_access,
+ *oplock);
creat_exit:
SMB2_open_free(&rqst);
free_rsp_buf(resp_buftype, rsp);
@@ -3491,6 +3536,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
@@ -3612,14 +3659,14 @@ ioctl_exit:
int
SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
- u64 persistent_fid, u64 volatile_fid)
+ u64 persistent_fid, u64 volatile_fid,
+ __u16 compression_state)
{
int rc;
struct compress_ioctl fsctl_input;
char *ret_data = NULL;
- fsctl_input.CompressionState =
- cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
+ fsctl_input.CompressionState = cpu_to_le16(compression_state);
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
FSCTL_SET_COMPRESSION,
@@ -3685,6 +3732,8 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
query_attrs = false;
server = cifs_pick_channel(ses);
@@ -3897,6 +3946,8 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
allocated = false;
server = cifs_pick_channel(ses);
@@ -3989,24 +4040,6 @@ int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
NULL);
}
-#if 0
-/* currently unused, as now we are doing compounding instead (see smb311_posix_query_path_info) */
-int
-SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon,
- u64 persistent_fid, u64 volatile_fid,
- struct smb311_posix_qinfo *data, u32 *plen)
-{
- size_t output_len = sizeof(struct smb311_posix_qinfo *) +
- (sizeof(struct smb_sid) * 2) + (PATH_MAX * 2);
- *plen = 0;
-
- return query_info(xid, tcon, persistent_fid, volatile_fid,
- SMB_FIND_FILE_POSIX_INFO, SMB2_O_INFO_FILE, 0,
- output_len, sizeof(struct smb311_posix_qinfo), (void **)&data, plen);
- /* Note caller must free "data" (passed in above). It may be allocated in query_info call */
-}
-#endif
-
int
SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
@@ -4087,6 +4120,8 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
@@ -4429,6 +4464,8 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
@@ -4529,8 +4566,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
if (rc)
return rc;
- if (server == NULL)
- return -ECONNABORTED;
+ if (!server) {
+ rc = -ECONNABORTED;
+ goto free_req;
+ }
shdr = &req->hdr;
shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid);
@@ -4561,8 +4600,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->subreq.io_iter,
true, need_invalidate);
- if (!rdata->mr)
- return -EAGAIN;
+ if (!rdata->mr) {
+ rc = -EAGAIN;
+ goto free_req;
+ }
req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
if (need_invalidate)
@@ -4572,9 +4613,7 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
req->ReadChannelInfoLength =
cpu_to_le16(sizeof(struct smbdirect_buffer_descriptor_v1));
v1 = (struct smbdirect_buffer_descriptor_v1 *) &req->Buffer[0];
- v1->offset = cpu_to_le64(rdata->mr->mr->iova);
- v1->token = cpu_to_le32(rdata->mr->mr->rkey);
- v1->length = cpu_to_le32(rdata->mr->mr->length);
+ smbd_mr_fill_buffer_descriptor(rdata->mr, v1);
*total_len += sizeof(*v1) - 1;
}
@@ -4605,6 +4644,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
*buf = req;
return rc;
+
+free_req:
+ cifs_small_buf_release(req);
+ return rc;
}
static void
@@ -4614,6 +4657,7 @@ smb2_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
struct netfs_inode *ictx = netfs_inode(rdata->rreq->inode);
struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink);
struct smb2_hdr *shdr = (struct smb2_hdr *)rdata->iov[0].iov_base;
+ struct inode *inode = &ictx->inode;
struct cifs_credits credits = {
.value = 0,
.instance = 0,
@@ -4727,7 +4771,7 @@ do_retry:
} else {
size_t trans = rdata->subreq.transferred + rdata->got_bytes;
if (trans < rdata->subreq.len &&
- rdata->subreq.start + trans >= ictx->remote_i_size) {
+ rdata->subreq.start + trans >= netfs_read_remote_i_size(inode)) {
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
rdata->result = 0;
}
@@ -4851,6 +4895,7 @@ out:
smb2_should_replay(tcon,
&rdata->retries,
&rdata->cur_sleep)) {
+ rdata->replay = true;
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed);
__set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags);
}
@@ -4960,7 +5005,7 @@ smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
unsigned int rreq_debug_id = wdata->rreq->debug_id;
unsigned int subreq_debug_index = wdata->subreq.debug_index;
ssize_t result = 0;
- size_t written;
+ size_t written = 0;
WARN_ONCE(wdata->server != server,
"wdata server %p != mid server %p",
@@ -5173,9 +5218,7 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
req->WriteChannelInfoLength =
cpu_to_le16(sizeof(struct smbdirect_buffer_descriptor_v1));
v1 = (struct smbdirect_buffer_descriptor_v1 *) &req->Buffer[0];
- v1->offset = cpu_to_le64(wdata->mr->mr->iova);
- v1->token = cpu_to_le32(wdata->mr->mr->rkey);
- v1->length = cpu_to_le32(wdata->mr->mr->length);
+ smbd_mr_fill_buffer_descriptor(wdata->mr, v1);
rqst.rq_iov[0].iov_len += sizeof(*v1);
@@ -5325,7 +5368,10 @@ replay_again:
memset(&rqst, 0, sizeof(struct smb_rqst));
rqst.rq_iov = iov;
- rqst.rq_nvec = n_vec + 1;
+ /* iov[0] is the SMB header; move payload to rq_iter for encryption safety */
+ rqst.rq_nvec = 1;
+ iov_iter_kvec(&rqst.rq_iter, ITER_SOURCE, &iov[1], n_vec,
+ io_parms->length);
if (retries) {
/* Back-off before retry */
@@ -5372,7 +5418,7 @@ int posix_info_sid_size(const void *beg, const void *end)
size_t subauth;
int total;
- if (beg + 1 > end)
+ if (beg + 2 > end)
return -1;
subauth = *(u8 *)(beg+1);
@@ -5640,6 +5686,8 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
if (srch_inf->ntwrk_buf_start) {
if (srch_inf->smallBuf)
cifs_small_buf_release(srch_inf->ntwrk_buf_start);
+ else if (srch_inf->is_dynamic_buf)
+ kfree(srch_inf->ntwrk_buf_start);
else
cifs_buf_release(srch_inf->ntwrk_buf_start);
}
@@ -5659,12 +5707,18 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n",
srch_inf->entries_in_buffer, srch_inf->index_of_last_entry,
srch_inf->srch_entries_start, srch_inf->last_entry);
- if (resp_buftype == CIFS_LARGE_BUFFER)
+ if (resp_buftype == CIFS_LARGE_BUFFER) {
srch_inf->smallBuf = false;
- else if (resp_buftype == CIFS_SMALL_BUFFER)
+ srch_inf->is_dynamic_buf = false;
+ } else if (resp_buftype == CIFS_SMALL_BUFFER) {
srch_inf->smallBuf = true;
- else
+ srch_inf->is_dynamic_buf = false;
+ } else if (resp_buftype == CIFS_DYNAMIC_BUFFER) {
+ srch_inf->smallBuf = false;
+ srch_inf->is_dynamic_buf = true;
+ } else {
cifs_tcon_dbg(VFS, "Invalid search buffer type\n");
+ }
return 0;
}
@@ -5687,6 +5741,8 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
@@ -5905,6 +5961,25 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
}
int
+SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 pid,
+ loff_t allocation_size)
+{
+ struct smb2_file_alloc_info info;
+ void *data;
+ unsigned int size;
+
+ info.AllocationSize = cpu_to_le64(allocation_size);
+
+ data = &info;
+ size = sizeof(struct smb2_file_alloc_info);
+
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ pid, FILE_ALLOCATION_INFORMATION, SMB2_O_INFO_FILE,
+ 0, 1, &data, &size);
+}
+
+int
SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
struct smb_ntsd *pnntsd, int pacllen, int aclflag)
@@ -6162,8 +6237,8 @@ replay_again:
max_len = sizeof(struct smb3_fs_ss_info);
min_len = sizeof(struct smb3_fs_ss_info);
} else if (level == FS_VOLUME_INFORMATION) {
- max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN;
- min_len = sizeof(struct smb3_fs_vol_info);
+ max_len = sizeof(struct filesystem_vol_info) + MAX_VOL_LABEL_LEN;
+ min_len = sizeof(struct filesystem_vol_info);
} else {
cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level);
return -EINVAL;
@@ -6218,9 +6293,9 @@ replay_again:
tcon->perf_sector_size =
le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf);
} else if (level == FS_VOLUME_INFORMATION) {
- struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *)
+ struct filesystem_vol_info *vol_info = (struct filesystem_vol_info *)
(offset + (char *)rsp);
- tcon->vol_serial_number = vol_info->VolumeSerialNumber;
+ tcon->vol_serial_number = le32_to_cpu(vol_info->VolumeSerialNumber);
tcon->vol_create_time = vol_info->VolumeCreationTime;
}
@@ -6292,6 +6367,11 @@ replay_again:
smb2_set_replay(server, &rqst);
}
+ trace_smb3_lock_enter(xid, persist_fid, tcon->tid, tcon->ses->Suid,
+ le64_to_cpu(buf[0].Offset),
+ le64_to_cpu(buf[0].Length),
+ le32_to_cpu(buf[0].Flags), num_lock, 0);
+
rc = cifs_send_recv(xid, tcon->ses, server,
&rqst, &resp_buf_type, flags,
&rsp_iov);
@@ -6300,7 +6380,15 @@ replay_again:
cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc);
cifs_stats_fail_inc(tcon, SMB2_LOCK_HE);
trace_smb3_lock_err(xid, persist_fid, tcon->tid,
- tcon->ses->Suid, rc);
+ tcon->ses->Suid,
+ le64_to_cpu(buf[0].Offset),
+ le64_to_cpu(buf[0].Length),
+ le32_to_cpu(buf[0].Flags), num_lock, rc);
+ } else {
+ trace_smb3_lock_done(xid, persist_fid, tcon->tid, tcon->ses->Suid,
+ le64_to_cpu(buf[0].Offset),
+ le64_to_cpu(buf[0].Length),
+ le32_to_cpu(buf[0].Flags), num_lock, 0);
}
if (is_replayable_error(rc) &&
diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h
index 78bb99f29d38..ab6c667bebc0 100644
--- a/fs/smb/client/smb2pdu.h
+++ b/fs/smb/client/smb2pdu.h
@@ -21,30 +21,6 @@
/* The total header size for SMB2 read and write */
#define SMB2_READWRITE_PDU_HEADER_SIZE (48 + sizeof(struct smb2_hdr))
-/* See MS-SMB2 2.2.43 */
-struct smb2_rdma_transform {
- __le16 RdmaDescriptorOffset;
- __le16 RdmaDescriptorLength;
- __le32 Channel; /* for values see channel description in smb2 read above */
- __le16 TransformCount;
- __le16 Reserved1;
- __le32 Reserved2;
-} __packed;
-
-/* TransformType */
-#define SMB2_RDMA_TRANSFORM_TYPE_ENCRYPTION 0x0001
-#define SMB2_RDMA_TRANSFORM_TYPE_SIGNING 0x0002
-
-struct smb2_rdma_crypto_transform {
- __le16 TransformType;
- __le16 SignatureLength;
- __le16 NonceLength;
- __u16 Reserved;
- __u8 Signature[]; /* variable length */
- /* u8 Nonce[] */
- /* followed by padding */
-} __packed;
-
/*
* Definitions for SMB2 Protocol Data Units (network frames)
*
@@ -195,10 +171,6 @@ struct network_resiliency_req {
#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */
-struct compress_ioctl {
- __le16 CompressionState; /* See cifspdu.h for possible flag values */
-} __packed;
-
/*
* Maximum number of iovs we need for an ioctl request.
* [0] : struct smb2_ioctl_req
@@ -224,7 +196,7 @@ struct smb2_file_reparse_point_info {
__le32 Tag;
} __packed;
-/* See MS-FSCC 2.4.21 */
+/* See MS-FSCC 2.4.26 */
struct smb2_file_id_information {
__le64 VolumeSerialNumber;
__u64 PersistentFileId; /* opaque endianness */
@@ -251,7 +223,10 @@ struct smb2_file_id_extd_directory_info {
extern char smb2_padding[7];
-/* equivalent of the contents of SMB3.1.1 POSIX open context response */
+/*
+ * See POSIX-SMB2 2.2.14.2.16
+ * Link: https://gitlab.com/samba-team/smb3-posix-spec/-/blob/master/smb3_posix_extensions.md
+ */
struct create_posix_rsp {
u32 nlink;
u32 reparse_tag;
diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
index 881e42cf66ce..2e9f70096825 100644
--- a/fs/smb/client/smb2proto.h
+++ b/fs/smb/client/smb2proto.h
@@ -14,7 +14,6 @@
#include <linux/key-type.h>
#include "cached_dir.h"
-struct statfs;
struct smb_rqst;
/*
@@ -24,6 +23,12 @@ struct smb_rqst;
*/
int map_smb2_to_linux_error(char *buf, bool log_err);
int smb2_init_maperror(void);
+#if IS_ENABLED(CONFIG_SMB_KUNIT_TESTS)
+const struct status_to_posix_error *smb2_get_err_map_test(__u32 smb2_status);
+extern const struct status_to_posix_error *smb2_error_map_table_test;
+extern unsigned int smb2_error_map_num;
+#endif
+
int smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
struct TCP_Server_Info *server);
unsigned int smb2_calc_size(void *buf);
@@ -131,7 +136,7 @@ int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
struct cifs_tcon *tcon, const struct nls_table *cp);
int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
- __le16 *path, __u8 *oplock, struct smb2_file_all_info *buf,
+ __le16 *path, __u8 *oplock, struct cifs_open_info_data *buf,
struct create_posix_rsp *posix, struct kvec *err_iov,
int *buftype);
int SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
@@ -167,9 +172,6 @@ int SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst,
struct cifs_tcon *tcon, struct TCP_Server_Info *server,
u64 persistent_fid, u64 volatile_fid);
void SMB2_flush_free(struct smb_rqst *rqst);
-int SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon,
- u64 persistent_fid, u64 volatile_fid,
- struct smb311_posix_qinfo *data, u32 *plen);
int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
struct smb2_file_all_info *data);
@@ -202,6 +204,9 @@ void SMB2_query_directory_free(struct smb_rqst *rqst);
int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 pid,
loff_t new_eof);
+int SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 pid,
+ loff_t allocation_size);
int SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
struct smb_rqst *rqst, u64 persistent_fid,
u64 volatile_fid, u32 pid, u8 info_class, u8 info_type,
@@ -214,7 +219,8 @@ int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
struct smb2_file_full_ea_info *buf, int len);
int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
- u64 persistent_fid, u64 volatile_fid);
+ u64 persistent_fid, u64 volatile_fid,
+ __u16 compression_state);
int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
const u64 persistent_fid, const u64 volatile_fid,
__u8 oplock_level);
@@ -255,7 +261,6 @@ int smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length,
char *data);
void smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
struct kstatfs *kst);
-int smb3_crypto_shash_allocate(struct TCP_Server_Info *server);
void smb311_update_preauth_hash(struct cifs_ses *ses,
struct TCP_Server_Info *server,
struct kvec *iov, int nvec);
diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
index 8b9000a83181..c407f30e0040 100644
--- a/fs/smb/client/smb2transport.c
+++ b/fs/smb/client/smb2transport.c
@@ -19,7 +19,9 @@
#include <linux/mempool.h>
#include <linux/highmem.h>
#include <crypto/aead.h>
+#include <crypto/aes-cbc-macs.h>
#include <crypto/sha2.h>
+#include <crypto/utils.h>
#include "cifsglob.h"
#include "cifsproto.h"
#include "smb2proto.h"
@@ -27,14 +29,6 @@
#include "../common/smb2status.h"
#include "smb2glob.h"
-int
-smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
-{
- struct cifs_secmech *p = &server->secmech;
-
- return cifs_alloc_hash("cmac(aes)", &p->aes_cmac);
-}
-
static
int smb3_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
{
@@ -175,7 +169,9 @@ smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid)
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
if (tcon->tid != tid)
continue;
+ spin_lock(&tcon->tc_lock);
++tcon->tc_count;
+ spin_unlock(&tcon->tc_lock);
trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
netfs_trace_tcon_ref_get_find_sess_tcon);
return tcon;
@@ -210,8 +206,7 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid)
}
static int
-smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
- bool allocate_crypto)
+smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{
int rc;
unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
@@ -254,17 +249,19 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
if (!rc)
memcpy(shdr->Signature, smb2_signature, SMB2_SIGNATURE_SIZE);
+ memzero_explicit(key, sizeof(key));
+ memzero_explicit(&hmac_ctx, sizeof(hmac_ctx));
return rc;
}
-static int generate_key(struct cifs_ses *ses, struct kvec label,
- struct kvec context, __u8 *key, unsigned int key_size)
+static void generate_key(struct cifs_ses *ses, struct kvec label,
+ struct kvec context, __u8 *key, unsigned int key_size,
+ unsigned int full_key_size)
{
unsigned char zero = 0x0;
__u8 i[4] = {0, 0, 0, 1};
__u8 L128[4] = {0, 0, 0, 128};
__u8 L256[4] = {0, 0, 1, 0};
- int rc = 0;
unsigned char prfhash[SMB2_HMACSHA256_SIZE];
struct TCP_Server_Info *server = ses->server;
struct hmac_sha256_ctx hmac_ctx;
@@ -272,14 +269,8 @@ static int generate_key(struct cifs_ses *ses, struct kvec label,
memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
memset(key, 0x0, key_size);
- rc = smb3_crypto_shash_allocate(server);
- if (rc) {
- cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__);
- return rc;
- }
-
hmac_sha256_init_usingrawkey(&hmac_ctx, ses->auth_key.response,
- SMB2_NTLMV2_SESSKEY_SIZE);
+ full_key_size);
hmac_sha256_update(&hmac_ctx, i, 4);
hmac_sha256_update(&hmac_ctx, label.iov_base, label.iov_len);
hmac_sha256_update(&hmac_ctx, &zero, 1);
@@ -294,7 +285,7 @@ static int generate_key(struct cifs_ses *ses, struct kvec label,
hmac_sha256_final(&hmac_ctx, prfhash);
memcpy(key, prfhash, key_size);
- return 0;
+ memzero_explicit(prfhash, sizeof(prfhash));
}
struct derivation {
@@ -313,7 +304,7 @@ generate_smb3signingkey(struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct derivation_triplet *ptriplet)
{
- int rc;
+ unsigned int full_key_size = SMB2_NTLMV2_SESSKEY_SIZE;
bool is_binding = false;
int chan_index = 0;
@@ -344,19 +335,26 @@ generate_smb3signingkey(struct cifs_ses *ses,
*/
if (is_binding) {
- rc = generate_key(ses, ptriplet->signing.label,
- ptriplet->signing.context,
- ses->chans[chan_index].signkey,
- SMB3_SIGN_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(ses, ptriplet->signing.label,
+ ptriplet->signing.context,
+ ses->chans[chan_index].signkey, SMB3_SIGN_KEY_SIZE,
+ SMB2_NTLMV2_SESSKEY_SIZE);
} else {
- rc = generate_key(ses, ptriplet->signing.label,
- ptriplet->signing.context,
- ses->smb3signingkey,
- SMB3_SIGN_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(ses, ptriplet->signing.label,
+ ptriplet->signing.context, ses->smb3signingkey,
+ SMB3_SIGN_KEY_SIZE, SMB2_NTLMV2_SESSKEY_SIZE);
+
+ /*
+ * Per MS-SMB2 3.2.5.3.1, signing key always uses Session.SessionKey
+ * (first 16 bytes). Encryption/decryption keys use
+ * Session.FullSessionKey when dialect is 3.1.1 and cipher is
+ * AES-256-CCM or AES-256-GCM, otherwise Session.SessionKey.
+ */
+
+ if (server->dialect == SMB311_PROT_ID &&
+ (server->cipher_type == SMB2_ENCRYPTION_AES256_CCM ||
+ server->cipher_type == SMB2_ENCRYPTION_AES256_GCM))
+ full_key_size = ses->auth_key.len;
/* safe to access primary channel, since it will never go away */
spin_lock(&ses->chan_lock);
@@ -364,18 +362,15 @@ generate_smb3signingkey(struct cifs_ses *ses,
SMB3_SIGN_KEY_SIZE);
spin_unlock(&ses->chan_lock);
- rc = generate_key(ses, ptriplet->encryption.label,
- ptriplet->encryption.context,
- ses->smb3encryptionkey,
- SMB3_ENC_DEC_KEY_SIZE);
- if (rc)
- return rc;
- rc = generate_key(ses, ptriplet->decryption.label,
- ptriplet->decryption.context,
- ses->smb3decryptionkey,
- SMB3_ENC_DEC_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(ses, ptriplet->encryption.label,
+ ptriplet->encryption.context,
+ ses->smb3encryptionkey, SMB3_ENC_DEC_KEY_SIZE,
+ full_key_size);
+
+ generate_key(ses, ptriplet->decryption.label,
+ ptriplet->decryption.context,
+ ses->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE,
+ full_key_size);
}
#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
@@ -388,7 +383,7 @@ generate_smb3signingkey(struct cifs_ses *ses,
&ses->Suid);
cifs_dbg(VFS, "Cipher type %d\n", server->cipher_type);
cifs_dbg(VFS, "Session Key %*ph\n",
- SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response);
+ (int)ses->auth_key.len, ses->auth_key.response);
cifs_dbg(VFS, "Signing Key %*ph\n",
SMB3_SIGN_KEY_SIZE, ses->smb3signingkey);
if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) ||
@@ -404,7 +399,7 @@ generate_smb3signingkey(struct cifs_ses *ses,
SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3decryptionkey);
}
#endif
- return rc;
+ return 0;
}
int
@@ -466,19 +461,19 @@ generate_smb311signingkey(struct cifs_ses *ses,
}
static int
-smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
- bool allocate_crypto)
+smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{
int rc;
unsigned char smb3_signature[SMB2_CMACAES_SIZE];
struct kvec *iov = rqst->rq_iov;
struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base;
- struct shash_desc *shash = NULL;
+ struct aes_cmac_key cmac_key __cleanup(aes_cmac_zeroize_key);
+ struct aes_cmac_ctx cmac_ctx __cleanup(aes_cmac_zeroize_ctx);
struct smb_rqst drqst;
u8 key[SMB3_SIGN_KEY_SIZE];
if (server->vals->protocol_id <= SMB21_PROT_ID)
- return smb2_calc_signature(rqst, server, allocate_crypto);
+ return smb2_calc_signature(rqst, server);
rc = smb3_get_sign_key(le64_to_cpu(shdr->SessionId), server, key);
if (unlikely(rc)) {
@@ -486,33 +481,17 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
return rc;
}
- if (allocate_crypto) {
- rc = cifs_alloc_hash("cmac(aes)", &shash);
- if (rc)
- return rc;
- } else {
- shash = server->secmech.aes_cmac;
- }
-
memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
- rc = crypto_shash_setkey(shash->tfm, key, SMB2_CMACAES_SIZE);
+ rc = aes_cmac_preparekey(&cmac_key, key, SMB2_CMACAES_SIZE);
+ memzero_explicit(key, sizeof(key));
if (rc) {
cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
- goto out;
+ return rc;
}
- /*
- * we already allocate aes_cmac when we init smb3 signing key,
- * so unlike smb2 case we do not have to check here if secmech are
- * initialized
- */
- rc = crypto_shash_init(shash);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
- goto out;
- }
+ aes_cmac_init(&cmac_ctx, &cmac_key);
/*
* For SMB2+, __cifs_calc_signature() expects to sign only the actual
@@ -523,26 +502,16 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
*/
drqst = *rqst;
if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) {
- rc = crypto_shash_update(shash, iov[0].iov_base,
- iov[0].iov_len);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not update with payload\n",
- __func__);
- goto out;
- }
+ aes_cmac_update(&cmac_ctx, iov[0].iov_base, iov[0].iov_len);
drqst.rq_iov++;
drqst.rq_nvec--;
}
rc = __cifs_calc_signature(
&drqst, server, smb3_signature,
- &(struct cifs_calc_sig_ctx){ .shash = shash });
+ &(struct cifs_calc_sig_ctx){ .cmac = &cmac_ctx });
if (!rc)
memcpy(shdr->Signature, smb3_signature, SMB2_SIGNATURE_SIZE);
-
-out:
- if (allocate_crypto)
- cifs_free_hash(&shash);
return rc;
}
@@ -576,7 +545,7 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return 0;
}
- return smb3_calc_signature(rqst, server, false);
+ return smb3_calc_signature(rqst, server);
}
int
@@ -612,12 +581,13 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE);
- rc = smb3_calc_signature(rqst, server, true);
+ rc = smb3_calc_signature(rqst, server);
if (rc)
return rc;
- if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE)) {
+ if (crypto_memneq(server_response_sig, shdr->Signature,
+ SMB2_SIGNATURE_SIZE)) {
cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n",
shdr->Command, shdr->MessageId);
return -EACCES;
diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c
index c79304012b08..563ef488a225 100644
--- a/fs/smb/client/smbdirect.c
+++ b/fs/smb/client/smbdirect.c
@@ -4,61 +4,12 @@
*
* Author(s): Long Li <longli@microsoft.com>
*/
-#include <linux/module.h>
-#include <linux/highmem.h>
-#include <linux/folio_queue.h>
-#define __SMBDIRECT_SOCKET_DISCONNECT(__sc) smbd_disconnect_rdma_connection(__sc)
-#include "../common/smbdirect/smbdirect_pdu.h"
+
#include "smbdirect.h"
#include "cifs_debug.h"
#include "cifsproto.h"
#include "smb2proto.h"
-const struct smbdirect_socket_parameters *smbd_get_parameters(struct smbd_connection *conn)
-{
- struct smbdirect_socket *sc = &conn->socket;
-
- return &sc->parameters;
-}
-
-static struct smbdirect_recv_io *get_receive_buffer(
- struct smbdirect_socket *sc);
-static void put_receive_buffer(
- struct smbdirect_socket *sc,
- struct smbdirect_recv_io *response);
-static int allocate_receive_buffers(struct smbdirect_socket *sc, int num_buf);
-static void destroy_receive_buffers(struct smbdirect_socket *sc);
-
-static void enqueue_reassembly(
- struct smbdirect_socket *sc,
- struct smbdirect_recv_io *response, int data_length);
-static struct smbdirect_recv_io *_get_first_reassembly(
- struct smbdirect_socket *sc);
-
-static int smbd_post_send(struct smbdirect_socket *sc,
- struct smbdirect_send_batch *batch,
- struct smbdirect_send_io *request);
-
-static int smbd_post_recv(
- struct smbdirect_socket *sc,
- struct smbdirect_recv_io *response);
-
-static int smbd_post_send_empty(struct smbdirect_socket *sc);
-
-static void destroy_mr_list(struct smbdirect_socket *sc);
-static int allocate_mr_list(struct smbdirect_socket *sc);
-
-struct smb_extract_to_rdma {
- struct ib_sge *sge;
- unsigned int nr_sge;
- unsigned int max_sge;
- struct ib_device *device;
- u32 local_dma_lkey;
- enum dma_data_direction direction;
-};
-static ssize_t smb_extract_iter_to_rdma(struct iov_iter *iter, size_t len,
- struct smb_extract_to_rdma *rdma);
-
/* Port numbers for SMBD transport */
#define SMB_PORT 445
#define SMBD_PORT 5445
@@ -72,21 +23,12 @@ static ssize_t smb_extract_iter_to_rdma(struct iov_iter *iter, size_t len,
/* The timeout to wait for a keepalive message from peer in seconds */
#define KEEPALIVE_RECV_TIMEOUT 5
-/* SMBD minimum receive size and fragmented sized defined in [MS-SMBD] */
-#define SMBD_MIN_RECEIVE_SIZE 128
-#define SMBD_MIN_FRAGMENTED_SIZE 131072
-
/*
* Default maximum number of RDMA read/write outstanding on this connection
* This value is possibly decreased during QP creation on hardware limit
*/
#define SMBD_CM_RESPONDER_RESOURCES 32
-/* Maximum number of retries on data transfer operations */
-#define SMBD_CM_RETRY 6
-/* No need to retry on Receiver Not Ready since SMBD manages credits */
-#define SMBD_CM_RNR_RETRY 0
-
/*
* User configurable initial values per SMBD transport connection
* as defined in [MS-SMBD] 3.1.1.1
@@ -162,6 +104,43 @@ module_param(smbd_logging_level, uint, 0644);
MODULE_PARM_DESC(smbd_logging_level,
"Logging level for SMBD transport, 0 (default): error, 1: info");
+static bool smbd_logging_needed(struct smbdirect_socket *sc,
+ void *private_ptr,
+ unsigned int lvl,
+ unsigned int cls)
+{
+#define BUILD_BUG_SAME(x) BUILD_BUG_ON(x != SMBDIRECT_LOG_ ##x)
+ BUILD_BUG_SAME(ERR);
+ BUILD_BUG_SAME(INFO);
+#undef BUILD_BUG_SAME
+#define BUILD_BUG_SAME(x) BUILD_BUG_ON(x != SMBDIRECT_ ##x)
+ BUILD_BUG_SAME(LOG_OUTGOING);
+ BUILD_BUG_SAME(LOG_INCOMING);
+ BUILD_BUG_SAME(LOG_READ);
+ BUILD_BUG_SAME(LOG_WRITE);
+ BUILD_BUG_SAME(LOG_RDMA_SEND);
+ BUILD_BUG_SAME(LOG_RDMA_RECV);
+ BUILD_BUG_SAME(LOG_KEEP_ALIVE);
+ BUILD_BUG_SAME(LOG_RDMA_EVENT);
+ BUILD_BUG_SAME(LOG_RDMA_MR);
+#undef BUILD_BUG_SAME
+
+ if (lvl <= smbd_logging_level || cls & smbd_logging_class)
+ return true;
+ return false;
+}
+
+static void smbd_logging_vaprintf(struct smbdirect_socket *sc,
+ const char *func,
+ unsigned int line,
+ void *private_ptr,
+ unsigned int lvl,
+ unsigned int cls,
+ struct va_format *vaf)
+{
+ cifs_dbg(VFS, "%s:%u %pV", func, line, vaf);
+}
+
#define log_rdma(level, class, fmt, args...) \
do { \
if (level <= smbd_logging_level || class & smbd_logging_class) \
@@ -185,1695 +164,34 @@ do { \
#define log_rdma_mr(level, fmt, args...) \
log_rdma(level, LOG_RDMA_MR, fmt, ##args)
-static void smbd_disconnect_wake_up_all(struct smbdirect_socket *sc)
-{
- /*
- * Wake up all waiters in all wait queues
- * in order to notice the broken connection.
- */
- wake_up_all(&sc->status_wait);
- wake_up_all(&sc->send_io.lcredits.wait_queue);
- wake_up_all(&sc->send_io.credits.wait_queue);
- wake_up_all(&sc->send_io.pending.dec_wait_queue);
- wake_up_all(&sc->send_io.pending.zero_wait_queue);
- wake_up_all(&sc->recv_io.reassembly.wait_queue);
- wake_up_all(&sc->mr_io.ready.wait_queue);
- wake_up_all(&sc->mr_io.cleanup.wait_queue);
-}
-
-static void smbd_disconnect_rdma_work(struct work_struct *work)
-{
- struct smbdirect_socket *sc =
- container_of(work, struct smbdirect_socket, disconnect_work);
-
- if (sc->first_error == 0)
- sc->first_error = -ECONNABORTED;
-
- /*
- * make sure this and other work is not queued again
- * but here we don't block and avoid
- * disable[_delayed]_work_sync()
- */
- disable_work(&sc->disconnect_work);
- disable_work(&sc->recv_io.posted.refill_work);
- disable_work(&sc->mr_io.recovery_work);
- disable_work(&sc->idle.immediate_work);
- disable_delayed_work(&sc->idle.timer_work);
-
- switch (sc->status) {
- case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED:
- case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING:
- case SMBDIRECT_SOCKET_NEGOTIATE_FAILED:
- case SMBDIRECT_SOCKET_CONNECTED:
- case SMBDIRECT_SOCKET_ERROR:
- sc->status = SMBDIRECT_SOCKET_DISCONNECTING;
- rdma_disconnect(sc->rdma.cm_id);
- break;
-
- case SMBDIRECT_SOCKET_CREATED:
- case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED:
- case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING:
- case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
- case SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED:
- case SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING:
- case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED:
- case SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED:
- case SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING:
- case SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED:
- /*
- * rdma_connect() never reached
- * RDMA_CM_EVENT_ESTABLISHED
- */
- sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
- break;
-
- case SMBDIRECT_SOCKET_DISCONNECTING:
- case SMBDIRECT_SOCKET_DISCONNECTED:
- case SMBDIRECT_SOCKET_DESTROYED:
- break;
- }
-
- /*
- * Wake up all waiters in all wait queues
- * in order to notice the broken connection.
- */
- smbd_disconnect_wake_up_all(sc);
-}
-
-static void smbd_disconnect_rdma_connection(struct smbdirect_socket *sc)
-{
- if (sc->first_error == 0)
- sc->first_error = -ECONNABORTED;
-
- /*
- * make sure other work (than disconnect_work) is
- * not queued again but here we don't block and avoid
- * disable[_delayed]_work_sync()
- */
- disable_work(&sc->recv_io.posted.refill_work);
- disable_work(&sc->mr_io.recovery_work);
- disable_work(&sc->idle.immediate_work);
- disable_delayed_work(&sc->idle.timer_work);
-
- switch (sc->status) {
- case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
- case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED:
- case SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED:
- case SMBDIRECT_SOCKET_NEGOTIATE_FAILED:
- case SMBDIRECT_SOCKET_ERROR:
- case SMBDIRECT_SOCKET_DISCONNECTING:
- case SMBDIRECT_SOCKET_DISCONNECTED:
- case SMBDIRECT_SOCKET_DESTROYED:
- /*
- * Keep the current error status
- */
- break;
-
- case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED:
- case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING:
- sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED;
- break;
-
- case SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED:
- case SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING:
- sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED;
- break;
-
- case SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED:
- case SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING:
- sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED;
- break;
-
- case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED:
- case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING:
- sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
- break;
-
- case SMBDIRECT_SOCKET_CREATED:
- sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
- break;
-
- case SMBDIRECT_SOCKET_CONNECTED:
- sc->status = SMBDIRECT_SOCKET_ERROR;
- break;
- }
-
- /*
- * Wake up all waiters in all wait queues
- * in order to notice the broken connection.
- */
- smbd_disconnect_wake_up_all(sc);
-
- queue_work(sc->workqueue, &sc->disconnect_work);
-}
-
-/* Upcall from RDMA CM */
-static int smbd_conn_upcall(
- struct rdma_cm_id *id, struct rdma_cm_event *event)
-{
- struct smbdirect_socket *sc = id->context;
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- const char *event_name = rdma_event_msg(event->event);
- u8 peer_initiator_depth;
- u8 peer_responder_resources;
-
- log_rdma_event(INFO, "event=%s status=%d\n",
- event_name, event->status);
-
- switch (event->event) {
- case RDMA_CM_EVENT_ADDR_RESOLVED:
- if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING))
- break;
- sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED;
- wake_up(&sc->status_wait);
- break;
-
- case RDMA_CM_EVENT_ROUTE_RESOLVED:
- if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING))
- break;
- sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED;
- wake_up(&sc->status_wait);
- break;
-
- case RDMA_CM_EVENT_ADDR_ERROR:
- log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
- sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED;
- smbd_disconnect_rdma_work(&sc->disconnect_work);
- break;
-
- case RDMA_CM_EVENT_ROUTE_ERROR:
- log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
- sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED;
- smbd_disconnect_rdma_work(&sc->disconnect_work);
- break;
-
- case RDMA_CM_EVENT_ESTABLISHED:
- log_rdma_event(INFO, "connected event=%s\n", event_name);
-
- /*
- * Here we work around an inconsistency between
- * iWarp and other devices (at least rxe and irdma using RoCEv2)
- */
- if (rdma_protocol_iwarp(id->device, id->port_num)) {
- /*
- * iWarp devices report the peer's values
- * with the perspective of the peer here.
- * Tested with siw and irdma (in iwarp mode)
- * We need to change to our perspective here,
- * so we need to switch the values.
- */
- peer_initiator_depth = event->param.conn.responder_resources;
- peer_responder_resources = event->param.conn.initiator_depth;
- } else {
- /*
- * Non iWarp devices report the peer's values
- * already changed to our perspective here.
- * Tested with rxe and irdma (in roce mode).
- */
- peer_initiator_depth = event->param.conn.initiator_depth;
- peer_responder_resources = event->param.conn.responder_resources;
- }
- if (rdma_protocol_iwarp(id->device, id->port_num) &&
- event->param.conn.private_data_len == 8) {
- /*
- * Legacy clients with only iWarp MPA v1 support
- * need a private blob in order to negotiate
- * the IRD/ORD values.
- */
- const __be32 *ird_ord_hdr = event->param.conn.private_data;
- u32 ird32 = be32_to_cpu(ird_ord_hdr[0]);
- u32 ord32 = be32_to_cpu(ird_ord_hdr[1]);
-
- /*
- * cifs.ko sends the legacy IRD/ORD negotiation
- * event if iWarp MPA v2 was used.
- *
- * Here we check that the values match and only
- * mark the client as legacy if they don't match.
- */
- if ((u32)event->param.conn.initiator_depth != ird32 ||
- (u32)event->param.conn.responder_resources != ord32) {
- /*
- * There are broken clients (old cifs.ko)
- * using little endian and also
- * struct rdma_conn_param only uses u8
- * for initiator_depth and responder_resources,
- * so we truncate the value to U8_MAX.
- *
- * smb_direct_accept_client() will then
- * do the real negotiation in order to
- * select the minimum between client and
- * server.
- */
- ird32 = min_t(u32, ird32, U8_MAX);
- ord32 = min_t(u32, ord32, U8_MAX);
-
- sc->rdma.legacy_iwarp = true;
- peer_initiator_depth = (u8)ird32;
- peer_responder_resources = (u8)ord32;
- }
- }
-
- /*
- * negotiate the value by using the minimum
- * between client and server if the client provided
- * non 0 values.
- */
- if (peer_initiator_depth != 0)
- sp->initiator_depth =
- min_t(u8, sp->initiator_depth,
- peer_initiator_depth);
- if (peer_responder_resources != 0)
- sp->responder_resources =
- min_t(u8, sp->responder_resources,
- peer_responder_resources);
-
- if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING))
- break;
- sc->status = SMBDIRECT_SOCKET_NEGOTIATE_NEEDED;
- wake_up(&sc->status_wait);
- break;
-
- case RDMA_CM_EVENT_CONNECT_ERROR:
- case RDMA_CM_EVENT_UNREACHABLE:
- case RDMA_CM_EVENT_REJECTED:
- log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
- sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED;
- smbd_disconnect_rdma_work(&sc->disconnect_work);
- break;
-
- case RDMA_CM_EVENT_DEVICE_REMOVAL:
- case RDMA_CM_EVENT_DISCONNECTED:
- /* This happens when we fail the negotiation */
- if (sc->status == SMBDIRECT_SOCKET_NEGOTIATE_FAILED) {
- log_rdma_event(ERR, "event=%s during negotiation\n", event_name);
- }
-
- sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
- smbd_disconnect_rdma_work(&sc->disconnect_work);
- break;
-
- default:
- log_rdma_event(ERR, "unexpected event=%s status=%d\n",
- event_name, event->status);
- break;
- }
-
- return 0;
-}
-
-/* Upcall from RDMA QP */
-static void
-smbd_qp_async_error_upcall(struct ib_event *event, void *context)
-{
- struct smbdirect_socket *sc = context;
-
- log_rdma_event(ERR, "%s on device %s socket %p\n",
- ib_event_msg(event->event), event->device->name, sc);
-
- switch (event->event) {
- case IB_EVENT_CQ_ERR:
- case IB_EVENT_QP_FATAL:
- smbd_disconnect_rdma_connection(sc);
- break;
-
- default:
- break;
- }
-}
-
-static inline void *smbdirect_send_io_payload(struct smbdirect_send_io *request)
-{
- return (void *)request->packet;
-}
-
-static inline void *smbdirect_recv_io_payload(struct smbdirect_recv_io *response)
-{
- return (void *)response->packet;
-}
-
-static struct smbdirect_send_io *smbd_alloc_send_io(struct smbdirect_socket *sc)
-{
- struct smbdirect_send_io *msg;
-
- msg = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
- if (!msg)
- return ERR_PTR(-ENOMEM);
- msg->socket = sc;
- INIT_LIST_HEAD(&msg->sibling_list);
- msg->num_sge = 0;
-
- return msg;
-}
-
-static void smbd_free_send_io(struct smbdirect_send_io *msg)
-{
- struct smbdirect_socket *sc = msg->socket;
- size_t i;
-
- /*
- * The list needs to be empty!
- * The caller should take care of it.
- */
- WARN_ON_ONCE(!list_empty(&msg->sibling_list));
-
- /*
- * Note we call ib_dma_unmap_page(), even if some sges are mapped using
- * ib_dma_map_single().
- *
- * The difference between _single() and _page() only matters for the
- * ib_dma_map_*() case.
- *
- * For the ib_dma_unmap_*() case it does not matter as both take the
- * dma_addr_t and dma_unmap_single_attrs() is just an alias to
- * dma_unmap_page_attrs().
- */
- for (i = 0; i < msg->num_sge; i++)
- ib_dma_unmap_page(sc->ib.dev,
- msg->sge[i].addr,
- msg->sge[i].length,
- DMA_TO_DEVICE);
-
- mempool_free(msg, sc->send_io.mem.pool);
-}
-
-/* Called when a RDMA send is done */
-static void send_done(struct ib_cq *cq, struct ib_wc *wc)
-{
- struct smbdirect_send_io *request =
- container_of(wc->wr_cqe, struct smbdirect_send_io, cqe);
- struct smbdirect_socket *sc = request->socket;
- struct smbdirect_send_io *sibling, *next;
- int lcredits = 0;
-
- log_rdma_send(INFO, "smbdirect_send_io 0x%p completed wc->status=%s\n",
- request, ib_wc_status_msg(wc->status));
-
- if (unlikely(!(request->wr.send_flags & IB_SEND_SIGNALED))) {
- /*
- * This happens when smbdirect_send_io is a sibling
- * before the final message, it is signaled on
- * error anyway, so we need to skip
- * smbdirect_connection_free_send_io here,
- * otherwise is will destroy the memory
- * of the siblings too, which will cause
- * use after free problems for the others
- * triggered from ib_drain_qp().
- */
- if (wc->status != IB_WC_SUCCESS)
- goto skip_free;
-
- /*
- * This should not happen!
- * But we better just close the
- * connection...
- */
- log_rdma_send(ERR,
- "unexpected send completion wc->status=%s (%d) wc->opcode=%d\n",
- ib_wc_status_msg(wc->status), wc->status, wc->opcode);
- smbd_disconnect_rdma_connection(sc);
- return;
- }
-
- /*
- * Free possible siblings and then the main send_io
- */
- list_for_each_entry_safe(sibling, next, &request->sibling_list, sibling_list) {
- list_del_init(&sibling->sibling_list);
- smbd_free_send_io(sibling);
- lcredits += 1;
- }
- /* Note this frees wc->wr_cqe, but not wc */
- smbd_free_send_io(request);
- lcredits += 1;
-
- if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
-skip_free:
- if (wc->status != IB_WC_WR_FLUSH_ERR)
- log_rdma_send(ERR, "wc->status=%s wc->opcode=%d\n",
- ib_wc_status_msg(wc->status), wc->opcode);
- smbd_disconnect_rdma_connection(sc);
- return;
- }
-
- atomic_add(lcredits, &sc->send_io.lcredits.count);
- wake_up(&sc->send_io.lcredits.wait_queue);
-
- if (atomic_dec_and_test(&sc->send_io.pending.count))
- wake_up(&sc->send_io.pending.zero_wait_queue);
-
- wake_up(&sc->send_io.pending.dec_wait_queue);
-}
-
-static void dump_smbdirect_negotiate_resp(struct smbdirect_negotiate_resp *resp)
-{
- log_rdma_event(INFO, "resp message min_version %u max_version %u negotiated_version %u credits_requested %u credits_granted %u status %u max_readwrite_size %u preferred_send_size %u max_receive_size %u max_fragmented_size %u\n",
- resp->min_version, resp->max_version,
- resp->negotiated_version, resp->credits_requested,
- resp->credits_granted, resp->status,
- resp->max_readwrite_size, resp->preferred_send_size,
- resp->max_receive_size, resp->max_fragmented_size);
-}
-
-/*
- * Process a negotiation response message, according to [MS-SMBD]3.1.5.7
- * response, packet_length: the negotiation response message
- * return value: true if negotiation is a success, false if failed
- */
-static bool process_negotiation_response(
- struct smbdirect_recv_io *response, int packet_length)
-{
- struct smbdirect_socket *sc = response->socket;
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- struct smbdirect_negotiate_resp *packet = smbdirect_recv_io_payload(response);
-
- if (packet_length < sizeof(struct smbdirect_negotiate_resp)) {
- log_rdma_event(ERR,
- "error: packet_length=%d\n", packet_length);
- return false;
- }
-
- if (le16_to_cpu(packet->negotiated_version) != SMBDIRECT_V1) {
- log_rdma_event(ERR, "error: negotiated_version=%x\n",
- le16_to_cpu(packet->negotiated_version));
- return false;
- }
-
- if (packet->credits_requested == 0) {
- log_rdma_event(ERR, "error: credits_requested==0\n");
- return false;
- }
- sc->recv_io.credits.target = le16_to_cpu(packet->credits_requested);
- sc->recv_io.credits.target = min_t(u16, sc->recv_io.credits.target, sp->recv_credit_max);
-
- if (packet->credits_granted == 0) {
- log_rdma_event(ERR, "error: credits_granted==0\n");
- return false;
- }
- atomic_set(&sc->send_io.lcredits.count, sp->send_credit_target);
- atomic_set(&sc->send_io.credits.count, le16_to_cpu(packet->credits_granted));
-
- if (le32_to_cpu(packet->preferred_send_size) > sp->max_recv_size) {
- log_rdma_event(ERR, "error: preferred_send_size=%d\n",
- le32_to_cpu(packet->preferred_send_size));
- return false;
- }
- sp->max_recv_size = le32_to_cpu(packet->preferred_send_size);
-
- if (le32_to_cpu(packet->max_receive_size) < SMBD_MIN_RECEIVE_SIZE) {
- log_rdma_event(ERR, "error: max_receive_size=%d\n",
- le32_to_cpu(packet->max_receive_size));
- return false;
- }
- sp->max_send_size = min_t(u32, sp->max_send_size,
- le32_to_cpu(packet->max_receive_size));
-
- if (le32_to_cpu(packet->max_fragmented_size) <
- SMBD_MIN_FRAGMENTED_SIZE) {
- log_rdma_event(ERR, "error: max_fragmented_size=%d\n",
- le32_to_cpu(packet->max_fragmented_size));
- return false;
- }
- sp->max_fragmented_send_size =
- le32_to_cpu(packet->max_fragmented_size);
-
-
- sp->max_read_write_size = min_t(u32,
- le32_to_cpu(packet->max_readwrite_size),
- sp->max_frmr_depth * PAGE_SIZE);
- sp->max_frmr_depth = sp->max_read_write_size / PAGE_SIZE;
-
- atomic_set(&sc->send_io.bcredits.count, 1);
- sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER;
- return true;
-}
-
-static void smbd_post_send_credits(struct work_struct *work)
-{
- int rc;
- struct smbdirect_recv_io *response;
- struct smbdirect_socket *sc =
- container_of(work, struct smbdirect_socket, recv_io.posted.refill_work);
- int posted = 0;
-
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
- return;
- }
-
- if (sc->recv_io.credits.target >
- atomic_read(&sc->recv_io.credits.count)) {
- while (true) {
- response = get_receive_buffer(sc);
- if (!response)
- break;
-
- response->first_segment = false;
- rc = smbd_post_recv(sc, response);
- if (rc) {
- log_rdma_recv(ERR,
- "post_recv failed rc=%d\n", rc);
- put_receive_buffer(sc, response);
- break;
- }
-
- atomic_inc(&sc->recv_io.posted.count);
- posted += 1;
- }
- }
-
- atomic_add(posted, &sc->recv_io.credits.available);
-
- /*
- * If the last send credit is waiting for credits
- * it can grant we need to wake it up
- */
- if (posted &&
- atomic_read(&sc->send_io.bcredits.count) == 0 &&
- atomic_read(&sc->send_io.credits.count) == 0)
- wake_up(&sc->send_io.credits.wait_queue);
-
- /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */
- if (atomic_read(&sc->recv_io.credits.count) <
- sc->recv_io.credits.target - 1) {
- log_keep_alive(INFO, "schedule send of an empty message\n");
- queue_work(sc->workqueue, &sc->idle.immediate_work);
- }
-}
-
-/* Called from softirq, when recv is done */
-static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
-{
- struct smbdirect_data_transfer *data_transfer;
- struct smbdirect_recv_io *response =
- container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
- struct smbdirect_socket *sc = response->socket;
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- int current_recv_credits;
- u16 old_recv_credit_target;
- u32 data_offset = 0;
- u32 data_length = 0;
- u32 remaining_data_length = 0;
- bool negotiate_done = false;
-
- log_rdma_recv(INFO,
- "response=0x%p type=%d wc status=%s wc opcode %d byte_len=%d pkey_index=%u\n",
- response, sc->recv_io.expected,
- ib_wc_status_msg(wc->status), wc->opcode,
- wc->byte_len, wc->pkey_index);
-
- if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) {
- if (wc->status != IB_WC_WR_FLUSH_ERR)
- log_rdma_recv(ERR, "wc->status=%s opcode=%d\n",
- ib_wc_status_msg(wc->status), wc->opcode);
- goto error;
- }
-
- ib_dma_sync_single_for_cpu(
- wc->qp->device,
- response->sge.addr,
- response->sge.length,
- DMA_FROM_DEVICE);
-
- /*
- * Reset timer to the keepalive interval in
- * order to trigger our next keepalive message.
- */
- sc->idle.keepalive = SMBDIRECT_KEEPALIVE_NONE;
- mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
- msecs_to_jiffies(sp->keepalive_interval_msec));
-
- switch (sc->recv_io.expected) {
- /* SMBD negotiation response */
- case SMBDIRECT_EXPECT_NEGOTIATE_REP:
- dump_smbdirect_negotiate_resp(smbdirect_recv_io_payload(response));
- sc->recv_io.reassembly.full_packet_received = true;
- negotiate_done =
- process_negotiation_response(response, wc->byte_len);
- put_receive_buffer(sc, response);
- if (SMBDIRECT_CHECK_STATUS_WARN(sc, SMBDIRECT_SOCKET_NEGOTIATE_RUNNING))
- negotiate_done = false;
- if (!negotiate_done) {
- sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
- smbd_disconnect_rdma_connection(sc);
- } else {
- sc->status = SMBDIRECT_SOCKET_CONNECTED;
- wake_up(&sc->status_wait);
- }
-
- return;
-
- /* SMBD data transfer packet */
- case SMBDIRECT_EXPECT_DATA_TRANSFER:
- data_transfer = smbdirect_recv_io_payload(response);
-
- if (wc->byte_len <
- offsetof(struct smbdirect_data_transfer, padding))
- goto error;
-
- remaining_data_length = le32_to_cpu(data_transfer->remaining_data_length);
- data_offset = le32_to_cpu(data_transfer->data_offset);
- data_length = le32_to_cpu(data_transfer->data_length);
- if (wc->byte_len < data_offset ||
- (u64)wc->byte_len < (u64)data_offset + data_length)
- goto error;
-
- if (remaining_data_length > sp->max_fragmented_recv_size ||
- data_length > sp->max_fragmented_recv_size ||
- (u64)remaining_data_length + (u64)data_length > (u64)sp->max_fragmented_recv_size)
- goto error;
-
- if (data_length) {
- if (sc->recv_io.reassembly.full_packet_received)
- response->first_segment = true;
-
- if (le32_to_cpu(data_transfer->remaining_data_length))
- sc->recv_io.reassembly.full_packet_received = false;
- else
- sc->recv_io.reassembly.full_packet_received = true;
- }
-
- atomic_dec(&sc->recv_io.posted.count);
- current_recv_credits = atomic_dec_return(&sc->recv_io.credits.count);
-
- old_recv_credit_target = sc->recv_io.credits.target;
- sc->recv_io.credits.target =
- le16_to_cpu(data_transfer->credits_requested);
- sc->recv_io.credits.target =
- min_t(u16, sc->recv_io.credits.target, sp->recv_credit_max);
- sc->recv_io.credits.target =
- max_t(u16, sc->recv_io.credits.target, 1);
- if (le16_to_cpu(data_transfer->credits_granted)) {
- atomic_add(le16_to_cpu(data_transfer->credits_granted),
- &sc->send_io.credits.count);
- /*
- * We have new send credits granted from remote peer
- * If any sender is waiting for credits, unblock it
- */
- wake_up(&sc->send_io.credits.wait_queue);
- }
-
- log_incoming(INFO, "data flags %d data_offset %d data_length %d remaining_data_length %d\n",
- le16_to_cpu(data_transfer->flags),
- le32_to_cpu(data_transfer->data_offset),
- le32_to_cpu(data_transfer->data_length),
- le32_to_cpu(data_transfer->remaining_data_length));
-
- /* Send an immediate response right away if requested */
- if (le16_to_cpu(data_transfer->flags) &
- SMBDIRECT_FLAG_RESPONSE_REQUESTED) {
- log_keep_alive(INFO, "schedule send of immediate response\n");
- queue_work(sc->workqueue, &sc->idle.immediate_work);
- }
-
- /*
- * If this is a packet with data playload place the data in
- * reassembly queue and wake up the reading thread
- */
- if (data_length) {
- if (current_recv_credits <= (sc->recv_io.credits.target / 4) ||
- sc->recv_io.credits.target > old_recv_credit_target)
- queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
-
- enqueue_reassembly(sc, response, data_length);
- wake_up(&sc->recv_io.reassembly.wait_queue);
- } else
- put_receive_buffer(sc, response);
-
- return;
-
- case SMBDIRECT_EXPECT_NEGOTIATE_REQ:
- /* Only server... */
- break;
- }
-
- /*
- * This is an internal error!
- */
- log_rdma_recv(ERR, "unexpected response type=%d\n", sc->recv_io.expected);
- WARN_ON_ONCE(sc->recv_io.expected != SMBDIRECT_EXPECT_DATA_TRANSFER);
-error:
- put_receive_buffer(sc, response);
- smbd_disconnect_rdma_connection(sc);
-}
-
-static struct rdma_cm_id *smbd_create_id(
- struct smbdirect_socket *sc,
- struct sockaddr *dstaddr, int port)
-{
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- struct rdma_cm_id *id;
- u8 node_type = RDMA_NODE_UNSPECIFIED;
- int rc;
- __be16 *sport;
-
- id = rdma_create_id(&init_net, smbd_conn_upcall, sc,
- RDMA_PS_TCP, IB_QPT_RC);
- if (IS_ERR(id)) {
- rc = PTR_ERR(id);
- log_rdma_event(ERR, "rdma_create_id() failed %i\n", rc);
- return id;
- }
-
- switch (port) {
- case SMBD_PORT:
- /*
- * only allow iWarp devices
- * for port 5445.
- */
- node_type = RDMA_NODE_RNIC;
- break;
- case SMB_PORT:
- /*
- * only allow InfiniBand, RoCEv1 or RoCEv2
- * devices for port 445.
- *
- * (Basically don't allow iWarp devices)
- */
- node_type = RDMA_NODE_IB_CA;
- break;
- }
- rc = rdma_restrict_node_type(id, node_type);
- if (rc) {
- log_rdma_event(ERR, "rdma_restrict_node_type(%u) failed %i\n",
- node_type, rc);
- goto out;
- }
-
- if (dstaddr->sa_family == AF_INET6)
- sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port;
- else
- sport = &((struct sockaddr_in *)dstaddr)->sin_port;
-
- *sport = htons(port);
-
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED);
- sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING;
- rc = rdma_resolve_addr(id, NULL, (struct sockaddr *)dstaddr,
- sp->resolve_addr_timeout_msec);
- if (rc) {
- log_rdma_event(ERR, "rdma_resolve_addr() failed %i\n", rc);
- goto out;
- }
- rc = wait_event_interruptible_timeout(
- sc->status_wait,
- sc->status != SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING,
- msecs_to_jiffies(sp->resolve_addr_timeout_msec));
- /* e.g. if interrupted returns -ERESTARTSYS */
- if (rc < 0) {
- log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc);
- goto out;
- }
- if (sc->status == SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING) {
- rc = -ETIMEDOUT;
- log_rdma_event(ERR, "rdma_resolve_addr() completed %i\n", rc);
- goto out;
- }
- if (sc->status != SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED) {
- rc = -EHOSTUNREACH;
- log_rdma_event(ERR, "rdma_resolve_addr() completed %i\n", rc);
- goto out;
- }
-
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED);
- sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING;
- rc = rdma_resolve_route(id, sp->resolve_route_timeout_msec);
- if (rc) {
- log_rdma_event(ERR, "rdma_resolve_route() failed %i\n", rc);
- goto out;
- }
- rc = wait_event_interruptible_timeout(
- sc->status_wait,
- sc->status != SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING,
- msecs_to_jiffies(sp->resolve_route_timeout_msec));
- /* e.g. if interrupted returns -ERESTARTSYS */
- if (rc < 0) {
- log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc);
- goto out;
- }
- if (sc->status == SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING) {
- rc = -ETIMEDOUT;
- log_rdma_event(ERR, "rdma_resolve_route() completed %i\n", rc);
- goto out;
- }
- if (sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED) {
- rc = -ENETUNREACH;
- log_rdma_event(ERR, "rdma_resolve_route() completed %i\n", rc);
- goto out;
- }
-
- return id;
-
-out:
- rdma_destroy_id(id);
- return ERR_PTR(rc);
-}
-
-/*
- * Test if FRWR (Fast Registration Work Requests) is supported on the device
- * This implementation requires FRWR on RDMA read/write
- * return value: true if it is supported
- */
-static bool 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 smbd_ia_open(
- struct smbdirect_socket *sc,
- struct sockaddr *dstaddr, int port)
-{
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- int rc;
-
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_CREATED);
- sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED;
-
- sc->rdma.cm_id = smbd_create_id(sc, dstaddr, port);
- if (IS_ERR(sc->rdma.cm_id)) {
- rc = PTR_ERR(sc->rdma.cm_id);
- goto out1;
- }
- sc->ib.dev = sc->rdma.cm_id->device;
-
- if (!frwr_is_supported(&sc->ib.dev->attrs)) {
- log_rdma_event(ERR, "Fast Registration Work Requests (FRWR) is not supported\n");
- log_rdma_event(ERR, "Device capability flags = %llx max_fast_reg_page_list_len = %u\n",
- sc->ib.dev->attrs.device_cap_flags,
- sc->ib.dev->attrs.max_fast_reg_page_list_len);
- rc = -EPROTONOSUPPORT;
- goto out2;
- }
- sp->max_frmr_depth = min_t(u32,
- sp->max_frmr_depth,
- sc->ib.dev->attrs.max_fast_reg_page_list_len);
- sc->mr_io.type = IB_MR_TYPE_MEM_REG;
- if (sc->ib.dev->attrs.kernel_cap_flags & IBK_SG_GAPS_REG)
- sc->mr_io.type = IB_MR_TYPE_SG_GAPS;
-
- return 0;
-
-out2:
- rdma_destroy_id(sc->rdma.cm_id);
- sc->rdma.cm_id = NULL;
-
-out1:
- return rc;
-}
-
-/*
- * Send a negotiation request message to the peer
- * The negotiation procedure is in [MS-SMBD] 3.1.5.2 and 3.1.5.3
- * After negotiation, the transport is connected and ready for
- * carrying upper layer SMB payload
- */
-static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc)
-{
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- int rc;
- struct smbdirect_send_io *request;
- struct smbdirect_negotiate_req *packet;
-
- request = smbd_alloc_send_io(sc);
- if (IS_ERR(request))
- return PTR_ERR(request);
-
- packet = smbdirect_send_io_payload(request);
- packet->min_version = cpu_to_le16(SMBDIRECT_V1);
- packet->max_version = cpu_to_le16(SMBDIRECT_V1);
- packet->reserved = 0;
- packet->credits_requested = cpu_to_le16(sp->send_credit_target);
- packet->preferred_send_size = cpu_to_le32(sp->max_send_size);
- packet->max_receive_size = cpu_to_le32(sp->max_recv_size);
- packet->max_fragmented_size =
- cpu_to_le32(sp->max_fragmented_recv_size);
-
- request->sge[0].addr = ib_dma_map_single(
- sc->ib.dev, (void *)packet,
- sizeof(*packet), DMA_TO_DEVICE);
- if (ib_dma_mapping_error(sc->ib.dev, request->sge[0].addr)) {
- rc = -EIO;
- goto dma_mapping_failed;
- }
- request->num_sge = 1;
-
- request->sge[0].length = sizeof(*packet);
- request->sge[0].lkey = sc->ib.pd->local_dma_lkey;
-
- rc = smbd_post_send(sc, NULL, request);
- if (!rc)
- return 0;
-
- if (rc == -EAGAIN)
- rc = -EIO;
-
-dma_mapping_failed:
- smbd_free_send_io(request);
- return rc;
-}
-
-/*
- * Extend the credits to remote peer
- * This implements [MS-SMBD] 3.1.5.9
- * The idea is that we should extend credits to remote peer as quickly as
- * it's allowed, to maintain data flow. We allocate as much receive
- * buffer as possible, and extend the receive credits to remote peer
- * return value: the new credtis being granted.
- */
-static int manage_credits_prior_sending(struct smbdirect_socket *sc)
-{
- int missing;
- int available;
- int new_credits;
-
- if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target)
- return 0;
-
- missing = (int)sc->recv_io.credits.target - atomic_read(&sc->recv_io.credits.count);
- available = atomic_xchg(&sc->recv_io.credits.available, 0);
- new_credits = (u16)min3(U16_MAX, missing, available);
- if (new_credits <= 0) {
- /*
- * If credits are available, but not granted
- * we need to re-add them again.
- */
- if (available)
- atomic_add(available, &sc->recv_io.credits.available);
- return 0;
- }
-
- if (new_credits < available) {
- /*
- * Readd the remaining available again.
- */
- available -= new_credits;
- atomic_add(available, &sc->recv_io.credits.available);
- }
-
- /*
- * Remember we granted the credits
- */
- atomic_add(new_credits, &sc->recv_io.credits.count);
- return new_credits;
-}
-
-/*
- * Check if we need to send a KEEP_ALIVE message
- * The idle connection timer triggers a KEEP_ALIVE message when expires
- * SMBDIRECT_FLAG_RESPONSE_REQUESTED is set in the message flag to have peer send
- * back a response.
- * return value:
- * 1 if SMBDIRECT_FLAG_RESPONSE_REQUESTED needs to be set
- * 0: otherwise
- */
-static int manage_keep_alive_before_sending(struct smbdirect_socket *sc)
-{
- struct smbdirect_socket_parameters *sp = &sc->parameters;
-
- if (sc->idle.keepalive == SMBDIRECT_KEEPALIVE_PENDING) {
- sc->idle.keepalive = SMBDIRECT_KEEPALIVE_SENT;
- /*
- * Now use the keepalive timeout (instead of keepalive interval)
- * in order to wait for a response
- */
- mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
- msecs_to_jiffies(sp->keepalive_timeout_msec));
- return 1;
- }
- return 0;
-}
-
-static int smbd_ib_post_send(struct smbdirect_socket *sc,
- struct ib_send_wr *wr)
-{
- int ret;
-
- atomic_inc(&sc->send_io.pending.count);
- ret = ib_post_send(sc->ib.qp, wr, NULL);
- if (ret) {
- pr_err("failed to post send: %d\n", ret);
- smbd_disconnect_rdma_connection(sc);
- ret = -EAGAIN;
- }
- return ret;
-}
-
-/* Post the send request */
-static int smbd_post_send(struct smbdirect_socket *sc,
- struct smbdirect_send_batch *batch,
- struct smbdirect_send_io *request)
-{
- int i;
-
- for (i = 0; i < request->num_sge; i++) {
- log_rdma_send(INFO,
- "rdma_request sge[%d] addr=0x%llx length=%u\n",
- i, request->sge[i].addr, request->sge[i].length);
- ib_dma_sync_single_for_device(
- sc->ib.dev,
- request->sge[i].addr,
- request->sge[i].length,
- DMA_TO_DEVICE);
- }
-
- request->cqe.done = send_done;
- request->wr.next = NULL;
- request->wr.sg_list = request->sge;
- request->wr.num_sge = request->num_sge;
- request->wr.opcode = IB_WR_SEND;
-
- if (batch) {
- request->wr.wr_cqe = NULL;
- request->wr.send_flags = 0;
- if (!list_empty(&batch->msg_list)) {
- struct smbdirect_send_io *last;
-
- last = list_last_entry(&batch->msg_list,
- struct smbdirect_send_io,
- sibling_list);
- last->wr.next = &request->wr;
- }
- list_add_tail(&request->sibling_list, &batch->msg_list);
- batch->wr_cnt++;
- return 0;
- }
-
- request->wr.wr_cqe = &request->cqe;
- request->wr.send_flags = IB_SEND_SIGNALED;
- return smbd_ib_post_send(sc, &request->wr);
-}
-
-static void smbd_send_batch_init(struct smbdirect_send_batch *batch,
- bool need_invalidate_rkey,
- unsigned int remote_key)
-{
- INIT_LIST_HEAD(&batch->msg_list);
- batch->wr_cnt = 0;
- batch->need_invalidate_rkey = need_invalidate_rkey;
- batch->remote_key = remote_key;
- batch->credit = 0;
-}
-
-static int smbd_send_batch_flush(struct smbdirect_socket *sc,
- struct smbdirect_send_batch *batch,
- bool is_last)
-{
- struct smbdirect_send_io *first, *last;
- int ret = 0;
-
- if (list_empty(&batch->msg_list))
- goto release_credit;
-
- first = list_first_entry(&batch->msg_list,
- struct smbdirect_send_io,
- sibling_list);
- last = list_last_entry(&batch->msg_list,
- struct smbdirect_send_io,
- sibling_list);
-
- if (batch->need_invalidate_rkey) {
- first->wr.opcode = IB_WR_SEND_WITH_INV;
- first->wr.ex.invalidate_rkey = batch->remote_key;
- batch->need_invalidate_rkey = false;
- batch->remote_key = 0;
- }
-
- last->wr.send_flags = IB_SEND_SIGNALED;
- last->wr.wr_cqe = &last->cqe;
-
- /*
- * Remove last from batch->msg_list
- * and splice the rest of batch->msg_list
- * to last->sibling_list.
- *
- * batch->msg_list is a valid empty list
- * at the end.
- */
- list_del_init(&last->sibling_list);
- list_splice_tail_init(&batch->msg_list, &last->sibling_list);
- batch->wr_cnt = 0;
-
- ret = smbd_ib_post_send(sc, &first->wr);
- if (ret) {
- struct smbdirect_send_io *sibling, *next;
-
- list_for_each_entry_safe(sibling, next, &last->sibling_list, sibling_list) {
- list_del_init(&sibling->sibling_list);
- smbd_free_send_io(sibling);
- }
- smbd_free_send_io(last);
- }
-
-release_credit:
- if (is_last && !ret && batch->credit) {
- atomic_add(batch->credit, &sc->send_io.bcredits.count);
- batch->credit = 0;
- wake_up(&sc->send_io.bcredits.wait_queue);
- }
-
- return ret;
-}
-
-static int wait_for_credits(struct smbdirect_socket *sc,
- wait_queue_head_t *waitq, atomic_t *total_credits,
- int needed)
-{
- int ret;
-
- do {
- if (atomic_sub_return(needed, total_credits) >= 0)
- return 0;
-
- atomic_add(needed, total_credits);
- ret = wait_event_interruptible(*waitq,
- atomic_read(total_credits) >= needed ||
- sc->status != SMBDIRECT_SOCKET_CONNECTED);
-
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
- return -ENOTCONN;
- else if (ret < 0)
- return ret;
- } while (true);
-}
-
-static int wait_for_send_bcredit(struct smbdirect_socket *sc,
- struct smbdirect_send_batch *batch)
-{
- int ret;
-
- if (batch->credit)
- return 0;
-
- ret = wait_for_credits(sc,
- &sc->send_io.bcredits.wait_queue,
- &sc->send_io.bcredits.count,
- 1);
- if (ret)
- return ret;
-
- batch->credit = 1;
- return 0;
-}
-
-static int wait_for_send_lcredit(struct smbdirect_socket *sc,
- struct smbdirect_send_batch *batch)
-{
- if (batch && (atomic_read(&sc->send_io.lcredits.count) <= 1)) {
- int ret;
-
- ret = smbd_send_batch_flush(sc, batch, false);
- if (ret)
- return ret;
- }
-
- return wait_for_credits(sc,
- &sc->send_io.lcredits.wait_queue,
- &sc->send_io.lcredits.count,
- 1);
-}
-
-static int wait_for_send_credits(struct smbdirect_socket *sc,
- struct smbdirect_send_batch *batch)
-{
- if (batch &&
- (batch->wr_cnt >= 16 || atomic_read(&sc->send_io.credits.count) <= 1)) {
- int ret;
-
- ret = smbd_send_batch_flush(sc, batch, false);
- if (ret)
- return ret;
- }
-
- return wait_for_credits(sc,
- &sc->send_io.credits.wait_queue,
- &sc->send_io.credits.count,
- 1);
-}
-
-static int smbd_post_send_iter(struct smbdirect_socket *sc,
- struct smbdirect_send_batch *batch,
- struct iov_iter *iter,
- int *_remaining_data_length)
-{
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- int rc;
- int header_length;
- int data_length;
- struct smbdirect_send_io *request;
- struct smbdirect_data_transfer *packet;
- int new_credits = 0;
- struct smbdirect_send_batch _batch;
-
- if (!batch) {
- smbd_send_batch_init(&_batch, false, 0);
- batch = &_batch;
- }
-
- rc = wait_for_send_bcredit(sc, batch);
- if (rc) {
- log_outgoing(ERR, "disconnected not sending on wait_bcredit\n");
- rc = -EAGAIN;
- goto err_wait_bcredit;
- }
-
- rc = wait_for_send_lcredit(sc, batch);
- if (rc) {
- log_outgoing(ERR, "disconnected not sending on wait_lcredit\n");
- rc = -EAGAIN;
- goto err_wait_lcredit;
- }
-
- rc = wait_for_send_credits(sc, batch);
- if (rc) {
- log_outgoing(ERR, "disconnected not sending on wait_credit\n");
- rc = -EAGAIN;
- goto err_wait_credit;
- }
-
- new_credits = manage_credits_prior_sending(sc);
- if (new_credits == 0 &&
- atomic_read(&sc->send_io.credits.count) == 0 &&
- atomic_read(&sc->recv_io.credits.count) == 0) {
- queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
- rc = wait_event_interruptible(sc->send_io.credits.wait_queue,
- atomic_read(&sc->send_io.credits.count) >= 1 ||
- atomic_read(&sc->recv_io.credits.available) >= 1 ||
- sc->status != SMBDIRECT_SOCKET_CONNECTED);
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
- rc = -ENOTCONN;
- if (rc < 0) {
- log_outgoing(ERR, "disconnected not sending on last credit\n");
- rc = -EAGAIN;
- goto err_wait_credit;
- }
-
- new_credits = manage_credits_prior_sending(sc);
- }
-
- request = smbd_alloc_send_io(sc);
- if (IS_ERR(request)) {
- rc = PTR_ERR(request);
- goto err_alloc;
- }
-
- memset(request->sge, 0, sizeof(request->sge));
-
- /* Map the packet to DMA */
- header_length = sizeof(struct smbdirect_data_transfer);
- /* If this is a packet without payload, don't send padding */
- if (!iter)
- header_length = offsetof(struct smbdirect_data_transfer, padding);
-
- packet = smbdirect_send_io_payload(request);
- request->sge[0].addr = ib_dma_map_single(sc->ib.dev,
- (void *)packet,
- header_length,
- DMA_TO_DEVICE);
- if (ib_dma_mapping_error(sc->ib.dev, request->sge[0].addr)) {
- rc = -EIO;
- goto err_dma;
- }
-
- request->sge[0].length = header_length;
- request->sge[0].lkey = sc->ib.pd->local_dma_lkey;
- request->num_sge = 1;
-
- /* Fill in the data payload to find out how much data we can add */
- if (iter) {
- struct smb_extract_to_rdma extract = {
- .nr_sge = request->num_sge,
- .max_sge = SMBDIRECT_SEND_IO_MAX_SGE,
- .sge = request->sge,
- .device = sc->ib.dev,
- .local_dma_lkey = sc->ib.pd->local_dma_lkey,
- .direction = DMA_TO_DEVICE,
- };
- size_t payload_len = umin(*_remaining_data_length,
- sp->max_send_size - sizeof(*packet));
-
- rc = smb_extract_iter_to_rdma(iter, payload_len,
- &extract);
- if (rc < 0)
- goto err_dma;
- data_length = rc;
- request->num_sge = extract.nr_sge;
- *_remaining_data_length -= data_length;
- } else {
- data_length = 0;
- }
-
- /* Fill in the packet header */
- packet->credits_requested = cpu_to_le16(sp->send_credit_target);
- packet->credits_granted = cpu_to_le16(new_credits);
-
- packet->flags = 0;
- if (manage_keep_alive_before_sending(sc))
- packet->flags |= cpu_to_le16(SMBDIRECT_FLAG_RESPONSE_REQUESTED);
-
- packet->reserved = 0;
- if (!data_length)
- packet->data_offset = 0;
- else
- packet->data_offset = cpu_to_le32(24);
- packet->data_length = cpu_to_le32(data_length);
- packet->remaining_data_length = cpu_to_le32(*_remaining_data_length);
- packet->padding = 0;
-
- log_outgoing(INFO, "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));
-
- rc = smbd_post_send(sc, batch, request);
- if (!rc) {
- if (batch != &_batch)
- return 0;
-
- rc = smbd_send_batch_flush(sc, batch, true);
- if (!rc)
- return 0;
- }
-
-err_dma:
- smbd_free_send_io(request);
-
-err_alloc:
- atomic_inc(&sc->send_io.credits.count);
- wake_up(&sc->send_io.credits.wait_queue);
-
-err_wait_credit:
- atomic_inc(&sc->send_io.lcredits.count);
- wake_up(&sc->send_io.lcredits.wait_queue);
-
-err_wait_lcredit:
- atomic_add(batch->credit, &sc->send_io.bcredits.count);
- batch->credit = 0;
- wake_up(&sc->send_io.bcredits.wait_queue);
-
-err_wait_bcredit:
- return rc;
-}
-
-/*
- * Send an empty message
- * Empty message is used to extend credits to peer to for keep live
- * while there is no upper layer payload to send at the time
- */
-static int smbd_post_send_empty(struct smbdirect_socket *sc)
-{
- int remaining_data_length = 0;
-
- sc->statistics.send_empty++;
- return smbd_post_send_iter(sc, NULL, NULL, &remaining_data_length);
-}
-
static int smbd_post_send_full_iter(struct smbdirect_socket *sc,
struct smbdirect_send_batch *batch,
struct iov_iter *iter,
- int *_remaining_data_length)
+ u32 remaining_data_length)
{
- int rc = 0;
+ int bytes = 0;
/*
- * smbd_post_send_iter() respects the
+ * smbdirect_connection_send_single_iter() respects the
* negotiated max_send_size, so we need to
* loop until the full iter is posted
*/
while (iov_iter_count(iter) > 0) {
- rc = smbd_post_send_iter(sc, batch, iter, _remaining_data_length);
- if (rc < 0)
- break;
- }
-
- return rc;
-}
-
-/*
- * Post a receive request to the transport
- * The remote peer can only send data when a receive request is posted
- * The interaction is controlled by send/receive credit system
- */
-static int smbd_post_recv(
- struct smbdirect_socket *sc, struct smbdirect_recv_io *response)
-{
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- struct ib_recv_wr recv_wr;
- int rc = -EIO;
-
- response->sge.addr = ib_dma_map_single(
- sc->ib.dev, response->packet,
- sp->max_recv_size, DMA_FROM_DEVICE);
- if (ib_dma_mapping_error(sc->ib.dev, response->sge.addr))
- return rc;
-
- response->sge.length = sp->max_recv_size;
- response->sge.lkey = sc->ib.pd->local_dma_lkey;
-
- response->cqe.done = recv_done;
-
- recv_wr.wr_cqe = &response->cqe;
- recv_wr.next = NULL;
- recv_wr.sg_list = &response->sge;
- recv_wr.num_sge = 1;
-
- rc = ib_post_recv(sc->ib.qp, &recv_wr, NULL);
- if (rc) {
- ib_dma_unmap_single(sc->ib.dev, response->sge.addr,
- response->sge.length, DMA_FROM_DEVICE);
- response->sge.length = 0;
- smbd_disconnect_rdma_connection(sc);
- log_rdma_recv(ERR, "ib_post_recv failed rc=%d\n", rc);
- }
-
- return rc;
-}
-
-/* Perform SMBD negotiate according to [MS-SMBD] 3.1.5.2 */
-static int smbd_negotiate(struct smbdirect_socket *sc)
-{
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- int rc;
- struct smbdirect_recv_io *response = get_receive_buffer(sc);
-
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_NEGOTIATE_NEEDED);
- sc->status = SMBDIRECT_SOCKET_NEGOTIATE_RUNNING;
-
- sc->recv_io.expected = SMBDIRECT_EXPECT_NEGOTIATE_REP;
- rc = smbd_post_recv(sc, response);
- log_rdma_event(INFO, "smbd_post_recv rc=%d iov.addr=0x%llx iov.length=%u iov.lkey=0x%x\n",
- rc, response->sge.addr,
- response->sge.length, response->sge.lkey);
- if (rc) {
- put_receive_buffer(sc, response);
- return rc;
- }
-
- rc = smbd_post_send_negotiate_req(sc);
- if (rc)
- return rc;
-
- rc = wait_event_interruptible_timeout(
- sc->status_wait,
- sc->status != SMBDIRECT_SOCKET_NEGOTIATE_RUNNING,
- msecs_to_jiffies(sp->negotiate_timeout_msec));
- log_rdma_event(INFO, "wait_event_interruptible_timeout rc=%d\n", rc);
-
- if (sc->status == SMBDIRECT_SOCKET_CONNECTED)
- return 0;
-
- if (rc == 0)
- rc = -ETIMEDOUT;
- else if (rc == -ERESTARTSYS)
- rc = -EINTR;
- else
- rc = -ENOTCONN;
-
- return rc;
-}
-
-/*
- * Implement Connection.FragmentReassemblyBuffer defined in [MS-SMBD] 3.1.1.1
- * This is a queue for reassembling upper layer payload and present to upper
- * layer. All the inncoming payload go to the reassembly queue, regardless of
- * if reassembly is required. The uuper layer code reads from the queue for all
- * incoming payloads.
- * Put a received packet to the reassembly queue
- * response: the packet received
- * data_length: the size of payload in this packet
- */
-static void enqueue_reassembly(
- struct smbdirect_socket *sc,
- struct smbdirect_recv_io *response,
- int data_length)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
- list_add_tail(&response->list, &sc->recv_io.reassembly.list);
- sc->recv_io.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();
- sc->recv_io.reassembly.data_length += data_length;
- spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
- sc->statistics.enqueue_reassembly_queue++;
-}
-
-/*
- * Get the first entry at the front of reassembly queue
- * Caller is responsible for locking
- * return value: the first entry if any, NULL if queue is empty
- */
-static struct smbdirect_recv_io *_get_first_reassembly(struct smbdirect_socket *sc)
-{
- struct smbdirect_recv_io *ret = NULL;
-
- if (!list_empty(&sc->recv_io.reassembly.list)) {
- ret = list_first_entry(
- &sc->recv_io.reassembly.list,
- struct smbdirect_recv_io, list);
- }
- return ret;
-}
-
-/*
- * Get a receive buffer
- * For each remote send, we need to post a receive. The receive buffers are
- * pre-allocated in advance.
- * return value: the receive buffer, NULL if none is available
- */
-static struct smbdirect_recv_io *get_receive_buffer(struct smbdirect_socket *sc)
-{
- struct smbdirect_recv_io *ret = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&sc->recv_io.free.lock, flags);
- if (!list_empty(&sc->recv_io.free.list)) {
- ret = list_first_entry(
- &sc->recv_io.free.list,
- struct smbdirect_recv_io, list);
- list_del(&ret->list);
- sc->statistics.get_receive_buffer++;
- }
- spin_unlock_irqrestore(&sc->recv_io.free.lock, flags);
-
- return ret;
-}
-
-/*
- * Return a receive buffer
- * Upon returning of a receive buffer, we can post new receive and extend
- * more receive credits to remote peer. This is done immediately after a
- * receive buffer is returned.
- */
-static void put_receive_buffer(
- struct smbdirect_socket *sc, struct smbdirect_recv_io *response)
-{
- unsigned long flags;
-
- if (likely(response->sge.length != 0)) {
- ib_dma_unmap_single(sc->ib.dev,
- response->sge.addr,
- response->sge.length,
- DMA_FROM_DEVICE);
- response->sge.length = 0;
- }
-
- spin_lock_irqsave(&sc->recv_io.free.lock, flags);
- list_add_tail(&response->list, &sc->recv_io.free.list);
- sc->statistics.put_receive_buffer++;
- spin_unlock_irqrestore(&sc->recv_io.free.lock, flags);
-
- queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
-}
-
-/* Preallocate all receive buffer on transport establishment */
-static int allocate_receive_buffers(struct smbdirect_socket *sc, int num_buf)
-{
- struct smbdirect_recv_io *response;
- int i;
-
- for (i = 0; i < num_buf; i++) {
- response = mempool_alloc(sc->recv_io.mem.pool, GFP_KERNEL);
- if (!response)
- goto allocate_failed;
-
- response->socket = sc;
- response->sge.length = 0;
- list_add_tail(&response->list, &sc->recv_io.free.list);
- }
-
- return 0;
-
-allocate_failed:
- while (!list_empty(&sc->recv_io.free.list)) {
- response = list_first_entry(
- &sc->recv_io.free.list,
- struct smbdirect_recv_io, list);
- list_del(&response->list);
-
- mempool_free(response, sc->recv_io.mem.pool);
- }
- return -ENOMEM;
-}
-
-static void destroy_receive_buffers(struct smbdirect_socket *sc)
-{
- struct smbdirect_recv_io *response;
-
- while ((response = get_receive_buffer(sc)))
- mempool_free(response, sc->recv_io.mem.pool);
-}
-
-static void send_immediate_empty_message(struct work_struct *work)
-{
- struct smbdirect_socket *sc =
- container_of(work, struct smbdirect_socket, idle.immediate_work);
-
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
- return;
-
- log_keep_alive(INFO, "send an empty message\n");
- smbd_post_send_empty(sc);
-}
+ int rc;
-/* Implement idle connection timer [MS-SMBD] 3.1.6.2 */
-static void idle_connection_timer(struct work_struct *work)
-{
- struct smbdirect_socket *sc =
- container_of(work, struct smbdirect_socket, idle.timer_work.work);
- struct smbdirect_socket_parameters *sp = &sc->parameters;
-
- if (sc->idle.keepalive != SMBDIRECT_KEEPALIVE_NONE) {
- log_keep_alive(ERR,
- "error status sc->idle.keepalive=%d\n",
- sc->idle.keepalive);
- smbd_disconnect_rdma_connection(sc);
- return;
+ rc = smbdirect_connection_send_single_iter(sc,
+ batch,
+ iter,
+ 0, /* flags */
+ remaining_data_length);
+ if (rc < 0)
+ return rc;
+ remaining_data_length -= rc;
+ bytes += rc;
}
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
- return;
-
- /*
- * Now use the keepalive timeout (instead of keepalive interval)
- * in order to wait for a response
- */
- sc->idle.keepalive = SMBDIRECT_KEEPALIVE_PENDING;
- mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
- msecs_to_jiffies(sp->keepalive_timeout_msec));
- log_keep_alive(INFO, "schedule send of empty idle message\n");
- queue_work(sc->workqueue, &sc->idle.immediate_work);
+ return bytes;
}
/*
@@ -1884,88 +202,14 @@ static void idle_connection_timer(struct work_struct *work)
void smbd_destroy(struct TCP_Server_Info *server)
{
struct smbd_connection *info = server->smbd_conn;
- struct smbdirect_socket *sc;
- struct smbdirect_recv_io *response;
- unsigned long flags;
if (!info) {
log_rdma_event(INFO, "rdma session already destroyed\n");
return;
}
- sc = &info->socket;
-
- log_rdma_event(INFO, "cancelling and disable disconnect_work\n");
- disable_work_sync(&sc->disconnect_work);
-
- log_rdma_event(INFO, "destroying rdma session\n");
- if (sc->status < SMBDIRECT_SOCKET_DISCONNECTING)
- smbd_disconnect_rdma_work(&sc->disconnect_work);
- if (sc->status < SMBDIRECT_SOCKET_DISCONNECTED) {
- log_rdma_event(INFO, "wait for transport being disconnected\n");
- wait_event(sc->status_wait, sc->status == SMBDIRECT_SOCKET_DISCONNECTED);
- log_rdma_event(INFO, "waited for transport being disconnected\n");
- }
-
- /*
- * Wake up all waiters in all wait queues
- * in order to notice the broken connection.
- *
- * Most likely this was already called via
- * smbd_disconnect_rdma_work(), but call it again...
- */
- smbd_disconnect_wake_up_all(sc);
- log_rdma_event(INFO, "cancelling recv_io.posted.refill_work\n");
- disable_work_sync(&sc->recv_io.posted.refill_work);
+ smbdirect_socket_release(info->socket);
- log_rdma_event(INFO, "destroying qp\n");
- ib_drain_qp(sc->ib.qp);
- rdma_destroy_qp(sc->rdma.cm_id);
- sc->ib.qp = NULL;
-
- log_rdma_event(INFO, "cancelling idle timer\n");
- disable_delayed_work_sync(&sc->idle.timer_work);
- log_rdma_event(INFO, "cancelling send immediate work\n");
- disable_work_sync(&sc->idle.immediate_work);
-
- /* It's not possible for upper layer to get to reassembly */
- log_rdma_event(INFO, "drain the reassembly queue\n");
- do {
- spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
- response = _get_first_reassembly(sc);
- if (response) {
- list_del(&response->list);
- spin_unlock_irqrestore(
- &sc->recv_io.reassembly.lock, flags);
- put_receive_buffer(sc, response);
- } else
- spin_unlock_irqrestore(
- &sc->recv_io.reassembly.lock, flags);
- } while (response);
- sc->recv_io.reassembly.data_length = 0;
-
- log_rdma_event(INFO, "free receive buffers\n");
- destroy_receive_buffers(sc);
-
- log_rdma_event(INFO, "freeing mr list\n");
- destroy_mr_list(sc);
-
- ib_free_cq(sc->ib.send_cq);
- ib_free_cq(sc->ib.recv_cq);
- ib_dealloc_pd(sc->ib.pd);
- rdma_destroy_id(sc->rdma.cm_id);
-
- /* free mempools */
- mempool_destroy(sc->send_io.mem.pool);
- kmem_cache_destroy(sc->send_io.mem.cache);
-
- mempool_destroy(sc->recv_io.mem.pool);
- kmem_cache_destroy(sc->recv_io.mem.cache);
-
- sc->status = SMBDIRECT_SOCKET_DESTROYED;
-
- destroy_workqueue(sc->workqueue);
- log_rdma_event(INFO, "rdma session destroyed\n");
kfree(info);
server->smbd_conn = NULL;
}
@@ -1987,10 +231,8 @@ int smbd_reconnect(struct TCP_Server_Info *server)
* This is possible if transport is disconnected and we haven't received
* notification from RDMA, but upper layer has detected timeout
*/
- if (server->smbd_conn->socket.status == SMBDIRECT_SOCKET_CONNECTED) {
- log_rdma_event(INFO, "disconnecting transport\n");
- smbd_destroy(server);
- }
+ log_rdma_event(INFO, "disconnecting transport\n");
+ smbd_destroy(server);
create_conn:
log_rdma_event(INFO, "creating rdma session\n");
@@ -2006,112 +248,43 @@ create_conn:
return -ENOENT;
}
-static void destroy_caches(struct smbdirect_socket *sc)
-{
- destroy_receive_buffers(sc);
- mempool_destroy(sc->recv_io.mem.pool);
- kmem_cache_destroy(sc->recv_io.mem.cache);
- mempool_destroy(sc->send_io.mem.pool);
- kmem_cache_destroy(sc->send_io.mem.cache);
-}
-
-#define MAX_NAME_LEN 80
-static int allocate_caches(struct smbdirect_socket *sc)
-{
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- char name[MAX_NAME_LEN];
- int rc;
-
- if (WARN_ON_ONCE(sp->max_recv_size < sizeof(struct smbdirect_data_transfer)))
- return -ENOMEM;
-
- scnprintf(name, MAX_NAME_LEN, "smbdirect_send_io_%p", sc);
- sc->send_io.mem.cache =
- kmem_cache_create(
- name,
- sizeof(struct smbdirect_send_io) +
- sizeof(struct smbdirect_data_transfer),
- 0, SLAB_HWCACHE_ALIGN, NULL);
- if (!sc->send_io.mem.cache)
- return -ENOMEM;
-
- sc->send_io.mem.pool =
- mempool_create(sp->send_credit_target, mempool_alloc_slab,
- mempool_free_slab, sc->send_io.mem.cache);
- if (!sc->send_io.mem.pool)
- goto out1;
-
- scnprintf(name, MAX_NAME_LEN, "smbdirect_recv_io_%p", sc);
-
- struct kmem_cache_args response_args = {
- .align = __alignof__(struct smbdirect_recv_io),
- .useroffset = (offsetof(struct smbdirect_recv_io, packet) +
- sizeof(struct smbdirect_data_transfer)),
- .usersize = sp->max_recv_size - sizeof(struct smbdirect_data_transfer),
- };
- sc->recv_io.mem.cache =
- kmem_cache_create(name,
- sizeof(struct smbdirect_recv_io) + sp->max_recv_size,
- &response_args, SLAB_HWCACHE_ALIGN);
- if (!sc->recv_io.mem.cache)
- goto out2;
-
- sc->recv_io.mem.pool =
- mempool_create(sp->recv_credit_max, mempool_alloc_slab,
- mempool_free_slab, sc->recv_io.mem.cache);
- if (!sc->recv_io.mem.pool)
- goto out3;
-
- rc = allocate_receive_buffers(sc, sp->recv_credit_max);
- if (rc) {
- log_rdma_event(ERR, "failed to allocate receive buffers\n");
- goto out4;
- }
-
- return 0;
-
-out4:
- mempool_destroy(sc->recv_io.mem.pool);
-out3:
- kmem_cache_destroy(sc->recv_io.mem.cache);
-out2:
- mempool_destroy(sc->send_io.mem.pool);
-out1:
- kmem_cache_destroy(sc->send_io.mem.cache);
- return -ENOMEM;
-}
-
/* Create a SMBD connection, called by upper layer */
static struct smbd_connection *_smbd_get_connection(
struct TCP_Server_Info *server, struct sockaddr *dstaddr, int port)
{
- int rc;
+ struct net *net = cifs_net_ns(server);
struct smbd_connection *info;
struct smbdirect_socket *sc;
+ struct smbdirect_socket_parameters init_params = {};
struct smbdirect_socket_parameters *sp;
- struct rdma_conn_param conn_param;
- struct ib_qp_cap qp_cap;
- struct ib_qp_init_attr qp_attr;
- struct sockaddr_in *addr_in = (struct sockaddr_in *) dstaddr;
- struct ib_port_immutable port_immutable;
- __be32 ird_ord_hdr[2];
- char wq_name[80];
- struct workqueue_struct *workqueue;
-
- info = kzalloc_obj(struct smbd_connection);
- if (!info)
- return NULL;
- sc = &info->socket;
- scnprintf(wq_name, ARRAY_SIZE(wq_name), "smbd_%p", sc);
- workqueue = create_workqueue(wq_name);
- if (!workqueue)
- goto create_wq_failed;
- smbdirect_socket_init(sc);
- sc->workqueue = workqueue;
- sp = &sc->parameters;
+ __be16 *sport;
+ u64 port_flags = 0;
+ int ret;
- INIT_WORK(&sc->disconnect_work, smbd_disconnect_rdma_work);
+ switch (port) {
+ case SMBD_PORT:
+ /*
+ * only allow iWarp devices
+ * for port 5445.
+ */
+ port_flags |= SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW;
+ break;
+ case SMB_PORT:
+ /*
+ * only allow InfiniBand, RoCEv1 or RoCEv2
+ * devices for port 445.
+ *
+ * (Basically don't allow iWarp devices)
+ */
+ port_flags |= SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB;
+ break;
+ }
+ /*
+ * Create the initial parameters
+ */
+ sp = &init_params;
+ sp->flags = port_flags;
sp->resolve_addr_timeout_msec = RDMA_RESOLVE_TIMEOUT;
sp->resolve_route_timeout_msec = RDMA_RESOLVE_TIMEOUT;
sp->rdma_connect_timeout_msec = RDMA_RESOLVE_TIMEOUT;
@@ -2127,213 +300,55 @@ static struct smbd_connection *_smbd_get_connection(
sp->keepalive_interval_msec = smbd_keep_alive_interval * 1000;
sp->keepalive_timeout_msec = KEEPALIVE_RECV_TIMEOUT * 1000;
- rc = smbd_ia_open(sc, dstaddr, port);
- if (rc) {
- log_rdma_event(INFO, "smbd_ia_open rc=%d\n", rc);
- goto create_id_failed;
- }
-
- if (sp->send_credit_target > sc->ib.dev->attrs.max_cqe ||
- sp->send_credit_target > sc->ib.dev->attrs.max_qp_wr) {
- log_rdma_event(ERR, "consider lowering send_credit_target = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n",
- sp->send_credit_target,
- sc->ib.dev->attrs.max_cqe,
- sc->ib.dev->attrs.max_qp_wr);
- goto config_failed;
- }
-
- if (sp->recv_credit_max > sc->ib.dev->attrs.max_cqe ||
- sp->recv_credit_max > sc->ib.dev->attrs.max_qp_wr) {
- log_rdma_event(ERR, "consider lowering receive_credit_max = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n",
- sp->recv_credit_max,
- sc->ib.dev->attrs.max_cqe,
- sc->ib.dev->attrs.max_qp_wr);
- goto config_failed;
- }
-
- if (sc->ib.dev->attrs.max_send_sge < SMBDIRECT_SEND_IO_MAX_SGE ||
- sc->ib.dev->attrs.max_recv_sge < SMBDIRECT_RECV_IO_MAX_SGE) {
- log_rdma_event(ERR,
- "device %.*s max_send_sge/max_recv_sge = %d/%d too small\n",
- IB_DEVICE_NAME_MAX,
- sc->ib.dev->name,
- sc->ib.dev->attrs.max_send_sge,
- sc->ib.dev->attrs.max_recv_sge);
- goto config_failed;
- }
-
- sp->responder_resources =
- min_t(u8, sp->responder_resources,
- sc->ib.dev->attrs.max_qp_rd_atom);
- log_rdma_mr(INFO, "responder_resources=%d\n",
- sp->responder_resources);
-
- /*
- * We use allocate sp->responder_resources * 2 MRs
- * and each MR needs WRs for REG and INV, so
- * we use '* 4'.
- *
- * +1 for ib_drain_qp()
- */
- memset(&qp_cap, 0, sizeof(qp_cap));
- qp_cap.max_send_wr = sp->send_credit_target + sp->responder_resources * 4 + 1;
- qp_cap.max_recv_wr = sp->recv_credit_max + 1;
- qp_cap.max_send_sge = SMBDIRECT_SEND_IO_MAX_SGE;
- qp_cap.max_recv_sge = SMBDIRECT_RECV_IO_MAX_SGE;
-
- sc->ib.pd = ib_alloc_pd(sc->ib.dev, 0);
- if (IS_ERR(sc->ib.pd)) {
- rc = PTR_ERR(sc->ib.pd);
- sc->ib.pd = NULL;
- log_rdma_event(ERR, "ib_alloc_pd() returned %d\n", rc);
- goto alloc_pd_failed;
- }
-
- sc->ib.send_cq =
- ib_alloc_cq_any(sc->ib.dev, sc,
- qp_cap.max_send_wr, IB_POLL_SOFTIRQ);
- if (IS_ERR(sc->ib.send_cq)) {
- sc->ib.send_cq = NULL;
- goto alloc_cq_failed;
- }
-
- sc->ib.recv_cq =
- ib_alloc_cq_any(sc->ib.dev, sc,
- qp_cap.max_recv_wr, IB_POLL_SOFTIRQ);
- if (IS_ERR(sc->ib.recv_cq)) {
- sc->ib.recv_cq = NULL;
- goto alloc_cq_failed;
- }
-
- memset(&qp_attr, 0, sizeof(qp_attr));
- qp_attr.event_handler = smbd_qp_async_error_upcall;
- qp_attr.qp_context = sc;
- qp_attr.cap = qp_cap;
- qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
- qp_attr.qp_type = IB_QPT_RC;
- qp_attr.send_cq = sc->ib.send_cq;
- qp_attr.recv_cq = sc->ib.recv_cq;
- qp_attr.port_num = ~0;
-
- rc = rdma_create_qp(sc->rdma.cm_id, sc->ib.pd, &qp_attr);
- if (rc) {
- log_rdma_event(ERR, "rdma_create_qp failed %i\n", rc);
- goto create_qp_failed;
- }
- sc->ib.qp = sc->rdma.cm_id->qp;
-
- memset(&conn_param, 0, sizeof(conn_param));
- conn_param.initiator_depth = sp->initiator_depth;
- conn_param.responder_resources = sp->responder_resources;
-
- /* Need to send IRD/ORD in private data for iWARP */
- sc->ib.dev->ops.get_port_immutable(
- sc->ib.dev, sc->rdma.cm_id->port_num, &port_immutable);
- if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) {
- ird_ord_hdr[0] = cpu_to_be32(conn_param.responder_resources);
- ird_ord_hdr[1] = cpu_to_be32(conn_param.initiator_depth);
- 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 = SMBD_CM_RETRY;
- conn_param.rnr_retry_count = SMBD_CM_RNR_RETRY;
- conn_param.flow_control = 0;
-
- log_rdma_event(INFO, "connecting to IP %pI4 port %d\n",
- &addr_in->sin_addr, port);
-
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED);
- sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING;
- rc = rdma_connect(sc->rdma.cm_id, &conn_param);
- if (rc) {
- log_rdma_event(ERR, "rdma_connect() failed with %i\n", rc);
- goto rdma_connect_failed;
- }
-
- wait_event_interruptible_timeout(
- sc->status_wait,
- sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING,
- msecs_to_jiffies(sp->rdma_connect_timeout_msec));
+ info = kzalloc_obj(*info);
+ if (!info)
+ return NULL;
+ ret = smbdirect_socket_create_kern(net, &sc);
+ if (ret)
+ goto socket_init_failed;
+ smbdirect_socket_set_logging(sc, NULL, smbd_logging_needed, smbd_logging_vaprintf);
+ ret = smbdirect_socket_set_initial_parameters(sc, sp);
+ if (ret)
+ goto set_params_failed;
+ ret = smbdirect_socket_set_kernel_settings(sc, IB_POLL_SOFTIRQ, GFP_KERNEL);
+ if (ret)
+ goto set_settings_failed;
- if (sc->status != SMBDIRECT_SOCKET_NEGOTIATE_NEEDED) {
- log_rdma_event(ERR, "rdma_connect failed port=%d\n", port);
- goto rdma_connect_failed;
- }
+ if (dstaddr->sa_family == AF_INET6)
+ sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port;
+ else
+ sport = &((struct sockaddr_in *)dstaddr)->sin_port;
- log_rdma_event(INFO, "rdma_connect connected\n");
+ *sport = htons(port);
- rc = allocate_caches(sc);
- if (rc) {
- log_rdma_event(ERR, "cache allocation failed\n");
- goto allocate_cache_failed;
+ ret = smbdirect_connect_sync(sc, dstaddr);
+ if (ret) {
+ log_rdma_event(ERR, "connect to %pISpsfc failed: %1pe\n",
+ dstaddr, ERR_PTR(ret));
+ goto connect_failed;
}
- INIT_WORK(&sc->idle.immediate_work, send_immediate_empty_message);
- INIT_DELAYED_WORK(&sc->idle.timer_work, idle_connection_timer);
- /*
- * start with the negotiate timeout and SMBDIRECT_KEEPALIVE_PENDING
- * so that the timer will cause a disconnect.
- */
- sc->idle.keepalive = SMBDIRECT_KEEPALIVE_PENDING;
- mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
- msecs_to_jiffies(sp->negotiate_timeout_msec));
+ info->socket = sc;
+ return info;
- INIT_WORK(&sc->recv_io.posted.refill_work, smbd_post_send_credits);
+connect_failed:
+set_settings_failed:
+set_params_failed:
+ smbdirect_socket_release(sc);
+socket_init_failed:
+ kfree(info);
+ return NULL;
+}
- rc = smbd_negotiate(sc);
- if (rc) {
- log_rdma_event(ERR, "smbd_negotiate rc=%d\n", rc);
- goto negotiation_failed;
- }
+const struct smbdirect_socket_parameters *smbd_get_parameters(struct smbd_connection *conn)
+{
+ if (unlikely(!conn->socket)) {
+ static const struct smbdirect_socket_parameters zero_params;
- rc = allocate_mr_list(sc);
- if (rc) {
- log_rdma_mr(ERR, "memory registration allocation failed\n");
- goto allocate_mr_failed;
+ return &zero_params;
}
- return info;
-
-allocate_mr_failed:
- /* At this point, need to a full transport shutdown */
- server->smbd_conn = info;
- smbd_destroy(server);
- return NULL;
-
-negotiation_failed:
- disable_delayed_work_sync(&sc->idle.timer_work);
- destroy_caches(sc);
- sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
- rdma_disconnect(sc->rdma.cm_id);
- wait_event(sc->status_wait,
- sc->status == SMBDIRECT_SOCKET_DISCONNECTED);
-
-allocate_cache_failed:
-rdma_connect_failed:
- rdma_destroy_qp(sc->rdma.cm_id);
-
-create_qp_failed:
-alloc_cq_failed:
- if (sc->ib.send_cq)
- ib_free_cq(sc->ib.send_cq);
- if (sc->ib.recv_cq)
- ib_free_cq(sc->ib.recv_cq);
-
- ib_dealloc_pd(sc->ib.pd);
-
-alloc_pd_failed:
-config_failed:
- rdma_destroy_id(sc->rdma.cm_id);
-
-create_id_failed:
- destroy_workqueue(sc->workqueue);
-create_wq_failed:
- kfree(info);
- return NULL;
+ return smbdirect_socket_get_current_parameters(conn->socket);
}
struct smbd_connection *smbd_get_connection(
@@ -2354,7 +369,7 @@ try_again:
if (!ret)
return NULL;
- sp = &ret->socket.parameters;
+ sp = smbd_get_parameters(ret);
server->rdma_readwrite_threshold =
rdma_readwrite_threshold > sp->max_fragmented_send_size ?
@@ -2380,138 +395,12 @@ try_again:
*/
int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
{
- struct smbdirect_socket *sc = &info->socket;
- struct smbdirect_recv_io *response;
- struct smbdirect_data_transfer *data_transfer;
- size_t size = iov_iter_count(&msg->msg_iter);
- int to_copy, to_read, data_read, offset;
- u32 data_length, remaining_data_length, data_offset;
- int rc;
-
- if (WARN_ON_ONCE(iov_iter_rw(&msg->msg_iter) == WRITE))
- return -EINVAL; /* It's a bug in upper layer to get there */
-
-again:
- /*
- * 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
- */
- log_read(INFO, "size=%zd sc->recv_io.reassembly.data_length=%d\n", size,
- sc->recv_io.reassembly.data_length);
- if (sc->recv_io.reassembly.data_length >= size) {
- int queue_length;
- int queue_removed = 0;
- unsigned long flags;
+ struct smbdirect_socket *sc = info->socket;
- /*
- * 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 = sc->recv_io.reassembly.queue_length;
- data_read = 0;
- to_read = size;
- offset = sc->recv_io.reassembly.first_entry_offset;
- while (data_read < size) {
- response = _get_first_reassembly(sc);
- data_transfer = smbdirect_recv_io_payload(response);
- 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 (response->first_segment && size == 4) {
- unsigned int rfc1002_len =
- data_length + remaining_data_length;
- __be32 rfc1002_hdr = cpu_to_be32(rfc1002_len);
- if (copy_to_iter(&rfc1002_hdr, sizeof(rfc1002_hdr),
- &msg->msg_iter) != sizeof(rfc1002_hdr))
- return -EFAULT;
- data_read = 4;
- response->first_segment = false;
- log_read(INFO, "returning rfc1002 length %d\n",
- rfc1002_len);
- goto read_rfc1002_done;
- }
+ if (!smbdirect_connection_is_connected(sc))
+ return -ENOTCONN;
- to_copy = min_t(int, data_length - offset, to_read);
- if (copy_to_iter((char *)data_transfer + data_offset + offset,
- to_copy, &msg->msg_iter) != to_copy)
- return -EFAULT;
-
- /* 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(&response->list);
- else {
- spin_lock_irqsave(
- &sc->recv_io.reassembly.lock, flags);
- list_del(&response->list);
- spin_unlock_irqrestore(
- &sc->recv_io.reassembly.lock, flags);
- }
- queue_removed++;
- sc->statistics.dequeue_reassembly_queue++;
- put_receive_buffer(sc, response);
- offset = 0;
- log_read(INFO, "put_receive_buffer offset=0\n");
- } else
- offset += to_copy;
-
- to_read -= to_copy;
- data_read += to_copy;
-
- log_read(INFO, "_get_first_reassembly memcpy %d bytes data_transfer_length-offset=%d after that to_read=%d data_read=%d offset=%d\n",
- to_copy, data_length - offset,
- to_read, data_read, offset);
- }
-
- spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
- sc->recv_io.reassembly.data_length -= data_read;
- sc->recv_io.reassembly.queue_length -= queue_removed;
- spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
-
- sc->recv_io.reassembly.first_entry_offset = offset;
- log_read(INFO, "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n",
- data_read, sc->recv_io.reassembly.data_length,
- sc->recv_io.reassembly.first_entry_offset);
-read_rfc1002_done:
- return data_read;
- }
-
- log_read(INFO, "wait_event on more data\n");
- rc = wait_event_interruptible(
- sc->recv_io.reassembly.wait_queue,
- sc->recv_io.reassembly.data_length >= size ||
- sc->status != SMBDIRECT_SOCKET_CONNECTED);
- /* Don't return any data if interrupted */
- if (rc)
- return rc;
-
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
- log_read(ERR, "disconnected\n");
- return -ECONNABORTED;
- }
-
- goto again;
+ return smbdirect_connection_recvmsg(sc, msg, 0);
}
/*
@@ -2524,16 +413,17 @@ int smbd_send(struct TCP_Server_Info *server,
int num_rqst, struct smb_rqst *rqst_array)
{
struct smbd_connection *info = server->smbd_conn;
- struct smbdirect_socket *sc = &info->socket;
- struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smbdirect_socket *sc = info->socket;
+ const struct smbdirect_socket_parameters *sp = smbd_get_parameters(info);
struct smb_rqst *rqst;
struct iov_iter iter;
- struct smbdirect_send_batch batch;
+ struct smbdirect_send_batch_storage bstorage;
+ struct smbdirect_send_batch *batch;
unsigned int remaining_data_length, klen;
int rc, i, rqst_idx;
int error = 0;
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
+ if (!smbdirect_connection_is_connected(sc))
return -EAGAIN;
/*
@@ -2556,7 +446,7 @@ int smbd_send(struct TCP_Server_Info *server,
num_rqst, remaining_data_length);
rqst_idx = 0;
- smbd_send_batch_init(&batch, false, 0);
+ batch = smbdirect_init_send_batch_storage(&bstorage, false, 0);
do {
rqst = &rqst_array[rqst_idx];
@@ -2575,25 +465,27 @@ int smbd_send(struct TCP_Server_Info *server,
klen += rqst->rq_iov[i].iov_len;
iov_iter_kvec(&iter, ITER_SOURCE, rqst->rq_iov, rqst->rq_nvec, klen);
- rc = smbd_post_send_full_iter(sc, &batch, &iter, &remaining_data_length);
+ rc = smbd_post_send_full_iter(sc, batch, &iter, remaining_data_length);
if (rc < 0) {
error = rc;
break;
}
+ remaining_data_length -= rc;
if (iov_iter_count(&rqst->rq_iter) > 0) {
/* And then the data pages if there are any */
- rc = smbd_post_send_full_iter(sc, &batch, &rqst->rq_iter,
- &remaining_data_length);
+ rc = smbd_post_send_full_iter(sc, batch, &rqst->rq_iter,
+ remaining_data_length);
if (rc < 0) {
error = rc;
break;
}
+ remaining_data_length -= rc;
}
} while (++rqst_idx < num_rqst);
- rc = smbd_send_batch_flush(sc, &batch, true);
+ rc = smbdirect_connection_send_batch_flush(sc, batch, true);
if (unlikely(!rc && error))
rc = error;
@@ -2604,298 +496,15 @@ int smbd_send(struct TCP_Server_Info *server,
* that means all the I/Os have been out and we are good to return
*/
- wait_event(sc->send_io.pending.zero_wait_queue,
- atomic_read(&sc->send_io.pending.count) == 0 ||
- sc->status != SMBDIRECT_SOCKET_CONNECTED);
-
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED && rc == 0)
- rc = -EAGAIN;
-
- return rc;
-}
-
-static void register_mr_done(struct ib_cq *cq, struct ib_wc *wc)
-{
- struct smbdirect_mr_io *mr =
- container_of(wc->wr_cqe, struct smbdirect_mr_io, cqe);
- struct smbdirect_socket *sc = mr->socket;
-
- if (wc->status) {
- log_rdma_mr(ERR, "status=%d\n", wc->status);
- smbd_disconnect_rdma_connection(sc);
- }
-}
-
-/*
- * The work queue function that recovers MRs
- * We need to call ib_dereg_mr() and ib_alloc_mr() before this MR can be used
- * again. Both calls are slow, so finish them in a workqueue. This will not
- * block I/O path.
- * There is one workqueue that recovers MRs, there is no need to lock as the
- * I/O requests calling smbd_register_mr will never update the links in the
- * mr_list.
- */
-static void smbd_mr_recovery_work(struct work_struct *work)
-{
- struct smbdirect_socket *sc =
- container_of(work, struct smbdirect_socket, mr_io.recovery_work);
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- struct smbdirect_mr_io *smbdirect_mr;
- int rc;
-
- list_for_each_entry(smbdirect_mr, &sc->mr_io.all.list, list) {
- if (smbdirect_mr->state == SMBDIRECT_MR_ERROR) {
-
- /* recover this MR entry */
- rc = ib_dereg_mr(smbdirect_mr->mr);
- if (rc) {
- log_rdma_mr(ERR,
- "ib_dereg_mr failed rc=%x\n",
- rc);
- smbd_disconnect_rdma_connection(sc);
- continue;
- }
-
- smbdirect_mr->mr = ib_alloc_mr(
- sc->ib.pd, sc->mr_io.type,
- sp->max_frmr_depth);
- if (IS_ERR(smbdirect_mr->mr)) {
- log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n",
- sc->mr_io.type,
- sp->max_frmr_depth);
- smbd_disconnect_rdma_connection(sc);
- continue;
- }
- } else
- /* This MR is being used, don't recover it */
- continue;
-
- smbdirect_mr->state = SMBDIRECT_MR_READY;
-
- /* smbdirect_mr->state is updated by this function
- * and is read and updated by I/O issuing CPUs trying
- * to get a MR, the call to atomic_inc_return
- * implicates a memory barrier and guarantees this
- * value is updated before waking up any calls to
- * get_mr() from the I/O issuing CPUs
- */
- if (atomic_inc_return(&sc->mr_io.ready.count) == 1)
- wake_up(&sc->mr_io.ready.wait_queue);
- }
-}
-
-static void smbd_mr_disable_locked(struct smbdirect_mr_io *mr)
-{
- struct smbdirect_socket *sc = mr->socket;
-
- lockdep_assert_held(&mr->mutex);
-
- if (mr->state == SMBDIRECT_MR_DISABLED)
- return;
+ error = rc;
+ rc = smbdirect_connection_send_wait_zero_pending(sc);
+ if (unlikely(rc && !error))
+ error = -EAGAIN;
- if (mr->mr)
- ib_dereg_mr(mr->mr);
- if (mr->sgt.nents)
- ib_dma_unmap_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir);
- kfree(mr->sgt.sgl);
-
- mr->mr = NULL;
- mr->sgt.sgl = NULL;
- mr->sgt.nents = 0;
-
- mr->state = SMBDIRECT_MR_DISABLED;
-}
-
-static void smbd_mr_free_locked(struct kref *kref)
-{
- struct smbdirect_mr_io *mr =
- container_of(kref, struct smbdirect_mr_io, kref);
-
- lockdep_assert_held(&mr->mutex);
-
- /*
- * smbd_mr_disable_locked() should already be called!
- */
- if (WARN_ON_ONCE(mr->state != SMBDIRECT_MR_DISABLED))
- smbd_mr_disable_locked(mr);
-
- mutex_unlock(&mr->mutex);
- mutex_destroy(&mr->mutex);
- kfree(mr);
-}
-
-static void destroy_mr_list(struct smbdirect_socket *sc)
-{
- struct smbdirect_mr_io *mr, *tmp;
- LIST_HEAD(all_list);
- unsigned long flags;
-
- disable_work_sync(&sc->mr_io.recovery_work);
-
- spin_lock_irqsave(&sc->mr_io.all.lock, flags);
- list_splice_tail_init(&sc->mr_io.all.list, &all_list);
- spin_unlock_irqrestore(&sc->mr_io.all.lock, flags);
-
- list_for_each_entry_safe(mr, tmp, &all_list, list) {
- mutex_lock(&mr->mutex);
-
- smbd_mr_disable_locked(mr);
- list_del(&mr->list);
- mr->socket = NULL;
-
- /*
- * No kref_put_mutex() as it's already locked.
- *
- * If smbd_mr_free_locked() is called
- * and the mutex is unlocked and mr is gone,
- * in that case kref_put() returned 1.
- *
- * If kref_put() returned 0 we know that
- * smbd_mr_free_locked() didn't
- * run. Not by us nor by anyone else, as we
- * still hold the mutex, so we need to unlock.
- *
- * If the mr is still registered it will
- * be dangling (detached from the connection
- * waiting for smbd_deregister_mr() to be
- * called in order to free the memory.
- */
- if (!kref_put(&mr->kref, smbd_mr_free_locked))
- mutex_unlock(&mr->mutex);
- }
-}
-
-/*
- * Allocate MRs used for RDMA read/write
- * The number of MRs will not exceed hardware capability in responder_resources
- * All MRs are kept in mr_list. The MR can be recovered after it's used
- * Recovery is done in smbd_mr_recovery_work. The content of list entry changes
- * as MRs are used and recovered for I/O, but the list links will not change
- */
-static int allocate_mr_list(struct smbdirect_socket *sc)
-{
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- struct smbdirect_mr_io *mr;
- int ret;
- u32 i;
-
- if (sp->responder_resources == 0) {
- log_rdma_mr(ERR, "responder_resources negotiated as 0\n");
- return -EINVAL;
- }
-
- /* Allocate more MRs (2x) than hardware responder_resources */
- for (i = 0; i < sp->responder_resources * 2; i++) {
- mr = kzalloc_obj(*mr);
- if (!mr) {
- ret = -ENOMEM;
- goto kzalloc_mr_failed;
- }
-
- kref_init(&mr->kref);
- mutex_init(&mr->mutex);
-
- mr->mr = ib_alloc_mr(sc->ib.pd,
- sc->mr_io.type,
- sp->max_frmr_depth);
- if (IS_ERR(mr->mr)) {
- ret = PTR_ERR(mr->mr);
- log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n",
- sc->mr_io.type, sp->max_frmr_depth);
- goto ib_alloc_mr_failed;
- }
-
- mr->sgt.sgl = kzalloc_objs(struct scatterlist,
- sp->max_frmr_depth);
- if (!mr->sgt.sgl) {
- ret = -ENOMEM;
- log_rdma_mr(ERR, "failed to allocate sgl\n");
- goto kcalloc_sgl_failed;
- }
- mr->state = SMBDIRECT_MR_READY;
- mr->socket = sc;
-
- list_add_tail(&mr->list, &sc->mr_io.all.list);
- atomic_inc(&sc->mr_io.ready.count);
- }
-
- INIT_WORK(&sc->mr_io.recovery_work, smbd_mr_recovery_work);
+ if (unlikely(error))
+ return error;
return 0;
-
-kcalloc_sgl_failed:
- ib_dereg_mr(mr->mr);
-ib_alloc_mr_failed:
- mutex_destroy(&mr->mutex);
- kfree(mr);
-kzalloc_mr_failed:
- destroy_mr_list(sc);
- return ret;
-}
-
-/*
- * Get a MR from mr_list. This function waits until there is at least one
- * MR available in the list. It may access the list while the
- * smbd_mr_recovery_work is recovering the MR list. This doesn't need a lock
- * as they never modify the same places. However, there may be several CPUs
- * issuing I/O trying to get MR at the same time, mr_list_lock is used to
- * protect this situation.
- */
-static struct smbdirect_mr_io *get_mr(struct smbdirect_socket *sc)
-{
- struct smbdirect_mr_io *ret;
- unsigned long flags;
- int rc;
-again:
- rc = wait_event_interruptible(sc->mr_io.ready.wait_queue,
- atomic_read(&sc->mr_io.ready.count) ||
- sc->status != SMBDIRECT_SOCKET_CONNECTED);
- if (rc) {
- log_rdma_mr(ERR, "wait_event_interruptible rc=%x\n", rc);
- return NULL;
- }
-
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
- log_rdma_mr(ERR, "sc->status=%x\n", sc->status);
- return NULL;
- }
-
- spin_lock_irqsave(&sc->mr_io.all.lock, flags);
- list_for_each_entry(ret, &sc->mr_io.all.list, list) {
- if (ret->state == SMBDIRECT_MR_READY) {
- ret->state = SMBDIRECT_MR_REGISTERED;
- kref_get(&ret->kref);
- spin_unlock_irqrestore(&sc->mr_io.all.lock, flags);
- atomic_dec(&sc->mr_io.ready.count);
- atomic_inc(&sc->mr_io.used.count);
- return ret;
- }
- }
-
- spin_unlock_irqrestore(&sc->mr_io.all.lock, flags);
- /*
- * It is possible that we could fail to get MR because other processes may
- * try to acquire a MR at the same time. If this is the case, retry it.
- */
- goto again;
-}
-
-/*
- * Transcribe the pages from an iterator into an MR scatterlist.
- */
-static int smbd_iter_to_mr(struct iov_iter *iter,
- struct sg_table *sgt,
- unsigned int max_sg)
-{
- int ret;
-
- memset(sgt->sgl, 0, max_sg * sizeof(struct scatterlist));
-
- ret = extract_iter_to_sg(iter, iov_iter_count(iter), sgt, max_sg, 0);
- WARN_ON(ret < 0);
- if (sgt->nents > 0)
- sg_mark_end(&sgt->sgl[sgt->nents - 1]);
- return ret;
}
/*
@@ -2909,132 +518,18 @@ struct smbdirect_mr_io *smbd_register_mr(struct smbd_connection *info,
struct iov_iter *iter,
bool writing, bool need_invalidate)
{
- struct smbdirect_socket *sc = &info->socket;
- struct smbdirect_socket_parameters *sp = &sc->parameters;
- struct smbdirect_mr_io *mr;
- int rc, num_pages;
- struct ib_reg_wr *reg_wr;
-
- num_pages = iov_iter_npages(iter, sp->max_frmr_depth + 1);
- if (num_pages > sp->max_frmr_depth) {
- log_rdma_mr(ERR, "num_pages=%d max_frmr_depth=%d\n",
- num_pages, sp->max_frmr_depth);
- WARN_ON_ONCE(1);
- return NULL;
- }
+ struct smbdirect_socket *sc = info->socket;
- mr = get_mr(sc);
- if (!mr) {
- log_rdma_mr(ERR, "get_mr returning NULL\n");
+ if (!smbdirect_connection_is_connected(sc))
return NULL;
- }
-
- mutex_lock(&mr->mutex);
-
- mr->dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- mr->need_invalidate = need_invalidate;
- mr->sgt.nents = 0;
- mr->sgt.orig_nents = 0;
-
- log_rdma_mr(INFO, "num_pages=0x%x count=0x%zx depth=%u\n",
- num_pages, iov_iter_count(iter), sp->max_frmr_depth);
- smbd_iter_to_mr(iter, &mr->sgt, sp->max_frmr_depth);
- rc = ib_dma_map_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir);
- if (!rc) {
- log_rdma_mr(ERR, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n",
- num_pages, mr->dir, rc);
- goto dma_map_error;
- }
-
- rc = ib_map_mr_sg(mr->mr, mr->sgt.sgl, mr->sgt.nents, NULL, PAGE_SIZE);
- if (rc != mr->sgt.nents) {
- log_rdma_mr(ERR,
- "ib_map_mr_sg failed rc = %d nents = %x\n",
- rc, mr->sgt.nents);
- goto map_mr_error;
- }
-
- ib_update_fast_reg_key(mr->mr, ib_inc_rkey(mr->mr->rkey));
- reg_wr = &mr->wr;
- reg_wr->wr.opcode = IB_WR_REG_MR;
- mr->cqe.done = register_mr_done;
- reg_wr->wr.wr_cqe = &mr->cqe;
- reg_wr->wr.num_sge = 0;
- reg_wr->wr.send_flags = IB_SEND_SIGNALED;
- reg_wr->mr = mr->mr;
- reg_wr->key = mr->mr->rkey;
- reg_wr->access = writing ?
- IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE :
- IB_ACCESS_REMOTE_READ;
-
- /*
- * There is no need for waiting for complemtion on ib_post_send
- * on IB_WR_REG_MR. Hardware enforces a barrier and order of execution
- * on the next ib_post_send when we actually send I/O to remote peer
- */
- rc = ib_post_send(sc->ib.qp, &reg_wr->wr, NULL);
- if (!rc) {
- /*
- * get_mr() gave us a reference
- * via kref_get(&mr->kref), we keep that and let
- * the caller use smbd_deregister_mr()
- * to remove it again.
- */
- mutex_unlock(&mr->mutex);
- return mr;
- }
-
- log_rdma_mr(ERR, "ib_post_send failed rc=%x reg_wr->key=%x\n",
- rc, reg_wr->key);
-
- /* If all failed, attempt to recover this MR by setting it SMBDIRECT_MR_ERROR*/
-map_mr_error:
- ib_dma_unmap_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir);
-
-dma_map_error:
- mr->sgt.nents = 0;
- mr->state = SMBDIRECT_MR_ERROR;
- if (atomic_dec_and_test(&sc->mr_io.used.count))
- wake_up(&sc->mr_io.cleanup.wait_queue);
-
- smbd_disconnect_rdma_connection(sc);
-
- /*
- * get_mr() gave us a reference
- * via kref_get(&mr->kref), we need to remove it again
- * on error.
- *
- * No kref_put_mutex() as it's already locked.
- *
- * If smbd_mr_free_locked() is called
- * and the mutex is unlocked and mr is gone,
- * in that case kref_put() returned 1.
- *
- * If kref_put() returned 0 we know that
- * smbd_mr_free_locked() didn't
- * run. Not by us nor by anyone else, as we
- * still hold the mutex, so we need to unlock.
- */
- if (!kref_put(&mr->kref, smbd_mr_free_locked))
- mutex_unlock(&mr->mutex);
-
- return NULL;
+ return smbdirect_connection_register_mr_io(sc, iter, writing, need_invalidate);
}
-static void local_inv_done(struct ib_cq *cq, struct ib_wc *wc)
+void smbd_mr_fill_buffer_descriptor(struct smbdirect_mr_io *mr,
+ struct smbdirect_buffer_descriptor_v1 *v1)
{
- struct smbdirect_mr_io *smbdirect_mr;
- struct ib_cqe *cqe;
-
- cqe = wc->wr_cqe;
- smbdirect_mr = container_of(cqe, struct smbdirect_mr_io, cqe);
- smbdirect_mr->state = SMBDIRECT_MR_INVALIDATED;
- if (wc->status != IB_WC_SUCCESS) {
- log_rdma_mr(ERR, "invalidate failed status=%x\n", wc->status);
- smbdirect_mr->state = SMBDIRECT_MR_ERROR;
- }
- complete(&smbdirect_mr->invalidate_done);
+ smbdirect_mr_io_fill_buffer_descriptor(mr, v1);
}
/*
@@ -3045,300 +540,22 @@ static void local_inv_done(struct ib_cq *cq, struct ib_wc *wc)
*/
void smbd_deregister_mr(struct smbdirect_mr_io *mr)
{
- struct smbdirect_socket *sc = mr->socket;
-
- mutex_lock(&mr->mutex);
- if (mr->state == SMBDIRECT_MR_DISABLED)
- goto put_kref;
-
- if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
- smbd_mr_disable_locked(mr);
- goto put_kref;
- }
-
- if (mr->need_invalidate) {
- struct ib_send_wr *wr = &mr->inv_wr;
- int rc;
-
- /* Need to finish local invalidation before returning */
- wr->opcode = IB_WR_LOCAL_INV;
- mr->cqe.done = local_inv_done;
- wr->wr_cqe = &mr->cqe;
- wr->num_sge = 0;
- wr->ex.invalidate_rkey = mr->mr->rkey;
- wr->send_flags = IB_SEND_SIGNALED;
-
- init_completion(&mr->invalidate_done);
- rc = ib_post_send(sc->ib.qp, wr, NULL);
- if (rc) {
- log_rdma_mr(ERR, "ib_post_send failed rc=%x\n", rc);
- smbd_mr_disable_locked(mr);
- smbd_disconnect_rdma_connection(sc);
- goto done;
- }
- wait_for_completion(&mr->invalidate_done);
- mr->need_invalidate = false;
- } else
- /*
- * For remote invalidation, just set it to SMBDIRECT_MR_INVALIDATED
- * and defer to mr_recovery_work to recover the MR for next use
- */
- mr->state = SMBDIRECT_MR_INVALIDATED;
-
- if (mr->sgt.nents) {
- ib_dma_unmap_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir);
- mr->sgt.nents = 0;
- }
-
- if (mr->state == SMBDIRECT_MR_INVALIDATED) {
- mr->state = SMBDIRECT_MR_READY;
- if (atomic_inc_return(&sc->mr_io.ready.count) == 1)
- wake_up(&sc->mr_io.ready.wait_queue);
- } else
- /*
- * Schedule the work to do MR recovery for future I/Os MR
- * recovery is slow and don't want it to block current I/O
- */
- queue_work(sc->workqueue, &sc->mr_io.recovery_work);
-
-done:
- if (atomic_dec_and_test(&sc->mr_io.used.count))
- wake_up(&sc->mr_io.cleanup.wait_queue);
-
-put_kref:
- /*
- * No kref_put_mutex() as it's already locked.
- *
- * If smbd_mr_free_locked() is called
- * and the mutex is unlocked and mr is gone,
- * in that case kref_put() returned 1.
- *
- * If kref_put() returned 0 we know that
- * smbd_mr_free_locked() didn't
- * run. Not by us nor by anyone else, as we
- * still hold the mutex, so we need to unlock
- * and keep the mr in SMBDIRECT_MR_READY or
- * SMBDIRECT_MR_ERROR state.
- */
- if (!kref_put(&mr->kref, smbd_mr_free_locked))
- mutex_unlock(&mr->mutex);
-}
-
-static bool smb_set_sge(struct smb_extract_to_rdma *rdma,
- struct page *lowest_page, size_t off, size_t len)
-{
- struct ib_sge *sge = &rdma->sge[rdma->nr_sge];
- u64 addr;
-
- addr = ib_dma_map_page(rdma->device, lowest_page,
- off, len, rdma->direction);
- if (ib_dma_mapping_error(rdma->device, addr))
- return false;
-
- sge->addr = addr;
- sge->length = len;
- sge->lkey = rdma->local_dma_lkey;
- rdma->nr_sge++;
- return true;
-}
-
-/*
- * Extract page fragments from a BVEC-class iterator and add them to an RDMA
- * element list. The pages are not pinned.
- */
-static ssize_t smb_extract_bvec_to_rdma(struct iov_iter *iter,
- struct smb_extract_to_rdma *rdma,
- ssize_t maxsize)
-{
- const struct bio_vec *bv = iter->bvec;
- unsigned long start = iter->iov_offset;
- unsigned int i;
- ssize_t ret = 0;
-
- for (i = 0; i < iter->nr_segs; i++) {
- size_t off, len;
-
- len = bv[i].bv_len;
- if (start >= len) {
- start -= len;
- continue;
- }
-
- len = min_t(size_t, maxsize, len - start);
- off = bv[i].bv_offset + start;
-
- if (!smb_set_sge(rdma, bv[i].bv_page, off, len))
- return -EIO;
-
- ret += len;
- maxsize -= len;
- if (rdma->nr_sge >= rdma->max_sge || maxsize <= 0)
- break;
- start = 0;
- }
-
- if (ret > 0)
- iov_iter_advance(iter, ret);
- return ret;
+ smbdirect_connection_deregister_mr_io(mr);
}
-/*
- * Extract fragments from a KVEC-class iterator and add them to an RDMA list.
- * This can deal with vmalloc'd buffers as well as kmalloc'd or static buffers.
- * The pages are not pinned.
- */
-static ssize_t smb_extract_kvec_to_rdma(struct iov_iter *iter,
- struct smb_extract_to_rdma *rdma,
- ssize_t maxsize)
+void smbd_debug_proc_show(struct TCP_Server_Info *server, struct seq_file *m)
{
- const struct kvec *kv = iter->kvec;
- unsigned long start = iter->iov_offset;
- unsigned int i;
- ssize_t ret = 0;
-
- for (i = 0; i < iter->nr_segs; i++) {
- struct page *page;
- unsigned long kaddr;
- size_t off, len, seg;
-
- len = kv[i].iov_len;
- if (start >= len) {
- start -= len;
- continue;
- }
-
- kaddr = (unsigned long)kv[i].iov_base + start;
- off = kaddr & ~PAGE_MASK;
- len = min_t(size_t, maxsize, len - start);
- kaddr &= PAGE_MASK;
-
- maxsize -= len;
- do {
- seg = min_t(size_t, len, PAGE_SIZE - off);
-
- if (is_vmalloc_or_module_addr((void *)kaddr))
- page = vmalloc_to_page((void *)kaddr);
- else
- page = virt_to_page((void *)kaddr);
-
- if (!smb_set_sge(rdma, page, off, seg))
- return -EIO;
-
- ret += seg;
- len -= seg;
- kaddr += PAGE_SIZE;
- off = 0;
- } while (len > 0 && rdma->nr_sge < rdma->max_sge);
-
- if (rdma->nr_sge >= rdma->max_sge || maxsize <= 0)
- break;
- start = 0;
- }
-
- if (ret > 0)
- iov_iter_advance(iter, ret);
- return ret;
-}
+ if (!server->rdma)
+ return;
-/*
- * Extract folio fragments from a FOLIOQ-class iterator and add them to an RDMA
- * list. The folios are not pinned.
- */
-static ssize_t smb_extract_folioq_to_rdma(struct iov_iter *iter,
- struct smb_extract_to_rdma *rdma,
- ssize_t maxsize)
-{
- const struct folio_queue *folioq = iter->folioq;
- unsigned int slot = iter->folioq_slot;
- ssize_t ret = 0;
- size_t offset = iter->iov_offset;
-
- BUG_ON(!folioq);
-
- if (slot >= folioq_nr_slots(folioq)) {
- folioq = folioq->next;
- if (WARN_ON_ONCE(!folioq))
- return -EIO;
- slot = 0;
+ if (!server->smbd_conn) {
+ seq_puts(m, "\nSMBDirect transport not available");
+ return;
}
- do {
- struct folio *folio = folioq_folio(folioq, slot);
- size_t fsize = folioq_folio_size(folioq, slot);
-
- if (offset < fsize) {
- size_t part = umin(maxsize, fsize - offset);
-
- if (!smb_set_sge(rdma, folio_page(folio, 0), offset, part))
- return -EIO;
-
- offset += part;
- ret += part;
- maxsize -= part;
- }
-
- if (offset >= fsize) {
- offset = 0;
- slot++;
- if (slot >= folioq_nr_slots(folioq)) {
- if (!folioq->next) {
- WARN_ON_ONCE(ret < iter->count);
- break;
- }
- folioq = folioq->next;
- slot = 0;
- }
- }
- } while (rdma->nr_sge < rdma->max_sge && maxsize > 0);
-
- iter->folioq = folioq;
- iter->folioq_slot = slot;
- iter->iov_offset = offset;
- iter->count -= ret;
- return ret;
+ smbdirect_connection_legacy_debug_proc_show(server->smbd_conn->socket,
+ server->rdma_readwrite_threshold,
+ m);
}
-/*
- * Extract page fragments from up to the given amount of the source iterator
- * and build up an RDMA list that refers to all of those bits. The RDMA list
- * is appended to, up to the maximum number of elements set in the parameter
- * block.
- *
- * The extracted page fragments are not pinned or ref'd in any way; if an
- * IOVEC/UBUF-type iterator is to be used, it should be converted to a
- * BVEC-type iterator and the pages pinned, ref'd or otherwise held in some
- * way.
- */
-static ssize_t smb_extract_iter_to_rdma(struct iov_iter *iter, size_t len,
- struct smb_extract_to_rdma *rdma)
-{
- ssize_t ret;
- int before = rdma->nr_sge;
-
- switch (iov_iter_type(iter)) {
- case ITER_BVEC:
- ret = smb_extract_bvec_to_rdma(iter, rdma, len);
- break;
- case ITER_KVEC:
- ret = smb_extract_kvec_to_rdma(iter, rdma, len);
- break;
- case ITER_FOLIOQ:
- ret = smb_extract_folioq_to_rdma(iter, rdma, len);
- break;
- default:
- WARN_ON_ONCE(1);
- return -EIO;
- }
-
- if (ret < 0) {
- while (rdma->nr_sge > before) {
- struct ib_sge *sge = &rdma->sge[rdma->nr_sge--];
-
- ib_dma_unmap_single(rdma->device, sge->addr, sge->length,
- rdma->direction);
- sge->addr = 0;
- }
- }
-
- return ret;
-}
+MODULE_IMPORT_NS("SMBDIRECT");
diff --git a/fs/smb/client/smbdirect.h b/fs/smb/client/smbdirect.h
index 577d37dbeb8a..be205ec02077 100644
--- a/fs/smb/client/smbdirect.h
+++ b/fs/smb/client/smbdirect.h
@@ -11,12 +11,8 @@
#define cifs_rdma_enabled(server) ((server)->rdma)
#include "cifsglob.h"
-#include <rdma/ib_verbs.h>
-#include <rdma/rdma_cm.h>
-#include <linux/mempool.h>
-#include "../common/smbdirect/smbdirect.h"
-#include "../common/smbdirect/smbdirect_socket.h"
+#include <linux/smbdirect.h>
extern int rdma_readwrite_threshold;
extern int smbd_max_frmr_depth;
@@ -27,17 +23,8 @@ extern int smbd_max_send_size;
extern int smbd_send_credit_target;
extern int smbd_receive_credit_max;
-/*
- * The context for the SMBDirect transport
- * Everything related to the transport is here. It has several logical parts
- * 1. RDMA related structures
- * 2. SMBDirect connection parameters
- * 3. Memory registrations
- * 4. Receive and reassembly queues for data receive path
- * 5. mempools for allocating packets
- */
struct smbd_connection {
- struct smbdirect_socket socket;
+ struct smbdirect_socket *socket;
};
/* Create a SMBDirect session */
@@ -60,8 +47,12 @@ int smbd_send(struct TCP_Server_Info *server,
struct smbdirect_mr_io *smbd_register_mr(
struct smbd_connection *info, struct iov_iter *iter,
bool writing, bool need_invalidate);
+void smbd_mr_fill_buffer_descriptor(struct smbdirect_mr_io *mr,
+ struct smbdirect_buffer_descriptor_v1 *v1);
void smbd_deregister_mr(struct smbdirect_mr_io *mr);
+void smbd_debug_proc_show(struct TCP_Server_Info *server, struct seq_file *m);
+
#else
#define cifs_rdma_enabled(server) 0
struct smbd_connection {};
diff --git a/fs/smb/client/smberr.h b/fs/smb/client/smberr.h
index 6fb63f9e9a95..5c3415bf18d7 100644
--- a/fs/smb/client/smberr.h
+++ b/fs/smb/client/smberr.h
@@ -9,163 +9,290 @@
*
*/
-#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. */
+struct smb_to_posix_error {
+ __u16 smb_err;
+ int posix_code;
+};
+
+/* The request was successful. */
+#define SUCCESS 0x00
+/* Error is from the core DOS operating system set */
+#define ERRDOS 0x01
+/* Error is generated by the file server daemon */
+#define ERRSRV 0x02
+/* Error is a hardware error. */
+#define ERRHRD 0x03
+/* Command was not in the "SMB" format. */
+#define ERRCMD 0xFF
/* 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.*/
+/*
+ * The following error codes may be generated with the ERRDOS error class.
+ * The comment at the end of each definition indicates the POSIX error
+ * code; it is used to generate the `mapping_table_ERRDOS` array.
+ */
-#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 */
+/*
+ * 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 ERRbadfunc 1 // -EINVAL
+/*
+ * File not found. The last component
+ * of a file's pathname could not be
+ * found.
+ */
+#define ERRbadfile 2 // -ENOENT
+/*
+ * Directory invalid. A directory
+ * component in a pathname could not be
+ * found.
+ */
+#define ERRbadpath 3 // -ENOTDIR
+/*
+ * Too many open files. The server has
+ * no file handles available.
+ */
+#define ERRnofids 4 // -EMFILE
+/*
+ * 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 ERRnoaccess 5 // -EACCES
+/*
+ * Invalid file handle. The file handle
+ * specified was not recognized by the
+ * server.
+ */
+#define ERRbadfid 6 // -EBADF
+/* Memory control blocks destroyed. */
+#define ERRbadmcb 7 // -EIO
+/*
+ * Insufficient server memory to
+ * perform the requested function.
+ */
+#define ERRnomem 8 // -EREMOTEIO
+/* Invalid memory block address. */
+#define ERRbadmem 9 // -EFAULT
+/* Invalid environment. */
+#define ERRbadenv 10 // -EFAULT
+/* Invalid format. */
+#define ERRbadformat 11 // -EINVAL
+/* Invalid open mode. */
+#define ERRbadaccess 12 // -EACCES
+/*
+ * Invalid data (generated only by
+ * IOCTL calls within the server).
+ */
+#define ERRbaddata 13 // -EIO
+/* Invalid drive specified. */
+#define ERRbaddrive 15 // -ENXIO
+/*
+ * A Delete Directory request attempted
+ * to remove the server's current
+ * directory.
+ */
+#define ERRremcd 16 // -EACCES
+/*
+ * Not same device (e.g., a cross
+ * volume rename was attempted
+ */
+#define ERRdiffdevice 17 // -EXDEV
+/*
+ * A File Search command can find no
+ * more files matching the specified
+ * criteria.
+ */
+#define ERRnofiles 18 // -ENOENT
+/* media is write protected */
+#define ERRwriteprot 19 // -EROFS
#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 ERRunknownlevel 124
-#define ERRdirnotempty 145
-#define ERRnotlocked 158
-#define ERRcancelviolation 173
-#define ERRalreadyexists 183
+/*
+ * The sharing mode specified for an
+ * Open conflicts with existing FIDs on
+ * the file.
+ */
+#define ERRbadshare 32 // -EBUSY
+/*
+ * 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 ERRlock 33 // -EACCES
+#define ERRunsup 50 // -EINVAL
+#define ERRnosuchshare 67 // -ENXIO
+/*
+ * The file named in the request
+ * already exists.
+ */
+#define ERRfilexists 80 // -EEXIST
+#define ERRinvparm 87 // -EINVAL
+#define ERRdiskfull 112 // -ENOSPC
+#define ERRinvname 123 // -ENOENT
+#define ERRunknownlevel 124 // -EOPNOTSUPP
+#define ERRdirnotempty 145 // -ENOTEMPTY
+#define ERRnotlocked 158 // -ENOLCK
+#define ERRcancelviolation 173 // -ENOLCK
+#define ERRalreadyexists 183 // -EEXIST
#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. */
+#define ERRmoredata 234 // -EOVERFLOW
+#define ERReasnotsupported 282 // -EOPNOTSUPP
+/*
+ * The operation would cause a quota
+ * limit to be exceeded.
+ */
+#define ErrQuota 0x200 // -EDQUOT
+/*
+ * A link operation was performed on a
+ * pathname that was not a link.
+ */
+#define ErrNotALink 0x201 // -ENOLINK
+#define ERRnetlogonNotStarted 2455 // -ENOPROTOOPT
/* 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
+#define ERRsymlink 0xFFFD // -EOPNOTSUPP
+#define ErrTooManyLinks 0xFFFE // -EMLINK
-/* Following error codes may be generated with the ERRSRV error class.*/
+/*
+ * The following error codes may be generated with the ERRSRV error class.
+ * The comment at the end of each definition indicates the POSIX error
+ * code; it is used to generate the `mapping_table_ERRSRV` array.
+ */
-#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
+/*
+ * 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 ERRerror 1 // -EIO
+/*
+ * Bad password - name/password pair in
+ * a TreeConnect or Session Setup are
+ * invalid.
+ */
+#define ERRbadpw 2 // -EACCES
+/*
+ * used for indicating DFS referral
+ * needed
+ */
+#define ERRbadtype 3 // -EREMOTE
+/*
+ * The client does not have the
+ * necessary access rights within the
+ * specified context for requested
+ * function.
+ */
+#define ERRaccess 4 // -EACCES
+/*
+ * The Tid specified in a command was
+ * invalid.
+ */
+#define ERRinvtid 5 // -ENXIO
+/*
+ * Invalid network name in tree
+ * connect.
+ */
+#define ERRinvnetname 6 // -ENXIO
+/*
+ * Invalid device - printer request
+ * made to non-printer connection or
+ * non-printer request made to printer
+ * connection.
+ */
+#define ERRinvdevice 7 // -ENXIO
+/*
+ * Print queue full (files) -- returned
+ * by open print file.
+ */
+#define ERRqfull 49 // -ENOSPC
+/* Print queue full -- no space. */
+#define ERRqtoobig 50 // -ENOSPC
+/* EOF on print queue dump */
+#define ERRqeof 51 // -EIO
+/* Invalid print file FID. */
+#define ERRinvpfid 52 // -EBADF
+/*
+ * The server did not recognize the
+ * command received.
+ */
+#define ERRsmbcmd 64 // -EBADRQC
+/*
+ * The server encountered an internal
+ * error, e.g., system file
+ * unavailable.
+ */
+#define ERRsrverror 65 // -EIO
+/* (obsolete) */
+#define ERRbadBID 66 // -EIO
+/*
+ * The Fid and pathname parameters
+ * contained an invalid combination of
+ * values.
+ */
+#define ERRfilespecs 67 // -EINVAL
+/* (obsolete) */
+#define ERRbadLink 68 // -EIO
+/*
+ * The access permissions specified for
+ * a file or directory are not a valid
+ * combination.
+ */
+#define ERRbadpermits 69 // -EINVAL
+#define ERRbadPID 70 // -ESRCH
+/* attribute (mode) is invalid */
+#define ERRsetattrmode 71 // -EINVAL
+/* Server is paused */
+#define ERRpaused 81 // -EHOSTDOWN
+/* reserved - messaging off */
+#define ERRmsgoff 82 // -EHOSTDOWN
+/* reserved - no room for message */
+#define ERRnoroom 83 // -ENOSPC
+/* reserved - too many remote names */
+#define ERRrmuns 87 // -EUSERS
+/* operation timed out */
+#define ERRtimeout 88 // -ETIME
+/* No resources available for request */
+#define ERRnoresource 89 // -EREMOTEIO
+/* Too many UIDs active on this session */
+#define ERRtoomanyuids 90 // -EUSERS
+/* The UID is not known as a valid user */
+#define ERRbaduid 91 // -EACCES
+/* temporarily unable to use raw */
+#define ERRusempx 250 // -EIO
+/*
+ * temporarily unable to use either raw
+ * or mpx
+ */
+#define ERRusestd 251 // -EIO
+#define ERR_NOTIFY_ENUM_DIR 1024 // -ENOBUFS
+/* user account does not exist */
+#define ERRnoSuchUser 2238 // -EACCES
+#define ERRaccountexpired 2239 // -EKEYEXPIRED
+/* can not logon from this client */
+#define ERRbadclient 2240 // -EACCES
+/* logon hours do not allow this */
+#define ERRbadLogonTime 2241 // -EACCES
+#define ERRpasswordExpired 2242 // -EKEYEXPIRED
+#define ERRnosupport 0xFFFF // -EINVAL
diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
index 9228f95cae2b..bb8d0197cb54 100644
--- a/fs/smb/client/trace.h
+++ b/fs/smb/client/trace.h
@@ -27,6 +27,7 @@
EM(smb_eio_trace_copychunk_overcopy_c, "copychunk_overcopy_c") \
EM(smb_eio_trace_create_rsp_too_small, "create_rsp_too_small") \
EM(smb_eio_trace_dfsref_no_rsp, "dfsref_no_rsp") \
+ EM(smb_eio_trace_ea_next_offset, "ea_next_offset") \
EM(smb_eio_trace_ea_overrun, "ea_overrun") \
EM(smb_eio_trace_extract_will_pin, "extract_will_pin") \
EM(smb_eio_trace_forced_shutdown, "forced_shutdown") \
@@ -79,6 +80,7 @@
EM(smb_eio_trace_qreparse_setup_count, "qreparse_setup_count") \
EM(smb_eio_trace_qreparse_sizes_wrong, "qreparse_sizes_wrong") \
EM(smb_eio_trace_qsym_bcc_too_small, "qsym_bcc_too_small") \
+ EM(smb_eio_trace_read_bad_offset, "read_bad_offset") \
EM(smb_eio_trace_read_mid_state_unknown, "read_mid_state_unknown") \
EM(smb_eio_trace_read_overlarge, "read_overlarge") \
EM(smb_eio_trace_read_rsp_malformed, "read_rsp_malformed") \
@@ -133,6 +135,7 @@
EM(smb_eio_trace_sym_slash, "sym_slash") \
EM(smb_eio_trace_sym_target_len, "sym_target_len") \
EM(smb_eio_trace_symlink_file_size, "symlink_file_size") \
+ EM(smb_eio_trace_tcon_bcc_too_small, "tcon_bcc_too_small") \
EM(smb_eio_trace_tdis_in_reconnect, "tdis_in_reconnect") \
EM(smb_eio_trace_tx_chained_async, "tx_chained_async") \
EM(smb_eio_trace_tx_compress_failed, "tx_compress_failed") \
@@ -176,10 +179,12 @@
EM(netfs_trace_tcon_ref_get_cached_laundromat, "GET Ch-Lau") \
EM(netfs_trace_tcon_ref_get_cached_lease_break, "GET Ch-Lea") \
EM(netfs_trace_tcon_ref_get_cancelled_close, "GET Cn-Cls") \
+ EM(netfs_trace_tcon_ref_get_close_defer_files, "GET Cl-Def") \
EM(netfs_trace_tcon_ref_get_dfs_refer, "GET DfsRef") \
EM(netfs_trace_tcon_ref_get_find, "GET Find ") \
EM(netfs_trace_tcon_ref_get_find_sess_tcon, "GET FndSes") \
EM(netfs_trace_tcon_ref_get_reconnect_server, "GET Reconn") \
+ EM(netfs_trace_tcon_ref_get_swn_notify, "GET SwnNot") \
EM(netfs_trace_tcon_ref_new, "NEW ") \
EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \
EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \
@@ -187,9 +192,11 @@
EM(netfs_trace_tcon_ref_put_cancelled_close, "PUT Cn-Cls") \
EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \
EM(netfs_trace_tcon_ref_put_cancelled_mid, "PUT Cn-Mid") \
+ EM(netfs_trace_tcon_ref_put_close_defer_files, "PUT Cl-Def") \
EM(netfs_trace_tcon_ref_put_mnt_ctx, "PUT MntCtx") \
EM(netfs_trace_tcon_ref_put_dfs_refer, "PUT DfsRfr") \
EM(netfs_trace_tcon_ref_put_reconnect_server, "PUT Reconn") \
+ EM(netfs_trace_tcon_ref_put_swn_notify, "PUT SwnNot") \
EM(netfs_trace_tcon_ref_put_tlink, "PUT Tlink ") \
EM(netfs_trace_tcon_ref_see_cancelled_close, "SEE Cn-Cls") \
EM(netfs_trace_tcon_ref_see_fscache_collision, "SEE FV-CO!") \
@@ -209,7 +216,7 @@
#define EM(a, b) a,
#define E_(a, b) a
-enum smb_eio_trace { smb_eio_traces } __mode(byte);
+enum smb_eio_trace { smb_eio_traces };
enum smb3_rw_credits_trace { smb3_rw_credits_traces } __mode(byte);
enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte);
@@ -668,9 +675,100 @@ DEFINE_EVENT(smb3_fd_err_class, smb3_##name, \
TP_ARGS(xid, fid, tid, sesid, rc))
DEFINE_SMB3_FD_ERR_EVENT(flush_err);
-DEFINE_SMB3_FD_ERR_EVENT(lock_err);
DEFINE_SMB3_FD_ERR_EVENT(close_err);
+DECLARE_EVENT_CLASS(smb3_lock_class,
+ TP_PROTO(unsigned int xid,
+ __u64 fid,
+ __u32 tid,
+ __u64 sesid,
+ __u64 offset,
+ __u64 len,
+ __u32 flags,
+ __u32 num_lock,
+ int rc),
+ TP_ARGS(xid, fid, tid, sesid, offset, len, flags, num_lock, rc),
+ TP_STRUCT__entry(
+ __field(unsigned int, xid)
+ __field(__u64, fid)
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(__u64, offset)
+ __field(__u64, len)
+ __field(__u32, flags)
+ __field(__u32, num_lock)
+ __field(int, rc)
+ ),
+ TP_fast_assign(
+ __entry->xid = xid;
+ __entry->fid = fid;
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->offset = offset;
+ __entry->len = len;
+ __entry->flags = flags;
+ __entry->num_lock = num_lock;
+ __entry->rc = rc;
+ ),
+ TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%llx flags=0x%x num_lock=%u rc=%d",
+ __entry->xid, __entry->sesid, __entry->tid, __entry->fid,
+ __entry->offset, __entry->len, __entry->flags, __entry->num_lock,
+ __entry->rc)
+)
+
+#define DEFINE_SMB3_LOCK_EVENT(name) \
+DEFINE_EVENT(smb3_lock_class, smb3_##name, \
+ TP_PROTO(unsigned int xid, \
+ __u64 fid, \
+ __u32 tid, \
+ __u64 sesid, \
+ __u64 offset, \
+ __u64 len, \
+ __u32 flags, \
+ __u32 num_lock, \
+ int rc), \
+ TP_ARGS(xid, fid, tid, sesid, offset, len, flags, num_lock, rc))
+
+DEFINE_SMB3_LOCK_EVENT(lock_enter);
+DEFINE_SMB3_LOCK_EVENT(lock_done);
+DEFINE_SMB3_LOCK_EVENT(lock_err);
+DEFINE_SMB3_LOCK_EVENT(lock_cached);
+
+TRACE_EVENT(smb3_lock_conflict,
+ TP_PROTO(__u64 fid,
+ __u64 req_offset,
+ __u64 req_len,
+ __u8 req_type,
+ __u64 conf_offset,
+ __u64 conf_len,
+ __u16 conf_type,
+ __u32 conf_pid),
+ TP_ARGS(fid, req_offset, req_len, req_type, conf_offset, conf_len, conf_type, conf_pid),
+ TP_STRUCT__entry(
+ __field(__u64, fid)
+ __field(__u64, req_offset)
+ __field(__u64, req_len)
+ __field(__u8, req_type)
+ __field(__u64, conf_offset)
+ __field(__u64, conf_len)
+ __field(__u16, conf_type)
+ __field(__u32, conf_pid)
+ ),
+ TP_fast_assign(
+ __entry->fid = fid;
+ __entry->req_offset = req_offset;
+ __entry->req_len = req_len;
+ __entry->req_type = req_type;
+ __entry->conf_offset = conf_offset;
+ __entry->conf_len = conf_len;
+ __entry->conf_type = conf_type;
+ __entry->conf_pid = conf_pid;
+ ),
+ TP_printk("fid=0x%llx req=[0x%llx:0x%llx] type=0x%x conflicts with [0x%llx:0x%llx] type=0x%x pid=%u",
+ __entry->fid, __entry->req_offset, __entry->req_len, __entry->req_type,
+ __entry->conf_offset, __entry->conf_len, __entry->conf_type, __entry->conf_pid)
+);
+
/*
* For handle based query/set info calls
*/
@@ -1228,8 +1326,9 @@ DECLARE_EVENT_CLASS(smb3_open_done_class,
__u32 tid,
__u64 sesid,
int create_options,
- int desired_access),
- TP_ARGS(xid, fid, tid, sesid, create_options, desired_access),
+ int desired_access,
+ __u8 oplock),
+ TP_ARGS(xid, fid, tid, sesid, create_options, desired_access, oplock),
TP_STRUCT__entry(
__field(unsigned int, xid)
__field(__u64, fid)
@@ -1237,6 +1336,7 @@ DECLARE_EVENT_CLASS(smb3_open_done_class,
__field(__u64, sesid)
__field(int, create_options)
__field(int, desired_access)
+ __field(__u8, oplock)
),
TP_fast_assign(
__entry->xid = xid;
@@ -1245,10 +1345,11 @@ DECLARE_EVENT_CLASS(smb3_open_done_class,
__entry->sesid = sesid;
__entry->create_options = create_options;
__entry->desired_access = desired_access;
+ __entry->oplock = oplock;
),
- TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx cr_opts=0x%x des_access=0x%x",
+ TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx cr_opts=0x%x des_access=0x%x oplock=0x%x",
__entry->xid, __entry->sesid, __entry->tid, __entry->fid,
- __entry->create_options, __entry->desired_access)
+ __entry->create_options, __entry->desired_access, __entry->oplock)
)
#define DEFINE_SMB3_OPEN_DONE_EVENT(name) \
@@ -1258,12 +1359,64 @@ DEFINE_EVENT(smb3_open_done_class, smb3_##name, \
__u32 tid, \
__u64 sesid, \
int create_options, \
- int desired_access), \
- TP_ARGS(xid, fid, tid, sesid, create_options, desired_access))
+ int desired_access, \
+ __u8 oplock), \
+ TP_ARGS(xid, fid, tid, sesid, create_options, desired_access, oplock))
DEFINE_SMB3_OPEN_DONE_EVENT(open_done);
DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done);
+TRACE_EVENT(smb3_open_cached,
+ TP_PROTO(unsigned int xid,
+ __u32 tid,
+ __u64 sesid,
+ __u64 fid,
+ unsigned int oflags,
+ unsigned int cflags),
+ TP_ARGS(xid, tid, sesid, fid, oflags, cflags),
+ TP_STRUCT__entry(
+ __field(unsigned int, xid)
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(__u64, fid)
+ __field(unsigned int, oflags)
+ __field(unsigned int, cflags)
+ ),
+ TP_fast_assign(
+ __entry->xid = xid;
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->fid = fid;
+ __entry->oflags = oflags;
+ __entry->cflags = cflags;
+ ),
+ TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx oflags=0x%x cflags=0x%x",
+ __entry->xid, __entry->sesid, __entry->tid, __entry->fid,
+ __entry->oflags, __entry->cflags)
+);
+
+TRACE_EVENT(smb3_close_cached,
+ TP_PROTO(__u32 tid,
+ __u64 sesid,
+ __u64 fid,
+ unsigned long delay_jiffies),
+ TP_ARGS(tid, sesid, fid, delay_jiffies),
+ TP_STRUCT__entry(
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(__u64, fid)
+ __field(unsigned long, delay_jiffies)
+ ),
+ TP_fast_assign(
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->fid = fid;
+ __entry->delay_jiffies = delay_jiffies;
+ ),
+ TP_printk("sid=0x%llx tid=0x%x fid=0x%llx delay_jiffies=%lu",
+ __entry->sesid, __entry->tid, __entry->fid, __entry->delay_jiffies)
+);
+
DECLARE_EVENT_CLASS(smb3_lease_done_class,
TP_PROTO(__u32 lease_state,
diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c
index 05f8099047e1..e266859818a4 100644
--- a/fs/smb/client/transport.c
+++ b/fs/smb/client/transport.c
@@ -101,12 +101,11 @@ void __release_mid(struct TCP_Server_Info *server, struct mid_q_entry *midEntry)
trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid,
midEntry->when_sent, midEntry->when_received);
if (cifsFYI & CIFS_TIMER) {
- pr_debug("slow rsp: cmd %d mid %llu",
- midEntry->command, midEntry->mid);
- cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n",
- now - midEntry->when_alloc,
- now - midEntry->when_sent,
- now - midEntry->when_received);
+ pr_debug("slow rsp: cmd %d mid %llu A: 0x%lx S: 0x%lx R: 0x%lx\n",
+ midEntry->command, midEntry->mid,
+ now - midEntry->when_alloc,
+ now - midEntry->when_sent,
+ now - midEntry->when_received);
}
}
#endif
@@ -1158,7 +1157,7 @@ int
cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
int length, len;
- unsigned int data_offset, data_len;
+ unsigned int data_offset, data_len, end_off;
struct cifs_io_subrequest *rdata = mid->callback_data;
char *buf = server->smallbuf;
unsigned int buflen = server->pdu_size;
@@ -1256,11 +1255,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
use_rdma_mr = rdata->mr;
#endif
data_len = server->ops->read_data_length(buf, use_rdma_mr);
- if (!use_rdma_mr && (data_offset + data_len > buflen)) {
- /* data_len is corrupt -- discard frame */
- rdata->result = smb_EIO2(smb_eio_trace_read_rsp_malformed,
- data_offset + data_len, buflen);
- return cifs_readv_discard(server, mid);
+ if (!use_rdma_mr) {
+ if (check_add_overflow(data_offset, data_len, &end_off) ||
+ end_off > buflen) {
+ /* data_len is corrupt -- discard frame */
+ rdata->result = smb_EIO2(smb_eio_trace_read_rsp_malformed,
+ end_off, buflen);
+ return cifs_readv_discard(server, mid);
+ }
}
#ifdef CONFIG_CIFS_SMB_DIRECT
diff --git a/fs/smb/client/xattr.c b/fs/smb/client/xattr.c
index 23227f2f9428..5091f6c0d7fe 100644
--- a/fs/smb/client/xattr.c
+++ b/fs/smb/client/xattr.c
@@ -272,7 +272,7 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, void *value, size_t size)
{
- ssize_t rc = -EOPNOTSUPP;
+ int rc = -EOPNOTSUPP;
unsigned int xid;
struct super_block *sb = dentry->d_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
@@ -354,7 +354,7 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
inode, full_path, &acllen, extra_info);
if (IS_ERR(pacl)) {
rc = PTR_ERR(pacl);
- cifs_dbg(VFS, "%s: error %zd getting sec desc\n",
+ cifs_dbg(VFS, "%s: error %d getting sec desc\n",
__func__, rc);
} else {
if (value) {