| ... | ... | @@ -56,10 +56,10 @@ pub const OpenFileOptions = struct { |
| 56 | 56 | /// TODO when share_access_nonblocking is false, this implementation uses |
| 57 | 57 | /// untinterruptible sleep() to block. This is not the final iteration of the API. |
| 58 | 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 | 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 | 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 | 82 | .SecurityQualityOfService = null, |
| 83 | 83 | }; |
| 84 | 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 | 90 | var delay: usize = 1; |
| 87 | 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 | 92 | const rc = ntdll.NtCreateFile( |
| 92 | 93 | &result, |
| 93 | 94 | options.access_mask, |
| ... | ... | @@ -645,21 +646,57 @@ pub const ReadLinkError = error{ |
| 645 | 646 | }; |
| 646 | 647 | |
| 647 | 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 | | .dir = dir, |
| 650 | | .access_mask = FILE_READ_ATTRIBUTES, |
| 651 | | .share_access = FILE_SHARE_READ, |
| 652 | | .creation = FILE_OPEN, |
| 653 | | .io_mode = .blocking, |
| 654 | | .follow_symlinks = false, |
| 655 | | }) catch |err| switch (err) { |
| 656 | | error.WouldBlock => unreachable, |
| 657 | | error.PipeBusy => unreachable, |
| 658 | | error.IsDir => unreachable, |
| 659 | | error.NoDevice => unreachable, |
| 660 | | error.PathAlreadyExists => unreachable, |
| 661 | | else => |e| return e, |
| 649 | // Here, we use `NtCreateFile` to shave off one syscall if we were to use `OpenFile` wrapper. |
| 650 | // With the latter, we'd need to call `NtCreateFile` twice, once for file symlink, and if that |
| 651 | // failed, again for dir symlink. Omitting any mention of file/dir flags makes it possible |
| 652 | // to open the symlink there and then. |
| 653 | const path_len_bytes = math.cast(u16, sub_path_w.len * 2) catch |err| switch (err) { |
| 654 | error.Overflow => return error.NameTooLong, |
| 655 | }; |
| 656 | var nt_name = UNICODE_STRING{ |
| 657 | .Length = path_len_bytes, |
| 658 | .MaximumLength = path_len_bytes, |
| 659 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)), |
| 660 | }; |
| 661 | var attr = OBJECT_ATTRIBUTES{ |
| 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 | 700 | defer CloseHandle(result_handle); |
| 664 | 701 | |
| 665 | 702 | var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; |