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