This commit was generated by cvs2svn to compensate for changes in r2,
[linux-2.6.git] / drivers / block / ioctl.c
1 #include <linux/sched.h>                /* for capable() */
2 #include <linux/blkdev.h>
3 #include <linux/blkpg.h>
4 #include <linux/backing-dev.h>
5 #include <linux/buffer_head.h>
6 #include <asm/uaccess.h>
7
8 static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg)
9 {
10         struct block_device *bdevp;
11         struct gendisk *disk;
12         struct blkpg_ioctl_arg a;
13         struct blkpg_partition p;
14         long long start, length;
15         int part;
16         int i;
17
18         if (!capable(CAP_SYS_ADMIN))
19                 return -EACCES;
20         if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
21                 return -EFAULT;
22         if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
23                 return -EFAULT;
24         disk = bdev->bd_disk;
25         if (bdev != bdev->bd_contains)
26                 return -EINVAL;
27         part = p.pno;
28         if (part <= 0 || part >= disk->minors)
29                 return -EINVAL;
30         switch (a.op) {
31                 case BLKPG_ADD_PARTITION:
32                         start = p.start >> 9;
33                         length = p.length >> 9;
34                         /* check for fit in a hd_struct */ 
35                         if (sizeof(sector_t) == sizeof(long) && 
36                             sizeof(long long) > sizeof(long)) {
37                                 long pstart = start, plength = length;
38                                 if (pstart != start || plength != length
39                                     || pstart < 0 || plength < 0)
40                                         return -EINVAL;
41                         }
42                         /* partition number in use? */
43                         down(&bdev->bd_sem);
44                         if (disk->part[part - 1]) {
45                                 up(&bdev->bd_sem);
46                                 return -EBUSY;
47                         }
48                         /* overlap? */
49                         for (i = 0; i < disk->minors - 1; i++) {
50                                 struct hd_struct *s = disk->part[i];
51
52                                 if (!s)
53                                         continue;
54                                 if (!(start+length <= s->start_sect ||
55                                       start >= s->start_sect + s->nr_sects)) {
56                                         up(&bdev->bd_sem);
57                                         return -EBUSY;
58                                 }
59                         }
60                         /* all seems OK */
61                         add_partition(disk, part, start, length);
62                         up(&bdev->bd_sem);
63                         return 0;
64                 case BLKPG_DEL_PARTITION:
65                         if (!disk->part[part-1])
66                                 return -ENXIO;
67                         if (disk->part[part - 1]->nr_sects == 0)
68                                 return -ENXIO;
69                         bdevp = bdget_disk(disk, part);
70                         if (!bdevp)
71                                 return -ENOMEM;
72                         down(&bdevp->bd_sem);
73                         if (bdevp->bd_openers) {
74                                 up(&bdevp->bd_sem);
75                                 bdput(bdevp);
76                                 return -EBUSY;
77                         }
78                         /* all seems OK */
79                         fsync_bdev(bdevp);
80                         invalidate_bdev(bdevp, 0);
81
82                         down(&bdev->bd_sem);
83                         delete_partition(disk, part);
84                         up(&bdev->bd_sem);
85                         up(&bdevp->bd_sem);
86                         bdput(bdevp);
87
88                         return 0;
89                 default:
90                         return -EINVAL;
91         }
92 }
93
94 static int blkdev_reread_part(struct block_device *bdev)
95 {
96         struct gendisk *disk = bdev->bd_disk;
97         int res;
98
99         if (disk->minors == 1 || bdev != bdev->bd_contains)
100                 return -EINVAL;
101         if (!capable(CAP_SYS_ADMIN))
102                 return -EACCES;
103         if (down_trylock(&bdev->bd_sem))
104                 return -EBUSY;
105         res = rescan_partitions(disk, bdev);
106         up(&bdev->bd_sem);
107         return res;
108 }
109
110 static int put_ushort(unsigned long arg, unsigned short val)
111 {
112         return put_user(val, (unsigned short *)arg);
113 }
114
115 static int put_int(unsigned long arg, int val)
116 {
117         return put_user(val, (int *)arg);
118 }
119
120 static int put_long(unsigned long arg, long val)
121 {
122         return put_user(val, (long *)arg);
123 }
124
125 static int put_ulong(unsigned long arg, unsigned long val)
126 {
127         return put_user(val, (unsigned long *)arg);
128 }
129
130 static int put_u64(unsigned long arg, u64 val)
131 {
132         return put_user(val, (u64 *)arg);
133 }
134
135 int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
136                         unsigned long arg)
137 {
138         struct block_device *bdev = inode->i_bdev;
139         struct gendisk *disk = bdev->bd_disk;
140         struct backing_dev_info *bdi;
141         int ret, n;
142
143         switch (cmd) {
144         case BLKRAGET:
145         case BLKFRAGET:
146                 if (!arg)
147                         return -EINVAL;
148                 bdi = blk_get_backing_dev_info(bdev);
149                 if (bdi == NULL)
150                         return -ENOTTY;
151                 return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512);
152         case BLKROGET:
153                 return put_int(arg, bdev_read_only(bdev) != 0);
154         case BLKBSZGET: /* get the logical block size (cf. BLKSSZGET) */
155                 return put_int(arg, block_size(bdev));
156         case BLKSSZGET: /* get block device hardware sector size */
157                 return put_int(arg, bdev_hardsect_size(bdev));
158         case BLKSECTGET:
159                 return put_ushort(arg, bdev_get_queue(bdev)->max_sectors);
160         case BLKRASET:
161         case BLKFRASET:
162                 if(!capable(CAP_SYS_ADMIN))
163                         return -EACCES;
164                 bdi = blk_get_backing_dev_info(bdev);
165                 if (bdi == NULL)
166                         return -ENOTTY;
167                 bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
168                 return 0;
169         case BLKBSZSET:
170                 /* set the logical block size */
171                 if (!capable(CAP_SYS_ADMIN))
172                         return -EACCES;
173                 if (!arg)
174                         return -EINVAL;
175                 if (get_user(n, (int *) arg))
176                         return -EFAULT;
177                 if (bd_claim(bdev, file) < 0)
178                         return -EBUSY;
179                 ret = set_blocksize(bdev, n);
180                 bd_release(bdev);
181                 return ret;
182         case BLKPG:
183                 return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg *) arg);
184         case BLKRRPART:
185                 return blkdev_reread_part(bdev);
186         case BLKGETSIZE:
187                 if ((bdev->bd_inode->i_size >> 9) > ~0UL)
188                         return -EFBIG;
189                 return put_ulong(arg, bdev->bd_inode->i_size >> 9);
190         case BLKGETSIZE64:
191                 return put_u64(arg, bdev->bd_inode->i_size);
192         case BLKFLSBUF:
193                 if (!capable(CAP_SYS_ADMIN))
194                         return -EACCES;
195                 if (disk->fops->ioctl) {
196                         ret = disk->fops->ioctl(inode, file, cmd, arg);
197                         if (ret != -EINVAL)
198                                 return ret;
199                 }
200                 fsync_bdev(bdev);
201                 invalidate_bdev(bdev, 0);
202                 return 0;
203         case BLKROSET:
204                 if (disk->fops->ioctl) {
205                         ret = disk->fops->ioctl(inode, file, cmd, arg);
206                         if (ret != -EINVAL)
207                                 return ret;
208                 }
209                 if (!capable(CAP_SYS_ADMIN))
210                         return -EACCES;
211                 if (get_user(n, (int *)(arg)))
212                         return -EFAULT;
213                 set_device_ro(bdev, n);
214                 return 0;
215         default:
216                 if (disk->fops->ioctl)
217                         return disk->fops->ioctl(inode, file, cmd, arg);
218         }
219         return -ENOTTY;
220 }