update nodeRestoreExtendedAttributes() for win

- also other platforms
- move xattr include/exclude filter parsing into
  separate func

Signed-off-by: Tesshu Flower <tflower@redhat.com>
This commit is contained in:
Tesshu Flower 2024-11-15 15:55:29 -05:00
parent 0d6e008114
commit bb4c6d4934
No known key found for this signature in database
GPG key ID: DC7556B1903A252B
7 changed files with 62 additions and 46 deletions

View file

@ -101,9 +101,6 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
hasExcludes := len(excludePatternFns) > 0 hasExcludes := len(excludePatternFns) > 0
hasIncludes := len(includePatternFns) > 0 hasIncludes := len(includePatternFns) > 0
hasXattrExcludes := len(opts.ExcludeXattrPattern) > 0
hasXattrIncludes := len(opts.IncludeXattrPattern) > 0
switch { switch {
case len(args) == 0: case len(args) == 0:
return errors.Fatal("no snapshot ID specified") return errors.Fatal("no snapshot ID specified")
@ -119,10 +116,6 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
return errors.Fatal("exclude and include patterns are mutually exclusive") return errors.Fatal("exclude and include patterns are mutually exclusive")
} }
if hasXattrExcludes && hasXattrIncludes {
return errors.Fatal("exclude and include xattr patterns are mutually exclusive")
}
if opts.DryRun && opts.Verify { if opts.DryRun && opts.Verify {
return errors.Fatal("--dry-run and --verify are mutually exclusive") return errors.Fatal("--dry-run and --verify are mutually exclusive")
} }
@ -232,29 +225,9 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
res.SelectFilter = selectIncludeFilter res.SelectFilter = selectIncludeFilter
} }
if !hasXattrExcludes && !hasXattrIncludes { res.XattrSelectFilter, err = getXattrSelectFilter(opts)
// set default of including xattrs from the 'user' namespace if err != nil {
opts.IncludeXattrPattern = []string{"user.*"} return err
}
if hasXattrExcludes {
if err := filter.ValidatePatterns(opts.ExcludeXattrPattern); err != nil {
return errors.Fatalf("--exclude-xattr: %s", err)
}
res.XattrSelectFilter = func(xattrName string) bool {
shouldReject := filter.RejectByPattern(opts.ExcludeXattrPattern, Warnf)(xattrName)
return !shouldReject
}
} else {
// User has either input include xattr pattern(s) or we're using our default include pattern
if err := filter.ValidatePatterns(opts.IncludeXattrPattern); err != nil {
return errors.Fatalf("--include-xattr: %s", err)
}
res.XattrSelectFilter = func(xattrName string) bool {
shouldInclude, _ := filter.IncludeByPattern(opts.IncludeXattrPattern, Warnf)(xattrName)
return shouldInclude
}
} }
if !gopts.JSON { if !gopts.JSON {
@ -295,3 +268,38 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
return nil return nil
} }
func getXattrSelectFilter(opts RestoreOptions) (func(xattrName string) bool, error) {
hasXattrExcludes := len(opts.ExcludeXattrPattern) > 0
hasXattrIncludes := len(opts.IncludeXattrPattern) > 0
if hasXattrExcludes && hasXattrIncludes {
return nil, errors.Fatal("exclude and include xattr patterns are mutually exclusive")
}
if hasXattrExcludes {
if err := filter.ValidatePatterns(opts.ExcludeXattrPattern); err != nil {
return nil, errors.Fatalf("--exclude-xattr: %s", err)
}
return func(xattrName string) bool {
shouldReject := filter.RejectByPattern(opts.ExcludeXattrPattern, Warnf)(xattrName)
return !shouldReject
}, nil
}
if hasXattrIncludes {
// User has either input include xattr pattern(s) or we're using our default include pattern
if err := filter.ValidatePatterns(opts.IncludeXattrPattern); err != nil {
return nil, errors.Fatalf("--include-xattr: %s", err)
}
return func(xattrName string) bool {
shouldInclude, _ := filter.IncludeByPattern(opts.IncludeXattrPattern, Warnf)(xattrName)
return shouldInclude
}, nil
}
// no includes or excludes, set default of including all xattrs
return func(_ string) bool { return true }, nil
}

View file

