mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-24 12:42:16 -04:00
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>
This commit is contained in:
committed by
Steve French
parent
ba3cf6ee4f
commit
7db0da9915
@@ -1130,7 +1130,7 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level,
|
||||
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user