authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-30 23:00:25+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-31 16:32:21+02:00
log390194431e7fa439d67686e5a0e9efceed2d898a
tree8581d23993963aa7ba2c8eebecd08a46d90d0be2
parent8981b18fee9dc1db7faa8343d0d2151c3c0671fd

Use NtCreateFile directly in ReadLink

This shaves off one syscall (we use one instead of two if we were to use `windows.OpenFile` wrapper). Clean up flag generation in `OpenFile`. Hopefully, we're in a much better place to *almost* support `openW` and `openatW`.

1 files changed, 56 insertions(+), 19 deletions(-)

lib/std/os/windows.zig+56-19
...@@ -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 uses56/// 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.
58pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {58pub 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 }
6565
...@@ -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;
8589
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};
646647
647pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 {648pub 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);
664701
665 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;702 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;