]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ksmbd: honor stream delete sharing for base file
authorNamjae Jeon <linkinjeon@kernel.org>
Sun, 21 Jun 2026 10:44:09 +0000 (19:44 +0900)
committerSteve French <stfrench@microsoft.com>
Tue, 23 Jun 2026 01:15:05 +0000 (20:15 -0500)
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>
fs/smb/server/smb2pdu.c
fs/smb/server/vfs_cache.c
fs/smb/server/vfs_cache.h

index cc9cd929755785c69ff38cb299d04bf5193e6eb3..a3ae37e8b24dce43e258318c58b5fae7ff79331e 100644 (file)
@@ -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;
index b617edef950aa537aba844d990cd623b535ea0f3..11b51320b96e610dedbb2e0b20121fbfc6ffe3a4 100644 (file)
@@ -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)
 {
index 52a0e8b1f79f0a2141f6246c526804d0c91e55b2..8aa87843025f215564d4bedefff29ae24fbf0db3 100644 (file)
@@ -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);