forked from TrueCloudLab/restic
backup: Only return a warning for duplicate directory entries
The backup command failed if a directory contains duplicate entries. Downgrade the severity of this problem from fatal error to a warning. This allows users to still create a backup.
This commit is contained in:
parent
d7d7b4ab27
commit
403b01b788
4 changed files with 25 additions and 1 deletions
12
changelog/unreleased/issue-1866
Normal file
12
changelog/unreleased/issue-1866
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Enhancement: Improve handling of directories with duplicate entries
|
||||||
|
|
||||||
|
If for some reason a directory contains a duplicate entry, this caused the
|
||||||
|
backup command to fail with a `node "path/to/file" already present` or `nodes
|
||||||
|
are not ordered got "path/to/file", last "path/to/file"` error.
|
||||||
|
|
||||||
|
The error handling has been changed to only report a warning in this case. Make
|
||||||
|
sure to check that the filesystem in question is not damaged!
|
||||||
|
|
||||||
|
https://github.com/restic/restic/issues/1866
|
||||||
|
https://github.com/restic/restic/issues/3937
|
||||||
|
https://github.com/restic/restic/pull/3880
|
|
@ -2,6 +2,7 @@ package archiver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/restic/restic/internal/debug"
|
"github.com/restic/restic/internal/debug"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
|
@ -79,6 +80,7 @@ func (s *TreeSaver) save(ctx context.Context, job *saveTreeJob) (*restic.Node, I
|
||||||
job.nodes = nil
|
job.nodes = nil
|
||||||
|
|
||||||
builder := restic.NewTreeJSONBuilder()
|
builder := restic.NewTreeJSONBuilder()
|
||||||
|
var lastNode *restic.Node
|
||||||
|
|
||||||
for i, fn := range nodes {
|
for i, fn := range nodes {
|
||||||
// fn is a copy, so clear the original value explicitly
|
// fn is a copy, so clear the original value explicitly
|
||||||
|
@ -105,9 +107,15 @@ func (s *TreeSaver) save(ctx context.Context, job *saveTreeJob) (*restic.Node, I
|
||||||
|
|
||||||
debug.Log("insert %v", fnr.node.Name)
|
debug.Log("insert %v", fnr.node.Name)
|
||||||
err := builder.AddNode(fnr.node)
|
err := builder.AddNode(fnr.node)
|
||||||
|
if err != nil && errors.Is(err, restic.ErrTreeNotOrdered) && lastNode != nil && fnr.node.Equals(*lastNode) {
|
||||||
|
// ignore error if an _identical_ node already exists, but nevertheless issue a warning
|
||||||
|
_ = s.errFn(fnr.target, err)
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, stats, err
|
return nil, stats, err
|
||||||
}
|
}
|
||||||
|
lastNode = fnr.node
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, err := builder.Finalize()
|
buf, err := builder.Finalize()
|
||||||
|
|
|
@ -145,6 +145,8 @@ func SaveTree(ctx context.Context, r BlobSaver, t *Tree) (ID, error) {
|
||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ErrTreeNotOrdered = errors.Errorf("nodes are not ordered or duplicate")
|
||||||
|
|
||||||
type TreeJSONBuilder struct {
|
type TreeJSONBuilder struct {
|
||||||
buf bytes.Buffer
|
buf bytes.Buffer
|
||||||
lastName string
|
lastName string
|
||||||
|
@ -158,7 +160,7 @@ func NewTreeJSONBuilder() *TreeJSONBuilder {
|
||||||
|
|
||||||
func (builder *TreeJSONBuilder) AddNode(node *Node) error {
|
func (builder *TreeJSONBuilder) AddNode(node *Node) error {
|
||||||
if node.Name <= builder.lastName {
|
if node.Name <= builder.lastName {
|
||||||
return errors.Errorf("nodes are not ordered got %q, last %q", node.Name, builder.lastName)
|
return fmt.Errorf("node %q, last%q: %w", node.Name, builder.lastName, ErrTreeNotOrdered)
|
||||||
}
|
}
|
||||||
if builder.lastName != "" {
|
if builder.lastName != "" {
|
||||||
_ = builder.buf.WriteByte(',')
|
_ = builder.buf.WriteByte(',')
|
||||||
|
|
|
@ -3,6 +3,7 @@ package restic_test
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -136,6 +137,7 @@ func TestTreeEqualSerialization(t *testing.T) {
|
||||||
|
|
||||||
rtest.Assert(t, tree.Insert(node) != nil, "no error on duplicate node")
|
rtest.Assert(t, tree.Insert(node) != nil, "no error on duplicate node")
|
||||||
rtest.Assert(t, builder.AddNode(node) != nil, "no error on duplicate node")
|
rtest.Assert(t, builder.AddNode(node) != nil, "no error on duplicate node")
|
||||||
|
rtest.Assert(t, errors.Is(builder.AddNode(node), restic.ErrTreeNotOrdered), "wrong error returned")
|
||||||
}
|
}
|
||||||
|
|
||||||
treeBytes, err := json.Marshal(tree)
|
treeBytes, err := json.Marshal(tree)
|
||||||
|
|
Loading…
Reference in a new issue