diff --git a/internal/restorer/restorer.go b/internal/restorer/restorer.go index 37072d9a9..a47fd5ef6 100644 --- a/internal/restorer/restorer.go +++ b/internal/restorer/restorer.go @@ -347,6 +347,14 @@ func (res *Restorer) RestoreTo(ctx context.Context, dst string) error { } } + if !res.opts.DryRun { + // ensure that the target directory exists and is actually a directory + // Using ensureDir is too aggressive here as it also removes unexpected files + if err := fs.MkdirAll(dst, 0700); err != nil { + return fmt.Errorf("cannot create target directory: %w", err) + } + } + idx := NewHardlinkIndex[string]() filerestorer := newFileRestorer(dst, res.repo.LoadBlobsFromPack, res.repo.LookupBlob, res.repo.Connections(), res.opts.Sparse, res.opts.Delete, res.opts.Progress) diff --git a/internal/restorer/restorer_test.go b/internal/restorer/restorer_test.go index a343bda2c..2f28265cc 100644 --- a/internal/restorer/restorer_test.go +++ b/internal/restorer/restorer_test.go @@ -1372,3 +1372,25 @@ func TestRestoreDelete(t *testing.T) { }) } } + +func TestRestoreToFile(t *testing.T) { + snapshot := Snapshot{ + Nodes: map[string]Node{ + "foo": File{Data: "content: foo\n"}, + }, + } + + repo := repository.TestRepository(t) + tempdir := filepath.Join(rtest.TempDir(t), "target") + + // create a file in the place of the target directory + rtest.OK(t, os.WriteFile(tempdir, []byte{}, 0o700)) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + sn, _ := saveSnapshot(t, repo, snapshot, noopGetGenericAttributes) + res := NewRestorer(repo, sn, Options{}) + err := res.RestoreTo(ctx, tempdir) + rtest.Assert(t, strings.Contains(err.Error(), "cannot create target directory"), "unexpected error %v", err) +}