Merge tag 'v7.2-rc1-smb3-server-fixes' of git://git.samba.org/ksmbd

Pull smb server fixes from Steve French:

 - Fix several use-after-free races in durable handle reconnect,
   supersede, and oplock handling

 - Avoid holding the inode oplock lock while waiting for a lease break
   acknowledgement. This removes delays of up to 35 seconds when cifs.ko
   closes a deferred handle in response to a lease break

 - Fix malformed security descriptor handling, including an undersized
   DACL allocation issue and an out-of-bounds ACE SID read

 - Fix memory leaks in security descriptor and DOS attribute xattr
   encoding/decoding error paths

 - Fix outstanding SMB2 credit leaks on aborted requests and correct the
   QUERY_INFO credit charge calculation

 - Fix hard-link creation without replacement being incorrectly rejected
   when the handle lacks DELETE access

 - Avoid unnecessary zeroing of large SMB2 read buffers

 - Add an oplock list lockdep annotation and update the documented
   support status for durable handles and SMB3.1.1 compression

 - Durable handle fixes to address ownership and lifetime races during
   reconnect, session teardown, oplock handling, and superseding opens,
   preventing stale session and file references from being used by
   concurrent operations

* tag 'v7.2-rc1-smb3-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: fix app-instance durable supersede session UAF
  ksmbd: snapshot previous oplock state before durable checks
  ksmbd: close superseded durable handles through refcount handoff
  ksmbd: fix use-after-free of fp->owner.name in durable handle owner check
  smb/server: do not require delete access for non-replacing links
  ksmbd: don't hold ci->m_lock while waiting for a lease break ack
  ksmbd: doc: update feature support status for durable handles and compression
  ksmbd: annotate oplock list traversals under m_lock
  ksmbd: fix outstanding credit leak on abort and error paths
  ksmbd: fix credit charge calculation for SMB2 QUERY_INFO
  ksmbd: avoid zeroing the read buffer in smb2_read()
  ksmbd: validate num_subauth when copying ACE in set_ntacl_dacl
  ksmbd: reject undersized DACLs before parsing ACEs
  ksmbd: fix n.data memory leak in ksmbd_vfs_set_dos_attrib_xattr
  ksmbd: Fix acl.sd_buf memory leak and invalid sd_size error handling
  ksmbd: fix sd_ndr.data memory leak in ksmbd_vfs_set_sd_xattr
This commit is contained in:
Linus Torvalds
2026-07-03 18:55:34 -10:00
9 changed files with 216 additions and 61 deletions

View File

@@ -97,7 +97,7 @@ ACLs Partially Supported. only DACLs available, SACLs
to allow future support for running as a domain
member.
Kerberos Supported.
Durable handle v1,v2 Planned for future.
Durable handle v1,v2 Supported.
Persistent handle Planned for future.
SMB2 notify Planned for future.
Sparse file support Supported.
@@ -111,7 +111,7 @@ DCE/RPC support Partially Supported. a few calls(NetShareEnumAll,
for Witness protocol e.g.)
ksmbd/nfsd interoperability Planned for future. The features that ksmbd
support are Leases, Notify, ACLs and Share modes.
SMB3.1.1 Compression Planned for future.
SMB3.1.1 Compression Supported.
SMB3.1.1 over QUIC Planned for future.
Signing/Encryption over RDMA Planned for future.
SMB3.1.1 GMAC signing support Planned for future.

View File

