diff options
author | Laurent Vivier <laurent@vivier.eu> | 2011-03-30 00:12:12 +0200 |
---|---|---|
committer | Riku Voipio <riku.voipio@iki.fi> | 2011-04-26 10:15:40 +0300 |
commit | 059c2f2cd773e0f3d7284a6eab662fd26f9cbad2 (patch) | |
tree | 2b47afbe0de749868b8f7aecba53fc7d2be17a3e | |
parent | 608e55921770bbae1609135aa0c351238f57fc5f (diff) |
linux-user: convert ioctl(SIOCGIFCONF, ...) result.
The result needs to be converted as it is stored in an array of struct
ifreq and sizeof(struct ifreq) differs according to target and host
alignment rules.
This patch allows to execute correctly the following program on arm
and m68k:
#include <stdio.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <alloca.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
int s, ret;
struct ifconf ifc;
int i;
memset( &ifc, 0, sizeof( struct ifconf ) );
ifc.ifc_len = 8 * sizeof(struct ifreq);
ifc.ifc_buf = alloca(ifc.ifc_len);
s = socket( AF_INET, SOCK_DGRAM, 0 );
if (s < 0) {
perror("Cannot open socket");
return 1;
}
ret = ioctl( s, SIOCGIFCONF, &ifc );
if (s < 0) {
perror("ioctl() failed");
return 1;
}
for (i = 0; i < ifc.ifc_len / sizeof(struct ifreq) ; i ++) {
struct sockaddr_in *s;
s = (struct sockaddr_in*)&ifc.ifc_req[i].ifr_addr;
printf("%s\n", ifc.ifc_req[i].ifr_name);
printf("%s\n", inet_ntoa(s->sin_addr));
}
}
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
Signed-off-by: Riku Voipio <riku.voipio@iki.fi>
-rw-r--r-- | linux-user/ioctls.h | 3 | ||||
-rw-r--r-- | linux-user/syscall.c | 96 |
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__ } }, |