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.
This commit is contained in:
Michael Eischer 2020-07-24 22:29:37 +02:00
parent 3e93b36ca4
commit 8554332894

View file

@ -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)
}()