#include "InitRD.h"
#include "Log.h"
#include "arch/MMU.h"
#include "boot/bootboot.h"
#include "fs/VFS.h"
#include "memory/MemoryManager.h"
#include <bits/modes.h>
#include <luna/Alignment.h>

TarStream g_initrd;
extern const BOOTBOOT bootboot;

void InitRD::initialize()
{
    u64 virtual_initrd_address = MMU::translate_physical_address(bootboot.initrd_ptr);

    g_initrd.initialize((void*)virtual_initrd_address, bootboot.initrd_size);
}

static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode)
{
    auto rc = VFS::create_directory(path);
    if (rc.has_error())
    {
        if (rc.error() == EEXIST) return {};
        return rc.release_error();
    }
    rc.value()->chmod(mode & (mode_t)~S_IFMT);
    return {};
}

Result<void> InitRD::populate_vfs()
{
    TarStream::Entry entry;
    while (TRY(g_initrd.read_next_entry(entry)))
    {
        if (entry.type == TarStream::EntryType::RegularFile)
        {
            auto file = TRY(VFS::create_file(entry.name));
            file->write(entry.data(), 0, entry.size);
            file->chmod(entry.mode & (mode_t)~S_IFMT);
        }
        else if (entry.type == TarStream::EntryType::Directory)
        {
            TRY(vfs_create_dir_if_not_exists(entry.name, entry.mode));
        }
    }

    // Now we don't need the original initrd anymore
    MemoryManager::free_frames(bootboot.initrd_ptr, get_blocks_from_size(bootboot.initrd_size, ARCH_PAGE_SIZE));

    return {};
}