github.com/tetratelabs/wazero@v1.2.1/internal/sysfs/stat_windows.go (about) 1 //go:build (amd64 || arm64) && windows 2 3 package sysfs 4 5 import ( 6 "io/fs" 7 "os" 8 "path" 9 "syscall" 10 11 "github.com/tetratelabs/wazero/internal/fsapi" 12 "github.com/tetratelabs/wazero/internal/platform" 13 ) 14 15 func lstat(path string) (fsapi.Stat_t, syscall.Errno) { 16 attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) 17 // Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink. 18 // See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted 19 attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT 20 return statPath(attrs, path) 21 } 22 23 func stat(path string) (fsapi.Stat_t, syscall.Errno) { 24 attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) 25 return statPath(attrs, path) 26 } 27 28 func statPath(createFileAttrs uint32, path string) (fsapi.Stat_t, syscall.Errno) { 29 if len(path) == 0 { 30 return fsapi.Stat_t{}, syscall.ENOENT 31 } 32 pathp, err := syscall.UTF16PtrFromString(path) 33 if err != nil { 34 return fsapi.Stat_t{}, syscall.EINVAL 35 } 36 37 // open the file handle 38 h, err := syscall.CreateFile(pathp, 0, 0, nil, 39 syscall.OPEN_EXISTING, createFileAttrs, 0) 40 if err != nil { 41 // To match expectations of WASI, e.g. TinyGo TestStatBadDir, return 42 // ENOENT, not ENOTDIR. 43 if err == syscall.ENOTDIR { 44 err = syscall.ENOENT 45 } 46 return fsapi.Stat_t{}, platform.UnwrapOSError(err) 47 } 48 defer syscall.CloseHandle(h) 49 50 return statHandle(h) 51 } 52 53 func statFile(f *os.File) (fsapi.Stat_t, syscall.Errno) { 54 // Attempt to get the stat by handle, which works for normal files 55 st, err := statHandle(syscall.Handle(f.Fd())) 56 57 // ERROR_INVALID_HANDLE happens before Go 1.20. Don't fail as we only 58 // use that approach to fill in inode data, which is not critical. 59 // 60 // Note: statHandle uses UnwrapOSError which coerces 61 // ERROR_INVALID_HANDLE to EBADF. 62 if err != syscall.EBADF { 63 return st, err 64 } 65 return defaultStatFile(f) 66 } 67 68 // inoFromFileInfo uses stat to get the inode information of the file. 69 func inoFromFileInfo(filePath string, t fs.FileInfo) (ino uint64, errno syscall.Errno) { 70 if filePath == "" { 71 // This is a fs.File backed implementation which doesn't have access to 72 // the original file path. 73 return 74 } 75 // ino is no not in Win32FileAttributeData 76 inoPath := path.Clean(path.Join(filePath, t.Name())) 77 var st fsapi.Stat_t 78 if st, errno = lstat(inoPath); errno == 0 { 79 ino = st.Ino 80 } 81 return 82 } 83 84 func statFromFileInfo(t fs.FileInfo) fsapi.Stat_t { 85 if d, ok := t.Sys().(*syscall.Win32FileAttributeData); ok { 86 st := fsapi.Stat_t{} 87 st.Ino = 0 // not in Win32FileAttributeData 88 st.Dev = 0 // not in Win32FileAttributeData 89 st.Mode = t.Mode() 90 st.Nlink = 1 // not in Win32FileAttributeData 91 st.Size = t.Size() 92 st.Atim = d.LastAccessTime.Nanoseconds() 93 st.Mtim = d.LastWriteTime.Nanoseconds() 94 st.Ctim = d.CreationTime.Nanoseconds() 95 return st 96 } else { 97 return StatFromDefaultFileInfo(t) 98 } 99 } 100 101 func statHandle(h syscall.Handle) (fsapi.Stat_t, syscall.Errno) { 102 winFt, err := syscall.GetFileType(h) 103 if err != nil { 104 return fsapi.Stat_t{}, platform.UnwrapOSError(err) 105 } 106 107 var fi syscall.ByHandleFileInformation 108 if err = syscall.GetFileInformationByHandle(h, &fi); err != nil { 109 return fsapi.Stat_t{}, platform.UnwrapOSError(err) 110 } 111 112 var m fs.FileMode 113 if fi.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 { 114 m |= 0o444 115 } else { 116 m |= 0o666 117 } 118 119 switch { // check whether this is a symlink first 120 case fi.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0: 121 m |= fs.ModeSymlink 122 case winFt == syscall.FILE_TYPE_PIPE: 123 m |= fs.ModeNamedPipe 124 case winFt == syscall.FILE_TYPE_CHAR: 125 m |= fs.ModeDevice | fs.ModeCharDevice 126 case fi.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0: 127 m |= fs.ModeDir | 0o111 // e.g. 0o444 -> 0o555 128 } 129 130 st := fsapi.Stat_t{} 131 // FileIndex{High,Low} can be combined and used as a unique identifier like inode. 132 // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information 133 st.Dev = uint64(fi.VolumeSerialNumber) 134 st.Ino = (uint64(fi.FileIndexHigh) << 32) | uint64(fi.FileIndexLow) 135 st.Mode = m 136 st.Nlink = uint64(fi.NumberOfLinks) 137 st.Size = int64(fi.FileSizeHigh)<<32 + int64(fi.FileSizeLow) 138 st.Atim = fi.LastAccessTime.Nanoseconds() 139 st.Mtim = fi.LastWriteTime.Nanoseconds() 140 st.Ctim = fi.CreationTime.Nanoseconds() 141 return st, 0 142 }