@ -8,7 +8,7 @@ import (
) )
// nodeRestoreExtendedAttributes is a no-op // nodeRestoreExtendedAttributes is a no-op
func nodeRestoreExtendedAttributes(_ *restic.Node, _ string) error { func nodeRestoreExtendedAttributes(_ *restic.Node, _ string, _ func(xattrName string) bool) error {
return nil return nil
} }

View file

@ -217,8 +217,9 @@ func TestNodeRestoreAt(t *testing.T) {
nodePath = filepath.Join(tempdir, test.Name) nodePath = filepath.Join(tempdir, test.Name)
} }
rtest.OK(t, NodeCreateAt(&test, nodePath)) rtest.OK(t, NodeCreateAt(&test, nodePath))
// Restore metadata, restoring all xattrs
rtest.OK(t, NodeRestoreMetadata(&test, nodePath, func(msg string) { rtest.OK(t, fmt.Errorf("Warning triggered for path: %s: %s", nodePath, msg)) }, 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 } /* restore all xattrs */)) func(_ string) bool { return true }))
fs := &Local{} fs := &Local{}
meta, err := fs.OpenFile(nodePath, O_NOFOLLOW, true) meta, err := fs.OpenFile(nodePath, O_NOFOLLOW, true)

View file

@ -69,17 +69,22 @@ func utimesNano(path string, atime, mtime int64, _ restic.NodeType) error {
} }
// restore extended attributes for windows // restore extended attributes for windows
func nodeRestoreExtendedAttributes(node *restic.Node, path string) (err error) { func nodeRestoreExtendedAttributes(node *restic.Node, path string, xattrSelectFilter func(xattrName string) bool) error {
count := len(node.ExtendedAttributes) count := len(node.ExtendedAttributes)
if count > 0 { if count > 0 {
eas := make([]extendedAttribute, count) eas := []extendedAttribute{}
for i, attr := range node.ExtendedAttributes { for _, attr := range node.ExtendedAttributes {
eas[i] = extendedAttribute{Name: attr.Name, Value: attr.Value} // Filter for xattrs we want to include/exclude
if xattrSelectFilter(attr.Name) {
eas = append(eas, extendedAttribute{Name: attr.Name, Value: attr.Value})
} }
}
if len(eas) > 0 {
if errExt := restoreExtendedAttributes(node.Type, path, eas); errExt != nil { if errExt := restoreExtendedAttributes(node.Type, path, eas); errExt != nil {
return errExt return errExt
} }
} }
}
return nil return nil
} }

View file

@ -218,7 +218,7 @@ func restoreAndGetNode(t *testing.T, tempDir string, testNode *restic.Node, warn
// If warning is not expected, this code should not get triggered. // If warning is not expected, this code should not get triggered.
test.OK(t, fmt.Errorf("Warning triggered for path: %s: %s", testPath, msg)) test.OK(t, fmt.Errorf("Warning triggered for path: %s: %s", testPath, msg))
} }
}) }, func(_ string) bool { return true })
test.OK(t, errors.Wrapf(err, "Failed to restore metadata for: %s", testPath)) test.OK(t, errors.Wrapf(err, "Failed to restore metadata for: %s", testPath))
fs := &Local{} fs := &Local{}

View file

@ -26,7 +26,8 @@ func setAndVerifyXattr(t *testing.T, file string, attrs []restic.ExtendedAttribu
Type: restic.NodeTypeFile, Type: restic.NodeTypeFile,
ExtendedAttributes: attrs, ExtendedAttributes: attrs,
} }
rtest.OK(t, nodeRestoreExtendedAttributes(node, file, func(_ string) bool { return true } /*restore all xattrs*/)) /* restore all xattrs */
rtest.OK(t, nodeRestoreExtendedAttributes(node, file, func(_ string) bool { return true }))
nodeActual := &restic.Node{ nodeActual := &restic.Node{
Type: restic.NodeTypeFile, Type: restic.NodeTypeFile,

View file

@ -104,6 +104,7 @@ func NewRestorer(repo restic.Repository, sn *restic.Snapshot, opts Options) *Res
fileList: make(map[string]bool), fileList: make(map[string]bool),
Error: restorerAbortOnAllErrors, Error: restorerAbortOnAllErrors,
SelectFilter: func(string, bool) (bool, bool) { return true, true }, SelectFilter: func(string, bool) (bool, bool) { return true, true },
XattrSelectFilter: func(string) bool { return true },
sn: sn, sn: sn,
} }