aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux-user/ioctls.h3
-rw-r--r--linux-user/syscall.c96
2 files changed, 97 insertions, 2 deletions
diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
index 526aaa2a76..ab15b867ec 100644
--- a/linux-user/ioctls.h
+++ b/linux-user/ioctls.h
@@ -112,7 +112,8 @@
IOCTL(SIOCADDMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
IOCTL(SIOCDELMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
IOCTL(SIOCSIFLINK, 0, TYPE_NULL)
- IOCTL(SIOCGIFCONF, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_ifconf)))
+ IOCTL_SPECIAL(SIOCGIFCONF, IOC_W | IOC_R, do_ioctl_ifconf,
+ MK_PTR(MK_STRUCT(STRUCT_ifconf)))
IOCTL(SIOCGIFENCAP, IOC_RW, MK_PTR(TYPE_INT))
IOCTL(SIOCSIFENCAP, IOC_W, MK_PTR(TYPE_INT))
IOCTL(SIOCDARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 732f71a6a0..123909f190 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -59,6 +59,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
//#include <sys/user.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
+#include <net/if.h>
#include <qemu-common.h>
#ifdef TARGET_GPROF
#include <sys/gmon.h>
@@ -2970,7 +2971,6 @@ static abi_long do_ipc(unsigned int call, int first,
#endif
/* kernel structure types definitions */
-#define IFNAMSIZ 16
#define STRUCT(name, ...) STRUCT_ ## name,
#define STRUCT_SPECIAL(name) STRUCT_ ## name,
@@ -3095,6 +3095,100 @@ static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp,
}
#endif
+static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp,
+ int fd, abi_long cmd, abi_long arg)
+{
+ const argtype *arg_type = ie->arg_type;
+ int target_size;
+ void *argptr;
+ int ret;
+ struct ifconf *host_ifconf;
+ uint32_t outbufsz;
+ const argtype ifreq_arg_type[] = { MK_STRUCT(STRUCT_sockaddr_ifreq) };
+ int target_ifreq_size;
+ int nb_ifreq;
+ int free_buf = 0;
+ int i;
+ int target_ifc_len;
+ abi_long target_ifc_buf;
+ int host_ifc_len;
+ char *host_ifc_buf;
+
+ assert(arg_type[0] == TYPE_PTR);
+ assert(ie->access == IOC_RW);
+
+ arg_type++;
+ target_size = thunk_type_size(arg_type, 0);
+
+ argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+ if (!argptr)
+ return -TARGET_EFAULT;
+ thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+ unlock_user(argptr, arg, 0);
+
+ host_ifconf = (struct ifconf *)(unsigned long)buf_temp;
+ target_ifc_len = host_ifconf->ifc_len;
+ target_ifc_buf = (abi_long)(unsigned long)host_ifconf->ifc_buf;
+
+ target_ifreq_size = thunk_type_size(ifreq_arg_type, 0);
+ nb_ifreq = target_ifc_len / target_ifreq_size;
+ host_ifc_len = nb_ifreq * sizeof(struct ifreq);
+
+ outbufsz = sizeof(*host_ifconf) + host_ifc_len;
+ if (outbufsz > MAX_STRUCT_SIZE) {
+ /* We can't fit all the extents into the fixed size buffer.
+ * Allocate one that is large enough and use it instead.
+ */
+ host_ifconf = malloc(outbufsz);
+ if (!host_ifconf) {
+ return -TARGET_ENOMEM;
+ }
+ memcpy(host_ifconf, buf_temp, sizeof(*host_ifconf));
+ free_buf = 1;
+ }
+ host_ifc_buf = (char*)host_ifconf + sizeof(*host_ifconf);
+
+ host_ifconf->ifc_len = host_ifc_len;
+ host_ifconf->ifc_buf = host_ifc_buf;
+
+ ret = get_errno(ioctl(fd, ie->host_cmd, host_ifconf));
+ if (!is_error(ret)) {
+ /* convert host ifc_len to target ifc_len */
+
+ nb_ifreq = host_ifconf->ifc_len / sizeof(struct ifreq);
+ target_ifc_len = nb_ifreq * target_ifreq_size;
+ host_ifconf->ifc_len = target_ifc_len;
+
+ /* restore target ifc_buf */
+
+ host_ifconf->ifc_buf = (char *)(unsigned long)target_ifc_buf;
+
+ /* copy struct ifconf to target user */
+
+ argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
+ if (!argptr)
+ return -TARGET_EFAULT;
+ thunk_convert(argptr, host_ifconf, arg_type, THUNK_TARGET);
+ unlock_user(argptr, arg, target_size);
+
+ /* copy ifreq[] to target user */
+
+ argptr = lock_user(VERIFY_WRITE, target_ifc_buf, target_ifc_len, 0);
+ for (i = 0; i < nb_ifreq ; i++) {
+ thunk_convert(argptr + i * target_ifreq_size,
+ host_ifc_buf + i * sizeof(struct ifreq),
+ ifreq_arg_type, THUNK_TARGET);
+ }
+ unlock_user(argptr, target_ifc_buf, target_ifc_len);
+ }
+
+ if (free_buf) {
+ free(host_ifconf);
+ }
+
+ return ret;
+}
+
static IOCTLEntry ioctl_entries[] = {
#define IOCTL(cmd, access, ...) \
{ TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } },