io-util: add pwritev_full() and pwrite_full()
loop_write() writes one buffer at the file position. To write at an offset, callers call pwrite() or pwritev() directly and handle short writes themselves. Add pwritev_full(), which writes all iovecs at an offset, retries on EINTR and short writes, splits batches larger than IOV_MAX, and reports the number of bytes written, also on failure. Add pwrite_full(), which does the same for one buffer. (cherry picked from commit 4d36be491a568180dc6336b91b0c496bb8cb6601)
This commit is contained in:
committed by
Luca Boccassi
parent
fcaf217ab2
commit
8b9fb1bbec
@@ -11,6 +11,7 @@
|
||||
#include "errno-util.h"
|
||||
#include "fiber-ops.h"
|
||||
#include "io-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
uint32_t poll_events_to_epoll(uint32_t events) {
|
||||
@@ -249,6 +250,55 @@ int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pwritev_full(int fd, struct iovec *iovec, size_t n, uint64_t offset, uint64_t *ret_written) {
|
||||
uint64_t written = 0;
|
||||
int r = 0;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(iovec || n == 0);
|
||||
|
||||
/* Writes all of the iovecs at the offset. The iovecs are advanced past what was written, and
|
||||
* 'ret_written' tells how much that was, also if the write fails halfway. */
|
||||
|
||||
for (size_t i = 0;;) {
|
||||
size_t m;
|
||||
ssize_t k;
|
||||
|
||||
while (i < n && !iovec_is_set(iovec + i))
|
||||
i++;
|
||||
if (i >= n)
|
||||
break;
|
||||
|
||||
m = MIN(n - i, (size_t) IOV_MAX);
|
||||
k = pwritev(fd, iovec + i, m, offset + written);
|
||||
if (k < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
r = -errno;
|
||||
break;
|
||||
}
|
||||
if (k == 0) {
|
||||
r = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
written += k;
|
||||
(void) iovec_inc_many(iovec + i, m, k);
|
||||
}
|
||||
|
||||
if (ret_written)
|
||||
*ret_written = written;
|
||||
return r;
|
||||
}
|
||||
|
||||
int pwrite_full(int fd, const void *buf, size_t nbytes, uint64_t offset) {
|
||||
assert(fd >= 0);
|
||||
assert(buf || nbytes == 0);
|
||||
|
||||
return pwritev_full(fd, &IOVEC_MAKE((void*) buf, nbytes), 1, offset, /* ret_written= */ NULL);
|
||||
}
|
||||
|
||||
int pipe_eof(int fd) {
|
||||
int r;
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ static inline int loop_write(int fd, const void *buf, size_t nbytes) {
|
||||
return loop_write_full(fd, buf, nbytes, 0);
|
||||
}
|
||||
|
||||
int pwritev_full(int fd, struct iovec *iovec, size_t n, uint64_t offset, uint64_t *ret_written);
|
||||
int pwrite_full(int fd, const void *buf, size_t nbytes, uint64_t offset) _nonnull_if_nonzero_(2, 3);
|
||||
|
||||
int pipe_eof(int fd);
|
||||
|
||||
int ppoll_usec_full(struct pollfd *fds, size_t n_fds, usec_t timeout, const sigset_t *ss) _nonnull_if_nonzero_(1, 2);
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
#include "fd-util.h"
|
||||
#include "io-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "tests.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
static void test_sparse_write_one(int fd, const char *buffer, size_t n) {
|
||||
char check[n];
|
||||
@@ -43,4 +45,51 @@ TEST(sparse_write) {
|
||||
test_sparse_write_one(fd, test_e, sizeof(test_e));
|
||||
}
|
||||
|
||||
TEST(pwritev_full) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
struct iovec iovec[2 + 2 * IOV_MAX];
|
||||
char expected[3 + 2 * IOV_MAX], buf[sizeof(expected)];
|
||||
uint64_t written;
|
||||
size_t n = 0;
|
||||
|
||||
ASSERT_OK(fd = open_tmpfile_unlinkable(NULL, O_RDWR|O_CLOEXEC));
|
||||
|
||||
/* Nothing, or only empty iovecs, writes nothing */
|
||||
ASSERT_OK(pwritev_full(fd, NULL, 0, 0, &written));
|
||||
ASSERT_EQ(written, 0U);
|
||||
iovec[0] = (struct iovec) {};
|
||||
ASSERT_OK(pwritev_full(fd, iovec, 1, 0, &written));
|
||||
ASSERT_EQ(written, 0U);
|
||||
|
||||
/* More iovecs than one pwritev() takes, some of them empty */
|
||||
iovec[n++] = IOVEC_MAKE_STRING("abc");
|
||||
iovec[n++] = (struct iovec) {};
|
||||
memcpy(expected, "abc", 3);
|
||||
for (size_t k = 0; k < 2 * IOV_MAX; k++) {
|
||||
expected[3 + k] = 'a' + k % 26;
|
||||
iovec[n++] = IOVEC_MAKE(expected + 3 + k, 1);
|
||||
}
|
||||
|
||||
ASSERT_OK(pwritev_full(fd, iovec, n, 7, &written));
|
||||
ASSERT_EQ(written, sizeof(expected));
|
||||
ASSERT_EQ(pread(fd, buf, sizeof(buf), 7), (ssize_t) sizeof(buf));
|
||||
ASSERT_EQ(memcmp(buf, expected, sizeof(expected)), 0);
|
||||
|
||||
/* Written iovecs are advanced past the data */
|
||||
FOREACH_ARRAY(i, iovec, n)
|
||||
ASSERT_FALSE(iovec_is_set(i));
|
||||
}
|
||||
|
||||
TEST(pwrite_full) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
char buf[3];
|
||||
|
||||
ASSERT_OK(fd = open_tmpfile_unlinkable(NULL, O_RDWR|O_CLOEXEC));
|
||||
|
||||
ASSERT_OK(pwrite_full(fd, NULL, 0, 0));
|
||||
ASSERT_OK(pwrite_full(fd, "abc", 3, 5));
|
||||
ASSERT_EQ(pread(fd, buf, sizeof(buf), 5), (ssize_t) sizeof(buf));
|
||||
ASSERT_EQ(memcmp(buf, "abc", 3), 0);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
||||
Reference in New Issue
Block a user