From 5abfa01ccd920ba6d3dfe9d25ae199c14987f719 Mon Sep 17 00:00:00 2001 From: Marek Ledworowski Date: Sun, 9 Nov 2025 23:40:56 +0100 Subject: [PATCH] Implemented dirents (normal) Minor movements --- CMakeLists.txt | 1 + src/core/file_sys/devices/console_device.h | 2 +- src/core/file_sys/devices/deci_tty6_device.h | 2 +- src/core/file_sys/devices/logger.h | 2 +- src/core/file_sys/devices/nop_device.h | 2 +- src/core/file_sys/devices/null_device.h | 2 +- src/core/file_sys/devices/random_device.h | 2 +- src/core/file_sys/devices/rng_device.h | 2 +- src/core/file_sys/devices/srandom_device.h | 2 +- src/core/file_sys/devices/zero_device.h | 2 +- .../file_sys/directories/normal_directory.h | 4 +- src/core/file_sys/hostio/host_io_virtual.h | 2 +- src/core/file_sys/hostio/src/host_io_base.cpp | 4 +- src/core/file_sys/hostio/src/host_io_base.h | 2 +- .../file_sys/hostio/src/host_io_posix.cpp | 2 +- .../file_sys/hostio/src/host_io_virtual.cpp | 6 +- src/core/file_sys/quasifs/quasi_sys_fcntl.h | 5 +- src/core/file_sys/quasifs/quasi_types.h | 4 +- src/core/file_sys/quasifs/quasifs.h | 2 +- src/core/file_sys/quasifs/quasifs_inode.h | 2 +- .../quasifs/quasifs_inode_quasi_directory.h | 39 ++++----- .../quasifs_inode_quasi_directory_pfs.h | 18 ++-- src/core/file_sys/quasifs/quasifs_partition.h | 17 ++-- src/core/file_sys/quasifs/src/quasifs.cpp | 6 +- .../src/quasifs_inode_quasi_directory.cpp | 77 ++++++++++++++++- .../src/quasifs_inode_quasi_directory_pfs.cpp | 84 +++++++++++++++++++ .../quasifs/src/quasifs_partition.cpp | 20 ++--- .../file_sys/quasifs/src/quasifs_vdriver.cpp | 4 +- .../libraries/app_content/app_content.cpp | 12 ++- src/core/libraries/kernel/file_system.cpp | 5 +- .../libraries/save_data/save_instance.cpp | 8 +- src/emulator.cpp | 22 ++--- 32 files changed, 263 insertions(+), 101 deletions(-) create mode 100644 src/core/file_sys/quasifs/src/quasifs_inode_quasi_directory_pfs.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 64577343c..ffd5524ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -659,6 +659,7 @@ set(QUASIFS src/core/file_sys/quasifs/src/quasifs.cpp src/core/file_sys/quasifs/src/quasifs_vdriver.cpp src/core/file_sys/quasifs/src/quasifs_inode_quasi_device.cpp src/core/file_sys/quasifs/src/quasifs_inode_quasi_directory.cpp + src/core/file_sys/quasifs/src/quasifs_inode_quasi_directory_pfs.cpp src/core/file_sys/quasifs/src/quasifs_inode_quasi_file.cpp src/core/file_sys/quasifs/src/quasifs_inode_quasi_socket.cpp src/core/file_sys/quasifs/src/quasifs_inode_quasi_file_virtual.cpp diff --git a/src/core/file_sys/devices/console_device.h b/src/core/file_sys/devices/console_device.h index b5b2414e6..e83aa59a6 100644 --- a/src/core/file_sys/devices/console_device.h +++ b/src/core/file_sys/devices/console_device.h @@ -30,7 +30,7 @@ public: s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); }; s32 fsync() override { DEVICE_STUB(); }; s32 ftruncate(s64 length) override { DEVICE_STUB(); }; - s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); }; + s64 getdents(void* buf, u32 nbytes, s64 offset, s64* basep) override { DEVICE_STUB(); }; // clang-format on }; diff --git a/src/core/file_sys/devices/deci_tty6_device.h b/src/core/file_sys/devices/deci_tty6_device.h index 5df077430..c488be716 100644 --- a/src/core/file_sys/devices/deci_tty6_device.h +++ b/src/core/file_sys/devices/deci_tty6_device.h @@ -30,7 +30,7 @@ public: s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); }; s32 fsync() override { DEVICE_STUB(); }; s32 ftruncate(s64 length) override { DEVICE_STUB(); }; - s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); }; + s64 getdents(void* buf, u32 nbytes, s64 offset, s64* basep) override { DEVICE_STUB(); }; // clang-format on }; diff --git a/src/core/file_sys/devices/logger.h b/src/core/file_sys/devices/logger.h index 9399f2d93..cf0d3dca7 100644 --- a/src/core/file_sys/devices/logger.h +++ b/src/core/file_sys/devices/logger.h @@ -39,7 +39,7 @@ public: s64 lseek(s64 offset, int whence) override { DEVICE_STUB(); } s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); } s32 ftruncate(s64 length) override { DEVICE_STUB(); } - s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); } + s64 getdents(void* buf, u32 nbytes, s64 offset, s64* basep) override { DEVICE_STUB(); } // clang-format on private: diff --git a/src/core/file_sys/devices/nop_device.h b/src/core/file_sys/devices/nop_device.h index 249112d3a..d43050457 100644 --- a/src/core/file_sys/devices/nop_device.h +++ b/src/core/file_sys/devices/nop_device.h @@ -23,7 +23,7 @@ public: s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { return 0; }; s32 fsync() override { return 0; }; s32 ftruncate(s64 length) override { return 0; }; - s64 getdents(void* buf, u32 nbytes, s64* basep) override { return 0; }; + s64 getdents(void* buf, u32 nbytes, s64 offset, s64* basep) override { return 0; }; // clang-format on }; diff --git a/src/core/file_sys/devices/null_device.h b/src/core/file_sys/devices/null_device.h index 502fd43ba..cde48a13c 100644 --- a/src/core/file_sys/devices/null_device.h +++ b/src/core/file_sys/devices/null_device.h @@ -30,7 +30,7 @@ public: s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); } s32 fsync() override { DEVICE_STUB(); } s32 ftruncate(s64 length) override { DEVICE_STUB(); } - s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); } + s64 getdents(void* buf, u32 nbytes, s64 offset, s64* basep) override { DEVICE_STUB(); } // clang-format on }; diff --git a/src/core/file_sys/devices/random_device.h b/src/core/file_sys/devices/random_device.h index f89c18f55..efe8ed804 100644 --- a/src/core/file_sys/devices/random_device.h +++ b/src/core/file_sys/devices/random_device.h @@ -29,7 +29,7 @@ public: s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); } s32 fsync() override { DEVICE_STUB(); } s32 ftruncate(s64 length) override { DEVICE_STUB(); } - s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); } + s64 getdents(void* buf, u32 nbytes, s64 offset, s64* basep) override { DEVICE_STUB(); } // clang-format on }; diff --git a/src/core/file_sys/devices/rng_device.h b/src/core/file_sys/devices/rng_device.h index 2a9276b1d..edaf69c2a 100644 --- a/src/core/file_sys/devices/rng_device.h +++ b/src/core/file_sys/devices/rng_device.h @@ -30,7 +30,7 @@ public: s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); } s32 fsync() override { DEVICE_STUB(); } s32 ftruncate(s64 length) override { DEVICE_STUB(); } - s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); } + s64 getdents(void* buf, u32 nbytes, s64 offset, s64* basep) override { DEVICE_STUB(); } // clang-format on private: diff --git a/src/core/file_sys/devices/srandom_device.h b/src/core/file_sys/devices/srandom_device.h index 6ef38e21d..a4b72abe1 100644 --- a/src/core/file_sys/devices/srandom_device.h +++ b/src/core/file_sys/devices/srandom_device.h @@ -30,7 +30,7 @@ public: s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); } s32 fsync() override { DEVICE_STUB(); } s32 ftruncate(s64 length) override { DEVICE_STUB(); } - s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); } + s64 getdents(void* buf, u32 nbytes, s64 offset, s64* basep) override { DEVICE_STUB(); } // clang-format on }; diff --git a/src/core/file_sys/devices/zero_device.h b/src/core/file_sys/devices/zero_device.h index 483249ff9..1eb9031e7 100644 --- a/src/core/file_sys/devices/zero_device.h +++ b/src/core/file_sys/devices/zero_device.h @@ -30,7 +30,7 @@ public: s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); } s32 fsync() override { DEVICE_STUB(); } s32 ftruncate(s64 length) override { DEVICE_STUB(); } - s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); } + s64 getdents(void* buf, u32 nbytes, s64 offset, s64* basep) override { DEVICE_STUB(); } // clang-format on }; diff --git a/src/core/file_sys/directories/normal_directory.h b/src/core/file_sys/directories/normal_directory.h index 70e52f581..0cd8a8f8d 100644 --- a/src/core/file_sys/directories/normal_directory.h +++ b/src/core/file_sys/directories/normal_directory.h @@ -18,13 +18,13 @@ public: explicit NormalDirectory(std::string_view guest_path); ~NormalDirectory() override = default; - virtual s64 read(void* buf, u64 nbytes) override; + virtual s64 read(void* buf, u64 count) override; virtual s64 readv(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt) override; virtual s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt, s64 offset) override; virtual s64 lseek(s64 offset, s32 whence) override; virtual s32 fstat(Libraries::Kernel::OrbisKernelStat* stat) override; - virtual s64 getdents(void* buf, u64 nbytes, s64* basep) override; + virtual s64 getdents(void* buf, u64 count, s64* basep) override; private: static constexpr s32 MAX_LENGTH = 255; diff --git a/src/core/file_sys/hostio/host_io_virtual.h b/src/core/file_sys/hostio/host_io_virtual.h index 933eea103..c725d66dd 100644 --- a/src/core/file_sys/hostio/host_io_virtual.h +++ b/src/core/file_sys/hostio/host_io_virtual.h @@ -80,7 +80,7 @@ public: s32 Chmod(const fs::path& path, u16 mode) override; s32 FChmod(const s32 fd, u16 mode) override; - s64 GetDents(s32 fd, void* buf, u32 nbytes, s64* basep) override; + s64 GetDents(const s32 fd, void* buf, u64 count, s64* basep) override; // // Derived, complex functions are to be handled by main FS class // diff --git a/src/core/file_sys/hostio/src/host_io_base.cpp b/src/core/file_sys/hostio/src/host_io_base.cpp index c19e33a8f..02b6092e6 100644 --- a/src/core/file_sys/hostio/src/host_io_base.cpp +++ b/src/core/file_sys/hostio/src/host_io_base.cpp @@ -53,9 +53,7 @@ s32 HostIO_Base::FStat(const s32 fd, OrbisKernelStat* statbuf) { STUB(); } s32 HostIO_Base::Chmod(const fs::path& path, u16 mode) { STUB(); } s32 HostIO_Base::FChmod(const s32 fd, u16 mode) { STUB(); } -s64 HostIO_Base::GetDents(s32 fd, void* buf, u32 nbytes, s64* basep) { - STUB() -} +s64 HostIO_Base::GetDents(const s32 fd, void* buf, u64 count, s64* basep) { STUB(); } // clang-format on } // namespace HostIODriver \ No newline at end of file diff --git a/src/core/file_sys/hostio/src/host_io_base.h b/src/core/file_sys/hostio/src/host_io_base.h index c5bb24c6b..639e1a93d 100644 --- a/src/core/file_sys/hostio/src/host_io_base.h +++ b/src/core/file_sys/hostio/src/host_io_base.h @@ -56,7 +56,7 @@ public: virtual s32 Chmod(const fs::path& path, u16 mode); virtual s32 FChmod(const s32 fd, u16 mode); - virtual s64 GetDents(s32 fd, void* buf, u32 nbytes, s64* basep); + virtual s64 GetDents(const s32 fd, void* buf, u64 count, s64* basep); // // Derived, complex functions are to be handled by main FS class // diff --git a/src/core/file_sys/hostio/src/host_io_posix.cpp b/src/core/file_sys/hostio/src/host_io_posix.cpp index 81d751777..71aab581e 100644 --- a/src/core/file_sys/hostio/src/host_io_posix.cpp +++ b/src/core/file_sys/hostio/src/host_io_posix.cpp @@ -259,6 +259,6 @@ s32 HostIO_POSIX::FChmod(const s32 fd, u16 mode) { return 0 == status ? status : -errno; } -// s64 HostIO_POSIX::GetDents(void* buf, u32 nbytes, s64* basep) { return -QUASI_ENOSYS; } +// s32 HostIO_POSIX::GetDents(void* buf, u32 count, s64* basep) { return -QUASI_ENOSYS; } } // namespace HostIODriver \ No newline at end of file diff --git a/src/core/file_sys/hostio/src/host_io_virtual.cpp b/src/core/file_sys/hostio/src/host_io_virtual.cpp index ca73793ce..47108f894 100644 --- a/src/core/file_sys/hostio/src/host_io_virtual.cpp +++ b/src/core/file_sys/hostio/src/host_io_virtual.cpp @@ -380,7 +380,7 @@ s32 HostIO_Virtual::FChmod(const s32 fd, u16 mode) { return Partition::chmod(node, mode); } -s64 HostIO_Virtual::GetDents(s32 fd, void* buf, u32 nbytes, s64* basep) { +s64 HostIO_Virtual::GetDents(const s32 fd, void* buf, u64 count, s64* basep) { if (nullptr == this->handle) return -QUASI_EINVAL; @@ -389,10 +389,8 @@ s64 HostIO_Virtual::GetDents(s32 fd, void* buf, u32 nbytes, s64* basep) { if (nullptr == node) return -QUASI_EBADF; - if (nullptr != basep) - *basep = handle->pos; + s64 br = node->getdents(buf, count, handle->pos, basep); - s64 br = node->getdents(static_cast(buf) + handle->pos, nbytes, basep); if (br > 0) handle->pos += br; diff --git a/src/core/file_sys/quasifs/quasi_sys_fcntl.h b/src/core/file_sys/quasifs/quasi_sys_fcntl.h index 56ca563eb..d5bc8e134 100644 --- a/src/core/file_sys/quasifs/quasi_sys_fcntl.h +++ b/src/core/file_sys/quasifs/quasi_sys_fcntl.h @@ -20,4 +20,7 @@ #define __QUASI_O_ALLFLAGS_AT_ONCE \ (QUASI_O_RDONLY | QUASI_O_WRONLY | QUASI_O_RDWR | QUASI_O_NONBLOCK | QUASI_O_APPEND | \ QUASI_O_FSYNC | QUASI_O_SYNC | QUASI_O_CREAT | QUASI_O_TRUNC | QUASI_O_EXCL | QUASI_O_DSYNC | \ - QUASI_O_DIRECT | QUASI_O_DIRECTORY) \ No newline at end of file + QUASI_O_DIRECT | QUASI_O_DIRECTORY) + +// not implemented +#define QUASI_O_CLOEXEC 0x100000 diff --git a/src/core/file_sys/quasifs/quasi_types.h b/src/core/file_sys/quasifs/quasi_types.h index a7c1fc5e8..0faa524f1 100644 --- a/src/core/file_sys/quasifs/quasi_types.h +++ b/src/core/file_sys/quasifs/quasi_types.h @@ -58,12 +58,12 @@ using fd_handle_ptr = std::shared_ptr; struct File { File() = default; ~File() = default; - int host_fd{-1}; // fd if opened with HostIO + s32 host_fd{-1}; // fd if opened with HostIO inode_ptr node{nullptr}; // inode bool read{false}; // read permission bool write{false}; // write permission bool append{false}; // append - u64 pos{0}; // cursor offset + s64 pos{0}; // cursor offset static fd_handle_ptr Create() { return std::shared_ptr(new File()); diff --git a/src/core/file_sys/quasifs/quasifs.h b/src/core/file_sys/quasifs/quasifs.h index 867e9a0f8..0b4c21c8b 100644 --- a/src/core/file_sys/quasifs/quasifs.h +++ b/src/core/file_sys/quasifs/quasifs.h @@ -96,7 +96,7 @@ private: s32 Chmod(const fs::path& path, u16 mode) override; s32 FChmod(const s32 fd, u16 mode) override; - s64 GetDents(s32 fd, void* buf, u32 nbytes, s64* basep) override; + s64 GetDents(const s32 fd, void* buf, u64 count, s64* basep) override; }; public: diff --git a/src/core/file_sys/quasifs/quasifs_inode.h b/src/core/file_sys/quasifs/quasifs_inode.h index 8136b0bca..a0f41f0e3 100644 --- a/src/core/file_sys/quasifs/quasifs_inode.h +++ b/src/core/file_sys/quasifs/quasifs_inode.h @@ -89,7 +89,7 @@ public: return -QUASI_EINVAL; } - virtual s64 getdents(void* buf, u32 nbytes, s64* basep) { + virtual s64 getdents(void* buf, u32 count, s64 offset, s64* basep) { return -QUASI_EINVAL; } diff --git a/src/core/file_sys/quasifs/quasifs_inode_quasi_directory.h b/src/core/file_sys/quasifs/quasifs_inode_quasi_directory.h index 0403d4998..8ca6d98e6 100644 --- a/src/core/file_sys/quasifs/quasifs_inode_quasi_directory.h +++ b/src/core/file_sys/quasifs/quasifs_inode_quasi_directory.h @@ -7,6 +7,7 @@ #include #include "common/assert.h" +#include "common/logging/log.h" #include "quasi_types.h" #include "quasifs_inode.h" @@ -16,23 +17,21 @@ namespace QuasiFS { // Directory class QuasiDirectory : public Inode { -private: - std::map entries{}; - - time_t last_dirent_rebuild_time{0}; - void RebuildDirents(void); - static constexpr s32 MAX_LENGTH = 255; - static constexpr s64 DIRECTORY_ALIGNMENT = 0x200; - +protected: #pragma pack(push, 1) typedef struct dirent_t { - quasi_ino_t d_ino{}; - u64 d_off{}; - unsigned short d_reclen{}; - unsigned char d_type{}; - char d_name[256]{}; + u32 d_fileno; + u16 d_reclen; + u8 d_type; + u8 d_namlen; + char d_name[256]; } dirent_t; #pragma pack(pop) + std::map entries{}; + + void RebuildDirents(void); + time_t last_dirent_rebuild_time{0}; + std::map dirent_cache{}; public: dir_ptr mounted_root = nullptr; @@ -58,17 +57,13 @@ public: // Inode overrides // - // s64 pread(void* buf, size_t count, u64 offset) override; + s64 pread(void* buf, u64 count, s64 offset) override; // s64 pwrite(const void* buf, size_t count, u64 offset) override; - s32 ftruncate(s64 length) final override { - return -QUASI_EISDIR; - } - s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { - this->st.st_size = entries.size() * 32; - *sb = st; - return 0; - } + s32 ftruncate(s64 length) final override; + s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override; + + s64 getdents(void* buf, u32 count, s64 offset, s64* basep) override; // // Dir-specific diff --git a/src/core/file_sys/quasifs/quasifs_inode_quasi_directory_pfs.h b/src/core/file_sys/quasifs/quasifs_inode_quasi_directory_pfs.h index 3391f5121..68c49765b 100644 --- a/src/core/file_sys/quasifs/quasifs_inode_quasi_directory_pfs.h +++ b/src/core/file_sys/quasifs/quasifs_inode_quasi_directory_pfs.h @@ -16,23 +16,29 @@ namespace QuasiFS { // Directory class DirectoryPFS final : public QuasiDirectory { -private: - static constexpr s32 MAX_LENGTH = 255; - static constexpr s32 DIRECTORY_ALIGNMENT = 0x10000; +protected: #pragma pack(push, 1) typedef struct dirent_pfs_t { u32 d_fileno; u32 d_type; u32 d_namlen; u32 d_reclen; - char d_name[MAX_LENGTH + 1]; + char d_name[256]; } dirent_pfs_t; #pragma pack(pop) +private: + // void RebuildDirents(void); + public: - DirectoryPFS() = default; - ~DirectoryPFS() = default; + DirectoryPFS(); + ~DirectoryPFS(); + + s64 pread(void* buf, u64 count, s64 offset) override; + + s64 getdents(void* buf, u32 count, s64 offset, s64* basep) override; + }; } // namespace QuasiFS \ No newline at end of file diff --git a/src/core/file_sys/quasifs/quasifs_partition.h b/src/core/file_sys/quasifs/quasifs_partition.h index 04666c01c..47da55a4b 100644 --- a/src/core/file_sys/quasifs/quasifs_partition.h +++ b/src/core/file_sys/quasifs/quasifs_partition.h @@ -4,6 +4,9 @@ #include +#include "common/alignment.h" +#include "core/file_sys/quasifs/quasifs_inode_quasi_directory.h" + #include "quasi_types.h" namespace QuasiFS { @@ -31,16 +34,15 @@ private: // IO block size, allocation unit (multiples of 512) const u32 ioblock_size{4096}; - // amount of raw on-disk blocks per io block - // this is pretty much so we don't recalculate it over and over and over - // and - const u32 block_size{512}; public: // host-bound directory, permissions for root directory Partition(); Partition(const fs::path& host_root = "", const int root_permissions = 0755, - const u32 block_size = 512, const u32 ioblock_size = 4096); + const u32 ioblock_size = 4096); + Partition(dir_ptr root_directory = Directory::Create(), + const fs::path& host_root = "", const int root_permissions = 0755, + const u32 ioblock_size = 4096); ~Partition() = default; template @@ -62,11 +64,6 @@ public: blkid_t GetBlkId(void) { return this->block_id; } - void AdjustStat(Libraries::Kernel::OrbisKernelStat* statbuf) { - auto& s = statbuf->st_size; - statbuf->st_blksize = this->block_size; - statbuf->st_blocks = (1 + (s / this->ioblock_size)) * this->ioblock_size / this->block_size; - } inode_ptr GetInodeByFileno(fileno_t fileno); int Resolve(fs::path& path, Resolved& res); diff --git a/src/core/file_sys/quasifs/src/quasifs.cpp b/src/core/file_sys/quasifs/src/quasifs.cpp index c2e959455..04053b760 100644 --- a/src/core/file_sys/quasifs/src/quasifs.cpp +++ b/src/core/file_sys/quasifs/src/quasifs.cpp @@ -114,7 +114,7 @@ void printTree(const dir_ptr& node, const std::string& name, int depth) { } QFS::QFS(const fs::path& host_path) { - this->rootfs = Partition::Create(host_path); + this->rootfs = Partition::Create(Directory::Create(), host_path); this->root = rootfs->GetRoot(); mount_t mount_options = { @@ -435,7 +435,9 @@ void QFS::SyncHostImpl(partition_ptr part) { LOG_ERROR(Kernel_Fs, "Cannot stat file: {}", entry_path.string()); continue; } - part->AdjustStat(&new_inode->st); + new_inode->st.st_blocks = + Common::AlignUp(static_cast(new_inode->st.st_size), new_inode->st.st_blksize) / + 512; } } catch (const std::exception& e) { LOG_CRITICAL(Kernel_Fs, "An error occurred when syncing [{}]: {}", host_path.string(), diff --git a/src/core/file_sys/quasifs/src/quasifs_inode_quasi_directory.cpp b/src/core/file_sys/quasifs/src/quasifs_inode_quasi_directory.cpp index 42e4d768f..af2098c6a 100644 --- a/src/core/file_sys/quasifs/src/quasifs_inode_quasi_directory.cpp +++ b/src/core/file_sys/quasifs/src/quasifs_inode_quasi_directory.cpp @@ -3,6 +3,7 @@ #include #include +#include "common/alignment.h" #include "core/file_sys/quasifs/quasi_errno.h" #include "core/file_sys/quasifs/quasifs_inode_quasi_directory.h" @@ -12,11 +13,59 @@ QuasiDirectory::QuasiDirectory() { st.st_mode |= QUASI_S_IFDIR; } +s64 QuasiDirectory::pread(void* buf, u64 count, s64 offset) { + return getdents(buf, count, offset, nullptr); +} + +s32 QuasiDirectory::ftruncate(s64 length) { + return -QUASI_EISDIR; +} + +s32 QuasiDirectory::fstat(Libraries::Kernel::OrbisKernelStat* sb) { + RebuildDirents(); + *sb = st; + return 0; +} + +s64 QuasiDirectory::getdents(void* buf, u32 count, s64 offset, s64* basep) { + RebuildDirents(); + + auto it = dirent_cache.lower_bound(offset); + + if (it == dirent_cache.end()) + return 0; + + u64 cumulative_offset = 0; + u16 last_reclen = 0; + for (; it != dirent_cache.end(); it++) { + auto dirent = it->second; + + if (dirent.d_reclen + cumulative_offset > count) + break; + + memcpy(static_cast(buf) + cumulative_offset, &dirent, dirent.d_reclen); + last_reclen = dirent.d_reclen; + cumulative_offset += last_reclen; + } + + if (basep) + *basep = cumulative_offset; + // offset of the last reclen + auto _val = cumulative_offset - last_reclen + 4; + // pointer to last reclen in buffer + u8* placement = static_cast(buf) + _val; + // casting buffer to u16 to write whole value at once (2 bytes) + u16* pos = reinterpret_cast(placement); + *pos += Common::AlignUp(cumulative_offset, count) - cumulative_offset; + + return count; +} + inode_ptr QuasiDirectory::lookup(const std::string& name) { + st.st_atim.tv_sec = time(0); auto it = entries.find(name); if (it == entries.end()) return nullptr; - st.st_atim.tv_sec = time(0); return it->second; } @@ -69,9 +118,35 @@ std::vector QuasiDirectory::list() { } void QuasiDirectory::RebuildDirents(void) { + // adding/removing entries changes mtime if (this->st.st_mtim.tv_sec == this->last_dirent_rebuild_time) return; this->last_dirent_rebuild_time = this->st.st_mtim.tv_sec; + + u64 dirent_size = 0; + this->dirent_cache.clear(); + + for (auto entry = entries.begin(); entry != entries.end(); ++entry) { + dirent_t tmp{}; + inode_ptr node = entry->second; + std::string name = entry->first; + + tmp.d_fileno = node->fileno; + tmp.d_namlen = name.size(); + strncpy(tmp.d_name, name.data(), tmp.d_namlen + 1); + tmp.d_type = node->type() >> 12; + tmp.d_reclen = + Common::AlignUp(sizeof(tmp.d_fileno) + sizeof(tmp.d_type) + sizeof(tmp.d_namlen) + + sizeof(tmp.d_reclen) + (tmp.d_namlen + 1), + 4); + + dirent_cache[dirent_size] = tmp; + dirent_size += tmp.d_reclen; + } + + this->st.st_size = dirent_size; + + return; } } // namespace QuasiFS \ No newline at end of file diff --git a/src/core/file_sys/quasifs/src/quasifs_inode_quasi_directory_pfs.cpp b/src/core/file_sys/quasifs/src/quasifs_inode_quasi_directory_pfs.cpp new file mode 100644 index 000000000..0a3555e61 --- /dev/null +++ b/src/core/file_sys/quasifs/src/quasifs_inode_quasi_directory_pfs.cpp @@ -0,0 +1,84 @@ +// INAA License @marecl 2025 + +#include +#include + +#include "common/alignment.h" +#include "core/file_sys/quasifs/quasi_errno.h" +#include "core/file_sys/quasifs/quasifs_inode_quasi_directory_pfs.h" + +namespace QuasiFS { + +DirectoryPFS::DirectoryPFS() { + this->st.st_size = 65536; +} + +DirectoryPFS::~DirectoryPFS() = default; + +s64 DirectoryPFS::pread(void* buf, u64 count, s64 offset) { + RebuildDirents(); + + auto it = dirent_cache.lower_bound(offset); + + if (it == dirent_cache.end()) + return 0; + + u64 cumulative_offset = 0; + u16 last_reclen = 0; + for (; it != dirent_cache.end(); it++) { + auto dirent = it->second; + + if (dirent.d_reclen + cumulative_offset > count) + break; + + memcpy(static_cast(buf) + cumulative_offset, &dirent, dirent.d_reclen); + last_reclen = dirent.d_reclen; + cumulative_offset += last_reclen; + } + + // offset of the last reclen + auto _val = cumulative_offset - last_reclen + 4; + // pointer to last reclen in buffer + u8* placement = static_cast(buf) + _val; + // casting buffer to u16 to write whole value at once (2 bytes) + u16* pos = reinterpret_cast(placement); + *pos += Common::AlignUp(cumulative_offset, count) - cumulative_offset; + + return count; +} + +s64 DirectoryPFS::getdents(void* buf, u32 count, s64 offset, s64* basep) { + RebuildDirents(); + + auto it = dirent_cache.lower_bound(offset); + + if (it == dirent_cache.end()) + return 0; + + u64 cumulative_offset = 0; + u16 last_reclen = 0; + for (; it != dirent_cache.end(); it++) { + auto dirent = it->second; + + if (dirent.d_reclen + cumulative_offset > count) + break; + + memcpy(static_cast(buf) + cumulative_offset, &dirent, dirent.d_reclen); + last_reclen = dirent.d_reclen; + cumulative_offset += last_reclen; + } + + if (basep) + *basep = cumulative_offset; + // offset of the last reclen + auto _val = cumulative_offset - last_reclen + 4; + // pointer to last reclen in buffer + u8* placement = static_cast(buf) + _val; + // casting buffer to u16 to write whole value at once (2 bytes) + u16* pos = reinterpret_cast(placement); + *pos += Common::AlignUp(cumulative_offset, count) - cumulative_offset; + + return count; +} + +} // namespace QuasiFS \ No newline at end of file diff --git a/src/core/file_sys/quasifs/src/quasifs_partition.cpp b/src/core/file_sys/quasifs/src/quasifs_partition.cpp index ac300fdd2..15d61e5e2 100644 --- a/src/core/file_sys/quasifs/src/quasifs_partition.cpp +++ b/src/core/file_sys/quasifs/src/quasifs_partition.cpp @@ -12,11 +12,14 @@ namespace QuasiFS { -Partition::Partition() : Partition("", 0755, 512, 4096) {} +Partition::Partition() : Partition(Directory::Create(), "", 0755, 4096) {} -Partition::Partition(const fs::path& host_root, const int root_permissions, const u32 block_size, +Partition::Partition(const fs::path& host_root, const int root_permissions, const u32 ioblock_size) + : Partition(Directory::Create(), host_root, root_permissions, ioblock_size) {} + +Partition::Partition(dir_ptr root_directory, const fs::path& host_root, const int root_permissions, const u32 ioblock_size) - : block_id(next_block_id++), host_root(host_root.lexically_normal()), block_size(block_size), + : block_id(next_block_id++), host_root(host_root.lexically_normal()), ioblock_size(ioblock_size) { this->root = Directory::Create(); // clear defaults, write @@ -223,15 +226,6 @@ int Partition::touch(dir_ptr parent, const std::string& name, inode_ptr child) { if (nullptr == parent) return -QUASI_EINVAL; - // auto* csize = &child->st.st_size; - // auto* cbsize = &child->st.st_blksize; - // auto* cblks = &child->st.st_blocks; - - // *cbsize = this->ioblock_size; - // *cblks = blocks_per_io * (*csize) / (*cbsize); - - child->st.st_blksize = ioblock_size; - auto ret = parent->link(name, child); if (ret == 0) IndexInode(child); @@ -370,6 +364,8 @@ bool Partition::IndexInode(inode_ptr node) { node->st.st_ino = node_fileno; node->st.st_dev = block_id; + node->st.st_blksize = ioblock_size; + node->st.st_blocks = Common::AlignUp(static_cast(node->st.st_size), ioblock_size) / 512; return true; } diff --git a/src/core/file_sys/quasifs/src/quasifs_vdriver.cpp b/src/core/file_sys/quasifs/src/quasifs_vdriver.cpp index cc24b66ee..8516d56cc 100644 --- a/src/core/file_sys/quasifs/src/quasifs_vdriver.cpp +++ b/src/core/file_sys/quasifs/src/quasifs_vdriver.cpp @@ -936,7 +936,7 @@ s32 QFS::OperationImpl::FChmod(const s32 fd, u16 mode) { return vio_status; } -s64 QFS::OperationImpl::GetDents(s32 fd, void* buf, u32 nbytes, s64* basep) { +s64 QFS::OperationImpl::GetDents(const s32 fd, void* buf, u64 count, s64* basep) { if (fd < 0) return -QUASI_EBADF; @@ -948,7 +948,7 @@ s64 QFS::OperationImpl::GetDents(s32 fd, void* buf, u32 nbytes, s64* basep) { return -QUASI_EBADF; qfs.vio_driver.SetCtx(nullptr, false, handle); - int vio_status = qfs.vio_driver.GetDents(fd, buf, nbytes, basep); + int vio_status = qfs.vio_driver.GetDents(fd, buf, count, basep); qfs.vio_driver.ClearCtx(); return vio_status; diff --git a/src/core/libraries/app_content/app_content.cpp b/src/core/libraries/app_content/app_content.cpp index fd1918bf8..f47fa99b1 100644 --- a/src/core/libraries/app_content/app_content.cpp +++ b/src/core/libraries/app_content/app_content.cpp @@ -14,12 +14,15 @@ #include "core/libraries/system/systemservice.h" #include "core/file_sys/quasifs/quasifs.h" +#include "core/file_sys/quasifs/quasifs_inode_quasi_directory_pfs.h" #include "core/file_sys/quasifs/quasifs_partition.h" -static QuasiFS::QFS* g_qfs = Common::Singleton::Instance(); +namespace qfs = QuasiFS; namespace Libraries::AppContent { +static qfs::QFS* g_qfs = Common::Singleton::Instance(); + struct AddContInfo { char entitlement_label[ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE]; OrbisAppContentAddcontDownloadStatus status; @@ -109,11 +112,12 @@ int PS4_SYSV_ABI sceAppContentAddcontMount(u32 service_label, // We've located the correct folder. g_qfs->Operation.MKDir(mount_point->data, 0555 /* I think it's like /app0*/); - QuasiFS::partition_ptr partition_dlc = QuasiFS::Partition::Create(entry.path(), 0555); - g_qfs->Mount(mount_point->data, partition_dlc, QuasiFS::MountOptions::MOUNT_RW); + qfs::partition_ptr partition_dlc = qfs::Partition::Create( + qfs::Directory::Create(), entry.path(), 0555); + g_qfs->Mount(mount_point->data, partition_dlc, qfs::MountOptions::MOUNT_RW); g_qfs->SyncHost(mount_point->data); g_qfs->Mount(mount_point->data, partition_dlc, - QuasiFS::MountOptions::MOUNT_REMOUNT | QuasiFS::MountOptions::MOUNT_NOOPT); + qfs::MountOptions::MOUNT_REMOUNT | qfs::MountOptions::MOUNT_NOOPT); return ORBIS_OK; } diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 2014bfe96..c318174f9 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -40,10 +40,11 @@ #endif namespace qfs = QuasiFS; -static QuasiFS::QFS* g_qfs = Common::Singleton::Instance(); namespace Libraries::Kernel { +static QuasiFS::QFS* g_qfs = Common::Singleton::Instance(); + s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {:#o}", raw_path, flags, mode); @@ -537,7 +538,7 @@ static s64 GetDents(s32 fd, char* buf, u64 nbytes, s64* basep) { return -1; } - return ORBIS_OK; + return result; } s64 PS4_SYSV_ABI posix_getdents(s32 fd, char* buf, u64 nbytes) { diff --git a/src/core/libraries/save_data/save_instance.cpp b/src/core/libraries/save_data/save_instance.cpp index c3c2fc3c0..16b834d7b 100644 --- a/src/core/libraries/save_data/save_instance.cpp +++ b/src/core/libraries/save_data/save_instance.cpp @@ -19,10 +19,8 @@ constexpr auto OrbisSaveDataBlocksMin2 = 96; // 3MiB constexpr auto OrbisSaveDataBlocksMax = 32768; // 1 GiB constexpr std::string_view sce_sys = "sce_sys"; // system folder inside save -namespace qfs = QuasiFS; -static qfs::QFS* g_qfs = Common::Singleton::Instance(); - namespace fs = std::filesystem; +namespace qfs = QuasiFS; // clang-format off static const std::unordered_map default_title = { @@ -49,6 +47,8 @@ static const std::unordered_map default_title = { namespace Libraries::SaveData { +static qfs::QFS* g_qfs = Common::Singleton::Instance(); + fs::path SaveInstance::MakeTitleSavePath(OrbisUserServiceUserId user_id, std::string_view game_serial) { return Config::GetSaveDataPath() / std::to_string(user_id) / game_serial; @@ -192,7 +192,7 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor max_blocks = static_cast(GetMaxBlockFromSFO(param_sfo)); g_qfs->Operation.MKDir(mount_point); - auto part = qfs::Partition::Create(save_path); + auto part = qfs::Partition::Create(qfs::Directory::Create(), save_path); g_qfs->Mount(mount_point, part, read_only ? qfs::MountOptions::MOUNT_NOOPT : qfs::MountOptions::MOUNT_RW); g_qfs->SyncHost(mount_point); diff --git a/src/emulator.cpp b/src/emulator.cpp index 2b861054d..badd4dbc2 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -47,6 +47,7 @@ #include "core/file_sys/quasifs/quasi_sys_fcntl.h" #include "core/file_sys/quasifs/quasifs.h" #include "core/file_sys/quasifs/quasifs_inode_quasi_device.h" +#include "core/file_sys/quasifs/quasifs_inode_quasi_directory_pfs.h" #include "core/file_sys/quasifs/quasifs_partition.h" #include "core/file_sys/devices/console_device.h" @@ -93,12 +94,14 @@ Emulator::~Emulator() {} void Emulator::LoadFilesystem(const std::filesystem::path& game_folder) { auto* qfs = Common::Singleton::Instance(); - qfs::partition_ptr partition_app0 = qfs::Partition::Create(game_folder, 0555, 512, 65536); - qfs::partition_ptr partition_av_contents = qfs::Partition::Create("", 0775, 512, 16384); - qfs::partition_ptr partition_av_contents_photo = qfs::Partition::Create("", 0755, 4096, 32768); - qfs::partition_ptr partition_av_contents_thumbs = qfs::Partition::Create("", 0755, 4096, 32768); - qfs::partition_ptr partition_av_contents_video = qfs::Partition::Create("", 0755, 4096, 32768); - qfs::partition_ptr partition_dev = qfs::Partition::Create("", 0755, 16384, 16384); + qfs::partition_ptr partition_app0 = qfs::Partition::Create(game_folder, 0555, 65536); + // qfs::partition_ptr partition_app0 = qfs::Partition::Create( + // qfs::Directory::Create(), game_folder, 0555, 512, 65536); + qfs::partition_ptr partition_av_contents = qfs::Partition::Create("", 0775, 16384); + qfs::partition_ptr partition_av_contents_photo = qfs::Partition::Create("", 0755, 32768); + qfs::partition_ptr partition_av_contents_thumbs = qfs::Partition::Create("", 0755, 32768); + qfs::partition_ptr partition_av_contents_video = qfs::Partition::Create("", 0755, 32768); + qfs::partition_ptr partition_dev = qfs::Partition::Create("", 0755, 16384); qfs->Operation.MKDir("/app0", 0555); qfs->Operation.MKDir("/av_contents", 0775); @@ -264,10 +267,9 @@ void Emulator::Run(std::filesystem::path file, std::vector args, qfs->Operation.MKDir("/download0", 0777); // not sure about perms here qfs->Operation.MKDir("/temp", 0777); qfs->Operation.MKDir("/temp0", 0777); - qfs::partition_ptr partition_data = qfs::Partition::Create(mount_data_dir, 0777, 4096, 32768); - qfs::partition_ptr partition_download = - qfs::Partition::Create(mount_download_dir, 0777, 512, 65536); - qfs::partition_ptr partition_temp = qfs::Partition::Create(mount_temp_dir, 0777, 512, 16384); + qfs::partition_ptr partition_data = qfs::Partition::Create(mount_data_dir, 0777, 32768); + qfs::partition_ptr partition_download = qfs::Partition::Create(mount_download_dir, 0777, 65536); + qfs::partition_ptr partition_temp = qfs::Partition::Create(mount_temp_dir, 0777, 16384); qfs->Mount("/data", partition_data, qfs::MountOptions::MOUNT_RW); qfs->Mount("/download0", partition_download, qfs::MountOptions::MOUNT_RW); qfs->Mount("/temp", partition_temp, qfs::MountOptions::MOUNT_RW);