~aleteoryx/muditaos

8be15b96e4113903c3501a6dee3fa6108665ff29 — Lucjan Bryndza 5 years ago 6352dd0
[EGD-4498] Virtual Filesystem Core Framework (#1074)

* [EGD-4498] Registering and unregistering VFS

* [EGD-4498] Mount structures added

* [EGD-4498] Mount filesystem completed

* [EGD-4498] Mount / unmount / and find mount impl

* [EGD-4498] Per thread directory TLS

* [EGD-4489] Absolute paths implementation

* [EGD-4498] Git ignore vim

* [EGD-4498] VFS core functions without cwd

* [EGD-4498] Other syscalls

* [EGD-4498] Code review fixes

* [EGD-4498] Code review #2

Signed-off-by: Lucjan Bryndza <lucjan.bryndza@mudita.com>

* [EGD-4498] Code review small fixes vol #3

Signed-off-by: Lucjan Bryndza <lucjan.bryndza@mudita.com>
M .gitignore => .gitignore +6 -0
@@ 43,3 43,9 @@ docker/Dockerfile
docker/assets/actions*
docker/assets/cmake*
docker/assets/gcc-arm*

# vim temporary files
*.swp
*.swo
tags
.ycm_extra_conf.py

A module-utils/split_sv.hpp => module-utils/split_sv.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 <list>
#include <string>
#include <type_traits>

namespace utils
{
    template <template <class, class> class Container, class T, class Allocator = std::allocator<T>>
    auto split(T strv, T delims = " ")
    {
        static_assert(std::is_same<T, std::string>::value || std::is_same<T, std::string_view>::value,
                      "std::string or std::string_view expected");
        Container<T, Allocator> output;
        size_t first = 0;

        while (first < strv.size()) {
            const auto second = strv.find_first_of(delims, first);

            if (first != second)
                output.emplace_back(strv.substr(first, second - first));

            if (second == std::string_view::npos)
                break;

            first = second + 1;
        }

        return output;
    }
} // namespace utils

M module-vfs/CMakeLists.txt => module-vfs/CMakeLists.txt +4 -0
@@ 49,6 49,10 @@ set(SOURCES ""
        src/purefs/blkdev/disk.cpp
        src/purefs/blkdev/partition_parser.cpp
        src/purefs/blkdev/disk_handle.cpp
        src/purefs/fs/filesystem.cpp
        src/purefs/fs/filesystem_operations.cpp
        src/purefs/fs/filesystem_syscalls.cpp
        src/purefs/fs/filesystem_cwd.cpp
        src/deprecated/vfs-utils.cpp
        src/deprecated/vfs.cpp
        src/deprecated/vfsNotifier.cpp

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

#include <string>
#include <purefs/fs/thread_local_cwd.hpp>

namespace purefs::fs::internal
{

    namespace
    {
        thread_local std::string cwd_per_thread{"/"};
    }
    auto get_thread_local_cwd_path() noexcept -> std::string_view
    {
        return cwd_per_thread;
    }
    auto set_thread_cwd_path(std::string_view path) noexcept -> int
    {
        cwd_per_thread = path;
        return 0;
    }
    auto cleanup_thread_local_cwd_mem() -> void
    {
        cwd_per_thread.erase();
    }
} // namespace purefs::fs::internal

A module-vfs/board/rt1051/purefs/src/fs/thread_local_cwd.cpp => module-vfs/board/rt1051/purefs/src/fs/thread_local_cwd.cpp +57 -0
@@ 0,0 1,57 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <thread.hpp>
#include <limits.h>
#include <cstring>
#include <purefs/fs/thread_local_cwd.hpp>

#if configNUM_THREAD_LOCAL_STORAGE_POINTERS < 4
#error Not enough TLS pointers
#endif

namespace purefs::fs::internal
{
    namespace
    {
        constexpr auto CWD_THREAD_LOCAL_INDEX = 3;
        auto get_tls() noexcept -> char *
        {
            auto pcwd = reinterpret_cast<char *>(pvTaskGetThreadLocalStoragePointer(nullptr, CWD_THREAD_LOCAL_INDEX));
            if (pcwd == nullptr) {
                pcwd = new (std::nothrow) char[PATH_MAX + 1];
                if (pcwd) {
                    std::strncpy(pcwd, "/", PATH_MAX);
                    vTaskSetThreadLocalStoragePointer(nullptr, CWD_THREAD_LOCAL_INDEX, pcwd);
                }
            }
            return pcwd;
        }
    } // namespace
    auto get_thread_local_cwd_path() noexcept -> std::string_view
    {
        return get_tls();
    }
    auto set_thread_cwd_path(std::string_view path) noexcept -> int
    {
        if (path.size() > PATH_MAX) {
            return -ERANGE;
        }
        auto tls = get_tls();
        if (!tls) {
            return -ENOMEM;
        }
        else {
            std::memcpy(tls, path.data(), path.size());
            tls[path.size()] = '\0';
            return 0;
        }
    }
    auto cleanup_thread_local_cwd_mem() -> void
    {
        auto pcwd = reinterpret_cast<char *>(pvTaskGetThreadLocalStoragePointer(nullptr, CWD_THREAD_LOCAL_INDEX));
        if (pcwd) {
            delete[] pcwd;
            vTaskSetThreadLocalStoragePointer(nullptr, CWD_THREAD_LOCAL_INDEX, nullptr);
        }
    }
} // namespace purefs::fs::internal

M module-vfs/include/internal/purefs/fs/directory_handle.hpp => module-vfs/include/internal/purefs/fs/directory_handle.hpp +4 -2
@@ 10,8 10,10 @@ namespace purefs::fs::internal
    class directory_handle
    {
      public:
        directory_handle(int error, std::shared_ptr<mount_point> mount_point = nullptr)
            : m_error(error), m_mount_point(mount_point)
        {}
        directory_handle(const directory_handle &) = delete;
        virtual ~directory_handle()                = delete;
        auto operator=(const directory_handle &) = delete;
        auto error(int error) noexcept -> void
        {


@@ 24,6 26,6 @@ namespace purefs::fs::internal

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

M module-vfs/include/internal/purefs/fs/file_handle.hpp => module-vfs/include/internal/purefs/fs/file_handle.hpp +7 -1
@@ 14,6 14,8 @@ namespace purefs::fs::internal
      public:
        file_handle(const file_handle &) = delete;
        virtual ~file_handle()           = default;
        explicit file_handle(std::shared_ptr<mount_point> mp) : m_mount_point(mp)
        {}
        [[nodiscard]] auto error() const noexcept
        {
            return m_error;


@@ 30,9 32,13 @@ namespace purefs::fs::internal
        {
            return m_flags;
        }
        std::weak_ptr<mount_point> m_mount_point;
        [[nodiscard]] auto mntpoint() const noexcept
        {
            return m_mount_point;
        }

      private:
        const std::weak_ptr<mount_point> m_mount_point;
        int m_error{};
        int m_flags{};
    };

R module-vfs/include/internal/purefs/utils/handle_mapper.hpp => module-vfs/include/internal/purefs/fs/handle_mapper.hpp +2 -2
@@ 4,7 4,7 @@
#pragma once

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

    template <typename T> class handle_mapper


@@ 44,4 44,4 @@ namespace purefs::utils::internal
        data[result] = value;
        return result;
    }
} // namespace purefs::utils::internal
} // namespace purefs::fs::internal

M module-vfs/include/internal/purefs/fs/mount_point.hpp => module-vfs/include/internal/purefs/fs/mount_point.hpp +26 -8
@@ 5,25 5,43 @@
#include <memory>
#include <string>

namespace purefs::blkdev
namespace purefs::blkdev::internal
{
    class disk;
    class disk_handle;
}

namespace purefs::fs
{
    class filesystem_operation;
    class filesystem_operations;
}

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
      public:
        mount_point(std::shared_ptr<blkdev::internal::disk_handle> diskh,
                    std::string_view path,
                    std::shared_ptr<filesystem_operations> fs)
            : m_diskh(diskh), m_path(path), m_fs(fs)
        {}
        auto disk() const noexcept
        {
            return m_diskh;
        }
        auto mount_path() const noexcept
        {
            return m_path;
        }
        auto fs_ops() const noexcept
        {
            return m_fs;
        }

      private:
        const std::weak_ptr<blkdev::internal::disk_handle> m_diskh;
        const std::string_view m_path;                   //! Mounted path
        const std::weak_ptr<filesystem_operations> m_fs; //! Filesystem operation
    };
} // namespace purefs::fs::internal

