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:
parent
da4a84c4fb
commit
400b0a3958
4 changed files with 59 additions and 20 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue