From 29d17becab12303b6e7d19e13ec0df2a364a323a Mon Sep 17 00:00:00 2001 From: Jindrich Novy Date: Fri, 18 Jul 2025 10:42:25 +0200 Subject: [PATCH] Fix k8s-file log corruption during log rotation The k8s-file log driver was corrupting log entries when the log file reached max_size and needed rotation. This manifested as garbled output with repeated timestamps and broken log entries. The root cause was in writev_buffer_flush() which incorrectly handled partial writes. When writev() returned a partial write, the function would modify the original iovec base pointers, corrupting the buffer state. During log rotation, this corrupted state would carry over to the new file descriptor, causing subsequent log entries to be malformed. Changes: - Simplify writev_buffer_flush() to use individual write() calls instead of complex writev() partial write handling - Always reset buffer state after log rotation to ensure clean state with new file descriptor - Remove conditional buffer reset that could leave corrupted state This fixes the issue where long-running containers (like GitLab CE) would produce corrupted logs after reaching the configured max_size. Fixes: Log corruption with --log-driver k8s-file --log-opt max_size=20mb Signed-off-by: Jindrich Novy --- src/ctr_logging.c | 48 +++++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/src/ctr_logging.c b/src/ctr_logging.c index 3270ff7..d472102 100644 --- a/src/ctr_logging.c +++ b/src/ctr_logging.c @@ -444,12 +444,13 @@ static int write_k8s_log(stdpipe_t pipe, const char *buf, ssize_t buflen) if ((log_size_max > 0) && (k8s_bytes_written + bytes_to_be_written) > log_size_max) { if (writev_buffer_flush(k8s_log_fd, &bufv) < 0) { nwarn("failed to flush buffer to log"); - /* - * We are going to reopen the file anyway, in case of - * errors discard all we have in the buffer. - */ - bufv.iovcnt = 0; } + /* + * Always reset the buffer after rotation to ensure clean state + * with the new file descriptor. Any unflushed data is lost, but + * this prevents corruption of subsequent log entries. + */ + bufv.iovcnt = 0; reopen_k8s_file(); } @@ -520,35 +521,28 @@ static bool get_line_len(ptrdiff_t *line_len, const char *buf, ssize_t buflen) static ssize_t writev_buffer_flush(int fd, writev_buffer_t *buf) { size_t count = 0; - int iovcnt = buf->iovcnt; - struct iovec *iov = buf->iov; - while (iovcnt > 0) { - ssize_t res; - do { - res = writev(fd, iov, iovcnt); - } while (res == -1 && errno == EINTR); + for (int i = 0; i < buf->iovcnt; i++) { + const char *ptr = buf->iov[i].iov_base; + size_t remaining = buf->iov[i].iov_len; - if (res <= 0) - return -1; - - count += res; - - while (res > 0) { - size_t from_this = MIN((size_t)res, iov->iov_len); - iov->iov_len -= from_this; - iov->iov_base += from_this; - res -= from_this; - - if (iov->iov_len == 0) { - iov++; - iovcnt--; + while (remaining > 0) { + ssize_t written = write(fd, ptr, remaining); + if (written < 0) { + if (errno == EINTR) + continue; + return -1; } + if (written == 0) + return -1; + + ptr += written; + remaining -= written; + count += written; } } buf->iovcnt = 0; - return count; }