Commit c6ff1a7160

Ryan Liptak <squeek502@hotmail.com>
2022-10-14 06:21:16
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
1 parent 33fdc43
Changed files (2)
lib
lib/std/fs/test.zig
@@ -703,6 +703,44 @@ test "makePath in a directory that no longer exists" {
     try testing.expectError(error.FileNotFound, tmp.dir.makePath("sub-path"));
 }
 
+fn testFilenameLimits(iterable_dir: IterableDir, maxed_filename: []const u8) !void {
+    // setup, create a dir and a nested file both with maxed filenames, and walk the dir
+    {
+        var maxed_dir = try iterable_dir.dir.makeOpenPath(maxed_filename, .{});
+        defer maxed_dir.close();
+
+        try maxed_dir.writeFile(maxed_filename, "");
+
+        var walker = try iterable_dir.walk(testing.allocator);
+        defer walker.deinit();
+
+        var count: usize = 0;
+        while (try walker.next()) |entry| {
+            try testing.expectEqualStrings(maxed_filename, entry.basename);
+            count += 1;
+        }
+        try testing.expectEqual(@as(usize, 2), count);
+    }
+
+    // ensure that we can delete the tree
+    try iterable_dir.dir.deleteTree(maxed_filename);
+}
+
+test "filename limits" {
+    var tmp = tmpIterableDir(.{});
+    defer tmp.cleanup();
+
+    if (builtin.os.tag == .windows) {
+        // € is the character with the largest codepoint that is encoded as a single u16 in UTF-16,
+        // so Windows allows for NAME_MAX of them
+        const maxed_windows_filename = ("€".*) ** std.os.windows.NAME_MAX;
+        try testFilenameLimits(tmp.iterable_dir, &maxed_windows_filename);
+    } else {
+        const maxed_ascii_filename = [_]u8{'1'} ** std.fs.MAX_NAME_BYTES;
+        try testFilenameLimits(tmp.iterable_dir, &maxed_ascii_filename);
+    }
+}
+
 test "writev, readv" {
     var tmp = tmpDir(.{});
     defer tmp.cleanup();
lib/std/fs.zig
@@ -58,7 +58,7 @@ pub const MAX_NAME_BYTES = switch (builtin.os.tag) {
     // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
     // If it would require 4 UTF-8 bytes, then there would be a surrogate
     // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
-    .windows => os.NAME_MAX * 3,
+    .windows => os.windows.NAME_MAX * 3,
     else => if (@hasDecl(root, "os") and @hasDecl(root.os, "NAME_MAX"))
         root.os.NAME_MAX
     else
@@ -697,7 +697,7 @@ pub const IterableDir = struct {
             index: usize,
             end_index: usize,
             first_iter: bool,
-            name_data: [256]u8,
+            name_data: [MAX_NAME_BYTES]u8,
 
             const Self = @This();