diff options
author | Nico Weber <thakis@chromium.org> | 2020-09-16 11:45:00 -0400 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-09-17 17:23:01 +0200 |
commit | b36a2d66865ae8205b6a7589b8566f27746002f4 (patch) | |
tree | f3d72289d78ca87784817cdeaf3552bd8ad05620 /DevTools/UserspaceEmulator | |
parent | 19f2203b53d3f895b4335bb1e46f703168288d4f (diff) | |
download | serenity-b36a2d66865ae8205b6a7589b8566f27746002f4.zip |
Kernel+LibC+UserspaceEmulator: Mostly add recvmsg(), sendmsg()
The implementation only supports a single iovec for now.
Some might say having more than one iovec is the main point of
recvmsg() and sendmsg(), but I'm interested in the control message
bits.
Diffstat (limited to 'DevTools/UserspaceEmulator')
-rw-r--r-- | DevTools/UserspaceEmulator/Emulator.cpp | 80 | ||||
-rw-r--r-- | DevTools/UserspaceEmulator/Emulator.h | 4 |
2 files changed, 51 insertions, 33 deletions
diff --git a/DevTools/UserspaceEmulator/Emulator.cpp b/DevTools/UserspaceEmulator/Emulator.cpp index 657a8fa390..f523758f7c 100644 --- a/DevTools/UserspaceEmulator/Emulator.cpp +++ b/DevTools/UserspaceEmulator/Emulator.cpp @@ -43,6 +43,7 @@ #include <sys/socket.h> #include <sys/stat.h> #include <sys/time.h> +#include <sys/uio.h> #include <termios.h> #include <unistd.h> @@ -354,10 +355,10 @@ u32 Emulator::virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3) return virt$listen(arg1, arg2); case SC_select: return virt$select(arg1); - case SC_sendto: - return virt$sendto(arg1); - case SC_recvfrom: - return virt$recvfrom(arg1); + case SC_recvmsg: + return virt$recvmsg(arg1, arg2, arg3); + case SC_sendmsg: + return virt$sendmsg(arg1, arg2, arg3); case SC_kill: return virt$kill(arg1, arg2); case SC_set_mmap_name: @@ -593,49 +594,66 @@ int Emulator::virt$socket(int domain, int type, int protocol) return syscall(SC_socket, domain, type, protocol); } -int Emulator::virt$recvfrom(FlatPtr params_addr) +int Emulator::virt$recvmsg(int sockfd, FlatPtr msg_addr, int flags) { - Syscall::SC_recvfrom_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - auto buffer = ByteBuffer::create_uninitialized(params.buffer.size); + msghdr mmu_msg; + mmu().copy_from_vm(&mmu_msg, msg_addr, sizeof(mmu_msg)); - if (!params.addr_length && params.addr) - return -EINVAL; + if (mmu_msg.msg_iovlen != 1) + return -ENOTSUP; // FIXME: Support this :) + iovec mmu_iov; + mmu().copy_from_vm(&mmu_iov, (FlatPtr)mmu_msg.msg_iov, sizeof(mmu_iov)); + auto buffer = ByteBuffer::create_uninitialized(mmu_iov.iov_len); - socklen_t address_length = 0; - if (params.addr_length) - mmu().copy_from_vm(&address_length, (FlatPtr)params.addr_length, sizeof(address_length)); - - sockaddr_storage address; - if (params.addr) - mmu().copy_from_vm(&address, (FlatPtr)params.addr, min(sizeof(address), (size_t)address_length)); + ByteBuffer control_buffer; + if (mmu_msg.msg_control) + control_buffer = ByteBuffer::create_uninitialized(mmu_msg.msg_controllen); - int rc = recvfrom(params.sockfd, buffer.data(), buffer.size(), params.flags, params.addr ? (struct sockaddr*)&address : nullptr, params.addr_length ? &address_length : nullptr); + sockaddr_storage addr; + iovec iov = { buffer.data(), buffer.size() }; + msghdr msg = { &addr, sizeof(addr), &iov, 1, mmu_msg.msg_control ? control_buffer.data() : nullptr, mmu_msg.msg_controllen, mmu_msg.msg_flags }; + int rc = recvmsg(sockfd, &msg, flags); if (rc < 0) return -errno; - mmu().copy_to_vm((FlatPtr)params.buffer.data, buffer.data(), buffer.size()); - - if (params.addr) - mmu().copy_to_vm((FlatPtr)params.addr, &address, address_length); - if (params.addr_length) - mmu().copy_to_vm((FlatPtr)params.addr_length, &address_length, sizeof(address_length)); + mmu().copy_to_vm((FlatPtr)mmu_iov.iov_base, buffer.data(), mmu_iov.iov_len); + if (mmu_msg.msg_name) + mmu().copy_to_vm((FlatPtr)mmu_msg.msg_name, &addr, min(sizeof(addr), (size_t)mmu_msg.msg_namelen)); + if (mmu_msg.msg_control) + mmu().copy_to_vm((FlatPtr)mmu_msg.msg_control, control_buffer.data(), min(mmu_msg.msg_controllen, msg.msg_controllen)); + mmu_msg.msg_namelen = msg.msg_namelen; + mmu_msg.msg_controllen = msg.msg_controllen; + mmu_msg.msg_flags = msg.msg_flags; + mmu().copy_to_vm(msg_addr, &mmu_msg, sizeof(mmu_msg)); return rc; } -int Emulator::virt$sendto(FlatPtr params_addr) +int Emulator::virt$sendmsg(int sockfd, FlatPtr msg_addr, int flags) { - Syscall::SC_sendto_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); + msghdr mmu_msg; + mmu().copy_from_vm(&mmu_msg, msg_addr, sizeof(mmu_msg)); + + if (mmu_msg.msg_iovlen != 1) + return -ENOTSUP; // FIXME: Support this :) + iovec mmu_iov; + mmu().copy_from_vm(&mmu_iov, (FlatPtr)mmu_msg.msg_iov, sizeof(mmu_iov)); + auto buffer = mmu().copy_buffer_from_vm((FlatPtr)mmu_iov.iov_base, mmu_iov.iov_len); - auto buffer = mmu().copy_buffer_from_vm((FlatPtr)params.data.data, params.data.size); + ByteBuffer control_buffer; + if (mmu_msg.msg_control) + control_buffer = ByteBuffer::create_uninitialized(mmu_msg.msg_controllen); sockaddr_storage address; - if (params.addr) - mmu().copy_from_vm(&address, (FlatPtr)params.addr, min(sizeof(address), (size_t)params.addr_length)); + socklen_t address_length = 0; + if (mmu_msg.msg_name) { + address_length = min(sizeof(address), (size_t)mmu_msg.msg_namelen); + mmu().copy_from_vm(&address, (FlatPtr)mmu_msg.msg_name, address_length); + } - return sendto(params.sockfd, buffer.data(), buffer.size(), params.flags, params.addr ? (struct sockaddr*)&address : nullptr, params.addr_length); + iovec iov = { buffer.data(), buffer.size() }; + msghdr msg = { mmu_msg.msg_name ? &address : nullptr, address_length, &iov, 1, mmu_msg.msg_control ? control_buffer.data() : nullptr, mmu_msg.msg_controllen, mmu_msg.msg_flags }; + return sendmsg(sockfd, &msg, flags); } int Emulator::virt$select(FlatPtr params_addr) diff --git a/DevTools/UserspaceEmulator/Emulator.h b/DevTools/UserspaceEmulator/Emulator.h index b914ad9513..6cef885ba4 100644 --- a/DevTools/UserspaceEmulator/Emulator.h +++ b/DevTools/UserspaceEmulator/Emulator.h @@ -131,8 +131,8 @@ private: int virt$select(FlatPtr); int virt$accept(int sockfd, FlatPtr address, FlatPtr address_length); int virt$bind(int sockfd, FlatPtr address, socklen_t address_length); - int virt$recvfrom(FlatPtr); - int virt$sendto(FlatPtr); + int virt$recvmsg(int sockfd, FlatPtr msg_addr, int flags); + int virt$sendmsg(int sockfd, FlatPtr msg_addr, int flags); int virt$connect(int sockfd, FlatPtr address, socklen_t address_size); void virt$exit(int); ssize_t virt$getrandom(FlatPtr buffer, size_t buffer_size, unsigned int flags); |