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 ¤t_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 ¤t_state) -> int;
+ auto pm_read(std::string_view device_name, pm_state ¤t_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 ¤t_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 ¤t_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 ¤t_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);
+}