mirror of
https://github.com/restic/restic.git
synced 2026-11-10 08:41:00 +00:00
backup: prevent exclude of backup targets
Track backup targets explicitly specified by the user and prevent excluding them. This for example ensures that `restic backup --exclude-if-present .git /home/user/data` backs up the `data` folder even if there is a `.git` folder in `/home/user`. Note that this does not suffice for commands like `restic backup --exclude data /home/user/data` as the exclude pattern will still match every single file within `data`.
This commit is contained in:
1 parent
c221cd06ad
commit
02cf8e5f23
6 files changed
+125
-70
No files matched your search
+24
-11
@@ -25,6 +25,7 @@ type tree struct {
|
||||
Path string // where the files/dirs to be saved are found
|
||||
FileInfoPath string // where the dir can be found that is not included itself, but its subdirs
|
||||
Root string // parent directory of the tree
|
||||
Explicit bool // Explicit is true when Path was passed as a backup target
|
||||
}
|
||||
|
||||
// pathComponents returns all path components of p. If a virtual directory
|
||||
@@ -94,8 +95,9 @@ func rootDirectory(fs fs.FS, target string) string {
|
||||
return rel
|
||||
}
|
||||
|
||||
// Add adds a new file or directory to the tree.
|
||||
func (t *tree) Add(fs fs.FS, path string) error {
|
||||
// Add adds a new file or directory to the tree. explicit marks a path the user
|
||||
// named as a backup target (see BackupTarget.Explicit).
|
||||
func (t *tree) Add(fs fs.FS, path string, explicit bool) error {
|
||||
if path == "" {
|
||||
panic("invalid path (empty string)")
|
||||
}
|
||||
@@ -138,13 +140,14 @@ func (t *tree) Add(fs fs.FS, path string) error {
|
||||
// use the original root dir if this is a virtual directory (volume name on Windows)
|
||||
subroot = root
|
||||
}
|
||||
err := tree.add(fs, path, subroot, pc[1:])
|
||||
err := tree.add(fs, path, subroot, pc[1:], explicit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tree.FileInfoPath = subroot
|
||||
} else {
|
||||
tree.Path = path
|
||||
tree.Explicit = explicit
|
||||
}
|
||||
|
||||
t.Nodes[name] = tree
|
||||
@@ -152,7 +155,7 @@ func (t *tree) Add(fs fs.FS, path string) error {
|
||||
}
|
||||
|
||||
// add adds a new target path into the tree.
|
||||
func (t *tree) add(fs fs.FS, target, root string, pc []string) error {
|
||||
func (t *tree) add(fs fs.FS, target, root string, pc []string, explicit bool) error {
|
||||
if len(pc) == 0 {
|
||||
return errors.Errorf("invalid path %q", target)
|
||||
}
|
||||
@@ -167,7 +170,7 @@ func (t *tree) add(fs fs.FS, target, root string, pc []string) error {
|
||||
node, ok := t.Nodes[name]
|
||||
|
||||
if !ok {
|
||||
t.Nodes[name] = tree{Path: target}
|
||||
t.Nodes[name] = tree{Path: target, Explicit: explicit}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -175,6 +178,7 @@ func (t *tree) add(fs fs.FS, target, root string, pc []string) error {
|
||||
return errors.Errorf("path is already set for target %v", target)
|
||||
}
|
||||
node.Path = target
|
||||
node.Explicit = explicit
|
||||
t.Nodes[name] = node
|
||||
return nil
|
||||
}
|
||||
@@ -187,7 +191,7 @@ func (t *tree) add(fs fs.FS, target, root string, pc []string) error {
|
||||
subroot := fs.Join(root, name)
|
||||
node.FileInfoPath = subroot
|
||||
|
||||
err := node.add(fs, target, subroot, pc[1:])
|
||||
err := node.add(fs, target, subroot, pc[1:], explicit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -221,7 +225,7 @@ func (t tree) NodeNames() []string {
|
||||
// formatTree returns a text representation of the tree t.
|
||||
func formatTree(t tree, indent string) (s string) {
|
||||
for name, node := range t.Nodes {
|
||||
s += fmt.Sprintf("%v/%v, root %q, path %q, meta %q\n", indent, name, node.Root, node.Path, node.FileInfoPath)
|
||||
s += fmt.Sprintf("%v/%v, root %q, path %q, meta %q, explicit %v\n", indent, name, node.Root, node.Path, node.FileInfoPath, node.Explicit)
|
||||
s += formatTree(node, indent+" ")
|
||||
}
|
||||
return s
|
||||
@@ -255,6 +259,7 @@ func unrollTree(f fs.FS, t *tree) error {
|
||||
t.Nodes[entry] = tree{Path: f.Join(t.Path, entry)}
|
||||
}
|
||||
t.Path = ""
|
||||
t.Explicit = false
|
||||
}
|
||||
|
||||
for i, subtree := range t.Nodes {
|
||||
@@ -269,13 +274,21 @@ func unrollTree(f fs.FS, t *tree) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
Path string
|
||||
Explicit bool
|
||||
}
|
||||
|
||||
// newTree creates a Tree from the target files/directories.
|
||||
func newTree(fs fs.FS, targets []string) (*tree, error) {
|
||||
func newTree(fs fs.FS, targets []BackupTarget) (*tree, error) {
|
||||
debug.Log("targets: %v", targets)
|
||||
tree := &tree{}
|
||||
seen := make(map[string]struct{})
|
||||
for _, target := range targets {
|
||||
target = fs.Clean(target)
|
||||
for _, tgt := range targets {
|
||||
target := fs.Clean(tgt.Path)
|
||||
|
||||
// skip duplicate targets
|
||||
if _, ok := seen[target]; ok {
|
||||
@@ -283,7 +296,7 @@ func newTree(fs fs.FS, targets []string) (*tree, error) {
|
||||
}
|
||||
seen[target] = struct{}{}
|
||||
|
||||
err := tree.Add(fs, target)
|
||||
err := tree.Add(fs, target, tgt.Explicit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user