blob: 5e33292bec68197771b515c695eafec4386630cb [file] [log] [blame]
Colin Cross7bb052a2015-02-03 12:59:37 -08001// 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
5package os
6
7import (
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07008 "internal/syscall/windows"
Colin Cross7bb052a2015-02-03 12:59:37 -08009 "sync"
10 "syscall"
11 "time"
Dan Willemsenf3f2eb62018-08-28 11:28:58 -070012 "unsafe"
Colin Cross7bb052a2015-02-03 12:59:37 -080013)
14
15// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
16type fileStat struct {
Dan Willemsenf3f2eb62018-08-28 11:28:58 -070017 name string
18
19 // from ByHandleFileInformation, Win32FileAttributeData and Win32finddata
20 FileAttributes uint32
21 CreationTime syscall.Filetime
22 LastAccessTime syscall.Filetime
23 LastWriteTime syscall.Filetime
24 FileSizeHigh uint32
25 FileSizeLow uint32
26
27 // from Win32finddata
28 Reserved0 uint32
29
30 // what syscall.GetFileType returns
31 filetype uint32
Colin Cross7bb052a2015-02-03 12:59:37 -080032
33 // used to implement SameFile
34 sync.Mutex
Dan Willemsenc78f7142017-07-26 13:08:14 -070035 path string
36 vol uint32
37 idxhi uint32
38 idxlo uint32
39 appendNameToPath bool
Colin Cross7bb052a2015-02-03 12:59:37 -080040}
41
Dan Willemsenf3f2eb62018-08-28 11:28:58 -070042// newFileStatFromGetFileInformationByHandle calls GetFileInformationByHandle
43// to gather all required information about the file handle h.
44func newFileStatFromGetFileInformationByHandle(path string, h syscall.Handle) (fs *fileStat, err error) {
45 var d syscall.ByHandleFileInformation
46 err = syscall.GetFileInformationByHandle(h, &d)
47 if err != nil {
48 return nil, &PathError{"GetFileInformationByHandle", path, err}
49 }
Colin Cross1371fe42019-03-19 21:08:48 -070050
51 var ti windows.FILE_ATTRIBUTE_TAG_INFO
52 err = windows.GetFileInformationByHandleEx(h, windows.FileAttributeTagInfo, (*byte)(unsafe.Pointer(&ti)), uint32(unsafe.Sizeof(ti)))
53 if err != nil {
54 if errno, ok := err.(syscall.Errno); ok && errno == windows.ERROR_INVALID_PARAMETER {
55 // It appears calling GetFileInformationByHandleEx with
56 // FILE_ATTRIBUTE_TAG_INFO fails on FAT file system with
57 // ERROR_INVALID_PARAMETER. Clear ti.ReparseTag in that
58 // instance to indicate no symlinks are possible.
59 ti.ReparseTag = 0
60 } else {
61 return nil, &PathError{"GetFileInformationByHandleEx", path, err}
62 }
63 }
64
Dan Willemsenf3f2eb62018-08-28 11:28:58 -070065 return &fileStat{
66 name: basename(path),
67 FileAttributes: d.FileAttributes,
68 CreationTime: d.CreationTime,
69 LastAccessTime: d.LastAccessTime,
70 LastWriteTime: d.LastWriteTime,
71 FileSizeHigh: d.FileSizeHigh,
72 FileSizeLow: d.FileSizeLow,
73 vol: d.VolumeSerialNumber,
74 idxhi: d.FileIndexHigh,
75 idxlo: d.FileIndexLow,
Colin Cross1371fe42019-03-19 21:08:48 -070076 Reserved0: ti.ReparseTag,
Dan Willemsenf3f2eb62018-08-28 11:28:58 -070077 // fileStat.path is used by os.SameFile to decide if it needs
78 // to fetch vol, idxhi and idxlo. But these are already set,
79 // so set fileStat.path to "" to prevent os.SameFile doing it again.
80 }, nil
81}
82
83// newFileStatFromWin32finddata copies all required information
84// from syscall.Win32finddata d into the newly created fileStat.
85func newFileStatFromWin32finddata(d *syscall.Win32finddata) *fileStat {
86 return &fileStat{
87 FileAttributes: d.FileAttributes,
88 CreationTime: d.CreationTime,
89 LastAccessTime: d.LastAccessTime,
90 LastWriteTime: d.LastWriteTime,
91 FileSizeHigh: d.FileSizeHigh,
92 FileSizeLow: d.FileSizeLow,
93 Reserved0: d.Reserved0,
94 }
95}
96
Dan Willemsenf3f2eb62018-08-28 11:28:58 -070097func (fs *fileStat) isSymlink() bool {
98 // Use instructions described at
99 // https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/
100 // to recognize whether it's a symlink.
101 if fs.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
102 return false
103 }
104 return fs.Reserved0 == syscall.IO_REPARSE_TAG_SYMLINK ||
105 fs.Reserved0 == windows.IO_REPARSE_TAG_MOUNT_POINT
106}
107
Colin Cross7bb052a2015-02-03 12:59:37 -0800108func (fs *fileStat) Size() int64 {
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700109 return int64(fs.FileSizeHigh)<<32 + int64(fs.FileSizeLow)
Colin Cross7bb052a2015-02-03 12:59:37 -0800110}
111
112func (fs *fileStat) Mode() (m FileMode) {
113 if fs == &devNullStat {
114 return ModeDevice | ModeCharDevice | 0666
115 }
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700116 if fs.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
Colin Cross7bb052a2015-02-03 12:59:37 -0800117 m |= 0444
118 } else {
119 m |= 0666
120 }
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700121 if fs.isSymlink() {
Dan Willemsenc78f7142017-07-26 13:08:14 -0700122 return m | ModeSymlink
Colin Cross7bb052a2015-02-03 12:59:37 -0800123 }
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700124 if fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
Dan Willemsenc78f7142017-07-26 13:08:14 -0700125 m |= ModeDir | 0111
126 }
127 switch fs.filetype {
128 case syscall.FILE_TYPE_PIPE:
Dan Willemsen0c157092016-07-08 13:57:52 -0700129 m |= ModeNamedPipe
Dan Willemsenc78f7142017-07-26 13:08:14 -0700130 case syscall.FILE_TYPE_CHAR:
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700131 m |= ModeDevice | ModeCharDevice
Dan Willemsen0c157092016-07-08 13:57:52 -0700132 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800133 return m
134}
135
136func (fs *fileStat) ModTime() time.Time {
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700137 return time.Unix(0, fs.LastWriteTime.Nanoseconds())
Colin Cross7bb052a2015-02-03 12:59:37 -0800138}
139
140// Sys returns syscall.Win32FileAttributeData for file fs.
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700141func (fs *fileStat) Sys() interface{} {
142 return &syscall.Win32FileAttributeData{
143 FileAttributes: fs.FileAttributes,
144 CreationTime: fs.CreationTime,
145 LastAccessTime: fs.LastAccessTime,
146 LastWriteTime: fs.LastWriteTime,
147 FileSizeHigh: fs.FileSizeHigh,
148 FileSizeLow: fs.FileSizeLow,
149 }
150}
Colin Cross7bb052a2015-02-03 12:59:37 -0800151
152func (fs *fileStat) loadFileId() error {
153 fs.Lock()
154 defer fs.Unlock()
155 if fs.path == "" {
156 // already done
157 return nil
158 }
Dan Willemsenc78f7142017-07-26 13:08:14 -0700159 var path string
160 if fs.appendNameToPath {
161 path = fs.path + `\` + fs.name
162 } else {
163 path = fs.path
164 }
165 pathp, err := syscall.UTF16PtrFromString(path)
Colin Cross7bb052a2015-02-03 12:59:37 -0800166 if err != nil {
167 return err
168 }
Colin Cross1371fe42019-03-19 21:08:48 -0700169 attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
170 if fs.isSymlink() {
171 // Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
172 // See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted
173 attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
174 }
175 h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0)
Colin Cross7bb052a2015-02-03 12:59:37 -0800176 if err != nil {
177 return err
178 }
179 defer syscall.CloseHandle(h)
180 var i syscall.ByHandleFileInformation
Dan Willemsen0c157092016-07-08 13:57:52 -0700181 err = syscall.GetFileInformationByHandle(h, &i)
Colin Cross7bb052a2015-02-03 12:59:37 -0800182 if err != nil {
183 return err
184 }
185 fs.path = ""
186 fs.vol = i.VolumeSerialNumber
187 fs.idxhi = i.FileIndexHigh
188 fs.idxlo = i.FileIndexLow
189 return nil
190}
191
192// devNullStat is fileStat structure describing DevNull file ("NUL").
193var devNullStat = fileStat{
194 name: DevNull,
195 // hopefully this will work for SameFile
196 vol: 0,
197 idxhi: 0,
198 idxlo: 0,
199}
200
201func sameFile(fs1, fs2 *fileStat) bool {
202 e := fs1.loadFileId()
203 if e != nil {
204 return false
205 }
206 e = fs2.loadFileId()
207 if e != nil {
208 return false
209 }
210 return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo
211}
212
213// For testing.
214func atime(fi FileInfo) time.Time {
215 return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
216}