[RFC PATCH 9/9] ksmbd: avoid self oplock breaks for EA opens
Ze Tan <[email protected]> Wed, 15 Jul 2026 15:48:50 +0800
| Newsgroups | gmane.linux.kernel,gmane.linux.kernel.cifs,gmane.network.samba.internals |
|---|---|
| Message-ID | <[email protected]> |
generic/093 writes to a file after setcap sets security.capability. The CIFS client can reopen the file to query or update EAs while the same session holds an oplock. ksmbd breaks this oplock and waits for the same client, so the write can block. Skip the oplock break for same-session EA or metadata opens. Keep the normal break when another session holds an oplock. Signed-off-by: Ze Tan <[email protected]> --- fs/smb/server/oplock.c | 49 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 74bd2fadc757..bfd813969511 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -1436,6 +1436,49 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) ksmbd_inode_put(p_ci); } +static bool ksmbd_skip_session_ea_break(struct ksmbd_work *work, + struct ksmbd_file *fp, + int req_op_level, + struct lease_ctx_info *lctx) +{ + struct oplock_info *opinfo; + bool same_session = false; + bool foreign_session = false; + __le32 ea_meta_mask = FILE_READ_EA_LE | FILE_WRITE_EA_LE | + FILE_READ_ATTRIBUTES_LE | + FILE_WRITE_ATTRIBUTES_LE | + FILE_READ_CONTROL_LE | FILE_SYNCHRONIZE_LE; + + if (!work || !fp || lctx || req_op_level != SMB2_OPLOCK_LEVEL_NONE) + return false; + + /* + * Avoid self-deadlock when the same session reopens the file only + * to read/write EAs or metadata, e.g. CIFS killpriv querying or + * removing security.capability during buffered write. + */ + if (!(fp->daccess & (FILE_READ_EA_LE | FILE_WRITE_EA_LE)) || + (fp->daccess & ~ea_meta_mask)) + return false; + + down_read(&fp->f_ci->m_lock); + list_for_each_entry(opinfo, &fp->f_ci->m_op_list, op_entry) { + if (!opinfo->conn || opinfo->level == SMB2_OPLOCK_LEVEL_NONE) + continue; + + if (opinfo->sess == work->sess) + same_session = true; + else + foreign_session = true; + + if (same_session && foreign_session) + break; + } + up_read(&fp->f_ci->m_lock); + + return same_session && !foreign_session; +} + /** * smb_grant_oplock() - handle oplock/lease request on file open * @work: smb work @@ -1542,6 +1585,12 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, goto err_out; } + if (share_ret >= 0 && + ksmbd_skip_session_ea_break(work, fp, req_op_level, lctx)) { + opinfo_put(prev_opinfo); + goto set_lev; + } + break_needed = prev_opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || (share_ret < 0 && prev_op_has_lease && -- 2.43.0