Files
wazero/internal/platform/futimens.go
Crypt Keeper 36bf277534 sysfs: requires all methods to return syscall.Errno (#1264)
This forces all syscall functions, notably filesystem, to return numeric
codes as opposed to mapping in two different areas. The result of this
change is better consolidation in call sites of `sysfs.FS`, while
further refactoring is needed to address consolidation of file errors.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2023-03-22 07:47:57 +01:00

147 lines
4.4 KiB
Go

package platform
import (
"io/fs"
"syscall"
"time"
"unsafe"
)
const (
// UTIME_NOW is a special syscall.Timespec NSec value used to set the
// file's timestamp to a value close to, but not greater than the current
// system time.
UTIME_NOW = _UTIME_NOW
// UTIME_OMIT is a special syscall.Timespec NSec value used to avoid
// setting the file's timestamp.
UTIME_OMIT = _UTIME_OMIT
)
// Utimens set file access and modification times on a path resolved to the
// current working directory, at nanosecond precision.
//
// # Parameters
//
// The `times` parameter includes the access and modification timestamps to
// assign. Special syscall.Timespec NSec values UTIME_NOW and UTIME_OMIT may be
// specified instead of real timestamps. A nil `times` parameter behaves the
// same as if both were set to UTIME_NOW.
//
// When the `symlinkFollow` parameter is true and the path is a symbolic link,
// the target of expanding that link is updated.
//
// # Errors
//
// The following errors are expected:
// - syscall.EINVAL: `path` is invalid.
// - syscall.EEXIST: `path` exists and is a directory.
// - syscall.ENOTDIR: `path` exists and is a file.
//
// # Notes
//
// - This is similar to syscall.UtimesNano, except that doesn't have flags to
// control expansion of symbolic links. It also doesn't support special
// values UTIME_NOW or UTIME_NOW.
// - This is like `utimensat` with `AT_FDCWD` in POSIX. See
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html
func Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscall.Errno {
err := utimens(path, times, symlinkFollow)
return UnwrapOSError(err)
}
// UtimensFile is like Utimens, except it works on a file, not a path.
//
// # Notes
//
// - Windows requires files to be open with syscall.O_RDWR, which means you
// cannot use this to update timestamps on a directory (syscall.EPERM).
// - This is like the function `futimens` in POSIX. See
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html
func UtimensFile(f fs.File, times *[2]syscall.Timespec) syscall.Errno {
if f, ok := f.(fdFile); ok {
err := futimens(f.Fd(), times)
return UnwrapOSError(err)
}
return syscall.ENOSYS
}
func timesToPtr(times *[2]syscall.Timespec) unsafe.Pointer { //nolint:unused
var _p0 unsafe.Pointer
if times != nil {
_p0 = unsafe.Pointer(&times[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
return _p0
}
func utimensPortable(path string, times *[2]syscall.Timespec, symlinkFollow bool) error { //nolint:unused
if !symlinkFollow {
return syscall.ENOSYS
}
// Handle when both inputs are current system time.
if times == nil || times[0].Nsec == UTIME_NOW && times[1].Nsec == UTIME_NOW {
ts := nowTimespec()
return syscall.UtimesNano(path, []syscall.Timespec{ts, ts})
}
// When both inputs are omitted, there is nothing to change.
if times[0].Nsec == UTIME_OMIT && times[1].Nsec == UTIME_OMIT {
return nil
}
// Handle when neither input are special values
if times[0].Nsec != UTIME_NOW && times[1].Nsec != UTIME_NOW &&
times[0].Nsec != UTIME_OMIT && times[1].Nsec != UTIME_OMIT {
return syscall.UtimesNano(path, times[:])
}
// Now, either atim or mtim is a special value, but not both.
// Now, either one of the inputs is a special value, or neither. This means
// we don't have a risk of re-reading the clock or re-doing stat.
if atim, err := normalizeTimespec(path, times, 0); err != 0 {
return err
} else if mtim, err := normalizeTimespec(path, times, 1); err != 0 {
return err
} else {
return syscall.UtimesNano(path, []syscall.Timespec{atim, mtim})
}
}
func normalizeTimespec(path string, times *[2]syscall.Timespec, i int) (ts syscall.Timespec, err syscall.Errno) { //nolint:unused
switch times[i].Nsec {
case UTIME_NOW: // declined in Go per golang/go#31880.
ts = nowTimespec()
return
case UTIME_OMIT:
// UTIME_OMIT is expensive until progress is made in Go, as it requires a
// stat to read-back the value to re-apply.
// - https://github.com/golang/go/issues/32558.
// - https://go-review.googlesource.com/c/go/+/219638 (unmerged)
var st Stat_t
if st, err = stat(path); err != 0 {
return
}
switch i {
case 0:
ts = syscall.NsecToTimespec(st.Atim)
case 1:
ts = syscall.NsecToTimespec(st.Mtim)
default:
panic("BUG")
}
return
default: // not special
ts = times[i]
return
}
}
func nowTimespec() syscall.Timespec { //nolint:unused
now := time.Now().UnixNano()
return syscall.NsecToTimespec(now)
}