github.com/epfl-dcsl/gotee@v0.0.0-20200909122901-014b35f5e5e9/src/os/stat_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 "syscall" 10 "unsafe" 11 ) 12 13 // Stat returns the FileInfo structure describing file. 14 // If there is an error, it will be of type *PathError. 15 func (file *File) Stat() (FileInfo, error) { 16 if file == nil { 17 return nil, ErrInvalid 18 } 19 20 if file.isdir() { 21 // I don't know any better way to do that for directory 22 return Stat(file.dirinfo.path) 23 } 24 if file.name == DevNull { 25 return &devNullStat, nil 26 } 27 28 ft, err := file.pfd.GetFileType() 29 if err != nil { 30 return nil, &PathError{"GetFileType", file.name, err} 31 } 32 switch ft { 33 case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR: 34 return &fileStat{name: basename(file.name), filetype: ft}, nil 35 } 36 37 var d syscall.ByHandleFileInformation 38 err = file.pfd.GetFileInformationByHandle(&d) 39 if err != nil { 40 return nil, &PathError{"GetFileInformationByHandle", file.name, err} 41 } 42 return &fileStat{ 43 name: basename(file.name), 44 sys: syscall.Win32FileAttributeData{ 45 FileAttributes: d.FileAttributes, 46 CreationTime: d.CreationTime, 47 LastAccessTime: d.LastAccessTime, 48 LastWriteTime: d.LastWriteTime, 49 FileSizeHigh: d.FileSizeHigh, 50 FileSizeLow: d.FileSizeLow, 51 }, 52 filetype: ft, 53 vol: d.VolumeSerialNumber, 54 idxhi: d.FileIndexHigh, 55 idxlo: d.FileIndexLow, 56 }, nil 57 } 58 59 // statNolog implements Stat for Windows. 60 func statNolog(name string) (FileInfo, error) { 61 if len(name) == 0 { 62 return nil, &PathError{"Stat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} 63 } 64 if name == DevNull { 65 return &devNullStat, nil 66 } 67 namep, err := syscall.UTF16PtrFromString(fixLongPath(name)) 68 if err != nil { 69 return nil, &PathError{"Stat", name, err} 70 } 71 // Apparently (see https://golang.org/issues/19922#issuecomment-300031421) 72 // GetFileAttributesEx is fastest approach to get file info. 73 // It does not work for symlinks. But symlinks are rare, 74 // so try GetFileAttributesEx first. 75 var fs fileStat 76 err = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fs.sys))) 77 if err == nil && fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { 78 fs.path = name 79 if !isAbs(fs.path) { 80 fs.path, err = syscall.FullPath(fs.path) 81 if err != nil { 82 return nil, &PathError{"FullPath", name, err} 83 } 84 } 85 fs.name = basename(name) 86 return &fs, nil 87 } 88 // Use Windows I/O manager to dereference the symbolic link, as per 89 // https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/ 90 h, err := syscall.CreateFile(namep, 0, 0, nil, 91 syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) 92 if err != nil { 93 if err == windows.ERROR_SHARING_VIOLATION { 94 // try FindFirstFile now that CreateFile failed 95 return statWithFindFirstFile(name, namep) 96 } 97 return nil, &PathError{"CreateFile", name, err} 98 } 99 defer syscall.CloseHandle(h) 100 101 var d syscall.ByHandleFileInformation 102 err = syscall.GetFileInformationByHandle(h, &d) 103 if err != nil { 104 return nil, &PathError{"GetFileInformationByHandle", name, err} 105 } 106 return &fileStat{ 107 name: basename(name), 108 sys: syscall.Win32FileAttributeData{ 109 FileAttributes: d.FileAttributes, 110 CreationTime: d.CreationTime, 111 LastAccessTime: d.LastAccessTime, 112 LastWriteTime: d.LastWriteTime, 113 FileSizeHigh: d.FileSizeHigh, 114 FileSizeLow: d.FileSizeLow, 115 }, 116 vol: d.VolumeSerialNumber, 117 idxhi: d.FileIndexHigh, 118 idxlo: d.FileIndexLow, 119 // fileStat.path is used by os.SameFile to decide if it needs 120 // to fetch vol, idxhi and idxlo. But these are already set, 121 // so set fileStat.path to "" to prevent os.SameFile doing it again. 122 // Also do not set fileStat.filetype, because it is only used for 123 // console and stdin/stdout. But you cannot call os.Stat for these. 124 }, nil 125 } 126 127 // statWithFindFirstFile is used by Stat to handle special case of statting 128 // c:\pagefile.sys. We might discover that other files need similar treatment. 129 func statWithFindFirstFile(name string, namep *uint16) (FileInfo, error) { 130 var fd syscall.Win32finddata 131 h, err := syscall.FindFirstFile(namep, &fd) 132 if err != nil { 133 return nil, &PathError{"FindFirstFile", name, err} 134 } 135 syscall.FindClose(h) 136 137 fullpath := name 138 if !isAbs(fullpath) { 139 fullpath, err = syscall.FullPath(fullpath) 140 if err != nil { 141 return nil, &PathError{"FullPath", name, err} 142 } 143 } 144 return &fileStat{ 145 name: basename(name), 146 path: fullpath, 147 sys: syscall.Win32FileAttributeData{ 148 FileAttributes: fd.FileAttributes, 149 CreationTime: fd.CreationTime, 150 LastAccessTime: fd.LastAccessTime, 151 LastWriteTime: fd.LastWriteTime, 152 FileSizeHigh: fd.FileSizeHigh, 153 FileSizeLow: fd.FileSizeLow, 154 }, 155 }, nil 156 } 157 158 // lstatNolog implements Lstat for Windows. 159 func lstatNolog(name string) (FileInfo, error) { 160 if len(name) == 0 { 161 return nil, &PathError{"Lstat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} 162 } 163 if name == DevNull { 164 return &devNullStat, nil 165 } 166 fs := &fileStat{name: basename(name)} 167 namep, e := syscall.UTF16PtrFromString(fixLongPath(name)) 168 if e != nil { 169 return nil, &PathError{"Lstat", name, e} 170 } 171 e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fs.sys))) 172 if e != nil { 173 if e != windows.ERROR_SHARING_VIOLATION { 174 return nil, &PathError{"GetFileAttributesEx", name, e} 175 } 176 // try FindFirstFile now that GetFileAttributesEx failed 177 return statWithFindFirstFile(name, namep) 178 } 179 fs.path = name 180 if !isAbs(fs.path) { 181 fs.path, e = syscall.FullPath(fs.path) 182 if e != nil { 183 return nil, &PathError{"FullPath", name, e} 184 } 185 } 186 return fs, nil 187 }