From 42ea4d257bb78891fdb445020cf758cdc060d84a Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Mon, 10 Apr 2017 22:17:50 +0200 Subject: [PATCH] sftp: first step of conversion to Layout --- src/restic/backend/sftp/sftp.go | 58 ++++++++++---------- src/restic/backend/sftp/sftp_backend_test.go | 4 +- src/restic/backend/sftp/sshcmd_test.go | 52 ++++++++++-------- 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/src/restic/backend/sftp/sftp.go b/src/restic/backend/sftp/sftp.go index 61b1bf489..81f02f023 100644 --- a/src/restic/backend/sftp/sftp.go +++ b/src/restic/backend/sftp/sftp.go @@ -134,7 +134,7 @@ func Open(cfg Config) (*SFTP, error) { return nil, err } - l, err := backend.ParseLayout(sftp, cfg.Layout, defaultLayout, cfg.Path) + sftp.Layout, err = backend.ParseLayout(sftp, cfg.Layout, defaultLayout, cfg.Path) if err != nil { return nil, err } @@ -146,6 +146,8 @@ func Open(cfg Config) (*SFTP, error) { } } + debug.Log("layout: %v\n", sftp.Layout) + sftp.Config = cfg sftp.p = cfg.Path return sftp, nil @@ -161,6 +163,16 @@ func (r *SFTP) ReadDir(dir string) ([]os.FileInfo, error) { return r.c.ReadDir(dir) } +// IsNotExist returns true if the error is caused by a not existing file. +func (r *SFTP) IsNotExist(err error) bool { + statusError, ok := err.(*sftp.StatusError) + if !ok { + return false + } + + return statusError.Error() == `sftp: "No such file" (SSH_FX_NO_SUCH_FILE)` +} + func buildSSHCommand(cfg Config) (cmd string, args []string, err error) { if cfg.Command != "" { return SplitShellArgs(cfg.Command) @@ -184,7 +196,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(cfg Config) (*SFTP, error) { +func Create(cfg Config) (*SFTP, error) { cmd, args, err := buildSSHCommand(cfg) if err != nil { return nil, err @@ -196,7 +208,7 @@ func create(cfg Config) (*SFTP, error) { return nil, err } - l, err := backend.ParseLayout(sftp, cfg.Layout, defaultLayout, cfg.Path) + sftp.Layout, err = backend.ParseLayout(sftp, cfg.Layout, defaultLayout, cfg.Path) if err != nil { return nil, err } @@ -351,14 +363,22 @@ func (r *SFTP) Save(h restic.Handle, rd io.Reader) (err error) { } } - // test if new file exists - if _, err := r.c.Lstat(filename); err == nil { - return errors.Errorf("Close(): file %v already exists", filename) + // create new file + f, err := r.c.OpenFile(filename, os.O_CREATE|os.O_EXCL|os.O_WRONLY) + if err != nil { + return errors.Wrap(err, "OpenFile") } - err := r.c.Rename(oldname, filename) + // save data + _, err = io.Copy(f, rd) if err != nil { - return errors.Wrap(err, "Rename") + f.Close() + return errors.Wrap(err, "Write") + } + + err = f.Close() + if err != nil { + return errors.Wrap(err, "Close") } // set mode to read-only @@ -369,28 +389,6 @@ func (r *SFTP) Save(h restic.Handle, rd io.Reader) (err error) { err = r.c.Chmod(filename, fi.Mode()&os.FileMode(^uint32(0222))) return errors.Wrap(err, "Chmod") - - filename, tmpfile, err := r.tempFile() - if err != nil { - return err - } - - n, err := io.Copy(tmpfile, rd) - if err != nil { - return errors.Wrap(err, "Write") - } - - debug.Log("saved %v (%d bytes) to %v", h, n, filename) - - err = tmpfile.Close() - if err != nil { - return errors.Wrap(err, "Close") - } - - err = r.renameFile(filename, h) - debug.Log("save %v: rename %v: %v", - h, path.Base(filename), err) - return err } // Load returns a reader that yields the contents of the file at h at the diff --git a/src/restic/backend/sftp/sftp_backend_test.go b/src/restic/backend/sftp/sftp_backend_test.go index 90b96d769..289c62206 100644 --- a/src/restic/backend/sftp/sftp_backend_test.go +++ b/src/restic/backend/sftp/sftp_backend_test.go @@ -61,7 +61,7 @@ func init() { return nil, err } - cfg.Dir = tempBackendDir + cfg.Path = tempBackendDir return sftp.Create(cfg) } @@ -72,7 +72,7 @@ func init() { return nil, err } - cfg.Dir = tempBackendDir + cfg.Path = tempBackendDir return sftp.Open(cfg) } diff --git a/src/restic/backend/sftp/sshcmd_test.go b/src/restic/backend/sftp/sshcmd_test.go index d98309c67..dea811a35 100644 --- a/src/restic/backend/sftp/sshcmd_test.go +++ b/src/restic/backend/sftp/sshcmd_test.go @@ -1,46 +1,52 @@ package sftp -import "testing" +import ( + "reflect" + "testing" +) var sshcmdTests = []struct { - cfg Config - s []string + cfg Config + cmd string + args []string }{ { - Config{User: "user", Host: "host", Dir: "dir/subdir"}, + Config{User: "user", Host: "host", Path: "dir/subdir"}, + "ssh", []string{"host", "-l", "user", "-s", "sftp"}, }, { - Config{Host: "host", Dir: "dir/subdir"}, + Config{Host: "host", Path: "dir/subdir"}, + "ssh", []string{"host", "-s", "sftp"}, }, { - Config{Host: "host:10022", Dir: "/dir/subdir"}, + Config{Host: "host:10022", Path: "/dir/subdir"}, + "ssh", []string{"host", "-p", "10022", "-s", "sftp"}, }, { - Config{User: "user", Host: "host:10022", Dir: "/dir/subdir"}, + Config{User: "user", Host: "host:10022", Path: "/dir/subdir"}, + "ssh", []string{"host", "-p", "10022", "-l", "user", "-s", "sftp"}, }, } func TestBuildSSHCommand(t *testing.T) { - for i, test := range sshcmdTests { - cmd := buildSSHCommand(test.cfg) - failed := false - if len(cmd) != len(test.s) { - failed = true - } else { - for l := range test.s { - if test.s[l] != cmd[l] { - failed = true - break - } + for _, test := range sshcmdTests { + t.Run("", func(t *testing.T) { + cmd, args, err := buildSSHCommand(test.cfg) + if err != nil { + t.Fatal(err) } - } - if failed { - t.Errorf("test %d: wrong cmd, want:\n %v\ngot:\n %v", - i, test.s, cmd) - } + + if cmd != test.cmd { + t.Fatalf("cmd: want %v, got %v", test.cmd, cmd) + } + + if !reflect.DeepEqual(test.args, args) { + t.Fatalf("wrong args, want:\n %v\ngot:\n %v", test.args, args) + } + }) } }