mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
18 ksmbd server fixes
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmnqdE8ACgkQiiy9cAdy T1E4uwwAtRDds+fqFZHgqEW/0Vd8O1RJCvUGomoampb4z9rzHOMrekofAjRT6OJs M6jVx5L/22TT9Vf+Ya+WMrOkxQbjyFy8j6IjdefJi2SxX5Z9QM7ZvEWhQlDhdVUV Hfb7Zd3jsdDk6GvvIfVzlEXMLbWtkD5zhGYCVOfNuh/RlyGy+orkjbUfbGEI56c4 WPXkVUvGqHnniU/AB4/9pDFFMOwy4IAY9Bs8u2b65FWoxDsPFbz8ntJ1+Ehcy+Er Try0JqSQT7uJNHN7O334NeylbsxLyszkqDyYUv3A8un7Txzi4OIZFNJuHE4Av95S XVbmrkCgZ7Bm4wvPBxc35usZk+7WFdIgLM5vA37pG93zQ/n/zdjTdOiGQ6+8qw/L rHgG6A1ti6/f48Lk5vj01fOcCoNIEBcwVYtajSGU45f44cGjyqgnDe0Id8OZe1yY DMhsthL45kK1mmYPaq9h85mdxCeec3aKTrMd79dgwiyVMIFEeVJwd6Yvs8SH6qFb +OSVq87r =WKB0 -----END PGP SIGNATURE----- Merge tag 'v7.1-rc-part2-ksmbd-fixes' of git://git.samba.org/ksmbd Pull more smb server updates from Steve French: - move fs/smb/common/smbdirect to fs/smb/smbdirect - change signature calc to use AES-CMAC library, simpler and faster - invalid signature fix - multichannel fix - open create options fix - fix durable handle leak - cap maximum lock count to avoid potential denial of service - four connection fixes: connection free and session destroy IDA fixes, refcount fix, connection leak fix, max_connections off by one fix - IPC validation fix - fix out of bounds write in getting xattrs - fix use after free in durable handle reconnect - three ACL fixes: fix potential ACL overflow, harden num_aces check, and fix minimum ACE size check * tag 'v7.1-rc-part2-ksmbd-fixes' of git://git.samba.org/ksmbd: smb: smbdirect: move fs/smb/common/smbdirect/ to fs/smb/smbdirect/ smb: server: stop sending fake security descriptors ksmbd: scope conn->binding slowpath to bound sessions only ksmbd: fix CreateOptions sanitization clobbering the whole field ksmbd: fix durable fd leak on ClientGUID mismatch in durable v2 open ksmbd: fix O(N^2) DoS in smb2_lock via unbounded LockCount ksmbd: destroy async_ida in ksmbd_conn_free() ksmbd: destroy tree_conn_ida in ksmbd_session_destroy() ksmbd: Use AES-CMAC library for SMB3 signature calculation ksmbd: reset rcount per connection in ksmbd_conn_wait_idle_sess_id() ksmbd: fix out-of-bounds write in smb2_get_ea() EA alignment ksmbd: use check_add_overflow() to prevent u16 DACL size overflow ksmbd: fix use-after-free in smb2_open during durable reconnect ksmbd: validate num_aces and harden ACE walk in smb_inherit_dacl() smb: server: fix max_connections off-by-one in tcp accept path ksmbd: require minimum ACE size in smb_check_perm_dacl() ksmbd: validate response sizes in ipc_validate_msg() smb: server: fix active_num_conn leak on transport allocation failure
This commit is contained in:
commit
bd1886d6e4
|
|
@ -24662,7 +24662,7 @@ L: linux-cifs@vger.kernel.org
|
|||
L: samba-technical@lists.samba.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: fs/smb/client/smbdirect.*
|
||||
F: fs/smb/common/smbdirect/
|
||||
F: fs/smb/smbdirect/
|
||||
F: fs/smb/server/transport_rdma.*
|
||||
|
||||
SMC91x ETHERNET DRIVER
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
source "fs/smb/client/Kconfig"
|
||||
source "fs/smb/server/Kconfig"
|
||||
source "fs/smb/common/smbdirect/Kconfig"
|
||||
source "fs/smb/smbdirect/Kconfig"
|
||||
|
||||
config SMBFS
|
||||
tristate
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_SMBFS) += common/
|
||||
obj-$(CONFIG_SMBDIRECT) += smbdirect/
|
||||
obj-$(CONFIG_CIFS) += client/
|
||||
obj-$(CONFIG_SMB_SERVER) += server/
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ config CIFS_SMB_DIRECT
|
|||
bool "SMB Direct support"
|
||||
depends on CIFS && INFINIBAND && INFINIBAND_ADDR_TRANS
|
||||
depends on CIFS=m || INFINIBAND=y
|
||||
select SMB_COMMON_SMBDIRECT
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#include "cifs_debug.h"
|
||||
#include "cifsproto.h"
|
||||
#include "smb2proto.h"
|
||||
#include "../common/smbdirect/smbdirect_public.h"
|
||||
#include "../smbdirect/public.h"
|
||||
|
||||
/* Port numbers for SMBD transport */
|
||||
#define SMB_PORT 445
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include "cifsglob.h"
|
||||
|
||||
#include "../common/smbdirect/smbdirect.h"
|
||||
#include "../smbdirect/smbdirect.h"
|
||||
|
||||
extern int rdma_readwrite_threshold;
|
||||
extern int smbd_max_frmr_depth;
|
||||
|
|
|
|||
|
|
@ -4,4 +4,3 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_SMBFS) += cifs_md4.o
|
||||
obj-$(CONFIG_SMB_COMMON_SMBDIRECT) += smbdirect/
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Makefile for smbdirect support
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SMB_COMMON_SMBDIRECT) += smbdirect.o
|
||||
|
||||
smbdirect-y := \
|
||||
smbdirect_socket.o \
|
||||
smbdirect_connection.o \
|
||||
smbdirect_mr.o \
|
||||
smbdirect_rw.o \
|
||||
smbdirect_debug.o \
|
||||
smbdirect_connect.o \
|
||||
smbdirect_listen.o \
|
||||
smbdirect_accept.o \
|
||||
smbdirect_devices.o \
|
||||
smbdirect_main.o
|
||||
|
|
@ -7,13 +7,13 @@ config SMB_SERVER
|
|||
select NLS_UTF8
|
||||
select NLS_UCS2_UTILS
|
||||
select CRYPTO
|
||||
select CRYPTO_LIB_AES_CBC_MACS
|
||||
select CRYPTO_LIB_ARC4
|
||||
select CRYPTO_LIB_DES
|
||||
select CRYPTO_LIB_MD5
|
||||
select CRYPTO_LIB_SHA256
|
||||
select CRYPTO_LIB_SHA512
|
||||
select CRYPTO_LIB_UTILS
|
||||
select CRYPTO_CMAC
|
||||
select CRYPTO_AEAD2
|
||||
select CRYPTO_CCM
|
||||
select CRYPTO_GCM
|
||||
|
|
@ -49,7 +49,7 @@ config SMB_SERVER_SMBDIRECT
|
|||
bool "Support for SMB Direct protocol"
|
||||
depends on SMB_SERVER && INFINIBAND && INFINIBAND_ADDR_TRANS
|
||||
depends on SMB_SERVER=m || INFINIBAND=y
|
||||
select SMB_COMMON_SMBDIRECT
|
||||
select SMBDIRECT
|
||||
default n
|
||||
|
||||
help
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
#include <linux/writeback.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/aes-cbc-macs.h>
|
||||
#include <crypto/md5.h>
|
||||
#include <crypto/sha2.h>
|
||||
#include <crypto/utils.h>
|
||||
|
|
@ -490,46 +490,21 @@ void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
|
|||
* @sig: signature value generated for client request packet
|
||||
*
|
||||
*/
|
||||
int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
|
||||
int n_vec, char *sig)
|
||||
void ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
|
||||
int n_vec, char *sig)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
int rc, i;
|
||||
struct aes_cmac_key cmac_key;
|
||||
struct aes_cmac_ctx cmac_ctx;
|
||||
int i;
|
||||
|
||||
ctx = ksmbd_crypto_ctx_find_cmacaes();
|
||||
if (!ctx) {
|
||||
ksmbd_debug(AUTH, "could not crypto alloc cmac\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* This cannot fail, since we always pass a valid key length. */
|
||||
static_assert(SMB2_CMACAES_SIZE == AES_KEYSIZE_128);
|
||||
aes_cmac_preparekey(&cmac_key, key, SMB2_CMACAES_SIZE);
|
||||
|
||||
rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx),
|
||||
key,
|
||||
SMB2_CMACAES_SIZE);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = crypto_shash_init(CRYPTO_CMACAES(ctx));
|
||||
if (rc) {
|
||||
ksmbd_debug(AUTH, "cmaces init error %d\n", rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < n_vec; i++) {
|
||||
rc = crypto_shash_update(CRYPTO_CMACAES(ctx),
|
||||
iov[i].iov_base,
|
||||
iov[i].iov_len);
|
||||
if (rc) {
|
||||
ksmbd_debug(AUTH, "cmaces update error %d\n", rc);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig);
|
||||
if (rc)
|
||||
ksmbd_debug(AUTH, "cmaces generation error %d\n", rc);
|
||||
out:
|
||||
ksmbd_release_crypto_ctx(ctx);
|
||||
return rc;
|
||||
aes_cmac_init(&cmac_ctx, &cmac_key);
|
||||
for (i = 0; i < n_vec; i++)
|
||||
aes_cmac_update(&cmac_ctx, iov[i].iov_base, iov[i].iov_len);
|
||||
aes_cmac_final(&cmac_ctx, sig);
|
||||
}
|
||||
|
||||
struct derivation {
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
|
|||
int in_len, char *out_blob, int *out_len);
|
||||
void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
|
||||
int n_vec, char *sig);
|
||||
int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
|
||||
int n_vec, char *sig);
|
||||
void ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
|
||||
int n_vec, char *sig);
|
||||
int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess,
|
||||
struct ksmbd_conn *conn);
|
||||
int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess,
|
||||
|
|
|
|||
|
|
@ -98,6 +98,15 @@ void ksmbd_conn_free(struct ksmbd_conn *conn)
|
|||
kfree(conn->preauth_info);
|
||||
kfree(conn->mechToken);
|
||||
if (atomic_dec_and_test(&conn->refcnt)) {
|
||||
/*
|
||||
* async_ida is embedded in struct ksmbd_conn, so pair
|
||||
* ida_destroy() with the final kfree() rather than with
|
||||
* the unconditional field teardown above. This keeps
|
||||
* the IDA valid for the entire lifetime of the struct,
|
||||
* even while other refcount holders (oplock / vfs
|
||||
* durable handles) still reference the connection.
|
||||
*/
|
||||
ida_destroy(&conn->async_ida);
|
||||
conn->transport->ops->free_transport(conn->transport);
|
||||
kfree(conn);
|
||||
}
|
||||
|
|
@ -237,7 +246,7 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)
|
|||
{
|
||||
struct ksmbd_conn *conn;
|
||||
int rc, retry_count = 0, max_timeout = 120;
|
||||
int rcount = 1, bkt;
|
||||
int rcount, bkt;
|
||||
|
||||
retry_idle:
|
||||
if (retry_count >= max_timeout)
|
||||
|
|
@ -246,8 +255,7 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)
|
|||
down_read(&conn_list_lock);
|
||||
hash_for_each(conn_list, bkt, conn, hlist) {
|
||||
if (conn->binding || xa_load(&conn->sessions, sess_id)) {
|
||||
if (conn == curr_conn)
|
||||
rcount = 2;
|
||||
rcount = (conn == curr_conn) ? 2 : 1;
|
||||
if (atomic_read(&conn->req_running) >= rcount) {
|
||||
rc = wait_event_timeout(conn->req_running_q,
|
||||
atomic_read(&conn->req_running) < rcount,
|
||||
|
|
|
|||
|
|
@ -28,14 +28,6 @@ static inline void free_aead(struct crypto_aead *aead)
|
|||
crypto_free_aead(aead);
|
||||
}
|
||||
|
||||
static void free_shash(struct shash_desc *shash)
|
||||
{
|
||||
if (shash) {
|
||||
crypto_free_shash(shash->tfm);
|
||||
kfree(shash);
|
||||
}
|
||||
}
|
||||
|
||||
static struct crypto_aead *alloc_aead(int id)
|
||||
{
|
||||
struct crypto_aead *tfm = NULL;
|
||||
|
|
@ -60,37 +52,10 @@ static struct crypto_aead *alloc_aead(int id)
|
|||
return tfm;
|
||||
}
|
||||
|
||||
static struct shash_desc *alloc_shash_desc(int id)
|
||||
{
|
||||
struct crypto_shash *tfm = NULL;
|
||||
struct shash_desc *shash;
|
||||
|
||||
switch (id) {
|
||||
case CRYPTO_SHASH_CMACAES:
|
||||
tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (IS_ERR(tfm))
|
||||
return NULL;
|
||||
|
||||
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
|
||||
KSMBD_DEFAULT_GFP);
|
||||
if (!shash)
|
||||
crypto_free_shash(tfm);
|
||||
else
|
||||
shash->tfm = tfm;
|
||||
return shash;
|
||||
}
|
||||
|
||||
static void ctx_free(struct ksmbd_crypto_ctx *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CRYPTO_SHASH_MAX; i++)
|
||||
free_shash(ctx->desc[i]);
|
||||
for (i = 0; i < CRYPTO_AEAD_MAX; i++)
|
||||
free_aead(ctx->ccmaes[i]);
|
||||
kfree(ctx);
|
||||
|
|
@ -153,29 +118,6 @@ void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx)
|
|||
ctx_free(ctx);
|
||||
}
|
||||
|
||||
static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
if (id >= CRYPTO_SHASH_MAX)
|
||||
return NULL;
|
||||
|
||||
ctx = ksmbd_find_crypto_ctx();
|
||||
if (ctx->desc[id])
|
||||
return ctx;
|
||||
|
||||
ctx->desc[id] = alloc_shash_desc(id);
|
||||
if (ctx->desc[id])
|
||||
return ctx;
|
||||
ksmbd_release_crypto_ctx(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES);
|
||||
}
|
||||
|
||||
static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
|
|
|||
|
|
@ -6,14 +6,8 @@
|
|||
#ifndef __CRYPTO_CTX_H__
|
||||
#define __CRYPTO_CTX_H__
|
||||
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/aead.h>
|
||||
|
||||
enum {
|
||||
CRYPTO_SHASH_CMACAES = 0,
|
||||
CRYPTO_SHASH_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
CRYPTO_AEAD_AES_GCM = 16,
|
||||
CRYPTO_AEAD_AES_CCM,
|
||||
|
|
@ -23,19 +17,13 @@ enum {
|
|||
struct ksmbd_crypto_ctx {
|
||||
struct list_head list;
|
||||
|
||||
struct shash_desc *desc[CRYPTO_SHASH_MAX];
|
||||
struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX];
|
||||
};
|
||||
|
||||
#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES])
|
||||
|
||||
#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm)
|
||||
|
||||
#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM])
|
||||
#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM])
|
||||
|
||||
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void);
|
||||
void ksmbd_crypto_destroy(void);
|
||||
|
|
|
|||
|
|
@ -56,12 +56,6 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
|
|||
goto err_free;
|
||||
|
||||
if (resp_ext) {
|
||||
if (resp_ext->ngroups > NGROUPS_MAX) {
|
||||
pr_err("ngroups(%u) from login response exceeds max groups(%d)\n",
|
||||
resp_ext->ngroups, NGROUPS_MAX);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
user->sgid = kmemdup(resp_ext->____payload,
|
||||
resp_ext->ngroups * sizeof(gid_t),
|
||||
KSMBD_DEFAULT_GFP);
|
||||
|
|
|
|||
|
|
@ -391,6 +391,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
|
|||
free_channel_list(sess);
|
||||
kfree(sess->Preauth_HashValue);
|
||||
ksmbd_release_id(&session_ida, sess->id);
|
||||
ida_destroy(&sess->tree_conn_ida);
|
||||
kfree(sess);
|
||||
}
|
||||
|
||||
|
|
@ -547,8 +548,13 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
|
|||
struct ksmbd_session *sess;
|
||||
|
||||
sess = ksmbd_session_lookup(conn, id);
|
||||
if (!sess && conn->binding)
|
||||
if (!sess && conn->binding) {
|
||||
sess = ksmbd_session_lookup_slowpath(id);
|
||||
if (sess && !xa_load(&sess->ksmbd_chann_list, (long)conn)) {
|
||||
ksmbd_user_session_put(sess);
|
||||
sess = NULL;
|
||||
}
|
||||
}
|
||||
if (sess && sess->state != SMB2_SESSION_VALID) {
|
||||
ksmbd_user_session_put(sess);
|
||||
sess = NULL;
|
||||
|
|
@ -665,6 +671,8 @@ static struct ksmbd_session *__session_create(int protocol)
|
|||
if (!sess)
|
||||
return NULL;
|
||||
|
||||
ida_init(&sess->tree_conn_ida);
|
||||
|
||||
if (ksmbd_init_file_table(&sess->file_table))
|
||||
goto error;
|
||||
|
||||
|
|
@ -684,8 +692,6 @@ static struct ksmbd_session *__session_create(int protocol)
|
|||
if (ret)
|
||||
goto error;
|
||||
|
||||
ida_init(&sess->tree_conn_ida);
|
||||
|
||||
down_write(&sessions_table_lock);
|
||||
hash_add(sessions_table, &sess->hlist, sess->id);
|
||||
up_write(&sessions_table_lock);
|
||||
|
|
|
|||
|
|
@ -631,7 +631,6 @@ MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER");
|
|||
MODULE_LICENSE("GPL");
|
||||
MODULE_SOFTDEP("pre: nls");
|
||||
MODULE_SOFTDEP("pre: aes");
|
||||
MODULE_SOFTDEP("pre: cmac");
|
||||
MODULE_SOFTDEP("pre: aead2");
|
||||
MODULE_SOFTDEP("pre: ccm");
|
||||
MODULE_SOFTDEP("pre: gcm");
|
||||
|
|
|
|||
|
|
@ -2844,6 +2844,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
|
|||
dh_info->reconnected = true;
|
||||
goto out;
|
||||
}
|
||||
ksmbd_put_durable_fd(dh_info->fp);
|
||||
dh_info->fp = NULL;
|
||||
}
|
||||
|
||||
if ((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
|
||||
|
|
@ -3014,29 +3016,23 @@ int smb2_open(struct ksmbd_work *work)
|
|||
if (dh_info.reconnected == true) {
|
||||
rc = smb2_check_durable_oplock(conn, share, dh_info.fp,
|
||||
lc, sess->user, name);
|
||||
if (rc) {
|
||||
ksmbd_put_durable_fd(dh_info.fp);
|
||||
if (rc)
|
||||
goto err_out2;
|
||||
}
|
||||
|
||||
rc = ksmbd_reopen_durable_fd(work, dh_info.fp);
|
||||
if (rc) {
|
||||
ksmbd_put_durable_fd(dh_info.fp);
|
||||
if (rc)
|
||||
goto err_out2;
|
||||
}
|
||||
|
||||
fp = dh_info.fp;
|
||||
|
||||
if (ksmbd_override_fsids(work)) {
|
||||
rc = -ENOMEM;
|
||||
ksmbd_put_durable_fd(dh_info.fp);
|
||||
goto err_out2;
|
||||
}
|
||||
|
||||
file_info = FILE_OPENED;
|
||||
|
||||
rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat);
|
||||
ksmbd_put_durable_fd(fp);
|
||||
if (rc)
|
||||
goto err_out2;
|
||||
|
||||
|
|
@ -3061,7 +3057,7 @@ int smb2_open(struct ksmbd_work *work)
|
|||
} else {
|
||||
if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE &&
|
||||
req->CreateOptions & FILE_RANDOM_ACCESS_LE)
|
||||
req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE);
|
||||
req->CreateOptions &= ~FILE_SEQUENTIAL_ONLY_LE;
|
||||
|
||||
if (req->CreateOptions &
|
||||
(FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION |
|
||||
|
|
@ -3075,7 +3071,7 @@ int smb2_open(struct ksmbd_work *work)
|
|||
rc = -EINVAL;
|
||||
goto err_out2;
|
||||
} else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) {
|
||||
req->CreateOptions = ~(FILE_NO_COMPRESSION_LE);
|
||||
req->CreateOptions &= ~FILE_NO_COMPRESSION_LE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3806,6 +3802,9 @@ int smb2_open(struct ksmbd_work *work)
|
|||
ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status);
|
||||
}
|
||||
|
||||
if (dh_info.reconnected)
|
||||
ksmbd_put_durable_fd(dh_info.fp);
|
||||
|
||||
kfree(name);
|
||||
kfree(lc);
|
||||
|
||||
|
|
@ -4821,6 +4820,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
|
|||
/* align next xattr entry at 4 byte bundary */
|
||||
alignment_bytes = ((next_offset + 3) & ~3) - next_offset;
|
||||
if (alignment_bytes) {
|
||||
if (buf_free_len < alignment_bytes)
|
||||
break;
|
||||
memset(ptr, '\0', alignment_bytes);
|
||||
ptr += alignment_bytes;
|
||||
next_offset += alignment_bytes;
|
||||
|
|
@ -5745,20 +5746,8 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
|
|||
ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n",
|
||||
addition_info);
|
||||
|
||||
pntsd = kzalloc(ALIGN(sizeof(struct smb_ntsd), 8),
|
||||
KSMBD_DEFAULT_GFP);
|
||||
if (!pntsd)
|
||||
return -ENOMEM;
|
||||
|
||||
pntsd->revision = cpu_to_le16(1);
|
||||
pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED);
|
||||
pntsd->osidoffset = 0;
|
||||
pntsd->gsidoffset = 0;
|
||||
pntsd->sacloffset = 0;
|
||||
pntsd->dacloffset = 0;
|
||||
|
||||
secdesclen = sizeof(struct smb_ntsd);
|
||||
goto iov_pin;
|
||||
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (work->next_smb2_rcv_hdr_off) {
|
||||
|
|
@ -5825,7 +5814,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
|
|||
if (rc)
|
||||
goto err_out;
|
||||
|
||||
iov_pin:
|
||||
rsp->OutputBufferLength = cpu_to_le32(secdesclen);
|
||||
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
|
||||
rsp, work->response_buf);
|
||||
|
|
@ -7492,7 +7480,12 @@ int smb2_lock(struct ksmbd_work *work)
|
|||
lock_ele = req->locks;
|
||||
|
||||
ksmbd_debug(SMB, "lock count is %d\n", lock_count);
|
||||
if (!lock_count) {
|
||||
/*
|
||||
* Cap lock_count at 64. The MS-SMB2 spec defines Open.LockSequenceArray
|
||||
* as exactly 64 entries so 64 is the intended ceiling. No real workload
|
||||
* comes close to this in a single request.
|
||||
*/
|
||||
if (!lock_count || lock_count > 64) {
|
||||
err = -EINVAL;
|
||||
goto out2;
|
||||
}
|
||||
|
|
@ -9067,8 +9060,7 @@ int smb3_check_sign_req(struct ksmbd_work *work)
|
|||
iov[0].iov_base = (char *)&hdr->ProtocolId;
|
||||
iov[0].iov_len = len;
|
||||
|
||||
if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature))
|
||||
return 0;
|
||||
ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature);
|
||||
|
||||
if (crypto_memneq(signature, signature_req, SMB2_SIGNATURE_SIZE)) {
|
||||
pr_err("bad smb2 signature\n");
|
||||
|
|
@ -9119,9 +9111,8 @@ void smb3_set_sign_rsp(struct ksmbd_work *work)
|
|||
iov = &work->iov[work->iov_idx];
|
||||
}
|
||||
|
||||
if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec,
|
||||
signature))
|
||||
memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
|
||||
ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature);
|
||||
memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -596,6 +596,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
|
|||
struct smb_sid *sid;
|
||||
struct smb_ace *ntace;
|
||||
int i, j;
|
||||
u16 ace_sz;
|
||||
|
||||
if (!fattr->cf_acls)
|
||||
goto posix_default_acl;
|
||||
|
|
@ -640,8 +641,10 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
|
|||
flags = 0x03;
|
||||
|
||||
ntace = (struct smb_ace *)((char *)pndace + *size);
|
||||
*size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags,
|
||||
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags,
|
||||
pace->e_perm, 0777);
|
||||
if (check_add_overflow(*size, ace_sz, size))
|
||||
break;
|
||||
(*num_aces)++;
|
||||
if (pace->e_tag == ACL_USER)
|
||||
ntace->access_req |=
|
||||
|
|
@ -650,8 +653,10 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
|
|||
if (S_ISDIR(fattr->cf_mode) &&
|
||||
(pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) {
|
||||
ntace = (struct smb_ace *)((char *)pndace + *size);
|
||||
*size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED,
|
||||
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED,
|
||||
0x03, pace->e_perm, 0777);
|
||||
if (check_add_overflow(*size, ace_sz, size))
|
||||
break;
|
||||
(*num_aces)++;
|
||||
if (pace->e_tag == ACL_USER)
|
||||
ntace->access_req |=
|
||||
|
|
@ -691,8 +696,10 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
|
|||
}
|
||||
|
||||
ntace = (struct smb_ace *)((char *)pndace + *size);
|
||||
*size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b,
|
||||
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b,
|
||||
pace->e_perm, 0777);
|
||||
if (check_add_overflow(*size, ace_sz, size))
|
||||
break;
|
||||
(*num_aces)++;
|
||||
if (pace->e_tag == ACL_USER)
|
||||
ntace->access_req |=
|
||||
|
|
@ -728,7 +735,8 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,
|
|||
break;
|
||||
|
||||
memcpy((char *)pndace + size, ntace, nt_ace_size);
|
||||
size += nt_ace_size;
|
||||
if (check_add_overflow(size, nt_ace_size, &size))
|
||||
break;
|
||||
aces_size -= nt_ace_size;
|
||||
ntace = (struct smb_ace *)((char *)ntace + nt_ace_size);
|
||||
num_aces++;
|
||||
|
|
@ -1106,8 +1114,24 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
|
|||
goto free_parent_pntsd;
|
||||
}
|
||||
|
||||
aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2,
|
||||
KSMBD_DEFAULT_GFP);
|
||||
aces_size = pdacl_size - sizeof(struct smb_acl);
|
||||
|
||||
/*
|
||||
* Validate num_aces against the DACL payload before allocating.
|
||||
* Each ACE must be at least as large as its fixed-size header
|
||||
* (up to the SID base), so num_aces cannot exceed the payload
|
||||
* divided by the minimum ACE size. This mirrors the existing
|
||||
* check in parse_dacl().
|
||||
*/
|
||||
if (num_aces > aces_size / (offsetof(struct smb_ace, sid) +
|
||||
offsetof(struct smb_sid, sub_auth) +
|
||||
sizeof(__le16))) {
|
||||
rc = -EINVAL;
|
||||
goto free_parent_pntsd;
|
||||
}
|
||||
|
||||
aces_base = kmalloc_array(num_aces * 2, sizeof(struct smb_ace),
|
||||
KSMBD_DEFAULT_GFP);
|
||||
if (!aces_base) {
|
||||
rc = -ENOMEM;
|
||||
goto free_parent_pntsd;
|
||||
|
|
@ -1116,7 +1140,6 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
|
|||
aces = (struct smb_ace *)aces_base;
|
||||
parent_aces = (struct smb_ace *)((char *)parent_pdacl +
|
||||
sizeof(struct smb_acl));
|
||||
aces_size = acl_len - sizeof(struct smb_acl);
|
||||
|
||||
if (pntsd_type & DACL_AUTO_INHERITED)
|
||||
inherited_flags = INHERITED_ACE;
|
||||
|
|
@ -1124,11 +1147,14 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
|
|||
for (i = 0; i < num_aces; i++) {
|
||||
int pace_size;
|
||||
|
||||
if (offsetof(struct smb_ace, access_req) > aces_size)
|
||||
if (aces_size < offsetof(struct smb_ace, sid) +
|
||||
CIFS_SID_BASE_SIZE)
|
||||
break;
|
||||
|
||||
pace_size = le16_to_cpu(parent_aces->size);
|
||||
if (pace_size > aces_size)
|
||||
if (pace_size > aces_size ||
|
||||
pace_size < offsetof(struct smb_ace, sid) +
|
||||
CIFS_SID_BASE_SIZE)
|
||||
break;
|
||||
|
||||
aces_size -= pace_size;
|
||||
|
|
@ -1342,10 +1368,13 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
|
|||
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
|
||||
aces_size = acl_size - sizeof(struct smb_acl);
|
||||
for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
|
||||
if (offsetof(struct smb_ace, access_req) > aces_size)
|
||||
if (offsetof(struct smb_ace, sid) +
|
||||
aces_size < CIFS_SID_BASE_SIZE)
|
||||
break;
|
||||
ace_size = le16_to_cpu(ace->size);
|
||||
if (ace_size > aces_size)
|
||||
if (ace_size > aces_size ||
|
||||
ace_size < offsetof(struct smb_ace, sid) +
|
||||
CIFS_SID_BASE_SIZE)
|
||||
break;
|
||||
aces_size -= ace_size;
|
||||
granted |= le32_to_cpu(ace->access_req);
|
||||
|
|
@ -1360,13 +1389,19 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
|
|||
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
|
||||
aces_size = acl_size - sizeof(struct smb_acl);
|
||||
for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
|
||||
if (offsetof(struct smb_ace, access_req) > aces_size)
|
||||
if (offsetof(struct smb_ace, sid) +
|
||||
aces_size < CIFS_SID_BASE_SIZE)
|
||||
break;
|
||||
ace_size = le16_to_cpu(ace->size);
|
||||
if (ace_size > aces_size)
|
||||
if (ace_size > aces_size ||
|
||||
ace_size < offsetof(struct smb_ace, sid) +
|
||||
CIFS_SID_BASE_SIZE)
|
||||
break;
|
||||
aces_size -= ace_size;
|
||||
|
||||
if (ace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES)
|
||||
break;
|
||||
|
||||
if (!compare_sids(&sid, &ace->sid) ||
|
||||
!compare_sids(&sid_unix_NFS_mode, &ace->sid)) {
|
||||
found = 1;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <net/genetlink.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/overflow.h>
|
||||
|
||||
#include "vfs_cache.h"
|
||||
#include "transport_ipc.h"
|
||||
|
|
@ -496,7 +497,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
|
|||
{
|
||||
struct ksmbd_rpc_command *resp = entry->response;
|
||||
|
||||
msg_sz = sizeof(struct ksmbd_rpc_command) + resp->payload_sz;
|
||||
if (check_add_overflow(sizeof(struct ksmbd_rpc_command),
|
||||
resp->payload_sz, &msg_sz))
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
case KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST:
|
||||
|
|
@ -515,8 +518,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
|
|||
if (resp->payload_sz < resp->veto_list_sz)
|
||||
return -EINVAL;
|
||||
|
||||
msg_sz = sizeof(struct ksmbd_share_config_response) +
|
||||
resp->payload_sz;
|
||||
if (check_add_overflow(sizeof(struct ksmbd_share_config_response),
|
||||
resp->payload_sz, &msg_sz))
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -525,6 +529,12 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
|
|||
struct ksmbd_login_response_ext *resp = entry->response;
|
||||
|
||||
if (resp->ngroups) {
|
||||
if (resp->ngroups < 0 ||
|
||||
resp->ngroups > NGROUPS_MAX) {
|
||||
pr_err("ngroups(%d) from login response exceeds max groups(%d)\n",
|
||||
resp->ngroups, NGROUPS_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
msg_sz = sizeof(struct ksmbd_login_response_ext) +
|
||||
resp->ngroups * sizeof(gid_t);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#include "smb_common.h"
|
||||
#include "../common/smb2status.h"
|
||||
#include "transport_rdma.h"
|
||||
#include "../common/smbdirect/smbdirect_public.h"
|
||||
#include "../smbdirect/public.h"
|
||||
|
||||
|
||||
#define SMB_DIRECT_PORT_IWARP 5445
|
||||
|
|
|
|||
|
|
@ -25,6 +25,6 @@ static inline void init_smbd_max_io_size(unsigned int sz) { }
|
|||
static inline unsigned int get_smbd_max_read_write_size(struct ksmbd_transport *kt) { return 0; }
|
||||
#endif
|
||||
|
||||
#include "../common/smbdirect/smbdirect.h"
|
||||
#include "../smbdirect/smbdirect.h"
|
||||
|
||||
#endif /* __KSMBD_TRANSPORT_RDMA_H__ */
|
||||
|
|
|
|||
|
|
@ -183,6 +183,8 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk)
|
|||
t = alloc_transport(client_sk);
|
||||
if (!t) {
|
||||
sock_release(client_sk);
|
||||
if (server_conf.max_connections)
|
||||
atomic_dec(&active_num_conn);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
@ -279,7 +281,7 @@ static int ksmbd_kthread_fn(void *p)
|
|||
|
||||
skip_max_ip_conns_limit:
|
||||
if (server_conf.max_connections &&
|
||||
atomic_inc_return(&active_num_conn) >= server_conf.max_connections) {
|
||||
atomic_inc_return(&active_num_conn) > server_conf.max_connections) {
|
||||
pr_info_ratelimited("Limit the maximum number of connections(%u)\n",
|
||||
atomic_read(&active_num_conn));
|
||||
atomic_dec(&active_num_conn);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#
|
||||
# smbdirect configuration
|
||||
|
||||
config SMB_COMMON_SMBDIRECT
|
||||
config SMBDIRECT
|
||||
def_tristate n
|
||||
depends on INFINIBAND && INFINIBAND_ADDR_TRANS
|
||||
depends on m || INFINIBAND=y
|
||||
18
fs/smb/smbdirect/Makefile
Normal file
18
fs/smb/smbdirect/Makefile
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Makefile for smbdirect support
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SMBDIRECT) += smbdirect.o
|
||||
|
||||
smbdirect-y := \
|
||||
socket.o \
|
||||
connection.o \
|
||||
mr.o \
|
||||
rw.o \
|
||||
debug.o \
|
||||
connect.o \
|
||||
listen.o \
|
||||
accept.o \
|
||||
devices.o \
|
||||
main.o
|
||||
|
|
@ -5,9 +5,9 @@
|
|||
* Copyright (c) 2025, Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
#include "internal.h"
|
||||
#include <net/sock.h>
|
||||
#include "../../common/smb2status.h"
|
||||
#include "../common/smb2status.h"
|
||||
|
||||
static int smbdirect_accept_rdma_event_handler(struct rdma_cm_id *id,
|
||||
struct rdma_cm_event *event);
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
* Copyright (c) 2012,2016,2017,2025 Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
#include "../../common/smb2status.h"
|
||||
#include "internal.h"
|
||||
#include "../common/smb2status.h"
|
||||
|
||||
static int smbdirect_connect_setup_connection(struct smbdirect_socket *sc);
|
||||
static int smbdirect_connect_resolve_addr(struct smbdirect_socket *sc,
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (c) 2025, Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
#include "internal.h"
|
||||
#include <linux/folio_queue.h>
|
||||
|
||||
struct smbdirect_map_sges {
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (c) 2025, Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
#include "internal.h"
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
void smbdirect_connection_legacy_debug_proc_show(struct smbdirect_socket *sc,
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright (c) 2025 Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
#include "internal.h"
|
||||
|
||||
static u8 smbdirect_ib_device_rdma_capable_node_type(struct ib_device *ib_dev)
|
||||
{
|
||||
|
|
@ -9,8 +9,8 @@
|
|||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include "smbdirect.h"
|
||||
#include "smbdirect_pdu.h"
|
||||
#include "smbdirect_public.h"
|
||||
#include "pdu.h"
|
||||
#include "public.h"
|
||||
|
||||
#include <linux/mutex.h>
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ struct smbdirect_module_state {
|
|||
|
||||
extern struct smbdirect_module_state smbdirect_globals;
|
||||
|
||||
#include "smbdirect_socket.h"
|
||||
#include "socket.h"
|
||||
|
||||
struct smbdirect_device {
|
||||
struct list_head list;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright (c) 2025, Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
#include "internal.h"
|
||||
|
||||
static int smbdirect_listen_rdma_event_handler(struct rdma_cm_id *id,
|
||||
struct rdma_cm_event *event);
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
* Copyright (c) 2025, Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
#include "internal.h"
|
||||
#include <linux/module.h>
|
||||
|
||||
struct smbdirect_module_state smbdirect_globals = {
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (c) 2025, Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* Allocate MRs used for RDMA read/write
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright (c) 2025, Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
#include "internal.h"
|
||||
|
||||
static int smbdirect_connection_wait_for_rw_credits(struct smbdirect_socket *sc,
|
||||
int credits)
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (c) 2025, Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#include "smbdirect_internal.h"
|
||||
#include "internal.h"
|
||||
|
||||
bool smbdirect_frwr_is_supported(const struct ib_device_attr *attrs)
|
||||
{
|
||||
Loading…
Reference in New Issue
Block a user