From 9d7641ff3e64537ad7a2f4b2f01c422b251fdccd Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Thu, 13 Apr 2023 15:49:44 +0100 Subject: [PATCH] copy: improve error detection from closed pipes If we get io.ErrClosedPipe in pushWriter.Write, there are three possible scenarios: - The request has failed, we need to attempt a reset, so we can expect a new pipe incoming on pipeC. - The request has failed, we don't need to attempt a reset, so we can expect an incoming error on errC. - Something else externally has called Close, so we can expect the done channel to be closed. This patch ensures that we block for as long as possible (while still handling each of the above cases, so we avoid hanging), to make sure that we properly return an appropriate error message each time. Signed-off-by: Justin Chadwell --- core/remotes/docker/pusher.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/core/remotes/docker/pusher.go b/core/remotes/docker/pusher.go index 27f5a94d7..9efe553b4 100644 --- a/core/remotes/docker/pusher.go +++ b/core/remotes/docker/pusher.go @@ -420,9 +420,22 @@ func (pw *pushWriter) Write(p []byte) (n int, err error) { // if the pipe is closed, we might have the original error on the error // channel - so we should try and get it select { - case err2 := <-pw.errC: - err = err2 - default: + case <-pw.done: + case err = <-pw.errC: + case p := <-pw.pipeC: + pw.pipe.CloseWithError(content.ErrReset) + pw.pipe = p + + // If content has already been written, the bytes + // cannot be written again and the caller must reset + status, err := pw.tracker.GetStatus(pw.ref) + if err != nil { + return 0, err + } + status.Offset = 0 + status.UpdatedAt = time.Now() + pw.tracker.SetStatus(pw.ref, status) + return 0, content.ErrReset } } status.Offset += int64(n)