A module-vfs/include/internal/purefs/fs/thread_local_cwd.hpp => module-vfs/include/internal/purefs/fs/thread_local_cwd.hpp +12 -0
@@ 0,0 1,12 @@
// 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>

namespace purefs::fs::internal
{
    auto get_thread_local_cwd_path() noexcept -> std::string_view;
    auto set_thread_cwd_path(std::string_view path) noexcept -> int;
    auto cleanup_thread_local_cwd_mem() -> void;
} // namespace purefs::fs::internal

M module-vfs/include/user/purefs/fs/filesystem.hpp => module-vfs/include/user/purefs/fs/filesystem.hpp +117 -9
@@ 6,18 6,29 @@
#include <memory>
#include <list>
#include <array>
#include <mutex.hpp>
#include <unordered_map>
#include <map>
#include <functional>
#include <ctime>
#include <purefs/fs/handle_mapper.hpp>
#include <purefs/fs/file_handle.hpp>
#include <purefs/fs/mount_point.hpp>

namespace purefs::fs
namespace purefs::blkdev
{
    class disk_manager;
}

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;
    class filesystem_operations;
    namespace internal
    {
        class directory_handle;


@@ 26,7 37,10 @@ namespace purefs::fs
    class filesystem
    {
      public:
        static constexpr auto path_separator = '/';
        using fsdir                    = std::shared_ptr<internal::directory_handle>;
        using fsfile                         = std::shared_ptr<internal::file_handle>;
        explicit filesystem(std::shared_ptr<blkdev::disk_manager> diskmm);
        filesystem(const filesystem &) = delete;
        auto operator=(const filesystem &) = delete;
        /** Utility API */


@@ 35,7 49,7 @@ namespace purefs::fs
         * @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;
        auto register_filesystem(std::string_view fsname, std::shared_ptr<filesystem_operations> fops) -> int;
        /** Unregister filesystem driver
         * @param[in] fsname Unique filesystem name for example fat
         * @return zero on success otherwise error


@@ 72,8 86,8 @@ namespace purefs::fs
        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 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;


@@ 91,13 105,107 @@ namespace purefs::fs
        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 flock(int fd, int cmd) noexcept -> int;
        auto isatty(int fd) 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 getcwd() noexcept -> std::string_view;
        auto chdir(std::string_view name) noexcept -> int;

      private:
        /** Find the mount point object matching to the mount point path
         * @param[in] path Absolute input path
         * @return mount point object and the matching shortest path
         */
        auto find_mount_point(std::string_view path) const noexcept
            -> std::tuple<std::shared_ptr<internal::mount_point>, size_t>;
        /** Return absolute path from the relative path
         * @param[in] path Unormalized path
         * @return Full Normalized path
         */
        static auto absolute_path(std::string_view path) noexcept -> std::string;
        /** Normalize full path
         * @param[in] path Unnormalized full path
         * @param[out] Normalized path
         */
        static auto normalize_path(std::string_view path) noexcept -> std::string;
        /** Add handle to file descriptor
         */
        auto add_filehandle(fsfile file) noexcept -> int;
        auto remove_filehandle(int fds) noexcept -> fsfile;
        auto find_filehandle(int fds) const noexcept -> fsfile;

      private:
        template <class Base, class T, typename... Args>
        inline auto invoke_fops(T Base::*method, int fds, Args &&... args)
            -> decltype((static_cast<Base *>(nullptr)->*method)(0, std::forward<Args>(args)...))
        {
            auto fil = find_filehandle(fds);
            if (!fil) {
                return -EBADF;
            }
            else {
                auto mp = fil->mntpoint().lock();
                if (!mp) {
                    return -ENOENT;
                }
                auto fsops = mp->fs_ops().lock();
                if (!fsops) {
                    return -EIO;
                }
                else {
                    return (fsops.get()->*method)(fil, std::forward<Args>(args)...);
                }
            }
        }

        template <class Base, class T, typename... Args>
        inline auto invoke_fops(T Base::*method, std::string_view path, Args &&... args) const
            -> decltype((static_cast<Base *>(nullptr)->*method)(nullptr, {}, std::forward<Args>(args)...))
        {
            const auto abspath     = absolute_path(path);
            auto [mountp, pathpos] = find_mount_point(abspath);
            if (!mountp) {
                return -ENOENT;
            }
            auto fsops = mountp->fs_ops().lock();
            if (fsops)
                return (fsops.get()->*method)(mountp, abspath, std::forward<Args>(args)...);
            else
                return -EIO;
        }

        template <class Base, class T, typename... Args>
        inline auto invoke_fops_same_mp(T Base::*method,
                                        std::string_view path,
                                        std::string_view path2,
                                        Args &&... args) const
            -> decltype((static_cast<Base *>(nullptr)->*method)(nullptr, {}, {}, std::forward<Args>(args)...))
        {
            const auto abspath     = absolute_path(path);
            const auto abspath2    = absolute_path(path2);
            auto [mountp, pathpos] = find_mount_point(abspath);
            if (!mountp) {
                return -ENOENT;
            }
            if (path.compare(0, pathpos, path2, 0, pathpos) != 0) {
                // Mount points are not the same
                return -EXDEV;
            }
            auto fsops = mountp->fs_ops().lock();
            if (fsops)
                return (fsops.get()->*method)(mountp, abspath, abspath2, std::forward<Args>(args)...);
            else
                return -EIO;
        }

      private:
        std::weak_ptr<blkdev::disk_manager> m_diskmm;
        std::unordered_map<std::string, std::shared_ptr<filesystem_operations>> m_fstypes;
        std::map<std::string, std::shared_ptr<internal::mount_point>> m_mounts;
        internal::handle_mapper<fsfile> m_fds;
        mutable cpp_freertos::MutexRecursive m_lock;
    };
} // namespace purefs::fs

