aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
authorNamjae Jeon <linkinjeon@kernel.org>2026-06-21 19:44:09 +0900
committerSteve French <stfrench@microsoft.com>2026-06-22 20:15:05 -0500
commit19043971c947d307c9fc76e8b5e750ce7140b486 (patch)
treed088b4af30b76d671a314b76a3523e48b21cf02b /fs
parentaffcd98fddc57d81648d8ede8546d31e4a3f8450 (diff)
downloadath-19043971c947d307c9fc76e8b5e750ce7140b486.tar.gz
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 <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/smb/server/smb2pdu.c9
-rw-r--r--fs/smb/server/vfs_cache.c27
-rw-r--r--fs/smb/server/vfs_cache.h1
3 files changed, 37 insertions, 0 deletions
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);