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