Source file
src/os/types_windows.go
1
2
3
4
5 package os
6
7 import (
8 "internal/syscall/windows"
9 "sync"
10 "syscall"
11 "time"
12 "unsafe"
13 )
14
15
16 type fileStat struct {
17 name string
18
19
20 FileAttributes uint32
21 CreationTime syscall.Filetime
22 LastAccessTime syscall.Filetime
23 LastWriteTime syscall.Filetime
24 FileSizeHigh uint32
25 FileSizeLow uint32
26
27
28 ReparseTag uint32
29
30
31 filetype uint32
32
33
34 sync.Mutex
35 path string
36 vol uint32
37 idxhi uint32
38 idxlo uint32
39 appendNameToPath bool
40 }
41
42
43
44 func 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{Op: "GetFileInformationByHandle", Path: path, Err: err}
49 }
50
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
56
57
58
59 ti.ReparseTag = 0
60 } else {
61 return nil, &PathError{Op: "GetFileInformationByHandleEx", Path: path, Err: err}
62 }
63 }
64
65 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,
76 ReparseTag: ti.ReparseTag,
77
78
79
80 }, nil
81 }
82
83
84
85 func newFileStatFromWin32finddata(d *syscall.Win32finddata) *fileStat {
86 fs := &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 }
94 if d.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
95
96
97
98
99 fs.ReparseTag = d.Reserved0
100 }
101 return fs
102 }
103
104 func (fs *fileStat) isSymlink() bool {
105
106
107
108
109
110
111
112
113
114
115
116
117 return fs.ReparseTag == syscall.IO_REPARSE_TAG_SYMLINK ||
118 fs.ReparseTag == windows.IO_REPARSE_TAG_MOUNT_POINT
119 }
120
121 func (fs *fileStat) Size() int64 {
122 return int64(fs.FileSizeHigh)<<32 + int64(fs.FileSizeLow)
123 }
124
125 func (fs *fileStat) Mode() (m FileMode) {
126 if fs.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
127 m |= 0444
128 } else {
129 m |= 0666
130 }
131 if fs.isSymlink() {
132 return m | ModeSymlink
133 }
134 if fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
135 m |= ModeDir | 0111
136 }
137 switch fs.filetype {
138 case syscall.FILE_TYPE_PIPE:
139 m |= ModeNamedPipe
140 case syscall.FILE_TYPE_CHAR:
141 m |= ModeDevice | ModeCharDevice
142 }
143 if fs.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 && m&ModeType == 0 {
144 if fs.ReparseTag == windows.IO_REPARSE_TAG_DEDUP {
145
146
147
148
149
150
151
152
153
154
155
156
157
158 } else {
159 m |= ModeIrregular
160 }
161 }
162 return m
163 }
164
165 func (fs *fileStat) ModTime() time.Time {
166 return time.Unix(0, fs.LastWriteTime.Nanoseconds())
167 }
168
169
170 func (fs *fileStat) Sys() any {
171 return &syscall.Win32FileAttributeData{
172 FileAttributes: fs.FileAttributes,
173 CreationTime: fs.CreationTime,
174 LastAccessTime: fs.LastAccessTime,
175 LastWriteTime: fs.LastWriteTime,
176 FileSizeHigh: fs.FileSizeHigh,
177 FileSizeLow: fs.FileSizeLow,
178 }
179 }
180
181 func (fs *fileStat) loadFileId() error {
182 fs.Lock()
183 defer fs.Unlock()
184 if fs.path == "" {
185
186 return nil
187 }
188 var path string
189 if fs.appendNameToPath {
190 path = fs.path + `\` + fs.name
191 } else {
192 path = fs.path
193 }
194 pathp, err := syscall.UTF16PtrFromString(path)
195 if err != nil {
196 return err
197 }
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213 attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS | syscall.FILE_FLAG_OPEN_REPARSE_POINT)
214
215 h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0)
216 if err != nil {
217 return err
218 }
219 defer syscall.CloseHandle(h)
220 var i syscall.ByHandleFileInformation
221 err = syscall.GetFileInformationByHandle(h, &i)
222 if err != nil {
223 return err
224 }
225 fs.path = ""
226 fs.vol = i.VolumeSerialNumber
227 fs.idxhi = i.FileIndexHigh
228 fs.idxlo = i.FileIndexLow
229 return nil
230 }
231
232
233
234 func (fs *fileStat) saveInfoFromPath(path string) error {
235 fs.path = path
236 if !isAbs(fs.path) {
237 var err error
238 fs.path, err = syscall.FullPath(fs.path)
239 if err != nil {
240 return &PathError{Op: "FullPath", Path: path, Err: err}
241 }
242 }
243 fs.name = basename(path)
244 return nil
245 }
246
247 func sameFile(fs1, fs2 *fileStat) bool {
248 e := fs1.loadFileId()
249 if e != nil {
250 return false
251 }
252 e = fs2.loadFileId()
253 if e != nil {
254 return false
255 }
256 return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo
257 }
258
259
260 func atime(fi FileInfo) time.Time {
261 return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
262 }
263
View as plain text