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