]> git.ipfire.org Git - people/ms/linux.git/commitdiff
vfs: allow unprivileged whiteout creation
authorMiklos Szeredi <mszeredi@redhat.com>
Thu, 14 May 2020 14:44:23 +0000 (16:44 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 14 May 2020 14:44:23 +0000 (16:44 +0200)
Whiteouts, unlike real device node should not require privileges to create.

The general concern with device nodes is that opening them can have side
effects.  The kernel already avoids zero major (see
Documentation/admin-guide/devices.txt).  To be on the safe side the patch
explicitly forbids registering a char device with 0/0 number (see
cdev_add()).

This guarantees that a non-O_PATH open on a whiteout will fail with ENODEV;
i.e. it won't have any side effect.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/char_dev.c
fs/namei.c
include/linux/device_cgroup.h
include/linux/fs.h

index c5e6eff5a38164e2dda4b20e1918c287233be1b3..ba0ded7842a7790769951357ba713146c2455640 100644 (file)
@@ -483,6 +483,9 @@ int cdev_add(struct cdev *p, dev_t dev, unsigned count)
        p->dev = dev;
        p->count = count;
 
+       if (WARN_ON(dev == WHITEOUT_DEV))
+               return -EBUSY;
+
        error = kobj_map(cdev_map, dev, count, NULL,
                         exact_match, exact_lock, p);
        if (error)
index a320371899cf9a9fab82d0f3de7272f03414f820..d81f73ff1a8ba45e615c22ec4b31b4558b3231a5 100644 (file)
@@ -3505,12 +3505,14 @@ EXPORT_SYMBOL(user_path_create);
 
 int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
 {
+       bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;
        int error = may_create(dir, dentry);
 
        if (error)
                return error;
 
-       if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
+       if ((S_ISCHR(mode) || S_ISBLK(mode)) && !is_whiteout &&
+           !capable(CAP_MKNOD))
                return -EPERM;
 
        if (!dir->i_op->mknod)
@@ -4345,9 +4347,6 @@ static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
            (flags & RENAME_EXCHANGE))
                return -EINVAL;
 
-       if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD))
-               return -EPERM;
-
        if (flags & RENAME_EXCHANGE)
                target_flags = 0;
 
@@ -4483,20 +4482,6 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
        return do_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 }
 
-int vfs_whiteout(struct inode *dir, struct dentry *dentry)
-{
-       int error = may_create(dir, dentry);
-       if (error)
-               return error;
-
-       if (!dir->i_op->mknod)
-               return -EPERM;
-
-       return dir->i_op->mknod(dir, dentry,
-                               S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
-}
-EXPORT_SYMBOL(vfs_whiteout);
-
 int readlink_copy(char __user *buffer, int buflen, const char *link)
 {
        int len = PTR_ERR(link);
index fa35b52e0002e03a4a2476f2f854daade6e6742d..57e63bd63370bbacb835adce8a64836463ad0967 100644 (file)
@@ -51,6 +51,9 @@ static inline int devcgroup_inode_mknod(int mode, dev_t dev)
        if (!S_ISBLK(mode) && !S_ISCHR(mode))
                return 0;
 
+       if (S_ISCHR(mode) && dev == WHITEOUT_DEV)
+               return 0;
+
        if (S_ISBLK(mode))
                type = DEVCG_DEV_BLOCK;
        else
index 4f6f59b4f22a807e55e479468df1f1cb7068cfdc..15665ef1ef905dc5e18cf580086b6e5092304311 100644 (file)
@@ -1721,7 +1721,11 @@ extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct ino
 extern int vfs_rmdir(struct inode *, struct dentry *);
 extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
 extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
-extern int vfs_whiteout(struct inode *, struct dentry *);
+
+static inline int vfs_whiteout(struct inode *dir, struct dentry *dentry)
+{
+       return vfs_mknod(dir, dentry, S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
+}
 
 extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode,
                                  int open_flag);