diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2026-01-09 14:39:39 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2026-01-09 16:55:27 +0000 |
| commit | bc54aa9fe40d6d1ffa6f80a1e04a18689ddbc54c (patch) | |
| tree | 471210fbf516a516aaa4e15e3bbb99d037fb2177 /pkg | |
| parent | 36eb9783384b89dffb86e2eed340838f1b3ff1c1 (diff) | |
pkg/osutil: fix CreationTime
We return Ctime from CreationTime. But "C" does not stand for "creation",
it stands for "status change" (inode update). It may or may not be the
creation time.
Use Btime (birth time) for creation time.
Fixes #6547
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/manager/crash.go | 4 | ||||
| -rw-r--r-- | pkg/osutil/osutil.go | 7 | ||||
| -rw-r--r-- | pkg/osutil/osutil_linux.go | 17 | ||||
| -rw-r--r-- | pkg/osutil/osutil_nonlinux.go | 10 |
4 files changed, 26 insertions, 12 deletions
diff --git a/pkg/manager/crash.go b/pkg/manager/crash.go index a62ec8280..e537cdf60 100644 --- a/pkg/manager/crash.go +++ b/pkg/manager/crash.go @@ -235,7 +235,7 @@ func (cs *CrashStore) BugInfo(id string, full bool) (*BugInfo, error) { if err != nil { return nil, err } - stat, err := os.Stat(filepath.Join(dir, "description")) + ret.FirstTime, ret.LastTime, err = osutil.FileTimes(filepath.Join(dir, "description")) if err != nil { return nil, err } @@ -250,8 +250,6 @@ func (cs *CrashStore) BugInfo(id string, full bool) (*BugInfo, error) { } } - ret.FirstTime = osutil.CreationTime(stat) - ret.LastTime = stat.ModTime() files, err := osutil.ListDir(dir) if err != nil { return nil, err diff --git a/pkg/osutil/osutil.go b/pkg/osutil/osutil.go index fa963a3fb..d56682732 100644 --- a/pkg/osutil/osutil.go +++ b/pkg/osutil/osutil.go @@ -351,10 +351,9 @@ func Abs(path string) string { return filepath.Clean(path) } -// CreationTime returns file creation time. -// May return zero time, if not known. -func CreationTime(fi os.FileInfo) time.Time { - return creationTime(fi) +// FileTimes returns file creation and modification times. +func FileTimes(file string) (time.Time, time.Time, error) { + return fileTimes(file) } // MonotonicNano returns monotonic time in nanoseconds from some unspecified point in time. diff --git a/pkg/osutil/osutil_linux.go b/pkg/osutil/osutil_linux.go index 50be9b047..5ab639280 100644 --- a/pkg/osutil/osutil_linux.go +++ b/pkg/osutil/osutil_linux.go @@ -18,9 +18,20 @@ import ( "golang.org/x/sys/unix" ) -func creationTime(fi os.FileInfo) time.Time { - st := fi.Sys().(*syscall.Stat_t) - return time.Unix(int64(st.Ctim.Sec), int64(st.Ctim.Nsec)) // nolint: unconvert +func fileTimes(file string) (time.Time, time.Time, error) { + // Btime stands for "birth" time, which is creation time. + var statx unix.Statx_t + err := unix.Statx(unix.AT_FDCWD, file, unix.AT_SYMLINK_NOFOLLOW, unix.STATX_BTIME|unix.STATX_MTIME, &statx) + if err != nil { + return time.Time{}, time.Time{}, err + } + modTime := time.Unix(statx.Mtime.Sec, int64(statx.Mtime.Nsec)) + // Some filesystems may not store the birth time. + creationTime := modTime + if statx.Mask&unix.STATX_BTIME != 0 { + creationTime = time.Unix(statx.Btime.Sec, int64(statx.Btime.Nsec)) + } + return creationTime, modTime, nil } // RemoveAll is similar to os.RemoveAll, but can handle more cases. diff --git a/pkg/osutil/osutil_nonlinux.go b/pkg/osutil/osutil_nonlinux.go index 1d3ee8b82..bc696d3fc 100644 --- a/pkg/osutil/osutil_nonlinux.go +++ b/pkg/osutil/osutil_nonlinux.go @@ -12,8 +12,14 @@ import ( "time" ) -func creationTime(fi os.FileInfo) time.Time { - return time.Time{} +func fileTimes(file string) (time.Time, time.Time, error) { + stat, err := os.Stat(file) + if err != nil { + return time.Time{}, time.Time{}, err + } + // Creation time is not present in stat, so we use modification time for both. + modTime := stat.ModTime() + return modTime, modTime, nil } func RemoveAll(dir string) error { |