@@ -67,6 +67,13 @@ struct ksmbd_work {
/* Number of granted credits */
unsigned int credits_granted;
/*
* Credit charge added to conn->outstanding_credits at receive time
* for the SMB2 PDU currently being processed, pending release. Zero
* once the charge has been returned (on the response or error path).
*/
unsigned short credit_charge;
/* response smb header size */
unsigned int response_sz;

View File

@@ -278,10 +278,24 @@ struct oplock_info *opinfo_get(struct ksmbd_file *fp)
return opinfo;
}
static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
struct oplock_snapshot {
bool durable_open;
bool durable_detached;
unsigned long long fid;
};
static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci,
struct ksmbd_file *skip_fp,
struct oplock_snapshot *snapshot)
{
struct oplock_info *opinfo;
if (snapshot) {
snapshot->durable_open = false;
snapshot->durable_detached = false;
snapshot->fid = KSMBD_NO_FID;
}
down_read(&ci->m_lock);
opinfo = list_first_entry_or_null(&ci->m_op_list, struct oplock_info,
op_entry);
@@ -295,6 +309,16 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
opinfo = NULL;
}
}
if (opinfo && snapshot && opinfo->o_fp &&
opinfo->o_fp != skip_fp &&
READ_ONCE(opinfo->o_fp->is_durable)) {
snapshot->durable_open = true;
snapshot->durable_detached =
!READ_ONCE(opinfo->o_fp->conn) ||
!READ_ONCE(opinfo->o_fp->tcon);
snapshot->fid = opinfo->fid;
}
}
up_read(&ci->m_lock);
@@ -314,7 +338,7 @@ void opinfo_put(struct oplock_info *opinfo)
static bool ksmbd_inode_has_lease(struct ksmbd_inode *ci)
{
struct oplock_info *opinfo = opinfo_get_list(ci);
struct oplock_info *opinfo = opinfo_get_list(ci, NULL, NULL);
bool is_lease;
if (!opinfo)
@@ -1180,6 +1204,36 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level,
return err;
}
struct oplock_break_entry {
struct list_head list;
struct oplock_info *opinfo;
};
static int oplock_break_add(struct list_head *head, struct oplock_info *opinfo)
{
struct oplock_break_entry *ent;
ent = kmalloc_obj(struct oplock_break_entry, KSMBD_DEFAULT_GFP);
if (!ent)
return -ENOMEM;
ent->opinfo = opinfo;
list_add_tail(&ent->list, head);
return 0;
}
static void oplock_break_drain_none(struct list_head *head)
{
struct oplock_break_entry *ent, *tmp;
list_for_each_entry_safe(ent, tmp, head, list) {
oplock_break(ent->opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false);
list_del(&ent->list);
opinfo_put(ent->opinfo);
kfree(ent);
}
}
void destroy_lease_table(struct ksmbd_conn *conn)
{
struct lease_table *lb, *lbtmp;
@@ -1289,6 +1343,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
{
struct oplock_info *opinfo;
struct ksmbd_inode *p_ci = NULL;
LIST_HEAD(brk_list);
if (lctx->version != 2)
return;
@@ -1314,12 +1369,14 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
continue;
}
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false);
opinfo_put(opinfo);
if (oplock_break_add(&brk_list, opinfo))
opinfo_put(opinfo);
}
}
up_read(&p_ci->m_lock);
oplock_break_drain_none(&brk_list);
ksmbd_inode_put(p_ci);
}
@@ -1327,6 +1384,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
{
struct oplock_info *opinfo;
struct ksmbd_inode *p_ci = NULL;
LIST_HEAD(brk_list);
rcu_read_lock();
opinfo = rcu_dereference(fp->f_opinfo);
@@ -1355,12 +1413,14 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
continue;
}
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false);
opinfo_put(opinfo);
if (oplock_break_add(&brk_list, opinfo))
opinfo_put(opinfo);
}
}
up_read(&p_ci->m_lock);
oplock_break_drain_none(&brk_list);
ksmbd_inode_put(p_ci);
}
@@ -1385,6 +1445,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
struct oplock_info *opinfo = NULL, *prev_opinfo = NULL;
struct ksmbd_inode *ci = fp->f_ci;
struct lease_table *new_lb = NULL;
struct oplock_snapshot prev_op_snapshot;
bool prev_op_has_lease;
bool prev_durable_open = false;
bool prev_durable_detached = false;
@@ -1452,7 +1513,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
goto out;
}
}
prev_opinfo = opinfo_get_list(ci);
prev_opinfo = opinfo_get_list(ci, fp, &prev_op_snapshot);
if (!prev_opinfo ||
(prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) {
opinfo_put(prev_opinfo);
@@ -1474,13 +1535,9 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
goto op_break_not_needed;
}
if (prev_opinfo->o_fp && prev_opinfo->o_fp != fp &&
prev_opinfo->o_fp->is_durable) {
prev_durable_open = true;
prev_durable_detached = !prev_opinfo->o_fp->conn ||
!prev_opinfo->o_fp->tcon;
prev_fid = prev_opinfo->fid;
}
prev_durable_open = prev_op_snapshot.durable_open;
prev_durable_detached = prev_op_snapshot.durable_detached;
prev_fid = prev_op_snapshot.fid;
err = oplock_break(prev_opinfo, break_level, work,
share_ret < 0 && prev_opinfo->is_lease);
@@ -1571,7 +1628,7 @@ static bool smb_break_all_write_oplock(struct ksmbd_work *work,
struct oplock_info *brk_opinfo;
bool sent_break = false;
brk_opinfo = opinfo_get_list(fp->f_ci);
brk_opinfo = opinfo_get_list(fp->f_ci, NULL, NULL);
if (!brk_opinfo)
return false;
if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
@@ -1602,9 +1659,11 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work,
bool send_interim, bool send_oplock_break)
{
struct oplock_info *op, *brk_op;
struct oplock_break_entry *ent, *tmp;
struct ksmbd_inode *ci;
struct ksmbd_conn *conn = work->conn;
bool sent_interim = false;
LIST_HEAD(brk_list);
if (!test_share_config_flag(work->tcon->share_conf,
KSMBD_SHARE_FLAG_OPLOCKS))
@@ -1646,6 +1705,22 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work,
SMB2_LEASE_KEY_SIZE))
goto next;
brk_op->open_trunc = is_trunc;
/*
* Defer the break until ci->m_lock is released: oplock_break()
* may block waiting for the lease break acknowledgment, and the
* close that wakes that wait needs ci->m_lock for write.
*/
if (!oplock_break_add(&brk_list, brk_op))
continue;
next:
opinfo_put(brk_op);
}
up_read(&ci->m_lock);
list_for_each_entry_safe(ent, tmp, &brk_list, list) {
brk_op = ent->opinfo;
if (!brk_op->is_lease && !send_oplock_break) {
brk_op->level = SMB2_OPLOCK_LEVEL_NONE;
brk_op->op_state = OPLOCK_STATE_NONE;
@@ -1657,10 +1732,10 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work,
false);
}
sent_interim = true;
next:
list_del(&ent->list);
opinfo_put(brk_op);
kfree(ent);
}
up_read(&ci->m_lock);
if (op)
opinfo_put(op);

