From 400b0a3958ed4efc06562e92a3ccfdd48ef55019 Mon Sep 17 00:00:00 2001 From: aneesh-n <99904+aneesh-n@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:59:40 +0530 Subject: [PATCH] Add support for restoring ads --- internal/restorer/filerestorer.go | 17 ++++++++++------- internal/restorer/restorer.go | 24 ++++++++++++------------ internal/restorer/restorer_unix.go | 13 +++++++++++++ internal/restorer/restorer_windows.go | 25 ++++++++++++++++++++++++- 4 files changed, 59 insertions(+), 20 deletions(-) diff --git a/internal/restorer/filerestorer.go b/internal/restorer/filerestorer.go index 31234b960..8e282762b 100644 --- a/internal/restorer/filerestorer.go +++ b/internal/restorer/filerestorer.go @@ -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) } diff --git a/internal/restorer/restorer.go b/internal/restorer/restorer.go index 14a8edeac..500c25ade 100644 --- a/internal/restorer/restorer.go +++ b/internal/restorer/restorer.go @@ -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 } diff --git a/internal/restorer/restorer_unix.go b/internal/restorer/restorer_unix.go index 7316f7b5d..2f5add893 100644 --- a/internal/restorer/restorer_unix.go +++ b/internal/restorer/restorer_unix.go @@ -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) +} diff --git a/internal/restorer/restorer_windows.go b/internal/restorer/restorer_windows.go index 9ddc0a932..60c6b5a4e 100644 --- a/internal/restorer/restorer_windows.go +++ b/internal/restorer/restorer_windows.go @@ -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) + } +}