aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds2012-01-05 17:40:12 -0600
committerLinus Torvalds2012-01-05 17:40:12 -0600
commit07d106d0a33d6063d2061305903deb02489eba20 (patch)
tree6f257f877a9c2e653ca0515253e930fa6606239a /block/ioctl.c
parent805a6af8dba5dfdd35ec35dc52ec0122400b2610 (diff)
downloadkernel-common-07d106d0a33d6063d2061305903deb02489eba20.tar.gz
kernel-common-07d106d0a33d6063d2061305903deb02489eba20.tar.xz
kernel-common-07d106d0a33d6063d2061305903deb02489eba20.zip
vfs: fix up ENOIOCTLCMD error handling
We're doing some odd things there, which already messes up various users (see the net/socket.c code that this removes), and it was going to add yet more crud to the block layer because of the incorrect error code translation. ENOIOCTLCMD is not an error return that should be returned to user mode from the "ioctl()" system call, but it should *not* be translated as EINVAL ("Invalid argument"). It should be translated as ENOTTY ("Inappropriate ioctl for device"). That EINVAL confusion has apparently so permeated some code that the block layer actually checks for it, which is sad. We continue to do so for now, but add a big comment about how wrong that is, and we should remove it entirely eventually. In the meantime, this tries to keep the changes localized to just the EINVAL -> ENOTTY fix, and removing code that makes it harder to do the right thing. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'block/ioctl.c')
-rw-r--r--block/ioctl.c26
1 files changed, 22 insertions, 4 deletions
diff --git a/block/ioctl.c b/block/ioctl.c
index ca939fc1030..d510c2a4eff 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -180,6 +180,26 @@ int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
180EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl); 180EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl);
181 181
182/* 182/*
183 * Is it an unrecognized ioctl? The correct returns are either
184 * ENOTTY (final) or ENOIOCTLCMD ("I don't know this one, try a
185 * fallback"). ENOIOCTLCMD gets turned into ENOTTY by the ioctl
186 * code before returning.
187 *
188 * Confused drivers sometimes return EINVAL, which is wrong. It
189 * means "I understood the ioctl command, but the parameters to
190 * it were wrong".
191 *
192 * We should aim to just fix the broken drivers, the EINVAL case
193 * should go away.
194 */
195static inline int is_unrecognized_ioctl(int ret)
196{
197 return ret == -EINVAL ||
198 ret == -ENOTTY ||
199 ret == -ENOIOCTLCMD;
200}
201
202/*
183 * always keep this in sync with compat_blkdev_ioctl() 203 * always keep this in sync with compat_blkdev_ioctl()
184 */ 204 */
185int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, 205int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
@@ -196,8 +216,7 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
196 return -EACCES; 216 return -EACCES;
197 217
198 ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); 218 ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
199 /* -EINVAL to handle old uncorrected drivers */ 219 if (!is_unrecognized_ioctl(ret))
200 if (ret != -EINVAL && ret != -ENOTTY)
201 return ret; 220 return ret;
202 221
203 fsync_bdev(bdev); 222 fsync_bdev(bdev);
@@ -206,8 +225,7 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
206 225
207 case BLKROSET: 226 case BLKROSET:
208 ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); 227 ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
209 /* -EINVAL to handle old uncorrected drivers */ 228 if (!is_unrecognized_ioctl(ret))
210 if (ret != -EINVAL && ret != -ENOTTY)
211 return ret; 229 return ret;
212 if (!capable(CAP_SYS_ADMIN)) 230 if (!capable(CAP_SYS_ADMIN))
213 return -EACCES; 231 return -EACCES;