View File

@@ -242,6 +242,20 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
} while (is_chained == true);
send:
/*
* Release any credit charge still outstanding for this request. On
* the normal path smb2_set_rsp_credits() already returned it, but the
* abort, error and send-no-response paths skip that call, so the
* charge would otherwise leak and eventually exhaust the connection's
* outstanding credit window.
*/
if (work->credit_charge) {
spin_lock(&conn->credits_lock);
conn->outstanding_credits -= work->credit_charge;
work->credit_charge = 0;
spin_unlock(&conn->credits_lock);
}
if (work->tcon)
ksmbd_tree_connect_put(work->tcon);
smb3_preauth_hash_rsp(work);

View File

@@ -261,8 +261,12 @@ static int smb2_calc_size(void *buf, unsigned int *len)
static inline int smb2_query_info_req_len(struct smb2_query_info_req *h)
{
return le32_to_cpu(h->InputBufferLength) +
le32_to_cpu(h->OutputBufferLength);
return le32_to_cpu(h->InputBufferLength);
}
static inline int smb2_query_info_resp_len(struct smb2_query_info_req *h)
{
return le32_to_cpu(h->OutputBufferLength);
}
static inline int smb2_set_info_req_len(struct smb2_set_info_req *h)
@@ -297,9 +301,10 @@ static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h)
le32_to_cpu(h->MaxOutputResponse);
}
static int smb2_validate_credit_charge(struct ksmbd_conn *conn,
static int smb2_validate_credit_charge(struct ksmbd_work *work,
struct smb2_hdr *hdr)
{
struct ksmbd_conn *conn = work->conn;
unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len;
unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge);
void *__hdr = hdr;
@@ -308,6 +313,7 @@ static int smb2_validate_credit_charge(struct ksmbd_conn *conn,
switch (hdr->Command) {
case SMB2_QUERY_INFO:
req_len = smb2_query_info_req_len(__hdr);
expect_resp_len = smb2_query_info_resp_len(__hdr);
break;
case SMB2_SET_INFO:
req_len = smb2_set_info_req_len(__hdr);
@@ -356,8 +362,10 @@ static int smb2_validate_credit_charge(struct ksmbd_conn *conn,
ksmbd_debug(SMB, "Limits exceeding the maximum allowable outstanding requests, given : %u, pending : %u\n",
credit_charge, conn->outstanding_credits);
ret = 1;
} else
} else {
conn->outstanding_credits += credit_charge;
work->credit_charge = credit_charge;
}
spin_unlock(&conn->credits_lock);
@@ -460,7 +468,7 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
validate_credit:
if ((work->conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
smb2_validate_credit_charge(work->conn, hdr))
smb2_validate_credit_charge(work, hdr))
return 1;
return 0;

View File

@@ -363,6 +363,7 @@ int smb2_set_rsp_credits(struct ksmbd_work *work)
conn->total_credits -= credit_charge;
conn->outstanding_credits -= credit_charge;
work->credit_charge = 0;
credits_requested = max_t(unsigned short,
le16_to_cpu(req_hdr->CreditRequest), 1);
@@ -6920,16 +6921,18 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
}
case FILE_LINK_INFORMATION:
{
if (!(fp->daccess & FILE_DELETE_LE)) {
pr_err("no right to delete : 0x%x\n", fp->daccess);
return -EACCES;
}
struct smb2_file_link_info *file_info;
if (buf_len < sizeof(struct smb2_file_link_info))
return -EMSGSIZE;
return smb2_create_link(work, work->tcon->share_conf,
(struct smb2_file_link_info *)buffer,
file_info = (struct smb2_file_link_info *)buffer;
if (file_info->ReplaceIfExists && !(fp->daccess & FILE_DELETE_LE)) {
pr_err("no right to delete : 0x%x\n", fp->daccess);
return -EACCES;
}
return smb2_create_link(work, work->tcon->share_conf, file_info,
buf_len, fp->filp,
work->conn->local_nls);
}
@@ -7324,7 +7327,7 @@ int smb2_read(struct ksmbd_work *work)
ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n",
fp->filp, offset, length);
aux_payload_buf = kvzalloc(ALIGN(length, 8), KSMBD_DEFAULT_GFP);
aux_payload_buf = kvmalloc(ALIGN(length, 8), KSMBD_DEFAULT_GFP);
if (!aux_payload_buf) {
err = -ENOMEM;
goto out;

View File

@@ -374,6 +374,7 @@ static void parse_dacl(struct mnt_idmap *idmap,
{
int i, ret;
u16 num_aces = 0;
u16 dacl_size;
unsigned int acl_size;
char *acl_base;
struct smb_ace **ppace;
@@ -403,7 +404,11 @@ static void parse_dacl(struct mnt_idmap *idmap,
if (num_aces <= 0)
return;
if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) /
dacl_size = le16_to_cpu(pdacl->size);
if (dacl_size < sizeof(struct smb_acl))
return;
if (num_aces > (dacl_size - sizeof(struct smb_acl)) /
(offsetof(struct smb_ace, sid) +
offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
return;
@@ -740,12 +745,18 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,
if (nt_ace_size > aces_size)
break;
if (ntace->sid.num_subauth == 0 ||
ntace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES)
goto next_ace;
memcpy((char *)pndace + size, ntace, nt_ace_size);
if (check_add_overflow(size, nt_ace_size, &size))
break;
num_aces++;
next_ace:
aces_size -= nt_ace_size;
ntace = (struct smb_ace *)((char *)ntace + nt_ace_size);
num_aces++;
}
}

View File

@@ -1487,8 +1487,8 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
if (rc < 0)
pr_err("Failed to store XATTR ntacl :%d\n", rc);
kfree(sd_ndr.data);
out:
kfree(sd_ndr.data);
kfree(acl_ndr.data);
kfree(smb_acl);
kfree(def_smb_acl);
@@ -1504,7 +1504,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
struct ndr n;
struct inode *inode = d_inode(dentry);
struct ndr acl_ndr = {0};
struct xattr_ntacl acl;
struct xattr_ntacl acl = {0};
struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL;
__u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0};
@@ -1515,7 +1515,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
n.length = rc;
rc = ndr_decode_v4_ntacl(&n, &acl);
if (rc)
goto free_n_data;
goto out_free;
smb_acl = ksmbd_vfs_make_xattr_posix_acl(idmap, inode,
ACL_TYPE_ACCESS);
@@ -1541,6 +1541,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
*pntsd = acl.sd_buf;
if (acl.sd_size < sizeof(struct smb_ntsd)) {
pr_err("sd size is invalid\n");
rc = -EINVAL;
goto out_free;
}
@@ -1560,8 +1561,6 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
kfree(acl.sd_buf);
*pntsd = NULL;
}
free_n_data:
kfree(n.data);
return rc;
}
@@ -1576,14 +1575,15 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap,
err = ndr_encode_dos_attr(&n, da);
if (err)
return err;
goto out;
err = ksmbd_vfs_setxattr(idmap, path, XATTR_NAME_DOS_ATTRIBUTE,
(void *)n.data, n.offset, 0, get_write);
if (err)
ksmbd_debug(SMB, "failed to store dos attribute in xattr\n");
kfree(n.data);
out:
kfree(n.data);
return err;
}

