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