ksmbd: deny renaming directory with open children

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>
This commit is contained in:
Namjae Jeon 2026-06-21 19:37:56 +09:00 committed by Steve French
parent 9a5784f4d5
commit c841bd3d8d
3 changed files with 32 additions and 0 deletions

View File

@ -700,6 +700,12 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
if (err) if (err)
goto out_drop_write; 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); parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent);
if (parent_fp) { if (parent_fp) {
if ((parent_fp->daccess & FILE_DELETE_LE) || if ((parent_fp->daccess & FILE_DELETE_LE) ||

View File

@ -10,6 +10,7 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/dcache.h>
#include "glob.h" #include "glob.h"
#include "vfs_cache.h" #include "vfs_cache.h"
@ -914,6 +915,30 @@ struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry)
return NULL; 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_VOLATILE_ID (0)
#define OPEN_ID_TYPE_PERSISTENT_ID (1) #define OPEN_ID_TYPE_PERSISTENT_ID (1)

View 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); 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_cguid(char *cguid);
struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); 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); unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp);
struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp);
void ksmbd_launch_ksmbd_durable_scavenger(void); void ksmbd_launch_ksmbd_durable_scavenger(void);