mirror of
https://github.com/restic/restic.git
synced 2024-12-21 23:33:03 +00:00
fs: remove os.FileInfo from fs.ExtendedFileInfo
Only the `Sys()` value from os.FileInfo is kept as field `sys` to support Windows. The os.FileInfo removal ensures that for values like `ModTime` that existed in both data structures there's no more confusion which value is actually used.
This commit is contained in:
parent
847b2efba2
commit
9a99141a5f
17 changed files with 80 additions and 178 deletions
|
@ -505,7 +505,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
|
|||
}
|
||||
|
||||
switch {
|
||||
case fi.Mode().IsRegular():
|
||||
case fi.Mode.IsRegular():
|
||||
debug.Log(" %v regular file", target)
|
||||
|
||||
// check if the file has not changed before performing a fopen operation (more expensive, specially
|
||||
|
@ -555,7 +555,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
|
|||
}
|
||||
|
||||
// make sure it's still a file
|
||||
if !fi.Mode().IsRegular() {
|
||||
if !fi.Mode.IsRegular() {
|
||||
err = errors.Errorf("file %q changed type, refusing to archive", target)
|
||||
return filterError(err)
|
||||
}
|
||||
|
@ -571,7 +571,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
|
|||
arch.trackItem(snPath, previous, node, stats, time.Since(start))
|
||||
})
|
||||
|
||||
case fi.IsDir():
|
||||
case fi.Mode.IsDir():
|
||||
debug.Log(" %v dir", target)
|
||||
|
||||
snItem := snPath + "/"
|
||||
|
@ -592,7 +592,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
|
|||
return futureNode{}, false, err
|
||||
}
|
||||
|
||||
case fi.Mode()&os.ModeSocket > 0:
|
||||
case fi.Mode&os.ModeSocket > 0:
|
||||
debug.Log(" %v is a socket, ignoring", target)
|
||||
return futureNode{}, true, nil
|
||||
|
||||
|
|
|
@ -2303,19 +2303,26 @@ func TestMetadataChanged(t *testing.T) {
|
|||
t.Fatalf("metadata does not match:\n%v", cmp.Diff(want, node2))
|
||||
}
|
||||
|
||||
// modify the mode by wrapping it in a new struct, uses the consts defined above
|
||||
fs.overrideFI = wrapFileInfo(fi)
|
||||
// modify the mode and UID/GID
|
||||
modFI := *fi
|
||||
modFI.Mode = mockFileInfoMode
|
||||
if runtime.GOOS != "windows" {
|
||||
modFI.UID = mockFileInfoUID
|
||||
modFI.GID = mockFileInfoGID
|
||||
}
|
||||
|
||||
fs.overrideFI = &modFI
|
||||
rtest.Assert(t, !fileChanged(fs.overrideFI, node2, 0), "testfile must not be considered as changed")
|
||||
|
||||
// set the override values in the 'want' node which
|
||||
want.Mode = 0400
|
||||
want.Mode = mockFileInfoMode
|
||||
// ignore UID and GID on Windows
|
||||
if runtime.GOOS != "windows" {
|
||||
want.UID = 51234
|
||||
want.GID = 51235
|
||||
want.UID = mockFileInfoUID
|
||||
want.GID = mockFileInfoGID
|
||||
}
|
||||
// update mock node accordingly
|
||||
fs.overrideNode.Mode = 0400
|
||||
fs.overrideNode.Mode = want.Mode
|
||||
fs.overrideNode.UID = want.UID
|
||||
fs.overrideNode.GID = want.GID
|
||||
|
||||
|
@ -2456,10 +2463,12 @@ func TestIrregularFile(t *testing.T) {
|
|||
|
||||
tempfile := filepath.Join(tempdir, "testfile")
|
||||
fi := lstat(t, "testfile")
|
||||
// patch mode to irregular
|
||||
fi.Mode = (fi.Mode &^ os.ModeType) | os.ModeIrregular
|
||||
|
||||
override := &overrideFS{
|
||||
FS: fs.Local{},
|
||||
overrideFI: wrapIrregularFileInfo(fi),
|
||||
overrideFI: fi,
|
||||
overrideNode: &restic.Node{
|
||||
Type: restic.NodeTypeIrregular,
|
||||
},
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
package archiver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/restic/restic/internal/feature"
|
||||
|
@ -14,48 +12,6 @@ import (
|
|||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
type wrappedFileInfo struct {
|
||||
os.FileInfo
|
||||
sys interface{}
|
||||
mode os.FileMode
|
||||
}
|
||||
|
||||
func (fi wrappedFileInfo) Sys() interface{} {
|
||||
return fi.sys
|
||||
}
|
||||
|
||||
func (fi wrappedFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
|
||||
// wrapFileInfo returns a new os.FileInfo with the mode, owner, and group fields changed.
|
||||
func wrapFileInfo(fi *fs.ExtendedFileInfo) *fs.ExtendedFileInfo {
|
||||
// get the underlying stat_t and modify the values
|
||||
stat := fi.Sys().(*syscall.Stat_t)
|
||||
stat.Mode = mockFileInfoMode
|
||||
stat.Uid = mockFileInfoUID
|
||||
stat.Gid = mockFileInfoGID
|
||||
|
||||
// wrap the os.FileInfo so we can return a modified stat_t
|
||||
return fs.ExtendedStat(wrappedFileInfo{
|
||||
FileInfo: fi.FileInfo,
|
||||
sys: stat,
|
||||
mode: mockFileInfoMode,
|
||||
})
|
||||
}
|
||||
|
||||
// wrapIrregularFileInfo returns a new os.FileInfo with the mode changed to irregular file
|
||||
func wrapIrregularFileInfo(fi *fs.ExtendedFileInfo) *fs.ExtendedFileInfo {
|
||||
// wrap the os.FileInfo so we can return a modified stat_t
|
||||
return &fs.ExtendedFileInfo{
|
||||
FileInfo: wrappedFileInfo{
|
||||
FileInfo: fi.FileInfo,
|
||||
sys: fi.Sys(),
|
||||
mode: (fi.Mode() &^ os.ModeType) | os.ModeIrregular,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func statAndSnapshot(t *testing.T, repo archiverRepo, name string) (*restic.Node, *restic.Node) {
|
||||
want := nodeFromFile(t, &fs.Local{}, name)
|
||||
_, node := snapshot(t, repo, &fs.Local{}, nil, name)
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package archiver
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/restic/restic/internal/fs"
|
||||
)
|
||||
|
||||
type wrappedFileInfo struct {
|
||||
os.FileInfo
|
||||
mode os.FileMode
|
||||
}
|
||||
|
||||
func (fi wrappedFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
|
||||
// wrapFileInfo returns a new os.FileInfo with the mode, owner, and group fields changed.
|
||||
func wrapFileInfo(fi *fs.ExtendedFileInfo) *fs.ExtendedFileInfo {
|
||||
// wrap the os.FileInfo and return the modified mode, uid and gid are ignored on Windows
|
||||
return fs.ExtendedStat(wrappedFileInfo{
|
||||
FileInfo: fi.FileInfo,
|
||||
mode: mockFileInfoMode,
|
||||
})
|
||||
}
|
||||
|
||||
// wrapIrregularFileInfo returns a new os.FileInfo with the mode changed to irregular file
|
||||
func wrapIrregularFileInfo(fi *fs.ExtendedFileInfo) *fs.ExtendedFileInfo {
|
||||
return &fs.ExtendedFileInfo{
|
||||
FileInfo: wrappedFileInfo{
|
||||
FileInfo: fi.FileInfo,
|
||||
mode: (fi.Mode() &^ os.ModeType) | os.ModeIrregular,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -267,7 +267,7 @@ func RejectByDevice(samples []string, filesystem fs.FS) (RejectFunc, error) {
|
|||
}
|
||||
|
||||
// reject everything except directories
|
||||
if !fi.IsDir() {
|
||||
if !fi.Mode.IsDir() {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -303,7 +303,7 @@ func RejectByDevice(samples []string, filesystem fs.FS) (RejectFunc, error) {
|
|||
func RejectBySize(maxSize int64) (RejectFunc, error) {
|
||||
return func(item string, fi *fs.ExtendedFileInfo, _ fs.FS) bool {
|
||||
// directory will be ignored
|
||||
if fi.IsDir() {
|
||||
if fi.Mode.IsDir() {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -118,10 +118,10 @@ func (s *Scanner) scan(ctx context.Context, stats ScanStats, target string) (Sca
|
|||
}
|
||||
|
||||
switch {
|
||||
case fi.Mode().IsRegular():
|
||||
case fi.Mode.IsRegular():
|
||||
stats.Files++
|
||||
stats.Bytes += uint64(fi.Size)
|
||||
case fi.Mode().IsDir():
|
||||
case fi.Mode.IsDir():
|
||||
names, err := fs.Readdirnames(s.FS, target, fs.O_NOFOLLOW)
|
||||
if err != nil {
|
||||
return stats, s.Error(target, err)
|
||||
|
|
|
@ -57,7 +57,7 @@ func TestScanner(t *testing.T) {
|
|||
},
|
||||
},
|
||||
selFn: func(item string, fi *fs.ExtendedFileInfo, fs fs.FS) bool {
|
||||
if fi.IsDir() {
|
||||
if fi.Mode.IsDir() {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ func checkMetadata(t *testing.T, f File, path string, follow bool, nodeType rest
|
|||
fi2, err = os.Lstat(path)
|
||||
}
|
||||
rtest.OK(t, err)
|
||||
assertFIEqual(t, fi2, fi.FileInfo)
|
||||
assertFIEqual(t, fi2, fi)
|
||||
|
||||
node, err := f.ToNode(false)
|
||||
rtest.OK(t, err)
|
||||
|
@ -94,13 +94,12 @@ func checkMetadata(t *testing.T, f File, path string, follow bool, nodeType rest
|
|||
rtest.Equals(t, nodeType, node.Type, "node Type")
|
||||
}
|
||||
|
||||
func assertFIEqual(t *testing.T, want os.FileInfo, got os.FileInfo) {
|
||||
func assertFIEqual(t *testing.T, want os.FileInfo, got *ExtendedFileInfo) {
|
||||
t.Helper()
|
||||
rtest.Equals(t, want.Name(), got.Name(), "Name")
|
||||
rtest.Equals(t, want.IsDir(), got.IsDir(), "IsDir")
|
||||
rtest.Equals(t, want.ModTime(), got.ModTime(), "ModTime")
|
||||
rtest.Equals(t, want.Mode(), got.Mode(), "Mode")
|
||||
rtest.Equals(t, want.Size(), got.Size(), "Size")
|
||||
rtest.Equals(t, want.Name(), got.Name, "Name")
|
||||
rtest.Equals(t, want.ModTime(), got.ModTime, "ModTime")
|
||||
rtest.Equals(t, want.Mode(), got.Mode, "Mode")
|
||||
rtest.Equals(t, want.Size(), got.Size, "Size")
|
||||
}
|
||||
|
||||
func TestFSLocalRead(t *testing.T) {
|
||||
|
@ -206,7 +205,7 @@ func TestFSLocalTypeChange(t *testing.T) {
|
|||
|
||||
fi, err := f.Stat()
|
||||
rtest.OK(t, err)
|
||||
if !fi.IsDir() {
|
||||
if !fi.Mode.IsDir() {
|
||||
// a file handle based implementation should still reference the file
|
||||
checkMetadata(t, f, pathNew, false, restic.NodeTypeFile)
|
||||
|
||||
|
|
|
@ -325,7 +325,7 @@ func TestVSSFS(t *testing.T) {
|
|||
|
||||
lstatFi, err := localVss.Lstat(tempfile)
|
||||
rtest.OK(t, err)
|
||||
rtest.Equals(t, origFi.Mode(), lstatFi.Mode())
|
||||
rtest.Equals(t, origFi.Mode, lstatFi.Mode)
|
||||
|
||||
f, err := localVss.OpenFile(tempfile, os.O_RDONLY, false)
|
||||
rtest.OK(t, err)
|
||||
|
@ -335,7 +335,7 @@ func TestVSSFS(t *testing.T) {
|
|||
|
||||
node, err := f.ToNode(false)
|
||||
rtest.OK(t, err)
|
||||
rtest.Equals(t, node.Mode, lstatFi.Mode())
|
||||
rtest.Equals(t, node.Mode, lstatFi.Mode)
|
||||
|
||||
rtest.OK(t, f.Close())
|
||||
}
|
||||
|
|
|
@ -43,12 +43,10 @@ func (fs *Reader) VolumeName(_ string) string {
|
|||
|
||||
func (fs *Reader) fi() *ExtendedFileInfo {
|
||||
return &ExtendedFileInfo{
|
||||
FileInfo: fakeFileInfo{
|
||||
name: fs.Name,
|
||||
size: fs.Size,
|
||||
mode: fs.Mode,
|
||||
modtime: fs.ModTime,
|
||||
},
|
||||
Name: fs.Name,
|
||||
Mode: fs.Mode,
|
||||
ModTime: fs.ModTime,
|
||||
Size: fs.Size,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,7 +69,7 @@ func (fs *Reader) OpenFile(name string, flag int, _ bool) (f File, err error) {
|
|||
return f, nil
|
||||
case "/", ".":
|
||||
f = fakeDir{
|
||||
entries: []string{fs.fi().Name()},
|
||||
entries: []string{fs.fi().Name},
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
@ -85,13 +83,12 @@ func (fs *Reader) OpenFile(name string, flag int, _ bool) (f File, err error) {
|
|||
// If there is an error, it will be of type *os.PathError.
|
||||
func (fs *Reader) Lstat(name string) (*ExtendedFileInfo, error) {
|
||||
getDirInfo := func(name string) *ExtendedFileInfo {
|
||||
fi := fakeFileInfo{
|
||||
name: fs.Base(name),
|
||||
size: 0,
|
||||
mode: os.ModeDir | 0755,
|
||||
modtime: time.Now(),
|
||||
return &ExtendedFileInfo{
|
||||
Name: fs.Base(name),
|
||||
Size: 0,
|
||||
Mode: os.ModeDir | 0755,
|
||||
ModTime: time.Now(),
|
||||
}
|
||||
return &ExtendedFileInfo{FileInfo: fi}
|
||||
}
|
||||
|
||||
switch name {
|
||||
|
@ -164,7 +161,7 @@ func newReaderFile(rd io.ReadCloser, fi *ExtendedFileInfo, allowEmptyFile bool)
|
|||
AllowEmptyFile: allowEmptyFile,
|
||||
fakeFile: fakeFile{
|
||||
fi: fi,
|
||||
name: fi.Name(),
|
||||
name: fi.Name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +230,7 @@ func (f fakeFile) Stat() (*ExtendedFileInfo, error) {
|
|||
}
|
||||
|
||||
func (f fakeFile) ToNode(_ bool) (*restic.Node, error) {
|
||||
node := buildBasicNode(f.name, f.fi.FileInfo)
|
||||
node := buildBasicNode(f.name, f.fi)
|
||||
|
||||
// fill minimal info with current values for uid, gid
|
||||
node.UID = uint32(os.Getuid())
|
||||
|
@ -256,38 +253,6 @@ func (d fakeDir) Readdirnames(n int) ([]string, error) {
|
|||
return slices.Clone(d.entries), nil
|
||||
}
|
||||
|
||||
// fakeFileInfo implements the bare minimum of os.FileInfo.
|
||||
type fakeFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modtime time.Time
|
||||
}
|
||||
|
||||
func (fi fakeFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
|
||||
func (fi fakeFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
|
||||
func (fi fakeFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
|
||||
func (fi fakeFileInfo) ModTime() time.Time {
|
||||
return fi.modtime
|
||||
}
|
||||
|
||||
func (fi fakeFileInfo) IsDir() bool {
|
||||
return fi.mode&os.ModeDir > 0
|
||||
}
|
||||
|
||||
func (fi fakeFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func pathError(op, name string, err error) *os.PathError {
|
||||
return &os.PathError{Op: op, Path: name, Err: err}
|
||||
}
|
||||
|
|
|
@ -61,24 +61,24 @@ func verifyDirectoryContents(t testing.TB, fs FS, dir string, want []string) {
|
|||
}
|
||||
|
||||
func checkFileInfo(t testing.TB, fi *ExtendedFileInfo, filename string, modtime time.Time, mode os.FileMode, isdir bool) {
|
||||
if fi.IsDir() != isdir {
|
||||
t.Errorf("IsDir returned %t, want %t", fi.IsDir(), isdir)
|
||||
if fi.Mode.IsDir() != isdir {
|
||||
t.Errorf("IsDir returned %t, want %t", fi.Mode.IsDir(), isdir)
|
||||
}
|
||||
|
||||
if fi.Mode() != mode {
|
||||
t.Errorf("Mode() returned wrong value, want 0%o, got 0%o", mode, fi.Mode())
|
||||
if fi.Mode != mode {
|
||||
t.Errorf("Mode has wrong value, want 0%o, got 0%o", mode, fi.Mode)
|
||||
}
|
||||
|
||||
if !modtime.Equal(time.Time{}) && !fi.FileInfo.ModTime().Equal(modtime) {
|
||||
t.Errorf("ModTime() returned wrong value, want %v, got %v", modtime, fi.FileInfo.ModTime())
|
||||
if !modtime.Equal(time.Time{}) && !fi.ModTime.Equal(modtime) {
|
||||
t.Errorf("ModTime has wrong value, want %v, got %v", modtime, fi.ModTime)
|
||||
}
|
||||
|
||||
if path.Base(fi.Name()) != fi.Name() {
|
||||
t.Errorf("Name() returned is not base, want %q, got %q", path.Base(fi.Name()), fi.Name())
|
||||
if path.Base(fi.Name) != fi.Name {
|
||||
t.Errorf("Name is not base, want %q, got %q", path.Base(fi.Name), fi.Name)
|
||||
}
|
||||
|
||||
if fi.Name() != path.Base(filename) {
|
||||
t.Errorf("Name() returned wrong value, want %q, got %q", path.Base(filename), fi.Name())
|
||||
if fi.Name != path.Base(filename) {
|
||||
t.Errorf("Name has wrong value, want %q, got %q", path.Base(filename), fi.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
// 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) (*restic.Node, error) {
|
||||
node := buildBasicNode(path, fi.FileInfo)
|
||||
node := buildBasicNode(path, fi)
|
||||
|
||||
if err := nodeFillExtendedStat(node, path, fi); err != nil {
|
||||
return node, err
|
||||
|
@ -27,18 +27,18 @@ func nodeFromFileInfo(path string, fi *ExtendedFileInfo, ignoreXattrListError bo
|
|||
return node, err
|
||||
}
|
||||
|
||||
func buildBasicNode(path string, fi os.FileInfo) *restic.Node {
|
||||
func buildBasicNode(path string, fi *ExtendedFileInfo) *restic.Node {
|
||||
mask := os.ModePerm | os.ModeType | os.ModeSetuid | os.ModeSetgid | os.ModeSticky
|
||||
node := &restic.Node{
|
||||
Path: path,
|
||||
Name: fi.Name(),
|
||||
Mode: fi.Mode() & mask,
|
||||
ModTime: fi.ModTime(),
|
||||
Name: fi.Name,
|
||||
Mode: fi.Mode & mask,
|
||||
ModTime: fi.ModTime,
|
||||
}
|
||||
|
||||
node.Type = nodeTypeFromFileInfo(fi.Mode())
|
||||
node.Type = nodeTypeFromFileInfo(fi.Mode)
|
||||
if node.Type == restic.NodeTypeFile {
|
||||
node.Size = uint64(fi.Size())
|
||||
node.Size = uint64(fi.Size)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
|
|
@ -361,7 +361,7 @@ func nodeFillGenericAttributes(node *restic.Node, path string, stat *ExtendedFil
|
|||
}
|
||||
}
|
||||
|
||||
winFI := stat.Sys().(*syscall.Win32FileAttributeData)
|
||||
winFI := stat.sys.(*syscall.Win32FileAttributeData)
|
||||
|
||||
// Add Windows attributes
|
||||
node.GenericAttributes, err = restic.WindowsAttrsToGenericAttributes(restic.WindowsAttributes{
|
||||
|
|
|
@ -8,7 +8,8 @@ import (
|
|||
// ExtendedFileInfo is an extended stat_t, filled with attributes that are
|
||||
// supported by most operating systems. The original FileInfo is embedded.
|
||||
type ExtendedFileInfo struct {
|
||||
os.FileInfo
|
||||
Name string
|
||||
Mode os.FileMode
|
||||
|
||||
DeviceID uint64 // ID of device containing the file
|
||||
Inode uint64 // Inode number
|
||||
|
@ -23,6 +24,9 @@ type ExtendedFileInfo struct {
|
|||
AccessTime time.Time // last access time stamp
|
||||
ModTime time.Time // last (content) modification time stamp
|
||||
ChangeTime time.Time // last status change time stamp
|
||||
|
||||
// nolint:unused // only used on Windows
|
||||
sys any // Value returned by os.FileInfo.Sys()
|
||||
}
|
||||
|
||||
// ExtendedStat returns an ExtendedFileInfo constructed from the os.FileInfo.
|
||||
|
|
|
@ -14,7 +14,9 @@ func extendedStat(fi os.FileInfo) *ExtendedFileInfo {
|
|||
s := fi.Sys().(*syscall.Stat_t)
|
||||
|
||||
return &ExtendedFileInfo{
|
||||
FileInfo: fi,
|
||||
Name: fi.Name(),
|
||||
Mode: fi.Mode(),
|
||||
|
||||
DeviceID: uint64(s.Dev),
|
||||
Inode: uint64(s.Ino),
|
||||
Links: uint64(s.Nlink),
|
||||
|
|
|
@ -14,7 +14,9 @@ func extendedStat(fi os.FileInfo) *ExtendedFileInfo {
|
|||
s := fi.Sys().(*syscall.Stat_t)
|
||||
|
||||
return &ExtendedFileInfo{
|
||||
FileInfo: fi,
|
||||
Name: fi.Name(),
|
||||
Mode: fi.Mode(),
|
||||
|
||||
DeviceID: uint64(s.Dev),
|
||||
Inode: s.Ino,
|
||||
Links: uint64(s.Nlink),
|
||||
|
|
|
@ -18,8 +18,11 @@ func extendedStat(fi os.FileInfo) *ExtendedFileInfo {
|
|||
}
|
||||
|
||||
extFI := ExtendedFileInfo{
|
||||
FileInfo: fi,
|
||||
Name: fi.Name(),
|
||||
Mode: fi.Mode(),
|
||||
|
||||
Size: int64(s.FileSizeLow) | (int64(s.FileSizeHigh) << 32),
|
||||
sys: fi.Sys(),
|
||||
}
|
||||
|
||||
atime := syscall.NsecToTimespec(s.LastAccessTime.Nanoseconds())
|
||||
|
|
Loading…
Reference in a new issue