ksmbd: route stream FileDispositionInformation through stream delete flag

set_file_disposition_info() calls ksmbd_set_inode_pending_delete() /
ksmbd_clear_inode_pending_delete() unconditionally, which always sets
S_DEL_PENDING on the whole inode (ci->m_flags), regardless of whether
the handle being closed is a regular file or an alternate data stream.

Requesting delete-pending on a single stream handle (e.g. deleting
just an alternate data stream some clients keep alongside a file)
would therefore incorrectly schedule deletion of the entire file's
data, not just the stream.

Add ksmbd_fd_set_delete_pending()/ksmbd_fd_clear_delete_pending(),
following the same stream-vs-whole-file routing pattern already used
by ksmbd_fd_set_delete_on_close() for the CREATE-time DeleteOnClose
option, and switch set_file_disposition_info() to use them.

Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Gael Blivet 2026-07-07 11:55:02 +02:00 committed by Namjae Jeon
parent d68d4b3293
commit 495ade881b
3 changed files with 36 additions and 2 deletions

View File

@ -7123,9 +7123,9 @@ static int set_file_disposition_info(struct ksmbd_work *work,
ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY)
return -EBUSY;
smb_break_all_levII_oplock_for_delete(work, fp);
ksmbd_set_inode_pending_delete(fp);
ksmbd_fd_set_delete_pending(fp);
} else {
ksmbd_clear_inode_pending_delete(fp);
ksmbd_fd_clear_delete_pending(fp);
}
return 0;
}

View File

@ -294,6 +294,38 @@ void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
up_write(&ci->m_lock);
}
/*
* FileDispositionInformation (SET_INFO) on a stream handle must only
* mark the stream for deletion, not the whole file -- otherwise
* deleting a single alternate data stream (e.g. AFP_AfpInfo) deletes
* the entire file's data along with it.
*/
void ksmbd_fd_set_delete_pending(struct ksmbd_file *fp)
{
struct ksmbd_inode *ci = fp->f_ci;
if (ksmbd_stream_fd(fp)) {
down_write(&ci->m_lock);
ci->m_flags |= S_DEL_ON_CLS_STREAM;
up_write(&ci->m_lock);
} else {
ksmbd_set_inode_pending_delete(fp);
}
}
void ksmbd_fd_clear_delete_pending(struct ksmbd_file *fp)
{
struct ksmbd_inode *ci = fp->f_ci;
if (ksmbd_stream_fd(fp)) {
down_write(&ci->m_lock);
ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
up_write(&ci->m_lock);
} else {
ksmbd_clear_inode_pending_delete(fp);
}
}
static void ksmbd_inode_hash(struct ksmbd_inode *ci)
{
struct hlist_head *b = inode_hashtable +

View File

@ -214,6 +214,8 @@ void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp);
void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp);
void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
int file_info);
void ksmbd_fd_set_delete_pending(struct ksmbd_file *fp);
void ksmbd_fd_clear_delete_pending(struct ksmbd_file *fp);
int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp);
int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share,
struct ksmbd_file *fp, char *name);