D module-vfs/include/user/purefs/fs/filesystem_operation.hpp => module-vfs/include/user/purefs/fs/filesystem_operation.hpp +0 -69
@@ 1,69 0,0 @@
// 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/include/user/purefs/fs/filesystem_operations.hpp => module-vfs/include/user/purefs/fs/filesystem_operations.hpp +84 -0
@@ 0,0 1,84 @@
// 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::blkdev::internal
{
    class disk_handle;
}

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_operations
    {
      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_operations(const filesystem_operations &) = delete;
        virtual ~filesystem_operations()                     = default;
        auto operator=(const filesystem_operations &) = delete;
        /** Allocate mount point class specify to the VFS
         * @return Allocated mount point structure
         */
        virtual auto mount_prealloc(std::shared_ptr<blkdev::internal::disk_handle> diskh, std::string_view path)
            -> fsmount                                   = 0;
        virtual auto mount(fsmount mnt) noexcept -> int  = 0;
        virtual auto umount(fsmount mnt) noexcept -> int = 0;
        virtual auto stat_vfs(fsmount mnt, std::string_view path, statvfs &stat) const noexcept -> int;

        /** 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;
        virtual auto fstat(fsfile zfile, struct stat &st) noexcept -> int;
        virtual auto stat(fsmount mnt, std::string_view file, struct stat &st) noexcept -> int;
        virtual auto link(fsmount mnt, std::string_view existing, std::string_view newlink) noexcept -> int;
        virtual auto symlink(fsmount mnt, std::string_view existing, std::string_view newlink) noexcept -> int;
        virtual auto unlink(fsmount mnt, std::string_view name) noexcept -> int;
        virtual auto rename(fsmount mnt, std::string_view oldname, std::string_view newname) noexcept -> int;
        virtual auto mkdir(fsmount mnt, std::string_view path, int mode) noexcept -> int;

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

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

        virtual auto chmod(fsmount mnt, std::string_view path, mode_t mode) noexcept -> int;
        virtual auto fchmod(fsfile zfile, mode_t mode) noexcept -> int;

        /** Internal use */
        auto mount_count() const noexcept
        {
            return m_mount_count;
        }

      private:
        std::size_t m_mount_count{};
    };
} // namespace purefs::fs

