Newer
Older
minerva / Userland / Utilities / runc / LayoutParsing.cpp
@minerva minerva on 13 Jul 6 KB Initial commit
/*
 * Copyright (c) 2024, Liav A. <liavalb@hotmail.co.il>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include "LayoutParsing.h"

namespace LayoutParsing {

struct JSONPropertyHandler {
    ErrorOr<bool> (*probe)(JsonObject const& object) = nullptr;
    ErrorOr<void> (*handle)(VFSRootContextLayout& layout, JsonObject const& object) = nullptr;
};

static ErrorOr<bool> mount_object_probe(JsonObject const& object)
{
    VERIFY(object.has_string("type"sv));
    auto type = object.get_byte_string("type"sv).value();
    if (type != "mount"sv)
        return false;

    if (!object.has_null("source"sv) && !object.has_string("source"sv))
        return Error::from_string_view("Object source property not found"sv);
    if (!object.has_string("target"sv))
        return Error::from_string_view("Object mount property not found"sv);
    if (!object.has_string("fs_type"sv))
        return Error::from_string_view("Object fs_type property not found"sv);

    return true;
}

static ErrorOr<void> mount_object_handle(VFSRootContextLayout& layout, JsonObject const& object)
{
    VERIFY(object.has_string("target"sv));
    VERIFY(object.has_string("source"sv) || object.has_null("source"sv));
    VERIFY(object.has_string("fs_type"sv));
    auto target = object.get_byte_string("target"sv).value();
    auto fs_type = object.get_byte_string("fs_type"sv).value();
    auto source = object.get_byte_string("source"sv);
    StringView actual_source = "none"sv;
    if (source.has_value())
        actual_source = source.value();
    TRY(layout.mount_new_filesystem(fs_type, actual_source, target, 0));
    return {};
}

static ErrorOr<bool> directory_object_probe(JsonObject const& object)
{
    VERIFY(object.has_string("type"sv));
    auto type = object.get_byte_string("type"sv).value();
    if (type != "directory"sv)
        return false;

    if (!object.has_string("target"sv))
        return Error::from_string_view("Object (directory type) target property not found"sv);

    return true;
}

static ErrorOr<void> directory_object_handle(VFSRootContextLayout& layout, JsonObject const& object)
{
    VERIFY(object.has_string("target"sv));
    auto target = object.get_byte_string("target"sv).value();
    TRY(layout.mkdir(target));
    return {};
}

static ErrorOr<bool> copy_custom_object_probe(JsonObject const& object)
{
    VERIFY(object.has_string("type"sv));
    auto type = object.get_byte_string("type"sv).value();
    if (type != "copy_custom"sv)
        return false;

    if (!object.has_string("source"sv))
        return Error::from_string_view("Object (copy_custom type) source property not found"sv);

    if (!object.has_string("target"sv))
        return Error::from_string_view("Object (copy_custom type) target property not found"sv);

    return true;
}

static ErrorOr<void> copy_custom_object_handle(VFSRootContextLayout& layout, JsonObject const& object)
{
    VERIFY(object.has_string("source"sv));
    VERIFY(object.has_string("target"sv));
    auto source = object.get_byte_string("source"sv).value();
    auto target = object.get_byte_string("target"sv).value();
    TRY(layout.copy_to_custom_location(source, target));
    return {};
}

static ErrorOr<bool> copy_original_object_probe(JsonObject const& object)
{
    VERIFY(object.has_string("type"sv));
    auto type = object.get_byte_string("type"sv).value();
    if (type != "copy_original"sv)
        return false;

    if (!object.has_array("sources"sv))
        return Error::from_string_view("Object (copy_original type) sources array property not found"sv);

    return true;
}

static ErrorOr<void> copy_original_object_handle(VFSRootContextLayout& layout, JsonObject const& object)
{
    VERIFY(object.has_array("sources"sv));
    auto sources = object.get_array("sources"sv).value();
    for (size_t index = 0; index < sources.size(); index++) {
        auto& path = sources[index];
        if (!path.is_string())
            return Error::from_string_view("Object (copy_original type) sources array property invalid"sv);
        TRY(layout.copy_as_original(path.as_string()));
    }
    return {};
}

static ErrorOr<bool> symlink_object_probe(JsonObject const& object)
{
    VERIFY(object.has_string("type"sv));
    auto type = object.get_byte_string("type"sv).value();
    if (type != "symlink"sv)
        return false;

    if (!object.has_string("path"sv))
        return Error::from_string_view("Object (symlink) path property not found"sv);
    if (!object.has_string("target"sv))
        return Error::from_string_view("Object (symlink) target property not found"sv);

    return true;
}

static ErrorOr<void> symlink_object_handle(VFSRootContextLayout& layout, JsonObject const& object)
{
    VERIFY(object.has_string("path"sv));
    VERIFY(object.has_string("target"sv));
    auto path = object.get_byte_string("path"sv).value();
    auto target = object.get_byte_string("target"sv).value();
    TRY(layout.symlink(path, target));
    return {};
}

static constexpr JSONPropertyHandler s_handlers[] = {
    { mount_object_probe, mount_object_handle },
    { directory_object_probe, directory_object_handle },
    { copy_custom_object_probe, copy_custom_object_handle },
    { copy_original_object_probe, copy_original_object_handle },
    { symlink_object_probe, symlink_object_handle },
};

static ErrorOr<void> handle_property(VFSRootContextLayout& layout, JsonObject const& object)
{
    for (auto& handler : s_handlers) {
        auto found_handler = TRY(handler.probe(object));
        if (!found_handler)
            continue;
        return handler.handle(layout, object);
    }

    auto type = object.get_byte_string("type"sv).value();
    dbgln("WARNING: Unknown object type - {}, it might affect layout creation severely", type);
    return {};
}

ErrorOr<void> handle_creation_sequence(VFSRootContextLayout& layout, JsonArray const& layout_creation_sequence)
{
    for (size_t index = 0; index < layout_creation_sequence.size(); index++) {
        auto& object = layout_creation_sequence[index];
        if (!object.is_object())
            return Error::from_string_view("Invalid layout JSON object"sv);
        if (!object.as_object().has_string("type"sv))
            return Error::from_string_view("Invalid layout JSON object - no type being specified"sv);
        TRY(handle_property(layout, object.as_object()));
    }
    return {};
}

}