github.com/tetratelabs/wazero@v1.2.1/internal/sysfs/futimens.go (about) 1 package sysfs 2 3 import ( 4 "syscall" 5 "time" 6 "unsafe" 7 8 "github.com/tetratelabs/wazero/internal/fsapi" 9 "github.com/tetratelabs/wazero/internal/platform" 10 ) 11 12 const ( 13 // UTIME_NOW is a special syscall.Timespec NSec value used to set the 14 // file's timestamp to a value close to, but not greater than the current 15 // system time. 16 UTIME_NOW = _UTIME_NOW 17 18 // UTIME_OMIT is a special syscall.Timespec NSec value used to avoid 19 // setting the file's timestamp. 20 UTIME_OMIT = _UTIME_OMIT 21 ) 22 23 // Utimens set file access and modification times on a path resolved to the 24 // current working directory, at nanosecond precision. 25 // 26 // # Parameters 27 // 28 // The `times` parameter includes the access and modification timestamps to 29 // assign. Special syscall.Timespec NSec values UTIME_NOW and UTIME_OMIT may be 30 // specified instead of real timestamps. A nil `times` parameter behaves the 31 // same as if both were set to UTIME_NOW. 32 // 33 // When the `symlinkFollow` parameter is true and the path is a symbolic link, 34 // the target of expanding that link is updated. 35 // 36 // # Errors 37 // 38 // A zero syscall.Errno is success. The below are expected otherwise: 39 // - syscall.ENOSYS: the implementation does not support this function. 40 // - syscall.EINVAL: `path` is invalid. 41 // - syscall.EEXIST: `path` exists and is a directory. 42 // - syscall.ENOTDIR: `path` exists and is a file. 43 // 44 // # Notes 45 // 46 // - This is like syscall.UtimesNano and `utimensat` with `AT_FDCWD` in 47 // POSIX. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html 48 func Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscall.Errno { 49 err := utimens(path, times, symlinkFollow) 50 return platform.UnwrapOSError(err) 51 } 52 53 var _zero uintptr //nolint:unused 54 55 func timesToPtr(times *[2]syscall.Timespec) unsafe.Pointer { //nolint:unused 56 var _p0 unsafe.Pointer 57 if times != nil { 58 _p0 = unsafe.Pointer(×[0]) 59 } else { 60 _p0 = unsafe.Pointer(&_zero) 61 } 62 return _p0 63 } 64 65 func utimensPortable(path string, times *[2]syscall.Timespec, symlinkFollow bool) error { //nolint:unused 66 if !symlinkFollow { 67 return syscall.ENOSYS 68 } 69 70 // Handle when both inputs are current system time. 71 if times == nil || times[0].Nsec == UTIME_NOW && times[1].Nsec == UTIME_NOW { 72 ts := nowTimespec() 73 return syscall.UtimesNano(path, []syscall.Timespec{ts, ts}) 74 } 75 76 // When both inputs are omitted, there is nothing to change. 77 if times[0].Nsec == UTIME_OMIT && times[1].Nsec == UTIME_OMIT { 78 return nil 79 } 80 81 // Handle when neither input are special values 82 if times[0].Nsec != UTIME_NOW && times[1].Nsec != UTIME_NOW && 83 times[0].Nsec != UTIME_OMIT && times[1].Nsec != UTIME_OMIT { 84 return syscall.UtimesNano(path, times[:]) 85 } 86 87 // Now, either atim or mtim is a special value, but not both. 88 89 // Now, either one of the inputs is a special value, or neither. This means 90 // we don't have a risk of re-reading the clock or re-doing stat. 91 if atim, err := normalizeTimespec(path, times, 0); err != 0 { 92 return err 93 } else if mtim, err := normalizeTimespec(path, times, 1); err != 0 { 94 return err 95 } else { 96 return syscall.UtimesNano(path, []syscall.Timespec{atim, mtim}) 97 } 98 } 99 100 func normalizeTimespec(path string, times *[2]syscall.Timespec, i int) (ts syscall.Timespec, err syscall.Errno) { //nolint:unused 101 switch times[i].Nsec { 102 case UTIME_NOW: // declined in Go per golang/go#31880. 103 ts = nowTimespec() 104 return 105 case UTIME_OMIT: 106 // UTIME_OMIT is expensive until progress is made in Go, as it requires a 107 // stat to read-back the value to re-apply. 108 // - https://github.com/golang/go/issues/32558. 109 // - https://go-review.googlesource.com/c/go/+/219638 (unmerged) 110 var st fsapi.Stat_t 111 if st, err = stat(path); err != 0 { 112 return 113 } 114 switch i { 115 case 0: 116 ts = syscall.NsecToTimespec(st.Atim) 117 case 1: 118 ts = syscall.NsecToTimespec(st.Mtim) 119 default: 120 panic("BUG") 121 } 122 return 123 default: // not special 124 ts = times[i] 125 return 126 } 127 } 128 129 func nowTimespec() syscall.Timespec { //nolint:unused 130 now := time.Now().UnixNano() 131 return syscall.NsecToTimespec(now) 132 }