aboutsummaryrefslogtreecommitdiff
path: root/run-command.c
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2022-08-25 14:42:31 -0700
committerJunio C Hamano <gitster@pobox.com>2022-08-25 14:42:32 -0700
commita103ad6f3d58cf3d297a6b102876e2bbf09c98d7 (patch)
tree763c63d41c7c3d9fadf0ebbe76e9fc90f81743a8 /run-command.c
parent098b7bfaa6c415f2b23885b82d6b5a31a8cdb7d2 (diff)
parent716c1f649e372a0784b9826cd3839e7b373e2ea9 (diff)
downloadgit-a103ad6f3d58cf3d297a6b102876e2bbf09c98d7.tar.xz
Merge branch 'jk/pipe-command-nonblock'
Fix deadlocks between main Git process and subprocess spawned via the pipe_command() API, that can kill "git add -p" that was reimplemented in C recently. * jk/pipe-command-nonblock: pipe_command(): mark stdin descriptor as non-blocking pipe_command(): handle ENOSPC when writing to a pipe pipe_command(): avoid xwrite() for writing to pipe git-compat-util: make MAX_IO_SIZE define globally available nonblock: support Windows compat: add function to enable nonblocking pipes
Diffstat (limited to 'run-command.c')
-rw-r--r--run-command.c33
1 files changed, 28 insertions, 5 deletions
diff --git a/run-command.c b/run-command.c
index 14f17830f5..5ec3a46dcc 100644
--- a/run-command.c
+++ b/run-command.c
@@ -10,6 +10,7 @@
#include "config.h"
#include "packfile.h"
#include "hook.h"
+#include "compat/nonblock.h"
void child_process_init(struct child_process *child)
{
@@ -1364,12 +1365,25 @@ static int pump_io_round(struct io_pump *slots, int nr, struct pollfd *pfd)
continue;
if (io->type == POLLOUT) {
- ssize_t len = xwrite(io->fd,
- io->u.out.buf, io->u.out.len);
+ ssize_t len;
+
+ /*
+ * Don't use xwrite() here. It loops forever on EAGAIN,
+ * and we're in our own poll() loop here.
+ *
+ * Note that we lose xwrite()'s handling of MAX_IO_SIZE
+ * and EINTR, so we have to implement those ourselves.
+ */
+ len = write(io->fd, io->u.out.buf,
+ io->u.out.len <= MAX_IO_SIZE ?
+ io->u.out.len : MAX_IO_SIZE);
if (len < 0) {
- io->error = errno;
- close(io->fd);
- io->fd = -1;
+ if (errno != EINTR && errno != EAGAIN &&
+ errno != ENOSPC) {
+ io->error = errno;
+ close(io->fd);
+ io->fd = -1;
+ }
} else {
io->u.out.buf += len;
io->u.out.len -= len;
@@ -1438,6 +1452,15 @@ int pipe_command(struct child_process *cmd,
return -1;
if (in) {
+ if (enable_pipe_nonblock(cmd->in) < 0) {
+ error_errno("unable to make pipe non-blocking");
+ close(cmd->in);
+ if (out)
+ close(cmd->out);
+ if (err)
+ close(cmd->err);
+ return -1;
+ }
io[nr].fd = cmd->in;
io[nr].type = POLLOUT;
io[nr].u.out.buf = in;