aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
authorNamjae Jeon <linkinjeon@kernel.org>2026-06-21 19:48:27 +0900
committerSteve French <stfrench@microsoft.com>2026-06-22 20:15:05 -0500
commit7db0da9915fdfd40156d01f20faac08f040fcfa3 (patch)
tree3787ef029988c41ef5f3f5e7ec9babb1687fc172 /fs
parentba3cf6ee4f0eacc1f8c607b80188e3b32ef5e0e3 (diff)
downloadath-7db0da9915fdfd40156d01f20faac08f040fcfa3.tar.gz
ksmbd: downgrade oplock after break timeout
smb2.oplock.batch22a opens a file with a batch oplock and then issues a second open that waits for the oplock break timeout. After the timeout the second open should succeed, but the granted oplock level must be level II. When the break times out, oplock_break() returns -ENOENT after invalidating the previous opener. smb_grant_oplock() went straight to set_lev with the original requested oplock level, so the second open could be granted a new batch oplock. Downgrade the requested oplock to level II on the -ENOENT break-timeout path before granting the oplock to the new open. A break that completes because the previous owner closed its handle from the oplock break handler must be distinguished from a real timeout. smb2.oplock.batch7 closes the first handle during the break wait, and the second open is then expected to be granted the originally requested batch oplock. Return -EAGAIN from the non-lease break path when the previous opener closed during the break wait, recheck sharing in smb_grant_oplock(), and grant the requested oplock if the close removed the conflict. Real break timeouts still return -ENOENT and keep the downgrade to level II. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/smb/server/oplock.c15
1 files changed, 13 insertions, 2 deletions
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index 0fddbaa5ba639..3f35ee7f7d005 100644
--- a/fs/smb/server/oplock.c
+++ b/fs/smb/server/oplock.c
@@ -1130,7 +1130,7 @@ again:
ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level);
if (brk_opinfo->op_state == OPLOCK_CLOSING)
- err = -ENOENT;
+ err = -EAGAIN;
wake_up_oplock_break(brk_opinfo);
return err;
@@ -1434,8 +1434,19 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
if (prev_durable_detached || (prev_durable_open && err == -ENOENT))
ksmbd_invalidate_durable_fd(prev_fid);
opinfo_put(prev_opinfo);
- if (err == -ENOENT)
+ if (err == -EAGAIN) {
+ share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp);
+ if (share_ret < 0) {
+ err = share_ret;
+ goto err_out;
+ }
goto set_lev;
+ }
+ if (err == -ENOENT) {
+ if (req_op_level != SMB2_OPLOCK_LEVEL_NONE)
+ req_op_level = SMB2_OPLOCK_LEVEL_II;
+ goto set_lev;
+ }
/* Check all oplock was freed by close */
else if (err < 0)
goto err_out;