From: Namjae Jeon Date: Sun, 21 Jun 2026 10:44:09 +0000 (+0900) Subject: ksmbd: honor stream delete sharing for base file X-Git-Tag: v7.2-rc1~23^2~19 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=19043971c947d307c9fc76e8b5e750ce7140b486;p=thirdparty%2Flinux.git ksmbd: honor stream delete sharing for base file smb2.streams.delete opens an alternate data stream without FILE_SHARE_DELETE and then tries to delete the base file. Windows rejects the base-file delete with STATUS_SHARING_VIOLATION while the stream handle is open. ksmbd tracks stream opens on the same ksmbd_inode as the base file, but the delete-on-close path only checked delete access on the base handle before marking the inode delete-pending. As a result, deleting the base file succeeded even though an open stream handle denied delete sharing. Add a helper to detect open stream handles on the same inode that do not allow FILE_SHARE_DELETE, and reject base-file delete pending and DELETE opens with a sharing violation in that case. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index cc9cd92975578..a3ae37e8b24dc 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3737,6 +3737,12 @@ int smb2_open(struct ksmbd_work *work) goto err_out; } + if (!stream_name && daccess & FILE_DELETE_LE && + ksmbd_has_stream_without_delete_share(fp)) { + rc = -EPERM; + goto err_out; + } + if (file_present || created) path_put(&path); @@ -6758,6 +6764,9 @@ static int set_file_disposition_info(struct ksmbd_work *work, inode = file_inode(fp->filp); if (file_info->DeletePending) { + if (ksmbd_has_stream_without_delete_share(fp)) + return -ESHARE; + if (S_ISDIR(inode->i_mode) && ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) return -EBUSY; diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index b617edef950aa..11b51320b96e6 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -254,6 +254,33 @@ void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) up_write(&ci->m_lock); } +bool ksmbd_has_stream_without_delete_share(struct ksmbd_file *fp) +{ + struct ksmbd_file *prev_fp; + struct ksmbd_inode *ci = fp->f_ci; + bool ret = false; + + if (ksmbd_stream_fd(fp)) + return false; + + down_read(&ci->m_lock); + list_for_each_entry(prev_fp, &ci->m_fp_list, node) { + if (prev_fp == fp || !ksmbd_stream_fd(prev_fp)) + continue; + + if (file_inode(fp->filp) != file_inode(prev_fp->filp)) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE)) { + ret = true; + break; + } + } + up_read(&ci->m_lock); + + return ret; +} + void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, int file_info) { diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 52a0e8b1f79f0..8aa87843025f2 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -169,6 +169,7 @@ struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); void ksmbd_put_durable_fd(struct ksmbd_file *fp); int ksmbd_invalidate_durable_fd(unsigned long long id); bool ksmbd_has_other_active_fd(struct ksmbd_file *fp); +bool ksmbd_has_stream_without_delete_share(struct ksmbd_file *fp); int ksmbd_close_fd_app_instance_id(char *app_instance_id); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);