github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/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 "syscall" 9 "unsafe" 10 ) 11 12 // Stat returns the FileInfo structure describing file. 13 // If there is an error, it will be of type *PathError. 14 func (file *File) Stat() (FileInfo, error) { 15 if file == nil { 16 return nil, ErrInvalid 17 } 18 if file == nil || file.fd < 0 { 19 return nil, syscall.EINVAL 20 } 21 if file.isdir() { 22 // I don't know any better way to do that for directory 23 return Stat(file.dirinfo.path) 24 } 25 if file.name == DevNull { 26 return &devNullStat, nil 27 } 28 29 ft, err := syscall.GetFileType(file.fd) 30 if err != nil { 31 return nil, &PathError{"GetFileType", file.name, err} 32 } 33 if ft == syscall.FILE_TYPE_PIPE { 34 return &fileStat{name: basename(file.name), pipe: true}, nil 35 } 36 37 var d syscall.ByHandleFileInformation 38 err = syscall.GetFileInformationByHandle(file.fd, &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 vol: d.VolumeSerialNumber, 53 idxhi: d.FileIndexHigh, 54 idxlo: d.FileIndexLow, 55 pipe: false, 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 var fi FileInfo 63 var err error 64 for { 65 fi, err = Lstat(name) 66 if err != nil { 67 return fi, err 68 } 69 if fi.Mode()&ModeSymlink == 0 { 70 return fi, nil 71 } 72 name, err = Readlink(name) 73 if err != nil { 74 return fi, err 75 } 76 } 77 } 78 79 // Lstat returns the FileInfo structure describing the named file. 80 // If the file is a symbolic link, the returned FileInfo 81 // describes the symbolic link. Lstat makes no attempt to follow the link. 82 // If there is an error, it will be of type *PathError. 83 func Lstat(name string) (FileInfo, error) { 84 if len(name) == 0 { 85 return nil, &PathError{"Lstat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} 86 } 87 if name == DevNull { 88 return &devNullStat, nil 89 } 90 fs := &fileStat{name: basename(name)} 91 namep, e := syscall.UTF16PtrFromString(name) 92 if e != nil { 93 return nil, &PathError{"Lstat", name, e} 94 } 95 e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fs.sys))) 96 if e != nil { 97 return nil, &PathError{"GetFileAttributesEx", name, e} 98 } 99 fs.path = name 100 if !isAbs(fs.path) { 101 fs.path, e = syscall.FullPath(fs.path) 102 if e != nil { 103 return nil, e 104 } 105 } 106 return fs, nil 107 } 108 109 // basename removes trailing slashes and the leading 110 // directory name and drive letter from path name. 111 func basename(name string) string { 112 // Remove drive letter 113 if len(name) == 2 && name[1] == ':' { 114 name = "." 115 } else if len(name) > 2 && name[1] == ':' { 116 name = name[2:] 117 } 118 i := len(name) - 1 119 // Remove trailing slashes 120 for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- { 121 name = name[:i] 122 } 123 // Remove leading directory name 124 for i--; i >= 0; i-- { 125 if name[i] == '/' || name[i] == '\\' { 126 name = name[i+1:] 127 break 128 } 129 } 130 return name 131 } 132 133 func isAbs(path string) (b bool) { 134 v := volumeName(path) 135 if v == "" { 136 return false 137 } 138 path = path[len(v):] 139 if path == "" { 140 return false 141 } 142 return IsPathSeparator(path[0]) 143 } 144 145 func volumeName(path string) (v string) { 146 if len(path) < 2 { 147 return "" 148 } 149 // with drive letter 150 c := path[0] 151 if path[1] == ':' && 152 ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 153 'A' <= c && c <= 'Z') { 154 return path[:2] 155 } 156 // is it UNC 157 if l := len(path); l >= 5 && IsPathSeparator(path[0]) && IsPathSeparator(path[1]) && 158 !IsPathSeparator(path[2]) && path[2] != '.' { 159 // first, leading `\\` and next shouldn't be `\`. its server name. 160 for n := 3; n < l-1; n++ { 161 // second, next '\' shouldn't be repeated. 162 if IsPathSeparator(path[n]) { 163 n++ 164 // third, following something characters. its share name. 165 if !IsPathSeparator(path[n]) { 166 if path[n] == '.' { 167 break 168 } 169 for ; n < l; n++ { 170 if IsPathSeparator(path[n]) { 171 break 172 } 173 } 174 return path[:n] 175 } 176 break 177 } 178 } 179 } 180 return "" 181 }