Skip to content

Commit

Permalink
vfs: allow unprivileged whiteout creation
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Miklos Szeredi committed May 14, 2020
1 parent 0e698df commit a3c751a
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 19 deletions.
3 changes: 3 additions & 0 deletions fs/char_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
21 changes: 3 additions & 18 deletions fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions include/linux/device_cgroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit a3c751a

Please sign in to comment.