From cf0a8d77586dd7a821e173ba968fcc568db2c2e8 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 20 Aug 2022 19:49:30 +0200 Subject: [PATCH] sftp: Only connect once for repository creation This is especially useful if ssh asks for a password or if closing the initial connection could return an error due to a problematic server implementation. --- changelog/unreleased/issue-2152 | 11 +++++++++ internal/backend/sftp/sftp.go | 44 ++++++++++++++------------------- 2 files changed, 30 insertions(+), 25 deletions(-) create mode 100644 changelog/unreleased/issue-2152 diff --git a/changelog/unreleased/issue-2152 b/changelog/unreleased/issue-2152 new file mode 100644 index 000000000..25492e5ed --- /dev/null +++ b/changelog/unreleased/issue-2152 @@ -0,0 +1,11 @@ +Enhancement: only open connection once for `init` command using sftp backend + +The `init` command using the sftp backend used to connect twice to the +repository. This can be inconvenient if the user must enter a password or cause +`init` to fail if the server does not correctly close the first sftp +connection. + +This has been fixed by reusing the initial sftp connection. + +https://github.com/restic/restic/issues/2152 +https://github.com/restic/restic/pull/3882 diff --git a/internal/backend/sftp/sftp.go b/internal/backend/sftp/sftp.go index 0aa2700a6..a1b8f5cdf 100644 --- a/internal/backend/sftp/sftp.go +++ b/internal/backend/sftp/sftp.go @@ -44,7 +44,12 @@ var _ restic.Backend = &SFTP{} const defaultLayout = "default" -func startClient(program string, args ...string) (*SFTP, error) { +func startClient(cfg Config) (*SFTP, error) { + program, args, err := buildSSHCommand(cfg) + if err != nil { + return nil, err + } + debug.Log("start client %v %v", program, args) // Connect to a remote host and request the sftp subsystem via the 'ssh' // command. This assumes that passwordless login is correctly configured. @@ -121,22 +126,21 @@ func (r *SFTP) clientError() error { func Open(ctx context.Context, cfg Config) (*SFTP, error) { debug.Log("open backend with config %#v", cfg) - sem, err := sema.New(cfg.Connections) - if err != nil { - return nil, err - } - - cmd, args, err := buildSSHCommand(cfg) - if err != nil { - return nil, err - } - - sftp, err := startClient(cmd, args...) + sftp, err := startClient(cfg) if err != nil { debug.Log("unable to start program: %v", err) return nil, err } + return open(ctx, sftp, cfg) +} + +func open(ctx context.Context, sftp *SFTP, cfg Config) (*SFTP, error) { + sem, err := sema.New(cfg.Connections) + if err != nil { + return nil, err + } + sftp.Layout, err = backend.ParseLayout(ctx, sftp, cfg.Layout, defaultLayout, cfg.Path) if err != nil { return nil, err @@ -230,12 +234,7 @@ func buildSSHCommand(cfg Config) (cmd string, args []string, err error) { // Create creates an sftp backend as described by the config by running "ssh" // with the appropriate arguments (or cfg.Command, if set). func Create(ctx context.Context, cfg Config) (*SFTP, error) { - cmd, args, err := buildSSHCommand(cfg) - if err != nil { - return nil, err - } - - sftp, err := startClient(cmd, args...) + sftp, err := startClient(cfg) if err != nil { debug.Log("unable to start program: %v", err) return nil, err @@ -259,13 +258,8 @@ func Create(ctx context.Context, cfg Config) (*SFTP, error) { return nil, err } - err = sftp.Close() - if err != nil { - return nil, errors.Wrap(err, "Close") - } - - // open backend - return Open(ctx, cfg) + // repurpose existing connection + return open(ctx, sftp, cfg) } func (r *SFTP) Connections() uint {