mirror of
https://github.com/octoleo/restic.git
synced 2024-11-26 23:06:32 +00:00
fs: replace statT with ExtendedFileInfo
This commit is contained in:
parent
6d3a5260d3
commit
f0329bb4e6
@ -6,7 +6,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/restic/restic/internal/debug"
|
"github.com/restic/restic/internal/debug"
|
||||||
"github.com/restic/restic/internal/errors"
|
"github.com/restic/restic/internal/errors"
|
||||||
@ -57,8 +56,7 @@ func nodeTypeFromFileInfo(fi os.FileInfo) restic.NodeType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func nodeFillExtra(node *restic.Node, path string, fi os.FileInfo, ignoreXattrListError bool) error {
|
func nodeFillExtra(node *restic.Node, path string, fi os.FileInfo, ignoreXattrListError bool) error {
|
||||||
stat, ok := toStatT(fi.Sys())
|
if fi.Sys() == nil {
|
||||||
if !ok {
|
|
||||||
// fill minimal info with current values for uid, gid
|
// fill minimal info with current values for uid, gid
|
||||||
node.UID = uint32(os.Getuid())
|
node.UID = uint32(os.Getuid())
|
||||||
node.GID = uint32(os.Getgid())
|
node.GID = uint32(os.Getgid())
|
||||||
@ -66,38 +64,43 @@ func nodeFillExtra(node *restic.Node, path string, fi os.FileInfo, ignoreXattrLi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
node.Inode = uint64(stat.ino())
|
stat := ExtendedStat(fi)
|
||||||
node.DeviceID = uint64(stat.dev())
|
|
||||||
|
|
||||||
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 {
|
switch node.Type {
|
||||||
case restic.NodeTypeFile:
|
case restic.NodeTypeFile:
|
||||||
node.Size = uint64(stat.size())
|
node.Size = uint64(stat.Size)
|
||||||
node.Links = uint64(stat.nlink())
|
node.Links = stat.Links
|
||||||
case restic.NodeTypeDir:
|
case restic.NodeTypeDir:
|
||||||
case restic.NodeTypeSymlink:
|
case restic.NodeTypeSymlink:
|
||||||
var err error
|
var err error
|
||||||
node.LinkTarget, err = os.Readlink(fixpath(path))
|
node.LinkTarget, err = os.Readlink(fixpath(path))
|
||||||
node.Links = uint64(stat.nlink())
|
node.Links = stat.Links
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
case restic.NodeTypeDev:
|
case restic.NodeTypeDev:
|
||||||
node.Device = uint64(stat.rdev())
|
node.Device = stat.Device
|
||||||
node.Links = uint64(stat.nlink())
|
node.Links = stat.Links
|
||||||
case restic.NodeTypeCharDev:
|
case restic.NodeTypeCharDev:
|
||||||
node.Device = uint64(stat.rdev())
|
node.Device = stat.Device
|
||||||
node.Links = uint64(stat.nlink())
|
node.Links = stat.Links
|
||||||
case restic.NodeTypeFifo:
|
case restic.NodeTypeFifo:
|
||||||
case restic.NodeTypeSocket:
|
case restic.NodeTypeSocket:
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("unsupported file type %q", node.Type)
|
return errors.Errorf("unsupported file type %q", node.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
allowExtended, err := nodeFillGenericAttributes(node, path, fi, stat)
|
allowExtended, err := nodeFillGenericAttributes(node, path, &stat)
|
||||||
if allowExtended {
|
if allowExtended {
|
||||||
// Skip processing ExtendedAttributes if allowExtended is false.
|
// Skip processing ExtendedAttributes if allowExtended is false.
|
||||||
err = errors.CombineErrors(err, nodeFillExtendedAttributes(node, path, ignoreXattrListError))
|
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
|
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 (
|
var (
|
||||||
uidLookupCache = make(map[uint32]string)
|
uidLookupCache = make(map[uint32]string)
|
||||||
uidLookupCacheMutex = sync.RWMutex{}
|
uidLookupCacheMutex = sync.RWMutex{}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
@ -14,17 +13,6 @@ func nodeRestoreSymlinkTimestamps(_ string, _ [2]syscall.Timespec) error {
|
|||||||
return nil
|
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.
|
// nodeRestoreExtendedAttributes is a no-op on AIX.
|
||||||
func nodeRestoreExtendedAttributes(_ *restic.Node, _ string) error {
|
func nodeRestoreExtendedAttributes(_ *restic.Node, _ string) error {
|
||||||
return nil
|
return nil
|
||||||
@ -46,6 +34,6 @@ func nodeRestoreGenericAttributes(node *restic.Node, _ string, warn func(msg str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// nodeFillGenericAttributes is a no-op on AIX.
|
// 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
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,3 @@ import "syscall"
|
|||||||
func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
||||||
return nil
|
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 }
|
|
||||||
|
@ -12,7 +12,3 @@ func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error
|
|||||||
func mknod(path string, mode uint32, dev uint64) (err error) {
|
func mknod(path string, mode uint32, dev uint64) (err error) {
|
||||||
return syscall.Mknod(path, mode, dev)
|
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 }
|
|
||||||
|
@ -31,7 +31,3 @@ func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error
|
|||||||
|
|
||||||
return dir.Close()
|
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 }
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
@ -11,10 +10,6 @@ func nodeRestoreSymlinkTimestamps(_ string, _ [2]syscall.Timespec) error {
|
|||||||
return nil
|
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.
|
// nodeRestoreExtendedAttributes is a no-op on netbsd.
|
||||||
func nodeRestoreExtendedAttributes(_ *restic.Node, _ string) error {
|
func nodeRestoreExtendedAttributes(_ *restic.Node, _ string) error {
|
||||||
return nil
|
return nil
|
||||||
@ -36,6 +31,6 @@ func nodeRestoreGenericAttributes(node *restic.Node, _ string, warn func(msg str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// nodeFillGenericAttributes is a no-op on netbsd.
|
// 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
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
@ -11,10 +10,6 @@ func nodeRestoreSymlinkTimestamps(_ string, _ [2]syscall.Timespec) error {
|
|||||||
return nil
|
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.
|
// nodeRestoreExtendedAttributes is a no-op on openbsd.
|
||||||
func nodeRestoreExtendedAttributes(_ *restic.Node, _ string) error {
|
func nodeRestoreExtendedAttributes(_ *restic.Node, _ string) error {
|
||||||
return nil
|
return nil
|
||||||
@ -36,6 +31,6 @@ func nodeRestoreGenericAttributes(node *restic.Node, _ string, warn func(msg str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fillGenericAttributes is a no-op on openbsd.
|
// 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
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,3 @@ import "syscall"
|
|||||||
func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
func nodeRestoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
||||||
return nil
|
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 }
|
|
||||||
|
@ -5,27 +5,8 @@ package fs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func lchown(name string, uid, gid int) error {
|
func lchown(name string, uid, gid int) error {
|
||||||
return os.Lchown(name, uid, gid)
|
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) }
|
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
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
|
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()
|
t.Helper()
|
||||||
|
|
||||||
|
stat := fi.Sys().(*syscall.Stat_t)
|
||||||
|
|
||||||
if uint32(node.Mode.Perm()) != uint32(stat.Mode&0777) {
|
if uint32(node.Mode.Perm()) != uint32(stat.Mode&0777) {
|
||||||
t.Errorf("Mode does not match, want %v, got %v", stat.Mode&0777, node.Mode)
|
t.Errorf("Mode does not match, want %v, got %v", stat.Mode&0777, node.Mode)
|
||||||
}
|
}
|
||||||
@ -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
|
// use the os dependent function to compare the timestamps
|
||||||
s, ok := toStatT(stat)
|
s := ExtendedStat(fi)
|
||||||
if !ok {
|
if node.ModTime != s.ModTime {
|
||||||
return
|
t.Errorf("ModTime does not match, want %v, got %v", s.ModTime, node.ModTime)
|
||||||
}
|
}
|
||||||
|
if node.ChangeTime != s.ChangeTime {
|
||||||
mtime := s.mtim()
|
t.Errorf("ChangeTime does not match, want %v, got %v", s.ChangeTime, node.ChangeTime)
|
||||||
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.AccessTime != s.AccessTime {
|
||||||
ctime := s.ctim()
|
t.Errorf("AccessTime does not match, want %v, got %v", s.AccessTime, node.AccessTime)
|
||||||
if node.ChangeTime != time.Unix(ctime.Unix()) {
|
|
||||||
t.Errorf("ChangeTime does not match, want %v, got %v", time.Unix(ctime.Unix()), node.ChangeTime)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
if node.Device != uint64(stat.Rdev) {
|
||||||
t.Errorf("Rdev does not match, want %v, got %v", stat.Rdev, node.Device)
|
t.Errorf("Rdev does not match, want %v, got %v", stat.Rdev, node.Device)
|
||||||
}
|
}
|
||||||
@ -123,12 +117,6 @@ func TestNodeFromFileInfo(t *testing.T) {
|
|||||||
return
|
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)
|
node, err := NodeFromFileInfo(test.filename, fi, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -136,10 +124,10 @@ func TestNodeFromFileInfo(t *testing.T) {
|
|||||||
|
|
||||||
switch node.Type {
|
switch node.Type {
|
||||||
case restic.NodeTypeFile, restic.NodeTypeSymlink:
|
case restic.NodeTypeFile, restic.NodeTypeSymlink:
|
||||||
checkFile(t, s, node)
|
checkFile(t, fi, node)
|
||||||
case restic.NodeTypeDev, restic.NodeTypeCharDev:
|
case restic.NodeTypeDev, restic.NodeTypeCharDev:
|
||||||
checkFile(t, s, node)
|
checkFile(t, fi, node)
|
||||||
checkDevice(t, s, node)
|
checkDevice(t, fi, node)
|
||||||
default:
|
default:
|
||||||
t.Fatalf("invalid node type %q", node.Type)
|
t.Fatalf("invalid node type %q", node.Type)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package fs
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -175,40 +174,6 @@ func restoreExtendedAttributes(nodeType restic.NodeType, path string, eas []exte
|
|||||||
return nil
|
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
|
// restoreGenericAttributes restores generic attributes for Windows
|
||||||
func nodeRestoreGenericAttributes(node *restic.Node, path string, warn func(msg string)) (err error) {
|
func nodeRestoreGenericAttributes(node *restic.Node, path string, warn func(msg string)) (err error) {
|
||||||
if len(node.GenericAttributes) == 0 {
|
if len(node.GenericAttributes) == 0 {
|
||||||
@ -365,7 +330,7 @@ func decryptFile(pathPointer *uint16) error {
|
|||||||
// Created time and Security Descriptors.
|
// Created time and Security Descriptors.
|
||||||
// It also checks if the volume supports extended attributes and stores the result in a map
|
// 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.
|
// 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), ":") {
|
if strings.Contains(filepath.Base(path), ":") {
|
||||||
// Do not process for Alternate Data Streams in Windows
|
// Do not process for Alternate Data Streams in Windows
|
||||||
// Also do not allow processing of extended attributes for ADS.
|
// Also do not allow processing of extended attributes for ADS.
|
||||||
@ -396,10 +361,13 @@ func nodeFillGenericAttributes(node *restic.Node, path string, fi os.FileInfo, s
|
|||||||
return allowExtended, err
|
return allowExtended, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
winFI := stat.Sys().(*syscall.Win32FileAttributeData)
|
||||||
|
|
||||||
// Add Windows attributes
|
// Add Windows attributes
|
||||||
node.GenericAttributes, err = WindowsAttrsToGenericAttributes(WindowsAttributes{
|
node.GenericAttributes, err = WindowsAttrsToGenericAttributes(WindowsAttributes{
|
||||||
CreationTime: getCreationTime(fi, path),
|
CreationTime: &winFI.CreationTime,
|
||||||
FileAttributes: &stat.FileAttributes,
|
FileAttributes: &winFI.FileAttributes,
|
||||||
SecurityDescriptor: sd,
|
SecurityDescriptor: sd,
|
||||||
})
|
})
|
||||||
return allowExtended, err
|
return allowExtended, err
|
||||||
@ -501,18 +469,3 @@ func WindowsAttrsToGenericAttributes(windowsAttributes WindowsAttributes) (attrs
|
|||||||
windowsAttributesValue := reflect.ValueOf(windowsAttributes)
|
windowsAttributesValue := reflect.ValueOf(windowsAttributes)
|
||||||
return restic.OSAttrsToGenericAttributes(reflect.TypeOf(windowsAttributes), &windowsAttributesValue, runtime.GOOS)
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -80,10 +80,10 @@ func TestRestoreCreationTime(t *testing.T) {
|
|||||||
path := t.TempDir()
|
path := t.TempDir()
|
||||||
fi, err := os.Lstat(path)
|
fi, err := os.Lstat(path)
|
||||||
test.OK(t, errors.Wrapf(err, "Could not Lstat for path: %s", path))
|
test.OK(t, errors.Wrapf(err, "Could not Lstat for path: %s", path))
|
||||||
creationTimeAttribute := getCreationTime(fi, path)
|
attr := fi.Sys().(*syscall.Win32FileAttributeData)
|
||||||
test.OK(t, errors.Wrapf(err, "Could not get creation time for path: %s", path))
|
creationTimeAttribute := attr.CreationTime
|
||||||
//Using the temp dir creation time as the test creation time for the test file and folder
|
//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, WindowsAttributes{CreationTime: &creationTimeAttribute}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRestoreFileAttributes(t *testing.T) {
|
func TestRestoreFileAttributes(t *testing.T) {
|
||||||
|
@ -71,7 +71,7 @@ func nodeRestoreGenericAttributes(node *restic.Node, _ string, warn func(msg str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// nodeFillGenericAttributes is a no-op.
|
// 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
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ func extendedStat(fi os.FileInfo) ExtendedFileInfo {
|
|||||||
|
|
||||||
extFI := ExtendedFileInfo{
|
extFI := ExtendedFileInfo{
|
||||||
FileInfo: fi,
|
FileInfo: fi,
|
||||||
Size: int64(s.FileSizeLow) + int64(s.FileSizeHigh)<<32,
|
Size: int64(s.FileSizeLow) | (int64(s.FileSizeHigh) << 32),
|
||||||
}
|
}
|
||||||
|
|
||||||
atime := syscall.NsecToTimespec(s.LastAccessTime.Nanoseconds())
|
atime := syscall.NsecToTimespec(s.LastAccessTime.Nanoseconds())
|
||||||
@ -28,6 +28,7 @@ func extendedStat(fi os.FileInfo) ExtendedFileInfo {
|
|||||||
mtime := syscall.NsecToTimespec(s.LastWriteTime.Nanoseconds())
|
mtime := syscall.NsecToTimespec(s.LastWriteTime.Nanoseconds())
|
||||||
extFI.ModTime = time.Unix(mtime.Unix())
|
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
|
extFI.ChangeTime = extFI.ModTime
|
||||||
|
|
||||||
return extFI
|
return extFI
|
||||||
|
Loading…
Reference in New Issue
Block a user