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