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