diff --git a/boot/efi.zig b/boot/efi.zig index 87931fd..e541faa 100644 --- a/boot/efi.zig +++ b/boot/efi.zig @@ -448,17 +448,34 @@ fn walkDirectory( const info: *const uefi.protocol.File.Info.File = @ptrCast(@alignCast(&info_buffer)); const name16 = info.getFileName(); - // Convert the (ASCII in practice) UTF-16 name; skip "." and "..". + // Convert the (ASCII in practice) UTF-16 name. A hostile-shaped entry + // (too long, non-ASCII) is SKIPPED, never fatal — one odd file on a + // hand-written stick must not cost the whole boot. Names are lowered: + // the danos tree is canonically lowercase and FAT lookups are + // case-insensitive, but firmware ENUMERATION returns whatever the + // directory stores — an 8.3 short entry comes back uppercase ("INIT"), + // which would otherwise poison every path comparison downstream. var name_buffer: [initial_ramdisk.maximum_name]u8 = undefined; var name_length: usize = 0; + var name_ok = true; while (name16[name_length] != 0) : (name_length += 1) { - if (name_length == name_buffer.len) return error.NameTooLong; + if (name_length == name_buffer.len) { + name_ok = false; + break; + } const c = name16[name_length]; - if (c > 0x7F) return error.UnsupportedName; - name_buffer[name_length] = @intCast(c); + if (c > 0x7F) { + name_ok = false; + break; + } + name_buffer[name_length] = std.ascii.toLower(@intCast(c)); } + if (!name_ok) continue; const name = name_buffer[0..name_length]; - if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue; + // Skip dot entries: "." / ".." and host-OS litter (macOS "._*" AppleDouble + // resource forks, ".fseventsd", ".Spotlight-V100") a copied-onto stick + // accumulates — none of it is a danos binary. + if (name.len == 0 or name[0] == '.') continue; if (info.attribute.directory) { if (depth == maximum_tree_depth) continue; @@ -474,12 +491,12 @@ fn walkDirectory( if (count.* == maximum_bundled) return error.TooManyBinaries; var entry: *Bundled = &list[count.*]; - const path = try std.fmt.bufPrint(&entry.path, "{s}/{s}", .{ prefix, name }); + const path = std.fmt.bufPrint(&entry.path, "{s}/{s}", .{ prefix, name }) catch continue; // path too long: skip the file, keep the boot entry.path_len = path.len; - const file = try directory.open(name16, .read, .{}); + const file = directory.open(name16, .read, .{}) catch continue; defer _ = file.close() catch {}; - entry.data = try readWholeFile(bs, file); + entry.data = readWholeFile(bs, file) catch continue; // unreadable/empty: skip count.* += 1; } } diff --git a/system/initial-ramdisk.zig b/system/initial-ramdisk.zig index e74f208..07b4005 100644 --- a/system/initial-ramdisk.zig +++ b/system/initial-ramdisk.zig @@ -76,17 +76,20 @@ pub const Reader = struct { /// Look a binary up by name: an exact path match wins; otherwise a unique /// basename match ("fat" finds "/system/services/fat") keeps pre-path callers - /// working. The returned Item's name is always the stored full path. + /// working. Comparisons are ASCII case-insensitive — the entries come from a + /// FAT volume, whose name lookups are case-insensitive by definition (and + /// whose short entries store uppercase). The returned Item's name is always + /// the stored full path. pub fn find(self: Reader, name: []const u8) ?Item { var i: u32 = 0; while (i < self.count) : (i += 1) { const item = self.entry(i) orelse continue; - if (std.mem.eql(u8, item.name, name)) return item; + if (std.ascii.eqlIgnoreCase(item.name, name)) return item; } i = 0; while (i < self.count) : (i += 1) { const item = self.entry(i) orelse continue; - if (std.mem.eql(u8, basename(item.name), name)) return item; + if (std.ascii.eqlIgnoreCase(basename(item.name), name)) return item; } return null; }