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) ...@@ -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 * @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 * Get a reference to the blockdevice at @path in the current
* for the @holder and properly set it up for @kind usage. * namespace if possible and return it. Return ERR_PTR(error)
* otherwise.
*/ */
struct block_device *open_bdev_excl(const char *path, int flags, struct block_device *lookup_bdev(const char *path)
int kind, void *holder)
{ {
struct inode *inode;
struct block_device *bdev; struct block_device *bdev;
struct inode *inode;
struct nameidata nd; struct nameidata nd;
mode_t mode = FMODE_READ; int error;
int error = 0;
if (!path || !*path) if (!path || !*path)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
...@@ -828,17 +824,44 @@ struct block_device *open_bdev_excl(const char *path, int flags, ...@@ -828,17 +824,44 @@ struct block_device *open_bdev_excl(const char *path, int flags,
inode = nd.dentry->d_inode; inode = nd.dentry->d_inode;
error = -ENOTBLK; error = -ENOTBLK;
if (!S_ISBLK(inode->i_mode)) if (!S_ISBLK(inode->i_mode))
goto path_release; goto fail;
error = -EACCES; error = -EACCES;
if (nd.mnt->mnt_flags & MNT_NODEV) if (nd.mnt->mnt_flags & MNT_NODEV)
goto path_release; goto fail;
error = bd_acquire(inode); error = bd_acquire(inode);
if (error) if (error)
goto path_release; goto fail;
bdev = inode->i_bdev; bdev = inode->i_bdev;
/* Done with lookups */ out:
path_release(&nd); 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)) if (!(flags & MS_RDONLY))
mode |= FMODE_WRITE; mode |= FMODE_WRITE;
...@@ -857,10 +880,6 @@ struct block_device *open_bdev_excl(const char *path, int flags, ...@@ -857,10 +880,6 @@ struct block_device *open_bdev_excl(const char *path, int flags,
blkdev_put: blkdev_put:
blkdev_put(bdev, BDEV_FS); blkdev_put(bdev, BDEV_FS);
return ERR_PTR(error); 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 ...@@ -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); 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 */ /* Copy parameters and call proper function */
static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr) 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 ...@@ -249,21 +220,24 @@ asmlinkage long sys_quotactl(unsigned int cmd, const char *special, qid_t id, ca
{ {
uint cmds, type; uint cmds, type;
struct super_block *sb = NULL; struct super_block *sb = NULL;
int ret = -EINVAL; struct block_device *bdev;
int ret = -ENODEV;
cmds = cmd >> SUBCMDSHIFT; cmds = cmd >> SUBCMDSHIFT;
type = cmd & SUBCMDMASK; type = cmd & SUBCMDMASK;
if (IS_ERR(sb = resolve_dev(special))) { bdev = lookup_bdev(special);
ret = PTR_ERR(sb); if (IS_ERR(bdev))
sb = NULL; return PTR_ERR(bdev);
goto out; sb = get_super(bdev);
} bdput(bdev);
if ((ret = check_quotactl_valid(sb, type, cmds, id)) < 0)
goto out; if (sb) {
ret = check_quotactl_valid(sb, type, cmds, id);
if (ret >= 0)
ret = do_quotactl(sb, type, cmds, id, addr); ret = do_quotactl(sb, type, cmds, id, addr);
out:
if (sb)
drop_super(sb); drop_super(sb);
}
return ret; return ret;
} }
...@@ -1098,6 +1098,7 @@ extern inline const char *bdevname(struct block_device *bdev) ...@@ -1098,6 +1098,7 @@ extern inline const char *bdevname(struct block_device *bdev)
{ {
return __bdevname(bdev->bd_dev); 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 struct block_device *open_bdev_excl(const char *, int, int, void *);
extern void close_bdev_excl(struct block_device *, int); 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