data: split node and snapshot code from restic package

This commit is contained in:
Michael Eischer
2025-09-23 20:01:09 +02:00
parent c85b157e0e
commit 56ac8360c7
166 changed files with 1170 additions and 1107 deletions

View File

@@ -19,6 +19,7 @@ import (
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/restic/restic/internal/archiver" "github.com/restic/restic/internal/archiver"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/filter"
@@ -76,7 +77,7 @@ type BackupOptions struct {
filter.ExcludePatternOptions filter.ExcludePatternOptions
Parent string Parent string
GroupBy restic.SnapshotGroupByOptions GroupBy data.SnapshotGroupByOptions
Force bool Force bool
ExcludeOtherFS bool ExcludeOtherFS bool
ExcludeIfPresent []string ExcludeIfPresent []string
@@ -86,7 +87,7 @@ type BackupOptions struct {
Stdin bool Stdin bool
StdinFilename string StdinFilename string
StdinCommand bool StdinCommand bool
Tags restic.TagLists Tags data.TagLists
Host string Host string
FilesFrom []string FilesFrom []string
FilesFromVerbatim []string FilesFromVerbatim []string
@@ -104,7 +105,7 @@ type BackupOptions struct {
func (opts *BackupOptions) AddFlags(f *pflag.FlagSet) { func (opts *BackupOptions) AddFlags(f *pflag.FlagSet) {
f.StringVar(&opts.Parent, "parent", "", "use this parent `snapshot` (default: latest snapshot in the group determined by --group-by and not newer than the timestamp determined by --time)") f.StringVar(&opts.Parent, "parent", "", "use this parent `snapshot` (default: latest snapshot in the group determined by --group-by and not newer than the timestamp determined by --time)")
opts.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true} opts.GroupBy = data.SnapshotGroupByOptions{Host: true, Path: true}
f.VarP(&opts.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma (disable grouping with '')") f.VarP(&opts.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma (disable grouping with '')")
f.BoolVarP(&opts.Force, "force", "f", false, `force re-reading the source files/directories (overrides the "parent" flag)`) f.BoolVarP(&opts.Force, "force", "f", false, `force re-reading the source files/directories (overrides the "parent" flag)`)
@@ -446,7 +447,7 @@ func collectTargets(opts BackupOptions, args []string, warnf func(msg string, ar
// parent returns the ID of the parent snapshot. If there is none, nil is // parent returns the ID of the parent snapshot. If there is none, nil is
// returned. // returned.
func findParentSnapshot(ctx context.Context, repo restic.ListerLoaderUnpacked, opts BackupOptions, targets []string, timeStampLimit time.Time) (*restic.Snapshot, error) { func findParentSnapshot(ctx context.Context, repo restic.ListerLoaderUnpacked, opts BackupOptions, targets []string, timeStampLimit time.Time) (*data.Snapshot, error) {
if opts.Force { if opts.Force {
return nil, nil return nil, nil
} }
@@ -455,7 +456,7 @@ func findParentSnapshot(ctx context.Context, repo restic.ListerLoaderUnpacked, o
if snName == "" { if snName == "" {
snName = "latest" snName = "latest"
} }
f := restic.SnapshotFilter{TimestampLimit: timeStampLimit} f := data.SnapshotFilter{TimestampLimit: timeStampLimit}
if opts.GroupBy.Host { if opts.GroupBy.Host {
f.Hosts = []string{opts.Host} f.Hosts = []string{opts.Host}
} }
@@ -463,12 +464,12 @@ func findParentSnapshot(ctx context.Context, repo restic.ListerLoaderUnpacked, o
f.Paths = targets f.Paths = targets
} }
if opts.GroupBy.Tag { if opts.GroupBy.Tag {
f.Tags = []restic.TagList{opts.Tags.Flatten()} f.Tags = []data.TagList{opts.Tags.Flatten()}
} }
sn, _, err := f.FindLatest(ctx, repo, repo, snName) sn, _, err := f.FindLatest(ctx, repo, repo, snName)
// Snapshot not found is ok if no explicit parent was set // Snapshot not found is ok if no explicit parent was set
if opts.Parent == "" && errors.Is(err, restic.ErrNoSnapshotFound) { if opts.Parent == "" && errors.Is(err, data.ErrNoSnapshotFound) {
err = nil err = nil
} }
return sn, err return sn, err
@@ -529,7 +530,7 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
return err return err
} }
var parentSnapshot *restic.Snapshot var parentSnapshot *data.Snapshot
if !opts.Stdin { if !opts.Stdin {
parentSnapshot, err = findParentSnapshot(ctx, repo, opts, targets, timeStamp) parentSnapshot, err = findParentSnapshot(ctx, repo, opts, targets, timeStamp)
if err != nil { if err != nil {

View File

@@ -9,6 +9,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
@@ -22,7 +23,7 @@ func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts
defer cleanup() defer cleanup()
} }
opts.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true} opts.GroupBy = data.SnapshotGroupByOptions{Host: true, Path: true}
return runBackup(ctx, opts, gopts, gopts.term, target) return runBackup(ctx, opts, gopts, gopts.term, target)
}) })
} }
@@ -473,7 +474,7 @@ func TestBackupTags(t *testing.T) {
"expected no tags, got %v", newest.Tags) "expected no tags, got %v", newest.Tags)
parent := newest parent := newest
opts.Tags = restic.TagLists{[]string{"NL"}} opts.Tags = data.TagLists{[]string{"NL"}}
testRunBackup(t, "", []string{env.testdata}, opts, env.gopts) testRunBackup(t, "", []string{env.testdata}, opts, env.gopts)
testRunCheck(t, env.gopts) testRunCheck(t, env.gopts)
newest, _ = testRunSnapshots(t, env.gopts) newest, _ = testRunSnapshots(t, env.gopts)

View File

@@ -7,6 +7,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
@@ -105,7 +106,7 @@ func runCat(ctx context.Context, gopts GlobalOptions, args []string, term ui.Ter
printer.S(string(buf)) printer.S(string(buf))
return nil return nil
case "snapshot": case "snapshot":
sn, _, err := restic.FindSnapshot(ctx, repo, repo, args[1]) sn, _, err := data.FindSnapshot(ctx, repo, repo, args[1])
if err != nil { if err != nil {
return errors.Fatalf("could not find snapshot: %v", err) return errors.Fatalf("could not find snapshot: %v", err)
} }
@@ -190,7 +191,7 @@ func runCat(ctx context.Context, gopts GlobalOptions, args []string, term ui.Ter
return errors.Fatal("blob not found") return errors.Fatal("blob not found")
case "tree": case "tree":
sn, subfolder, err := restic.FindSnapshot(ctx, repo, repo, args[1]) sn, subfolder, err := data.FindSnapshot(ctx, repo, repo, args[1])
if err != nil { if err != nil {
return errors.Fatalf("could not find snapshot: %v", err) return errors.Fatalf("could not find snapshot: %v", err)
} }
@@ -200,7 +201,7 @@ func runCat(ctx context.Context, gopts GlobalOptions, args []string, term ui.Ter
return err return err
} }
sn.Tree, err = restic.FindTreeDirectory(ctx, repo, sn.Tree, subfolder) sn.Tree, err = data.FindTreeDirectory(ctx, repo, sn.Tree, subfolder)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
@@ -59,7 +60,7 @@ Exit status is 12 if the password is incorrect.
// CopyOptions bundles all options for the copy command. // CopyOptions bundles all options for the copy command.
type CopyOptions struct { type CopyOptions struct {
secondaryRepoOptions secondaryRepoOptions
restic.SnapshotFilter data.SnapshotFilter
} }
func (opts *CopyOptions) AddFlags(f *pflag.FlagSet) { func (opts *CopyOptions) AddFlags(f *pflag.FlagSet) {
@@ -109,7 +110,7 @@ func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []
return err return err
} }
dstSnapshotByOriginal := make(map[restic.ID][]*restic.Snapshot) dstSnapshotByOriginal := make(map[restic.ID][]*data.Snapshot)
for sn := range FindFilteredSnapshots(ctx, dstSnapshotLister, dstRepo, &opts.SnapshotFilter, nil, printer) { for sn := range FindFilteredSnapshots(ctx, dstSnapshotLister, dstRepo, &opts.SnapshotFilter, nil, printer) {
if sn.Original != nil && !sn.Original.IsNull() { if sn.Original != nil && !sn.Original.IsNull() {
dstSnapshotByOriginal[*sn.Original] = append(dstSnapshotByOriginal[*sn.Original], sn) dstSnapshotByOriginal[*sn.Original] = append(dstSnapshotByOriginal[*sn.Original], sn)
@@ -158,7 +159,7 @@ func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []
if sn.Original == nil { if sn.Original == nil {
sn.Original = sn.ID() sn.Original = sn.ID()
} }
newID, err := restic.SaveSnapshot(ctx, dstRepo, sn) newID, err := data.SaveSnapshot(ctx, dstRepo, sn)
if err != nil { if err != nil {
return err return err
} }
@@ -167,7 +168,7 @@ func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []
return ctx.Err() return ctx.Err()
} }
func similarSnapshots(sna *restic.Snapshot, snb *restic.Snapshot) bool { func similarSnapshots(sna *data.Snapshot, snb *data.Snapshot) bool {
// everything except Parent and Original must match // everything except Parent and Original must match
if !sna.Time.Equal(snb.Time) || !sna.Tree.Equal(*snb.Tree) || sna.Hostname != snb.Hostname || if !sna.Time.Equal(snb.Time) || !sna.Tree.Equal(*snb.Tree) || sna.Hostname != snb.Hostname ||
sna.Username != snb.Username || sna.UID != snb.UID || sna.GID != snb.GID || sna.Username != snb.Username || sna.UID != snb.UID || sna.GID != snb.GID ||
@@ -191,7 +192,7 @@ func copyTree(ctx context.Context, srcRepo restic.Repository, dstRepo restic.Rep
wg, wgCtx := errgroup.WithContext(ctx) wg, wgCtx := errgroup.WithContext(ctx)
treeStream := restic.StreamTrees(wgCtx, wg, srcRepo, restic.IDs{rootTreeID}, func(treeID restic.ID) bool { treeStream := data.StreamTrees(wgCtx, wg, srcRepo, restic.IDs{rootTreeID}, func(treeID restic.ID) bool {
visited := visitedTrees.Has(treeID) visited := visitedTrees.Has(treeID)
visitedTrees.Insert(treeID) visitedTrees.Insert(treeID)
return visited return visited

View File

@@ -22,6 +22,7 @@ import (
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/restic/restic/internal/crypto" "github.com/restic/restic/internal/crypto"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/repository/index" "github.com/restic/restic/internal/repository/index"
@@ -115,7 +116,7 @@ func prettyPrintJSON(wr io.Writer, item interface{}) error {
} }
func debugPrintSnapshots(ctx context.Context, repo *repository.Repository, wr io.Writer) error { func debugPrintSnapshots(ctx context.Context, repo *repository.Repository, wr io.Writer) error {
return restic.ForAllSnapshots(ctx, repo, repo, nil, func(id restic.ID, snapshot *restic.Snapshot, err error) error { return data.ForAllSnapshots(ctx, repo, repo, nil, func(id restic.ID, snapshot *data.Snapshot, err error) error {
if err != nil { if err != nil {
return err return err
} }

View File

@@ -7,6 +7,7 @@ import (
"reflect" "reflect"
"sort" "sort"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
@@ -69,8 +70,8 @@ func (opts *DiffOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.ShowMetadata, "metadata", false, "print changes in metadata") f.BoolVar(&opts.ShowMetadata, "metadata", false, "print changes in metadata")
} }
func loadSnapshot(ctx context.Context, be restic.Lister, repo restic.LoaderUnpacked, desc string) (*restic.Snapshot, string, error) { func loadSnapshot(ctx context.Context, be restic.Lister, repo restic.LoaderUnpacked, desc string) (*data.Snapshot, string, error) {
sn, subfolder, err := restic.FindSnapshot(ctx, be, repo, desc) sn, subfolder, err := data.FindSnapshot(ctx, be, repo, desc)
if err != nil { if err != nil {
return nil, "", errors.Fatalf("%s", err) return nil, "", errors.Fatalf("%s", err)
} }
@@ -106,15 +107,15 @@ type DiffStat struct {
} }
// Add adds stats information for node to s. // Add adds stats information for node to s.
func (s *DiffStat) Add(node *restic.Node) { func (s *DiffStat) Add(node *data.Node) {
if node == nil { if node == nil {
return return
} }
switch node.Type { switch node.Type {
case restic.NodeTypeFile: case data.NodeTypeFile:
s.Files++ s.Files++
case restic.NodeTypeDir: case data.NodeTypeDir:
s.Dirs++ s.Dirs++
default: default:
s.Others++ s.Others++
@@ -122,13 +123,13 @@ func (s *DiffStat) Add(node *restic.Node) {
} }
// addBlobs adds the blobs of node to s. // addBlobs adds the blobs of node to s.
func addBlobs(bs restic.BlobSet, node *restic.Node) { func addBlobs(bs restic.BlobSet, node *data.Node) {
if node == nil { if node == nil {
return return
} }
switch node.Type { switch node.Type {
case restic.NodeTypeFile: case data.NodeTypeFile:
for _, blob := range node.Content { for _, blob := range node.Content {
h := restic.BlobHandle{ h := restic.BlobHandle{
ID: blob, ID: blob,
@@ -136,7 +137,7 @@ func addBlobs(bs restic.BlobSet, node *restic.Node) {
} }
bs.Insert(h) bs.Insert(h)
} }
case restic.NodeTypeDir: case data.NodeTypeDir:
h := restic.BlobHandle{ h := restic.BlobHandle{
ID: *node.Subtree, ID: *node.Subtree,
Type: restic.TreeBlob, Type: restic.TreeBlob,
@@ -177,7 +178,7 @@ func updateBlobs(repo restic.Loader, blobs restic.BlobSet, stats *DiffStat, prin
func (c *Comparer) printDir(ctx context.Context, mode string, stats *DiffStat, blobs restic.BlobSet, prefix string, id restic.ID) error { func (c *Comparer) printDir(ctx context.Context, mode string, stats *DiffStat, blobs restic.BlobSet, prefix string, id restic.ID) error {
debug.Log("print %v tree %v", mode, id) debug.Log("print %v tree %v", mode, id)
tree, err := restic.LoadTree(ctx, c.repo, id) tree, err := data.LoadTree(ctx, c.repo, id)
if err != nil { if err != nil {
return err return err
} }
@@ -188,14 +189,14 @@ func (c *Comparer) printDir(ctx context.Context, mode string, stats *DiffStat, b
} }
name := path.Join(prefix, node.Name) name := path.Join(prefix, node.Name)
if node.Type == restic.NodeTypeDir { if node.Type == data.NodeTypeDir {
name += "/" name += "/"
} }
c.printChange(NewChange(name, mode)) c.printChange(NewChange(name, mode))
stats.Add(node) stats.Add(node)
addBlobs(blobs, node) addBlobs(blobs, node)
if node.Type == restic.NodeTypeDir { if node.Type == data.NodeTypeDir {
err := c.printDir(ctx, mode, stats, blobs, name, *node.Subtree) err := c.printDir(ctx, mode, stats, blobs, name, *node.Subtree)
if err != nil && err != context.Canceled { if err != nil && err != context.Canceled {
c.printError("error: %v", err) c.printError("error: %v", err)
@@ -208,7 +209,7 @@ func (c *Comparer) printDir(ctx context.Context, mode string, stats *DiffStat, b
func (c *Comparer) collectDir(ctx context.Context, blobs restic.BlobSet, id restic.ID) error { func (c *Comparer) collectDir(ctx context.Context, blobs restic.BlobSet, id restic.ID) error {
debug.Log("print tree %v", id) debug.Log("print tree %v", id)
tree, err := restic.LoadTree(ctx, c.repo, id) tree, err := data.LoadTree(ctx, c.repo, id)
if err != nil { if err != nil {
return err return err
} }
@@ -220,7 +221,7 @@ func (c *Comparer) collectDir(ctx context.Context, blobs restic.BlobSet, id rest
addBlobs(blobs, node) addBlobs(blobs, node)
if node.Type == restic.NodeTypeDir { if node.Type == data.NodeTypeDir {
err := c.collectDir(ctx, blobs, *node.Subtree) err := c.collectDir(ctx, blobs, *node.Subtree)
if err != nil && err != context.Canceled { if err != nil && err != context.Canceled {
c.printError("error: %v", err) c.printError("error: %v", err)
@@ -231,15 +232,15 @@ func (c *Comparer) collectDir(ctx context.Context, blobs restic.BlobSet, id rest
return ctx.Err() return ctx.Err()
} }
func uniqueNodeNames(tree1, tree2 *restic.Tree) (tree1Nodes, tree2Nodes map[string]*restic.Node, uniqueNames []string) { func uniqueNodeNames(tree1, tree2 *data.Tree) (tree1Nodes, tree2Nodes map[string]*data.Node, uniqueNames []string) {
names := make(map[string]struct{}) names := make(map[string]struct{})
tree1Nodes = make(map[string]*restic.Node) tree1Nodes = make(map[string]*data.Node)
for _, node := range tree1.Nodes { for _, node := range tree1.Nodes {
tree1Nodes[node.Name] = node tree1Nodes[node.Name] = node
names[node.Name] = struct{}{} names[node.Name] = struct{}{}
} }
tree2Nodes = make(map[string]*restic.Node) tree2Nodes = make(map[string]*data.Node)
for _, node := range tree2.Nodes { for _, node := range tree2.Nodes {
tree2Nodes[node.Name] = node tree2Nodes[node.Name] = node
names[node.Name] = struct{}{} names[node.Name] = struct{}{}
@@ -256,12 +257,12 @@ func uniqueNodeNames(tree1, tree2 *restic.Tree) (tree1Nodes, tree2Nodes map[stri
func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, prefix string, id1, id2 restic.ID) error { func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, prefix string, id1, id2 restic.ID) error {
debug.Log("diffing %v to %v", id1, id2) debug.Log("diffing %v to %v", id1, id2)
tree1, err := restic.LoadTree(ctx, c.repo, id1) tree1, err := data.LoadTree(ctx, c.repo, id1)
if err != nil { if err != nil {
return err return err
} }
tree2, err := restic.LoadTree(ctx, c.repo, id2) tree2, err := data.LoadTree(ctx, c.repo, id2)
if err != nil { if err != nil {
return err return err
} }
@@ -288,12 +289,12 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
mod += "T" mod += "T"
} }
if node2.Type == restic.NodeTypeDir { if node2.Type == data.NodeTypeDir {
name += "/" name += "/"
} }
if node1.Type == restic.NodeTypeFile && if node1.Type == data.NodeTypeFile &&
node2.Type == restic.NodeTypeFile && node2.Type == data.NodeTypeFile &&
!reflect.DeepEqual(node1.Content, node2.Content) { !reflect.DeepEqual(node1.Content, node2.Content) {
mod += "M" mod += "M"
stats.ChangedFiles++ stats.ChangedFiles++
@@ -315,7 +316,7 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
c.printChange(NewChange(name, mod)) c.printChange(NewChange(name, mod))
} }
if node1.Type == restic.NodeTypeDir && node2.Type == restic.NodeTypeDir { if node1.Type == data.NodeTypeDir && node2.Type == data.NodeTypeDir {
var err error var err error
if (*node1.Subtree).Equal(*node2.Subtree) { if (*node1.Subtree).Equal(*node2.Subtree) {
err = c.collectDir(ctx, stats.BlobsCommon, *node1.Subtree) err = c.collectDir(ctx, stats.BlobsCommon, *node1.Subtree)
@@ -328,13 +329,13 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
} }
case t1 && !t2: case t1 && !t2:
prefix := path.Join(prefix, name) prefix := path.Join(prefix, name)
if node1.Type == restic.NodeTypeDir { if node1.Type == data.NodeTypeDir {
prefix += "/" prefix += "/"
} }
c.printChange(NewChange(prefix, "-")) c.printChange(NewChange(prefix, "-"))
stats.Removed.Add(node1) stats.Removed.Add(node1)
if node1.Type == restic.NodeTypeDir { if node1.Type == data.NodeTypeDir {
err := c.printDir(ctx, "-", &stats.Removed, stats.BlobsBefore, prefix, *node1.Subtree) err := c.printDir(ctx, "-", &stats.Removed, stats.BlobsBefore, prefix, *node1.Subtree)
if err != nil && err != context.Canceled { if err != nil && err != context.Canceled {
c.printError("error: %v", err) c.printError("error: %v", err)
@@ -342,13 +343,13 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
} }
case !t1 && t2: case !t1 && t2:
prefix := path.Join(prefix, name) prefix := path.Join(prefix, name)
if node2.Type == restic.NodeTypeDir { if node2.Type == data.NodeTypeDir {
prefix += "/" prefix += "/"
} }
c.printChange(NewChange(prefix, "+")) c.printChange(NewChange(prefix, "+"))
stats.Added.Add(node2) stats.Added.Add(node2)
if node2.Type == restic.NodeTypeDir { if node2.Type == data.NodeTypeDir {
err := c.printDir(ctx, "+", &stats.Added, stats.BlobsAfter, prefix, *node2.Subtree) err := c.printDir(ctx, "+", &stats.Added, stats.BlobsAfter, prefix, *node2.Subtree)
if err != nil && err != context.Canceled { if err != nil && err != context.Canceled {
c.printError("error: %v", err) c.printError("error: %v", err)
@@ -403,12 +404,12 @@ func runDiff(ctx context.Context, opts DiffOptions, gopts GlobalOptions, args []
return errors.Errorf("snapshot %v has nil tree", sn2.ID().Str()) return errors.Errorf("snapshot %v has nil tree", sn2.ID().Str())
} }
sn1.Tree, err = restic.FindTreeDirectory(ctx, repo, sn1.Tree, subfolder1) sn1.Tree, err = data.FindTreeDirectory(ctx, repo, sn1.Tree, subfolder1)
if err != nil { if err != nil {
return err return err
} }
sn2.Tree, err = restic.FindTreeDirectory(ctx, repo, sn2.Tree, subfolder2) sn2.Tree, err = data.FindTreeDirectory(ctx, repo, sn2.Tree, subfolder2)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -7,6 +7,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/dump" "github.com/restic/restic/internal/dump"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
@@ -57,7 +58,7 @@ Exit status is 12 if the password is incorrect.
// DumpOptions collects all options for the dump command. // DumpOptions collects all options for the dump command.
type DumpOptions struct { type DumpOptions struct {
restic.SnapshotFilter data.SnapshotFilter
Archive string Archive string
Target string Target string
} }
@@ -77,7 +78,7 @@ func splitPath(p string) []string {
return append(s, f) return append(s, f)
} }
func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoader, prefix string, pathComponents []string, d *dump.Dumper, canWriteArchiveFunc func() error) error { func printFromTree(ctx context.Context, tree *data.Tree, repo restic.BlobLoader, prefix string, pathComponents []string, d *dump.Dumper, canWriteArchiveFunc func() error) error {
// If we print / we need to assume that there are multiple nodes at that // If we print / we need to assume that there are multiple nodes at that
// level in the tree. // level in the tree.
if pathComponents[0] == "" { if pathComponents[0] == "" {
@@ -98,26 +99,26 @@ func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoade
// first item it finds and dump that according to the switch case below. // first item it finds and dump that according to the switch case below.
if node.Name == pathComponents[0] { if node.Name == pathComponents[0] {
switch { switch {
case l == 1 && node.Type == restic.NodeTypeFile: case l == 1 && node.Type == data.NodeTypeFile:
return d.WriteNode(ctx, node) return d.WriteNode(ctx, node)
case l > 1 && node.Type == restic.NodeTypeDir: case l > 1 && node.Type == data.NodeTypeDir:
subtree, err := restic.LoadTree(ctx, repo, *node.Subtree) subtree, err := data.LoadTree(ctx, repo, *node.Subtree)
if err != nil { if err != nil {
return errors.Wrapf(err, "cannot load subtree for %q", item) return errors.Wrapf(err, "cannot load subtree for %q", item)
} }
return printFromTree(ctx, subtree, repo, item, pathComponents[1:], d, canWriteArchiveFunc) return printFromTree(ctx, subtree, repo, item, pathComponents[1:], d, canWriteArchiveFunc)
case node.Type == restic.NodeTypeDir: case node.Type == data.NodeTypeDir:
if err := canWriteArchiveFunc(); err != nil { if err := canWriteArchiveFunc(); err != nil {
return err return err
} }
subtree, err := restic.LoadTree(ctx, repo, *node.Subtree) subtree, err := data.LoadTree(ctx, repo, *node.Subtree)
if err != nil { if err != nil {
return err return err
} }
return d.DumpTree(ctx, subtree, item) return d.DumpTree(ctx, subtree, item)
case l > 1: case l > 1:
return fmt.Errorf("%q should be a dir, but is a %q", item, node.Type) return fmt.Errorf("%q should be a dir, but is a %q", item, node.Type)
case node.Type != restic.NodeTypeFile: case node.Type != data.NodeTypeFile:
return fmt.Errorf("%q should be a file, but is a %q", item, node.Type) return fmt.Errorf("%q should be a file, but is a %q", item, node.Type)
} }
} }
@@ -151,7 +152,7 @@ func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args []
} }
defer unlock() defer unlock()
sn, subfolder, err := (&restic.SnapshotFilter{ sn, subfolder, err := (&data.SnapshotFilter{
Hosts: opts.Hosts, Hosts: opts.Hosts,
Paths: opts.Paths, Paths: opts.Paths,
Tags: opts.Tags, Tags: opts.Tags,
@@ -165,12 +166,12 @@ func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args []
return err return err
} }
sn.Tree, err = restic.FindTreeDirectory(ctx, repo, sn.Tree, subfolder) sn.Tree, err = data.FindTreeDirectory(ctx, repo, sn.Tree, subfolder)
if err != nil { if err != nil {
return err return err
} }
tree, err := restic.LoadTree(ctx, repo, *sn.Tree) tree, err := data.LoadTree(ctx, repo, *sn.Tree)
if err != nil { if err != nil {
return errors.Fatalf("loading tree for snapshot %q failed: %v", snapshotIDString, err) return errors.Fatalf("loading tree for snapshot %q failed: %v", snapshotIDString, err)
} }

View File

@@ -12,6 +12,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/filter"
@@ -70,7 +71,7 @@ type FindOptions struct {
ListLong bool ListLong bool
HumanReadable bool HumanReadable bool
Reverse bool Reverse bool
restic.SnapshotFilter data.SnapshotFilter
} }
func (opts *FindOptions) AddFlags(f *pflag.FlagSet) { func (opts *FindOptions) AddFlags(f *pflag.FlagSet) {
@@ -124,8 +125,8 @@ type statefulOutput struct {
HumanReadable bool HumanReadable bool
JSON bool JSON bool
inuse bool inuse bool
newsn *restic.Snapshot newsn *data.Snapshot
oldsn *restic.Snapshot oldsn *data.Snapshot
hits int hits int
printer interface { printer interface {
S(string, ...interface{}) S(string, ...interface{})
@@ -135,8 +136,8 @@ type statefulOutput struct {
stdout io.Writer stdout io.Writer
} }
func (s *statefulOutput) PrintPatternJSON(path string, node *restic.Node) { func (s *statefulOutput) PrintPatternJSON(path string, node *data.Node) {
type findNode restic.Node type findNode data.Node
b, err := json.Marshal(struct { b, err := json.Marshal(struct {
// Add these attributes // Add these attributes
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
@@ -179,7 +180,7 @@ func (s *statefulOutput) PrintPatternJSON(path string, node *restic.Node) {
s.hits++ s.hits++
} }
func (s *statefulOutput) PrintPatternNormal(path string, node *restic.Node) { func (s *statefulOutput) PrintPatternNormal(path string, node *data.Node) {
if s.newsn != s.oldsn { if s.newsn != s.oldsn {
if s.oldsn != nil { if s.oldsn != nil {
s.printer.P("") s.printer.P("")
@@ -190,7 +191,7 @@ func (s *statefulOutput) PrintPatternNormal(path string, node *restic.Node) {
s.printer.S(formatNode(path, node, s.ListLong, s.HumanReadable)) s.printer.S(formatNode(path, node, s.ListLong, s.HumanReadable))
} }
func (s *statefulOutput) PrintPattern(path string, node *restic.Node) { func (s *statefulOutput) PrintPattern(path string, node *data.Node) {
if s.JSON { if s.JSON {
s.PrintPatternJSON(path, node) s.PrintPatternJSON(path, node)
} else { } else {
@@ -198,7 +199,7 @@ func (s *statefulOutput) PrintPattern(path string, node *restic.Node) {
} }
} }
func (s *statefulOutput) PrintObjectJSON(kind, id, nodepath, treeID string, sn *restic.Snapshot) { func (s *statefulOutput) PrintObjectJSON(kind, id, nodepath, treeID string, sn *data.Snapshot) {
b, err := json.Marshal(struct { b, err := json.Marshal(struct {
// Add these attributes // Add these attributes
ObjectType string `json:"object_type"` ObjectType string `json:"object_type"`
@@ -230,7 +231,7 @@ func (s *statefulOutput) PrintObjectJSON(kind, id, nodepath, treeID string, sn *
s.hits++ s.hits++
} }
func (s *statefulOutput) PrintObjectNormal(kind, id, nodepath, treeID string, sn *restic.Snapshot) { func (s *statefulOutput) PrintObjectNormal(kind, id, nodepath, treeID string, sn *data.Snapshot) {
s.printer.S("Found %s %s", kind, id) s.printer.S("Found %s %s", kind, id)
if kind == "blob" { if kind == "blob" {
s.printer.S(" ... in file %s", nodepath) s.printer.S(" ... in file %s", nodepath)
@@ -241,7 +242,7 @@ func (s *statefulOutput) PrintObjectNormal(kind, id, nodepath, treeID string, sn
s.printer.S(" ... in snapshot %s (%s)", sn.ID().Str(), sn.Time.Local().Format(TimeFormat)) s.printer.S(" ... in snapshot %s (%s)", sn.ID().Str(), sn.Time.Local().Format(TimeFormat))
} }
func (s *statefulOutput) PrintObject(kind, id, nodepath, treeID string, sn *restic.Snapshot) { func (s *statefulOutput) PrintObject(kind, id, nodepath, treeID string, sn *data.Snapshot) {
if s.JSON { if s.JSON {
s.PrintObjectJSON(kind, id, nodepath, treeID, sn) s.PrintObjectJSON(kind, id, nodepath, treeID, sn)
} else { } else {
@@ -279,7 +280,7 @@ type Finder struct {
} }
} }
func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error { func (f *Finder) findInSnapshot(ctx context.Context, sn *data.Snapshot) error {
debug.Log("searching in snapshot %s\n for entries within [%s %s]", sn.ID(), f.pat.oldest, f.pat.newest) debug.Log("searching in snapshot %s\n for entries within [%s %s]", sn.ID(), f.pat.oldest, f.pat.newest)
if sn.Tree == nil { if sn.Tree == nil {
@@ -287,7 +288,7 @@ func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error
} }
f.out.newsn = sn f.out.newsn = sn
return walker.Walk(ctx, f.repo, *sn.Tree, walker.WalkVisitor{ProcessNode: func(parentTreeID restic.ID, nodepath string, node *restic.Node, err error) error { return walker.Walk(ctx, f.repo, *sn.Tree, walker.WalkVisitor{ProcessNode: func(parentTreeID restic.ID, nodepath string, node *data.Node, err error) error {
if err != nil { if err != nil {
debug.Log("Error loading tree %v: %v", parentTreeID, err) debug.Log("Error loading tree %v: %v", parentTreeID, err)
@@ -320,7 +321,7 @@ func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error
} }
var errIfNoMatch error var errIfNoMatch error
if node.Type == restic.NodeTypeDir { if node.Type == data.NodeTypeDir {
var childMayMatch bool var childMayMatch bool
for _, pat := range f.pat.pattern { for _, pat := range f.pat.pattern {
mayMatch, err := filter.ChildMatch(pat, normalizedNodepath) mayMatch, err := filter.ChildMatch(pat, normalizedNodepath)
@@ -378,7 +379,7 @@ func (f *Finder) findTree(treeID restic.ID, nodepath string) error {
return nil return nil
} }
func (f *Finder) findIDs(ctx context.Context, sn *restic.Snapshot) error { func (f *Finder) findIDs(ctx context.Context, sn *data.Snapshot) error {
debug.Log("searching IDs in snapshot %s", sn.ID()) debug.Log("searching IDs in snapshot %s", sn.ID())
if sn.Tree == nil { if sn.Tree == nil {
@@ -386,7 +387,7 @@ func (f *Finder) findIDs(ctx context.Context, sn *restic.Snapshot) error {
} }
f.out.newsn = sn f.out.newsn = sn
return walker.Walk(ctx, f.repo, *sn.Tree, walker.WalkVisitor{ProcessNode: func(parentTreeID restic.ID, nodepath string, node *restic.Node, err error) error { return walker.Walk(ctx, f.repo, *sn.Tree, walker.WalkVisitor{ProcessNode: func(parentTreeID restic.ID, nodepath string, node *data.Node, err error) error {
if err != nil { if err != nil {
debug.Log("Error loading tree %v: %v", parentTreeID, err) debug.Log("Error loading tree %v: %v", parentTreeID, err)
@@ -411,7 +412,7 @@ func (f *Finder) findIDs(ctx context.Context, sn *restic.Snapshot) error {
} }
} }
if node.Type == restic.NodeTypeFile && f.blobIDs != nil { if node.Type == data.NodeTypeFile && f.blobIDs != nil {
for _, id := range node.Content { for _, id := range node.Content {
if ctx.Err() != nil { if ctx.Err() != nil {
return ctx.Err() return ctx.Err()
@@ -654,7 +655,7 @@ func runFind(ctx context.Context, opts FindOptions, gopts GlobalOptions, args []
} }
} }
var filteredSnapshots []*restic.Snapshot var filteredSnapshots []*data.Snapshot
for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, &opts.SnapshotFilter, opts.Snapshots, printer) { for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, &opts.SnapshotFilter, opts.Snapshots, printer) {
filteredSnapshots = append(filteredSnapshots, sn) filteredSnapshots = append(filteredSnapshots, sn)
} }

View File

@@ -7,6 +7,7 @@ import (
"io" "io"
"strconv" "strconv"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@@ -102,21 +103,21 @@ type ForgetOptions struct {
Weekly ForgetPolicyCount Weekly ForgetPolicyCount
Monthly ForgetPolicyCount Monthly ForgetPolicyCount
Yearly ForgetPolicyCount Yearly ForgetPolicyCount
Within restic.Duration Within data.Duration
WithinHourly restic.Duration WithinHourly data.Duration
WithinDaily restic.Duration WithinDaily data.Duration
WithinWeekly restic.Duration WithinWeekly data.Duration
WithinMonthly restic.Duration WithinMonthly data.Duration
WithinYearly restic.Duration WithinYearly data.Duration
KeepTags restic.TagLists KeepTags data.TagLists
UnsafeAllowRemoveAll bool UnsafeAllowRemoveAll bool
restic.SnapshotFilter data.SnapshotFilter
Compact bool Compact bool
// Grouping // Grouping
GroupBy restic.SnapshotGroupByOptions GroupBy data.SnapshotGroupByOptions
DryRun bool DryRun bool
Prune bool Prune bool
} }
@@ -147,7 +148,7 @@ func (opts *ForgetOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, false) initMultiSnapshotFilter(f, &opts.SnapshotFilter, false)
f.BoolVarP(&opts.Compact, "compact", "c", false, "use compact output format") f.BoolVarP(&opts.Compact, "compact", "c", false, "use compact output format")
opts.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true} opts.GroupBy = data.SnapshotGroupByOptions{Host: true, Path: true}
f.VarP(&opts.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma (disable grouping with '')") f.VarP(&opts.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma (disable grouping with '')")
f.BoolVarP(&opts.DryRun, "dry-run", "n", false, "do not delete anything, just print what would be done") f.BoolVarP(&opts.DryRun, "dry-run", "n", false, "do not delete anything, just print what would be done")
f.BoolVar(&opts.Prune, "prune", false, "automatically run the 'prune' command if snapshots have been removed") f.BoolVar(&opts.Prune, "prune", false, "automatically run the 'prune' command if snapshots have been removed")
@@ -161,7 +162,7 @@ func verifyForgetOptions(opts *ForgetOptions) error {
return errors.Fatal("negative values other than -1 are not allowed for --keep-*") return errors.Fatal("negative values other than -1 are not allowed for --keep-*")
} }
for _, d := range []restic.Duration{opts.Within, opts.WithinHourly, opts.WithinDaily, for _, d := range []data.Duration{opts.Within, opts.WithinHourly, opts.WithinDaily,
opts.WithinMonthly, opts.WithinWeekly, opts.WithinYearly} { opts.WithinMonthly, opts.WithinWeekly, opts.WithinYearly} {
if d.Hours < 0 || d.Days < 0 || d.Months < 0 || d.Years < 0 { if d.Hours < 0 || d.Days < 0 || d.Months < 0 || d.Years < 0 {
return errors.Fatal("durations containing negative values are not allowed for --keep-within*") return errors.Fatal("durations containing negative values are not allowed for --keep-within*")
@@ -193,7 +194,7 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption
} }
defer unlock() defer unlock()
var snapshots restic.Snapshots var snapshots data.Snapshots
removeSnIDs := restic.NewIDSet() removeSnIDs := restic.NewIDSet()
for sn := range FindFilteredSnapshots(ctx, repo, repo, &opts.SnapshotFilter, args, printer) { for sn := range FindFilteredSnapshots(ctx, repo, repo, &opts.SnapshotFilter, args, printer) {
@@ -211,12 +212,12 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption
removeSnIDs.Insert(*sn.ID()) removeSnIDs.Insert(*sn.ID())
} }
} else { } else {
snapshotGroups, _, err := restic.GroupSnapshots(snapshots, opts.GroupBy) snapshotGroups, _, err := data.GroupSnapshots(snapshots, opts.GroupBy)
if err != nil { if err != nil {
return err return err
} }
policy := restic.ExpirePolicy{ policy := data.ExpirePolicy{
Last: int(opts.Last), Last: int(opts.Last),
Hourly: int(opts.Hourly), Hourly: int(opts.Hourly),
Daily: int(opts.Daily), Daily: int(opts.Daily),
@@ -257,7 +258,7 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption
} }
} }
var key restic.SnapshotGroupKey var key data.SnapshotGroupKey
if json.Unmarshal([]byte(k), &key) != nil { if json.Unmarshal([]byte(k), &key) != nil {
return err return err
} }
@@ -267,7 +268,7 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption
fg.Host = key.Hostname fg.Host = key.Hostname
fg.Paths = key.Paths fg.Paths = key.Paths
keep, remove, reasons := restic.ApplyPolicy(snapshotGroup, policy) keep, remove, reasons := data.ApplyPolicy(snapshotGroup, policy)
if !policy.Empty() && len(keep) == 0 { if !policy.Empty() && len(keep) == 0 {
return fmt.Errorf("refusing to delete last snapshot of snapshot group \"%v\"", key.String()) return fmt.Errorf("refusing to delete last snapshot of snapshot group \"%v\"", key.String())
@@ -361,7 +362,7 @@ type ForgetGroup struct {
Reasons []KeepReason `json:"reasons"` Reasons []KeepReason `json:"reasons"`
} }
func asJSONSnapshots(list restic.Snapshots) []Snapshot { func asJSONSnapshots(list data.Snapshots) []Snapshot {
var resultList []Snapshot var resultList []Snapshot
for _, sn := range list { for _, sn := range list {
k := Snapshot{ k := Snapshot{
@@ -380,7 +381,7 @@ type KeepReason struct {
Matches []string `json:"matches"` Matches []string `json:"matches"`
} }
func asJSONKeeps(list []restic.KeepReason) []KeepReason { func asJSONKeeps(list []data.KeepReason) []KeepReason {
var resultList []KeepReason var resultList []KeepReason
for _, keep := range list { for _, keep := range list {
k := KeepReason{ k := KeepReason{

View File

@@ -6,7 +6,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/data"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
@@ -38,8 +38,8 @@ func TestRunForgetSafetyNet(t *testing.T) {
// --keep-tags invalid // --keep-tags invalid
err := testRunForgetMayFail(t, env.gopts, ForgetOptions{ err := testRunForgetMayFail(t, env.gopts, ForgetOptions{
KeepTags: restic.TagLists{restic.TagList{"invalid"}}, KeepTags: data.TagLists{data.TagList{"invalid"}},
GroupBy: restic.SnapshotGroupByOptions{Host: true, Path: true}, GroupBy: data.SnapshotGroupByOptions{Host: true, Path: true},
}) })
rtest.Assert(t, strings.Contains(err.Error(), `refusing to delete last snapshot of snapshot group "host example, path`), "wrong error message got %v", err) rtest.Assert(t, strings.Contains(err.Error(), `refusing to delete last snapshot of snapshot group "host example, path`), "wrong error message got %v", err)
@@ -56,8 +56,8 @@ func TestRunForgetSafetyNet(t *testing.T) {
// `forget --host example --unsafe-allow-remove-all` should work // `forget --host example --unsafe-allow-remove-all` should work
testRunForget(t, env.gopts, ForgetOptions{ testRunForget(t, env.gopts, ForgetOptions{
UnsafeAllowRemoveAll: true, UnsafeAllowRemoveAll: true,
GroupBy: restic.SnapshotGroupByOptions{Host: true, Path: true}, GroupBy: data.SnapshotGroupByOptions{Host: true, Path: true},
SnapshotFilter: restic.SnapshotFilter{ SnapshotFilter: data.SnapshotFilter{
Hosts: []string{opts.Host}, Hosts: []string{opts.Host},
}, },
}) })

View File

@@ -3,7 +3,7 @@ package main
import ( import (
"testing" "testing"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/data"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
@@ -69,18 +69,18 @@ func TestForgetOptionValues(t *testing.T) {
{ForgetOptions{Weekly: -2}, negValErrorMsg}, {ForgetOptions{Weekly: -2}, negValErrorMsg},
{ForgetOptions{Monthly: -2}, negValErrorMsg}, {ForgetOptions{Monthly: -2}, negValErrorMsg},
{ForgetOptions{Yearly: -2}, negValErrorMsg}, {ForgetOptions{Yearly: -2}, negValErrorMsg},
{ForgetOptions{Within: restic.ParseDurationOrPanic("1y2m3d3h")}, ""}, {ForgetOptions{Within: data.ParseDurationOrPanic("1y2m3d3h")}, ""},
{ForgetOptions{WithinHourly: restic.ParseDurationOrPanic("1y2m3d3h")}, ""}, {ForgetOptions{WithinHourly: data.ParseDurationOrPanic("1y2m3d3h")}, ""},
{ForgetOptions{WithinDaily: restic.ParseDurationOrPanic("1y2m3d3h")}, ""}, {ForgetOptions{WithinDaily: data.ParseDurationOrPanic("1y2m3d3h")}, ""},
{ForgetOptions{WithinWeekly: restic.ParseDurationOrPanic("1y2m3d3h")}, ""}, {ForgetOptions{WithinWeekly: data.ParseDurationOrPanic("1y2m3d3h")}, ""},
{ForgetOptions{WithinMonthly: restic.ParseDurationOrPanic("2y4m6d8h")}, ""}, {ForgetOptions{WithinMonthly: data.ParseDurationOrPanic("2y4m6d8h")}, ""},
{ForgetOptions{WithinYearly: restic.ParseDurationOrPanic("2y4m6d8h")}, ""}, {ForgetOptions{WithinYearly: data.ParseDurationOrPanic("2y4m6d8h")}, ""},
{ForgetOptions{Within: restic.ParseDurationOrPanic("-1y2m3d3h")}, negDurationValErrorMsg}, {ForgetOptions{Within: data.ParseDurationOrPanic("-1y2m3d3h")}, negDurationValErrorMsg},
{ForgetOptions{WithinHourly: restic.ParseDurationOrPanic("1y-2m3d3h")}, negDurationValErrorMsg}, {ForgetOptions{WithinHourly: data.ParseDurationOrPanic("1y-2m3d3h")}, negDurationValErrorMsg},
{ForgetOptions{WithinDaily: restic.ParseDurationOrPanic("1y2m-3d3h")}, negDurationValErrorMsg}, {ForgetOptions{WithinDaily: data.ParseDurationOrPanic("1y2m-3d3h")}, negDurationValErrorMsg},
{ForgetOptions{WithinWeekly: restic.ParseDurationOrPanic("1y2m3d-3h")}, negDurationValErrorMsg}, {ForgetOptions{WithinWeekly: data.ParseDurationOrPanic("1y2m3d-3h")}, negDurationValErrorMsg},
{ForgetOptions{WithinMonthly: restic.ParseDurationOrPanic("-2y4m6d8h")}, negDurationValErrorMsg}, {ForgetOptions{WithinMonthly: data.ParseDurationOrPanic("-2y4m6d8h")}, negDurationValErrorMsg},
{ForgetOptions{WithinYearly: restic.ParseDurationOrPanic("2y-4m6d8h")}, negDurationValErrorMsg}, {ForgetOptions{WithinYearly: data.ParseDurationOrPanic("2y-4m6d8h")}, negDurationValErrorMsg},
} }
for _, testCase := range testCases { for _, testCase := range testCases {

View File

@@ -15,6 +15,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
@@ -70,7 +71,7 @@ Exit status is 12 if the password is incorrect.
// LsOptions collects all options for the ls command. // LsOptions collects all options for the ls command.
type LsOptions struct { type LsOptions struct {
ListLong bool ListLong bool
restic.SnapshotFilter data.SnapshotFilter
Recursive bool Recursive bool
HumanReadable bool HumanReadable bool
Ncdu bool Ncdu bool
@@ -89,8 +90,8 @@ func (opts *LsOptions) AddFlags(f *pflag.FlagSet) {
} }
type lsPrinter interface { type lsPrinter interface {
Snapshot(sn *restic.Snapshot) error Snapshot(sn *data.Snapshot) error
Node(path string, node *restic.Node, isPrefixDirectory bool) error Node(path string, node *data.Node, isPrefixDirectory bool) error
LeaveDir(path string) error LeaveDir(path string) error
Close() error Close() error
} }
@@ -99,9 +100,9 @@ type jsonLsPrinter struct {
enc *json.Encoder enc *json.Encoder
} }
func (p *jsonLsPrinter) Snapshot(sn *restic.Snapshot) error { func (p *jsonLsPrinter) Snapshot(sn *data.Snapshot) error {
type lsSnapshot struct { type lsSnapshot struct {
*restic.Snapshot *data.Snapshot
ID *restic.ID `json:"id"` ID *restic.ID `json:"id"`
ShortID string `json:"short_id"` // deprecated ShortID string `json:"short_id"` // deprecated
MessageType string `json:"message_type"` // "snapshot" MessageType string `json:"message_type"` // "snapshot"
@@ -118,14 +119,14 @@ func (p *jsonLsPrinter) Snapshot(sn *restic.Snapshot) error {
} }
// Node formats node in our custom JSON format, followed by a newline. // Node formats node in our custom JSON format, followed by a newline.
func (p *jsonLsPrinter) Node(path string, node *restic.Node, isPrefixDirectory bool) error { func (p *jsonLsPrinter) Node(path string, node *data.Node, isPrefixDirectory bool) error {
if isPrefixDirectory { if isPrefixDirectory {
return nil return nil
} }
return lsNodeJSON(p.enc, path, node) return lsNodeJSON(p.enc, path, node)
} }
func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error { func lsNodeJSON(enc *json.Encoder, path string, node *data.Node) error {
n := &struct { n := &struct {
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
@@ -161,7 +162,7 @@ func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
} }
// Always print size for regular files, even when empty, // Always print size for regular files, even when empty,
// but never for other types. // but never for other types.
if node.Type == restic.NodeTypeFile { if node.Type == data.NodeTypeFile {
n.Size = &n.size n.Size = &n.size
} }
@@ -179,7 +180,7 @@ type ncduLsPrinter struct {
// Snapshot prints a restic snapshot in Ncdu save format. // Snapshot prints a restic snapshot in Ncdu save format.
// It opens the JSON list. Nodes are added with lsNodeNcdu and the list is closed by lsCloseNcdu. // It opens the JSON list. Nodes are added with lsNodeNcdu and the list is closed by lsCloseNcdu.
// Format documentation: https://dev.yorhel.nl/ncdu/jsonfmt // Format documentation: https://dev.yorhel.nl/ncdu/jsonfmt
func (p *ncduLsPrinter) Snapshot(sn *restic.Snapshot) error { func (p *ncduLsPrinter) Snapshot(sn *data.Snapshot) error {
const NcduMajorVer = 1 const NcduMajorVer = 1
const NcduMinorVer = 2 const NcduMinorVer = 2
@@ -192,7 +193,7 @@ func (p *ncduLsPrinter) Snapshot(sn *restic.Snapshot) error {
return err return err
} }
func lsNcduNode(_ string, node *restic.Node) ([]byte, error) { func lsNcduNode(_ string, node *data.Node) ([]byte, error) {
type NcduNode struct { type NcduNode struct {
Name string `json:"name"` Name string `json:"name"`
Asize uint64 `json:"asize"` Asize uint64 `json:"asize"`
@@ -217,7 +218,7 @@ func lsNcduNode(_ string, node *restic.Node) ([]byte, error) {
Dev: node.DeviceID, Dev: node.DeviceID,
Ino: node.Inode, Ino: node.Inode,
NLink: node.Links, NLink: node.Links,
NotReg: node.Type != restic.NodeTypeDir && node.Type != restic.NodeTypeFile, NotReg: node.Type != data.NodeTypeDir && node.Type != data.NodeTypeFile,
UID: node.UID, UID: node.UID,
GID: node.GID, GID: node.GID,
Mode: uint16(node.Mode & os.ModePerm), Mode: uint16(node.Mode & os.ModePerm),
@@ -241,13 +242,13 @@ func lsNcduNode(_ string, node *restic.Node) ([]byte, error) {
return json.Marshal(outNode) return json.Marshal(outNode)
} }
func (p *ncduLsPrinter) Node(path string, node *restic.Node, _ bool) error { func (p *ncduLsPrinter) Node(path string, node *data.Node, _ bool) error {
out, err := lsNcduNode(path, node) out, err := lsNcduNode(path, node)
if err != nil { if err != nil {
return err return err
} }
if node.Type == restic.NodeTypeDir { if node.Type == data.NodeTypeDir {
_, err = fmt.Fprintf(p.out, ",\n%s[\n%s%s", strings.Repeat(" ", p.depth), strings.Repeat(" ", p.depth+1), string(out)) _, err = fmt.Fprintf(p.out, ",\n%s[\n%s%s", strings.Repeat(" ", p.depth), strings.Repeat(" ", p.depth+1), string(out))
p.depth++ p.depth++
} else { } else {
@@ -277,11 +278,11 @@ type textLsPrinter struct {
} }
} }
func (p *textLsPrinter) Snapshot(sn *restic.Snapshot) error { func (p *textLsPrinter) Snapshot(sn *data.Snapshot) error {
p.termPrinter.P("%v filtered by %v:", sn, p.dirs) p.termPrinter.P("%v filtered by %v:", sn, p.dirs)
return nil return nil
} }
func (p *textLsPrinter) Node(path string, node *restic.Node, isPrefixDirectory bool) error { func (p *textLsPrinter) Node(path string, node *data.Node, isPrefixDirectory bool) error {
if !isPrefixDirectory { if !isPrefixDirectory {
p.termPrinter.S("%s", formatNode(path, node, p.ListLong, p.HumanReadable)) p.termPrinter.S("%s", formatNode(path, node, p.ListLong, p.HumanReadable))
} }
@@ -298,7 +299,7 @@ func (p *textLsPrinter) Close() error {
// for ls -l output sorting // for ls -l output sorting
type toSortOutput struct { type toSortOutput struct {
nodepath string nodepath string
node *restic.Node node *data.Node
} }
func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
@@ -403,7 +404,7 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
} }
} }
sn, subfolder, err := (&restic.SnapshotFilter{ sn, subfolder, err := (&data.SnapshotFilter{
Hosts: opts.Hosts, Hosts: opts.Hosts,
Paths: opts.Paths, Paths: opts.Paths,
Tags: opts.Tags, Tags: opts.Tags,
@@ -412,7 +413,7 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
return err return err
} }
sn.Tree, err = restic.FindTreeDirectory(ctx, repo, sn.Tree, subfolder) sn.Tree, err = data.FindTreeDirectory(ctx, repo, sn.Tree, subfolder)
if err != nil { if err != nil {
return err return err
} }
@@ -421,7 +422,7 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
return err return err
} }
processNode := func(_ restic.ID, nodepath string, node *restic.Node, err error) error { processNode := func(_ restic.ID, nodepath string, node *data.Node, err error) error {
if err != nil { if err != nil {
return err return err
} }
@@ -456,7 +457,7 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
// otherwise, signal the walker to not walk recursively into any // otherwise, signal the walker to not walk recursively into any
// subdirs // subdirs
if node.Type == restic.NodeTypeDir { if node.Type == data.NodeTypeDir {
// immediately generate leaveDir if the directory is skipped // immediately generate leaveDir if the directory is skipped
if printedDir { if printedDir {
if err := printer.LeaveDir(nodepath); err != nil { if err := printer.LeaveDir(nodepath); err != nil {
@@ -493,10 +494,10 @@ type sortedPrinter struct {
reverse bool reverse bool
} }
func (p *sortedPrinter) Snapshot(sn *restic.Snapshot) error { func (p *sortedPrinter) Snapshot(sn *data.Snapshot) error {
return p.printer.Snapshot(sn) return p.printer.Snapshot(sn)
} }
func (p *sortedPrinter) Node(path string, node *restic.Node, isPrefixDirectory bool) error { func (p *sortedPrinter) Node(path string, node *data.Node, isPrefixDirectory bool) error {
if !isPrefixDirectory { if !isPrefixDirectory {
p.collector = append(p.collector, toSortOutput{path, node}) p.collector = append(p.collector, toSortOutput{path, node})
} }

View File

@@ -8,6 +8,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
@@ -129,7 +130,7 @@ func TestRunLsJson(t *testing.T) {
// partial copy of snapshot structure from cmd_ls // partial copy of snapshot structure from cmd_ls
type lsSnapshot struct { type lsSnapshot struct {
*restic.Snapshot *data.Snapshot
ID *restic.ID `json:"id"` ID *restic.ID `json:"id"`
ShortID string `json:"short_id"` // deprecated ShortID string `json:"short_id"` // deprecated
MessageType string `json:"message_type"` // "snapshot" MessageType string `json:"message_type"` // "snapshot"

View File

@@ -7,13 +7,13 @@ import (
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/data"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
type lsTestNode struct { type lsTestNode struct {
path string path string
restic.Node data.Node
} }
var lsTestNodes = []lsTestNode{ var lsTestNodes = []lsTestNode{
@@ -21,9 +21,9 @@ var lsTestNodes = []lsTestNode{
// Permissions, by convention is "-" per mode bit // Permissions, by convention is "-" per mode bit
{ {
path: "/bar/baz", path: "/bar/baz",
Node: restic.Node{ Node: data.Node{
Name: "baz", Name: "baz",
Type: restic.NodeTypeFile, Type: data.NodeTypeFile,
Size: 12345, Size: 12345,
UID: 10000000, UID: 10000000,
GID: 20000000, GID: 20000000,
@@ -37,9 +37,9 @@ var lsTestNodes = []lsTestNode{
// Even empty files get an explicit size. // Even empty files get an explicit size.
{ {
path: "/foo/empty", path: "/foo/empty",
Node: restic.Node{ Node: data.Node{
Name: "empty", Name: "empty",
Type: restic.NodeTypeFile, Type: data.NodeTypeFile,
Size: 0, Size: 0,
UID: 1001, UID: 1001,
GID: 1001, GID: 1001,
@@ -54,9 +54,9 @@ var lsTestNodes = []lsTestNode{
// Mode is printed in decimal, including the type bits. // Mode is printed in decimal, including the type bits.
{ {
path: "/foo/link", path: "/foo/link",
Node: restic.Node{ Node: data.Node{
Name: "link", Name: "link",
Type: restic.NodeTypeSymlink, Type: data.NodeTypeSymlink,
Mode: os.ModeSymlink | 0777, Mode: os.ModeSymlink | 0777,
LinkTarget: "not printed", LinkTarget: "not printed",
}, },
@@ -64,9 +64,9 @@ var lsTestNodes = []lsTestNode{
{ {
path: "/some/directory", path: "/some/directory",
Node: restic.Node{ Node: data.Node{
Name: "directory", Name: "directory",
Type: restic.NodeTypeDir, Type: data.NodeTypeDir,
Mode: os.ModeDir | 0755, Mode: os.ModeDir | 0755,
ModTime: time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC), ModTime: time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC),
AccessTime: time.Date(2021, 2, 3, 4, 5, 6, 7, time.UTC), AccessTime: time.Date(2021, 2, 3, 4, 5, 6, 7, time.UTC),
@@ -77,9 +77,9 @@ var lsTestNodes = []lsTestNode{
// Test encoding of setuid/setgid/sticky bit // Test encoding of setuid/setgid/sticky bit
{ {
path: "/some/sticky", path: "/some/sticky",
Node: restic.Node{ Node: data.Node{
Name: "sticky", Name: "sticky",
Type: restic.NodeTypeDir, Type: data.NodeTypeDir,
Mode: os.ModeDir | 0755 | os.ModeSetuid | os.ModeSetgid | os.ModeSticky, Mode: os.ModeDir | 0755 | os.ModeSetuid | os.ModeSetgid | os.ModeSticky,
}, },
}, },
@@ -134,24 +134,24 @@ func TestLsNcdu(t *testing.T) {
} }
modTime := time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC) modTime := time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC)
rtest.OK(t, printer.Snapshot(&restic.Snapshot{ rtest.OK(t, printer.Snapshot(&data.Snapshot{
Hostname: "host", Hostname: "host",
Paths: []string{"/example"}, Paths: []string{"/example"},
})) }))
rtest.OK(t, printer.Node("/directory", &restic.Node{ rtest.OK(t, printer.Node("/directory", &data.Node{
Type: restic.NodeTypeDir, Type: data.NodeTypeDir,
Name: "directory", Name: "directory",
ModTime: modTime, ModTime: modTime,
}, false)) }, false))
rtest.OK(t, printer.Node("/directory/data", &restic.Node{ rtest.OK(t, printer.Node("/directory/data", &data.Node{
Type: restic.NodeTypeFile, Type: data.NodeTypeFile,
Name: "data", Name: "data",
Size: 42, Size: 42,
ModTime: modTime, ModTime: modTime,
}, false)) }, false))
rtest.OK(t, printer.LeaveDir("/directory")) rtest.OK(t, printer.LeaveDir("/directory"))
rtest.OK(t, printer.Node("/file", &restic.Node{ rtest.OK(t, printer.Node("/file", &data.Node{
Type: restic.NodeTypeFile, Type: data.NodeTypeFile,
Name: "file", Name: "file",
Size: 12345, Size: 12345,
ModTime: modTime, ModTime: modTime,

View File

@@ -13,9 +13,9 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/fuse" "github.com/restic/restic/internal/fuse"
@@ -95,7 +95,7 @@ type MountOptions struct {
OwnerRoot bool OwnerRoot bool
AllowOther bool AllowOther bool
NoDefaultPermissions bool NoDefaultPermissions bool
restic.SnapshotFilter data.SnapshotFilter
TimeTemplate string TimeTemplate string
PathTemplates []string PathTemplates []string
} }

View File

@@ -13,6 +13,7 @@ import (
"time" "time"
systemFuse "github.com/anacrolix/fuse" systemFuse "github.com/anacrolix/fuse"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
@@ -137,7 +138,7 @@ func checkSnapshots(t testing.TB, gopts GlobalOptions, mountpoint string, snapsh
defer unlock() defer unlock()
for _, id := range snapshotIDs { for _, id := range snapshotIDs {
snapshot, err := restic.LoadSnapshot(ctx, repo, id) snapshot, err := data.LoadSnapshot(ctx, repo, id)
rtest.OK(t, err) rtest.OK(t, err)
ts := snapshot.Time.Format(time.RFC3339) ts := snapshot.Time.Format(time.RFC3339)

View File

@@ -7,6 +7,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
@@ -278,8 +279,8 @@ func printPruneStats(printer progress.Printer, stats repository.PruneStats) erro
func getUsedBlobs(ctx context.Context, repo restic.Repository, usedBlobs restic.FindBlobSet, ignoreSnapshots restic.IDSet, printer progress.Printer) error { func getUsedBlobs(ctx context.Context, repo restic.Repository, usedBlobs restic.FindBlobSet, ignoreSnapshots restic.IDSet, printer progress.Printer) error {
var snapshotTrees restic.IDs var snapshotTrees restic.IDs
printer.P("loading all snapshots...\n") printer.P("loading all snapshots...\n")
err := restic.ForAllSnapshots(ctx, repo, repo, ignoreSnapshots, err := data.ForAllSnapshots(ctx, repo, repo, ignoreSnapshots,
func(id restic.ID, sn *restic.Snapshot, err error) error { func(id restic.ID, sn *data.Snapshot, err error) error {
if err != nil { if err != nil {
debug.Log("failed to load snapshot %v (error %v)", id, err) debug.Log("failed to load snapshot %v (error %v)", id, err)
return err return err
@@ -298,5 +299,5 @@ func getUsedBlobs(ctx context.Context, repo restic.Repository, usedBlobs restic.
bar.SetMax(uint64(len(snapshotTrees))) bar.SetMax(uint64(len(snapshotTrees)))
defer bar.Done() defer bar.Done()
return restic.FindUsedBlobs(ctx, repo, snapshotTrees, usedBlobs, bar) return data.FindUsedBlobs(ctx, repo, snapshotTrees, usedBlobs, bar)
} }

View File

@@ -5,6 +5,7 @@ import (
"os" "os"
"time" "time"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
@@ -87,7 +88,7 @@ func runRecover(ctx context.Context, gopts GlobalOptions, term ui.Terminal) erro
bar := printer.NewCounter("trees loaded") bar := printer.NewCounter("trees loaded")
bar.SetMax(uint64(len(trees))) bar.SetMax(uint64(len(trees)))
for id := range trees { for id := range trees {
tree, err := restic.LoadTree(ctx, repo, id) tree, err := data.LoadTree(ctx, repo, id)
if ctx.Err() != nil { if ctx.Err() != nil {
return ctx.Err() return ctx.Err()
} }
@@ -97,7 +98,7 @@ func runRecover(ctx context.Context, gopts GlobalOptions, term ui.Terminal) erro
} }
for _, node := range tree.Nodes { for _, node := range tree.Nodes {
if node.Type == restic.NodeTypeDir && node.Subtree != nil { if node.Type == data.NodeTypeDir && node.Subtree != nil {
trees[*node.Subtree] = true trees[*node.Subtree] = true
} }
} }
@@ -106,7 +107,7 @@ func runRecover(ctx context.Context, gopts GlobalOptions, term ui.Terminal) erro
bar.Done() bar.Done()
printer.P("load snapshots\n") printer.P("load snapshots\n")
err = restic.ForAllSnapshots(ctx, snapshotLister, repo, nil, func(_ restic.ID, sn *restic.Snapshot, _ error) error { err = data.ForAllSnapshots(ctx, snapshotLister, repo, nil, func(_ restic.ID, sn *data.Snapshot, _ error) error {
trees[*sn.Tree] = true trees[*sn.Tree] = true
return nil return nil
}) })
@@ -133,11 +134,11 @@ func runRecover(ctx context.Context, gopts GlobalOptions, term ui.Terminal) erro
return ctx.Err() return ctx.Err()
} }
tree := restic.NewTree(len(roots)) tree := data.NewTree(len(roots))
for id := range roots { for id := range roots {
var subtreeID = id var subtreeID = id
node := restic.Node{ node := data.Node{
Type: restic.NodeTypeDir, Type: data.NodeTypeDir,
Name: id.Str(), Name: id.Str(),
Mode: 0755, Mode: 0755,
Subtree: &subtreeID, Subtree: &subtreeID,
@@ -157,7 +158,7 @@ func runRecover(ctx context.Context, gopts GlobalOptions, term ui.Terminal) erro
var treeID restic.ID var treeID restic.ID
wg.Go(func() error { wg.Go(func() error {
var err error var err error
treeID, err = restic.SaveTree(wgCtx, repo, tree) treeID, err = data.SaveTree(wgCtx, repo, tree)
if err != nil { if err != nil {
return errors.Fatalf("unable to save new tree to the repository: %v", err) return errors.Fatalf("unable to save new tree to the repository: %v", err)
} }
@@ -178,14 +179,14 @@ func runRecover(ctx context.Context, gopts GlobalOptions, term ui.Terminal) erro
} }
func createSnapshot(ctx context.Context, printer progress.Printer, name, hostname string, tags []string, repo restic.SaverUnpacked[restic.WriteableFileType], tree *restic.ID) error { func createSnapshot(ctx context.Context, printer progress.Printer, name, hostname string, tags []string, repo restic.SaverUnpacked[restic.WriteableFileType], tree *restic.ID) error {
sn, err := restic.NewSnapshot([]string{name}, tags, hostname, time.Now()) sn, err := data.NewSnapshot([]string{name}, tags, hostname, time.Now())
if err != nil { if err != nil {
return errors.Fatalf("unable to save snapshot: %v", err) return errors.Fatalf("unable to save snapshot: %v", err)
} }
sn.Tree = tree sn.Tree = tree
id, err := restic.SaveSnapshot(ctx, repo, sn) id, err := data.SaveSnapshot(ctx, repo, sn)
if err != nil { if err != nil {
return errors.Fatalf("unable to save snapshot: %v", err) return errors.Fatalf("unable to save snapshot: %v", err)
} }

View File

@@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@@ -63,7 +64,7 @@ type RepairOptions struct {
DryRun bool DryRun bool
Forget bool Forget bool
restic.SnapshotFilter data.SnapshotFilter
} }
func (opts *RepairOptions) AddFlags(f *pflag.FlagSet) { func (opts *RepairOptions) AddFlags(f *pflag.FlagSet) {
@@ -96,12 +97,12 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt
// - trees which cannot be loaded (-> the tree contents will be removed) // - trees which cannot be loaded (-> the tree contents will be removed)
// - files whose contents are not fully available (-> file will be modified) // - files whose contents are not fully available (-> file will be modified)
rewriter := walker.NewTreeRewriter(walker.RewriteOpts{ rewriter := walker.NewTreeRewriter(walker.RewriteOpts{
RewriteNode: func(node *restic.Node, path string) *restic.Node { RewriteNode: func(node *data.Node, path string) *data.Node {
if node.Type == restic.NodeTypeIrregular || node.Type == restic.NodeTypeInvalid { if node.Type == data.NodeTypeIrregular || node.Type == data.NodeTypeInvalid {
printer.P(" file %q: removed node with invalid type %q", path, node.Type) printer.P(" file %q: removed node with invalid type %q", path, node.Type)
return nil return nil
} }
if node.Type != restic.NodeTypeFile { if node.Type != data.NodeTypeFile {
return node return node
} }
@@ -135,7 +136,7 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt
} }
// If a subtree fails to load, remove it // If a subtree fails to load, remove it
printer.P(" dir %q: replaced with empty directory", path) printer.P(" dir %q: replaced with empty directory", path)
emptyID, err := restic.SaveTree(ctx, repo, &restic.Tree{}) emptyID, err := data.SaveTree(ctx, repo, &data.Tree{})
if err != nil { if err != nil {
return restic.ID{}, err return restic.ID{}, err
} }
@@ -148,7 +149,7 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt
for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, &opts.SnapshotFilter, args, printer) { for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, &opts.SnapshotFilter, args, printer) {
printer.P("\n%v", sn) printer.P("\n%v", sn)
changed, err := filterAndReplaceSnapshot(ctx, repo, sn, changed, err := filterAndReplaceSnapshot(ctx, repo, sn,
func(ctx context.Context, sn *restic.Snapshot) (restic.ID, *restic.SnapshotSummary, error) { func(ctx context.Context, sn *data.Snapshot) (restic.ID, *data.SnapshotSummary, error) {
id, err := rewriter.RewriteTree(ctx, repo, "/", *sn.Tree) id, err := rewriter.RewriteTree(ctx, repo, "/", *sn.Tree)
return id, nil, err return id, nil, err
}, opts.DryRun, opts.Forget, nil, "repaired", printer) }, opts.DryRun, opts.Forget, nil, "repaired", printer)

View File

@@ -5,10 +5,10 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/restorer" "github.com/restic/restic/internal/restorer"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/progress"
@@ -59,7 +59,7 @@ type RestoreOptions struct {
filter.ExcludePatternOptions filter.ExcludePatternOptions
filter.IncludePatternOptions filter.IncludePatternOptions
Target string Target string
restic.SnapshotFilter data.SnapshotFilter
DryRun bool DryRun bool
Sparse bool Sparse bool
Verify bool Verify bool
@@ -142,7 +142,7 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
} }
defer unlock() defer unlock()
sn, subfolder, err := (&restic.SnapshotFilter{ sn, subfolder, err := (&data.SnapshotFilter{
Hosts: opts.Hosts, Hosts: opts.Hosts,
Paths: opts.Paths, Paths: opts.Paths,
Tags: opts.Tags, Tags: opts.Tags,
@@ -156,7 +156,7 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
return err return err
} }
sn.Tree, err = restic.FindTreeDirectory(ctx, repo, sn.Tree, subfolder) sn.Tree, err = data.FindTreeDirectory(ctx, repo, sn.Tree, subfolder)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -11,6 +11,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
@@ -37,7 +38,7 @@ func testRunRestoreAssumeFailure(t testing.TB, snapshotID string, opts RestoreOp
func testRunRestoreLatest(t testing.TB, gopts GlobalOptions, dir string, paths []string, hosts []string) { func testRunRestoreLatest(t testing.TB, gopts GlobalOptions, dir string, paths []string, hosts []string) {
opts := RestoreOptions{ opts := RestoreOptions{
Target: dir, Target: dir,
SnapshotFilter: restic.SnapshotFilter{ SnapshotFilter: data.SnapshotFilter{
Hosts: hosts, Hosts: hosts,
Paths: paths, Paths: paths,
}, },

View File

@@ -8,6 +8,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/filter"
@@ -105,7 +106,7 @@ type RewriteOptions struct {
SnapshotSummary bool SnapshotSummary bool
Metadata snapshotMetadataArgs Metadata snapshotMetadataArgs
restic.SnapshotFilter data.SnapshotFilter
filter.ExcludePatternOptions filter.ExcludePatternOptions
} }
@@ -122,9 +123,9 @@ func (opts *RewriteOptions) AddFlags(f *pflag.FlagSet) {
// rewriteFilterFunc returns the filtered tree ID or an error. If a snapshot summary is returned, the snapshot will // rewriteFilterFunc returns the filtered tree ID or an error. If a snapshot summary is returned, the snapshot will
// be updated accordingly. // be updated accordingly.
type rewriteFilterFunc func(ctx context.Context, sn *restic.Snapshot) (restic.ID, *restic.SnapshotSummary, error) type rewriteFilterFunc func(ctx context.Context, sn *data.Snapshot) (restic.ID, *data.SnapshotSummary, error)
func rewriteSnapshot(ctx context.Context, repo *repository.Repository, sn *restic.Snapshot, opts RewriteOptions, printer progress.Printer) (bool, error) { func rewriteSnapshot(ctx context.Context, repo *repository.Repository, sn *data.Snapshot, opts RewriteOptions, printer progress.Printer) (bool, error) {
if sn.Tree == nil { if sn.Tree == nil {
return false, errors.Errorf("snapshot %v has nil tree", sn.ID().Str()) return false, errors.Errorf("snapshot %v has nil tree", sn.ID().Str())
} }
@@ -152,7 +153,7 @@ func rewriteSnapshot(ctx context.Context, repo *repository.Repository, sn *resti
return true return true
} }
rewriteNode := func(node *restic.Node, path string) *restic.Node { rewriteNode := func(node *data.Node, path string) *data.Node {
if selectByName(path) { if selectByName(path) {
return node return node
} }
@@ -162,13 +163,13 @@ func rewriteSnapshot(ctx context.Context, repo *repository.Repository, sn *resti
rewriter, querySize := walker.NewSnapshotSizeRewriter(rewriteNode) rewriter, querySize := walker.NewSnapshotSizeRewriter(rewriteNode)
filter = func(ctx context.Context, sn *restic.Snapshot) (restic.ID, *restic.SnapshotSummary, error) { filter = func(ctx context.Context, sn *data.Snapshot) (restic.ID, *data.SnapshotSummary, error) {
id, err := rewriter.RewriteTree(ctx, repo, "/", *sn.Tree) id, err := rewriter.RewriteTree(ctx, repo, "/", *sn.Tree)
if err != nil { if err != nil {
return restic.ID{}, nil, err return restic.ID{}, nil, err
} }
ss := querySize() ss := querySize()
summary := &restic.SnapshotSummary{} summary := &data.SnapshotSummary{}
if sn.Summary != nil { if sn.Summary != nil {
*summary = *sn.Summary *summary = *sn.Summary
} }
@@ -178,7 +179,7 @@ func rewriteSnapshot(ctx context.Context, repo *repository.Repository, sn *resti
} }
} else { } else {
filter = func(_ context.Context, sn *restic.Snapshot) (restic.ID, *restic.SnapshotSummary, error) { filter = func(_ context.Context, sn *data.Snapshot) (restic.ID, *data.SnapshotSummary, error) {
return *sn.Tree, nil, nil return *sn.Tree, nil, nil
} }
} }
@@ -187,14 +188,14 @@ func rewriteSnapshot(ctx context.Context, repo *repository.Repository, sn *resti
filter, opts.DryRun, opts.Forget, metadata, "rewrite", printer) filter, opts.DryRun, opts.Forget, metadata, "rewrite", printer)
} }
func filterAndReplaceSnapshot(ctx context.Context, repo restic.Repository, sn *restic.Snapshot, func filterAndReplaceSnapshot(ctx context.Context, repo restic.Repository, sn *data.Snapshot,
filter rewriteFilterFunc, dryRun bool, forget bool, newMetadata *snapshotMetadata, addTag string, printer progress.Printer) (bool, error) { filter rewriteFilterFunc, dryRun bool, forget bool, newMetadata *snapshotMetadata, addTag string, printer progress.Printer) (bool, error) {
wg, wgCtx := errgroup.WithContext(ctx) wg, wgCtx := errgroup.WithContext(ctx)
repo.StartPackUploader(wgCtx, wg) repo.StartPackUploader(wgCtx, wg)
var filteredTree restic.ID var filteredTree restic.ID
var summary *restic.SnapshotSummary var summary *data.SnapshotSummary
wg.Go(func() error { wg.Go(func() error {
var err error var err error
filteredTree, summary, err = filter(ctx, sn) filteredTree, summary, err = filter(ctx, sn)
@@ -273,7 +274,7 @@ func filterAndReplaceSnapshot(ctx context.Context, repo restic.Repository, sn *r
} }
// Save the new snapshot. // Save the new snapshot.
id, err := restic.SaveSnapshot(ctx, repo, sn) id, err := data.SaveSnapshot(ctx, repo, sn)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@@ -5,6 +5,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
@@ -37,17 +38,17 @@ func createBasicRewriteRepo(t testing.TB, env *testEnvironment) restic.ID {
return snapshotIDs[0] return snapshotIDs[0]
} }
func getSnapshot(t testing.TB, snapshotID restic.ID, env *testEnvironment) *restic.Snapshot { func getSnapshot(t testing.TB, snapshotID restic.ID, env *testEnvironment) *data.Snapshot {
t.Helper() t.Helper()
var snapshots []*restic.Snapshot var snapshots []*data.Snapshot
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
defer unlock() defer unlock()
snapshots, err = restic.TestLoadAllSnapshots(ctx, repo, nil) snapshots, err = data.TestLoadAllSnapshots(ctx, repo, nil)
return err return err
}) })
rtest.OK(t, err) rtest.OK(t, err)
@@ -116,14 +117,14 @@ func testRewriteMetadata(t *testing.T, metadata snapshotMetadataArgs) {
createBasicRewriteRepo(t, env) createBasicRewriteRepo(t, env)
testRunRewriteExclude(t, env.gopts, []string{}, true, metadata) testRunRewriteExclude(t, env.gopts, []string{}, true, metadata)
var snapshots []*restic.Snapshot var snapshots []*data.Snapshot
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
defer unlock() defer unlock()
snapshots, err = restic.TestLoadAllSnapshots(ctx, repo, nil) snapshots, err = data.TestLoadAllSnapshots(ctx, repo, nil)
return err return err
}) })
rtest.OK(t, err) rtest.OK(t, err)
@@ -164,19 +165,19 @@ func TestRewriteSnaphotSummary(t *testing.T) {
snapshots := testListSnapshots(t, env.gopts, 1) snapshots := testListSnapshots(t, env.gopts, 1)
// replace snapshot by one without a summary // replace snapshot by one without a summary
var oldSummary *restic.SnapshotSummary var oldSummary *data.SnapshotSummary
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
_, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) _, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
defer unlock() defer unlock()
sn, err := restic.LoadSnapshot(ctx, repo, snapshots[0]) sn, err := data.LoadSnapshot(ctx, repo, snapshots[0])
rtest.OK(t, err) rtest.OK(t, err)
oldSummary = sn.Summary oldSummary = sn.Summary
sn.Summary = nil sn.Summary = nil
rtest.OK(t, repo.RemoveUnpacked(ctx, restic.WriteableSnapshotFile, snapshots[0])) rtest.OK(t, repo.RemoveUnpacked(ctx, restic.WriteableSnapshotFile, snapshots[0]))
snapshots[0], err = restic.SaveSnapshot(ctx, repo, sn) snapshots[0], err = data.SaveSnapshot(ctx, repo, sn)
return err return err
}) })
rtest.OK(t, err) rtest.OK(t, err)

View File

@@ -8,6 +8,7 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/table" "github.com/restic/restic/internal/ui/table"
@@ -46,11 +47,11 @@ Exit status is 12 if the password is incorrect.
// SnapshotOptions bundles all options for the snapshots command. // SnapshotOptions bundles all options for the snapshots command.
type SnapshotOptions struct { type SnapshotOptions struct {
restic.SnapshotFilter data.SnapshotFilter
Compact bool Compact bool
Last bool // This option should be removed in favour of Latest. Last bool // This option should be removed in favour of Latest.
Latest int Latest int
GroupBy restic.SnapshotGroupByOptions GroupBy data.SnapshotGroupByOptions
} }
func (opts *SnapshotOptions) AddFlags(f *pflag.FlagSet) { func (opts *SnapshotOptions) AddFlags(f *pflag.FlagSet) {
@@ -74,14 +75,14 @@ func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions
} }
defer unlock() defer unlock()
var snapshots restic.Snapshots var snapshots data.Snapshots
for sn := range FindFilteredSnapshots(ctx, repo, repo, &opts.SnapshotFilter, args, printer) { for sn := range FindFilteredSnapshots(ctx, repo, repo, &opts.SnapshotFilter, args, printer) {
snapshots = append(snapshots, sn) snapshots = append(snapshots, sn)
} }
if ctx.Err() != nil { if ctx.Err() != nil {
return ctx.Err() return ctx.Err()
} }
snapshotGroups, grouped, err := restic.GroupSnapshots(snapshots, opts.GroupBy) snapshotGroups, grouped, err := data.GroupSnapshots(snapshots, opts.GroupBy)
if err != nil { if err != nil {
return err return err
} }
@@ -137,7 +138,7 @@ type filterLastSnapshotsKey struct {
} }
// newFilterLastSnapshotsKey initializes a filterLastSnapshotsKey from a Snapshot // newFilterLastSnapshotsKey initializes a filterLastSnapshotsKey from a Snapshot
func newFilterLastSnapshotsKey(sn *restic.Snapshot) filterLastSnapshotsKey { func newFilterLastSnapshotsKey(sn *data.Snapshot) filterLastSnapshotsKey {
// Shallow slice copy // Shallow slice copy
var paths = make([]string, len(sn.Paths)) var paths = make([]string, len(sn.Paths))
copy(paths, sn.Paths) copy(paths, sn.Paths)
@@ -149,13 +150,13 @@ func newFilterLastSnapshotsKey(sn *restic.Snapshot) filterLastSnapshotsKey {
// the limit last entries for each hostname and path. If the snapshot // the limit last entries for each hostname and path. If the snapshot
// contains multiple paths, they will be joined and treated as one // contains multiple paths, they will be joined and treated as one
// item. // item.
func FilterLatestSnapshots(list restic.Snapshots, limit int) restic.Snapshots { func FilterLatestSnapshots(list data.Snapshots, limit int) data.Snapshots {
// Sort the snapshots so that the newer ones are listed first // Sort the snapshots so that the newer ones are listed first
sort.SliceStable(list, func(i, j int) bool { sort.SliceStable(list, func(i, j int) bool {
return list[i].Time.After(list[j].Time) return list[i].Time.After(list[j].Time)
}) })
var results restic.Snapshots var results data.Snapshots
seen := make(map[filterLastSnapshotsKey]int) seen := make(map[filterLastSnapshotsKey]int)
for _, sn := range list { for _, sn := range list {
key := newFilterLastSnapshotsKey(sn) key := newFilterLastSnapshotsKey(sn)
@@ -168,10 +169,10 @@ func FilterLatestSnapshots(list restic.Snapshots, limit int) restic.Snapshots {
} }
// PrintSnapshots prints a text table of the snapshots in list to stdout. // PrintSnapshots prints a text table of the snapshots in list to stdout.
func PrintSnapshots(stdout io.Writer, list restic.Snapshots, reasons []restic.KeepReason, compact bool) error { func PrintSnapshots(stdout io.Writer, list data.Snapshots, reasons []data.KeepReason, compact bool) error {
// keep the reasons a snasphot is being kept in a map, so that it doesn't // keep the reasons a snasphot is being kept in a map, so that it doesn't
// get lost when the list of snapshots is sorted // get lost when the list of snapshots is sorted
keepReasons := make(map[restic.ID]restic.KeepReason, len(reasons)) keepReasons := make(map[restic.ID]data.KeepReason, len(reasons))
if len(reasons) > 0 { if len(reasons) > 0 {
for i, sn := range list { for i, sn := range list {
id := sn.ID() id := sn.ID()
@@ -287,7 +288,7 @@ func PrintSnapshots(stdout io.Writer, list restic.Snapshots, reasons []restic.Ke
// following snapshots belong to. // following snapshots belong to.
// Prints nothing, if we did not group at all. // Prints nothing, if we did not group at all.
func PrintSnapshotGroupHeader(stdout io.Writer, groupKeyJSON string) error { func PrintSnapshotGroupHeader(stdout io.Writer, groupKeyJSON string) error {
var key restic.SnapshotGroupKey var key data.SnapshotGroupKey
err := json.Unmarshal([]byte(groupKeyJSON), &key) err := json.Unmarshal([]byte(groupKeyJSON), &key)
if err != nil { if err != nil {
@@ -320,7 +321,7 @@ func PrintSnapshotGroupHeader(stdout io.Writer, groupKeyJSON string) error {
// Snapshot helps to print Snapshots as JSON with their ID included. // Snapshot helps to print Snapshots as JSON with their ID included.
type Snapshot struct { type Snapshot struct {
*restic.Snapshot *data.Snapshot
ID *restic.ID `json:"id"` ID *restic.ID `json:"id"`
ShortID string `json:"short_id"` // deprecated ShortID string `json:"short_id"` // deprecated
@@ -328,17 +329,17 @@ type Snapshot struct {
// SnapshotGroup helps to print SnapshotGroups as JSON with their GroupReasons included. // SnapshotGroup helps to print SnapshotGroups as JSON with their GroupReasons included.
type SnapshotGroup struct { type SnapshotGroup struct {
GroupKey restic.SnapshotGroupKey `json:"group_key"` GroupKey data.SnapshotGroupKey `json:"group_key"`
Snapshots []Snapshot `json:"snapshots"` Snapshots []Snapshot `json:"snapshots"`
} }
// printSnapshotGroupJSON writes the JSON representation of list to stdout. // printSnapshotGroupJSON writes the JSON representation of list to stdout.
func printSnapshotGroupJSON(stdout io.Writer, snGroups map[string]restic.Snapshots, grouped bool) error { func printSnapshotGroupJSON(stdout io.Writer, snGroups map[string]data.Snapshots, grouped bool) error {
if grouped { if grouped {
snapshotGroups := []SnapshotGroup{} snapshotGroups := []SnapshotGroup{}
for k, list := range snGroups { for k, list := range snGroups {
var key restic.SnapshotGroupKey var key data.SnapshotGroupKey
var err error var err error
var snapshots []Snapshot var snapshots []Snapshot

View File

@@ -10,6 +10,7 @@ import (
"github.com/restic/chunker" "github.com/restic/chunker"
"github.com/restic/restic/internal/crypto" "github.com/restic/restic/internal/crypto"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/restorer" "github.com/restic/restic/internal/restorer"
@@ -79,7 +80,7 @@ type StatsOptions struct {
// the mode of counting to perform (see consts for available modes) // the mode of counting to perform (see consts for available modes)
countMode string countMode string
restic.SnapshotFilter data.SnapshotFilter
} }
func (opts *StatsOptions) AddFlags(f *pflag.FlagSet) { func (opts *StatsOptions) AddFlags(f *pflag.FlagSet) {
@@ -200,7 +201,7 @@ func runStats(ctx context.Context, opts StatsOptions, gopts GlobalOptions, args
return nil return nil
} }
func statsWalkSnapshot(ctx context.Context, snapshot *restic.Snapshot, repo restic.Loader, opts StatsOptions, stats *statsContainer) error { func statsWalkSnapshot(ctx context.Context, snapshot *data.Snapshot, repo restic.Loader, opts StatsOptions, stats *statsContainer) error {
if snapshot.Tree == nil { if snapshot.Tree == nil {
return fmt.Errorf("snapshot %s has nil tree", snapshot.ID().Str()) return fmt.Errorf("snapshot %s has nil tree", snapshot.ID().Str())
} }
@@ -210,7 +211,7 @@ func statsWalkSnapshot(ctx context.Context, snapshot *restic.Snapshot, repo rest
if opts.countMode == countModeRawData { if opts.countMode == countModeRawData {
// count just the sizes of unique blobs; we don't need to walk the tree // count just the sizes of unique blobs; we don't need to walk the tree
// ourselves in this case, since a nifty function does it for us // ourselves in this case, since a nifty function does it for us
return restic.FindUsedBlobs(ctx, repo, restic.IDs{*snapshot.Tree}, stats.blobs, nil) return data.FindUsedBlobs(ctx, repo, restic.IDs{*snapshot.Tree}, stats.blobs, nil)
} }
hardLinkIndex := restorer.NewHardlinkIndex[struct{}]() hardLinkIndex := restorer.NewHardlinkIndex[struct{}]()
@@ -225,7 +226,7 @@ func statsWalkSnapshot(ctx context.Context, snapshot *restic.Snapshot, repo rest
} }
func statsWalkTree(repo restic.Loader, opts StatsOptions, stats *statsContainer, hardLinkIndex *restorer.HardlinkIndex[struct{}]) walker.WalkFunc { func statsWalkTree(repo restic.Loader, opts StatsOptions, stats *statsContainer, hardLinkIndex *restorer.HardlinkIndex[struct{}]) walker.WalkFunc {
return func(parentTreeID restic.ID, npath string, node *restic.Node, nodeErr error) error { return func(parentTreeID restic.ID, npath string, node *data.Node, nodeErr error) error {
if nodeErr != nil { if nodeErr != nil {
return nodeErr return nodeErr
} }
@@ -281,7 +282,7 @@ func statsWalkTree(repo restic.Loader, opts StatsOptions, stats *statsContainer,
// will still be restored // will still be restored
stats.TotalFileCount++ stats.TotalFileCount++
if node.Links == 1 || node.Type == restic.NodeTypeDir { if node.Links == 1 || node.Type == data.NodeTypeDir {
stats.TotalSize += node.Size stats.TotalSize += node.Size
} else { } else {
// if hardlinks are present only count each deviceID+inode once // if hardlinks are present only count each deviceID+inode once
@@ -298,7 +299,7 @@ func statsWalkTree(repo restic.Loader, opts StatsOptions, stats *statsContainer,
// makeFileIDByContents returns a hash of the blob IDs of the // makeFileIDByContents returns a hash of the blob IDs of the
// node's Content in sequence. // node's Content in sequence.
func makeFileIDByContents(node *restic.Node) fileID { func makeFileIDByContents(node *data.Node) fileID {
var bb []byte var bb []byte
for _, c := range node.Content { for _, c := range node.Content {
bb = append(bb, c[:]...) bb = append(bb, c[:]...)

View File

@@ -6,6 +6,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
@@ -49,10 +50,10 @@ Exit status is 12 if the password is incorrect.
// TagOptions bundles all options for the 'tag' command. // TagOptions bundles all options for the 'tag' command.
type TagOptions struct { type TagOptions struct {
restic.SnapshotFilter data.SnapshotFilter
SetTags restic.TagLists SetTags data.TagLists
AddTags restic.TagLists AddTags data.TagLists
RemoveTags restic.TagLists RemoveTags data.TagLists
} }
func (opts *TagOptions) AddFlags(f *pflag.FlagSet) { func (opts *TagOptions) AddFlags(f *pflag.FlagSet) {
@@ -73,7 +74,7 @@ type changedSnapshotsSummary struct {
ChangedSnapshots int `json:"changed_snapshots"` ChangedSnapshots int `json:"changed_snapshots"`
} }
func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Snapshot, setTags, addTags, removeTags []string, printFunc func(changedSnapshot)) (bool, error) { func changeTags(ctx context.Context, repo *repository.Repository, sn *data.Snapshot, setTags, addTags, removeTags []string, printFunc func(changedSnapshot)) (bool, error) {
var changed bool var changed bool
if len(setTags) != 0 { if len(setTags) != 0 {
@@ -97,7 +98,7 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Sna
} }
// Save the new snapshot. // Save the new snapshot.
id, err := restic.SaveSnapshot(ctx, repo, sn) id, err := data.SaveSnapshot(ctx, repo, sn)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@@ -4,7 +4,7 @@ import (
"context" "context"
"testing" "testing"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/data"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
@@ -32,7 +32,7 @@ func TestTag(t *testing.T) {
"expected original ID to be nil, got %v", newest.Original) "expected original ID to be nil, got %v", newest.Original)
originalID := *newest.ID originalID := *newest.ID
testRunTag(t, TagOptions{SetTags: restic.TagLists{[]string{"NL"}}}, env.gopts) testRunTag(t, TagOptions{SetTags: data.TagLists{[]string{"NL"}}}, env.gopts)
testRunCheck(t, env.gopts) testRunCheck(t, env.gopts)
newest, _ = testRunSnapshots(t, env.gopts) newest, _ = testRunSnapshots(t, env.gopts)
if newest == nil { if newest == nil {
@@ -44,7 +44,7 @@ func TestTag(t *testing.T) {
rtest.Assert(t, *newest.Original == originalID, rtest.Assert(t, *newest.Original == originalID,
"expected original ID to be set to the first snapshot id") "expected original ID to be set to the first snapshot id")
testRunTag(t, TagOptions{AddTags: restic.TagLists{[]string{"CH"}}}, env.gopts) testRunTag(t, TagOptions{AddTags: data.TagLists{[]string{"CH"}}}, env.gopts)
testRunCheck(t, env.gopts) testRunCheck(t, env.gopts)
newest, _ = testRunSnapshots(t, env.gopts) newest, _ = testRunSnapshots(t, env.gopts)
if newest == nil { if newest == nil {
@@ -56,7 +56,7 @@ func TestTag(t *testing.T) {
rtest.Assert(t, *newest.Original == originalID, rtest.Assert(t, *newest.Original == originalID,
"expected original ID to be set to the first snapshot id") "expected original ID to be set to the first snapshot id")
testRunTag(t, TagOptions{RemoveTags: restic.TagLists{[]string{"NL"}}}, env.gopts) testRunTag(t, TagOptions{RemoveTags: data.TagLists{[]string{"NL"}}}, env.gopts)
testRunCheck(t, env.gopts) testRunCheck(t, env.gopts)
newest, _ = testRunSnapshots(t, env.gopts) newest, _ = testRunSnapshots(t, env.gopts)
if newest == nil { if newest == nil {
@@ -68,8 +68,8 @@ func TestTag(t *testing.T) {
rtest.Assert(t, *newest.Original == originalID, rtest.Assert(t, *newest.Original == originalID,
"expected original ID to be set to the first snapshot id") "expected original ID to be set to the first snapshot id")
testRunTag(t, TagOptions{AddTags: restic.TagLists{[]string{"US", "RU"}}}, env.gopts) testRunTag(t, TagOptions{AddTags: data.TagLists{[]string{"US", "RU"}}}, env.gopts)
testRunTag(t, TagOptions{RemoveTags: restic.TagLists{[]string{"CH", "US", "RU"}}}, env.gopts) testRunTag(t, TagOptions{RemoveTags: data.TagLists{[]string{"CH", "US", "RU"}}}, env.gopts)
testRunCheck(t, env.gopts) testRunCheck(t, env.gopts)
newest, _ = testRunSnapshots(t, env.gopts) newest, _ = testRunSnapshots(t, env.gopts)
if newest == nil { if newest == nil {
@@ -82,7 +82,7 @@ func TestTag(t *testing.T) {
"expected original ID to be set to the first snapshot id") "expected original ID to be set to the first snapshot id")
// Check special case of removing all tags. // Check special case of removing all tags.
testRunTag(t, TagOptions{SetTags: restic.TagLists{[]string{""}}}, env.gopts) testRunTag(t, TagOptions{SetTags: data.TagLists{[]string{""}}}, env.gopts)
testRunCheck(t, env.gopts) testRunCheck(t, env.gopts)
newest, _ = testRunSnapshots(t, env.gopts) newest, _ = testRunSnapshots(t, env.gopts)
if newest == nil { if newest == nil {

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"os" "os"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/progress"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@@ -11,7 +12,7 @@ import (
// initMultiSnapshotFilter is used for commands that work on multiple snapshots // initMultiSnapshotFilter is used for commands that work on multiple snapshots
// MUST be combined with restic.FindFilteredSnapshots or FindFilteredSnapshots // MUST be combined with restic.FindFilteredSnapshots or FindFilteredSnapshots
func initMultiSnapshotFilter(flags *pflag.FlagSet, filt *restic.SnapshotFilter, addHostShorthand bool) { func initMultiSnapshotFilter(flags *pflag.FlagSet, filt *data.SnapshotFilter, addHostShorthand bool) {
hostShorthand := "H" hostShorthand := "H"
if !addHostShorthand { if !addHostShorthand {
hostShorthand = "" hostShorthand = ""
@@ -28,7 +29,7 @@ func initMultiSnapshotFilter(flags *pflag.FlagSet, filt *restic.SnapshotFilter,
// initSingleSnapshotFilter is used for commands that work on a single snapshot // initSingleSnapshotFilter is used for commands that work on a single snapshot
// MUST be combined with restic.FindFilteredSnapshot // MUST be combined with restic.FindFilteredSnapshot
func initSingleSnapshotFilter(flags *pflag.FlagSet, filt *restic.SnapshotFilter) { func initSingleSnapshotFilter(flags *pflag.FlagSet, filt *data.SnapshotFilter) {
flags.StringArrayVarP(&filt.Hosts, "host", "H", nil, "only consider snapshots for this `host`, when snapshot ID \"latest\" is given (can be specified multiple times) (default: $RESTIC_HOST)") flags.StringArrayVarP(&filt.Hosts, "host", "H", nil, "only consider snapshots for this `host`, when snapshot ID \"latest\" is given (can be specified multiple times) (default: $RESTIC_HOST)")
flags.Var(&filt.Tags, "tag", "only consider snapshots including `tag[,tag,...]`, when snapshot ID \"latest\" is given (can be specified multiple times)") flags.Var(&filt.Tags, "tag", "only consider snapshots including `tag[,tag,...]`, when snapshot ID \"latest\" is given (can be specified multiple times)")
flags.StringArrayVar(&filt.Paths, "path", nil, "only consider snapshots including this (absolute) `path`, when snapshot ID \"latest\" is given (can be specified multiple times, snapshots must include all specified paths)") flags.StringArrayVar(&filt.Paths, "path", nil, "only consider snapshots including this (absolute) `path`, when snapshot ID \"latest\" is given (can be specified multiple times, snapshots must include all specified paths)")
@@ -40,8 +41,8 @@ func initSingleSnapshotFilter(flags *pflag.FlagSet, filt *restic.SnapshotFilter)
} }
// FindFilteredSnapshots yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots. // FindFilteredSnapshots yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots.
func FindFilteredSnapshots(ctx context.Context, be restic.Lister, loader restic.LoaderUnpacked, f *restic.SnapshotFilter, snapshotIDs []string, printer progress.Printer) <-chan *restic.Snapshot { func FindFilteredSnapshots(ctx context.Context, be restic.Lister, loader restic.LoaderUnpacked, f *data.SnapshotFilter, snapshotIDs []string, printer progress.Printer) <-chan *data.Snapshot {
out := make(chan *restic.Snapshot) out := make(chan *data.Snapshot)
go func() { go func() {
defer close(out) defer close(out)
be, err := restic.MemorizeList(ctx, be, restic.SnapshotFile) be, err := restic.MemorizeList(ctx, be, restic.SnapshotFile)
@@ -50,7 +51,7 @@ func FindFilteredSnapshots(ctx context.Context, be restic.Lister, loader restic.
return return
} }
err = f.FindAll(ctx, be, loader, snapshotIDs, func(id string, sn *restic.Snapshot, err error) error { err = f.FindAll(ctx, be, loader, snapshotIDs, func(id string, sn *data.Snapshot, err error) error {
if err != nil { if err != nil {
printer.E("Ignoring %q: %v", id, err) printer.E("Ignoring %q: %v", id, err)
} else { } else {

View File

@@ -3,7 +3,7 @@ package main
import ( import (
"testing" "testing"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/data"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
@@ -45,7 +45,7 @@ func TestSnapshotFilter(t *testing.T) {
for _, mode := range []bool{false, true} { for _, mode := range []bool{false, true} {
set := pflag.NewFlagSet("test", pflag.PanicOnError) set := pflag.NewFlagSet("test", pflag.PanicOnError)
flt := &restic.SnapshotFilter{} flt := &data.SnapshotFilter{}
if mode { if mode {
initMultiSnapshotFilter(set, flt, false) initMultiSnapshotFilter(set, flt, false)
} else { } else {

View File

@@ -4,11 +4,11 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
) )
func formatNode(path string, n *restic.Node, long bool, human bool) string { func formatNode(path string, n *data.Node, long bool, human bool) string {
if !long { if !long {
return path return path
} }
@@ -24,20 +24,20 @@ func formatNode(path string, n *restic.Node, long bool, human bool) string {
} }
switch n.Type { switch n.Type {
case restic.NodeTypeFile: case data.NodeTypeFile:
mode = 0 mode = 0
case restic.NodeTypeDir: case data.NodeTypeDir:
mode = os.ModeDir mode = os.ModeDir
case restic.NodeTypeSymlink: case data.NodeTypeSymlink:
mode = os.ModeSymlink mode = os.ModeSymlink
target = fmt.Sprintf(" -> %v", n.LinkTarget) target = fmt.Sprintf(" -> %v", n.LinkTarget)
case restic.NodeTypeDev: case data.NodeTypeDev:
mode = os.ModeDevice mode = os.ModeDevice
case restic.NodeTypeCharDev: case data.NodeTypeCharDev:
mode = os.ModeDevice | os.ModeCharDevice mode = os.ModeDevice | os.ModeCharDevice
case restic.NodeTypeFifo: case data.NodeTypeFifo:
mode = os.ModeNamedPipe mode = os.ModeNamedPipe
case restic.NodeTypeSocket: case data.NodeTypeSocket:
mode = os.ModeSocket mode = os.ModeSocket
} }

View File

@@ -4,7 +4,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/data"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
@@ -17,9 +17,9 @@ func TestFormatNode(t *testing.T) {
}() }()
testPath := "/test/path" testPath := "/test/path"
node := restic.Node{ node := data.Node{
Name: "baz", Name: "baz",
Type: restic.NodeTypeFile, Type: data.NodeTypeFile,
Size: 14680064, Size: 14680064,
UID: 1000, UID: 1000,
GID: 2000, GID: 2000,
@@ -28,7 +28,7 @@ func TestFormatNode(t *testing.T) {
for _, c := range []struct { for _, c := range []struct {
path string path string
restic.Node data.Node
long bool long bool
human bool human bool
expect string expect string

View File

@@ -15,6 +15,7 @@ import (
"github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/backend/retry" "github.com/restic/restic/internal/backend/retry"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/options" "github.com/restic/restic/internal/options"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
@@ -365,15 +366,15 @@ func lastSnapshot(old, new map[string]struct{}) (map[string]struct{}, string) {
return old, "" return old, ""
} }
func testLoadSnapshot(t testing.TB, gopts GlobalOptions, id restic.ID) *restic.Snapshot { func testLoadSnapshot(t testing.TB, gopts GlobalOptions, id restic.ID) *data.Snapshot {
var snapshot *restic.Snapshot var snapshot *data.Snapshot
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) _, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
defer unlock() defer unlock()
snapshot, err = restic.LoadSnapshot(ctx, repo, id) snapshot, err = data.LoadSnapshot(ctx, repo, id)
return err return err
}) })
rtest.OK(t, err) rtest.OK(t, err)

View File

@@ -9,6 +9,7 @@ import (
"testing" "testing"
"github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
@@ -170,7 +171,7 @@ func TestFindListOnce(t *testing.T) {
snapshotIDs = restic.NewIDSet() snapshotIDs = restic.NewIDSet()
// specify the two oldest snapshots explicitly and use "latest" to reference the newest one // specify the two oldest snapshots explicitly and use "latest" to reference the newest one
for sn := range FindFilteredSnapshots(ctx, repo, repo, &restic.SnapshotFilter{}, []string{ for sn := range FindFilteredSnapshots(ctx, repo, repo, &data.SnapshotFilter{}, []string{
secondSnapshot[0].String(), secondSnapshot[0].String(),
secondSnapshot[1].String()[:8], secondSnapshot[1].String()[:8],
"latest", "latest",

View File

@@ -11,6 +11,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/feature" "github.com/restic/restic/internal/feature"
@@ -66,9 +67,9 @@ func (s *ItemStats) Add(other ItemStats) {
s.TreeSizeInRepo += other.TreeSizeInRepo s.TreeSizeInRepo += other.TreeSizeInRepo
} }
// ToNoder returns a restic.Node for a File. // ToNoder returns a data.Node for a File.
type ToNoder interface { type ToNoder interface {
ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*restic.Node, error) ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*data.Node, error)
} }
type archiverRepo interface { type archiverRepo interface {
@@ -116,7 +117,7 @@ type Archiver struct {
// //
// CompleteItem may be called asynchronously from several different // CompleteItem may be called asynchronously from several different
// goroutines! // goroutines!
CompleteItem func(item string, previous, current *restic.Node, s ItemStats, d time.Duration) CompleteItem func(item string, previous, current *data.Node, s ItemStats, d time.Duration)
// StartFile is called when a file is being processed by a worker. // StartFile is called when a file is being processed by a worker.
StartFile func(filename string) StartFile func(filename string)
@@ -193,7 +194,7 @@ func New(repo archiverRepo, filesystem fs.FS, opts Options) *Archiver {
FS: filesystem, FS: filesystem,
Options: opts.ApplyDefaults(), Options: opts.ApplyDefaults(),
CompleteItem: func(string, *restic.Node, *restic.Node, ItemStats, time.Duration) {}, CompleteItem: func(string, *data.Node, *data.Node, ItemStats, time.Duration) {},
StartFile: func(string) {}, StartFile: func(string) {},
CompleteBlob: func(uint64) {}, CompleteBlob: func(uint64) {},
} }
@@ -223,7 +224,7 @@ func (arch *Archiver) error(item string, err error) error {
return errf return errf
} }
func (arch *Archiver) trackItem(item string, previous, current *restic.Node, s ItemStats, d time.Duration) { func (arch *Archiver) trackItem(item string, previous, current *data.Node, s ItemStats, d time.Duration) {
arch.CompleteItem(item, previous, current, s, d) arch.CompleteItem(item, previous, current, s, d)
arch.mu.Lock() arch.mu.Lock()
@@ -239,7 +240,7 @@ func (arch *Archiver) trackItem(item string, previous, current *restic.Node, s I
} }
switch current.Type { switch current.Type {
case restic.NodeTypeDir: case data.NodeTypeDir:
switch { switch {
case previous == nil: case previous == nil:
arch.summary.Dirs.New++ arch.summary.Dirs.New++
@@ -249,7 +250,7 @@ func (arch *Archiver) trackItem(item string, previous, current *restic.Node, s I
arch.summary.Dirs.Changed++ arch.summary.Dirs.Changed++
} }
case restic.NodeTypeFile: case data.NodeTypeFile:
switch { switch {
case previous == nil: case previous == nil:
arch.summary.Files.New++ arch.summary.Files.New++
@@ -262,7 +263,7 @@ func (arch *Archiver) trackItem(item string, previous, current *restic.Node, s I
} }
// nodeFromFileInfo returns the restic node from an os.FileInfo. // nodeFromFileInfo returns the restic node from an os.FileInfo.
func (arch *Archiver) nodeFromFileInfo(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*restic.Node, error) { func (arch *Archiver) nodeFromFileInfo(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*data.Node, error) {
node, err := meta.ToNode(ignoreXattrListError, func(format string, args ...any) { node, err := meta.ToNode(ignoreXattrListError, func(format string, args ...any) {
_ = arch.error(filename, fmt.Errorf(format, args...)) _ = arch.error(filename, fmt.Errorf(format, args...))
}) })
@@ -275,7 +276,7 @@ func (arch *Archiver) nodeFromFileInfo(snPath, filename string, meta ToNoder, ig
node.AccessTime = node.ModTime node.AccessTime = node.ModTime
} }
if feature.Flag.Enabled(feature.DeviceIDForHardlinks) { if feature.Flag.Enabled(feature.DeviceIDForHardlinks) {
if node.Links == 1 || node.Type == restic.NodeTypeDir { if node.Links == 1 || node.Type == data.NodeTypeDir {
// the DeviceID is only necessary for hardlinked files // the DeviceID is only necessary for hardlinked files
// when using subvolumes or snapshots their deviceIDs tend to change which causes // when using subvolumes or snapshots their deviceIDs tend to change which causes
// restic to upload new tree blobs // restic to upload new tree blobs
@@ -285,7 +286,7 @@ func (arch *Archiver) nodeFromFileInfo(snPath, filename string, meta ToNoder, ig
// overwrite name to match that within the snapshot // overwrite name to match that within the snapshot
node.Name = path.Base(snPath) node.Name = path.Base(snPath)
// do not filter error for nodes of irregular or invalid type // do not filter error for nodes of irregular or invalid type
if node.Type != restic.NodeTypeIrregular && node.Type != restic.NodeTypeInvalid && err != nil { if node.Type != data.NodeTypeIrregular && node.Type != data.NodeTypeInvalid && err != nil {
err = fmt.Errorf("incomplete metadata for %v: %w", filename, err) err = fmt.Errorf("incomplete metadata for %v: %w", filename, err)
return node, arch.error(filename, err) return node, arch.error(filename, err)
} }
@@ -294,12 +295,12 @@ func (arch *Archiver) nodeFromFileInfo(snPath, filename string, meta ToNoder, ig
// loadSubtree tries to load the subtree referenced by node. In case of an error, nil is returned. // loadSubtree tries to load the subtree referenced by node. In case of an error, nil is returned.
// If there is no node to load, then nil is returned without an error. // If there is no node to load, then nil is returned without an error.
func (arch *Archiver) loadSubtree(ctx context.Context, node *restic.Node) (*restic.Tree, error) { func (arch *Archiver) loadSubtree(ctx context.Context, node *data.Node) (*data.Tree, error) {
if node == nil || node.Type != restic.NodeTypeDir || node.Subtree == nil { if node == nil || node.Type != data.NodeTypeDir || node.Subtree == nil {
return nil, nil return nil, nil
} }
tree, err := restic.LoadTree(ctx, arch.Repo, *node.Subtree) tree, err := data.LoadTree(ctx, arch.Repo, *node.Subtree)
if err != nil { if err != nil {
debug.Log("unable to load tree %v: %v", node.Subtree.Str(), err) debug.Log("unable to load tree %v: %v", node.Subtree.Str(), err)
// a tree in the repository is not readable -> warn the user // a tree in the repository is not readable -> warn the user
@@ -320,7 +321,7 @@ func (arch *Archiver) wrapLoadTreeError(id restic.ID, err error) error {
// saveDir stores a directory in the repo and returns the node. snPath is the // saveDir stores a directory in the repo and returns the node. snPath is the
// path within the current snapshot. // path within the current snapshot.
func (arch *Archiver) saveDir(ctx context.Context, snPath string, dir string, meta fs.File, previous *restic.Tree, complete fileCompleteFunc) (d futureNode, err error) { func (arch *Archiver) saveDir(ctx context.Context, snPath string, dir string, meta fs.File, previous *data.Tree, complete fileCompleteFunc) (d futureNode, err error) {
debug.Log("%v %v", snPath, dir) debug.Log("%v %v", snPath, dir)
treeNode, names, err := arch.dirToNodeAndEntries(snPath, dir, meta) treeNode, names, err := arch.dirToNodeAndEntries(snPath, dir, meta)
@@ -365,7 +366,7 @@ func (arch *Archiver) saveDir(ctx context.Context, snPath string, dir string, me
return fn, nil return fn, nil
} }
func (arch *Archiver) dirToNodeAndEntries(snPath, dir string, meta fs.File) (node *restic.Node, names []string, err error) { func (arch *Archiver) dirToNodeAndEntries(snPath, dir string, meta fs.File) (node *data.Node, names []string, err error) {
err = meta.MakeReadable() err = meta.MakeReadable()
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("openfile for readdirnames failed: %w", err) return nil, nil, fmt.Errorf("openfile for readdirnames failed: %w", err)
@@ -375,7 +376,7 @@ func (arch *Archiver) dirToNodeAndEntries(snPath, dir string, meta fs.File) (nod
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if node.Type != restic.NodeTypeDir { if node.Type != data.NodeTypeDir {
return nil, nil, fmt.Errorf("directory %q changed type, refusing to archive", snPath) return nil, nil, fmt.Errorf("directory %q changed type, refusing to archive", snPath)
} }
@@ -400,7 +401,7 @@ type futureNode struct {
type futureNodeResult struct { type futureNodeResult struct {
snPath, target string snPath, target string
node *restic.Node node *data.Node
stats ItemStats stats ItemStats
err error err error
} }
@@ -438,7 +439,7 @@ func (fn *futureNode) take(ctx context.Context) futureNodeResult {
// allBlobsPresent checks if all blobs (contents) of the given node are // allBlobsPresent checks if all blobs (contents) of the given node are
// present in the index. // present in the index.
func (arch *Archiver) allBlobsPresent(previous *restic.Node) bool { func (arch *Archiver) allBlobsPresent(previous *data.Node) bool {
// check if all blobs are contained in index // check if all blobs are contained in index
for _, id := range previous.Content { for _, id := range previous.Content {
if _, ok := arch.Repo.LookupBlobSize(restic.DataBlob, id); !ok { if _, ok := arch.Repo.LookupBlobSize(restic.DataBlob, id); !ok {
@@ -455,7 +456,7 @@ func (arch *Archiver) allBlobsPresent(previous *restic.Node) bool {
// Errors and completion needs to be handled by the caller. // Errors and completion needs to be handled by the caller.
// //
// snPath is the path within the current snapshot. // snPath is the path within the current snapshot.
func (arch *Archiver) save(ctx context.Context, snPath, target string, previous *restic.Node) (fn futureNode, excluded bool, err error) { func (arch *Archiver) save(ctx context.Context, snPath, target string, previous *data.Node) (fn futureNode, excluded bool, err error) {
start := time.Now() start := time.Now()
debug.Log("%v target %q, previous %v", snPath, target, previous) debug.Log("%v target %q, previous %v", snPath, target, previous)
@@ -574,7 +575,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
arch.StartFile(snPath) arch.StartFile(snPath)
}, func() { }, func() {
arch.trackItem(snPath, nil, nil, ItemStats{}, 0) arch.trackItem(snPath, nil, nil, ItemStats{}, 0)
}, func(node *restic.Node, stats ItemStats) { }, func(node *data.Node, stats ItemStats) {
arch.trackItem(snPath, previous, node, stats, time.Since(start)) arch.trackItem(snPath, previous, node, stats, time.Since(start))
}) })
@@ -591,7 +592,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
} }
fn, err = arch.saveDir(ctx, snPath, target, meta, oldSubtree, fn, err = arch.saveDir(ctx, snPath, target, meta, oldSubtree,
func(node *restic.Node, stats ItemStats) { func(node *data.Node, stats ItemStats) {
arch.trackItem(snItem, previous, node, stats, time.Since(start)) arch.trackItem(snItem, previous, node, stats, time.Since(start))
}) })
if err != nil { if err != nil {
@@ -625,11 +626,11 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
// fileChanged tries to detect whether a file's content has changed compared // fileChanged tries to detect whether a file's content has changed compared
// to the contents of node, which describes the same path in the parent backup. // to the contents of node, which describes the same path in the parent backup.
// It should only be run for regular files. // It should only be run for regular files.
func fileChanged(fi *fs.ExtendedFileInfo, node *restic.Node, ignoreFlags uint) bool { func fileChanged(fi *fs.ExtendedFileInfo, node *data.Node, ignoreFlags uint) bool {
switch { switch {
case node == nil: case node == nil:
return true return true
case node.Type != restic.NodeTypeFile: case node.Type != data.NodeTypeFile:
// We're only called for regular files, so this is a type change. // We're only called for regular files, so this is a type change.
return true return true
case uint64(fi.Size) != node.Size: case uint64(fi.Size) != node.Size:
@@ -658,9 +659,9 @@ func join(elem ...string) string {
// saveTree stores a Tree in the repo, returned is the tree. snPath is the path // saveTree stores a Tree in the repo, returned is the tree. snPath is the path
// within the current snapshot. // within the current snapshot.
func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree, previous *restic.Tree, complete fileCompleteFunc) (futureNode, int, error) { func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree, previous *data.Tree, complete fileCompleteFunc) (futureNode, int, error) {
var node *restic.Node var node *data.Node
if snPath != "/" { if snPath != "/" {
if atree.FileInfoPath == "" { if atree.FileInfoPath == "" {
return futureNode{}, 0, errors.Errorf("FileInfoPath for %v is empty", snPath) return futureNode{}, 0, errors.Errorf("FileInfoPath for %v is empty", snPath)
@@ -673,7 +674,7 @@ func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree,
} }
} else { } else {
// fake root node // fake root node
node = &restic.Node{} node = &data.Node{}
} }
debug.Log("%v (%v nodes), parent %v", snPath, len(atree.Nodes), previous) debug.Log("%v (%v nodes), parent %v", snPath, len(atree.Nodes), previous)
@@ -721,7 +722,7 @@ func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree,
} }
// not a leaf node, archive subtree // not a leaf node, archive subtree
fn, _, err := arch.saveTree(ctx, join(snPath, name), &subatree, oldSubtree, func(n *restic.Node, is ItemStats) { fn, _, err := arch.saveTree(ctx, join(snPath, name), &subatree, oldSubtree, func(n *data.Node, is ItemStats) {
arch.trackItem(snItem, oldNode, n, is, time.Since(start)) arch.trackItem(snItem, oldNode, n, is, time.Since(start))
}) })
if err != nil { if err != nil {
@@ -740,7 +741,7 @@ func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree,
return fn, len(nodes), nil return fn, len(nodes), nil
} }
func (arch *Archiver) dirPathToNode(snPath, target string) (node *restic.Node, err error) { func (arch *Archiver) dirPathToNode(snPath, target string) (node *data.Node, err error) {
meta, err := arch.FS.OpenFile(target, 0, true) meta, err := arch.FS.OpenFile(target, 0, true)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -759,7 +760,7 @@ func (arch *Archiver) dirPathToNode(snPath, target string) (node *restic.Node, e
if err != nil { if err != nil {
return nil, err return nil, err
} }
if node.Type != restic.NodeTypeDir { if node.Type != data.NodeTypeDir {
return nil, errors.Errorf("path is not a directory: %v", target) return nil, errors.Errorf("path is not a directory: %v", target)
} }
return node, err return node, err
@@ -802,19 +803,19 @@ func resolveRelativeTargets(filesys fs.FS, targets []string) ([]string, error) {
// SnapshotOptions collect attributes for a new snapshot. // SnapshotOptions collect attributes for a new snapshot.
type SnapshotOptions struct { type SnapshotOptions struct {
Tags restic.TagList Tags data.TagList
Hostname string Hostname string
Excludes []string Excludes []string
BackupStart time.Time BackupStart time.Time
Time time.Time Time time.Time
ParentSnapshot *restic.Snapshot ParentSnapshot *data.Snapshot
ProgramVersion string ProgramVersion string
// SkipIfUnchanged omits the snapshot creation if it is identical to the parent snapshot. // SkipIfUnchanged omits the snapshot creation if it is identical to the parent snapshot.
SkipIfUnchanged bool SkipIfUnchanged bool
} }
// loadParentTree loads a tree referenced by snapshot id. If id is null, nil is returned. // loadParentTree loads a tree referenced by snapshot id. If id is null, nil is returned.
func (arch *Archiver) loadParentTree(ctx context.Context, sn *restic.Snapshot) *restic.Tree { func (arch *Archiver) loadParentTree(ctx context.Context, sn *data.Snapshot) *data.Tree {
if sn == nil { if sn == nil {
return nil return nil
} }
@@ -825,7 +826,7 @@ func (arch *Archiver) loadParentTree(ctx context.Context, sn *restic.Snapshot) *
} }
debug.Log("load parent tree %v", *sn.Tree) debug.Log("load parent tree %v", *sn.Tree)
tree, err := restic.LoadTree(ctx, arch.Repo, *sn.Tree) tree, err := data.LoadTree(ctx, arch.Repo, *sn.Tree)
if err != nil { if err != nil {
debug.Log("unable to load tree %v: %v", *sn.Tree, err) debug.Log("unable to load tree %v: %v", *sn.Tree, err)
_ = arch.error("/", arch.wrapLoadTreeError(*sn.Tree, err)) _ = arch.error("/", arch.wrapLoadTreeError(*sn.Tree, err))
@@ -858,7 +859,7 @@ func (arch *Archiver) stopWorkers() {
} }
// Snapshot saves several targets and returns a snapshot. // Snapshot saves several targets and returns a snapshot.
func (arch *Archiver) Snapshot(ctx context.Context, targets []string, opts SnapshotOptions) (*restic.Snapshot, restic.ID, *Summary, error) { func (arch *Archiver) Snapshot(ctx context.Context, targets []string, opts SnapshotOptions) (*data.Snapshot, restic.ID, *Summary, error) {
arch.summary = &Summary{ arch.summary = &Summary{
BackupStart: opts.BackupStart, BackupStart: opts.BackupStart,
} }
@@ -886,7 +887,7 @@ func (arch *Archiver) Snapshot(ctx context.Context, targets []string, opts Snaps
arch.runWorkers(wgCtx, wg) arch.runWorkers(wgCtx, wg)
debug.Log("starting snapshot") debug.Log("starting snapshot")
fn, nodeCount, err := arch.saveTree(wgCtx, "/", atree, arch.loadParentTree(wgCtx, opts.ParentSnapshot), func(_ *restic.Node, is ItemStats) { fn, nodeCount, err := arch.saveTree(wgCtx, "/", atree, arch.loadParentTree(wgCtx, opts.ParentSnapshot), func(_ *data.Node, is ItemStats) {
arch.trackItem("/", nil, nil, is, time.Since(start)) arch.trackItem("/", nil, nil, is, time.Since(start))
}) })
if err != nil { if err != nil {
@@ -934,7 +935,7 @@ func (arch *Archiver) Snapshot(ctx context.Context, targets []string, opts Snaps
} }
} }
sn, err := restic.NewSnapshot(targets, opts.Tags, opts.Hostname, opts.Time) sn, err := data.NewSnapshot(targets, opts.Tags, opts.Hostname, opts.Time)
if err != nil { if err != nil {
return nil, restic.ID{}, nil, err return nil, restic.ID{}, nil, err
} }
@@ -946,7 +947,7 @@ func (arch *Archiver) Snapshot(ctx context.Context, targets []string, opts Snaps
} }
sn.Tree = &rootTreeID sn.Tree = &rootTreeID
arch.summary.BackupEnd = time.Now() arch.summary.BackupEnd = time.Now()
sn.Summary = &restic.SnapshotSummary{ sn.Summary = &data.SnapshotSummary{
BackupStart: arch.summary.BackupStart, BackupStart: arch.summary.BackupStart,
BackupEnd: arch.summary.BackupEnd, BackupEnd: arch.summary.BackupEnd,
@@ -964,7 +965,7 @@ func (arch *Archiver) Snapshot(ctx context.Context, targets []string, opts Snaps
TotalBytesProcessed: arch.summary.ProcessedBytes, TotalBytesProcessed: arch.summary.ProcessedBytes,
} }
id, err := restic.SaveSnapshot(ctx, arch.Repo, sn) id, err := data.SaveSnapshot(ctx, arch.Repo, sn)
if err != nil { if err != nil {
return nil, restic.ID{}, nil, err return nil, restic.ID{}, nil, err
} }

View File

@@ -19,6 +19,7 @@ import (
"github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/backend/mem" "github.com/restic/restic/internal/backend/mem"
"github.com/restic/restic/internal/checker" "github.com/restic/restic/internal/checker"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/feature" "github.com/restic/restic/internal/feature"
"github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/fs"
@@ -37,7 +38,7 @@ func prepareTempdirRepoSrc(t testing.TB, src TestDir) (string, restic.Repository
return tempdir, repo return tempdir, repo
} }
func saveFile(t testing.TB, repo archiverRepo, filename string, filesystem fs.FS) (*restic.Node, ItemStats) { func saveFile(t testing.TB, repo archiverRepo, filename string, filesystem fs.FS) (*data.Node, ItemStats) {
wg, ctx := errgroup.WithContext(context.TODO()) wg, ctx := errgroup.WithContext(context.TODO())
repo.StartPackUploader(ctx, wg) repo.StartPackUploader(ctx, wg)
@@ -52,7 +53,7 @@ func saveFile(t testing.TB, repo archiverRepo, filename string, filesystem fs.FS
var ( var (
completeReadingCallback bool completeReadingCallback bool
completeCallbackNode *restic.Node completeCallbackNode *data.Node
completeCallbackStats ItemStats completeCallbackStats ItemStats
completeCallback bool completeCallback bool
@@ -66,7 +67,7 @@ func saveFile(t testing.TB, repo archiverRepo, filename string, filesystem fs.FS
} }
} }
complete := func(node *restic.Node, stats ItemStats) { complete := func(node *data.Node, stats ItemStats) {
completeCallback = true completeCallback = true
completeCallbackNode = node completeCallbackNode = node
completeCallbackStats = stats completeCallbackStats = stats
@@ -427,8 +428,8 @@ func (repo *blobCountingRepo) SaveBlob(ctx context.Context, t restic.BlobType, b
return id, exists, size, err return id, exists, size, err
} }
func (repo *blobCountingRepo) SaveTree(ctx context.Context, t *restic.Tree) (restic.ID, error) { func (repo *blobCountingRepo) SaveTree(ctx context.Context, t *data.Tree) (restic.ID, error) {
id, err := restic.SaveTree(ctx, repo.archiverRepo, t) id, err := data.SaveTree(ctx, repo.archiverRepo, t)
h := restic.BlobHandle{ID: id, Type: restic.TreeBlob} h := restic.BlobHandle{ID: id, Type: restic.TreeBlob}
repo.m.Lock() repo.m.Lock()
repo.saved[h]++ repo.saved[h]++
@@ -548,7 +549,7 @@ func rename(t testing.TB, oldname, newname string) {
} }
} }
func nodeFromFile(t testing.TB, localFs fs.FS, filename string) *restic.Node { func nodeFromFile(t testing.TB, localFs fs.FS, filename string) *data.Node {
meta, err := localFs.OpenFile(filename, fs.O_NOFOLLOW, true) meta, err := localFs.OpenFile(filename, fs.O_NOFOLLOW, true)
rtest.OK(t, err) rtest.OK(t, err)
node, err := meta.ToNode(false, t.Logf) node, err := meta.ToNode(false, t.Logf)
@@ -724,7 +725,7 @@ func TestFilChangedSpecialCases(t *testing.T) {
t.Run("type-change", func(t *testing.T) { t.Run("type-change", func(t *testing.T) {
fi := lstat(t, filename) fi := lstat(t, filename)
node := nodeFromFile(t, &fs.Local{}, filename) node := nodeFromFile(t, &fs.Local{}, filename)
node.Type = restic.NodeTypeSymlink node.Type = data.NodeTypeSymlink
if !fileChanged(fi, node, 0) { if !fileChanged(fi, node, 0) {
t.Fatal("node with changed type detected as unchanged") t.Fatal("node with changed type detected as unchanged")
} }
@@ -865,8 +866,8 @@ func TestArchiverSaveDir(t *testing.T) {
} }
node.Name = targetNodeName node.Name = targetNodeName
tree := &restic.Tree{Nodes: []*restic.Node{node}} tree := &data.Tree{Nodes: []*data.Node{node}}
treeID, err := restic.SaveTree(ctx, repo, tree) treeID, err := data.SaveTree(ctx, repo, tree)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -1679,7 +1680,7 @@ func (f MockFile) Read(p []byte) (int, error) {
return n, err return n, err
} }
func checkSnapshotStats(t *testing.T, sn *restic.Snapshot, stat Summary) { func checkSnapshotStats(t *testing.T, sn *data.Snapshot, stat Summary) {
t.Helper() t.Helper()
rtest.Equals(t, stat.BackupStart, sn.Summary.BackupStart, "BackupStart") rtest.Equals(t, stat.BackupStart, sn.Summary.BackupStart, "BackupStart")
// BackupEnd is set to time.Now() and can't be compared to a fixed value // BackupEnd is set to time.Now() and can't be compared to a fixed value
@@ -2219,7 +2220,7 @@ func TestArchiverAbortEarlyOnError(t *testing.T) {
} }
} }
func snapshot(t testing.TB, repo archiverRepo, fs fs.FS, parent *restic.Snapshot, filename string) (*restic.Snapshot, *restic.Node) { func snapshot(t testing.TB, repo archiverRepo, fs fs.FS, parent *data.Snapshot, filename string) (*data.Snapshot, *data.Node) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@@ -2234,7 +2235,7 @@ func snapshot(t testing.TB, repo archiverRepo, fs fs.FS, parent *restic.Snapshot
t.Fatal(err) t.Fatal(err)
} }
tree, err := restic.LoadTree(ctx, repo, *snapshot.Tree) tree, err := data.LoadTree(ctx, repo, *snapshot.Tree)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -2251,7 +2252,7 @@ type overrideFS struct {
fs.FS fs.FS
overrideFI *fs.ExtendedFileInfo overrideFI *fs.ExtendedFileInfo
resetFIOnRead bool resetFIOnRead bool
overrideNode *restic.Node overrideNode *data.Node
overrideErr error overrideErr error
} }
@@ -2287,7 +2288,7 @@ func (f overrideFile) MakeReadable() error {
return f.File.MakeReadable() return f.File.MakeReadable()
} }
func (f overrideFile) ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*restic.Node, error) { func (f overrideFile) ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*data.Node, error) {
if f.ofs.overrideNode == nil { if f.ofs.overrideNode == nil {
return f.File.ToNode(ignoreXattrListError, warnf) return f.File.ToNode(ignoreXattrListError, warnf)
} }
@@ -2328,7 +2329,7 @@ func TestMetadataChanged(t *testing.T) {
fs := &overrideFS{ fs := &overrideFS{
FS: localFS, FS: localFS,
overrideFI: fi, overrideFI: fi,
overrideNode: &restic.Node{}, overrideNode: &data.Node{},
} }
*fs.overrideNode = *want *fs.overrideNode = *want
@@ -2451,11 +2452,11 @@ func TestRacyFileTypeSwap(t *testing.T) {
} }
type mockToNoder struct { type mockToNoder struct {
node *restic.Node node *data.Node
err error err error
} }
func (m *mockToNoder) ToNode(_ bool, _ func(format string, args ...any)) (*restic.Node, error) { func (m *mockToNoder) ToNode(_ bool, _ func(format string, args ...any)) (*data.Node, error) {
return m.node, m.err return m.node, m.err
} }
@@ -2474,7 +2475,7 @@ func TestMetadataBackupErrorFiltering(t *testing.T) {
} }
nonExistNoder := &mockToNoder{ nonExistNoder := &mockToNoder{
node: &restic.Node{Type: restic.NodeTypeFile}, node: &data.Node{Type: data.NodeTypeFile},
err: fmt.Errorf("not found"), err: fmt.Errorf("not found"),
} }
@@ -2487,7 +2488,7 @@ func TestMetadataBackupErrorFiltering(t *testing.T) {
// check that errors from reading irregular file are not filtered // check that errors from reading irregular file are not filtered
filteredErr = nil filteredErr = nil
nonExistNoder = &mockToNoder{ nonExistNoder = &mockToNoder{
node: &restic.Node{Type: restic.NodeTypeIrregular}, node: &data.Node{Type: data.NodeTypeIrregular},
err: fmt.Errorf(`unsupported file type "irregular"`), err: fmt.Errorf(`unsupported file type "irregular"`),
} }
node, err = arch.nodeFromFileInfo("file", filename, nonExistNoder, false) node, err = arch.nodeFromFileInfo("file", filename, nonExistNoder, false)
@@ -2515,8 +2516,8 @@ func TestIrregularFile(t *testing.T) {
override := &overrideFS{ override := &overrideFS{
FS: fs.Local{}, FS: fs.Local{},
overrideFI: fi, overrideFI: fi,
overrideNode: &restic.Node{ overrideNode: &data.Node{
Type: restic.NodeTypeIrregular, Type: data.NodeTypeIrregular,
}, },
overrideErr: fmt.Errorf(`unsupported file type "irregular"`), overrideErr: fmt.Errorf(`unsupported file type "irregular"`),
} }

View File

@@ -6,13 +6,13 @@ package archiver
import ( import (
"testing" "testing"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/feature" "github.com/restic/restic/internal/feature"
"github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func statAndSnapshot(t *testing.T, repo archiverRepo, name string) (*restic.Node, *restic.Node) { func statAndSnapshot(t *testing.T, repo archiverRepo, name string) (*data.Node, *data.Node) {
want := nodeFromFile(t, &fs.Local{}, name) want := nodeFromFile(t, &fs.Local{}, name)
_, node := snapshot(t, repo, &fs.Local{}, nil, name) _, node := snapshot(t, repo, &fs.Local{}, nil, name)
return want, node return want, node

View File

@@ -7,6 +7,7 @@ import (
"sync" "sync"
"github.com/restic/chunker" "github.com/restic/chunker"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/fs"
@@ -28,7 +29,7 @@ type fileSaver struct {
CompleteBlob func(bytes uint64) CompleteBlob func(bytes uint64)
NodeFromFileInfo func(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*restic.Node, error) NodeFromFileInfo func(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*data.Node, error)
} }
// newFileSaver returns a new file saver. A worker pool with fileWorkers is // newFileSaver returns a new file saver. A worker pool with fileWorkers is
@@ -64,7 +65,7 @@ func (s *fileSaver) TriggerShutdown() {
} }
// fileCompleteFunc is called when the file has been saved. // fileCompleteFunc is called when the file has been saved.
type fileCompleteFunc func(*restic.Node, ItemStats) type fileCompleteFunc func(*data.Node, ItemStats)
// Save stores the file f and returns the data once it has been completed. The // Save stores the file f and returns the data once it has been completed. The
// file is closed by Save. completeReading is only called if the file was read // file is closed by Save. completeReading is only called if the file was read
@@ -160,7 +161,7 @@ func (s *fileSaver) saveFile(ctx context.Context, chnker *chunker.Chunker, snPat
return return
} }
if node.Type != restic.NodeTypeFile { if node.Type != data.NodeTypeFile {
_ = f.Close() _ = f.Close()
completeError(errors.Errorf("node type %q is wrong", node.Type)) completeError(errors.Errorf("node type %q is wrong", node.Type))
return return

View File

@@ -9,6 +9,7 @@ import (
"testing" "testing"
"github.com/restic/chunker" "github.com/restic/chunker"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/test" "github.com/restic/restic/internal/test"
@@ -49,7 +50,7 @@ func startFileSaver(ctx context.Context, t testing.TB, _ fs.FS) (*fileSaver, con
} }
s := newFileSaver(ctx, wg, saveBlob, pol, workers, workers) s := newFileSaver(ctx, wg, saveBlob, pol, workers, workers)
s.NodeFromFileInfo = func(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*restic.Node, error) { s.NodeFromFileInfo = func(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*data.Node, error) {
return meta.ToNode(ignoreXattrListError, t.Logf) return meta.ToNode(ignoreXattrListError, t.Logf)
} }
@@ -64,7 +65,7 @@ func TestFileSaver(t *testing.T) {
startFn := func() {} startFn := func() {}
completeReadingFn := func() {} completeReadingFn := func() {}
completeFn := func(*restic.Node, ItemStats) {} completeFn := func(*data.Node, ItemStats) {}
testFs := fs.Local{} testFs := fs.Local{}
s, ctx, wg := startFileSaver(ctx, t, testFs) s, ctx, wg := startFileSaver(ctx, t, testFs)

View File

@@ -11,13 +11,14 @@ import (
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
) )
// TestSnapshot creates a new snapshot of path. // TestSnapshot creates a new snapshot of path.
func TestSnapshot(t testing.TB, repo restic.Repository, path string, parent *restic.ID) *restic.Snapshot { func TestSnapshot(t testing.TB, repo restic.Repository, path string, parent *restic.ID) *data.Snapshot {
arch := New(repo, fs.Local{}, Options{}) arch := New(repo, fs.Local{}, Options{})
opts := SnapshotOptions{ opts := SnapshotOptions{
Time: time.Now(), Time: time.Now(),
@@ -25,7 +26,7 @@ func TestSnapshot(t testing.TB, repo restic.Repository, path string, parent *res
Tags: []string{"test"}, Tags: []string{"test"},
} }
if parent != nil { if parent != nil {
sn, err := restic.LoadSnapshot(context.TODO(), repo, *parent) sn, err := data.LoadSnapshot(context.TODO(), repo, *parent)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -232,7 +233,7 @@ func TestEnsureFiles(t testing.TB, target string, dir TestDir) {
} }
// TestEnsureFileContent checks if the file in the repo is the same as file. // TestEnsureFileContent checks if the file in the repo is the same as file.
func TestEnsureFileContent(ctx context.Context, t testing.TB, repo restic.BlobLoader, filename string, node *restic.Node, file TestFile) { func TestEnsureFileContent(ctx context.Context, t testing.TB, repo restic.BlobLoader, filename string, node *data.Node, file TestFile) {
if int(node.Size) != len(file.Content) { if int(node.Size) != len(file.Content) {
t.Fatalf("%v: wrong node size: want %d, got %d", filename, node.Size, len(file.Content)) t.Fatalf("%v: wrong node size: want %d, got %d", filename, node.Size, len(file.Content))
return return
@@ -263,7 +264,7 @@ func TestEnsureFileContent(ctx context.Context, t testing.TB, repo restic.BlobLo
func TestEnsureTree(ctx context.Context, t testing.TB, prefix string, repo restic.BlobLoader, treeID restic.ID, dir TestDir) { func TestEnsureTree(ctx context.Context, t testing.TB, prefix string, repo restic.BlobLoader, treeID restic.ID, dir TestDir) {
t.Helper() t.Helper()
tree, err := restic.LoadTree(ctx, repo, treeID) tree, err := data.LoadTree(ctx, repo, treeID)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
return return
@@ -289,7 +290,7 @@ func TestEnsureTree(ctx context.Context, t testing.TB, prefix string, repo resti
switch e := entry.(type) { switch e := entry.(type) {
case TestDir: case TestDir:
if node.Type != restic.NodeTypeDir { if node.Type != data.NodeTypeDir {
t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "dir") t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "dir")
return return
} }
@@ -301,12 +302,12 @@ func TestEnsureTree(ctx context.Context, t testing.TB, prefix string, repo resti
TestEnsureTree(ctx, t, path.Join(prefix, node.Name), repo, *node.Subtree, e) TestEnsureTree(ctx, t, path.Join(prefix, node.Name), repo, *node.Subtree, e)
case TestFile: case TestFile:
if node.Type != restic.NodeTypeFile { if node.Type != data.NodeTypeFile {
t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "file") t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "file")
} }
TestEnsureFileContent(ctx, t, repo, nodePrefix, node, e) TestEnsureFileContent(ctx, t, repo, nodePrefix, node, e)
case TestSymlink: case TestSymlink:
if node.Type != restic.NodeTypeSymlink { if node.Type != data.NodeTypeSymlink {
t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "symlink") t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "symlink")
} }
@@ -331,7 +332,7 @@ func TestEnsureSnapshot(t testing.TB, repo restic.Repository, snapshotID restic.
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
sn, err := restic.LoadSnapshot(ctx, repo, snapshotID) sn, err := data.LoadSnapshot(ctx, repo, snapshotID)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
return return

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
@@ -42,7 +43,7 @@ func (s *treeSaver) TriggerShutdown() {
} }
// Save stores the dir d and returns the data once it has been completed. // Save stores the dir d and returns the data once it has been completed.
func (s *treeSaver) Save(ctx context.Context, snPath string, target string, node *restic.Node, nodes []futureNode, complete fileCompleteFunc) futureNode { func (s *treeSaver) Save(ctx context.Context, snPath string, target string, node *data.Node, nodes []futureNode, complete fileCompleteFunc) futureNode {
fn, ch := newFutureNode() fn, ch := newFutureNode()
job := saveTreeJob{ job := saveTreeJob{
snPath: snPath, snPath: snPath,
@@ -65,22 +66,22 @@ func (s *treeSaver) Save(ctx context.Context, snPath string, target string, node
type saveTreeJob struct { type saveTreeJob struct {
snPath string snPath string
target string target string
node *restic.Node node *data.Node
nodes []futureNode nodes []futureNode
ch chan<- futureNodeResult ch chan<- futureNodeResult
complete fileCompleteFunc complete fileCompleteFunc
} }
// save stores the nodes as a tree in the repo. // save stores the nodes as a tree in the repo.
func (s *treeSaver) save(ctx context.Context, job *saveTreeJob) (*restic.Node, ItemStats, error) { func (s *treeSaver) save(ctx context.Context, job *saveTreeJob) (*data.Node, ItemStats, error) {
var stats ItemStats var stats ItemStats
node := job.node node := job.node
nodes := job.nodes nodes := job.nodes
// allow GC of nodes array once the loop is finished // allow GC of nodes array once the loop is finished
job.nodes = nil job.nodes = nil
builder := restic.NewTreeJSONBuilder() builder := data.NewTreeJSONBuilder()
var lastNode *restic.Node var lastNode *data.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
@@ -110,7 +111,7 @@ func (s *treeSaver) save(ctx context.Context, job *saveTreeJob) (*restic.Node, I
} }
err := builder.AddNode(fnr.node) err := builder.AddNode(fnr.node)
if err != nil && errors.Is(err, restic.ErrTreeNotOrdered) && lastNode != nil && fnr.node.Equals(*lastNode) { if err != nil && errors.Is(err, data.ErrTreeNotOrdered) && lastNode != nil && fnr.node.Equals(*lastNode) {
debug.Log("insert %v failed: %v", fnr.node.Name, err) debug.Log("insert %v failed: %v", fnr.node.Name, err)
// ignore error if an _identical_ node already exists, but nevertheless issue a warning // ignore error if an _identical_ node already exists, but nevertheless issue a warning
_ = s.errFn(fnr.target, err) _ = s.errFn(fnr.target, err)

View File

@@ -6,6 +6,7 @@ import (
"runtime" "runtime"
"testing" "testing"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/test" "github.com/restic/restic/internal/test"
@@ -46,7 +47,7 @@ func TestTreeSaver(t *testing.T) {
var results []futureNode var results []futureNode
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
node := &restic.Node{ node := &data.Node{
Name: fmt.Sprintf("file-%d", i), Name: fmt.Sprintf("file-%d", i),
} }
@@ -86,11 +87,11 @@ func TestTreeSaverError(t *testing.T) {
var results []futureNode var results []futureNode
for i := 0; i < test.trees; i++ { for i := 0; i < test.trees; i++ {
node := &restic.Node{ node := &data.Node{
Name: fmt.Sprintf("file-%d", i), Name: fmt.Sprintf("file-%d", i),
} }
nodes := []futureNode{ nodes := []futureNode{
newFutureNodeWithResult(futureNodeResult{node: &restic.Node{ newFutureNodeWithResult(futureNodeResult{node: &data.Node{
Name: fmt.Sprintf("child-%d", i), Name: fmt.Sprintf("child-%d", i),
}}), }}),
} }
@@ -125,20 +126,20 @@ func TestTreeSaverDuplicates(t *testing.T) {
ctx, cancel, b, shutdown := setupTreeSaver() ctx, cancel, b, shutdown := setupTreeSaver()
defer cancel() defer cancel()
node := &restic.Node{ node := &data.Node{
Name: "file", Name: "file",
} }
nodes := []futureNode{ nodes := []futureNode{
newFutureNodeWithResult(futureNodeResult{node: &restic.Node{ newFutureNodeWithResult(futureNodeResult{node: &data.Node{
Name: "child", Name: "child",
}}), }}),
} }
if identicalNodes { if identicalNodes {
nodes = append(nodes, newFutureNodeWithResult(futureNodeResult{node: &restic.Node{ nodes = append(nodes, newFutureNodeWithResult(futureNodeResult{node: &data.Node{
Name: "child", Name: "child",
}})) }}))
} else { } else {
nodes = append(nodes, newFutureNodeWithResult(futureNodeResult{node: &restic.Node{ nodes = append(nodes, newFutureNodeWithResult(futureNodeResult{node: &data.Node{
Name: "child", Name: "child",
Size: 42, Size: 42,
}})) }}))

View File

@@ -8,6 +8,7 @@ import (
"sync" "sync"
"github.com/klauspost/compress/zstd" "github.com/klauspost/compress/zstd"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
@@ -256,7 +257,7 @@ func (e *TreeError) Error() string {
} }
// checkTreeWorker checks the trees received and sends out errors to errChan. // checkTreeWorker checks the trees received and sends out errors to errChan.
func (c *Checker) checkTreeWorker(ctx context.Context, trees <-chan restic.TreeItem, out chan<- error) { func (c *Checker) checkTreeWorker(ctx context.Context, trees <-chan data.TreeItem, out chan<- error) {
for job := range trees { for job := range trees {
debug.Log("check tree %v (tree %v, err %v)", job.ID, job.Tree, job.Error) debug.Log("check tree %v (tree %v, err %v)", job.ID, job.Tree, job.Error)
@@ -281,7 +282,7 @@ func (c *Checker) checkTreeWorker(ctx context.Context, trees <-chan restic.TreeI
} }
func loadSnapshotTreeIDs(ctx context.Context, lister restic.Lister, repo restic.LoaderUnpacked) (ids restic.IDs, errs []error) { func loadSnapshotTreeIDs(ctx context.Context, lister restic.Lister, repo restic.LoaderUnpacked) (ids restic.IDs, errs []error) {
err := restic.ForAllSnapshots(ctx, lister, repo, nil, func(id restic.ID, sn *restic.Snapshot, err error) error { err := data.ForAllSnapshots(ctx, lister, repo, nil, func(id restic.ID, sn *data.Snapshot, err error) error {
if err != nil { if err != nil {
errs = append(errs, err) errs = append(errs, err)
return nil return nil
@@ -315,7 +316,7 @@ func (c *Checker) Structure(ctx context.Context, p *progress.Counter, errChan ch
} }
wg, ctx := errgroup.WithContext(ctx) wg, ctx := errgroup.WithContext(ctx)
treeStream := restic.StreamTrees(ctx, wg, c.repo, trees, func(treeID restic.ID) bool { treeStream := data.StreamTrees(ctx, wg, c.repo, trees, func(treeID restic.ID) bool {
// blobRefs may be accessed in parallel by checkTree // blobRefs may be accessed in parallel by checkTree
c.blobRefs.Lock() c.blobRefs.Lock()
h := restic.BlobHandle{ID: treeID, Type: restic.TreeBlob} h := restic.BlobHandle{ID: treeID, Type: restic.TreeBlob}
@@ -344,12 +345,12 @@ func (c *Checker) Structure(ctx context.Context, p *progress.Counter, errChan ch
} }
} }
func (c *Checker) checkTree(id restic.ID, tree *restic.Tree) (errs []error) { func (c *Checker) checkTree(id restic.ID, tree *data.Tree) (errs []error) {
debug.Log("checking tree %v", id) debug.Log("checking tree %v", id)
for _, node := range tree.Nodes { for _, node := range tree.Nodes {
switch node.Type { switch node.Type {
case restic.NodeTypeFile: case data.NodeTypeFile:
if node.Content == nil { if node.Content == nil {
errs = append(errs, &Error{TreeID: id, Err: errors.Errorf("file %q has nil blob list", node.Name)}) errs = append(errs, &Error{TreeID: id, Err: errors.Errorf("file %q has nil blob list", node.Name)})
} }
@@ -385,7 +386,7 @@ func (c *Checker) checkTree(id restic.ID, tree *restic.Tree) (errs []error) {
c.blobRefs.Unlock() c.blobRefs.Unlock()
} }
case restic.NodeTypeDir: case data.NodeTypeDir:
if node.Subtree == nil { if node.Subtree == nil {
errs = append(errs, &Error{TreeID: id, Err: errors.Errorf("dir node %q has no subtree", node.Name)}) errs = append(errs, &Error{TreeID: id, Err: errors.Errorf("dir node %q has no subtree", node.Name)})
continue continue
@@ -396,7 +397,7 @@ func (c *Checker) checkTree(id restic.ID, tree *restic.Tree) (errs []error) {
continue continue
} }
case restic.NodeTypeSymlink, restic.NodeTypeSocket, restic.NodeTypeCharDev, restic.NodeTypeDev, restic.NodeTypeFifo: case data.NodeTypeSymlink, data.NodeTypeSocket, data.NodeTypeCharDev, data.NodeTypeDev, data.NodeTypeFifo:
// nothing to check // nothing to check
default: default:

View File

@@ -16,6 +16,7 @@ import (
"github.com/restic/restic/internal/archiver" "github.com/restic/restic/internal/archiver"
"github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/checker" "github.com/restic/restic/internal/checker"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/repository/hashing" "github.com/restic/restic/internal/repository/hashing"
@@ -440,7 +441,7 @@ type loadTreesOnceRepository struct {
DuplicateTree bool DuplicateTree bool
} }
func (r *loadTreesOnceRepository) LoadTree(ctx context.Context, id restic.ID) (*restic.Tree, error) { func (r *loadTreesOnceRepository) LoadTree(ctx context.Context, id restic.ID) (*data.Tree, error) {
r.mutex.Lock() r.mutex.Lock()
defer r.mutex.Unlock() defer r.mutex.Unlock()
@@ -450,7 +451,7 @@ func (r *loadTreesOnceRepository) LoadTree(ctx context.Context, id restic.ID) (*
return nil, errors.Errorf("trying to load tree with id %v twice", id) return nil, errors.Errorf("trying to load tree with id %v twice", id)
} }
r.loadedTrees.Insert(id) r.loadedTrees.Insert(id)
return restic.LoadTree(ctx, r.Repository, id) return data.LoadTree(ctx, r.Repository, id)
} }
func TestCheckerNoDuplicateTreeDecodes(t *testing.T) { func TestCheckerNoDuplicateTreeDecodes(t *testing.T) {
@@ -481,11 +482,11 @@ type delayRepository struct {
Unblocker sync.Once Unblocker sync.Once
} }
func (r *delayRepository) LoadTree(ctx context.Context, id restic.ID) (*restic.Tree, error) { func (r *delayRepository) LoadTree(ctx context.Context, id restic.ID) (*data.Tree, error) {
if id == r.DelayTree { if id == r.DelayTree {
<-r.UnblockChannel <-r.UnblockChannel
} }
return restic.LoadTree(ctx, r.Repository, id) return data.LoadTree(ctx, r.Repository, id)
} }
func (r *delayRepository) LookupBlobSize(t restic.BlobType, id restic.ID) (uint, bool) { func (r *delayRepository) LookupBlobSize(t restic.BlobType, id restic.ID) (uint, bool) {
@@ -507,20 +508,20 @@ func TestCheckerBlobTypeConfusion(t *testing.T) {
repo := repository.TestRepository(t) repo := repository.TestRepository(t)
damagedNode := &restic.Node{ damagedNode := &data.Node{
Name: "damaged", Name: "damaged",
Type: restic.NodeTypeFile, Type: data.NodeTypeFile,
Mode: 0644, Mode: 0644,
Size: 42, Size: 42,
Content: restic.IDs{restic.TestParseID("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")}, Content: restic.IDs{restic.TestParseID("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")},
} }
damagedTree := &restic.Tree{ damagedTree := &data.Tree{
Nodes: []*restic.Node{damagedNode}, Nodes: []*data.Node{damagedNode},
} }
wg, wgCtx := errgroup.WithContext(ctx) wg, wgCtx := errgroup.WithContext(ctx)
repo.StartPackUploader(wgCtx, wg) repo.StartPackUploader(wgCtx, wg)
id, err := restic.SaveTree(ctx, repo, damagedTree) id, err := data.SaveTree(ctx, repo, damagedTree)
test.OK(t, repo.Flush(ctx)) test.OK(t, repo.Flush(ctx))
test.OK(t, err) test.OK(t, err)
@@ -532,35 +533,35 @@ func TestCheckerBlobTypeConfusion(t *testing.T) {
_, _, _, err = repo.SaveBlob(ctx, restic.DataBlob, buf, id, false) _, _, _, err = repo.SaveBlob(ctx, restic.DataBlob, buf, id, false)
test.OK(t, err) test.OK(t, err)
malNode := &restic.Node{ malNode := &data.Node{
Name: "aaaaa", Name: "aaaaa",
Type: restic.NodeTypeFile, Type: data.NodeTypeFile,
Mode: 0644, Mode: 0644,
Size: uint64(len(buf)), Size: uint64(len(buf)),
Content: restic.IDs{id}, Content: restic.IDs{id},
} }
dirNode := &restic.Node{ dirNode := &data.Node{
Name: "bbbbb", Name: "bbbbb",
Type: restic.NodeTypeDir, Type: data.NodeTypeDir,
Mode: 0755, Mode: 0755,
Subtree: &id, Subtree: &id,
} }
rootTree := &restic.Tree{ rootTree := &data.Tree{
Nodes: []*restic.Node{malNode, dirNode}, Nodes: []*data.Node{malNode, dirNode},
} }
rootID, err := restic.SaveTree(ctx, repo, rootTree) rootID, err := data.SaveTree(ctx, repo, rootTree)
test.OK(t, err) test.OK(t, err)
test.OK(t, repo.Flush(ctx)) test.OK(t, repo.Flush(ctx))
snapshot, err := restic.NewSnapshot([]string{"/damaged"}, []string{"test"}, "foo", time.Now()) snapshot, err := data.NewSnapshot([]string{"/damaged"}, []string{"test"}, "foo", time.Now())
test.OK(t, err) test.OK(t, err)
snapshot.Tree = &rootID snapshot.Tree = &rootID
snapID, err := restic.SaveSnapshot(ctx, repo, snapshot) snapID, err := data.SaveSnapshot(ctx, repo, snapshot)
test.OK(t, err) test.OK(t, err)
t.Logf("saved snapshot %v", snapID.Str()) t.Logf("saved snapshot %v", snapID.Str())
@@ -637,7 +638,7 @@ func benchmarkSnapshotScaling(t *testing.B, newSnapshots int) {
defer cleanup() defer cleanup()
snID := restic.TestParseID("51d249d28815200d59e4be7b3f21a157b864dc343353df9d8e498220c2499b02") snID := restic.TestParseID("51d249d28815200d59e4be7b3f21a157b864dc343353df9d8e498220c2499b02")
sn2, err := restic.LoadSnapshot(context.TODO(), repo, snID) sn2, err := data.LoadSnapshot(context.TODO(), repo, snID)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -645,13 +646,13 @@ func benchmarkSnapshotScaling(t *testing.B, newSnapshots int) {
treeID := sn2.Tree treeID := sn2.Tree
for i := 0; i < newSnapshots; i++ { for i := 0; i < newSnapshots; i++ {
sn, err := restic.NewSnapshot([]string{"test" + strconv.Itoa(i)}, nil, "", time.Now()) sn, err := data.NewSnapshot([]string{"test" + strconv.Itoa(i)}, nil, "", time.Now())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
sn.Tree = treeID sn.Tree = treeID
_, err = restic.SaveSnapshot(context.TODO(), repo, sn) _, err = data.SaveSnapshot(context.TODO(), repo, sn)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -1,4 +1,4 @@
package restic package data
import ( import (
"fmt" "fmt"

View File

@@ -1,4 +1,4 @@
package restic package data
import ( import (
"testing" "testing"

View File

@@ -1,23 +1,24 @@
package restic package data
import ( import (
"context" "context"
"sync" "sync"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/progress"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
// FindUsedBlobs traverses the tree ID and adds all seen blobs (trees and data // FindUsedBlobs traverses the tree ID and adds all seen blobs (trees and data
// blobs) to the set blobs. Already seen tree blobs will not be visited again. // blobs) to the set blobs. Already seen tree blobs will not be visited again.
func FindUsedBlobs(ctx context.Context, repo Loader, treeIDs IDs, blobs FindBlobSet, p *progress.Counter) error { func FindUsedBlobs(ctx context.Context, repo restic.Loader, treeIDs restic.IDs, blobs restic.FindBlobSet, p *progress.Counter) error {
var lock sync.Mutex var lock sync.Mutex
wg, ctx := errgroup.WithContext(ctx) wg, ctx := errgroup.WithContext(ctx)
treeStream := StreamTrees(ctx, wg, repo, treeIDs, func(treeID ID) bool { treeStream := StreamTrees(ctx, wg, repo, treeIDs, func(treeID restic.ID) bool {
// locking is necessary the goroutine below concurrently adds data blobs // locking is necessary the goroutine below concurrently adds data blobs
lock.Lock() lock.Lock()
h := BlobHandle{ID: treeID, Type: TreeBlob} h := restic.BlobHandle{ID: treeID, Type: restic.TreeBlob}
blobReferenced := blobs.Has(h) blobReferenced := blobs.Has(h)
// noop if already referenced // noop if already referenced
blobs.Insert(h) blobs.Insert(h)
@@ -36,7 +37,7 @@ func FindUsedBlobs(ctx context.Context, repo Loader, treeIDs IDs, blobs FindBlob
switch node.Type { switch node.Type {
case NodeTypeFile: case NodeTypeFile:
for _, blob := range node.Content { for _, blob := range node.Content {
blobs.Insert(BlobHandle{ID: blob, Type: DataBlob}) blobs.Insert(restic.BlobHandle{ID: blob, Type: restic.DataBlob})
} }
} }
} }

View File

@@ -1,4 +1,4 @@
package restic_test package data_test
import ( import (
"bufio" "bufio"
@@ -12,6 +12,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
@@ -86,9 +87,9 @@ var findTestTime = time.Unix(1469960361, 23)
func TestFindUsedBlobs(t *testing.T) { func TestFindUsedBlobs(t *testing.T) {
repo := repository.TestRepository(t) repo := repository.TestRepository(t)
var snapshots []*restic.Snapshot var snapshots []*data.Snapshot
for i := 0; i < findTestSnapshots; i++ { for i := 0; i < findTestSnapshots; i++ {
sn := restic.TestCreateSnapshot(t, repo, findTestTime.Add(time.Duration(i)*time.Second), findTestDepth) sn := data.TestCreateSnapshot(t, repo, findTestTime.Add(time.Duration(i)*time.Second), findTestDepth)
t.Logf("snapshot %v saved, tree %v", sn.ID().Str(), sn.Tree.Str()) t.Logf("snapshot %v saved, tree %v", sn.ID().Str(), sn.Tree.Str())
snapshots = append(snapshots, sn) snapshots = append(snapshots, sn)
} }
@@ -98,7 +99,7 @@ func TestFindUsedBlobs(t *testing.T) {
for i, sn := range snapshots { for i, sn := range snapshots {
usedBlobs := restic.NewBlobSet() usedBlobs := restic.NewBlobSet()
err := restic.FindUsedBlobs(context.TODO(), repo, restic.IDs{*sn.Tree}, usedBlobs, p) err := data.FindUsedBlobs(context.TODO(), repo, restic.IDs{*sn.Tree}, usedBlobs, p)
if err != nil { if err != nil {
t.Errorf("FindUsedBlobs returned error: %v", err) t.Errorf("FindUsedBlobs returned error: %v", err)
continue continue
@@ -131,7 +132,7 @@ func TestMultiFindUsedBlobs(t *testing.T) {
var snapshotTrees restic.IDs var snapshotTrees restic.IDs
for i := 0; i < findTestSnapshots; i++ { for i := 0; i < findTestSnapshots; i++ {
sn := restic.TestCreateSnapshot(t, repo, findTestTime.Add(time.Duration(i)*time.Second), findTestDepth) sn := data.TestCreateSnapshot(t, repo, findTestTime.Add(time.Duration(i)*time.Second), findTestDepth)
t.Logf("snapshot %v saved, tree %v", sn.ID().Str(), sn.Tree.Str()) t.Logf("snapshot %v saved, tree %v", sn.ID().Str(), sn.Tree.Str())
snapshotTrees = append(snapshotTrees, *sn.Tree) snapshotTrees = append(snapshotTrees, *sn.Tree)
} }
@@ -148,7 +149,7 @@ func TestMultiFindUsedBlobs(t *testing.T) {
// run twice to check progress bar handling of duplicate tree roots // run twice to check progress bar handling of duplicate tree roots
usedBlobs := restic.NewBlobSet() usedBlobs := restic.NewBlobSet()
for i := 1; i < 3; i++ { for i := 1; i < 3; i++ {
err := restic.FindUsedBlobs(context.TODO(), repo, snapshotTrees, usedBlobs, p) err := data.FindUsedBlobs(context.TODO(), repo, snapshotTrees, usedBlobs, p)
test.OK(t, err) test.OK(t, err)
v, _ := p.Get() v, _ := p.Get()
test.Equals(t, v, uint64(i*len(snapshotTrees))) test.Equals(t, v, uint64(i*len(snapshotTrees)))
@@ -177,16 +178,16 @@ func (r ForbiddenRepo) Connections() uint {
func TestFindUsedBlobsSkipsSeenBlobs(t *testing.T) { func TestFindUsedBlobsSkipsSeenBlobs(t *testing.T) {
repo := repository.TestRepository(t) repo := repository.TestRepository(t)
snapshot := restic.TestCreateSnapshot(t, repo, findTestTime, findTestDepth) snapshot := data.TestCreateSnapshot(t, repo, findTestTime, findTestDepth)
t.Logf("snapshot %v saved, tree %v", snapshot.ID().Str(), snapshot.Tree.Str()) t.Logf("snapshot %v saved, tree %v", snapshot.ID().Str(), snapshot.Tree.Str())
usedBlobs := restic.NewBlobSet() usedBlobs := restic.NewBlobSet()
err := restic.FindUsedBlobs(context.TODO(), repo, restic.IDs{*snapshot.Tree}, usedBlobs, nil) err := data.FindUsedBlobs(context.TODO(), repo, restic.IDs{*snapshot.Tree}, usedBlobs, nil)
if err != nil { if err != nil {
t.Fatalf("FindUsedBlobs returned error: %v", err) t.Fatalf("FindUsedBlobs returned error: %v", err)
} }
err = restic.FindUsedBlobs(context.TODO(), ForbiddenRepo{}, restic.IDs{*snapshot.Tree}, usedBlobs, nil) err = data.FindUsedBlobs(context.TODO(), ForbiddenRepo{}, restic.IDs{*snapshot.Tree}, usedBlobs, nil)
if err != nil { if err != nil {
t.Fatalf("FindUsedBlobs returned error: %v", err) t.Fatalf("FindUsedBlobs returned error: %v", err)
} }
@@ -195,13 +196,13 @@ func TestFindUsedBlobsSkipsSeenBlobs(t *testing.T) {
func BenchmarkFindUsedBlobs(b *testing.B) { func BenchmarkFindUsedBlobs(b *testing.B) {
repo := repository.TestRepository(b) repo := repository.TestRepository(b)
sn := restic.TestCreateSnapshot(b, repo, findTestTime, findTestDepth) sn := data.TestCreateSnapshot(b, repo, findTestTime, findTestDepth)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
blobs := restic.NewBlobSet() blobs := restic.NewBlobSet()
err := restic.FindUsedBlobs(context.TODO(), repo, restic.IDs{*sn.Tree}, blobs, nil) err := data.FindUsedBlobs(context.TODO(), repo, restic.IDs{*sn.Tree}, blobs, nil)
if err != nil { if err != nil {
b.Error(err) b.Error(err)
} }

View File

@@ -1,4 +1,4 @@
package restic package data
import ( import (
"bytes" "bytes"
@@ -13,6 +13,7 @@ import (
"unicode/utf8" "unicode/utf8"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
) )
@@ -105,8 +106,8 @@ type Node struct {
ExtendedAttributes []ExtendedAttribute `json:"extended_attributes,omitempty"` ExtendedAttributes []ExtendedAttribute `json:"extended_attributes,omitempty"`
GenericAttributes map[GenericAttributeType]json.RawMessage `json:"generic_attributes,omitempty"` GenericAttributes map[GenericAttributeType]json.RawMessage `json:"generic_attributes,omitempty"`
Device uint64 `json:"device,omitempty"` // in case of Type == "dev", stat.st_rdev Device uint64 `json:"device,omitempty"` // in case of Type == "dev", stat.st_rdev
Content IDs `json:"content"` Content restic.IDs `json:"content"`
Subtree *ID `json:"subtree,omitempty"` Subtree *restic.ID `json:"subtree,omitempty"`
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`

View File

@@ -1,4 +1,4 @@
package restic package data
import ( import (
"encoding/json" "encoding/json"

View File

@@ -1,4 +1,4 @@
package restic package data
import ( import (
"encoding/json" "encoding/json"

View File

@@ -1,4 +1,4 @@
package restic package data
import ( import (
"context" "context"
@@ -9,26 +9,27 @@ import (
"time" "time"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/restic"
) )
// Snapshot is the state of a resource at one point in time. // Snapshot is the state of a resource at one point in time.
type Snapshot struct { type Snapshot struct {
Time time.Time `json:"time"` Time time.Time `json:"time"`
Parent *ID `json:"parent,omitempty"` Parent *restic.ID `json:"parent,omitempty"`
Tree *ID `json:"tree"` Tree *restic.ID `json:"tree"`
Paths []string `json:"paths"` Paths []string `json:"paths"`
Hostname string `json:"hostname,omitempty"` Hostname string `json:"hostname,omitempty"`
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
UID uint32 `json:"uid,omitempty"` UID uint32 `json:"uid,omitempty"`
GID uint32 `json:"gid,omitempty"` GID uint32 `json:"gid,omitempty"`
Excludes []string `json:"excludes,omitempty"` Excludes []string `json:"excludes,omitempty"`
Tags []string `json:"tags,omitempty"` Tags []string `json:"tags,omitempty"`
Original *ID `json:"original,omitempty"` Original *restic.ID `json:"original,omitempty"`
ProgramVersion string `json:"program_version,omitempty"` ProgramVersion string `json:"program_version,omitempty"`
Summary *SnapshotSummary `json:"summary,omitempty"` Summary *SnapshotSummary `json:"summary,omitempty"`
id *ID // plaintext ID, used during restore id *restic.ID // plaintext ID, used during restore
} }
type SnapshotSummary struct { type SnapshotSummary struct {
@@ -79,9 +80,9 @@ func NewSnapshot(paths []string, tags []string, hostname string, time time.Time)
} }
// LoadSnapshot loads the snapshot with the id and returns it. // LoadSnapshot loads the snapshot with the id and returns it.
func LoadSnapshot(ctx context.Context, loader LoaderUnpacked, id ID) (*Snapshot, error) { func LoadSnapshot(ctx context.Context, loader restic.LoaderUnpacked, id restic.ID) (*Snapshot, error) {
sn := &Snapshot{id: &id} sn := &Snapshot{id: &id}
err := LoadJSONUnpacked(ctx, loader, SnapshotFile, id, sn) err := restic.LoadJSONUnpacked(ctx, loader, restic.SnapshotFile, id, sn)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load snapshot %v: %w", id.Str(), err) return nil, fmt.Errorf("failed to load snapshot %v: %w", id.Str(), err)
} }
@@ -90,8 +91,8 @@ func LoadSnapshot(ctx context.Context, loader LoaderUnpacked, id ID) (*Snapshot,
} }
// SaveSnapshot saves the snapshot sn and returns its ID. // SaveSnapshot saves the snapshot sn and returns its ID.
func SaveSnapshot(ctx context.Context, repo SaverUnpacked[WriteableFileType], sn *Snapshot) (ID, error) { func SaveSnapshot(ctx context.Context, repo restic.SaverUnpacked[restic.WriteableFileType], sn *Snapshot) (restic.ID, error) {
return SaveJSONUnpacked(ctx, repo, WriteableSnapshotFile, sn) return restic.SaveJSONUnpacked(ctx, repo, restic.WriteableSnapshotFile, sn)
} }
// ForAllSnapshots reads all snapshots in parallel and calls the // ForAllSnapshots reads all snapshots in parallel and calls the
@@ -99,11 +100,11 @@ func SaveSnapshot(ctx context.Context, repo SaverUnpacked[WriteableFileType], sn
// If the called function returns an error, this function is cancelled and // If the called function returns an error, this function is cancelled and
// also returns this error. // also returns this error.
// If a snapshot ID is in excludeIDs, it will be ignored. // If a snapshot ID is in excludeIDs, it will be ignored.
func ForAllSnapshots(ctx context.Context, be Lister, loader LoaderUnpacked, excludeIDs IDSet, fn func(ID, *Snapshot, error) error) error { func ForAllSnapshots(ctx context.Context, be restic.Lister, loader restic.LoaderUnpacked, excludeIDs restic.IDSet, fn func(restic.ID, *Snapshot, error) error) error {
var m sync.Mutex var m sync.Mutex
// For most snapshots decoding is nearly for free, thus just assume were only limited by IO // For most snapshots decoding is nearly for free, thus just assume were only limited by IO
return ParallelList(ctx, be, SnapshotFile, loader.Connections(), func(ctx context.Context, id ID, _ int64) error { return restic.ParallelList(ctx, be, restic.SnapshotFile, loader.Connections(), func(ctx context.Context, id restic.ID, _ int64) error {
if excludeIDs.Has(id) { if excludeIDs.Has(id) {
return nil return nil
} }
@@ -121,7 +122,7 @@ func (sn Snapshot) String() string {
} }
// ID returns the snapshot's ID. // ID returns the snapshot's ID.
func (sn Snapshot) ID() *ID { func (sn Snapshot) ID() *restic.ID {
return sn.id return sn.id
} }
@@ -133,7 +134,7 @@ func (sn *Snapshot) fillUserInfo() error {
sn.Username = usr.Username sn.Username = usr.Username
// set userid and groupid // set userid and groupid
sn.UID, sn.GID, err = uidGidInt(usr) sn.UID, sn.GID, err = restic.UidGidInt(usr)
return err return err
} }

View File

@@ -1,4 +1,4 @@
package restic package data
import ( import (
"context" "context"
@@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/restic"
) )
// ErrNoSnapshotFound is returned when no snapshot for the given criteria could be found. // ErrNoSnapshotFound is returned when no snapshot for the given criteria could be found.
@@ -34,7 +35,7 @@ func (f *SnapshotFilter) matches(sn *Snapshot) bool {
// findLatest finds the latest snapshot with optional target/directory, // findLatest finds the latest snapshot with optional target/directory,
// tags, hostname, and timestamp filters. // tags, hostname, and timestamp filters.
func (f *SnapshotFilter) findLatest(ctx context.Context, be Lister, loader LoaderUnpacked) (*Snapshot, error) { func (f *SnapshotFilter) findLatest(ctx context.Context, be restic.Lister, loader restic.LoaderUnpacked) (*Snapshot, error) {
var err error var err error
absTargets := make([]string, 0, len(f.Paths)) absTargets := make([]string, 0, len(f.Paths))
@@ -51,7 +52,7 @@ func (f *SnapshotFilter) findLatest(ctx context.Context, be Lister, loader Loade
var latest *Snapshot var latest *Snapshot
err = ForAllSnapshots(ctx, be, loader, nil, func(id ID, snapshot *Snapshot, err error) error { err = ForAllSnapshots(ctx, be, loader, nil, func(id restic.ID, snapshot *Snapshot, err error) error {
if err != nil { if err != nil {
return errors.Errorf("Error loading snapshot %v: %v", id.Str(), err) return errors.Errorf("Error loading snapshot %v: %v", id.Str(), err)
} }
@@ -90,14 +91,14 @@ func splitSnapshotID(s string) (id, subfolder string) {
// FindSnapshot takes a string and tries to find a snapshot whose ID matches // FindSnapshot takes a string and tries to find a snapshot whose ID matches
// the string as closely as possible. // the string as closely as possible.
func FindSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, s string) (*Snapshot, string, error) { func FindSnapshot(ctx context.Context, be restic.Lister, loader restic.LoaderUnpacked, s string) (*Snapshot, string, error) {
s, subfolder := splitSnapshotID(s) s, subfolder := splitSnapshotID(s)
// no need to list snapshots if `s` is already a full id // no need to list snapshots if `s` is already a full id
id, err := ParseID(s) id, err := restic.ParseID(s)
if err != nil { if err != nil {
// find snapshot id with prefix // find snapshot id with prefix
id, err = Find(ctx, be, SnapshotFile, s) id, err = restic.Find(ctx, be, restic.SnapshotFile, s)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
@@ -108,7 +109,7 @@ func FindSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, s strin
// FindLatest returns either the latest of a filtered list of all snapshots // FindLatest returns either the latest of a filtered list of all snapshots
// or a snapshot specified by `snapshotID`. // or a snapshot specified by `snapshotID`.
func (f *SnapshotFilter) FindLatest(ctx context.Context, be Lister, loader LoaderUnpacked, snapshotID string) (*Snapshot, string, error) { func (f *SnapshotFilter) FindLatest(ctx context.Context, be restic.Lister, loader restic.LoaderUnpacked, snapshotID string) (*Snapshot, string, error) {
id, subfolder := splitSnapshotID(snapshotID) id, subfolder := splitSnapshotID(snapshotID)
if id == "latest" { if id == "latest" {
sn, err := f.findLatest(ctx, be, loader) sn, err := f.findLatest(ctx, be, loader)
@@ -126,12 +127,12 @@ type SnapshotFindCb func(string, *Snapshot, error) error
var ErrInvalidSnapshotSyntax = errors.New("<snapshot>:<subfolder> syntax not allowed") var ErrInvalidSnapshotSyntax = errors.New("<snapshot>:<subfolder> syntax not allowed")
// FindAll yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots. // FindAll yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots.
func (f *SnapshotFilter) FindAll(ctx context.Context, be Lister, loader LoaderUnpacked, snapshotIDs []string, fn SnapshotFindCb) error { func (f *SnapshotFilter) FindAll(ctx context.Context, be restic.Lister, loader restic.LoaderUnpacked, snapshotIDs []string, fn SnapshotFindCb) error {
if len(snapshotIDs) != 0 { if len(snapshotIDs) != 0 {
var err error var err error
usedFilter := false usedFilter := false
ids := NewIDSet() ids := restic.NewIDSet()
// Process all snapshot IDs given as arguments. // Process all snapshot IDs given as arguments.
for _, s := range snapshotIDs { for _, s := range snapshotIDs {
if ctx.Err() != nil { if ctx.Err() != nil {
@@ -183,7 +184,7 @@ func (f *SnapshotFilter) FindAll(ctx context.Context, be Lister, loader LoaderUn
return nil return nil
} }
return ForAllSnapshots(ctx, be, loader, nil, func(id ID, sn *Snapshot, err error) error { return ForAllSnapshots(ctx, be, loader, nil, func(id restic.ID, sn *Snapshot, err error) error {
if err == nil && !f.matches(sn) { if err == nil && !f.matches(sn) {
return nil return nil
} }

View File

@@ -1,21 +1,21 @@
package restic_test package data_test
import ( import (
"context" "context"
"testing" "testing"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/test" "github.com/restic/restic/internal/test"
) )
func TestFindLatestSnapshot(t *testing.T) { func TestFindLatestSnapshot(t *testing.T) {
repo := repository.TestRepository(t) repo := repository.TestRepository(t)
restic.TestCreateSnapshot(t, repo, parseTimeUTC("2015-05-05 05:05:05"), 1) data.TestCreateSnapshot(t, repo, parseTimeUTC("2015-05-05 05:05:05"), 1)
restic.TestCreateSnapshot(t, repo, parseTimeUTC("2017-07-07 07:07:07"), 1) data.TestCreateSnapshot(t, repo, parseTimeUTC("2017-07-07 07:07:07"), 1)
latestSnapshot := restic.TestCreateSnapshot(t, repo, parseTimeUTC("2019-09-09 09:09:09"), 1) latestSnapshot := data.TestCreateSnapshot(t, repo, parseTimeUTC("2019-09-09 09:09:09"), 1)
f := restic.SnapshotFilter{Hosts: []string{"foo"}} f := data.SnapshotFilter{Hosts: []string{"foo"}}
sn, _, err := f.FindLatest(context.TODO(), repo, repo, "latest") sn, _, err := f.FindLatest(context.TODO(), repo, repo, "latest")
if err != nil { if err != nil {
t.Fatalf("FindLatest returned error: %v", err) t.Fatalf("FindLatest returned error: %v", err)
@@ -28,11 +28,11 @@ func TestFindLatestSnapshot(t *testing.T) {
func TestFindLatestSnapshotWithMaxTimestamp(t *testing.T) { func TestFindLatestSnapshotWithMaxTimestamp(t *testing.T) {
repo := repository.TestRepository(t) repo := repository.TestRepository(t)
restic.TestCreateSnapshot(t, repo, parseTimeUTC("2015-05-05 05:05:05"), 1) data.TestCreateSnapshot(t, repo, parseTimeUTC("2015-05-05 05:05:05"), 1)
desiredSnapshot := restic.TestCreateSnapshot(t, repo, parseTimeUTC("2017-07-07 07:07:07"), 1) desiredSnapshot := data.TestCreateSnapshot(t, repo, parseTimeUTC("2017-07-07 07:07:07"), 1)
restic.TestCreateSnapshot(t, repo, parseTimeUTC("2019-09-09 09:09:09"), 1) data.TestCreateSnapshot(t, repo, parseTimeUTC("2019-09-09 09:09:09"), 1)
sn, _, err := (&restic.SnapshotFilter{ sn, _, err := (&data.SnapshotFilter{
Hosts: []string{"foo"}, Hosts: []string{"foo"},
TimestampLimit: parseTimeUTC("2018-08-08 08:08:08"), TimestampLimit: parseTimeUTC("2018-08-08 08:08:08"),
}).FindLatest(context.TODO(), repo, repo, "latest") }).FindLatest(context.TODO(), repo, repo, "latest")
@@ -47,8 +47,8 @@ func TestFindLatestSnapshotWithMaxTimestamp(t *testing.T) {
func TestFindLatestWithSubpath(t *testing.T) { func TestFindLatestWithSubpath(t *testing.T) {
repo := repository.TestRepository(t) repo := repository.TestRepository(t)
restic.TestCreateSnapshot(t, repo, parseTimeUTC("2015-05-05 05:05:05"), 1) data.TestCreateSnapshot(t, repo, parseTimeUTC("2015-05-05 05:05:05"), 1)
desiredSnapshot := restic.TestCreateSnapshot(t, repo, parseTimeUTC("2017-07-07 07:07:07"), 1) desiredSnapshot := data.TestCreateSnapshot(t, repo, parseTimeUTC("2017-07-07 07:07:07"), 1)
for _, exp := range []struct { for _, exp := range []struct {
query string query string
@@ -62,7 +62,7 @@ func TestFindLatestWithSubpath(t *testing.T) {
{desiredSnapshot.ID().String() + ":subfolder", "subfolder"}, {desiredSnapshot.ID().String() + ":subfolder", "subfolder"},
} { } {
t.Run("", func(t *testing.T) { t.Run("", func(t *testing.T) {
sn, subfolder, err := (&restic.SnapshotFilter{}).FindLatest(context.TODO(), repo, repo, exp.query) sn, subfolder, err := (&data.SnapshotFilter{}).FindLatest(context.TODO(), repo, repo, exp.query)
if err != nil { if err != nil {
t.Fatalf("FindLatest returned error: %v", err) t.Fatalf("FindLatest returned error: %v", err)
} }
@@ -75,13 +75,13 @@ func TestFindLatestWithSubpath(t *testing.T) {
func TestFindAllSubpathError(t *testing.T) { func TestFindAllSubpathError(t *testing.T) {
repo := repository.TestRepository(t) repo := repository.TestRepository(t)
desiredSnapshot := restic.TestCreateSnapshot(t, repo, parseTimeUTC("2017-07-07 07:07:07"), 1) desiredSnapshot := data.TestCreateSnapshot(t, repo, parseTimeUTC("2017-07-07 07:07:07"), 1)
count := 0 count := 0
test.OK(t, (&restic.SnapshotFilter{}).FindAll(context.TODO(), repo, repo, test.OK(t, (&data.SnapshotFilter{}).FindAll(context.TODO(), repo, repo,
[]string{"latest:subfolder", desiredSnapshot.ID().Str() + ":subfolder"}, []string{"latest:subfolder", desiredSnapshot.ID().Str() + ":subfolder"},
func(id string, sn *restic.Snapshot, err error) error { func(id string, sn *data.Snapshot, err error) error {
if err == restic.ErrInvalidSnapshotSyntax { if err == data.ErrInvalidSnapshotSyntax {
count++ count++
return nil return nil
} }

View File

@@ -1,4 +1,4 @@
package restic package data
import ( import (
"encoding/json" "encoding/json"

View File

@@ -1,41 +1,41 @@
package restic_test package data_test
import ( import (
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/test" "github.com/restic/restic/internal/test"
) )
func TestGroupByOptions(t *testing.T) { func TestGroupByOptions(t *testing.T) {
for _, exp := range []struct { for _, exp := range []struct {
from string from string
opts restic.SnapshotGroupByOptions opts data.SnapshotGroupByOptions
normalized string normalized string
}{ }{
{ {
from: "", from: "",
opts: restic.SnapshotGroupByOptions{}, opts: data.SnapshotGroupByOptions{},
normalized: "", normalized: "",
}, },
{ {
from: "host,paths", from: "host,paths",
opts: restic.SnapshotGroupByOptions{Host: true, Path: true}, opts: data.SnapshotGroupByOptions{Host: true, Path: true},
normalized: "host,paths", normalized: "host,paths",
}, },
{ {
from: "host,path,tag", from: "host,path,tag",
opts: restic.SnapshotGroupByOptions{Host: true, Path: true, Tag: true}, opts: data.SnapshotGroupByOptions{Host: true, Path: true, Tag: true},
normalized: "host,paths,tags", normalized: "host,paths,tags",
}, },
{ {
from: "hosts,paths,tags", from: "hosts,paths,tags",
opts: restic.SnapshotGroupByOptions{Host: true, Path: true, Tag: true}, opts: data.SnapshotGroupByOptions{Host: true, Path: true, Tag: true},
normalized: "host,paths,tags", normalized: "host,paths,tags",
}, },
} { } {
var opts restic.SnapshotGroupByOptions var opts data.SnapshotGroupByOptions
test.OK(t, opts.Set(exp.from)) test.OK(t, opts.Set(exp.from))
if !cmp.Equal(opts, exp.opts) { if !cmp.Equal(opts, exp.opts) {
t.Errorf("unexpected opts %s", cmp.Diff(opts, exp.opts)) t.Errorf("unexpected opts %s", cmp.Diff(opts, exp.opts))
@@ -43,7 +43,7 @@ func TestGroupByOptions(t *testing.T) {
test.Equals(t, opts.String(), exp.normalized) test.Equals(t, opts.String(), exp.normalized)
} }
var opts restic.SnapshotGroupByOptions var opts data.SnapshotGroupByOptions
err := opts.Set("tags,invalid") err := opts.Set("tags,invalid")
test.Assert(t, err != nil, "missing error on invalid tags") test.Assert(t, err != nil, "missing error on invalid tags")
test.Assert(t, !opts.Host && !opts.Path && !opts.Tag, "unexpected opts %s %s %s", opts.Host, opts.Path, opts.Tag) test.Assert(t, !opts.Host && !opts.Path && !opts.Tag, "unexpected opts %s %s %s", opts.Host, opts.Path, opts.Tag)

View File

@@ -1,4 +1,4 @@
package restic package data
import ( import (
"fmt" "fmt"

View File

@@ -1,4 +1,4 @@
package restic_test package data_test
import ( import (
"encoding/json" "encoding/json"
@@ -10,7 +10,7 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/data"
) )
func parseTimeUTC(s string) time.Time { func parseTimeUTC(s string) time.Time {
@@ -24,7 +24,7 @@ func parseTimeUTC(s string) time.Time {
// Returns the maximum number of snapshots to be kept according to this policy. // Returns the maximum number of snapshots to be kept according to this policy.
// If any of the counts is -1 it will return 0. // If any of the counts is -1 it will return 0.
func policySum(e *restic.ExpirePolicy) int { func policySum(e *data.ExpirePolicy) int {
if e.Last == -1 || e.Hourly == -1 || e.Daily == -1 || e.Weekly == -1 || e.Monthly == -1 || e.Yearly == -1 { if e.Last == -1 || e.Hourly == -1 || e.Daily == -1 || e.Weekly == -1 || e.Monthly == -1 || e.Yearly == -1 {
return 0 return 0
} }
@@ -36,11 +36,11 @@ func TestExpireSnapshotOps(t *testing.T) {
data := []struct { data := []struct {
expectEmpty bool expectEmpty bool
expectSum int expectSum int
p *restic.ExpirePolicy p *data.ExpirePolicy
}{ }{
{true, 0, &restic.ExpirePolicy{}}, {true, 0, &data.ExpirePolicy{}},
{true, 0, &restic.ExpirePolicy{Tags: []restic.TagList{}}}, {true, 0, &data.ExpirePolicy{Tags: []data.TagList{}}},
{false, 22, &restic.ExpirePolicy{Daily: 7, Weekly: 2, Monthly: 3, Yearly: 10}}, {false, 22, &data.ExpirePolicy{Daily: 7, Weekly: 2, Monthly: 3, Yearly: 10}},
} }
for i, d := range data { for i, d := range data {
isEmpty := d.p.Empty() isEmpty := d.p.Empty()
@@ -57,8 +57,8 @@ func TestExpireSnapshotOps(t *testing.T) {
// ApplyPolicyResult is used to marshal/unmarshal the golden files for // ApplyPolicyResult is used to marshal/unmarshal the golden files for
// TestApplyPolicy. // TestApplyPolicy.
type ApplyPolicyResult struct { type ApplyPolicyResult struct {
Keep restic.Snapshots `json:"keep"` Keep data.Snapshots `json:"keep"`
Reasons []restic.KeepReason `json:"reasons,omitempty"` Reasons []data.KeepReason `json:"reasons,omitempty"`
} }
func loadGoldenFile(t testing.TB, filename string) (res ApplyPolicyResult) { func loadGoldenFile(t testing.TB, filename string) (res ApplyPolicyResult) {
@@ -75,7 +75,7 @@ func loadGoldenFile(t testing.TB, filename string) (res ApplyPolicyResult) {
return res return res
} }
func saveGoldenFile(t testing.TB, filename string, keep restic.Snapshots, reasons []restic.KeepReason) { func saveGoldenFile(t testing.TB, filename string, keep data.Snapshots, reasons []data.KeepReason) {
res := ApplyPolicyResult{ res := ApplyPolicyResult{
Keep: keep, Keep: keep,
Reasons: reasons, Reasons: reasons,
@@ -92,7 +92,7 @@ func saveGoldenFile(t testing.TB, filename string, keep restic.Snapshots, reason
} }
func TestApplyPolicy(t *testing.T) { func TestApplyPolicy(t *testing.T) {
var testExpireSnapshots = restic.Snapshots{ var testExpireSnapshots = data.Snapshots{
{Time: parseTimeUTC("2014-09-01 10:20:30")}, {Time: parseTimeUTC("2014-09-01 10:20:30")},
{Time: parseTimeUTC("2014-09-02 10:20:30")}, {Time: parseTimeUTC("2014-09-02 10:20:30")},
{Time: parseTimeUTC("2014-09-05 10:20:30")}, {Time: parseTimeUTC("2014-09-05 10:20:30")},
@@ -198,7 +198,7 @@ func TestApplyPolicy(t *testing.T) {
{Time: parseTimeUTC("2016-01-18 12:02:03")}, {Time: parseTimeUTC("2016-01-18 12:02:03")},
} }
var tests = []restic.ExpirePolicy{ var tests = []data.ExpirePolicy{
{}, {},
{Last: 10}, {Last: 10},
{Last: 15}, {Last: 15},
@@ -217,29 +217,29 @@ func TestApplyPolicy(t *testing.T) {
{Daily: 2, Weekly: 2, Monthly: 6}, {Daily: 2, Weekly: 2, Monthly: 6},
{Yearly: 10}, {Yearly: 10},
{Daily: 7, Weekly: 2, Monthly: 3, Yearly: 10}, {Daily: 7, Weekly: 2, Monthly: 3, Yearly: 10},
{Tags: []restic.TagList{{"foo"}}}, {Tags: []data.TagList{{"foo"}}},
{Tags: []restic.TagList{{"foo", "bar"}}}, {Tags: []data.TagList{{"foo", "bar"}}},
{Tags: []restic.TagList{{"foo"}, {"bar"}}}, {Tags: []data.TagList{{"foo"}, {"bar"}}},
{Within: restic.ParseDurationOrPanic("1d")}, {Within: data.ParseDurationOrPanic("1d")},
{Within: restic.ParseDurationOrPanic("2d")}, {Within: data.ParseDurationOrPanic("2d")},
{Within: restic.ParseDurationOrPanic("7d")}, {Within: data.ParseDurationOrPanic("7d")},
{Within: restic.ParseDurationOrPanic("1m")}, {Within: data.ParseDurationOrPanic("1m")},
{Within: restic.ParseDurationOrPanic("1m14d")}, {Within: data.ParseDurationOrPanic("1m14d")},
{Within: restic.ParseDurationOrPanic("1y1d1m")}, {Within: data.ParseDurationOrPanic("1y1d1m")},
{Within: restic.ParseDurationOrPanic("13d23h")}, {Within: data.ParseDurationOrPanic("13d23h")},
{Within: restic.ParseDurationOrPanic("2m2h")}, {Within: data.ParseDurationOrPanic("2m2h")},
{Within: restic.ParseDurationOrPanic("1y2m3d3h")}, {Within: data.ParseDurationOrPanic("1y2m3d3h")},
{WithinHourly: restic.ParseDurationOrPanic("1y2m3d3h")}, {WithinHourly: data.ParseDurationOrPanic("1y2m3d3h")},
{WithinDaily: restic.ParseDurationOrPanic("1y2m3d3h")}, {WithinDaily: data.ParseDurationOrPanic("1y2m3d3h")},
{WithinWeekly: restic.ParseDurationOrPanic("1y2m3d3h")}, {WithinWeekly: data.ParseDurationOrPanic("1y2m3d3h")},
{WithinMonthly: restic.ParseDurationOrPanic("1y2m3d3h")}, {WithinMonthly: data.ParseDurationOrPanic("1y2m3d3h")},
{WithinYearly: restic.ParseDurationOrPanic("1y2m3d3h")}, {WithinYearly: data.ParseDurationOrPanic("1y2m3d3h")},
{Within: restic.ParseDurationOrPanic("1h"), {Within: data.ParseDurationOrPanic("1h"),
WithinHourly: restic.ParseDurationOrPanic("1d"), WithinHourly: data.ParseDurationOrPanic("1d"),
WithinDaily: restic.ParseDurationOrPanic("7d"), WithinDaily: data.ParseDurationOrPanic("7d"),
WithinWeekly: restic.ParseDurationOrPanic("1m"), WithinWeekly: data.ParseDurationOrPanic("1m"),
WithinMonthly: restic.ParseDurationOrPanic("1y"), WithinMonthly: data.ParseDurationOrPanic("1y"),
WithinYearly: restic.ParseDurationOrPanic("9999y")}, WithinYearly: data.ParseDurationOrPanic("9999y")},
{Last: -1}, // keep all {Last: -1}, // keep all
{Last: -1, Hourly: -1}, // keep all (Last overrides Hourly) {Last: -1, Hourly: -1}, // keep all (Last overrides Hourly)
{Hourly: -1}, // keep all hourlies {Hourly: -1}, // keep all hourlies
@@ -249,7 +249,7 @@ func TestApplyPolicy(t *testing.T) {
for i, p := range tests { for i, p := range tests {
t.Run("", func(t *testing.T) { t.Run("", func(t *testing.T) {
keep, remove, reasons := restic.ApplyPolicy(testExpireSnapshots, p) keep, remove, reasons := data.ApplyPolicy(testExpireSnapshots, p)
if len(keep)+len(remove) != len(testExpireSnapshots) { if len(keep)+len(remove) != len(testExpireSnapshots) {
t.Errorf("len(keep)+len(remove) = %d != len(testExpireSnapshots) = %d", t.Errorf("len(keep)+len(remove) = %d != len(testExpireSnapshots) = %d",
@@ -273,7 +273,7 @@ func TestApplyPolicy(t *testing.T) {
want := loadGoldenFile(t, goldenFilename) want := loadGoldenFile(t, goldenFilename)
cmpOpts := cmpopts.IgnoreUnexported(restic.Snapshot{}) cmpOpts := cmpopts.IgnoreUnexported(data.Snapshot{})
if !cmp.Equal(want.Keep, keep, cmpOpts) { if !cmp.Equal(want.Keep, keep, cmpOpts) {
t.Error(cmp.Diff(want.Keep, keep, cmpOpts)) t.Error(cmp.Diff(want.Keep, keep, cmpOpts))

View File

@@ -1,19 +1,19 @@
package restic_test package data_test
import ( import (
"context" "context"
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func TestNewSnapshot(t *testing.T) { func TestNewSnapshot(t *testing.T) {
paths := []string{"/home/foobar"} paths := []string{"/home/foobar"}
_, err := restic.NewSnapshot(paths, nil, "foo", time.Now()) _, err := data.NewSnapshot(paths, nil, "foo", time.Now())
rtest.OK(t, err) rtest.OK(t, err)
} }
@@ -21,7 +21,7 @@ func TestTagList(t *testing.T) {
paths := []string{"/home/foobar"} paths := []string{"/home/foobar"}
tags := []string{""} tags := []string{""}
sn, _ := restic.NewSnapshot(paths, nil, "foo", time.Now()) sn, _ := data.NewSnapshot(paths, nil, "foo", time.Now())
r := sn.HasTags(tags) r := sn.HasTags(tags)
rtest.Assert(t, r, "Failed to match untagged snapshot") rtest.Assert(t, r, "Failed to match untagged snapshot")
@@ -35,15 +35,15 @@ func testLoadJSONUnpacked(t *testing.T, version uint) {
repo, _, _ := repository.TestRepositoryWithVersion(t, version) repo, _, _ := repository.TestRepositoryWithVersion(t, version)
// archive a snapshot // archive a snapshot
sn := restic.Snapshot{} sn := data.Snapshot{}
sn.Hostname = "foobar" sn.Hostname = "foobar"
sn.Username = "test!" sn.Username = "test!"
id, err := restic.SaveSnapshot(context.TODO(), repo, &sn) id, err := data.SaveSnapshot(context.TODO(), repo, &sn)
rtest.OK(t, err) rtest.OK(t, err)
// restore // restore
sn2, err := restic.LoadSnapshot(context.TODO(), repo, id) sn2, err := data.LoadSnapshot(context.TODO(), repo, id)
rtest.OK(t, err) rtest.OK(t, err)
rtest.Equals(t, sn.Hostname, sn2.Hostname) rtest.Equals(t, sn.Hostname, sn2.Hostname)

View File

@@ -1,4 +1,4 @@
package restic package data
import ( import (
"fmt" "fmt"

View File

@@ -1,4 +1,4 @@
package restic package data
import ( import (
"testing" "testing"

Some files were not shown because too many files have changed in this diff Show More