~aleteoryx/muditaos

b83605d227599dd29c9d16a552a95f281c28aae4 — Lucjan Bryndza 4 years ago 8cc1f77
[EGD-7573] Add support for reliance edge fs

Added support for reliance edge file system.
The initial port is done.
The whole effort is still WIP.
Left a note in readme file what should be done next.
37 files changed, 2827 insertions(+), 13 deletions(-)

M .gitmodules
M module-platform/linux/src/DiskImage.cpp
M module-platform/linux/tests/CMakeLists.txt
A module-platform/linux/tests/unittest_filesystem_reedgefs.cpp
M module-platform/rt1051/src/disk_emmc.cpp
M module-vfs/CMakeLists.txt
M module-vfs/README.md
A module-vfs/drivers/include/purefs/fs/drivers/directory_handle_reedgefs.hpp
A module-vfs/drivers/include/purefs/fs/drivers/file_handle_reedgefs.hpp
A module-vfs/drivers/include/purefs/fs/drivers/filesystem_reedgefs.hpp
M module-vfs/drivers/include/purefs/fs/drivers/mount_point_ext4.hpp
M module-vfs/drivers/include/purefs/fs/drivers/mount_point_littlefs.hpp
A module-vfs/drivers/include/purefs/fs/drivers/mount_point_reedgefs.hpp
M module-vfs/drivers/include/purefs/fs/drivers/mount_point_vfat.hpp
A module-vfs/drivers/include/thirdparty/reedgefs/redconf.h
A module-vfs/drivers/include/thirdparty/reedgefs/redconf/redconf.h
A module-vfs/drivers/include/thirdparty/reedgefs/redostypes.h
A module-vfs/drivers/include/thirdparty/reedgefs/redtypes.h
A module-vfs/drivers/include/thirdparty/reedgefs/volume_mapper.hpp
A module-vfs/drivers/src/purefs/fs/filesystem_reedgefs.cpp
A module-vfs/drivers/src/thirdparty/reedgefs/glue.cpp
A module-vfs/drivers/src/thirdparty/reedgefs/include/redostypes.h
A module-vfs/drivers/src/thirdparty/reedgefs/redconf.c
A module-vfs/drivers/src/thirdparty/reedgefs/services/osassert.c
A module-vfs/drivers/src/thirdparty/reedgefs/services/osbdev.c
A module-vfs/drivers/src/thirdparty/reedgefs/services/osbdev_custom.h
A module-vfs/drivers/src/thirdparty/reedgefs/services/osclock.c
A module-vfs/drivers/src/thirdparty/reedgefs/services/osmutex.c
A module-vfs/drivers/src/thirdparty/reedgefs/services/osoutput.c
A module-vfs/drivers/src/thirdparty/reedgefs/services/ostask.c
A module-vfs/drivers/src/thirdparty/reedgefs/services/ostimestamp.c
M module-vfs/include/user/purefs/blkdev/defs.hpp
M module-vfs/include/user/purefs/fs/mount_point.hpp
M module-vfs/src/purefs/blkdev/disk_manager.cpp
M third-party/CMakeLists.txt
A third-party/reedgefs/CMakeLists.txt
A third-party/reedgefs/src
M .gitmodules => .gitmodules +4 -0
@@ 102,3 102,7 @@
[submodule "third-party/dr_libs/src"]
	path = third-party/dr_libs/src
	url = https://github.com/mudita/dr_libs.git
[submodule "reliance-edge"]
	path = third-party/reedgefs/src
	url = ../reliance-edge.git
    branch = mudita

M module-platform/linux/src/DiskImage.cpp => module-platform/linux/src/DiskImage.cpp +2 -0
@@ 151,6 151,8 @@ namespace purefs::blkdev
            return m_sectors[hwpart];
        case info_type::erase_block:
            return 1;
        case info_type::start_sector:
            return -ENOTSUP;
        }
        return -ENOTSUP;
    }

