mirror of
https://github.com/restic/restic.git
synced 2026-06-17 22:24:17 +00:00
Merge pull request #21851 from MichaelEischer/misc-cleanups2
Unexport lots of methods and structs
This commit is contained in:
@@ -567,7 +567,7 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts global.Options, te
|
||||
return err
|
||||
}
|
||||
|
||||
var targetFS fs.FS = fs.Local{}
|
||||
targetFS := fs.NewLocal()
|
||||
if runtime.GOOS == "windows" && opts.UseFsSnapshot {
|
||||
if err = fs.HasSufficientPrivilegesForVSS(); err != nil {
|
||||
return err
|
||||
|
||||
@@ -65,9 +65,11 @@ func testRunKeyAddNewKeyUserHost(t testing.TB, gopts global.Options) {
|
||||
_ = withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
|
||||
repo, err := global.OpenRepository(ctx, gopts, progress.NewNoopPrinter())
|
||||
rtest.OK(t, err)
|
||||
key, err := repository.SearchKey(ctx, repo, testKeyNewPassword, 2, "")
|
||||
err = repo.SearchKey(ctx, testKeyNewPassword, 2, "")
|
||||
rtest.OK(t, err)
|
||||
|
||||
key, err := repository.LoadKey(ctx, repo, repo.KeyID())
|
||||
rtest.OK(t, err)
|
||||
rtest.Equals(t, "john", key.Username)
|
||||
rtest.Equals(t, "example.com", key.Hostname)
|
||||
return nil
|
||||
@@ -107,9 +109,11 @@ func testRunKeyPasswdUserHost(t testing.TB, newPassword string, gopts global.Opt
|
||||
_ = withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
|
||||
repo, err := global.OpenRepository(ctx, gopts, progress.NewNoopPrinter())
|
||||
rtest.OK(t, err)
|
||||
key, err := repository.SearchKey(ctx, repo, testKeyNewPassword, 1, "")
|
||||
err = repo.SearchKey(ctx, testKeyNewPassword, 1, "")
|
||||
rtest.OK(t, err)
|
||||
|
||||
key, err := repository.LoadKey(ctx, repo, repo.KeyID())
|
||||
rtest.OK(t, err)
|
||||
rtest.Equals(t, "john", key.Username)
|
||||
rtest.Equals(t, "example.com", key.Hostname)
|
||||
return nil
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ func internalOpenWithLocked(ctx context.Context, gopts global.Options, dryRun bo
|
||||
|
||||
unlock := func() {}
|
||||
if !dryRun {
|
||||
var lock *repository.Unlocker
|
||||
var lock repository.Unlocker
|
||||
|
||||
lock, ctx, err = repository.Lock(ctx, repo, exclusive, gopts.RetryLock, func(msg string) {
|
||||
if !gopts.JSON {
|
||||
|
||||
@@ -67,8 +67,8 @@ func (s *ItemStats) Add(other ItemStats) {
|
||||
s.TreeSizeInRepo += other.TreeSizeInRepo
|
||||
}
|
||||
|
||||
// ToNoder returns a data.Node for a File.
|
||||
type ToNoder interface {
|
||||
// toNoder returns a data.Node for a File.
|
||||
type toNoder interface {
|
||||
ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*data.Node, error)
|
||||
}
|
||||
|
||||
@@ -149,9 +149,9 @@ type Options struct {
|
||||
SaveTreeConcurrency uint
|
||||
}
|
||||
|
||||
// ApplyDefaults returns a copy of o with the default options set for all unset
|
||||
// applyDefaults returns a copy of o with the default options set for all unset
|
||||
// fields.
|
||||
func (o Options) ApplyDefaults() Options {
|
||||
func (o Options) applyDefaults() Options {
|
||||
if o.ReadConcurrency == 0 {
|
||||
// two is a sweet spot for almost all situations. We've done some
|
||||
// experiments documented here:
|
||||
@@ -178,7 +178,7 @@ func New(repo archiverRepo, filesystem fs.FS, opts Options) *Archiver {
|
||||
SelectByName: func(_ string) bool { return true },
|
||||
Select: func(_ string, _ *fs.ExtendedFileInfo, _ fs.FS) bool { return true },
|
||||
FS: filesystem,
|
||||
Options: opts.ApplyDefaults(),
|
||||
Options: opts.applyDefaults(),
|
||||
|
||||
CompleteItem: func(string, *data.Node, *data.Node, ItemStats, time.Duration) {},
|
||||
StartFile: func(string) {},
|
||||
@@ -249,7 +249,7 @@ func (arch *Archiver) trackItem(item string, previous, current *data.Node, s Ite
|
||||
}
|
||||
|
||||
// nodeFromFileInfo returns the restic node from an os.FileInfo.
|
||||
func (arch *Archiver) nodeFromFileInfo(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*data.Node, error) {
|
||||
func (arch *Archiver) nodeFromFileInfo(snPath, filename string, meta toNoder, ignoreXattrListError bool) (*data.Node, error) {
|
||||
node, err := meta.ToNode(ignoreXattrListError, func(format string, args ...any) {
|
||||
_ = arch.error(filename, fmt.Errorf(format, args...))
|
||||
})
|
||||
@@ -781,9 +781,9 @@ func (arch *Archiver) dirPathToNode(snPath, target string) (node *data.Node, err
|
||||
//
|
||||
// Paths returned with Explicit true are those the user listed literally; paths
|
||||
// inserted from directory expansion have Explicit false.
|
||||
func resolveRelativeTargets(filesys fs.FS, targets []string) ([]BackupTarget, error) {
|
||||
func resolveRelativeTargets(filesys fs.FS, targets []string) ([]backupTarget, error) {
|
||||
debug.Log("targets before resolving: %v", targets)
|
||||
result := make([]BackupTarget, 0, len(targets))
|
||||
result := make([]backupTarget, 0, len(targets))
|
||||
for _, target := range targets {
|
||||
if target != "" && filesys.VolumeName(target) == target {
|
||||
// special case to allow users to also specify a volume name "C:" instead of a path "C:\"
|
||||
@@ -793,7 +793,7 @@ func resolveRelativeTargets(filesys fs.FS, targets []string) ([]BackupTarget, er
|
||||
}
|
||||
pc, _ := pathComponents(filesys, target, false)
|
||||
if len(pc) > 0 {
|
||||
result = append(result, BackupTarget{Path: target, Explicit: true})
|
||||
result = append(result, backupTarget{Path: target, Explicit: true})
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -805,7 +805,7 @@ func resolveRelativeTargets(filesys fs.FS, targets []string) ([]BackupTarget, er
|
||||
sort.Strings(entries)
|
||||
|
||||
for _, name := range entries {
|
||||
result = append(result, BackupTarget{
|
||||
result = append(result, backupTarget{
|
||||
Path: filesys.Join(target, name),
|
||||
Explicit: false,
|
||||
})
|
||||
|
||||
@@ -136,7 +136,7 @@ func TestArchiverSaveFile(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
tempdir, repo := prepareTempdirRepoSrc(t, TestDir{"file": testfile})
|
||||
node, stats := saveFile(t, repo, filepath.Join(tempdir, "file"), fs.Track{FS: fs.Local{}})
|
||||
node, stats := saveFile(t, repo, filepath.Join(tempdir, "file"), fs.Track{FS: fs.NewLocal()})
|
||||
|
||||
TestEnsureFileContent(ctx, t, repo, "file", node, testfile)
|
||||
if stats.DataSize != uint64(len(testfile.Content)) {
|
||||
@@ -211,7 +211,7 @@ func TestArchiverSave(t *testing.T) {
|
||||
|
||||
tempdir, repo := prepareTempdirRepoSrc(t, TestDir{"file": testfile})
|
||||
|
||||
arch := New(repo, fs.Track{FS: fs.Local{}}, Options{})
|
||||
arch := New(repo, fs.Track{FS: fs.NewLocal()}, Options{})
|
||||
arch.Error = func(item string, err error) error {
|
||||
t.Errorf("archiver error for %v: %v", item, err)
|
||||
return err
|
||||
@@ -357,7 +357,7 @@ func BenchmarkArchiverSaveFileSmall(b *testing.B) {
|
||||
tempdir, repo := prepareTempdirRepoSrc(b, d)
|
||||
b.StartTimer()
|
||||
|
||||
_, stats := saveFile(b, repo, filepath.Join(tempdir, "file"), fs.Track{FS: fs.Local{}})
|
||||
_, stats := saveFile(b, repo, filepath.Join(tempdir, "file"), fs.Track{FS: fs.NewLocal()})
|
||||
|
||||
b.StopTimer()
|
||||
if stats.DataSize != fileSize {
|
||||
@@ -389,7 +389,7 @@ func BenchmarkArchiverSaveFileLarge(b *testing.B) {
|
||||
tempdir, repo := prepareTempdirRepoSrc(b, d)
|
||||
b.StartTimer()
|
||||
|
||||
_, stats := saveFile(b, repo, filepath.Join(tempdir, "file"), fs.Track{FS: fs.Local{}})
|
||||
_, stats := saveFile(b, repo, filepath.Join(tempdir, "file"), fs.Track{FS: fs.NewLocal()})
|
||||
|
||||
b.StopTimer()
|
||||
if stats.DataSize != fileSize {
|
||||
@@ -479,7 +479,7 @@ func TestArchiverSaveFileIncremental(t *testing.T) {
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
appendToFile(t, testfile, data)
|
||||
node, _ := saveFile(t, repo, testfile, fs.Track{FS: fs.Local{}})
|
||||
node, _ := saveFile(t, repo, testfile, fs.Track{FS: fs.NewLocal()})
|
||||
|
||||
t.Logf("node blobs: %v", node.Content)
|
||||
|
||||
@@ -691,7 +691,7 @@ func TestFileChanged(t *testing.T) {
|
||||
}
|
||||
save(t, filename, content)
|
||||
|
||||
fs := &fs.Local{}
|
||||
fs := fs.NewLocal()
|
||||
fiBefore, err := fs.Lstat(filename)
|
||||
rtest.OK(t, err)
|
||||
node := nodeFromFile(t, fs, filename)
|
||||
@@ -735,7 +735,7 @@ func TestFilChangedSpecialCases(t *testing.T) {
|
||||
|
||||
t.Run("type-change", func(t *testing.T) {
|
||||
fi := lstat(t, filename)
|
||||
node := nodeFromFile(t, &fs.Local{}, filename)
|
||||
node := nodeFromFile(t, fs.NewLocal(), filename)
|
||||
node.Type = data.NodeTypeSymlink
|
||||
if !fileChanged(fi, node, 0) {
|
||||
t.Fatal("node with changed type detected as unchanged")
|
||||
@@ -837,7 +837,7 @@ func TestArchiverSaveDir(t *testing.T) {
|
||||
t.Run("", func(t *testing.T) {
|
||||
tempdir, repo := prepareTempdirRepoSrc(t, test.src)
|
||||
|
||||
testFS := fs.Track{FS: fs.Local{}}
|
||||
testFS := fs.Track{FS: fs.NewLocal()}
|
||||
arch := New(repo, testFS, Options{})
|
||||
arch.summary = &Summary{}
|
||||
|
||||
@@ -907,7 +907,7 @@ func TestArchiverSaveDirIncremental(t *testing.T) {
|
||||
// save the empty directory several times in a row, then have a look if the
|
||||
// archiver did save the same tree several times
|
||||
for i := 0; i < 5; i++ {
|
||||
testFS := fs.Track{FS: fs.Local{}}
|
||||
testFS := fs.Track{FS: fs.NewLocal()}
|
||||
arch := New(repo, testFS, Options{})
|
||||
arch.summary = &Summary{}
|
||||
|
||||
@@ -1089,7 +1089,7 @@ func TestArchiverSaveTree(t *testing.T) {
|
||||
t.Run("", func(t *testing.T) {
|
||||
tempdir, repo := prepareTempdirRepoSrc(t, test.src)
|
||||
|
||||
testFS := fs.Track{FS: fs.Local{}}
|
||||
testFS := fs.Track{FS: fs.NewLocal()}
|
||||
|
||||
arch := New(repo, testFS, Options{})
|
||||
arch.summary = &Summary{}
|
||||
@@ -1388,7 +1388,7 @@ func TestArchiverSnapshot(t *testing.T) {
|
||||
|
||||
tempdir, repo := prepareTempdirRepoSrc(t, test.src)
|
||||
|
||||
arch := New(repo, fs.Track{FS: fs.Local{}}, Options{})
|
||||
arch := New(repo, fs.Track{FS: fs.NewLocal()}, Options{})
|
||||
|
||||
chdir := tempdir
|
||||
if test.chdir != "" {
|
||||
@@ -1487,7 +1487,7 @@ func TestResolveRelativeTargetsSpecial(t *testing.T) {
|
||||
t.Skip("skip test on unix")
|
||||
}
|
||||
|
||||
targets, err := resolveRelativeTargets(&fs.Local{}, test.targets)
|
||||
targets, err := resolveRelativeTargets(fs.NewLocal(), test.targets)
|
||||
rtest.OK(t, err)
|
||||
paths := make([]string, len(targets))
|
||||
for i, tgt := range targets {
|
||||
@@ -1609,7 +1609,7 @@ func TestArchiverSnapshotSelect(t *testing.T) {
|
||||
|
||||
tempdir, repo := prepareTempdirRepoSrc(t, test.src)
|
||||
|
||||
arch := New(repo, fs.Track{FS: fs.Local{}}, Options{})
|
||||
arch := New(repo, fs.Track{FS: fs.NewLocal()}, Options{})
|
||||
arch.Select = test.selFn
|
||||
|
||||
back := rtest.Chdir(t, tempdir)
|
||||
@@ -1717,7 +1717,7 @@ func TestArchiverExplicitBackupTarget(t *testing.T) {
|
||||
|
||||
tempdir, repo := prepareTempdirRepoSrc(t, test.src)
|
||||
|
||||
arch := New(repo, fs.Track{FS: fs.Local{}}, Options{})
|
||||
arch := New(repo, fs.Track{FS: fs.NewLocal()}, Options{})
|
||||
arch.Select = test.selFn
|
||||
|
||||
back := rtest.Chdir(t, tempdir)
|
||||
@@ -1884,7 +1884,7 @@ func TestArchiverParent(t *testing.T) {
|
||||
tempdir, repo := prepareTempdirRepoSrc(t, test.src)
|
||||
|
||||
testFS := &MockFS{
|
||||
FS: fs.Track{FS: fs.Local{}},
|
||||
FS: fs.Track{FS: fs.NewLocal()},
|
||||
bytesRead: make(map[string]int),
|
||||
}
|
||||
|
||||
@@ -2078,7 +2078,7 @@ func TestArchiverErrorReporting(t *testing.T) {
|
||||
test.prepare(t)
|
||||
}
|
||||
|
||||
arch := New(repo, fs.Track{FS: fs.Local{}}, Options{})
|
||||
arch := New(repo, fs.Track{FS: fs.NewLocal()}, Options{})
|
||||
arch.Error = test.errFn
|
||||
|
||||
target := test.targets
|
||||
@@ -2159,7 +2159,7 @@ func TestArchiverContextCanceled(t *testing.T) {
|
||||
back := rtest.Chdir(t, tempdir)
|
||||
defer back()
|
||||
|
||||
arch := New(repo, fs.Track{FS: fs.Local{}}, Options{})
|
||||
arch := New(repo, fs.Track{FS: fs.NewLocal()}, Options{})
|
||||
|
||||
_, snapshotID, _, err := arch.Snapshot(ctx, []string{"."}, SnapshotOptions{Time: time.Now()})
|
||||
|
||||
@@ -2301,7 +2301,7 @@ func TestArchiverAbortEarlyOnError(t *testing.T) {
|
||||
defer back()
|
||||
|
||||
testFS := &TrackFS{
|
||||
FS: fs.Track{FS: fs.Local{}},
|
||||
FS: fs.Track{FS: fs.NewLocal()},
|
||||
opened: make(map[string]uint),
|
||||
}
|
||||
|
||||
@@ -2434,7 +2434,7 @@ func TestMetadataChanged(t *testing.T) {
|
||||
|
||||
// get metadata
|
||||
fi := lstat(t, "testfile")
|
||||
localFS := &fs.Local{}
|
||||
localFS := fs.NewLocal()
|
||||
meta, err := localFS.OpenFile("testfile", fs.O_NOFOLLOW, true)
|
||||
rtest.OK(t, err)
|
||||
want, err := meta.ToNode(false, t.Logf)
|
||||
@@ -2535,7 +2535,7 @@ func TestRacyFileTypeSwap(t *testing.T) {
|
||||
tempfile := filepath.Join(tempdir, realName)
|
||||
|
||||
statfs := &overrideFS{
|
||||
FS: fs.Local{},
|
||||
FS: fs.NewLocal(),
|
||||
overrideFI: fakeFI,
|
||||
resetFIOnRead: true,
|
||||
}
|
||||
@@ -2582,7 +2582,7 @@ func TestMetadataBackupErrorFiltering(t *testing.T) {
|
||||
filename := filepath.Join(tempdir, "file")
|
||||
repo := repository.TestRepository(t)
|
||||
|
||||
arch := New(repo, fs.Local{}, Options{})
|
||||
arch := New(repo, fs.NewLocal(), Options{})
|
||||
|
||||
var filteredErr error
|
||||
replacementErr := fmt.Errorf("replacement")
|
||||
@@ -2631,7 +2631,7 @@ func TestIrregularFile(t *testing.T) {
|
||||
fi.Mode = (fi.Mode &^ os.ModeType) | os.ModeIrregular
|
||||
|
||||
override := &overrideFS{
|
||||
FS: fs.Local{},
|
||||
FS: fs.NewLocal(),
|
||||
overrideFI: fi,
|
||||
overrideNode: &data.Node{
|
||||
Type: data.NodeTypeIrregular,
|
||||
@@ -2692,7 +2692,7 @@ func TestDisappearedFile(t *testing.T) {
|
||||
// depending on the underlying FS implementation a missing file may be detected by OpenFile or
|
||||
// the subsequent file.Stat() call. Thus test both cases.
|
||||
for _, errorOnOpen := range []bool{false, true} {
|
||||
arch := New(repo, fs.Track{FS: &missingFS{FS: &fs.Local{}, errorOnOpen: errorOnOpen}}, Options{})
|
||||
arch := New(repo, fs.Track{FS: &missingFS{FS: fs.NewLocal(), errorOnOpen: errorOnOpen}}, Options{})
|
||||
_, excluded, err := arch.save(ctx, "/", filepath.Join(tempdir, "testdir"), nil, false)
|
||||
rtest.OK(t, err)
|
||||
rtest.Assert(t, excluded, "testfile should have been excluded")
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
)
|
||||
|
||||
func statAndSnapshot(t *testing.T, repo archiverRepo, name string) (*data.Node, *data.Node) {
|
||||
want := nodeFromFile(t, &fs.Local{}, name)
|
||||
_, node := snapshot(t, repo, &fs.Local{}, nil, name)
|
||||
want := nodeFromFile(t, fs.NewLocal(), name)
|
||||
_, node := snapshot(t, repo, fs.NewLocal(), nil, name)
|
||||
return want, node
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestIsExcludedByFile(t *testing.T) {
|
||||
if tc.content == "" {
|
||||
h = ""
|
||||
}
|
||||
if got := isExcludedByFile(foo, tagFilename, h, newRejectionCache(), &fs.Local{}, func(msg string, args ...interface{}) { t.Logf(msg, args...) }); tc.want != got {
|
||||
if got := isExcludedByFile(foo, tagFilename, h, newRejectionCache(), fs.NewLocal(), func(msg string, args ...interface{}) { t.Logf(msg, args...) }); tc.want != got {
|
||||
t.Fatalf("expected %v, got %v", tc.want, got)
|
||||
}
|
||||
})
|
||||
@@ -111,8 +111,8 @@ func TestMultipleIsExcludedByFile(t *testing.T) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
excludedByFoo := fooExclude(p, nil, &fs.Local{})
|
||||
excludedByBar := barExclude(p, nil, &fs.Local{})
|
||||
excludedByFoo := fooExclude(p, nil, fs.NewLocal())
|
||||
excludedByBar := barExclude(p, nil, fs.NewLocal())
|
||||
excluded := excludedByFoo || excludedByBar
|
||||
// the log message helps debugging in case the test fails
|
||||
t.Logf("%q: %v || %v = %v", p, excludedByFoo, excludedByBar, excluded)
|
||||
@@ -243,7 +243,7 @@ func TestDeviceMap(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run("", func(t *testing.T) {
|
||||
res, err := deviceMap.IsAllowed(filepath.FromSlash(test.item), test.deviceID, &fs.Local{})
|
||||
res, err := deviceMap.IsAllowed(filepath.FromSlash(test.item), test.deviceID, fs.NewLocal())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ type fileSaver struct {
|
||||
|
||||
CompleteBlob func(bytes uint64)
|
||||
|
||||
NodeFromFileInfo func(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*data.Node, error)
|
||||
NodeFromFileInfo func(snPath, filename string, meta toNoder, ignoreXattrListError bool) (*data.Node, error)
|
||||
}
|
||||
|
||||
// newFileSaver returns a new file saver. A worker pool with fileWorkers is
|
||||
|
||||
@@ -41,7 +41,7 @@ func startFileSaver(ctx context.Context, t testing.TB, _ fs.FS) (*fileSaver, *mo
|
||||
|
||||
saver := &mockSaver{saved: make(map[string]int)}
|
||||
s := newFileSaver(ctx, wg, saver, pol, workers)
|
||||
s.NodeFromFileInfo = func(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*data.Node, error) {
|
||||
s.NodeFromFileInfo = func(snPath, filename string, meta toNoder, ignoreXattrListError bool) (*data.Node, error) {
|
||||
return meta.ToNode(ignoreXattrListError, t.Logf)
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func TestFileSaver(t *testing.T) {
|
||||
completeFn := func(*data.Node, ItemStats) {}
|
||||
|
||||
files := createTestFiles(t, 15)
|
||||
testFs := fs.Local{}
|
||||
testFs := fs.NewLocal()
|
||||
s, saver, ctx, wg := startFileSaver(ctx, t, testFs)
|
||||
|
||||
var results []futureNode
|
||||
|
||||
@@ -92,7 +92,7 @@ func TestScanner(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sc := NewScanner(fs.Track{FS: fs.Local{}})
|
||||
sc := NewScanner(fs.Track{FS: fs.NewLocal()})
|
||||
if test.selFn != nil {
|
||||
sc.Select = test.selFn
|
||||
}
|
||||
@@ -231,7 +231,7 @@ func TestScannerError(t *testing.T) {
|
||||
test.prepare(t)
|
||||
}
|
||||
|
||||
sc := NewScanner(fs.Track{FS: fs.Local{}})
|
||||
sc := NewScanner(fs.Track{FS: fs.NewLocal()})
|
||||
if test.selFn != nil {
|
||||
sc.Select = test.selFn
|
||||
}
|
||||
@@ -299,7 +299,7 @@ func TestScannerCancel(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sc := NewScanner(fs.Track{FS: fs.Local{}})
|
||||
sc := NewScanner(fs.Track{FS: fs.NewLocal()})
|
||||
var lastStats ScanStats
|
||||
sc.Result = func(item string, s ScanStats) {
|
||||
lastStats = s
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
|
||||
// TestSnapshot creates a new snapshot of path.
|
||||
func TestSnapshot(t testing.TB, repo restic.Repository, path string, parent *restic.ID) *data.Snapshot {
|
||||
arch := New(repo, fs.Local{}, Options{})
|
||||
arch := New(repo, fs.NewLocal(), Options{})
|
||||
opts := SnapshotOptions{
|
||||
Time: time.Now(),
|
||||
Hostname: "localhost",
|
||||
|
||||
@@ -467,7 +467,7 @@ func TestTestEnsureSnapshot(t *testing.T) {
|
||||
|
||||
repo := repository.TestRepository(t)
|
||||
|
||||
arch := New(repo, fs.Local{}, Options{})
|
||||
arch := New(repo, fs.NewLocal(), Options{})
|
||||
opts := SnapshotOptions{
|
||||
Time: time.Now(),
|
||||
Hostname: "localhost",
|
||||
|
||||
@@ -274,16 +274,16 @@ func unrollTree(f fs.FS, t *tree) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BackupTarget is a resolved backup path and whether the user passed this path
|
||||
// backupTarget is a resolved backup path and whether the user passed this path
|
||||
// literally. Paths inserted when expanding a target with no path components
|
||||
// (for example ".") have Explicit false so include/exclude rules still apply.
|
||||
type BackupTarget struct {
|
||||
type backupTarget struct {
|
||||
Path string
|
||||
Explicit bool
|
||||
}
|
||||
|
||||
// newTree creates a Tree from the target files/directories.
|
||||
func newTree(fs fs.FS, targets []BackupTarget) (*tree, error) {
|
||||
func newTree(fs fs.FS, targets []backupTarget) (*tree, error) {
|
||||
debug.Log("targets: %v", targets)
|
||||
tree := &tree{}
|
||||
seen := make(map[string]struct{})
|
||||
|
||||
@@ -14,10 +14,10 @@ import (
|
||||
// debug.Log requires Tree.String.
|
||||
var _ fmt.Stringer = tree{}
|
||||
|
||||
func testBackupTargets(paths []string) []BackupTarget {
|
||||
tgts := make([]BackupTarget, len(paths))
|
||||
func testBackupTargets(paths []string) []backupTarget {
|
||||
tgts := make([]backupTarget, len(paths))
|
||||
for i, p := range paths {
|
||||
tgts[i] = BackupTarget{Path: p, Explicit: true}
|
||||
tgts[i] = backupTarget{Path: p, Explicit: true}
|
||||
}
|
||||
return tgts
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func TestPathComponents(t *testing.T) {
|
||||
t.Skip("skip test on unix")
|
||||
}
|
||||
|
||||
c, v := pathComponents(fs.Local{}, filepath.FromSlash(test.p), test.rel)
|
||||
c, v := pathComponents(fs.NewLocal(), filepath.FromSlash(test.p), test.rel)
|
||||
if !cmp.Equal(test.c, c) {
|
||||
t.Error(test.c, c)
|
||||
}
|
||||
@@ -137,7 +137,7 @@ func TestRootDirectory(t *testing.T) {
|
||||
t.Skip("skip test on unix")
|
||||
}
|
||||
|
||||
root := rootDirectory(fs.Local{}, filepath.FromSlash(test.target))
|
||||
root := rootDirectory(fs.NewLocal(), filepath.FromSlash(test.target))
|
||||
want := filepath.FromSlash(test.root)
|
||||
if root != want {
|
||||
t.Fatalf("wrong root directory, want %v, got %v", want, root)
|
||||
@@ -455,7 +455,7 @@ func TestTree(t *testing.T) {
|
||||
back := rtest.Chdir(t, tempdir)
|
||||
defer back()
|
||||
|
||||
tree, err := newTree(fs.Local{}, testBackupTargets(test.targets))
|
||||
tree, err := newTree(fs.NewLocal(), testBackupTargets(test.targets))
|
||||
if test.mustError {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
|
||||
@@ -44,9 +44,9 @@ func New(size int) *Cache {
|
||||
return c
|
||||
}
|
||||
|
||||
// Add adds key id with value blob to c.
|
||||
// add adds key id with value blob to c.
|
||||
// It may return an evicted buffer for reuse.
|
||||
func (c *Cache) Add(id restic.ID, blob []byte) (old []byte) {
|
||||
func (c *Cache) add(id restic.ID, blob []byte) (old []byte) {
|
||||
debug.Log("bloblru.Cache: add %v", id)
|
||||
|
||||
size := cap(blob) + overhead
|
||||
@@ -77,7 +77,7 @@ func (c *Cache) Add(id restic.ID, blob []byte) (old []byte) {
|
||||
return old
|
||||
}
|
||||
|
||||
func (c *Cache) Get(id restic.ID) ([]byte, bool) {
|
||||
func (c *Cache) get(id restic.ID) ([]byte, bool) {
|
||||
c.mu.Lock()
|
||||
blob, ok := c.c.Get(id)
|
||||
c.mu.Unlock()
|
||||
@@ -89,7 +89,7 @@ func (c *Cache) Get(id restic.ID) ([]byte, bool) {
|
||||
|
||||
func (c *Cache) GetOrCompute(id restic.ID, compute func() ([]byte, error)) ([]byte, error) {
|
||||
// check if already cached
|
||||
blob, ok := c.Get(id)
|
||||
blob, ok := c.get(id)
|
||||
if ok {
|
||||
return blob, nil
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func (c *Cache) GetOrCompute(id restic.ID, compute func() ([]byte, error)) ([]by
|
||||
// takes over, caches the computed value and cleans up its channel in c.inProgress.
|
||||
// Then goroutine A continues, does not detect a parallel computation and would try
|
||||
// to call compute() again.
|
||||
blob, ok = c.Get(id)
|
||||
blob, ok = c.get(id)
|
||||
if ok {
|
||||
return blob, nil
|
||||
}
|
||||
@@ -132,7 +132,7 @@ func (c *Cache) GetOrCompute(id restic.ID, compute func() ([]byte, error)) ([]by
|
||||
// download it
|
||||
blob, err := compute()
|
||||
if err == nil {
|
||||
c.Add(id, blob)
|
||||
c.add(id, blob)
|
||||
}
|
||||
|
||||
return blob, err
|
||||
|
||||
@@ -25,8 +25,8 @@ func TestCache(t *testing.T) {
|
||||
c := New(cacheSize)
|
||||
|
||||
addAndCheck := func(id restic.ID, exp []byte) {
|
||||
c.Add(id, exp)
|
||||
blob, ok := c.Get(id)
|
||||
c.add(id, exp)
|
||||
blob, ok := c.get(id)
|
||||
rtest.Assert(t, ok, "blob %v added but not found in cache", id)
|
||||
rtest.Equals(t, &exp[0], &blob[0])
|
||||
rtest.Equals(t, exp, blob)
|
||||
@@ -38,13 +38,13 @@ func TestCache(t *testing.T) {
|
||||
addAndCheck(id2, make([]byte, 1, 30*kiB))
|
||||
addAndCheck(id3, make([]byte, 1, 10*kiB))
|
||||
|
||||
_, ok := c.Get(id2)
|
||||
_, ok := c.get(id2)
|
||||
rtest.Assert(t, ok, "blob %v not present", id2)
|
||||
_, ok = c.Get(id1)
|
||||
_, ok = c.get(id1)
|
||||
rtest.Assert(t, !ok, "blob %v present, but should have been evicted", id1)
|
||||
|
||||
c.Add(id1, make([]byte, 1+c.size))
|
||||
_, ok = c.Get(id1)
|
||||
c.add(id1, make([]byte, 1+c.size))
|
||||
_, ok = c.get(id1)
|
||||
rtest.Assert(t, !ok, "blob %v too large but still added to cache")
|
||||
|
||||
c.c.Remove(id1)
|
||||
@@ -141,6 +141,6 @@ func BenchmarkAdd(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Add(ids[i%nblobs], buf[:sizes[i%nblobs]])
|
||||
c.add(ids[i%nblobs], buf[:sizes[i%nblobs]])
|
||||
}
|
||||
}
|
||||
|
||||
+9
-16
@@ -32,8 +32,8 @@ type ExtendedAttribute struct {
|
||||
// and not create GenericAttributes for them.
|
||||
type GenericAttributeType string
|
||||
|
||||
// OSType is the type created to represent each specific OS
|
||||
type OSType string
|
||||
// osType is the type created to represent each specific OS
|
||||
type osType string
|
||||
|
||||
const (
|
||||
// When new GenericAttributeType are defined, they must be added in the init function as well.
|
||||
@@ -56,14 +56,14 @@ func init() {
|
||||
}
|
||||
|
||||
// genericAttributesForOS maintains a map of known genericAttributesForOS to the OSType
|
||||
var genericAttributesForOS = map[GenericAttributeType]OSType{}
|
||||
var genericAttributesForOS = map[GenericAttributeType]osType{}
|
||||
|
||||
// storeGenericAttributeType adds and entry in genericAttributesForOS map
|
||||
func storeGenericAttributeType(attributeTypes ...GenericAttributeType) {
|
||||
for _, attributeType := range attributeTypes {
|
||||
// Get the OS attribute type from the GenericAttributeType
|
||||
osAttributeName := strings.Split(string(attributeType), ".")[0]
|
||||
genericAttributesForOS[attributeType] = OSType(osAttributeName)
|
||||
genericAttributesForOS[attributeType] = osType(osAttributeName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,13 +114,6 @@ type Node struct {
|
||||
Path string `json:"-"`
|
||||
}
|
||||
|
||||
// Nodes is a slice of nodes that can be sorted.
|
||||
type Nodes []*Node
|
||||
|
||||
func (n Nodes) Len() int { return len(n) }
|
||||
func (n Nodes) Less(i, j int) bool { return n[i].Name < n[j].Name }
|
||||
func (n Nodes) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
||||
|
||||
func (node Node) String() string {
|
||||
var mode os.FileMode
|
||||
switch node.Type {
|
||||
@@ -154,11 +147,11 @@ func (node Node) GetExtendedAttribute(a string) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FixTime returns a time.Time which can safely be used to marshal as JSON. If
|
||||
// fixTime returns a time.Time which can safely be used to marshal as JSON. If
|
||||
// the timestamp is earlier than year zero, the year is set to zero. In the same
|
||||
// way, if the year is larger than 9999, the year is set to 9999. Other than
|
||||
// the year nothing is changed.
|
||||
func FixTime(t time.Time) time.Time {
|
||||
func fixTime(t time.Time) time.Time {
|
||||
switch {
|
||||
case t.Year() < 0000:
|
||||
return t.AddDate(-t.Year(), 0, 0)
|
||||
@@ -172,9 +165,9 @@ func FixTime(t time.Time) time.Time {
|
||||
func (node Node) MarshalJSON() ([]byte, error) {
|
||||
// make sure invalid timestamps for mtime and atime are converted to
|
||||
// something we can actually save.
|
||||
node.ModTime = FixTime(node.ModTime)
|
||||
node.AccessTime = FixTime(node.AccessTime)
|
||||
node.ChangeTime = FixTime(node.ChangeTime)
|
||||
node.ModTime = fixTime(node.ModTime)
|
||||
node.AccessTime = fixTime(node.AccessTime)
|
||||
node.ChangeTime = fixTime(node.ChangeTime)
|
||||
|
||||
type nodeJSON Node
|
||||
nj := nodeJSON(node)
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestFixTime(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run("", func(t *testing.T) {
|
||||
res := FixTime(test.src)
|
||||
res := fixTime(test.src)
|
||||
if !res.Equal(test.want) {
|
||||
t.Fatalf("wrong result for %v, want:\n %v\ngot:\n %v", test.src, test.want, res)
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ func TestNodeMarshal(t *testing.T) {
|
||||
}
|
||||
|
||||
func nodeForFile(t *testing.T, name string) *data.Node {
|
||||
f, err := (&fs.Local{}).OpenFile(name, fs.O_NOFOLLOW, true)
|
||||
f, err := fs.NewLocal().OpenFile(name, fs.O_NOFOLLOW, true)
|
||||
rtest.OK(t, err)
|
||||
node, err := f.ToNode(false, t.Logf)
|
||||
rtest.OK(t, err)
|
||||
|
||||
@@ -84,7 +84,7 @@ func WriteTest(t *testing.T, format string, cd CheckDump) {
|
||||
defer cancel()
|
||||
|
||||
tmpdir, repo, be := prepareTempdirRepoSrc(t, tt.args)
|
||||
arch := archiver.New(repo, fs.Track{FS: fs.Local{}}, archiver.Options{})
|
||||
arch := archiver.New(repo, fs.Track{FS: fs.NewLocal()}, archiver.Options{})
|
||||
|
||||
back := rtest.Chdir(t, tmpdir)
|
||||
defer back()
|
||||
|
||||
@@ -17,6 +17,6 @@ func TestReaddirnamesFifo(t *testing.T) {
|
||||
fifoFn := filepath.Join(tempdir, "fifo")
|
||||
rtest.OK(t, mkfifo(fifoFn, 0o600))
|
||||
|
||||
_, err := Readdirnames(&Local{}, fifoFn, 0)
|
||||
_, err := Readdirnames(NewLocal(), fifoFn, 0)
|
||||
rtest.Assert(t, errors.Is(err, syscall.ENOTDIR), "unexpected error %v", err)
|
||||
}
|
||||
|
||||
+19
-14
@@ -14,16 +14,21 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
// Local is the local file system. Most methods are just passed on to the stdlib.
|
||||
type Local struct{}
|
||||
// local is the local file system. Most methods are just passed on to the stdlib.
|
||||
type local struct{}
|
||||
|
||||
// statically ensure that Local implements FS.
|
||||
var _ FS = &Local{}
|
||||
// NewLocal returns an FS for the local file system. Most methods are just passed on to the stdlib.
|
||||
func NewLocal() FS {
|
||||
return local{}
|
||||
}
|
||||
|
||||
// statically ensure that local implements FS.
|
||||
var _ FS = &local{}
|
||||
|
||||
// VolumeName returns leading volume name. Given "C:\foo\bar" it returns "C:"
|
||||
// on Windows. Given "\\host\share\foo" it returns "\\host\share". On other
|
||||
// platforms it returns "".
|
||||
func (fs Local) VolumeName(path string) string {
|
||||
func (fs local) VolumeName(path string) string {
|
||||
return filepath.VolumeName(path)
|
||||
}
|
||||
|
||||
@@ -35,7 +40,7 @@ func (fs Local) VolumeName(path string) string {
|
||||
// delay actually accessing the underlying filesystem.
|
||||
//
|
||||
// Only the O_NOFOLLOW and O_DIRECTORY flags are supported.
|
||||
func (fs Local) OpenFile(name string, flag int, metadataOnly bool) (File, error) {
|
||||
func (fs local) OpenFile(name string, flag int, metadataOnly bool) (File, error) {
|
||||
return newLocalFile(name, flag, metadataOnly)
|
||||
}
|
||||
|
||||
@@ -43,7 +48,7 @@ func (fs Local) OpenFile(name string, flag int, metadataOnly bool) (File, error)
|
||||
// If the file is a symbolic link, the returned FileInfo
|
||||
// describes the symbolic link. Lstat makes no attempt to follow the link.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func (fs Local) Lstat(name string) (*ExtendedFileInfo, error) {
|
||||
func (fs local) Lstat(name string) (*ExtendedFileInfo, error) {
|
||||
fi, err := os.Lstat(fixpath(name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -55,17 +60,17 @@ func (fs Local) Lstat(name string) (*ExtendedFileInfo, error) {
|
||||
// Separator if necessary. Join calls Clean on the result; in particular, all
|
||||
// empty strings are ignored. On Windows, the result is a UNC path if and only
|
||||
// if the first path element is a UNC path.
|
||||
func (fs Local) Join(elem ...string) string {
|
||||
func (fs local) Join(elem ...string) string {
|
||||
return filepath.Join(elem...)
|
||||
}
|
||||
|
||||
// Separator returns the OS and FS dependent separator for dirs/subdirs/files.
|
||||
func (fs Local) Separator() string {
|
||||
func (fs local) Separator() string {
|
||||
return string(filepath.Separator)
|
||||
}
|
||||
|
||||
// IsAbs reports whether the path is absolute.
|
||||
func (fs Local) IsAbs(path string) bool {
|
||||
func (fs local) IsAbs(path string) bool {
|
||||
return filepath.IsAbs(path)
|
||||
}
|
||||
|
||||
@@ -73,22 +78,22 @@ func (fs Local) IsAbs(path string) bool {
|
||||
// it will be joined with the current working directory to turn it into an
|
||||
// absolute path. The absolute path name for a given file is not guaranteed to
|
||||
// be unique. Abs calls Clean on the result.
|
||||
func (fs Local) Abs(path string) (string, error) {
|
||||
func (fs local) Abs(path string) (string, error) {
|
||||
return filepath.Abs(path)
|
||||
}
|
||||
|
||||
// Clean returns the cleaned path. For details, see filepath.Clean.
|
||||
func (fs Local) Clean(p string) string {
|
||||
func (fs local) Clean(p string) string {
|
||||
return filepath.Clean(p)
|
||||
}
|
||||
|
||||
// Base returns the last element of path.
|
||||
func (fs Local) Base(path string) string {
|
||||
func (fs local) Base(path string) string {
|
||||
return filepath.Base(path)
|
||||
}
|
||||
|
||||
// Dir returns path without the last element.
|
||||
func (fs Local) Dir(path string) string {
|
||||
func (fs local) Dir(path string) string {
|
||||
return filepath.Dir(path)
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ func runFSLocalTestcase(t *testing.T, test fsLocalMetadataTestcase) {
|
||||
path := filepath.Join(tmp, "item")
|
||||
test.setup(t, path)
|
||||
|
||||
testFs := &Local{}
|
||||
testFs := NewLocal()
|
||||
flags := 0
|
||||
if !test.follow {
|
||||
flags |= O_NOFOLLOW
|
||||
@@ -124,7 +124,7 @@ func testFSLocalRead(t *testing.T, makeReadable bool) {
|
||||
}
|
||||
|
||||
func openReadable(t *testing.T, path string, useMakeReadable bool) File {
|
||||
testFs := &Local{}
|
||||
testFs := NewLocal()
|
||||
f, err := testFs.OpenFile(path, O_NOFOLLOW, useMakeReadable)
|
||||
rtest.OK(t, err)
|
||||
if useMakeReadable {
|
||||
@@ -163,7 +163,7 @@ func TestFSLocalReadableRace(t *testing.T) {
|
||||
testdata := "example"
|
||||
rtest.OK(t, os.WriteFile(path, []byte(testdata), 0o600))
|
||||
|
||||
testFs := &Local{}
|
||||
testFs := NewLocal()
|
||||
f, err := testFs.OpenFile(path, O_NOFOLLOW, true)
|
||||
rtest.OK(t, err)
|
||||
|
||||
@@ -189,7 +189,7 @@ func TestFSLocalTypeChange(t *testing.T) {
|
||||
testdata := "example"
|
||||
rtest.OK(t, os.WriteFile(path, []byte(testdata), 0o600))
|
||||
|
||||
testFs := &Local{}
|
||||
testFs := NewLocal()
|
||||
f, err := testFs.OpenFile(path, O_NOFOLLOW, true)
|
||||
rtest.OK(t, err)
|
||||
// cache metadata
|
||||
|
||||
+11
-16
@@ -25,16 +25,11 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
// NewVSSConfig returns a new VSSConfig with the default values filled in.
|
||||
func NewVSSConfig() VSSConfig {
|
||||
return VSSConfig{
|
||||
Timeout: time.Second * 120,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseVSSConfig parses a VSS extended options to VSSConfig struct.
|
||||
func ParseVSSConfig(o options.Options) (VSSConfig, error) {
|
||||
cfg := NewVSSConfig()
|
||||
cfg := VSSConfig{
|
||||
Timeout: time.Second * 120,
|
||||
}
|
||||
o = o.Extract("vss")
|
||||
if err := o.Apply("vss", &cfg); err != nil {
|
||||
return VSSConfig{}, err
|
||||
@@ -49,14 +44,14 @@ type ErrorHandler func(item string, err error)
|
||||
// MessageHandler is used to report errors/messages via callbacks.
|
||||
type MessageHandler func(msg string, args ...interface{})
|
||||
|
||||
// VolumeFilter is used to filter volumes by their mount point or GUID path.
|
||||
type VolumeFilter func(volume string) bool
|
||||
// volumeFilter is used to filter volumes by their mount point or GUID path.
|
||||
type volumeFilter func(volume string) bool
|
||||
|
||||
// LocalVss is a wrapper around the local file system which uses windows volume
|
||||
// shadow copy service (VSS) in a transparent way.
|
||||
type LocalVss struct {
|
||||
FS
|
||||
snapshots map[string]VssSnapshot
|
||||
snapshots map[string]vssSnapshot
|
||||
failedSnapshots map[string]struct{}
|
||||
mutex sync.RWMutex
|
||||
msgError ErrorHandler
|
||||
@@ -95,8 +90,8 @@ func parseMountPoints(list string, msgError ErrorHandler) (volumes map[string]st
|
||||
// shadow copy service to access locked files.
|
||||
func NewLocalVss(msgError ErrorHandler, msgMessage MessageHandler, cfg VSSConfig) *LocalVss {
|
||||
return &LocalVss{
|
||||
FS: Local{},
|
||||
snapshots: make(map[string]VssSnapshot),
|
||||
FS: NewLocal(),
|
||||
snapshots: make(map[string]vssSnapshot),
|
||||
failedSnapshots: make(map[string]struct{}),
|
||||
msgError: msgError,
|
||||
msgMessage: msgMessage,
|
||||
@@ -112,7 +107,7 @@ func (fs *LocalVss) DeleteSnapshots() {
|
||||
fs.mutex.Lock()
|
||||
defer fs.mutex.Unlock()
|
||||
|
||||
activeSnapshots := make(map[string]VssSnapshot)
|
||||
activeSnapshots := make(map[string]vssSnapshot)
|
||||
|
||||
for volumeName, snapshot := range fs.snapshots {
|
||||
if err := snapshot.Delete(); err != nil {
|
||||
@@ -190,14 +185,14 @@ func (fs *LocalVss) snapshotPath(path string) string {
|
||||
} else {
|
||||
fs.msgMessage("creating VSS snapshot for [%s]\n", vssVolume)
|
||||
|
||||
var includeVolume VolumeFilter
|
||||
var includeVolume volumeFilter
|
||||
if !fs.excludeAllMountPoints {
|
||||
includeVolume = func(volume string) bool {
|
||||
return fs.isMountPointIncluded(volume)
|
||||
}
|
||||
}
|
||||
|
||||
if snapshot, err := NewVssSnapshot(fs.provider, vssVolume, fs.timeout, includeVolume, fs.msgError); err != nil {
|
||||
if snapshot, err := newVssSnapshot(fs.provider, vssVolume, fs.timeout, includeVolume, fs.msgError); err != nil {
|
||||
fs.msgError(vssVolume, errors.Errorf("failed to create snapshot for [%s]: %s",
|
||||
vssVolume, err))
|
||||
fs.failedSnapshots[volumeNameLower] = struct{}{}
|
||||
|
||||
+18
-18
@@ -14,11 +14,7 @@ import (
|
||||
"github.com/restic/restic/internal/errors"
|
||||
)
|
||||
|
||||
// Reader is a file system which provides a directory with a single file. When
|
||||
// this file is opened for reading, the reader is passed through. The file can
|
||||
// be opened once, all subsequent open calls return syscall.EIO. For Lstat(),
|
||||
// the provided FileInfo is returned.
|
||||
type Reader struct {
|
||||
type reader struct {
|
||||
items map[string]readerItem
|
||||
}
|
||||
|
||||
@@ -40,9 +36,13 @@ type readerItem struct {
|
||||
}
|
||||
|
||||
// statically ensure that Local implements FS.
|
||||
var _ FS = &Reader{}
|
||||
var _ FS = &reader{}
|
||||
|
||||
func NewReader(name string, r io.ReadCloser, opts ReaderOptions) (*Reader, error) {
|
||||
// NewReader returns a new FS which provides a directory with a single file. When
|
||||
// this file is opened for reading, the reader is passed through. The file can
|
||||
// be opened once, all subsequent open calls return syscall.EIO. For Lstat(),
|
||||
// the provided FileInfo is returned.
|
||||
func NewReader(name string, r io.ReadCloser, opts ReaderOptions) (FS, error) {
|
||||
items := make(map[string]readerItem)
|
||||
name = readerCleanPath(name)
|
||||
if name == "/" {
|
||||
@@ -90,7 +90,7 @@ func NewReader(name string, r io.ReadCloser, opts ReaderOptions) (*Reader, error
|
||||
|
||||
name = parent
|
||||
}
|
||||
return &Reader{
|
||||
return &reader{
|
||||
items: items,
|
||||
}, nil
|
||||
}
|
||||
@@ -101,11 +101,11 @@ func readerCleanPath(name string) string {
|
||||
|
||||
// VolumeName returns leading volume name, for the Reader file system it's
|
||||
// always the empty string.
|
||||
func (fs *Reader) VolumeName(_ string) string {
|
||||
func (fs *reader) VolumeName(_ string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (fs *Reader) OpenFile(name string, flag int, _ bool) (f File, err error) {
|
||||
func (fs *reader) OpenFile(name string, flag int, _ bool) (f File, err error) {
|
||||
if flag & ^(O_RDONLY|O_NOFOLLOW) != 0 {
|
||||
return nil, pathError("open", name,
|
||||
fmt.Errorf("invalid combination of flags 0x%x", flag))
|
||||
@@ -141,7 +141,7 @@ func (fs *Reader) OpenFile(name string, flag int, _ bool) (f File, err error) {
|
||||
|
||||
// Lstat returns the FileInfo structure describing the named file.
|
||||
// If there is an error, it will be of type *os.PathError.
|
||||
func (fs *Reader) Lstat(name string) (*ExtendedFileInfo, error) {
|
||||
func (fs *reader) Lstat(name string) (*ExtendedFileInfo, error) {
|
||||
name = readerCleanPath(name)
|
||||
item, ok := fs.items[name]
|
||||
if !ok {
|
||||
@@ -154,17 +154,17 @@ func (fs *Reader) Lstat(name string) (*ExtendedFileInfo, error) {
|
||||
// Separator if necessary. Join calls Clean on the result; in particular, all
|
||||
// empty strings are ignored. On Windows, the result is a UNC path if and only
|
||||
// if the first path element is a UNC path.
|
||||
func (fs *Reader) Join(elem ...string) string {
|
||||
func (fs *reader) Join(elem ...string) string {
|
||||
return path.Join(elem...)
|
||||
}
|
||||
|
||||
// Separator returns the OS and FS dependent separator for dirs/subdirs/files.
|
||||
func (fs *Reader) Separator() string {
|
||||
func (fs *reader) Separator() string {
|
||||
return "/"
|
||||
}
|
||||
|
||||
// IsAbs reports whether the path is absolute. For the Reader, this is always the case.
|
||||
func (fs *Reader) IsAbs(_ string) bool {
|
||||
func (fs *reader) IsAbs(_ string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -174,22 +174,22 @@ func (fs *Reader) IsAbs(_ string) bool {
|
||||
// be unique. Abs calls Clean on the result.
|
||||
//
|
||||
// For the Reader, all paths are absolute.
|
||||
func (fs *Reader) Abs(p string) (string, error) {
|
||||
func (fs *reader) Abs(p string) (string, error) {
|
||||
return readerCleanPath(p), nil
|
||||
}
|
||||
|
||||
// Clean returns the cleaned path. For details, see filepath.Clean.
|
||||
func (fs *Reader) Clean(p string) string {
|
||||
func (fs *reader) Clean(p string) string {
|
||||
return path.Clean(p)
|
||||
}
|
||||
|
||||
// Base returns the last element of p.
|
||||
func (fs *Reader) Base(p string) string {
|
||||
func (fs *reader) Base(p string) string {
|
||||
return path.Base(p)
|
||||
}
|
||||
|
||||
// Dir returns p without the last element.
|
||||
func (fs *Reader) Dir(p string) string {
|
||||
func (fs *reader) Dir(p string) string {
|
||||
return path.Dir(p)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
"github.com/restic/restic/internal/errors"
|
||||
)
|
||||
|
||||
// CommandReader wraps a command such that its standard output can be read using
|
||||
// commandReader wraps a command such that its standard output can be read using
|
||||
// a io.ReadCloser. Close() waits for the command to terminate, reporting
|
||||
// any error back to the caller.
|
||||
type CommandReader struct {
|
||||
type commandReader struct {
|
||||
cmd *exec.Cmd
|
||||
stdout io.ReadCloser
|
||||
|
||||
@@ -28,7 +28,7 @@ type CommandReader struct {
|
||||
alreadyClosedReadErr error
|
||||
}
|
||||
|
||||
func NewCommandReader(ctx context.Context, args []string, errorOutput func(msg string, args ...interface{})) (*CommandReader, error) {
|
||||
func NewCommandReader(ctx context.Context, args []string, errorOutput func(msg string, args ...interface{})) (io.ReadCloser, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, fmt.Errorf("no command was specified as argument")
|
||||
}
|
||||
@@ -56,14 +56,14 @@ func NewCommandReader(ctx context.Context, args []string, errorOutput func(msg s
|
||||
return nil, fmt.Errorf("failed to start command: %w", err)
|
||||
}
|
||||
|
||||
return &CommandReader{
|
||||
return &commandReader{
|
||||
cmd: command,
|
||||
stdout: stdout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read populate the array with data from the process stdout.
|
||||
func (fp *CommandReader) Read(p []byte) (int, error) {
|
||||
func (fp *commandReader) Read(p []byte) (int, error) {
|
||||
if fp.alreadyClosedReadErr != nil {
|
||||
return 0, fp.alreadyClosedReadErr
|
||||
}
|
||||
@@ -83,7 +83,7 @@ func (fp *CommandReader) Read(p []byte) (int, error) {
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (fp *CommandReader) wait() error {
|
||||
func (fp *commandReader) wait() error {
|
||||
err := fp.cmd.Wait()
|
||||
if err != nil {
|
||||
// Use a fatal error to abort the snapshot.
|
||||
@@ -92,7 +92,7 @@ func (fp *CommandReader) wait() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fp *CommandReader) Close() error {
|
||||
func (fp *commandReader) Close() error {
|
||||
if fp.waitHandled {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func BenchmarkNodeFromFileInfo(t *testing.B) {
|
||||
path := tempfile.Name()
|
||||
rtest.OK(t, tempfile.Close())
|
||||
|
||||
fs := Local{}
|
||||
fs := NewLocal()
|
||||
f, err := fs.OpenFile(path, O_NOFOLLOW, true)
|
||||
rtest.OK(t, err)
|
||||
_, err = f.Stat()
|
||||
@@ -222,7 +222,7 @@ func TestNodeRestoreAt(t *testing.T) {
|
||||
rtest.OK(t, NodeRestoreMetadata(&test, nodePath, func(msg string) { rtest.OK(t, fmt.Errorf("Warning triggered for path: %s: %s", nodePath, msg)) },
|
||||
func(_ string) bool { return true }, ownershipByName))
|
||||
|
||||
fs := &Local{}
|
||||
fs := NewLocal()
|
||||
meta, err := fs.OpenFile(nodePath, O_NOFOLLOW, true)
|
||||
rtest.OK(t, err)
|
||||
n2, err := meta.ToNode(false, t.Logf)
|
||||
|
||||
@@ -115,7 +115,7 @@ func TestNodeFromFileInfo(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
fs := &Local{}
|
||||
fs := NewLocal()
|
||||
meta, err := fs.OpenFile(test.filename, O_NOFOLLOW, true)
|
||||
rtest.OK(t, err)
|
||||
node, err := meta.ToNode(false, t.Logf)
|
||||
|
||||
@@ -390,7 +390,7 @@ func restoreAndGetNode(t *testing.T, tempDir string, testNode *data.Node, warnin
|
||||
}, func(_ string) bool { return true }, false)
|
||||
test.OK(t, errors.Wrapf(err, "Failed to restore metadata for: %s", testPath))
|
||||
|
||||
fs := &Local{}
|
||||
fs := NewLocal()
|
||||
meta, err := fs.OpenFile(testPath, O_NOFOLLOW, true)
|
||||
test.OK(t, err)
|
||||
nodeFromFileInfo, err := meta.ToNode(false, t.Logf)
|
||||
|
||||
+13
-13
@@ -8,23 +8,23 @@ import (
|
||||
"github.com/restic/restic/internal/errors"
|
||||
)
|
||||
|
||||
// MountPoint is a dummy for non-windows platforms to let client code compile.
|
||||
type MountPoint struct {
|
||||
// mountPoint is a dummy for non-windows platforms to let client code compile.
|
||||
type mountPoint struct {
|
||||
}
|
||||
|
||||
// IsSnapshotted is true if this mount point was snapshotted successfully.
|
||||
func (p *MountPoint) IsSnapshotted() bool {
|
||||
func (p *mountPoint) IsSnapshotted() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetSnapshotDeviceObject returns root path to access the snapshot files and folders.
|
||||
func (p *MountPoint) GetSnapshotDeviceObject() string {
|
||||
func (p *mountPoint) GetSnapshotDeviceObject() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// VssSnapshot is a dummy for non-windows platforms to let client code compile.
|
||||
type VssSnapshot struct {
|
||||
mountPointInfo map[string]MountPoint
|
||||
// vssSnapshot is a dummy for non-windows platforms to let client code compile.
|
||||
type vssSnapshot struct {
|
||||
mountPointInfo map[string]mountPoint
|
||||
}
|
||||
|
||||
// HasSufficientPrivilegesForVSS returns nil if the user is allowed to use VSS.
|
||||
@@ -38,20 +38,20 @@ func getVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
|
||||
return mountPoint, nil
|
||||
}
|
||||
|
||||
// NewVssSnapshot creates a new vss snapshot. If creating the snapshots doesn't
|
||||
// newVssSnapshot creates a new vss snapshot. If creating the snapshots doesn't
|
||||
// finish within the timeout an error is returned.
|
||||
func NewVssSnapshot(_ string,
|
||||
_ string, _ time.Duration, _ VolumeFilter, _ ErrorHandler) (VssSnapshot, error) {
|
||||
return VssSnapshot{}, errors.New("VSS snapshots are only supported on windows")
|
||||
func newVssSnapshot(_ string,
|
||||
_ string, _ time.Duration, _ volumeFilter, _ ErrorHandler) (vssSnapshot, error) {
|
||||
return vssSnapshot{}, errors.New("VSS snapshots are only supported on windows")
|
||||
}
|
||||
|
||||
// Delete deletes the created snapshot.
|
||||
func (p *VssSnapshot) Delete() error {
|
||||
func (p *vssSnapshot) Delete() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSnapshotDeviceObject returns root path to access the snapshot files
|
||||
// and folders.
|
||||
func (p *VssSnapshot) GetSnapshotDeviceObject() string {
|
||||
func (p *vssSnapshot) GetSnapshotDeviceObject() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
+46
-46
@@ -518,7 +518,7 @@ func (vss *IVssBackupComponents) DeleteSnapshots(snapshotID ole.GUID) (int32, ol
|
||||
|
||||
// GetSnapshotProperties calls the equivalent VSS api.
|
||||
func (vss *IVssBackupComponents) GetSnapshotProperties(snapshotID ole.GUID,
|
||||
properties *VssSnapshotProperties) error {
|
||||
properties *vssSnapshotProperties) error {
|
||||
var result uintptr
|
||||
|
||||
if runtime.GOARCH == "386" {
|
||||
@@ -537,7 +537,7 @@ func (vss *IVssBackupComponents) GetSnapshotProperties(snapshotID ole.GUID,
|
||||
}
|
||||
|
||||
// vssFreeSnapshotProperties calls the equivalent VSS api.
|
||||
func vssFreeSnapshotProperties(properties *VssSnapshotProperties) error {
|
||||
func vssFreeSnapshotProperties(properties *vssSnapshotProperties) error {
|
||||
proc, err := findVssProc("VssFreeSnapshotProperties")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -557,9 +557,9 @@ func (vss *IVssBackupComponents) BackupComplete() (*IVSSAsync, error) {
|
||||
return vss.convertToVSSAsync(oleIUnknown, err)
|
||||
}
|
||||
|
||||
// VssSnapshotProperties defines the properties of a VSS snapshot as part of the VSS api.
|
||||
// vssSnapshotProperties defines the properties of a VSS snapshot as part of the VSS api.
|
||||
// nolint:structcheck
|
||||
type VssSnapshotProperties struct {
|
||||
type vssSnapshotProperties struct {
|
||||
snapshotID ole.GUID
|
||||
snapshotSetID ole.GUID
|
||||
snapshotsCount uint32
|
||||
@@ -595,7 +595,7 @@ func vssFreeProviderProperties(p *VssProviderProperties) {
|
||||
|
||||
// GetSnapshotDeviceObject returns root path to access the snapshot files
|
||||
// and folders.
|
||||
func (p *VssSnapshotProperties) GetSnapshotDeviceObject() string {
|
||||
func (p *vssSnapshotProperties) GetSnapshotDeviceObject() string {
|
||||
return ole.UTF16PtrToString(p.snapshotDeviceObject)
|
||||
}
|
||||
|
||||
@@ -759,38 +759,38 @@ func (vssEnum *IVssEnumObject) Next(count uint, props unsafe.Pointer) (uint, err
|
||||
return uint(fetched), newVssErrorIfResultNotOK("Next() failed", HRESULT(result))
|
||||
}
|
||||
|
||||
// MountPoint wraps all information of a snapshot of a mountpoint on a volume.
|
||||
type MountPoint struct {
|
||||
// mountPoint wraps all information of a snapshot of a mountpoint on a volume.
|
||||
type mountPoint struct {
|
||||
isSnapshotted bool
|
||||
snapshotSetID ole.GUID
|
||||
snapshotProperties VssSnapshotProperties
|
||||
snapshotProperties vssSnapshotProperties
|
||||
snapshotDeviceObject string
|
||||
}
|
||||
|
||||
// IsSnapshotted is true if this mount point was snapshotted successfully.
|
||||
func (p *MountPoint) IsSnapshotted() bool {
|
||||
func (p *mountPoint) IsSnapshotted() bool {
|
||||
return p.isSnapshotted
|
||||
}
|
||||
|
||||
// GetSnapshotDeviceObject returns root path to access the snapshot files and folders.
|
||||
func (p *MountPoint) GetSnapshotDeviceObject() string {
|
||||
func (p *mountPoint) GetSnapshotDeviceObject() string {
|
||||
return p.snapshotDeviceObject
|
||||
}
|
||||
|
||||
// VssSnapshot wraps windows volume shadow copy api (vss) via a simple
|
||||
// vssSnapshot wraps windows volume shadow copy api (vss) via a simple
|
||||
// interface to create and delete a vss snapshot.
|
||||
type VssSnapshot struct {
|
||||
type vssSnapshot struct {
|
||||
iVssBackupComponents *IVssBackupComponents
|
||||
snapshotID ole.GUID
|
||||
snapshotProperties VssSnapshotProperties
|
||||
snapshotProperties vssSnapshotProperties
|
||||
snapshotDeviceObject string
|
||||
mountPointInfo map[string]MountPoint
|
||||
mountPointInfo map[string]mountPoint
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// GetSnapshotDeviceObject returns root path to access the snapshot files
|
||||
// and folders.
|
||||
func (p *VssSnapshot) GetSnapshotDeviceObject() string {
|
||||
func (p *vssSnapshot) GetSnapshotDeviceObject() string {
|
||||
return p.snapshotDeviceObject
|
||||
}
|
||||
|
||||
@@ -883,18 +883,18 @@ func getVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
|
||||
return syscall.UTF16ToString(volumeNameBuffer), nil
|
||||
}
|
||||
|
||||
// NewVssSnapshot creates a new vss snapshot. If creating the snapshots doesn't
|
||||
// newVssSnapshot creates a new vss snapshot. If creating the snapshots doesn't
|
||||
// finish within the timeout an error is returned.
|
||||
func NewVssSnapshot(provider string,
|
||||
volume string, timeout time.Duration, filter VolumeFilter, msgError ErrorHandler) (VssSnapshot, error) {
|
||||
func newVssSnapshot(provider string,
|
||||
volume string, timeout time.Duration, filter volumeFilter, msgError ErrorHandler) (vssSnapshot, error) {
|
||||
is64Bit, err := isRunningOn64BitWindows()
|
||||
if err != nil {
|
||||
return VssSnapshot{}, newVssTextError(fmt.Sprintf(
|
||||
return vssSnapshot{}, newVssTextError(fmt.Sprintf(
|
||||
"Failed to detect windows architecture: %s", err.Error()))
|
||||
}
|
||||
|
||||
if (is64Bit && runtime.GOARCH != "amd64") || (!is64Bit && runtime.GOARCH != "386") {
|
||||
return VssSnapshot{}, newVssTextError(fmt.Sprintf("executables compiled for %s can't use "+
|
||||
return vssSnapshot{}, newVssTextError(fmt.Sprintf("executables compiled for %s can't use "+
|
||||
"VSS on other architectures. Please use an executable compiled for your platform.",
|
||||
runtime.GOARCH))
|
||||
}
|
||||
@@ -906,12 +906,12 @@ func NewVssSnapshot(provider string,
|
||||
defer oleIUnknown.Release()
|
||||
}
|
||||
if err != nil {
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
comInterface, err := queryInterface(oleIUnknown, UUID_IVSS)
|
||||
if err != nil {
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -936,39 +936,39 @@ func NewVssSnapshot(provider string,
|
||||
providerID, err := getProviderID(provider)
|
||||
if err != nil {
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
if err := iVssBackupComponents.InitializeForBackup(); err != nil {
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
if err := iVssBackupComponents.SetContext(VSS_CTX_BACKUP); err != nil {
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
// see https://techcommunity.microsoft.com/t5/Storage-at-Microsoft/What-is-the-difference-between-VSS-Full-Backup-and-VSS-Copy/ba-p/423575
|
||||
|
||||
if err := iVssBackupComponents.SetBackupState(false, false, VSS_BT_COPY, false); err != nil {
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
err = callAsyncFunctionAndWait(iVssBackupComponents.GatherWriterMetadata,
|
||||
"GatherWriterMetadata", deadline)
|
||||
if err != nil {
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
if isSupported, err := iVssBackupComponents.IsVolumeSupported(providerID, volume); err != nil {
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
} else if !isSupported {
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, newVssTextError(fmt.Sprintf("Snapshots are not supported for volume "+
|
||||
return vssSnapshot{}, newVssTextError(fmt.Sprintf("Snapshots are not supported for volume "+
|
||||
"%s", volume))
|
||||
}
|
||||
|
||||
@@ -985,7 +985,7 @@ func NewVssSnapshot(provider string,
|
||||
|
||||
if err != nil {
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
} else {
|
||||
break
|
||||
}
|
||||
@@ -993,10 +993,10 @@ func NewVssSnapshot(provider string,
|
||||
|
||||
if err := iVssBackupComponents.AddToSnapshotSet(volume, providerID, &snapshotSetID); err != nil {
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
mountPointInfo := make(map[string]MountPoint)
|
||||
mountPointInfo := make(map[string]mountPoint)
|
||||
|
||||
// if filter==nil just don't process mount points for this volume at all
|
||||
if filter != nil {
|
||||
@@ -1004,32 +1004,32 @@ func NewVssSnapshot(provider string,
|
||||
if err != nil {
|
||||
iVssBackupComponents.Release()
|
||||
|
||||
return VssSnapshot{}, newVssTextError(fmt.Sprintf(
|
||||
return vssSnapshot{}, newVssTextError(fmt.Sprintf(
|
||||
"failed to enumerate mount points for volume %s: %s", volume, err))
|
||||
}
|
||||
|
||||
for _, mountPoint := range mountPoints {
|
||||
for _, mp := range mountPoints {
|
||||
// ensure every mountpoint is available even without a valid
|
||||
// snapshot because we need to consider this when backing up files
|
||||
mountPointInfo[mountPoint] = MountPoint{isSnapshotted: false}
|
||||
mountPointInfo[mp] = mountPoint{isSnapshotted: false}
|
||||
|
||||
if !filter(mountPoint) {
|
||||
if !filter(mp) {
|
||||
continue
|
||||
} else if isSupported, err := iVssBackupComponents.IsVolumeSupported(providerID, mountPoint); err != nil {
|
||||
} else if isSupported, err := iVssBackupComponents.IsVolumeSupported(providerID, mp); err != nil {
|
||||
continue
|
||||
} else if !isSupported {
|
||||
continue
|
||||
}
|
||||
|
||||
var mountPointSnapshotSetID ole.GUID
|
||||
err := iVssBackupComponents.AddToSnapshotSet(mountPoint, providerID, &mountPointSnapshotSetID)
|
||||
err := iVssBackupComponents.AddToSnapshotSet(mp, providerID, &mountPointSnapshotSetID)
|
||||
if err != nil {
|
||||
iVssBackupComponents.Release()
|
||||
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
mountPointInfo[mountPoint] = MountPoint{
|
||||
mountPointInfo[mp] = mountPoint{
|
||||
isSnapshotted: true,
|
||||
snapshotSetID: mountPointSnapshotSetID,
|
||||
}
|
||||
@@ -1044,7 +1044,7 @@ func NewVssSnapshot(provider string,
|
||||
// It is not necessary to call BackupComplete before releasing the VSS instance afterwards.
|
||||
iVssBackupComponents.AbortBackup()
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
err = callAsyncFunctionAndWait(iVssBackupComponents.DoSnapshotSet, "DoSnapshotSet",
|
||||
@@ -1052,15 +1052,15 @@ func NewVssSnapshot(provider string,
|
||||
if err != nil {
|
||||
_ = iVssBackupComponents.AbortBackup()
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
var snapshotProperties VssSnapshotProperties
|
||||
var snapshotProperties vssSnapshotProperties
|
||||
err = iVssBackupComponents.GetSnapshotProperties(snapshotSetID, &snapshotProperties)
|
||||
if err != nil {
|
||||
_ = iVssBackupComponents.AbortBackup()
|
||||
iVssBackupComponents.Release()
|
||||
return VssSnapshot{}, err
|
||||
return vssSnapshot{}, err
|
||||
}
|
||||
|
||||
for mountPoint, info := range mountPointInfo {
|
||||
@@ -1082,14 +1082,14 @@ func NewVssSnapshot(provider string,
|
||||
mountPointInfo[mountPoint] = info
|
||||
}
|
||||
|
||||
return VssSnapshot{
|
||||
return vssSnapshot{
|
||||
iVssBackupComponents, snapshotSetID, snapshotProperties,
|
||||
snapshotProperties.GetSnapshotDeviceObject(), mountPointInfo, time.Until(deadline),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Delete deletes the created snapshot.
|
||||
func (p *VssSnapshot) Delete() error {
|
||||
func (p *vssSnapshot) Delete() error {
|
||||
var err error
|
||||
if err = vssFreeSnapshotProperties(&p.snapshotProperties); err != nil {
|
||||
return err
|
||||
|
||||
@@ -79,8 +79,8 @@ type Checker struct {
|
||||
repo *Repository
|
||||
}
|
||||
|
||||
// NewChecker creates a new Checker.
|
||||
func NewChecker(repo *Repository) *Checker {
|
||||
// newChecker creates a new Checker.
|
||||
func newChecker(repo *Repository) *Checker {
|
||||
return &Checker{
|
||||
repo: repo,
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ func setupChecker(t *testing.T, wrap func(backend.Backend) backend.Backend) *Che
|
||||
// Re-open the same backend (now containing real pack files) through
|
||||
// the corruption wrapper so the checker reads corrupted data.
|
||||
checkRepo := TestOpenBackend(t, wrap(be))
|
||||
chkr := NewChecker(checkRepo)
|
||||
chkr := newChecker(checkRepo)
|
||||
|
||||
// make sure the index is loaded
|
||||
err := checkRepo.LoadIndex(context.TODO(), nil)
|
||||
|
||||
@@ -61,8 +61,8 @@ func createMasterKey(ctx context.Context, s *Repository, password string) (*Key,
|
||||
return AddKey(ctx, s, password, "", "", nil)
|
||||
}
|
||||
|
||||
// OpenKey tries do decrypt the key specified by name with the given password.
|
||||
func OpenKey(ctx context.Context, s *Repository, id restic.ID, password string) (*Key, error) {
|
||||
// openKey tries do decrypt the key specified by name with the given password.
|
||||
func openKey(ctx context.Context, s *Repository, id restic.ID, password string) (*Key, error) {
|
||||
k, err := LoadKey(ctx, s, id)
|
||||
if err != nil {
|
||||
debug.Log("LoadKey(%v) returned error %v", id.String(), err)
|
||||
@@ -108,18 +108,18 @@ func OpenKey(ctx context.Context, s *Repository, id restic.ID, password string)
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// SearchKey tries to decrypt at most maxKeys keys in the backend with the
|
||||
// searchKey tries to decrypt at most maxKeys keys in the backend with the
|
||||
// given password. If none could be found, ErrNoKeyFound is returned. When
|
||||
// maxKeys is reached, ErrMaxKeysReached is returned. When setting maxKeys to
|
||||
// zero, all keys in the repo are checked.
|
||||
func SearchKey(ctx context.Context, s *Repository, password string, maxKeys int, keyHint string) (k *Key, err error) {
|
||||
func searchKey(ctx context.Context, s *Repository, password string, maxKeys int, keyHint string) (k *Key, err error) {
|
||||
checked := 0
|
||||
|
||||
if len(keyHint) > 0 {
|
||||
id, err := restic.Find(ctx, s, restic.KeyFile, keyHint)
|
||||
|
||||
if err == nil {
|
||||
key, err := OpenKey(ctx, s, id, password)
|
||||
key, err := openKey(ctx, s, id, password)
|
||||
|
||||
if err == nil {
|
||||
debug.Log("successfully opened hinted key %v", id)
|
||||
@@ -143,7 +143,7 @@ func SearchKey(ctx context.Context, s *Repository, password string, maxKeys int,
|
||||
}
|
||||
|
||||
debug.Log("trying key %q", id.String())
|
||||
key, err := OpenKey(ctx, s, id, password)
|
||||
key, err := openKey(ctx, s, id, password)
|
||||
if err != nil {
|
||||
debug.Log("key %v returned error %v", id.String(), err)
|
||||
|
||||
|
||||
@@ -37,13 +37,13 @@ var lockerInst = &locker{
|
||||
refreshabilityTimeout: restic.StaleLockTimeout - defaultRefreshInterval*3/2,
|
||||
}
|
||||
|
||||
func Lock(ctx context.Context, repo *Repository, exclusive bool, retryLock time.Duration, printRetry func(msg string), logger func(format string, args ...interface{})) (*Unlocker, context.Context, error) {
|
||||
func Lock(ctx context.Context, repo *Repository, exclusive bool, retryLock time.Duration, printRetry func(msg string), logger func(format string, args ...interface{})) (Unlocker, context.Context, error) {
|
||||
return lockerInst.Lock(ctx, repo, exclusive, retryLock, printRetry, logger)
|
||||
}
|
||||
|
||||
// Lock wraps the ctx such that it is cancelled when the repository is unlocked
|
||||
// cancelling the original context also stops the lock refresh
|
||||
func (l *locker) Lock(ctx context.Context, r *Repository, exclusive bool, retryLock time.Duration, printRetry func(msg string), logger func(format string, args ...interface{})) (*Unlocker, context.Context, error) {
|
||||
func (l *locker) Lock(ctx context.Context, r *Repository, exclusive bool, retryLock time.Duration, printRetry func(msg string), logger func(format string, args ...interface{})) (Unlocker, context.Context, error) {
|
||||
var lock *restic.Lock
|
||||
var err error
|
||||
|
||||
@@ -102,7 +102,7 @@ retryLoop:
|
||||
go l.refreshLocks(ctx, repo.be, lockInfo, refreshChan, forceRefreshChan, logger)
|
||||
go l.monitorLockRefresh(ctx, lockInfo, refreshChan, forceRefreshChan, logger)
|
||||
|
||||
return &Unlocker{lockInfo}, ctx, nil
|
||||
return &unlocker{lockInfo}, ctx, nil
|
||||
}
|
||||
|
||||
func minDuration(a, b time.Duration) time.Duration {
|
||||
@@ -261,11 +261,15 @@ func tryRefreshStaleLock(ctx context.Context, be backend.Backend, lock *restic.L
|
||||
return true
|
||||
}
|
||||
|
||||
type Unlocker struct {
|
||||
type Unlocker interface {
|
||||
Unlock()
|
||||
}
|
||||
|
||||
type unlocker struct {
|
||||
info *lockContext
|
||||
}
|
||||
|
||||
func (l *Unlocker) Unlock() {
|
||||
func (l *unlocker) Unlock() {
|
||||
l.info.cancel()
|
||||
l.info.refreshWG.Wait()
|
||||
}
|
||||
|
||||
@@ -34,11 +34,11 @@ func openLockTestRepo(t *testing.T, wrapper backendWrapper) (*Repository, backen
|
||||
return TestOpenBackend(t, be), be
|
||||
}
|
||||
|
||||
func checkedLockRepo(ctx context.Context, t *testing.T, repo *Repository, lockerInst *locker, retryLock time.Duration) (*Unlocker, context.Context) {
|
||||
func checkedLockRepo(ctx context.Context, t *testing.T, repo *Repository, lockerInst *locker, retryLock time.Duration) (Unlocker, context.Context) {
|
||||
lock, wrappedCtx, err := lockerInst.Lock(ctx, repo, false, retryLock, func(msg string) {}, func(format string, args ...interface{}) {})
|
||||
rtest.OK(t, err)
|
||||
rtest.OK(t, wrappedCtx.Err())
|
||||
if lock.info.lock.Stale() {
|
||||
if lock.(*unlocker).info.lock.Stale() {
|
||||
t.Fatal("lock returned stale lock")
|
||||
}
|
||||
return lock, wrappedCtx
|
||||
|
||||
@@ -180,7 +180,7 @@ func (r *Repository) SetDryRun() {
|
||||
}
|
||||
|
||||
func (r *Repository) Checker() *Checker {
|
||||
return NewChecker(r)
|
||||
return newChecker(r)
|
||||
}
|
||||
|
||||
// LoadUnpacked loads and decrypts the file with the given type and ID.
|
||||
@@ -862,7 +862,7 @@ func (r *Repository) prepareCache() error {
|
||||
// SearchKey finds a key with the supplied password, afterwards the config is
|
||||
// read and parsed. It tries at most maxKeys key files in the repo.
|
||||
func (r *Repository) SearchKey(ctx context.Context, password string, maxKeys int, keyHint string) error {
|
||||
key, err := SearchKey(ctx, r, password, maxKeys, keyHint)
|
||||
key, err := searchKey(ctx, r, password, maxKeys, keyHint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ func TestNewLock(_ *testing.T, repo *Repository, exclusive bool) (*restic.Lock,
|
||||
|
||||
// TestCheckRepo runs the checker on repo.
|
||||
func TestCheckRepo(t testing.TB, repo *Repository) {
|
||||
chkr := NewChecker(repo)
|
||||
chkr := newChecker(repo)
|
||||
|
||||
hints, errs := chkr.LoadIndex(context.TODO(), nil)
|
||||
if len(errs) != 0 {
|
||||
|
||||
@@ -7,18 +7,18 @@ import (
|
||||
"github.com/restic/restic/internal/restic"
|
||||
)
|
||||
|
||||
type WarmupJob struct {
|
||||
type warmupJob struct {
|
||||
repo *Repository
|
||||
handlesWarmingUp []backend.Handle
|
||||
}
|
||||
|
||||
// HandleCount returns the number of handles that are currently warming up.
|
||||
func (job *WarmupJob) HandleCount() int {
|
||||
func (job *warmupJob) HandleCount() int {
|
||||
return len(job.handlesWarmingUp)
|
||||
}
|
||||
|
||||
// Wait waits for all handles to be warm.
|
||||
func (job *WarmupJob) Wait(ctx context.Context) error {
|
||||
func (job *warmupJob) Wait(ctx context.Context) error {
|
||||
return job.repo.be.WarmupWait(ctx, job.handlesWarmingUp)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ func (r *Repository) StartWarmup(ctx context.Context, packs restic.IDSet) (resti
|
||||
)
|
||||
}
|
||||
handlesWarmingUp, err := r.be.Warmup(ctx, handles)
|
||||
return &WarmupJob{
|
||||
return &warmupJob{
|
||||
repo: r,
|
||||
handlesWarmingUp: handlesWarmingUp,
|
||||
}, err
|
||||
|
||||
@@ -4,21 +4,21 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// HardlinkKey is a composed key for finding inodes on a specific device.
|
||||
type HardlinkKey struct {
|
||||
// hardlinkKey is a composed key for finding inodes on a specific device.
|
||||
type hardlinkKey struct {
|
||||
Inode, Device uint64
|
||||
}
|
||||
|
||||
// HardlinkIndex maps inodes on devices to associated values.
|
||||
type HardlinkIndex[T any] struct {
|
||||
m sync.Mutex
|
||||
Index map[HardlinkKey]T
|
||||
index map[hardlinkKey]T
|
||||
}
|
||||
|
||||
// NewHardlinkIndex create a new index for hard links
|
||||
func NewHardlinkIndex[T any]() *HardlinkIndex[T] {
|
||||
return &HardlinkIndex[T]{
|
||||
Index: make(map[HardlinkKey]T),
|
||||
index: make(map[hardlinkKey]T),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func NewHardlinkIndex[T any]() *HardlinkIndex[T] {
|
||||
func (idx *HardlinkIndex[T]) Has(inode uint64, device uint64) bool {
|
||||
idx.m.Lock()
|
||||
defer idx.m.Unlock()
|
||||
_, ok := idx.Index[HardlinkKey{inode, device}]
|
||||
_, ok := idx.index[hardlinkKey{inode, device}]
|
||||
|
||||
return ok
|
||||
}
|
||||
@@ -35,10 +35,10 @@ func (idx *HardlinkIndex[T]) Has(inode uint64, device uint64) bool {
|
||||
func (idx *HardlinkIndex[T]) Add(inode uint64, device uint64, value T) {
|
||||
idx.m.Lock()
|
||||
defer idx.m.Unlock()
|
||||
_, ok := idx.Index[HardlinkKey{inode, device}]
|
||||
_, ok := idx.index[hardlinkKey{inode, device}]
|
||||
|
||||
if !ok {
|
||||
idx.Index[HardlinkKey{inode, device}] = value
|
||||
idx.index[hardlinkKey{inode, device}] = value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +46,12 @@ func (idx *HardlinkIndex[T]) Add(inode uint64, device uint64, value T) {
|
||||
func (idx *HardlinkIndex[T]) Value(inode uint64, device uint64) T {
|
||||
idx.m.Lock()
|
||||
defer idx.m.Unlock()
|
||||
return idx.Index[HardlinkKey{inode, device}]
|
||||
return idx.index[hardlinkKey{inode, device}]
|
||||
}
|
||||
|
||||
// Remove removes a link from the index.
|
||||
func (idx *HardlinkIndex[T]) Remove(inode uint64, device uint64) {
|
||||
idx.m.Lock()
|
||||
defer idx.m.Unlock()
|
||||
delete(idx.Index, HardlinkKey{inode, device})
|
||||
delete(idx.index, hardlinkKey{inode, device})
|
||||
}
|
||||
|
||||
@@ -489,7 +489,7 @@ func (res *Restorer) removeUnexpectedFiles(ctx context.Context, target, location
|
||||
panic("internal error")
|
||||
}
|
||||
|
||||
entries, err := fs.Readdirnames(fs.Local{}, target, fs.O_NOFOLLOW)
|
||||
entries, err := fs.Readdirnames(fs.NewLocal(), target, fs.O_NOFOLLOW)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
|
||||
@@ -1537,7 +1537,7 @@ func TestRestorerLongPath(t *testing.T) {
|
||||
|
||||
repo := repository.TestRepository(t)
|
||||
|
||||
local := &fs.Local{}
|
||||
local := fs.NewLocal()
|
||||
sc := archiver.NewScanner(local)
|
||||
rtest.OK(t, sc.Scan(context.TODO(), []string{tmp}))
|
||||
arch := archiver.New(repo, local, archiver.Options{})
|
||||
|
||||
Reference in New Issue
Block a user