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.
This commit is contained in:
Michael Eischer 2022-08-20 19:49:30 +02:00
parent f7808245aa
commit cf0a8d7758
2 changed files with 30 additions and 25 deletions

View File

@ -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

View File

@ -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 {