]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ksmbd: deny renaming directory with open children
authorNamjae Jeon <linkinjeon@kernel.org>
Sun, 21 Jun 2026 10:37:56 +0000 (19:37 +0900)
committerSteve French <stfrench@microsoft.com>
Tue, 23 Jun 2026 01:15:05 +0000 (20:15 -0500)
Windows denies renaming a directory while a file below that directory is
still open. smb2.rename.rename_dir_openfile checks this by keeping a file
handle open under the directory and then attempting to rename the directory
handle.  ksmbd did not check open children before calling vfs_rename(), so
the rename incorrectly succeeded.

For non-POSIX clients, scan the global open file table for active handles
whose dentries are below the directory being renamed.  If any child is
open, fail the rename with -EACCES so the client receives
STATUS_ACCESS_DENIED.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/server/vfs.c
fs/smb/server/vfs_cache.c
fs/smb/server/vfs_cache.h

index 80fd27752b2c0a28729b08a54ef6ac8cc54df86d..f5fa22d8760361074781d614afc60348a28fc9ee 100644 (file)
@@ -700,6 +700,12 @@ retry:
        if (err)
                goto out_drop_write;
 
+       if (!work->tcon->posix_extensions && d_is_dir(old_child) &&
+           ksmbd_has_open_files(old_child)) {
+               err = -EACCES;
+               goto out3;
+       }
+
        parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent);
        if (parent_fp) {
                if ((parent_fp->daccess & FILE_DELETE_LE) ||
index 5a2fddadcddfe2e924a7d301b66ea2e0be9c1c75..98c5bac93d634e8d281ae5977673b993c0ccf0a9 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/vmalloc.h>
 #include <linux/kthread.h>
 #include <linux/freezer.h>
+#include <linux/dcache.h>
 
 #include "glob.h"
 #include "vfs_cache.h"
@@ -914,6 +915,30 @@ struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry)
        return NULL;
 }
 
+bool ksmbd_has_open_files(struct dentry *dentry)
+{
+       struct ksmbd_file *fp;
+       unsigned int id;
+       bool ret = false;
+
+       read_lock(&global_ft.lock);
+       idr_for_each_entry(global_ft.idr, fp, id) {
+               struct dentry *fp_dentry = fp->filp->f_path.dentry;
+
+               if (fp->f_state != FP_INITED)
+                       continue;
+               if (fp_dentry == dentry)
+                       continue;
+               if (is_subdir(fp_dentry, dentry)) {
+                       ret = true;
+                       break;
+               }
+       }
+       read_unlock(&global_ft.lock);
+
+       return ret;
+}
+
 #define OPEN_ID_TYPE_VOLATILE_ID       (0)
 #define OPEN_ID_TYPE_PERSISTENT_ID     (1)
 
index 8c456484cab26906752c2e463039ff40bdd885ef..52a0e8b1f79f0a2141f6246c526804d0c91e55b2 100644 (file)
@@ -172,6 +172,7 @@ bool ksmbd_has_other_active_fd(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);
+bool ksmbd_has_open_files(struct dentry *dentry);
 unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp);
 struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp);
 void ksmbd_launch_ksmbd_durable_scavenger(void);