A module-vfs/src/purefs/fs/filesystem.cpp => module-vfs/src/purefs/fs/filesystem.cpp +214 -0
@@ 0,0 1,214 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <purefs/fs/filesystem.hpp>
#include <purefs/fs/filesystem_operations.hpp>
#include <purefs/fs/mount_point.hpp>
#include <purefs/blkdev/disk_manager.hpp>
#include <purefs/fs/thread_local_cwd.hpp>
#include <log/log.hpp>
#include <split_sv.hpp>
#include <errno.h>

namespace purefs::fs
{
    filesystem::filesystem(std::shared_ptr<blkdev::disk_manager> diskmm) : m_diskmm(diskmm)
    {}

    auto filesystem::register_filesystem(std::string_view fsname, std::shared_ptr<filesystem_operations> fops) -> int
    {
        cpp_freertos::LockGuard _lck(m_lock);
        const auto ret = m_fstypes.find(std::string(fsname));
        if (ret != std::end(m_fstypes)) {
            LOG_ERROR("Disc: %.*s already registered.", int(fsname.length()), fsname.data());
            return -EEXIST;
        }
        else {
            m_fstypes.emplace(std::make_pair(fsname, fops));
            return {};
        }
    }

    auto filesystem::unregister_filesystem(std::string_view fsname) -> int
    {
        cpp_freertos::LockGuard _lck(m_lock);
        const auto it = m_fstypes.find(std::string(fsname));
        if (it == std::end(m_fstypes)) {
            LOG_ERROR("VFS: filesystem %.*s doesn't exists in manager.", int(fsname.length()), fsname.data());
            return -ENOENT;
        }
        if (it->second->mount_count() > 0) {
            LOG_ERROR("VFS: fileystem %.*s  is already used", int(fsname.length()), fsname.data());
            return -EBUSY;
        }
        m_fstypes.erase(it);
        return {};
    }

