Source file
src/os/root_windows.go
1
2
3
4
5
6
7 package os
8
9 import (
10 "errors"
11 "internal/filepathlite"
12 "internal/stringslite"
13 "internal/syscall/windows"
14 "runtime"
15 "syscall"
16 "unsafe"
17 )
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 func rootCleanPath(s string, prefix, suffix []string) (string, error) {
39
40 if stringslite.IndexByte(s, '?') >= 0 {
41 return "", windows.ERROR_INVALID_NAME
42 }
43
44 const fixedPrefix = `\\?\?`
45 buf := []byte(fixedPrefix)
46 for _, p := range prefix {
47 buf = append(buf, '\\')
48 buf = append(buf, []byte(p)...)
49 }
50 buf = append(buf, '\\')
51 buf = append(buf, []byte(s)...)
52 for _, p := range suffix {
53 buf = append(buf, '\\')
54 buf = append(buf, []byte(p)...)
55 }
56 s = string(buf)
57
58 s, err := syscall.FullPath(s)
59 if err != nil {
60 return "", err
61 }
62
63 s, ok := stringslite.CutPrefix(s, fixedPrefix)
64 if !ok {
65 return "", errPathEscapes
66 }
67 s = stringslite.TrimPrefix(s, `\`)
68 if s == "" {
69 s = "."
70 }
71
72 if !filepathlite.IsLocal(s) {
73 return "", errPathEscapes
74 }
75
76 return s, nil
77 }
78
79 type sysfdType = syscall.Handle
80
81
82 func openRootNolog(name string) (*Root, error) {
83 if name == "" {
84 return nil, &PathError{Op: "open", Path: name, Err: syscall.ENOENT}
85 }
86 path := fixLongPath(name)
87 fd, err := syscall.Open(path, syscall.O_RDONLY|syscall.O_CLOEXEC, 0)
88 if err != nil {
89 return nil, &PathError{Op: "open", Path: name, Err: err}
90 }
91 return newRoot(fd, name)
92 }
93
94
95
96 func newRoot(fd syscall.Handle, name string) (*Root, error) {
97
98
99
100
101 var fi syscall.ByHandleFileInformation
102 err := syscall.GetFileInformationByHandle(fd, &fi)
103 if err == nil && fi.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 {
104 syscall.CloseHandle(fd)
105 return nil, &PathError{Op: "open", Path: name, Err: errors.New("not a directory")}
106 }
107
108 r := &Root{root{
109 fd: fd,
110 name: name,
111 }}
112 runtime.SetFinalizer(&r.root, (*root).Close)
113 return r, nil
114 }
115
116
117 func openRootInRoot(r *Root, name string) (*Root, error) {
118 fd, err := doInRoot(r, name, rootOpenDir)
119 if err != nil {
120 return nil, &PathError{Op: "openat", Path: name, Err: err}
121 }
122 return newRoot(fd, name)
123 }
124
125
126 func rootOpenFileNolog(root *Root, name string, flag int, perm FileMode) (*File, error) {
127 fd, err := doInRoot(root, name, func(parent syscall.Handle, name string) (syscall.Handle, error) {
128 return openat(parent, name, flag, perm)
129 })
130 if err != nil {
131 return nil, &PathError{Op: "openat", Path: name, Err: err}
132 }
133 return newFile(fd, joinPath(root.Name(), name), "file"), nil
134 }
135
136 func openat(dirfd syscall.Handle, name string, flag int, perm FileMode) (syscall.Handle, error) {
137 h, err := windows.Openat(dirfd, name, flag|syscall.O_CLOEXEC|windows.O_NOFOLLOW_ANY, syscallMode(perm))
138 if err == syscall.ELOOP || err == syscall.ENOTDIR {
139 if link, err := readReparseLinkAt(dirfd, name); err == nil {
140 return syscall.InvalidHandle, errSymlink(link)
141 }
142 }
143 return h, err
144 }
145
146 func readReparseLinkAt(dirfd syscall.Handle, name string) (string, error) {
147 objectName, err := windows.NewNTUnicodeString(name)
148 if err != nil {
149 return "", err
150 }
151 objAttrs := &windows.OBJECT_ATTRIBUTES{
152 ObjectName: objectName,
153 }
154 if dirfd != syscall.InvalidHandle {
155 objAttrs.RootDirectory = dirfd
156 }
157 objAttrs.Length = uint32(unsafe.Sizeof(*objAttrs))
158 var h syscall.Handle
159 err = windows.NtCreateFile(
160 &h,
161 windows.FILE_GENERIC_READ,
162 objAttrs,
163 &windows.IO_STATUS_BLOCK{},
164 nil,
165 uint32(syscall.FILE_ATTRIBUTE_NORMAL),
166 syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
167 windows.FILE_OPEN,
168 windows.FILE_SYNCHRONOUS_IO_NONALERT|windows.FILE_OPEN_REPARSE_POINT,
169 0,
170 0,
171 )
172 if err != nil {
173 return "", err
174 }
175 defer syscall.CloseHandle(h)
176 return readReparseLinkHandle(h)
177 }
178
179 func rootOpenDir(parent syscall.Handle, name string) (syscall.Handle, error) {
180 h, err := openat(parent, name, syscall.O_RDONLY|syscall.O_CLOEXEC|windows.O_DIRECTORY, 0)
181 if err == syscall.ERROR_FILE_NOT_FOUND {
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196 err = syscall.ERROR_PATH_NOT_FOUND
197 }
198 return h, err
199 }
200
201 func rootStat(r *Root, name string, lstat bool) (FileInfo, error) {
202 if len(name) > 0 && IsPathSeparator(name[len(name)-1]) {
203
204
205
206
207
208 lstat = false
209 }
210 fi, err := doInRoot(r, name, func(parent syscall.Handle, n string) (FileInfo, error) {
211 fd, err := openat(parent, n, windows.O_OPEN_REPARSE, 0)
212 if err != nil {
213 return nil, err
214 }
215 defer syscall.CloseHandle(fd)
216 fi, err := statHandle(name, fd)
217 if err != nil {
218 return nil, err
219 }
220 if !lstat && fi.(*fileStat).isReparseTagNameSurrogate() {
221 link, err := readReparseLinkHandle(fd)
222 if err != nil {
223 return nil, err
224 }
225 return nil, errSymlink(link)
226 }
227 return fi, nil
228 })
229 if err != nil {
230 return nil, &PathError{Op: "statat", Path: name, Err: err}
231 }
232 return fi, nil
233 }
234
235 func mkdirat(dirfd syscall.Handle, name string, perm FileMode) error {
236 return windows.Mkdirat(dirfd, name, syscallMode(perm))
237 }
238
239 func removeat(dirfd syscall.Handle, name string) error {
240 return windows.Deleteat(dirfd, name)
241 }
242
View as plain text