mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-24 04:30:35 -04:00
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 <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
committed by
Steve French
parent
7db0da9915
commit
ec476c2580
@@ -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 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work,
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user