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

Add support for ads in archiver

This commit is contained in:
aneesh-n 2024-12-03 17:59:01 +05:30
parent 691890c502
commit da4a84c4fb
No known key found for this signature in database
GPG key ID: 6F5A52831C046F44
2 changed files with 192 additions and 0 deletions

View file

@ -0,0 +1,64 @@
//go:build !windows
// +build !windows
package archiver
import (
"os"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic"
)
// preProcessTargets performs preprocessing of the targets before the loop.
// It is a no-op on non-windows OS as we do not need to do an
// extra iteration on the targets before the loop.
// We process each target inside the loop.
func preProcessTargets(_ fs.FS, _ *[]string) {
// no-op
}
// processTarget processes each target in the loop.
// In case of non-windows OS it uses the passed filesys to clean the target.
func processTarget(filesys fs.FS, target string) string {
return filesys.Clean(target)
}
// preProcessPaths processes paths before looping.
func (arch *Archiver) preProcessPaths(_ string, names []string) (paths []string) {
// In case of non-windows OS this is no-op as we process the paths within the loop
// and avoid the extra looping before hand.
return names
}
// processPath processes the path in the loop.
func (arch *Archiver) processPath(dir string, name string) (path string) {
//In case of non-windows OS we prepare the path in the loop.
return arch.FS.Join(dir, name)
}
// getNameFromPathname gets the name from pathname.
// In case for non-windows the pathname is same as the name.
func getNameFromPathname(pathname string) (name string) {
return pathname
}
// processTargets is no-op for non-windows OS
func (arch *Archiver) processTargets(_ string, _ string, _ string, fiMain os.FileInfo) (fi os.FileInfo, shouldReturn bool, fn futureNode, excluded bool, err error) {
return fiMain, false, futureNode{}, false, nil
}
// incrementNewFiles increments the new files count
func (c *ChangeStats) incrementNewFiles(_ *restic.Node) {
c.New++
}
// incrementNewFiles increments the unchanged files count
func (c *ChangeStats) incrementUnchangedFiles(_ *restic.Node) {
c.Unchanged++
}
// incrementNewFiles increments the changed files count
func (c *ChangeStats) incrementChangedFiles(_ *restic.Node) {
c.Changed++
}

View file

@ -0,0 +1,128 @@
package archiver
import (
"path/filepath"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic"
)
// preProcessTargets performs preprocessing of the targets before the loop.
// For Windows, it cleans each target and it also adds ads stream for each
// target to the targets array.
// We read the ADS from each file and add them as independent Nodes with
// the full ADS name as the name of the file.
// During restore the ADS files are restored using the ADS name and that
// automatically attaches them as ADS to the main file.
func preProcessTargets(filesys fs.FS, targets *[]string) {
for _, target := range *targets {
if target != "" && filesys.VolumeName(target) == target {
// special case to allow users to also specify a volume name "C:" instead of a path "C:\"
target = target + filesys.Separator()
} else {
target = filesys.Clean(target)
}
addADSStreams(target, targets)
}
}
// processTarget processes each target in the loop.
// In case of windows the clean up of target is already done
// in preProcessTargets before the loop, hence this is no-op.
func processTarget(_ fs.FS, target string) string {
return target
}
// getNameFromPathname gets the name from pathname.
// In case for windows the pathname is the full path, so it need to get the base name.
func getNameFromPathname(pathname string) (name string) {
return filepath.Base(pathname)
}
// preProcessPaths processes paths before looping.
func (arch *Archiver) preProcessPaths(dir string, names []string) (paths []string) {
// In case of windows we want to add the ADS paths as well before sorting.
return arch.getPathsIncludingADS(dir, names)
}
// processPath processes the path in the loop.
func (arch *Archiver) processPath(_ string, name string) (path string) {
// In case of windows we have already prepared the paths before the loop.
// Hence this is a no-op.
return name
}
// getPathsIncludingADS iterates all passed path names and adds the ads
// contained in those paths before returning all full paths including ads
func (arch *Archiver) getPathsIncludingADS(dir string, names []string) []string {
paths := make([]string, 0, len(names))
for _, name := range names {
pathname := arch.FS.Join(dir, name)
paths = append(paths, pathname)
addADSStreams(pathname, &paths)
}
return paths
}
// addADSStreams gets the ads streams if any in the pathname passed and adds them to the passed paths
func addADSStreams(pathname string, paths *[]string) {
success, adsStreams, err := restic.GetADStreamNames(pathname)
if success {
streamCount := len(adsStreams)
if streamCount > 0 {
debug.Log("ADS Streams for file: %s, streams: %v", pathname, adsStreams)
for i := 0; i < streamCount; i++ {
adsStream := adsStreams[i]
adsPath := pathname + adsStream
*paths = append(*paths, adsPath)
}
}
} else if err != nil {
debug.Log("No ADS found for path: %s, err: %v", pathname, err)
}
}
// processTargets in windows performs Lstat for the ADS files since the file info would not be available for them yet.
func (arch *Archiver) processTargets(target string, targetMain string, abstarget string, fiMain fs.ExtendedFileInfo) (fi *fs.ExtendedFileInfo, shouldReturn bool, fn futureNode, excluded bool, err error) {
if target != targetMain {
//If this is an ADS file we need to Lstat again for the file info.
fi, err = arch.FS.Lstat(target)
if err != nil {
debug.Log("lstat() for %v returned error: %v", target, err)
err = arch.error(abstarget, err)
if err != nil {
return nil, true, futureNode{}, false, errors.WithStack(err)
}
//If this is an ads file, shouldReturn should be true because we want to
// skip the remaining processing of the file.
return nil, true, futureNode{}, true, nil
}
} else {
fi = &fiMain
}
return fi, false, futureNode{}, false, nil
}
// incrementNewFiles increments the new files count
func (c *ChangeStats) incrementNewFiles(node *restic.Node) {
if node.IsMainFile() {
c.New++
}
}
// incrementNewFiles increments the unchanged files count
func (c *ChangeStats) incrementUnchangedFiles(node *restic.Node) {
if node.IsMainFile() {
c.Unchanged++
}
}
// incrementNewFiles increments the changed files count
func (c *ChangeStats) incrementChangedFiles(node *restic.Node) {
if node.IsMainFile() {
c.Changed++
}
}