| ... | @@ -604,6 +604,52 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 { | ... | @@ -604,6 +604,52 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 { |
| 604 | | 604 | |
| 605 | pub const CreateSymbolicLinkError = error{ AccessDenied, PathAlreadyExists, FileNotFound, NameTooLong, InvalidUtf8, BadPathName, Unexpected }; | 605 | pub const CreateSymbolicLinkError = error{ AccessDenied, PathAlreadyExists, FileNotFound, NameTooLong, InvalidUtf8, BadPathName, Unexpected }; |
| 606 | | 606 | |
| | 607 | pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: []const u16, target_path: []const u16) CreateSymbolicLinkError!void { |
| | 608 | const SYMLINK_DATA = extern struct { |
| | 609 | ReparseTag: ULONG, |
| | 610 | ReparseDataLength: USHORT, |
| | 611 | Reserved: USHORT, |
| | 612 | SubstituteNameOffset: USHORT, |
| | 613 | SubstituteNameLength: USHORT, |
| | 614 | PrintNameOffset: USHORT, |
| | 615 | PrintNameLength: USHORT, |
| | 616 | Flags: ULONG, |
| | 617 | }; |
| | 618 | |
| | 619 | const symlink_handle = OpenFile(sym_link_path, .{ |
| | 620 | .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, |
| | 621 | .dir = dir, |
| | 622 | .creation = FILE_CREATE, |
| | 623 | .io_mode = .blocking, |
| | 624 | }) catch |err| switch (err) { |
| | 625 | error.IsDir => unreachable, // TODO |
| | 626 | else => |e| unreachable, |
| | 627 | }; |
| | 628 | defer CloseHandle(symlink_handle); |
| | 629 | |
| | 630 | // prepare reparse data buffer |
| | 631 | var buffer: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; |
| | 632 | const buf_len = @sizeOf(SYMLINK_DATA) + target_path.len * 4; |
| | 633 | const header_len = @sizeOf(ULONG) + @sizeOf(USHORT) * 2; |
| | 634 | const symlink_data = SYMLINK_DATA{ |
| | 635 | .ReparseTag = IO_REPARSE_TAG_SYMLINK, |
| | 636 | .ReparseDataLength = @intCast(u16, buf_len - header_len), |
| | 637 | .Reserved = 0, |
| | 638 | .SubstituteNameOffset = @intCast(u16, target_path.len * 2), |
| | 639 | .SubstituteNameLength = @intCast(u16, target_path.len * 2), |
| | 640 | .PrintNameOffset = 0, |
| | 641 | .PrintNameLength = @intCast(u16, target_path.len * 2), |
| | 642 | .Flags = if (dir) |_| SYMLINK_FLAG_RELATIVE else 0, |
| | 643 | }; |
| | 644 | |
| | 645 | std.mem.copy(u8, buffer[0..], std.mem.asBytes(&symlink_data)); |
| | 646 | @memcpy(buffer[@sizeOf(SYMLINK_DATA)..], @ptrCast([*]const u8, target_path), target_path.len * 2); |
| | 647 | const paths_start = @sizeOf(SYMLINK_DATA) + target_path.len * 2; |
| | 648 | @memcpy(buffer[paths_start..].ptr, @ptrCast([*]const u8, target_path), target_path.len * 2); |
| | 649 | // TODO replace with NtDeviceIoControl |
| | 650 | _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null, null); |
| | 651 | } |
| | 652 | |
| 607 | pub fn CreateSymbolicLink( | 653 | pub fn CreateSymbolicLink( |
| 608 | sym_link_path: []const u8, | 654 | sym_link_path: []const u8, |
| 609 | target_path: []const u8, | 655 | target_path: []const u8, |