M module-platform/linux/tests/CMakeLists.txt => module-platform/linux/tests/CMakeLists.txt +15 -5
@@ 11,7 11,7 @@ add_custom_command(
add_catch2_executable(
    NAME vfs-disk
    SRCS
        ${CMAKE_CURRENT_LIST_DIR}/unittest_disk_manager.cpp
        unittest_disk_manager.cpp
    LIBS
        platform
        purefs-paths


@@ 23,7 23,7 @@ add_catch2_executable(
add_catch2_executable(
    NAME vfs-core-fs
    SRCS
        ${CMAKE_CURRENT_LIST_DIR}/unittest_filesystem_core.cpp
        unittest_filesystem_core.cpp
    LIBS
        platform
        module-vfs


@@ 48,7 48,7 @@ add_custom_target(
add_catch2_executable(
    NAME vfs-littlefs
    SRCS
        ${CMAKE_CURRENT_LIST_DIR}/unittest_filesystem_littlefs.cpp
        unittest_filesystem_littlefs.cpp
    LIBS
        platform
        module-vfs


@@ 60,7 60,7 @@ add_catch2_executable(
add_catch2_executable(
    NAME vfs-ext4
    SRCS
        ${CMAKE_CURRENT_LIST_DIR}/unittest_filesystem_ext4.cpp
        unittest_filesystem_ext4.cpp
    LIBS
        platform
        module-vfs


@@ 72,7 72,7 @@ add_catch2_executable(
add_catch2_executable(
    NAME vfs-dualmount
    SRCS
        ${CMAKE_CURRENT_LIST_DIR}/unittest_filesystem_dualmount.cpp
        unittest_filesystem_dualmount.cpp
    LIBS
        platform
        module-vfs


@@ 94,6 94,16 @@ add_catch2_executable(
    USE_FS
)

add_catch2_executable(
    NAME vfs-reedgefs
    SRCS
        unittest_filesystem_reedgefs.cpp
    LIBS
        platform
        module-vfs
    USE_FS
)

# prepare test assets
set(ASSETS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test_dir")
set(ASSETS_TARGET_DIR "${CMAKE_BINARY_DIR}/module-platform/test_dir")

A module-platform/linux/tests/unittest_filesystem_reedgefs.cpp => module-platform/linux/tests/unittest_filesystem_reedgefs.cpp +379 -0
@@ 0,0 1,379 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>

#include <platform/linux/DiskImage.hpp>

#include <purefs/fs/filesystem.hpp>
#include <purefs/blkdev/disk_manager.hpp>
#include <purefs/fs/drivers/filesystem_reedgefs.hpp>
#include <purefs/vfs_subsystem.hpp>

#include <purefs/fs/thread_local_cwd.hpp>

#include "test-setup.hpp"

#include <tuple>

#include <sys/statvfs.h>
#include <sys/stat.h>
#include <fcntl.h>

TEST_CASE("reedgefs: Registering and unregistering block device")
{
    using namespace purefs;
    auto dm   = std::make_shared<blkdev::disk_manager>();
    auto disk = std::make_shared<blkdev::disk_image>(::testing::vfs::disk_image);
    REQUIRE(disk);
    REQUIRE(dm->register_device(disk, "emmc0") == 0);
    purefs::fs::filesystem fscore(dm);
    /* Requested filesystem is not registered */
    REQUIRE(fscore.mount("emmc0", "/sys", "reedgefs") == -ENODEV);
    const auto vfs_reedgefs = std::make_shared<fs::drivers::filesystem_reedgefs>();
    REQUIRE(vfs_reedgefs->mount_count() == 0);
    auto ret = fscore.register_filesystem("reedgefs", vfs_reedgefs);
    REQUIRE(ret == 0);
    ret = fscore.mount("emmc0part0", "/sys", "reedgefs");
    REQUIRE(ret == 0);
    REQUIRE(vfs_reedgefs->mount_count() == 1);
    REQUIRE(fscore.umount("/ala") == -ENOENT);
    ret = fscore.mount("emmc0part0", "/sys", "reedgefs");
    REQUIRE(ret == -EBUSY);
    ret = fscore.mount("emmc0part0", "/path", "reedgefs");
    REQUIRE(ret == -EBUSY);
    ret = fscore.mount("emmc0part2", "/path", "nonexisting_fs");
    REQUIRE(ret == -ENODEV);
    ret = fscore.umount("/sys");
    REQUIRE(ret == 0);
    REQUIRE(vfs_reedgefs->mount_count() == 0);
    ret = fscore.mount("emmc0part0", "/path", "reedgefs");
    REQUIRE(ret == 0);
    REQUIRE(vfs_reedgefs->mount_count() == 1);
    ret = fscore.umount("/path");
    REQUIRE(ret == 0);
}

TEST_CASE("reedgefs: Basic API test")
{
    using namespace purefs;
    auto dm   = std::make_shared<blkdev::disk_manager>();
    auto disk = std::make_shared<blkdev::disk_image>(::testing::vfs::disk_image);
    REQUIRE(disk);
    REQUIRE(dm->register_device(disk, "emmc0") == 0);
    purefs::fs::filesystem fscore(dm);
    const auto vfs_reedgefs = std::make_shared<fs::drivers::filesystem_reedgefs>();
    REQUIRE(vfs_reedgefs->mount_count() == 0);
    auto ret = fscore.register_filesystem("reedgefs", vfs_reedgefs);
    REQUIRE(ret == 0);
    // List partitions
    for (const auto &part : dm->partitions("emmc0")) {
        std::cout << part.name << " " << part.bootable << std::endl;
    }
    ret = fscore.mount("emmc0part0", "/sys", "reedgefs");
    REQUIRE(ret == 0);
    {
        struct statvfs ssv;
        ret = fscore.stat_vfs("/sys/", ssv);
        REQUIRE(ret == 0);
    }

    // TODO: switch to image once image is ready
    {
        ret = fscore.open("/sys/ala/ma/kota/", 0, 0);
        REQUIRE(ret == -ENOENT);
        // Simple file test
        // TODO: to remove when image ready
        {
            int hwnd = fscore.open("/sys/.boot.json", O_RDWR | O_CREAT, 0);
            REQUIRE(hwnd >= 3);
            const std::string text = "test";
            REQUIRE(fscore.write(hwnd, text.c_str(), text.size()) == ssize_t(text.length()));
            REQUIRE(fscore.close(hwnd) == 0);
        }
        int hwnd = fscore.open("/sys/.boot.json", 0, 0);
        REQUIRE(hwnd >= 3);
        std::cout << "File open handle " << hwnd << std::endl;
        struct stat st;
        ret = fscore.fstat(hwnd, st);
        REQUIRE(ret == 0);
        std::cout << "File size " << st.st_size << std::endl;
        ret = fscore.stat("/sys/.boot.json", st);
        REQUIRE(ret == 0);
        std::cout << "File size " << st.st_size << std::endl;
        char buf[4096]{};
        ret = fscore.read(hwnd, buf, sizeof buf);
        REQUIRE(ret > 0);
        ret = fscore.close(hwnd);
        REQUIRE(ret == 0);
        {
            // TODO: to remove when image ready
            {
                int hwnd = fscore.open("/sys/.boot2.json", O_RDWR | O_CREAT, 0);
                REQUIRE(hwnd >= 3);
                const std::string text = "test";
                REQUIRE(fscore.write(hwnd, text.c_str(), text.size()) == ssize_t(text.length()));
                REQUIRE(fscore.close(hwnd) == 0);
            }
            // Simple directory test
            auto dirhandle = fscore.diropen("/sys");
            REQUIRE(dirhandle);
            REQUIRE(dirhandle->error() == 0);
            for (std::string fnm;;) {
                if (fscore.dirnext(dirhandle, fnm, st) != 0) {
                    break;
                }
                else {
                    std::cout << "name " << fnm << " size " << st.st_size << std::endl;
                }
            }
            fscore.dirclose(dirhandle);
            dirhandle = nullptr;
        }
    }
    REQUIRE(fscore.umount("/sys") == 0);
}

TEST_CASE("reedgefs: Create new file, write, read from it")
{
    using namespace purefs;
    auto dm   = std::make_shared<blkdev::disk_manager>();
    auto disk = std::make_shared<blkdev::disk_image>(::testing::vfs::disk_image);
    REQUIRE(disk);
    REQUIRE(dm->register_device(disk, "emmc0") == 0);
    purefs::fs::filesystem fscore(dm);
    const auto vfs_reedgefs = std::make_shared<fs::drivers::filesystem_reedgefs>();
    REQUIRE(vfs_reedgefs->mount_count() == 0);
    auto ret = fscore.register_filesystem("reedgefs", vfs_reedgefs);
    REQUIRE(ret == 0);
    REQUIRE(fscore.mount("emmc0part0", "/sys", "reedgefs") == 0);

    int hwnd = fscore.open("/sys/test.txt", O_RDWR | O_CREAT, 0660);
    REQUIRE(hwnd >= 3);

    const std::string text = "test";
    fscore.write(hwnd, text.c_str(), text.size());

    REQUIRE(fscore.close(hwnd) == 0);
    SECTION("Read from file")
    {
        int hwnd = fscore.open("/sys/test.txt", O_RDONLY, 0);
        REQUIRE(hwnd >= 3);
        char buf[sizeof(text.c_str())] = {0};
        REQUIRE(fscore.read(hwnd, buf, sizeof(buf)) == 4);
        REQUIRE(strcmp(buf, text.c_str()) == 0);
        fscore.close(hwnd);
        REQUIRE(fscore.umount("/sys") == 0);
    }

    SECTION("Test seek file")
    {
        int hwnd = fscore.open("/sys/test.txt", O_RDONLY, 0);
        REQUIRE(hwnd >= 3);
        char buf[4096]{};
        REQUIRE(fscore.read(hwnd, buf, sizeof(buf)) == 4);
        REQUIRE(fscore.seek(hwnd, 0, SEEK_END) == 4);
        REQUIRE(fscore.read(hwnd, buf, sizeof(buf)) == 0);
        REQUIRE(fscore.seek(hwnd, 0, SEEK_SET) == 0);
        fscore.close(hwnd);
        REQUIRE(fscore.umount("/sys") == 0);
    }
}

TEST_CASE("reedgefs: Mount empty strings")
{
    using namespace purefs;
    auto dm   = std::make_shared<blkdev::disk_manager>();
    auto disk = std::make_shared<blkdev::disk_image>(::testing::vfs::disk_image);
    REQUIRE(disk);
    REQUIRE(dm->register_device(disk, "emmc0") == 0);
    purefs::fs::filesystem fscore(dm);
    const auto vfs_reedgefs = std::make_shared<fs::drivers::filesystem_reedgefs>();
    REQUIRE(vfs_reedgefs->mount_count() == 0);
    auto ret = fscore.register_filesystem("reedgefs", vfs_reedgefs);
    REQUIRE(ret == 0);
    REQUIRE(fscore.mount("", "", "") == -EINVAL);
    REQUIRE(fscore.umount("") == -ENOENT);
}

TEST_CASE("reedgefs: Write to not valid file descriptor")
{
    using namespace purefs;
    auto dm   = std::make_shared<blkdev::disk_manager>();
    auto disk = std::make_shared<blkdev::disk_image>(::testing::vfs::disk_image);
    REQUIRE(disk);
    REQUIRE(dm->register_device(disk, "emmc0") == 0);
    purefs::fs::filesystem fscore(dm);
    const auto vfs_reedgefs = std::make_shared<fs::drivers::filesystem_reedgefs>();
    REQUIRE(vfs_reedgefs->mount_count() == 0);
    auto ret = fscore.register_filesystem("reedgefs", vfs_reedgefs);
    REQUIRE(ret == 0);
    REQUIRE(fscore.mount("emmc0part0", "/sys", "reedgefs") == 0);

    int fd = fscore.open("/sys/.boot.json", O_RDWR | O_CREAT, 0);
    REQUIRE(fd >= 3);
    const auto text = "test";
    REQUIRE(fscore.write(0, text, sizeof(text)) == -EBADF);
    REQUIRE(fscore.write(fd + 1, text, sizeof(text)) == -EBADF);
    REQUIRE(fscore.close(fd) == 0);
    REQUIRE(fscore.umount("/sys") == 0);
}

TEST_CASE("reedgefs: Directory operations")
{
    using namespace purefs;
    auto dm   = std::make_shared<blkdev::disk_manager>();
    auto disk = std::make_shared<blkdev::disk_image>(::testing::vfs::disk_image);
    REQUIRE(disk);
    REQUIRE(dm->register_device(disk, "emmc0") == 0);
    purefs::fs::filesystem fscore(dm);
    const auto vfs_reedgefs = std::make_shared<fs::drivers::filesystem_reedgefs>();
    REQUIRE(vfs_reedgefs->mount_count() == 0);
    auto ret = fscore.register_filesystem("reedgefs", vfs_reedgefs);
    REQUIRE(ret == 0);
    REQUIRE(fscore.mount("emmc0part0", "/sys", "reedgefs") == 0);

    REQUIRE(fscore.mkdir("/sys/current", 0) == 0);

    const auto dirhandle = fscore.diropen("/sys/current");
    REQUIRE(dirhandle);
    REQUIRE(dirhandle->error() == 0);

    SECTION("Null pointer handle dirnext")
    {
        struct stat st;
        std::string fnm;
        REQUIRE(fscore.dirnext(nullptr, fnm, st) == -ENXIO);
        REQUIRE(fscore.dirclose(dirhandle) == 0);
        REQUIRE(fscore.umount("/sys") == 0);
    }

    SECTION("Null pointer handle dirclose")
    {
        REQUIRE(fscore.dirclose(nullptr) == -ENXIO);
        REQUIRE(fscore.dirclose(dirhandle) == 0);
        REQUIRE(fscore.umount("/sys") == 0);
    }

    SECTION("Directory reset")
    {
        struct stat st;
        std::vector<std::tuple<std::string, struct stat>> vec;
        for (std::string fnm;;) {
            if (fscore.dirnext(dirhandle, fnm, st) != 0) {
                break;
            }
            else {
                vec.push_back(std::make_tuple(fnm, st));
                std::cout << "name " << fnm << " size " << st.st_size << std::endl;
            }
        }

        fscore.dirreset(dirhandle);
        int i = 0;
        for (std::string fnm;; i++) {
            if (fscore.dirnext(dirhandle, fnm, st) != 0) {
                break;
            }
            else {
                const auto [fnm_vec, st_vec] = vec[i];
                REQUIRE(fnm == fnm_vec);
                REQUIRE(st_vec.st_size == st.st_size);
                std::cout << "name " << fnm << " size " << st.st_size << std::endl;
            }
        }
        REQUIRE(fscore.dirclose(dirhandle) == 0);
        REQUIRE(fscore.umount("/sys") == 0);
    }
}

TEST_CASE("reedgefs: Read only filesystem")
{
    using namespace purefs;
    auto dm   = std::make_shared<blkdev::disk_manager>();
    auto disk = std::make_shared<blkdev::disk_image>(::testing::vfs::disk_image);
    REQUIRE(disk);
    REQUIRE(dm->register_device(disk, "emmc0") == 0);
    purefs::fs::filesystem fscore(dm);
    const auto vfs_reedgefs = std::make_shared<fs::drivers::filesystem_reedgefs>();
    REQUIRE(vfs_reedgefs->mount_count() == 0);
    auto ret = fscore.register_filesystem("reedgefs", vfs_reedgefs);
    REQUIRE(ret == 0);
    REQUIRE(fscore.mount("emmc0part0", "/sys", "reedgefs", fs::mount_flags::read_only) == 0);
    SECTION("Open file in O_RDWR")
    {
        int hwnd = fscore.open("/sys/rotest.txt", O_RDWR | O_CREAT, 0660);
        REQUIRE(hwnd == -EACCES);
        const std::string text = "test";
        fscore.write(hwnd, text.c_str(), text.size());
    }
    SECTION("Check function which not modify fs")
    {
        struct statvfs ssv;
        ret = fscore.stat_vfs("/sys/", ssv);
        REQUIRE(ret == 0);
    }
    SECTION("Check stat to not set S_IW...")
    {
        struct stat st;
        ret = fscore.stat("/sys", st);
        REQUIRE(ret == 0);
        REQUIRE(st.st_mode & S_IFDIR);
        REQUIRE((st.st_mode & (S_IWGRP | S_IWUSR | S_IWOTH)) == 0);
    }
    REQUIRE(fscore.umount("/sys") == 0);
}

TEST_CASE("reedgefs: Remount filesystem from RO to RW and to RO")
{
    using namespace purefs;
    auto dm   = std::make_shared<blkdev::disk_manager>();
    auto disk = std::make_shared<blkdev::disk_image>(::testing::vfs::disk_image);
    REQUIRE(disk);
    REQUIRE(dm->register_device(disk, "emmc0") == 0);
    auto fscore             = std::make_shared<purefs::fs::filesystem>(dm);
    const auto vfs_reedgefs = std::make_shared<fs::drivers::filesystem_reedgefs>();
    REQUIRE(vfs_reedgefs->mount_count() == 0);
    auto ret = fscore->register_filesystem("reedgefs", vfs_reedgefs);
    REQUIRE(ret == 0);
    REQUIRE(fscore->mount("emmc0part0", "/sys", "reedgefs", fs::mount_flags::read_only) == 0);

    {
        const int hwnd = fscore->open("/sys/remount_test.txt", O_RDWR | O_CREAT, 0660);
        REQUIRE(hwnd == -EACCES);
    }
    REQUIRE(fscore->mount("", "/sys", "", fs::mount_flags::remount) == 0);
    {
        int hwnd = fscore->open("/sys/remount_test4.txt", O_RDWR | O_CREAT, 0660);
        REQUIRE(hwnd > 2);
        const std::string text = "test";
        fscore->write(hwnd, text.c_str(), text.size());
        REQUIRE(fscore->close(hwnd) == 0);
    }
    {
        REQUIRE(fscore->mkdir("/sys/current", 0660) == 0);
        struct stat st;
        ret = fscore->stat("/sys", st);
        REQUIRE(ret == 0);
        REQUIRE(st.st_mode & S_IFDIR);
        REQUIRE(st.st_mode & (S_IWGRP | S_IWUSR | S_IWOTH));
    }
    REQUIRE(fscore->umount("/sys") == 0);
}

// TEST_CASE("reedgefs: Autodetect filesystems")
// {
//     using namespace purefs;
//     auto dm   = std::make_shared<blkdev::disk_manager>();
//     auto disk = std::make_shared<blkdev::disk_image>(::testing::vfs::disk_image);
//     REQUIRE(disk);
//     REQUIRE(dm->register_device(disk, "emmc0") == 0);
//     auto fscore         = std::make_shared<purefs::fs::filesystem>(dm);
//     const auto vfs_reedgefs = std::make_shared<fs::drivers::filesystem_reedgefs>();
//     REQUIRE(vfs_reedgefs->mount_count() == 0);
//     auto ret = fscore->register_filesystem("reedgefs", vfs_reedgefs);
//     REQUIRE(ret == 0);
//     REQUIRE(fscore->mount("emmc0part0", "/sys", "auto") == 0);
//     REQUIRE(fscore->umount("/sys") == 0);
// }

M module-platform/rt1051/src/disk_emmc.cpp => module-platform/rt1051/src/disk_emmc.cpp +3 -0
@@ 155,6 155,9 @@ namespace purefs::blkdev
        case info_type::erase_block:
            // not supported
            return 0;
        case info_type::start_sector:
            // not supported
            return 0;
        }
        return -ENOTSUP;
    }

M module-vfs/CMakeLists.txt => module-vfs/CMakeLists.txt +18 -0
@@ 16,26 16,42 @@ target_sources(module-vfs

        drivers/include/purefs/fs/drivers/directory_handle_littlefs.hpp
        drivers/include/purefs/fs/drivers/directory_handle_vfat.hpp
        drivers/include/purefs/fs/drivers/directory_handle_reedgefs.hpp
        drivers/include/purefs/fs/drivers/file_handle_littlefs.hpp
        drivers/include/purefs/fs/drivers/file_handle_vfat.hpp
        drivers/include/purefs/fs/drivers/file_handle_reedgefs.hpp
        drivers/include/purefs/fs/drivers/filesystem_littlefs.hpp
        drivers/include/purefs/fs/drivers/filesystem_ext4.hpp
        drivers/include/purefs/fs/drivers/filesystem_vfat.hpp
        drivers/include/purefs/fs/drivers/filesystem_reedgefs.hpp
        drivers/include/purefs/fs/drivers/mount_point_littlefs.hpp
        drivers/include/purefs/fs/drivers/mount_point_vfat.hpp
        drivers/include/purefs/fs/drivers/mount_point_ext4.hpp
        drivers/include/purefs/fs/drivers/mount_point_reedgefs.hpp
        drivers/include/thirdparty/fatfs/ffconf.h
        drivers/include/thirdparty/reedgefs/redconf.h
        drivers/include/thirdparty/fatfs/volume_mapper.hpp
        drivers/include/thirdparty/littlefs/volume_mapper.hpp
        drivers/include/thirdparty/lwext4/ext4_bdev.hpp
        drivers/src/purefs/fs/filesystem_littlefs.cpp
        drivers/src/purefs/fs/filesystem_ext4.cpp
        drivers/src/purefs/fs/filesystem_vfat.cpp
        drivers/src/purefs/fs/filesystem_reedgefs.cpp
        drivers/src/thirdparty/fatfs/ff_glue.cpp
        drivers/src/thirdparty/fatfs/ffsystem.cpp
        drivers/src/thirdparty/littlefs/lfs_glue.cpp
        drivers/src/thirdparty/lwext4/ext4_bdev.cpp
        drivers/src/purefs/fs/mount_point_ext4.cpp
        drivers/src/thirdparty/reedgefs/redconf.c
        drivers/src/thirdparty/reedgefs/glue.cpp
        drivers/src/thirdparty/reedgefs/services/osassert.c
        drivers/src/thirdparty/reedgefs/services/osbdev_custom.h
        drivers/src/thirdparty/reedgefs/services/osbdev.c
        drivers/src/thirdparty/reedgefs/services/osclock.c
        drivers/src/thirdparty/reedgefs/services/osmutex.c
        drivers/src/thirdparty/reedgefs/services/osoutput.c
        drivers/src/thirdparty/reedgefs/services/ostask.c
        drivers/src/thirdparty/reedgefs/services/ostimestamp.c

        include/internal/purefs/blkdev/disk_handle.hpp
        include/internal/purefs/blkdev/partition_parser.hpp


@@ 87,6 103,7 @@ target_include_directories(module-vfs
            $<$<STREQUAL:${PROJECT_TARGET},TARGET_Linux>:${CMAKE_CURRENT_SOURCE_DIR}/board/linux/purefs/include>
            ${CMAKE_CURRENT_SOURCE_DIR}/drivers/include/thirdparty
            ${CMAKE_CURRENT_SOURCE_DIR}/drivers/include/thirdparty/fatfs
            ${CMAKE_CURRENT_SOURCE_DIR}/drivers/include/thirdparty/reedgefs
            ${CMAKE_CURRENT_SOURCE_DIR}/include/internal
        >
    PUBLIC


@@ 98,6 115,7 @@ target_include_directories(module-vfs

target_link_libraries(module-vfs 
    PRIVATE
        reliance-edge::fs
        fatfs::fatfs
        lwext4::lwext4
        json::json

M module-vfs/README.md => module-vfs/README.md +10 -0
@@ 42,3 42,13 @@ For LittleFS you will have to use fuse to mount partition. Tool is already build
2. Mount: `./lfsfuse --block_size=32768 /dev/sdX mymount` where sdbx is device where you expect LFS partition
3. Enter here and play with data
4. Umount and eject the device i.e. with: `udisksctl power-off -b /dev/sdb`

## Reliance edge - redgefs

Reliance edge support is considered as WIP. Below are the proposed steps to finish implementation
* prepare tool to create image
* do NOT format partion on the fly during mounting
* run UT on image with redgefs partion 
* run rt1051 with redgefs partiion and do functional and performance tests e.g. check if statvfs is not too slow
* prepare fuse tool
* port redgefs to Updater

A module-vfs/drivers/include/purefs/fs/drivers/directory_handle_reedgefs.hpp => module-vfs/drivers/include/purefs/fs/drivers/directory_handle_reedgefs.hpp +31 -0
@@ 0,0 1,31 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <purefs/fs/directory_handle.hpp>
#include <redfs.h>
#include <redposix.h>

namespace purefs::fs::drivers
{
    class directory_handle_reedgefs final : public internal::directory_handle
    {
      public:
        directory_handle_reedgefs(std::shared_ptr<internal::mount_point> mp, int error)
            : internal::directory_handle(mp, error)
        {}
        virtual ~directory_handle_reedgefs() = default;
        auto reedgefs_dirp() const noexcept
        {
            return m_dir;
        }
        auto reedgefs_dirp(::REDDIR *dir) noexcept
        {
            m_dir = dir;
        }

      private:
        ::REDDIR *m_dir{};
    };
} // namespace purefs::fs::drivers

A module-vfs/drivers/include/purefs/fs/drivers/file_handle_reedgefs.hpp => module-vfs/drivers/include/purefs/fs/drivers/file_handle_reedgefs.hpp +35 -0
@@ 0,0 1,35 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <purefs/fs/file_handle.hpp>

namespace purefs::fs::drivers
{
    class file_handle_reedgefs final : public internal::file_handle
    {
      public:
        file_handle_reedgefs(std::shared_ptr<internal::mount_point> mp, std::string_view path, unsigned flags)
            : file_handle(mp, flags), m_path(path)
        {}
        virtual ~file_handle_reedgefs() = default;
        auto fd() const noexcept
        {
            return m_fd;
        }
        auto fd(int32_t fd) noexcept -> void
        {
            m_fd = fd;
        }
        auto open_path() const noexcept -> std::string override
        {
            return m_path;
        }

      private:
        int32_t m_fd{};
        //! Store full path because some handle based fncs are not in ff_Fat
        const std::string m_path;
    };
} // namespace purefs::fs::drivers

A module-vfs/drivers/include/purefs/fs/drivers/filesystem_reedgefs.hpp => module-vfs/drivers/include/purefs/fs/drivers/filesystem_reedgefs.hpp +49 -0
@@ 0,0 1,49 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <purefs/fs/filesystem_operations.hpp>

namespace purefs::fs::drivers
{
    /** Filesystem specific driver base class */
    class filesystem_reedgefs final : public filesystem_operations
    {
      public:
        filesystem_reedgefs();
        virtual ~filesystem_reedgefs();

      private:
        auto mount_prealloc(std::shared_ptr<blkdev::internal::disk_handle> diskh, std::string_view path, unsigned flags)
            -> fsmount override;
        auto mount(fsmount mnt, const void *data) noexcept -> int override;
        auto umount(fsmount mnt) noexcept -> int override;

        auto stat_vfs(fsmount mnt, std::string_view path, statvfs &stat) const noexcept -> int override;

        /** Standard file access API */
        auto open(fsmount mnt, std::string_view path, int flags, int mode) noexcept -> fsfile override;
        auto close(fsfile zfile) noexcept -> int override;
        auto write(fsfile zfile, const char *ptr, size_t len) noexcept -> ssize_t override;
        auto read(fsfile zfile, char *ptr, size_t len) noexcept -> ssize_t override;
        auto seek(fsfile zfile, off_t pos, int dir) noexcept -> off_t override;
        auto fstat(fsfile zfile, struct stat &st) noexcept -> int override;
        auto stat(fsmount mnt, std::string_view file, struct stat &st) noexcept -> int override;
        auto unlink(fsmount mnt, std::string_view name) noexcept -> int override;
        auto rename(fsmount mnt, std::string_view oldname, std::string_view newname) noexcept -> int override;
        auto mkdir(fsmount mnt, std::string_view path, int mode) noexcept -> int override;

        /** Directory support API */
        auto diropen(fsmount mnt, std::string_view path) noexcept -> fsdir override;
        auto dirreset(fsdir dirstate) noexcept -> int override;
        auto dirnext(fsdir dirstate, std::string &filename, struct stat &filestat) -> int override;
        auto dirclose(fsdir dirstate) noexcept -> int override;

        /** Other fops API */
        auto ftruncate(fsfile zfile, off_t len) noexcept -> int override;
        auto fsync(fsfile zfile) noexcept -> int override;
        auto isatty(fsfile zfile) noexcept -> int override;

        auto filesystem_register_completed() const noexcept -> int override;
    };
} // namespace purefs::fs::drivers

M module-vfs/drivers/include/purefs/fs/drivers/mount_point_ext4.hpp => module-vfs/drivers/include/purefs/fs/drivers/mount_point_ext4.hpp +1 -1
@@ 33,7 33,7 @@ namespace purefs::fs::drivers
        auto unlock() noexcept -> void;

      private:
        auto native_root() const noexcept -> std::string_view override
        auto native_root() const noexcept -> std::string override
        {
            return m_root;
        }

M module-vfs/drivers/include/purefs/fs/drivers/mount_point_littlefs.hpp => module-vfs/drivers/include/purefs/fs/drivers/mount_point_littlefs.hpp +1 -1
@@ 28,7 28,7 @@ namespace purefs::fs::drivers
        }

      private:
        auto native_root() const noexcept -> std::string_view override
        auto native_root() const noexcept -> std::string override
        {
            return "";
        }

A module-vfs/drivers/include/purefs/fs/drivers/mount_point_reedgefs.hpp => module-vfs/drivers/include/purefs/fs/drivers/mount_point_reedgefs.hpp +38 -0
@@ 0,0 1,38 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <purefs/fs/mount_point.hpp>

namespace purefs::fs::drivers
{
    class mount_point_reedgefs final : public purefs::fs::internal::mount_point
    {
      public:
        mount_point_reedgefs(std::shared_ptr<blkdev::internal::disk_handle> diskh,
                             std::string_view path,
                             unsigned flags,
                             std::shared_ptr<filesystem_operations> fs)
            : mount_point(diskh, path, flags, fs)
        {}
        virtual ~mount_point_reedgefs() = default;

        auto volume_name(std::string volume_name) -> void
        {
            m_volume_name = volume_name;
        }

        auto volume_name() const noexcept -> std::string
        {
            return m_volume_name;
        }

      private:
        auto native_root() const noexcept -> std::string override
        {
            return volume_name();
        }
        std::string m_volume_name;
    };
} // namespace purefs::fs::drivers

M module-vfs/drivers/include/purefs/fs/drivers/mount_point_vfat.hpp => module-vfs/drivers/include/purefs/fs/drivers/mount_point_vfat.hpp +1 -1
@@ 43,7 43,7 @@ namespace purefs::fs::drivers
        }

      private:
        auto native_root() const noexcept -> std::string_view override
        auto native_root() const noexcept -> std::string override
        {
            return ff_drive();
        }

A module-vfs/drivers/include/thirdparty/reedgefs/redconf.h => module-vfs/drivers/include/thirdparty/reedgefs/redconf.h +125 -0
@@ 0,0 1,125 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*  THIS FILE WAS GENERATED BY THE TUXERA RELIANCE EDGE CONFIGURATION UTILITY.
    DO NOT MODIFY.

    Generated by configuration utility version 2.5
*/
/** @file
 */
#ifndef REDCONF_H
#define REDCONF_H

#include <string.h>

#define REDCONF_READ_ONLY 0

#define REDCONF_API_POSIX 1

#define REDCONF_API_FSE 0

#define REDCONF_API_POSIX_FORMAT 1

#define REDCONF_API_POSIX_LINK 1

#define REDCONF_API_POSIX_UNLINK 1

#define REDCONF_API_POSIX_MKDIR 1

#define REDCONF_API_POSIX_RMDIR 1

#define REDCONF_API_POSIX_RENAME 1

#define REDCONF_RENAME_ATOMIC 1

#define REDCONF_API_POSIX_FTRUNCATE 1

#define REDCONF_API_POSIX_READDIR 1

#define REDCONF_API_POSIX_CWD 0

#define REDCONF_API_POSIX_FSTRIM 0

#define REDCONF_NAME_MAX 256U

#define REDCONF_PATH_SEPARATOR '/'

#define REDCONF_TASK_COUNT 20U

#define REDCONF_HANDLE_COUNT 512U

#define REDCONF_API_FSE_FORMAT 0

#define REDCONF_API_FSE_TRUNCATE 0

#define REDCONF_API_FSE_TRANSMASKGET 0

#define REDCONF_API_FSE_TRANSMASKSET 0

#define REDCONF_OUTPUT 1

#define REDCONF_ASSERTS 1

#define REDCONF_BLOCK_SIZE 1024U

#define REDCONF_VOLUME_COUNT 4U

#define REDCONF_ENDIAN_BIG 0

#define REDCONF_ALIGNMENT_SIZE 4U

#define REDCONF_CRC_ALGORITHM CRC_SLICEBY8

#define REDCONF_INODE_BLOCKS 1

#define REDCONF_INODE_TIMESTAMPS 1

#define REDCONF_ATIME 0

#define REDCONF_DIRECT_POINTERS 20U

#define REDCONF_INDIRECT_POINTERS 40U

#define REDCONF_BUFFER_COUNT 20U

#define REDCONF_BUFFER_ALIGNMENT 8U

#define REDCONF_BUFFER_WRITE_GATHER_SIZE_KB 0U

#define RedMemCpyUnchecked memcpy

#define RedMemMoveUnchecked memmove

#define RedMemSetUnchecked memset

#define RedMemCmpUnchecked memcmp

#define RedStrLenUnchecked strlen

#define RedStrCmpUnchecked strcmp

#define RedStrNCmpUnchecked strncmp

#define RedStrNCpyUnchecked strncpy

#define REDCONF_TRANSACT_DEFAULT                                                                                       \
    ((RED_TRANSACT_CREAT | RED_TRANSACT_MKDIR | RED_TRANSACT_RENAME | RED_TRANSACT_LINK | RED_TRANSACT_UNLINK |        \
      RED_TRANSACT_FSYNC | RED_TRANSACT_CLOSE | RED_TRANSACT_VOLFULL | RED_TRANSACT_UMOUNT | RED_TRANSACT_SYNC) &      \
     RED_TRANSACT_MASK)

#define REDCONF_IMAP_INLINE 1

#define REDCONF_IMAP_EXTERNAL 1

#define REDCONF_DISCARDS 0

#define REDCONF_IMAGE_BUILDER 0

#define REDCONF_CHECKER 0

#define RED_CONFIG_UTILITY_VERSION 0x2050000U

#define RED_CONFIG_MINCOMPAT_VER 0x2030000U

#endif

A module-vfs/drivers/include/thirdparty/reedgefs/redconf/redconf.h => module-vfs/drivers/include/thirdparty/reedgefs/redconf/redconf.h +111 -0
@@ 0,0 1,111 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/** @file
 */

/*  Inherit most settings from the target configuration.
 */
#include "../redconf.h"

#ifndef HOST_REDCONF_H
#define HOST_REDCONF_H

/*  Assuming the host machine is little endian.  If the host machine is actually
    big endian, this can be worked around by changing the below "== 1" to "== 0"
    and setting REDCONF_ENDIAN_BIG to 1 below.
*/
#if REDCONF_ENDIAN_BIG == 1
#define REDCONF_ENDIAN_SWAP
#endif

#undef REDCONF_ENDIAN_BIG
#define REDCONF_ENDIAN_BIG 0

/*  Ignore the target system memory alignment.  For Linux, 4 bytes works well.
 */
#undef REDCONF_ALIGNMENT_SIZE
#define REDCONF_ALIGNMENT_SIZE 4U

/*  Host tools always have output.
 */
#undef REDCONF_OUTPUT
#define REDCONF_OUTPUT 1

/*  Read-only must be disabled for the image builder.
 */
#undef REDCONF_READ_ONLY
#define REDCONF_READ_ONLY 0

/*  Enable the checker host tool.
 */
#undef REDCONF_CHECKER
#define REDCONF_CHECKER 1

/*  Enable the formatter code in POSIX-like API configurations for the image
    builder and formatter host tools.
*/
#undef REDCONF_API_POSIX_FORMAT
#define REDCONF_API_POSIX_FORMAT 1

/*  Enable the image builder host tool.
 */
#undef REDCONF_IMAGE_BUILDER
#define REDCONF_IMAGE_BUILDER 1

/*  The image builder needs red_mkdir().
 */
#undef REDCONF_API_POSIX_MKDIR
#define REDCONF_API_POSIX_MKDIR 1

/*  The image copier utility needs red_readdir().
 */
#undef REDCONF_API_POSIX_READDIR
#define REDCONF_API_POSIX_READDIR 1

/*  The image copier utility needs a handle for every level of directory depth.
    While Reliance Edge has no maximum directory depth or path depth, Windows
    limits paths to 260 bytes, and each level of depth eats up at least two
    characters, 130 handles will be sufficient for all images that can be
    copied.
*/
#undef REDCONF_HANDLE_COUNT
#define REDCONF_HANDLE_COUNT 130U

/*  Use the fastest CRC algorithm for the tools.  Slice-by-8 will work well on
    a host machine and the extra code space is a nonissue.
*/
#undef REDCONF_CRC_ALGORITHM
#define REDCONF_CRC_ALGORITHM CRC_SLICEBY8

/*  The target redconf.h may have configured the memory and string functions to
    use custom implementations that are only available on the target system.  So
    for the host, we just use the C library versions.
*/
#include <string.h>

#undef RedMemCpyUnchecked
#define RedMemCpyUnchecked memcpy
#undef RedMemMoveUnchecked
#define RedMemMoveUnchecked memmove
#undef RedMemSetUnchecked
#define RedMemSetUnchecked memset
#undef RedMemCmpUnchecked
#define RedMemCmpUnchecked memcmp

#undef RedStrLenUnchecked
#define RedStrLenUnchecked strlen
#undef RedStrCmpUnchecked
#define RedStrCmpUnchecked strcmp
#undef RedStrNCmpUnchecked
#define RedStrNCmpUnchecked strncmp
#undef RedStrNCpyUnchecked
#define RedStrNCpyUnchecked strncpy

/*  Avoid extra transactions to improve image builder performance.
 */
#undef REDCONF_TRANSACT_DEFAULT
#define REDCONF_TRANSACT_DEFAULT                                                                                       \
    ((RED_TRANSACT_FSYNC | RED_TRANSACT_VOLFULL | RED_TRANSACT_UMOUNT | RED_TRANSACT_SYNC) & RED_TRANSACT_MASK)

#endif

A module-vfs/drivers/include/thirdparty/reedgefs/redostypes.h => module-vfs/drivers/include/thirdparty/reedgefs/redostypes.h +42 -0
@@ 0,0 1,42 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                  Copyright (c) 2014-2021 Tuxera US Inc.
                      All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license must obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
    @brief Defines OS-specific types for use in common code.
*/
#ifndef REDOSTYPES_H
#define REDOSTYPES_H

/** @brief Implementation-defined timestamp type.

    This can be an integer, a structure, or a pointer: anything that is
    convenient for the implementation.  Since the underlying type is not fixed,
    common code should treat this as an opaque type.
*/
typedef uint32_t REDTIMESTAMP;

#endif

A module-vfs/drivers/include/thirdparty/reedgefs/redtypes.h => module-vfs/drivers/include/thirdparty/reedgefs/redtypes.h +97 -0
@@ 0,0 1,97 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                  Copyright (c) 2014-2021 Tuxera US Inc.
                      All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license must obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
    @brief Defines basic types used by Reliance Edge.

    The following types *must* be defined by this header, either directly (using
    typedef) or indirectly (by including other headers, such as the C99 headers
    stdint.h and stdbool.h):

    - bool: Boolean type, capable of storing true (1) or false (0)
    - uint8_t: Unsigned 8-bit integer
    - int8_t: Signed 8-bit integer
    - uint16_t: Unsigned 16-bit integer
    - int16_t: Signed 16-bit integer
    - uint32_t: Unsigned 32-bit integer
    - int32_t: Signed 32-bit integer
    - uint64_t: Unsigned 64-bit integer
    - int64_t: Signed 64-bit integer
    - uintptr_t: Unsigned integer capable of storing a pointer, preferably the
      same size as pointers themselves.

    These types deliberately use the same names as the standard C99 types, so
    that if the C99 headers stdint.h and stdbool.h are available, they may be
    included here.

    If the user application defines similar types, those may be reused.  For
    example, suppose there is an application header apptypes.h which defines
    types with a similar purpose but different names.  That header could be
    reused to define the types Reliance Edge needs:

    ~~~{.c}
    #include <apptypes.h>

    typedef BOOL bool;
    typedef BYTE uint8_t;
    typedef INT8 int8_t;
    // And so on...
    ~~~

    If there are neither C99 headers nor suitable types in application headers,
    this header should be populated with typedefs that define the required types
    in terms of the standard C types.  This requires knowledge of the size of
    the C types on the target hardware (e.g., how big is an "int" or a pointer).
    Below is an example which assumes the target has 8-bit chars, 16-bit shorts,
    32-bit ints, 32-bit pointers, and 64-bit long longs:

    ~~~{.c}
    typedef int bool;
    typedef unsigned char uint8_t;
    typedef signed char int8_t;
    typedef unsigned short uint16_t;
    typedef short int16_t;
    typedef unsigned int uint32_t;
    typedef int int32_t;
    typedef unsigned long long uint64_t;
    typedef long long int64_t;
    typedef uint32_t uintptr_t;
    ~~~
*/
#ifndef REDTYPES_H
#define REDTYPES_H

/*  Defines bool.
 */
#include <stdbool.h>

/*  Defines uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t,
    int64_t, and uintptr_t.
*/
#include <stdint.h>

#endif

A module-vfs/drivers/include/thirdparty/reedgefs/volume_mapper.hpp => module-vfs/drivers/include/thirdparty/reedgefs/volume_mapper.hpp +34 -0
@@ 0,0 1,34 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <string>
#include <memory>
#include <purefs/blkdev/defs.hpp>

namespace purefs::blkdev
{
    class disk_manager;
}
namespace purefs::fs::drivers::reedgefs::internal
{

    /** Map volume to partion of logical drive
     * @param[in] disk Disk manager handle to attach partition
     * @return Volume identifier or error if negative
     */
    std::pair<std::string, int> append_volume(blkdev::disk_fd diskh, const std::string &path);

    /** Clear mapping table of logical drives
     * @param[in] diskmm Initialized disk manager object
     */
    void reset_volumes(std::shared_ptr<blkdev::disk_manager> diskmm);

    /** Remove volume from map
     * @param [in] diskh Disk manager handle object
     * @return 0 on success otherwise error
     */
    int remove_volume(blkdev::disk_fd diskh);

} // namespace purefs::fs::drivers::reedgefs::internal

A module-vfs/drivers/src/purefs/fs/filesystem_reedgefs.cpp => module-vfs/drivers/src/purefs/fs/filesystem_reedgefs.cpp +519 -0
@@ 0,0 1,519 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <purefs/fs/drivers/filesystem_reedgefs.hpp>
#include <purefs/fs/drivers/mount_point_reedgefs.hpp>
#include <purefs/blkdev/disk_manager.hpp>
#include <purefs/blkdev/disk_handle.hpp>
#include <purefs/fs/drivers/file_handle_reedgefs.hpp>
#include <purefs/fs/drivers/directory_handle_reedgefs.hpp>
#include <purefs/fs/mount_flags.hpp>
#include <log/log.hpp>
#include <reedgefs/volume_mapper.hpp>
#include <sys/statvfs.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <redfs.h>
#include <redposix.h>

#include <cstring>

namespace purefs::fs::drivers
{
    namespace
    {
        int translate_error(int error)
        {
            switch (error) {
            case -RED_ENOTSUPP:
                return -ENOTSUP;
            default:
                return error;
            }
        }

        uint8_t translate_flags(unsigned flags)
        {
            uint8_t mode = 0;
            switch (flags & O_ACCMODE) {
            case O_RDONLY:
                mode |= RED_O_RDONLY;
                break;
            case O_WRONLY:
                mode |= RED_O_WRONLY;
                break;
            case O_RDWR:
                mode |= RED_O_RDWR;
                break;
            }

            if (flags & O_APPEND) {
                mode |= RED_O_APPEND;
            }
            if (flags & O_CREAT) {
                mode |= RED_O_CREAT;
            }
            if (flags & O_TRUNC) {
                mode |= RED_O_TRUNC;
            }
            if (flags & O_EXCL) {
                mode |= RED_O_EXCL;
            }
            return mode;
        }

        auto translate_mode_to_st_mode(uint16_t _mode, bool readOnly)
        {
            decltype(std::declval<struct stat *>()->st_mode) mode = _mode | S_IRUSR | S_IRGRP | S_IROTH;
            if (!readOnly) {
                mode |= S_IWUSR | S_IWGRP | S_IWOTH;
            }
            return mode;
        }

        void translate_redstat_to_stat(const REDSTAT &fs, uint32_t blksize, struct stat &st, bool ro)
        {
            std::memset(&st, 0, sizeof st);
            st.st_dev   = fs.st_dev;
            st.st_ino   = fs.st_ino;
            st.st_mode  = translate_mode_to_st_mode(fs.st_mode, ro);
            st.st_nlink = fs.st_nlink;
            st.st_uid   = 0;
            st.st_gid   = 0;
            st.st_rdev  = 0;
            st.st_size  = fs.st_size;

            st.st_blksize = blksize;
            st.st_blocks  = fs.st_blocks;
            // For some reason compiler returns error here even if REDCONF_INODE_TIMESTAMPS is set to 1
            // #if REDCONF_INODE_TIMESTAMPS == 1
            //             st.st_atime = fs.st_atime;
            //             st.st_mtime = fs.st_mtime;
            //             st.st_ctime = fs.st_ctime;
            // #endif
        }
    } // namespace

    filesystem_reedgefs::filesystem_reedgefs()
    {
        red_init();
    }

    filesystem_reedgefs::~filesystem_reedgefs()
    {
        red_uninit();
    }

    auto filesystem_reedgefs::mount_prealloc(std::shared_ptr<blkdev::internal::disk_handle> diskh,
                                             std::string_view path,
                                             unsigned flags) -> fsmount
    {
        return std::make_shared<mount_point_reedgefs>(diskh, path, flags, shared_from_this());
    }

    auto filesystem_reedgefs::mount(fsmount mnt, const void *data) noexcept -> int
    {
        auto disk = mnt->disk();
        if (!disk) {
            return -EIO;
        }
        auto vmnt = std::dynamic_pointer_cast<mount_point_reedgefs>(mnt);
        if (!vmnt) {
            LOG_ERROR("Non reedgefs mount point");
            return -EIO;
        }
        const auto mountPath   = vmnt->mount_path();
        auto [volumeName, ret] = reedgefs::internal::append_volume(disk, mountPath);
        if (ret < 0) {
            LOG_ERROR("Unable to attach volume to ff layer with errno %i", ret);
            return ret;
        }
        // TODO: This is temporary fix, need to be removed when image creation is ready
        red_format(volumeName.c_str());
        // TODO: ////////////////////////////////

        if (red_mount(volumeName.c_str()) < 0) {
            return translate_error(-red_errno);
        }
        vmnt->volume_name(volumeName);
        filesystem_operations::mount(mnt, data);
        return ret;
    }

    auto filesystem_reedgefs::filesystem_register_completed() const noexcept -> int
    {
        const auto dmgr = disk_mngr();
        if (dmgr) {
            reedgefs::internal::reset_volumes(dmgr);
            return 0;
        }
        else {
            return -EIO;
        }
    }

    auto filesystem_reedgefs::umount(fsmount mnt) noexcept -> int
    {
        auto vmnt = std::dynamic_pointer_cast<mount_point_reedgefs>(mnt);
        if (!vmnt) {
            LOG_ERROR("Non reedgefs mount point");
            return -EIO;
        }

        auto disk = mnt->disk();
        if (!disk) {
            return -EIO;
        }

        int ret = red_umount(vmnt->volume_name().c_str());
        if (ret < 0) {
            ret = translate_error(-red_errno);
        }
        if (!ret) {
            ret = reedgefs::internal::remove_volume(disk);
            filesystem_operations::umount(mnt);
        }
        return ret;
    }

    auto filesystem_reedgefs::stat_vfs(fsmount mnt, std::string_view, struct statvfs &stat) const noexcept -> int
    {
        auto vmnt = std::dynamic_pointer_cast<mount_point_reedgefs>(mnt);
        if (!vmnt) {
            LOG_ERROR("Non reedgefs mount point");
            return -EIO;
        }
        REDSTATFS statvfs;
        auto ret = red_statvfs(vmnt->volume_name().c_str(), &statvfs);
        if (ret != 0) {
            return translate_error(-red_errno);
        }

        stat.f_bsize   = statvfs.f_bsize;
        stat.f_frsize  = statvfs.f_frsize;
        stat.f_blocks  = statvfs.f_blocks;
        stat.f_bfree   = statvfs.f_bfree;
        stat.f_bavail  = statvfs.f_bavail;
        stat.f_flag    = statvfs.f_flag;
        stat.f_files   = statvfs.f_files;
        stat.f_ffree   = statvfs.f_ffree;
        stat.f_favail  = statvfs.f_favail;
        stat.f_fsid    = statvfs.f_fsid;
        stat.f_namemax = statvfs.f_namemax;
        return 0;
    }

    auto filesystem_reedgefs::open(fsmount mnt, std::string_view path, int flags, int mode) noexcept -> fsfile
    {
        auto vmnt = std::dynamic_pointer_cast<mount_point_reedgefs>(mnt);
        if (!vmnt) {
            LOG_ERROR("Non reedgefs mount point");
            return nullptr;
        }
        const auto fspath  = vmnt->native_path(path);
        const auto fsflags = translate_flags(flags);
        auto fileo         = std::make_shared<file_handle_reedgefs>(mnt, fspath, flags);
        auto ret           = red_open(fspath.c_str(), fsflags);
        if (ret < 0) {
            fileo->error(translate_error(-red_errno));
        }
        else {
            fileo->fd(ret);
        }

        return fileo;
    }

    auto filesystem_reedgefs::close(fsfile zfile) noexcept -> int
    {
        auto vfile = std::dynamic_pointer_cast<file_handle_reedgefs>(zfile);
        if (!vfile) {
            LOG_ERROR("Non reedgefs filesystem pointer");
            return -EBADF;
        }
        const auto ret = red_close(vfile->fd());
        if (ret < 0) {
            return translate_error(-red_errno);
        }
        return 0;
    }

    auto filesystem_reedgefs::write(fsfile zfile, const char *ptr, size_t len) noexcept -> ssize_t
    {
        auto vfile = std::dynamic_pointer_cast<file_handle_reedgefs>(zfile);
        if (!vfile) {
            LOG_ERROR("Non reedgefs filesystem pointer");
            return -EBADF;
        }

        const auto ret = red_write(vfile->fd(), ptr, len);
        if (ret < 0) {
            return translate_error(-red_errno);
        }

        return ret;
    }

    auto filesystem_reedgefs::read(fsfile zfile, char *ptr, size_t len) noexcept -> ssize_t
    {
        auto vfile = std::dynamic_pointer_cast<file_handle_reedgefs>(zfile);
        if (!vfile) {
            LOG_ERROR("Non reedgefs filesystem pointer");
            return -EBADF;
        }
        const auto ret = red_read(vfile->fd(), ptr, len);
        if (ret < 0) {
            return translate_error(-red_errno);
        }

        return ret;
    }

    auto filesystem_reedgefs::seek(fsfile zfile, off_t pos, int dir) noexcept -> off_t
    {
        auto vfile = std::dynamic_pointer_cast<file_handle_reedgefs>(zfile);
        if (!vfile) {
            LOG_ERROR("Non reedgefs filesystem pointer");
            return -EBADF;
        }

        auto ret = red_lseek(vfile->fd(), pos, static_cast<REDWHENCE>(dir));
        if (ret < 0) {
            return translate_error(-red_errno);
        }

        return ret;
    }

    auto filesystem_reedgefs::fstat(fsfile zfile, struct stat &st) noexcept -> int
    {
        auto vfile = std::dynamic_pointer_cast<file_handle_reedgefs>(zfile);
        if (!vfile) {
            LOG_ERROR("Non reedgefs filesystem pointer");
            return -EBADF;
        }
        REDSTAT redstat;
        const auto ret = red_fstat(vfile->fd(), &redstat);
        if (ret < 0) {
            return translate_error(-red_errno);
        }

        const auto mnt = vfile->mntpoint();
        if (mnt) {
            auto vmnt = std::dynamic_pointer_cast<mount_point_reedgefs>(mnt);
            if (!vmnt) {
                LOG_ERROR("Non reedgefs mount point");
                return -EIO;
            }
            REDSTATFS redstatfs;
            auto ret = red_statvfs(vmnt->volume_name().c_str(), &redstatfs);
            if (ret != 0) {
                return translate_error(-red_errno);
            }
            translate_redstat_to_stat(redstat, redstatfs.f_bsize, st, vmnt->is_ro());
        }
        else {
            return -EIO;
        }

        return 0;
    }

    auto filesystem_reedgefs::stat(fsmount mnt, std::string_view file, struct stat &st) noexcept -> int
    {
        auto vmnt = std::dynamic_pointer_cast<mount_point_reedgefs>(mnt);
        if (!vmnt) {
            LOG_ERROR("Non reedgefs mount point");
            return -EBADF;
        }

        const auto fspath = vmnt->native_path(file);
        auto fh           = red_open(fspath.c_str(), RED_O_RDONLY);
        if (fh < 0) {
            return translate_error(-red_errno);
        }
        REDSTAT redstat;
        auto ret = red_fstat(fh, &redstat);
        if (ret < 0) {
            return translate_error(-red_errno);
        }
        REDSTATFS redstatfs;
        ret = red_statvfs(vmnt->volume_name().c_str(), &redstatfs);
        if (ret != 0) {
            return translate_error(-red_errno);
        }
        translate_redstat_to_stat(redstat, redstatfs.f_bsize, st, vmnt->is_ro());
        ret = red_close(fh);
        if (ret < 0) {
            return translate_error(-red_errno);
        }
        return 0;
    }

    auto filesystem_reedgefs::unlink(fsmount mnt, std::string_view name) noexcept -> int
    {
        auto vmnt = std::dynamic_pointer_cast<mount_point_reedgefs>(mnt);
        if (!vmnt) {
            LOG_ERROR("Non reedgefs mount point");
            return -ENXIO;
        }
        const auto fspath = vmnt->native_path(name);
        const auto fret   = red_unlink(fspath.c_str());
        if (fret != 0) {
            return translate_error(-red_errno);
        }
        return fret;
    }

    auto filesystem_reedgefs::rename(fsmount mnt, std::string_view oldname, std::string_view newname) noexcept -> int
    {
        auto vmnt = std::dynamic_pointer_cast<mount_point_reedgefs>(mnt);
        if (!vmnt) {
            LOG_ERROR("Non reedgefs mount point");
            return -ENXIO;
        }
        const auto fsold = vmnt->native_path(oldname);
        const auto fsnew = vmnt->native_path(newname);
        const auto fret  = red_rename(fsold.c_str(), fsnew.c_str());
        if (fret != 0) {
            return translate_error(-red_errno);
        }
        return fret;
    }

    auto filesystem_reedgefs::mkdir(fsmount mnt, std::string_view path, int mode) noexcept -> int
    {
        auto vmnt = std::dynamic_pointer_cast<mount_point_reedgefs>(mnt);
        if (!vmnt) {
            LOG_ERROR("Non reedgefs mount point");
            return -ENXIO;
        }
        const auto fspath = vmnt->native_path(path);
        const auto fret   = red_mkdir(fspath.c_str());
        if (fret != 0) {
            return translate_error(-red_errno);
        }
        return fret;
    }

    auto filesystem_reedgefs::diropen(fsmount mnt, std::string_view path) noexcept -> fsdir
    {
        auto vmnt = std::dynamic_pointer_cast<mount_point_reedgefs>(mnt);
        if (!vmnt) {
            LOG_ERROR("Non reedgefs mount point");
            return nullptr;
        }
        const auto fspath = vmnt->native_path(path);
        const auto dirp   = std::make_shared<directory_handle_reedgefs>(mnt, 0);
        const auto fret   = red_opendir(fspath.c_str());
        if (fret == nullptr) {
            dirp->error(translate_error(-red_errno));
        }
        else {
            dirp->reedgefs_dirp(fret);
        }

        return dirp;
    }

    auto filesystem_reedgefs::dirreset(fsdir dirstate) noexcept -> int
    {
        auto dirp = std::dynamic_pointer_cast<directory_handle_reedgefs>(dirstate);
        if (!dirp) {
            LOG_ERROR("Non reedgefs directory handle");
            return -ENXIO;
        }
        red_rewinddir(dirp->reedgefs_dirp());
        return 0;
    }

    auto filesystem_reedgefs::dirnext(fsdir dirstate, std::string &filename, struct stat &filestat) -> int
    {
        auto dirp = std::dynamic_pointer_cast<directory_handle_reedgefs>(dirstate);
        if (!dirp) {
            LOG_ERROR("Non reedgefs directory handle");
            return -ENXIO;
        }
        red_errno      = 0;
        auto reddirent = red_readdir(dirp->reedgefs_dirp());
        if (reddirent != nullptr) {
            if (reddirent->d_name[0] == '\0') {
                return -ENODATA;
            }
            else {
                const auto mnt = dirp->mntpoint();
                if (mnt) {
                    auto vmnt = std::dynamic_pointer_cast<mount_point_reedgefs>(mnt);
                    if (!vmnt) {
                        LOG_ERROR("Non reedgefs mount point");
                        return -EIO;
                    }

                    REDSTATFS redstatfs;
                    auto ret = red_statvfs(vmnt->volume_name().c_str(), &redstatfs);
                    if (ret != 0) {
                        return translate_error(-red_errno);
                    }
                    translate_redstat_to_stat(reddirent->d_stat, redstatfs.f_bsize, filestat, vmnt->is_ro());
                    filename = reddirent->d_name;
                    return 0;
                }
                else {
                    return -EIO;
                }
            }
        }
        return red_errno != 0 ? translate_error(-red_errno) : -ENODATA;
    }

    auto filesystem_reedgefs::dirclose(fsdir dirstate) noexcept -> int
    {
        auto dirp = std::dynamic_pointer_cast<directory_handle_reedgefs>(dirstate);
        if (!dirp) {
            LOG_ERROR("Not a reedgefs directory handle");
            return -ENXIO;
        }
        const auto fret = red_closedir(dirp->reedgefs_dirp());
        if (fret != 0) {
            return translate_error(-red_errno);
        }
        return fret;
    }

    auto filesystem_reedgefs::ftruncate(fsfile zfile, off_t len) noexcept -> int
    {
        auto vfile = std::dynamic_pointer_cast<file_handle_reedgefs>(zfile);
        if (!vfile) {
            LOG_ERROR("Non reedgefs filesystem pointer");
            return -EBADF;
        }
        const int fret = red_ftruncate(vfile->fd(), len);
        if (fret != 0) {
            return translate_error(-red_errno);
        }
        return fret;
    }

    auto filesystem_reedgefs::fsync(fsfile zfile) noexcept -> int
    {
        auto vfile = std::dynamic_pointer_cast<file_handle_reedgefs>(zfile);
        if (!vfile) {
            LOG_ERROR("Non reedgefs filesystem pointer");
            return -EBADF;
        }
        const int fret = red_fsync(vfile->fd());
        if (fret != 0) {
            return translate_error(-red_errno);
        }
        return fret;
    }

    auto filesystem_reedgefs::isatty(fsfile zfile) noexcept -> int
    {
        // NOTE: Handle is always not a tty
        return 0;
    }

} // namespace purefs::fs::drivers

A module-vfs/drivers/src/thirdparty/reedgefs/glue.cpp => module-vfs/drivers/src/thirdparty/reedgefs/glue.cpp +204 -0
@@ 0,0 1,204 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
// ffFAT glue layer code

#include <errno.h>
#include <cstring>
#include <string>
#include <unordered_map>
#include "services/osbdev_custom.h"
#include <reedgefs/volume_mapper.hpp>
#include <mutex.hpp>
#include <purefs/blkdev/defs.hpp>
#include <purefs/blkdev/disk_handle.hpp>
#include <purefs/blkdev/disk_manager.hpp>
#include <purefs/fs/handle_mapper.hpp>
#include <redvolume.h>

namespace purefs::fs::drivers::reedgefs::internal
{
    extern "C"
    {
        extern VOLCONF gaRedVolConf[REDCONF_VOLUME_COUNT];
        extern BDEVINFO gaRedBdevInfo[REDCONF_VOLUME_COUNT];
    }
    namespace
    {
        cpp_freertos::MutexRecursive g_lock;
        // Device manager part / LUN map
        std::unordered_map<std::string, int> g_registered_vol;
        fs::internal::handle_mapper<std::shared_ptr<blkdev::internal::disk_handle>> g_disk_handles;
        std::weak_ptr<blkdev::disk_manager> g_disk_mm;
    } // namespace
    namespace
    {
        inline auto disk_handle_from_pdrive(size_t pdrive)
        {
            cpp_freertos::LockGuard _lck(g_lock);
            return (g_disk_handles.exists(pdrive)) ? g_disk_handles[pdrive] : nullptr;
        }
    } // namespace

    std::pair<std::string, int> append_volume(blkdev::disk_fd diskh, const std::string &path)
    {
        cpp_freertos::LockGuard _lck(g_lock);
        const auto vol = g_registered_vol.find(std::string(diskh->name()));
        if (vol != std::end(g_registered_vol)) {
            return {"", vol->second};
        }
        if (g_disk_handles.size() >= REDCONF_VOLUME_COUNT) {
            return {"", -EOVERFLOW};
        }
        auto diskmm = g_disk_mm.lock();
        if (!diskmm) {
            return {"", -EIO};
        }

        if (path.length() > sizeof(VOLCONF::pszPathPrefix)) {
            return {"", -EIO};
        }

        const auto hwnd = g_disk_handles.insert(diskh);
        g_registered_vol.emplace(std::make_pair(diskh->name(), hwnd));
        gaRedVolConf[hwnd].ullSectorOffset = diskmm->get_info(diskh, blkdev::info_type::start_sector);
        return {std::string(gaRedVolConf[hwnd].pszPathPrefix), hwnd};
    }

    int remove_volume(blkdev::disk_fd diskh)
    {
        cpp_freertos::LockGuard _lck(g_lock);
        const auto vol_it = g_registered_vol.find(std::string(diskh->name()));
        if (vol_it == std::end(g_registered_vol)) {
            return -EBADF;
        }
        g_disk_handles.remove(vol_it->second);
        g_registered_vol.erase(vol_it);
        return 0;
    }

    void reset_volumes(std::shared_ptr<blkdev::disk_manager> diskmm)
    {
        cpp_freertos::LockGuard _lck(g_lock);
        g_disk_mm = diskmm;
        g_registered_vol.clear();
        g_disk_handles.clear();
    }

    extern "C"
    {

        REDSTATUS DiskOpen(uint8_t bVolNum, BDEVOPENMODE mode)
        {
            return disk_handle_from_pdrive(bVolNum) ? (0) : (-RED_EIO);
        }

        REDSTATUS DiskClose(uint8_t bVolNum)
        {
            (void)bVolNum;
            return 0;
        }

        REDSTATUS DiskGetGeometry(uint8_t bVolNum, BDEVINFO *pInfo)
        {
            const auto diskh = disk_handle_from_pdrive(bVolNum);
            if (!diskh) {
                return -RED_EIO;
            }
            const auto diskmm = g_disk_mm.lock();
            if (!diskmm) {
                return -RED_EIO;
            }
            auto sectorSize  = diskmm->get_info(diskh, blkdev::info_type::sector_size);
            auto sectorCount = diskmm->get_info(diskh, blkdev::info_type::sector_count);

            if (sectorSize > 0 && sectorCount > 0) {
                pInfo->ulSectorSize   = sectorSize;
                pInfo->ullSectorCount = sectorCount;
                return 0;
            }

            return -RED_EIO;
        }

        REDSTATUS DiskRead(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, void *pBuffer)
        {
            uint32_t ulSectorIdx  = 0U;
            uint32_t ulSectorSize = gaRedBdevInfo[bVolNum].ulSectorSize;
            auto pbBuffer         = static_cast<uint8_t *>(pBuffer);
            const auto diskh      = disk_handle_from_pdrive(bVolNum);
            if (!diskh) {
                return -RED_EIO;
            }
            const auto diskmm = g_disk_mm.lock();
            if (!diskmm) {
                return -RED_EIO;
            }

            while (ulSectorIdx < ulSectorCount) {
                uint32_t ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER);

                auto result = diskmm->read(
                    diskh, &pbBuffer[ulSectorIdx * ulSectorSize], (ullSectorStart + ulSectorIdx), ulTransfer);
                if (result < 0) {
                    LOG_ERROR("write error %i", result);
                    return (result == -ERANGE) ? (-RED_ERANGE) : (-RED_EIO);
                }

                ulSectorIdx += ulTransfer;
            }

            return 0;
        }

        REDSTATUS DiskWrite(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, const void *pBuffer)
        {
            uint32_t ulSectorIdx  = 0U;
            uint32_t ulSectorSize = gaRedBdevInfo[bVolNum].ulSectorSize;
            auto pbBuffer         = static_cast<const uint8_t *>(pBuffer);
            const auto diskh      = disk_handle_from_pdrive(bVolNum);
            if (!diskh) {
                return -RED_EIO;
            }
            const auto diskmm = g_disk_mm.lock();
            if (!diskmm) {
                return -RED_EIO;
            }

            while (ulSectorIdx < ulSectorCount) {
                uint32_t ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER);

                auto result = diskmm->write(
                    diskh, &pbBuffer[ulSectorIdx * ulSectorSize], (ullSectorStart + ulSectorIdx), ulTransfer);
                if (result < 0) {
                    LOG_ERROR("write error %i", result);
                    return (result == -ERANGE) ? (-RED_ERANGE) : (-RED_EIO);
                }

                ulSectorIdx += ulTransfer;
            }

            return 0;
        }

        REDSTATUS DiskFlush(uint8_t bVolNum)
        {
            const auto diskh = disk_handle_from_pdrive(bVolNum);
            if (!diskh) {
                return -RED_EIO;
            }
            const auto diskmm = g_disk_mm.lock();
            if (!diskmm) {
                return -RED_EIO;
            }

            auto result = diskmm->sync(diskh);
            if (result < 0) {
                LOG_ERROR("sync error %i", result);
                return -RED_EIO;
            }

            return 0;
        }
    }

} // namespace purefs::fs::drivers::reedgefs::internal

A module-vfs/drivers/src/thirdparty/reedgefs/include/redostypes.h => module-vfs/drivers/src/thirdparty/reedgefs/include/redostypes.h +42 -0
@@ 0,0 1,42 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                  Copyright (c) 2014-2021 Tuxera US Inc.
                      All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license must obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
    @brief Defines OS-specific types for use in common code.
*/
#ifndef REDOSTYPES_H
#define REDOSTYPES_H

/** @brief Implementation-defined timestamp type.

    This can be an integer, a structure, or a pointer: anything that is
    convenient for the implementation.  Since the underlying type is not fixed,
    common code should treat this as an opaque type.
*/
typedef uint32_t REDTIMESTAMP;

#endif

A module-vfs/drivers/src/thirdparty/reedgefs/redconf.c => module-vfs/drivers/src/thirdparty/reedgefs/redconf.c +19 -0
@@ 0,0 1,19 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*  THIS FILE WAS GENERATED BY THE TUXERA RELIANCE EDGE CONFIGURATION UTILITY.
    DO NOT MODIFY.

    Generated by configuration utility version 2.5
*/
/** @file
 */
#include <redconf.h>
#include <redtypes.h>
#include <redmacs.h>
#include <redvolume.h>

VOLCONF gaRedVolConf[REDCONF_VOLUME_COUNT] = {{SECTOR_SIZE_AUTO, SECTOR_COUNT_AUTO, 0U, false, 10000U, 2U, "VOL0:"},
                                              {SECTOR_SIZE_AUTO, SECTOR_COUNT_AUTO, 0U, false, 10000U, 2U, "VOL1:"},
                                              {SECTOR_SIZE_AUTO, SECTOR_COUNT_AUTO, 0U, false, 10000U, 2U, "VOL2:"},
                                              {SECTOR_SIZE_AUTO, SECTOR_COUNT_AUTO, 0U, false, 10000U, 2U, "VOL3:"}};

A module-vfs/drivers/src/thirdparty/reedgefs/services/osassert.c => module-vfs/drivers/src/thirdparty/reedgefs/services/osassert.c +59 -0
@@ 0,0 1,59 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                  Copyright (c) 2014-2021 Tuxera US Inc.
                      All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license must obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
    @brief Implements assertion handling.
*/
#include <redfs.h>
#include <log/log.hpp>

#include <stdlib.h>

#if REDCONF_ASSERTS == 1

#if REDCONF_OUTPUT == 1
#include <stdio.h>
#endif

/** @brief Invoke the native assertion handler.

    @param pszFileName  Null-terminated string containing the name of the file
                        where the assertion fired.
    @param ulLineNum    Line number in @p pszFileName where the assertion
                        fired.
*/
void RedOsAssertFail(const char *pszFileName, uint32_t ulLineNum)
{
#if REDCONF_OUTPUT == 1
    LOG_FATAL(
        "Assertion failed in \"%s\" at line %u\n\r", (pszFileName == NULL) ? "" : pszFileName, (unsigned)ulLineNum);
#endif

    abort();
}

#endif

A module-vfs/drivers/src/thirdparty/reedgefs/services/osbdev.c => module-vfs/drivers/src/thirdparty/reedgefs/services/osbdev.c +353 -0
@@ 0,0 1,353 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                  Copyright (c) 2014-2021 Tuxera US Inc.
                      All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license must obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
    @brief Implements block device I/O.
*/
#include <FreeRTOS.h>

#include <redfs.h>
#include <redvolume.h>
#include <redbdev.h>

/*------------------------------------------------------------------------------
    Porting Note:

    Several example implementations of this module for FreeRTOS are available.
    If you are lucky, you can use one of these implementations; otherwise, these
    can serve as examples of how to implement this service.
------------------------------------------------------------------------------*/

/** @brief A custom implementation, initially stubbed out.

    This the default setting: it is initially stubbed out (does nothing) and
    produces an error when compiled so that it is obvious that this file needs
    to be modified.  You can edit this code to provide your own implementation
    of the block device.  Alternatively, you can delete all of the Disk*()
    functions and put the custom code directly into the RedOsBDev*() functions.
*/
#define BDEV_CUSTOM (0U)

/** @brief Tuxera FlashFX Tera driver implementation.

    This implementation uses Tuxera's FlashFX Tera driver to use raw flash
    storage with Reliance Edge.

    This option is only available in commercial releases of Reliance Edge.
*/
#define BDEV_FLASHFX (1U)

/** @brief The FatFs example implementation.

    This implementation is designed to reuse an existing block device driver
    that was written for FatFs.  If you have such a driver, it can be linked
    in and used immediately.  The FatFs `diskio.h` header must be in the include
    directory path.
*/
#define BDEV_FATFS (3U)

/** @brief The Atmel Studio Framework SD/MMC driver example implementation.

    This implementation uses a modified version of the open source SD/MMC driver
    included in the Atmel Studio Framework (ASF) and will work as-is for many
    varieties of Atmel hardware.  This example assumes relatively minor
    modifications to the ASF SD/MMC driver to make it support multi-sector read
    and write requests, which greatly improves performance.  The modified driver
    is distributed with the Reliance Edge commercial kit and is included in
    FreeRTOS Atmel projects that come with the commercial kit (such as in
    projects/freertos/atmel/sam4e-ek/src/ASF).

    This example can easily be modified to work with an unmodified version of
    the ASF SD/MMC driver.  Simply replace sd_mmc_mem_2_ram_multi() and
    sd_mmc_ram_2_mem_multi() with sd_mmc_mem_2_ram() and sd_mmc_ram_2_mem()
    respectively, and add a for loop to loop over each sector in the request.
    However, as described in the manual, there are considerable performance
    advantages to issuing real multi-sector requests, so using the modified
    driver is recommended.
*/
#define BDEV_ATMEL_SDMMC (4U)

/** @brief The ST Microelectronics STM32 SDIO driver example implementation.

    This implementation accesses the microSD card through the BSP utilities
    provided as part of the STM32Cube package, used with the STM32 HAL drivers.
    The STM3240G-EVAL and STM32F746NG-Discovery boards are currently supported.
*/
#define BDEV_STM32_SDIO (5U)

/** @brief The RAM disk example implementation.

    This implementation uses a RAM disk.  It will allow you to compile and test
    Reliance Edge even if your storage driver is not yet ready.  On typical
    target hardware, the amount of spare RAM will be limited so generally only
    very small disks will be available.
*/
#define BDEV_RAM_DISK (6U)

/** @brief Pick which example implementation is compiled.

    Must be one of:
    - #BDEV_CUSTOM
    - #BDEV_FLASHFX
    - #BDEV_FATFS
    - #BDEV_ATMEL_SDMMC
    - #BDEV_STM32_SDIO
    - #BDEV_RAM_DISK
*/
#ifndef BDEV_EXAMPLE_IMPLEMENTATION
#define BDEV_EXAMPLE_IMPLEMENTATION BDEV_CUSTOM
#endif

/*  The DiskOpen(), DiskClose(), DiskRead(), DiskWrite(), DiskFlush(), and
    DiskDiscard() functions used below are defined in these header files:
*/
#if BDEV_EXAMPLE_IMPLEMENTATION == BDEV_CUSTOM
#include "osbdev_custom.h"
#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_FLASHFX
#if RED_KIT == RED_KIT_GPL
#error "FlashFX block device only supported in commercial versions of Reliance Edge."
#endif
#include "osbdev_flashfx.h"
#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_FATFS
#include "osbdev_fatfs.h"
#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_ATMEL_SDMMC
#include "osbdev_asfsdmmc.h"
#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_STM32_SDIO
#include "osbdev_stm32sdio.h"
#elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_RAM_DISK
#include "osbdev_ramdisk.h"
#else
#error "Invalid BDEV_EXAMPLE_IMPLEMENTATION value"
#endif /* BDEV_EXAMPLE_IMPLEMENTATION == ... */

/** @brief Initialize a block device.

    This function is called when the file system needs access to a block
    device.

    Upon successful return, the block device should be fully initialized and
    ready to service read/write/flush/close requests.

    The behavior of calling this function on a block device which is already
    open is undefined.

    @param bVolNum  The volume number of the volume whose block device is being
                    initialized.
    @param mode     The open mode, indicating the type of access required.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EINVAL @p bVolNum is an invalid volume number.
    @retval -RED_EIO    A disk I/O error occurred.
*/
REDSTATUS RedOsBDevOpen(uint8_t bVolNum, BDEVOPENMODE mode)
{
    REDSTATUS ret;

    if (bVolNum >= REDCONF_VOLUME_COUNT) {
        ret = -RED_EINVAL;
    }
    else {
        ret = DiskOpen(bVolNum, mode);
    }

    return ret;
}

/** @brief Uninitialize a block device.

    This function is called when the file system no longer needs access to a
    block device.  If any resource were allocated by RedOsBDevOpen() to service
    block device requests, they should be freed at this time.

    Upon successful return, the block device must be in such a state that it
    can be opened again.

    The behavior of calling this function on a block device which is already
    closed is undefined.

    @param bVolNum  The volume number of the volume whose block device is being
                    uninitialized.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EINVAL @p bVolNum is an invalid volume number.
*/
REDSTATUS RedOsBDevClose(uint8_t bVolNum)
{
    REDSTATUS ret;

    if (bVolNum >= REDCONF_VOLUME_COUNT) {
        ret = -RED_EINVAL;
    }
    else {
        ret = DiskClose(bVolNum);
    }

    return ret;
}

/** @brief Return the block device geometry.

    The behavior of calling this function is undefined if the block device is
    closed.

    @param bVolNum  The volume number of the volume whose block device geometry
                    is being queried.
    @param pInfo    On successful return, populated with the geometry of the
                    block device.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0               Operation was successful.
    @retval -RED_EINVAL     @p bVolNum is an invalid volume number, or @p pInfo
                            is `NULL`.
    @retval -RED_EIO        A disk I/O error occurred.
    @retval -RED_ENOTSUPP   The geometry cannot be queried on this block device.
*/
REDSTATUS RedOsBDevGetGeometry(uint8_t bVolNum, BDEVINFO *pInfo)
{
    REDSTATUS ret;

    if ((bVolNum >= REDCONF_VOLUME_COUNT) || (pInfo == NULL)) {
        ret = -RED_EINVAL;
    }
    else {
        ret = DiskGetGeometry(bVolNum, pInfo);
    }

    return ret;
}

/** @brief Read sectors from a physical block device.

    The behavior of calling this function is undefined if the block device is
    closed or if it was opened with ::BDEV_O_WRONLY.

    @param bVolNum          The volume number of the volume whose block device
                            is being read from.
    @param ullSectorStart   The starting sector number.
    @param ulSectorCount    The number of sectors to read.
    @param pBuffer          The buffer into which to read the sector data.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EINVAL @p bVolNum is an invalid volume number, @p pBuffer is
                        `NULL`, or @p ullStartSector and/or @p ulSectorCount
                        refer to an invalid range of sectors.
    @retval -RED_EIO    A disk I/O error occurred.
*/
REDSTATUS RedOsBDevRead(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, void *pBuffer)
{
    REDSTATUS ret;

    if ((bVolNum >= REDCONF_VOLUME_COUNT) || !VOLUME_SECTOR_RANGE_IS_VALID(bVolNum, ullSectorStart, ulSectorCount) ||
        (pBuffer == NULL)) {
        ret = -RED_EINVAL;
    }
    else {
        ret = DiskRead(bVolNum, ullSectorStart, ulSectorCount, pBuffer);
    }

    return ret;
}

#if REDCONF_READ_ONLY == 0

/** @brief Write sectors to a physical block device.

    The behavior of calling this function is undefined if the block device is
    closed or if it was opened with ::BDEV_O_RDONLY.

    @param bVolNum          The volume number of the volume whose block device
                            is being written to.
    @param ullSectorStart   The starting sector number.
    @param ulSectorCount    The number of sectors to write.
    @param pBuffer          The buffer from which to write the sector data.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EINVAL @p bVolNum is an invalid volume number, @p pBuffer is
                        `NULL`, or @p ullStartSector and/or @p ulSectorCount
                        refer to an invalid range of sectors.
    @retval -RED_EIO    A disk I/O error occurred.
*/
REDSTATUS RedOsBDevWrite(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, const void *pBuffer)
{
    REDSTATUS ret;

    if ((bVolNum >= REDCONF_VOLUME_COUNT) || !VOLUME_SECTOR_RANGE_IS_VALID(bVolNum, ullSectorStart, ulSectorCount) ||
        (pBuffer == NULL)) {
        ret = -RED_EINVAL;
    }
    else {
        ret = DiskWrite(bVolNum, ullSectorStart, ulSectorCount, pBuffer);
    }

    return ret;
}

/** @brief Flush any caches beneath the file system.

    This function must synchronously flush all software and hardware caches
    beneath the file system, ensuring that all sectors written previously are
    committed to permanent storage.

    If the environment has no caching beneath the file system, the
    implementation of this function can do nothing and return success.

    The behavior of calling this function is undefined if the block device is
    closed or if it was opened with ::BDEV_O_RDONLY.

    @param bVolNum  The volume number of the volume whose block device is being
                    flushed.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EINVAL @p bVolNum is an invalid volume number.
    @retval -RED_EIO    A disk I/O error occurred.
*/
REDSTATUS RedOsBDevFlush(uint8_t bVolNum)
{
    REDSTATUS ret;

    if (bVolNum >= REDCONF_VOLUME_COUNT) {
        ret = -RED_EINVAL;
    }
    else {
        ret = DiskFlush(bVolNum);
    }

    return ret;
}

#endif /* REDCONF_READ_ONLY == 0 */

A module-vfs/drivers/src/thirdparty/reedgefs/services/osbdev_custom.h => module-vfs/drivers/src/thirdparty/reedgefs/services/osbdev_custom.h +129 -0
@@ 0,0 1,129 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                  Copyright (c) 2014-2021 Tuxera US Inc.
                      All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license must obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @brief
    @file FreeRTOS block device implementation; see osbdev.c for details.
*/
#ifndef OSBDEV_CUSTOM_H
#define OSBDEV_CUSTOM_H

#include <redfs.h>

#define MAX_SECTOR_TRANSFER UINT8_MAX

/*  If you need to include headers to access your block device, do so here.  For
    example, if you are using an SD card driver whose interfaces are defined in
    sd.h, that would be included here.
#include <foobar.h>
*/

/** @brief Initialize a disk.

    @param bVolNum  The volume number of the volume whose block device is being
                    initialized.
    @param mode     The open mode, indicating the type of access required.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EIO    A disk I/O error occurred.
*/
REDSTATUS DiskOpen(uint8_t bVolNum, BDEVOPENMODE mode);

/** @brief Uninitialize a disk.

    @param bVolNum  The volume number of the volume whose block device is being
                    uninitialized.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0   Operation was successful.
*/
REDSTATUS DiskClose(uint8_t bVolNum);

/** @brief Return the disk geometry.

    @param bVolNum  The volume number of the volume whose block device geometry
                    is being queried.
    @param pInfo    On successful return, populated with the geometry of the
                    block device.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0               Operation was successful.
    @retval -RED_EIO        A disk I/O or driver error occurred.
    @retval -RED_ENOTSUPP   The geometry cannot be queried on this block device.
*/
REDSTATUS DiskGetGeometry(uint8_t bVolNum, BDEVINFO *pInfo);

/** @brief Read sectors from a disk.

    @param bVolNum          The volume number of the volume whose block device
                            is being read from.
    @param ullSectorStart   The starting sector number.
    @param ulSectorCount    The number of sectors to read.
    @param pBuffer          The buffer into which to read the sector data.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EIO    A disk I/O error occurred.
*/
REDSTATUS DiskRead(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, void *pBuffer);

#if REDCONF_READ_ONLY == 0

/** @brief Write sectors to a disk.

    @param bVolNum          The volume number of the volume whose block device
                            is being written to.
    @param ullSectorStart   The starting sector number.
    @param ulSectorCount    The number of sectors to write.
    @param pBuffer          The buffer from which to write the sector data.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EIO    A disk I/O error occurred.
*/
REDSTATUS DiskWrite(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, const void *pBuffer);

/** @brief Flush any caches beneath the file system.

    @param bVolNum  The volume number of the volume whose block device is being
                    flushed.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EIO    A disk I/O error occurred.
*/
REDSTATUS DiskFlush(uint8_t bVolNum);

#endif /* REDCONF_READ_ONLY == 0 */

#endif /* OSBDEV_CUSTOM_H */

A module-vfs/drivers/src/thirdparty/reedgefs/services/osclock.c => module-vfs/drivers/src/thirdparty/reedgefs/services/osclock.c +78 -0
@@ 0,0 1,78 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                  Copyright (c) 2014-2021 Tuxera US Inc.
                      All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license must obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
    @brief Implements real-time clock functions.
*/
#include <redfs.h>

/** @brief Initialize the real time clock.

    The behavior of calling this function when the RTC is already initialized
    is undefined.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0   Operation was successful.
*/
REDSTATUS RedOsClockInit(void)
{
    return 0;
}

/** @brief Uninitialize the real time clock.

    The behavior of calling this function when the RTC is not initialized is
    undefined.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0   Operation was successful.
*/
REDSTATUS RedOsClockUninit(void)
{
    return 0;
}

/** @brief Get the date/time.

    The behavior of calling this function when the RTC is not initialized is
    undefined.

    @return The number of seconds since January 1, 1970 excluding leap seconds
            (in other words, standard Unix time).  If the resolution or epoch
            of the RTC is different than this, the implementation must convert
            it to the expected representation.
*/
uint32_t RedOsClockGetTime(void)
{
    /*  FreeRTOS does not provide an RTC abstraction since most of the systems
        it targets have no RTC hardware.  If your hardware includes an RTC that
        you would like to use, this function must be customized.
    */
    return 0;
}

A module-vfs/drivers/src/thirdparty/reedgefs/services/osmutex.c => module-vfs/drivers/src/thirdparty/reedgefs/services/osmutex.c +126 -0
@@ 0,0 1,126 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                  Copyright (c) 2014-2021 Tuxera US Inc.
                      All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license must obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
    @brief Implements a synchronization object to provide mutual exclusion.
*/
#include <FreeRTOS.h>
#include <semphr.h>

#include <redfs.h>

#if REDCONF_TASK_COUNT > 1U

static SemaphoreHandle_t xMutex;
#if defined(configSUPPORT_STATIC_ALLOCATION) && (configSUPPORT_STATIC_ALLOCATION == 1)
static StaticSemaphore_t xMutexBuffer;
#endif

/** @brief Initialize the mutex.

    After initialization, the mutex is in the released state.

    The behavior of calling this function when the mutex is still initialized
    is undefined.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0   Operation was successful.
*/
REDSTATUS RedOsMutexInit(void)
{
    REDSTATUS ret = 0;

#if defined(configSUPPORT_STATIC_ALLOCATION) && (configSUPPORT_STATIC_ALLOCATION == 1)
    xMutex = xSemaphoreCreateMutexStatic(&xMutexBuffer);

    if (xMutex == NULL) {
        /*  The only error case for xSemaphoreCreateMutexStatic is that the mutex
            buffer parameter is NULL, which is not the case.
        */
        REDERROR();
        ret = -RED_EINVAL;
    }

#else
    xMutex = xSemaphoreCreateMutex();
    if (xMutex == NULL) {
        ret = -RED_ENOMEM;
    }
#endif

    return ret;
}

/** @brief Uninitialize the mutex.

    The behavior of calling this function when the mutex is not initialized is
    undefined; likewise, the behavior of uninitializing the mutex when it is
    in the acquired state is undefined.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0   Operation was successful.
*/
REDSTATUS RedOsMutexUninit(void)
{
    vSemaphoreDelete(xMutex);
    xMutex = NULL;

    return 0;
}

/** @brief Acquire the mutex.

    The behavior of calling this function when the mutex is not initialized is
    undefined; likewise, the behavior of recursively acquiring the mutex is
    undefined.
*/
void RedOsMutexAcquire(void)
{
    while (xSemaphoreTake(xMutex, portMAX_DELAY) != pdTRUE) {}
}

/** @brief Release the mutex.

    The behavior is undefined in the following cases:

    - Releasing the mutex when the mutex is not initialized.
    - Releasing the mutex when it is not in the acquired state.
    - Releasing the mutex from a task or thread other than the one which
      acquired the mutex.
*/
void RedOsMutexRelease(void)
{
    BaseType_t xSuccess;

    xSuccess = xSemaphoreGive(xMutex);
    REDASSERT(xSuccess == pdTRUE);
    (void)xSuccess;
}

#endif

A module-vfs/drivers/src/thirdparty/reedgefs/services/osoutput.c => module-vfs/drivers/src/thirdparty/reedgefs/services/osoutput.c +55 -0
@@ 0,0 1,55 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                  Copyright (c) 2014-2021 Tuxera US Inc.
                      All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license must obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
    @brief Implements outputting a character string.
*/
#include <redfs.h>
#include <log/log.hpp>

#if REDCONF_OUTPUT == 1

#include <stdio.h>

/** @brief Write a string to a user-visible output location.

    Write a null-terminated string to the serial port, console, terminal, or
    other display device, such that the text is visible to the user.

    @param pszString    A null-terminated string.
*/
void RedOsOutputString(const char *pszString)
{
    if (pszString == NULL) {
        REDERROR();
    }
    else {
        LOG_INFO("%s", pszString);
    }
}

#endif

A module-vfs/drivers/src/thirdparty/reedgefs/services/ostask.c => module-vfs/drivers/src/thirdparty/reedgefs/services/ostask.c +67 -0
@@ 0,0 1,67 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                  Copyright (c) 2014-2021 Tuxera US Inc.
                      All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license must obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
    @brief Implements task functions.
*/
#include <FreeRTOS.h>
#include <task.h>

#include <redfs.h>

#if (REDCONF_TASK_COUNT > 1U) && (REDCONF_API_POSIX == 1)

#if INCLUDE_xTaskGetCurrentTaskHandle != 1
#error "INCLUDE_xTaskGetCurrentTaskHandle must be 1 when REDCONF_TASK_COUNT > 1 and REDCONF_API_POSIX == 1"
#endif

/** @brief Get the current task ID.

    This task ID must be unique for all tasks using the file system.

    @return The task ID.  Must not be 0.
*/
uint32_t RedOsTaskId(void)
{
    /*  Simply casting the xTaskGetCurrentTaskHandle() return value results in
        warnings from some compilers, so use variables.
    */
    TaskHandle_t xTask = xTaskGetCurrentTaskHandle();
    uintptr_t taskptr  = (uintptr_t)xTask;
    uint32_t ulTaskPtr = (uint32_t)taskptr;

    /*  Assert no information was lost casting from uintptr_t to uint32_t.
     */
    REDASSERT(ulTaskPtr == taskptr);

    /*  NULL is a valid task handle in FreeRTOS, so add one to all task IDs.
     */
    REDASSERT((ulTaskPtr + 1U) != 0U);
    return ulTaskPtr + 1U;
}

#endif

A module-vfs/drivers/src/thirdparty/reedgefs/services/ostimestamp.c => module-vfs/drivers/src/thirdparty/reedgefs/services/ostimestamp.c +105 -0
@@ 0,0 1,105 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                  Copyright (c) 2014-2021 Tuxera US Inc.
                      All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license must obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
    @brief Implements timestamp functions.

    The functionality implemented herein is not needed for the file system
    driver, only to provide accurate results with performance tests.
*/
#include <FreeRTOS.h>
#include <task.h>

#include <redfs.h>

/*  configTICK_RATE_HZ is almost always 100, 250, 500, or 1000.  If
    1000000U % configTICK_RATE_HZ != 0, then RedOsTimePassed() will be a
    little inaccurate.
*/
#define MICROSECS_PER_TICK (1000000U / configTICK_RATE_HZ)

/** @brief Initialize the timestamp service.

    The behavior of invoking this function when timestamps are already
    initialized is undefined.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_ENOSYS The timestamp service has not been implemented.
*/
REDSTATUS RedOsTimestampInit(void)
{
    return 0;
}

/** @brief Uninitialize the timestamp service.

    The behavior of invoking this function when timestamps are not initialized
    is undefined.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0   Operation was successful.
*/
REDSTATUS RedOsTimestampUninit(void)
{
    return 0;
}

/** @brief Retrieve a timestamp.

    The behavior of invoking this function when timestamps are not initialized
    is undefined

    @return A timestamp which can later be passed to RedOsTimePassed() to
            determine the amount of time which passed between the two calls.
*/
REDTIMESTAMP RedOsTimestamp(void)
{
    return xTaskGetTickCount();
}

/** @brief Determine how much time has passed since a timestamp was retrieved.

    The behavior of invoking this function when timestamps are not initialized
    is undefined.

    @param tsSince  A timestamp acquired earlier via RedOsTimestamp().

    @return The number of microseconds which have passed since @p tsSince.
*/
uint64_t RedOsTimePassed(REDTIMESTAMP tsSince)
{
    /*  This works even if the tick count has wrapped around, provided it has
        only wrapped around once.
    */
    uint32_t ulTicksPassed = (uint32_t)xTaskGetTickCount() - tsSince;
    uint64_t ullMicrosecs  = (uint64_t)ulTicksPassed * MICROSECS_PER_TICK;

    return ullMicrosecs;
}

M module-vfs/include/user/purefs/blkdev/defs.hpp => module-vfs/include/user/purefs/blkdev/defs.hpp +2 -1
@@ 36,7 36,8 @@ namespace purefs::blkdev
    {
        sector_count, //! Number of sectors on disk or part
        sector_size,  //! Single sector size
        erase_block   //! Number of sectors in erase block
        erase_block,  //! Number of sectors in erase block
        start_sector  //! First sector
    };

    //! Power control states

M module-vfs/include/user/purefs/fs/mount_point.hpp => module-vfs/include/user/purefs/fs/mount_point.hpp +1 -1
@@ 71,7 71,7 @@ namespace purefs::fs::internal
        }

      private:
        virtual auto native_root() const noexcept -> std::string_view = 0;
        virtual auto native_root() const noexcept -> std::string = 0;

      private:
        const std::weak_ptr<blkdev::internal::disk_handle> m_diskh;

M module-vfs/src/purefs/blkdev/disk_manager.cpp => module-vfs/src/purefs/blkdev/disk_manager.cpp +9 -3
@@ 327,14 327,20 @@ namespace purefs::blkdev
            LOG_ERROR("Disk doesn't exists");
            return {};
        }
        //! When it is partition as for partition sectors count
        if (what == info_type::sector_count && dfd->is_user_partition()) {
        //! When it is partition ask for partition info
        if ((what == info_type::sector_count || what == info_type::start_sector) && dfd->is_user_partition()) {
            if (unsigned(dfd->partition()) >= disk->partitions().size()) {
                LOG_ERROR("Partition number out of range");
                return -ERANGE;
            }
            const auto part = disk->partitions()[dfd->partition()];
            return part.num_sectors;
            if (what == info_type::sector_count) {
                return part.num_sectors;
            }
            if (what == info_type::start_sector) {
                return part.start_sector;
            }
            return {};
        }
        else {
            return disk->get_info(what, dfd->system_partition());

M third-party/CMakeLists.txt => third-party/CMakeLists.txt +1 -0
@@ 3,6 3,7 @@

add_subdirectory(base64)
add_subdirectory(date)
add_subdirectory(reedgefs)
add_subdirectory(fatfs)
add_subdirectory(gsl)
add_subdirectory(hash-library)

A third-party/reedgefs/CMakeLists.txt => third-party/reedgefs/CMakeLists.txt +61 -0
@@ 0,0 1,61 @@

add_library(reedgefs INTERFACE)
add_library(reliance-edge::fs ALIAS reedgefs)

target_include_directories(reedgefs
    INTERFACE
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/include>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/core/include>
)

target_sources( reedgefs
    INTERFACE
    src/bdev/bdev.c
    src/core/driver/blockio.c
    src/core/driver/buffer.c
    src/core/driver/buffercmn.c
    src/core/driver/core.c
    src/core/driver/dir.c
    src/core/driver/format.c
    src/core/driver/imap.c
    src/core/driver/imapextern.c
    src/core/driver/imapinline.c
    src/core/driver/inode.c
    src/core/driver/inodedata.c
    src/core/driver/volume.c
    src/fse/fse.c
    src/posix/path.c
    src/posix/posix.c
    src/util/bitmap.c
    src/util/crc.c
    src/util/endian.c
    src/util/memory.c
    src/util/namelen.c
    src/util/sign.c
    src/util/string.c

    src/include/redapimacs.h
    src/include/redbdev.h
    src/include/redconfigchk.h
    src/include/redcoreapi.h
    src/include/rederrno.h
    src/include/redexclude.h
    src/include/redformat.h
    src/include/redfs.h
    src/include/redfse.h
    src/include/redgetopt.h
    src/include/redmacs.h
    src/include/redmditer.h
    src/include/redmisc.h
    src/include/redosserv.h
    src/include/redpath.h
    src/include/redposix.h
    src/include/redstat.h
    src/include/redtests.h
    src/include/redtestutils.h
    src/include/redtoolcmn.h
    src/include/redtools.h
    src/include/redutils.h
    src/include/redver.h
    src/include/redvolume.h
)

A third-party/reedgefs/src => third-party/reedgefs/src +1 -0
@@ 0,0 1,1 @@
Subproject commit 2b939184534184dafb28575026ca76c92b37f185