liyujie
2025-08-28 d9927380ed7c8366f762049be9f3fee225860833
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
 
package os
 
import (
   "internal/syscall/windows"
   "syscall"
   "unsafe"
)
 
// isNulName reports whether name is NUL file name.
// For example, it returns true for both "NUL" and "nul".
func isNulName(name string) bool {
   if len(name) != 3 {
       return false
   }
   if name[0] != 'n' && name[0] != 'N' {
       return false
   }
   if name[1] != 'u' && name[1] != 'U' {
       return false
   }
   if name[2] != 'l' && name[2] != 'L' {
       return false
   }
   return true
}
 
// Stat returns the FileInfo structure describing file.
// If there is an error, it will be of type *PathError.
func (file *File) Stat() (FileInfo, error) {
   if file == nil {
       return nil, ErrInvalid
   }
 
   if file.isdir() {
       // I don't know any better way to do that for directory
       return Stat(file.dirinfo.path)
   }
   if isNulName(file.name) {
       return &devNullStat, nil
   }
 
   ft, err := file.pfd.GetFileType()
   if err != nil {
       return nil, &PathError{"GetFileType", file.name, err}
   }
   switch ft {
   case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR:
       return &fileStat{name: basename(file.name), filetype: ft}, nil
   }
 
   fs, err := newFileStatFromGetFileInformationByHandle(file.name, file.pfd.Sysfd)
   if err != nil {
       return nil, err
   }
   fs.filetype = ft
   return fs, err
}
 
// stat implements both Stat and Lstat of a file.
func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) {
   if len(name) == 0 {
       return nil, &PathError{funcname, name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)}
   }
   if isNulName(name) {
       return &devNullStat, nil
   }
   namep, err := syscall.UTF16PtrFromString(fixLongPath(name))
   if err != nil {
       return nil, &PathError{funcname, name, err}
   }
 
   // Try GetFileAttributesEx first, because it is faster than CreateFile.
   // See https://golang.org/issues/19922#issuecomment-300031421 for details.
   var fa syscall.Win32FileAttributeData
   err = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
   if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
       // Not a symlink.
       fs := &fileStat{
           path:           name,
           FileAttributes: fa.FileAttributes,
           CreationTime:   fa.CreationTime,
           LastAccessTime: fa.LastAccessTime,
           LastWriteTime:  fa.LastWriteTime,
           FileSizeHigh:   fa.FileSizeHigh,
           FileSizeLow:    fa.FileSizeLow,
       }
       // Gather full path to be used by os.SameFile later.
       if !isAbs(fs.path) {
           fs.path, err = syscall.FullPath(fs.path)
           if err != nil {
               return nil, &PathError{"FullPath", name, err}
           }
       }
       fs.name = basename(name)
       return fs, nil
   }
   // GetFileAttributesEx fails with ERROR_SHARING_VIOLATION error for
   // files, like c:\pagefile.sys. Use FindFirstFile for such files.
   if err == windows.ERROR_SHARING_VIOLATION {
       var fd syscall.Win32finddata
       sh, err := syscall.FindFirstFile(namep, &fd)
       if err != nil {
           return nil, &PathError{"FindFirstFile", name, err}
       }
       syscall.FindClose(sh)
       return newFileStatFromWin32finddata(&fd), nil
   }
 
   // Finally use CreateFile.
   h, err := syscall.CreateFile(namep, 0, 0, nil,
       syscall.OPEN_EXISTING, createFileAttrs, 0)
   if err != nil {
       return nil, &PathError{"CreateFile", name, err}
   }
   defer syscall.CloseHandle(h)
 
   return newFileStatFromGetFileInformationByHandle(name, h)
}
 
// statNolog implements Stat for Windows.
func statNolog(name string) (FileInfo, error) {
   return stat("Stat", name, syscall.FILE_FLAG_BACKUP_SEMANTICS)
}
 
// lstatNolog implements Lstat for Windows.
func lstatNolog(name string) (FileInfo, error) {
   attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
   // Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
   // See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted
   attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
   return stat("Lstat", name, attrs)
}