Add preadv2/pwritev2 wrappers.
They're in glibc, though not in musl. Also add basic doc comments to the whole of <sys/uio.h>. Bug: http://b/203002492 Test: treehugger Change-Id: Ic607f7f349e5b7c9bf66c25b7bd68f827da530d6
This commit is contained in:
parent
ad60768bdf
commit
cf59e19e22
7 changed files with 263 additions and 34 deletions
|
@ -1121,6 +1121,7 @@ cc_library_static {
|
|||
"bionic/posix_fallocate.cpp",
|
||||
"bionic/posix_madvise.cpp",
|
||||
"bionic/posix_timers.cpp",
|
||||
"bionic/preadv_pwritev.cpp",
|
||||
"bionic/ptrace.cpp",
|
||||
"bionic/pty.cpp",
|
||||
"bionic/raise.cpp",
|
||||
|
|
|
@ -97,13 +97,14 @@ ssize_t pwrite64(int, void*, size_t, off64_t) lp32
|
|||
ssize_t pwrite64|pwrite(int, void*, size_t, off_t) lp64
|
||||
|
||||
# On LP32, preadv/pwritev don't use off64_t --- they use pairs of 32-bit
|
||||
# arguments to avoid problems on architectures like ARM where 64-bit arguments
|
||||
# arguments to avoid problems on architectures like arm32 where 64-bit arguments
|
||||
# must be in a register pair starting with an even-numbered register.
|
||||
# See linux/fs/read_write.c and https://lwn.net/Articles/311630/.
|
||||
ssize_t __preadv64:preadv(int, const struct iovec*, int, long, long) lp32
|
||||
ssize_t preadv|preadv64(int, const struct iovec*, int, off_t) lp64
|
||||
ssize_t __pwritev64:pwritev(int, const struct iovec*, int, long, long) lp32
|
||||
ssize_t pwritev|pwritev64(int, const struct iovec*, int, off_t) lp64
|
||||
# Note that there's an unused always-0 second long even on LP64!
|
||||
ssize_t __preadv64:preadv(int, const struct iovec*, int, long, long) all
|
||||
ssize_t __pwritev64:pwritev(int, const struct iovec*, int, long, long) all
|
||||
ssize_t __preadv64v2:preadv2(int, const struct iovec*, int, long, long, int) all
|
||||
ssize_t __pwritev64v2:pwritev2(int, const struct iovec*, int, long, long, int) all
|
||||
|
||||
int __close:close(int) all
|
||||
pid_t __getpid:getpid() all
|
||||
|
|
|
@ -45,8 +45,6 @@
|
|||
|
||||
// System calls we need.
|
||||
extern "C" int __llseek(int, unsigned long, unsigned long, off64_t*, int);
|
||||
extern "C" int __preadv64(int, const struct iovec*, int, long, long);
|
||||
extern "C" int __pwritev64(int, const struct iovec*, int, long, long);
|
||||
|
||||
// For lseek64 we need to use the llseek system call which splits the off64_t in two and
|
||||
// returns the off64_t result via a pointer because 32-bit kernels can't return 64-bit results.
|
||||
|
@ -70,23 +68,6 @@ ssize_t pwrite(int fd, const void* buf, size_t byte_count, off_t offset) {
|
|||
return pwrite64(fd, buf, byte_count, static_cast<off64_t>(offset));
|
||||
}
|
||||
|
||||
// On LP32, there is no off_t preadv/pwritev, and even the 64-bit preadv/pwritev
|
||||
// don't use off64_t (see SYSCALLS.TXT for more). Here, this means that we need
|
||||
// to implement all four functions because the two system calls don't match any
|
||||
// of the userspace functions. Unlike llseek, the pair is split lo-hi, not hi-lo.
|
||||
ssize_t preadv(int fd, const struct iovec* ios, int count, off_t offset) {
|
||||
return preadv64(fd, ios, count, offset);
|
||||
}
|
||||
ssize_t preadv64(int fd, const struct iovec* ios, int count, off64_t offset) {
|
||||
return __preadv64(fd, ios, count, offset, offset >> 32);
|
||||
}
|
||||
ssize_t pwritev(int fd, const struct iovec* ios, int count, off_t offset) {
|
||||
return pwritev64(fd, ios, count, offset);
|
||||
}
|
||||
ssize_t pwritev64(int fd, const struct iovec* ios, int count, off64_t offset) {
|
||||
return __pwritev64(fd, ios, count, offset, offset >> 32);
|
||||
}
|
||||
|
||||
// There is no fallocate for 32-bit off_t, so we need to widen and call fallocate64.
|
||||
int fallocate(int fd, int mode, off_t offset, off_t length) {
|
||||
return fallocate64(fd, mode, static_cast<off64_t>(offset), static_cast<off64_t>(length));
|
||||
|
|
91
libc/bionic/preadv_pwritev.cpp
Normal file
91
libc/bionic/preadv_pwritev.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/uio.h>
|
||||
|
||||
// System calls we need.
|
||||
extern "C" int __preadv64(int, const struct iovec*, int, long, long);
|
||||
extern "C" int __preadv64v2(int, const struct iovec*, int, long, long, int);
|
||||
extern "C" int __pwritev64(int, const struct iovec*, int, long, long);
|
||||
extern "C" int __pwritev64v2(int, const struct iovec*, int, long, long, int);
|
||||
|
||||
// There is no 32-bit off_t preadv/pwritev (even on LP32).
|
||||
// To avoid 32-bit ABI issues about which register pairs you're allowed
|
||||
// to pass 64-bit values in, the kernel just takes two `long` arguments --
|
||||
// which are int32_t for LP32, remember -- and stitches them together.
|
||||
// It even does this for LP64, taking a second unused always-zero `long`.
|
||||
// (The first long was int64_t, which is the same as off64_t.)
|
||||
// The pair is split lo-hi (not hi-lo, as llseek is).
|
||||
|
||||
ssize_t preadv(int fd, const struct iovec* ios, int count, off_t offset) {
|
||||
return preadv64(fd, ios, count, offset);
|
||||
}
|
||||
|
||||
ssize_t preadv64(int fd, const struct iovec* ios, int count, off64_t offset) {
|
||||
#if defined(__LP64__)
|
||||
return __preadv64(fd, ios, count, offset, 0);
|
||||
#else
|
||||
return __preadv64(fd, ios, count, offset, offset >> 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
ssize_t pwritev(int fd, const struct iovec* ios, int count, off_t offset) {
|
||||
return pwritev64(fd, ios, count, offset);
|
||||
}
|
||||
|
||||
ssize_t pwritev64(int fd, const struct iovec* ios, int count, off64_t offset) {
|
||||
#if defined(__LP64__)
|
||||
return __pwritev64(fd, ios, count, offset, 0);
|
||||
#else
|
||||
return __pwritev64(fd, ios, count, offset, offset >> 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
ssize_t preadv2(int fd, const struct iovec* ios, int count, off_t offset, int flags) {
|
||||
return preadv64v2(fd, ios, count, offset, flags);
|
||||
}
|
||||
|
||||
ssize_t preadv64v2(int fd, const struct iovec* ios, int count, off64_t offset, int flags) {
|
||||
#if defined(__LP64__)
|
||||
return __preadv64v2(fd, ios, count, offset, 0, flags);
|
||||
#else
|
||||
return __preadv64v2(fd, ios, count, offset, offset >> 32, flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
ssize_t pwritev2(int fd, const struct iovec* ios, int count, off_t offset, int flags) {
|
||||
return pwritev64v2(fd, ios, count, offset, flags);
|
||||
}
|
||||
|
||||
ssize_t pwritev64v2(int fd, const struct iovec* ios, int count, off64_t offset, int flags) {
|
||||
#if defined(__LP64__)
|
||||
return __pwritev64v2(fd, ios, count, offset, 0, flags);
|
||||
#else
|
||||
return __pwritev64v2(fd, ios, count, offset, offset >> 32, flags);
|
||||
#endif
|
||||
}
|
|
@ -26,8 +26,12 @@
|
|||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_UIO_H_
|
||||
#define _SYS_UIO_H_
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file sys/uio.h
|
||||
* @brief Multi-buffer ("vector") I/O operations using `struct iovec`.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -35,21 +39,124 @@
|
|||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* [readv(2)](http://man7.org/linux/man-pages/man2/readv.2.html) reads
|
||||
* from an fd into the `__count` buffers described by `__iov`.
|
||||
*
|
||||
* Returns the number of bytes read on success,
|
||||
* and returns -1 and sets `errno` on failure.
|
||||
*/
|
||||
ssize_t readv(int __fd, const struct iovec* __iov, int __count);
|
||||
|
||||
/**
|
||||
* [writev(2)](http://man7.org/linux/man-pages/man2/writev.2.html) writes
|
||||
* to an fd from the `__count` buffers described by `__iov`.
|
||||
*
|
||||
* Returns the number of bytes written on success,
|
||||
* and returns -1 and sets `errno` on failure.
|
||||
*/
|
||||
ssize_t writev(int __fd, const struct iovec* __iov, int __count);
|
||||
|
||||
#if defined(__USE_GNU)
|
||||
ssize_t preadv(int __fd, const struct iovec* __iov, int __count, off_t __offset) __RENAME_IF_FILE_OFFSET64(preadv64) __INTRODUCED_IN(24);
|
||||
ssize_t pwritev(int __fd, const struct iovec* __iov, int __count, off_t __offset) __RENAME_IF_FILE_OFFSET64(pwritev64) __INTRODUCED_IN(24);
|
||||
ssize_t preadv64(int __fd, const struct iovec* __iov, int __count, off64_t __offset) __INTRODUCED_IN(24);
|
||||
ssize_t pwritev64(int __fd, const struct iovec* __iov, int __count, off64_t __offset) __INTRODUCED_IN(24);
|
||||
#endif
|
||||
|
||||
#if defined(__USE_GNU)
|
||||
/**
|
||||
* [preadv(2)](http://man7.org/linux/man-pages/man2/preadv.2.html) reads
|
||||
* from an fd into the `__count` buffers described by `__iov`, starting at
|
||||
* offset `__offset` into the file.
|
||||
*
|
||||
* Returns the number of bytes read on success,
|
||||
* and returns -1 and sets `errno` on failure.
|
||||
*
|
||||
* Available since API level 24.
|
||||
*/
|
||||
ssize_t preadv(int __fd, const struct iovec* __iov, int __count, off_t __offset) __RENAME_IF_FILE_OFFSET64(preadv64) __INTRODUCED_IN(24);
|
||||
|
||||
/**
|
||||
* [pwritev(2)](http://man7.org/linux/man-pages/man2/pwritev.2.html) writes
|
||||
* to an fd from the `__count` buffers described by `__iov`, starting at offset
|
||||
* `__offset` into the file.
|
||||
*
|
||||
* Returns the number of bytes written on success,
|
||||
* and returns -1 and sets `errno` on failure.
|
||||
*
|
||||
* Available since API level 24.
|
||||
*/
|
||||
ssize_t pwritev(int __fd, const struct iovec* __iov, int __count, off_t __offset) __RENAME_IF_FILE_OFFSET64(pwritev64) __INTRODUCED_IN(24);
|
||||
|
||||
/**
|
||||
* Like preadv() but with a 64-bit offset even in a 32-bit process.
|
||||
*
|
||||
* Available since API level 24.
|
||||
*/
|
||||
ssize_t preadv64(int __fd, const struct iovec* __iov, int __count, off64_t __offset) __INTRODUCED_IN(24);
|
||||
|
||||
/**
|
||||
* Like pwritev() but with a 64-bit offset even in a 32-bit process.
|
||||
*
|
||||
* Available since API level 24.
|
||||
*/
|
||||
ssize_t pwritev64(int __fd, const struct iovec* __iov, int __count, off64_t __offset) __INTRODUCED_IN(24);
|
||||
|
||||
/**
|
||||
* [preadv2(2)](http://man7.org/linux/man-pages/man2/preadv2.2.html) reads
|
||||
* from an fd into the `__count` buffers described by `__iov`, starting at
|
||||
* offset `__offset` into the file, with the given flags.
|
||||
*
|
||||
* Returns the number of bytes read on success,
|
||||
* and returns -1 and sets `errno` on failure.
|
||||
*
|
||||
* Available since API level 33.
|
||||
*/
|
||||
ssize_t preadv2(int __fd, const struct iovec* __iov, int __count, off_t __offset, int __flags) __RENAME_IF_FILE_OFFSET64(preadv64v2) __INTRODUCED_IN(33);
|
||||
|
||||
/**
|
||||
* [pwritev2(2)](http://man7.org/linux/man-pages/man2/pwritev2.2.html) writes
|
||||
* to an fd from the `__count` buffers described by `__iov`, starting at offset
|
||||
* `__offset` into the file, with the given flags.
|
||||
*
|
||||
* Returns the number of bytes written on success,
|
||||
* and returns -1 and sets `errno` on failure.
|
||||
*
|
||||
* Available since API level 33.
|
||||
*/
|
||||
ssize_t pwritev2(int __fd, const struct iovec* __iov, int __count, off_t __offset, int __flags) __RENAME_IF_FILE_OFFSET64(pwritev64v2) __INTRODUCED_IN(33);
|
||||
|
||||
/**
|
||||
* Like preadv2() but with a 64-bit offset even in a 32-bit process.
|
||||
*
|
||||
* Available since API level 33.
|
||||
*/
|
||||
ssize_t preadv64v2(int __fd, const struct iovec* __iov, int __count, off64_t __offset, int __flags) __INTRODUCED_IN(33);
|
||||
|
||||
/**
|
||||
* Like pwritev2() but with a 64-bit offset even in a 32-bit process.
|
||||
*
|
||||
* Available since API level 33.
|
||||
*/
|
||||
ssize_t pwritev64v2(int __fd, const struct iovec* __iov, int __count, off64_t __offset, int __flags) __INTRODUCED_IN(33);
|
||||
|
||||
/**
|
||||
* [process_vm_readv(2)](http://man7.org/linux/man-pages/man2/process_vm_readv.2.html)
|
||||
* reads from the address space of another process.
|
||||
*
|
||||
* Returns the number of bytes read on success,
|
||||
* and returns -1 and sets `errno` on failure.
|
||||
*
|
||||
* Available since API level 23.
|
||||
*/
|
||||
ssize_t process_vm_readv(pid_t __pid, const struct iovec* __local_iov, unsigned long __local_iov_count, const struct iovec* __remote_iov, unsigned long __remote_iov_count, unsigned long __flags) __INTRODUCED_IN(23);
|
||||
|
||||
/**
|
||||
* [process_vm_writev(2)](http://man7.org/linux/man-pages/man2/process_vm_writev.2.html)
|
||||
* writes to the address space of another process.
|
||||
*
|
||||
* Returns the number of bytes read on success,
|
||||
* and returns -1 and sets `errno` on failure.
|
||||
*
|
||||
* Available since API level 23.
|
||||
*/
|
||||
ssize_t process_vm_writev(pid_t __pid, const struct iovec* __local_iov, unsigned long __local_iov_count, const struct iovec* __remote_iov, unsigned long __remote_iov_count, unsigned long __flags) __INTRODUCED_IN(23);
|
||||
|
||||
#endif
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1565,6 +1565,14 @@ LIBC_S { # introduced=S
|
|||
process_madvise;
|
||||
} LIBC_R;
|
||||
|
||||
LIBC_T { # introduced=Tiramisu
|
||||
global:
|
||||
preadv2;
|
||||
preadv64v2;
|
||||
pwritev2;
|
||||
pwritev64v2;
|
||||
} LIBC_S;
|
||||
|
||||
LIBC_PRIVATE {
|
||||
global:
|
||||
__accept4; # arm x86
|
||||
|
|
|
@ -16,7 +16,11 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
|
@ -68,6 +72,42 @@ TEST(sys_uio, preadv64_pwritev64) {
|
|||
TestPreadVPwriteV(preadv64, pwritev64);
|
||||
}
|
||||
|
||||
template <typename ReadFn, typename WriteFn>
|
||||
void TestPreadV2PwriteV2(ReadFn read_fn, WriteFn write_fn) {
|
||||
TemporaryFile tf;
|
||||
|
||||
char buf[] = "world";
|
||||
iovec ios[] = {{buf, 5}};
|
||||
|
||||
ASSERT_EQ(5, write_fn(tf.fd, ios, 1, 5, 0)) << strerror(errno);
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_CUR));
|
||||
|
||||
strcpy(buf, "hello");
|
||||
ASSERT_EQ(5, write_fn(tf.fd, ios, 1, 0, 0)) << strerror(errno);
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_CUR));
|
||||
|
||||
ASSERT_EQ(5, read_fn(tf.fd, ios, 1, 5, 0)) << strerror(errno);
|
||||
ASSERT_STREQ("world", buf);
|
||||
ASSERT_EQ(5, read_fn(tf.fd, ios, 1, 0, 0)) << strerror(errno);
|
||||
ASSERT_STREQ("hello", buf);
|
||||
}
|
||||
|
||||
TEST(sys_uio, preadv2_pwritev2) {
|
||||
#if defined(__BIONIC__)
|
||||
TestPreadV2PwriteV2(preadv2, pwritev2);
|
||||
#else
|
||||
GTEST_SKIP() << "preadv2/pwritev2 not available";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(sys_uio, preadv64v2_pwritev64v2) {
|
||||
#if defined(__BIONIC__)
|
||||
TestPreadV2PwriteV2(preadv64v2, pwritev64v2);
|
||||
#else
|
||||
GTEST_SKIP() << "preadv2/pwritev2 not available";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(sys_uio, process_vm_readv) {
|
||||
ASSERT_EQ(0, process_vm_readv(0, nullptr, 0, nullptr, 0, 0));
|
||||
|
||||
|
|
Loading…
Reference in a new issue