| ... | @@ -56,10 +56,10 @@ pub const OpenFileOptions = struct { | ... | @@ -56,10 +56,10 @@ pub const OpenFileOptions = struct { |
| 56 | /// TODO when share_access_nonblocking is false, this implementation uses | 56 | /// TODO when share_access_nonblocking is false, this implementation uses |
| 57 | /// untinterruptible sleep() to block. This is not the final iteration of the API. | 57 | /// untinterruptible sleep() to block. This is not the final iteration of the API. |
| 58 | pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE { | 58 | pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE { |
| 59 | if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir and options.follow_symlinks) { | 59 | if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir) { |
| 60 | return error.IsDir; | 60 | return error.IsDir; |
| 61 | } | 61 | } |
| 62 | if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.open_dir and options.follow_symlinks) { | 62 | if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.open_dir) { |
| 63 | return error.IsDir; | 63 | return error.IsDir; |
| 64 | } | 64 | } |
| 65 | | 65 | |
| ... | @@ -82,12 +82,13 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN | ... | @@ -82,12 +82,13 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 82 | .SecurityQualityOfService = null, | 82 | .SecurityQualityOfService = null, |
| 83 | }; | 83 | }; |
| 84 | var io: IO_STATUS_BLOCK = undefined; | 84 | var io: IO_STATUS_BLOCK = undefined; |
| | 85 | const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0; |
| | 86 | const file_or_dir_flag: ULONG = if (options.open_dir) FILE_DIRECTORY_FILE else FILE_NON_DIRECTORY_FILE; |
| | 87 | // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. |
| | 88 | const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT; |
| 85 | | 89 | |
| 86 | var delay: usize = 1; | 90 | var delay: usize = 1; |
| 87 | while (true) { | 91 | while (true) { |
| 88 | const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0; | | |
| 89 | const file_or_dir_flag: ULONG = if (options.open_dir) FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT else FILE_NON_DIRECTORY_FILE; | | |
| 90 | const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else FILE_OPEN_REPARSE_POINT; | | |
| 91 | const rc = ntdll.NtCreateFile( | 92 | const rc = ntdll.NtCreateFile( |
| 92 | &result, | 93 | &result, |
| 93 | options.access_mask, | 94 | options.access_mask, |
| ... | @@ -645,21 +646,57 @@ pub const ReadLinkError = error{ | ... | @@ -645,21 +646,57 @@ pub const ReadLinkError = error{ |
| 645 | }; | 646 | }; |
| 646 | | 647 | |
| 647 | pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 { | 648 | pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 { |
| 648 | const result_handle = OpenFile(sub_path_w, .{ | 649 | // Here, we use `NtCreateFile` to shave off one syscall if we were to use `OpenFile` wrapper. |
| 649 | .dir = dir, | 650 | // With the latter, we'd need to call `NtCreateFile` twice, once for file symlink, and if that |
| 650 | .access_mask = FILE_READ_ATTRIBUTES, | 651 | // failed, again for dir symlink. Omitting any mention of file/dir flags makes it possible |
| 651 | .share_access = FILE_SHARE_READ, | 652 | // to open the symlink there and then. |
| 652 | .creation = FILE_OPEN, | 653 | const path_len_bytes = math.cast(u16, sub_path_w.len * 2) catch |err| switch (err) { |
| 653 | .io_mode = .blocking, | 654 | error.Overflow => return error.NameTooLong, |
| 654 | .follow_symlinks = false, | 655 | }; |
| 655 | }) catch |err| switch (err) { | 656 | var nt_name = UNICODE_STRING{ |
| 656 | error.WouldBlock => unreachable, | 657 | .Length = path_len_bytes, |
| 657 | error.PipeBusy => unreachable, | 658 | .MaximumLength = path_len_bytes, |
| 658 | error.IsDir => unreachable, | 659 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)), |
| 659 | error.NoDevice => unreachable, | 660 | }; |
| 660 | error.PathAlreadyExists => unreachable, | 661 | var attr = OBJECT_ATTRIBUTES{ |
| 661 | else => |e| return e, | 662 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| | 663 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else dir, |
| | 664 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. |
| | 665 | .ObjectName = &nt_name, |
| | 666 | .SecurityDescriptor = null, |
| | 667 | .SecurityQualityOfService = null, |
| 662 | }; | 668 | }; |
| | 669 | var result_handle: HANDLE = undefined; |
| | 670 | var io: IO_STATUS_BLOCK = undefined; |
| | 671 | |
| | 672 | const rc = ntdll.NtCreateFile( |
| | 673 | &result_handle, |
| | 674 | FILE_READ_ATTRIBUTES, |
| | 675 | &attr, |
| | 676 | &io, |
| | 677 | null, |
| | 678 | FILE_ATTRIBUTE_NORMAL, |
| | 679 | FILE_SHARE_READ, |
| | 680 | FILE_OPEN, |
| | 681 | FILE_OPEN_REPARSE_POINT, |
| | 682 | null, |
| | 683 | 0, |
| | 684 | ); |
| | 685 | switch (rc) { |
| | 686 | .SUCCESS => {}, |
| | 687 | .OBJECT_NAME_INVALID => unreachable, |
| | 688 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| | 689 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| | 690 | .NO_MEDIA_IN_DEVICE => return error.FileNotFound, |
| | 691 | .INVALID_PARAMETER => unreachable, |
| | 692 | .SHARING_VIOLATION => return error.AccessDenied, |
| | 693 | .ACCESS_DENIED => return error.AccessDenied, |
| | 694 | .PIPE_BUSY => return error.AccessDenied, |
| | 695 | .OBJECT_PATH_SYNTAX_BAD => unreachable, |
| | 696 | .OBJECT_NAME_COLLISION => unreachable, |
| | 697 | .FILE_IS_A_DIRECTORY => unreachable, |
| | 698 | else => return unexpectedStatus(rc), |
| | 699 | } |
| 663 | defer CloseHandle(result_handle); | 700 | defer CloseHandle(result_handle); |
| 664 | | 701 | |
| 665 | var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; | 702 | var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; |