    auto filesystem::mount(std::string_view dev_or_part,
                           std::string_view target,
                           std::string_view fs_type,
                           unsigned flags) -> int
    {
        // Sanity check input data
        if (target.size() <= 1 || target[0] != '/') {
            LOG_ERROR("VFS: Invalid target mountpoint path %.*s", int(target.length()), target.data());
            return -EINVAL;
        }
        {
            cpp_freertos::LockGuard _lock(m_lock);
            const auto mpi = m_mounts.find(std::string(target));
            if (mpi != std::end(m_mounts)) {
                LOG_ERROR("VFS: mount point already exists %.*s", int(target.length()), target.data());
                return -EBUSY;
            }
            const auto vsi = m_fstypes.find(std::string(fs_type));
            if (vsi == std::end(m_fstypes)) {
                LOG_ERROR("VFS: requested filesystem %.*s not registered", int(fs_type.length()), fs_type.data());
            }
            // Trying to open disk or part by manager
            blkdev::disk_fd_t diskh;
            {
                auto disk_mgr = m_diskmm.lock();
                if (disk_mgr) {
                    diskh = disk_mgr->device_handle(dev_or_part);
                }
                else {
                    LOG_ERROR("VFS: Unable to lock device manager");
                    return -EIO;
                }
            }
            if (diskh) {
                const auto mnt_point = vsi->second->mount_prealloc(diskh, target);
                const auto ret_mnt   = vsi->second->mount(mnt_point);
                if (!ret_mnt)
                    m_mounts.emplace(std::make_pair(target, mnt_point));
                else
                    return ret_mnt;
            }
        }
        return {};
    }

