mirror of
https://github.com/restic/restic.git
synced 2025-12-11 18:47:50 +00:00
data: split node and snapshot code from restic package
This commit is contained in:
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/data"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@@ -115,7 +115,7 @@ func clearAttribute(path string, attribute uint32) 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) {
|
||||
func openHandleForEA(nodeType data.NodeType, path string, writeAccess bool) (handle windows.Handle, err error) {
|
||||
path = fixpath(path)
|
||||
fileAccess := windows.FILE_READ_EA
|
||||
if writeAccess {
|
||||
@@ -123,10 +123,10 @@ func openHandleForEA(nodeType restic.NodeType, path string, writeAccess bool) (h
|
||||
}
|
||||
|
||||
switch nodeType {
|
||||
case restic.NodeTypeFile:
|
||||
case data.NodeTypeFile:
|
||||
utf16Path := windows.StringToUTF16Ptr(path)
|
||||
handle, err = windows.CreateFile(utf16Path, uint32(fileAccess), 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0)
|
||||
case restic.NodeTypeDir:
|
||||
case data.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:
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/data"
|
||||
)
|
||||
|
||||
// Local is the local file system. Most methods are just passed on to the stdlib.
|
||||
@@ -152,7 +152,7 @@ func (f *localFile) Stat() (*ExtendedFileInfo, error) {
|
||||
return f.fi, err
|
||||
}
|
||||
|
||||
func (f *localFile) ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*restic.Node, error) {
|
||||
func (f *localFile) ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*data.Node, error) {
|
||||
if err := f.cacheFI(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/data"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ type fsLocalMetadataTestcase struct {
|
||||
name string
|
||||
follow bool
|
||||
setup func(t *testing.T, path string)
|
||||
nodeType restic.NodeType
|
||||
nodeType data.NodeType
|
||||
}
|
||||
|
||||
func TestFSLocalMetadata(t *testing.T) {
|
||||
@@ -25,21 +25,21 @@ func TestFSLocalMetadata(t *testing.T) {
|
||||
setup: func(t *testing.T, path string) {
|
||||
rtest.OK(t, os.WriteFile(path, []byte("example"), 0o600))
|
||||
},
|
||||
nodeType: restic.NodeTypeFile,
|
||||
nodeType: data.NodeTypeFile,
|
||||
},
|
||||
{
|
||||
name: "directory",
|
||||
setup: func(t *testing.T, path string) {
|
||||
rtest.OK(t, os.Mkdir(path, 0o600))
|
||||
},
|
||||
nodeType: restic.NodeTypeDir,
|
||||
nodeType: data.NodeTypeDir,
|
||||
},
|
||||
{
|
||||
name: "symlink",
|
||||
setup: func(t *testing.T, path string) {
|
||||
rtest.OK(t, os.Symlink(path+"old", path))
|
||||
},
|
||||
nodeType: restic.NodeTypeSymlink,
|
||||
nodeType: data.NodeTypeSymlink,
|
||||
},
|
||||
{
|
||||
name: "symlink file",
|
||||
@@ -48,7 +48,7 @@ func TestFSLocalMetadata(t *testing.T) {
|
||||
rtest.OK(t, os.WriteFile(path+"file", []byte("example"), 0o600))
|
||||
rtest.OK(t, os.Symlink(path+"file", path))
|
||||
},
|
||||
nodeType: restic.NodeTypeFile,
|
||||
nodeType: data.NodeTypeFile,
|
||||
},
|
||||
} {
|
||||
runFSLocalTestcase(t, test)
|
||||
@@ -74,7 +74,7 @@ func runFSLocalTestcase(t *testing.T, test fsLocalMetadataTestcase) {
|
||||
|
||||
}
|
||||
|
||||
func checkMetadata(t *testing.T, f File, path string, follow bool, nodeType restic.NodeType) {
|
||||
func checkMetadata(t *testing.T, f File, path string, follow bool, nodeType data.NodeType) {
|
||||
fi, err := f.Stat()
|
||||
rtest.OK(t, err)
|
||||
var fi2 os.FileInfo
|
||||
@@ -114,7 +114,7 @@ func testFSLocalRead(t *testing.T, makeReadable bool) {
|
||||
rtest.OK(t, os.WriteFile(path, []byte(testdata), 0o600))
|
||||
|
||||
f := openReadable(t, path, makeReadable)
|
||||
checkMetadata(t, f, path, false, restic.NodeTypeFile)
|
||||
checkMetadata(t, f, path, false, data.NodeTypeFile)
|
||||
|
||||
data, err := io.ReadAll(f)
|
||||
rtest.OK(t, err)
|
||||
@@ -147,7 +147,7 @@ func testFSLocalReaddir(t *testing.T, makeReadable bool) {
|
||||
rtest.OK(t, os.WriteFile(filepath.Join(path, entries[0]), []byte("example"), 0o600))
|
||||
|
||||
f := openReadable(t, path, makeReadable)
|
||||
checkMetadata(t, f, path, false, restic.NodeTypeDir)
|
||||
checkMetadata(t, f, path, false, data.NodeTypeDir)
|
||||
|
||||
names, err := f.Readdirnames(-1)
|
||||
rtest.OK(t, err)
|
||||
@@ -173,7 +173,7 @@ func TestFSLocalReadableRace(t *testing.T) {
|
||||
err = f.MakeReadable()
|
||||
if err == nil {
|
||||
// a file handle based implementation should still work
|
||||
checkMetadata(t, f, pathNew, false, restic.NodeTypeFile)
|
||||
checkMetadata(t, f, pathNew, false, data.NodeTypeFile)
|
||||
|
||||
data, err := io.ReadAll(f)
|
||||
rtest.OK(t, err)
|
||||
@@ -207,7 +207,7 @@ func TestFSLocalTypeChange(t *testing.T) {
|
||||
rtest.OK(t, err)
|
||||
if !fi.Mode.IsDir() {
|
||||
// a file handle based implementation should still reference the file
|
||||
checkMetadata(t, f, pathNew, false, restic.NodeTypeFile)
|
||||
checkMetadata(t, f, pathNew, false, data.NodeTypeFile)
|
||||
|
||||
data, err := io.ReadAll(f)
|
||||
rtest.OK(t, err)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/data"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
@@ -24,14 +24,14 @@ func TestFSLocalMetadataUnix(t *testing.T) {
|
||||
addr := &syscall.SockaddrUnix{Name: path}
|
||||
rtest.OK(t, syscall.Bind(fd, addr))
|
||||
},
|
||||
nodeType: restic.NodeTypeSocket,
|
||||
nodeType: data.NodeTypeSocket,
|
||||
},
|
||||
{
|
||||
name: "fifo",
|
||||
setup: func(t *testing.T, path string) {
|
||||
rtest.OK(t, mkfifo(path, 0o600))
|
||||
},
|
||||
nodeType: restic.NodeTypeFifo,
|
||||
nodeType: data.NodeTypeFifo,
|
||||
},
|
||||
// device files can only be created as root
|
||||
} {
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/data"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
)
|
||||
|
||||
// Reader is a file system which provides a directory with a single file. When
|
||||
@@ -267,7 +267,7 @@ func (f fakeFile) Stat() (*ExtendedFileInfo, error) {
|
||||
return f.fi, nil
|
||||
}
|
||||
|
||||
func (f fakeFile) ToNode(_ bool, _ func(format string, args ...any)) (*restic.Node, error) {
|
||||
func (f fakeFile) ToNode(_ bool, _ func(format string, args ...any)) (*data.Node, error) {
|
||||
node := buildBasicNode(f.name, f.fi)
|
||||
|
||||
// fill minimal info with current values for uid, gid
|
||||
|
||||
@@ -3,7 +3,7 @@ package fs
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/data"
|
||||
)
|
||||
|
||||
// FS bundles all methods needed for a file system.
|
||||
@@ -45,8 +45,8 @@ type File interface {
|
||||
|
||||
Readdirnames(n int) ([]string, error)
|
||||
Stat() (*ExtendedFileInfo, error)
|
||||
// ToNode returns a restic.Node for the File. The internally used os.FileInfo
|
||||
// ToNode returns a data.Node for the File. The internally used os.FileInfo
|
||||
// must be consistent with that returned by Stat(). In particular, the metadata
|
||||
// returned by consecutive calls to Stat() and ToNode() must match.
|
||||
ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*restic.Node, error)
|
||||
ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*data.Node, error)
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/restic/restic/internal/data"
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
)
|
||||
|
||||
// nodeFromFileInfo returns a new node from the given path and FileInfo. It
|
||||
// returns the first error that is encountered, together with a node.
|
||||
func nodeFromFileInfo(path string, fi *ExtendedFileInfo, ignoreXattrListError bool, warnf func(format string, args ...any)) (*restic.Node, error) {
|
||||
func nodeFromFileInfo(path string, fi *ExtendedFileInfo, ignoreXattrListError bool, warnf func(format string, args ...any)) (*data.Node, error) {
|
||||
node := buildBasicNode(path, fi)
|
||||
|
||||
if err := nodeFillExtendedStat(node, path, fi); err != nil {
|
||||
@@ -27,9 +27,9 @@ func nodeFromFileInfo(path string, fi *ExtendedFileInfo, ignoreXattrListError bo
|
||||
return node, err
|
||||
}
|
||||
|
||||
func buildBasicNode(path string, fi *ExtendedFileInfo) *restic.Node {
|
||||
func buildBasicNode(path string, fi *ExtendedFileInfo) *data.Node {
|
||||
mask := os.ModePerm | os.ModeType | os.ModeSetuid | os.ModeSetgid | os.ModeSticky
|
||||
node := &restic.Node{
|
||||
node := &data.Node{
|
||||
Path: path,
|
||||
Name: fi.Name,
|
||||
Mode: fi.Mode & mask,
|
||||
@@ -37,36 +37,36 @@ func buildBasicNode(path string, fi *ExtendedFileInfo) *restic.Node {
|
||||
}
|
||||
|
||||
node.Type = nodeTypeFromFileInfo(fi.Mode)
|
||||
if node.Type == restic.NodeTypeFile {
|
||||
if node.Type == data.NodeTypeFile {
|
||||
node.Size = uint64(fi.Size)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
func nodeTypeFromFileInfo(mode os.FileMode) restic.NodeType {
|
||||
func nodeTypeFromFileInfo(mode os.FileMode) data.NodeType {
|
||||
switch mode & os.ModeType {
|
||||
case 0:
|
||||
return restic.NodeTypeFile
|
||||
return data.NodeTypeFile
|
||||
case os.ModeDir:
|
||||
return restic.NodeTypeDir
|
||||
return data.NodeTypeDir
|
||||
case os.ModeSymlink:
|
||||
return restic.NodeTypeSymlink
|
||||
return data.NodeTypeSymlink
|
||||
case os.ModeDevice | os.ModeCharDevice:
|
||||
return restic.NodeTypeCharDev
|
||||
return data.NodeTypeCharDev
|
||||
case os.ModeDevice:
|
||||
return restic.NodeTypeDev
|
||||
return data.NodeTypeDev
|
||||
case os.ModeNamedPipe:
|
||||
return restic.NodeTypeFifo
|
||||
return data.NodeTypeFifo
|
||||
case os.ModeSocket:
|
||||
return restic.NodeTypeSocket
|
||||
return data.NodeTypeSocket
|
||||
case os.ModeIrregular:
|
||||
return restic.NodeTypeIrregular
|
||||
return data.NodeTypeIrregular
|
||||
}
|
||||
|
||||
return restic.NodeTypeInvalid
|
||||
return data.NodeTypeInvalid
|
||||
}
|
||||
|
||||
func nodeFillExtendedStat(node *restic.Node, path string, stat *ExtendedFileInfo) error {
|
||||
func nodeFillExtendedStat(node *data.Node, path string, stat *ExtendedFileInfo) error {
|
||||
node.Inode = stat.Inode
|
||||
node.DeviceID = stat.DeviceID
|
||||
node.ChangeTime = stat.ChangeTime
|
||||
@@ -78,25 +78,25 @@ func nodeFillExtendedStat(node *restic.Node, path string, stat *ExtendedFileInfo
|
||||
node.Group = lookupGroup(stat.GID)
|
||||
|
||||
switch node.Type {
|
||||
case restic.NodeTypeFile:
|
||||
case data.NodeTypeFile:
|
||||
node.Size = uint64(stat.Size)
|
||||
node.Links = stat.Links
|
||||
case restic.NodeTypeDir:
|
||||
case restic.NodeTypeSymlink:
|
||||
case data.NodeTypeDir:
|
||||
case data.NodeTypeSymlink:
|
||||
var err error
|
||||
node.LinkTarget, err = os.Readlink(fixpath(path))
|
||||
node.Links = stat.Links
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
case restic.NodeTypeDev:
|
||||
case data.NodeTypeDev:
|
||||
node.Device = stat.Device
|
||||
node.Links = stat.Links
|
||||
case restic.NodeTypeCharDev:
|
||||
case data.NodeTypeCharDev:
|
||||
node.Device = stat.Device
|
||||
node.Links = stat.Links
|
||||
case restic.NodeTypeFifo:
|
||||
case restic.NodeTypeSocket:
|
||||
case data.NodeTypeFifo:
|
||||
case data.NodeTypeSocket:
|
||||
default:
|
||||
return errors.Errorf("unsupported file type %q", node.Type)
|
||||
}
|
||||
@@ -158,23 +158,23 @@ func lookupGroup(gid uint32) string {
|
||||
}
|
||||
|
||||
// NodeCreateAt creates the node at the given path but does NOT restore node meta data.
|
||||
func NodeCreateAt(node *restic.Node, path string) (err error) {
|
||||
func NodeCreateAt(node *data.Node, path string) (err error) {
|
||||
debug.Log("create node %v at %v", node.Name, path)
|
||||
|
||||
switch node.Type {
|
||||
case restic.NodeTypeDir:
|
||||
case data.NodeTypeDir:
|
||||
err = nodeCreateDirAt(node, path)
|
||||
case restic.NodeTypeFile:
|
||||
case data.NodeTypeFile:
|
||||
err = nodeCreateFileAt(path)
|
||||
case restic.NodeTypeSymlink:
|
||||
case data.NodeTypeSymlink:
|
||||
err = nodeCreateSymlinkAt(node, path)
|
||||
case restic.NodeTypeDev:
|
||||
case data.NodeTypeDev:
|
||||
err = nodeCreateDevAt(node, path)
|
||||
case restic.NodeTypeCharDev:
|
||||
case data.NodeTypeCharDev:
|
||||
err = nodeCreateCharDevAt(node, path)
|
||||
case restic.NodeTypeFifo:
|
||||
case data.NodeTypeFifo:
|
||||
err = nodeCreateFifoAt(path)
|
||||
case restic.NodeTypeSocket:
|
||||
case data.NodeTypeSocket:
|
||||
err = nil
|
||||
default:
|
||||
err = errors.Errorf("filetype %q not implemented", node.Type)
|
||||
@@ -183,7 +183,7 @@ func NodeCreateAt(node *restic.Node, path string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
func nodeCreateDirAt(node *restic.Node, path string) error {
|
||||
func nodeCreateDirAt(node *data.Node, path string) error {
|
||||
err := os.Mkdir(fixpath(path), node.Mode)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return errors.WithStack(err)
|
||||
@@ -205,7 +205,7 @@ func nodeCreateFileAt(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func nodeCreateSymlinkAt(node *restic.Node, path string) error {
|
||||
func nodeCreateSymlinkAt(node *data.Node, path string) error {
|
||||
if err := os.Symlink(node.LinkTarget, fixpath(path)); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
@@ -213,11 +213,11 @@ func nodeCreateSymlinkAt(node *restic.Node, path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func nodeCreateDevAt(node *restic.Node, path string) error {
|
||||
func nodeCreateDevAt(node *data.Node, path string) error {
|
||||
return mknod(path, syscall.S_IFBLK|0600, node.Device)
|
||||
}
|
||||
|
||||
func nodeCreateCharDevAt(node *restic.Node, path string) error {
|
||||
func nodeCreateCharDevAt(node *data.Node, path string) error {
|
||||
return mknod(path, syscall.S_IFCHR|0600, node.Device)
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ func mkfifo(path string, mode uint32) (err error) {
|
||||
}
|
||||
|
||||
// NodeRestoreMetadata restores node metadata
|
||||
func NodeRestoreMetadata(node *restic.Node, path string, warn func(msg string), xattrSelectFilter func(xattrName string) bool) error {
|
||||
func NodeRestoreMetadata(node *data.Node, path string, warn func(msg string), xattrSelectFilter func(xattrName string) bool) error {
|
||||
err := nodeRestoreMetadata(node, path, warn, xattrSelectFilter)
|
||||
if err != nil {
|
||||
// It is common to have permission errors for folders like /home
|
||||
@@ -246,7 +246,7 @@ func NodeRestoreMetadata(node *restic.Node, path string, warn func(msg string),
|
||||
return err
|
||||
}
|
||||
|
||||
func nodeRestoreMetadata(node *restic.Node, path string, warn func(msg string), xattrSelectFilter func(xattrName string) bool) error {
|
||||
func nodeRestoreMetadata(node *data.Node, path string, warn func(msg string), xattrSelectFilter func(xattrName string) bool) error {
|
||||
var firsterr error
|
||||
|
||||
if err := lchown(path, int(node.UID), int(node.GID)); err != nil {
|
||||
@@ -277,7 +277,7 @@ 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 != restic.NodeTypeSymlink {
|
||||
if node.Type != data.NodeTypeSymlink {
|
||||
if err := chmod(path, node.Mode); err != nil {
|
||||
if firsterr == nil {
|
||||
firsterr = errors.WithStack(err)
|
||||
@@ -288,7 +288,7 @@ func nodeRestoreMetadata(node *restic.Node, path string, warn func(msg string),
|
||||
return firsterr
|
||||
}
|
||||
|
||||
func nodeRestoreTimestamps(node *restic.Node, path string) error {
|
||||
func nodeRestoreTimestamps(node *data.Node, path string) error {
|
||||
atime := node.AccessTime.UnixNano()
|
||||
mtime := node.ModTime.UnixNano()
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/data"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// utimesNano is like syscall.UtimesNano, except that it does not follow symlinks.
|
||||
func utimesNano(path string, atime, mtime int64, _ restic.NodeType) error {
|
||||
func utimesNano(path string, atime, mtime int64, _ data.NodeType) error {
|
||||
times := []unix.Timespec{
|
||||
unix.NsecToTimespec(atime),
|
||||
unix.NsecToTimespec(mtime),
|
||||
|
||||
@@ -5,14 +5,14 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/restic/restic/internal/data"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
func TestRestoreSymlinkTimestampsError(t *testing.T) {
|
||||
d := t.TempDir()
|
||||
node := restic.Node{Type: restic.NodeTypeSymlink}
|
||||
node := data.Node{Type: data.NodeTypeSymlink}
|
||||
err := nodeRestoreTimestamps(&node, d+"/nosuchfile")
|
||||
rtest.Assert(t, errors.Is(err, fs.ErrNotExist), "want ErrNotExist, got %q", err)
|
||||
rtest.Assert(t, strings.Contains(err.Error(), d), "filename not in %q", err)
|
||||
|
||||
@@ -3,16 +3,14 @@
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"github.com/restic/restic/internal/restic"
|
||||
)
|
||||
import "github.com/restic/restic/internal/data"
|
||||
|
||||
// nodeRestoreExtendedAttributes is a no-op
|
||||
func nodeRestoreExtendedAttributes(_ *restic.Node, _ string, _ func(xattrName string) bool) error {
|
||||
func nodeRestoreExtendedAttributes(_ *data.Node, _ string, _ func(xattrName string) bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// nodeFillExtendedAttributes is a no-op
|
||||
func nodeFillExtendedAttributes(_ *restic.Node, _ string, _ bool, _ func(format string, args ...any)) error {
|
||||
func nodeFillExtendedAttributes(_ *data.Node, _ string, _ bool, _ func(format string, args ...any)) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/restic/restic/internal/data"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
@@ -47,10 +48,10 @@ func parseTime(s string) time.Time {
|
||||
return t.Local()
|
||||
}
|
||||
|
||||
var nodeTests = []restic.Node{
|
||||
var nodeTests = []data.Node{
|
||||
{
|
||||
Name: "testFile",
|
||||
Type: restic.NodeTypeFile,
|
||||
Type: data.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@@ -61,7 +62,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
{
|
||||
Name: "testSuidFile",
|
||||
Type: restic.NodeTypeFile,
|
||||
Type: data.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@@ -72,7 +73,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
{
|
||||
Name: "testSuidFile2",
|
||||
Type: restic.NodeTypeFile,
|
||||
Type: data.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@@ -83,7 +84,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
{
|
||||
Name: "testSticky",
|
||||
Type: restic.NodeTypeFile,
|
||||
Type: data.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@@ -94,7 +95,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
{
|
||||
Name: "testDir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Type: data.NodeTypeDir,
|
||||
Subtree: nil,
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@@ -105,7 +106,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
{
|
||||
Name: "testSymlink",
|
||||
Type: restic.NodeTypeSymlink,
|
||||
Type: data.NodeTypeSymlink,
|
||||
LinkTarget: "invalid",
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@@ -119,7 +120,7 @@ var nodeTests = []restic.Node{
|
||||
// metadata, so we can test if CreateAt works with pre-existing files.
|
||||
{
|
||||
Name: "testFile",
|
||||
Type: restic.NodeTypeFile,
|
||||
Type: data.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@@ -130,7 +131,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
{
|
||||
Name: "testDir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Type: data.NodeTypeDir,
|
||||
Subtree: nil,
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@@ -141,7 +142,7 @@ var nodeTests = []restic.Node{
|
||||
},
|
||||
{
|
||||
Name: "testXattrFile",
|
||||
Type: restic.NodeTypeFile,
|
||||
Type: data.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@@ -149,13 +150,13 @@ var nodeTests = []restic.Node{
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
ChangeTime: parseTime("2005-05-14 21:07:05.333"),
|
||||
ExtendedAttributes: []restic.ExtendedAttribute{
|
||||
ExtendedAttributes: []data.ExtendedAttribute{
|
||||
{Name: "user.foo", Value: []byte("bar")},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "testXattrDir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Type: data.NodeTypeDir,
|
||||
Subtree: nil,
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@@ -163,13 +164,13 @@ var nodeTests = []restic.Node{
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
ChangeTime: parseTime("2005-05-14 21:07:05.333"),
|
||||
ExtendedAttributes: []restic.ExtendedAttribute{
|
||||
ExtendedAttributes: []data.ExtendedAttribute{
|
||||
{Name: "user.foo", Value: []byte("bar")},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "testXattrFileMacOSResourceFork",
|
||||
Type: restic.NodeTypeFile,
|
||||
Type: data.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@@ -177,7 +178,7 @@ var nodeTests = []restic.Node{
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
ChangeTime: parseTime("2005-05-14 21:07:05.333"),
|
||||
ExtendedAttributes: []restic.ExtendedAttribute{
|
||||
ExtendedAttributes: []data.ExtendedAttribute{
|
||||
{Name: "com.apple.ResourceFork", Value: []byte("bar")},
|
||||
},
|
||||
},
|
||||
@@ -242,7 +243,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 != restic.NodeTypeSymlink {
|
||||
if test.Type != data.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,
|
||||
@@ -262,11 +263,11 @@ func TestNodeRestoreAt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func AssertFsTimeEqual(t *testing.T, label string, nodeType restic.NodeType, t1 time.Time, t2 time.Time) {
|
||||
func AssertFsTimeEqual(t *testing.T, label string, nodeType data.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 == restic.NodeTypeSymlink {
|
||||
if nodeType == data.NodeTypeSymlink {
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "freebsd", "openbsd", "netbsd", "solaris":
|
||||
return
|
||||
|
||||
@@ -6,7 +6,7 @@ package fs
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/data"
|
||||
)
|
||||
|
||||
func lchown(name string, uid, gid int) error {
|
||||
@@ -14,11 +14,11 @@ func lchown(name string, uid, gid int) error {
|
||||
}
|
||||
|
||||
// nodeRestoreGenericAttributes is no-op.
|
||||
func nodeRestoreGenericAttributes(node *restic.Node, _ string, warn func(msg string)) error {
|
||||
return restic.HandleAllUnknownGenericAttributesFound(node.GenericAttributes, warn)
|
||||
func nodeRestoreGenericAttributes(node *data.Node, _ string, warn func(msg string)) error {
|
||||
return data.HandleAllUnknownGenericAttributesFound(node.GenericAttributes, warn)
|
||||
}
|
||||
|
||||
// nodeFillGenericAttributes is a no-op.
|
||||
func nodeFillGenericAttributes(_ *restic.Node, _ string, _ *ExtendedFileInfo) error {
|
||||
func nodeFillGenericAttributes(_ *data.Node, _ string, _ *ExtendedFileInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ package fs
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/data"
|
||||
)
|
||||
|
||||
// utimesNano is like syscall.UtimesNano, except that it skips symlinks.
|
||||
func utimesNano(path string, atime, mtime int64, typ restic.NodeType) error {
|
||||
if typ == restic.NodeTypeSymlink {
|
||||
func utimesNano(path string, atime, mtime int64, typ data.NodeType) error {
|
||||
if typ == data.NodeTypeSymlink {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/restic/restic/internal/data"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ func stat(t testing.TB, filename string) (fi os.FileInfo, ok bool) {
|
||||
return fi, true
|
||||
}
|
||||
|
||||
func checkFile(t testing.TB, fi fs.FileInfo, node *restic.Node) {
|
||||
func checkFile(t testing.TB, fi fs.FileInfo, node *data.Node) {
|
||||
t.Helper()
|
||||
|
||||
stat := fi.Sys().(*syscall.Stat_t)
|
||||
@@ -47,7 +47,7 @@ func checkFile(t testing.TB, fi fs.FileInfo, 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 != restic.NodeTypeSymlink {
|
||||
if node.Size != uint64(stat.Size) && node.Type != data.NodeTypeSymlink {
|
||||
t.Errorf("Size does not match, want %v, got %v", stat.Size, node.Size)
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func checkFile(t testing.TB, fi fs.FileInfo, node *restic.Node) {
|
||||
}
|
||||
}
|
||||
|
||||
func checkDevice(t testing.TB, fi fs.FileInfo, node *restic.Node) {
|
||||
func checkDevice(t testing.TB, fi fs.FileInfo, node *data.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)
|
||||
@@ -124,9 +124,9 @@ func TestNodeFromFileInfo(t *testing.T) {
|
||||
rtest.OK(t, err)
|
||||
|
||||
switch node.Type {
|
||||
case restic.NodeTypeFile, restic.NodeTypeSymlink:
|
||||
case data.NodeTypeFile, data.NodeTypeSymlink:
|
||||
checkFile(t, fi, node)
|
||||
case restic.NodeTypeDev, restic.NodeTypeCharDev:
|
||||
case data.NodeTypeDev, data.NodeTypeCharDev:
|
||||
checkFile(t, fi, node)
|
||||
checkDevice(t, fi, node)
|
||||
default:
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/restic/restic/internal/data"
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@@ -43,7 +43,7 @@ func lchown(_ string, _ int, _ int) (err error) {
|
||||
}
|
||||
|
||||
// utimesNano is like syscall.UtimesNano, except that it sets FILE_FLAG_OPEN_REPARSE_POINT.
|
||||
func utimesNano(path string, atime, mtime int64, _ restic.NodeType) error {
|
||||
func utimesNano(path string, atime, mtime int64, _ data.NodeType) error {
|
||||
// tweaked version of UtimesNano from go/src/syscall/syscall_windows.go
|
||||
pathp, e := syscall.UTF16PtrFromString(fixpath(path))
|
||||
if e != nil {
|
||||
@@ -69,7 +69,7 @@ func utimesNano(path string, atime, mtime int64, _ restic.NodeType) error {
|
||||
}
|
||||
|
||||
// restore extended attributes for windows
|
||||
func nodeRestoreExtendedAttributes(node *restic.Node, path string, xattrSelectFilter func(xattrName string) bool) error {
|
||||
func nodeRestoreExtendedAttributes(node *data.Node, path string, xattrSelectFilter func(xattrName string) bool) error {
|
||||
count := len(node.ExtendedAttributes)
|
||||
if count > 0 {
|
||||
eas := []extendedAttribute{}
|
||||
@@ -91,14 +91,14 @@ func nodeRestoreExtendedAttributes(node *restic.Node, path string, xattrSelectFi
|
||||
// fill extended attributes in the node
|
||||
// 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 nodeFillExtendedAttributes(node *restic.Node, path string, _ bool, _ func(format string, args ...any)) (err error) {
|
||||
func nodeFillExtendedAttributes(node *data.Node, path string, _ bool, _ func(format string, args ...any)) (err error) {
|
||||
if strings.Contains(filepath.Base(path), ":") {
|
||||
// Do not process for Alternate Data Streams in Windows
|
||||
return nil
|
||||
}
|
||||
|
||||
// only capture xattrs for file/dir
|
||||
if node.Type != restic.NodeTypeFile && node.Type != restic.NodeTypeDir {
|
||||
if node.Type != data.NodeTypeFile && node.Type != data.NodeTypeDir {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ func nodeFillExtendedAttributes(node *restic.Node, path string, _ bool, _ func(f
|
||||
|
||||
//Fill the ExtendedAttributes in the node using the name/value pairs in the windows EA
|
||||
for _, attr := range extAtts {
|
||||
extendedAttr := restic.ExtendedAttribute{
|
||||
extendedAttr := data.ExtendedAttribute{
|
||||
Name: attr.Name,
|
||||
Value: attr.Value,
|
||||
}
|
||||
@@ -151,7 +151,7 @@ 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 restic.NodeType, path string, eas []extendedAttribute) (err error) {
|
||||
func restoreExtendedAttributes(nodeType data.NodeType, path string, eas []extendedAttribute) (err error) {
|
||||
var fileHandle windows.Handle
|
||||
if fileHandle, err = openHandleForEA(nodeType, path, true); fileHandle == 0 {
|
||||
return nil
|
||||
@@ -188,7 +188,7 @@ func restoreExtendedAttributes(nodeType restic.NodeType, path string, eas []exte
|
||||
}
|
||||
|
||||
// restoreGenericAttributes restores generic attributes for Windows
|
||||
func nodeRestoreGenericAttributes(node *restic.Node, path string, warn func(msg string)) (err error) {
|
||||
func nodeRestoreGenericAttributes(node *data.Node, path string, warn func(msg string)) (err error) {
|
||||
if len(node.GenericAttributes) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -213,14 +213,14 @@ func nodeRestoreGenericAttributes(node *restic.Node, path string, warn func(msg
|
||||
}
|
||||
}
|
||||
|
||||
restic.HandleUnknownGenericAttributesFound(unknownAttribs, warn)
|
||||
data.HandleUnknownGenericAttributesFound(unknownAttribs, warn)
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// 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 restic.WindowsAttributes, unknownAttribs []restic.GenericAttributeType, err error) {
|
||||
func genericAttributesToWindowsAttrs(attrs map[data.GenericAttributeType]json.RawMessage) (windowsAttributes data.WindowsAttributes, unknownAttribs []data.GenericAttributeType, err error) {
|
||||
waValue := reflect.ValueOf(&windowsAttributes).Elem()
|
||||
unknownAttribs, err = restic.GenericAttributesToOSAttrs(attrs, reflect.TypeOf(windowsAttributes), &waValue, "windows")
|
||||
unknownAttribs, err = data.GenericAttributesToOSAttrs(attrs, reflect.TypeOf(windowsAttributes), &waValue, "windows")
|
||||
return windowsAttributes, unknownAttribs, err
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ func decryptFile(pathPointer *uint16) error {
|
||||
|
||||
// nodeFillGenericAttributes fills in the generic attributes for windows like File Attributes,
|
||||
// Created time and Security Descriptors.
|
||||
func nodeFillGenericAttributes(node *restic.Node, path string, stat *ExtendedFileInfo) error {
|
||||
func nodeFillGenericAttributes(node *data.Node, path string, stat *ExtendedFileInfo) error {
|
||||
if strings.Contains(filepath.Base(path), ":") {
|
||||
// Do not process for Alternate Data Streams in Windows
|
||||
return nil
|
||||
@@ -360,7 +360,7 @@ func nodeFillGenericAttributes(node *restic.Node, path string, stat *ExtendedFil
|
||||
}
|
||||
|
||||
var sd *[]byte
|
||||
if node.Type == restic.NodeTypeFile || node.Type == restic.NodeTypeDir {
|
||||
if node.Type == data.NodeTypeFile || node.Type == data.NodeTypeDir {
|
||||
if sd, err = getSecurityDescriptor(path); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -369,7 +369,7 @@ func nodeFillGenericAttributes(node *restic.Node, path string, stat *ExtendedFil
|
||||
winFI := stat.sys.(*syscall.Win32FileAttributeData)
|
||||
|
||||
// Add Windows attributes
|
||||
node.GenericAttributes, err = restic.WindowsAttrsToGenericAttributes(restic.WindowsAttributes{
|
||||
node.GenericAttributes, err = data.WindowsAttrsToGenericAttributes(data.WindowsAttributes{
|
||||
CreationTime: &winFI.CreationTime,
|
||||
FileAttributes: &winFI.FileAttributes,
|
||||
SecurityDescriptor: sd,
|
||||
|
||||
@@ -14,8 +14,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/data"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/test"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
@@ -24,19 +24,19 @@ func TestRestoreSecurityDescriptors(t *testing.T) {
|
||||
t.Parallel()
|
||||
tempDir := t.TempDir()
|
||||
for i, sd := range testFileSDs {
|
||||
testRestoreSecurityDescriptor(t, sd, tempDir, restic.NodeTypeFile, fmt.Sprintf("testfile%d", i))
|
||||
testRestoreSecurityDescriptor(t, sd, tempDir, data.NodeTypeFile, fmt.Sprintf("testfile%d", i))
|
||||
}
|
||||
for i, sd := range testDirSDs {
|
||||
testRestoreSecurityDescriptor(t, sd, tempDir, restic.NodeTypeDir, fmt.Sprintf("testdir%d", i))
|
||||
testRestoreSecurityDescriptor(t, sd, tempDir, data.NodeTypeDir, fmt.Sprintf("testdir%d", i))
|
||||
}
|
||||
}
|
||||
|
||||
func testRestoreSecurityDescriptor(t *testing.T, sd string, tempDir string, fileType restic.NodeType, fileName string) {
|
||||
func testRestoreSecurityDescriptor(t *testing.T, sd string, tempDir string, fileType data.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 := restic.WindowsAttrsToGenericAttributes(restic.WindowsAttributes{CreationTime: nil, FileAttributes: nil, SecurityDescriptor: &sdInputBytes})
|
||||
genericAttributes, err := data.WindowsAttrsToGenericAttributes(data.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)
|
||||
@@ -56,8 +56,8 @@ func testRestoreSecurityDescriptor(t *testing.T, sd string, tempDir string, file
|
||||
compareSecurityDescriptors(t, testPath, *sdByteFromRestoredNode, *sdBytesFromRestoredPath)
|
||||
}
|
||||
|
||||
func getNode(name string, fileType restic.NodeType, genericAttributes map[restic.GenericAttributeType]json.RawMessage) restic.Node {
|
||||
return restic.Node{
|
||||
func getNode(name string, fileType data.NodeType, genericAttributes map[data.GenericAttributeType]json.RawMessage) data.Node {
|
||||
return data.Node{
|
||||
Name: name,
|
||||
Type: fileType,
|
||||
Mode: 0644,
|
||||
@@ -68,7 +68,7 @@ func getNode(name string, fileType restic.NodeType, genericAttributes map[restic
|
||||
}
|
||||
}
|
||||
|
||||
func getWindowsAttr(t *testing.T, testPath string, node *restic.Node) restic.WindowsAttributes {
|
||||
func getWindowsAttr(t *testing.T, testPath string, node *data.Node) data.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)
|
||||
@@ -83,19 +83,19 @@ func TestRestoreCreationTime(t *testing.T) {
|
||||
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, restic.WindowsAttributes{CreationTime: &creationTimeAttribute}, false)
|
||||
runGenericAttributesTest(t, path, data.TypeCreationTime, data.WindowsAttributes{CreationTime: &creationTimeAttribute}, false)
|
||||
}
|
||||
|
||||
func TestRestoreFileAttributes(t *testing.T) {
|
||||
t.Parallel()
|
||||
genericAttributeName := restic.TypeFileAttributes
|
||||
genericAttributeName := data.TypeFileAttributes
|
||||
tempDir := t.TempDir()
|
||||
normal := uint32(syscall.FILE_ATTRIBUTE_NORMAL)
|
||||
hidden := uint32(syscall.FILE_ATTRIBUTE_HIDDEN)
|
||||
system := uint32(syscall.FILE_ATTRIBUTE_SYSTEM)
|
||||
archive := uint32(syscall.FILE_ATTRIBUTE_ARCHIVE)
|
||||
encrypted := uint32(windows.FILE_ATTRIBUTE_ENCRYPTED)
|
||||
fileAttributes := []restic.WindowsAttributes{
|
||||
fileAttributes := []data.WindowsAttributes{
|
||||
//normal
|
||||
{FileAttributes: &normal},
|
||||
//hidden
|
||||
@@ -108,12 +108,12 @@ func TestRestoreFileAttributes(t *testing.T) {
|
||||
{FileAttributes: &encrypted},
|
||||
}
|
||||
for i, fileAttr := range fileAttributes {
|
||||
genericAttrs, err := restic.WindowsAttrsToGenericAttributes(fileAttr)
|
||||
genericAttrs, err := data.WindowsAttrsToGenericAttributes(fileAttr)
|
||||
test.OK(t, err)
|
||||
expectedNodes := []restic.Node{
|
||||
expectedNodes := []data.Node{
|
||||
{
|
||||
Name: fmt.Sprintf("testfile%d", i),
|
||||
Type: restic.NodeTypeFile,
|
||||
Type: data.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 := []restic.WindowsAttributes{
|
||||
folderAttributes := []data.WindowsAttributes{
|
||||
//normal
|
||||
{FileAttributes: &normal},
|
||||
//hidden
|
||||
@@ -141,12 +141,12 @@ func TestRestoreFileAttributes(t *testing.T) {
|
||||
{FileAttributes: &encrypted},
|
||||
}
|
||||
for i, folderAttr := range folderAttributes {
|
||||
genericAttrs, err := restic.WindowsAttrsToGenericAttributes(folderAttr)
|
||||
genericAttrs, err := data.WindowsAttrsToGenericAttributes(folderAttr)
|
||||
test.OK(t, err)
|
||||
expectedNodes := []restic.Node{
|
||||
expectedNodes := []data.Node{
|
||||
{
|
||||
Name: fmt.Sprintf("testdirectory%d", i),
|
||||
Type: restic.NodeTypeDir,
|
||||
Type: data.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 restic.WindowsAttributes, warningExpected bool) {
|
||||
genericAttributes, err := restic.WindowsAttrsToGenericAttributes(genericAttributeExpected)
|
||||
func runGenericAttributesTest(t *testing.T, tempDir string, genericAttributeName data.GenericAttributeType, genericAttributeExpected data.WindowsAttributes, warningExpected bool) {
|
||||
genericAttributes, err := data.WindowsAttrsToGenericAttributes(genericAttributeExpected)
|
||||
test.OK(t, err)
|
||||
expectedNodes := []restic.Node{
|
||||
expectedNodes := []data.Node{
|
||||
{
|
||||
Name: "testfile",
|
||||
Type: restic.NodeTypeFile,
|
||||
Type: data.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: restic.NodeTypeDir,
|
||||
Type: data.NodeTypeDir,
|
||||
Mode: 0755,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
@@ -183,29 +183,29 @@ 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 restic.WindowsAttributes, warningExpected bool) {
|
||||
func runGenericAttributesTestForNodes(t *testing.T, expectedNodes []data.Node, tempDir string, genericAttr data.GenericAttributeType, genericAttributeExpected data.WindowsAttributes, warningExpected bool) {
|
||||
|
||||
for _, testNode := range expectedNodes {
|
||||
testPath, node := restoreAndGetNode(t, tempDir, &testNode, warningExpected)
|
||||
rawMessage := node.GenericAttributes[genericAttr]
|
||||
genericAttrsExpected, err := restic.WindowsAttrsToGenericAttributes(genericAttributeExpected)
|
||||
genericAttrsExpected, err := data.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)
|
||||
}
|
||||
}
|
||||
|
||||
func restoreAndGetNode(t *testing.T, tempDir string, testNode *restic.Node, warningExpected bool) (string, *restic.Node) {
|
||||
func restoreAndGetNode(t *testing.T, tempDir string, testNode *data.Node, warningExpected bool) (string, *data.Node) {
|
||||
testPath := filepath.Join(tempDir, "001", testNode.Name)
|
||||
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 == restic.NodeTypeFile {
|
||||
if testNode.Type == data.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 == restic.NodeTypeDir {
|
||||
} else if testNode.Type == data.NodeTypeDir {
|
||||
|
||||
err := os.Mkdir(testPath, testNode.Mode)
|
||||
test.OK(t, errors.Wrapf(err, "Failed to create test directory: %s", testPath))
|
||||
@@ -231,19 +231,19 @@ func restoreAndGetNode(t *testing.T, tempDir string, testNode *restic.Node, warn
|
||||
return testPath, nodeFromFileInfo
|
||||
}
|
||||
|
||||
const TypeSomeNewAttribute restic.GenericAttributeType = "MockAttributes.SomeNewAttribute"
|
||||
const TypeSomeNewAttribute data.GenericAttributeType = "MockAttributes.SomeNewAttribute"
|
||||
|
||||
func TestNewGenericAttributeType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
newGenericAttribute := map[restic.GenericAttributeType]json.RawMessage{}
|
||||
newGenericAttribute := map[data.GenericAttributeType]json.RawMessage{}
|
||||
newGenericAttribute[TypeSomeNewAttribute] = []byte("any value")
|
||||
|
||||
tempDir := t.TempDir()
|
||||
expectedNodes := []restic.Node{
|
||||
expectedNodes := []data.Node{
|
||||
{
|
||||
Name: "testfile",
|
||||
Type: restic.NodeTypeFile,
|
||||
Type: data.NodeTypeFile,
|
||||
Mode: 0644,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
@@ -252,7 +252,7 @@ func TestNewGenericAttributeType(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Name: "testdirectory",
|
||||
Type: restic.NodeTypeDir,
|
||||
Type: data.NodeTypeDir,
|
||||
Mode: 0755,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
@@ -272,26 +272,26 @@ func TestNewGenericAttributeType(t *testing.T) {
|
||||
func TestRestoreExtendedAttributes(t *testing.T) {
|
||||
t.Parallel()
|
||||
tempDir := t.TempDir()
|
||||
expectedNodes := []restic.Node{
|
||||
expectedNodes := []data.Node{
|
||||
{
|
||||
Name: "testfile",
|
||||
Type: restic.NodeTypeFile,
|
||||
Type: data.NodeTypeFile,
|
||||
Mode: 0644,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
ChangeTime: parseTime("2005-05-14 21:07:05.333"),
|
||||
ExtendedAttributes: []restic.ExtendedAttribute{
|
||||
ExtendedAttributes: []data.ExtendedAttribute{
|
||||
{"user.foo", []byte("bar")},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "testdirectory",
|
||||
Type: restic.NodeTypeDir,
|
||||
Type: data.NodeTypeDir,
|
||||
Mode: 0755,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
ChangeTime: parseTime("2005-05-14 21:07:05.333"),
|
||||
ExtendedAttributes: []restic.ExtendedAttribute{
|
||||
ExtendedAttributes: []data.ExtendedAttribute{
|
||||
{"user.foo", []byte("bar")},
|
||||
},
|
||||
},
|
||||
@@ -302,9 +302,9 @@ func TestRestoreExtendedAttributes(t *testing.T) {
|
||||
var handle windows.Handle
|
||||
var err error
|
||||
utf16Path := windows.StringToUTF16Ptr(testPath)
|
||||
if node.Type == restic.NodeTypeFile {
|
||||
if node.Type == data.NodeTypeFile {
|
||||
handle, err = windows.CreateFile(utf16Path, windows.FILE_READ_EA, 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0)
|
||||
} else if node.Type == restic.NodeTypeDir {
|
||||
} else if node.Type == data.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))
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/restic/restic/internal/data"
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
|
||||
"github.com/pkg/xattr"
|
||||
)
|
||||
@@ -63,7 +63,7 @@ func handleXattrErr(err error) error {
|
||||
}
|
||||
}
|
||||
|
||||
func nodeRestoreExtendedAttributes(node *restic.Node, path string, xattrSelectFilter func(xattrName string) bool) error {
|
||||
func nodeRestoreExtendedAttributes(node *data.Node, path string, xattrSelectFilter func(xattrName string) bool) error {
|
||||
expectedAttrs := map[string]struct{}{}
|
||||
for _, attr := range node.ExtendedAttributes {
|
||||
// Only restore xattrs that match the filter
|
||||
@@ -96,7 +96,7 @@ func nodeRestoreExtendedAttributes(node *restic.Node, path string, xattrSelectFi
|
||||
return nil
|
||||
}
|
||||
|
||||
func nodeFillExtendedAttributes(node *restic.Node, path string, ignoreListError bool, warnf func(format string, args ...any)) error {
|
||||
func nodeFillExtendedAttributes(node *data.Node, path string, ignoreListError bool, warnf func(format string, args ...any)) error {
|
||||
xattrs, err := listxattr(path)
|
||||
debug.Log("fillExtendedAttributes(%v) %v %v", path, xattrs, err)
|
||||
if err != nil {
|
||||
@@ -106,14 +106,14 @@ func nodeFillExtendedAttributes(node *restic.Node, path string, ignoreListError
|
||||
return err
|
||||
}
|
||||
|
||||
node.ExtendedAttributes = make([]restic.ExtendedAttribute, 0, len(xattrs))
|
||||
node.ExtendedAttributes = make([]data.ExtendedAttribute, 0, len(xattrs))
|
||||
for _, attr := range xattrs {
|
||||
attrVal, err := getxattr(path, attr)
|
||||
if err != nil {
|
||||
warnf("can not obtain extended attribute %v for %v:\n", attr, path)
|
||||
continue
|
||||
}
|
||||
attr := restic.ExtendedAttribute{
|
||||
attr := data.ExtendedAttribute{
|
||||
Name: attr,
|
||||
Value: attrVal,
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/restic/restic/internal/data"
|
||||
"github.com/restic/restic/internal/filter"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
func setAndVerifyXattr(t *testing.T, file string, attrs []restic.ExtendedAttribute) {
|
||||
func setAndVerifyXattr(t *testing.T, file string, attrs []data.ExtendedAttribute) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// windows seems to convert the xattr name to upper case
|
||||
for i := range attrs {
|
||||
@@ -24,15 +24,15 @@ func setAndVerifyXattr(t *testing.T, file string, attrs []restic.ExtendedAttribu
|
||||
}
|
||||
}
|
||||
|
||||
node := &restic.Node{
|
||||
Type: restic.NodeTypeFile,
|
||||
node := &data.Node{
|
||||
Type: data.NodeTypeFile,
|
||||
ExtendedAttributes: attrs,
|
||||
}
|
||||
/* restore all xattrs */
|
||||
rtest.OK(t, nodeRestoreExtendedAttributes(node, file, func(_ string) bool { return true }))
|
||||
|
||||
nodeActual := &restic.Node{
|
||||
Type: restic.NodeTypeFile,
|
||||
nodeActual := &data.Node{
|
||||
Type: data.NodeTypeFile,
|
||||
}
|
||||
rtest.OK(t, nodeFillExtendedAttributes(nodeActual, file, false, t.Logf))
|
||||
|
||||
@@ -40,7 +40,7 @@ func setAndVerifyXattr(t *testing.T, file string, attrs []restic.ExtendedAttribu
|
||||
}
|
||||
|
||||
func setAndVerifyXattrWithSelectFilter(t *testing.T, file string, testAttr []testXattrToRestore, xattrSelectFilter func(_ string) bool) {
|
||||
attrs := make([]restic.ExtendedAttribute, len(testAttr))
|
||||
attrs := make([]data.ExtendedAttribute, len(testAttr))
|
||||
for i := range testAttr {
|
||||
// windows seems to convert the xattr name to upper case
|
||||
if runtime.GOOS == "windows" {
|
||||
@@ -49,15 +49,15 @@ func setAndVerifyXattrWithSelectFilter(t *testing.T, file string, testAttr []tes
|
||||
attrs[i] = testAttr[i].xattr
|
||||
}
|
||||
|
||||
node := &restic.Node{
|
||||
Type: restic.NodeTypeFile,
|
||||
node := &data.Node{
|
||||
Type: data.NodeTypeFile,
|
||||
ExtendedAttributes: attrs,
|
||||
}
|
||||
|
||||
rtest.OK(t, nodeRestoreExtendedAttributes(node, file, xattrSelectFilter))
|
||||
|
||||
nodeActual := &restic.Node{
|
||||
Type: restic.NodeTypeFile,
|
||||
nodeActual := &data.Node{
|
||||
Type: data.NodeTypeFile,
|
||||
}
|
||||
rtest.OK(t, nodeFillExtendedAttributes(nodeActual, file, false, t.Logf))
|
||||
|
||||
@@ -82,7 +82,7 @@ func setAndVerifyXattrWithSelectFilter(t *testing.T, file string, testAttr []tes
|
||||
}
|
||||
|
||||
type testXattrToRestore struct {
|
||||
xattr restic.ExtendedAttribute
|
||||
xattr data.ExtendedAttribute
|
||||
shouldRestore bool
|
||||
}
|
||||
|
||||
@@ -91,14 +91,14 @@ func TestOverwriteXattr(t *testing.T) {
|
||||
file := filepath.Join(dir, "file")
|
||||
rtest.OK(t, os.WriteFile(file, []byte("hello world"), 0o600))
|
||||
|
||||
setAndVerifyXattr(t, file, []restic.ExtendedAttribute{
|
||||
setAndVerifyXattr(t, file, []data.ExtendedAttribute{
|
||||
{
|
||||
Name: "user.foo",
|
||||
Value: []byte("bar"),
|
||||
},
|
||||
})
|
||||
|
||||
setAndVerifyXattr(t, file, []restic.ExtendedAttribute{
|
||||
setAndVerifyXattr(t, file, []data.ExtendedAttribute{
|
||||
{
|
||||
Name: "user.other",
|
||||
Value: []byte("some"),
|
||||
@@ -133,21 +133,21 @@ func TestOverwriteXattrWithSelectFilter(t *testing.T) {
|
||||
|
||||
setAndVerifyXattrWithSelectFilter(t, file, []testXattrToRestore{
|
||||
{
|
||||
xattr: restic.ExtendedAttribute{
|
||||
xattr: data.ExtendedAttribute{
|
||||
Name: "user.foo",
|
||||
Value: []byte("bar"),
|
||||
},
|
||||
shouldRestore: true,
|
||||
},
|
||||
{
|
||||
xattr: restic.ExtendedAttribute{
|
||||
xattr: data.ExtendedAttribute{
|
||||
Name: "user.test",
|
||||
Value: []byte("testxattr"),
|
||||
},
|
||||
shouldRestore: true,
|
||||
},
|
||||
{
|
||||
xattr: restic.ExtendedAttribute{
|
||||
xattr: data.ExtendedAttribute{
|
||||
Name: "security.other",
|
||||
Value: []byte("testing"),
|
||||
},
|
||||
@@ -163,35 +163,35 @@ func TestOverwriteXattrWithSelectFilter(t *testing.T) {
|
||||
|
||||
setAndVerifyXattrWithSelectFilter(t, file, []testXattrToRestore{
|
||||
{
|
||||
xattr: restic.ExtendedAttribute{
|
||||
xattr: data.ExtendedAttribute{
|
||||
Name: "user.other",
|
||||
Value: []byte("some"),
|
||||
},
|
||||
shouldRestore: true,
|
||||
},
|
||||
{
|
||||
xattr: restic.ExtendedAttribute{
|
||||
xattr: data.ExtendedAttribute{
|
||||
Name: "security.other",
|
||||
Value: []byte("testing"),
|
||||
},
|
||||
shouldRestore: false,
|
||||
},
|
||||
{
|
||||
xattr: restic.ExtendedAttribute{
|
||||
xattr: data.ExtendedAttribute{
|
||||
Name: "user.open",
|
||||
Value: []byte("door"),
|
||||
},
|
||||
shouldRestore: true,
|
||||
},
|
||||
{
|
||||
xattr: restic.ExtendedAttribute{
|
||||
xattr: data.ExtendedAttribute{
|
||||
Name: "user.common",
|
||||
Value: []byte("testing"),
|
||||
},
|
||||
shouldRestore: true,
|
||||
},
|
||||
{
|
||||
xattr: restic.ExtendedAttribute{
|
||||
xattr: data.ExtendedAttribute{
|
||||
Name: "user.bad",
|
||||
Value: []byte("dontincludeme"),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user