From 8554332894d5be615f192298a4f2a1e49b78be30 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Fri, 24 Jul 2020 22:29:37 +0200 Subject: [PATCH] rclone: Close rclone side of stdio_conn pipes restic did not notice when the rclone subprocess exited unexpectedly. restic manually created pipes for stdin and stdout and used these for the connection to the rclone subprocess. The process creating a pipe gets file descriptors for the sender and receiver side of a pipe and passes them on to the subprocess. The expected behavior would be that reads or writes in the parent process fail / return once the child process dies as a pipe would now just have a reader or writer but not both. However, this never happened as restic kept the reader and writer file descriptors of the pipes. `cmd.StdinPipe` and `cmd.StdoutPipe` close the subprocess side of pipes once the child process was started and close the parent process side of pipes once wait has finished. We can't use these functions as we need access to the raw `os.File` so just replicate that behavior. --- internal/backend/rclone/backend.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/internal/backend/rclone/backend.go b/internal/backend/rclone/backend.go index e54819529..5d7e62871 100644 --- a/internal/backend/rclone/backend.go +++ b/internal/backend/rclone/backend.go @@ -63,6 +63,9 @@ func run(command string, args ...string) (*StdioConn, *exec.Cmd, *sync.WaitGroup stdout, w, err := os.Pipe() if err != nil { + // close first pipe + r.Close() + stdin.Close() return nil, nil, nil, nil, err } @@ -70,6 +73,16 @@ func run(command string, args ...string) (*StdioConn, *exec.Cmd, *sync.WaitGroup cmd.Stdout = w bg, err := backend.StartForeground(cmd) + // close rclone side of pipes + errR := r.Close() + errW := w.Close() + // return first error + if err == nil { + err = errR + } + if err == nil { + err = errW + } if err != nil { return nil, nil, nil, nil, err } @@ -183,6 +196,8 @@ func newBackend(cfg Config, lim limiter.Limiter) (*Backend, error) { err := cmd.Wait() debug.Log("Wait returned %v", err) be.waitResult = err + // close our side of the pipes to rclone + stdioConn.Close() close(waitCh) }()