osbase/net/sshtakeover: fix upload cancelation

This updates sftp to the latest version, which has various improvements.

The previous method of canceling uploads doesn't work anymore, because
Close tries to take a lock already held by ReadFromWithConcurrency.
Even with the previous version of sftp, this wasn't a correct way to
cancel an upload, because it just sent a message to the server to close
the handle, while continuing the upload in the other goroutine.

There is a work in progress v2 of sftp where Close actually can be used
to cancel an upload: https://github.com/pkg/sftp/issues/603

For now, I think the best way to cancel an upload is to make the reader
return an error.

Change-Id: Id70ea7d35526a2a98295168de26469e28fd127dd
Reviewed-on: https://review.monogon.dev/c/monogon/+/4048
Tested-by: Jenkins CI
Reviewed-by: Tim Windelschmidt <tim@monogon.tech>
diff --git a/osbase/net/sshtakeover/sshtakeover.go b/osbase/net/sshtakeover/sshtakeover.go
index 20a70d2..b8fd8b2 100644
--- a/osbase/net/sshtakeover/sshtakeover.go
+++ b/osbase/net/sshtakeover/sshtakeover.go
@@ -79,31 +79,32 @@
 	}
 }
 
+type contextReader struct {
+	r   io.Reader
+	ctx context.Context
+}
+
+func (r *contextReader) Read(p []byte) (n int, err error) {
+	if r.ctx.Err() != nil {
+		return 0, r.ctx.Err()
+	}
+	return r.r.Read(p)
+}
+
 // Upload a given blob to a targetPath on the system.
 func (p *Client) Upload(ctx context.Context, targetPath string, src io.Reader) error {
+	src = &contextReader{r: src, ctx: ctx}
+
 	df, err := p.sc.Create(targetPath)
 	if err != nil {
 		return fmt.Errorf("while creating file on the host: %w", err)
 	}
-
-	doneC := make(chan error, 1)
-
-	go func() {
-		_, err := df.ReadFromWithConcurrency(src, 0)
-		df.Close()
-		doneC <- err
-	}()
-
-	select {
-	case err := <-doneC:
-		if err != nil {
-			return fmt.Errorf("while copying file: %w", err)
-		}
-	case <-ctx.Done():
-		df.Close()
-		return ctx.Err()
+	_, err = df.ReadFromWithConcurrency(src, 0)
+	closeErr := df.Close()
+	if err != nil {
+		return err
 	}
-	return nil
+	return closeErr
 }
 
 // UploadExecutable uploads a given blob to a targetPath on the system