github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/os/types_windows.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package os 6 7 import ( 8 "internal/syscall/windows" 9 "sync" 10 "syscall" 11 "time" 12 "unsafe" 13 ) 14 15 // A fileStat is the implementation of FileInfo returned by Stat and Lstat. 16 type fileStat struct { 17 name string 18 19 // from ByHandleFileInformation, Win32FileAttributeData and Win32finddata 20 FileAttributes uint32 21 CreationTime syscall.Filetime 22 LastAccessTime syscall.Filetime 23 LastWriteTime syscall.Filetime 24 FileSizeHigh uint32 25 FileSizeLow uint32 26 27 // from Win32finddata 28 Reserved0 uint32 29 30 // what syscall.GetFileType returns 31 filetype uint32 32 33 // used to implement SameFile 34 sync.Mutex 35 path string 36 vol uint32 37 idxhi uint32 38 idxlo uint32 39 appendNameToPath bool 40 } 41 42 // newFileStatFromGetFileInformationByHandle calls GetFileInformationByHandle 43 // to gather all required information about the file handle h. 44 func newFileStatFromGetFileInformationByHandle(path string, h syscall.Handle) (fs *fileStat, err error) { 45 var d syscall.ByHandleFileInformation 46 err = syscall.GetFileInformationByHandle(h, &d) 47 if err != nil { 48 return nil, &PathError{Op: "GetFileInformationByHandle", Path: path, Err: err} 49 } 50 51 var ti windows.FILE_ATTRIBUTE_TAG_INFO 52 err = windows.GetFileInformationByHandleEx(h, windows.FileAttributeTagInfo, (*byte)(unsafe.Pointer(&ti)), uint32(unsafe.Sizeof(ti))) 53 if err != nil { 54 if errno, ok := err.(syscall.Errno); ok && errno == windows.ERROR_INVALID_PARAMETER { 55 // It appears calling GetFileInformationByHandleEx with 56 // FILE_ATTRIBUTE_TAG_INFO fails on FAT file system with 57 // ERROR_INVALID_PARAMETER. Clear ti.ReparseTag in that 58 // instance to indicate no symlinks are possible. 59 ti.ReparseTag = 0 60 } else { 61 return nil, &PathError{Op: "GetFileInformationByHandleEx", Path: path, Err: err} 62 } 63 } 64 65 return &fileStat{ 66 name: basename(path), 67 FileAttributes: d.FileAttributes, 68 CreationTime: d.CreationTime, 69 LastAccessTime: d.LastAccessTime, 70 LastWriteTime: d.LastWriteTime, 71 FileSizeHigh: d.FileSizeHigh, 72 FileSizeLow: d.FileSizeLow, 73 vol: d.VolumeSerialNumber, 74 idxhi: d.FileIndexHigh, 75 idxlo: d.FileIndexLow, 76 Reserved0: ti.ReparseTag, 77 // fileStat.path is used by os.SameFile to decide if it needs 78 // to fetch vol, idxhi and idxlo. But these are already set, 79 // so set fileStat.path to "" to prevent os.SameFile doing it again. 80 }, nil 81 } 82 83 // newFileStatFromWin32finddata copies all required information 84 // from syscall.Win32finddata d into the newly created fileStat. 85 func newFileStatFromWin32finddata(d *syscall.Win32finddata) *fileStat { 86 return &fileStat{ 87 FileAttributes: d.FileAttributes, 88 CreationTime: d.CreationTime, 89 LastAccessTime: d.LastAccessTime, 90 LastWriteTime: d.LastWriteTime, 91 FileSizeHigh: d.FileSizeHigh, 92 FileSizeLow: d.FileSizeLow, 93 Reserved0: d.Reserved0, 94 } 95 } 96 97 func (fs *fileStat) isSymlink() bool { 98 // Use instructions described at 99 // https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/ 100 // to recognize whether it's a symlink. 101 if fs.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { 102 return false 103 } 104 return fs.Reserved0 == syscall.IO_REPARSE_TAG_SYMLINK || 105 fs.Reserved0 == windows.IO_REPARSE_TAG_MOUNT_POINT 106 } 107 108 func (fs *fileStat) Size() int64 { 109 return int64(fs.FileSizeHigh)<<32 + int64(fs.FileSizeLow) 110 } 111 112 func (fs *fileStat) Mode() (m FileMode) { 113 if fs == &devNullStat { 114 return ModeDevice | ModeCharDevice | 0666 115 } 116 if fs.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 { 117 m |= 0444 118 } else { 119 m |= 0666 120 } 121 if fs.isSymlink() { 122 return m | ModeSymlink 123 } 124 if fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { 125 m |= ModeDir | 0111 126 } 127 switch fs.filetype { 128 case syscall.FILE_TYPE_PIPE: 129 m |= ModeNamedPipe 130 case syscall.FILE_TYPE_CHAR: 131 m |= ModeDevice | ModeCharDevice 132 } 133 return m 134 } 135 136 func (fs *fileStat) ModTime() time.Time { 137 return time.Unix(0, fs.LastWriteTime.Nanoseconds()) 138 } 139 140 // Sys returns syscall.Win32FileAttributeData for file fs. 141 func (fs *fileStat) Sys() interface{} { 142 return &syscall.Win32FileAttributeData{ 143 FileAttributes: fs.FileAttributes, 144 CreationTime: fs.CreationTime, 145 LastAccessTime: fs.LastAccessTime, 146 LastWriteTime: fs.LastWriteTime, 147 FileSizeHigh: fs.FileSizeHigh, 148 FileSizeLow: fs.FileSizeLow, 149 } 150 } 151 152 func (fs *fileStat) loadFileId() error { 153 fs.Lock() 154 defer fs.Unlock() 155 if fs.path == "" { 156 // already done 157 return nil 158 } 159 var path string 160 if fs.appendNameToPath { 161 path = fs.path + `\` + fs.name 162 } else { 163 path = fs.path 164 } 165 pathp, err := syscall.UTF16PtrFromString(path) 166 if err != nil { 167 return err 168 } 169 attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) 170 if fs.isSymlink() { 171 // Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink. 172 // See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted 173 attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT 174 } 175 h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0) 176 if err != nil { 177 return err 178 } 179 defer syscall.CloseHandle(h) 180 var i syscall.ByHandleFileInformation 181 err = syscall.GetFileInformationByHandle(h, &i) 182 if err != nil { 183 return err 184 } 185 fs.path = "" 186 fs.vol = i.VolumeSerialNumber 187 fs.idxhi = i.FileIndexHigh 188 fs.idxlo = i.FileIndexLow 189 return nil 190 } 191 192 // saveInfoFromPath saves full path of the file to be used by os.SameFile later, 193 // and set name from path. 194 func (fs *fileStat) saveInfoFromPath(path string) error { 195 fs.path = path 196 if !isAbs(fs.path) { 197 var err error 198 fs.path, err = syscall.FullPath(fs.path) 199 if err != nil { 200 return &PathError{Op: "FullPath", Path: path, Err: err} 201 } 202 } 203 fs.name = basename(path) 204 return nil 205 } 206 207 // devNullStat is fileStat structure describing DevNull file ("NUL"). 208 var devNullStat = fileStat{ 209 name: DevNull, 210 // hopefully this will work for SameFile 211 vol: 0, 212 idxhi: 0, 213 idxlo: 0, 214 } 215 216 func sameFile(fs1, fs2 *fileStat) bool { 217 e := fs1.loadFileId() 218 if e != nil { 219 return false 220 } 221 e = fs2.loadFileId() 222 if e != nil { 223 return false 224 } 225 return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo 226 } 227 228 // For testing. 229 func atime(fi FileInfo) time.Time { 230 return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) 231 }