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.name) 24 } 25 if file.name == DevNull { 26 return &devNullStat, nil 27 } 28 var d syscall.ByHandleFileInformation 29 e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &d) 30 if e != nil { 31 return nil, &PathError{"GetFileInformationByHandle", file.name, e} 32 } 33 return &fileStat{ 34 name: basename(file.name), 35 sys: syscall.Win32FileAttributeData{ 36 FileAttributes: d.FileAttributes, 37 CreationTime: d.CreationTime, 38 LastAccessTime: d.LastAccessTime, 39 LastWriteTime: d.LastWriteTime, 40 FileSizeHigh: d.FileSizeHigh, 41 FileSizeLow: d.FileSizeLow, 42 }, 43 vol: d.VolumeSerialNumber, 44 idxhi: d.FileIndexHigh, 45 idxlo: d.FileIndexLow, 46 }, nil 47 } 48 49 // Stat returns a FileInfo structure describing the named file. 50 // If there is an error, it will be of type *PathError. 51 func Stat(name string) (FileInfo, error) { 52 var fi FileInfo 53 var err error 54 for { 55 fi, err = Lstat(name) 56 if err != nil { 57 return fi, err 58 } 59 if fi.Mode()&ModeSymlink == 0 { 60 return fi, nil 61 } 62 name, err = Readlink(name) 63 if err != nil { 64 return fi, err 65 } 66 } 67 } 68 69 // Lstat returns the FileInfo structure describing the named file. 70 // If the file is a symbolic link, the returned FileInfo 71 // describes the symbolic link. Lstat makes no attempt to follow the link. 72 // If there is an error, it will be of type *PathError. 73 func Lstat(name string) (FileInfo, error) { 74 if len(name) == 0 { 75 return nil, &PathError{"Lstat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} 76 } 77 if name == DevNull { 78 return &devNullStat, nil 79 } 80 fs := &fileStat{name: basename(name)} 81 namep, e := syscall.UTF16PtrFromString(name) 82 if e != nil { 83 return nil, &PathError{"Lstat", name, e} 84 } 85 e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fs.sys))) 86 if e != nil { 87 return nil, &PathError{"GetFileAttributesEx", name, e} 88 } 89 fs.path = name 90 if !isAbs(fs.path) { 91 fs.path, e = syscall.FullPath(fs.path) 92 if e != nil { 93 return nil, e 94 } 95 } 96 return fs, nil 97 } 98 99 // basename removes trailing slashes and the leading 100 // directory name and drive letter from path name. 101 func basename(name string) string { 102 // Remove drive letter 103 if len(name) == 2 && name[1] == ':' { 104 name = "." 105 } else if len(name) > 2 && name[1] == ':' { 106 name = name[2:] 107 } 108 i := len(name) - 1 109 // Remove trailing slashes 110 for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- { 111 name = name[:i] 112 } 113 // Remove leading directory name 114 for i--; i >= 0; i-- { 115 if name[i] == '/' || name[i] == '\\' { 116 name = name[i+1:] 117 break 118 } 119 } 120 return name 121 } 122 123 func isAbs(path string) (b bool) { 124 v := volumeName(path) 125 if v == "" { 126 return false 127 } 128 path = path[len(v):] 129 if path == "" { 130 return false 131 } 132 return IsPathSeparator(path[0]) 133 } 134 135 func volumeName(path string) (v string) { 136 if len(path) < 2 { 137 return "" 138 } 139 // with drive letter 140 c := path[0] 141 if path[1] == ':' && 142 ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 143 'A' <= c && c <= 'Z') { 144 return path[:2] 145 } 146 // is it UNC 147 if l := len(path); l >= 5 && IsPathSeparator(path[0]) && IsPathSeparator(path[1]) && 148 !IsPathSeparator(path[2]) && path[2] != '.' { 149 // first, leading `\\` and next shouldn't be `\`. its server name. 150 for n := 3; n < l-1; n++ { 151 // second, next '\' shouldn't be repeated. 152 if IsPathSeparator(path[n]) { 153 n++ 154 // third, following something characters. its share name. 155 if !IsPathSeparator(path[n]) { 156 if path[n] == '.' { 157 break 158 } 159 for ; n < l; n++ { 160 if IsPathSeparator(path[n]) { 161 break 162 } 163 } 164 return path[:n] 165 } 166 break 167 } 168 } 169 } 170 return "" 171 } 172