| author | |
| committer | |
| log | 988031c07c1959b05682f007ed3bc848a75a43d0 |
| tree | 372035990a592f73cc2a065106205c1a23494834 |
| parent | 94b0d0e80242563f4ad7ad41e3c0f5193a60b70c |
| parent | 67e51311c3352ab4b2a381bd90dc386032254058 |
8 files changed, 300 insertions(+), 253 deletions(-)
lib/std/debug.zig+169-165| ... | ... | @@ -666,158 +666,160 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { |
| 666 | 666 | |
| 667 | 667 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 668 | 668 | fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo { |
| 669 | const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{}); | |
| 670 | errdefer coff_file.close(); | |
| 669 | noasync { | |
| 670 | const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{}); | |
| 671 | errdefer coff_file.close(); | |
| 671 | 672 | |
| 672 | const coff_obj = try allocator.create(coff.Coff); | |
| 673 | coff_obj.* = coff.Coff.init(allocator, coff_file); | |
| 673 | const coff_obj = try allocator.create(coff.Coff); | |
| 674 | coff_obj.* = coff.Coff.init(allocator, coff_file); | |
| 674 | 675 | |
| 675 | var di = ModuleDebugInfo{ | |
| 676 | .base_address = undefined, | |
| 677 | .coff = coff_obj, | |
| 678 | .pdb = undefined, | |
| 679 | .sect_contribs = undefined, | |
| 680 | .modules = undefined, | |
| 681 | }; | |
| 676 | var di = ModuleDebugInfo{ | |
| 677 | .base_address = undefined, | |
| 678 | .coff = coff_obj, | |
| 679 | .pdb = undefined, | |
| 680 | .sect_contribs = undefined, | |
| 681 | .modules = undefined, | |
| 682 | }; | |
| 682 | 683 | |
| 683 | try di.coff.loadHeader(); | |
| 684 | try di.coff.loadHeader(); | |
| 684 | 685 | |
| 685 | var path_buf: [windows.MAX_PATH]u8 = undefined; | |
| 686 | const len = try di.coff.getPdbPath(path_buf[0..]); | |
| 687 | const raw_path = path_buf[0..len]; | |
| 686 | var path_buf: [windows.MAX_PATH]u8 = undefined; | |
| 687 | const len = try di.coff.getPdbPath(path_buf[0..]); | |
| 688 | const raw_path = path_buf[0..len]; | |
| 688 | 689 | |
| 689 | const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); | |
| 690 | const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); | |
| 690 | 691 | |
| 691 | try di.pdb.openFile(di.coff, path); | |
| 692 | try di.pdb.openFile(di.coff, path); | |
| 692 | 693 | |
| 693 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; | |
| 694 | const version = try pdb_stream.inStream().readIntLittle(u32); | |
| 695 | const signature = try pdb_stream.inStream().readIntLittle(u32); | |
| 696 | const age = try pdb_stream.inStream().readIntLittle(u32); | |
| 697 | var guid: [16]u8 = undefined; | |
| 698 | try pdb_stream.inStream().readNoEof(&guid); | |
| 699 | if (version != 20000404) // VC70, only value observed by LLVM team | |
| 700 | return error.UnknownPDBVersion; | |
| 701 | if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) | |
| 702 | return error.PDBMismatch; | |
| 703 | // We validated the executable and pdb match. | |
| 694 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; | |
| 695 | const version = try pdb_stream.inStream().readIntLittle(u32); | |
| 696 | const signature = try pdb_stream.inStream().readIntLittle(u32); | |
| 697 | const age = try pdb_stream.inStream().readIntLittle(u32); | |
| 698 | var guid: [16]u8 = undefined; | |
| 699 | try pdb_stream.inStream().readNoEof(&guid); | |
| 700 | if (version != 20000404) // VC70, only value observed by LLVM team | |
| 701 | return error.UnknownPDBVersion; | |
| 702 | if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) | |
| 703 | return error.PDBMismatch; | |
| 704 | // We validated the executable and pdb match. | |
| 704 | 705 | |
| 705 | const string_table_index = str_tab_index: { | |
| 706 | const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32); | |
| 707 | const name_bytes = try allocator.alloc(u8, name_bytes_len); | |
| 708 | try pdb_stream.inStream().readNoEof(name_bytes); | |
| 706 | const string_table_index = str_tab_index: { | |
| 707 | const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32); | |
| 708 | const name_bytes = try allocator.alloc(u8, name_bytes_len); | |
| 709 | try pdb_stream.inStream().readNoEof(name_bytes); | |
| 709 | 710 | |
| 710 | const HashTableHeader = packed struct { | |
| 711 | Size: u32, | |
| 712 | Capacity: u32, | |
| 711 | const HashTableHeader = packed struct { | |
| 712 | Size: u32, | |
| 713 | Capacity: u32, | |
| 713 | 714 | |
| 714 | fn maxLoad(cap: u32) u32 { | |
| 715 | return cap * 2 / 3 + 1; | |
| 716 | } | |
| 717 | }; | |
| 718 | const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader); | |
| 719 | if (hash_tbl_hdr.Capacity == 0) | |
| 720 | return error.InvalidDebugInfo; | |
| 715 | fn maxLoad(cap: u32) u32 { | |
| 716 | return cap * 2 / 3 + 1; | |
| 717 | } | |
| 718 | }; | |
| 719 | const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader); | |
| 720 | if (hash_tbl_hdr.Capacity == 0) | |
| 721 | return error.InvalidDebugInfo; | |
| 721 | 722 | |
| 722 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) | |
| 723 | return error.InvalidDebugInfo; | |
| 723 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) | |
| 724 | return error.InvalidDebugInfo; | |
| 724 | 725 | |
| 725 | const present = try readSparseBitVector(&pdb_stream.inStream(), allocator); | |
| 726 | if (present.len != hash_tbl_hdr.Size) | |
| 727 | return error.InvalidDebugInfo; | |
| 728 | const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator); | |
| 726 | const present = try readSparseBitVector(&pdb_stream.inStream(), allocator); | |
| 727 | if (present.len != hash_tbl_hdr.Size) | |
| 728 | return error.InvalidDebugInfo; | |
| 729 | const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator); | |
| 729 | 730 | |
| 730 | const Bucket = struct { | |
| 731 | first: u32, | |
| 732 | second: u32, | |
| 733 | }; | |
| 734 | const bucket_list = try allocator.alloc(Bucket, present.len); | |
| 735 | for (present) |_| { | |
| 736 | const name_offset = try pdb_stream.inStream().readIntLittle(u32); | |
| 737 | const name_index = try pdb_stream.inStream().readIntLittle(u32); | |
| 738 | const name = mem.spanZ(@ptrCast([*:0]u8, name_bytes.ptr + name_offset)); | |
| 739 | if (mem.eql(u8, name, "/names")) { | |
| 740 | break :str_tab_index name_index; | |
| 731 | const Bucket = struct { | |
| 732 | first: u32, | |
| 733 | second: u32, | |
| 734 | }; | |
| 735 | const bucket_list = try allocator.alloc(Bucket, present.len); | |
| 736 | for (present) |_| { | |
| 737 | const name_offset = try pdb_stream.inStream().readIntLittle(u32); | |
| 738 | const name_index = try pdb_stream.inStream().readIntLittle(u32); | |
| 739 | const name = mem.spanZ(@ptrCast([*:0]u8, name_bytes.ptr + name_offset)); | |
| 740 | if (mem.eql(u8, name, "/names")) { | |
| 741 | break :str_tab_index name_index; | |
| 742 | } | |
| 741 | 743 | } |
| 742 | } | |
| 743 | return error.MissingDebugInfo; | |
| 744 | }; | |
| 744 | return error.MissingDebugInfo; | |
| 745 | }; | |
| 745 | 746 | |
| 746 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.MissingDebugInfo; | |
| 747 | di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; | |
| 747 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.MissingDebugInfo; | |
| 748 | di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; | |
| 748 | 749 | |
| 749 | const dbi = di.pdb.dbi; | |
| 750 | const dbi = di.pdb.dbi; | |
| 750 | 751 | |
| 751 | // Dbi Header | |
| 752 | const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader); | |
| 753 | if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team | |
| 754 | return error.UnknownPDBVersion; | |
| 755 | if (dbi_stream_header.Age != age) | |
| 756 | return error.UnmatchingPDB; | |
| 752 | // Dbi Header | |
| 753 | const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader); | |
| 754 | if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team | |
| 755 | return error.UnknownPDBVersion; | |
| 756 | if (dbi_stream_header.Age != age) | |
| 757 | return error.UnmatchingPDB; | |
| 757 | 758 | |
| 758 | const mod_info_size = dbi_stream_header.ModInfoSize; | |
| 759 | const section_contrib_size = dbi_stream_header.SectionContributionSize; | |
| 759 | const mod_info_size = dbi_stream_header.ModInfoSize; | |
| 760 | const section_contrib_size = dbi_stream_header.SectionContributionSize; | |
| 760 | 761 | |
| 761 | var modules = ArrayList(Module).init(allocator); | |
| 762 | var modules = ArrayList(Module).init(allocator); | |
| 762 | 763 | |
| 763 | // Module Info Substream | |
| 764 | var mod_info_offset: usize = 0; | |
| 765 | while (mod_info_offset != mod_info_size) { | |
| 766 | const mod_info = try dbi.inStream().readStruct(pdb.ModInfo); | |
| 767 | var this_record_len: usize = @sizeOf(pdb.ModInfo); | |
| 764 | // Module Info Substream | |
| 765 | var mod_info_offset: usize = 0; | |
| 766 | while (mod_info_offset != mod_info_size) { | |
| 767 | const mod_info = try dbi.inStream().readStruct(pdb.ModInfo); | |
| 768 | var this_record_len: usize = @sizeOf(pdb.ModInfo); | |
| 768 | 769 | |
| 769 | const module_name = try dbi.readNullTermString(allocator); | |
| 770 | this_record_len += module_name.len + 1; | |
| 770 | const module_name = try dbi.readNullTermString(allocator); | |
| 771 | this_record_len += module_name.len + 1; | |
| 771 | 772 | |
| 772 | const obj_file_name = try dbi.readNullTermString(allocator); | |
| 773 | this_record_len += obj_file_name.len + 1; | |
| 773 | const obj_file_name = try dbi.readNullTermString(allocator); | |
| 774 | this_record_len += obj_file_name.len + 1; | |
| 774 | 775 | |
| 775 | if (this_record_len % 4 != 0) { | |
| 776 | const round_to_next_4 = (this_record_len | 0x3) + 1; | |
| 777 | const march_forward_bytes = round_to_next_4 - this_record_len; | |
| 778 | try dbi.seekBy(@intCast(isize, march_forward_bytes)); | |
| 779 | this_record_len += march_forward_bytes; | |
| 780 | } | |
| 776 | if (this_record_len % 4 != 0) { | |
| 777 | const round_to_next_4 = (this_record_len | 0x3) + 1; | |
| 778 | const march_forward_bytes = round_to_next_4 - this_record_len; | |
| 779 | try dbi.seekBy(@intCast(isize, march_forward_bytes)); | |
| 780 | this_record_len += march_forward_bytes; | |
| 781 | } | |
| 781 | 782 | |
| 782 | try modules.append(Module{ | |
| 783 | .mod_info = mod_info, | |
| 784 | .module_name = module_name, | |
| 785 | .obj_file_name = obj_file_name, | |
| 783 | try modules.append(Module{ | |
| 784 | .mod_info = mod_info, | |
| 785 | .module_name = module_name, | |
| 786 | .obj_file_name = obj_file_name, | |
| 786 | 787 | |
| 787 | .populated = false, | |
| 788 | .symbols = undefined, | |
| 789 | .subsect_info = undefined, | |
| 790 | .checksum_offset = null, | |
| 791 | }); | |
| 788 | .populated = false, | |
| 789 | .symbols = undefined, | |
| 790 | .subsect_info = undefined, | |
| 791 | .checksum_offset = null, | |
| 792 | }); | |
| 792 | 793 | |
| 793 | mod_info_offset += this_record_len; | |
| 794 | if (mod_info_offset > mod_info_size) | |
| 795 | return error.InvalidDebugInfo; | |
| 796 | } | |
| 794 | mod_info_offset += this_record_len; | |
| 795 | if (mod_info_offset > mod_info_size) | |
| 796 | return error.InvalidDebugInfo; | |
| 797 | } | |
| 797 | 798 | |
| 798 | di.modules = modules.toOwnedSlice(); | |
| 799 | di.modules = modules.toOwnedSlice(); | |
| 799 | 800 | |
| 800 | // Section Contribution Substream | |
| 801 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); | |
| 802 | var sect_cont_offset: usize = 0; | |
| 803 | if (section_contrib_size != 0) { | |
| 804 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32)); | |
| 805 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) | |
| 806 | return error.InvalidDebugInfo; | |
| 807 | sect_cont_offset += @sizeOf(u32); | |
| 808 | } | |
| 809 | while (sect_cont_offset != section_contrib_size) { | |
| 810 | const entry = try sect_contribs.addOne(); | |
| 811 | entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry); | |
| 812 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); | |
| 801 | // Section Contribution Substream | |
| 802 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); | |
| 803 | var sect_cont_offset: usize = 0; | |
| 804 | if (section_contrib_size != 0) { | |
| 805 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32)); | |
| 806 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) | |
| 807 | return error.InvalidDebugInfo; | |
| 808 | sect_cont_offset += @sizeOf(u32); | |
| 809 | } | |
| 810 | while (sect_cont_offset != section_contrib_size) { | |
| 811 | const entry = try sect_contribs.addOne(); | |
| 812 | entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry); | |
| 813 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); | |
| 813 | 814 | |
| 814 | if (sect_cont_offset > section_contrib_size) | |
| 815 | return error.InvalidDebugInfo; | |
| 816 | } | |
| 815 | if (sect_cont_offset > section_contrib_size) | |
| 816 | return error.InvalidDebugInfo; | |
| 817 | } | |
| 817 | 818 | |
| 818 | di.sect_contribs = sect_contribs.toOwnedSlice(); | |
| 819 | di.sect_contribs = sect_contribs.toOwnedSlice(); | |
| 819 | 820 | |
| 820 | return di; | |
| 821 | return di; | |
| 822 | } | |
| 821 | 823 | } |
| 822 | 824 | |
| 823 | 825 | fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { |
| ... | ... | @@ -1410,59 +1412,61 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) { |
| 1410 | 1412 | } |
| 1411 | 1413 | |
| 1412 | 1414 | fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { |
| 1413 | // Translate the VA into an address into this object | |
| 1414 | const relocated_address = address - self.base_address; | |
| 1415 | assert(relocated_address >= 0x100000000); | |
| 1416 | ||
| 1417 | // Find the .o file where this symbol is defined | |
| 1418 | const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse | |
| 1419 | return SymbolInfo{}; | |
| 1420 | ||
| 1421 | // Take the symbol name from the N_FUN STAB entry, we're going to | |
| 1422 | // use it if we fail to find the DWARF infos | |
| 1423 | const stab_symbol = mem.spanZ(self.strings[symbol.nlist.n_strx..]); | |
| 1415 | noasync { | |
| 1416 | // Translate the VA into an address into this object | |
| 1417 | const relocated_address = address - self.base_address; | |
| 1418 | assert(relocated_address >= 0x100000000); | |
| 1424 | 1419 | |
| 1425 | if (symbol.ofile == null) | |
| 1426 | return SymbolInfo{ .symbol_name = stab_symbol }; | |
| 1420 | // Find the .o file where this symbol is defined | |
| 1421 | const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse | |
| 1422 | return SymbolInfo{}; | |
| 1427 | 1423 | |
| 1428 | const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]); | |
| 1424 | // Take the symbol name from the N_FUN STAB entry, we're going to | |
| 1425 | // use it if we fail to find the DWARF infos | |
| 1426 | const stab_symbol = mem.spanZ(self.strings[symbol.nlist.n_strx..]); | |
| 1429 | 1427 | |
| 1430 | // Check if its debug infos are already in the cache | |
| 1431 | var o_file_di = self.ofiles.getValue(o_file_path) orelse | |
| 1432 | (self.loadOFile(o_file_path) catch |err| switch (err) { | |
| 1433 | error.FileNotFound, | |
| 1434 | error.MissingDebugInfo, | |
| 1435 | error.InvalidDebugInfo, | |
| 1436 | => { | |
| 1428 | if (symbol.ofile == null) | |
| 1437 | 1429 | return SymbolInfo{ .symbol_name = stab_symbol }; |
| 1438 | }, | |
| 1439 | else => return err, | |
| 1440 | }); | |
| 1441 | 1430 | |
| 1442 | // Translate again the address, this time into an address inside the | |
| 1443 | // .o file | |
| 1444 | const relocated_address_o = relocated_address - symbol.reloc; | |
| 1431 | const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]); | |
| 1445 | 1432 | |
| 1446 | if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| { | |
| 1447 | return SymbolInfo{ | |
| 1448 | .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???", | |
| 1449 | .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT_name) catch |err| switch (err) { | |
| 1450 | error.MissingDebugInfo, error.InvalidDebugInfo => "???", | |
| 1451 | else => return err, | |
| 1433 | // Check if its debug infos are already in the cache | |
| 1434 | var o_file_di = self.ofiles.getValue(o_file_path) orelse | |
| 1435 | (self.loadOFile(o_file_path) catch |err| switch (err) { | |
| 1436 | error.FileNotFound, | |
| 1437 | error.MissingDebugInfo, | |
| 1438 | error.InvalidDebugInfo, | |
| 1439 | => { | |
| 1440 | return SymbolInfo{ .symbol_name = stab_symbol }; | |
| 1452 | 1441 | }, |
| 1453 | .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o) catch |err| switch (err) { | |
| 1454 | error.MissingDebugInfo, error.InvalidDebugInfo => null, | |
| 1455 | else => return err, | |
| 1442 | else => return err, | |
| 1443 | }); | |
| 1444 | ||
| 1445 | // Translate again the address, this time into an address inside the | |
| 1446 | // .o file | |
| 1447 | const relocated_address_o = relocated_address - symbol.reloc; | |
| 1448 | ||
| 1449 | if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| { | |
| 1450 | return SymbolInfo{ | |
| 1451 | .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???", | |
| 1452 | .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT_name) catch |err| switch (err) { | |
| 1453 | error.MissingDebugInfo, error.InvalidDebugInfo => "???", | |
| 1454 | else => return err, | |
| 1455 | }, | |
| 1456 | .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o) catch |err| switch (err) { | |
| 1457 | error.MissingDebugInfo, error.InvalidDebugInfo => null, | |
| 1458 | else => return err, | |
| 1459 | }, | |
| 1460 | }; | |
| 1461 | } else |err| switch (err) { | |
| 1462 | error.MissingDebugInfo, error.InvalidDebugInfo => { | |
| 1463 | return SymbolInfo{ .symbol_name = stab_symbol }; | |
| 1456 | 1464 | }, |
| 1457 | }; | |
| 1458 | } else |err| switch (err) { | |
| 1459 | error.MissingDebugInfo, error.InvalidDebugInfo => { | |
| 1460 | return SymbolInfo{ .symbol_name = stab_symbol }; | |
| 1461 | }, | |
| 1462 | else => return err, | |
| 1463 | } | |
| 1465 | else => return err, | |
| 1466 | } | |
| 1464 | 1467 | |
| 1465 | unreachable; | |
| 1468 | unreachable; | |
| 1469 | } | |
| 1466 | 1470 | } |
| 1467 | 1471 | }, |
| 1468 | 1472 | .uefi, .windows => struct { |
lib/std/fs.zig+38-29| ... | ... | @@ -639,21 +639,28 @@ pub const Dir = struct { |
| 639 | 639 | |
| 640 | 640 | /// Same as `openFile` but Windows-only and the path parameter is |
| 641 | 641 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. |
| 642 | pub fn openFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File { | |
| 642 | pub fn openFileW(self: Dir, sub_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File { | |
| 643 | 643 | const w = os.windows; |
| 644 | const access_mask = w.SYNCHRONIZE | | |
| 645 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | | |
| 646 | (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0); | |
| 647 | ||
| 648 | const share_access = switch (flags.lock) { | |
| 649 | .None => @as(?w.ULONG, null), | |
| 650 | .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | |
| 651 | .Exclusive => w.FILE_SHARE_DELETE, | |
| 652 | }; | |
| 653 | ||
| 654 | 644 | return @as(File, .{ |
| 655 | .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, flags.lock_nonblocking, w.FILE_OPEN), | |
| 645 | .handle = try os.windows.OpenFile(sub_path_w, .{ | |
| 646 | .dir = self.fd, | |
| 647 | .access_mask = w.SYNCHRONIZE | | |
| 648 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | | |
| 649 | (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0), | |
| 650 | .share_access = switch (flags.lock) { | |
| 651 | .None => @as(?w.ULONG, null), | |
| 652 | .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | |
| 653 | .Exclusive => w.FILE_SHARE_DELETE, | |
| 654 | }, | |
| 655 | .share_access_nonblocking = flags.lock_nonblocking, | |
| 656 | .creation = w.FILE_OPEN, | |
| 657 | .enable_async_io = std.io.is_async and !flags.always_blocking, | |
| 658 | }), | |
| 656 | 659 | .io_mode = .blocking, |
| 660 | .async_block_allowed = if (flags.always_blocking) | |
| 661 | File.async_block_allowed_yes | |
| 662 | else | |
| 663 | File.async_block_allowed_no, | |
| 657 | 664 | }); |
| 658 | 665 | } |
| 659 | 666 | |
| ... | ... | @@ -713,25 +720,27 @@ pub const Dir = struct { |
| 713 | 720 | |
| 714 | 721 | /// Same as `createFile` but Windows-only and the path parameter is |
| 715 | 722 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. |
| 716 | pub fn createFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File { | |
| 723 | pub fn createFileW(self: Dir, sub_path_w: []const u16, flags: File.CreateFlags) File.OpenError!File { | |
| 717 | 724 | const w = os.windows; |
| 718 | const access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | | |
| 719 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0); | |
| 720 | const creation = if (flags.exclusive) | |
| 721 | @as(u32, w.FILE_CREATE) | |
| 722 | else if (flags.truncate) | |
| 723 | @as(u32, w.FILE_OVERWRITE_IF) | |
| 724 | else | |
| 725 | @as(u32, w.FILE_OPEN_IF); | |
| 726 | ||
| 727 | const share_access = switch (flags.lock) { | |
| 728 | .None => @as(?w.ULONG, null), | |
| 729 | .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | |
| 730 | .Exclusive => w.FILE_SHARE_DELETE, | |
| 731 | }; | |
| 732 | ||
| 725 | const read_flag = if (flags.read) @as(u32, w.GENERIC_READ) else 0; | |
| 733 | 726 | return @as(File, .{ |
| 734 | .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, flags.lock_nonblocking, creation), | |
| 727 | .handle = try os.windows.OpenFile(sub_path_w, .{ | |
| 728 | .dir = self.fd, | |
| 729 | .access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | read_flag, | |
| 730 | .share_access = switch (flags.lock) { | |
| 731 | .None => @as(?w.ULONG, null), | |
| 732 | .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | |
| 733 | .Exclusive => w.FILE_SHARE_DELETE, | |
| 734 | }, | |
| 735 | .share_access_nonblocking = flags.lock_nonblocking, | |
| 736 | .creation = if (flags.exclusive) | |
| 737 | @as(u32, w.FILE_CREATE) | |
| 738 | else if (flags.truncate) | |
| 739 | @as(u32, w.FILE_OVERWRITE_IF) | |
| 740 | else | |
| 741 | @as(u32, w.FILE_OPEN_IF), | |
| 742 | .enable_async_io = std.io.is_async, | |
| 743 | }), | |
| 735 | 744 | .io_mode = .blocking, |
| 736 | 745 | }); |
| 737 | 746 | } |
lib/std/fs/file.zig+20-2| ... | ... | @@ -62,12 +62,14 @@ pub const File = struct { |
| 62 | 62 | |
| 63 | 63 | /// Sets whether or not to wait until the file is locked to return. If set to true, |
| 64 | 64 | /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file |
| 65 | /// is available to proceed. | |
| 65 | /// is available to proceed. In async I/O mode, non-blocking at the OS level is always | |
| 66 | /// used, and `true` means `error.WouldBlock` is returned, and `false` means | |
| 67 | /// `error.WouldBlock` is handled by the event loop. | |
| 66 | 68 | lock_nonblocking: bool = false, |
| 67 | 69 | |
| 68 | 70 | /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`. |
| 69 | 71 | /// It allows the use of `noasync` when calling functions related to opening |
| 70 | /// the file, reading, and writing. | |
| 72 | /// the file, reading, writing, as well as locking functionality. | |
| 71 | 73 | always_blocking: bool = false, |
| 72 | 74 | }; |
| 73 | 75 | |
| ... | ... | @@ -295,6 +297,10 @@ pub const File = struct { |
| 295 | 297 | pub const PReadError = os.PReadError; |
| 296 | 298 | |
| 297 | 299 | pub fn read(self: File, buffer: []u8) ReadError!usize { |
| 300 | if (builtin.os.tag == .windows) { | |
| 301 | const enable_async_io = std.io.is_async and !self.async_block_allowed; | |
| 302 | return windows.ReadFile(self.handle, buffer, null, enable_async_io); | |
| 303 | } | |
| 298 | 304 | if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { |
| 299 | 305 | return std.event.Loop.instance.?.read(self.handle, buffer); |
| 300 | 306 | } else { |
| ... | ... | @@ -315,6 +321,10 @@ pub const File = struct { |
| 315 | 321 | } |
| 316 | 322 | |
| 317 | 323 | pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| 324 | if (builtin.os.tag == .windows) { | |
| 325 | const enable_async_io = std.io.is_async and !self.async_block_allowed; | |
| 326 | return windows.ReadFile(self.handle, buffer, offset, enable_async_io); | |
| 327 | } | |
| 318 | 328 | if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { |
| 319 | 329 | return std.event.Loop.instance.?.pread(self.handle, buffer, offset); |
| 320 | 330 | } else { |
| ... | ... | @@ -406,6 +416,10 @@ pub const File = struct { |
| 406 | 416 | pub const PWriteError = os.PWriteError; |
| 407 | 417 | |
| 408 | 418 | pub fn write(self: File, bytes: []const u8) WriteError!usize { |
| 419 | if (builtin.os.tag == .windows) { | |
| 420 | const enable_async_io = std.io.is_async and !self.async_block_allowed; | |
| 421 | return windows.WriteFile(self.handle, bytes, null, enable_async_io); | |
| 422 | } | |
| 409 | 423 | if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { |
| 410 | 424 | return std.event.Loop.instance.?.write(self.handle, bytes); |
| 411 | 425 | } else { |
| ... | ... | @@ -421,6 +435,10 @@ pub const File = struct { |
| 421 | 435 | } |
| 422 | 436 | |
| 423 | 437 | pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize { |
| 438 | if (builtin.os.tag == .windows) { | |
| 439 | const enable_async_io = std.io.is_async and !self.async_block_allowed; | |
| 440 | return windows.WriteFile(self.handle, bytes, offset, enable_async_io); | |
| 441 | } | |
| 424 | 442 | if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { |
| 425 | 443 | return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset); |
| 426 | 444 | } else { |
lib/std/fs/path.zig+4| ... | ... | @@ -177,6 +177,10 @@ pub fn isAbsoluteWindowsW(path_w: [*:0]const u16) bool { |
| 177 | 177 | return isAbsoluteWindowsImpl(u16, mem.spanZ(path_w)); |
| 178 | 178 | } |
| 179 | 179 | |
| 180 | pub fn isAbsoluteWindowsWTF16(path: []const u16) bool { | |
| 181 | return isAbsoluteWindowsImpl(u16, path); | |
| 182 | } | |
| 183 | ||
| 180 | 184 | pub const isAbsoluteWindowsC = @compileError("deprecated: renamed to isAbsoluteWindowsZ"); |
| 181 | 185 | |
| 182 | 186 | pub fn isAbsoluteWindowsZ(path_c: [*:0]const u8) bool { |
lib/std/io.zig+4| ... | ... | @@ -42,10 +42,12 @@ fn getStdOutHandle() os.fd_t { |
| 42 | 42 | return os.STDOUT_FILENO; |
| 43 | 43 | } |
| 44 | 44 | |
| 45 | // TODO: async stdout on windows (https://github.com/ziglang/zig/pull/4816#issuecomment-604521023) | |
| 45 | 46 | pub fn getStdOut() File { |
| 46 | 47 | return File{ |
| 47 | 48 | .handle = getStdOutHandle(), |
| 48 | 49 | .io_mode = .blocking, |
| 50 | .async_block_allowed = if (builtin.os.tag == .windows) File.async_block_allowed_yes else File.async_block_allowed_no, | |
| 49 | 51 | }; |
| 50 | 52 | } |
| 51 | 53 | |
| ... | ... | @@ -81,10 +83,12 @@ fn getStdInHandle() os.fd_t { |
| 81 | 83 | return os.STDIN_FILENO; |
| 82 | 84 | } |
| 83 | 85 | |
| 86 | // TODO: async stdin on windows (https://github.com/ziglang/zig/pull/4816#issuecomment-604521023) | |
| 84 | 87 | pub fn getStdIn() File { |
| 85 | 88 | return File{ |
| 86 | 89 | .handle = getStdInHandle(), |
| 87 | 90 | .io_mode = .blocking, |
| 91 | .async_block_allowed = if (builtin.os.tag == .windows) File.async_block_allowed_yes else File.async_block_allowed_no, | |
| 88 | 92 | }; |
| 89 | 93 | } |
| 90 | 94 |
lib/std/os.zig+20-18| ... | ... | @@ -305,7 +305,7 @@ pub const ReadError = error{ |
| 305 | 305 | /// For POSIX the limit is `math.maxInt(isize)`. |
| 306 | 306 | pub fn read(fd: fd_t, buf: []u8) ReadError!usize { |
| 307 | 307 | if (builtin.os.tag == .windows) { |
| 308 | return windows.ReadFile(fd, buf, null); | |
| 308 | return windows.ReadFile(fd, buf, null, false); | |
| 309 | 309 | } |
| 310 | 310 | |
| 311 | 311 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| ... | ... | @@ -370,7 +370,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { |
| 370 | 370 | const first = iov[0]; |
| 371 | 371 | return read(fd, first.iov_base[0..first.iov_len]); |
| 372 | 372 | } |
| 373 | ||
| 373 | ||
| 374 | 374 | const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31); |
| 375 | 375 | while (true) { |
| 376 | 376 | // TODO handle the case when iov_len is too large and get rid of this @intCast |
| ... | ... | @@ -408,7 +408,7 @@ pub const PReadError = ReadError || error{Unseekable}; |
| 408 | 408 | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. |
| 409 | 409 | pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { |
| 410 | 410 | if (builtin.os.tag == .windows) { |
| 411 | return windows.ReadFile(fd, buf, offset); | |
| 411 | return windows.ReadFile(fd, buf, offset, false); | |
| 412 | 412 | } |
| 413 | 413 | |
| 414 | 414 | while (true) { |
| ... | ... | @@ -584,7 +584,7 @@ pub const WriteError = error{ |
| 584 | 584 | /// The corresponding POSIX limit is `math.maxInt(isize)`. |
| 585 | 585 | pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { |
| 586 | 586 | if (builtin.os.tag == .windows) { |
| 587 | return windows.WriteFile(fd, bytes, null); | |
| 587 | return windows.WriteFile(fd, bytes, null, false); | |
| 588 | 588 | } |
| 589 | 589 | |
| 590 | 590 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| ... | ... | @@ -709,7 +709,7 @@ pub const PWriteError = WriteError || error{Unseekable}; |
| 709 | 709 | /// The corresponding POSIX limit is `math.maxInt(isize)`. |
| 710 | 710 | pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { |
| 711 | 711 | if (std.Target.current.os.tag == .windows) { |
| 712 | return windows.WriteFile(fd, bytes, offset); | |
| 712 | return windows.WriteFile(fd, bytes, offset, false); | |
| 713 | 713 | } |
| 714 | 714 | |
| 715 | 715 | // Prevent EINVAL. |
| ... | ... | @@ -1670,38 +1670,40 @@ pub fn renameatZ( |
| 1670 | 1670 | } |
| 1671 | 1671 | } |
| 1672 | 1672 | |
| 1673 | /// Same as `renameat` except the parameters are null-terminated UTF16LE encoded byte arrays. | |
| 1674 | /// Assumes target is Windows. | |
| 1675 | /// TODO these args can actually be slices when using ntdll. audit the rest of the W functions too. | |
| 1673 | /// Same as `renameat` but Windows-only and the path parameters are | |
| 1674 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. | |
| 1676 | 1675 | pub fn renameatW( |
| 1677 | 1676 | old_dir_fd: fd_t, |
| 1678 | old_path: [*:0]const u16, | |
| 1677 | old_path_w: []const u16, | |
| 1679 | 1678 | new_dir_fd: fd_t, |
| 1680 | new_path_w: [*:0]const u16, | |
| 1679 | new_path_w: []const u16, | |
| 1681 | 1680 | ReplaceIfExists: windows.BOOLEAN, |
| 1682 | 1681 | ) RenameError!void { |
| 1683 | const access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE; | |
| 1684 | const src_fd = windows.OpenFileW(old_dir_fd, old_path, null, access_mask, null, false, windows.FILE_OPEN) catch |err| switch (err) { | |
| 1685 | error.WouldBlock => unreachable, | |
| 1682 | const src_fd = windows.OpenFile(old_path_w, .{ | |
| 1683 | .dir = old_dir_fd, | |
| 1684 | .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE, | |
| 1685 | .creation = windows.FILE_OPEN, | |
| 1686 | .enable_async_io = false, | |
| 1687 | }) catch |err| switch (err) { | |
| 1688 | error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`. | |
| 1686 | 1689 | else => |e| return e, |
| 1687 | 1690 | }; |
| 1688 | 1691 | defer windows.CloseHandle(src_fd); |
| 1689 | 1692 | |
| 1690 | 1693 | const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1); |
| 1691 | 1694 | var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined; |
| 1692 | const new_path = mem.span(new_path_w); | |
| 1693 | const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path.len * 2; | |
| 1695 | const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path_w.len * 2; | |
| 1694 | 1696 | if (struct_len > struct_buf_len) return error.NameTooLong; |
| 1695 | 1697 | |
| 1696 | 1698 | const rename_info = @ptrCast(*windows.FILE_RENAME_INFORMATION, &rename_info_buf); |
| 1697 | 1699 | |
| 1698 | 1700 | rename_info.* = .{ |
| 1699 | 1701 | .ReplaceIfExists = ReplaceIfExists, |
| 1700 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(new_path_w)) null else new_dir_fd, | |
| 1701 | .FileNameLength = @intCast(u32, new_path.len * 2), // already checked error.NameTooLong | |
| 1702 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd, | |
| 1703 | .FileNameLength = @intCast(u32, new_path_w.len * 2), // already checked error.NameTooLong | |
| 1702 | 1704 | .FileName = undefined, |
| 1703 | 1705 | }; |
| 1704 | std.mem.copy(u16, @as([*]u16, &rename_info.FileName)[0..new_path.len], new_path); | |
| 1706 | std.mem.copy(u16, @as([*]u16, &rename_info.FileName)[0..new_path_w.len], new_path_w); | |
| 1705 | 1707 | |
| 1706 | 1708 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 1707 | 1709 |
lib/std/os/windows.zig+44-38| ... | ... | @@ -103,17 +103,19 @@ pub const OpenError = error{ |
| 103 | 103 | WouldBlock, |
| 104 | 104 | }; |
| 105 | 105 | |
| 106 | /// TODO rename to CreateFileW | |
| 107 | /// TODO actually we don't need the path parameter to be null terminated | |
| 108 | pub fn OpenFileW( | |
| 109 | dir: ?HANDLE, | |
| 110 | sub_path_w: [*:0]const u16, | |
| 111 | sa: ?*SECURITY_ATTRIBUTES, | |
| 106 | pub const OpenFileOptions = struct { | |
| 112 | 107 | access_mask: ACCESS_MASK, |
| 113 | share_access_opt: ?ULONG, | |
| 114 | share_access_nonblocking: bool, | |
| 108 | dir: ?HANDLE = null, | |
| 109 | sa: ?*SECURITY_ATTRIBUTES = null, | |
| 110 | share_access: ULONG = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, | |
| 111 | share_access_nonblocking: bool = false, | |
| 115 | 112 | creation: ULONG, |
| 116 | ) OpenError!HANDLE { | |
| 113 | enable_async_io: bool = std.io.is_async, | |
| 114 | }; | |
| 115 | ||
| 116 | /// TODO when share_access_nonblocking is false, this implementation uses | |
| 117 | /// untinterruptible sleep() to block. This is not the final iteration of the API. | |
| 118 | pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE { | |
| 117 | 119 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { |
| 118 | 120 | return error.IsDir; |
| 119 | 121 | } |
| ... | ... | @@ -123,37 +125,37 @@ pub fn OpenFileW( |
| 123 | 125 | |
| 124 | 126 | var result: HANDLE = undefined; |
| 125 | 127 | |
| 126 | const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) { | |
| 128 | const path_len_bytes = math.cast(u16, sub_path_w.len * 2) catch |err| switch (err) { | |
| 127 | 129 | error.Overflow => return error.NameTooLong, |
| 128 | 130 | }; |
| 129 | 131 | var nt_name = UNICODE_STRING{ |
| 130 | 132 | .Length = path_len_bytes, |
| 131 | 133 | .MaximumLength = path_len_bytes, |
| 132 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), | |
| 134 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)), | |
| 133 | 135 | }; |
| 134 | 136 | var attr = OBJECT_ATTRIBUTES{ |
| 135 | 137 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| 136 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir, | |
| 138 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir, | |
| 137 | 139 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. |
| 138 | 140 | .ObjectName = &nt_name, |
| 139 | .SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null, | |
| 141 | .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null, | |
| 140 | 142 | .SecurityQualityOfService = null, |
| 141 | 143 | }; |
| 142 | 144 | var io: IO_STATUS_BLOCK = undefined; |
| 143 | const share_access = share_access_opt orelse (FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE); | |
| 144 | 145 | |
| 145 | 146 | var delay: usize = 1; |
| 146 | 147 | while (true) { |
| 148 | const blocking_flag: ULONG = if (!options.enable_async_io) FILE_SYNCHRONOUS_IO_NONALERT else 0; | |
| 147 | 149 | const rc = ntdll.NtCreateFile( |
| 148 | 150 | &result, |
| 149 | access_mask, | |
| 151 | options.access_mask, | |
| 150 | 152 | &attr, |
| 151 | 153 | &io, |
| 152 | 154 | null, |
| 153 | 155 | FILE_ATTRIBUTE_NORMAL, |
| 154 | share_access, | |
| 155 | creation, | |
| 156 | FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, | |
| 156 | options.share_access, | |
| 157 | options.creation, | |
| 158 | FILE_NON_DIRECTORY_FILE | blocking_flag, | |
| 157 | 159 | null, |
| 158 | 160 | 0, |
| 159 | 161 | ); |
| ... | ... | @@ -165,14 +167,16 @@ pub fn OpenFileW( |
| 165 | 167 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, |
| 166 | 168 | .INVALID_PARAMETER => unreachable, |
| 167 | 169 | .SHARING_VIOLATION => { |
| 168 | if (share_access_nonblocking) { | |
| 170 | if (options.share_access_nonblocking) { | |
| 169 | 171 | return error.WouldBlock; |
| 170 | 172 | } |
| 173 | // TODO sleep in a way that is interruptable | |
| 174 | // TODO integrate with async I/O | |
| 171 | 175 | std.time.sleep(delay); |
| 172 | 176 | if (delay < 1 * std.time.ns_per_s) { |
| 173 | 177 | delay *= 2; |
| 174 | 178 | } |
| 175 | continue; // TODO: don't loop for async | |
| 179 | continue; | |
| 176 | 180 | }, |
| 177 | 181 | .ACCESS_DENIED => return error.AccessDenied, |
| 178 | 182 | .PIPE_BUSY => return error.PipeBusy, |
| ... | ... | @@ -447,10 +451,11 @@ pub const ReadFileError = error{ |
| 447 | 451 | |
| 448 | 452 | /// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into |
| 449 | 453 | /// multiple non-atomic reads. |
| 450 | pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usize { | |
| 451 | if (std.event.Loop.instance) |loop| { | |
| 454 | pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, enable_async_io: bool) ReadFileError!usize { | |
| 455 | if (std.event.Loop.instance != null and enable_async_io) { | |
| 456 | const loop = std.event.Loop.instance.?; | |
| 452 | 457 | // TODO support async ReadFile with no offset |
| 453 | const off = offset.?; | |
| 458 | const off = if (offset == null) 0 else offset.?; | |
| 454 | 459 | var resume_node = std.event.Loop.ResumeNode.Basic{ |
| 455 | 460 | .base = .{ |
| 456 | 461 | .id = .Basic, |
| ... | ... | @@ -465,20 +470,20 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usiz |
| 465 | 470 | }, |
| 466 | 471 | }; |
| 467 | 472 | // TODO only call create io completion port once per fd |
| 468 | _ = windows.CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined) catch undefined; | |
| 473 | _ = CreateIoCompletionPort(in_hFile, loop.os_data.io_port, undefined, undefined) catch undefined; | |
| 469 | 474 | loop.beginOneEvent(); |
| 470 | 475 | suspend { |
| 471 | 476 | // TODO handle buffer bigger than DWORD can hold |
| 472 | _ = windows.kernel32.ReadFile(fd, buffer.ptr, @intCast(windows.DWORD, buffer.len), null, &resume_node.base.overlapped); | |
| 477 | _ = kernel32.ReadFile(in_hFile, buffer.ptr, @intCast(DWORD, buffer.len), null, &resume_node.base.overlapped); | |
| 473 | 478 | } |
| 474 | var bytes_transferred: windows.DWORD = undefined; | |
| 475 | if (windows.kernel32.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { | |
| 476 | switch (windows.kernel32.GetLastError()) { | |
| 479 | var bytes_transferred: DWORD = undefined; | |
| 480 | if (kernel32.GetOverlappedResult(in_hFile, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) { | |
| 481 | switch (kernel32.GetLastError()) { | |
| 477 | 482 | .IO_PENDING => unreachable, |
| 478 | 483 | .OPERATION_ABORTED => return error.OperationAborted, |
| 479 | 484 | .BROKEN_PIPE => return error.BrokenPipe, |
| 480 | 485 | .HANDLE_EOF => return @as(usize, bytes_transferred), |
| 481 | else => |err| return windows.unexpectedError(err), | |
| 486 | else => |err| return unexpectedError(err), | |
| 482 | 487 | } |
| 483 | 488 | } |
| 484 | 489 | return @as(usize, bytes_transferred); |
| ... | ... | @@ -520,10 +525,11 @@ pub const WriteFileError = error{ |
| 520 | 525 | Unexpected, |
| 521 | 526 | }; |
| 522 | 527 | |
| 523 | pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError!usize { | |
| 524 | if (std.event.Loop.instance) |loop| { | |
| 528 | pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64, enable_async_io: bool) WriteFileError!usize { | |
| 529 | if (std.event.Loop.instance != null and enable_async_io) { | |
| 530 | const loop = std.event.Loop.instance.?; | |
| 525 | 531 | // TODO support async WriteFile with no offset |
| 526 | const off = offset.?; | |
| 532 | const off = if (offset == null) 0 else offset.?; | |
| 527 | 533 | var resume_node = std.event.Loop.ResumeNode.Basic{ |
| 528 | 534 | .base = .{ |
| 529 | 535 | .id = .Basic, |
| ... | ... | @@ -538,14 +544,14 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError |
| 538 | 544 | }, |
| 539 | 545 | }; |
| 540 | 546 | // TODO only call create io completion port once per fd |
| 541 | _ = CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined); | |
| 547 | _ = CreateIoCompletionPort(handle, loop.os_data.io_port, undefined, undefined) catch undefined; | |
| 542 | 548 | loop.beginOneEvent(); |
| 543 | 549 | suspend { |
| 544 | const adjusted_len = math.cast(windows.DWORD, bytes.len) catch maxInt(windows.DWORD); | |
| 545 | _ = kernel32.WriteFile(fd, bytes.ptr, adjusted_len, null, &resume_node.base.overlapped); | |
| 550 | const adjusted_len = math.cast(DWORD, bytes.len) catch maxInt(DWORD); | |
| 551 | _ = kernel32.WriteFile(handle, bytes.ptr, adjusted_len, null, &resume_node.base.overlapped); | |
| 546 | 552 | } |
| 547 | var bytes_transferred: windows.DWORD = undefined; | |
| 548 | if (kernel32.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) { | |
| 553 | var bytes_transferred: DWORD = undefined; | |
| 554 | if (kernel32.GetOverlappedResult(handle, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) { | |
| 549 | 555 | switch (kernel32.GetLastError()) { |
| 550 | 556 | .IO_PENDING => unreachable, |
| 551 | 557 | .INVALID_USER_BUFFER => return error.SystemResources, |
| ... | ... | @@ -553,7 +559,7 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError |
| 553 | 559 | .OPERATION_ABORTED => return error.OperationAborted, |
| 554 | 560 | .NOT_ENOUGH_QUOTA => return error.SystemResources, |
| 555 | 561 | .BROKEN_PIPE => return error.BrokenPipe, |
| 556 | else => |err| return windows.unexpectedError(err), | |
| 562 | else => |err| return unexpectedError(err), | |
| 557 | 563 | } |
| 558 | 564 | } |
| 559 | 565 | return bytes_transferred; |
lib/std/pdb.zig+1-1| ... | ... | @@ -470,7 +470,7 @@ pub const Pdb = struct { |
| 470 | 470 | msf: Msf, |
| 471 | 471 | |
| 472 | 472 | pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void { |
| 473 | self.in_file = try fs.cwd().openFile(file_name, .{}); | |
| 473 | self.in_file = try fs.cwd().openFile(file_name, .{ .always_blocking = true }); | |
| 474 | 474 | self.allocator = coff_ptr.allocator; |
| 475 | 475 | self.coff = coff_ptr; |
| 476 | 476 |