View File

@@ -581,7 +581,20 @@ bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry)
if (fp->conn || !fp->is_durable ||
fp->f_state != FP_INITED)
continue;
list_move_tail(&fp->node, &dispose);
/*
* Claim the close before unlinking fp from m_fp_list.
* refcount == 1 means only the durable lifetime ref is
* left. Add a transient ref so final close can drop both.
*/
write_lock(&global_ft.lock);
if (atomic_read(&fp->refcount) == 1) {
atomic_inc(&fp->refcount);
__ksmbd_remove_durable_fd(fp);
ksmbd_mark_fp_closed(fp);
list_move_tail(&fp->node, &dispose);
}
write_unlock(&global_ft.lock);
}
}
up_write(&ci->m_lock);
@@ -589,16 +602,18 @@ bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry)
/*
* Drop our lookup reference before closing so the last __ksmbd_close_fd()
* can drop m_count to zero and unlink the delete-on-close file. The
* collected handles still hold references, so ci stays valid until they
* are closed below.
* collected handles still hold the transient reference taken above, so
* ci stays valid until they are closed below.
*/
ksmbd_inode_put(ci);
while (!list_empty(&dispose)) {
fp = list_first_entry(&dispose, struct ksmbd_file, node);
list_del_init(&fp->node);
__ksmbd_close_fd(NULL, fp);
closed = true;
if (atomic_sub_and_test(2, &fp->refcount)) {
__ksmbd_close_fd(NULL, fp);
closed = true;
}
}
return closed;
@@ -838,19 +853,24 @@ int ksmbd_close_fd_app_instance_id(char *app_instance_id)
return 0;
opinfo = opinfo_get(fp);
if (!opinfo || !opinfo->sess)
if (!opinfo)
goto out;
down_read(&fp->f_ci->m_lock);
if (!opinfo->conn) {
up_read(&fp->f_ci->m_lock);
goto out;
}
ft = &opinfo->sess->file_table;
write_lock(&ft->lock);
if (fp->f_state == FP_INITED) {
if (has_file_id(fp->volatile_id)) {
idr_remove(ft->idr, fp->volatile_id);
fp->volatile_id = KSMBD_NO_FID;
}
if (fp->f_state == FP_INITED && has_file_id(fp->volatile_id)) {
idr_remove(ft->idr, fp->volatile_id);
fp->volatile_id = KSMBD_NO_FID;
n_to_drop = ksmbd_mark_fp_closed(fp);
}
write_unlock(&ft->lock);
up_read(&fp->f_ci->m_lock);
opinfo_put(opinfo);
opinfo = NULL;
@@ -1461,16 +1481,21 @@ void ksmbd_stop_durable_scavenger(void)
static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp,
struct ksmbd_user *user)
{
char *name;
if (!user)
return -EINVAL;
/* Duplicate the user name to ensure identity persistence */
fp->owner.name = kstrdup(user->name, GFP_KERNEL);
if (!fp->owner.name)
name = kstrdup(user->name, GFP_KERNEL);
if (!name)
return -ENOMEM;
spin_lock(&fp->f_lock);
fp->owner.uid = user->uid;
fp->owner.gid = user->gid;
fp->owner.name = name;
spin_unlock(&fp->f_lock);
return 0;
}
@@ -1488,18 +1513,24 @@ static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp,
bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp,
struct ksmbd_user *user)
{
if (!user || !fp->owner.name)
bool ret = false;
if (!user)
return false;
spin_lock(&fp->f_lock);
if (!fp->owner.name)
goto out;
/* Check if the UID and GID match first (fast path) */
if (fp->owner.uid != user->uid || fp->owner.gid != user->gid)
return false;
goto out;
/* Validate the account name to ensure the same SecurityContext */
if (strcmp(fp->owner.name, user->name))
return false;
return true;
ret = (strcmp(fp->owner.name, user->name) == 0);
out:
spin_unlock(&fp->f_lock);
return ret;
}
static bool session_fd_check(struct ksmbd_tree_connect *tcon,
@@ -1530,11 +1561,13 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
conn = fp->conn;
ci = fp->f_ci;
down_write(&ci->m_lock);
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry,
lockdep_is_held(&ci->m_lock)) {
if (op->conn != conn)
continue;
ksmbd_conn_put(op->conn);
op->conn = NULL;
op->sess = NULL;
}
up_write(&ci->m_lock);
@@ -1685,16 +1718,20 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
ci = fp->f_ci;
down_write(&ci->m_lock);
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry,
lockdep_is_held(&ci->m_lock)) {
if (op->conn)
continue;
op->conn = ksmbd_conn_get(fp->conn);
op->sess = work->sess;
}
up_write(&ci->m_lock);
spin_lock(&fp->f_lock);
fp->owner.uid = fp->owner.gid = 0;
kfree(fp->owner.name);
fp->owner.name = NULL;
spin_unlock(&fp->f_lock);
return 0;
}