| ... | @@ -604,7 +604,7 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 { | ... | @@ -604,7 +604,7 @@ 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 { | 607 | pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: [:0]const u16, target_path: [:0]const u16, is_directory: bool) CreateSymbolicLinkError!void { |
| 608 | const SYMLINK_DATA = extern struct { | 608 | const SYMLINK_DATA = extern struct { |
| 609 | ReparseTag: ULONG, | 609 | ReparseTag: ULONG, |
| 610 | ReparseDataLength: USHORT, | 610 | ReparseDataLength: USHORT, |
| ... | @@ -616,15 +616,67 @@ pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: []const u16, target_pa | ... | @@ -616,15 +616,67 @@ pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: []const u16, target_pa |
| 616 | Flags: ULONG, | 616 | Flags: ULONG, |
| 617 | }; | 617 | }; |
| 618 | | 618 | |
| 619 | const symlink_handle = OpenFile(sym_link_path, .{ | 619 | var symlink_handle: HANDLE = undefined; |
| 620 | .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, | 620 | if (is_directory) { |
| 621 | .dir = dir, | 621 | const sym_link_len_bytes = math.cast(u16, sym_link_path.len * 2) catch |err| switch (err) { |
| 622 | .creation = FILE_CREATE, | 622 | error.Overflow => return error.NameTooLong, |
| 623 | .io_mode = .blocking, | 623 | }; |
| 624 | }) catch |err| switch (err) { | 624 | var nt_name = UNICODE_STRING{ |
| 625 | error.IsDir => unreachable, // TODO | 625 | .Length = sym_link_len_bytes, |
| 626 | else => |e| unreachable, | 626 | .MaximumLength = sym_link_len_bytes, |
| 627 | }; | 627 | .Buffer = @intToPtr([*]u16, @ptrToInt(sym_link_path.ptr)), |
| | 628 | }; |
| | 629 | |
| | 630 | if (sym_link_path[0] == '.' and sym_link_path[1] == 0) { |
| | 631 | // Windows does not recognize this, but it does work with empty string. |
| | 632 | nt_name.Length = 0; |
| | 633 | } |
| | 634 | |
| | 635 | var attr = OBJECT_ATTRIBUTES{ |
| | 636 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| | 637 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sym_link_path)) null else dir, |
| | 638 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. |
| | 639 | .ObjectName = &nt_name, |
| | 640 | .SecurityDescriptor = null, |
| | 641 | .SecurityQualityOfService = null, |
| | 642 | }; |
| | 643 | |
| | 644 | var io: IO_STATUS_BLOCK = undefined; |
| | 645 | const rc = ntdll.NtCreateFile( |
| | 646 | &symlink_handle, |
| | 647 | GENERIC_READ | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, |
| | 648 | &attr, |
| | 649 | &io, |
| | 650 | null, |
| | 651 | FILE_ATTRIBUTE_NORMAL, |
| | 652 | FILE_SHARE_READ, |
| | 653 | FILE_CREATE, |
| | 654 | FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT, |
| | 655 | null, |
| | 656 | 0, |
| | 657 | ); |
| | 658 | switch (rc) { |
| | 659 | .SUCCESS => {}, |
| | 660 | .OBJECT_NAME_INVALID => unreachable, |
| | 661 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| | 662 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| | 663 | // .NO_MEDIA_IN_DEVICE => return error.NoDevice, |
| | 664 | .INVALID_PARAMETER => unreachable, |
| | 665 | .ACCESS_DENIED => return error.AccessDenied, |
| | 666 | .OBJECT_PATH_SYNTAX_BAD => unreachable, |
| | 667 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, |
| | 668 | else => return unexpectedStatus(rc), |
| | 669 | } |
| | 670 | } else { |
| | 671 | symlink_handle = OpenFile(sym_link_path, .{ |
| | 672 | .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, |
| | 673 | .dir = dir, |
| | 674 | .creation = FILE_CREATE, |
| | 675 | .io_mode = .blocking, |
| | 676 | }) catch |err| switch (err) { |
| | 677 | else => |e| unreachable, |
| | 678 | }; |
| | 679 | } |
| 628 | defer CloseHandle(symlink_handle); | 680 | defer CloseHandle(symlink_handle); |
| 629 | | 681 | |
| 630 | // prepare reparse data buffer | 682 | // prepare reparse data buffer |