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:
Daan De Meyer
2026-10-16 17:24:51 +01:00
committed by Luca Boccassi
parent fcaf217ab2
commit 8b9fb1bbec
3 changed files with 102 additions and 0 deletions
+50
View File
@@ -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;
+3
View File
@@ -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);