Commit c6973580 authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] block ioctl cleanup

	guts of blkpg.c and blkdev_ioctl() sanitized up and moved into a new
file - drivers/block/ioctl.c.  blkpg.c is gone.
parent 8b290eb1
......@@ -11,7 +11,7 @@
export-objs := elevator.o ll_rw_blk.o loop.o genhd.o acsi.o \
scsi_ioctl.o deadline-iosched.o
obj-y := elevator.o ll_rw_blk.o blkpg.o genhd.o scsi_ioctl.o deadline-iosched.o
obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o deadline-iosched.o
obj-$(CONFIG_MAC_FLOPPY) += swim3.o
obj-$(CONFIG_BLK_DEV_FD) += floppy.o
......
/*
* Partition table and disk geometry handling
*
* This obsoletes the partition-handling code in genhd.c:
* Userspace can look at a disk in arbitrary format and tell
* the kernel what partitions there are on the disk, and how
* these should be numbered.
* It also allows one to repartition a disk that is being used.
*
* A single ioctl with lots of subfunctions:
*
* Device number stuff:
* get_whole_disk() (given the device number of a partition, find
* the device number of the encompassing disk)
* get_all_partitions() (given the device number of a disk, return the
* device numbers of all its known partitions)
*
* Partition stuff:
* add_partition()
* delete_partition()
* test_partition_in_use() (also for test_disk_in_use)
*
* Geometry stuff:
* get_geometry()
* set_geometry()
* get_bios_drivedata()
*
* For today, only the partition stuff - aeb, 990515
*/
#include <linux/errno.h>
#include <linux/fs.h> /* for BLKROSET, ... */
#include <linux/sched.h> /* for capable() */
#include <linux/blk.h> /* for set_device_ro() */
#include <linux/blkpg.h>
#include <linux/genhd.h>
#include <linux/module.h> /* for EXPORT_SYMBOL */
#include <linux/backing-dev.h>
#include <linux/buffer_head.h>
#include <asm/uaccess.h>
/*
* What is the data describing a partition?
*
* 1. a device number (kdev_t)
* 2. a starting sector and number of sectors (hd_struct)
* given in the part[] array of the gendisk structure for the drive.
*
* The number of sectors is replicated in the sizes[] array of
* the gendisk structure for the major, which again is copied to
* the blk_size[][] array.
* (However, hd_struct has the number of 512-byte sectors,
* g->sizes[] and blk_size[][] have the number of 1024-byte blocks.)
* Note that several drives may have the same major.
*/
/*
* Add a partition.
*
* returns: EINVAL: bad parameters
* ENXIO: cannot find drive
* EBUSY: proposed partition overlaps an existing one
* or has the same number as an existing one
* 0: all OK.
*/
int add_partition(struct block_device *bdev, struct blkpg_partition *p)
{
struct gendisk *g;
long long ppstart, pplength;
int part, i;
/* convert bytes to sectors */
ppstart = (p->start >> 9);
pplength = (p->length >> 9);
/* check for fit in a hd_struct */
if (sizeof(sector_t) == sizeof(long) &&
sizeof(long long) > sizeof(long)) {
long pstart, plength;
pstart = ppstart;
plength = pplength;
if (pstart != ppstart || plength != pplength
|| pstart < 0 || plength < 0)
return -EINVAL;
}
/* find the drive major */
g = get_gendisk(bdev->bd_dev, &part);
if (!g)
return -ENXIO;
/* existing drive? */
/* drive and partition number OK? */
if (bdev != bdev->bd_contains)
return -EINVAL;
if (part)
BUG();
if (p->pno <= 0 || p->pno >= g->minors)
return -EINVAL;
/* partition number in use? */
if (g->part[p->pno - 1].nr_sects != 0)
return -EBUSY;
/* overlap? */
for (i = 0; i < g->minors - 1; i++)
if (!(ppstart+pplength <= g->part[i].start_sect ||
ppstart >= g->part[i].start_sect + g->part[i].nr_sects))
return -EBUSY;
/* all seems OK */
g->part[p->pno - 1].start_sect = ppstart;
g->part[p->pno - 1].nr_sects = pplength;
update_partition(g, p->pno);
return 0;
}
/*
* Delete a partition given by partition number
*
* returns: EINVAL: bad parameters
* ENXIO: cannot find partition
* EBUSY: partition is busy
* 0: all OK.
*
* Note that the dev argument refers to the entire disk, not the partition.
*/
int del_partition(struct block_device *bdev, struct blkpg_partition *p)
{
struct gendisk *g;
struct block_device *bdevp;
int part;
int holder;
/* find the drive major */
g = get_gendisk(bdev->bd_dev, &part);
if (!g)
return -ENXIO;
if (bdev != bdev->bd_contains)
return -EINVAL;
if (part)
BUG();
if (p->pno <= 0 || p->pno >= g->minors)
return -EINVAL;
/* existing drive and partition? */
if (g->part[p->pno - 1].nr_sects == 0)
return -ENXIO;
/* partition in use? Incomplete check for now. */
bdevp = bdget(MKDEV(g->major, g->first_minor + p->pno));
if (!bdevp)
return -ENOMEM;
if (bd_claim(bdevp, &holder) < 0) {
bdput(bdevp);
return -EBUSY;
}
/* all seems OK */
fsync_bdev(bdevp);
invalidate_bdev(bdevp, 0);
g->part[p->pno - 1].start_sect = 0;
g->part[p->pno - 1].nr_sects = 0;
update_partition(g, p->pno);
bd_release(bdevp);
bdput(bdevp);
return 0;
}
int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg)
{
struct blkpg_ioctl_arg a;
struct blkpg_partition p;
int len;
if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
return -EFAULT;
switch (a.op) {
case BLKPG_ADD_PARTITION:
case BLKPG_DEL_PARTITION:
len = a.datalen;
if (len < sizeof(struct blkpg_partition))
return -EINVAL;
if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
return -EFAULT;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (a.op == BLKPG_ADD_PARTITION)
return add_partition(bdev, &p);
else
return del_partition(bdev, &p);
default:
return -EINVAL;
}
}
/*
* Common ioctl's for block devices
*/
int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg)
{
request_queue_t *q;
u64 ullval = 0;
int intval;
unsigned short usval;
kdev_t dev = to_kdev_t(bdev->bd_dev);
int holder;
struct backing_dev_info *bdi;
switch (cmd) {
case BLKROSET:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (get_user(intval, (int *)(arg)))
return -EFAULT;
set_device_ro(dev, intval);
return 0;
case BLKROGET:
intval = (bdev_read_only(bdev) != 0);
return put_user(intval, (int *)(arg));
case BLKRASET:
case BLKFRASET:
if(!capable(CAP_SYS_ADMIN))
return -EACCES;
bdi = blk_get_backing_dev_info(bdev);
if (bdi == NULL)
return -ENOTTY;
bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
return 0;
case BLKRAGET:
case BLKFRAGET:
if (!arg)
return -EINVAL;
bdi = blk_get_backing_dev_info(bdev);
if (bdi == NULL)
return -ENOTTY;
return put_user((bdi->ra_pages * PAGE_CACHE_SIZE) / 512,
(long *)arg);
case BLKSECTGET:
if ((q = bdev_get_queue(bdev)) == NULL)
return -EINVAL;
usval = q->max_sectors;
blk_put_queue(q);
return put_user(usval, (unsigned short *)arg);
case BLKFLSBUF:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
fsync_bdev(bdev);
invalidate_bdev(bdev, 0);
return 0;
case BLKSSZGET:
/* get block device hardware sector size */
intval = bdev_hardsect_size(bdev);
return put_user(intval, (int *) arg);
case BLKGETSIZE:
{
unsigned long ret;
/* size in sectors, works up to 2 TB */
ullval = bdev->bd_inode->i_size;
ret = ullval >> 9;
if ((u64)ret != (ullval >> 9))
return -EFBIG;
return put_user(ret, (unsigned long *) arg);
}
case BLKGETSIZE64:
/* size in bytes */
ullval = bdev->bd_inode->i_size;
return put_user(ullval, (u64 *) arg);
case BLKPG:
return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg *) arg);
case BLKBSZGET:
/* get the logical block size (cf. BLKSSZGET) */
intval = block_size(bdev);
return put_user(intval, (int *) arg);
case BLKBSZSET:
/* set the logical block size */
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (!arg)
return -EINVAL;
if (get_user(intval, (int *) arg))
return -EFAULT;
if (intval > PAGE_SIZE || intval < 512 ||
(intval & (intval - 1)))
return -EINVAL;
if (bd_claim(bdev, &holder) < 0)
return -EBUSY;
set_blocksize(bdev, intval);
bd_release(bdev);
return 0;
default:
return -ENOTTY;
}
}
......@@ -3488,16 +3488,6 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
loc.start = 0;
return _COPYOUT(loc);
}
case BLKGETSIZE:
ECALL(get_floppy_geometry(drive, type, &g));
return put_user(g->size, (unsigned long *) param);
case BLKGETSIZE64:
ECALL(get_floppy_geometry(drive, type, &g));
return put_user((u64)g->size << 9, (u64 *) param);
/* BLKRRPART is not defined as floppies don't have
* partition tables */
}
/* convert the old style command into a new style command */
......
#include <linux/sched.h> /* for capable() */
#include <linux/blk.h> /* for set_device_ro() */
#include <linux/blkpg.h>
#include <linux/backing-dev.h>
#include <linux/buffer_head.h>
#include <asm/uaccess.h>
static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg)
{
struct block_device *bdevp;
int holder;
struct gendisk *disk;
struct blkpg_ioctl_arg a;
struct blkpg_partition p;
long long start, length;
int part;
int i;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
return -EFAULT;
if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
return -EFAULT;
disk = get_gendisk(bdev->bd_dev, &part);
if (!disk)
return -ENXIO;
if (bdev != bdev->bd_contains)
return -EINVAL;
if (part)
BUG();
part = p.pno;
if (part <= 0 || part >= disk->minors)
return -EINVAL;
switch (a.op) {
case BLKPG_ADD_PARTITION:
start = p.start >> 9;
length = p.length >> 9;
/* check for fit in a hd_struct */
if (sizeof(sector_t) == sizeof(long) &&
sizeof(long long) > sizeof(long)) {
long pstart = start, plength = length;
if (pstart != start || plength != length
|| pstart < 0 || plength < 0)
return -EINVAL;
}
/* partition number in use? */
if (disk->part[part - 1].nr_sects != 0)
return -EBUSY;
/* overlap? */
for (i = 0; i < disk->minors - 1; i++) {
struct hd_struct *s = &disk->part[i];
if (!(start+length <= s->start_sect ||
start >= s->start_sect + s->nr_sects))
return -EBUSY;
}
/* all seems OK */
disk->part[part - 1].start_sect = start;
disk->part[part - 1].nr_sects = length;
update_partition(disk, part);
return 0;
case BLKPG_DEL_PARTITION:
if (disk->part[part - 1].nr_sects == 0)
return -ENXIO;
/* partition in use? Incomplete check for now. */
bdevp = bdget(MKDEV(disk->major, disk->first_minor) + part);
if (!bdevp)
return -ENOMEM;
if (bd_claim(bdevp, &holder) < 0) {
bdput(bdevp);
return -EBUSY;
}
/* all seems OK */
fsync_bdev(bdevp);
invalidate_bdev(bdevp, 0);
disk->part[part].start_sect = 0;
disk->part[part].nr_sects = 0;
update_partition(disk, part);
bd_release(bdevp);
bdput(bdevp);
return 0;
default:
return -EINVAL;
}
}
static int blkdev_reread_part(struct block_device *bdev)
{
int part;
struct gendisk *disk = get_gendisk(bdev->bd_dev, &part);
int res = 0;
if (!disk || disk->minors == 1 || bdev != bdev->bd_contains)
return -EINVAL;
if (part)
BUG();
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (down_trylock(&bdev->bd_sem))
return -EBUSY;
res = rescan_partitions(disk, bdev);
up(&bdev->bd_sem);
return res;
}
static int put_ushort(unsigned long arg, unsigned short val)
{
return put_user(val, (unsigned short *)arg);
}
static int put_int(unsigned long arg, int val)
{
return put_user(val, (int *)arg);
}
static int put_long(unsigned long arg, long val)
{
return put_user(val, (long *)arg);
}
static int put_ulong(unsigned long arg, unsigned long val)
{
return put_user(val, (unsigned long *)arg);
}
static int put_u64(unsigned long arg, u64 val)
{
return put_user(val, (u64 *)arg);
}
int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
unsigned long arg)
{
struct block_device *bdev = inode->i_bdev;
struct backing_dev_info *bdi;
int holder;
int ret, n;
switch (cmd) {
case BLKELVGET:
case BLKELVSET:
/* deprecated, use the /proc/iosched interface instead */
return -ENOTTY;
case BLKRAGET:
case BLKFRAGET:
if (!arg)
return -EINVAL;
bdi = blk_get_backing_dev_info(bdev);
if (bdi == NULL)
return -ENOTTY;
return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512);
case BLKROGET:
return put_int(arg, bdev_read_only(bdev) != 0);
case BLKBSZGET: /* get the logical block size (cf. BLKSSZGET) */
return put_int(arg, block_size(bdev));
case BLKSSZGET: /* get block device hardware sector size */
return put_int(arg, bdev_hardsect_size(bdev));
case BLKSECTGET:
return put_ushort(arg, bdev->bd_queue->max_sectors);
case BLKRASET:
case BLKFRASET:
if(!capable(CAP_SYS_ADMIN))
return -EACCES;
bdi = blk_get_backing_dev_info(bdev);
if (bdi == NULL)
return -ENOTTY;
bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
return 0;
case BLKBSZSET:
/* set the logical block size */
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (!arg)
return -EINVAL;
if (get_user(n, (int *) arg))
return -EFAULT;
if (n > PAGE_SIZE || n < 512 || (n & (n - 1)))
return -EINVAL;
if (bd_claim(bdev, &holder) < 0)
return -EBUSY;
set_blocksize(bdev, n);
bd_release(bdev);
return 0;
case BLKPG:
return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg *) arg);
case BLKRRPART:
return blkdev_reread_part(bdev);
case BLKGETSIZE:
if ((bdev->bd_inode->i_size >> 9) > ~0UL)
return -EFBIG;
return put_ulong(arg, bdev->bd_inode->i_size >> 9);
case BLKGETSIZE64:
return put_u64(arg, bdev->bd_inode->i_size);
case BLKFLSBUF:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (bdev->bd_op->ioctl) {
ret = bdev->bd_op->ioctl(inode, file, cmd, arg);
if (ret != -EINVAL)
return ret;
}
fsync_bdev(bdev);
invalidate_bdev(bdev, 0);
return 0;
case BLKROSET:
if (bdev->bd_op->ioctl) {
ret = bdev->bd_op->ioctl(inode, file, cmd, arg);
if (ret != -EINVAL)
return ret;
}
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (get_user(n, (int *)(arg)))
return -EFAULT;
set_device_ro(to_kdev_t(bdev->bd_dev), n);
return 0;
default:
if (bdev->bd_op->ioctl) {
ret = bdev->bd_op->ioctl(inode, file, cmd, arg);
if (ret != -EINVAL)
return ret;
}
}
return -ENOTTY;
}
......@@ -291,8 +291,6 @@ static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un
if (cmd != BLKFLSBUF)
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
/* special: we want to release the ramdisk memory,
it's not like with the other blockdevices where
this ioctl only flushes away the buffer cache. */
......
......@@ -517,8 +517,6 @@ static int mtdblock_ioctl(struct inode * inode, struct file * file,
switch (cmd) {
case BLKFLSBUF:
if(!capable(CAP_SYS_ADMIN))
return -EACCES;
fsync_bdev(inode->i_bdev);
invalidate_bdev(inode->i_bdev, 0);
down(&mtdblk->cache_sem);
......
......@@ -201,8 +201,6 @@ static int mtdblock_ioctl(struct inode * inode, struct file * file,
if (!mtd || cmd != BLKFLSBUF)
return -EINVAL;
if(!capable(CAP_SYS_ADMIN))
return -EACCES;
fsync_bdev(inode->i_bdev);
invalidate_bdev(inode->i_bdev, 0);
if (mtd->sync)
......
......@@ -770,7 +770,6 @@ static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd
return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0;
}
case BLKFLSBUF:
if (!capable(CAP_SYS_ADMIN)) return -EACCES;
fsync_bdev(inode->i_bdev);
invalidate_bdev(inode->i_bdev, 0);
if (nftl->mtd->sync)
......
......@@ -793,25 +793,6 @@ int blkdev_close(struct inode * inode, struct file * filp)
return blkdev_put(inode->i_bdev, BDEV_FILE);
}
static int blkdev_reread_part(struct block_device *bdev)
{
int part;
struct gendisk *disk = get_gendisk(bdev->bd_dev, &part);
int res = 0;
if (!disk || disk->minors == 1 || bdev != bdev->bd_contains)
return -EINVAL;
if (part)
BUG();
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (down_trylock(&bdev->bd_sem))
return -EBUSY;
res = rescan_partitions(disk, bdev);
up(&bdev->bd_sem);
return res;
}
static ssize_t blkdev_file_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
......@@ -820,16 +801,6 @@ static ssize_t blkdev_file_write(struct file *file, const char *buf,
return generic_file_write_nolock(file, &local_iov, 1, ppos);
}
static int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
unsigned long arg)
{
struct block_device *bdev = inode->i_bdev;
int ret = blk_ioctl(bdev, cmd, arg);
if (ret == -ENOTTY && bdev->bd_op->ioctl)
ret = bdev->bd_op->ioctl(inode, file, cmd, arg);
return ret;
}
struct address_space_operations def_blk_aops = {
.readpage = blkdev_readpage,
.writepage = blkdev_writepage,
......
......@@ -57,7 +57,6 @@ struct blkpg_partition {
#ifdef __KERNEL__
extern char * partition_name(dev_t dev);
extern int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg);
#endif /* __KERNEL__ */
......
......@@ -1087,6 +1087,7 @@ extern struct file_operations def_blk_fops;
extern struct address_space_operations def_blk_aops;
extern struct file_operations def_fifo_fops;
extern int ioctl_by_bdev(struct block_device *, unsigned, unsigned long);
extern int blkdev_ioctl(struct inode *, struct file *, unsigned, unsigned long);
extern int blkdev_get(struct block_device *, mode_t, unsigned, int);
extern int blkdev_put(struct block_device *, int);
extern int bd_claim(struct block_device *, void *);
......
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