mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 20:51:03 +02:00
net: separate SIOCGIFCONF handling from dev_ioctl()
Only two of dev_ioctl() callers may pass SIOCGIFCONF to it. Separating that codepath from the rest of dev_ioctl() allows both to simplify dev_ioctl() itself (all other cases work with struct ifreq *) *and* seriously simplify the compat side of that beast: all it takes is passing to inet_gifconf() an extra argument - the size of individual records (sizeof(struct ifreq) or sizeof(struct compat_ifreq)). With dev_ifconf() called directly from sock_do_ioctl()/compat_dev_ifconf() that's easy to arrange. As the result, compat side of SIOCGIFCONF doesn't need any allocations, copy_in_user() back and forth, etc. Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
79
net/socket.c
79
net/socket.c
@@ -961,10 +961,22 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
|
||||
* If this ioctl is unknown try to hand it down
|
||||
* to the NIC driver.
|
||||
*/
|
||||
if (err == -ENOIOCTLCMD)
|
||||
err = dev_ioctl(net, cmd, argp);
|
||||
if (err != -ENOIOCTLCMD)
|
||||
return err;
|
||||
|
||||
return err;
|
||||
if (cmd == SIOCGIFCONF) {
|
||||
struct ifconf ifc;
|
||||
if (copy_from_user(&ifc, argp, sizeof(struct ifconf)))
|
||||
return -EFAULT;
|
||||
rtnl_lock();
|
||||
err = dev_ifconf(net, &ifc, sizeof(struct ifreq));
|
||||
rtnl_unlock();
|
||||
if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf)))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
}
|
||||
|
||||
return dev_ioctl(net, cmd, argp);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2673,70 +2685,25 @@ static int dev_ifname32(struct net *net, struct compat_ifreq __user *uifr32)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32)
|
||||
static int compat_dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32)
|
||||
{
|
||||
struct compat_ifconf ifc32;
|
||||
struct ifconf ifc;
|
||||
struct ifconf __user *uifc;
|
||||
struct compat_ifreq __user *ifr32;
|
||||
struct ifreq __user *ifr;
|
||||
unsigned int i, j;
|
||||
int err;
|
||||
|
||||
if (copy_from_user(&ifc32, uifc32, sizeof(struct compat_ifconf)))
|
||||
return -EFAULT;
|
||||
|
||||
memset(&ifc, 0, sizeof(ifc));
|
||||
if (ifc32.ifcbuf == 0) {
|
||||
ifc32.ifc_len = 0;
|
||||
ifc.ifc_len = 0;
|
||||
ifc.ifc_req = NULL;
|
||||
uifc = compat_alloc_user_space(sizeof(struct ifconf));
|
||||
} else {
|
||||
size_t len = ((ifc32.ifc_len / sizeof(struct compat_ifreq)) + 1) *
|
||||
sizeof(struct ifreq);
|
||||
uifc = compat_alloc_user_space(sizeof(struct ifconf) + len);
|
||||
ifc.ifc_len = len;
|
||||
ifr = ifc.ifc_req = (void __user *)(uifc + 1);
|
||||
ifr32 = compat_ptr(ifc32.ifcbuf);
|
||||
for (i = 0; i < ifc32.ifc_len; i += sizeof(struct compat_ifreq)) {
|
||||
if (copy_in_user(ifr, ifr32, sizeof(struct compat_ifreq)))
|
||||
return -EFAULT;
|
||||
ifr++;
|
||||
ifr32++;
|
||||
}
|
||||
}
|
||||
if (copy_to_user(uifc, &ifc, sizeof(struct ifconf)))
|
||||
return -EFAULT;
|
||||
ifc.ifc_len = ifc32.ifc_len;
|
||||
ifc.ifc_req = compat_ptr(ifc32.ifcbuf);
|
||||
|
||||
err = dev_ioctl(net, SIOCGIFCONF, uifc);
|
||||
rtnl_lock();
|
||||
err = dev_ifconf(net, &ifc, sizeof(struct compat_ifreq));
|
||||
rtnl_unlock();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (copy_from_user(&ifc, uifc, sizeof(struct ifconf)))
|
||||
return -EFAULT;
|
||||
|
||||
ifr = ifc.ifc_req;
|
||||
ifr32 = compat_ptr(ifc32.ifcbuf);
|
||||
for (i = 0, j = 0;
|
||||
i + sizeof(struct compat_ifreq) <= ifc32.ifc_len && j < ifc.ifc_len;
|
||||
i += sizeof(struct compat_ifreq), j += sizeof(struct ifreq)) {
|
||||
if (copy_in_user(ifr32, ifr, sizeof(struct compat_ifreq)))
|
||||
return -EFAULT;
|
||||
ifr32++;
|
||||
ifr++;
|
||||
}
|
||||
|
||||
if (ifc32.ifcbuf == 0) {
|
||||
/* Translate from 64-bit structure multiple to
|
||||
* a 32-bit one.
|
||||
*/
|
||||
i = ifc.ifc_len;
|
||||
i = ((i / sizeof(struct ifreq)) * sizeof(struct compat_ifreq));
|
||||
ifc32.ifc_len = i;
|
||||
} else {
|
||||
ifc32.ifc_len = i;
|
||||
}
|
||||
ifc32.ifc_len = ifc.ifc_len;
|
||||
if (copy_to_user(uifc32, &ifc32, sizeof(struct compat_ifconf)))
|
||||
return -EFAULT;
|
||||
|
||||
@@ -3133,7 +3100,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
|
||||
case SIOCGIFNAME:
|
||||
return dev_ifname32(net, argp);
|
||||
case SIOCGIFCONF:
|
||||
return dev_ifconf(net, argp);
|
||||
return compat_dev_ifconf(net, argp);
|
||||
case SIOCETHTOOL:
|
||||
return ethtool_ioctl(net, argp);
|
||||
case SIOCWANDEV:
|
||||
|
Reference in New Issue
Block a user