From d2caebfd4e2b2c7bcaebe7760a7d75be413900b2 Mon Sep 17 00:00:00 2001 From: Ahmed Elaidy Date: Tue, 31 Mar 2026 15:19:12 +0200 Subject: [PATCH] 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 --- criu/bfd.c | 47 ++++++++++++++++++++++++++++++++++++++++------ criu/include/bfd.h | 2 +- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/criu/bfd.c b/criu/bfd.c index 4daec3d14..273352523 100644 --- a/criu/bfd.c +++ b/criu/bfd.c @@ -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++) { diff --git a/criu/include/bfd.h b/criu/include/bfd.h index 050158327..b9fbc820b 100644 --- a/criu/include/bfd.h +++ b/criu/include/bfd.h @@ -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