boot: survive hand-written sticks — case-fold the /system walk, skip host litter
The loader ENUMERATES /system now, so it sees whatever the stick's directory entries literally store — and a hand-copied stick differs from our generated image: firmware returns bare 8.3 short entries UPPERCASE (INIT, SYSTEM), and host OSes leave litter next to every file (macOS '._' AppleDouble forks, .fseventsd). Verified in QEMU/OVMF: an uppercase-stored volume booted to a dead kernel-only system before this change and boots fully (display up, logger writing /var/log) after. The walk now lowers ASCII names (the danos tree is canonically lowercase; FAT lookups are case-insensitive by definition), skips any dot-prefixed entry, and treats malformed or unreadable entries as skip-this-file instead of abort-the-whole-walk. Reader.find compares case-insensitively as belt and braces.
This commit is contained in:
parent
0a4388c3bc
commit
d446ddd2ed
33
boot/efi.zig
33
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue