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