Skip to content

Commit a3c751a

Browse files
author
Miklos Szeredi
committed
vfs: allow unprivileged whiteout creation
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]>
1 parent 0e698df commit a3c751a

File tree

4 files changed

+14
-19
lines changed

4 files changed

+14
-19
lines changed

fs/char_dev.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,9 @@ int cdev_add(struct cdev *p, dev_t dev, unsigned count)
483483
p->dev = dev;
484484
p->count = count;
485485

486+
if (WARN_ON(dev == WHITEOUT_DEV))
487+
return -EBUSY;
488+
486489
error = kobj_map(cdev_map, dev, count, NULL,
487490
exact_match, exact_lock, p);
488491
if (error)

fs/namei.c

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3505,12 +3505,14 @@ EXPORT_SYMBOL(user_path_create);
35053505

35063506
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
35073507
{
3508+
bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;
35083509
int error = may_create(dir, dentry);
35093510

35103511
if (error)
35113512
return error;
35123513

3513-
if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
3514+
if ((S_ISCHR(mode) || S_ISBLK(mode)) && !is_whiteout &&
3515+
!capable(CAP_MKNOD))
35143516
return -EPERM;
35153517

35163518
if (!dir->i_op->mknod)
@@ -4345,9 +4347,6 @@ static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
43454347
(flags & RENAME_EXCHANGE))
43464348
return -EINVAL;
43474349

4348-
if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD))
4349-
return -EPERM;
4350-
43514350
if (flags & RENAME_EXCHANGE)
43524351
target_flags = 0;
43534352

@@ -4483,20 +4482,6 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
44834482
return do_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
44844483
}
44854484

4486-
int vfs_whiteout(struct inode *dir, struct dentry *dentry)
4487-
{
4488-
int error = may_create(dir, dentry);
4489-
if (error)
4490-
return error;
4491-
4492-
if (!dir->i_op->mknod)
4493-
return -EPERM;
4494-
4495-
return dir->i_op->mknod(dir, dentry,
4496-
S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
4497-
}
4498-
EXPORT_SYMBOL(vfs_whiteout);
4499-
45004485
int readlink_copy(char __user *buffer, int buflen, const char *link)
45014486
{
45024487
int len = PTR_ERR(link);

include/linux/device_cgroup.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ static inline int devcgroup_inode_mknod(int mode, dev_t dev)
5151
if (!S_ISBLK(mode) && !S_ISCHR(mode))
5252
return 0;
5353

54+
if (S_ISCHR(mode) && dev == WHITEOUT_DEV)
55+
return 0;
56+
5457
if (S_ISBLK(mode))
5558
type = DEVCG_DEV_BLOCK;
5659
else

include/linux/fs.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1721,7 +1721,11 @@ extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct ino
17211721
extern int vfs_rmdir(struct inode *, struct dentry *);
17221722
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
17231723
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
1724-
extern int vfs_whiteout(struct inode *, struct dentry *);
1724+
1725+
static inline int vfs_whiteout(struct inode *dir, struct dentry *dentry)
1726+
{
1727+
return vfs_mknod(dir, dentry, S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
1728+
}
17251729

17261730
extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode,
17271731
int open_flag);

0 commit comments

Comments
 (0)