    auto filesystem::umount(std::string_view mount_point) -> int
    {
        cpp_freertos::LockGuard _lck(m_lock);
        auto mnti = m_mounts.find(std::string(mount_point));
        if (mnti == std::end(m_mounts)) {
            return -ENOENT;
        }
        auto fsops = mnti->second->fs_ops().lock();
        if (!fsops) {
            LOG_ERROR("Unable to lock filesystem operation");
            return -EIO;
        }
        const auto umnt_ret = fsops->umount(mnti->second);
        if (umnt_ret) {
            return umnt_ret;
        }
        m_mounts.erase(mnti);
        return {};
    }

    auto filesystem::read_mountpoints(std::list<std::string> &mountpoints) const -> int
    {
        cpp_freertos::LockGuard _lck(m_lock);
        for (const auto &mntp : m_mounts) {
            mountpoints.push_back(mntp.first);
        }
        return {};
    }

    auto filesystem::find_mount_point(std::string_view path) const noexcept
        -> std::tuple<std::shared_ptr<internal::mount_point>, size_t>
    {
        size_t longest_match{};
        std::shared_ptr<internal::mount_point> mount_pnt;
        cpp_freertos::LockGuard _lck(m_lock);
        for (const auto &mntp : m_mounts) {
            const auto slen = mntp.first.size();
            if ((slen < longest_match) || (slen > path.size())) {
                continue;
            }
            if ((slen > 1) && (path[slen] != '/') && (path[slen] != '\0')) {
                continue;
            }
            if (path.compare(0, slen, mntp.first) == 0) {
                mount_pnt     = mntp.second;
                longest_match = slen;
            }
        }
        return std::make_tuple(mount_pnt, longest_match);
    }

    auto filesystem::absolute_path(std::string_view path) noexcept -> std::string
    {
        std::string ret{};
        if (!path.empty() && path[0] == path_separator) {
            ret.append(path);
        }
        else {
            ret.append(internal::get_thread_local_cwd_path()).append("/").append(path);
        }
        return normalize_path(ret);
    }

    auto filesystem::normalize_path(std::string_view path) noexcept -> std::string
    {
        std::string ret;
        auto paths = utils::split<std::list, std::string_view>(path, "/");
        for (auto it = std::begin(paths); it != std::end(paths);) {
            if (!it->compare(".")) {
                it = paths.erase(it);
            }
            else if (!it->compare("..")) {
                auto pit = it;
                --pit;
                if (pit != std::end(paths)) {
                    it = paths.erase(pit);
                    if (it != std::end(paths))
                        it = paths.erase(it);
                }
                else {
                    it = paths.erase(it);
                }
            }
            else {
                ++it;
            }
        }
        for (auto ep : paths) {
            if (!ep.empty()) {
                ret.append("/").append(ep);
            }
        }
        if (ret.empty())
            ret.append("/");
        return ret;
    }

    auto filesystem::add_filehandle(fsfile file) noexcept -> int
    {
        cpp_freertos::LockGuard _lck(m_lock);
        return m_fds.insert(file);
    }

    auto filesystem::remove_filehandle(int fds) noexcept -> fsfile
    {
        cpp_freertos::LockGuard _lck(m_lock);
        fsfile ret{};
        if (m_fds.exists(fds)) {
            ret = m_fds[fds];
            m_fds.remove(fds);
        }
        return ret;
    }
    auto filesystem::find_filehandle(int fds) const noexcept -> fsfile
    {
        fsfile ret{};
        cpp_freertos::LockGuard _lck(m_lock);
        if (m_fds.exists(fds)) {
            ret = m_fds[fds];
        }
        return ret;
    }
} // namespace purefs::fs

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

