// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <purefs/fs/filesystem.hpp>
#include <purefs/fs/drivers/filesystem_vfat.hpp>
#include <purefs/fs/drivers/filesystem_littlefs.hpp>
#include <purefs/fs/drivers/filesystem_ext4.hpp>
#include <purefs/blkdev/disk_manager.hpp>
#include <purefs/vfs_subsystem.hpp>
#include <purefs/vfs_subsystem_internal.hpp>
#include <purefs/fs/thread_local_cwd.hpp>
#include <log/log.hpp>
#include <purefs/filesystem_paths.hpp>
#include <json11.hpp>
#include <sys/stat.h>
#include <fcntl.h>
namespace purefs::subsystem
{
namespace
{
constexpr auto default_blkdev_name = "emmc0";
constexpr auto default_nvrom_name = "nvrom0";
constexpr auto fat_part_code = 0x0B;
constexpr auto lfs_part_code = 0x9E;
constexpr auto linux_part_code = 0x83;
constexpr auto layout_part_count = 3;
constexpr auto boot_part_index = 0;
constexpr auto user_part_index = 2;
constexpr auto boot_size_limit = 16384L;
constexpr auto block_size_max_shift = 21;
constexpr auto block_size_min_shift = 8;
constexpr uint32_t nvrom_lfs_block_size = 128U;
namespace json
{
constexpr auto os_type = "ostype";
constexpr auto main = "main";
} // namespace json
std::weak_ptr<blkdev::disk_manager> g_disk_mgr;
std::weak_ptr<fs::filesystem> g_fs_core;
} // namespace
namespace
{
int read_mbr_lfs_erase_size(std::shared_ptr<blkdev::disk_manager> disk_mngr,
std::string_view dev_name,
int part_no)
{
static constexpr auto MBR_ERASE_BLK_OFFSET = 0x00E0;
if (part_no <= 0) {
return -EINVAL;
}
const auto sect_size = disk_mngr->get_info(dev_name, blkdev::info_type::sector_size);
if (sect_size <= MBR_ERASE_BLK_OFFSET + part_no) {
return (sect_size > 0) ? (-ERANGE) : (sect_size);
}
auto mbr_buf = std::make_unique<char[]>(sect_size);
int err = disk_mngr->read(dev_name, mbr_buf.get(), 0, 1);
if (err < 0) {
return err;
}
return mbr_buf[MBR_ERASE_BLK_OFFSET + part_no];
}
} // namespace
auto initialize(std::unique_ptr<DeviceFactory> deviceFactory)
-> std::tuple<std::shared_ptr<blkdev::disk_manager>, std::shared_ptr<fs::filesystem>>
{
auto disk_mgr = std::make_shared<blkdev::disk_manager>();
const std::shared_ptr<blkdev::disk> bdev = deviceFactory->makeDefaultBlockDevice();
auto err = disk_mgr->register_device(bdev, default_blkdev_name);
if (err) {
LOG_FATAL("Unable to register block device with error %i", err);
return {};
}
const std::shared_ptr<blkdev::disk> nvrom_bdev = deviceFactory->makeDefaultNvmDevice();
if (nvrom_bdev) {
err = disk_mgr->register_device(nvrom_bdev, default_nvrom_name, blkdev::flags::no_parts_scan);
if (err) {
LOG_WARN("Unable to register NVROM device err %i. Possible: NVROM unavailable", err);
}
}
else {
LOG_WARN("No NVROM driver available for this platform");
}
auto fs_core = std::make_shared<fs::filesystem>(disk_mgr);
err = fs_core->register_filesystem("vfat", std::make_shared<fs::drivers::filesystem_vfat>());
if (err) {
LOG_FATAL("Unable to register vfat filesystem with error %i", err);
return {};
}
err = fs_core->register_filesystem("littlefs", std::make_shared<fs::drivers::filesystem_littlefs>());
if (err) {
LOG_FATAL("Unable to register lfs filesystem with error %i", err);
return {};
}
err = fs_core->register_filesystem("ext4", std::make_shared<fs::drivers::filesystem_ext4>());
if (err) {
LOG_FATAL("Unable to register ext4 filesystem with error %i", err);
return {};
}
g_disk_mgr = disk_mgr;
g_fs_core = fs_core;
return {disk_mgr, fs_core};
}
auto disk_mgr() -> std::shared_ptr<blkdev::disk_manager>
{
return g_disk_mgr.lock();
}
auto vfs_core() -> std::shared_ptr<fs::filesystem>
{
return g_fs_core.lock();
}
auto mount_defaults() -> int
{
auto disk = g_disk_mgr.lock();
if (!disk) {
LOG_FATAL("Unable to lock disk");
return -EIO;
}
const auto parts = disk->partitions(default_blkdev_name);
if (parts.size() != layout_part_count) {
LOG_FATAL("Unknown partitions layout part size is %u", (unsigned)(parts.size()));
return -EIO;
}
const auto &boot_part = parts[boot_part_index];
const auto &user_part = parts[user_part_index];
if ((boot_part.type != fat_part_code) && (boot_part.type != linux_part_code)) {
LOG_FATAL("Invalid boot partition type expected code: %02X or %02X current code: %02X",
fat_part_code,
linux_part_code,
boot_part.type);
return -EIO;
}
if (user_part.type != linux_part_code) {
LOG_FATAL(
"Invalid user partition type expected code: %02X current code: %02X", linux_part_code, user_part.type);
return -EIO;
}
auto vfs = g_fs_core.lock();
if (!vfs) {
LOG_FATAL("Unable to lock vfs core");
return -EIO;
}
auto err = vfs->mount(boot_part.name, purefs::dir::getSystemDiskPath().string(), "auto");
if (err) {
return err;
}
if (user_part.type == lfs_part_code) {
const int lfs_block_log2 = read_mbr_lfs_erase_size(disk, default_blkdev_name, user_part.physical_number);
uint32_t lfs_block_size = 0;
uint32_t *lfs_block_size_ptr = nullptr;
if (lfs_block_log2 >= block_size_min_shift && lfs_block_log2 <= block_size_max_shift) {
lfs_block_size = 1U << lfs_block_log2;
lfs_block_size_ptr = &lfs_block_size;
}
err =
vfs->mount(user_part.name, purefs::dir::getUserDiskPath().string(), "littlefs", 0, lfs_block_size_ptr);
}
else {
err = vfs->mount(user_part.name, purefs::dir::getUserDiskPath().string(), "ext4");
}
fs::internal::set_default_thread_cwd(dir::getSystemDiskPath().string());
// Mount NVRAM memory
err = vfs->mount(default_nvrom_name,
purefs::dir::getMfgConfPath().c_str(),
"littlefs",
fs::mount_flags::read_only,
&nvrom_lfs_block_size);
if (err) {
LOG_WARN("Unable to mount NVROM partition err %i. Possible: NVROM unavailable", err);
err = 0;
}
return err;
}
auto unmount_all() -> int
{
auto vfs = g_fs_core.lock();
if (!vfs) {
LOG_ERROR("Unable to lock vfs");
return -EIO;
}
std::list<std::string> mount_points;
int err = vfs->read_mountpoints(mount_points);
if (err) {
return err;
}
for (const auto &mpoint : mount_points) {
err = vfs->umount(mpoint);
if (err)
break;
}
return err;
}
} // namespace purefs::subsystem