restorer: Fix traverseTree

traverseTree() was meant to call enterDir() whenever a directory is
selected for restore, either explicitly or implicitly (=contains a file
which is to be restored). After restoring a file, leaveDir() is called
in reverse order for all intermediate directories so that the metadata
can be restored.

When a directory is selected implicitly, the metadata for it is
restored. This is different from the previous restorer behavior, which
created implicitly selected intermediate directories with permissions
0700 (only user can read/write it).

This commit changes the behavior back to the old one. Only a directory
is explicitly selected for restore, enterDir()/leaveDir() are called for
it. Otherwise, only visitNode() is called, so visitNode() needs to make
sure the parent directory exists. If the directory is explicitly
included, leaveDir() will then restore the metadata correctly.

When we decide to change the behavior (restore metadata for all
intermediate directories, even if selected implicitly), we should do
that in the selection functions, not here.

This finally resolves #1870
This commit is contained in:
Alexander Neumann 2018-07-21 22:39:12 +02:00
parent ce19f26948
commit 44924ba043
1 changed files with 14 additions and 31 deletions

View File

@ -99,36 +99,16 @@ func (res *Restorer) traverseTree(ctx context.Context, target, location string,
return err
}
enteredDir := false
if node.Type == "dir" {
if node.Subtree == nil {
return errors.Errorf("Dir without subtree in tree %v", treeID.Str())
}
// ifedorenko: apparently a dir can be selected explicitly or implicitly when a child is selected
// to support implicit selection, visit the directory from within visitor#visitNode
if selectedForRestore {
enteredDir = true
err = sanitizeError(visitor.enterDir(node, nodeTarget, nodeLocation))
if err != nil {
return err
}
} else {
_visitor := visitor
visitor = treeVisitor{
enterDir: _visitor.enterDir,
visitNode: func(node *restic.Node, nodeTarget, nodeLocation string) error {
if !enteredDir {
enteredDir = true
derr := sanitizeError(_visitor.enterDir(node, nodeTarget, nodeLocation))
if derr != nil {
return derr
}
}
return _visitor.visitNode(node, nodeTarget, nodeLocation)
},
leaveDir: _visitor.leaveDir,
}
}
if childMayBeSelected {
@ -137,25 +117,21 @@ func (res *Restorer) traverseTree(ctx context.Context, target, location string,
return err
}
}
}
if selectedForRestore && node.Type != "dir" {
err = sanitizeError(visitor.visitNode(node, nodeTarget, nodeLocation))
if err != nil {
err = res.Error(nodeLocation, node, errors.Wrap(err, "restoreNodeTo"))
if selectedForRestore {
err = sanitizeError(visitor.leaveDir(node, nodeTarget, nodeLocation))
if err != nil {
return err
}
}
continue
}
if enteredDir {
err = sanitizeError(visitor.leaveDir(node, nodeTarget, nodeLocation))
if selectedForRestore {
err = sanitizeError(visitor.visitNode(node, nodeTarget, nodeLocation))
if err != nil {
err = res.Error(nodeLocation, node, errors.Wrap(err, "RestoreTimestamps"))
if err != nil {
return err
}
return err
}
}
}
@ -211,6 +187,13 @@ func (res *Restorer) RestoreTo(ctx context.Context, dst string) error {
return fs.MkdirAll(target, 0700)
},
visitNode: func(node *restic.Node, target, location string) error {
// create parent dir with default permissions
// #leaveDir restores dir metadata after visiting all children
err := fs.MkdirAll(filepath.Dir(target), 0700)
if err != nil {
return err
}
return res.restoreNodeTo(ctx, node, target, location, idx)
},
leaveDir: func(node *restic.Node, target, location string) error {