1
0
Fork 0
mirror of https://github.com/restic/restic.git synced 2025-03-09 00:00:02 +01:00

Add support for restoring ads

This commit is contained in:
aneesh-n 2024-12-03 17:59:40 +05:30
parent da4a84c4fb
commit 400b0a3958
No known key found for this signature in database
GPG key ID: 6F5A52831C046F44
4 changed files with 59 additions and 20 deletions

View file

@ -5,6 +5,8 @@ import (
"path/filepath"
"sync"
"encoding/json"
"golang.org/x/sync/errgroup"
"github.com/restic/restic/internal/debug"
@ -27,6 +29,7 @@ type fileInfo struct {
location string // file on local filesystem relative to restorer basedir
blobs interface{} // blobs of the file
state *fileState
attrs map[restic.GenericAttributeType]json.RawMessage
}
type fileBlobInfo struct {
@ -85,8 +88,8 @@ func newFileRestorer(dst string,
}
}
func (r *fileRestorer) addFile(location string, content restic.IDs, size int64, state *fileState) {
r.files = append(r.files, &fileInfo{location: location, blobs: content, size: size, state: state})
func (r *fileRestorer) addFile(location string, content restic.IDs, size int64, state *fileState, attrs map[restic.GenericAttributeType]json.RawMessage) {
r.files = append(r.files, &fileInfo{location: location, blobs: content, size: size, state: state, attrs: attrs})
}
func (r *fileRestorer) targetPath(location string) string {
@ -178,7 +181,7 @@ func (r *fileRestorer) restoreFiles(ctx context.Context) error {
// empty file or one with already uptodate content. Make sure that the file size is correct
if !restoredBlobs {
err := r.truncateFileToSize(file.location, file.size)
err := r.truncateFileToSize(file)
if errFile := r.sanitizeError(file, err); errFile != nil {
return errFile
}
@ -227,8 +230,8 @@ func (r *fileRestorer) restoreFiles(ctx context.Context) error {
return wg.Wait()
}
func (r *fileRestorer) truncateFileToSize(location string, size int64) error {
f, err := createFile(r.targetPath(location), size, false, r.allowRecursiveDelete)
func (r *fileRestorer) truncateFileToSize(file *fileInfo) error {
f, err := createOrOpenFile(r.targetPath(file.location), file.size, file, r.allowRecursiveDelete)
if err != nil {
return err
}
@ -358,7 +361,7 @@ func (r *fileRestorer) downloadBlobs(ctx context.Context, packID restic.ID,
file.inProgress = true
createSize = file.size
}
writeErr := r.filesWriter.writeToFile(r.targetPath(file.location), blobData, offset, createSize, file.sparse)
writeErr := r.filesWriter.writeToFile(r.targetPath(file.location), blobData, offset, createSize, file)
r.reportBlobProgress(file, uint64(len(blobData)))
return writeErr
}
@ -377,5 +380,5 @@ func (r *fileRestorer) reportBlobProgress(file *fileInfo, blobSize uint64) {
if file.state == nil {
action = restore.ActionFileRestored
}
r.progress.AddProgress(file.location, action, uint64(blobSize), uint64(file.size))
r.progress.AddProgress(file.location, action, uint64(blobSize), uint64(file.size), file.attrs)
}

View file

@ -279,7 +279,7 @@ func (res *Restorer) restoreNodeTo(node *restic.Node, target, location string) e
}
}
res.opts.Progress.AddProgress(location, restoreui.ActionOtherRestored, 0, 0)
res.opts.Progress.AddProgress(location, restoreui.ActionOtherRestored, 0, 0, node.GenericAttributes)
return res.restoreNodeMetadataTo(node, target, location)
}
@ -306,7 +306,7 @@ func (res *Restorer) restoreHardlinkAt(node *restic.Node, target, path, location
}
}
res.opts.Progress.AddProgress(location, restoreui.ActionOtherRestored, 0, 0)
res.opts.Progress.AddProgress(location, restoreui.ActionOtherRestored, 0, 0, node.GenericAttributes)
// TODO investigate if hardlinks have separate metadata on any supported system
return res.restoreNodeMetadataTo(node, path, location)
}
@ -363,10 +363,10 @@ func (res *Restorer) RestoreTo(ctx context.Context, dst string) (uint64, error)
// first tree pass: create directories and collect all files to restore
err = res.traverseTree(ctx, dst, *res.sn.Tree, treeVisitor{
enterDir: func(_ *restic.Node, target, location string) error {
enterDir: func(node *restic.Node, target, location string) error {
debug.Log("first pass, enterDir: mkdir %q, leaveDir should restore metadata", location)
if location != string(filepath.Separator) {
res.opts.Progress.AddFile(0)
res.addFile(node, 0)
}
return res.ensureDir(target)
},
@ -378,14 +378,14 @@ func (res *Restorer) RestoreTo(ctx context.Context, dst string) (uint64, error)
}
if node.Type != restic.NodeTypeFile {
res.opts.Progress.AddFile(0)
res.addFile(node, 0)
return nil
}
if node.Links > 1 {
if idx.Has(node.Inode, node.DeviceID) {
// a hardlinked file does not increase the restore size
res.opts.Progress.AddFile(0)
res.addFile(node, 0)
return nil
}
idx.Add(node.Inode, node.DeviceID, location)
@ -393,18 +393,18 @@ func (res *Restorer) RestoreTo(ctx context.Context, dst string) (uint64, error)
buf, err = res.withOverwriteCheck(ctx, node, target, location, false, buf, func(updateMetadataOnly bool, matches *fileState) error {
if updateMetadataOnly {
res.opts.Progress.AddSkippedFile(location, node.Size)
res.addSkippedFile(node, location, node.Size)
} else {
res.opts.Progress.AddFile(node.Size)
res.addFile(node, node.Size)
if !res.opts.DryRun {
filerestorer.addFile(location, node.Content, int64(node.Size), matches)
filerestorer.addFile(location, node.Content, int64(node.Size), matches, node.GenericAttributes)
} else {
action := restoreui.ActionFileUpdated
if matches == nil {
action = restoreui.ActionFileRestored
}
// immediately mark as completed
res.opts.Progress.AddProgress(location, action, node.Size, node.Size)
res.opts.Progress.AddProgress(location, action, node.Size, node.Size, node.GenericAttributes)
}
}
res.trackFile(location, updateMetadataOnly)
@ -466,7 +466,7 @@ func (res *Restorer) RestoreTo(ctx context.Context, dst string) (uint64, error)
err := res.restoreNodeMetadataTo(node, target, location)
if err == nil {
res.opts.Progress.AddProgress(location, restoreui.ActionDirRestored, 0, 0)
res.opts.Progress.AddProgress(location, restoreui.ActionDirRestored, 0, 0, node.GenericAttributes)
}
return err
},
@ -559,7 +559,7 @@ func (res *Restorer) withOverwriteCheck(ctx context.Context, node *restic.Node,
if isHardlink {
size = 0
}
res.opts.Progress.AddSkippedFile(location, size)
res.addSkippedFile(node, location, size)
return buf, nil
}

View file

@ -3,8 +3,21 @@
package restorer
import "github.com/restic/restic/internal/restic"
// toComparableFilename returns a filename suitable for equality checks. On Windows, it returns the
// uppercase version of the string. On all other systems, it returns the unmodified filename.
func toComparableFilename(path string) string {
return path
}
// addFile adds the file to restorer's progress tracker
func (res *Restorer) addFile(_ *restic.Node, size uint64) {
res.opts.Progress.AddFile(size)
}
// addSkippedFile adds the skipped file to restorer's progress tracker.
// If the node represents an ads file, it skips the file count.
func (res *Restorer) addSkippedFile(_ *restic.Node, location string, size uint64) {
res.opts.Progress.AddSkippedFile(location, size)
}

View file

@ -3,7 +3,11 @@
package restorer
import "strings"
import (
"strings"
"github.com/restic/restic/internal/restic"
)
// toComparableFilename returns a filename suitable for equality checks. On Windows, it returns the
// uppercase version of the string. On all other systems, it returns the unmodified filename.
@ -11,3 +15,22 @@ func toComparableFilename(path string) string {
// apparently NTFS internally uppercases filenames for comparison
return strings.ToUpper(path)
}
// addFile adds the file to restorer's progress tracker.
// If the node represents an ads file, it only adds the size without counting the ads file.
func (res *Restorer) addFile(node *restic.Node, size uint64) {
if node.IsMainFile() {
res.opts.Progress.AddFile(size)
} else {
// If this is not the main file, we just want to update the size and not the count.
res.opts.Progress.AddSize(size)
}
}
// addSkippedFile adds the skipped file to restorer's progress tracker.
// If the node represents an ads file, it skips the file count.
func (res *Restorer) addSkippedFile(node *restic.Node, location string, size uint64) {
if node.IsMainFile() {
res.opts.Progress.AddSkippedFile(location, size)
}
}