~aleteoryx/muditaos

9cbebacd78cb0cf5674e5b5ff4969e81e45ef6b8 — Lucjan Bryndza 5 years ago dc676ef
[EGD-4262] Disk manager (#1025)

* [EGD-4262] Disk manager API draft

* [EGD-4262] New filesystem master user class

* [EGD-4262] Directory fops

* [EGD-4262] Initial version of disk manager API

* [EGD-4261] Registering and finding block devices.

* [EGD-4262] Disk manager without partition parser

* [EGD-4262] Partition parser intial ver.

* [EGD-4262] Disk read driver for Linux

* [EGD-4262] Disk manager unittests.

This unittests should be improved. Currrently it checks
only minimum functionality. Boundary tests should be added
in the next stage.

Co-authored-by: Lucjan Bryndza <lucjan.bryndza@mudita.com>
M module-vfs/CMakeLists.txt => module-vfs/CMakeLists.txt +4 -0
@@ 45,6 45,10 @@ set(SOURCES ""
        ${FREERTOS_FAT_SOURCES}
        ${FREERTOS_FAT_EXTSOURCES}
        src/purefs/filesystem_paths.cpp
        src/purefs/blkdev/disk_manager.cpp
        src/purefs/blkdev/disk.cpp
        src/purefs/blkdev/partition_parser.cpp
        src/purefs/blkdev/disk_handle.cpp
        src/deprecated/vfs-utils.cpp
        src/deprecated/vfs.cpp
        src/deprecated/vfsNotifier.cpp

A module-vfs/board/linux/purefs/include/purefs/blkdev/disk_image.hpp => module-vfs/board/linux/purefs/include/purefs/blkdev/disk_image.hpp +33 -0
@@ 0,0 1,33 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <purefs/blkdev/disk.hpp>

namespace purefs::blkdev
{
    class disk_image final : public disk
    {
        static constexpr auto sector_size = 512UL;

      public:
        disk_image(std::string_view image_filename);
        virtual ~disk_image()
        {}

      private:
        auto probe(unsigned flags) -> int override;
        auto cleanup() -> int override;
        auto write(const void *buf, sector_t lba, std::size_t count) -> int override;
        auto read(void *buf, sector_t lba, std::size_t count) -> int override;
        auto sync() -> int override;
        auto status() const -> media_status override;
        auto get_info(info_type what) const -> scount_t override;

      private:
        int m_filedes{-1};
        unsigned long m_sectors{0};
        std::string m_image_name;
    };
} // namespace purefs::blkdev
\ No newline at end of file

A module-vfs/board/linux/purefs/src/blkdev/disk_image.cpp => module-vfs/board/linux/purefs/src/blkdev/disk_image.cpp +107 -0
@@ 0,0 1,107 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <purefs/blkdev/disk_image.hpp>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>

namespace purefs::blkdev
{

    disk_image::disk_image(std::string_view image_filename) : m_image_name(image_filename)
    {}

    auto disk_image::probe(unsigned int flags) -> int
    {
        m_filedes = ::open(m_image_name.c_str(), O_RDWR, O_SYNC);
        if (!m_filedes)
            return m_filedes;
        struct stat fst;
        auto ret = ::fstat(m_filedes, &fst);
        if (ret < 0) {
            return -errno;
        }
        m_sectors = fst.st_size / sector_size;
        return 0;
    }
    auto disk_image::cleanup() -> int
    {
        int ret{-EBADFD};
        if (m_filedes) {
            if (m_filedes) {
                ret = ::close(m_filedes);
                if (ret < 0)
                    ret = -errno;
            }
        }
        return ret;
    }
    auto disk_image::write(const void *buf, sector_t lba, std::size_t count) -> int
    {
        int ret = ::lseek(m_filedes, lba * sector_size, SEEK_SET);
        if (ret < 0) {
            return ret;
        }
        auto to_write = count * sector_size;
        auto buf_b    = reinterpret_cast<const uint8_t *>(buf);
        do {
            ret = ::write(m_filedes, buf_b, to_write);
            if (ret < 0) {
                return -errno;
            }
            to_write -= ret;
            buf_b += ret;
        } while (to_write > 0);
        return 0;
    }
    auto disk_image::read(void *buf, sector_t lba, std::size_t count) -> int
    {
        int ret = ::lseek(m_filedes, lba * sector_size, SEEK_SET);
        if (ret < 0) {
            return ret;
        }
        auto to_read = count * sector_size;
        auto buf_b   = reinterpret_cast<uint8_t *>(buf);
        do {
            ret = ::read(m_filedes, buf_b, to_read);
            if (ret < 0) {
                return -errno;
            }
            to_read -= ret;
            buf_b += ret;
        } while (to_read > 0);
        return 0;
    }
    auto disk_image::sync() -> int
    {
        int ret{-EBADFD};
        if (m_filedes) {
            ret = fsync(m_filedes);
            if (ret < 0)
                ret = -errno;
        }
        return ret;
    }
    auto disk_image::status() const -> media_status
    {
        struct stat st;
        auto ret = ::stat(m_image_name.c_str(), &st);
        if (!ret)
            return media_status::nomedia;
        else
            return media_status::healthly;
    }
    auto disk_image::get_info(info_type what) const -> scount_t
    {
        switch (what) {
        case info_type::sector_size:
            return sector_size;
        case info_type::sector_count:
            return m_sectors;
        case info_type::erase_block:
            return 0;
        }
        return -1;
    }
} // namespace purefs::blkdev

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

#pragma once

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

namespace purefs::blkdev
{
    class disk;
}

namespace purefs::blkdev::internal
{

    class disk_handle
    {
      public:
        static constexpr auto no_parition = -1;
        explicit disk_handle(std::weak_ptr<blkdev::disk> disk, std::string_view name, short partition = no_parition)
            : m_disk(disk), m_partition(partition), m_name(name)
        {}
        auto disk() const noexcept
        {
            return m_disk;
        }
        auto partition() const noexcept
        {
            return m_partition;
        }
        auto has_partition() const noexcept
        {
            return m_partition != no_parition;
        }
        auto sectors() const noexcept -> sector_t;
        auto name() const noexcept
        {
            return m_name;
        }

      private:
        const std::weak_ptr<blkdev::disk> m_disk;
        const short m_partition{-1};
        mutable sector_t m_sectors{0};
        const std::string_view m_name;
    };
} // namespace purefs::blkdev::internal

A module-vfs/include/internal/purefs/blkdev/partition_parser.hpp => module-vfs/include/internal/purefs/blkdev/partition_parser.hpp +34 -0
@@ 0,0 1,34 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <vector>
#include <memory>
#include <array>

namespace purefs::blkdev
{
    class disk;
    class partition;
    namespace internal
    {
        class partition_parser
        {
          public:
            partition_parser(std::shared_ptr<disk> disk, std::vector<partition> &parts) : m_disk(disk), m_parts(parts)
            {}
            partition_parser(const partition_parser &) = delete;
            auto operator=(const partition_parser &) -> partition_parser & = delete;
            auto partition_search() -> int;

          private:
            static auto read_partitions(const std::vector<uint8_t> &buffer, std::array<partition, 4> &parts) -> void;
            static auto is_extended(uint8_t type) -> bool;
            auto parse_extended(uint32_t lba, uint32_t count) -> int;

          private:
            const std::shared_ptr<disk> m_disk;
            std::vector<partition> &m_parts;
        };
    } // namespace internal
} // namespace purefs::blkdev

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

#pragma once
#include <memory>

namespace purefs::fs::internal
{
    class mount_point;
    class directory_handle
    {
      public:
        directory_handle(const directory_handle &) = delete;
        virtual ~directory_handle()                = delete;
        auto operator=(const directory_handle &) = delete;
        auto error(int error) noexcept -> void
        {
            m_error = error;
        }
        [[nodiscard]] auto error() noexcept
        {
            return m_error;
        }

      private:
        int m_error{};
        std::weak_ptr<mount_point> m_mount_point;
    };
} // namespace purefs::fs::internal

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

#pragma once

#include <memory>

namespace purefs::fs::internal
{
    class mount_point;
    // File handle used for internal operation
    class file_handle
    {
      public:
        file_handle(const file_handle &) = delete;
        virtual ~file_handle()           = default;
        [[nodiscard]] auto error() const noexcept
        {
            return m_error;
        }
        auto error(int error) noexcept -> void
        {
            m_error = error;
        }
        auto flags(int flags) noexcept -> void
        {
            m_flags = flags;
        }
        [[nodiscard]] auto flags() const noexcept
        {
            return m_flags;
        }
        std::weak_ptr<mount_point> m_mount_point;

      private:
        int m_error{};
        int m_flags{};
    };
} // namespace purefs::fs::internal

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

#pragma once
#include <memory>
#include <string>

namespace purefs::blkdev
{
    class disk;
}

namespace purefs::fs
{
    class filesystem_operation;
}

namespace purefs::fs::internal
{
    //! Mount point disk private structure
    //! TODO fill the blanks
    class mount_point
    {
        virtual ~mount_point() = default;
        int device{-1};                                 //! Owning device
        std::string mount_path;                         //! Mounted path
        std::weak_ptr<filesystem_operation> filesystem; //! Filesystem operation
    };
} // namespace purefs::fs::internal

A module-vfs/include/internal/purefs/utils/handle_mapper.hpp => module-vfs/include/internal/purefs/utils/handle_mapper.hpp +47 -0
@@ 0,0 1,47 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <vector>
namespace purefs::utils::internal
{

    template <typename T> class handle_mapper
    {
      public:
        T const &operator[](std::size_t index) const
        {
            return data[index];
        }
        T &operator[](std::size_t index)
        {
            return data[index];
        }
        void remove(std::size_t index)
        {
            unused.push_back(index);
        }
        bool exists(std::size_t index) const
        {
            return data.size() < index;
        }
        std::size_t insert(T const &value);

      private:
        std::vector<T> data;
        std::vector<std::size_t> unused;
    };

    template <typename T> std::size_t handle_mapper<T>::insert(T const &value)
    {
        if (unused.empty()) {
            data.push_back(value);
            return data.size() - 1;
        }
        const std::size_t result = unused.back();
        unused.pop_back();
        data[result] = value;
        return result;
    }
} // namespace purefs::utils::internal

A module-vfs/include/user/purefs/blkdev/defs.hpp => module-vfs/include/user/purefs/blkdev/defs.hpp +43 -0
@@ 0,0 1,43 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <cstdint>
#include <memory>

namespace purefs::blkdev
{
    using sector_t = uint64_t;
    using scount_t = int64_t;
    namespace internal
    {
        class disk_handle;
    }
    using disk_fd_t = std::shared_ptr<internal::disk_handle>;
    //! Disk status result
    enum class media_status
    {
        healthly, //! Disk OK
        uninit,   //! Disk unintialized
        nomedia,  //! No media
        wprotect, //! Write protected
        error,    //! Internal error
    };
    // Information parameter
    enum class info_type
    {
        sector_count,
        sector_size,
        erase_block
    };

    //! Power control states
    enum class pm_state
    {
        active,        //! Device is in active state
        low_power,     //! Device is in low power state
        suspend,       //! Device is in suspend state
        force_suspend, //! Device is in force suspend state
        power_off      //! Device is in poweroff state
    };
} // namespace purefs::blkdev

A module-vfs/include/user/purefs/blkdev/disk.hpp => module-vfs/include/user/purefs/blkdev/disk.hpp +103 -0
@@ 0,0 1,103 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "defs.hpp"
#include "partition.hpp"
#include <vector>

namespace purefs::blkdev
{
    class disk
    {
      public:
        disk(const disk &) = delete;
        auto operator=(const disk &) = delete;
        disk()                       = default;
        virtual ~disk()              = default;

        /** Initialize the disk this method is called by the disc manager
         * @param flags Flags passed to the disc manager
         * @return zero on success otherwise error
         */
        virtual auto probe(unsigned flags) -> int = 0;

        /** Disk cleanup just before unregistering
         * @return  zero on success otherwise error
         */
        virtual auto cleanup() -> int = 0;

        /** Write a data onto block device or partition
         * @param[in] buf Data buffer to write
         * @param[in] lba First sector
         * @param[in] Count sectors count
         * @return zero on success otherwise error
         */
        virtual auto write(const void *buf, sector_t lba, std::size_t count) -> int = 0;

        /** Read a data from block device or partition
         * @param[in] buf Data buffer for read
         * @param[in] lba First sector
         * @param[in] Count sectors count
         * @return zero on success otherwise error
         */
        virtual auto read(void *buf, sector_t lba, std::size_t count) -> int = 0;

        /** Erase selected area on the block device or partition
         * @param[in] lba First sector to erase
         * @param[in] count Sectors count for erase
         * @return zero or success otherwise error
         */
        virtual auto erase(sector_t lba, std::size_t count) -> int;

        /** Flush buffers and write all data into the physical device
         * @return zero or success otherwise error
         */
        virtual auto sync() -> int = 0;

        /** Set block device power state
         * @param[in] target_state Set the target power state
         * @return zero or success otherwise error
         * @note If the partition is changed whole device state will be suspended
         */
        virtual auto pm_control(pm_state target_state) -> int;

        /** Get block device power state
         * @param[out] currrent_state Device current state
         * @return zero or success otherwise error
         * @note If the partition is changed whole device state will be suspended
         */
        virtual auto pm_read(pm_state &current_state) -> int;

        /** Read the current media status
         * @param[in] device_name Device name or partition
         * @return Current media status @seee media_status
         */
        [[nodiscard]] virtual auto status() const -> media_status = 0;

        /** List the partitions on the underlaying device
         * @param[in] device_name Block device name
         * @return Partition list @see partition
         */
        [[nodiscard]] virtual auto get_info(info_type what) const -> scount_t = 0;

        /** List the partitions on the underlaying device
         * @param[in] device_name Block device name
         * @return Partition list @see partition
         */
        [[nodiscard]] auto partitions() -> std::vector<partition> &
        {
            return m_partitions;
        }

      protected:
        /** Clear all partitions */
        auto clear_partitions() -> void
        {
            m_partitions.clear();
        }

      private:
        std::vector<partition> m_partitions;
    };
} // namespace purefs::blkdev

A module-vfs/include/user/purefs/blkdev/disk_manager.hpp => module-vfs/include/user/purefs/blkdev/disk_manager.hpp +129 -0
@@ 0,0 1,129 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <memory>
#include <string>
#include <vector>
#include <unordered_map>
#include <mutex.hpp>
#include <tuple>
#include "defs.hpp"
#include "partition.hpp"

namespace purefs::blkdev
{
    class disk;

    /** Disk manager is a class for allows to control block devices media
     */
    class disk_manager
    {
      public:
        disk_manager(const disk_manager &) = delete;
        auto operator=(const disk_manager &) -> disk_manager & = delete;
        disk_manager()                                         = default;
        /** Register a new disc
         * @param[in] disk Block device register
         * @param[in] device_name Disk friendly name
         * @return zero on success othervise error
         */
        auto register_device(std::shared_ptr<disk> disk, std::string_view device_name, unsigned flags = 0) -> int;
        /** Unregister a disc from the manager
         *  param[in] Disc to unregister
         *  @return error code or 0 if success
         */
        auto unregister_device(std::string_view device_name) -> int;
        /** Write a data onto block device or partition
         * @param[in] dfd Disk manager fd
         * @param[in] buf Data buffer to write
         * @param[in] lba First sector
         * @param[in] Count sectors count
         * @return zero on success otherwise error
         */
        auto write(disk_fd_t dfd, const void *buf, sector_t lba, std::size_t count) -> int;
        auto write(std::string_view device_name, const void *buf, sector_t lba, std::size_t count) -> int;
        /** Read a data from block device or partition
         * @param[in] dfd Disk manager fd
         * @param[in] buf Data buffer for read
         * @param[in] lba First sector
         * @param[in] Count sectors count
         * @return zero on success otherwise error
         */

        auto read(disk_fd_t dfd, void *buf, sector_t lba, std::size_t count) -> int;
        auto read(std::string_view device_name, void *buf, sector_t lba, std::size_t count) -> int;
        /** Erase selected area on the block device or partition
         * @param[in] dfd Disk manager fd
         * @param[in] lba First sector to erase
         * @param[in] count Sectors count for erase
         * @return zero or success otherwise error
         */

        auto erase(disk_fd_t dfd, sector_t lba, std::size_t count) -> int;
        auto erase(std::string_view device_name, sector_t lba, std::size_t count) -> int;
        /** Flush buffers and write all data into the physical device
         * param[in] dfd Disc manager fd
         * @return zero or success otherwise error
         */
        auto sync(disk_fd_t dfd) -> int;
        auto sync(std::string_view device_name) -> int;
        /** Set block device power state
         * @param[in] device_name Device or partition name
         * @param[in] target_state Set the target power state
         * @return zero or success otherwise error
         * @note If the partition is changed whole device state will be suspended
         */
        auto pm_control(disk_fd_t dfd, pm_state target_state) -> int;
        auto pm_control(std::string_view device_name, pm_state target_state) -> int;
        /** Get block device power state
         * @param[in] dfd Disk device handle
         * @param[out] currrent_state Device current state
         * @return zero or success otherwise error
         * @note If the partition is changed whole device state will be suspended
         */
        auto pm_read(disk_fd_t dfd, pm_state &current_state) -> int;
        auto pm_read(std::string_view device_name, pm_state &current_state) -> int;
        /** Read the current media status
         * @param[in] dfd Disk manager handle
         * @return Current media status @seee media_status
         */
        [[nodiscard]] auto status(disk_fd_t dfd) const -> media_status;
        [[nodiscard]] auto status(std::string_view device_name) const -> media_status;
        /** List the partitions on the underlaying device
         * @param[in] dfd Disk manager fd
         * @return Partition list @see partition
         */
        [[nodiscard]] auto partitions(disk_fd_t dfd) const -> std::vector<partition>;
        [[nodiscard]] auto partitions(std::string_view device_name) const -> std::vector<partition>;
        /** Get media device info
         * @param[in] dfd Disk manager handle
         * @param[in] what Information type @see info_type
         * @return Desired data or error if negative
         */
        [[nodiscard]] auto get_info(disk_fd_t dfd, info_type what) const -> scount_t;
        [[nodiscard]] auto get_info(std::string_view device_name, info_type what) const -> scount_t;

        /** Force reread partition tables
         * @param dfd Disk manager handle
         * @return error code
         */
        auto reread_partitions(disk_fd_t dfd) -> int;
        auto reread_partitions(std::string_view device_name) -> int;
        /**
         * Return the device object based on friendly name
         * @param device_name  Device name
         * @return device object pointer
         */
        auto device_handle(std::string_view device_name) const -> disk_fd_t;

      private:
        static auto parse_device_name(std::string_view device) -> std::tuple<std::string_view, short>;
        static auto part_lba_to_disk_lba(disk_fd_t disk, sector_t part_lba, size_t count) -> scount_t;

      private:
        std::unordered_map<std::string, std::shared_ptr<disk>> m_dev_map;
        mutable cpp_freertos::MutexRecursive m_lock;
    };
} // namespace purefs::blkdev

A module-vfs/include/user/purefs/blkdev/partition.hpp => module-vfs/include/user/purefs/blkdev/partition.hpp +21 -0
@@ 0,0 1,21 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "defs.hpp"
#include <string>

namespace purefs::blkdev
{
    //! Disk partition table structure
    struct partition
    {
        int physical_number{};     //! Partition physical number in part table
        sector_t start_sector{};   //! First sector
        std::size_t num_sectors{}; //! Number of sectors
        bool bootable{};           //! Partition is bootable
        unsigned short type{};     //! Partition code
        std::string name;          //! Partition name in block manager
    };
} // namespace purefs::blkdev

A module-vfs/include/user/purefs/fs/filesystem.hpp => module-vfs/include/user/purefs/fs/filesystem.hpp +103 -0
@@ 0,0 1,103 @@
// Copyright (c) 2017-2020, 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 <list>
#include <array>

namespace purefs::fs
{

    /** This is the filesystem class layer
     * All methods can be called from the user thread
     * but generally those functions are for internal use for
     * example for newlib or glibc syscalls
     */
    class filesystem_operation;
    struct statvfs;
    struct timespec;
    namespace internal
    {
        class directory_handle;
    }

    class filesystem
    {
      public:
        using fsdir                    = std::shared_ptr<internal::directory_handle>;
        filesystem(const filesystem &) = delete;
        auto operator=(const filesystem &) = delete;
        /** Utility API */
        /** Register filesystem driver
         * @param[in] fsname Unique filesystem name for example fat
         * @param[in] fops Filesystem operation structure
         * @return zero on sucess otherwise error
         */
        auto register_filesystem(std::string_view fsname, std::shared_ptr<filesystem_operation> fops) -> int;
        /** Unregister filesystem driver
         * @param[in] fsname Unique filesystem name for example fat
         * @return zero on success otherwise error
         **/
        auto unregister_filesystem(std::string_view fsname) -> int;

        /** Mount filesystem to the the specified mount point
         * @param[in] dev_or_part Device or partition for mount
         * @param[in] target Target path where the fs will be mounted
         * @param[in] flags  Mount flags
         * @return zero on success otherwise error
         */
        auto mount(std::string_view dev_or_part, std::string_view target, std::string_view fs_type, unsigned flags)
            -> int;
        /** Unmont filesystem from selected mount point
         * @param[in] mount_point Mount point where the fs is mounted
         * @return zero on success otherwise error
         */
        auto umount(std::string_view mount_point) -> int;
        /** Return actually mounted filesystem list
         * @param[out] mountpoints List of mount points
         * @return zero on success otherwise error
         */
        auto read_mountpoints(std::list<std::string> &mountpoints) const -> int;
        /** Get actual filesystem statistics *  @see man statvfs
         * @param[in] path Pathname of any file within the mounted filesystem
         * @param[out] stat Pointer to a statvfs structure
         * @return zero on success otherwise error
         */
        auto stat_vfs(std::string_view path, statvfs &stat) const noexcept -> int;
        /** Standard file access API */
        auto open(std::string_view path, int flags, int mode) noexcept -> int;
        auto close(int fd) noexcept -> int;
        auto write(int fd, const char *ptr, size_t len) noexcept -> ssize_t;
        auto read(int fd, char *ptr, size_t len) noexcept -> ssize_t;
        auto seek(int fd, off_t pos, int dir) noexcept -> off_t;
        auto fstat(int fd, struct stat *st) noexcept -> int;
        auto stat(std::string_view file, struct stat *st) noexcept -> int;
        auto link(std::string_view existing, std::string_view newlink) noexcept -> int;
        auto symlink(std::string_view existing, std::string_view newlink) noexcept -> int;
        auto unlink(std::string_view name) noexcept -> int;
        auto rename(std::string_view oldname, std::string_view newname) noexcept -> int;
        auto mkdir(std::string_view path, int mode) noexcept -> int;

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

        /** Other fops API */
        auto ftruncate(int fd, off_t len) noexcept -> int;
        auto fsync(int fd) noexcept -> int;
        auto ioctl(std::string_view path, int cmd, void *arg) noexcept -> int;
        auto utimens(std::string_view path, std::array<timespec, 2> &tv) noexcept -> int;
        auto flock(std::string_view path, int cmd) noexcept -> int;
        auto isatty(std::string_view path) noexcept -> int;

        auto chmod(std::string_view path, mode_t mode) noexcept -> int;
        auto fchmod(int fd, mode_t mode) noexcept -> int;

        auto getcwd() noexcept -> std::string;
        auto chdir(std::string_view name) noexcept -> int;
    };
} // namespace purefs::fs

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

#pragma once
#include <string>
#include <list>
#include <tuple>
#include <memory>

namespace purefs::fs
{
    struct statvfs;
    namespace internal
    {
        class file_handle;
        class directory_handle;
        class mount_point;
    }; // namespace internal

    /** Filesystem specific driver base class */
    class filesystem_operation
    {
      public:
        using fsfile                                       = std::shared_ptr<internal::file_handle>;
        using fsdir                                        = std::shared_ptr<internal::directory_handle>;
        using fsmount                                      = std::shared_ptr<internal::mount_point>;
        filesystem_operation(const filesystem_operation &) = delete;
        virtual ~filesystem_operation()                    = default;
        auto operator=(const filesystem_operation &) = delete;
        /** Allocate mount point class specify to the VFS
         * @return Allocated mount point structure
         */
        virtual auto mount_prealloc() -> fsmount = 0;
        virtual auto mount(fsmount mnt) noexcept -> int;
        virtual auto umount(fsmount mnt) noexcept -> int                                  = 0;
        virtual auto stat_vfs(std::string_view path, statvfs &stat) const noexcept -> int = 0;

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

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

        /** Other fops API */
        virtual auto ftruncate(fsfile zfile, off_t len) noexcept -> int                                       = 0;
        virtual auto fsync(fsfile zfile) noexcept -> int                                                      = 0;
        virtual auto ioctl(fsfile zfile, int cmd, void *arg) noexcept -> int                                  = 0;
        virtual auto utimens(fsmount mnt, std::string_view path, std::array<timespec, 2> &tv) noexcept -> int = 0;
        virtual auto flock(fsfile zfile, int cmd) noexcept -> int                                             = 0;
        virtual auto isatty(fsfile zfile) noexcept -> int                                                     = 0;

        virtual auto chmod(fsmount mnt, std::string_view path, mode_t mode) noexcept -> int = 0;
        virtual auto fchmod(fsfile zfile, mode_t mode) noexcept -> int                      = 0;
    };
} // namespace purefs::fs

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

#include <purefs/blkdev/disk.hpp>
#include <errno.h>

namespace purefs::blkdev
{
    auto disk::erase(sector_t lba, std::size_t count) -> int
    {
        return -ENOTSUP;
    }
    auto disk::pm_control(pm_state target_state) -> int
    {
        return -ENOTSUP;
    }
    auto disk::pm_read(pm_state &current_state) -> int
    {
        return -ENOTSUP;
    }
} // namespace purefs::blkdev

A module-vfs/src/purefs/blkdev/disk_handle.cpp => module-vfs/src/purefs/blkdev/disk_handle.cpp +28 -0
@@ 0,0 1,28 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <purefs/blkdev/disk_handle.hpp>
#include <purefs/blkdev/disk.hpp>
#include <log/log.hpp>

namespace purefs::blkdev::internal
{
    auto disk_handle::sectors() const noexcept -> sector_t
    {
        if (!m_sectors) {
            auto pdisk = m_disk.lock();
            if (pdisk) {
                const auto sector_count = pdisk->get_info(info_type::sector_count);
                if (sector_count > 0) {
                    m_sectors = sector_count;
                }
                else {
                    LOG_ERROR("Unable to get sector count.");
                }
            }
            else {
                LOG_ERROR("Unable to get sector count.");
            }
        }
        return m_sectors;
    }
} // namespace purefs::blkdev::internal

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

#include <purefs/blkdev/disk_manager.hpp>
#include <purefs/blkdev/disk.hpp>
#include <log/log.hpp>
#include <errno.h>
#include <charconv>
#include <tuple>
#include <purefs/blkdev/disk_handle.hpp>
#include <purefs/blkdev/partition_parser.hpp>

/** NOTE: Device manager implementation without sector cache
 */
namespace purefs::blkdev
{
    namespace
    {
        using namespace std::literals;
        static constexpr auto part_suffix = "part"sv;
    } // namespace
    auto disk_manager::register_device(std::shared_ptr<disk> disk, std::string_view device_name, unsigned flags) -> int
    {
        {
            cpp_freertos::LockGuard _lck(m_lock);
            const auto ret = m_dev_map.find(std::string(device_name));
            if (ret != std::end(m_dev_map)) {
                LOG_ERROR("Disc: %.*s already registered.", int(device_name.length()), device_name.data());
                return -EEXIST;
            }
            else {
                auto ret = disk->probe(flags);
                if (ret < 0) {
                    LOG_ERROR("Unable to probe the disc errno %i", ret);
                    return ret;
                }
                const auto it = m_dev_map.emplace(std::make_pair(device_name, disk));
                return reread_partitions(std::make_shared<internal::disk_handle>(disk, it.first->first));
            }
        }
    }
    auto disk_manager::unregister_device(std::string_view device_name) -> int
    {
        cpp_freertos::LockGuard _lck(m_lock);
        auto it = m_dev_map.find(std::string(device_name));
        if (it == std::end(m_dev_map)) {
            LOG_ERROR("Disc: %.*s doesn't exists in manager.", int(device_name.length()), device_name.data());
            return -ENOENT;
        }
        auto ret = it->second->cleanup();
        m_dev_map.erase(it);
        if (ret < 0) {
            LOG_ERROR("Disk cleanup failed code %i", ret);
        }
        return ret;
    }

    auto disk_manager::device_handle(std::string_view device_name) const -> disk_fd_t
    {
        const auto [dev, part] = parse_device_name(device_name);
        disk_fd_t ret{};
        if (dev.empty()) {
            ret = nullptr;
        }
        else {
            cpp_freertos::LockGuard _lck(m_lock);
            const auto it = m_dev_map.find(std::string(dev));
            if (it == std::end(m_dev_map)) {
                ret = nullptr;
            }
            else {
                ret = std::make_shared<internal::disk_handle>(it->second, device_name, part);
            }
        }
        return ret;
    }

    auto disk_manager::parse_device_name(std::string_view device) -> std::tuple<std::string_view, short>
    {
        auto ret = device.rfind(part_suffix);
        if (ret != std::string::npos) {
            auto part_name = device.substr(0, ret);
            auto part_num  = device.substr(ret + part_suffix.length());
            short part_inum{-1};
            if (!part_num.empty()) {
                auto ires = std::from_chars(std::begin(part_num), std::end(part_num), part_inum);
                if (ires.ec == std::errc())
                    return std::make_tuple(part_name, part_inum);
                else
                    return std::make_tuple(""sv, -1);
            }
            else {
                return std::make_tuple(part_name, part_inum);
            }
        }
        else {
            return std::make_tuple(device, -1);
        }
    }
    auto disk_manager::part_lba_to_disk_lba(disk_fd_t disk, sector_t part_lba, size_t count) -> scount_t
    {
        if (!disk->has_partition()) {
            if (part_lba + count > disk->sectors()) {
                LOG_ERROR("Disk sector req out of range");
                return -ERANGE;
            }
            else {
                return part_lba;
            }
        }
        else {
            auto pdisc      = disk->disk().lock();
            const auto part = pdisc->partitions()[disk->partition()];
            if (part_lba + count > part.num_sectors) {
                LOG_ERROR("Partition sector req out of range");
                return -ERANGE;
            }
            else {
                return part_lba + part.start_sector;
            }
        }
    }
    auto disk_manager::write(disk_fd_t dfd, const void *buf, sector_t lba, std::size_t count) -> int
    {
        auto disk = dfd->disk().lock();
        if (!disk) {
            LOG_ERROR("Disk doesn't exists");
            return -ENOENT;
        }
        const auto calc_lba = part_lba_to_disk_lba(dfd, lba, count);
        if (calc_lba < 0) {
            return calc_lba;
        }
        else {
            return disk->write(buf, calc_lba, count);
        }
    }
    auto disk_manager::read(disk_fd_t dfd, void *buf, sector_t lba, std::size_t count) -> int
    {
        auto disk = dfd->disk().lock();
        if (!disk) {
            LOG_ERROR("Disk doesn't exists");
            return -ENOENT;
        }
        const auto calc_lba = part_lba_to_disk_lba(dfd, lba, count);
        if (calc_lba < 0) {
            return calc_lba;
        }
        else {
            return disk->read(buf, calc_lba, count);
        }
    }
    auto disk_manager::erase(disk_fd_t dfd, sector_t lba, std::size_t count) -> int
    {
        auto disk = dfd->disk().lock();
        if (!disk) {
            LOG_ERROR("Disk doesn't exists");
            return -ENOENT;
        }
        const auto calc_lba = part_lba_to_disk_lba(dfd, lba, count);
        if (calc_lba < 0) {
            return calc_lba;
        }
        else {
            return disk->erase(calc_lba, count);
        }
    }
    auto disk_manager::sync(disk_fd_t dfd) -> int
    {
        auto disk = dfd->disk().lock();
        if (!disk) {
            LOG_ERROR("Disk doesn't exists");
            return -ENOENT;
        }
        return disk->sync();
    }
    auto disk_manager::pm_control(disk_fd_t dfd, pm_state target_state) -> int
    {
        auto disk = dfd->disk().lock();
        if (!disk) {
            LOG_ERROR("Disk doesn't exists");
            return -ENOENT;
        }
        return disk->pm_control(target_state);
    }
    auto disk_manager::pm_read(disk_fd_t dfd, pm_state &current_state) -> int
    {
        auto disk = dfd->disk().lock();
        if (!disk) {
            LOG_ERROR("Disk doesn't exists");
            return -ENOENT;
        }
        return disk->pm_read(current_state);
    }
    auto disk_manager::status(disk_fd_t dfd) const -> media_status
    {
        auto disk = dfd->disk().lock();
        if (!disk) {
            LOG_ERROR("Disk doesn't exists");
            return media_status::error;
        }
        return disk->status();
    }
    auto disk_manager::partitions(disk_fd_t dfd) const -> std::vector<partition>
    {
        auto disk = dfd->disk().lock();
        if (!disk) {
            LOG_ERROR("Disk doesn't exists");
            return {};
        }
        return disk->partitions();
    }
    auto disk_manager::get_info(disk_fd_t dfd, info_type what) const -> scount_t
    {
        auto disk = dfd->disk().lock();
        if (!disk) {
            LOG_ERROR("Disk doesn't exists");
            return {};
        }
        return disk->get_info(what);
    }
    auto disk_manager::reread_partitions(disk_fd_t dfd) -> int
    {
        auto disk = dfd->disk().lock();
        if (!disk) {
            LOG_ERROR("Disk doesn't exists");
            return {};
        }
        internal::partition_parser pparser(disk, disk->partitions());
        auto ret = pparser.partition_search();
        // Fill the partition name
        if (!ret) {
            int no{};
            for (auto &parts : disk->partitions()) {
                parts.name = dfd->name();
                parts.name += part_suffix;
                parts.name += std::to_string(no++);
            }
        }
        return ret;
    }

    auto disk_manager::write(std::string_view device_name, const void *buf, sector_t lba, std::size_t count) -> int
    {
        auto dfd = device_handle(device_name);
        if (dfd)
            return write(dfd, buf, lba, count);
        else
            return -ENOENT;
    }
    auto disk_manager::read(std::string_view device_name, void *buf, sector_t lba, std::size_t count) -> int
    {
        auto dfd = device_handle(device_name);
        if (dfd)
            return read(dfd, buf, lba, count);
        else
            return -ENOENT;
    }
    auto disk_manager::erase(std::string_view device_name, sector_t lba, std::size_t count) -> int
    {
        auto dfd = device_handle(device_name);
        if (dfd)
            return erase(dfd, lba, count);
        else
            return -ENOENT;
    }
    auto disk_manager::sync(std::string_view device_name) -> int
    {
        auto dfd = device_handle(device_name);
        if (dfd)
            return sync(dfd);
        else
            return -ENOENT;
    }
    auto disk_manager::pm_control(std::string_view device_name, pm_state target_state) -> int
    {
        auto dfd = device_handle(device_name);
        if (dfd)
            return pm_control(dfd, target_state);
        else
            return -ENOENT;
    }
    auto disk_manager::pm_read(std::string_view device_name, pm_state &current_state) -> int
    {
        auto dfd = device_handle(device_name);
        if (dfd)
            return pm_read(dfd, current_state);
        else
            return -ENOENT;
    }
    auto disk_manager::status(std::string_view device_name) const -> media_status
    {
        auto dfd = device_handle(device_name);
        if (dfd)
            return status(dfd);
        else
            return media_status::error;
    }
    auto disk_manager::partitions(std::string_view device_name) const -> std::vector<partition>
    {
        auto dfd = device_handle(device_name);
        if (dfd)
            return partitions(dfd);
        else
            return {};
    }
    auto disk_manager::get_info(std::string_view device_name, info_type what) const -> scount_t
    {
        auto dfd = device_handle(device_name);
        if (dfd)
            return get_info(dfd, what);
        else
            return -ENOENT;
    }
    auto disk_manager::reread_partitions(std::string_view device_name) -> int
    {
        auto dfd = device_handle(device_name);
        if (dfd)
            return reread_partitions(dfd);
        else
            return -ENOENT;
    }
} // namespace purefs::blkdev

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

#include <purefs/blkdev/partition_parser.hpp>
#include <purefs/blkdev/disk.hpp>
#include <log/log.hpp>
#include <errno.h>

namespace purefs::blkdev::internal
{

    namespace defs
    {
        namespace
        {
            constexpr auto mbr_signature_offs = 0x1FE;
            constexpr auto mbr_ptbl_offs      = 0x1BE;
            constexpr auto mbr_ptbl_active    = 0x000;
            constexpr auto mbr_ptbl_type      = 0x004;
            constexpr auto mbr_ptbl_sect_cnt  = 0x00c;
            constexpr auto mbr_ptbl_lba       = 0x008;
            constexpr auto ptbl_offs          = 0x1be;
            constexpr auto ptbl_size          = 16;
            constexpr auto ext_part           = 0x05;
            constexpr auto ext_linux_part     = 0x85;
            constexpr auto ext_win98_part     = 0x0f;
            constexpr auto reserved_sect      = 0x00e;
            constexpr auto number_of_fats     = 0x010;
            constexpr auto num_parts          = 4;
        } // namespace
    }     // namespace defs
    namespace
    {
        inline auto to_word(const std::vector<uint8_t> &vec, std::size_t offs)
        {
            auto buf = &vec[offs];
            return (uint32_t(buf[0]) << 0U) | (uint32_t(buf[1]) << 8U) | (uint32_t(buf[2]) << 16U) |
                   (uint32_t(buf[3]) << 24U);
        }
        inline auto to_short(const std::vector<uint8_t> &vec, std::size_t offs)
        {
            auto buf = &vec[offs];
            return (uint16_t(buf[0]) << 0U) | (uint16_t(buf[1]) << 8U);
        }
    } // namespace

    // Partition search
    auto partition_parser::partition_search() -> int
    {
        const auto sect_size = m_disk->get_info(info_type::sector_size);
        if (sect_size < 0) {
            LOG_ERROR("Unable to get sector size");
            return sect_size;
        }
        std::vector<std::uint8_t> mbr_sect(sect_size);
        auto ret = m_disk->read(mbr_sect.data(), 0, 1);
        if (ret < 0) {
            return ret;
        }
        // Check initial signature
        if ((mbr_sect[defs::mbr_signature_offs] != 0x55) && (mbr_sect[defs::mbr_signature_offs + 1] != 0xAA)) {
            LOG_ERROR("Unable to find valid partition signature");
            return -ENXIO;
        }
        /* Copy the 4 partition records into partitions */
        std::array<partition, defs::num_parts> root_part;
        read_partitions(mbr_sect, root_part);
        // Add not extended partitions
        int part_no{1};
        for (auto &part : root_part) {
            if (is_extended(part.type))
                continue;
            if (part.num_sectors) {
                part.physical_number = part_no;
                m_parts.emplace_back(part);
            }
            ++part_no;
        }
        for (const auto &part : root_part) {
            if (is_extended(part.type)) {
                ret = parse_extended(part.start_sector, part.num_sectors);
                if (ret < 0)
                    return ret;
            }
        }
        return ret;
    }

    auto partition_parser::read_partitions(const std::vector<uint8_t> &buffer,
                                           std::array<partition, defs::num_parts> &parts) -> void
    {
        std::size_t offs = defs::ptbl_offs;
        for (auto &part : parts) {
            part.bootable     = buffer[defs::mbr_ptbl_active + offs] & 0x80;
            part.type         = buffer[defs::mbr_ptbl_type + offs];
            part.num_sectors  = to_word(buffer, defs::mbr_ptbl_sect_cnt + offs);
            part.start_sector = to_word(buffer, defs::mbr_ptbl_lba + offs);
            offs += defs::ptbl_size;
        }
    }
    auto partition_parser::is_extended(uint8_t type) -> bool
    {
        return type == defs::ext_linux_part || type == defs::ext_part || type == defs::ext_win98_part;
    }

    auto partition_parser::parse_extended(uint32_t lba, uint32_t count) -> int
    {
        static constexpr auto max_parts{100};
        auto sector_size = m_disk->get_info(info_type::sector_size);
        int extended_part_num;
        std::array<partition, defs::num_parts> parts;
        auto current_sector = lba;
        auto this_size      = count;
        if (sector_size < 0) {
            return sector_size;
        }
        uint32_t sector_in_buf{std::numeric_limits<uint32_t>::max()};
        std::vector<uint8_t> sect_buf(sector_size);

        auto try_count{max_parts};
        int error{};
        while (try_count--) {
            if (sector_in_buf != current_sector) {
                LOG_INFO("extended parse: Read sector %u\n", unsigned(current_sector));
                error = m_disk->read(sect_buf.data(), current_sector, 1);
                if (error < 0)
                    break;
                sector_in_buf = current_sector;
            }
            {
                const auto b1 = sect_buf[defs::mbr_signature_offs];
                const auto b2 = sect_buf[defs::mbr_signature_offs + 1];
                if (b1 != 0x55 || b2 != 0xAA) {
                    LOG_ERROR("extended_parse: No signature %02x,%02x", b1, b2);
                    break;
                }
            }

            read_partitions(sect_buf, parts);
            extended_part_num = -1;

            for (auto partition_num = 0U; partition_num < parts.size(); ++partition_num) {
                if (parts[partition_num].num_sectors == 0) {
                    /* Partition is empty */
                    continue;
                }
                if (is_extended(parts[partition_num].type)) {
                    if (extended_part_num < 0)
                        extended_part_num = partition_num;
                    continue; /* We'll examine this ext partition later */
                }

                /* Some sanity checks */
                const auto poffset = parts[partition_num].start_sector * sector_size;
                const auto psize   = parts[partition_num].num_sectors * sector_size;
                const auto pnext   = current_sector + poffset;
                if ((poffset + psize > this_size) || // oversized
                    (pnext < lba) ||                 // going backward
                    (pnext > lba + count)            // outsized
                ) {
                    LOG_WARN("Part %d looks strange: current_sector %u offset %u next %u\n",
                             int(partition_num),
                             unsigned(current_sector),
                             unsigned(poffset),
                             unsigned(pnext));
                    continue;
                }
                parts[partition_num].physical_number = partition_num + 1;
                m_parts.emplace_back(parts[partition_num]);
                m_parts.back().start_sector += current_sector;
                try_count = max_parts;
            }

            if (extended_part_num < 0) {
                LOG_DEBUG("No more extended partitions");
                break; /* nothing left to do */
            }
            /* Examine the next extended partition */
            current_sector = lba + parts[extended_part_num].start_sector * sector_size;
            this_size      = parts[extended_part_num].num_sectors * sector_size;
        }
        return error;
    }
} // namespace purefs::blkdev::internal

M module-vfs/targets/Target_Linux.cmake => module-vfs/targets/Target_Linux.cmake +6 -1
@@ 2,7 2,12 @@ set(BOARD_SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/board/linux/free_rtos_custom/portable/ff_image_user_disk.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/board/linux/free_rtos_custom/portable/common.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/board/linux/free_rtos_custom/portable/vfs.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/board/linux/purefs/src/blkdev/disk_image.cpp
        CACHE INTERNAL ""
)

set(BOARD_DIR_INCLUDES  ${CMAKE_CURRENT_SOURCE_DIR}/board/linux/free_rtos_custom/include CACHE INTERNAL "")
set(BOARD_DIR_INCLUDES
        ${CMAKE_CURRENT_SOURCE_DIR}/board/linux/free_rtos_custom/include
        ${CMAKE_CURRENT_SOURCE_DIR}/board/linux/purefs/include/
        CACHE INTERNAL ""
)

M module-vfs/tests/CMakeLists.txt => module-vfs/tests/CMakeLists.txt +9 -0
@@ 10,6 10,15 @@ add_catch2_executable(
        module-vfs
)

add_catch2_executable(
    NAME vfs-disk
    SRCS
        ${CMAKE_CURRENT_LIST_DIR}/unittest_disk_manager.cpp
    LIBS
        module-vfs
)


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

A module-vfs/tests/unittest_disk_manager.cpp => module-vfs/tests/unittest_disk_manager.cpp +67 -0
@@ 0,0 1,67 @@
// Copyright (c) 2017-2020, 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 <purefs/blkdev/disk_manager.hpp>
#include <purefs/blkdev/disk_image.hpp>

namespace
{
    constexpr auto disk_image = "PurePhone.img";
}
TEST_CASE("Registering and unregistering device")
{
    using namespace purefs;
    blkdev::disk_manager dm;
    auto disk = std::make_shared<blkdev::disk_image>(disk_image);
    REQUIRE(disk);
    REQUIRE(dm.register_device(disk, "emmc0") == 0);
    REQUIRE(dm.register_device(disk, "emmc0") == -EEXIST);
    REQUIRE(dm.unregister_device("emmc124") == -ENOENT);
    const auto handle = dm.device_handle("emmc0");
    REQUIRE(handle);
    REQUIRE(dm.unregister_device("emmc0") == 0);
    const auto handle2 = dm.device_handle("emmc0");
    REQUIRE(handle2 == nullptr);
}

TEST_CASE("Parsing and checking partititons")
{
    using namespace purefs;
    blkdev::disk_manager dm;
    auto disk = std::make_shared<blkdev::disk_image>(disk_image);
    REQUIRE(disk);
    REQUIRE(dm.register_device(disk, "emmc0") == 0);
    const auto parts = dm.partitions("emmc0");
    REQUIRE(parts.size() > 1);
    unsigned long prev_start = 0;
    int num{};
    for (const auto &part : parts) {
        REQUIRE(part.physical_number > 0);
        REQUIRE(part.start_sector >= 2048);
        REQUIRE(part.num_sectors > 0);
        REQUIRE(part.start_sector >= prev_start);
        REQUIRE(part.type > 0);
        REQUIRE(part.name == ("emmc0part" + std::to_string(num++)));
        prev_start = part.num_sectors + part.start_sector;
    }
}

TEST_CASE("RW boundary checking")
{
    using namespace purefs;
    blkdev::disk_manager dm;
    auto disk = std::make_shared<blkdev::disk_image>(disk_image);
    REQUIRE(disk);
    REQUIRE(dm.register_device(disk, "emmc0") == 0);
    const auto parts = dm.partitions("emmc0");
    REQUIRE(parts.size() > 1);
    const auto &part1 = parts[0];
    // Read sector by disk name
    const auto sect_size = dm.get_info("emmc0", blkdev::info_type::sector_size);
    REQUIRE(sect_size > 0);
    std::vector<uint8_t> buf1(sect_size), buf2(sect_size);
    REQUIRE(dm.read("emmc0", buf1.data(), part1.start_sector, 1) == 0);
    REQUIRE(dm.read(part1.name, buf2.data(), 0, 1) == 0);
    REQUIRE(buf1 == buf2);
}