From: Namjae Jeon Date: Sun, 21 Jun 2026 10:49:05 +0000 (+0900) Subject: ksmbd: avoid level II oplock break notification on unlink X-Git-Tag: v7.2-rc1~23^2~13 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ec476c2580050ea050c562eeeb508519fc69ea21;p=thirdparty%2Flinux.git ksmbd: avoid level II oplock break notification on unlink smb2_util_unlink() opens the target with FILE_DELETE_ON_CLOSE and then closes that handle. Other clients can also mark a file for delete with SMB2 SET_INFO FileDispositionInformation. When these unlink paths break existing SMB2 level II oplocks, ksmbd sends an unsolicited SMB2_OPLOCK_BREAK notification to none. This races with the synchronous CREATE or SET_INFO response expected by the client, and smbtorture reports NT_STATUS_INVALID_NETWORK_RESPONSE while running smb2.oplock.exclusive2. SMB2 level II oplock breaks do not require an acknowledgment in the delete path. Keep lease handling unchanged, but drop plain SMB2 level II oplocks locally for unlink requests without sending a break notification. Normal write/truncate paths still send the level II to none notification, preserving the behavior covered by smb2.oplock.levelII500. 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 3f35ee7f7d005..5abeb90ebebb3 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -1546,7 +1546,7 @@ static bool smb_break_all_write_oplock(struct ksmbd_work *work, */ static void __smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, int is_trunc, - bool send_interim) + bool send_interim, bool send_oplock_break) { struct oplock_info *op, *brk_op; struct ksmbd_inode *ci; @@ -1593,10 +1593,15 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work, SMB2_LEASE_KEY_SIZE)) goto next; brk_op->open_trunc = is_trunc; - oplock_break(brk_op, - brk_op->is_lease && !is_trunc ? - SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE, - send_interim && !sent_interim ? work : NULL); + if (!brk_op->is_lease && !send_oplock_break) { + brk_op->level = SMB2_OPLOCK_LEVEL_NONE; + brk_op->op_state = OPLOCK_STATE_NONE; + } else { + oplock_break(brk_op, + brk_op->is_lease && !is_trunc ? + SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE, + send_interim && !sent_interim ? work : NULL); + } sent_interim = true; next: opinfo_put(brk_op); @@ -1610,7 +1615,19 @@ next: void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, int is_trunc) { - __smb_break_all_levII_oplock(work, fp, is_trunc, true); + __smb_break_all_levII_oplock(work, fp, is_trunc, true, true); +} + +void smb_break_all_levII_oplock_no_interim(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc) +{ + __smb_break_all_levII_oplock(work, fp, is_trunc, false, true); +} + +void smb_break_all_levII_oplock_for_delete(struct ksmbd_work *work, + struct ksmbd_file *fp) +{ + __smb_break_all_levII_oplock(work, fp, 0, false, false); } /** @@ -1627,7 +1644,7 @@ void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) return; sent_break = smb_break_all_write_oplock(work, fp, 1); - __smb_break_all_levII_oplock(work, fp, 1, !sent_break); + __smb_break_all_levII_oplock(work, fp, 1, !sent_break, true); } /** diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h index 8d19435862468..3f581d22bb676 100644 --- a/fs/smb/server/oplock.h +++ b/fs/smb/server/oplock.h @@ -99,6 +99,10 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, struct lease_ctx_info *lctx, int share_ret); void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, int is_trunc); +void smb_break_all_levII_oplock_no_interim(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc); +void smb_break_all_levII_oplock_for_delete(struct ksmbd_work *work, + struct ksmbd_file *fp); int opinfo_write_to_read(struct oplock_info *opinfo); int opinfo_read_handle_to_read(struct oplock_info *opinfo); int opinfo_write_to_none(struct oplock_info *opinfo); diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 6b84a8ea5b154..d4a40cede7bd0 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3809,7 +3809,7 @@ int smb2_open(struct ksmbd_work *work) } if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { - smb_break_all_levII_oplock(work, fp, 0); + smb_break_all_levII_oplock_for_delete(work, fp); ksmbd_fd_set_delete_on_close(fp, file_info); } @@ -6796,7 +6796,7 @@ static int set_file_disposition_info(struct ksmbd_work *work, if (S_ISDIR(inode->i_mode) && ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) return -EBUSY; - smb_break_all_levII_oplock(work, fp, 0); + smb_break_all_levII_oplock_for_delete(work, fp); ksmbd_set_inode_pending_delete(fp); } else { ksmbd_clear_inode_pending_delete(fp);