mirror of
https://github.com/octoleo/restic.git
synced 2024-11-26 14:56:29 +00:00
Merge pull request #3250 from restic/add-golangci-lint-config
Add golangci lint config, add many error checks
This commit is contained in:
commit
99228be623
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -241,7 +241,7 @@ jobs:
|
|||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
with:
|
with:
|
||||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||||
version: v1.29
|
version: v1.36
|
||||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||||
only-new-issues: true
|
only-new-issues: true
|
||||||
args: --verbose --timeout 5m
|
args: --verbose --timeout 5m
|
||||||
|
57
.golangci.yml
Normal file
57
.golangci.yml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# This is the configuration for golangci-lint for the restic project.
|
||||||
|
#
|
||||||
|
# A sample config with all settings is here:
|
||||||
|
# https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml
|
||||||
|
|
||||||
|
linters:
|
||||||
|
# only enable the linters listed below
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
# make sure all errors returned by functions are handled
|
||||||
|
- errcheck
|
||||||
|
|
||||||
|
# find unused code
|
||||||
|
- deadcode
|
||||||
|
|
||||||
|
# show how code can be simplified
|
||||||
|
- gosimple
|
||||||
|
|
||||||
|
# # make sure code is formatted
|
||||||
|
- gofmt
|
||||||
|
|
||||||
|
# examine code and report suspicious constructs, such as Printf calls whose
|
||||||
|
# arguments do not align with the format string
|
||||||
|
- govet
|
||||||
|
|
||||||
|
# make sure names and comments are used according to the conventions
|
||||||
|
- golint
|
||||||
|
|
||||||
|
# detect when assignments to existing variables are not used
|
||||||
|
- ineffassign
|
||||||
|
|
||||||
|
# run static analysis and find errors
|
||||||
|
- staticcheck
|
||||||
|
|
||||||
|
# find unused variables, functions, structs, types, etc.
|
||||||
|
- unused
|
||||||
|
|
||||||
|
# find unused struct fields
|
||||||
|
- structcheck
|
||||||
|
|
||||||
|
# find unused global variables
|
||||||
|
- varcheck
|
||||||
|
|
||||||
|
# parse and typecheck code
|
||||||
|
- typecheck
|
||||||
|
|
||||||
|
issues:
|
||||||
|
# don't use the default exclude rules, this hides (among others) ignored
|
||||||
|
# errors from Close() calls
|
||||||
|
exclude-use-default: false
|
||||||
|
|
||||||
|
# list of things to not warn about
|
||||||
|
exclude:
|
||||||
|
# golint: do not warn about missing comments for exported stuff
|
||||||
|
- exported (function|method|var|type|const) `.*` should have comment or be unexported
|
||||||
|
# golint: ignore constants in all caps
|
||||||
|
- don't use ALL_CAPS in Go names; use CamelCase
|
@ -141,6 +141,14 @@ Installing the script `fmt-check` from https://github.com/edsrzf/gofmt-git-hook
|
|||||||
locally as a pre-commit hook checks formatting before committing automatically,
|
locally as a pre-commit hook checks formatting before committing automatically,
|
||||||
just copy this script to `.git/hooks/pre-commit`.
|
just copy this script to `.git/hooks/pre-commit`.
|
||||||
|
|
||||||
|
The project is using the program
|
||||||
|
[`golangci-lint`](https://github.com/golangci/golangci-lint) to run a list of
|
||||||
|
linters and checkers. It will be run on the code when you submit a PR. In order
|
||||||
|
to check your code beforehand, you can run `golangci-lint run` manually.
|
||||||
|
Eventually, we will enable `golangci-lint` for the whole code base. For now,
|
||||||
|
you can ignore warnings printed for lines you did not modify, those will be
|
||||||
|
ignored by the CI.
|
||||||
|
|
||||||
For each pull request, several different systems run the integration tests on
|
For each pull request, several different systems run the integration tests on
|
||||||
Linux, macOS and Windows. We won't merge any code that does not pass all tests
|
Linux, macOS and Windows. We won't merge any code that does not pass all tests
|
||||||
for all systems, so when a tests fails, try to find out what's wrong and fix
|
for all systems, so when a tests fails, try to find out what's wrong and fix
|
||||||
|
6
changelog/unreleased/pr-3250
Normal file
6
changelog/unreleased/pr-3250
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Enhancement: Add more error checks
|
||||||
|
|
||||||
|
We've added a lot more error checks in places where errors were ignored before
|
||||||
|
(as hinted by the static analysis programm `errcheck` via `golangci-lint`).
|
||||||
|
|
||||||
|
https://github.com/restic/restic/pull/3250
|
@ -119,7 +119,11 @@ func init() {
|
|||||||
|
|
||||||
f.StringVarP(&backupOptions.Host, "host", "H", "", "set the `hostname` for the snapshot manually. To prevent an expensive rescan use the \"parent\" flag")
|
f.StringVarP(&backupOptions.Host, "host", "H", "", "set the `hostname` for the snapshot manually. To prevent an expensive rescan use the \"parent\" flag")
|
||||||
f.StringVar(&backupOptions.Host, "hostname", "", "set the `hostname` for the snapshot manually")
|
f.StringVar(&backupOptions.Host, "hostname", "", "set the `hostname` for the snapshot manually")
|
||||||
f.MarkDeprecated("hostname", "use --host")
|
err := f.MarkDeprecated("hostname", "use --host")
|
||||||
|
if err != nil {
|
||||||
|
// MarkDeprecated only returns an error when the flag could not be found
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
f.StringArrayVar(&backupOptions.FilesFrom, "files-from", nil, "read the files to backup from `file` (can be combined with file args; can be specified multiple times)")
|
f.StringArrayVar(&backupOptions.FilesFrom, "files-from", nil, "read the files to backup from `file` (can be combined with file args; can be specified multiple times)")
|
||||||
f.StringArrayVar(&backupOptions.FilesFromVerbatim, "files-from-verbatim", nil, "read the files to backup from `file` (can be combined with file args; can be specified multiple times)")
|
f.StringArrayVar(&backupOptions.FilesFromVerbatim, "files-from-verbatim", nil, "read the files to backup from `file` (can be combined with file args; can be specified multiple times)")
|
||||||
@ -201,10 +205,21 @@ func readFilenamesFromFileRaw(filename string) (names []string, err error) {
|
|||||||
if f, err = os.Open(filename); err != nil {
|
if f, err = os.Open(filename); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return readFilenamesRaw(f)
|
names, err = readFilenamesRaw(f)
|
||||||
|
if err != nil {
|
||||||
|
// ignore subsequent errors
|
||||||
|
_ = f.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return names, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readFilenamesRaw(r io.Reader) (names []string, err error) {
|
func readFilenamesRaw(r io.Reader) (names []string, err error) {
|
||||||
|
@ -32,7 +32,7 @@ func TestCollectTargets(t *testing.T) {
|
|||||||
// All mentioned files must exist for collectTargets.
|
// All mentioned files must exist for collectTargets.
|
||||||
f, err := os.Create(filepath.Join(dir, filename))
|
f, err := os.Create(filepath.Join(dir, filename))
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
f.Close()
|
rtest.OK(t, f.Close())
|
||||||
|
|
||||||
expect = append(expect, f.Name())
|
expect = append(expect, f.Name())
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ func TestCollectTargets(t *testing.T) {
|
|||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
// Empty lines should be ignored. A line starting with '#' is a comment.
|
// Empty lines should be ignored. A line starting with '#' is a comment.
|
||||||
fmt.Fprintf(f1, "\n%s*\n # here's a comment\n", f1.Name())
|
fmt.Fprintf(f1, "\n%s*\n # here's a comment\n", f1.Name())
|
||||||
f1.Close()
|
rtest.OK(t, f1.Close())
|
||||||
|
|
||||||
f2, err := os.Create(filepath.Join(dir, "fromfile-verbatim"))
|
f2, err := os.Create(filepath.Join(dir, "fromfile-verbatim"))
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
@ -49,7 +49,7 @@ func TestCollectTargets(t *testing.T) {
|
|||||||
// Empty lines should be ignored. CR+LF is allowed.
|
// Empty lines should be ignored. CR+LF is allowed.
|
||||||
fmt.Fprintf(f2, "%s\r\n\n", filepath.Join(dir, filename))
|
fmt.Fprintf(f2, "%s\r\n\n", filepath.Join(dir, filename))
|
||||||
}
|
}
|
||||||
f2.Close()
|
rtest.OK(t, f2.Close())
|
||||||
|
|
||||||
f3, err := os.Create(filepath.Join(dir, "fromfile-raw"))
|
f3, err := os.Create(filepath.Join(dir, "fromfile-raw"))
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
@ -57,7 +57,7 @@ func TestCollectTargets(t *testing.T) {
|
|||||||
fmt.Fprintf(f3, "%s\x00", filepath.Join(dir, filename))
|
fmt.Fprintf(f3, "%s\x00", filepath.Join(dir, filename))
|
||||||
}
|
}
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
f3.Close()
|
rtest.OK(t, f3.Close())
|
||||||
|
|
||||||
opts := BackupOptions{
|
opts := BackupOptions{
|
||||||
FilesFrom: []string{f1.Name()},
|
FilesFrom: []string{f1.Name()},
|
||||||
|
@ -148,7 +148,7 @@ func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
tab.Write(gopts.stdout)
|
_ = tab.Write(gopts.stdout)
|
||||||
Printf("%d cache dirs in %s\n", len(dirs), cachedir)
|
Printf("%d cache dirs in %s\n", len(dirs), cachedir)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -563,7 +563,10 @@ func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.PackID {
|
if opts.PackID {
|
||||||
f.packsToBlobs(ctx, []string{f.pat.pattern[0]}) // TODO: support multiple packs
|
err := f.packsToBlobs(ctx, []string{f.pat.pattern[0]}) // TODO: support multiple packs
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Hosts, opts.Tags, opts.Paths, opts.Snapshots) {
|
for sn := range FindFilteredSnapshots(ctx, repo, opts.Hosts, opts.Tags, opts.Paths, opts.Snapshots) {
|
||||||
|
@ -68,7 +68,11 @@ func init() {
|
|||||||
f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)")
|
f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)")
|
||||||
f.StringArrayVar(&forgetOptions.Hosts, "host", nil, "only consider snapshots with the given `host` (can be specified multiple times)")
|
f.StringArrayVar(&forgetOptions.Hosts, "host", nil, "only consider snapshots with the given `host` (can be specified multiple times)")
|
||||||
f.StringArrayVar(&forgetOptions.Hosts, "hostname", nil, "only consider snapshots with the given `hostname` (can be specified multiple times)")
|
f.StringArrayVar(&forgetOptions.Hosts, "hostname", nil, "only consider snapshots with the given `hostname` (can be specified multiple times)")
|
||||||
f.MarkDeprecated("hostname", "use --host")
|
err := f.MarkDeprecated("hostname", "use --host")
|
||||||
|
if err != nil {
|
||||||
|
// MarkDeprecated only returns an error when the flag is not found
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
f.Var(&forgetOptions.Tags, "tag", "only consider snapshots which include this `taglist` in the format `tag[,tag,...]` (can be specified multiple times)")
|
f.Var(&forgetOptions.Tags, "tag", "only consider snapshots which include this `taglist` in the format `tag[,tag,...]` (can be specified multiple times)")
|
||||||
|
|
||||||
|
@ -159,16 +159,17 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
|
|||||||
enc := json.NewEncoder(gopts.stdout)
|
enc := json.NewEncoder(gopts.stdout)
|
||||||
|
|
||||||
printSnapshot = func(sn *restic.Snapshot) {
|
printSnapshot = func(sn *restic.Snapshot) {
|
||||||
enc.Encode(lsSnapshot{
|
err = enc.Encode(lsSnapshot{
|
||||||
Snapshot: sn,
|
Snapshot: sn,
|
||||||
ID: sn.ID(),
|
ID: sn.ID(),
|
||||||
ShortID: sn.ID().Str(),
|
ShortID: sn.ID().Str(),
|
||||||
StructType: "snapshot",
|
StructType: "snapshot",
|
||||||
})
|
})
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
printNode = func(path string, node *restic.Node) {
|
printNode = func(path string, node *restic.Node) {
|
||||||
enc.Encode(lsNode{
|
err = enc.Encode(lsNode{
|
||||||
Name: node.Name,
|
Name: node.Name,
|
||||||
Type: node.Type,
|
Type: node.Type,
|
||||||
Path: path,
|
Path: path,
|
||||||
@ -181,6 +182,7 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
|
|||||||
ChangeTime: node.ChangeTime,
|
ChangeTime: node.ChangeTime,
|
||||||
StructType: "node",
|
StructType: "node",
|
||||||
})
|
})
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
printSnapshot = func(sn *restic.Snapshot) {
|
printSnapshot = func(sn *restic.Snapshot) {
|
||||||
|
@ -117,7 +117,10 @@ func runRecover(gopts GlobalOptions) error {
|
|||||||
ModTime: time.Now(),
|
ModTime: time.Now(),
|
||||||
ChangeTime: time.Now(),
|
ChangeTime: time.Now(),
|
||||||
}
|
}
|
||||||
tree.Insert(&node)
|
err = tree.Insert(&node)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
treeID, err := repo.SaveTree(gopts.ctx, tree)
|
treeID, err := repo.SaveTree(gopts.ctx, tree)
|
||||||
|
@ -243,7 +243,10 @@ func PrintSnapshots(stdout io.Writer, list restic.Snapshots, reasons []restic.Ke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tab.Write(stdout)
|
err := tab.Write(stdout)
|
||||||
|
if err != nil {
|
||||||
|
Warnf("error printing: %v\n", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintSnapshotGroupHeader prints which group of the group-by option the
|
// PrintSnapshotGroupHeader prints which group of the group-by option the
|
||||||
|
@ -11,7 +11,8 @@ import (
|
|||||||
func TestEmptySnapshotGroupJSON(t *testing.T) {
|
func TestEmptySnapshotGroupJSON(t *testing.T) {
|
||||||
for _, grouped := range []bool{false, true} {
|
for _, grouped := range []bool{false, true} {
|
||||||
var w strings.Builder
|
var w strings.Builder
|
||||||
printSnapshotGroupJSON(&w, nil, grouped)
|
err := printSnapshotGroupJSON(&w, nil, grouped)
|
||||||
|
rtest.OK(t, err)
|
||||||
|
|
||||||
rtest.Equals(t, "[]", strings.TrimSpace(w.String()))
|
rtest.Equals(t, "[]", strings.TrimSpace(w.String()))
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
// DeleteFiles deletes the given fileList of fileType in parallel
|
// DeleteFiles deletes the given fileList of fileType in parallel
|
||||||
// it will print a warning if there is an error, but continue deleting the remaining files
|
// it will print a warning if there is an error, but continue deleting the remaining files
|
||||||
func DeleteFiles(gopts GlobalOptions, repo restic.Repository, fileList restic.IDSet, fileType restic.FileType) {
|
func DeleteFiles(gopts GlobalOptions, repo restic.Repository, fileList restic.IDSet, fileType restic.FileType) {
|
||||||
deleteFiles(gopts, true, repo, fileList, fileType)
|
_ = deleteFiles(gopts, true, repo, fileList, fileType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteFilesChecked deletes the given fileList of fileType in parallel
|
// DeleteFilesChecked deletes the given fileList of fileType in parallel
|
||||||
|
@ -180,7 +180,9 @@ func isDirExcludedByFile(dir, tagFilename, header string) bool {
|
|||||||
Warnf("could not open exclusion tagfile: %v", err)
|
Warnf("could not open exclusion tagfile: %v", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer func() {
|
||||||
|
_ = f.Close()
|
||||||
|
}()
|
||||||
buf := make([]byte, len(header))
|
buf := make([]byte, len(header))
|
||||||
_, err = io.ReadFull(f, buf)
|
_, err = io.ReadFull(f, buf)
|
||||||
// EOF is handled with a dedicated message, otherwise the warning were too cryptic
|
// EOF is handled with a dedicated message, otherwise the warning were too cryptic
|
||||||
|
@ -54,7 +54,7 @@ func TestReadRepo(t *testing.T) {
|
|||||||
|
|
||||||
var opts3 GlobalOptions
|
var opts3 GlobalOptions
|
||||||
opts3.RepositoryFile = foo + "-invalid"
|
opts3.RepositoryFile = foo + "-invalid"
|
||||||
repo, err = ReadRepo(opts3)
|
_, err = ReadRepo(opts3)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("must not read repository path from invalid file path")
|
t.Fatal("must not read repository path from invalid file path")
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ func testRunDiffOutput(gopts GlobalOptions, firstSnapshotID string, secondSnapsh
|
|||||||
ShowMetadata: false,
|
ShowMetadata: false,
|
||||||
}
|
}
|
||||||
err := runDiff(opts, gopts, []string{firstSnapshotID, secondSnapshotID})
|
err := runDiff(opts, gopts, []string{firstSnapshotID, secondSnapshotID})
|
||||||
return string(buf.Bytes()), err
|
return buf.String(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRunRebuildIndex(t testing.TB, gopts GlobalOptions) {
|
func testRunRebuildIndex(t testing.TB, gopts GlobalOptions) {
|
||||||
@ -566,9 +566,9 @@ func TestBackupErrors(t *testing.T) {
|
|||||||
|
|
||||||
// Assume failure
|
// Assume failure
|
||||||
inaccessibleFile := filepath.Join(env.testdata, "0", "0", "9", "0")
|
inaccessibleFile := filepath.Join(env.testdata, "0", "0", "9", "0")
|
||||||
os.Chmod(inaccessibleFile, 0000)
|
rtest.OK(t, os.Chmod(inaccessibleFile, 0000))
|
||||||
defer func() {
|
defer func() {
|
||||||
os.Chmod(inaccessibleFile, 0644)
|
rtest.OK(t, os.Chmod(inaccessibleFile, 0644))
|
||||||
}()
|
}()
|
||||||
opts := BackupOptions{}
|
opts := BackupOptions{}
|
||||||
gopts := env.gopts
|
gopts := env.gopts
|
||||||
@ -657,7 +657,11 @@ func TestBackupTags(t *testing.T) {
|
|||||||
testRunBackup(t, "", []string{env.testdata}, opts, env.gopts)
|
testRunBackup(t, "", []string{env.testdata}, opts, env.gopts)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
newest, _ := testRunSnapshots(t, env.gopts)
|
newest, _ := testRunSnapshots(t, env.gopts)
|
||||||
rtest.Assert(t, newest != nil, "expected a new backup, got nil")
|
|
||||||
|
if newest == nil {
|
||||||
|
t.Fatal("expected a backup, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
rtest.Assert(t, len(newest.Tags) == 0,
|
rtest.Assert(t, len(newest.Tags) == 0,
|
||||||
"expected no tags, got %v", newest.Tags)
|
"expected no tags, got %v", newest.Tags)
|
||||||
parent := newest
|
parent := newest
|
||||||
@ -666,7 +670,11 @@ func TestBackupTags(t *testing.T) {
|
|||||||
testRunBackup(t, "", []string{env.testdata}, opts, env.gopts)
|
testRunBackup(t, "", []string{env.testdata}, opts, env.gopts)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
newest, _ = testRunSnapshots(t, env.gopts)
|
newest, _ = testRunSnapshots(t, env.gopts)
|
||||||
rtest.Assert(t, newest != nil, "expected a new backup, got nil")
|
|
||||||
|
if newest == nil {
|
||||||
|
t.Fatal("expected a backup, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
rtest.Assert(t, len(newest.Tags) == 1 && newest.Tags[0] == "NL",
|
rtest.Assert(t, len(newest.Tags) == 1 && newest.Tags[0] == "NL",
|
||||||
"expected one NL tag, got %v", newest.Tags)
|
"expected one NL tag, got %v", newest.Tags)
|
||||||
// Tagged backup should have untagged backup as parent.
|
// Tagged backup should have untagged backup as parent.
|
||||||
@ -833,7 +841,10 @@ func TestTag(t *testing.T) {
|
|||||||
testRunBackup(t, "", []string{env.testdata}, BackupOptions{}, env.gopts)
|
testRunBackup(t, "", []string{env.testdata}, BackupOptions{}, env.gopts)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
newest, _ := testRunSnapshots(t, env.gopts)
|
newest, _ := testRunSnapshots(t, env.gopts)
|
||||||
rtest.Assert(t, newest != nil, "expected a new backup, got nil")
|
if newest == nil {
|
||||||
|
t.Fatal("expected a new backup, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
rtest.Assert(t, len(newest.Tags) == 0,
|
rtest.Assert(t, len(newest.Tags) == 0,
|
||||||
"expected no tags, got %v", newest.Tags)
|
"expected no tags, got %v", newest.Tags)
|
||||||
rtest.Assert(t, newest.Original == nil,
|
rtest.Assert(t, newest.Original == nil,
|
||||||
@ -843,7 +854,9 @@ func TestTag(t *testing.T) {
|
|||||||
testRunTag(t, TagOptions{SetTags: restic.TagLists{[]string{"NL"}}}, env.gopts)
|
testRunTag(t, TagOptions{SetTags: restic.TagLists{[]string{"NL"}}}, env.gopts)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
newest, _ = testRunSnapshots(t, env.gopts)
|
newest, _ = testRunSnapshots(t, env.gopts)
|
||||||
rtest.Assert(t, newest != nil, "expected a new backup, got nil")
|
if newest == nil {
|
||||||
|
t.Fatal("expected a backup, got nil")
|
||||||
|
}
|
||||||
rtest.Assert(t, len(newest.Tags) == 1 && newest.Tags[0] == "NL",
|
rtest.Assert(t, len(newest.Tags) == 1 && newest.Tags[0] == "NL",
|
||||||
"set failed, expected one NL tag, got %v", newest.Tags)
|
"set failed, expected one NL tag, got %v", newest.Tags)
|
||||||
rtest.Assert(t, newest.Original != nil, "expected original snapshot id, got nil")
|
rtest.Assert(t, newest.Original != nil, "expected original snapshot id, got nil")
|
||||||
@ -853,7 +866,9 @@ func TestTag(t *testing.T) {
|
|||||||
testRunTag(t, TagOptions{AddTags: restic.TagLists{[]string{"CH"}}}, env.gopts)
|
testRunTag(t, TagOptions{AddTags: restic.TagLists{[]string{"CH"}}}, env.gopts)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
newest, _ = testRunSnapshots(t, env.gopts)
|
newest, _ = testRunSnapshots(t, env.gopts)
|
||||||
rtest.Assert(t, newest != nil, "expected a new backup, got nil")
|
if newest == nil {
|
||||||
|
t.Fatal("expected a backup, got nil")
|
||||||
|
}
|
||||||
rtest.Assert(t, len(newest.Tags) == 2 && newest.Tags[0] == "NL" && newest.Tags[1] == "CH",
|
rtest.Assert(t, len(newest.Tags) == 2 && newest.Tags[0] == "NL" && newest.Tags[1] == "CH",
|
||||||
"add failed, expected CH,NL tags, got %v", newest.Tags)
|
"add failed, expected CH,NL tags, got %v", newest.Tags)
|
||||||
rtest.Assert(t, newest.Original != nil, "expected original snapshot id, got nil")
|
rtest.Assert(t, newest.Original != nil, "expected original snapshot id, got nil")
|
||||||
@ -863,7 +878,9 @@ func TestTag(t *testing.T) {
|
|||||||
testRunTag(t, TagOptions{RemoveTags: restic.TagLists{[]string{"NL"}}}, env.gopts)
|
testRunTag(t, TagOptions{RemoveTags: restic.TagLists{[]string{"NL"}}}, env.gopts)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
newest, _ = testRunSnapshots(t, env.gopts)
|
newest, _ = testRunSnapshots(t, env.gopts)
|
||||||
rtest.Assert(t, newest != nil, "expected a new backup, got nil")
|
if newest == nil {
|
||||||
|
t.Fatal("expected a backup, got nil")
|
||||||
|
}
|
||||||
rtest.Assert(t, len(newest.Tags) == 1 && newest.Tags[0] == "CH",
|
rtest.Assert(t, len(newest.Tags) == 1 && newest.Tags[0] == "CH",
|
||||||
"remove failed, expected one CH tag, got %v", newest.Tags)
|
"remove failed, expected one CH tag, got %v", newest.Tags)
|
||||||
rtest.Assert(t, newest.Original != nil, "expected original snapshot id, got nil")
|
rtest.Assert(t, newest.Original != nil, "expected original snapshot id, got nil")
|
||||||
@ -874,7 +891,9 @@ func TestTag(t *testing.T) {
|
|||||||
testRunTag(t, TagOptions{RemoveTags: restic.TagLists{[]string{"CH", "US", "RU"}}}, env.gopts)
|
testRunTag(t, TagOptions{RemoveTags: restic.TagLists{[]string{"CH", "US", "RU"}}}, env.gopts)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
newest, _ = testRunSnapshots(t, env.gopts)
|
newest, _ = testRunSnapshots(t, env.gopts)
|
||||||
rtest.Assert(t, newest != nil, "expected a new backup, got nil")
|
if newest == nil {
|
||||||
|
t.Fatal("expected a backup, got nil")
|
||||||
|
}
|
||||||
rtest.Assert(t, len(newest.Tags) == 0,
|
rtest.Assert(t, len(newest.Tags) == 0,
|
||||||
"expected no tags, got %v", newest.Tags)
|
"expected no tags, got %v", newest.Tags)
|
||||||
rtest.Assert(t, newest.Original != nil, "expected original snapshot id, got nil")
|
rtest.Assert(t, newest.Original != nil, "expected original snapshot id, got nil")
|
||||||
@ -885,7 +904,9 @@ func TestTag(t *testing.T) {
|
|||||||
testRunTag(t, TagOptions{SetTags: restic.TagLists{[]string{""}}}, env.gopts)
|
testRunTag(t, TagOptions{SetTags: restic.TagLists{[]string{""}}}, env.gopts)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
newest, _ = testRunSnapshots(t, env.gopts)
|
newest, _ = testRunSnapshots(t, env.gopts)
|
||||||
rtest.Assert(t, newest != nil, "expected a new backup, got nil")
|
if newest == nil {
|
||||||
|
t.Fatal("expected a backup, got nil")
|
||||||
|
}
|
||||||
rtest.Assert(t, len(newest.Tags) == 0,
|
rtest.Assert(t, len(newest.Tags) == 0,
|
||||||
"expected no tags, got %v", newest.Tags)
|
"expected no tags, got %v", newest.Tags)
|
||||||
rtest.Assert(t, newest.Original != nil, "expected original snapshot id, got nil")
|
rtest.Assert(t, newest.Original != nil, "expected original snapshot id, got nil")
|
||||||
@ -933,7 +954,7 @@ func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) {
|
|||||||
keyHostname = ""
|
keyHostname = ""
|
||||||
}()
|
}()
|
||||||
|
|
||||||
cmdKey.Flags().Parse([]string{"--user=john", "--host=example.com"})
|
rtest.OK(t, cmdKey.Flags().Parse([]string{"--user=john", "--host=example.com"}))
|
||||||
|
|
||||||
t.Log("adding key for john@example.com")
|
t.Log("adding key for john@example.com")
|
||||||
rtest.OK(t, runKey(gopts, []string{"add"}))
|
rtest.OK(t, runKey(gopts, []string{"add"}))
|
||||||
@ -1106,7 +1127,7 @@ func TestRestoreLatest(t *testing.T) {
|
|||||||
testRunBackup(t, "", []string{filepath.Base(env.testdata)}, opts, env.gopts)
|
testRunBackup(t, "", []string{filepath.Base(env.testdata)}, opts, env.gopts)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
|
|
||||||
os.Remove(p)
|
rtest.OK(t, os.Remove(p))
|
||||||
rtest.OK(t, appendRandomData(p, 101))
|
rtest.OK(t, appendRandomData(p, 101))
|
||||||
testRunBackup(t, "", []string{filepath.Base(env.testdata)}, opts, env.gopts)
|
testRunBackup(t, "", []string{filepath.Base(env.testdata)}, opts, env.gopts)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
@ -1747,16 +1768,35 @@ func copyFile(dst string, src string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer srcFile.Close()
|
|
||||||
|
|
||||||
dstFile, err := os.Create(dst)
|
dstFile, err := os.Create(dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// ignore subsequent errors
|
||||||
|
_ = srcFile.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer dstFile.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(dstFile, srcFile)
|
_, err = io.Copy(dstFile, srcFile)
|
||||||
|
if err != nil {
|
||||||
|
// ignore subsequent errors
|
||||||
|
_ = srcFile.Close()
|
||||||
|
_ = dstFile.Close()
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = srcFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
// ignore subsequent errors
|
||||||
|
_ = dstFile.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dstFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var diffOutputRegexPatterns = []string{
|
var diffOutputRegexPatterns = []string{
|
||||||
|
@ -237,7 +237,9 @@ func preCheckChangelogVersion() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
die("unable to open CHANGELOG.md: %v", err)
|
die("unable to open CHANGELOG.md: %v", err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer func() {
|
||||||
|
_ = f.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
sc := bufio.NewScanner(f)
|
sc := bufio.NewScanner(f)
|
||||||
for sc.Scan() {
|
for sc.Scan() {
|
||||||
|
@ -405,7 +405,10 @@ func (arch *Archiver) Save(ctx context.Context, snPath, target string, previous
|
|||||||
debug.Log("%v hasn't changed, but contents are missing!", target)
|
debug.Log("%v hasn't changed, but contents are missing!", target)
|
||||||
// There are contents missing - inform user!
|
// There are contents missing - inform user!
|
||||||
err := errors.Errorf("parts of %v not found in the repository index; storing the file again", target)
|
err := errors.Errorf("parts of %v not found in the repository index; storing the file again", target)
|
||||||
arch.error(abstarget, fi, err)
|
err = arch.error(abstarget, fi, err)
|
||||||
|
if err != nil {
|
||||||
|
return FutureNode{}, false, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reopen file and do an fstat() on the open file to check it is still
|
// reopen file and do an fstat() on the open file to check it is still
|
||||||
@ -457,7 +460,10 @@ func (arch *Archiver) Save(ctx context.Context, snPath, target string, previous
|
|||||||
start := time.Now()
|
start := time.Now()
|
||||||
oldSubtree, err := arch.loadSubtree(ctx, previous)
|
oldSubtree, err := arch.loadSubtree(ctx, previous)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
arch.error(abstarget, fi, err)
|
err = arch.error(abstarget, fi, err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return FutureNode{}, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fn.isTree = true
|
fn.isTree = true
|
||||||
@ -592,7 +598,10 @@ func (arch *Archiver) SaveTree(ctx context.Context, snPath string, atree *Tree,
|
|||||||
oldNode := previous.Find(name)
|
oldNode := previous.Find(name)
|
||||||
oldSubtree, err := arch.loadSubtree(ctx, oldNode)
|
oldSubtree, err := arch.loadSubtree(ctx, oldNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
arch.error(join(snPath, name), nil, err)
|
err = arch.error(join(snPath, name), nil, err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// not a leaf node, archive subtree
|
// not a leaf node, archive subtree
|
||||||
@ -751,7 +760,7 @@ func (arch *Archiver) loadParentTree(ctx context.Context, snapshotID restic.ID)
|
|||||||
tree, err := arch.Repo.LoadTree(ctx, *sn.Tree)
|
tree, err := arch.Repo.LoadTree(ctx, *sn.Tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("unable to load tree %v: %v", *sn.Tree, err)
|
debug.Log("unable to load tree %v: %v", *sn.Tree, err)
|
||||||
arch.error("/", nil, arch.wrapLoadTreeError(*sn.Tree, err))
|
_ = arch.error("/", nil, arch.wrapLoadTreeError(*sn.Tree, err))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return tree
|
return tree
|
||||||
|
@ -62,11 +62,11 @@ func TestBackendListRetry(t *testing.T) {
|
|||||||
// fail during first retry, succeed during second
|
// fail during first retry, succeed during second
|
||||||
retry++
|
retry++
|
||||||
if retry == 1 {
|
if retry == 1 {
|
||||||
fn(restic.FileInfo{Name: ID1})
|
_ = fn(restic.FileInfo{Name: ID1})
|
||||||
return errors.New("test list error")
|
return errors.New("test list error")
|
||||||
}
|
}
|
||||||
fn(restic.FileInfo{Name: ID1})
|
_ = fn(restic.FileInfo{Name: ID1})
|
||||||
fn(restic.FileInfo{Name: ID2})
|
_ = fn(restic.FileInfo{Name: ID2})
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,9 @@ func TestForeground(t *testing.T) {
|
|||||||
|
|
||||||
bg, err := backend.StartForeground(cmd)
|
bg, err := backend.StartForeground(cmd)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer cmd.Wait()
|
defer func() {
|
||||||
|
rtest.OK(t, cmd.Wait())
|
||||||
|
}()
|
||||||
|
|
||||||
err = bg()
|
err = bg()
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
@ -265,9 +265,15 @@ func visitDirs(ctx context.Context, dir string, fn func(restic.FileInfo) error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer d.Close()
|
|
||||||
|
|
||||||
sub, err := d.Readdirnames(-1)
|
sub, err := d.Readdirnames(-1)
|
||||||
|
if err != nil {
|
||||||
|
// ignore subsequent errors
|
||||||
|
_ = d.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -286,9 +292,15 @@ func visitFiles(ctx context.Context, dir string, fn func(restic.FileInfo) error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer d.Close()
|
|
||||||
|
|
||||||
sub, err := d.Readdir(-1)
|
sub, err := d.Readdir(-1)
|
||||||
|
if err != nil {
|
||||||
|
// ignore subsequent errors
|
||||||
|
_ = d.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,9 @@ func TestNoSpacePermanent(t *testing.T) {
|
|||||||
|
|
||||||
be, err := Open(context.Background(), Config{Path: dir})
|
be, err := Open(context.Background(), Config{Path: dir})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer be.Close()
|
defer func() {
|
||||||
|
rtest.OK(t, be.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
h := restic.Handle{Type: restic.ConfigFile}
|
h := restic.Handle{Type: restic.ConfigFile}
|
||||||
err = be.Save(context.Background(), h, nil)
|
err = be.Save(context.Background(), h, nil)
|
||||||
|
@ -63,9 +63,9 @@ func run(command string, args ...string) (*StdioConn, *exec.Cmd, *sync.WaitGroup
|
|||||||
|
|
||||||
stdout, w, err := os.Pipe()
|
stdout, w, err := os.Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// close first pipe
|
// close first pipe and ignore subsequent errors
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
stdin.Close()
|
_ = stdin.Close()
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,8 +197,8 @@ func newBackend(cfg Config, lim limiter.Limiter) (*Backend, error) {
|
|||||||
err := cmd.Wait()
|
err := cmd.Wait()
|
||||||
debug.Log("Wait returned %v", err)
|
debug.Log("Wait returned %v", err)
|
||||||
be.waitResult = err
|
be.waitResult = err
|
||||||
// close our side of the pipes to rclone
|
// close our side of the pipes to rclone, ignore errors
|
||||||
stdioConn.CloseAll()
|
_ = stdioConn.CloseAll()
|
||||||
close(waitCh)
|
close(waitCh)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -228,22 +228,25 @@ func newBackend(cfg Config, lim limiter.Limiter) (*Backend, error) {
|
|||||||
// rclone is able to accept HTTP requests.
|
// rclone is able to accept HTTP requests.
|
||||||
url := fmt.Sprintf("http://localhost/file-%d", rand.Uint64())
|
url := fmt.Sprintf("http://localhost/file-%d", rand.Uint64())
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.Header.Set("Accept", rest.ContentTypeV2)
|
req.Header.Set("Accept", rest.ContentTypeV2)
|
||||||
req.Cancel = ctx.Done()
|
|
||||||
|
|
||||||
res, err := ctxhttp.Do(ctx, client, req)
|
res, err := ctxhttp.Do(ctx, client, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bg()
|
// ignore subsequent errors
|
||||||
|
_ = bg()
|
||||||
_ = cmd.Process.Kill()
|
_ = cmd.Process.Kill()
|
||||||
return nil, errors.Errorf("error talking HTTP to rclone: %v", err)
|
return nil, errors.Errorf("error talking HTTP to rclone: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("HTTP status %q returned, moving instance to background", res.Status)
|
debug.Log("HTTP status %q returned, moving instance to background", res.Status)
|
||||||
bg()
|
err = bg()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error moving process to background: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return be, nil
|
return be, nil
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,10 @@ func TestRcloneExit(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer be.Close()
|
defer func() {
|
||||||
|
// ignore the error as the test will kill rclone (see below)
|
||||||
|
_ = be.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err = be.cmd.Process.Kill()
|
err = be.cmd.Process.Kill()
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
@ -52,7 +52,7 @@ func DefaultLoad(ctx context.Context, h restic.Handle, length int, offset int64,
|
|||||||
}
|
}
|
||||||
err = fn(rd)
|
err = fn(rd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rd.Close() // ignore secondary errors closing the reader
|
_ = rd.Close() // ignore secondary errors closing the reader
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return rd.Close()
|
return rd.Close()
|
||||||
|
2
internal/cache/backend.go
vendored
2
internal/cache/backend.go
vendored
@ -166,7 +166,7 @@ func (b *Backend) Load(ctx context.Context, h restic.Handle, length int, offset
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
err = consumer(rd)
|
err = consumer(rd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rd.Close() // ignore secondary errors
|
_ = rd.Close() // ignore secondary errors
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return rd.Close()
|
return rd.Close()
|
||||||
|
7
internal/cache/dir_test.go
vendored
7
internal/cache/dir_test.go
vendored
@ -17,7 +17,12 @@ func TestCacheDirEnv(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer os.Unsetenv("RESTIC_CACHE_DIR")
|
defer func() {
|
||||||
|
err := os.Unsetenv("RESTIC_CACHE_DIR")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, err := DefaultDir()
|
dir, err := DefaultDir()
|
||||||
|
@ -111,7 +111,11 @@ func (c *Checker) LoadIndex(ctx context.Context) (hints []error, errs []error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Merge index before computing pack sizes, as this needs removed duplicates
|
// Merge index before computing pack sizes, as this needs removed duplicates
|
||||||
c.masterIndex.MergeFinalIndexes()
|
err = c.masterIndex.MergeFinalIndexes()
|
||||||
|
if err != nil {
|
||||||
|
// abort if an error occurs merging the indexes
|
||||||
|
return hints, append(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
// compute pack size using index entries
|
// compute pack size using index entries
|
||||||
c.packs = c.masterIndex.PackSize(ctx, false)
|
c.packs = c.masterIndex.PackSize(ctx, false)
|
||||||
@ -323,7 +327,12 @@ func (c *Checker) Structure(ctx context.Context, p *progress.Counter, errChan ch
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
// the wait group should not return an error because no worker returns an
|
||||||
|
// error, so panic if that has changed somehow.
|
||||||
|
err := wg.Wait()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checker) checkTree(id restic.ID, tree *restic.Tree) (errs []error) {
|
func (c *Checker) checkTree(id restic.ID, tree *restic.Tree) (errs []error) {
|
||||||
|
@ -489,7 +489,7 @@ func TestCheckerBlobTypeConfusion(t *testing.T) {
|
|||||||
Nodes: []*restic.Node{malNode, dirNode},
|
Nodes: []*restic.Node{malNode, dirNode},
|
||||||
}
|
}
|
||||||
|
|
||||||
rootId, err := repo.SaveTree(ctx, rootTree)
|
rootID, err := repo.SaveTree(ctx, rootTree)
|
||||||
test.OK(t, err)
|
test.OK(t, err)
|
||||||
|
|
||||||
test.OK(t, repo.Flush(ctx))
|
test.OK(t, repo.Flush(ctx))
|
||||||
@ -498,12 +498,12 @@ func TestCheckerBlobTypeConfusion(t *testing.T) {
|
|||||||
snapshot, err := restic.NewSnapshot([]string{"/damaged"}, []string{"test"}, "foo", time.Now())
|
snapshot, err := restic.NewSnapshot([]string{"/damaged"}, []string{"test"}, "foo", time.Now())
|
||||||
test.OK(t, err)
|
test.OK(t, err)
|
||||||
|
|
||||||
snapshot.Tree = &rootId
|
snapshot.Tree = &rootID
|
||||||
|
|
||||||
snapId, err := repo.SaveJSONUnpacked(ctx, restic.SnapshotFile, snapshot)
|
snapID, err := repo.SaveJSONUnpacked(ctx, restic.SnapshotFile, snapshot)
|
||||||
test.OK(t, err)
|
test.OK(t, err)
|
||||||
|
|
||||||
t.Logf("saved snapshot %v", snapId.Str())
|
t.Logf("saved snapshot %v", snapID.Str())
|
||||||
|
|
||||||
delayRepo := &delayRepository{
|
delayRepo := &delayRepository{
|
||||||
Repository: repo,
|
Repository: repo,
|
||||||
|
@ -101,12 +101,23 @@ func (a *acl) decode(xattr []byte) {
|
|||||||
func (a *acl) encode() []byte {
|
func (a *acl) encode() []byte {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
ae := new(aclElem)
|
ae := new(aclElem)
|
||||||
binary.Write(buf, binary.LittleEndian, &a.Version)
|
|
||||||
|
err := binary.Write(buf, binary.LittleEndian, &a.Version)
|
||||||
|
// write to a bytes.Buffer always returns a nil error
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
for _, elem := range a.List {
|
for _, elem := range a.List {
|
||||||
ae.Tag = uint16(elem.getType())
|
ae.Tag = uint16(elem.getType())
|
||||||
ae.Perm = elem.Perm
|
ae.Perm = elem.Perm
|
||||||
ae.ID = elem.getID()
|
ae.ID = elem.getID()
|
||||||
binary.Write(buf, binary.LittleEndian, ae)
|
|
||||||
|
err := binary.Write(buf, binary.LittleEndian, ae)
|
||||||
|
// write to a bytes.Buffer always returns a nil error
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,8 @@ func writeDump(ctx context.Context, repo restic.Repository, tree *restic.Tree, r
|
|||||||
rootNode.Path = rootPath
|
rootNode.Path = rootPath
|
||||||
err := dumpTree(ctx, repo, rootNode, rootPath, dmp)
|
err := dumpTree(ctx, repo, rootNode, rootPath, dmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dmp.Close()
|
// ignore subsequent errors
|
||||||
|
_ = dmp.Close()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -23,10 +23,16 @@ func readZipFile(f *zip.File) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rc.Close()
|
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
_, err = b.ReadFrom(rc)
|
_, err = b.ReadFrom(rc)
|
||||||
|
if err != nil {
|
||||||
|
// ignore subsequent errors
|
||||||
|
_ = rc.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rc.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ func (fs *LocalVss) DeleteSnapshots() {
|
|||||||
|
|
||||||
for volumeName, snapshot := range fs.snapshots {
|
for volumeName, snapshot := range fs.snapshots {
|
||||||
if err := snapshot.Delete(); err != nil {
|
if err := snapshot.Delete(); err != nil {
|
||||||
fs.msgError(volumeName, errors.Errorf("failed to delete VSS snapshot: %s", err))
|
_ = fs.msgError(volumeName, errors.Errorf("failed to delete VSS snapshot: %s", err))
|
||||||
activeSnapshots[volumeName] = snapshot
|
activeSnapshots[volumeName] = snapshot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ func (fs *LocalVss) snapshotPath(path string) string {
|
|||||||
fs.msgMessage("creating VSS snapshot for [%s]\n", vssVolume)
|
fs.msgMessage("creating VSS snapshot for [%s]\n", vssVolume)
|
||||||
|
|
||||||
if snapshot, err := NewVssSnapshot(vssVolume, 120, fs.msgError); err != nil {
|
if snapshot, err := NewVssSnapshot(vssVolume, 120, fs.msgError); err != nil {
|
||||||
fs.msgError(vssVolume, errors.Errorf("failed to create snapshot for [%s]: %s\n",
|
_ = fs.msgError(vssVolume, errors.Errorf("failed to create snapshot for [%s]: %s\n",
|
||||||
vssVolume, err))
|
vssVolume, err))
|
||||||
fs.failedSnapshots[volumeNameLower] = struct{}{}
|
fs.failedSnapshots[volumeNameLower] = struct{}{}
|
||||||
} else {
|
} else {
|
||||||
|
@ -219,17 +219,17 @@ func TestFuseDir(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test top-level directories for their UID and GID.
|
// Test top-level directories for their UID and GID.
|
||||||
func TestTopUidGid(t *testing.T) {
|
func TestTopUIDGID(t *testing.T) {
|
||||||
repo, cleanup := repository.TestRepository(t)
|
repo, cleanup := repository.TestRepository(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
restic.TestCreateSnapshot(t, repo, time.Unix(1460289341, 207401672), 0, 0)
|
restic.TestCreateSnapshot(t, repo, time.Unix(1460289341, 207401672), 0, 0)
|
||||||
|
|
||||||
testTopUidGid(t, Config{}, repo, uint32(os.Getuid()), uint32(os.Getgid()))
|
testTopUIDGID(t, Config{}, repo, uint32(os.Getuid()), uint32(os.Getgid()))
|
||||||
testTopUidGid(t, Config{OwnerIsRoot: true}, repo, 0, 0)
|
testTopUIDGID(t, Config{OwnerIsRoot: true}, repo, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTopUidGid(t *testing.T, cfg Config, repo restic.Repository, uid, gid uint32) {
|
func testTopUIDGID(t *testing.T, cfg Config, repo restic.Repository, uid, gid uint32) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -229,7 +229,10 @@ func updateSnapshots(ctx context.Context, root *Root) error {
|
|||||||
|
|
||||||
if root.snCount != len(snapshots) {
|
if root.snCount != len(snapshots) {
|
||||||
root.snCount = len(snapshots)
|
root.snCount = len(snapshots)
|
||||||
root.repo.LoadIndex(ctx)
|
err := root.repo.LoadIndex(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
root.snapshots = snapshots
|
root.snapshots = snapshots
|
||||||
}
|
}
|
||||||
root.lastCheck = time.Now()
|
root.lastCheck = time.Now()
|
||||||
@ -272,7 +275,10 @@ func (d *SnapshotsDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
|||||||
debug.Log("ReadDirAll()")
|
debug.Log("ReadDirAll()")
|
||||||
|
|
||||||
// update snapshots
|
// update snapshots
|
||||||
updateSnapshots(ctx, d.root)
|
err := updateSnapshots(ctx, d.root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// update snapshot names
|
// update snapshot names
|
||||||
updateSnapshotNames(d, d.root.cfg.SnapshotTemplate)
|
updateSnapshotNames(d, d.root.cfg.SnapshotTemplate)
|
||||||
@ -314,7 +320,10 @@ func (d *SnapshotsIDSDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error)
|
|||||||
debug.Log("ReadDirAll()")
|
debug.Log("ReadDirAll()")
|
||||||
|
|
||||||
// update snapshots
|
// update snapshots
|
||||||
updateSnapshots(ctx, d.root)
|
err := updateSnapshots(ctx, d.root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// update snapshot ids
|
// update snapshot ids
|
||||||
updateSnapshotIDSNames(d)
|
updateSnapshotIDSNames(d)
|
||||||
@ -348,7 +357,10 @@ func (d *HostsDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
|||||||
debug.Log("ReadDirAll()")
|
debug.Log("ReadDirAll()")
|
||||||
|
|
||||||
// update snapshots
|
// update snapshots
|
||||||
updateSnapshots(ctx, d.root)
|
err := updateSnapshots(ctx, d.root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// update host names
|
// update host names
|
||||||
updateHostsNames(d)
|
updateHostsNames(d)
|
||||||
@ -382,7 +394,10 @@ func (d *TagsDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
|||||||
debug.Log("ReadDirAll()")
|
debug.Log("ReadDirAll()")
|
||||||
|
|
||||||
// update snapshots
|
// update snapshots
|
||||||
updateSnapshots(ctx, d.root)
|
err := updateSnapshots(ctx, d.root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// update tag names
|
// update tag names
|
||||||
updateTagNames(d)
|
updateTagNames(d)
|
||||||
@ -443,7 +458,10 @@ func (d *SnapshotsDir) Lookup(ctx context.Context, name string) (fs.Node, error)
|
|||||||
sn, ok := d.names[name]
|
sn, ok := d.names[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
// could not find entry. Updating repository-state
|
// could not find entry. Updating repository-state
|
||||||
updateSnapshots(ctx, d.root)
|
err := updateSnapshots(ctx, d.root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// update snapshot names
|
// update snapshot names
|
||||||
updateSnapshotNames(d, d.root.cfg.SnapshotTemplate)
|
updateSnapshotNames(d, d.root.cfg.SnapshotTemplate)
|
||||||
@ -476,7 +494,10 @@ func (d *SnapshotsIDSDir) Lookup(ctx context.Context, name string) (fs.Node, err
|
|||||||
sn, ok := d.names[name]
|
sn, ok := d.names[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
// could not find entry. Updating repository-state
|
// could not find entry. Updating repository-state
|
||||||
updateSnapshots(ctx, d.root)
|
err := updateSnapshots(ctx, d.root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// update snapshot ids
|
// update snapshot ids
|
||||||
updateSnapshotIDSNames(d)
|
updateSnapshotIDSNames(d)
|
||||||
@ -499,7 +520,10 @@ func (d *HostsDir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
|||||||
_, ok := d.hosts[name]
|
_, ok := d.hosts[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
// could not find entry. Updating repository-state
|
// could not find entry. Updating repository-state
|
||||||
updateSnapshots(ctx, d.root)
|
err := updateSnapshots(ctx, d.root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// update host names
|
// update host names
|
||||||
updateHostsNames(d)
|
updateHostsNames(d)
|
||||||
@ -522,7 +546,10 @@ func (d *TagsDir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
|||||||
_, ok := d.tags[name]
|
_, ok := d.tags[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
// could not find entry. Updating repository-state
|
// could not find entry. Updating repository-state
|
||||||
updateSnapshots(ctx, d.root)
|
err := updateSnapshots(ctx, d.root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// update tag names
|
// update tag names
|
||||||
updateTagNames(d)
|
updateTagNames(d)
|
||||||
|
@ -21,8 +21,16 @@ func NewWriter(w io.Writer, h hash.Hash) *Writer {
|
|||||||
|
|
||||||
// Write wraps the write method of the underlying writer and also hashes all data.
|
// Write wraps the write method of the underlying writer and also hashes all data.
|
||||||
func (h *Writer) Write(p []byte) (int, error) {
|
func (h *Writer) Write(p []byte) (int, error) {
|
||||||
|
// write the data to the underlying writing
|
||||||
n, err := h.w.Write(p)
|
n, err := h.w.Write(p)
|
||||||
h.h.Write(p[:n])
|
|
||||||
|
// according to the interface documentation, Write() on a hash.Hash never
|
||||||
|
// returns an error.
|
||||||
|
_, hashErr := h.h.Write(p[:n])
|
||||||
|
if hashErr != nil {
|
||||||
|
panic(hashErr)
|
||||||
|
}
|
||||||
|
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ func (m *Backend) Load(ctx context.Context, h restic.Handle, length int, offset
|
|||||||
}
|
}
|
||||||
err = fn(rd)
|
err = fn(rd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rd.Close() // ignore secondary errors closing the reader
|
_ = rd.Close() // ignore secondary errors closing the reader
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return rd.Close()
|
return rd.Close()
|
||||||
|
@ -157,7 +157,7 @@ var (
|
|||||||
const (
|
const (
|
||||||
// size of the header-length field at the end of the file; it is a uint32
|
// size of the header-length field at the end of the file; it is a uint32
|
||||||
headerLengthSize = 4
|
headerLengthSize = 4
|
||||||
// constant overhead of the header independent of #entries
|
// HeaderSize is the header's constant overhead (independent of #entries)
|
||||||
HeaderSize = headerLengthSize + crypto.Extension
|
HeaderSize = headerLengthSize + crypto.Extension
|
||||||
|
|
||||||
maxHeaderSize = 16 * 1024 * 1024
|
maxHeaderSize = 16 * 1024 * 1024
|
||||||
|
@ -63,7 +63,7 @@ func TestReadHeaderEagerLoad(t *testing.T) {
|
|||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
buf.Write(rtest.Random(0, dataSize)) // pack blobs data
|
buf.Write(rtest.Random(0, dataSize)) // pack blobs data
|
||||||
buf.Write(expectedHeader) // pack header
|
buf.Write(expectedHeader) // pack header
|
||||||
binary.Write(buf, binary.LittleEndian, uint32(len(expectedHeader))) // pack header length
|
rtest.OK(t, binary.Write(buf, binary.LittleEndian, uint32(len(expectedHeader)))) // pack header length
|
||||||
|
|
||||||
rd := &countingReaderAt{delegate: bytes.NewReader(buf.Bytes())}
|
rd := &countingReaderAt{delegate: bytes.NewReader(buf.Bytes())}
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ func TestReadRecords(t *testing.T) {
|
|||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
buf.Write(rtest.Random(0, dataSize)) // pack blobs data
|
buf.Write(rtest.Random(0, dataSize)) // pack blobs data
|
||||||
buf.Write(totalHeader) // pack header
|
buf.Write(totalHeader) // pack header
|
||||||
binary.Write(buf, binary.LittleEndian, uint32(len(totalHeader))) // pack header length
|
rtest.OK(t, binary.Write(buf, binary.LittleEndian, uint32(len(totalHeader)))) // pack header length
|
||||||
|
|
||||||
rd := bytes.NewReader(buf.Bytes())
|
rd := bytes.NewReader(buf.Bytes())
|
||||||
|
|
||||||
|
@ -39,7 +39,8 @@ func newPack(t testing.TB, k *crypto.Key, lengths []int) ([]Buf, []byte, uint) {
|
|||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
p := pack.NewPacker(k, &buf)
|
p := pack.NewPacker(k, &buf)
|
||||||
for _, b := range bufs {
|
for _, b := range bufs {
|
||||||
p.Add(restic.TreeBlob, b.id, b.data)
|
_, err := p.Add(restic.TreeBlob, b.id, b.data)
|
||||||
|
rtest.OK(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := p.Finalize()
|
_, err := p.Finalize()
|
||||||
|
@ -301,7 +301,11 @@ var (
|
|||||||
func initBenchmarkIndexJSON() {
|
func initBenchmarkIndexJSON() {
|
||||||
idx, _ := createRandomIndex(rand.New(rand.NewSource(0)), 200000)
|
idx, _ := createRandomIndex(rand.New(rand.NewSource(0)), 200000)
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
idx.Encode(&buf)
|
err := idx.Encode(&buf)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
benchmarkIndexJSON = buf.Bytes()
|
benchmarkIndexJSON = buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package repository
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/restic/restic/internal/debug"
|
"github.com/restic/restic/internal/debug"
|
||||||
@ -271,7 +272,7 @@ func (mi *MasterIndex) Each(ctx context.Context) <-chan restic.PackedBlob {
|
|||||||
// Indexes that are not final are left untouched.
|
// Indexes that are not final are left untouched.
|
||||||
// This merging can only be called after all index files are loaded - as
|
// This merging can only be called after all index files are loaded - as
|
||||||
// removing of superseded index contents is only possible for unmerged indexes.
|
// removing of superseded index contents is only possible for unmerged indexes.
|
||||||
func (mi *MasterIndex) MergeFinalIndexes() {
|
func (mi *MasterIndex) MergeFinalIndexes() error {
|
||||||
mi.idxMutex.Lock()
|
mi.idxMutex.Lock()
|
||||||
defer mi.idxMutex.Unlock()
|
defer mi.idxMutex.Unlock()
|
||||||
|
|
||||||
@ -284,10 +285,15 @@ func (mi *MasterIndex) MergeFinalIndexes() {
|
|||||||
if !idx.Final() {
|
if !idx.Final() {
|
||||||
newIdx = append(newIdx, idx)
|
newIdx = append(newIdx, idx)
|
||||||
} else {
|
} else {
|
||||||
mi.idx[0].merge(idx)
|
err := mi.idx[0].merge(idx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("MergeFinalIndexes: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mi.idx = newIdx
|
mi.idx = newIdx
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveIndexParallelism = 4
|
const saveIndexParallelism = 4
|
||||||
|
@ -163,7 +163,11 @@ func TestMasterMergeFinalIndexes(t *testing.T) {
|
|||||||
finalIndexes := mIdx.FinalizeNotFinalIndexes()
|
finalIndexes := mIdx.FinalizeNotFinalIndexes()
|
||||||
rtest.Equals(t, []*repository.Index{idx1, idx2}, finalIndexes)
|
rtest.Equals(t, []*repository.Index{idx1, idx2}, finalIndexes)
|
||||||
|
|
||||||
mIdx.MergeFinalIndexes()
|
err := mIdx.MergeFinalIndexes()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
allIndexes := mIdx.All()
|
allIndexes := mIdx.All()
|
||||||
rtest.Equals(t, 1, len(allIndexes))
|
rtest.Equals(t, 1, len(allIndexes))
|
||||||
|
|
||||||
@ -191,7 +195,11 @@ func TestMasterMergeFinalIndexes(t *testing.T) {
|
|||||||
finalIndexes = mIdx.FinalizeNotFinalIndexes()
|
finalIndexes = mIdx.FinalizeNotFinalIndexes()
|
||||||
rtest.Equals(t, []*repository.Index{idx3}, finalIndexes)
|
rtest.Equals(t, []*repository.Index{idx3}, finalIndexes)
|
||||||
|
|
||||||
mIdx.MergeFinalIndexes()
|
err = mIdx.MergeFinalIndexes()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
allIndexes = mIdx.All()
|
allIndexes = mIdx.All()
|
||||||
rtest.Equals(t, 1, len(allIndexes))
|
rtest.Equals(t, 1, len(allIndexes))
|
||||||
|
|
||||||
@ -209,7 +217,7 @@ func TestMasterMergeFinalIndexes(t *testing.T) {
|
|||||||
rtest.Equals(t, 2, blobCount)
|
rtest.Equals(t, 2, blobCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRandomMasterIndex(rng *rand.Rand, num, size int) (*repository.MasterIndex, restic.BlobHandle) {
|
func createRandomMasterIndex(t testing.TB, rng *rand.Rand, num, size int) (*repository.MasterIndex, restic.BlobHandle) {
|
||||||
mIdx := repository.NewMasterIndex()
|
mIdx := repository.NewMasterIndex()
|
||||||
for i := 0; i < num-1; i++ {
|
for i := 0; i < num-1; i++ {
|
||||||
idx, _ := createRandomIndex(rng, size)
|
idx, _ := createRandomIndex(rng, size)
|
||||||
@ -219,7 +227,10 @@ func createRandomMasterIndex(rng *rand.Rand, num, size int) (*repository.MasterI
|
|||||||
mIdx.Insert(idx1)
|
mIdx.Insert(idx1)
|
||||||
|
|
||||||
mIdx.FinalizeNotFinalIndexes()
|
mIdx.FinalizeNotFinalIndexes()
|
||||||
mIdx.MergeFinalIndexes()
|
err := mIdx.MergeFinalIndexes()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
return mIdx, lookupBh
|
return mIdx, lookupBh
|
||||||
}
|
}
|
||||||
@ -229,12 +240,12 @@ func BenchmarkMasterIndexAlloc(b *testing.B) {
|
|||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
createRandomMasterIndex(rng, 10000, 5)
|
createRandomMasterIndex(b, rng, 10000, 5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMasterIndexLookupSingleIndex(b *testing.B) {
|
func BenchmarkMasterIndexLookupSingleIndex(b *testing.B) {
|
||||||
mIdx, lookupBh := createRandomMasterIndex(rand.New(rand.NewSource(0)), 1, 200000)
|
mIdx, lookupBh := createRandomMasterIndex(b, rand.New(rand.NewSource(0)), 1, 200000)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
@ -244,7 +255,7 @@ func BenchmarkMasterIndexLookupSingleIndex(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMasterIndexLookupMultipleIndex(b *testing.B) {
|
func BenchmarkMasterIndexLookupMultipleIndex(b *testing.B) {
|
||||||
mIdx, lookupBh := createRandomMasterIndex(rand.New(rand.NewSource(0)), 100, 10000)
|
mIdx, lookupBh := createRandomMasterIndex(b, rand.New(rand.NewSource(0)), 100, 10000)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
@ -256,7 +267,7 @@ func BenchmarkMasterIndexLookupMultipleIndex(b *testing.B) {
|
|||||||
func BenchmarkMasterIndexLookupSingleIndexUnknown(b *testing.B) {
|
func BenchmarkMasterIndexLookupSingleIndexUnknown(b *testing.B) {
|
||||||
|
|
||||||
lookupBh := restic.NewRandomBlobHandle()
|
lookupBh := restic.NewRandomBlobHandle()
|
||||||
mIdx, _ := createRandomMasterIndex(rand.New(rand.NewSource(0)), 1, 200000)
|
mIdx, _ := createRandomMasterIndex(b, rand.New(rand.NewSource(0)), 1, 200000)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
@ -267,7 +278,7 @@ func BenchmarkMasterIndexLookupSingleIndexUnknown(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkMasterIndexLookupMultipleIndexUnknown(b *testing.B) {
|
func BenchmarkMasterIndexLookupMultipleIndexUnknown(b *testing.B) {
|
||||||
lookupBh := restic.NewRandomBlobHandle()
|
lookupBh := restic.NewRandomBlobHandle()
|
||||||
mIdx, _ := createRandomMasterIndex(rand.New(rand.NewSource(0)), 100, 10000)
|
mIdx, _ := createRandomMasterIndex(b, rand.New(rand.NewSource(0)), 100, 10000)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
@ -284,7 +295,7 @@ func BenchmarkMasterIndexLookupParallel(b *testing.B) {
|
|||||||
|
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
rng := rand.New(rand.NewSource(0))
|
rng := rand.New(rand.NewSource(0))
|
||||||
mIdx, lookupBh = createRandomMasterIndex(rng, numindices, 10000)
|
mIdx, lookupBh = createRandomMasterIndex(b, rng, numindices, 10000)
|
||||||
b.StartTimer()
|
b.StartTimer()
|
||||||
|
|
||||||
name := fmt.Sprintf("known,indices=%d", numindices)
|
name := fmt.Sprintf("known,indices=%d", numindices)
|
||||||
@ -310,7 +321,7 @@ func BenchmarkMasterIndexLookupParallel(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkMasterIndexLookupBlobSize(b *testing.B) {
|
func BenchmarkMasterIndexLookupBlobSize(b *testing.B) {
|
||||||
rng := rand.New(rand.NewSource(0))
|
rng := rand.New(rand.NewSource(0))
|
||||||
mIdx, lookupBh := createRandomMasterIndex(rand.New(rng), 5, 200000)
|
mIdx, lookupBh := createRandomMasterIndex(b, rand.New(rng), 5, 200000)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
@ -338,7 +349,10 @@ func TestIndexSave(t *testing.T) {
|
|||||||
repo, cleanup := createFilledRepo(t, 3, 0)
|
repo, cleanup := createFilledRepo(t, 3, 0)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
repo.LoadIndex(context.TODO())
|
err := repo.LoadIndex(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
obsoletes, err := repo.Index().(*repository.MasterIndex).Save(context.TODO(), repo, nil, nil, nil)
|
obsoletes, err := repo.Index().(*repository.MasterIndex).Save(context.TODO(), repo, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -201,7 +201,11 @@ func rebuildIndex(t *testing.T, repo restic.Repository) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func reloadIndex(t *testing.T, repo restic.Repository) {
|
func reloadIndex(t *testing.T, repo restic.Repository) {
|
||||||
repo.SetIndex(repository.NewMasterIndex())
|
err := repo.SetIndex(repository.NewMasterIndex())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := repo.LoadIndex(context.TODO()); err != nil {
|
if err := repo.LoadIndex(context.TODO()); err != nil {
|
||||||
t.Fatalf("error loading new index: %v", err)
|
t.Fatalf("error loading new index: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -419,9 +419,8 @@ func (r *Repository) saveIndex(ctx context.Context, indexes ...*Index) error {
|
|||||||
|
|
||||||
debug.Log("Saved index %d as %v", i, sid)
|
debug.Log("Saved index %d as %v", i, sid)
|
||||||
}
|
}
|
||||||
r.idx.MergeFinalIndexes()
|
|
||||||
|
|
||||||
return nil
|
return r.idx.MergeFinalIndexes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveIndex saves all new indexes in the backend.
|
// SaveIndex saves all new indexes in the backend.
|
||||||
@ -461,7 +460,10 @@ func (r *Repository) LoadIndex(ctx context.Context) error {
|
|||||||
return errors.Fatal(err.Error())
|
return errors.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
r.idx.MergeFinalIndexes()
|
err = r.idx.MergeFinalIndexes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// remove index files from the cache which have been removed in the repo
|
// remove index files from the cache which have been removed in the repo
|
||||||
return r.PrepareCache(validIndex)
|
return r.PrepareCache(validIndex)
|
||||||
@ -781,16 +783,19 @@ func DownloadAndHash(ctx context.Context, be Loader, h restic.Handle) (tmpfile *
|
|||||||
hash = restic.IDFromHash(hrd.Sum(nil))
|
hash = restic.IDFromHash(hrd.Sum(nil))
|
||||||
return ierr
|
return ierr
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tmpfile.Close()
|
// ignore subsequent errors
|
||||||
os.Remove(tmpfile.Name())
|
_ = tmpfile.Close()
|
||||||
|
_ = os.Remove(tmpfile.Name())
|
||||||
return nil, restic.ID{}, -1, errors.Wrap(err, "Load")
|
return nil, restic.ID{}, -1, errors.Wrap(err, "Load")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tmpfile.Seek(0, io.SeekStart)
|
_, err = tmpfile.Seek(0, io.SeekStart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tmpfile.Close()
|
// ignore subsequent errors
|
||||||
os.Remove(tmpfile.Name())
|
_ = tmpfile.Close()
|
||||||
|
_ = os.Remove(tmpfile.Name())
|
||||||
return nil, restic.ID{}, -1, errors.Wrap(err, "Seek")
|
return nil, restic.ID{}, -1, errors.Wrap(err, "Seek")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,9 @@ func (l Lock) processExists() bool {
|
|||||||
debug.Log("error searching for process %d: %v\n", l.PID, err)
|
debug.Log("error searching for process %d: %v\n", l.PID, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer proc.Release()
|
defer func() {
|
||||||
|
_ = proc.Release()
|
||||||
|
}()
|
||||||
|
|
||||||
debug.Log("sending SIGHUP to process %d\n", l.PID)
|
debug.Log("sending SIGHUP to process %d\n", l.PID)
|
||||||
err = proc.Signal(syscall.SIGHUP)
|
err = proc.Signal(syscall.SIGHUP)
|
||||||
|
@ -15,7 +15,6 @@ func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Open")
|
return errors.Wrap(err, "Open")
|
||||||
}
|
}
|
||||||
defer dir.Close()
|
|
||||||
|
|
||||||
times := []unix.Timespec{
|
times := []unix.Timespec{
|
||||||
{Sec: utimes[0].Sec, Nsec: utimes[0].Nsec},
|
{Sec: utimes[0].Sec, Nsec: utimes[0].Nsec},
|
||||||
@ -25,10 +24,12 @@ func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespe
|
|||||||
err = unix.UtimesNanoAt(int(dir.Fd()), filepath.Base(path), times, unix.AT_SYMLINK_NOFOLLOW)
|
err = unix.UtimesNanoAt(int(dir.Fd()), filepath.Base(path), times, unix.AT_SYMLINK_NOFOLLOW)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// ignore subsequent errors
|
||||||
|
_ = dir.Close()
|
||||||
return errors.Wrap(err, "UtimesNanoAt")
|
return errors.Wrap(err, "UtimesNanoAt")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return dir.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node Node) device() int {
|
func (node Node) device() int {
|
||||||
|
@ -29,7 +29,8 @@ func BenchmarkNodeFillUser(t *testing.B) {
|
|||||||
t.ResetTimer()
|
t.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < t.N; i++ {
|
for i := 0; i < t.N; i++ {
|
||||||
restic.NodeFromFileInfo(path, fi)
|
_, err := restic.NodeFromFileInfo(path, fi)
|
||||||
|
rtest.OK(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rtest.OK(t, tempfile.Close())
|
rtest.OK(t, tempfile.Close())
|
||||||
|
@ -44,14 +44,6 @@ func (i *TestRepo) Lookup(bh restic.BlobHandle) []restic.PackedBlob {
|
|||||||
return packs
|
return packs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *TestRepo) packName(pack *packInfo) string {
|
|
||||||
return i.packsIDToName[pack.id]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *TestRepo) packID(name string) restic.ID {
|
|
||||||
return i.packsNameToID[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *TestRepo) fileContent(file *fileInfo) string {
|
func (i *TestRepo) fileContent(file *fileInfo) string {
|
||||||
return i.filesPathToContent[file.location]
|
return i.filesPathToContent[file.location]
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,8 @@ func (w *filesWriter) writeToFile(path string, blob []byte, offset int64, create
|
|||||||
_, err = wr.WriteAt(blob, offset)
|
_, err = wr.WriteAt(blob, offset)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
releaseWriter(wr)
|
// ignore subsequent errors
|
||||||
|
_ = releaseWriter(wr)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,9 @@ func TestPreallocate(t *testing.T) {
|
|||||||
flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY
|
flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY
|
||||||
wr, err := os.OpenFile(path.Join(dirpath, "test"), flags, 0600)
|
wr, err := os.OpenFile(path.Join(dirpath, "test"), flags, 0600)
|
||||||
test.OK(t, err)
|
test.OK(t, err)
|
||||||
defer wr.Close()
|
defer func() {
|
||||||
|
test.OK(t, wr.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
err = preallocateFile(wr, i)
|
err = preallocateFile(wr, i)
|
||||||
test.OK(t, err)
|
test.OK(t, err)
|
||||||
|
@ -74,7 +74,7 @@ func saveDir(t testing.TB, repo restic.Repository, nodes map[string]Node, inode
|
|||||||
if mode == 0 {
|
if mode == 0 {
|
||||||
mode = 0644
|
mode = 0644
|
||||||
}
|
}
|
||||||
tree.Insert(&restic.Node{
|
err := tree.Insert(&restic.Node{
|
||||||
Type: "file",
|
Type: "file",
|
||||||
Mode: mode,
|
Mode: mode,
|
||||||
ModTime: node.ModTime,
|
ModTime: node.ModTime,
|
||||||
@ -86,6 +86,7 @@ func saveDir(t testing.TB, repo restic.Repository, nodes map[string]Node, inode
|
|||||||
Inode: fi,
|
Inode: fi,
|
||||||
Links: lc,
|
Links: lc,
|
||||||
})
|
})
|
||||||
|
rtest.OK(t, err)
|
||||||
case Dir:
|
case Dir:
|
||||||
id := saveDir(t, repo, node.Nodes, inode)
|
id := saveDir(t, repo, node.Nodes, inode)
|
||||||
|
|
||||||
@ -94,7 +95,7 @@ func saveDir(t testing.TB, repo restic.Repository, nodes map[string]Node, inode
|
|||||||
mode = 0755
|
mode = 0755
|
||||||
}
|
}
|
||||||
|
|
||||||
tree.Insert(&restic.Node{
|
err := tree.Insert(&restic.Node{
|
||||||
Type: "dir",
|
Type: "dir",
|
||||||
Mode: mode,
|
Mode: mode,
|
||||||
ModTime: node.ModTime,
|
ModTime: node.ModTime,
|
||||||
@ -103,6 +104,7 @@ func saveDir(t testing.TB, repo restic.Repository, nodes map[string]Node, inode
|
|||||||
GID: uint32(os.Getgid()),
|
GID: uint32(os.Getgid()),
|
||||||
Subtree: &id,
|
Subtree: &id,
|
||||||
})
|
})
|
||||||
|
rtest.OK(t, err)
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unknown node type %T", node)
|
t.Fatalf("unknown node type %T", node)
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,9 @@ func Random(seed, count int) []byte {
|
|||||||
func SetupTarTestFixture(t testing.TB, outputDir, tarFile string) {
|
func SetupTarTestFixture(t testing.TB, outputDir, tarFile string) {
|
||||||
input, err := os.Open(tarFile)
|
input, err := os.Open(tarFile)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
defer input.Close()
|
defer func() {
|
||||||
|
OK(t, input.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
var rd io.Reader
|
var rd io.Reader
|
||||||
switch filepath.Ext(tarFile) {
|
switch filepath.Ext(tarFile) {
|
||||||
@ -103,7 +105,9 @@ func SetupTarTestFixture(t testing.TB, outputDir, tarFile string) {
|
|||||||
r, err := gzip.NewReader(input)
|
r, err := gzip.NewReader(input)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
defer r.Close()
|
defer func() {
|
||||||
|
OK(t, r.Close())
|
||||||
|
}()
|
||||||
rd = r
|
rd = r
|
||||||
case ".bzip2":
|
case ".bzip2":
|
||||||
rd = bzip2.NewReader(input)
|
rd = bzip2.NewReader(input)
|
||||||
|
@ -25,7 +25,8 @@ func writeTempfile(t testing.TB, data []byte) (string, func()) {
|
|||||||
err = closeErr
|
err = closeErr
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Remove(name)
|
// ignore subsequent errors
|
||||||
|
_ = os.Remove(name)
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -79,7 +79,10 @@ func NewBackup(term *termstatus.Terminal, verbosity uint) *Backup {
|
|||||||
|
|
||||||
func toJSONString(status interface{}) string {
|
func toJSONString(status interface{}) string {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
json.NewEncoder(buf).Encode(status)
|
err := json.NewEncoder(buf).Encode(status)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,8 +12,9 @@ func TestIsProcessBackground(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Skipf("can't open terminal: %v", err)
|
t.Skipf("can't open terminal: %v", err)
|
||||||
}
|
}
|
||||||
defer tty.Close()
|
|
||||||
|
|
||||||
_, err = isProcessBackground(tty.Fd())
|
_, err = isProcessBackground(tty.Fd())
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
|
||||||
|
_ = tty.Close()
|
||||||
}
|
}
|
||||||
|
@ -28,17 +28,23 @@ func buildTreeMap(tree TestTree, m TreeMap) restic.ID {
|
|||||||
for name, item := range tree {
|
for name, item := range tree {
|
||||||
switch elem := item.(type) {
|
switch elem := item.(type) {
|
||||||
case TestFile:
|
case TestFile:
|
||||||
res.Insert(&restic.Node{
|
err := res.Insert(&restic.Node{
|
||||||
Name: name,
|
Name: name,
|
||||||
Type: "file",
|
Type: "file",
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
case TestTree:
|
case TestTree:
|
||||||
id := buildTreeMap(elem, m)
|
id := buildTreeMap(elem, m)
|
||||||
res.Insert(&restic.Node{
|
err := res.Insert(&restic.Node{
|
||||||
Name: name,
|
Name: name,
|
||||||
Subtree: &id,
|
Subtree: &id,
|
||||||
Type: "dir",
|
Type: "dir",
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("invalid type %T", elem))
|
panic(fmt.Sprintf("invalid type %T", elem))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user