Commit bae9a194 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Christoph Hellwig

[PATCH] stale bdev reference in quotactl

sys_quotacl tries to do a get_super on a struct block_device * to which
it doesn't hold a reference (nor does it actually have to be non-NULL).

As lookup bdev by name is a rather common operation I splitted out a new
helper, lookup_bdev() that does this out of open_bdev_excl and switched
quota.c to use it.  lookup_bdev() holds a proper reference that needs
to be dropped by bdput(), and it's well documented.
parent 298d8a04
......@@ -799,24 +799,20 @@ const char *__bdevname(dev_t dev)
}
/**
* open_bdev_excl - open a block device by name and set it up for use
* lookup_bdev - lookup a struct block_device by name
*
* @path: special file representing the block device
* @flags: %MS_RDONLY for opening read-only
* @kind: usage (same as the 4th paramter to blkdev_get)
* @holder: owner for exclusion
*
* Open the blockdevice described by the special file at @path, claim it
* for the @holder and properly set it up for @kind usage.
* Get a reference to the blockdevice at @path in the current
* namespace if possible and return it. Return ERR_PTR(error)
* otherwise.
*/
struct block_device *open_bdev_excl(const char *path, int flags,
int kind, void *holder)
struct block_device *lookup_bdev(const char *path)
{
struct inode *inode;
struct block_device *bdev;
struct inode *inode;
struct nameidata nd;
mode_t mode = FMODE_READ;
int error = 0;
int error;
if (!path || !*path)
return ERR_PTR(-EINVAL);
......@@ -828,17 +824,44 @@ struct block_device *open_bdev_excl(const char *path, int flags,
inode = nd.dentry->d_inode;
error = -ENOTBLK;
if (!S_ISBLK(inode->i_mode))
goto path_release;
goto fail;
error = -EACCES;
if (nd.mnt->mnt_flags & MNT_NODEV)
goto path_release;
goto fail;
error = bd_acquire(inode);
if (error)
goto path_release;
goto fail;
bdev = inode->i_bdev;
/* Done with lookups */
out:
path_release(&nd);
return bdev;
fail:
bdev = ERR_PTR(error);
goto out;
}
/**
* open_bdev_excl - open a block device by name and set it up for use
*
* @path: special file representing the block device
* @flags: %MS_RDONLY for opening read-only
* @kind: usage (same as the 4th paramter to blkdev_get)
* @holder: owner for exclusion
*
* Open the blockdevice described by the special file at @path, claim it
* for the @holder and properly set it up for @kind usage.
*/
struct block_device *open_bdev_excl(const char *path, int flags,
int kind, void *holder)
{
struct block_device *bdev;
mode_t mode = FMODE_READ;
int error = 0;
bdev = lookup_bdev(path);
if (IS_ERR(bdev))
return bdev;
if (!(flags & MS_RDONLY))
mode |= FMODE_WRITE;
......@@ -857,10 +880,6 @@ struct block_device *open_bdev_excl(const char *path, int flags,
blkdev_put:
blkdev_put(bdev, BDEV_FS);
return ERR_PTR(error);
path_release:
path_release(&nd);
return ERR_PTR(error);
}
/**
......
......@@ -102,35 +102,6 @@ static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t
return security_quotactl (cmd, type, id, sb);
}
/* Resolve device pathname to superblock */
static struct super_block *resolve_dev(const char *path)
{
int ret;
mode_t mode;
struct nameidata nd;
struct block_device *bdev;
struct super_block *sb;
ret = user_path_walk(path, &nd);
if (ret)
goto out;
bdev = nd.dentry->d_inode->i_bdev;
mode = nd.dentry->d_inode->i_mode;
path_release(&nd);
ret = -ENOTBLK;
if (!S_ISBLK(mode))
goto out;
ret = -ENODEV;
sb = get_super(bdev);
if (!sb)
goto out;
return sb;
out:
return ERR_PTR(ret);
}
/* Copy parameters and call proper function */
static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
{
......@@ -249,21 +220,24 @@ asmlinkage long sys_quotactl(unsigned int cmd, const char *special, qid_t id, ca
{
uint cmds, type;
struct super_block *sb = NULL;
int ret = -EINVAL;
struct block_device *bdev;
int ret = -ENODEV;
cmds = cmd >> SUBCMDSHIFT;
type = cmd & SUBCMDMASK;
if (IS_ERR(sb = resolve_dev(special))) {
ret = PTR_ERR(sb);
sb = NULL;
goto out;
}
if ((ret = check_quotactl_valid(sb, type, cmds, id)) < 0)
goto out;
bdev = lookup_bdev(special);
if (IS_ERR(bdev))
return PTR_ERR(bdev);
sb = get_super(bdev);
bdput(bdev);
if (sb) {
ret = check_quotactl_valid(sb, type, cmds, id);
if (ret >= 0)
ret = do_quotactl(sb, type, cmds, id, addr);
out:
if (sb)
drop_super(sb);
}
return ret;
}
......@@ -1098,6 +1098,7 @@ extern inline const char *bdevname(struct block_device *bdev)
{
return __bdevname(bdev->bd_dev);
}
extern struct block_device *lookup_bdev(const char *);
extern struct block_device *open_bdev_excl(const char *, int, int, void *);
extern void close_bdev_excl(struct block_device *, int);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment