github.com/tetratelabs/wazero@v1.2.1/internal/sysfs/futimens_windows.go (about)

     1  package sysfs
     2  
     3  import (
     4  	"syscall"
     5  	"time"
     6  
     7  	"github.com/tetratelabs/wazero/internal/platform"
     8  )
     9  
    10  // Define values even if not used except as sentinels.
    11  const (
    12  	_UTIME_NOW              = -1
    13  	_UTIME_OMIT             = -2
    14  	SupportsSymlinkNoFollow = false
    15  )
    16  
    17  func utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) error {
    18  	return utimensPortable(path, times, symlinkFollow)
    19  }
    20  
    21  func futimens(fd uintptr, times *[2]syscall.Timespec) error {
    22  	// Before Go 1.20, ERROR_INVALID_HANDLE was returned for too many reasons.
    23  	// Kick out so that callers can use path-based operations instead.
    24  	if !platform.IsGo120 {
    25  		return syscall.ENOSYS
    26  	}
    27  
    28  	// Per docs, zero isn't a valid timestamp as it cannot be differentiated
    29  	// from nil. In both cases, it is a marker like syscall.UTIME_OMIT.
    30  	// See https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfiletime
    31  	a, w := timespecToFiletime(times)
    32  
    33  	if a == nil && w == nil {
    34  		return nil // both omitted, so nothing to change
    35  	}
    36  
    37  	// Attempt to get the stat by handle, which works for normal files
    38  	h := syscall.Handle(fd)
    39  
    40  	// Note: This returns ERROR_ACCESS_DENIED when the input is a directory.
    41  	return syscall.SetFileTime(h, nil, a, w)
    42  }
    43  
    44  func timespecToFiletime(times *[2]syscall.Timespec) (a, w *syscall.Filetime) {
    45  	// Handle when both inputs are current system time.
    46  	if times == nil || times[0].Nsec == UTIME_NOW && times[1].Nsec == UTIME_NOW {
    47  		now := time.Now().UnixNano()
    48  		ft := syscall.NsecToFiletime(now)
    49  		return &ft, &ft
    50  	}
    51  
    52  	// Now, either one of the inputs is current time, or neither. This
    53  	// means we don't have a risk of re-reading the clock.
    54  	a = timespecToFileTime(times, 0)
    55  	w = timespecToFileTime(times, 1)
    56  	return
    57  }
    58  
    59  func timespecToFileTime(times *[2]syscall.Timespec, i int) *syscall.Filetime {
    60  	if times[i].Nsec == UTIME_OMIT {
    61  		return nil
    62  	}
    63  
    64  	var nsec int64
    65  	if times[i].Nsec == UTIME_NOW {
    66  		nsec = time.Now().UnixNano()
    67  	} else {
    68  		nsec = syscall.TimespecToNsec(times[i])
    69  	}
    70  	ft := syscall.NsecToFiletime(nsec)
    71  	return &ft
    72  }