Merge pull request #5024 from MichaelEischer/move-node-to-fs

Cleanup FS package
This commit is contained in:
Michael Eischer 2024-08-31 18:47:11 +02:00 committed by GitHub
commit d8be8f1e06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
81 changed files with 474 additions and 782 deletions

View file

@ -10,7 +10,6 @@ import (
"github.com/restic/restic/internal/backend/cache"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/table"
"github.com/spf13/cobra"
@ -89,7 +88,7 @@ func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error {
for _, item := range oldDirs {
dir := filepath.Join(cachedir, item.Name())
err = fs.RemoveAll(dir)
err = os.RemoveAll(dir)
if err != nil {
Warnf("unable to remove %v: %v\n", dir, err)
}

View file

@ -14,7 +14,6 @@ import (
"github.com/restic/restic/internal/backend/cache"
"github.com/restic/restic/internal/checker"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@ -202,7 +201,7 @@ func prepareCheckCache(opts CheckOptions, gopts *GlobalOptions, printer progress
printer.P("using temporary cache in %v\n", tempdir)
cleanup = func() {
err := fs.RemoveAll(tempdir)
err := os.RemoveAll(tempdir)
if err != nil {
printer.E("error removing temporary cache directory: %v\n", err)
}

View file

@ -108,9 +108,9 @@ func (s *DiffStat) Add(node *restic.Node) {
}
switch node.Type {
case "file":
case restic.NodeTypeFile:
s.Files++
case "dir":
case restic.NodeTypeDir:
s.Dirs++
default:
s.Others++
@ -124,7 +124,7 @@ func addBlobs(bs restic.BlobSet, node *restic.Node) {
}
switch node.Type {
case "file":
case restic.NodeTypeFile:
for _, blob := range node.Content {
h := restic.BlobHandle{
ID: blob,
@ -132,7 +132,7 @@ func addBlobs(bs restic.BlobSet, node *restic.Node) {
}
bs.Insert(h)
}
case "dir":
case restic.NodeTypeDir:
h := restic.BlobHandle{
ID: *node.Subtree,
Type: restic.TreeBlob,
@ -184,14 +184,14 @@ func (c *Comparer) printDir(ctx context.Context, mode string, stats *DiffStat, b
}
name := path.Join(prefix, node.Name)
if node.Type == "dir" {
if node.Type == restic.NodeTypeDir {
name += "/"
}
c.printChange(NewChange(name, mode))
stats.Add(node)
addBlobs(blobs, node)
if node.Type == "dir" {
if node.Type == restic.NodeTypeDir {
err := c.printDir(ctx, mode, stats, blobs, name, *node.Subtree)
if err != nil && err != context.Canceled {
Warnf("error: %v\n", err)
@ -216,7 +216,7 @@ func (c *Comparer) collectDir(ctx context.Context, blobs restic.BlobSet, id rest
addBlobs(blobs, node)
if node.Type == "dir" {
if node.Type == restic.NodeTypeDir {
err := c.collectDir(ctx, blobs, *node.Subtree)
if err != nil && err != context.Canceled {
Warnf("error: %v\n", err)
@ -284,12 +284,12 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
mod += "T"
}
if node2.Type == "dir" {
if node2.Type == restic.NodeTypeDir {
name += "/"
}
if node1.Type == "file" &&
node2.Type == "file" &&
if node1.Type == restic.NodeTypeFile &&
node2.Type == restic.NodeTypeFile &&
!reflect.DeepEqual(node1.Content, node2.Content) {
mod += "M"
stats.ChangedFiles++
@ -311,7 +311,7 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
c.printChange(NewChange(name, mod))
}
if node1.Type == "dir" && node2.Type == "dir" {
if node1.Type == restic.NodeTypeDir && node2.Type == restic.NodeTypeDir {
var err error
if (*node1.Subtree).Equal(*node2.Subtree) {
err = c.collectDir(ctx, stats.BlobsCommon, *node1.Subtree)
@ -324,13 +324,13 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
}
case t1 && !t2:
prefix := path.Join(prefix, name)
if node1.Type == "dir" {
if node1.Type == restic.NodeTypeDir {
prefix += "/"
}
c.printChange(NewChange(prefix, "-"))
stats.Removed.Add(node1)
if node1.Type == "dir" {
if node1.Type == restic.NodeTypeDir {
err := c.printDir(ctx, "-", &stats.Removed, stats.BlobsBefore, prefix, *node1.Subtree)
if err != nil && err != context.Canceled {
Warnf("error: %v\n", err)
@ -338,13 +338,13 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
}
case !t1 && t2:
prefix := path.Join(prefix, name)
if node2.Type == "dir" {
if node2.Type == restic.NodeTypeDir {
prefix += "/"
}
c.printChange(NewChange(prefix, "+"))
stats.Added.Add(node2)
if node2.Type == "dir" {
if node2.Type == restic.NodeTypeDir {
err := c.printDir(ctx, "+", &stats.Added, stats.BlobsAfter, prefix, *node2.Subtree)
if err != nil && err != context.Canceled {
Warnf("error: %v\n", err)

View file

@ -95,15 +95,15 @@ func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoade
// first item it finds and dump that according to the switch case below.
if node.Name == pathComponents[0] {
switch {
case l == 1 && dump.IsFile(node):
case l == 1 && node.Type == restic.NodeTypeFile:
return d.WriteNode(ctx, node)
case l > 1 && dump.IsDir(node):
case l > 1 && node.Type == restic.NodeTypeDir:
subtree, err := restic.LoadTree(ctx, repo, *node.Subtree)
if err != nil {
return errors.Wrapf(err, "cannot load subtree for %q", item)
}
return printFromTree(ctx, subtree, repo, item, pathComponents[1:], d, canWriteArchiveFunc)
case dump.IsDir(node):
case node.Type == restic.NodeTypeDir:
if err := canWriteArchiveFunc(); err != nil {
return err
}
@ -114,7 +114,7 @@ func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoade
return d.DumpTree(ctx, subtree, item)
case l > 1:
return fmt.Errorf("%q should be a dir, but is a %q", item, node.Type)
case !dump.IsFile(node):
case node.Type != restic.NodeTypeFile:
return fmt.Errorf("%q should be a file, but is a %q", item, node.Type)
}
}

View file

@ -298,7 +298,7 @@ func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error
}
var errIfNoMatch error
if node.Type == "dir" {
if node.Type == restic.NodeTypeDir {
var childMayMatch bool
for _, pat := range f.pat.pattern {
mayMatch, err := filter.ChildMatch(pat, normalizedNodepath)
@ -357,7 +357,7 @@ func (f *Finder) findIDs(ctx context.Context, sn *restic.Snapshot) error {
return nil
}
if node.Type == "dir" && f.treeIDs != nil {
if node.Type == restic.NodeTypeDir && f.treeIDs != nil {
treeID := node.Subtree
found := false
if _, ok := f.treeIDs[treeID.Str()]; ok {
@ -377,7 +377,7 @@ func (f *Finder) findIDs(ctx context.Context, sn *restic.Snapshot) error {
}
}
if node.Type == "file" && f.blobIDs != nil {
if node.Type == restic.NodeTypeFile && f.blobIDs != nil {
for _, id := range node.Content {
if ctx.Err() != nil {
return ctx.Err()

View file

@ -137,7 +137,7 @@ func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
size uint64 // Target for Size pointer.
}{
Name: node.Name,
Type: node.Type,
Type: string(node.Type),
Path: path,
UID: node.UID,
GID: node.GID,
@ -153,7 +153,7 @@ func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
}
// Always print size for regular files, even when empty,
// but never for other types.
if node.Type == "file" {
if node.Type == restic.NodeTypeFile {
n.Size = &n.size
}
@ -208,7 +208,7 @@ func lsNcduNode(_ string, node *restic.Node) ([]byte, error) {
Dev: node.DeviceID,
Ino: node.Inode,
NLink: node.Links,
NotReg: node.Type != "dir" && node.Type != "file",
NotReg: node.Type != restic.NodeTypeDir && node.Type != restic.NodeTypeFile,
UID: node.UID,
GID: node.GID,
Mode: uint16(node.Mode & os.ModePerm),
@ -238,7 +238,7 @@ func (p *ncduLsPrinter) Node(path string, node *restic.Node, _ bool) {
Warnf("JSON encode failed: %v\n", err)
}
if node.Type == "dir" {
if node.Type == restic.NodeTypeDir {
fmt.Fprintf(p.out, ",\n%s[\n%s%s", strings.Repeat(" ", p.depth), strings.Repeat(" ", p.depth+1), string(out))
p.depth++
} else {
@ -409,7 +409,7 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
// otherwise, signal the walker to not walk recursively into any
// subdirs
if node.Type == "dir" {
if node.Type == restic.NodeTypeDir {
// immediately generate leaveDir if the directory is skipped
if printedDir {
printer.LeaveDir(nodepath)

View file

@ -23,7 +23,7 @@ var lsTestNodes = []lsTestNode{
path: "/bar/baz",
Node: restic.Node{
Name: "baz",
Type: "file",
Type: restic.NodeTypeFile,
Size: 12345,
UID: 10000000,
GID: 20000000,
@ -39,7 +39,7 @@ var lsTestNodes = []lsTestNode{
path: "/foo/empty",
Node: restic.Node{
Name: "empty",
Type: "file",
Type: restic.NodeTypeFile,
Size: 0,
UID: 1001,
GID: 1001,
@ -56,7 +56,7 @@ var lsTestNodes = []lsTestNode{
path: "/foo/link",
Node: restic.Node{
Name: "link",
Type: "symlink",
Type: restic.NodeTypeSymlink,
Mode: os.ModeSymlink | 0777,
LinkTarget: "not printed",
},
@ -66,7 +66,7 @@ var lsTestNodes = []lsTestNode{
path: "/some/directory",
Node: restic.Node{
Name: "directory",
Type: "dir",
Type: restic.NodeTypeDir,
Mode: os.ModeDir | 0755,
ModTime: time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC),
AccessTime: time.Date(2021, 2, 3, 4, 5, 6, 7, time.UTC),
@ -79,7 +79,7 @@ var lsTestNodes = []lsTestNode{
path: "/some/sticky",
Node: restic.Node{
Name: "sticky",
Type: "dir",
Type: restic.NodeTypeDir,
Mode: os.ModeDir | 0755 | os.ModeSetuid | os.ModeSetgid | os.ModeSticky,
},
},
@ -139,19 +139,19 @@ func TestLsNcdu(t *testing.T) {
Paths: []string{"/example"},
})
printer.Node("/directory", &restic.Node{
Type: "dir",
Type: restic.NodeTypeDir,
Name: "directory",
ModTime: modTime,
}, false)
printer.Node("/directory/data", &restic.Node{
Type: "file",
Type: restic.NodeTypeFile,
Name: "data",
Size: 42,
ModTime: modTime,
}, false)
printer.LeaveDir("/directory")
printer.Node("/file", &restic.Node{
Type: "file",
Type: restic.NodeTypeFile,
Name: "file",
Size: 12345,
ModTime: modTime,

View file

@ -15,7 +15,6 @@ import (
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/restic"
resticfs "github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/fuse"
systemFuse "github.com/anacrolix/fuse"
@ -122,7 +121,7 @@ func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args
// Check the existence of the mount point at the earliest stage to
// prevent unnecessary computations while opening the repository.
if _, err := resticfs.Stat(mountpoint); errors.Is(err, os.ErrNotExist) {
if _, err := os.Stat(mountpoint); errors.Is(err, os.ErrNotExist) {
Verbosef("Mountpoint %s doesn't exist\n", mountpoint)
return err
}

View file

@ -88,7 +88,7 @@ func runRecover(ctx context.Context, gopts GlobalOptions) error {
}
for _, node := range tree.Nodes {
if node.Type == "dir" && node.Subtree != nil {
if node.Type == restic.NodeTypeDir && node.Subtree != nil {
trees[*node.Subtree] = true
}
}
@ -128,7 +128,7 @@ func runRecover(ctx context.Context, gopts GlobalOptions) error {
for id := range roots {
var subtreeID = id
node := restic.Node{
Type: "dir",
Type: restic.NodeTypeDir,
Name: id.Str(),
Mode: 0755,
Subtree: &subtreeID,

View file

@ -92,7 +92,7 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt
// - files whose contents are not fully available (-> file will be modified)
rewriter := walker.NewTreeRewriter(walker.RewriteOpts{
RewriteNode: func(node *restic.Node, path string) *restic.Node {
if node.Type != "file" {
if node.Type != restic.NodeTypeFile {
return node
}

View file

@ -276,7 +276,7 @@ func statsWalkTree(repo restic.Loader, opts StatsOptions, stats *statsContainer,
// will still be restored
stats.TotalFileCount++
if node.Links == 1 || node.Type == "dir" {
if node.Links == 1 || node.Type == restic.NodeTypeDir {
stats.TotalSize += node.Size
} else {
// if hardlinks are present only count each deviceID+inode once

View file

@ -24,20 +24,20 @@ func formatNode(path string, n *restic.Node, long bool, human bool) string {
}
switch n.Type {
case "file":
case restic.NodeTypeFile:
mode = 0
case "dir":
case restic.NodeTypeDir:
mode = os.ModeDir
case "symlink":
case restic.NodeTypeSymlink:
mode = os.ModeSymlink
target = fmt.Sprintf(" -> %v", n.LinkTarget)
case "dev":
case restic.NodeTypeDev:
mode = os.ModeDevice
case "chardev":
case restic.NodeTypeCharDev:
mode = os.ModeDevice | os.ModeCharDevice
case "fifo":
case restic.NodeTypeFifo:
mode = os.ModeNamedPipe
case "socket":
case restic.NodeTypeSocket:
mode = os.ModeSocket
}

View file

@ -19,7 +19,7 @@ func TestFormatNode(t *testing.T) {
testPath := "/test/path"
node := restic.Node{
Name: "baz",
Type: "file",
Type: restic.NodeTypeFile,
Size: 14680064,
UID: 1000,
GID: 2000,

View file

@ -29,7 +29,6 @@ import (
"github.com/restic/restic/internal/backend/sftp"
"github.com/restic/restic/internal/backend/swift"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/options"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
@ -548,7 +547,7 @@ func OpenRepository(ctx context.Context, opts GlobalOptions) (*repository.Reposi
}
for _, item := range oldCacheDirs {
dir := filepath.Join(c.Base, item.Name())
err = fs.RemoveAll(dir)
err = os.RemoveAll(dir)
if err != nil {
Warnf("unable to remove %v: %v\n", dir, err)
}

View file

@ -232,7 +232,7 @@ func (arch *Archiver) trackItem(item string, previous, current *restic.Node, s I
}
switch current.Type {
case "dir":
case restic.NodeTypeDir:
switch {
case previous == nil:
arch.summary.Dirs.New++
@ -242,7 +242,7 @@ func (arch *Archiver) trackItem(item string, previous, current *restic.Node, s I
arch.summary.Dirs.Changed++
}
case "file":
case restic.NodeTypeFile:
switch {
case previous == nil:
arch.summary.Files.New++
@ -261,7 +261,7 @@ func (arch *Archiver) nodeFromFileInfo(snPath, filename string, fi os.FileInfo,
node.AccessTime = node.ModTime
}
if feature.Flag.Enabled(feature.DeviceIDForHardlinks) {
if node.Links == 1 || node.Type == "dir" {
if node.Links == 1 || node.Type == restic.NodeTypeDir {
// the DeviceID is only necessary for hardlinked files
// when using subvolumes or snapshots their deviceIDs tend to change which causes
// restic to upload new tree blobs
@ -280,7 +280,7 @@ func (arch *Archiver) nodeFromFileInfo(snPath, filename string, fi os.FileInfo,
// loadSubtree tries to load the subtree referenced by node. In case of an error, nil is returned.
// If there is no node to load, then nil is returned without an error.
func (arch *Archiver) loadSubtree(ctx context.Context, node *restic.Node) (*restic.Tree, error) {
if node == nil || node.Type != "dir" || node.Subtree == nil {
if node == nil || node.Type != restic.NodeTypeDir || node.Subtree == nil {
return nil, nil
}
@ -583,7 +583,7 @@ func fileChanged(fs fs.FS, fi os.FileInfo, node *restic.Node, ignoreFlags uint)
switch {
case node == nil:
return true
case node.Type != "file":
case node.Type != restic.NodeTypeFile:
// We're only called for regular files, so this is a type change.
return true
case uint64(fi.Size()) != node.Size:

View file

@ -730,7 +730,7 @@ func TestFilChangedSpecialCases(t *testing.T) {
t.Run("type-change", func(t *testing.T) {
fi := lstat(t, filename)
node := nodeFromFI(t, filename, fi)
node.Type = "symlink"
node.Type = "restic.NodeTypeSymlink"
if !fileChanged(&fs.Local{}, fi, node, 0) {
t.Fatal("node with changed type detected as unchanged")
}
@ -846,7 +846,7 @@ func TestArchiverSaveDir(t *testing.T) {
back := rtest.Chdir(t, chdir)
defer back()
fi, err := fs.Lstat(test.target)
fi, err := os.Lstat(test.target)
if err != nil {
t.Fatal(err)
}
@ -920,7 +920,7 @@ func TestArchiverSaveDirIncremental(t *testing.T) {
arch.runWorkers(ctx, wg)
arch.summary = &Summary{}
fi, err := fs.Lstat(tempdir)
fi, err := os.Lstat(tempdir)
if err != nil {
t.Fatal(err)
}

View file

@ -163,7 +163,7 @@ func (s *fileSaver) saveFile(ctx context.Context, chnker *chunker.Chunker, snPat
return
}
if node.Type != "file" {
if node.Type != restic.NodeTypeFile {
_ = f.Close()
completeError(errors.Errorf("node type %q is wrong", node.Type))
return

View file

@ -95,17 +95,17 @@ func TestCreateFiles(t testing.TB, target string, dir TestDir) {
t.Fatal(err)
}
case TestSymlink:
err := fs.Symlink(filepath.FromSlash(it.Target), targetPath)
err := os.Symlink(filepath.FromSlash(it.Target), targetPath)
if err != nil {
t.Fatal(err)
}
case TestHardlink:
err := fs.Link(filepath.Join(target, filepath.FromSlash(it.Target)), targetPath)
err := os.Link(filepath.Join(target, filepath.FromSlash(it.Target)), targetPath)
if err != nil {
t.Fatal(err)
}
case TestDir:
err := fs.Mkdir(targetPath, 0755)
err := os.Mkdir(targetPath, 0755)
if err != nil {
t.Fatal(err)
}
@ -157,7 +157,7 @@ func TestEnsureFiles(t testing.TB, target string, dir TestDir) {
// first, test that all items are there
TestWalkFiles(t, target, dir, func(path string, item interface{}) error {
fi, err := fs.Lstat(path)
fi, err := os.Lstat(path)
if err != nil {
return err
}
@ -188,7 +188,7 @@ func TestEnsureFiles(t testing.TB, target string, dir TestDir) {
return nil
}
target, err := fs.Readlink(path)
target, err := os.Readlink(path)
if err != nil {
return err
}
@ -289,7 +289,7 @@ func TestEnsureTree(ctx context.Context, t testing.TB, prefix string, repo resti
switch e := entry.(type) {
case TestDir:
if node.Type != "dir" {
if node.Type != restic.NodeTypeDir {
t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "dir")
return
}
@ -301,13 +301,13 @@ func TestEnsureTree(ctx context.Context, t testing.TB, prefix string, repo resti
TestEnsureTree(ctx, t, path.Join(prefix, node.Name), repo, *node.Subtree, e)
case TestFile:
if node.Type != "file" {
if node.Type != restic.NodeTypeFile {
t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "file")
}
TestEnsureFileContent(ctx, t, repo, nodePrefix, node, e)
case TestSymlink:
if node.Type != "symlink" {
t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "file")
if node.Type != restic.NodeTypeSymlink {
t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "symlink")
}
if e.Target != node.LinkTarget {

View file

@ -54,7 +54,7 @@ func (t *MockT) Errorf(msg string, args ...interface{}) {
func createFilesAt(t testing.TB, targetdir string, files map[string]interface{}) {
for name, item := range files {
target := filepath.Join(targetdir, filepath.FromSlash(name))
err := fs.MkdirAll(filepath.Dir(target), 0700)
err := os.MkdirAll(filepath.Dir(target), 0700)
if err != nil {
t.Fatal(err)
}
@ -66,7 +66,7 @@ func createFilesAt(t testing.TB, targetdir string, files map[string]interface{})
t.Fatal(err)
}
case TestSymlink:
err := fs.Symlink(filepath.FromSlash(it.Target), target)
err := os.Symlink(filepath.FromSlash(it.Target), target)
if err != nil {
t.Fatal(err)
}
@ -105,7 +105,7 @@ func TestTestCreateFiles(t *testing.T) {
t.Run("", func(t *testing.T) {
tempdir := filepath.Join(tempdir, fmt.Sprintf("test-%d", i))
err := fs.MkdirAll(tempdir, 0700)
err := os.MkdirAll(tempdir, 0700)
if err != nil {
t.Fatal(err)
}
@ -114,7 +114,7 @@ func TestTestCreateFiles(t *testing.T) {
for name, item := range test.files {
targetPath := filepath.Join(tempdir, filepath.FromSlash(name))
fi, err := fs.Lstat(targetPath)
fi, err := os.Lstat(targetPath)
if err != nil {
t.Error(err)
continue
@ -142,7 +142,7 @@ func TestTestCreateFiles(t *testing.T) {
continue
}
target, err := fs.Readlink(targetPath)
target, err := os.Readlink(targetPath)
if err != nil {
t.Error(err)
continue
@ -455,7 +455,7 @@ func TestTestEnsureSnapshot(t *testing.T) {
tempdir := rtest.TempDir(t)
targetDir := filepath.Join(tempdir, "target")
err := fs.Mkdir(targetDir, 0700)
err := os.Mkdir(targetDir, 0700)
if err != nil {
t.Fatal(err)
}

View file

@ -12,7 +12,6 @@ import (
"github.com/pkg/errors"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic"
)
@ -54,7 +53,7 @@ const cachedirTagSignature = "Signature: 8a477f597d28d172789f06886806bc55\n"
func writeCachedirTag(dir string) error {
tagfile := filepath.Join(dir, "CACHEDIR.TAG")
f, err := fs.OpenFile(tagfile, os.O_CREATE|os.O_EXCL|os.O_WRONLY, fileMode)
f, err := os.OpenFile(tagfile, os.O_CREATE|os.O_EXCL|os.O_WRONLY, fileMode)
if err != nil {
if errors.Is(err, os.ErrExist) {
return nil
@ -85,7 +84,7 @@ func New(id string, basedir string) (c *Cache, err error) {
}
}
err = fs.MkdirAll(basedir, dirMode)
err = os.MkdirAll(basedir, dirMode)
if err != nil {
return nil, errors.WithStack(err)
}
@ -113,7 +112,7 @@ func New(id string, basedir string) (c *Cache, err error) {
case errors.Is(err, os.ErrNotExist):
// Create the repo cache dir. The parent exists, so Mkdir suffices.
err := fs.Mkdir(cachedir, dirMode)
err := os.Mkdir(cachedir, dirMode)
switch {
case err == nil:
created = true
@ -134,7 +133,7 @@ func New(id string, basedir string) (c *Cache, err error) {
}
for _, p := range cacheLayoutPaths {
if err = fs.MkdirAll(filepath.Join(cachedir, p), dirMode); err != nil {
if err = os.MkdirAll(filepath.Join(cachedir, p), dirMode); err != nil {
return nil, errors.WithStack(err)
}
}
@ -152,7 +151,7 @@ func New(id string, basedir string) (c *Cache, err error) {
// directory d to the current time.
func updateTimestamp(d string) error {
t := time.Now()
return fs.Chtimes(d, t, t)
return os.Chtimes(d, t, t)
}
// MaxCacheAge is the default age (30 days) after which cache directories are considered old.
@ -165,7 +164,7 @@ func validCacheDirName(s string) bool {
// listCacheDirs returns the list of cache directories.
func listCacheDirs(basedir string) ([]os.FileInfo, error) {
f, err := fs.Open(basedir)
f, err := os.Open(basedir)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
err = nil

View file

@ -12,7 +12,6 @@ import (
"github.com/restic/restic/internal/backend/util"
"github.com/restic/restic/internal/crypto"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic"
)
@ -44,7 +43,7 @@ func (c *Cache) load(h backend.Handle, length int, offset int64) (io.ReadCloser,
return nil, false, errors.New("cannot be cached")
}
f, err := fs.Open(c.filename(h))
f, err := os.Open(c.filename(h))
if err != nil {
return nil, false, errors.WithStack(err)
}
@ -91,7 +90,7 @@ func (c *Cache) save(h backend.Handle, rd io.Reader) error {
finalname := c.filename(h)
dir := filepath.Dir(finalname)
err := fs.Mkdir(dir, 0700)
err := os.Mkdir(dir, 0700)
if err != nil && !errors.Is(err, os.ErrExist) {
return err
}
@ -106,26 +105,26 @@ func (c *Cache) save(h backend.Handle, rd io.Reader) error {
n, err := io.Copy(f, rd)
if err != nil {
_ = f.Close()
_ = fs.Remove(f.Name())
_ = os.Remove(f.Name())
return errors.Wrap(err, "Copy")
}
if n <= int64(crypto.CiphertextLength(0)) {
_ = f.Close()
_ = fs.Remove(f.Name())
_ = os.Remove(f.Name())
debug.Log("trying to cache truncated file %v, removing", h)
return nil
}
// Close, then rename. Windows doesn't like the reverse order.
if err = f.Close(); err != nil {
_ = fs.Remove(f.Name())
_ = os.Remove(f.Name())
return errors.WithStack(err)
}
err = fs.Rename(f.Name(), finalname)
err = os.Rename(f.Name(), finalname)
if err != nil {
_ = fs.Remove(f.Name())
_ = os.Remove(f.Name())
}
if runtime.GOOS == "windows" && errors.Is(err, os.ErrPermission) {
// On Windows, renaming over an existing file is ok
@ -162,7 +161,7 @@ func (c *Cache) remove(h backend.Handle) (bool, error) {
return false, nil
}
err := fs.Remove(c.filename(h))
err := os.Remove(c.filename(h))
removed := err == nil
if errors.Is(err, os.ErrNotExist) {
err = nil
@ -189,7 +188,7 @@ func (c *Cache) Clear(t restic.FileType, valid restic.IDSet) error {
}
// ignore ErrNotExist to gracefully handle multiple processes running Clear() concurrently
if err = fs.Remove(c.filename(backend.Handle{Type: t, Name: id.String()})); err != nil && !errors.Is(err, os.ErrNotExist) {
if err = os.Remove(c.filename(backend.Handle{Type: t, Name: id.String()})); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
}
@ -236,6 +235,6 @@ func (c *Cache) Has(h backend.Handle) bool {
return false
}
_, err := fs.Stat(c.filename(h))
_, err := os.Stat(c.filename(h))
return err == nil
}

View file

@ -12,7 +12,6 @@ import (
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
@ -278,7 +277,7 @@ func TestFileSaveConcurrent(t *testing.T) {
func TestFileSaveAfterDamage(t *testing.T) {
c := TestNewCache(t)
rtest.OK(t, fs.RemoveAll(c.path))
rtest.OK(t, os.RemoveAll(c.path))
// save a few bytes of data in the cache
data := rtest.Random(123456789, 42)

View file

@ -40,7 +40,7 @@ func NewFactory() location.Factory {
func open(cfg Config) (*Local, error) {
l := layout.NewDefaultLayout(cfg.Path, filepath.Join)
fi, err := fs.Stat(l.Filename(backend.Handle{Type: backend.ConfigFile}))
fi, err := os.Stat(l.Filename(backend.Handle{Type: backend.ConfigFile}))
m := util.DeriveModesFromFileInfo(fi, err)
debug.Log("using (%03O file, %03O dir) permissions", m.File, m.Dir)
@ -68,14 +68,14 @@ func Create(_ context.Context, cfg Config) (*Local, error) {
}
// test if config file already exists
_, err = fs.Lstat(be.Filename(backend.Handle{Type: backend.ConfigFile}))
_, err = os.Lstat(be.Filename(backend.Handle{Type: backend.ConfigFile}))
if err == nil {
return nil, errors.New("config file already exists")
}
// create paths for data and refs
for _, d := range be.Paths() {
err := fs.MkdirAll(d, be.Modes.Dir)
err := os.MkdirAll(d, be.Modes.Dir)
if err != nil {
return nil, errors.WithStack(err)
}
@ -127,7 +127,7 @@ func (b *Local) Save(_ context.Context, h backend.Handle, rd backend.RewindReade
debug.Log("error %v: creating dir", err)
// error is caused by a missing directory, try to create it
mkdirErr := fs.MkdirAll(dir, b.Modes.Dir)
mkdirErr := os.MkdirAll(dir, b.Modes.Dir)
if mkdirErr != nil {
debug.Log("error creating dir %v: %v", dir, mkdirErr)
} else {
@ -147,7 +147,7 @@ func (b *Local) Save(_ context.Context, h backend.Handle, rd backend.RewindReade
// temporary's name and no other goroutine will get the same data to
// Save, so the temporary name should never be reused by another
// goroutine.
_ = fs.Remove(f.Name())
_ = os.Remove(f.Name())
}
}(f)
@ -211,7 +211,7 @@ func (b *Local) Load(ctx context.Context, h backend.Handle, length int, offset i
}
func (b *Local) openReader(_ context.Context, h backend.Handle, length int, offset int64) (io.ReadCloser, error) {
f, err := fs.Open(b.Filename(h))
f, err := os.Open(b.Filename(h))
if err != nil {
return nil, err
}
@ -245,7 +245,7 @@ func (b *Local) openReader(_ context.Context, h backend.Handle, length int, offs
// Stat returns information about a blob.
func (b *Local) Stat(_ context.Context, h backend.Handle) (backend.FileInfo, error) {
fi, err := fs.Stat(b.Filename(h))
fi, err := os.Stat(b.Filename(h))
if err != nil {
return backend.FileInfo{}, errors.WithStack(err)
}
@ -258,12 +258,12 @@ func (b *Local) Remove(_ context.Context, h backend.Handle) error {
fn := b.Filename(h)
// reset read-only flag
err := fs.Chmod(fn, 0666)
err := os.Chmod(fn, 0666)
if err != nil && !os.IsPermission(err) {
return errors.WithStack(err)
}
return fs.Remove(fn)
return os.Remove(fn)
}
// List runs fn for each file in the backend which has the type t. When an
@ -289,7 +289,7 @@ func (b *Local) List(ctx context.Context, t backend.FileType, fn func(backend.Fi
// Also, visitDirs assumes it sees a directory full of directories, while
// visitFiles wants a directory full or regular files.
func visitDirs(ctx context.Context, dir string, fn func(backend.FileInfo) error) error {
d, err := fs.Open(dir)
d, err := os.Open(dir)
if err != nil {
return err
}
@ -316,7 +316,7 @@ func visitDirs(ctx context.Context, dir string, fn func(backend.FileInfo) error)
}
func visitFiles(ctx context.Context, dir string, fn func(backend.FileInfo) error, ignoreNotADirectory bool) error {
d, err := fs.Open(dir)
d, err := os.Open(dir)
if err != nil {
return err
}
@ -362,7 +362,7 @@ func visitFiles(ctx context.Context, dir string, fn func(backend.FileInfo) error
// Delete removes the repository and all files.
func (b *Local) Delete(_ context.Context) error {
return fs.RemoveAll(b.Path)
return os.RemoveAll(b.Path)
}
// Close closes all open files.

View file

@ -8,8 +8,6 @@ import (
"os"
"runtime"
"syscall"
"github.com/restic/restic/internal/fs"
)
// fsyncDir flushes changes to the directory dir.
@ -45,5 +43,5 @@ func isMacENOTTY(err error) bool {
// set file to readonly
func setFileReadonly(f string, mode os.FileMode) error {
return fs.Chmod(f, mode&^0222)
return os.Chmod(f, mode&^0222)
}

View file

@ -344,7 +344,7 @@ func (c *Checker) checkTree(id restic.ID, tree *restic.Tree) (errs []error) {
for _, node := range tree.Nodes {
switch node.Type {
case "file":
case restic.NodeTypeFile:
if node.Content == nil {
errs = append(errs, &Error{TreeID: id, Err: errors.Errorf("file %q has nil blob list", node.Name)})
}
@ -380,7 +380,7 @@ func (c *Checker) checkTree(id restic.ID, tree *restic.Tree) (errs []error) {
c.blobRefs.Unlock()
}
case "dir":
case restic.NodeTypeDir:
if node.Subtree == nil {
errs = append(errs, &Error{TreeID: id, Err: errors.Errorf("dir node %q has no subtree", node.Name)})
continue
@ -391,7 +391,7 @@ func (c *Checker) checkTree(id restic.ID, tree *restic.Tree) (errs []error) {
continue
}
case "symlink", "socket", "chardev", "dev", "fifo":
case restic.NodeTypeSymlink, restic.NodeTypeSocket, restic.NodeTypeCharDev, restic.NodeTypeDev, restic.NodeTypeFifo:
// nothing to check
default:

View file

@ -482,7 +482,7 @@ func TestCheckerBlobTypeConfusion(t *testing.T) {
damagedNode := &restic.Node{
Name: "damaged",
Type: "file",
Type: restic.NodeTypeFile,
Mode: 0644,
Size: 42,
Content: restic.IDs{restic.TestParseID("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")},
@ -507,14 +507,14 @@ func TestCheckerBlobTypeConfusion(t *testing.T) {
malNode := &restic.Node{
Name: "aaaaa",
Type: "file",
Type: restic.NodeTypeFile,
Mode: 0644,
Size: uint64(len(buf)),
Content: restic.IDs{id},
}
dirNode := &restic.Node{
Name: "bbbbb",
Type: "dir",
Type: restic.NodeTypeDir,
Mode: 0755,
Subtree: &id,
}

View file

@ -67,7 +67,7 @@ func sendNodes(ctx context.Context, repo restic.BlobLoader, root *restic.Node, c
}
// If this is no directory we are finished
if !IsDir(root) {
if root.Type != restic.NodeTypeDir {
return nil
}
@ -81,7 +81,7 @@ func sendNodes(ctx context.Context, repo restic.BlobLoader, root *restic.Node, c
node.Path = path.Join(root.Path, nodepath)
if !IsFile(node) && !IsDir(node) && !IsLink(node) {
if node.Type != restic.NodeTypeFile && node.Type != restic.NodeTypeDir && node.Type != restic.NodeTypeSymlink {
return nil
}
@ -176,18 +176,3 @@ func (d *Dumper) writeNode(ctx context.Context, w io.Writer, node *restic.Node)
return wg.Wait()
}
// IsDir checks if the given node is a directory.
func IsDir(node *restic.Node) bool {
return node.Type == "dir"
}
// IsLink checks if the given node as a link.
func IsLink(node *restic.Node) bool {
return node.Type == "symlink"
}
// IsFile checks if the given node is a file.
func IsFile(node *restic.Node) bool {
return node.Type == "file"
}

View file

@ -79,16 +79,16 @@ func (d *Dumper) dumpNodeTar(ctx context.Context, node *restic.Node, w *tar.Writ
header.Mode |= cISVTX
}
if IsFile(node) {
if node.Type == restic.NodeTypeFile {
header.Typeflag = tar.TypeReg
}
if IsLink(node) {
if node.Type == restic.NodeTypeSymlink {
header.Typeflag = tar.TypeSymlink
header.Linkname = node.LinkTarget
}
if IsDir(node) {
if node.Type == restic.NodeTypeDir {
header.Typeflag = tar.TypeDir
header.Name += "/"
}

View file

@ -13,7 +13,6 @@ import (
"testing"
"time"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
)
@ -83,7 +82,7 @@ func checkTar(t *testing.T, testDir string, srcTar *bytes.Buffer) error {
return fmt.Errorf("foldernames must end with separator got %v", hdr.Name)
}
case tar.TypeSymlink:
target, err := fs.Readlink(matchPath)
target, err := os.Readlink(matchPath)
if err != nil {
return err
}
@ -124,7 +123,7 @@ func TestFieldTooLong(t *testing.T) {
node := restic.Node{
Name: "file_with_xattr",
Path: "/file_with_xattr",
Type: "file",
Type: restic.NodeTypeFile,
Mode: 0644,
ExtendedAttributes: []restic.ExtendedAttribute{
{

View file

@ -40,7 +40,7 @@ func (d *Dumper) dumpNodeZip(ctx context.Context, node *restic.Node, zw *zip.Wri
}
header.SetMode(node.Mode)
if IsDir(node) {
if node.Type == restic.NodeTypeDir {
header.Name += "/"
}
@ -49,7 +49,7 @@ func (d *Dumper) dumpNodeZip(ctx context.Context, node *restic.Node, zw *zip.Wri
return errors.Wrap(err, "ZipHeader")
}
if IsLink(node) {
if node.Type == restic.NodeTypeSymlink {
if _, err = w.Write([]byte(node.LinkTarget)); err != nil {
return errors.Wrap(err, "Write")
}

View file

@ -9,8 +9,6 @@ import (
"strings"
"testing"
"time"
"github.com/restic/restic/internal/fs"
)
func TestWriteZip(t *testing.T) {
@ -91,7 +89,7 @@ func checkZip(t *testing.T, testDir string, srcZip *bytes.Buffer) error {
return fmt.Errorf("foldernames must end with separator got %v", f.Name)
}
case f.Mode()&os.ModeSymlink != 0:
target, err := fs.Readlink(matchPath)
target, err := os.Readlink(matchPath)
if err != nil {
return err
}

View file

@ -56,14 +56,14 @@ var (
errEaValueTooLarge = errors.New("extended attribute value too large")
)
// ExtendedAttribute represents a single Windows EA.
type ExtendedAttribute struct {
// extendedAttribute represents a single Windows EA.
type extendedAttribute struct {
Name string
Value []byte
Flags uint8
}
func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
func parseEa(b []byte) (ea extendedAttribute, nb []byte, err error) {
var info fileFullEaInformation
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
if err != nil {
@ -90,9 +90,9 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
return ea, nb, err
}
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
// decodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
func decodeExtendedAttributes(b []byte) (eas []extendedAttribute, err error) {
for len(b) != 0 {
ea, nb, err := parseEa(b)
if err != nil {
@ -105,7 +105,7 @@ func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
return eas, err
}
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
func writeEa(buf *bytes.Buffer, ea *extendedAttribute, last bool) error {
if int(uint8(len(ea.Name))) != len(ea.Name) {
return errEaNameTooLarge
}
@ -153,9 +153,9 @@ func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
return nil
}
// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
// encodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
// buffer for use with BackupWrite, ZwSetEaFile, etc.
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
func encodeExtendedAttributes(eas []extendedAttribute) ([]byte, error) {
var buf bytes.Buffer
for i := range eas {
last := false
@ -217,11 +217,11 @@ const (
STATUS_NO_EAS_ON_FILE = -1073741742
)
// GetFileEA retrieves the extended attributes for the file represented by `handle`. The
// fgetEA retrieves the extended attributes for the file represented by `handle`. The
// `handle` must have been opened with file access flag FILE_READ_EA (0x8).
// The extended file attribute names in windows are case-insensitive and when fetching
// the attributes the names are generally returned in UPPER case.
func GetFileEA(handle windows.Handle) ([]ExtendedAttribute, error) {
func fgetEA(handle windows.Handle) ([]extendedAttribute, error) {
// default buffer size to start with
bufLen := 1024
buf := make([]byte, bufLen)
@ -246,13 +246,13 @@ func GetFileEA(handle windows.Handle) ([]ExtendedAttribute, error) {
}
break
}
return DecodeExtendedAttributes(buf)
return decodeExtendedAttributes(buf)
}
// SetFileEA sets the extended attributes for the file represented by `handle`. The
// fsetEA sets the extended attributes for the file represented by `handle`. The
// handle must have been opened with the file access flag FILE_WRITE_EA(0x10).
func SetFileEA(handle windows.Handle, attrs []ExtendedAttribute) error {
encodedEA, err := EncodeExtendedAttributes(attrs)
func fsetEA(handle windows.Handle, attrs []extendedAttribute) error {
encodedEA, err := encodeExtendedAttributes(attrs)
if err != nil {
return fmt.Errorf("failed to encoded extended attributes: %w", err)
}
@ -285,8 +285,8 @@ func setFileEA(handle windows.Handle, iosb *ioStatusBlock, buf *uint8, bufLen ui
return
}
// PathSupportsExtendedAttributes returns true if the path supports extended attributes.
func PathSupportsExtendedAttributes(path string) (supported bool, err error) {
// pathSupportsExtendedAttributes returns true if the path supports extended attributes.
func pathSupportsExtendedAttributes(path string) (supported bool, err error) {
var fileSystemFlags uint32
utf16Path, err := windows.UTF16PtrFromString(path)
if err != nil {
@ -300,8 +300,8 @@ func PathSupportsExtendedAttributes(path string) (supported bool, err error) {
return supported, nil
}
// GetVolumePathName returns the volume path name for the given path.
func GetVolumePathName(path string) (volumeName string, err error) {
// getVolumePathName returns the volume path name for the given path.
func getVolumePathName(path string) (volumeName string, err error) {
utf16Path, err := windows.UTF16PtrFromString(path)
if err != nil {
return "", err

View file

@ -46,7 +46,7 @@ import (
// under MIT license.
var (
testEas = []ExtendedAttribute{
testEas = []extendedAttribute{
{Name: "foo", Value: []byte("bar")},
{Name: "fizz", Value: []byte("buzz")},
}
@ -58,14 +58,14 @@ var (
)
func TestRoundTripEas(t *testing.T) {
b, err := EncodeExtendedAttributes(testEas)
b, err := encodeExtendedAttributes(testEas)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(testEasEncoded, b) {
t.Fatalf("Encoded mismatch %v %v", testEasEncoded, b)
}
eas, err := DecodeExtendedAttributes(b)
eas, err := decodeExtendedAttributes(b)
if err != nil {
t.Fatal(err)
}
@ -75,7 +75,7 @@ func TestRoundTripEas(t *testing.T) {
}
func TestEasDontNeedPaddingAtEnd(t *testing.T) {
eas, err := DecodeExtendedAttributes(testEasNotPadded)
eas, err := decodeExtendedAttributes(testEasNotPadded)
if err != nil {
t.Fatal(err)
}
@ -85,21 +85,21 @@ func TestEasDontNeedPaddingAtEnd(t *testing.T) {
}
func TestTruncatedEasFailCorrectly(t *testing.T) {
_, err := DecodeExtendedAttributes(testEasTruncated)
_, err := decodeExtendedAttributes(testEasTruncated)
if err == nil {
t.Fatal("expected error")
}
}
func TestNilEasEncodeAndDecodeAsNil(t *testing.T) {
b, err := EncodeExtendedAttributes(nil)
b, err := encodeExtendedAttributes(nil)
if err != nil {
t.Fatal(err)
}
if len(b) != 0 {
t.Fatal("expected empty")
}
eas, err := DecodeExtendedAttributes(nil)
eas, err := decodeExtendedAttributes(nil)
if err != nil {
t.Fatal(err)
}
@ -178,8 +178,8 @@ func setupTestFolder(t *testing.T) string {
return testfolderPath
}
func generateTestEAs(t *testing.T, nAttrs int, path string) []ExtendedAttribute {
testEAs := make([]ExtendedAttribute, nAttrs)
func generateTestEAs(t *testing.T, nAttrs int, path string) []extendedAttribute {
testEAs := make([]extendedAttribute, nAttrs)
for i := 0; i < nAttrs; i++ {
testEAs[i].Name = fmt.Sprintf("TESTEA%d", i+1)
testEAs[i].Value = make([]byte, getRandomInt())
@ -231,12 +231,12 @@ func cleanupTestFile(t *testing.T, path string) {
}
}
func testSetGetEA(t *testing.T, path string, handle windows.Handle, testEAs []ExtendedAttribute) {
if err := SetFileEA(handle, testEAs); err != nil {
func testSetGetEA(t *testing.T, path string, handle windows.Handle, testEAs []extendedAttribute) {
if err := fsetEA(handle, testEAs); err != nil {
t.Fatalf("set EA for path %s failed: %s", path, err)
}
readEAs, err := GetFileEA(handle)
readEAs, err := fgetEA(handle)
if err != nil {
t.Fatalf("get EA for path %s failed: %s", path, err)
}
@ -262,7 +262,7 @@ func TestPathSupportsExtendedAttributes(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
supported, err := PathSupportsExtendedAttributes(tc.path)
supported, err := pathSupportsExtendedAttributes(tc.path)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
@ -273,7 +273,7 @@ func TestPathSupportsExtendedAttributes(t *testing.T) {
}
// Test with an invalid path
_, err := PathSupportsExtendedAttributes("Z:\\NonExistentPath-UAS664da5s4dyu56das45f5as")
_, err := pathSupportsExtendedAttributes("Z:\\NonExistentPath-UAS664da5s4dyu56das45f5as")
if err == nil {
t.Error("Expected an error for non-existent path, but got nil")
}
@ -305,7 +305,7 @@ func TestGetVolumePathName(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
volumeName, err := GetVolumePathName(tc.path)
volumeName, err := getVolumePathName(tc.path)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
@ -316,7 +316,7 @@ func TestGetVolumePathName(t *testing.T) {
}
// Test with an invalid path
_, err := GetVolumePathName("Z:\\NonExistentPath")
_, err := getVolumePathName("Z:\\NonExistentPath")
if err == nil {
t.Error("Expected an error for non-existent path, but got nil")
}

View file

@ -3,15 +3,8 @@ package fs
import (
"fmt"
"os"
"time"
)
// Mkdir creates a new directory with the specified name and permission bits.
// If there is an error, it will be of type *PathError.
func Mkdir(name string, perm os.FileMode) error {
return os.Mkdir(fixpath(name), perm)
}
// MkdirAll creates a directory named path, along with any necessary parents,
// and returns nil, or else returns an error. The permission bits perm are used
// for all directories that MkdirAll creates. If path is already a directory,
@ -20,12 +13,6 @@ func MkdirAll(path string, perm os.FileMode) error {
return os.MkdirAll(fixpath(path), perm)
}
// Readlink returns the destination of the named symbolic link.
// If there is an error, it will be of type *PathError.
func Readlink(name string) (string, error) {
return os.Readlink(fixpath(name))
}
// Remove removes the named file or directory.
// If there is an error, it will be of type *PathError.
func Remove(name string) error {
@ -40,32 +27,12 @@ func RemoveAll(path string) error {
return os.RemoveAll(fixpath(path))
}
// Rename renames (moves) oldpath to newpath.
// If newpath already exists, Rename replaces it.
// OS-specific restrictions may apply when oldpath and newpath are in different directories.
// If there is an error, it will be of type *LinkError.
func Rename(oldpath, newpath string) error {
return os.Rename(fixpath(oldpath), fixpath(newpath))
}
// Symlink creates newname as a symbolic link to oldname.
// If there is an error, it will be of type *LinkError.
func Symlink(oldname, newname string) error {
return os.Symlink(oldname, fixpath(newname))
}
// Link creates newname as a hard link to oldname.
// If there is an error, it will be of type *LinkError.
func Link(oldname, newname string) error {
return os.Link(fixpath(oldname), fixpath(newname))
}
// Stat returns a FileInfo structure describing the named file.
// If there is an error, it will be of type *PathError.
func Stat(name string) (os.FileInfo, error) {
return os.Stat(fixpath(name))
}
// Lstat returns the FileInfo structure describing the named file.
// If the file is a symbolic link, the returned FileInfo
// describes the symbolic link. Lstat makes no attempt to follow the link.
@ -74,11 +41,6 @@ func Lstat(name string) (os.FileInfo, error) {
return os.Lstat(fixpath(name))
}
// Open opens a file for reading.
func Open(name string) (File, error) {
return os.Open(fixpath(name))
}
// OpenFile is the generalized open call; most users will use Open
// or Create instead. It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
@ -88,15 +50,6 @@ func OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) {
return os.OpenFile(fixpath(name), flag, perm)
}
// Chtimes changes the access and modification times of the named file,
// similar to the Unix utime() or utimes() functions.
//
// The underlying filesystem may truncate or round the values to a less
// precise time unit. If there is an error, it will be of type *PathError.
func Chtimes(name string, atime time.Time, mtime time.Time) error {
return os.Chtimes(fixpath(name), atime, mtime)
}
// IsAccessDenied checks if the error is due to permission error.
func IsAccessDenied(err error) bool {
return os.IsPermission(err)

View file

@ -37,8 +37,8 @@ func isNotSupported(err error) bool {
return false
}
// Chmod changes the mode of the named file to mode.
func Chmod(name string, mode os.FileMode) error {
// chmod changes the mode of the named file to mode.
func chmod(name string, mode os.FileMode) error {
err := os.Chmod(fixpath(name), mode)
// ignore the error if the FS does not support setting this mode (e.g. CIFS with gvfs on Linux)

View file

@ -8,6 +8,7 @@ import (
"strings"
"time"
"github.com/restic/restic/internal/restic"
"golang.org/x/sys/windows"
)
@ -74,17 +75,17 @@ func TempFile(dir, prefix string) (f *os.File, err error) {
}
// Chmod changes the mode of the named file to mode.
func Chmod(name string, mode os.FileMode) error {
func chmod(name string, mode os.FileMode) error {
return os.Chmod(fixpath(name), mode)
}
// ClearSystem removes the system attribute from the file.
func ClearSystem(path string) error {
return ClearAttribute(path, windows.FILE_ATTRIBUTE_SYSTEM)
// clearSystem removes the system attribute from the file.
func clearSystem(path string) error {
return clearAttribute(path, windows.FILE_ATTRIBUTE_SYSTEM)
}
// ClearAttribute removes the specified attribute from the file.
func ClearAttribute(path string, attribute uint32) error {
// clearAttribute removes the specified attribute from the file.
func clearAttribute(path string, attribute uint32) error {
ptr, err := windows.UTF16PtrFromString(fixpath(path))
if err != nil {
return err
@ -104,8 +105,8 @@ func ClearAttribute(path string, attribute uint32) error {
return nil
}
// OpenHandleForEA return a file handle for file or dir for setting/getting EAs
func OpenHandleForEA(nodeType, path string, writeAccess bool) (handle windows.Handle, err error) {
// openHandleForEA return a file handle for file or dir for setting/getting EAs
func openHandleForEA(nodeType restic.NodeType, path string, writeAccess bool) (handle windows.Handle, err error) {
path = fixpath(path)
fileAccess := windows.FILE_READ_EA
if writeAccess {
@ -113,10 +114,10 @@ func OpenHandleForEA(nodeType, path string, writeAccess bool) (handle windows.Ha
}
switch nodeType {
case "file":
case restic.NodeTypeFile:
utf16Path := windows.StringToUTF16Ptr(path)
handle, err = windows.CreateFile(utf16Path, uint32(fileAccess), 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0)
case "dir":
case restic.NodeTypeDir:
utf16Path := windows.StringToUTF16Ptr(path)
handle, err = windows.CreateFile(utf16Path, uint32(fileAccess), 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
default:

View file

@ -79,7 +79,7 @@ func parseMountPoints(list string, msgError ErrorHandler) (volumes map[string]st
return
}
for _, s := range strings.Split(list, ";") {
if v, err := GetVolumeNameForVolumeMountPoint(s); err != nil {
if v, err := getVolumeNameForVolumeMountPoint(s); err != nil {
msgError(s, errors.Errorf("failed to parse vss.exclude-volumes [%s]: %s", s, err))
} else {
if volumes == nil {
@ -130,12 +130,12 @@ func (fs *LocalVss) OpenFile(name string, flag int, perm os.FileMode) (File, err
return os.OpenFile(fs.snapshotPath(name), flag, perm)
}
// Stat wraps the Open method of the underlying file system.
// Stat wraps the Stat method of the underlying file system.
func (fs *LocalVss) Stat(name string) (os.FileInfo, error) {
return os.Stat(fs.snapshotPath(name))
}
// Lstat wraps the Open method of the underlying file system.
// Lstat wraps the Lstat method of the underlying file system.
func (fs *LocalVss) Lstat(name string) (os.FileInfo, error) {
return os.Lstat(fs.snapshotPath(name))
}
@ -146,7 +146,7 @@ func (fs *LocalVss) isMountPointIncluded(mountPoint string) bool {
return true
}
volume, err := GetVolumeNameForVolumeMountPoint(mountPoint)
volume, err := getVolumeNameForVolumeMountPoint(mountPoint)
if err != nil {
fs.msgError(mountPoint, errors.Errorf("failed to get volume from mount point [%s]: %s", mountPoint, err))
return true

View file

@ -1,3 +1,4 @@
//go:build windows
// +build windows
package fs
@ -120,10 +121,10 @@ func TestVSSConfig(t *testing.T) {
func TestParseMountPoints(t *testing.T) {
volumeMatch := regexp.MustCompile(`^\\\\\?\\Volume\{[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}\}\\$`)
// It's not a good idea to test functions based on GetVolumeNameForVolumeMountPoint by calling
// GetVolumeNameForVolumeMountPoint itself, but we have restricted test environment:
// It's not a good idea to test functions based on getVolumeNameForVolumeMountPoint by calling
// getVolumeNameForVolumeMountPoint itself, but we have restricted test environment:
// cannot manage volumes and can only be sure that the mount point C:\ exists
sysVolume, err := GetVolumeNameForVolumeMountPoint("C:")
sysVolume, err := getVolumeNameForVolumeMountPoint("C:")
if err != nil {
t.Fatal(err)
}

View file

@ -229,22 +229,10 @@ type fakeFile struct {
// ensure that fakeFile implements File
var _ File = fakeFile{}
func (f fakeFile) Fd() uintptr {
return 0
}
func (f fakeFile) Readdirnames(_ int) ([]string, error) {
return nil, pathError("readdirnames", f.name, os.ErrInvalid)
}
func (f fakeFile) Readdir(_ int) ([]os.FileInfo, error) {
return nil, pathError("readdir", f.name, os.ErrInvalid)
}
func (f fakeFile) Seek(int64, int) (int64, error) {
return 0, pathError("seek", f.name, os.ErrInvalid)
}
func (f fakeFile) Read(_ []byte) (int, error) {
return 0, pathError("read", f.name, os.ErrInvalid)
}
@ -279,13 +267,6 @@ func (d fakeDir) Readdirnames(n int) ([]string, error) {
return names, nil
}
func (d fakeDir) Readdir(n int) ([]os.FileInfo, error) {
if n > 0 {
return nil, pathError("readdir", d.name, errors.New("not implemented"))
}
return d.entries, nil
}
// fakeFileInfo implements the bare minimum of os.FileInfo.
type fakeFileInfo struct {
name string

View file

@ -10,7 +10,7 @@ import (
"github.com/restic/restic/internal/errors"
)
// CommandReader wrap a command such that its standard output can be read using
// CommandReader wraps a command such that its standard output can be read using
// a io.ReadCloser. Close() waits for the command to terminate, reporting
// any error back to the caller.
type CommandReader struct {

View file

@ -60,77 +60,6 @@ func verifyDirectoryContents(t testing.TB, fs FS, dir string, want []string) {
}
}
type fiSlice []os.FileInfo
func (s fiSlice) Len() int {
return len(s)
}
func (s fiSlice) Less(i, j int) bool {
return s[i].Name() < s[j].Name()
}
func (s fiSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func verifyDirectoryContentsFI(t testing.TB, fs FS, dir string, want []os.FileInfo) {
f, err := fs.OpenFile(dir, os.O_RDONLY, 0)
if err != nil {
t.Fatal(err)
}
entries, err := f.Readdir(-1)
if err != nil {
t.Fatal(err)
}
err = f.Close()
if err != nil {
t.Fatal(err)
}
sort.Sort(fiSlice(want))
sort.Sort(fiSlice(entries))
if len(want) != len(entries) {
t.Errorf("wrong number of entries returned, want %d, got %d", len(want), len(entries))
}
max := len(want)
if len(entries) < max {
max = len(entries)
}
for i := 0; i < max; i++ {
fi1 := want[i]
fi2 := entries[i]
if fi1.Name() != fi2.Name() {
t.Errorf("entry %d: wrong value for Name: want %q, got %q", i, fi1.Name(), fi2.Name())
}
if fi1.IsDir() != fi2.IsDir() {
t.Errorf("entry %d: wrong value for IsDir: want %v, got %v", i, fi1.IsDir(), fi2.IsDir())
}
if fi1.Mode() != fi2.Mode() {
t.Errorf("entry %d: wrong value for Mode: want %v, got %v", i, fi1.Mode(), fi2.Mode())
}
if fi1.ModTime() != fi2.ModTime() {
t.Errorf("entry %d: wrong value for ModTime: want %v, got %v", i, fi1.ModTime(), fi2.ModTime())
}
if fi1.Size() != fi2.Size() {
t.Errorf("entry %d: wrong value for Size: want %v, got %v", i, fi1.Size(), fi2.Size())
}
if fi1.Sys() != fi2.Sys() {
t.Errorf("entry %d: wrong value for Sys: want %v, got %v", i, fi1.Sys(), fi2.Sys())
}
}
}
func checkFileInfo(t testing.TB, fi os.FileInfo, filename string, modtime time.Time, mode os.FileMode, isdir bool) {
if fi.IsDir() != isdir {
t.Errorf("IsDir returned %t, want %t", fi.IsDir(), isdir)
@ -174,30 +103,6 @@ func TestFSReader(t *testing.T) {
verifyDirectoryContents(t, fs, ".", []string{filename})
},
},
{
name: "Readdir-slash",
f: func(t *testing.T, fs FS) {
fi := fakeFileInfo{
mode: 0644,
modtime: now,
name: filename,
size: int64(len(data)),
}
verifyDirectoryContentsFI(t, fs, "/", []os.FileInfo{fi})
},
},
{
name: "Readdir-current",
f: func(t *testing.T, fs FS) {
fi := fakeFileInfo{
mode: 0644,
modtime: now,
name: filename,
size: int64(len(data)),
}
verifyDirectoryContentsFI(t, fs, ".", []os.FileInfo{fi})
},
},
{
name: "file/OpenFile",
f: func(t *testing.T, fs FS) {

View file

@ -29,10 +29,7 @@ type File interface {
io.Reader
io.Closer
Fd() uintptr
Readdirnames(n int) ([]string, error)
Readdir(int) ([]os.FileInfo, error)
Seek(int64, int) (int64, error)
Stat() (os.FileInfo, error)
Name() string
}

View file

@ -6,7 +6,6 @@ import (
"strconv"
"sync"
"syscall"
"time"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
@ -25,7 +24,7 @@ func NodeFromFileInfo(path string, fi os.FileInfo, ignoreXattrListError bool) (*
}
node.Type = nodeTypeFromFileInfo(fi)
if node.Type == "file" {
if node.Type == restic.NodeTypeFile {
node.Size = uint64(fi.Size())
}
@ -33,32 +32,31 @@ func NodeFromFileInfo(path string, fi os.FileInfo, ignoreXattrListError bool) (*
return node, err
}
func nodeTypeFromFileInfo(fi os.FileInfo) string {
func nodeTypeFromFileInfo(fi os.FileInfo) restic.NodeType {
switch fi.Mode() & os.ModeType {
case 0:
return "file"
return restic.NodeTypeFile
case os.ModeDir:
return "dir"
return restic.NodeTypeDir
case os.ModeSymlink:
return "symlink"
return restic.NodeTypeSymlink
case os.ModeDevice | os.ModeCharDevice:
return "chardev"
return restic.NodeTypeCharDev
case os.ModeDevice:
return "dev"
return restic.NodeTypeDev
case os.ModeNamedPipe:
return "fifo"
return restic.NodeTypeFifo
case os.ModeSocket:
return "socket"
return restic.NodeTypeSocket
case os.ModeIrregular:
return "irregular"
return restic.NodeTypeIrregular
}
return ""
return restic.NodeTypeInvalid
}
func nodeFillExtra(node *restic.Node, path string, fi os.FileInfo, ignoreXattrListError bool) error {
stat, ok := toStatT(fi.Sys())
if !ok {
if fi.Sys() == nil {
// fill minimal info with current values for uid, gid
node.UID = uint32(os.Getuid())
node.GID = uint32(os.Getgid())
@ -66,38 +64,43 @@ func nodeFillExtra(node *restic.Node, path string, fi os.FileInfo, ignoreXattrLi
return nil
}
node.Inode = uint64(stat.ino())
node.DeviceID = uint64(stat.dev())
stat := ExtendedStat(fi)
nodeFillTimes(node, stat)
node.Inode = stat.Inode
node.DeviceID = stat.DeviceID
node.ChangeTime = stat.ChangeTime
node.AccessTime = stat.AccessTime
nodeFillUser(node, stat)
node.UID = stat.UID
node.GID = stat.GID
node.User = lookupUsername(stat.UID)
node.Group = lookupGroup(stat.GID)
switch node.Type {
case "file":
node.Size = uint64(stat.size())
node.Links = uint64(stat.nlink())
case "dir":
case "symlink":
case restic.NodeTypeFile:
node.Size = uint64(stat.Size)
node.Links = stat.Links
case restic.NodeTypeDir:
case restic.NodeTypeSymlink:
var err error
node.LinkTarget, err = Readlink(path)
node.Links = uint64(stat.nlink())
node.LinkTarget, err = os.Readlink(fixpath(path))
node.Links = stat.Links
if err != nil {
return errors.WithStack(err)
}
case "dev":
node.Device = uint64(stat.rdev())
node.Links = uint64(stat.nlink())
case "chardev":
node.Device = uint64(stat.rdev())
node.Links = uint64(stat.nlink())
case "fifo":
case "socket":
case restic.NodeTypeDev:
node.Device = stat.Device
node.Links = stat.Links
case restic.NodeTypeCharDev:
node.Device = stat.Device
node.Links = stat.Links
case restic.NodeTypeFifo:
case restic.NodeTypeSocket:
default:
return errors.Errorf("unsupported file type %q", node.Type)
}
allowExtended, err := nodeFillGenericAttributes(node, path, fi, stat)
allowExtended, err := nodeFillGenericAttributes(node, path, &stat)
if allowExtended {
// Skip processing ExtendedAttributes if allowExtended is false.
err = errors.CombineErrors(err, nodeFillExtendedAttributes(node, path, ignoreXattrListError))
@ -105,20 +108,6 @@ func nodeFillExtra(node *restic.Node, path string, fi os.FileInfo, ignoreXattrLi
return err
}
func nodeFillTimes(node *restic.Node, stat *statT) {
ctim := stat.ctim()
atim := stat.atim()
node.ChangeTime = time.Unix(ctim.Unix())
node.AccessTime = time.Unix(atim.Unix())
}
func nodeFillUser(node *restic.Node, stat *statT) {
uid, gid := stat.uid(), stat.gid()
node.UID, node.GID = uid, gid
node.User = lookupUsername(uid)
node.Group = lookupGroup(gid)
}
var (
uidLookupCache = make(map[uint32]string)
uidLookupCacheMutex = sync.RWMutex{}
@ -178,31 +167,31 @@ func NodeCreateAt(node *restic.Node, path string) error {
debug.Log("create node %v at %v", node.Name, path)
switch node.Type {
case "dir":
case restic.NodeTypeDir:
if err := nodeCreateDirAt(node, path); err != nil {
return err
}
case "file":
case restic.NodeTypeFile:
if err := nodeCreateFileAt(path); err != nil {
return err
}
case "symlink":
case restic.NodeTypeSymlink:
if err := nodeCreateSymlinkAt(node, path); err != nil {
return err
}
case "dev":
case restic.NodeTypeDev:
if err := nodeCreateDevAt(node, path); err != nil {
return err
}
case "chardev":
case restic.NodeTypeCharDev:
if err := nodeCreateCharDevAt(node, path); err != nil {
return err
}
case "fifo":
case restic.NodeTypeFifo:
if err := nodeCreateFifoAt(path); err != nil {
return err
}
case "socket":
case restic.NodeTypeSocket:
return nil
default:
return errors.Errorf("filetype %q not implemented", node.Type)
@ -212,7 +201,7 @@ func NodeCreateAt(node *restic.Node, path string) error {
}
func nodeCreateDirAt(node *restic.Node, path string) error {
err := Mkdir(path, node.Mode)
err := os.Mkdir(fixpath(path), node.Mode)
if err != nil && !os.IsExist(err) {
return errors.WithStack(err)
}
@ -234,7 +223,7 @@ func nodeCreateFileAt(path string) error {
}
func nodeCreateSymlinkAt(node *restic.Node, path string) error {
if err := Symlink(node.LinkTarget, path); err != nil {
if err := os.Symlink(node.LinkTarget, fixpath(path)); err != nil {
return errors.WithStack(err)
}
@ -295,7 +284,7 @@ func nodeRestoreMetadata(node *restic.Node, path string, warn func(msg string))
}
}
if err := NodeRestoreTimestamps(node, path); err != nil {
if err := nodeRestoreTimestamps(node, path); err != nil {
debug.Log("error restoring timestamps for %v: %v", path, err)
if firsterr == nil {
firsterr = err
@ -305,8 +294,8 @@ func nodeRestoreMetadata(node *restic.Node, path string, warn func(msg string))
// Moving RestoreTimestamps and restoreExtendedAttributes calls above as for readonly files in windows
// calling Chmod below will no longer allow any modifications to be made on the file and the
// calls above would fail.
if node.Type != "symlink" {
if err := Chmod(path, node.Mode); err != nil {
if node.Type != restic.NodeTypeSymlink {
if err := chmod(path, node.Mode); err != nil {
if firsterr == nil {
firsterr = errors.WithStack(err)
}
@ -316,13 +305,13 @@ func nodeRestoreMetadata(node *restic.Node, path string, warn func(msg string))
return firsterr
}
func NodeRestoreTimestamps(node *restic.Node, path string) error {
func nodeRestoreTimestamps(node *restic.Node, path string) error {
var utimes = [...]syscall.Timespec{
syscall.NsecToTimespec(node.AccessTime.UnixNano()),
syscall.NsecToTimespec(node.ModTime.UnixNano()),
}
if node.Type == "symlink" {
if node.Type == restic.NodeTypeSymlink {
return nodeRestoreSymlinkTimestamps(path, utimes)
}

View file

@ -4,7 +4,6 @@
package fs
import (
"os"
"syscall"
"github.com/restic/restic/internal/restic"
@ -14,17 +13,6 @@ func nodeRestoreSymlinkTimestamps(_ string, _ [2]syscall.Timespec) error {
return nil
}
// AIX has a funny timespec type in syscall, with 32-bit nanoseconds.
// golang.org/x/sys/unix handles this cleanly, but we're stuck with syscall
// because os.Stat returns a syscall type in its os.FileInfo.Sys().
func toTimespec(t syscall.StTimespec_t) syscall.Timespec {
return syscall.Timespec{Sec: t.Sec, Nsec: int64(t.Nsec)}
}
func (s statT) atim() syscall.Timespec { return toTimespec(s.Atim) }
func (s statT) mtim() syscall.Timespec { return toTimespec(s.Mtim) }
func (s statT) ctim() syscall.Timespec { return toTimespec(s.Ctim) }
// nodeRestoreExtendedAttributes is a no-op on AIX.
func nodeRestoreExtendedAttributes(_ *restic.Node, _ string) error {
return nil
@ -35,17 +23,12 @@ func nodeFillExtendedAttributes(_ *restic.Node, _ string, _ bool) error {
return nil
}
// isListxattrPermissionError is a no-op on AIX.
func isListxattrPermissionError(_ error) bool {
return false
}
// nodeRestoreGenericAttributes is no-op on AIX.
func nodeRestoreGenericAttributes(node *restic.Node, _ string, warn func(msg string)) error {
return restic.HandleAllUnknownGenericAttributesFound(node.GenericAttributes, warn)
}
// nodeFillGenericAttributes is a no-op on AIX.
func nodeFillGenericAttributes(_ *restic.Node, _ string, _ os.FileInfo, _ *statT) (allowExtended bool, err error) {
func nodeFillGenericAttributes(_ *restic.Node, _ string, _ *ExtendedFileInfo) (allowExtended bool, err error) {
return true, nil
}

View file

@ -5,7 +5,3 @@ import "syscall"
func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
return nil
}
func (s statT) atim() syscall.Timespec { return s.Atimespec }
func (s statT) mtim() syscall.Timespec { return s.Mtimespec }
func (s statT) ctim() syscall.Timespec { return s.Ctimespec }

View file

@ -12,7 +12,3 @@ func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error
func mknod(path string, mode uint32, dev uint64) (err error) {
return syscall.Mknod(path, mode, dev)
}
func (s statT) atim() syscall.Timespec { return s.Atimespec }
func (s statT) mtim() syscall.Timespec { return s.Mtimespec }
func (s statT) ctim() syscall.Timespec { return s.Ctimespec }

View file

@ -1,6 +1,7 @@
package fs
import (
"os"
"path/filepath"
"syscall"
@ -10,7 +11,7 @@ import (
)
func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
dir, err := Open(filepath.Dir(path))
dir, err := os.Open(fixpath(filepath.Dir(path)))
if err != nil {
return errors.WithStack(err)
}
@ -30,7 +31,3 @@ func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error
return dir.Close()
}
func (s statT) atim() syscall.Timespec { return s.Atim }
func (s statT) mtim() syscall.Timespec { return s.Mtim }
func (s statT) ctim() syscall.Timespec { return s.Ctim }

View file

@ -1,7 +1,6 @@
package fs
import (
"os"
"syscall"
"github.com/restic/restic/internal/restic"
@ -11,10 +10,6 @@ func nodeRestoreSymlinkTimestamps(_ string, _ [2]syscall.Timespec) error {
return nil
}
func (s statT) atim() syscall.Timespec { return s.Atimespec }
func (s statT) mtim() syscall.Timespec { return s.Mtimespec }
func (s statT) ctim() syscall.Timespec { return s.Ctimespec }
// nodeRestoreExtendedAttributes is a no-op on netbsd.
func nodeRestoreExtendedAttributes(_ *restic.Node, _ string) error {
return nil
@ -25,17 +20,12 @@ func nodeFillExtendedAttributes(_ *restic.Node, _ string, _ bool) error {
return nil
}
// isListxattrPermissionError is a no-op on netbsd.
func isListxattrPermissionError(_ error) bool {
return false
}
// nodeRestoreGenericAttributes is no-op on netbsd.
func nodeRestoreGenericAttributes(node *restic.Node, _ string, warn func(msg string)) error {
return restic.HandleAllUnknownGenericAttributesFound(node.GenericAttributes, warn)
}
// nodeFillGenericAttributes is a no-op on netbsd.
func nodeFillGenericAttributes(_ *restic.Node, _ string, _ os.FileInfo, _ *statT) (allowExtended bool, err error) {
func nodeFillGenericAttributes(_ *restic.Node, _ string, _ *ExtendedFileInfo) (allowExtended bool, err error) {
return true, nil
}

View file

@ -1,7 +1,6 @@
package fs
import (
"os"
"syscall"
"github.com/restic/restic/internal/restic"
@ -11,10 +10,6 @@ func nodeRestoreSymlinkTimestamps(_ string, _ [2]syscall.Timespec) error {
return nil
}
func (s statT) atim() syscall.Timespec { return s.Atim }
func (s statT) mtim() syscall.Timespec { return s.Mtim }
func (s statT) ctim() syscall.Timespec { return s.Ctim }
// nodeRestoreExtendedAttributes is a no-op on openbsd.
func nodeRestoreExtendedAttributes(_ *restic.Node, _ string) error {
return nil
@ -25,17 +20,12 @@ func nodeFillExtendedAttributes(_ *restic.Node, _ string, _ bool) error {
return nil
}
// isListxattrPermissionError is a no-op on openbsd.
func isListxattrPermissionError(_ error) bool {
return false
}
// nodeRestoreGenericAttributes is no-op on openbsd.
func nodeRestoreGenericAttributes(node *restic.Node, _ string, warn func(msg string)) error {
return restic.HandleAllUnknownGenericAttributesFound(node.GenericAttributes, warn)
}
// fillGenericAttributes is a no-op on openbsd.
func nodeFillGenericAttributes(_ *restic.Node, _ string, _ os.FileInfo, _ *statT) (allowExtended bool, err error) {
func nodeFillGenericAttributes(_ *restic.Node, _ string, _ *ExtendedFileInfo) (allowExtended bool, err error) {
return true, nil
}

View file

@ -5,7 +5,3 @@ import "syscall"
func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
return nil
}
func (s statT) atim() syscall.Timespec { return s.Atim }
func (s statT) mtim() syscall.Timespec { return s.Mtim }
func (s statT) ctim() syscall.Timespec { return s.Ctim }

View file

@ -79,7 +79,7 @@ func parseTime(s string) time.Time {
var nodeTests = []restic.Node{
{
Name: "testFile",
Type: "file",
Type: restic.NodeTypeFile,
Content: restic.IDs{},
UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()),
@ -90,7 +90,7 @@ var nodeTests = []restic.Node{
},
{
Name: "testSuidFile",
Type: "file",
Type: restic.NodeTypeFile,
Content: restic.IDs{},
UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()),
@ -101,7 +101,7 @@ var nodeTests = []restic.Node{
},
{
Name: "testSuidFile2",
Type: "file",
Type: restic.NodeTypeFile,
Content: restic.IDs{},
UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()),
@ -112,7 +112,7 @@ var nodeTests = []restic.Node{
},
{
Name: "testSticky",
Type: "file",
Type: restic.NodeTypeFile,
Content: restic.IDs{},
UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()),
@ -123,7 +123,7 @@ var nodeTests = []restic.Node{
},
{
Name: "testDir",
Type: "dir",
Type: restic.NodeTypeDir,
Subtree: nil,
UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()),
@ -134,7 +134,7 @@ var nodeTests = []restic.Node{
},
{
Name: "testSymlink",
Type: "symlink",
Type: restic.NodeTypeSymlink,
LinkTarget: "invalid",
UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()),
@ -148,7 +148,7 @@ var nodeTests = []restic.Node{
// metadata, so we can test if CreateAt works with pre-existing files.
{
Name: "testFile",
Type: "file",
Type: restic.NodeTypeFile,
Content: restic.IDs{},
UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()),
@ -159,7 +159,7 @@ var nodeTests = []restic.Node{
},
{
Name: "testDir",
Type: "dir",
Type: restic.NodeTypeDir,
Subtree: nil,
UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()),
@ -170,7 +170,7 @@ var nodeTests = []restic.Node{
},
{
Name: "testXattrFile",
Type: "file",
Type: restic.NodeTypeFile,
Content: restic.IDs{},
UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()),
@ -184,7 +184,7 @@ var nodeTests = []restic.Node{
},
{
Name: "testXattrDir",
Type: "dir",
Type: restic.NodeTypeDir,
Subtree: nil,
UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()),
@ -198,7 +198,7 @@ var nodeTests = []restic.Node{
},
{
Name: "testXattrFileMacOSResourceFork",
Type: "file",
Type: restic.NodeTypeFile,
Content: restic.IDs{},
UID: uint32(os.Getuid()),
GID: uint32(os.Getgid()),
@ -268,7 +268,7 @@ func TestNodeRestoreAt(t *testing.T) {
"%v: UID doesn't match (%v != %v)", test.Type, test.UID, n2.UID)
rtest.Assert(t, test.GID == n2.GID,
"%v: GID doesn't match (%v != %v)", test.Type, test.GID, n2.GID)
if test.Type != "symlink" {
if test.Type != restic.NodeTypeSymlink {
// On OpenBSD only root can set sticky bit (see sticky(8)).
if runtime.GOOS != "openbsd" && runtime.GOOS != "netbsd" && runtime.GOOS != "solaris" && test.Name == "testSticky" {
rtest.Assert(t, test.Mode == n2.Mode,
@ -288,11 +288,11 @@ func TestNodeRestoreAt(t *testing.T) {
}
}
func AssertFsTimeEqual(t *testing.T, label string, nodeType string, t1 time.Time, t2 time.Time) {
func AssertFsTimeEqual(t *testing.T, label string, nodeType restic.NodeType, t1 time.Time, t2 time.Time) {
var equal bool
// Go currently doesn't support setting timestamps of symbolic links on darwin and bsd
if nodeType == "symlink" {
if nodeType == restic.NodeTypeSymlink {
switch runtime.GOOS {
case "darwin", "freebsd", "openbsd", "netbsd", "solaris":
return

View file

@ -5,27 +5,8 @@ package fs
import (
"os"
"syscall"
)
func lchown(name string, uid, gid int) error {
return os.Lchown(name, uid, gid)
}
type statT syscall.Stat_t
func toStatT(i interface{}) (*statT, bool) {
s, ok := i.(*syscall.Stat_t)
if ok && s != nil {
return (*statT)(s), true
}
return nil, false
}
func (s statT) dev() uint64 { return uint64(s.Dev) }
func (s statT) ino() uint64 { return uint64(s.Ino) }
func (s statT) nlink() uint64 { return uint64(s.Nlink) }
func (s statT) uid() uint32 { return uint32(s.Uid) }
func (s statT) gid() uint32 { return uint32(s.Gid) }
func (s statT) rdev() uint64 { return uint64(s.Rdev) }
func (s statT) size() int64 { return int64(s.Size) }

View file

@ -4,12 +4,12 @@
package fs
import (
"io/fs"
"os"
"path/filepath"
"runtime"
"syscall"
"testing"
"time"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
@ -28,8 +28,11 @@ func stat(t testing.TB, filename string) (fi os.FileInfo, ok bool) {
return fi, true
}
func checkFile(t testing.TB, stat *syscall.Stat_t, node *restic.Node) {
func checkFile(t testing.TB, fi fs.FileInfo, node *restic.Node) {
t.Helper()
stat := fi.Sys().(*syscall.Stat_t)
if uint32(node.Mode.Perm()) != uint32(stat.Mode&0777) {
t.Errorf("Mode does not match, want %v, got %v", stat.Mode&0777, node.Mode)
}
@ -42,7 +45,7 @@ func checkFile(t testing.TB, stat *syscall.Stat_t, node *restic.Node) {
t.Errorf("Dev does not match, want %v, got %v", stat.Dev, node.DeviceID)
}
if node.Size != uint64(stat.Size) && node.Type != "symlink" {
if node.Size != uint64(stat.Size) && node.Type != restic.NodeTypeSymlink {
t.Errorf("Size does not match, want %v, got %v", stat.Size, node.Size)
}
@ -59,29 +62,20 @@ func checkFile(t testing.TB, stat *syscall.Stat_t, node *restic.Node) {
}
// use the os dependent function to compare the timestamps
s, ok := toStatT(stat)
if !ok {
return
s := ExtendedStat(fi)
if node.ModTime != s.ModTime {
t.Errorf("ModTime does not match, want %v, got %v", s.ModTime, node.ModTime)
}
mtime := s.mtim()
if node.ModTime != time.Unix(mtime.Unix()) {
t.Errorf("ModTime does not match, want %v, got %v", time.Unix(mtime.Unix()), node.ModTime)
if node.ChangeTime != s.ChangeTime {
t.Errorf("ChangeTime does not match, want %v, got %v", s.ChangeTime, node.ChangeTime)
}
ctime := s.ctim()
if node.ChangeTime != time.Unix(ctime.Unix()) {
t.Errorf("ChangeTime does not match, want %v, got %v", time.Unix(ctime.Unix()), node.ChangeTime)
if node.AccessTime != s.AccessTime {
t.Errorf("AccessTime does not match, want %v, got %v", s.AccessTime, node.AccessTime)
}
atime := s.atim()
if node.AccessTime != time.Unix(atime.Unix()) {
t.Errorf("AccessTime does not match, want %v, got %v", time.Unix(atime.Unix()), node.AccessTime)
}
}
func checkDevice(t testing.TB, stat *syscall.Stat_t, node *restic.Node) {
func checkDevice(t testing.TB, fi fs.FileInfo, node *restic.Node) {
stat := fi.Sys().(*syscall.Stat_t)
if node.Device != uint64(stat.Rdev) {
t.Errorf("Rdev does not match, want %v, got %v", stat.Rdev, node.Device)
}
@ -123,23 +117,17 @@ func TestNodeFromFileInfo(t *testing.T) {
return
}
s, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
t.Skipf("fi type is %T, not stat_t", fi.Sys())
return
}
node, err := NodeFromFileInfo(test.filename, fi, false)
if err != nil {
t.Fatal(err)
}
switch node.Type {
case "file", "symlink":
checkFile(t, s, node)
case "dev", "chardev":
checkFile(t, s, node)
checkDevice(t, s, node)
case restic.NodeTypeFile, restic.NodeTypeSymlink:
checkFile(t, fi, node)
case restic.NodeTypeDev, restic.NodeTypeCharDev:
checkFile(t, fi, node)
checkDevice(t, fi, node)
default:
t.Fatalf("invalid node type %q", node.Type)
}

View file

@ -3,10 +3,8 @@ package fs
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"sync"
"syscall"
@ -18,17 +16,6 @@ import (
"golang.org/x/sys/windows"
)
// WindowsAttributes are the genericAttributes for Windows OS
type WindowsAttributes struct {
// CreationTime is used for storing creation time for windows files.
CreationTime *syscall.Filetime `generic:"creation_time"`
// FileAttributes is used for storing file attributes for windows files.
FileAttributes *uint32 `generic:"file_attributes"`
// SecurityDescriptor is used for storing security descriptors which includes
// owner, group, discretionary access control list (DACL), system access control list (SACL)
SecurityDescriptor *[]byte `generic:"security_descriptor"`
}
var (
modAdvapi32 = syscall.NewLazyDLL("advapi32.dll")
procEncryptFile = modAdvapi32.NewProc("EncryptFileW")
@ -58,7 +45,7 @@ func lchown(_ string, _ int, _ int) (err error) {
// restoreSymlinkTimestamps restores timestamps for symlinks
func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
// tweaked version of UtimesNano from go/src/syscall/syscall_windows.go
pathp, e := syscall.UTF16PtrFromString(path)
pathp, e := syscall.UTF16PtrFromString(fixpath(path))
if e != nil {
return e
}
@ -85,9 +72,9 @@ func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error
func nodeRestoreExtendedAttributes(node *restic.Node, path string) (err error) {
count := len(node.ExtendedAttributes)
if count > 0 {
eas := make([]ExtendedAttribute, count)
eas := make([]extendedAttribute, count)
for i, attr := range node.ExtendedAttributes {
eas[i] = ExtendedAttribute{Name: attr.Name, Value: attr.Value}
eas[i] = extendedAttribute{Name: attr.Name, Value: attr.Value}
}
if errExt := restoreExtendedAttributes(node.Type, path, eas); errExt != nil {
return errExt
@ -99,7 +86,7 @@ func nodeRestoreExtendedAttributes(node *restic.Node, path string) (err error) {
// fill extended attributes in the node. This also includes the Generic attributes for windows.
func nodeFillExtendedAttributes(node *restic.Node, path string, _ bool) (err error) {
var fileHandle windows.Handle
if fileHandle, err = OpenHandleForEA(node.Type, path, false); fileHandle == 0 {
if fileHandle, err = openHandleForEA(node.Type, path, false); fileHandle == 0 {
return nil
}
if err != nil {
@ -107,8 +94,8 @@ func nodeFillExtendedAttributes(node *restic.Node, path string, _ bool) (err err
}
defer closeFileHandle(fileHandle, path) // Replaced inline defer with named function call
//Get the windows Extended Attributes using the file handle
var extAtts []ExtendedAttribute
extAtts, err = GetFileEA(fileHandle)
var extAtts []extendedAttribute
extAtts, err = fgetEA(fileHandle)
debug.Log("fillExtendedAttributes(%v) %v", path, extAtts)
if err != nil {
return errors.Errorf("get EA failed for path %v, with: %v", path, err)
@ -139,9 +126,9 @@ func closeFileHandle(fileHandle windows.Handle, path string) {
// restoreExtendedAttributes handles restore of the Windows Extended Attributes to the specified path.
// The Windows API requires setting of all the Extended Attributes in one call.
func restoreExtendedAttributes(nodeType, path string, eas []ExtendedAttribute) (err error) {
func restoreExtendedAttributes(nodeType restic.NodeType, path string, eas []extendedAttribute) (err error) {
var fileHandle windows.Handle
if fileHandle, err = OpenHandleForEA(nodeType, path, true); fileHandle == 0 {
if fileHandle, err = openHandleForEA(nodeType, path, true); fileHandle == 0 {
return nil
}
if err != nil {
@ -150,7 +137,7 @@ func restoreExtendedAttributes(nodeType, path string, eas []ExtendedAttribute) (
defer closeFileHandle(fileHandle, path) // Replaced inline defer with named function call
// clear old unexpected xattrs by setting them to an empty value
oldEAs, err := GetFileEA(fileHandle)
oldEAs, err := fgetEA(fileHandle)
if err != nil {
return err
}
@ -165,50 +152,16 @@ func restoreExtendedAttributes(nodeType, path string, eas []ExtendedAttribute) (
}
if !found {
eas = append(eas, ExtendedAttribute{Name: oldEA.Name, Value: nil})
eas = append(eas, extendedAttribute{Name: oldEA.Name, Value: nil})
}
}
if err = SetFileEA(fileHandle, eas); err != nil {
if err = fsetEA(fileHandle, eas); err != nil {
return errors.Errorf("set EA failed for path %v, with: %v", path, err)
}
return nil
}
type statT syscall.Win32FileAttributeData
func toStatT(i interface{}) (*statT, bool) {
s, ok := i.(*syscall.Win32FileAttributeData)
if ok && s != nil {
return (*statT)(s), true
}
return nil, false
}
func (s statT) dev() uint64 { return 0 }
func (s statT) ino() uint64 { return 0 }
func (s statT) nlink() uint64 { return 0 }
func (s statT) uid() uint32 { return 0 }
func (s statT) gid() uint32 { return 0 }
func (s statT) rdev() uint64 { return 0 }
func (s statT) size() int64 {
return int64(s.FileSizeLow) | (int64(s.FileSizeHigh) << 32)
}
func (s statT) atim() syscall.Timespec {
return syscall.NsecToTimespec(s.LastAccessTime.Nanoseconds())
}
func (s statT) mtim() syscall.Timespec {
return syscall.NsecToTimespec(s.LastWriteTime.Nanoseconds())
}
func (s statT) ctim() syscall.Timespec {
// Windows does not have the concept of a "change time" in the sense Unix uses it, so we're using the LastWriteTime here.
return s.mtim()
}
// restoreGenericAttributes restores generic attributes for Windows
func nodeRestoreGenericAttributes(node *restic.Node, path string, warn func(msg string)) (err error) {
if len(node.GenericAttributes) == 0 {
@ -230,7 +183,7 @@ func nodeRestoreGenericAttributes(node *restic.Node, path string, warn func(msg
}
}
if windowsAttributes.SecurityDescriptor != nil {
if err := SetSecurityDescriptor(path, windowsAttributes.SecurityDescriptor); err != nil {
if err := setSecurityDescriptor(path, windowsAttributes.SecurityDescriptor); err != nil {
errs = append(errs, fmt.Errorf("error restoring security descriptor for: %s : %v", path, err))
}
}
@ -240,7 +193,7 @@ func nodeRestoreGenericAttributes(node *restic.Node, path string, warn func(msg
}
// genericAttributesToWindowsAttrs converts the generic attributes map to a WindowsAttributes and also returns a string of unknown attributes that it could not convert.
func genericAttributesToWindowsAttrs(attrs map[restic.GenericAttributeType]json.RawMessage) (windowsAttributes WindowsAttributes, unknownAttribs []restic.GenericAttributeType, err error) {
func genericAttributesToWindowsAttrs(attrs map[restic.GenericAttributeType]json.RawMessage) (windowsAttributes restic.WindowsAttributes, unknownAttribs []restic.GenericAttributeType, err error) {
waValue := reflect.ValueOf(&windowsAttributes).Elem()
unknownAttribs, err = restic.GenericAttributesToOSAttrs(attrs, reflect.TypeOf(windowsAttributes), &waValue, "windows")
return windowsAttributes, unknownAttribs, err
@ -296,7 +249,7 @@ func fixEncryptionAttribute(path string, attrs *uint32, pathPointer *uint16) (er
if err != nil {
return fmt.Errorf("failed to encrypt file: failed to reset permissions: %s : %v", path, err)
}
err = ClearSystem(path)
err = clearSystem(path)
if err != nil {
return fmt.Errorf("failed to encrypt file: failed to clear system flag: %s : %v", path, err)
}
@ -324,7 +277,7 @@ func fixEncryptionAttribute(path string, attrs *uint32, pathPointer *uint16) (er
if err != nil {
return fmt.Errorf("failed to encrypt file: failed to reset permissions: %s : %v", path, err)
}
err = ClearSystem(path)
err = clearSystem(path)
if err != nil {
return fmt.Errorf("failed to decrypt file: failed to clear system flag: %s : %v", path, err)
}
@ -365,7 +318,7 @@ func decryptFile(pathPointer *uint16) error {
// Created time and Security Descriptors.
// It also checks if the volume supports extended attributes and stores the result in a map
// so that it does not have to be checked again for subsequent calls for paths in the same volume.
func nodeFillGenericAttributes(node *restic.Node, path string, fi os.FileInfo, stat *statT) (allowExtended bool, err error) {
func nodeFillGenericAttributes(node *restic.Node, path string, stat *ExtendedFileInfo) (allowExtended bool, err error) {
if strings.Contains(filepath.Base(path), ":") {
// Do not process for Alternate Data Streams in Windows
// Also do not allow processing of extended attributes for ADS.
@ -386,20 +339,23 @@ func nodeFillGenericAttributes(node *restic.Node, path string, fi os.FileInfo, s
}
var sd *[]byte
if node.Type == "file" || node.Type == "dir" {
if node.Type == restic.NodeTypeFile || node.Type == restic.NodeTypeDir {
// Check EA support and get security descriptor for file/dir only
allowExtended, err = checkAndStoreEASupport(path)
if err != nil {
return false, err
}
if sd, err = GetSecurityDescriptor(path); err != nil {
if sd, err = getSecurityDescriptor(path); err != nil {
return allowExtended, err
}
}
winFI := stat.Sys().(*syscall.Win32FileAttributeData)
// Add Windows attributes
node.GenericAttributes, err = WindowsAttrsToGenericAttributes(WindowsAttributes{
CreationTime: getCreationTime(fi, path),
FileAttributes: &stat.FileAttributes,
node.GenericAttributes, err = restic.WindowsAttrsToGenericAttributes(restic.WindowsAttributes{
CreationTime: &winFI.CreationTime,
FileAttributes: &winFI.FileAttributes,
SecurityDescriptor: sd,
})
return allowExtended, err
@ -422,7 +378,7 @@ func checkAndStoreEASupport(path string) (isEASupportedVolume bool, err error) {
return eaSupportedValue.(bool), nil
}
// If not found, check if EA is supported with manually prepared volume name
isEASupportedVolume, err = PathSupportsExtendedAttributes(volumeName + `\`)
isEASupportedVolume, err = pathSupportsExtendedAttributes(volumeName + `\`)
// If the prepared volume name is not valid, we will fetch the actual volume name next.
if err != nil && !errors.Is(err, windows.DNS_ERROR_INVALID_NAME) {
debug.Log("Error checking if extended attributes are supported for prepared volume name %s: %v", volumeName, err)
@ -431,8 +387,8 @@ func checkAndStoreEASupport(path string) (isEASupportedVolume bool, err error) {
return false, nil
}
}
// If an entry is not found, get the actual volume name using the GetVolumePathName function
volumeNameActual, err := GetVolumePathName(path)
// If an entry is not found, get the actual volume name
volumeNameActual, err := getVolumePathName(path)
if err != nil {
debug.Log("Error getting actual volume name %s for path %s: %v", volumeName, path, err)
// There can be multiple errors like path does not exist, bad network path, etc.
@ -447,7 +403,7 @@ func checkAndStoreEASupport(path string) (isEASupportedVolume bool, err error) {
return eaSupportedValue.(bool), nil
}
// If the actual volume name is different and is not in the map, again check if the new volume supports extended attributes with the actual volume name
isEASupportedVolume, err = PathSupportsExtendedAttributes(volumeNameActual + `\`)
isEASupportedVolume, err = pathSupportsExtendedAttributes(volumeNameActual + `\`)
// Debug log for cases where the prepared volume name is not valid
if err != nil {
debug.Log("Error checking if extended attributes are supported for actual volume name %s: %v", volumeNameActual, err)
@ -494,25 +450,3 @@ func prepareVolumeName(path string) (volumeName string, err error) {
}
return volumeName, nil
}
// windowsAttrsToGenericAttributes converts the WindowsAttributes to a generic attributes map using reflection
func WindowsAttrsToGenericAttributes(windowsAttributes WindowsAttributes) (attrs map[restic.GenericAttributeType]json.RawMessage, err error) {
// Get the value of the WindowsAttributes
windowsAttributesValue := reflect.ValueOf(windowsAttributes)
return restic.OSAttrsToGenericAttributes(reflect.TypeOf(windowsAttributes), &windowsAttributesValue, runtime.GOOS)
}
// getCreationTime gets the value for the WindowsAttribute CreationTime in a windows specific time format.
// The value is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC)
// split into two 32-bit parts: the low-order DWORD and the high-order DWORD for efficiency and interoperability.
// The low-order DWORD represents the number of 100-nanosecond intervals elapsed since January 1, 1601, modulo
// 2^32. The high-order DWORD represents the number of times the low-order DWORD has overflowed.
func getCreationTime(fi os.FileInfo, path string) (creationTimeAttribute *syscall.Filetime) {
attrib, success := fi.Sys().(*syscall.Win32FileAttributeData)
if success && attrib != nil {
return &attrib.CreationTime
} else {
debug.Log("Could not get create time for path: %s", path)
return nil
}
}

View file

@ -23,20 +23,20 @@ import (
func TestRestoreSecurityDescriptors(t *testing.T) {
t.Parallel()
tempDir := t.TempDir()
for i, sd := range TestFileSDs {
testRestoreSecurityDescriptor(t, sd, tempDir, "file", fmt.Sprintf("testfile%d", i))
for i, sd := range testFileSDs {
testRestoreSecurityDescriptor(t, sd, tempDir, restic.NodeTypeFile, fmt.Sprintf("testfile%d", i))
}
for i, sd := range TestDirSDs {
testRestoreSecurityDescriptor(t, sd, tempDir, "dir", fmt.Sprintf("testdir%d", i))
for i, sd := range testDirSDs {
testRestoreSecurityDescriptor(t, sd, tempDir, restic.NodeTypeDir, fmt.Sprintf("testdir%d", i))
}
}
func testRestoreSecurityDescriptor(t *testing.T, sd string, tempDir, fileType, fileName string) {
func testRestoreSecurityDescriptor(t *testing.T, sd string, tempDir string, fileType restic.NodeType, fileName string) {
// Decode the encoded string SD to get the security descriptor input in bytes.
sdInputBytes, err := base64.StdEncoding.DecodeString(sd)
test.OK(t, errors.Wrapf(err, "Error decoding SD for: %s", fileName))
// Wrap the security descriptor bytes in windows attributes and convert to generic attributes.
genericAttributes, err := WindowsAttrsToGenericAttributes(WindowsAttributes{CreationTime: nil, FileAttributes: nil, SecurityDescriptor: &sdInputBytes})
genericAttributes, err := restic.WindowsAttrsToGenericAttributes(restic.WindowsAttributes{CreationTime: nil, FileAttributes: nil, SecurityDescriptor: &sdInputBytes})
test.OK(t, errors.Wrapf(err, "Error constructing windows attributes for: %s", fileName))
// Construct a Node with the generic attributes.
expectedNode := getNode(fileName, fileType, genericAttributes)
@ -47,16 +47,16 @@ func testRestoreSecurityDescriptor(t *testing.T, sd string, tempDir, fileType, f
sdByteFromRestoredNode := getWindowsAttr(t, testPath, node).SecurityDescriptor
// Get the security descriptor for the test path after the restore.
sdBytesFromRestoredPath, err := GetSecurityDescriptor(testPath)
sdBytesFromRestoredPath, err := getSecurityDescriptor(testPath)
test.OK(t, errors.Wrapf(err, "Error while getting the security descriptor for: %s", testPath))
// Compare the input SD and the SD got from the restored file.
CompareSecurityDescriptors(t, testPath, sdInputBytes, *sdBytesFromRestoredPath)
compareSecurityDescriptors(t, testPath, sdInputBytes, *sdBytesFromRestoredPath)
// Compare the SD got from node constructed from the restored file info and the SD got directly from the restored file.
CompareSecurityDescriptors(t, testPath, *sdByteFromRestoredNode, *sdBytesFromRestoredPath)
compareSecurityDescriptors(t, testPath, *sdByteFromRestoredNode, *sdBytesFromRestoredPath)
}
func getNode(name string, fileType string, genericAttributes map[restic.GenericAttributeType]json.RawMessage) restic.Node {
func getNode(name string, fileType restic.NodeType, genericAttributes map[restic.GenericAttributeType]json.RawMessage) restic.Node {
return restic.Node{
Name: name,
Type: fileType,
@ -68,7 +68,7 @@ func getNode(name string, fileType string, genericAttributes map[restic.GenericA
}
}
func getWindowsAttr(t *testing.T, testPath string, node *restic.Node) WindowsAttributes {
func getWindowsAttr(t *testing.T, testPath string, node *restic.Node) restic.WindowsAttributes {
windowsAttributes, unknownAttribs, err := genericAttributesToWindowsAttrs(node.GenericAttributes)
test.OK(t, errors.Wrapf(err, "Error getting windows attr from generic attr: %s", testPath))
test.Assert(t, len(unknownAttribs) == 0, "Unknown attribs found: %s for: %s", unknownAttribs, testPath)
@ -80,10 +80,10 @@ func TestRestoreCreationTime(t *testing.T) {
path := t.TempDir()
fi, err := os.Lstat(path)
test.OK(t, errors.Wrapf(err, "Could not Lstat for path: %s", path))
creationTimeAttribute := getCreationTime(fi, path)
test.OK(t, errors.Wrapf(err, "Could not get creation time for path: %s", path))
attr := fi.Sys().(*syscall.Win32FileAttributeData)
creationTimeAttribute := attr.CreationTime
//Using the temp dir creation time as the test creation time for the test file and folder
runGenericAttributesTest(t, path, restic.TypeCreationTime, WindowsAttributes{CreationTime: creationTimeAttribute}, false)
runGenericAttributesTest(t, path, restic.TypeCreationTime, restic.WindowsAttributes{CreationTime: &creationTimeAttribute}, false)
}
func TestRestoreFileAttributes(t *testing.T) {
@ -95,7 +95,7 @@ func TestRestoreFileAttributes(t *testing.T) {
system := uint32(syscall.FILE_ATTRIBUTE_SYSTEM)
archive := uint32(syscall.FILE_ATTRIBUTE_ARCHIVE)
encrypted := uint32(windows.FILE_ATTRIBUTE_ENCRYPTED)
fileAttributes := []WindowsAttributes{
fileAttributes := []restic.WindowsAttributes{
//normal
{FileAttributes: &normal},
//hidden
@ -108,12 +108,12 @@ func TestRestoreFileAttributes(t *testing.T) {
{FileAttributes: &encrypted},
}
for i, fileAttr := range fileAttributes {
genericAttrs, err := WindowsAttrsToGenericAttributes(fileAttr)
genericAttrs, err := restic.WindowsAttrsToGenericAttributes(fileAttr)
test.OK(t, err)
expectedNodes := []restic.Node{
{
Name: fmt.Sprintf("testfile%d", i),
Type: "file",
Type: restic.NodeTypeFile,
Mode: 0655,
ModTime: parseTime("2005-05-14 21:07:03.111"),
AccessTime: parseTime("2005-05-14 21:07:04.222"),
@ -128,7 +128,7 @@ func TestRestoreFileAttributes(t *testing.T) {
system = uint32(syscall.FILE_ATTRIBUTE_DIRECTORY | windows.FILE_ATTRIBUTE_SYSTEM)
archive = uint32(syscall.FILE_ATTRIBUTE_DIRECTORY | windows.FILE_ATTRIBUTE_ARCHIVE)
encrypted = uint32(syscall.FILE_ATTRIBUTE_DIRECTORY | windows.FILE_ATTRIBUTE_ENCRYPTED)
folderAttributes := []WindowsAttributes{
folderAttributes := []restic.WindowsAttributes{
//normal
{FileAttributes: &normal},
//hidden
@ -141,12 +141,12 @@ func TestRestoreFileAttributes(t *testing.T) {
{FileAttributes: &encrypted},
}
for i, folderAttr := range folderAttributes {
genericAttrs, err := WindowsAttrsToGenericAttributes(folderAttr)
genericAttrs, err := restic.WindowsAttrsToGenericAttributes(folderAttr)
test.OK(t, err)
expectedNodes := []restic.Node{
{
Name: fmt.Sprintf("testdirectory%d", i),
Type: "dir",
Type: restic.NodeTypeDir,
Mode: 0755,
ModTime: parseTime("2005-05-14 21:07:03.111"),
AccessTime: parseTime("2005-05-14 21:07:04.222"),
@ -158,13 +158,13 @@ func TestRestoreFileAttributes(t *testing.T) {
}
}
func runGenericAttributesTest(t *testing.T, tempDir string, genericAttributeName restic.GenericAttributeType, genericAttributeExpected WindowsAttributes, warningExpected bool) {
genericAttributes, err := WindowsAttrsToGenericAttributes(genericAttributeExpected)
func runGenericAttributesTest(t *testing.T, tempDir string, genericAttributeName restic.GenericAttributeType, genericAttributeExpected restic.WindowsAttributes, warningExpected bool) {
genericAttributes, err := restic.WindowsAttrsToGenericAttributes(genericAttributeExpected)
test.OK(t, err)
expectedNodes := []restic.Node{
{
Name: "testfile",
Type: "file",
Type: restic.NodeTypeFile,
Mode: 0644,
ModTime: parseTime("2005-05-14 21:07:03.111"),
AccessTime: parseTime("2005-05-14 21:07:04.222"),
@ -173,7 +173,7 @@ func runGenericAttributesTest(t *testing.T, tempDir string, genericAttributeName
},
{
Name: "testdirectory",
Type: "dir",
Type: restic.NodeTypeDir,
Mode: 0755,
ModTime: parseTime("2005-05-14 21:07:03.111"),
AccessTime: parseTime("2005-05-14 21:07:04.222"),
@ -183,12 +183,12 @@ func runGenericAttributesTest(t *testing.T, tempDir string, genericAttributeName
}
runGenericAttributesTestForNodes(t, expectedNodes, tempDir, genericAttributeName, genericAttributeExpected, warningExpected)
}
func runGenericAttributesTestForNodes(t *testing.T, expectedNodes []restic.Node, tempDir string, genericAttr restic.GenericAttributeType, genericAttributeExpected WindowsAttributes, warningExpected bool) {
func runGenericAttributesTestForNodes(t *testing.T, expectedNodes []restic.Node, tempDir string, genericAttr restic.GenericAttributeType, genericAttributeExpected restic.WindowsAttributes, warningExpected bool) {
for _, testNode := range expectedNodes {
testPath, node := restoreAndGetNode(t, tempDir, &testNode, warningExpected)
rawMessage := node.GenericAttributes[genericAttr]
genericAttrsExpected, err := WindowsAttrsToGenericAttributes(genericAttributeExpected)
genericAttrsExpected, err := restic.WindowsAttrsToGenericAttributes(genericAttributeExpected)
test.OK(t, err)
rawMessageExpected := genericAttrsExpected[genericAttr]
test.Equals(t, rawMessageExpected, rawMessage, "Generic attribute: %s got from NodeFromFileInfo not equal for path: %s", string(genericAttr), testPath)
@ -200,12 +200,12 @@ func restoreAndGetNode(t *testing.T, tempDir string, testNode *restic.Node, warn
err := os.MkdirAll(filepath.Dir(testPath), testNode.Mode)
test.OK(t, errors.Wrapf(err, "Failed to create parent directories for: %s", testPath))
if testNode.Type == "file" {
if testNode.Type == restic.NodeTypeFile {
testFile, err := os.Create(testPath)
test.OK(t, errors.Wrapf(err, "Failed to create test file: %s", testPath))
testFile.Close()
} else if testNode.Type == "dir" {
} else if testNode.Type == restic.NodeTypeDir {
err := os.Mkdir(testPath, testNode.Mode)
test.OK(t, errors.Wrapf(err, "Failed to create test directory: %s", testPath))
@ -242,7 +242,7 @@ func TestNewGenericAttributeType(t *testing.T) {
expectedNodes := []restic.Node{
{
Name: "testfile",
Type: "file",
Type: restic.NodeTypeFile,
Mode: 0644,
ModTime: parseTime("2005-05-14 21:07:03.111"),
AccessTime: parseTime("2005-05-14 21:07:04.222"),
@ -251,7 +251,7 @@ func TestNewGenericAttributeType(t *testing.T) {
},
{
Name: "testdirectory",
Type: "dir",
Type: restic.NodeTypeDir,
Mode: 0755,
ModTime: parseTime("2005-05-14 21:07:03.111"),
AccessTime: parseTime("2005-05-14 21:07:04.222"),
@ -274,7 +274,7 @@ func TestRestoreExtendedAttributes(t *testing.T) {
expectedNodes := []restic.Node{
{
Name: "testfile",
Type: "file",
Type: restic.NodeTypeFile,
Mode: 0644,
ModTime: parseTime("2005-05-14 21:07:03.111"),
AccessTime: parseTime("2005-05-14 21:07:04.222"),
@ -285,7 +285,7 @@ func TestRestoreExtendedAttributes(t *testing.T) {
},
{
Name: "testdirectory",
Type: "dir",
Type: restic.NodeTypeDir,
Mode: 0755,
ModTime: parseTime("2005-05-14 21:07:03.111"),
AccessTime: parseTime("2005-05-14 21:07:04.222"),
@ -301,9 +301,9 @@ func TestRestoreExtendedAttributes(t *testing.T) {
var handle windows.Handle
var err error
utf16Path := windows.StringToUTF16Ptr(testPath)
if node.Type == "file" {
if node.Type == restic.NodeTypeFile {
handle, err = windows.CreateFile(utf16Path, windows.FILE_READ_EA, 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0)
} else if node.Type == "dir" {
} else if node.Type == restic.NodeTypeDir {
handle, err = windows.CreateFile(utf16Path, windows.FILE_READ_EA, 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
}
test.OK(t, errors.Wrapf(err, "Error opening file/directory for: %s", testPath))
@ -312,12 +312,12 @@ func TestRestoreExtendedAttributes(t *testing.T) {
test.OK(t, errors.Wrapf(err, "Error closing file for: %s", testPath))
}()
extAttr, err := GetFileEA(handle)
extAttr, err := fgetEA(handle)
test.OK(t, errors.Wrapf(err, "Error getting extended attributes for: %s", testPath))
test.Equals(t, len(node.ExtendedAttributes), len(extAttr))
for _, expectedExtAttr := range node.ExtendedAttributes {
var foundExtAttr *ExtendedAttribute
var foundExtAttr *extendedAttribute
for _, ea := range extAttr {
if strings.EqualFold(ea.Name, expectedExtAttr.Name) {
foundExtAttr = &ea
@ -491,13 +491,13 @@ func TestPrepareVolumeName(t *testing.T) {
test.Equals(t, tc.expectedVolume, volume)
if tc.isRealPath {
isEASupportedVolume, err := PathSupportsExtendedAttributes(volume + `\`)
isEASupportedVolume, err := pathSupportsExtendedAttributes(volume + `\`)
// If the prepared volume name is not valid, we will next fetch the actual volume name.
test.OK(t, err)
test.Equals(t, tc.expectedEASupported, isEASupportedVolume)
actualVolume, err := GetVolumePathName(tc.path)
actualVolume, err := getVolumePathName(tc.path)
test.OK(t, err)
test.Equals(t, tc.expectedVolume, actualVolume)
}

View file

@ -71,7 +71,7 @@ func nodeRestoreGenericAttributes(node *restic.Node, _ string, warn func(msg str
}
// nodeFillGenericAttributes is a no-op.
func nodeFillGenericAttributes(_ *restic.Node, _ string, _ os.FileInfo, _ *statT) (allowExtended bool, err error) {
func nodeFillGenericAttributes(_ *restic.Node, _ string, _ *ExtendedFileInfo) (allowExtended bool, err error) {
return true, nil
}

View file

@ -23,13 +23,13 @@ func setAndVerifyXattr(t *testing.T, file string, attrs []restic.ExtendedAttribu
}
node := &restic.Node{
Type: "file",
Type: restic.NodeTypeFile,
ExtendedAttributes: attrs,
}
rtest.OK(t, nodeRestoreExtendedAttributes(node, file))
nodeActual := &restic.Node{
Type: "file",
Type: restic.NodeTypeFile,
}
rtest.OK(t, nodeFillExtendedAttributes(nodeActual, file, false))

View file

@ -19,14 +19,14 @@ var (
onceBackup sync.Once
onceRestore sync.Once
// SeBackupPrivilege allows the application to bypass file and directory ACLs to back up files and directories.
SeBackupPrivilege = "SeBackupPrivilege"
// SeRestorePrivilege allows the application to bypass file and directory ACLs to restore files and directories.
SeRestorePrivilege = "SeRestorePrivilege"
// SeSecurityPrivilege allows read and write access to all SACLs.
SeSecurityPrivilege = "SeSecurityPrivilege"
// SeTakeOwnershipPrivilege allows the application to take ownership of files and directories, regardless of the permissions set on them.
SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
// seBackupPrivilege allows the application to bypass file and directory ACLs to back up files and directories.
seBackupPrivilege = "SeBackupPrivilege"
// seRestorePrivilege allows the application to bypass file and directory ACLs to restore files and directories.
seRestorePrivilege = "SeRestorePrivilege"
// seSecurityPrivilege allows read and write access to all SACLs.
seSecurityPrivilege = "SeSecurityPrivilege"
// seTakeOwnershipPrivilege allows the application to take ownership of files and directories, regardless of the permissions set on them.
seTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
lowerPrivileges atomic.Bool
)
@ -40,10 +40,10 @@ var lowBackupSecurityFlags windows.SECURITY_INFORMATION = windows.OWNER_SECURITY
// Flags for restore without admin permissions. If there are no admin permissions, only the DACL from the SD can be restored and owner and group will be set based on the current user.
var lowRestoreSecurityFlags windows.SECURITY_INFORMATION = windows.DACL_SECURITY_INFORMATION | windows.ATTRIBUTE_SECURITY_INFORMATION | windows.PROTECTED_DACL_SECURITY_INFORMATION
// GetSecurityDescriptor takes the path of the file and returns the SecurityDescriptor for the file.
// getSecurityDescriptor takes the path of the file and returns the SecurityDescriptor for the file.
// This needs admin permissions or SeBackupPrivilege for getting the full SD.
// If there are no admin permissions, only the current user's owner, group and DACL will be got.
func GetSecurityDescriptor(filePath string) (securityDescriptor *[]byte, err error) {
func getSecurityDescriptor(filePath string) (securityDescriptor *[]byte, err error) {
onceBackup.Do(enableBackupPrivilege)
var sd *windows.SECURITY_DESCRIPTOR
@ -59,7 +59,7 @@ func GetSecurityDescriptor(filePath string) (securityDescriptor *[]byte, err err
if !useLowerPrivileges && isHandlePrivilegeNotHeldError(err) {
// If ERROR_PRIVILEGE_NOT_HELD is encountered, fallback to backups/restores using lower non-admin privileges.
lowerPrivileges.Store(true)
return GetSecurityDescriptor(filePath)
return getSecurityDescriptor(filePath)
} else if errors.Is(err, windows.ERROR_NOT_SUPPORTED) {
return nil, nil
} else {
@ -74,15 +74,15 @@ func GetSecurityDescriptor(filePath string) (securityDescriptor *[]byte, err err
return &sdBytes, nil
}
// SetSecurityDescriptor sets the SecurityDescriptor for the file at the specified path.
// setSecurityDescriptor sets the SecurityDescriptor for the file at the specified path.
// This needs admin permissions or SeRestorePrivilege, SeSecurityPrivilege and SeTakeOwnershipPrivilege
// for setting the full SD.
// If there are no admin permissions/required privileges, only the DACL from the SD can be set and
// owner and group will be set based on the current user.
func SetSecurityDescriptor(filePath string, securityDescriptor *[]byte) error {
func setSecurityDescriptor(filePath string, securityDescriptor *[]byte) error {
onceRestore.Do(enableRestorePrivilege)
// Set the security descriptor on the file
sd, err := SecurityDescriptorBytesToStruct(*securityDescriptor)
sd, err := securityDescriptorBytesToStruct(*securityDescriptor)
if err != nil {
return fmt.Errorf("error converting bytes to security descriptor: %w", err)
}
@ -120,7 +120,7 @@ func SetSecurityDescriptor(filePath string, securityDescriptor *[]byte) error {
if !useLowerPrivileges && isHandlePrivilegeNotHeldError(err) {
// If ERROR_PRIVILEGE_NOT_HELD is encountered, fallback to backups/restores using lower non-admin privileges.
lowerPrivileges.Store(true)
return SetSecurityDescriptor(filePath, securityDescriptor)
return setSecurityDescriptor(filePath, securityDescriptor)
} else {
return fmt.Errorf("set named security info failed with: %w", err)
}
@ -150,7 +150,7 @@ func setNamedSecurityInfoLow(filePath string, dacl *windows.ACL) error {
// enableBackupPrivilege enables privilege for backing up security descriptors
func enableBackupPrivilege() {
err := enableProcessPrivileges([]string{SeBackupPrivilege})
err := enableProcessPrivileges([]string{seBackupPrivilege})
if err != nil {
debug.Log("error enabling backup privilege: %v", err)
}
@ -158,7 +158,7 @@ func enableBackupPrivilege() {
// enableBackupPrivilege enables privilege for restoring security descriptors
func enableRestorePrivilege() {
err := enableProcessPrivileges([]string{SeRestorePrivilege, SeSecurityPrivilege, SeTakeOwnershipPrivilege})
err := enableProcessPrivileges([]string{seRestorePrivilege, seSecurityPrivilege, seTakeOwnershipPrivilege})
if err != nil {
debug.Log("error enabling restore/security privilege: %v", err)
}
@ -174,9 +174,9 @@ func isHandlePrivilegeNotHeldError(err error) bool {
return false
}
// SecurityDescriptorBytesToStruct converts the security descriptor bytes representation
// securityDescriptorBytesToStruct converts the security descriptor bytes representation
// into a pointer to windows SECURITY_DESCRIPTOR.
func SecurityDescriptorBytesToStruct(sd []byte) (*windows.SECURITY_DESCRIPTOR, error) {
func securityDescriptorBytesToStruct(sd []byte) (*windows.SECURITY_DESCRIPTOR, error) {
if l := int(unsafe.Sizeof(windows.SECURITY_DESCRIPTOR{})); len(sd) < l {
return nil, fmt.Errorf("securityDescriptor (%d) smaller than expected (%d): %w", len(sd), l, windows.ERROR_INCORRECT_SIZE)
}
@ -245,13 +245,13 @@ var (
privNameMutex sync.Mutex
)
// PrivilegeError represents an error enabling privileges.
type PrivilegeError struct {
// privilegeError represents an error enabling privileges.
type privilegeError struct {
privileges []uint64
}
// Error returns the string message for the error.
func (e *PrivilegeError) Error() string {
func (e *privilegeError) Error() string {
s := "Could not enable privilege "
if len(e.privileges) > 1 {
s = "Could not enable privileges "

View file

@ -28,7 +28,7 @@ func TestSetGetFileSecurityDescriptors(t *testing.T) {
}
}()
testSecurityDescriptors(t, TestFileSDs, testfilePath)
testSecurityDescriptors(t, testFileSDs, testfilePath)
}
func TestSetGetFolderSecurityDescriptors(t *testing.T) {
@ -40,7 +40,7 @@ func TestSetGetFolderSecurityDescriptors(t *testing.T) {
t.Fatalf("failed to create temporary file: %s", err)
}
testSecurityDescriptors(t, TestDirSDs, testfolderPath)
testSecurityDescriptors(t, testDirSDs, testfolderPath)
}
func testSecurityDescriptors(t *testing.T, testSDs []string, testPath string) {
@ -48,13 +48,13 @@ func testSecurityDescriptors(t *testing.T, testSDs []string, testPath string) {
sdInputBytes, err := base64.StdEncoding.DecodeString(testSD)
test.OK(t, errors.Wrapf(err, "Error decoding SD: %s", testPath))
err = SetSecurityDescriptor(testPath, &sdInputBytes)
err = setSecurityDescriptor(testPath, &sdInputBytes)
test.OK(t, errors.Wrapf(err, "Error setting file security descriptor for: %s", testPath))
var sdOutputBytes *[]byte
sdOutputBytes, err = GetSecurityDescriptor(testPath)
sdOutputBytes, err = getSecurityDescriptor(testPath)
test.OK(t, errors.Wrapf(err, "Error getting file security descriptor for: %s", testPath))
CompareSecurityDescriptors(t, testPath, sdInputBytes, *sdOutputBytes)
compareSecurityDescriptors(t, testPath, sdInputBytes, *sdOutputBytes)
}
}

View file

@ -13,18 +13,18 @@ import (
)
var (
TestFileSDs = []string{"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAfAAEAAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABAUAP8BHwABAQAAAAAABRIAAAAAEBgA/wEfAAECAAAAAAAFIAAAACACAAAAECQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
testFileSDs = []string{"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAfAAEAAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABAUAP8BHwABAQAAAAAABRIAAAAAEBgA/wEfAAECAAAAAAAFIAAAACACAAAAECQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAyAAHAAAAAAAUAKkAEgABAQAAAAAABQcAAAAAABQAiQASAAEBAAAAAAAFBwAAAAAAJACpABIAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar7QMAAAAAJAC/ARMAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar6gMAAAAAFAD/AR8AAQEAAAAAAAUSAAAAAAAYAP8BHwABAgAAAAAABSAAAAAgAgAAAAAkAP8BHwABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAA",
"AQAUvBQAAAAwAAAA7AAAAEwAAAABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAoAAFAAAAAAAkAP8BHwABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABAUAP8BHwABAQAAAAAABRIAAAAAEBgA/wEfAAECAAAAAAAFIAAAACACAAAAECQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAACAHQAAwAAAAKAJAC/AQIAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtgQAAALAJAC/AQMAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDPgkAAAJAJAD/AQ8AAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtQQAAA==",
}
TestDirSDs = []string{"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAfAAEAAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABMUAP8BHwABAQAAAAAABRIAAAAAExgA/wEfAAECAAAAAAAFIAAAACACAAAAEyQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
testDirSDs = []string{"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAfAAEAAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABMUAP8BHwABAQAAAAAABRIAAAAAExgA/wEfAAECAAAAAAAFIAAAACACAAAAEyQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
"AQAUvBQAAAAwAAAAAAAAAEwAAAABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvqAwAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIA3AAIAAAAAAIUAKkAEgABAQAAAAAABQcAAAAAAxQAiQASAAEBAAAAAAAFBwAAAAAAJACpABIAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar7QMAAAAAJAC/ARMAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSar6gMAAAALFAC/ARMAAQEAAAAAAAMAAAAAABMUAP8BHwABAQAAAAAABRIAAAAAExgA/wEfAAECAAAAAAAFIAAAACACAAAAEyQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAA=",
"AQAUvBQAAAAwAAAA7AAAAEwAAAABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAQUAAAAAAAUVAAAAiJ9YrlaggurMvSarAQIAAAIAoAAFAAAAAAAkAP8BHwABBQAAAAAABRUAAAAvr7t03PyHGk2FokNHCAAAAAAkAKkAEgABBQAAAAAABRUAAACIn1iuVqCC6sy9JqvtAwAAABMUAP8BHwABAQAAAAAABRIAAAAAExgA/wEfAAECAAAAAAAFIAAAACACAAAAEyQA/wEfAAEFAAAAAAAFFQAAAIifWK5WoILqzL0mq+oDAAACAHQAAwAAAAKAJAC/AQIAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtgQAAALAJAC/AQMAAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDPgkAAAJAJAD/AQ8AAQUAAAAAAAUVAAAAL6+7dNz8hxpNhaJDtQQAAA==",
}
)
// IsAdmin checks if current user is an administrator.
func IsAdmin() (isAdmin bool, err error) {
// isAdmin checks if current user is an administrator.
func isAdmin() (isAdmin bool, err error) {
var sid *windows.SID
err = windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY, 2, windows.SECURITY_BUILTIN_DOMAIN_RID, windows.DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0, &sid)
@ -40,15 +40,15 @@ func IsAdmin() (isAdmin bool, err error) {
return member, nil
}
// CompareSecurityDescriptors runs tests for comparing 2 security descriptors in []byte format.
func CompareSecurityDescriptors(t *testing.T, testPath string, sdInputBytes, sdOutputBytes []byte) {
sdInput, err := SecurityDescriptorBytesToStruct(sdInputBytes)
// compareSecurityDescriptors runs tests for comparing 2 security descriptors in []byte format.
func compareSecurityDescriptors(t *testing.T, testPath string, sdInputBytes, sdOutputBytes []byte) {
sdInput, err := securityDescriptorBytesToStruct(sdInputBytes)
test.OK(t, errors.Wrapf(err, "Error converting SD to struct for: %s", testPath))
sdOutput, err := SecurityDescriptorBytesToStruct(sdOutputBytes)
sdOutput, err := securityDescriptorBytesToStruct(sdOutputBytes)
test.OK(t, errors.Wrapf(err, "Error converting SD to struct for: %s", testPath))
isAdmin, err := IsAdmin()
isAdmin, err := isAdmin()
test.OK(t, errors.Wrapf(err, "Error checking if user is admin: %s", testPath))
var ownerExpected *windows.SID

View file

@ -19,7 +19,7 @@ func TestNoatime(t *testing.T) {
defer func() {
_ = f.Close()
err = Remove(f.Name())
err = os.Remove(f.Name())
if err != nil {
t.Fatal(err)
}

View file

@ -19,7 +19,7 @@ func extendedStat(fi os.FileInfo) ExtendedFileInfo {
extFI := ExtendedFileInfo{
FileInfo: fi,
Size: int64(s.FileSizeLow) + int64(s.FileSizeHigh)<<32,
Size: int64(s.FileSizeLow) | (int64(s.FileSizeHigh) << 32),
}
atime := syscall.NsecToTimespec(s.LastAccessTime.Nanoseconds())
@ -28,6 +28,7 @@ func extendedStat(fi os.FileInfo) ExtendedFileInfo {
mtime := syscall.NsecToTimespec(s.LastWriteTime.Nanoseconds())
extFI.ModTime = time.Unix(mtime.Unix())
// Windows does not have the concept of a "change time" in the sense Unix uses it, so we're using the LastWriteTime here.
extFI.ChangeTime = extFI.ModTime
return extFI

View file

@ -33,9 +33,9 @@ func HasSufficientPrivilegesForVSS() error {
return errors.New("VSS snapshots are only supported on windows")
}
// GetVolumeNameForVolumeMountPoint add trailing backslash to input parameter
// getVolumeNameForVolumeMountPoint add trailing backslash to input parameter
// and calls the equivalent windows api.
func GetVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
func getVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
return mountPoint, nil
}

View file

@ -22,6 +22,7 @@ import (
type HRESULT uint
// HRESULT constant values necessary for using VSS api.
//
//nolint:golint
const (
S_OK HRESULT = 0x00000000
@ -830,9 +831,9 @@ func HasSufficientPrivilegesForVSS() error {
return err
}
// GetVolumeNameForVolumeMountPoint add trailing backslash to input parameter
// getVolumeNameForVolumeMountPoint add trailing backslash to input parameter
// and calls the equivalent windows api.
func GetVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
func getVolumeNameForVolumeMountPoint(mountPoint string) (string, error) {
if mountPoint != "" && mountPoint[len(mountPoint)-1] != filepath.Separator {
mountPoint += string(filepath.Separator)
}

View file

@ -59,7 +59,7 @@ func unwrapCtxCanceled(err error) error {
// replaceSpecialNodes replaces nodes with name "." and "/" by their contents.
// Otherwise, the node is returned.
func replaceSpecialNodes(ctx context.Context, repo restic.BlobLoader, node *restic.Node) ([]*restic.Node, error) {
if node.Type != "dir" || node.Subtree == nil {
if node.Type != restic.NodeTypeDir || node.Subtree == nil {
return []*restic.Node{node}, nil
}
@ -147,7 +147,7 @@ func (d *dir) calcNumberOfLinks() uint32 {
// of directories contained by d
count := uint32(2)
for _, node := range d.items {
if node.Type == "dir" {
if node.Type == restic.NodeTypeDir {
count++
}
}
@ -182,11 +182,11 @@ func (d *dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
name := cleanupNodeName(node.Name)
var typ fuse.DirentType
switch node.Type {
case "dir":
case restic.NodeTypeDir:
typ = fuse.DT_Dir
case "file":
case restic.NodeTypeFile:
typ = fuse.DT_File
case "symlink":
case restic.NodeTypeSymlink:
typ = fuse.DT_Link
}
@ -215,13 +215,13 @@ func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
}
inode := inodeFromNode(d.inode, node)
switch node.Type {
case "dir":
case restic.NodeTypeDir:
return newDir(d.root, inode, d.inode, node)
case "file":
case restic.NodeTypeFile:
return newFile(d.root, inode, node)
case "symlink":
case restic.NodeTypeSymlink:
return newLink(d.root, inode, node)
case "dev", "chardev", "fifo", "socket":
case restic.NodeTypeDev, restic.NodeTypeCharDev, restic.NodeTypeFifo, restic.NodeTypeSocket:
return newOther(d.root, inode, node)
default:
debug.Log(" node %v has unknown type %v", name, node.Type)

View file

@ -249,7 +249,7 @@ func TestBlocks(t *testing.T) {
}
func TestInodeFromNode(t *testing.T) {
node := &restic.Node{Name: "foo.txt", Type: "chardev", Links: 2}
node := &restic.Node{Name: "foo.txt", Type: restic.NodeTypeCharDev, Links: 2}
ino1 := inodeFromNode(1, node)
ino2 := inodeFromNode(2, node)
rtest.Assert(t, ino1 == ino2, "inodes %d, %d of hard links differ", ino1, ino2)
@ -261,9 +261,9 @@ func TestInodeFromNode(t *testing.T) {
// Regression test: in a path a/b/b, the grandchild should not get the
// same inode as the grandparent.
a := &restic.Node{Name: "a", Type: "dir", Links: 2}
ab := &restic.Node{Name: "b", Type: "dir", Links: 2}
abb := &restic.Node{Name: "b", Type: "dir", Links: 2}
a := &restic.Node{Name: "a", Type: restic.NodeTypeDir, Links: 2}
ab := &restic.Node{Name: "b", Type: restic.NodeTypeDir, Links: 2}
abb := &restic.Node{Name: "b", Type: restic.NodeTypeDir, Links: 2}
inoA := inodeFromNode(1, a)
inoAb := inodeFromNode(inoA, ab)
inoAbb := inodeFromNode(inoAb, abb)
@ -272,7 +272,7 @@ func TestInodeFromNode(t *testing.T) {
}
func TestLink(t *testing.T) {
node := &restic.Node{Name: "foo.txt", Type: "symlink", Links: 1, LinkTarget: "dst", ExtendedAttributes: []restic.ExtendedAttribute{
node := &restic.Node{Name: "foo.txt", Type: restic.NodeTypeSymlink, Links: 1, LinkTarget: "dst", ExtendedAttributes: []restic.ExtendedAttribute{
{Name: "foo", Value: []byte("bar")},
}}
@ -305,11 +305,11 @@ func BenchmarkInode(b *testing.B) {
}{
{
name: "no_hard_links",
node: restic.Node{Name: "a somewhat long-ish filename.svg.bz2", Type: "fifo"},
node: restic.Node{Name: "a somewhat long-ish filename.svg.bz2", Type: restic.NodeTypeFifo},
},
{
name: "hard_link",
node: restic.Node{Name: "some other filename", Type: "file", Links: 2},
node: restic.Node{Name: "some other filename", Type: restic.NodeTypeFile, Links: 2},
},
} {
b.Run(sub.name, func(b *testing.B) {

View file

@ -25,7 +25,7 @@ func inodeFromName(parent uint64, name string) uint64 {
// inodeFromNode generates an inode number for a file within a snapshot.
func inodeFromNode(parent uint64, node *restic.Node) (inode uint64) {
if node.Links > 1 && node.Type != "dir" {
if node.Links > 1 && node.Type != restic.NodeTypeDir {
// If node has hard links, give them all the same inode,
// irrespective of the parent.
var buf [16]byte

View file

@ -46,7 +46,7 @@ func FindUsedBlobs(ctx context.Context, repo Loader, treeIDs IDs, blobs FindBlob
lock.Lock()
for _, node := range tree.Nodes {
switch node.Type {
case "file":
case NodeTypeFile:
for _, blob := range node.Content {
blobs.Insert(BlobHandle{ID: blob, Type: DataBlob})
}

View file

@ -67,10 +67,24 @@ func storeGenericAttributeType(attributeTypes ...GenericAttributeType) {
}
}
type NodeType string
var (
NodeTypeFile = NodeType("file")
NodeTypeDir = NodeType("dir")
NodeTypeSymlink = NodeType("symlink")
NodeTypeDev = NodeType("dev")
NodeTypeCharDev = NodeType("chardev")
NodeTypeFifo = NodeType("fifo")
NodeTypeSocket = NodeType("socket")
NodeTypeIrregular = NodeType("irregular")
NodeTypeInvalid = NodeType("")
)
// Node is a file, directory or other item in a backup.
type Node struct {
Name string `json:"name"`
Type string `json:"type"`
Type NodeType `json:"type"`
Mode os.FileMode `json:"mode,omitempty"`
ModTime time.Time `json:"mtime,omitempty"`
AccessTime time.Time `json:"atime,omitempty"`
@ -110,19 +124,19 @@ func (n Nodes) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (node Node) String() string {
var mode os.FileMode
switch node.Type {
case "file":
case NodeTypeFile:
mode = 0
case "dir":
case NodeTypeDir:
mode = os.ModeDir
case "symlink":
case NodeTypeSymlink:
mode = os.ModeSymlink
case "dev":
case NodeTypeDev:
mode = os.ModeDevice
case "chardev":
case NodeTypeCharDev:
mode = os.ModeDevice | os.ModeCharDevice
case "fifo":
case NodeTypeFifo:
mode = os.ModeNamedPipe
case "socket":
case NodeTypeSocket:
mode = os.ModeSocket
}

View file

@ -0,0 +1,26 @@
package restic
import (
"encoding/json"
"reflect"
"runtime"
"syscall"
)
// WindowsAttributes are the genericAttributes for Windows OS
type WindowsAttributes struct {
// CreationTime is used for storing creation time for windows files.
CreationTime *syscall.Filetime `generic:"creation_time"`
// FileAttributes is used for storing file attributes for windows files.
FileAttributes *uint32 `generic:"file_attributes"`
// SecurityDescriptor is used for storing security descriptors which includes
// owner, group, discretionary access control list (DACL), system access control list (SACL)
SecurityDescriptor *[]byte `generic:"security_descriptor"`
}
// windowsAttrsToGenericAttributes converts the WindowsAttributes to a generic attributes map using reflection
func WindowsAttrsToGenericAttributes(windowsAttributes WindowsAttributes) (attrs map[GenericAttributeType]json.RawMessage, err error) {
// Get the value of the WindowsAttributes
windowsAttributesValue := reflect.ValueOf(windowsAttributes)
return OSAttrsToGenericAttributes(reflect.TypeOf(windowsAttributes), &windowsAttributesValue, runtime.GOOS)
}

View file

@ -81,7 +81,7 @@ func (fs *fakeFileSystem) saveTree(ctx context.Context, seed int64, depth int) I
node := &Node{
Name: fmt.Sprintf("dir-%v", treeSeed),
Type: "dir",
Type: NodeTypeDir,
Mode: 0755,
Subtree: &id,
}
@ -95,7 +95,7 @@ func (fs *fakeFileSystem) saveTree(ctx context.Context, seed int64, depth int) I
node := &Node{
Name: fmt.Sprintf("file-%v", fileSeed),
Type: "file",
Type: NodeTypeFile,
Mode: 0644,
Size: uint64(fileSize),
}

View file

@ -96,7 +96,7 @@ func (t *Tree) Sort() {
// Subtrees returns a slice of all subtree IDs of the tree.
func (t *Tree) Subtrees() (trees IDs) {
for _, node := range t.Nodes {
if node.Type == "dir" && node.Subtree != nil {
if node.Type == NodeTypeDir && node.Subtree != nil {
trees = append(trees, *node.Subtree)
}
}
@ -208,7 +208,7 @@ func FindTreeDirectory(ctx context.Context, repo BlobLoader, id *ID, dir string)
if node == nil {
return nil, fmt.Errorf("path %s: not found", subfolder)
}
if node.Type != "dir" || node.Subtree == nil {
if node.Type != NodeTypeDir || node.Subtree == nil {
return nil, fmt.Errorf("path %s: not a directory", subfolder)
}
id = node.Subtree

View file

@ -202,18 +202,18 @@ func (res *Restorer) traverseTreeInner(ctx context.Context, target, location str
}
// sockets cannot be restored
if node.Type == "socket" {
if node.Type == restic.NodeTypeSocket {
continue
}
selectedForRestore, childMayBeSelected := res.SelectFilter(nodeLocation, node.Type == "dir")
selectedForRestore, childMayBeSelected := res.SelectFilter(nodeLocation, node.Type == restic.NodeTypeDir)
debug.Log("SelectFilter returned %v %v for %q", selectedForRestore, childMayBeSelected, nodeLocation)
if selectedForRestore {
hasRestored = true
}
if node.Type == "dir" {
if node.Type == restic.NodeTypeDir {
if node.Subtree == nil {
return nil, hasRestored, errors.Errorf("Dir without subtree in tree %v", treeID.Str())
}
@ -377,7 +377,7 @@ func (res *Restorer) RestoreTo(ctx context.Context, dst string) (uint64, error)
return err
}
if node.Type != "file" {
if node.Type != restic.NodeTypeFile {
res.opts.Progress.AddFile(0)
return nil
}
@ -433,7 +433,7 @@ func (res *Restorer) RestoreTo(ctx context.Context, dst string) (uint64, error)
err = res.traverseTree(ctx, dst, *res.sn.Tree, treeVisitor{
visitNode: func(node *restic.Node, target, location string) error {
debug.Log("second pass, visitNode: restore node %q", location)
if node.Type != "file" {
if node.Type != restic.NodeTypeFile {
_, err := res.withOverwriteCheck(ctx, node, target, location, false, nil, func(_ bool, _ *fileState) error {
return res.restoreNodeTo(node, target, location)
})
@ -547,7 +547,7 @@ func (res *Restorer) withOverwriteCheck(ctx context.Context, node *restic.Node,
var matches *fileState
updateMetadataOnly := false
if node.Type == "file" && !isHardlink {
if node.Type == restic.NodeTypeFile && !isHardlink {
// if a file fails to verify, then matches is nil which results in restoring from scratch
matches, buf, _ = res.verifyFile(ctx, target, node, false, res.opts.Overwrite == OverwriteIfChanged, buf)
// skip files that are already correct completely
@ -616,7 +616,7 @@ func (res *Restorer) VerifyFiles(ctx context.Context, dst string, countRestoredF
err := res.traverseTree(ctx, dst, *res.sn.Tree, treeVisitor{
visitNode: func(node *restic.Node, target, location string) error {
if node.Type != "file" {
if node.Type != restic.NodeTypeFile {
return nil
}
if metadataOnly, ok := res.hasRestoredFile(location); !ok || metadataOnly {

View file

@ -108,7 +108,7 @@ func saveDir(t testing.TB, repo restic.BlobSaver, nodes map[string]Node, inode u
mode = 0644
}
err := tree.Insert(&restic.Node{
Type: "file",
Type: restic.NodeTypeFile,
Mode: mode,
ModTime: node.ModTime,
Name: name,
@ -123,7 +123,7 @@ func saveDir(t testing.TB, repo restic.BlobSaver, nodes map[string]Node, inode u
rtest.OK(t, err)
case Symlink:
err := tree.Insert(&restic.Node{
Type: "symlink",
Type: restic.NodeTypeSymlink,
Mode: os.ModeSymlink | 0o777,
ModTime: node.ModTime,
Name: name,
@ -143,7 +143,7 @@ func saveDir(t testing.TB, repo restic.BlobSaver, nodes map[string]Node, inode u
}
err := tree.Insert(&restic.Node{
Type: "dir",
Type: restic.NodeTypeDir,
Mode: mode,
ModTime: node.ModTime,
Name: name,
@ -1223,7 +1223,7 @@ func TestRestorerOverwriteSpecial(t *testing.T) {
}
}
for filename, target := range links {
link, err := fs.Readlink(filepath.Join(tempdir, filepath.FromSlash(filename)))
link, err := os.Readlink(filepath.Join(tempdir, filepath.FromSlash(filename)))
rtest.OK(t, err)
rtest.Equals(t, link, target, "wrong symlink target")
}

View file

@ -16,7 +16,6 @@ import (
"unsafe"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/test"
@ -264,7 +263,7 @@ func setup(t *testing.T, nodesMap map[string]Node) *Restorer {
//If the node is a directory add FILE_ATTRIBUTE_DIRECTORY to attributes
fileattr |= windows.FILE_ATTRIBUTE_DIRECTORY
}
attrs, err := fs.WindowsAttrsToGenericAttributes(fs.WindowsAttributes{FileAttributes: &fileattr})
attrs, err := restic.WindowsAttrsToGenericAttributes(restic.WindowsAttributes{FileAttributes: &fileattr})
test.OK(t, err)
return attrs
}

View file

@ -124,7 +124,7 @@ func (p *Progress) CompleteItem(item string, previous, current *restic.Node, s a
}
switch current.Type {
case "dir":
case restic.NodeTypeDir:
p.mu.Lock()
p.addProcessed(Counter{Dirs: 1})
p.mu.Unlock()
@ -138,7 +138,7 @@ func (p *Progress) CompleteItem(item string, previous, current *restic.Node, s a
p.printer.CompleteItem("dir modified", item, s, d)
}
case "file":
case restic.NodeTypeFile:
p.mu.Lock()
p.addProcessed(Counter{Files: 1})
delete(p.currentFiles, item)

View file

@ -55,10 +55,10 @@ func TestProgress(t *testing.T) {
prog.CompleteBlob(1024)
// "dir unchanged"
node := restic.Node{Type: "dir"}
node := restic.Node{Type: restic.NodeTypeDir}
prog.CompleteItem("foo", &node, &node, archiver.ItemStats{}, 0)
// "file new"
node.Type = "file"
node.Type = restic.NodeTypeFile
prog.CompleteItem("foo", nil, &node, archiver.ItemStats{}, 0)
time.Sleep(10 * time.Millisecond)

View file

@ -65,7 +65,7 @@ func NewSnapshotSizeRewriter(rewriteNode NodeRewriteFunc) (*TreeRewriter, QueryR
t := NewTreeRewriter(RewriteOpts{
RewriteNode: func(node *restic.Node, path string) *restic.Node {
node = rewriteNode(node, path)
if node != nil && node.Type == "file" {
if node != nil && node.Type == restic.NodeTypeFile {
count++
size += node.Size
}
@ -126,7 +126,7 @@ func (t *TreeRewriter) RewriteTree(ctx context.Context, repo BlobLoadSaver, node
continue
}
if node.Type != "dir" {
if node.Type != restic.NodeTypeDir {
err = tb.AddNode(node)
if err != nil {
return restic.ID{}, err

View file

@ -110,7 +110,7 @@ func checkIncreaseNodeSize(increase uint64) checkRewriteFunc {
return func(t testing.TB) (rewriter *TreeRewriter, final func(testing.TB)) {
rewriter = NewTreeRewriter(RewriteOpts{
RewriteNode: func(node *restic.Node, path string) *restic.Node {
if node.Type == "file" {
if node.Type == restic.NodeTypeFile {
node.Size += increase
}
return node
@ -329,7 +329,7 @@ func TestSnapshotSizeQuery(t *testing.T) {
if path == "/bar" {
return nil
}
if node.Type == "file" {
if node.Type == restic.NodeTypeFile {
node.Size += 21
}
return node

View file

@ -63,11 +63,11 @@ func walk(ctx context.Context, repo restic.BlobLoader, prefix string, parentTree
p := path.Join(prefix, node.Name)
if node.Type == "" {
if node.Type == restic.NodeTypeInvalid {
return errors.Errorf("node type is empty for node %q", node.Name)
}
if node.Type != "dir" {
if node.Type != restic.NodeTypeDir {
err := visitor.ProcessNode(parentTreeID, p, node, nil)
if err != nil {
if err == ErrSkipNode {

View file

@ -38,7 +38,7 @@ func buildTreeMap(tree TestTree, m TreeMap) restic.ID {
case TestFile:
err := tb.AddNode(&restic.Node{
Name: name,
Type: "file",
Type: restic.NodeTypeFile,
Size: elem.Size,
})
if err != nil {
@ -49,7 +49,7 @@ func buildTreeMap(tree TestTree, m TreeMap) restic.ID {
err := tb.AddNode(&restic.Node{
Name: name,
Subtree: &id,
Type: "dir",
Type: restic.NodeTypeDir,
})
if err != nil {
panic(err)