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 SYNCHRONIZE|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|FILE_SYNCHRONOUS_IO_NONALERT|options,
254 )
255 if err != nil {
256 return ntCreateFileError(err, 0)
257 }
258 defer syscall.CloseHandle(h)
259
260 if TestDeleteatFallback {
261 return deleteatFallback(h)
262 }
263
264 const FileDispositionInformationEx = 64
265
266
267
268
269
270
271
272
273
274
275
276 err = NtSetInformationFile(
277 h,
278 &IO_STATUS_BLOCK{},
279 unsafe.Pointer(&FILE_DISPOSITION_INFORMATION_EX{
280 Flags: FILE_DISPOSITION_DELETE |
281 FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK |
282 FILE_DISPOSITION_POSIX_SEMANTICS |
283
284
285
286 FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
287 }),
288 uint32(unsafe.Sizeof(FILE_DISPOSITION_INFORMATION_EX{})),
289 FileDispositionInformationEx,
290 )
291 switch err {
292 case nil:
293 return nil
294 case STATUS_INVALID_INFO_CLASS,
295 STATUS_INVALID_PARAMETER,
296 STATUS_NOT_SUPPORTED:
297 return deleteatFallback(h)
298 default:
299 return err.(NTStatus).Errno()
300 }
301 }
302
303
304
305 var TestDeleteatFallback bool
306
307
308
309
310 func deleteatFallback(h syscall.Handle) error {
311 var data syscall.ByHandleFileInformation
312 if err := syscall.GetFileInformationByHandle(h, &data); err == nil && data.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
313
314
315 wh, err := ReOpenFile(h,
316 FILE_WRITE_ATTRIBUTES,
317 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
318 syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS,
319 )
320 if err != nil {
321 return err
322 }
323 err = SetFileInformationByHandle(
324 wh,
325 FileBasicInfo,
326 unsafe.Pointer(&FILE_BASIC_INFO{
327 FileAttributes: data.FileAttributes &^ FILE_ATTRIBUTE_READONLY,
328 }),
329 uint32(unsafe.Sizeof(FILE_BASIC_INFO{})),
330 )
331 syscall.CloseHandle(wh)
332 if err != nil {
333 return err
334 }
335 }
336
337 return SetFileInformationByHandle(
338 h,
339 FileDispositionInfo,
340 unsafe.Pointer(&FILE_DISPOSITION_INFO{
341 DeleteFile: true,
342 }),
343 uint32(unsafe.Sizeof(FILE_DISPOSITION_INFO{})),
344 )
345 }
346
347 func Renameat(olddirfd syscall.Handle, oldpath string, newdirfd syscall.Handle, newpath string) error {
348 objAttrs := &OBJECT_ATTRIBUTES{}
349 if err := objAttrs.init(olddirfd, oldpath); err != nil {
350 return err
351 }
352 var h syscall.Handle
353 err := NtOpenFile(
354 &h,
355 SYNCHRONIZE|DELETE,
356 objAttrs,
357 &IO_STATUS_BLOCK{},
358 FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
359 FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT,
360 )
361 if err != nil {
362 return ntCreateFileError(err, 0)
363 }
364 defer syscall.CloseHandle(h)
365
366 renameInfoEx := FILE_RENAME_INFORMATION_EX{
367 Flags: FILE_RENAME_REPLACE_IF_EXISTS |
368 FILE_RENAME_POSIX_SEMANTICS,
369 RootDirectory: newdirfd,
370 }
371 p16, err := syscall.UTF16FromString(newpath)
372 if err != nil {
373 return err
374 }
375 if len(p16) > len(renameInfoEx.FileName) {
376 return syscall.EINVAL
377 }
378 copy(renameInfoEx.FileName[:], p16)
379 renameInfoEx.FileNameLength = uint32((len(p16) - 1) * 2)
380
381 const (
382 FileRenameInformation = 10
383 FileRenameInformationEx = 65
384 )
385 err = NtSetInformationFile(
386 h,
387 &IO_STATUS_BLOCK{},
388 unsafe.Pointer(&renameInfoEx),
389 uint32(unsafe.Sizeof(FILE_RENAME_INFORMATION_EX{})),
390 FileRenameInformationEx,
391 )
392 if err == nil {
393 return nil
394 }
395
396
397
398
399
400
401 renameInfo := FILE_RENAME_INFORMATION{
402 ReplaceIfExists: true,
403 RootDirectory: newdirfd,
404 }
405 copy(renameInfo.FileName[:], p16)
406 renameInfo.FileNameLength = renameInfoEx.FileNameLength
407
408 err = NtSetInformationFile(
409 h,
410 &IO_STATUS_BLOCK{},
411 unsafe.Pointer(&renameInfo),
412 uint32(unsafe.Sizeof(FILE_RENAME_INFORMATION{})),
413 FileRenameInformation,
414 )
415 if st, ok := err.(NTStatus); ok {
416 return st.Errno()
417 }
418 return err
419 }
420
421 func Linkat(olddirfd syscall.Handle, oldpath string, newdirfd syscall.Handle, newpath string) error {
422 objAttrs := &OBJECT_ATTRIBUTES{}
423 if err := objAttrs.init(olddirfd, oldpath); err != nil {
424 return err
425 }
426 var h syscall.Handle
427 err := NtOpenFile(
428 &h,
429 SYNCHRONIZE|FILE_WRITE_ATTRIBUTES,
430 objAttrs,
431 &IO_STATUS_BLOCK{},
432 FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
433 FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT,
434 )
435 if err != nil {
436 return ntCreateFileError(err, 0)
437 }
438 defer syscall.CloseHandle(h)
439
440 linkInfo := FILE_LINK_INFORMATION{
441 RootDirectory: newdirfd,
442 }
443 p16, err := syscall.UTF16FromString(newpath)
444 if err != nil {
445 return err
446 }
447 if len(p16) > len(linkInfo.FileName) {
448 return syscall.EINVAL
449 }
450 copy(linkInfo.FileName[:], p16)
451 linkInfo.FileNameLength = uint32((len(p16) - 1) * 2)
452
453 const (
454 FileLinkInformation = 11
455 )
456 err = NtSetInformationFile(
457 h,
458 &IO_STATUS_BLOCK{},
459 unsafe.Pointer(&linkInfo),
460 uint32(unsafe.Sizeof(FILE_LINK_INFORMATION{})),
461 FileLinkInformation,
462 )
463 if st, ok := err.(NTStatus); ok {
464 return st.Errno()
465 }
466 return err
467 }
468
469
470
471
472
473
474
475
476
477
478
479 type SymlinkatFlags uint
480
481 const (
482 SYMLINKAT_DIRECTORY = SymlinkatFlags(1 << iota)
483 SYMLINKAT_RELATIVE
484 )
485
486 func Symlinkat(oldname string, newdirfd syscall.Handle, newname string, flags SymlinkatFlags) error {
487
488
489
490
491
492 return withPrivilege("SeCreateSymbolicLinkPrivilege", func() error {
493 return symlinkat(oldname, newdirfd, newname, flags)
494 })
495 }
496
497 func symlinkat(oldname string, newdirfd syscall.Handle, newname string, flags SymlinkatFlags) error {
498 oldnameu16, err := syscall.UTF16FromString(oldname)
499 if err != nil {
500 return err
501 }
502 oldnameu16 = oldnameu16[:len(oldnameu16)-1]
503
504 var options uint32
505 if flags&SYMLINKAT_DIRECTORY != 0 {
506 options |= FILE_DIRECTORY_FILE
507 } else {
508 options |= FILE_NON_DIRECTORY_FILE
509 }
510
511 objAttrs := &OBJECT_ATTRIBUTES{}
512 if err := objAttrs.init(newdirfd, newname); err != nil {
513 return err
514 }
515 var h syscall.Handle
516 err = NtCreateFile(
517 &h,
518 SYNCHRONIZE|FILE_WRITE_ATTRIBUTES|DELETE,
519 objAttrs,
520 &IO_STATUS_BLOCK{},
521 nil,
522 syscall.FILE_ATTRIBUTE_NORMAL,
523 0,
524 FILE_CREATE,
525 FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT|options,
526 nil,
527 0,
528 )
529 if err != nil {
530 return ntCreateFileError(err, 0)
531 }
532 defer syscall.CloseHandle(h)
533
534
535 type reparseDataBufferT struct {
536 _ structs.HostLayout
537
538 ReparseTag uint32
539 ReparseDataLength uint16
540 Reserved uint16
541
542 SubstituteNameOffset uint16
543 SubstituteNameLength uint16
544 PrintNameOffset uint16
545 PrintNameLength uint16
546 Flags uint32
547 }
548
549 const (
550 headerSize = uint16(unsafe.Offsetof(reparseDataBufferT{}.SubstituteNameOffset))
551 bufferSize = uint16(unsafe.Sizeof(reparseDataBufferT{}))
552 )
553
554
555 rdbbuf := make([]byte, bufferSize+uint16(2*len(oldnameu16)))
556
557 rdb := (*reparseDataBufferT)(unsafe.Pointer(&rdbbuf[0]))
558 rdb.ReparseTag = syscall.IO_REPARSE_TAG_SYMLINK
559 rdb.ReparseDataLength = uint16(len(rdbbuf)) - uint16(headerSize)
560 rdb.SubstituteNameOffset = 0
561 rdb.SubstituteNameLength = uint16(2 * len(oldnameu16))
562 rdb.PrintNameOffset = 0
563 rdb.PrintNameLength = rdb.SubstituteNameLength
564 if flags&SYMLINKAT_RELATIVE != 0 {
565 rdb.Flags = SYMLINK_FLAG_RELATIVE
566 }
567
568 namebuf := rdbbuf[bufferSize:]
569 copy(namebuf, unsafe.String((*byte)(unsafe.Pointer(&oldnameu16[0])), 2*len(oldnameu16)))
570
571 err = syscall.DeviceIoControl(
572 h,
573 FSCTL_SET_REPARSE_POINT,
574 &rdbbuf[0],
575 uint32(len(rdbbuf)),
576 nil,
577 0,
578 nil,
579 nil)
580 if err != nil {
581
582 const FileDispositionInformation = 13
583 NtSetInformationFile(
584 h,
585 &IO_STATUS_BLOCK{},
586 unsafe.Pointer(&FILE_DISPOSITION_INFORMATION{
587 DeleteFile: true,
588 }),
589 uint32(unsafe.Sizeof(FILE_DISPOSITION_INFORMATION{})),
590 FileDispositionInformation,
591 )
592 return err
593 }
594
595 return nil
596 }
597
598
599
600
601 func withPrivilege(privilege string, f func() error) error {
602 runtime.LockOSThread()
603 defer runtime.UnlockOSThread()
604
605 err := ImpersonateSelf(SecurityImpersonation)
606 if err != nil {
607 return f()
608 }
609 defer RevertToSelf()
610
611 curThread, err := GetCurrentThread()
612 if err != nil {
613 return f()
614 }
615 var token syscall.Token
616 err = OpenThreadToken(curThread, syscall.TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, false, &token)
617 if err != nil {
618 return f()
619 }
620 defer syscall.CloseHandle(syscall.Handle(token))
621
622 privStr, err := syscall.UTF16PtrFromString(privilege)
623 if err != nil {
624 return f()
625 }
626 var tokenPriv TOKEN_PRIVILEGES
627 err = LookupPrivilegeValue(nil, privStr, &tokenPriv.Privileges[0].Luid)
628 if err != nil {
629 return f()
630 }
631
632 tokenPriv.PrivilegeCount = 1
633 tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED
634 err = AdjustTokenPrivileges(token, false, &tokenPriv, 0, nil, nil)
635 if err != nil {
636 return f()
637 }
638
639 return f()
640 }
641
View as plain text