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