diff options
Diffstat (limited to 'sysdeps/unix/sysv/linux/sendmmsg.c')
-rw-r--r-- | sysdeps/unix/sysv/linux/sendmmsg.c | 72 |
1 files changed, 51 insertions, 21 deletions
diff --git a/sysdeps/unix/sysv/linux/sendmmsg.c b/sysdeps/unix/sysv/linux/sendmmsg.c index 6e0d46be9e..9b193438ce 100644 --- a/sysdeps/unix/sysv/linux/sendmmsg.c +++ b/sysdeps/unix/sysv/linux/sendmmsg.c @@ -16,12 +16,10 @@ License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>. */ -#include <errno.h> #include <sys/socket.h> - +#include <socketcall.h> #include <sysdep-cancel.h> -#include <sys/syscall.h> -#include <kernel-features.h> +#include <shlib-compat.h> /* Do not use the sendmmsg syscall on socketcall architectures unless it was added at the same time as the socketcall support or can be @@ -32,31 +30,53 @@ # undef __NR_sendmmsg #endif -#ifdef __NR_sendmmsg +#if __WORDSIZE == 64 +static inline int +send_mmsghdr (int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags) +{ + /* Emulate kernel interface for vlen size. */ + if (vlen > IOV_MAX) + vlen = IOV_MAX; + if (vlen == 0) + return 0; + /* POSIX specifies that both msghdr::msg_iovlen and msghdr::msg_controllen + to be int and socklen_t respectively, however Linux defines it as both + size_t. So for 64-bit it requires some adjustments by copying to + temporary header and zeroing the pad fields. + The problem is sendmmsg's msghdr may points to an already-filled control + buffer and modifying it is not part of sendmmsg contract (the data may + be in ro map). So interact over the msghdr calling the sendmsg that + adjust the header using a temporary buffer. */ + for (unsigned int i = 0; i < vlen; i++) + { + ssize_t ret = __sendmsg (fd, &vmessages[i].msg_hdr, flags); + if (ret < 0) + return -1; + vmessages[i].msg_len = ret; + } + return 1; +} +#endif + int __sendmmsg (int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags) { +#if __WORDSIZE == 64 + return send_mmsghdr (fd, vmessages, vlen, flags); +#elif defined __NR_sendmmsg return SYSCALL_CANCEL (sendmmsg, fd, vmessages, vlen, flags); -} -libc_hidden_def (__sendmmsg) -weak_alias (__sendmmsg, sendmmsg) #elif defined __NR_socketcall -# include <socketcall.h> # ifdef __ASSUME_SENDMMSG_SOCKETCALL -int -__sendmmsg (int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags) -{ return SOCKETCALL_CANCEL (sendmmsg, fd, vmessages, vlen, flags); -} # else -static int have_sendmmsg; - -int -__sendmmsg (int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags) -{ + static int have_sendmmsg; if (__glibc_likely (have_sendmmsg >= 0)) { +# if __WORDSIZE == 64 + int ret = send_mmsghdr (fd, vmessages, vlen, flags); +# else int ret = SOCKETCALL_CANCEL (sendmmsg, fd, vmessages, vlen, flags); +# endif /* The kernel returns -EINVAL for unknown socket operations. We need to convert that error to an ENOSYS error. */ if (__builtin_expect (ret < 0, 0) @@ -84,10 +104,20 @@ __sendmmsg (int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags) } __set_errno (ENOSYS); return -1; -} # endif /* __ASSUME_SENDMMSG_SOCKETCALL */ +#else /* defined __NR_socketcall */ +# define STUB 1 + __set_errno (ENOSYS); + return -1; +#endif +} +#ifdef STUB +stub_warning (sendmmsg) +#endif + libc_hidden_def (__sendmmsg) -weak_alias (__sendmmsg, sendmmsg) +#if __WORDSIZE == 64 +versioned_symbol (libc, __sendmmsg, sendmmsg, GLIBC_2_24); #else -# include <socket/sendmmsg.c> +weak_alias (__sendmmsg, sendmmsg) #endif |