From b02117ef0b9aeb06781e470b45480e0705be938a Mon Sep 17 00:00:00 2001 From: Srigovind Nayak Date: Sun, 19 May 2024 23:30:14 +0530 Subject: [PATCH] restore: read includes, insensitive includes, excludes and insensitive excludes from a file feature for gh-4781 --- cmd/restic/cmd_restore.go | 60 +++++++++++++++++++++++++++++++++++---- cmd/restic/exclude.go | 20 ++++++------- 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/cmd/restic/cmd_restore.go b/cmd/restic/cmd_restore.go index 5161be50d..f8a62c360 100644 --- a/cmd/restic/cmd_restore.go +++ b/cmd/restic/cmd_restore.go @@ -45,11 +45,15 @@ Exit status is 0 if the command was successful, and non-zero if there was any er // RestoreOptions collects all options for the restore command. type RestoreOptions struct { - Exclude []string - InsensitiveExclude []string - Include []string - InsensitiveInclude []string - Target string + Exclude []string + ExcludeFiles []string + InsensitiveExclude []string + InsensitiveExcludeFiles []string + Include []string + IncludeFiles []string + InsensitiveInclude []string + InsensitiveIncludeFiles []string + Target string restic.SnapshotFilter Sparse bool Verify bool @@ -66,6 +70,10 @@ func init() { flags.StringArrayVarP(&restoreOptions.Include, "include", "i", nil, "include a `pattern`, exclude everything else (can be specified multiple times)") flags.StringArrayVar(&restoreOptions.InsensitiveInclude, "iinclude", nil, "same as --include but ignores the casing of `pattern`") flags.StringVarP(&restoreOptions.Target, "target", "t", "", "directory to extract data to") + flags.StringArrayVar(&restoreOptions.ExcludeFiles, "exclude-file", nil, "read exclude patterns from a `file` (can be specified multiple times)") + flags.StringArrayVar(&restoreOptions.InsensitiveExcludeFiles, "iexclude-file", nil, "same as --exclude-file but ignores casing of `file`names in patterns") + flags.StringArrayVar(&restoreOptions.IncludeFiles, "include-file", nil, "read include patterns from a `file` (can be specified multiple times)") + flags.StringArrayVar(&restoreOptions.InsensitiveIncludeFiles, "iinclude-file", nil, "same as --include-file but ignores casing of `file`names in patterns") initSingleSnapshotFilter(flags, &restoreOptions.SnapshotFilter) flags.BoolVar(&restoreOptions.Sparse, "sparse", false, "restore files as sparse") @@ -176,6 +184,27 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions, excludePatterns := filter.ParsePatterns(opts.Exclude) insensitiveExcludePatterns := filter.ParsePatterns(opts.InsensitiveExclude) + + if len(opts.ExcludeFiles) > 0 { + patternsFromFile, err := readPatternsFromFiles(opts.ExcludeFiles) + if err != nil { + return err + } + + excludePatternsFromFile := filter.ParsePatterns(patternsFromFile) + excludePatterns = append(excludePatterns, excludePatternsFromFile...) + } + + if len(opts.InsensitiveExcludeFiles) > 0 { + patternsFromFile, err := readPatternsFromFiles(opts.ExcludeFiles) + if err != nil { + return err + } + + iexcludePatternsFromFile := filter.ParsePatterns(patternsFromFile) + insensitiveExcludePatterns = append(insensitiveExcludePatterns, iexcludePatternsFromFile...) + } + selectExcludeFilter := func(item string, _ string, node *restic.Node) (selectedForRestore bool, childMayBeSelected bool) { matched, err := filter.List(excludePatterns, item) if err != nil { @@ -199,6 +228,27 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions, includePatterns := filter.ParsePatterns(opts.Include) insensitiveIncludePatterns := filter.ParsePatterns(opts.InsensitiveInclude) + + if len(opts.IncludeFiles) > 0 { + patternsFromFile, err := readPatternsFromFiles(opts.IncludeFiles) + if err != nil { + return err + } + + includePatternsFromFile := filter.ParsePatterns(patternsFromFile) + includePatterns = append(includePatterns, includePatternsFromFile...) + } + + if len(opts.InsensitiveIncludeFiles) > 0 { + patternsFromFile, err := readPatternsFromFiles(opts.InsensitiveIncludeFiles) + if err != nil { + return err + } + + iincludePatternsFromFile := filter.ParsePatterns(patternsFromFile) + insensitiveIncludePatterns = append(insensitiveIncludePatterns, iincludePatternsFromFile...) + } + selectIncludeFilter := func(item string, _ string, node *restic.Node) (selectedForRestore bool, childMayBeSelected bool) { matched, childMayMatch, err := filter.ListWithChild(includePatterns, item) if err != nil { diff --git a/cmd/restic/exclude.go b/cmd/restic/exclude.go index d9bb63aeb..4657e4915 100644 --- a/cmd/restic/exclude.go +++ b/cmd/restic/exclude.go @@ -385,12 +385,12 @@ func rejectBySize(maxSizeStr string) (RejectFunc, error) { }, nil } -// readExcludePatternsFromFiles reads all exclude files and returns the list of -// exclude patterns. For each line, leading and trailing white space is removed +// readPatternsFromFiles reads all files and returns the list of +// patterns. For each line, leading and trailing white space is removed // and comment lines are ignored. For each remaining pattern, environment // variables are resolved. For adding a literal dollar sign ($), write $$ to // the file. -func readExcludePatternsFromFiles(excludeFiles []string) ([]string, error) { +func readPatternsFromFiles(files []string) ([]string, error) { getenvOrDollar := func(s string) string { if s == "$" { return "$" @@ -398,8 +398,8 @@ func readExcludePatternsFromFiles(excludeFiles []string) ([]string, error) { return os.Getenv(s) } - var excludes []string - for _, filename := range excludeFiles { + var patterns []string + for _, filename := range files { err := func() (err error) { data, err := textfile.Read(filename) if err != nil { @@ -421,15 +421,15 @@ func readExcludePatternsFromFiles(excludeFiles []string) ([]string, error) { } line = os.Expand(line, getenvOrDollar) - excludes = append(excludes, line) + patterns = append(patterns, line) } return scanner.Err() }() if err != nil { - return nil, fmt.Errorf("failed to read excludes from file %q: %w", filename, err) + return nil, fmt.Errorf("failed to read patterns from file %q: %w", filename, err) } } - return excludes, nil + return patterns, nil } type excludePatternOptions struct { @@ -454,7 +454,7 @@ func (opts excludePatternOptions) CollectPatterns() ([]RejectByNameFunc, error) var fs []RejectByNameFunc // add patterns from file if len(opts.ExcludeFiles) > 0 { - excludePatterns, err := readExcludePatternsFromFiles(opts.ExcludeFiles) + excludePatterns, err := readPatternsFromFiles(opts.ExcludeFiles) if err != nil { return nil, err } @@ -467,7 +467,7 @@ func (opts excludePatternOptions) CollectPatterns() ([]RejectByNameFunc, error) } if len(opts.InsensitiveExcludeFiles) > 0 { - excludes, err := readExcludePatternsFromFiles(opts.InsensitiveExcludeFiles) + excludes, err := readPatternsFromFiles(opts.InsensitiveExcludeFiles) if err != nil { return nil, err }