| ... | ... | @@ -604,7 +604,7 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 { |
| 604 | 604 | |
| 605 | 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 | 608 | const SYMLINK_DATA = extern struct { |
| 609 | 609 | ReparseTag: ULONG, |
| 610 | 610 | ReparseDataLength: USHORT, |
| ... | ... | @@ -616,15 +616,67 @@ pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: []const u16, target_pa |
| 616 | 616 | Flags: ULONG, |
| 617 | 617 | }; |
| 618 | 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 | | }; |
| 619 | var symlink_handle: HANDLE = undefined; |
| 620 | if (is_directory) { |
| 621 | const sym_link_len_bytes = math.cast(u16, sym_link_path.len * 2) catch |err| switch (err) { |
| 622 | error.Overflow => return error.NameTooLong, |
| 623 | }; |
| 624 | var nt_name = UNICODE_STRING{ |
| 625 | .Length = sym_link_len_bytes, |
| 626 | .MaximumLength = sym_link_len_bytes, |
| 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 | 680 | defer CloseHandle(symlink_handle); |
| 629 | 681 | |
| 630 | 682 | // prepare reparse data buffer |