const (
AT_REMOVEDIR = 0x1
AT_SYMLINK_NOFOLLOW = 0x1
+ UTIME_OMIT = -0x3
)
--- /dev/null
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package unix
+
+const (
+ // UTIME_OMIT is the sentinel value to indicate that a time value should not
+ // be changed. It is useful for example to indicate for example with UtimesNano
+ // to avoid changing AccessTime or ModifiedTime.
+ // Its value must match syscall/fs_js.go
+ UTIME_OMIT = -0x2
+)
const (
AT_REMOVEDIR = 0x1
AT_SYMLINK_NOFOLLOW = 0x1000
+
+ UTIME_OMIT = -0x2
)
const AT_REMOVEDIR = 0x80
const AT_SYMLINK_NOFOLLOW = 0x0020
+
+const UTIME_OMIT = -0x2
const AT_REMOVEDIR = 0x2
const AT_SYMLINK_NOFOLLOW = 0x1
+
+const UTIME_OMIT = -0x1
AT_REMOVEDIR = 0x800
AT_SYMLINK_NOFOLLOW = 0x200
+ UTIME_OMIT = -0x2
+
unlinkatTrap uintptr = syscall.SYS_UNLINKAT
openatTrap uintptr = syscall.SYS_OPENAT
posixFallocateTrap uintptr = syscall.SYS_POSIX_FALLOCATE
AT_FDCWD = -0x64
AT_REMOVEDIR = 0x200
AT_SYMLINK_NOFOLLOW = 0x100
+
+ UTIME_OMIT = 0x3ffffffe
)
const AT_REMOVEDIR = 0x800
const AT_SYMLINK_NOFOLLOW = 0x200
+
+const UTIME_OMIT = (1 << 30) - 2
const AT_REMOVEDIR = 0x08
const AT_SYMLINK_NOFOLLOW = 0x02
+
+const UTIME_OMIT = -0x1
--- /dev/null
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package unix
+
+const (
+ // UTIME_OMIT is the sentinel value to indicate that a time value should not
+ // be changed. It is useful for example to indicate for example with UtimesNano
+ // to avoid changing AccessTime or ModifiedTime.
+ // Its value must match syscall/fs_wasip1.go
+ UTIME_OMIT = -0x2
+)
// Chtimes changes the access and modification times of the named
// file, similar to the Unix utime() or utimes() functions.
+// A zero time.Time value will leave the corresponding file time unchanged.
//
// The underlying filesystem may truncate or round the values to a
// less precise time unit.
d.Null()
d.Atime = uint32(atime.Unix())
d.Mtime = uint32(mtime.Unix())
+ if atime.IsZero() {
+ d.Atime = 0xFFFFFFFF
+ }
+ if mtime.IsZero() {
+ d.Mtime = 0xFFFFFFFF
+ }
var buf [syscall.STATFIXLEN]byte
n, err := d.Marshal(buf[:])
// Chtimes changes the access and modification times of the named
// file, similar to the Unix utime() or utimes() functions.
+// A zero time.Time value will leave the corresponding file time unchanged.
//
// The underlying filesystem may truncate or round the values to a
// less precise time unit.
// If there is an error, it will be of type *PathError.
func Chtimes(name string, atime time.Time, mtime time.Time) error {
var utimes [2]syscall.Timespec
- utimes[0] = syscall.NsecToTimespec(atime.UnixNano())
- utimes[1] = syscall.NsecToTimespec(mtime.UnixNano())
+ set := func(i int, t time.Time) {
+ if t.IsZero() {
+ utimes[i] = syscall.Timespec{Sec: _UTIME_OMIT, Nsec: _UTIME_OMIT}
+ } else {
+ utimes[i] = syscall.NsecToTimespec(t.UnixNano())
+ }
+ }
+ set(0, atime)
+ set(1, mtime)
if e := syscall.UtimesNano(fixLongPath(name), utimes[0:]); e != nil {
return &PathError{Op: "chtimes", Path: name, Err: e}
}
"syscall"
)
+const _UTIME_OMIT = unix.UTIME_OMIT
+
// fixLongPath is a noop on non-Windows platforms.
func fixLongPath(path string) string {
return path
"unsafe"
)
+const _UTIME_OMIT = 0
+
// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
// can overwrite this data, which could cause the finalizer
testChtimes(t, f.Name())
}
+func TestChtimesWithZeroTimes(t *testing.T) {
+ file := newFile("chtimes-with-zero", t)
+ _, err := file.Write([]byte("hello, world\n"))
+ if err != nil {
+ t.Fatalf("Write: %s", err)
+ }
+ fName := file.Name()
+ defer Remove(file.Name())
+ err = file.Close()
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+ fs, err := Stat(fName)
+ if err != nil {
+ t.Fatal(err)
+ }
+ startAtime := Atime(fs)
+ startMtime := fs.ModTime()
+ switch runtime.GOOS {
+ case "js":
+ startAtime = startAtime.Truncate(time.Second)
+ startMtime = startMtime.Truncate(time.Second)
+ }
+ at0 := startAtime
+ mt0 := startMtime
+ t0 := startMtime.Truncate(time.Second).Add(1 * time.Hour)
+
+ tests := []struct {
+ aTime time.Time
+ mTime time.Time
+ wantATime time.Time
+ wantMTime time.Time
+ }{
+ {
+ aTime: time.Time{},
+ mTime: time.Time{},
+ wantATime: startAtime,
+ wantMTime: startMtime,
+ },
+ {
+ aTime: t0.Add(200 * time.Second),
+ mTime: time.Time{},
+ wantATime: t0.Add(200 * time.Second),
+ wantMTime: startMtime,
+ },
+ {
+ aTime: time.Time{},
+ mTime: t0.Add(100 * time.Second),
+ wantATime: t0.Add(200 * time.Second),
+ wantMTime: t0.Add(100 * time.Second),
+ },
+ {
+ aTime: t0.Add(300 * time.Second),
+ mTime: t0.Add(100 * time.Second),
+ wantATime: t0.Add(300 * time.Second),
+ wantMTime: t0.Add(100 * time.Second),
+ },
+ }
+
+ for _, tt := range tests {
+ // Now change the times accordingly.
+ if err := Chtimes(fName, tt.aTime, tt.mTime); err != nil {
+ t.Error(err)
+ }
+
+ // Finally verify the expectations.
+ fs, err = Stat(fName)
+ if err != nil {
+ t.Error(err)
+ }
+ at0 = Atime(fs)
+ mt0 = fs.ModTime()
+
+ if got, want := at0, tt.wantATime; !got.Equal(want) {
+ errormsg := fmt.Sprintf("AccessTime mismatch with values ATime:%q-MTime:%q\ngot: %q\nwant: %q", tt.aTime, tt.mTime, got, want)
+ switch runtime.GOOS {
+ case "plan9":
+ // Mtime is the time of the last change of
+ // content. Similarly, atime is set whenever
+ // the contents are accessed; also, it is set
+ // whenever mtime is set.
+ case "windows":
+ t.Error(errormsg)
+ default: // unix's
+ if got, want := at0, tt.wantATime; !got.Equal(want) {
+ mounts, err := ReadFile("/bin/mounts")
+ if err != nil {
+ mounts, err = ReadFile("/etc/mtab")
+ }
+ if strings.Contains(string(mounts), "noatime") {
+ t.Log(errormsg)
+ t.Log("A filesystem is mounted with noatime; ignoring.")
+ } else {
+ switch runtime.GOOS {
+ case "netbsd", "dragonfly":
+ // On a 64-bit implementation, birth time is generally supported and cannot be changed.
+ // When supported, atime update is restricted and depends on the file system and on the
+ // OS configuration.
+ if strings.Contains(runtime.GOARCH, "64") {
+ t.Log(errormsg)
+ t.Log("Filesystem might not support atime changes; ignoring.")
+ }
+ default:
+ t.Error(errormsg)
+ }
+ }
+ }
+ }
+ }
+ if got, want := mt0, tt.wantMTime; !got.Equal(want) {
+ errormsg := fmt.Sprintf("ModTime mismatch with values ATime:%q-MTime:%q\ngot: %q\nwant: %q", tt.aTime, tt.mTime, got, want)
+ switch runtime.GOOS {
+ case "dragonfly":
+ t.Log(errormsg)
+ t.Log("Mtime is always updated; ignoring.")
+ default:
+ t.Error(errormsg)
+ }
+ }
+ }
+}
+
// Use TempDir (via newDir) to make sure we're on a local file system,
// so that timings are not distorted by latency and caching.
// On NFS, timings can be off due to caching of meta-data on
}
func UtimesNano(path string, ts []Timespec) error {
+ // UTIME_OMIT value must match internal/syscall/unix/at_js.go
+ const UTIME_OMIT = -0x2
if err := checkPath(path); err != nil {
return err
}
}
atime := ts[0].Sec
mtime := ts[1].Sec
+ if atime == UTIME_OMIT || mtime == UTIME_OMIT {
+ var st Stat_t
+ if err := Stat(path, &st); err != nil {
+ return err
+ }
+ if atime == UTIME_OMIT {
+ atime = st.Atime
+ }
+ if mtime == UTIME_OMIT {
+ mtime = st.Mtime
+ }
+ }
_, err := fsCall("utimes", path, atime, mtime)
return err
}
}
func UtimesNano(path string, ts []Timespec) error {
+ // UTIME_OMIT value must match internal/syscall/unix/at_wasip1.go
+ const UTIME_OMIT = -0x2
if path == "" {
return EINVAL
}
dirFd, pathPtr, pathLen := preparePath(path)
+ atime := TimespecToNsec(ts[0])
+ mtime := TimespecToNsec(ts[1])
+ if ts[0].Nsec == UTIME_OMIT || ts[1].Nsec == UTIME_OMIT {
+ var st Stat_t
+ if err := Stat(path, &st); err != nil {
+ return err
+ }
+ if ts[0].Nsec == UTIME_OMIT {
+ atime = int64(st.Atime)
+ }
+ if ts[1].Nsec == UTIME_OMIT {
+ mtime = int64(st.Mtime)
+ }
+ }
errno := path_filestat_set_times(
dirFd,
LOOKUP_SYMLINK_FOLLOW,
pathPtr,
pathLen,
- timestamp(TimespecToNsec(ts[0])),
- timestamp(TimespecToNsec(ts[1])),
+ timestamp(atime),
+ timestamp(mtime),
FILESTAT_SET_ATIM|FILESTAT_SET_MTIM,
)
return errnoErr(errno)
return e
}
defer Close(h)
- a := NsecToFiletime(tv[0].Nanoseconds())
- w := NsecToFiletime(tv[1].Nanoseconds())
+ a := Filetime{}
+ w := Filetime{}
+ if tv[0].Nanoseconds() != 0 {
+ a = NsecToFiletime(tv[0].Nanoseconds())
+ }
+ if tv[0].Nanoseconds() != 0 {
+ w = NsecToFiletime(tv[1].Nanoseconds())
+ }
return SetFileTime(h, nil, &a, &w)
}
return e
}
defer Close(h)
- a := NsecToFiletime(TimespecToNsec(ts[0]))
- w := NsecToFiletime(TimespecToNsec(ts[1]))
+ a := Filetime{}
+ w := Filetime{}
+ if TimespecToNsec(ts[0]) != 0 {
+ a = NsecToFiletime(TimespecToNsec(ts[0]))
+ }
+ if TimespecToNsec(ts[1]) != 0 {
+ w = NsecToFiletime(TimespecToNsec(ts[1]))
+ }
return SetFileTime(h, nil, &a, &w)
}