forked from TrueCloudLab/restic
stats: hardlinks only reduce restore within a snapshot
The `stats` command checks inodes to not count hardlinked files multiple times into the restore size. This check applies across all snapshots and not only within snapshots. As a result the result size was far too low when calculating it for multiple snapshots and it would vary depending on the order in which snapshots were listed.
This commit is contained in:
parent
cc8a03b1d0
commit
2c07f7fff3
2 changed files with 13 additions and 9 deletions
8
changelog/unreleased/issue-3736
Normal file
8
changelog/unreleased/issue-3736
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Bugfix: `stats` fix restore size calculation for multiple snapshots
|
||||||
|
|
||||||
|
Since restic 0.10.0 the restore size calculated by the `stats` command for
|
||||||
|
multiple snapshots was too low. The hardlink detection was accidentally applied
|
||||||
|
across multiple snapshots and thus ignored many files. This has been fixed.
|
||||||
|
|
||||||
|
https://github.com/restic/restic/issues/3736
|
||||||
|
https://github.com/restic/restic/pull/3740
|
|
@ -111,7 +111,6 @@ func runStats(gopts GlobalOptions, args []string) error {
|
||||||
// create a container for the stats (and other needed state)
|
// create a container for the stats (and other needed state)
|
||||||
stats := &statsContainer{
|
stats := &statsContainer{
|
||||||
uniqueFiles: make(map[fileID]struct{}),
|
uniqueFiles: make(map[fileID]struct{}),
|
||||||
uniqueInodes: make(map[uint64]struct{}),
|
|
||||||
fileBlobs: make(map[string]restic.IDSet),
|
fileBlobs: make(map[string]restic.IDSet),
|
||||||
blobs: restic.NewBlobSet(),
|
blobs: restic.NewBlobSet(),
|
||||||
snapshotsCount: 0,
|
snapshotsCount: 0,
|
||||||
|
@ -175,7 +174,8 @@ func statsWalkSnapshot(ctx context.Context, snapshot *restic.Snapshot, repo rest
|
||||||
return restic.FindUsedBlobs(ctx, repo, restic.IDs{*snapshot.Tree}, stats.blobs, nil)
|
return restic.FindUsedBlobs(ctx, repo, restic.IDs{*snapshot.Tree}, stats.blobs, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := walker.Walk(ctx, repo, *snapshot.Tree, restic.NewIDSet(), statsWalkTree(repo, stats))
|
uniqueInodes := make(map[uint64]struct{})
|
||||||
|
err := walker.Walk(ctx, repo, *snapshot.Tree, restic.NewIDSet(), statsWalkTree(repo, stats, uniqueInodes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("walking tree %s: %v", *snapshot.Tree, err)
|
return fmt.Errorf("walking tree %s: %v", *snapshot.Tree, err)
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ func statsWalkSnapshot(ctx context.Context, snapshot *restic.Snapshot, repo rest
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func statsWalkTree(repo restic.Repository, stats *statsContainer) walker.WalkFunc {
|
func statsWalkTree(repo restic.Repository, stats *statsContainer, uniqueInodes map[uint64]struct{}) walker.WalkFunc {
|
||||||
return func(parentTreeID restic.ID, npath string, node *restic.Node, nodeErr error) (bool, error) {
|
return func(parentTreeID restic.ID, npath string, node *restic.Node, nodeErr error) (bool, error) {
|
||||||
if nodeErr != nil {
|
if nodeErr != nil {
|
||||||
return true, nodeErr
|
return true, nodeErr
|
||||||
|
@ -242,8 +242,8 @@ func statsWalkTree(repo restic.Repository, stats *statsContainer) walker.WalkFun
|
||||||
|
|
||||||
// if inodes are present, only count each inode once
|
// if inodes are present, only count each inode once
|
||||||
// (hard links do not increase restore size)
|
// (hard links do not increase restore size)
|
||||||
if _, ok := stats.uniqueInodes[node.Inode]; !ok || node.Inode == 0 {
|
if _, ok := uniqueInodes[node.Inode]; !ok || node.Inode == 0 {
|
||||||
stats.uniqueInodes[node.Inode] = struct{}{}
|
uniqueInodes[node.Inode] = struct{}{}
|
||||||
stats.TotalSize += node.Size
|
stats.TotalSize += node.Size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,10 +290,6 @@ type statsContainer struct {
|
||||||
// contents (hashed sequence of content blob IDs)
|
// contents (hashed sequence of content blob IDs)
|
||||||
uniqueFiles map[fileID]struct{}
|
uniqueFiles map[fileID]struct{}
|
||||||
|
|
||||||
// uniqueInodes marks visited files according to their
|
|
||||||
// inode # (hashed sequence of inode numbers)
|
|
||||||
uniqueInodes map[uint64]struct{}
|
|
||||||
|
|
||||||
// fileBlobs maps a file name (path) to the set of
|
// fileBlobs maps a file name (path) to the set of
|
||||||
// blobs that have been seen as a part of the file
|
// blobs that have been seen as a part of the file
|
||||||
fileBlobs map[string]restic.IDSet
|
fileBlobs map[string]restic.IDSet
|
||||||
|
|
Loading…
Reference in a new issue