1
2
3
4
5 package windows
6
7 import (
8 "runtime"
9 "structs"
10 "syscall"
11 "unsafe"
12 )
13
14
15 const (
16 O_DIRECTORY = 0x04000
17 )
18
19
20
21
22
23
24
25
26
27 const (
28 O_NOFOLLOW_ANY = 0x200000000
29 O_OPEN_REPARSE = 0x400000000
30 O_WRITE_ATTRS = 0x800000000
31 )
32
33 func Openat(dirfd syscall.Handle, name string, flag uint64, perm uint32) (_ syscall.Handle, e1 error) {
34 if len(name) == 0 {
35 return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
36 }
37
38 var access, options uint32
39 switch flag & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
40 case syscall.O_RDONLY:
41
42 access = FILE_GENERIC_READ
43 case syscall.O_WRONLY:
44 access = FILE_GENERIC_WRITE
45 options |= FILE_NON_DIRECTORY_FILE
46 case syscall.O_RDWR:
47 access = FILE_GENERIC_READ | FILE_GENERIC_WRITE
48 options |= FILE_NON_DIRECTORY_FILE
49 default:
50
51
52 access = SYNCHRONIZE
53 }
54 if flag&syscall.O_CREAT != 0 {
55 access |= FILE_GENERIC_WRITE
56 }
57 if flag&syscall.O_APPEND != 0 {
58 access |= FILE_APPEND_DATA
59
60
61 if flag&syscall.O_TRUNC == 0 {
62 access &^= FILE_WRITE_DATA
63 }
64 }
65 if flag&O_DIRECTORY != 0 {
66 options |= FILE_DIRECTORY_FILE
67 access |= FILE_LIST_DIRECTORY
68 }
69 if flag&syscall.O_SYNC != 0 {
70 options |= FILE_WRITE_THROUGH
71 }
72 if flag&O_WRITE_ATTRS != 0 {
73 access |= FILE_WRITE_ATTRIBUTES
74 }
75
76 access |= STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | FILE_READ_EA
77
78 objAttrs := &OBJECT_ATTRIBUTES{}
79 if flag&O_NOFOLLOW_ANY != 0 {
80 objAttrs.Attributes |= OBJ_DONT_REPARSE
81 }
82 if flag&syscall.O_CLOEXEC == 0 {
83 objAttrs.Attributes |= OBJ_INHERIT
84 }
85 if err := objAttrs.init(dirfd, name); err != nil {
86 return syscall.InvalidHandle, err
87 }
88
89 if flag&O_OPEN_REPARSE != 0 {
90 options |= FILE_OPEN_REPARSE_POINT
91 }
92
93
94
95
96
97
98 var disposition uint32
99 switch {
100 case flag&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
101 disposition = FILE_CREATE
102 options |= FILE_OPEN_REPARSE_POINT
103 case flag&syscall.O_CREAT == syscall.O_CREAT:
104 disposition = FILE_OPEN_IF
105 default:
106 disposition = FILE_OPEN
107 }
108
109 fileAttrs := uint32(FILE_ATTRIBUTE_NORMAL)
110 if perm&syscall.S_IWRITE == 0 {
111 fileAttrs = FILE_ATTRIBUTE_READONLY
112 }
113
114 var h syscall.Handle
115 err := NtCreateFile(
116 &h,
117 SYNCHRONIZE|access,
118 objAttrs,
119 &IO_STATUS_BLOCK{},
120 nil,
121 fileAttrs,
122 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
123 disposition,
124 FILE_SYNCHRONOUS_IO_NONALERT|FILE_OPEN_FOR_BACKUP_INTENT|options,
125 nil,
126 0,
127 )
128 if err != nil {
129 return h, ntCreateFileError(err, flag)
130 }
131
132 if flag&syscall.O_TRUNC != 0 {
133 err = syscall.Ftruncate(h, 0)
134 if err != nil {
135 syscall.CloseHandle(h)
136 return syscall.InvalidHandle, err
137 }
138 }
139
140 return h, nil
141 }
142
143
144 func ntCreateFileError(err error, flag uint64) error {
145 s, ok := err.(NTStatus)
146 if !ok {
147
148 return err
149 }
150 switch s {
151 case STATUS_REPARSE_POINT_ENCOUNTERED:
152 return syscall.ELOOP
153 case STATUS_NOT_A_DIRECTORY:
154
155
156
157
158
159
160
161
162 if flag&O_DIRECTORY != 0 {
163 return syscall.ENOTDIR
164 }
165 case STATUS_FILE_IS_A_DIRECTORY:
166 return syscall.EISDIR
167 case STATUS_OBJECT_NAME_COLLISION:
168 return syscall.EEXIST
169 }
170 return s.Errno()
171 }
172
173 func Mkdirat(dirfd syscall.Handle, name string, mode uint32) error {
174 objAttrs := &OBJECT_ATTRIBUTES{}
175 if err := objAttrs.init(dirfd, name); err != nil {
176 return err
177 }
178 var h syscall.Handle
179 err := NtCreateFile(
180 &h,
181 FILE_GENERIC_READ,
182 objAttrs,
183 &IO_STATUS_BLOCK{},
184 nil,
185 syscall.FILE_ATTRIBUTE_NORMAL,
186 syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
187 FILE_CREATE,
188 FILE_DIRECTORY_FILE,
189 nil,
190 0,
191 )
192 if err != nil {
193 return ntCreateFileError(err, 0)
194 }
195 syscall.CloseHandle(h)
196 return nil
197 }
198
199 func Deleteat(dirfd syscall.Handle, name string, options uint32) error {
200 if name == "." {
201
202
203 return syscall.EINVAL
204 }
205 objAttrs := &OBJECT_ATTRIBUTES{}
206 if err := objAttrs.init(dirfd, name); err != nil {
207 return err
208 }
209 var h syscall.Handle
210 err := NtOpenFile(
211 &h,
212 SYNCHRONIZE|FILE_READ_ATTRIBUTES|DELETE,
213 objAttrs,
214 &IO_STATUS_BLOCK{},
215 FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
216 FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT|options,
217 )
218 if err != nil {
219 return ntCreateFileError(err, 0)
220 }
221 defer syscall.CloseHandle(h)
222
223 if TestDeleteatFallback {
224 return deleteatFallback(h)
225 }
226
227 const FileDispositionInformationEx = 64
228
229
230
231
232
233
234
235
236
237
238
239 err = NtSetInformationFile(
240 h,
241 &IO_STATUS_BLOCK{},
242 unsafe.Pointer(&FILE_DISPOSITION_INFORMATION_EX{
243 Flags: FILE_DISPOSITION_DELETE |
244 FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK |
245 FILE_DISPOSITION_POSIX_SEMANTICS |
246
247
248
249 FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
250 }),
251 uint32(unsafe.Sizeof(FILE_DISPOSITION_INFORMATION_EX{})),
252 FileDispositionInformationEx,
253 )
254 switch err {
255 case nil:
256 return nil
257 case STATUS_INVALID_INFO_CLASS,
258 STATUS_INVALID_PARAMETER,
259 STATUS_NOT_SUPPORTED:
260 return deleteatFallback(h)
261 default:
262 return err.(NTStatus).Errno()
263 }
264 }
265
266
267
268 var TestDeleteatFallback bool
269
270
271
272
273 func deleteatFallback(h syscall.Handle) error {
274 var data syscall.ByHandleFileInformation
275 if err := syscall.GetFileInformationByHandle(h, &data); err == nil && data.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
276
277
278 wh, err := ReOpenFile(h,
279 FILE_WRITE_ATTRIBUTES,
280 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
281 syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS,
282 )
283 if err != nil {
284 return err
285 }
286 err = SetFileInformationByHandle(
287 wh,
288 FileBasicInfo,
289 unsafe.Pointer(&FILE_BASIC_INFO{
290 FileAttributes: data.FileAttributes &^ FILE_ATTRIBUTE_READONLY,
291 }),
292 uint32(unsafe.Sizeof(FILE_BASIC_INFO{})),
293 )
294 syscall.CloseHandle(wh)
295 if err != nil {
296 return err
297 }
298 }
299
300 return SetFileInformationByHandle(
301 h,
302 FileDispositionInfo,
303 unsafe.Pointer(&FILE_DISPOSITION_INFO{
304 DeleteFile: true,
305 }),
306 uint32(unsafe.Sizeof(FILE_DISPOSITION_INFO{})),
307 )
308 }
309
310 func Renameat(olddirfd syscall.Handle, oldpath string, newdirfd syscall.Handle, newpath string) error {
311 objAttrs := &OBJECT_ATTRIBUTES{}
312 if err := objAttrs.init(olddirfd, oldpath); err != nil {
313 return err
314 }
315 var h syscall.Handle
316 err := NtOpenFile(
317 &h,
318 SYNCHRONIZE|DELETE,
319 objAttrs,
320 &IO_STATUS_BLOCK{},
321 FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
322 FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT,
323 )
324 if err != nil {
325 return ntCreateFileError(err, 0)
326 }
327 defer syscall.CloseHandle(h)
328
329 renameInfoEx := FILE_RENAME_INFORMATION_EX{
330 Flags: FILE_RENAME_REPLACE_IF_EXISTS |
331 FILE_RENAME_POSIX_SEMANTICS,
332 RootDirectory: newdirfd,
333 }
334 p16, err := syscall.UTF16FromString(newpath)
335 if err != nil {
336 return err
337 }
338 if len(p16) > len(renameInfoEx.FileName) {
339 return syscall.EINVAL
340 }
341 copy(renameInfoEx.FileName[:], p16)
342 renameInfoEx.FileNameLength = uint32((len(p16) - 1) * 2)
343
344 const (
345 FileRenameInformation = 10
346 FileRenameInformationEx = 65
347 )
348 err = NtSetInformationFile(
349 h,
350 &IO_STATUS_BLOCK{},
351 unsafe.Pointer(&renameInfoEx),
352 uint32(unsafe.Sizeof(FILE_RENAME_INFORMATION_EX{})),
353 FileRenameInformationEx,
354 )
355 if err == nil {
356 return nil
357 }
358
359
360
361
362
363
364 renameInfo := FILE_RENAME_INFORMATION{
365 ReplaceIfExists: true,
366 RootDirectory: newdirfd,
367 }
368 copy(renameInfo.FileName[:], p16)
369 renameInfo.FileNameLength = renameInfoEx.FileNameLength
370
371 err = NtSetInformationFile(
372 h,
373 &IO_STATUS_BLOCK{},
374 unsafe.Pointer(&renameInfo),
375 uint32(unsafe.Sizeof(FILE_RENAME_INFORMATION{})),
376 FileRenameInformation,
377 )
378 if st, ok := err.(NTStatus); ok {
379 return st.Errno()
380 }
381 return err
382 }
383
384 func Linkat(olddirfd syscall.Handle, oldpath string, newdirfd syscall.Handle, newpath string) error {
385 objAttrs := &OBJECT_ATTRIBUTES{}
386 if err := objAttrs.init(olddirfd, oldpath); err != nil {
387 return err
388 }
389 var h syscall.Handle
390 err := NtOpenFile(
391 &h,
392 SYNCHRONIZE|FILE_WRITE_ATTRIBUTES,
393 objAttrs,
394 &IO_STATUS_BLOCK{},
395 FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
396 FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT,
397 )
398 if err != nil {
399 return ntCreateFileError(err, 0)
400 }
401 defer syscall.CloseHandle(h)
402
403 linkInfo := FILE_LINK_INFORMATION{
404 RootDirectory: newdirfd,
405 }
406 p16, err := syscall.UTF16FromString(newpath)
407 if err != nil {
408 return err
409 }
410 if len(p16) > len(linkInfo.FileName) {
411 return syscall.EINVAL
412 }
413 copy(linkInfo.FileName[:], p16)
414 linkInfo.FileNameLength = uint32((len(p16) - 1) * 2)
415
416 const (
417 FileLinkInformation = 11
418 )
419 err = NtSetInformationFile(
420 h,
421 &IO_STATUS_BLOCK{},
422 unsafe.Pointer(&linkInfo),
423 uint32(unsafe.Sizeof(FILE_LINK_INFORMATION{})),
424 FileLinkInformation,
425 )
426 if st, ok := err.(NTStatus); ok {
427 return st.Errno()
428 }
429 return err
430 }
431
432
433
434
435
436
437
438
439
440
441
442 type SymlinkatFlags uint
443
444 const (
445 SYMLINKAT_DIRECTORY = SymlinkatFlags(1 << iota)
446 SYMLINKAT_RELATIVE
447 )
448
449 func Symlinkat(oldname string, newdirfd syscall.Handle, newname string, flags SymlinkatFlags) error {
450
451
452
453
454
455 return withPrivilege("SeCreateSymbolicLinkPrivilege", func() error {
456 return symlinkat(oldname, newdirfd, newname, flags)
457 })
458 }
459
460 func symlinkat(oldname string, newdirfd syscall.Handle, newname string, flags SymlinkatFlags) error {
461 oldnameu16, err := syscall.UTF16FromString(oldname)
462 if err != nil {
463 return err
464 }
465 oldnameu16 = oldnameu16[:len(oldnameu16)-1]
466
467 var options uint32
468 if flags&SYMLINKAT_DIRECTORY != 0 {
469 options |= FILE_DIRECTORY_FILE
470 } else {
471 options |= FILE_NON_DIRECTORY_FILE
472 }
473
474 objAttrs := &OBJECT_ATTRIBUTES{}
475 if err := objAttrs.init(newdirfd, newname); err != nil {
476 return err
477 }
478 var h syscall.Handle
479 err = NtCreateFile(
480 &h,
481 SYNCHRONIZE|FILE_WRITE_ATTRIBUTES|DELETE,
482 objAttrs,
483 &IO_STATUS_BLOCK{},
484 nil,
485 syscall.FILE_ATTRIBUTE_NORMAL,
486 0,
487 FILE_CREATE,
488 FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT|options,
489 nil,
490 0,
491 )
492 if err != nil {
493 return ntCreateFileError(err, 0)
494 }
495 defer syscall.CloseHandle(h)
496
497
498 type reparseDataBufferT struct {
499 _ structs.HostLayout
500
501 ReparseTag uint32
502 ReparseDataLength uint16
503 Reserved uint16
504
505 SubstituteNameOffset uint16
506 SubstituteNameLength uint16
507 PrintNameOffset uint16
508 PrintNameLength uint16
509 Flags uint32
510 }
511
512 const (
513 headerSize = uint16(unsafe.Offsetof(reparseDataBufferT{}.SubstituteNameOffset))
514 bufferSize = uint16(unsafe.Sizeof(reparseDataBufferT{}))
515 )
516
517
518 rdbbuf := make([]byte, bufferSize+uint16(2*len(oldnameu16)))
519
520 rdb := (*reparseDataBufferT)(unsafe.Pointer(&rdbbuf[0]))
521 rdb.ReparseTag = syscall.IO_REPARSE_TAG_SYMLINK
522 rdb.ReparseDataLength = uint16(len(rdbbuf)) - uint16(headerSize)
523 rdb.SubstituteNameOffset = 0
524 rdb.SubstituteNameLength = uint16(2 * len(oldnameu16))
525 rdb.PrintNameOffset = 0
526 rdb.PrintNameLength = rdb.SubstituteNameLength
527 if flags&SYMLINKAT_RELATIVE != 0 {
528 rdb.Flags = SYMLINK_FLAG_RELATIVE
529 }
530
531 namebuf := rdbbuf[bufferSize:]
532 copy(namebuf, unsafe.String((*byte)(unsafe.Pointer(&oldnameu16[0])), 2*len(oldnameu16)))
533
534 err = syscall.DeviceIoControl(
535 h,
536 FSCTL_SET_REPARSE_POINT,
537 &rdbbuf[0],
538 uint32(len(rdbbuf)),
539 nil,
540 0,
541 nil,
542 nil)
543 if err != nil {
544
545 const FileDispositionInformation = 13
546 NtSetInformationFile(
547 h,
548 &IO_STATUS_BLOCK{},
549 unsafe.Pointer(&FILE_DISPOSITION_INFORMATION{
550 DeleteFile: true,
551 }),
552 uint32(unsafe.Sizeof(FILE_DISPOSITION_INFORMATION{})),
553 FileDispositionInformation,
554 )
555 return err
556 }
557
558 return nil
559 }
560
561
562
563
564 func withPrivilege(privilege string, f func() error) error {
565 runtime.LockOSThread()
566 defer runtime.UnlockOSThread()
567
568 err := ImpersonateSelf(SecurityImpersonation)
569 if err != nil {
570 return f()
571 }
572 defer RevertToSelf()
573
574 curThread, err := GetCurrentThread()
575 if err != nil {
576 return f()
577 }
578 var token syscall.Token
579 err = OpenThreadToken(curThread, syscall.TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, false, &token)
580 if err != nil {
581 return f()
582 }
583 defer syscall.CloseHandle(syscall.Handle(token))
584
585 privStr, err := syscall.UTF16PtrFromString(privilege)
586 if err != nil {
587 return f()
588 }
589 var tokenPriv TOKEN_PRIVILEGES
590 err = LookupPrivilegeValue(nil, privStr, &tokenPriv.Privileges[0].Luid)
591 if err != nil {
592 return f()
593 }
594
595 tokenPriv.PrivilegeCount = 1
596 tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED
597 err = AdjustTokenPrivileges(token, false, &tokenPriv, 0, nil, nil)
598 if err != nil {
599 return f()
600 }
601
602 return f()
603 }
604
View as plain text