authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2022-10-13 21:21:16-07:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2022-10-29 14:30:44-07:00
logc6ff1a71601f42dab5c8607397b28b34085134d8
tree1b99e39173bcb2a9ce7d8f9318cec772224525dd
parent33fdc43714e34f5c4bc02c416ef4c6534acc56ca

Windows: Fix iterator name buffer size not handling all possible file name components

Each u16 within a file name component can be encoded as up to 3 UTF-8 bytes, so we need to use MAX_NAME_BYTES to account for all possible UTF-8 encoded names. Fixes #8268

2 files changed, 40 insertions(+), 2 deletions(-)

lib/std/fs.zig+2-2
......@@ -58,7 +58,7 @@ pub const MAX_NAME_BYTES = switch (builtin.os.tag) {
5858 // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
5959 // If it would require 4 UTF-8 bytes, then there would be a surrogate
6060 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
61 .windows => os.NAME_MAX * 3,
61 .windows => os.windows.NAME_MAX * 3,
6262 else => if (@hasDecl(root, "os") and @hasDecl(root.os, "NAME_MAX"))
6363 root.os.NAME_MAX
6464 else
......@@ -697,7 +697,7 @@ pub const IterableDir = struct {
697697 index: usize,
698698 end_index: usize,
699699 first_iter: bool,
700 name_data: [256]u8,
700 name_data: [MAX_NAME_BYTES]u8,
701701
702702 const Self = @This();
703703
lib/std/fs/test.zig+38
......@@ -703,6 +703,44 @@ test "makePath in a directory that no longer exists" {
703703 try testing.expectError(error.FileNotFound, tmp.dir.makePath("sub-path"));
704704}
705705
706fn testFilenameLimits(iterable_dir: IterableDir, maxed_filename: []const u8) !void {
707 // setup, create a dir and a nested file both with maxed filenames, and walk the dir
708 {
709 var maxed_dir = try iterable_dir.dir.makeOpenPath(maxed_filename, .{});
710 defer maxed_dir.close();
711
712 try maxed_dir.writeFile(maxed_filename, "");
713
714 var walker = try iterable_dir.walk(testing.allocator);
715 defer walker.deinit();
716
717 var count: usize = 0;
718 while (try walker.next()) |entry| {
719 try testing.expectEqualStrings(maxed_filename, entry.basename);
720 count += 1;
721 }
722 try testing.expectEqual(@as(usize, 2), count);
723 }
724
725 // ensure that we can delete the tree
726 try iterable_dir.dir.deleteTree(maxed_filename);
727}
728
729test "filename limits" {
730 var tmp = tmpIterableDir(.{});
731 defer tmp.cleanup();
732
733 if (builtin.os.tag == .windows) {
734 // € is the character with the largest codepoint that is encoded as a single u16 in UTF-16,
735 // so Windows allows for NAME_MAX of them
736 const maxed_windows_filename = ("€".*) ** std.os.windows.NAME_MAX;
737 try testFilenameLimits(tmp.iterable_dir, &maxed_windows_filename);
738 } else {
739 const maxed_ascii_filename = [_]u8{'1'} ** std.fs.MAX_NAME_BYTES;
740 try testFilenameLimits(tmp.iterable_dir, &maxed_ascii_filename);
741 }
742}
743
706744test "writev, readv" {
707745 var tmp = tmpDir(.{});
708746 defer tmp.cleanup();