summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 13:05:35 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 13:05:35 -0800
commit6124fa45e2d919eeb9fc2d6675f5824b44e344b0 (patch)
treee5aee7c2e224056de2c59ffeb9f1154a819aca95
parentdd466ea0029961ee0ee6e8e468faa1506275c8a9 (diff)
parentf97f020075e83d05695d3f86469d50e21eccffab (diff)
Merge tag 'vfs-7.0-rc1.btrfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull vfs updates for btrfs from Christian Brauner: "This contains some changes for btrfs that are taken to the vfs tree to stop duplicating VFS code for subvolume/snapshot dentry Btrfs has carried private copies of the VFS may_delete() and may_create() functions in fs/btrfs/ioctl.c for permission checks during subvolume creation and snapshot destruction. These copies have drifted out of sync with the VFS originals — btrfs_may_delete() is missing the uid/gid validity check and btrfs_may_create() is missing the audit_inode_child() call. Export the VFS functions as may_{create,delete}_dentry() and switch btrfs to use them, removing ~70 lines of duplicated code" * tag 'vfs-7.0-rc1.btrfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: btrfs: use may_create_dentry() in btrfs_mksubvol() btrfs: use may_delete_dentry() in btrfs_ioctl_snap_destroy() fs: export may_create() as may_create_dentry() fs: export may_delete() as may_delete_dentry()
-rw-r--r--fs/btrfs/ioctl.c73
-rw-r--r--fs/namei.c36
-rw-r--r--include/linux/fs.h5
3 files changed, 26 insertions, 88 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index acb484546b1d..d1ab03691606 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -815,75 +815,6 @@ free_pending:
return ret;
}
-/* copy of may_delete in fs/namei.c()
- * Check whether we can remove a link victim from directory dir, check
- * whether the type of victim is right.
- * 1. We can't do it if dir is read-only (done in permission())
- * 2. We should have write and exec permissions on dir
- * 3. We can't remove anything from append-only dir
- * 4. We can't do anything with immutable dir (done in permission())
- * 5. If the sticky bit on dir is set we should either
- * a. be owner of dir, or
- * b. be owner of victim, or
- * c. have CAP_FOWNER capability
- * 6. If the victim is append-only or immutable we can't do anything with
- * links pointing to it.
- * 7. If we were asked to remove a directory and victim isn't one - ENOTDIR.
- * 8. If we were asked to remove a non-directory and victim isn't one - EISDIR.
- * 9. We can't remove a root or mountpoint.
- * 10. We don't allow removal of NFS sillyrenamed files; it's handled by
- * nfs_async_unlink().
- */
-
-static int btrfs_may_delete(struct mnt_idmap *idmap,
- struct inode *dir, struct dentry *victim, int isdir)
-{
- int ret;
-
- if (d_really_is_negative(victim))
- return -ENOENT;
-
- /* The @victim is not inside @dir. */
- if (d_inode(victim->d_parent) != dir)
- return -EINVAL;
- audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
-
- ret = inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
- if (ret)
- return ret;
- if (IS_APPEND(dir))
- return -EPERM;
- if (check_sticky(idmap, dir, d_inode(victim)) ||
- IS_APPEND(d_inode(victim)) || IS_IMMUTABLE(d_inode(victim)) ||
- IS_SWAPFILE(d_inode(victim)))
- return -EPERM;
- if (isdir) {
- if (!d_is_dir(victim))
- return -ENOTDIR;
- if (IS_ROOT(victim))
- return -EBUSY;
- } else if (d_is_dir(victim))
- return -EISDIR;
- if (IS_DEADDIR(dir))
- return -ENOENT;
- if (victim->d_flags & DCACHE_NFSFS_RENAMED)
- return -EBUSY;
- return 0;
-}
-
-/* copy of may_create in fs/namei.c() */
-static inline int btrfs_may_create(struct mnt_idmap *idmap,
- struct inode *dir, const struct dentry *child)
-{
- if (d_really_is_positive(child))
- return -EEXIST;
- if (IS_DEADDIR(dir))
- return -ENOENT;
- if (!fsuidgid_has_mapping(dir->i_sb, idmap))
- return -EOVERFLOW;
- return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
-}
-
/*
* Create a new subvolume below @parent. This is largely modeled after
* sys_mkdirat and vfs_mkdir, but we only do a single component lookup
@@ -905,7 +836,7 @@ static noinline int btrfs_mksubvol(struct dentry *parent,
if (IS_ERR(dentry))
return PTR_ERR(dentry);
- ret = btrfs_may_create(idmap, dir, dentry);
+ ret = may_create_dentry(idmap, dir, dentry);
if (ret)
goto out_dput;
@@ -2420,7 +2351,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
}
/* check if subvolume may be deleted by a user */
- ret = btrfs_may_delete(idmap, dir, dentry, 1);
+ ret = may_delete_dentry(idmap, dir, dentry, true);
if (ret)
goto out_end_removing;
diff --git a/fs/namei.c b/fs/namei.c
index cf16b6822dd3..3e5884f46faf 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3613,7 +3613,7 @@ EXPORT_SYMBOL(__check_sticky);
* 11. We don't allow removal of NFS sillyrenamed files; it's handled by
* nfs_async_unlink().
*/
-static int may_delete(struct mnt_idmap *idmap, struct inode *dir,
+int may_delete_dentry(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *victim, bool isdir)
{
struct inode *inode = d_backing_inode(victim);
@@ -3655,6 +3655,7 @@ static int may_delete(struct mnt_idmap *idmap, struct inode *dir,
return -EBUSY;
return 0;
}
+EXPORT_SYMBOL(may_delete_dentry);
/* Check whether we can create an object with dentry child in directory
* dir.
@@ -3665,8 +3666,8 @@ static int may_delete(struct mnt_idmap *idmap, struct inode *dir,
* 4. We should have write and exec permissions on dir
* 5. We can't do it if dir is immutable (done in permission())
*/
-static inline int may_create(struct mnt_idmap *idmap,
- struct inode *dir, struct dentry *child)
+int may_create_dentry(struct mnt_idmap *idmap,
+ struct inode *dir, struct dentry *child)
{
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
if (child->d_inode)
@@ -3678,6 +3679,7 @@ static inline int may_create(struct mnt_idmap *idmap,
return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
}
+EXPORT_SYMBOL(may_create_dentry);
// p1 != p2, both are on the same filesystem, ->s_vfs_rename_mutex is held
static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2)
@@ -4124,7 +4126,7 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode,
struct inode *dir = d_inode(dentry->d_parent);
int error;
- error = may_create(idmap, dir, dentry);
+ error = may_create_dentry(idmap, dir, dentry);
if (error)
return error;
@@ -4150,7 +4152,7 @@ int vfs_mkobj(struct dentry *dentry, umode_t mode,
void *arg)
{
struct inode *dir = dentry->d_parent->d_inode;
- int error = may_create(&nop_mnt_idmap, dir, dentry);
+ int error = may_create_dentry(&nop_mnt_idmap, dir, dentry);
if (error)
return error;
@@ -4969,7 +4971,7 @@ int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
struct delegated_inode *delegated_inode)
{
bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;
- int error = may_create(idmap, dir, dentry);
+ int error = may_create_dentry(idmap, dir, dentry);
if (error)
return error;
@@ -5115,7 +5117,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
unsigned max_links = dir->i_sb->s_max_links;
struct dentry *de;
- error = may_create(idmap, dir, dentry);
+ error = may_create_dentry(idmap, dir, dentry);
if (error)
goto err;
@@ -5218,7 +5220,7 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, struct delegated_inode *delegated_inode)
{
- int error = may_delete(idmap, dir, dentry, 1);
+ int error = may_delete_dentry(idmap, dir, dentry, true);
if (error)
return error;
@@ -5353,7 +5355,7 @@ int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, struct delegated_inode *delegated_inode)
{
struct inode *target = dentry->d_inode;
- int error = may_delete(idmap, dir, dentry, 0);
+ int error = may_delete_dentry(idmap, dir, dentry, false);
if (error)
return error;
@@ -5505,7 +5507,7 @@ int vfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
{
int error;
- error = may_create(idmap, dir, dentry);
+ error = may_create_dentry(idmap, dir, dentry);
if (error)
return error;
@@ -5613,7 +5615,7 @@ int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
if (!inode)
return -ENOENT;
- error = may_create(idmap, dir, new_dentry);
+ error = may_create_dentry(idmap, dir, new_dentry);
if (error)
return error;
@@ -5825,21 +5827,21 @@ int vfs_rename(struct renamedata *rd)
if (source == target)
return 0;
- error = may_delete(rd->mnt_idmap, old_dir, old_dentry, is_dir);
+ error = may_delete_dentry(rd->mnt_idmap, old_dir, old_dentry, is_dir);
if (error)
return error;
if (!target) {
- error = may_create(rd->mnt_idmap, new_dir, new_dentry);
+ error = may_create_dentry(rd->mnt_idmap, new_dir, new_dentry);
} else {
new_is_dir = d_is_dir(new_dentry);
if (!(flags & RENAME_EXCHANGE))
- error = may_delete(rd->mnt_idmap, new_dir,
- new_dentry, is_dir);
+ error = may_delete_dentry(rd->mnt_idmap, new_dir,
+ new_dentry, is_dir);
else
- error = may_delete(rd->mnt_idmap, new_dir,
- new_dentry, new_is_dir);
+ error = may_delete_dentry(rd->mnt_idmap, new_dir,
+ new_dentry, new_is_dir);
}
if (error)
return error;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 4fb48f7b31f8..a3ff30bccf08 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2667,6 +2667,11 @@ static inline int path_permission(const struct path *path, int mask)
int __check_sticky(struct mnt_idmap *idmap, struct inode *dir,
struct inode *inode);
+int may_delete_dentry(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *victim, bool isdir);
+int may_create_dentry(struct mnt_idmap *idmap,
+ struct inode *dir, struct dentry *child);
+
static inline bool execute_ok(struct inode *inode)
{
return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode);