aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2026-01-09 14:39:39 +0100
committerDmitry Vyukov <dvyukov@google.com>2026-01-09 16:55:27 +0000
commitbc54aa9fe40d6d1ffa6f80a1e04a18689ddbc54c (patch)
tree471210fbf516a516aaa4e15e3bbb99d037fb2177 /pkg
parent36eb9783384b89dffb86e2eed340838f1b3ff1c1 (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.go4
-rw-r--r--pkg/osutil/osutil.go7
-rw-r--r--pkg/osutil/osutil_linux.go17
-rw-r--r--pkg/osutil/osutil_nonlinux.go10
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 {