diff options
author | Edgar E. Iglesias <edgar.iglesias@gmail.com> | 2013-09-23 14:11:53 +0200 |
---|---|---|
committer | Riku Voipio <riku.voipio@linaro.org> | 2013-09-24 10:47:07 +0300 |
commit | 53d09b761f032f50c4424e8649396a9041070bae (patch) | |
tree | e670304a2aabd0a3217b674b7f33142f8647ccf2 /linux-user | |
parent | 89aaf1a6ad91c4cb3224fcca461d71dac9fa3fa6 (diff) | |
download | qemu-53d09b761f032f50c4424e8649396a9041070bae.tar.gz qemu-53d09b761f032f50c4424e8649396a9041070bae.tar.bz2 qemu-53d09b761f032f50c4424e8649396a9041070bae.zip |
linux-user: Handle SOCK_CLOEXEC/NONBLOCK if unavailable on host
If the host lacks SOCK_CLOEXEC, bail out with -EINVAL.
If the host lacks SOCK_ONONBLOCK, try to emulate it with fcntl()
and O_NONBLOCK.
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
Diffstat (limited to 'linux-user')
-rw-r--r-- | linux-user/syscall.c | 40 |
1 files changed, 37 insertions, 3 deletions
diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b3822b32a0..4a14a43037 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1773,7 +1773,7 @@ static void unlock_iovec(struct iovec *vec, abi_ulong target_addr, free(vec); } -static inline void target_to_host_sock_type(int *type) +static inline int target_to_host_sock_type(int *type) { int host_type = 0; int target_type = *type; @@ -1790,22 +1790,56 @@ static inline void target_to_host_sock_type(int *type) break; } if (target_type & TARGET_SOCK_CLOEXEC) { +#if defined(SOCK_CLOEXEC) host_type |= SOCK_CLOEXEC; +#else + return -TARGET_EINVAL; +#endif } if (target_type & TARGET_SOCK_NONBLOCK) { +#if defined(SOCK_NONBLOCK) host_type |= SOCK_NONBLOCK; +#elif !defined(O_NONBLOCK) + return -TARGET_EINVAL; +#endif } *type = host_type; + return 0; +} + +/* Try to emulate socket type flags after socket creation. */ +static int sock_flags_fixup(int fd, int target_type) +{ +#if !defined(SOCK_NONBLOCK) && defined(O_NONBLOCK) + if (target_type & TARGET_SOCK_NONBLOCK) { + int flags = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, O_NONBLOCK | flags) == -1) { + close(fd); + return -TARGET_EINVAL; + } + } +#endif + return fd; } /* do_socket() Must return target values and target errnos. */ static abi_long do_socket(int domain, int type, int protocol) { - target_to_host_sock_type(&type); + int target_type = type; + int ret; + + ret = target_to_host_sock_type(&type); + if (ret) { + return ret; + } if (domain == PF_NETLINK) return -EAFNOSUPPORT; /* do not NETLINK socket connections possible */ - return get_errno(socket(domain, type, protocol)); + ret = get_errno(socket(domain, type, protocol)); + if (ret >= 0) { + ret = sock_flags_fixup(ret, target_type); + } + return ret; } /* do_bind() Must return target values and target errnos. */ |