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
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>
-rw-r--r--block/ioctl.c26
-rw-r--r--fs/compat_ioctl.c38
-rw-r--r--fs/ioctl.c2
-rw-r--r--net/socket.c16
4 files changed, 26 insertions, 56 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;
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index 51352de88ef..a10e428b32b 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -1506,35 +1506,6 @@ static long do_ioctl_trans(int fd, unsigned int cmd,
1506 return -ENOIOCTLCMD; 1506 return -ENOIOCTLCMD;
1507} 1507}
1508 1508
1509static void compat_ioctl_error(struct file *filp, unsigned int fd,
1510 unsigned int cmd, unsigned long arg)
1511{
1512 char buf[10];
1513 char *fn = "?";
1514 char *path;
1515
1516 /* find the name of the device. */
1517 path = (char *)__get_free_page(GFP_KERNEL);
1518 if (path) {
1519 fn = d_path(&filp->f_path, path, PAGE_SIZE);
1520 if (IS_ERR(fn))
1521 fn = "?";
1522 }
1523
1524 sprintf(buf,"'%c'", (cmd>>_IOC_TYPESHIFT) & _IOC_TYPEMASK);
1525 if (!isprint(buf[1]))
1526 sprintf(buf, "%02x", buf[1]);
1527 compat_printk("ioctl32(%s:%d): Unknown cmd fd(%d) "
1528 "cmd(%08x){t:%s;sz:%u} arg(%08x) on %s\n",
1529 current->comm, current->pid,
1530 (int)fd, (unsigned int)cmd, buf,
1531 (cmd >> _IOC_SIZESHIFT) & _IOC_SIZEMASK,
1532 (unsigned int)arg, fn);
1533
1534 if (path)
1535 free_page((unsigned long)path);
1536}
1537
1538static int compat_ioctl_check_table(unsigned int xcmd) 1509static int compat_ioctl_check_table(unsigned int xcmd)
1539{ 1510{
1540 int i; 1511 int i;
@@ -1621,13 +1592,8 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
1621 goto found_handler; 1592 goto found_handler;
1622 1593
1623 error = do_ioctl_trans(fd, cmd, arg, filp); 1594 error = do_ioctl_trans(fd, cmd, arg, filp);
1624 if (error == -ENOIOCTLCMD) { 1595 if (error == -ENOIOCTLCMD)
1625 static int count; 1596 error = -ENOTTY;
1626
1627 if (++count <= 50)
1628 compat_ioctl_error(filp, fd, cmd, arg);
1629 error = -EINVAL;
1630 }
1631 1597
1632 goto out_fput; 1598 goto out_fput;
1633 1599
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 1d9b9fcb2db..066836e8184 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -42,7 +42,7 @@ static long vfs_ioctl(struct file *filp, unsigned int cmd,
42 42
43 error = filp->f_op->unlocked_ioctl(filp, cmd, arg); 43 error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
44 if (error == -ENOIOCTLCMD) 44 if (error == -ENOIOCTLCMD)
45 error = -EINVAL; 45 error = -ENOTTY;
46 out: 46 out:
47 return error; 47 return error;
48} 48}
diff --git a/net/socket.c b/net/socket.c
index 2877647f347..a0053750e37 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2883,7 +2883,7 @@ static int bond_ioctl(struct net *net, unsigned int cmd,
2883 2883
2884 return dev_ioctl(net, cmd, uifr); 2884 return dev_ioctl(net, cmd, uifr);
2885 default: 2885 default:
2886 return -EINVAL; 2886 return -ENOIOCTLCMD;
2887 } 2887 }
2888} 2888}
2889 2889
@@ -3210,20 +3210,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
3210 return sock_do_ioctl(net, sock, cmd, arg); 3210 return sock_do_ioctl(net, sock, cmd, arg);
3211 } 3211 }
3212 3212
3213 /* Prevent warning from compat_sys_ioctl, these always
3214 * result in -EINVAL in the native case anyway. */
3215 switch (cmd) {
3216 case SIOCRTMSG:
3217 case SIOCGIFCOUNT:
3218 case SIOCSRARP:
3219 case SIOCGRARP:
3220 case SIOCDRARP:
3221 case SIOCSIFLINK:
3222 case SIOCGIFSLAVE:
3223 case SIOCSIFSLAVE:
3224 return -EINVAL;
3225 }
3226
3227 return -ENOIOCTLCMD; 3213 return -ENOIOCTLCMD;
3228} 3214}
3229 3215