5
5
package os
6
6
7
7
import (
8
- "internal/syscall/windows"
9
8
"syscall"
10
- "unsafe"
11
9
)
12
10
13
11
// Stat returns the FileInfo structure describing file.
@@ -34,26 +32,12 @@ func (file *File) Stat() (FileInfo, error) {
34
32
return & fileStat {name : basename (file .name ), filetype : ft }, nil
35
33
}
36
34
37
- var d syscall.ByHandleFileInformation
38
- err = file .pfd .GetFileInformationByHandle (& d )
35
+ fs , err := newFileStatFromGetFileInformationByHandle (file .name , file .pfd .Sysfd )
39
36
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
37
+ return nil , err
38
+ }
39
+ fs .filetype = ft
40
+ return fs , err
57
41
}
58
42
59
43
// statNolog implements Stat for Windows.
@@ -68,91 +52,27 @@ func statNolog(name string) (FileInfo, error) {
68
52
if err != nil {
69
53
return nil , & PathError {"Stat" , name , err }
70
54
}
71
- // Apparently (see https://golang.org/issues/19922#issuecomment-300031421)
72
- // GetFileAttributesEx is fastest approach to get file info.
73
- // It does not work for symlinks. But symlinks are rare,
74
- // so try GetFileAttributesEx first.
75
- var fs fileStat
76
- err = syscall .GetFileAttributesEx (namep , syscall .GetFileExInfoStandard , (* byte )(unsafe .Pointer (& fs .sys )))
77
- if err == nil && fs .sys .FileAttributes & syscall .FILE_ATTRIBUTE_REPARSE_POINT == 0 {
78
- fs .path = name
79
- if ! isAbs (fs .path ) {
80
- fs .path , err = syscall .FullPath (fs .path )
81
- if err != nil {
82
- return nil , & PathError {"FullPath" , name , err }
83
- }
55
+ fs , err := newFileStatFromGetFileAttributesExOrFindFirstFile (name , namep )
56
+ if err != nil {
57
+ return nil , err
58
+ }
59
+ if ! fs .isSymlink () {
60
+ err = fs .updatePathAndName (name )
61
+ if err != nil {
62
+ return nil , err
84
63
}
85
- fs .name = basename (name )
86
- return & fs , nil
64
+ return fs , nil
87
65
}
88
66
// Use Windows I/O manager to dereference the symbolic link, as per
89
67
// https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/
90
68
h , err := syscall .CreateFile (namep , 0 , 0 , nil ,
91
69
syscall .OPEN_EXISTING , syscall .FILE_FLAG_BACKUP_SEMANTICS , 0 )
92
70
if err != nil {
93
- if err == windows .ERROR_SHARING_VIOLATION {
94
- // try FindFirstFile now that CreateFile failed
95
- return statWithFindFirstFile (name , namep )
96
- }
97
71
return nil , & PathError {"CreateFile" , name , err }
98
72
}
99
73
defer syscall .CloseHandle (h )
100
74
101
- var d syscall.ByHandleFileInformation
102
- err = syscall .GetFileInformationByHandle (h , & d )
103
- if err != nil {
104
- return nil , & PathError {"GetFileInformationByHandle" , name , err }
105
- }
106
- return & fileStat {
107
- name : basename (name ),
108
- sys : syscall.Win32FileAttributeData {
109
- FileAttributes : d .FileAttributes ,
110
- CreationTime : d .CreationTime ,
111
- LastAccessTime : d .LastAccessTime ,
112
- LastWriteTime : d .LastWriteTime ,
113
- FileSizeHigh : d .FileSizeHigh ,
114
- FileSizeLow : d .FileSizeLow ,
115
- },
116
- vol : d .VolumeSerialNumber ,
117
- idxhi : d .FileIndexHigh ,
118
- idxlo : d .FileIndexLow ,
119
- // fileStat.path is used by os.SameFile to decide if it needs
120
- // to fetch vol, idxhi and idxlo. But these are already set,
121
- // so set fileStat.path to "" to prevent os.SameFile doing it again.
122
- // Also do not set fileStat.filetype, because it is only used for
123
- // console and stdin/stdout. But you cannot call os.Stat for these.
124
- }, nil
125
- }
126
-
127
- // statWithFindFirstFile is used by Stat to handle special case of statting
128
- // c:\pagefile.sys. We might discover that other files need similar treatment.
129
- func statWithFindFirstFile (name string , namep * uint16 ) (FileInfo , error ) {
130
- var fd syscall.Win32finddata
131
- h , err := syscall .FindFirstFile (namep , & fd )
132
- if err != nil {
133
- return nil , & PathError {"FindFirstFile" , name , err }
134
- }
135
- syscall .FindClose (h )
136
-
137
- fullpath := name
138
- if ! isAbs (fullpath ) {
139
- fullpath , err = syscall .FullPath (fullpath )
140
- if err != nil {
141
- return nil , & PathError {"FullPath" , name , err }
142
- }
143
- }
144
- return & fileStat {
145
- name : basename (name ),
146
- path : fullpath ,
147
- sys : syscall.Win32FileAttributeData {
148
- FileAttributes : fd .FileAttributes ,
149
- CreationTime : fd .CreationTime ,
150
- LastAccessTime : fd .LastAccessTime ,
151
- LastWriteTime : fd .LastWriteTime ,
152
- FileSizeHigh : fd .FileSizeHigh ,
153
- FileSizeLow : fd .FileSizeLow ,
154
- },
155
- }, nil
75
+ return newFileStatFromGetFileInformationByHandle (name , h )
156
76
}
157
77
158
78
// lstatNolog implements Lstat for Windows.
@@ -163,25 +83,17 @@ func lstatNolog(name string) (FileInfo, error) {
163
83
if name == DevNull {
164
84
return & devNullStat , nil
165
85
}
166
- fs := & fileStat {name : basename (name )}
167
- namep , e := syscall .UTF16PtrFromString (fixLongPath (name ))
168
- if e != nil {
169
- return nil , & PathError {"Lstat" , name , e }
86
+ namep , err := syscall .UTF16PtrFromString (fixLongPath (name ))
87
+ if err != nil {
88
+ return nil , & PathError {"Lstat" , name , err }
89
+ }
90
+ fs , err := newFileStatFromGetFileAttributesExOrFindFirstFile (name , namep )
91
+ if err != nil {
92
+ return nil , err
170
93
}
171
- e = syscall .GetFileAttributesEx (namep , syscall .GetFileExInfoStandard , (* byte )(unsafe .Pointer (& fs .sys )))
172
- if e != nil {
173
- if e != windows .ERROR_SHARING_VIOLATION {
174
- return nil , & PathError {"GetFileAttributesEx" , name , e }
175
- }
176
- // try FindFirstFile now that GetFileAttributesEx failed
177
- return statWithFindFirstFile (name , namep )
178
- }
179
- fs .path = name
180
- if ! isAbs (fs .path ) {
181
- fs .path , e = syscall .FullPath (fs .path )
182
- if e != nil {
183
- return nil , & PathError {"FullPath" , name , e }
184
- }
94
+ err = fs .updatePathAndName (name )
95
+ if err != nil {
96
+ return nil , err
185
97
}
186
98
return fs , nil
187
99
}
0 commit comments