bfd: fix partial write handling in bwritev()

When bfd is unbuffered, bwritev() used writev() directly, which can return
partial writes and leave checkpoint images incomplete.

Fix this by retrying writev() until all iovecs are written or an error occurs.
Temporarily modify the first iovec's base and length to track progress on
partial writes, then restore it before the next iteration.

Changes the function signature to accept non-const iovec, as we need to
modify it to track progress (same pattern as write_all() with buffers).

Signed-off-by: Ahmed Elaidy <elaidya225@gmail.com>
This commit is contained in:
Ahmed Elaidy 2026-03-31 15:19:12 +02:00 committed by Alexander Mikhalitsyn
parent b99cbc1e8c
commit d2caebfd4e
No known key found for this signature in database
GPG key ID: B1F47F5CB05B4FA3
2 changed files with 42 additions and 7 deletions

View file

@ -321,16 +321,51 @@ int bwrite(struct bfd *bfd, const void *buf, int size)
return __bwrite(bfd, buf, size);
}
int bwritev(struct bfd *bfd, const struct iovec *iov, int cnt)
int bwritev(struct bfd *bfd, struct iovec *iov, int cnt)
{
int i, written = 0;
if (!bfd_buffered(bfd)) {
/*
* FIXME writev() should be called again if writev() writes
* less bytes than requested.
*/
return writev(bfd->fd, iov, cnt);
size_t off = 0;
while (cnt) {
ssize_t ret;
/*
* Temporarily modify the first iovec to skip already-written
* bytes from previous partial writes, then restore it before
* the next iteration.
*/
iov[0].iov_base += off;
iov[0].iov_len -= off;
ret = writev(bfd->fd, iov, cnt);
iov[0].iov_base -= off;
iov[0].iov_len += off;
if (ret < 0) {
pr_perror("writev failed for fd %d", bfd->fd);
return ret;
}
if (ret == 0) {
errno = EIO;
pr_perror("writev made no progress (fd %d)", bfd->fd);
return -1;
}
written += ret;
ret += off;
while (ret) {
if (iov[0].iov_len > ret) {
off = ret;
break;
}
ret -= iov[0].iov_len;
iov++;
cnt--;
off = 0;
}
}
return written;
}
for (i = 0; i < cnt; i++) {

View file

@ -35,7 +35,7 @@ char *breadline(struct bfd *f);
char *breadchr(struct bfd *f, char c);
int bwrite(struct bfd *f, const void *buf, int sz);
struct iovec;
int bwritev(struct bfd *f, const struct iovec *iov, int cnt);
int bwritev(struct bfd *f, struct iovec *iov, int cnt);
int bread(struct bfd *f, void *buf, int sz);
int bfd_flush_images(void);
#endif