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