#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <purefs/fs/filesystem.hpp>
#include <purefs/fs/thread_local_cwd.hpp>
#include <unistd.h>

namespace purefs::fs
{
    auto filesystem::getcwd() noexcept -> std::string_view
    {
        return internal::get_thread_local_cwd_path();
    }
    auto filesystem::chdir(std::string_view name) noexcept -> int
    {
        struct stat sts;
        int ret;
        ret = this->stat(name, sts);
        if (ret) {
            return ret;
        }
        if ((sts.st_mode & S_IFMT) != S_IFDIR) {
            return -ENOTDIR;
        }
        const auto abspath = absolute_path(name);
        ret                = internal::set_thread_cwd_path(abspath);
        return ret;
    }
} // namespace purefs::fs

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

#include <errno.h>

namespace purefs::fs
{

    auto filesystem_operations::mount(fsmount mnt) noexcept -> int
    {
        ++m_mount_count;
        return -ENOTSUP;
    }
    auto filesystem_operations::umount(fsmount mnt) noexcept -> int
    {
        if (m_mount_count > 0) {
            --m_mount_count;
        }
        return -ENOTSUP;
    }
    auto filesystem_operations::stat_vfs(fsmount mntp, std::string_view path, statvfs &stat) const noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::seek(fsfile zfile, off_t pos, int dir) noexcept -> off_t
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::fstat(fsfile zfile, struct stat &st) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::stat(fsmount mnt, std::string_view file, struct stat &st) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::link(fsmount mnt, std::string_view existing, std::string_view newlink) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::symlink(fsmount mnt, std::string_view existing, std::string_view newlink) noexcept
        -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::unlink(fsmount mnt, std::string_view name) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::rename(fsmount mnt, std::string_view oldname, std::string_view newname) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::mkdir(fsmount mnt, std::string_view path, int mode) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::diropen(fsmount mnt, std::string_view path) noexcept -> fsdir
    {
        return std::make_shared<internal::directory_handle>(-ENOTSUP);
    }
    auto filesystem_operations::dirreset(fsdir dirstate) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::dirnext(fsdir dirstate, std::string &filename, struct stat &filestat) -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::dirclose(fsdir dirstate) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::ftruncate(fsfile zfile, off_t len) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::fsync(fsfile zfile) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::ioctl(fsmount mnt, std::string_view path, int cmd, void *arg) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::utimens(fsmount mnt, std::string_view path, std::array<timespec, 2> &tv) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::flock(fsfile zfile, int cmd) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::chmod(fsmount mnt, std::string_view path, mode_t mode) noexcept -> int
    {
        return -ENOTSUP;
    }
    auto filesystem_operations::fchmod(fsfile zfile, mode_t mode) noexcept -> int
    {
        return -ENOTSUP;
    }
} // namespace purefs::fs

A module-vfs/src/purefs/fs/filesystem_syscalls.cpp => module-vfs/src/purefs/fs/filesystem_syscalls.cpp +139 -0
@@ 0,0 1,139 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <purefs/fs/filesystem.hpp>
#include <errno.h>
#include <log/log.hpp>
#include <purefs/fs/mount_point.hpp>
#include <purefs/fs/filesystem_operations.hpp>
#include <purefs/fs/file_handle.hpp>
#include <purefs/fs/thread_local_cwd.hpp>

namespace purefs::fs
{
    auto filesystem::stat_vfs(std::string_view path, statvfs &stat) const noexcept -> int
    {
        return invoke_fops(&filesystem_operations::stat_vfs, path, stat);
    }

