diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index c690c7f0eb08..0d63752bb158 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -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; } diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index a141025581af..b53912ce5e2a 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -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 + diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index b9e27307a26c..111a4e315499 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -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);