From d6edae95e96b661fbb81a71e388136a18ad786ae Mon Sep 17 00:00:00 2001 From: Carlo Alberto Ferraris Date: Sun, 9 Jan 2022 08:07:18 +0000 Subject: [PATCH] utimes --- README.md | 2 ++ atomicfile.go | 63 ++++++++++++++++++++++++++++++++---- cmd/atomicfile/atomicfile.go | 17 ++++++++++ 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8e7e746..d0f1668 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ Flags: --perm=PERM File permissions --uid=UID File owner user --gid=GID File owner group + --mtime=MTIME File modification time (RFC 3339) + --atime=ATIME File access time (RFC 3339) Args: Name of the file to create diff --git a/atomicfile.go b/atomicfile.go index 2593568..4f303ba 100644 --- a/atomicfile.go +++ b/atomicfile.go @@ -9,6 +9,8 @@ import ( "path" "strconv" "strings" + "time" + "unsafe" "golang.org/x/sys/unix" ) @@ -83,6 +85,34 @@ func Ownership(uid, gid int) Option { }) } +func ModificationTime(t time.Time) Option { + return optionFunc(func(c *config) error { + if c.mtime != defaultConfig().mtime { + return &werror{"multiple modification times", nil} + } + ts, err := unix.TimeToTimespec(t) + if err != nil { + return &werror{"invalid modification time", err} + } + c.mtime = ts + return nil + }) +} + +func AccessTime(t time.Time) Option { + return optionFunc(func(c *config) error { + if c.atime != defaultConfig().atime { + return &werror{"multiple access times", nil} + } + ts, err := unix.TimeToTimespec(t) + if err != nil { + return &werror{"invalid access time", err} + } + c.atime = ts + return nil + }) +} + // TODO: owner/group, permissions, file times, lock, xattr, fadvise flags, fsync, ... type config struct { @@ -93,16 +123,20 @@ type config struct { name string value []byte } - perm uint32 - uid int - gid int + perm uint32 + uid int + gid int + mtime unix.Timespec + atime unix.Timespec } func defaultConfig() config { return config{ - perm: ^uint32(0), - uid: -1, - gid: -1, + perm: ^uint32(0), + uid: -1, + gid: -1, + mtime: unix.Timespec{Nsec: unix.UTIME_OMIT}, + atime: unix.Timespec{Nsec: unix.UTIME_OMIT}, } } @@ -173,6 +207,14 @@ func Create(filename string, options ...Option) error { return &werror{"setting xattr", err} } } + + if cfg.mtime != defaultConfig().mtime || cfg.atime != defaultConfig().atime { + err := futimens(int(f.Fd()), &[2]unix.Timespec{cfg.atime, cfg.mtime}) + if err != nil { + return &werror{"setting access/modification time", err} + } + } + if cfg.flushData { err := f.Sync() if err != nil { @@ -247,3 +289,12 @@ func guessContentSize(r io.Reader) int64 { } return 0 } + +// https://github.com/golang/go/issues/49699 +func futimens(fd int, times *[2]unix.Timespec) (err error) { + _, _, e1 := unix.Syscall6(unix.SYS_UTIMENSAT, uintptr(fd), 0, uintptr(unsafe.Pointer(times)), 0, 0, 0) + if e1 != 0 { + err = e1 + } + return +} diff --git a/cmd/atomicfile/atomicfile.go b/cmd/atomicfile/atomicfile.go index 6c5a2f8..5bfa515 100644 --- a/cmd/atomicfile/atomicfile.go +++ b/cmd/atomicfile/atomicfile.go @@ -3,6 +3,7 @@ package main import ( "os" "strconv" + "time" "github.com/CAFxX/atomicfile" "gopkg.in/alecthomas/kingpin.v2" @@ -16,6 +17,8 @@ func main() { perm := kingpin.Flag("perm", "File permissions").String() uid := kingpin.Flag("uid", "File owner user").Default("-1").PlaceHolder("UID").Int() gid := kingpin.Flag("gid", "File owner group").Default("-1").PlaceHolder("GID").Int() + mtime := kingpin.Flag("mtime", "File modification time (RFC 3339)").String() + atime := kingpin.Flag("atime", "File access time (RFC 3339)").String() kingpin.Parse() opts := []atomicfile.Option{ @@ -40,6 +43,20 @@ func main() { if *uid != -1 || *gid != -1 { opts = append(opts, atomicfile.Ownership(*uid, *gid)) } + if *mtime != "" { + t, err := time.Parse(time.RFC3339Nano, *mtime) + if err != nil { + fatal(err) + } + opts = append(opts, atomicfile.ModificationTime(t)) + } + if *atime != "" { + t, err := time.Parse(time.RFC3339Nano, *atime) + if err != nil { + fatal(err) + } + opts = append(opts, atomicfile.AccessTime(t)) + } err := atomicfile.Create(*filename, opts...) if err != nil {