    auto filesystem::stat(std::string_view file, struct stat &st) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::stat, file, st);
    }

    auto filesystem::unlink(std::string_view name) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::unlink, name);
    }

    auto filesystem::mkdir(std::string_view path, int mode) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::mkdir, path, mode);
    }

    auto filesystem::ioctl(std::string_view path, int cmd, void *arg) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::ioctl, path, cmd, arg);
    }

    auto filesystem::utimens(std::string_view path, std::array<timespec, 2> &tv) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::utimens, path, tv);
    }

    auto filesystem::flock(int fd, int cmd) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::flock, fd, cmd);
    }

    auto filesystem::isatty(int fd) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::isatty, fd);
    }

    auto filesystem::chmod(std::string_view path, mode_t mode) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::chmod, path, mode);
    }

    auto filesystem::close(int fd) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::close, fd);
    }

    auto filesystem::write(int fd, const char *ptr, size_t len) noexcept -> ssize_t
    {
        return invoke_fops(&filesystem_operations::write, fd, ptr, len);
    }

    auto filesystem::read(int fd, char *ptr, size_t len) noexcept -> ssize_t
    {
        return invoke_fops(&filesystem_operations::read, fd, ptr, len);
    }

    auto filesystem::seek(int fd, off_t pos, int dir) noexcept -> off_t
    {
        return invoke_fops(&filesystem_operations::seek, fd, pos, dir);
    }

    auto filesystem::fstat(int fd, struct stat &st) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::fstat, fd, st);
    }

    auto filesystem::ftruncate(int fd, off_t len) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::ftruncate, fd, len);
    }

    auto filesystem::fsync(int fd) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::fsync, fd);
    }

    auto filesystem::fchmod(int fd, mode_t mode) noexcept -> int
    {
        return invoke_fops(&filesystem_operations::fchmod, fd, mode);
    }

    auto filesystem::symlink(std::string_view existing, std::string_view newlink) noexcept -> int
    {
        return invoke_fops_same_mp(&filesystem_operations::symlink, existing, newlink);
    }

    auto filesystem::link(std::string_view existing, std::string_view newlink) noexcept -> int
    {
        return invoke_fops_same_mp(&filesystem_operations::link, existing, newlink);
    }

    auto filesystem::rename(std::string_view oldname, std::string_view newname) noexcept -> int
    {
        return invoke_fops_same_mp(&filesystem_operations::rename, oldname, newname);
    }

    auto filesystem::open(std::string_view path, int flags, int mode) noexcept -> int
    {
        const auto abspath     = absolute_path(path);
        auto [mountp, pathpos] = find_mount_point(abspath);
        if (!mountp) {
            LOG_ERROR("VFS: Unable to find mount point");
            return -ENOENT;
        }
        auto fsops = mountp->fs_ops().lock();
        if (fsops) {
            auto fh = fsops->open(mountp, abspath, flags, mode);
            if (!fh) {
                LOG_ERROR("VFS: Unable to get fops");
                return -EBADF;
            }
            const auto err = fh->error();
            if (err) {
                return err;
            }
            return add_filehandle(fh);
        }
        else {
            LOG_ERROR("VFS: Unable to lock fops");
            return -EIO;
        }
    }
} // namespace purefs::fs

M module-vfs/targets/Target_Linux.cmake => module-vfs/targets/Target_Linux.cmake +1 -0
@@ 3,6 3,7 @@ set(BOARD_SOURCES
        ${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
        ${CMAKE_CURRENT_SOURCE_DIR}/board/linux/purefs/src/fs/thread_local_cwd.cpp
        CACHE INTERNAL ""
)


M module-vfs/targets/Target_RT1051.cmake => module-vfs/targets/Target_RT1051.cmake +4 -3
@@ 2,12 2,13 @@ set(BOARD_SOURCES ${BOARD_SOURCES}
        src/newlib/vfs_io_syscalls.cpp
        src/newlib/vfs_internal_dirent.cpp
        src/newlib/HandleManager.cpp
        board/rt1051/purefs/src/fs/thread_local_cwd.cpp
        CACHE INTERNAL ""
)

set(BOARD_DIR_INCLUDES ${BOARD_DIR_INCLUDES}


        ${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051 CACHE INTERNAL "")
    ${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051
    CACHE INTERNAL ""
)