]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
lxc/{terminal, file_utils}: ensure complete data writes in ptx/peer io handlers
authorDreamConnected <1487442471@qq.com>
Sun, 26 Oct 2025 05:28:13 +0000 (13:28 +0800)
committerAlexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Thu, 22 Jan 2026 11:59:05 +0000 (12:59 +0100)
Previously, lxc_write_nointr could return without writing all data
when write() returned EAGAIN/EWOULDBLOCK due to buffer full conditions.

This change:
- Implements a loop to continue writing until all data is sent
- Handles EINTR, EAGAIN, and EWOULDBLOCK errors appropriately
- Uses poll() to wait for fd to become ready when blocked
- Maintains backward compatibility while fixing partial write issues

Signed-off-by: DreamConnected <1487442471@qq.com>
[ alex ]
- introduce a separate helper lxc_write_all and use it only in ptx/peer
  io handlers
- cleanup the code a bit
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
src/lxc/file_utils.c
src/lxc/file_utils.h
src/lxc/terminal.c

index e8bf9321b0402eb5eb868f3680cfa80549294fbc..ea54939f30dfdbbb46cabbee861a72e63bb4f7f0 100644 (file)
@@ -11,6 +11,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <time.h>
+#include <poll.h>
 
 #include "file_utils.h"
 #include "macro.h"
@@ -147,6 +148,91 @@ ssize_t lxc_read_try_buf_at(int dfd, const char *path, void *buf, size_t count)
        return ret;
 }
 
+static int __lxc_wait_for_io_ready(int fd, int event, int timeout_ms)
+{
+       int ret;
+       struct pollfd pfd = {
+               .fd = fd,
+               .events = event,
+               .revents = 0
+       };
+
+       do {
+               ret = poll(&pfd, 1, timeout_ms);
+       } while (ret < 0 && errno == EINTR);
+
+       if (ret < 0)
+               return -errno;
+
+       if (ret == 0)
+               return -ETIMEDOUT;
+
+       if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
+               if (pfd.revents & POLLERR)
+                       return -EPIPE;
+               if (pfd.revents & POLLHUP)
+                       return -EPIPE;
+               if (pfd.revents & POLLNVAL)
+                       return -EBADF;
+       }
+
+       if (!(pfd.revents & event))
+               return -EAGAIN;
+
+       return ret;
+}
+
+ssize_t lxc_write_all(int fd, const void *buf, size_t count)
+{
+       ssize_t left_to_write = count;
+       const char *ptr = buf;
+       int flags;
+
+       flags = fcntl(fd, F_GETFL);
+       if (flags < 0)
+               return ret_set_errno(-1, errno);
+
+       /* only non-blocking fds are allowed */
+       if (!(flags & O_NONBLOCK))
+               return ret_set_errno(-1, EINVAL);
+
+       while (left_to_write > 0) {
+               int ret = write(fd, ptr, left_to_write);
+
+               if (ret > 0) {
+                       left_to_write -= ret;
+                       ptr += ret;
+                       continue;
+               }
+
+               if (ret == 0)
+                       break;
+
+               /* ret < 0 */
+               if (errno == EINTR)
+                       continue;
+
+               if (errno == EAGAIN) {
+                       int pret = __lxc_wait_for_io_ready(fd, POLLOUT, 5000);
+
+                       /* we've got an event on fd */
+                       if (pret > 0)
+                               continue;
+
+                       if (pret == -ETIMEDOUT)
+                               break;
+
+                       if (pret < 0)
+                               return ret_set_errno(-1, -pret);
+               }
+
+               /* some other error */
+               return ret_set_errno(-1, errno);
+       }
+
+       return count - left_to_write;
+}
+
 ssize_t lxc_write_nointr(int fd, const void *buf, size_t count)
 {
        ssize_t ret;
index 4fcaa47855abfb02185ef890be8d83c4035b41e0..7d277dad33660bef45b815b5f492cfa8cba69e5f 100644 (file)
@@ -35,6 +35,8 @@ __hidden extern int lxc_read_from_file(const char *filename, void *buf, size_t c
     __access_w(2, 3);
 
 /* send and receive buffers completely */
+__hidden extern ssize_t lxc_write_all(int fd, const void *buf, size_t count) __access_r(2, 3);
+
 __hidden extern ssize_t lxc_write_nointr(int fd, const void *buf, size_t count) __access_r(2, 3);
 
 __hidden extern ssize_t lxc_pwrite_nointr(int fd, const void *buf, size_t count, off_t offset)
index 86fe785b6cb71c5640c55a0d603b5ac74d74b17a..d066c2b9ee65a39ba9f0e7bce9e685bfd1dfec99 100644 (file)
@@ -338,7 +338,7 @@ static int lxc_terminal_ptx_io(struct lxc_terminal *terminal)
        w_rbuf = w_log = 0;
        /* write to peer first */
        if (terminal->peer >= 0)
-               w = lxc_write_nointr(terminal->peer, buf, r);
+               w = lxc_write_all(terminal->peer, buf, r);
 
        /* write to terminal ringbuffer */
        if (terminal->buffer_size > 0)
@@ -375,7 +375,7 @@ static int lxc_terminal_peer_io(struct lxc_terminal *terminal)
                return -1;
        }
 
-       w = lxc_write_nointr(terminal->ptx, buf, r);
+       w = lxc_write_all(terminal->ptx, buf, r);
        if (w != r)
                WARN("Short write on terminal r:%d != w:%d", r, w);