mirror of
https://github.com/torvalds/linux
synced 2024-09-06 18:01:05 +00:00
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 <mszeredi@redhat.com>
This commit is contained in:
parent
0e698dfa28
commit
a3c751a50f
|
@ -483,6 +483,9 @@ int cdev_add(struct cdev *p, dev_t dev, unsigned count)
|
||||||
p->dev = dev;
|
p->dev = dev;
|
||||||
p->count = count;
|
p->count = count;
|
||||||
|
|
||||||
|
if (WARN_ON(dev == WHITEOUT_DEV))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
error = kobj_map(cdev_map, dev, count, NULL,
|
error = kobj_map(cdev_map, dev, count, NULL,
|
||||||
exact_match, exact_lock, p);
|
exact_match, exact_lock, p);
|
||||||
if (error)
|
if (error)
|
||||||
|
|
21
fs/namei.c
21
fs/namei.c
|
@ -3505,12 +3505,14 @@ EXPORT_SYMBOL(user_path_create);
|
||||||
|
|
||||||
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
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);
|
int error = may_create(dir, dentry);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return 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;
|
return -EPERM;
|
||||||
|
|
||||||
if (!dir->i_op->mknod)
|
if (!dir->i_op->mknod)
|
||||||
|
@ -4345,9 +4347,6 @@ static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
|
||||||
(flags & RENAME_EXCHANGE))
|
(flags & RENAME_EXCHANGE))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD))
|
|
||||||
return -EPERM;
|
|
||||||
|
|
||||||
if (flags & RENAME_EXCHANGE)
|
if (flags & RENAME_EXCHANGE)
|
||||||
target_flags = 0;
|
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);
|
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 readlink_copy(char __user *buffer, int buflen, const char *link)
|
||||||
{
|
{
|
||||||
int len = PTR_ERR(link);
|
int len = PTR_ERR(link);
|
||||||
|
|
|
@ -51,6 +51,9 @@ static inline int devcgroup_inode_mknod(int mode, dev_t dev)
|
||||||
if (!S_ISBLK(mode) && !S_ISCHR(mode))
|
if (!S_ISBLK(mode) && !S_ISCHR(mode))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (S_ISCHR(mode) && dev == WHITEOUT_DEV)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (S_ISBLK(mode))
|
if (S_ISBLK(mode))
|
||||||
type = DEVCG_DEV_BLOCK;
|
type = DEVCG_DEV_BLOCK;
|
||||||
else
|
else
|
||||||
|
|
|
@ -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_rmdir(struct inode *, struct dentry *);
|
||||||
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
|
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_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,
|
extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode,
|
||||||
int open_flag);
|
int open_flag);
|
||||||
|
|
Loading…
Reference in a new issue