From: Namjae Jeon Date: Sun, 21 Jun 2026 10:48:27 +0000 (+0900) Subject: ksmbd: downgrade oplock after break timeout X-Git-Tag: v7.2-rc1~23^2~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7db0da9915fdfd40156d01f20faac08f040fcfa3;p=thirdparty%2Fkernel%2Flinux.git 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 Signed-off-by: Steve French --- 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;