From 2f8e6e8875e1fdb6d128ddcd679d723ee983a549 Mon Sep 17 00:00:00 2001 From: Felicitas Pojtinger Date: Sun, 16 Jan 2022 01:18:45 +0100 Subject: [PATCH] feat: Add portable `Timespec` implementation for `FileInfo` --- internal/fs/file.go | 10 +++++- internal/fs/file_non_unix.go | 8 ----- internal/fs/file_unix.go | 7 ---- internal/fs/fileinfo.go | 61 +++++++++++++++++++------------- internal/fs/fileinfo_non_unix.go | 52 +++++++++++++++++++++++++++ internal/fs/fileinfo_unix.go | 23 ++++++++++++ 6 files changed, 120 insertions(+), 41 deletions(-) delete mode 100644 internal/fs/file_non_unix.go delete mode 100644 internal/fs/file_unix.go create mode 100644 internal/fs/fileinfo_non_unix.go create mode 100644 internal/fs/fileinfo_unix.go diff --git a/internal/fs/file.go b/internal/fs/file.go index eba5752..a1922a6 100644 --- a/internal/fs/file.go +++ b/internal/fs/file.go @@ -7,6 +7,7 @@ import ( "io/fs" "os" "sync" + "time" "github.com/pojntfx/stfs/internal/ioext" "github.com/pojntfx/stfs/pkg/cache" @@ -124,17 +125,24 @@ func (f *File) syncWithoutLocking() error { // Some OSes like i.e. Windows don't support numeric GIDs and UIDs, so use 0 instead gid := 0 uid := 0 + modTime := f.info.ModTime() + accessTime := f.info.ModTime() + changeTime := f.info.ModTime() sys, ok := f.info.Sys().(*Stat) if ok { gid = int(sys.Gid) uid = int(sys.Uid) + accessTime = time.Unix(0, sys.Atim.Nano()) + changeTime = time.Unix(0, sys.Ctim.Nano()) } f.info = NewFileInfo( f.info.Name(), size, f.info.Mode(), - f.info.ModTime(), + modTime, + accessTime, + changeTime, gid, uid, f.info.IsDir(), diff --git a/internal/fs/file_non_unix.go b/internal/fs/file_non_unix.go deleted file mode 100644 index de18407..0000000 --- a/internal/fs/file_non_unix.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build windows - -package fs - -type Stat struct { - Uid uint32 - Gid uint32 -} diff --git a/internal/fs/file_unix.go b/internal/fs/file_unix.go deleted file mode 100644 index 9ec31e9..0000000 --- a/internal/fs/file_unix.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !windows - -package fs - -import "syscall" - -type Stat syscall.Stat_t diff --git a/internal/fs/fileinfo.go b/internal/fs/fileinfo.go index 26fbc0c..4fc618b 100644 --- a/internal/fs/fileinfo.go +++ b/internal/fs/fileinfo.go @@ -12,13 +12,15 @@ import ( type FileInfo struct { os.FileInfo - name string - size int64 - mode fs.FileMode - modTime time.Time - gid int - uid int - isDir bool + name string + size int64 + mode fs.FileMode + modTime time.Time + accessTime time.Time + changeTime time.Time + gid int + uid int + isDir bool log logging.StructuredLogger } @@ -28,6 +30,8 @@ func NewFileInfo( size int64, mode fs.FileMode, modTime time.Time, + accessTime time.Time, + changeTime time.Time, gid int, uid int, isDir bool, @@ -35,13 +39,15 @@ func NewFileInfo( log logging.StructuredLogger, ) *FileInfo { return &FileInfo{ - name: name, - size: size, - mode: mode, - modTime: modTime, - gid: gid, - uid: uid, - isDir: isDir, + name: name, + size: size, + mode: mode, + modTime: modTime, + accessTime: accessTime, + changeTime: changeTime, + gid: gid, + uid: uid, + isDir: isDir, log: log, } @@ -53,13 +59,15 @@ func NewFileInfoFromTarHeader( log logging.StructuredLogger, ) *FileInfo { return &FileInfo{ - name: hdr.FileInfo().Name(), - size: hdr.FileInfo().Size(), - mode: hdr.FileInfo().Mode(), - modTime: hdr.FileInfo().ModTime(), - gid: hdr.Gid, - uid: hdr.Uid, - isDir: hdr.FileInfo().IsDir(), + name: hdr.FileInfo().Name(), + size: hdr.FileInfo().Size(), + mode: hdr.FileInfo().Mode(), + modTime: hdr.FileInfo().ModTime(), + accessTime: hdr.AccessTime, + changeTime: hdr.ChangeTime, + gid: hdr.Gid, + uid: hdr.Uid, + isDir: hdr.FileInfo().IsDir(), log: log, } @@ -110,8 +118,11 @@ func (f *FileInfo) Sys() interface{} { "name": f.name, }) - return &Stat{ - Uid: uint32(f.uid), - Gid: uint32(f.gid), - } + return NewStat( + uint32(f.uid), + uint32(f.gid), + f.modTime.UnixNano(), + f.accessTime.UnixNano(), + f.changeTime.UnixNano(), + ) } diff --git a/internal/fs/fileinfo_non_unix.go b/internal/fs/fileinfo_non_unix.go new file mode 100644 index 0000000..6c67060 --- /dev/null +++ b/internal/fs/fileinfo_non_unix.go @@ -0,0 +1,52 @@ +//go:build !linux + +package fs + +// From the Go stdlib for linux/amd64 +type Timespec struct { + Sec int64 + Nsec int64 +} + +func (ts *Timespec) Unix() (sec int64, nsec int64) { + return int64(ts.Sec), int64(ts.Nsec) +} + +func (ts *Timespec) Nano() int64 { + return int64(ts.Sec)*1e9 + int64(ts.Nsec) +} + +func NsecToTimespec(nsec int64) Timespec { + sec := nsec / 1e9 + nsec = nsec % 1e9 + if nsec < 0 { + nsec += 1e9 + sec-- + } + + return Timespec{Sec: sec, Nsec: nsec} +} + +type Stat struct { + Uid uint32 + Gid uint32 + Mtim Timespec + Atim Timespec + Ctim Timespec +} + +func NewStat( + uid uint32, + gid uint32, + mtim int64, + atim int64, + ctim int64, +) *Stat { + return &Stat{ + Uid: uid, + Gid: gid, + Mtim: NsecToTimespec(mtim), + Atim: NsecToTimespec(atim), + Ctim: NsecToTimespec(ctim), + } +} diff --git a/internal/fs/fileinfo_unix.go b/internal/fs/fileinfo_unix.go new file mode 100644 index 0000000..ec669c6 --- /dev/null +++ b/internal/fs/fileinfo_unix.go @@ -0,0 +1,23 @@ +//go:build linux + +package fs + +import "syscall" + +type Stat syscall.Stat_t + +func NewStat( + uid uint32, + gid uint32, + mtim int64, + atim int64, + ctim int64, +) *Stat { + return &Stat{ + Uid: uid, + Gid: gid, + Mtim: syscall.NsecToTimespec(mtim), + Atim: syscall.NsecToTimespec(atim), + Ctim: syscall.NsecToTimespec(ctim), + } +}