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