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.
8 "internal/syscall/windows"
15 // A fileStat is the implementation of FileInfo returned by Stat and Lstat.
16 type fileStat
struct {
19 // from ByHandleFileInformation, Win32FileAttributeData and Win32finddata
21 CreationTime syscall
.Filetime
22 LastAccessTime syscall
.Filetime
23 LastWriteTime syscall
.Filetime
30 // what syscall.GetFileType returns
33 // used to implement SameFile
42 // newFileStatFromGetFileInformationByHandle calls GetFileInformationByHandle
43 // to gather all required information about the file handle h.
44 func newFileStatFromGetFileInformationByHandle(path
string, h syscall
.Handle
) (fs
*fileStat
, err error
) {
45 var d syscall
.ByHandleFileInformation
46 err
= syscall
.GetFileInformationByHandle(h
, &d
)
48 return nil, &PathError
{"GetFileInformationByHandle", path
, err
}
51 var ti windows
.FILE_ATTRIBUTE_TAG_INFO
52 err
= windows
.GetFileInformationByHandleEx(h
, windows
.FileAttributeTagInfo
, (*byte)(unsafe
.Pointer(&ti
)), uint32(unsafe
.Sizeof(ti
)))
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.
61 return nil, &PathError
{"GetFileInformationByHandleEx", path
, err
}
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 Reserved0
: ti
.ReparseTag
,
77 // 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.
83 // newFileStatFromWin32finddata copies all required information
84 // from syscall.Win32finddata d into the newly created fileStat.
85 func newFileStatFromWin32finddata(d
*syscall
.Win32finddata
) *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
,
97 func (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 {
104 return fs
.Reserved0
== syscall
.IO_REPARSE_TAG_SYMLINK ||
105 fs
.Reserved0
== windows
.IO_REPARSE_TAG_MOUNT_POINT
108 func (fs
*fileStat
) Size() int64 {
109 return int64(fs
.FileSizeHigh
)<<32 + int64(fs
.FileSizeLow
)
112 func (fs
*fileStat
) Mode() (m FileMode
) {
113 if fs
== &devNullStat
{
114 return ModeDevice | ModeCharDevice |
0666
116 if fs
.FileAttributes
&syscall
.FILE_ATTRIBUTE_READONLY
!= 0 {
122 return m | ModeSymlink
124 if fs
.FileAttributes
&syscall
.FILE_ATTRIBUTE_DIRECTORY
!= 0 {
128 case syscall
.FILE_TYPE_PIPE
:
130 case syscall
.FILE_TYPE_CHAR
:
131 m |
= ModeDevice | ModeCharDevice
136 func (fs
*fileStat
) ModTime() time
.Time
{
137 return time
.Unix(0, fs
.LastWriteTime
.Nanoseconds())
140 // Sys returns syscall.Win32FileAttributeData for file fs.
141 func (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
,
152 func (fs
*fileStat
) loadFileId() error
{
160 if fs
.appendNameToPath
{
161 path
= fs
.path
+ `\` + fs
.name
165 pathp
, err
:= syscall
.UTF16PtrFromString(path
)
169 attrs
:= uint32(syscall
.FILE_FLAG_BACKUP_SEMANTICS
)
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
175 h
, err
:= syscall
.CreateFile(pathp
, 0, 0, nil, syscall
.OPEN_EXISTING
, attrs
, 0)
179 defer syscall
.CloseHandle(h
)
180 var i syscall
.ByHandleFileInformation
181 err
= syscall
.GetFileInformationByHandle(h
, &i
)
186 fs
.vol
= i
.VolumeSerialNumber
187 fs
.idxhi
= i
.FileIndexHigh
188 fs
.idxlo
= i
.FileIndexLow
192 // saveInfoFromPath saves full path of the file to be used by os.SameFile later,
193 // and set name from path.
194 func (fs
*fileStat
) saveInfoFromPath(path
string) error
{
198 fs
.path
, err
= syscall
.FullPath(fs
.path
)
200 return &PathError
{"FullPath", path
, err
}
203 fs
.name
= basename(path
)
207 // devNullStat is fileStat structure describing DevNull file ("NUL").
208 var devNullStat
= fileStat
{
210 // hopefully this will work for SameFile
216 func sameFile(fs1
, fs2
*fileStat
) bool {
217 e
:= fs1
.loadFileId()
225 return fs1
.vol
== fs2
.vol
&& fs1
.idxhi
== fs2
.idxhi
&& fs1
.idxlo
== fs2
.idxlo
229 func atime(fi FileInfo
) time
.Time
{
230 return time
.Unix(0, fi
.Sys().(*syscall
.Win32FileAttributeData
).LastAccessTime
.Nanoseconds())