diff --git a/internal/backend/local/local.go b/internal/backend/local/local.go index 27be5a2b7..d2dedacbb 100644 --- a/internal/backend/local/local.go +++ b/internal/backend/local/local.go @@ -25,6 +25,25 @@ var _ restic.Backend = &Local{} const defaultLayout = "default" +// dirExists returns true if the name exists and is a directory. +func dirExists(name string) bool { + f, err := fs.Open(name) + if err != nil { + return false + } + + fi, err := f.Stat() + if err != nil { + return false + } + + if err = f.Close(); err != nil { + return false + } + + return fi.IsDir() +} + // Open opens the local backend as specified by config. func Open(cfg Config) (*Local, error) { debug.Log("open local backend at %v (layout %q)", cfg.Path, cfg.Layout) @@ -35,11 +54,18 @@ func Open(cfg Config) (*Local, error) { be := &Local{Config: cfg, Layout: l} - // create paths for data and refs. MkdirAll does nothing if the directory already exists. - for _, d := range be.Paths() { - err := fs.MkdirAll(d, backend.Modes.Dir) - if err != nil { - return nil, errors.Wrap(err, "MkdirAll") + // if data dir exists, make sure that all subdirs also exist + datadir := be.Dirname(restic.Handle{Type: restic.DataFile}) + if dirExists(datadir) { + for _, d := range be.Paths() { + if _, err := filepath.Rel(datadir, d); err != nil { + continue + } + + err := fs.MkdirAll(d, backend.Modes.Dir) + if err != nil { + return nil, errors.Wrap(err, "MkdirAll") + } } } diff --git a/internal/backend/local/local_test.go b/internal/backend/local/local_test.go index a296fff5e..1d15f5ff9 100644 --- a/internal/backend/local/local_test.go +++ b/internal/backend/local/local_test.go @@ -2,6 +2,8 @@ package local_test import ( "io/ioutil" + "os" + "path/filepath" "testing" "github.com/restic/restic/internal/backend/local" @@ -59,3 +61,57 @@ func TestBackend(t *testing.T) { func BenchmarkBackend(t *testing.B) { newTestSuite(t).RunBenchmarks(t) } + +func readdirnames(t testing.TB, dir string) []string { + f, err := os.Open(dir) + if err != nil { + t.Fatal(err) + } + + entries, err := f.Readdirnames(-1) + if err != nil { + t.Fatal(err) + } + + err = f.Close() + if err != nil { + t.Fatal(err) + } + + return entries +} + +func empty(t testing.TB, dir string) { + entries := readdirnames(t, dir) + if len(entries) != 0 { + t.Fatalf("directory %v is not empty, contains: %v", dir, entries) + } +} + +func openclose(t testing.TB, dir string) { + cfg := local.Config{Path: dir} + + be, err := local.Open(cfg) + if err != nil { + t.Logf("Open returned error %v", err) + } + + if be != nil { + err = be.Close() + if err != nil { + t.Logf("Close returned error %v", err) + } + } +} + +func TestOpenNotExistingDirectory(t *testing.T) { + dir, cleanup := TempDir(t) + defer cleanup() + + // local.Open must not create any files dirs in the repo + openclose(t, filepath.Join(dir, "repo")) + empty(t, dir) + + openclose(t, dir) + empty(t, dir) +} diff --git a/internal/backend/sftp/sftp.go b/internal/backend/sftp/sftp.go index a85b9b315..9961ac65c 100644 --- a/internal/backend/sftp/sftp.go +++ b/internal/backend/sftp/sftp.go @@ -141,6 +141,10 @@ func (r *SFTP) checkDataSubdirs() error { // check if all paths for data/ exist entries, err := r.c.ReadDir(datadir) + if r.IsNotExist(err) { + return nil + } + if err != nil { return err }