mirror of
https://github.com/restic/restic.git
synced 2025-03-30 00:00:14 +01:00

This gives us access to additional information, notably the birth time of files on btrfs.
104 lines
2.6 KiB
Go
104 lines
2.6 KiB
Go
package fs
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// On Linux, we reimplement Stat and Lstat in terms of unix.Statx,
|
|
// which gives access to some interesting information that Stat doesn't provide.
|
|
|
|
func (fs Local) Stat(name string) (os.FileInfo, error) {
|
|
return statx(name, 0)
|
|
}
|
|
|
|
func (fs Local) Lstat(name string) (os.FileInfo, error) {
|
|
return statx(name, unix.AT_SYMLINK_NOFOLLOW)
|
|
}
|
|
|
|
func statx(name string, flags int) (*statxFileInfo, error) {
|
|
const mask = unix.STATX_BASIC_STATS | unix.STATX_BTIME
|
|
fi := &statxFileInfo{}
|
|
// XXX We could pick FORCE_SYNC or DONT_SYNC instead of SYNC_AS_STAT,
|
|
// to influence the behavior when dealing with remote filesystems.
|
|
err := unix.Statx(unix.AT_FDCWD, name, flags|unix.AT_STATX_SYNC_AS_STAT, mask, &fi.st)
|
|
if err != nil {
|
|
return nil, &os.PathError{Path: name, Op: "statx", Err: err}
|
|
}
|
|
|
|
fi.name = filepath.Base(name)
|
|
return fi, nil
|
|
}
|
|
|
|
type statxFileInfo struct {
|
|
name string
|
|
st unix.Statx_t
|
|
}
|
|
|
|
func (fi *statxFileInfo) Name() string { return fi.name }
|
|
func (fi *statxFileInfo) Size() int64 { return int64(fi.st.Size) }
|
|
func (fi *statxFileInfo) ModTime() time.Time { return timeFromStatx(fi.st.Mtime) }
|
|
func (fi *statxFileInfo) IsDir() bool { return fi.Mode().IsDir() }
|
|
func (fi *statxFileInfo) Sys() any { return &fi.st }
|
|
|
|
// Adapted from os/stat_linux.go in the Go stdlib.
|
|
func (fi *statxFileInfo) Mode() os.FileMode {
|
|
mode := os.FileMode(fi.st.Mode & 0o777)
|
|
|
|
switch fi.st.Mode & unix.S_IFMT {
|
|
case unix.S_IFBLK:
|
|
mode |= os.ModeDevice
|
|
case unix.S_IFCHR:
|
|
mode |= os.ModeDevice | os.ModeCharDevice
|
|
case unix.S_IFDIR:
|
|
mode |= os.ModeDir
|
|
case unix.S_IFIFO:
|
|
mode |= os.ModeNamedPipe
|
|
case unix.S_IFLNK:
|
|
mode |= os.ModeSymlink
|
|
case unix.S_IFREG:
|
|
// nothing to do
|
|
case unix.S_IFSOCK:
|
|
mode |= os.ModeSocket
|
|
}
|
|
|
|
if fi.st.Mode&unix.S_ISGID != 0 {
|
|
mode |= os.ModeSetgid
|
|
}
|
|
if fi.st.Mode&unix.S_ISUID != 0 {
|
|
mode |= os.ModeSetuid
|
|
}
|
|
if fi.st.Mode&unix.S_ISVTX != 0 {
|
|
mode |= os.ModeSticky
|
|
}
|
|
|
|
return mode
|
|
}
|
|
|
|
func extendedStat(fi os.FileInfo) ExtendedFileInfo {
|
|
s := fi.Sys().(*unix.Statx_t)
|
|
|
|
return ExtendedFileInfo{
|
|
FileInfo: fi,
|
|
DeviceID: uint64(s.Rdev_major)<<8 | uint64(s.Rdev_minor),
|
|
Inode: s.Ino,
|
|
Links: uint64(s.Nlink),
|
|
UID: s.Uid,
|
|
GID: s.Gid,
|
|
Device: uint64(s.Dev_major)<<8 | uint64(s.Dev_minor),
|
|
BlockSize: int64(s.Blksize),
|
|
Blocks: int64(s.Blocks),
|
|
Size: int64(s.Size),
|
|
|
|
AccessTime: timeFromStatx(s.Atime),
|
|
ModTime: timeFromStatx(s.Mtime),
|
|
ChangeTime: timeFromStatx(s.Ctime),
|
|
}
|
|
}
|
|
|
|
func timeFromStatx(ts unix.StatxTimestamp) time.Time {
|
|
return time.Unix(ts.Sec, int64(ts.Nsec))
|
|
}
|