Kernel: Load USTAR from a boot module if loading root partition fails
This allows banan-os to boot on hardware where we don't have working storage driver or the storage driver fails (pretty common with my usb mass storage drivers...)
This commit is contained in:
parent
17f1ac10e3
commit
a5b4cee298
|
@ -31,6 +31,7 @@ set(KERNEL_SOURCES
|
||||||
kernel/FS/ProcFS/Inode.cpp
|
kernel/FS/ProcFS/Inode.cpp
|
||||||
kernel/FS/TmpFS/FileSystem.cpp
|
kernel/FS/TmpFS/FileSystem.cpp
|
||||||
kernel/FS/TmpFS/Inode.cpp
|
kernel/FS/TmpFS/Inode.cpp
|
||||||
|
kernel/FS/USTARModule.cpp
|
||||||
kernel/FS/VirtualFileSystem.cpp
|
kernel/FS/VirtualFileSystem.cpp
|
||||||
kernel/GDT.cpp
|
kernel/GDT.cpp
|
||||||
kernel/IDT.cpp
|
kernel/IDT.cpp
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <kernel/BootInfo.h>
|
||||||
|
#include <kernel/FS/FileSystem.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
bool is_ustar_boot_module(const BootModule&);
|
||||||
|
BAN::ErrorOr<void> unpack_boot_module_into_filesystem(BAN::RefPtr<FileSystem>, const BootModule&);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
|
#include <kernel/FS/USTARModule.h>
|
||||||
|
|
||||||
|
#include <tar.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
bool is_ustar_boot_module(const BootModule& module)
|
||||||
|
{
|
||||||
|
if (module.start % PAGE_SIZE)
|
||||||
|
{
|
||||||
|
dprintln("ignoring non-page-aligned module");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module.size < 512)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool has_ustar_signature;
|
||||||
|
PageTable::with_fast_page(module.start, [&] {
|
||||||
|
has_ustar_signature = memcmp(PageTable::fast_page_as_ptr(257), "ustar", 5) == 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return has_ustar_signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> unpack_boot_module_into_filesystem(BAN::RefPtr<FileSystem> filesystem, const BootModule& module)
|
||||||
|
{
|
||||||
|
ASSERT(is_ustar_boot_module(module));
|
||||||
|
|
||||||
|
auto root_inode = filesystem->root_inode();
|
||||||
|
|
||||||
|
uint8_t* temp_page = static_cast<uint8_t*>(kmalloc(PAGE_SIZE));
|
||||||
|
if (temp_page == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
BAN::ScopeGuard _([temp_page] { kfree(temp_page); });
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
while (offset + 512 <= module.size)
|
||||||
|
{
|
||||||
|
size_t file_size = 0;
|
||||||
|
mode_t file_mode = 0;
|
||||||
|
uid_t file_uid = 0;
|
||||||
|
gid_t file_gid = 0;
|
||||||
|
uint8_t file_type = 0;
|
||||||
|
char file_path[100 + 1 + 155 + 1] {};
|
||||||
|
|
||||||
|
PageTable::with_fast_page((module.start + offset) & PAGE_ADDR_MASK, [&] {
|
||||||
|
const size_t page_off = offset % PAGE_SIZE;
|
||||||
|
|
||||||
|
const auto parse_octal =
|
||||||
|
[page_off](size_t offset, size_t length) -> size_t
|
||||||
|
{
|
||||||
|
size_t result = 0;
|
||||||
|
for (size_t i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
const char ch = PageTable::fast_page_as<char>(page_off + offset + i);
|
||||||
|
if (ch == '\0')
|
||||||
|
break;
|
||||||
|
result = (result * 8) + (ch - '0');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (memcmp(PageTable::fast_page_as_ptr(page_off + 257), "ustar", 5)) {
|
||||||
|
file_size = SIZE_MAX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(file_path, PageTable::fast_page_as_ptr(page_off + 345), 155);
|
||||||
|
const size_t prefix_len = strlen(file_path);
|
||||||
|
file_path[prefix_len] = '/';
|
||||||
|
memcpy(file_path + prefix_len + 1, PageTable::fast_page_as_ptr(page_off), 100);
|
||||||
|
|
||||||
|
file_mode = parse_octal(100, 8);
|
||||||
|
file_uid = parse_octal(108, 8);
|
||||||
|
file_gid = parse_octal(116, 8);
|
||||||
|
file_size = parse_octal(124, 12);
|
||||||
|
file_type = PageTable::fast_page_as<char>(page_off + 156);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (file_size == SIZE_MAX)
|
||||||
|
break;
|
||||||
|
if (offset + 512 + file_size > module.size)
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto parent_inode = filesystem->root_inode();
|
||||||
|
|
||||||
|
auto file_path_parts = TRY(BAN::StringView(file_path).split('/'));
|
||||||
|
for (size_t i = 0; i < file_path_parts.size() - 1; i++)
|
||||||
|
parent_inode = TRY(parent_inode->find_inode(file_path_parts[i]));
|
||||||
|
|
||||||
|
switch (file_type)
|
||||||
|
{
|
||||||
|
case REGTYPE:
|
||||||
|
case AREGTYPE: file_mode |= Inode::Mode::IFREG; break;
|
||||||
|
case LNKTYPE: break;
|
||||||
|
case SYMTYPE: file_mode |= Inode::Mode::IFLNK; break;
|
||||||
|
case CHRTYPE: file_mode |= Inode::Mode::IFCHR; break;
|
||||||
|
case BLKTYPE: file_mode |= Inode::Mode::IFBLK; break;
|
||||||
|
case DIRTYPE: file_mode |= Inode::Mode::IFDIR; break;
|
||||||
|
case FIFOTYPE: file_mode |= Inode::Mode::IFIFO; break;
|
||||||
|
default:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto file_name_sv = file_path_parts.back();
|
||||||
|
|
||||||
|
if (file_type == DIRTYPE)
|
||||||
|
{
|
||||||
|
TRY(parent_inode->create_directory(file_name_sv, file_mode, file_uid, file_gid));
|
||||||
|
}
|
||||||
|
else if (file_type == LNKTYPE)
|
||||||
|
{
|
||||||
|
dwarnln("TODO: hardlink");
|
||||||
|
}
|
||||||
|
else if (file_type == SYMTYPE)
|
||||||
|
{
|
||||||
|
TRY(parent_inode->create_file(file_name_sv, file_mode, file_uid, file_gid));
|
||||||
|
|
||||||
|
char link_target[101] {};
|
||||||
|
const paddr_t paddr = module.start + offset;
|
||||||
|
PageTable::with_fast_page(paddr & PAGE_ADDR_MASK, [&] {
|
||||||
|
memcpy(link_target, PageTable::fast_page_as_ptr((paddr % PAGE_SIZE) + 157), 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (link_target[0])
|
||||||
|
{
|
||||||
|
auto inode = TRY(parent_inode->find_inode(file_name_sv));
|
||||||
|
TRY(inode->set_link_target(link_target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TRY(parent_inode->create_file(file_name_sv, file_mode, file_uid, file_gid));
|
||||||
|
|
||||||
|
if (file_size)
|
||||||
|
{
|
||||||
|
auto inode = TRY(parent_inode->find_inode(file_name_sv));
|
||||||
|
|
||||||
|
size_t nwritten = 0;
|
||||||
|
while (nwritten < file_size)
|
||||||
|
{
|
||||||
|
const paddr_t paddr = module.start + offset + 512 + nwritten;
|
||||||
|
PageTable::with_fast_page(paddr & PAGE_ADDR_MASK, [&] {
|
||||||
|
memcpy(temp_page, PageTable::fast_page_as_ptr(), PAGE_SIZE);
|
||||||
|
});
|
||||||
|
|
||||||
|
const size_t page_off = paddr % PAGE_SIZE;
|
||||||
|
const size_t to_write = BAN::Math::min(file_size - nwritten, PAGE_SIZE - page_off);
|
||||||
|
TRY(inode->write(nwritten, { temp_page + page_off, to_write }));
|
||||||
|
nwritten += to_write;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += 512 + file_size;
|
||||||
|
if (auto rem = offset % 512)
|
||||||
|
offset += 512 - rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
#include <kernel/FS/DevFS/FileSystem.h>
|
#include <kernel/FS/DevFS/FileSystem.h>
|
||||||
#include <kernel/FS/ProcFS/FileSystem.h>
|
#include <kernel/FS/ProcFS/FileSystem.h>
|
||||||
#include <kernel/FS/TmpFS/FileSystem.h>
|
#include <kernel/FS/TmpFS/FileSystem.h>
|
||||||
|
#include <kernel/FS/USTARModule.h>
|
||||||
#include <kernel/FS/VirtualFileSystem.h>
|
#include <kernel/FS/VirtualFileSystem.h>
|
||||||
#include <kernel/Lock/LockGuard.h>
|
#include <kernel/Lock/LockGuard.h>
|
||||||
#include <kernel/Storage/Partition.h>
|
#include <kernel/Storage/Partition.h>
|
||||||
|
@ -51,7 +52,31 @@ namespace Kernel
|
||||||
return BAN::RefPtr<BlockDevice>(static_cast<BlockDevice*>(device_inode.ptr()));
|
return BAN::RefPtr<BlockDevice>(static_cast<BlockDevice*>(device_inode.ptr()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static BAN::RefPtr<BlockDevice> find_root_device(BAN::StringView root_path)
|
static BAN::RefPtr<FileSystem> load_fallback_root_filesystem()
|
||||||
|
{
|
||||||
|
if (g_boot_info.modules.empty())
|
||||||
|
panic("No fallback boot modules given");
|
||||||
|
|
||||||
|
auto filesystem_or_error = TmpFileSystem::create(-1, 0755, 0, 0);
|
||||||
|
if (filesystem_or_error.is_error())
|
||||||
|
panic("Failed to create fallback filesystem: {}", filesystem_or_error.error());
|
||||||
|
|
||||||
|
dwarnln("Attempting to load fallback filesystem from {} modules", g_boot_info.modules.size());
|
||||||
|
|
||||||
|
auto filesystem = BAN::RefPtr<FileSystem>::adopt(filesystem_or_error.release_value());
|
||||||
|
|
||||||
|
for (const auto& module : g_boot_info.modules)
|
||||||
|
{
|
||||||
|
if (!is_ustar_boot_module(module))
|
||||||
|
continue;
|
||||||
|
if (auto ret = unpack_boot_module_into_filesystem(filesystem, module); ret.is_error())
|
||||||
|
dwarnln("Failed to unpack boot module: {}", ret.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
return filesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BAN::RefPtr<FileSystem> load_root_filesystem(BAN::StringView root_path)
|
||||||
{
|
{
|
||||||
enum class RootType
|
enum class RootType
|
||||||
{
|
{
|
||||||
|
@ -66,19 +91,26 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
entry = root_path.substring(9);
|
entry = root_path.substring(9);
|
||||||
if (entry.size() != 36)
|
if (entry.size() != 36)
|
||||||
panic("Invalid UUID '{}'", entry);
|
{
|
||||||
|
derrorln("Invalid UUID '{}'", entry);
|
||||||
|
return load_fallback_root_filesystem();
|
||||||
|
}
|
||||||
type = RootType::PartitionUUID;
|
type = RootType::PartitionUUID;
|
||||||
}
|
}
|
||||||
else if (root_path.starts_with("/dev/"_sv))
|
else if (root_path.starts_with("/dev/"_sv))
|
||||||
{
|
{
|
||||||
entry = root_path.substring(5);
|
entry = root_path.substring(5);
|
||||||
if (entry.empty() || entry.contains('/'))
|
if (entry.empty() || entry.contains('/'))
|
||||||
panic("Invalid root path '{}'", root_path);
|
{
|
||||||
|
derrorln("Invalid root path '{}'", root_path);
|
||||||
|
return load_fallback_root_filesystem();
|
||||||
|
}
|
||||||
type = RootType::BlockDeviceName;
|
type = RootType::BlockDeviceName;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
panic("Unsupported root path format '{}'", root_path);
|
derrorln("Unsupported root path format '{}'", root_path);
|
||||||
|
return load_fallback_root_filesystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t timeout_ms = 10'000;
|
constexpr size_t timeout_ms = 10'000;
|
||||||
|
@ -99,15 +131,30 @@ namespace Kernel
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret.is_error())
|
if (!ret.is_error())
|
||||||
return ret.release_value();
|
{
|
||||||
|
auto filesystem_or_error = FileSystem::from_block_device(ret.release_value());
|
||||||
|
if (filesystem_or_error.is_error())
|
||||||
|
{
|
||||||
|
derrorln("Could not create filesystem from '{}': {}", root_path, filesystem_or_error.error());
|
||||||
|
return load_fallback_root_filesystem();
|
||||||
|
}
|
||||||
|
return filesystem_or_error.release_value();;
|
||||||
|
}
|
||||||
|
|
||||||
if (ret.error().get_error_code() != ENOENT)
|
if (ret.error().get_error_code() != ENOENT)
|
||||||
panic("could not open root device '{}': {}", root_path, ret.error());
|
{
|
||||||
|
derrorln("Could not open root device '{}': {}", root_path, ret.error());
|
||||||
|
return load_fallback_root_filesystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 4)
|
||||||
|
dwarnln("Could not find specified root device, waiting for it to get loaded...");
|
||||||
|
|
||||||
SystemTimer::get().sleep_ms(sleep_ms);
|
SystemTimer::get().sleep_ms(sleep_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("could not find root device '{}' after {} ms", root_path, timeout_ms);
|
derrorln("Could not find root device '{}' after {} ms", root_path, timeout_ms);
|
||||||
|
return load_fallback_root_filesystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VirtualFileSystem::initialize(BAN::StringView root_path)
|
void VirtualFileSystem::initialize(BAN::StringView root_path)
|
||||||
|
@ -115,13 +162,9 @@ namespace Kernel
|
||||||
ASSERT(!s_instance);
|
ASSERT(!s_instance);
|
||||||
s_instance = MUST(BAN::RefPtr<VirtualFileSystem>::create());
|
s_instance = MUST(BAN::RefPtr<VirtualFileSystem>::create());
|
||||||
|
|
||||||
auto root_device = find_root_device(root_path);
|
s_instance->m_root_fs = load_root_filesystem(root_path);
|
||||||
ASSERT(root_device);
|
if (!s_instance->m_root_fs)
|
||||||
|
panic("Could not load root filesystem");
|
||||||
auto filesystem_result = FileSystem::from_block_device(root_device);
|
|
||||||
if (filesystem_result.is_error())
|
|
||||||
panic("Could not create filesystem from '{}': {}", root_path, filesystem_result.error());
|
|
||||||
s_instance->m_root_fs = filesystem_result.release_value();
|
|
||||||
|
|
||||||
Credentials root_creds { 0, 0, 0, 0 };
|
Credentials root_creds { 0, 0, 0, 0 };
|
||||||
MUST(s_instance->mount(root_creds, &DevFileSystem::get(), "/dev"_sv));
|
MUST(s_instance->mount(root_creds, &DevFileSystem::get(), "/dev"_sv));
|
||||||
|
|
Loading…
Reference in New Issue