> diff --git a/include/uapi/linux/if.h b/include/uapi/linux/if.h
> index 797ba2c1562a..a332d6ae4dc6 100644
> --- a/include/uapi/linux/if.h
> +++ b/include/uapi/linux/if.h
> @@ -247,7 +247,13 @@ struct ifreq {
>               short   ifru_flags;
>               int     ifru_ivalue;
>               int     ifru_mtu;
> +#ifndef __KERNEL__
> +             /*
> +              * ifru_map is rarely used but causes the incompatibility
> +              * between native and compat mode.
> +              */
>               struct  ifmap ifru_map;
> +#endif

Do we need a way to verify that this never changes the struct size?

> +int dev_ifmap(struct net *net, struct ifreq __user *ifr, unsigned int cmd)
> +{
> +     struct net_device *dev;
> +     char ifname[IFNAMSIZ];
> +     char *colon;
> +     struct compat_ifmap cifmap;
> +     struct ifmap ifmap;
> +     int ret;
> +
> +     if (copy_from_user(ifname, ifr->ifr_name, sizeof(ifname)))
> +             return -EFAULT;
> +     ifname[IFNAMSIZ-1] = 0;
> +     colon = strchr(ifname, ':');
> +     if (colon)
> +             *colon = 0;
> +     dev_load(net, ifname);
> +
> +     switch (cmd) {
> +     case SIOCGIFMAP:
> +             rcu_read_lock();
> +             dev = dev_get_by_name_rcu(net, ifname);
> +             if (!dev) {
> +                     rcu_read_unlock();
> +                     return -ENODEV;
> +             }
> +
> +             if (in_compat_syscall()) {
> +                     cifmap.mem_start = dev->mem_start;
> +                     cifmap.mem_end   = dev->mem_end;
> +                     cifmap.base_addr = dev->base_addr;
> +                     cifmap.irq       = dev->irq;
> +                     cifmap.dma       = dev->dma;
> +                     cifmap.port      = dev->if_port;
> +                     rcu_read_unlock();
> +
> +                     ret = copy_to_user(&ifr->ifr_data,
> +                                        &cifmap, sizeof(cifmap));
> +             } else {
> +                     ifmap.mem_start  = dev->mem_start;
> +                     ifmap.mem_end    = dev->mem_end;
> +                     ifmap.base_addr  = dev->base_addr;
> +                     ifmap.irq        = dev->irq;
> +                     ifmap.dma        = dev->dma;
> +                     ifmap.port       = dev->if_port;
> +                     rcu_read_unlock();
> +
> +                     ret = copy_to_user(&ifr->ifr_data,
> +                                        &ifmap, sizeof(ifmap));
> +             }
> +             ret = ret ? -EFAULT : 0;
> +             break;
> +
> +     case SIOCSIFMAP:
> +             if (!capable(CAP_NET_ADMIN) ||
> +                 !ns_capable(net->user_ns, CAP_NET_ADMIN))
> +                     return -EPERM;
> +
> +             if (in_compat_syscall()) {
> +                     if (copy_from_user(&cifmap, &ifr->ifr_data,
> +                                        sizeof(cifmap)))
> +                             return -EFAULT;
> +
> +                     ifmap.mem_start  = cifmap.mem_start;
> +                     ifmap.mem_end    = cifmap.mem_end;
> +                     ifmap.base_addr  = cifmap.base_addr;
> +                     ifmap.irq        = cifmap.irq;
> +                     ifmap.dma        = cifmap.dma;
> +                     ifmap.port       = cifmap.port;
> +             } else {
> +                     if (copy_from_user(&ifmap, &ifr->ifr_data,
> +                                        sizeof(ifmap)))
> +                             return -EFAULT;
> +             }
> +
> +             rtnl_lock();
> +             dev = __dev_get_by_name(net, ifname);
> +             if (!dev || !netif_device_present(dev))
> +                     ret = -ENODEV;
> +             else if (!dev->netdev_ops->ndo_set_config)
> +                     ret = -EOPNOTSUPP;
> +             else
> +                     ret = dev->netdev_ops->ndo_set_config(dev, &ifmap);
> +             rtnl_unlock();
> +             break;
> +     }
> +     return ret;

I'd rather split this into a separate hepers for each ioctl command
instead of having anothr multiplexer here, maybe with another helper
for the common code.

I also find the rcu unlock inside the branches rather strange, but
I can't think of a good alternative.

Reply via email to