Added PFS dirents (WIP, throws nullptr exception on some tests)

Delegated lseek to inode
Corrected offset types
Directories can spawn one of their own kind
This commit is contained in:
Marek Ledworowski 2025-11-11 00:45:34 +01:00
parent 410d3834cc
commit 7df5d494dd
38 changed files with 257 additions and 171 deletions

View File

@ -783,12 +783,6 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/aerolib/aerolib.h
src/core/address_space.cpp
src/core/address_space.h
src/core/file_sys/directories/base_directory.cpp
src/core/file_sys/directories/base_directory.h
src/core/file_sys/directories/normal_directory.cpp
src/core/file_sys/directories/normal_directory.h
src/core/file_sys/directories/pfs_directory.cpp
src/core/file_sys/directories/pfs_directory.h
src/core/file_format/pfs.h
src/core/file_format/psf.cpp
src/core/file_format/psf.h

View File

@ -22,11 +22,15 @@ public:
ConsoleDevice();
~ConsoleDevice();
static QuasiFS::dev_ptr Create() {
return std::make_shared<ConsoleDevice>();
}
// clang-format off
s64 read(void* buf, u64 count) override { DEVICE_STUB(); };
s64 write(const void* buf, u64 count) override { DEVICE_STUB(); };
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); };
s64 lseek(s64 offset, int whence) override { DEVICE_STUB(); };
s64 lseek(s64 current, s64 offset, s32 whence) override { DEVICE_STUB(); };
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); };
s32 fsync() override { DEVICE_STUB(); };
s32 ftruncate(s64 length) override { DEVICE_STUB(); };

View File

@ -22,11 +22,15 @@ public:
DeciTty6Device();
~DeciTty6Device();
static QuasiFS::dev_ptr Create() {
return std::make_shared<DeciTty6Device>();
}
// clang-format off
s64 read(void* buf, u64 count) override { DEVICE_STUB(); };
s64 write(const void* buf, u64 count) override { DEVICE_STUB(); };
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); };
s64 lseek(s64 offset, int whence) override { DEVICE_STUB(); };
s64 lseek(s64 current, s64 offset, s32 whence) override { DEVICE_STUB(); };
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); };
s32 fsync() override { DEVICE_STUB(); };
s32 ftruncate(s64 length) override { DEVICE_STUB(); };

View File

@ -30,13 +30,17 @@ public:
explicit Logger(std::string prefix, bool is_err);
~Logger();
static QuasiFS::dev_ptr Create(std::string prefix, bool is_err) {
return std::make_shared<Logger>(prefix, is_err);
}
s64 write(const void* buf, u64 nbytes) override;
s32 fsync() override;
// clang-format off
s64 read(void* buf, u64 count) override { DEVICE_STUB(); };
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); }
s64 lseek(s64 offset, int whence) override { DEVICE_STUB(); }
s64 lseek(s64 current, s64 offset, s32 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 offset, s64* basep) override { DEVICE_STUB(); }

View File

@ -15,11 +15,15 @@ public:
NopDevice() = default;
~NopDevice() = default;
static QuasiFS::dev_ptr Create() {
return std::make_shared<NopDevice>();
}
// clang-format off
s64 read(void* buf, u64 count) override { return 0; };
s64 write(const void* buf, u64 count) override { return 0; };
s32 ioctl(u64 cmd, Common::VaCtx* args) override { return 0; };
s64 lseek(s64 offset, int whence) override { return 0; };
s64 lseek(s64 current, s64 offset, s32 whence) override { return 0; };
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { return 0; };
s32 fsync() override { return 0; };
s32 ftruncate(s64 length) override { return 0; };

View File

@ -21,12 +21,16 @@ public:
NullDevice();
~NullDevice();
static QuasiFS::dev_ptr Create() {
return std::make_shared<NullDevice>();
}
s64 read(void* buf, u64 count) override;
s64 write(const void* buf, u64 count) override;
// clang-format off
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); }
s64 lseek(s64 offset, int whence) override { DEVICE_STUB(); }
s64 lseek(s64 current, s64 offset, s32 whence) override { DEVICE_STUB(); }
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); }
s32 fsync() override { DEVICE_STUB(); }
s32 ftruncate(s64 length) override { DEVICE_STUB(); }

View File

@ -20,12 +20,16 @@ public:
RandomDevice();
~RandomDevice();
static QuasiFS::dev_ptr Create() {
return std::make_shared<RandomDevice>();
}
s64 read(void* buf, u64 count) override;
s64 write(const void* buf, u64 count) override;
// clang-format off
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); }
s64 lseek(s64 offset, int whence) override { DEVICE_STUB(); }
s64 lseek(s64 current, s64 offset, s32 whence) override { DEVICE_STUB(); }
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); }
s32 fsync() override { DEVICE_STUB(); }
s32 ftruncate(s64 length) override { DEVICE_STUB(); }

View File

@ -21,12 +21,16 @@ public:
RngDevice();
~RngDevice();
static QuasiFS::dev_ptr Create() {
return std::make_shared<RngDevice>();
}
s32 ioctl(u64 cmd, Common::VaCtx* args) override;
// clang-format off
s64 read(void* buf, u64 count) override { DEVICE_STUB(); }
s64 write(const void* buf, u64 count) override { DEVICE_STUB(); }
s64 lseek(s64 offset, int whence) override { DEVICE_STUB(); }
s64 lseek(s64 current, s64 offset, s32 whence) override { DEVICE_STUB(); }
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); }
s32 fsync() override { DEVICE_STUB(); }
s32 ftruncate(s64 length) override { DEVICE_STUB(); }

View File

@ -21,12 +21,16 @@ public:
SRandomDevice();
~SRandomDevice();
static QuasiFS::dev_ptr Create() {
return std::make_shared<SRandomDevice>();
}
s64 read(void* buf, u64 count) override;
s64 write(const void* buf, u64 count) override;
// clang-format off
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); }
s64 lseek(s64 offset, int whence) override { DEVICE_STUB(); }
s64 lseek(s64 current, s64 offset, s32 whence) override { DEVICE_STUB(); }
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); }
s32 fsync() override { DEVICE_STUB(); }
s32 ftruncate(s64 length) override { DEVICE_STUB(); }

View File

@ -21,12 +21,16 @@ public:
ZeroDevice();
~ZeroDevice();
static QuasiFS::dev_ptr Create() {
return std::make_shared<ZeroDevice>();
}
s64 read(void* buf, u64 count) override;
s64 write(const void* buf, u64 count) override;
// clang-format off
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); }
s64 lseek(s64 offset, int whence) override { DEVICE_STUB(); }
s64 lseek(s64 current, s64 offset, s32 whence) override { DEVICE_STUB(); }
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); }
s32 fsync() override { DEVICE_STUB(); }
s32 ftruncate(s64 length) override { DEVICE_STUB(); }

View File

@ -31,8 +31,8 @@ public:
// Conversion helpers
//
static constexpr s32 ToPOSIXSeekOrigin(QuasiFS::SeekOrigin origin) {
switch (origin) {
static constexpr s32 ToPOSIXSeekOrigin(s32 whence) {
switch (whence) {
case QuasiFS::SeekOrigin::ORIGIN:
return SEEK_SET;
case QuasiFS::SeekOrigin::CURRENT:
@ -94,19 +94,19 @@ public:
s32 Flush(const s32 fd) override;
s32 FSync(const s32 fd) override;
s64 LSeek(const s32 fd, u64 offset, QuasiFS::SeekOrigin origin) override;
s64 LSeek(const s32 fd, s64 offset, s32 whence) override;
s64 Tell(const s32 fd) override;
s32 Truncate(const fs::path& path, u64 size) override;
s32 FTruncate(const s32 fd, u64 size) override;
s64 Read(const s32 fd, void* buf, u64 count) override;
s64 PRead(const s32 fd, void* buf, u64 count, u64 offset) override;
s64 PRead(const s32 fd, void* buf, u64 count, s64 offset) override;
s64 ReadV(const s32 fd, OrbisKernelIovec* iov, u32 iovcnt) override;
s64 PReadV(const s32 fd, OrbisKernelIovec* iov, u32 iovcnt, s64 offset) override;
s64 Write(const s32 fd, const void* buf, u64 count) override;
s64 PWrite(const s32 fd, const void* buf, u64 count, u64 offset) override;
s64 PWrite(const s32 fd, const void* buf, u64 count, s64 offset) override;
s64 WriteV(const s32 fd, const OrbisKernelIovec* iov, u32 iovcnt) override;
s64 PWriteV(const s32 fd, const OrbisKernelIovec* iov, u32 iovcnt, s64 offset) override;

View File

@ -55,19 +55,19 @@ public:
s32 Flush(const s32 fd) override;
s32 FSync(const s32 fd) override;
s64 LSeek(const s32 fd, u64 offset, QuasiFS::SeekOrigin origin) override;
s64 LSeek(const s32 fd, s64 offset, s32 whence) override;
s64 Tell(const s32 fd) override;
s32 Truncate(const fs::path& path, u64 size) override;
s32 FTruncate(const s32 fd, u64 size) override;
s64 Read(const s32 fd, void* buf, u64 count) override;
s64 PRead(const s32 fd, void* buf, u64 count, u64 offset) override;
s64 PRead(const s32 fd, void* buf, u64 count, s64 offset) override;
s64 ReadV(const s32 fd, OrbisKernelIovec* iov, u32 iovcnt) override;
s64 PReadV(const s32 fd, OrbisKernelIovec* iov, u32 iovcnt, s64 offset) override;
s64 Write(const s32 fd, const void* buf, u64 count) override;
s64 PWrite(const s32 fd, const void* buf, u64 count, u64 offset) override;
s64 PWrite(const s32 fd, const void* buf, u64 count, s64 offset) override;
s64 WriteV(const s32 fd, const OrbisKernelIovec* iov, u32 iovcnt) override;
s64 PWriteV(const s32 fd, const OrbisKernelIovec* iov, u32 iovcnt, s64 offset) override;

View File

@ -85,7 +85,7 @@ public:
s32 Flush(const s32 fd) override;
s32 FSync(const s32 fd) override;
s64 LSeek(const s32 fd, u64 offset, QuasiFS::SeekOrigin origin) override;
s64 LSeek(const s32 fd, u64 offset, s32 whence) override;
s64 Tell(const s32 fd) override;
s32 Truncate(const fs::path& path, u64 size) override;

View File

@ -26,20 +26,20 @@ s32 HostIO_Base::LinkSymbolic(const fs::path& src, const fs::path& dst) { STUB()
s32 HostIO_Base::Flush(const s32 fd) { STUB(); }
s32 HostIO_Base::FSync(const s32 fd) { STUB(); }
s64 HostIO_Base::LSeek(const s32 fd, u64 offset, QuasiFS::SeekOrigin origin) { STUB(); }
s64 HostIO_Base::LSeek(const s32 fd, s64 offset, s32 whence) { STUB(); }
s64 HostIO_Base::Tell(const s32 fd) { STUB(); }
s32 HostIO_Base::Truncate(const fs::path& path, u64 size) { STUB(); }
s32 HostIO_Base::FTruncate(const s32 fd, u64 size) { STUB(); }
s64 HostIO_Base::Read(const s32 fd, void* buf, u64 count) { STUB(); }
s64 HostIO_Base::PRead(const s32 fd, void* buf, u64 count, u64 offset) { STUB(); }
s64 HostIO_Base::PRead(const s32 fd, void* buf, u64 count, s64 offset) { STUB(); }
s64 HostIO_Base::ReadV(const s32 fd, OrbisKernelIovec* iov, u32 iovcnt) { STUB(); }
s64 HostIO_Base::PReadV(const s32 fd, OrbisKernelIovec* iov, u32 iovcnt,
s64 offset) { STUB(); }
s64 HostIO_Base::Write(const s32 fd, const void* buf, u64 count) { STUB(); }
s64 HostIO_Base::PWrite(const s32 fd, const void* buf, u64 count, u64 offset) { STUB(); }
s64 HostIO_Base::PWrite(const s32 fd, const void* buf, u64 count, s64 offset) { STUB(); }
s64 HostIO_Base::WriteV(const s32 fd, const OrbisKernelIovec* iov, u32 iovcnt) { STUB(); }
s64 HostIO_Base::PWriteV(const s32 fd, const OrbisKernelIovec* iov, u32 iovcnt,
s64 offset) { STUB(); }

View File

@ -34,16 +34,16 @@ public:
virtual s32 FSync(const s32 fd);
virtual s32 Truncate(const fs::path& path, u64 size);
virtual s32 FTruncate(const s32 fd, u64 size);
virtual s64 LSeek(const s32 fd, u64 offset, QuasiFS::SeekOrigin origin);
virtual s64 LSeek(const s32 fd, s64 offset, s32 whence);
virtual s64 Tell(const s32 fd);
virtual s64 Read(const s32 fd, void* buf, u64 count);
virtual s64 PRead(const s32 fd, void* buf, u64 count, u64 offset);
virtual s64 PRead(const s32 fd, void* buf, u64 count, s64 offset);
virtual s64 ReadV(const s32 fd, OrbisKernelIovec* iov, u32 iovcnt);
virtual s64 PReadV(const s32 fd, OrbisKernelIovec* iov, u32 iovcnt, s64 offset);
virtual s64 Write(const s32 fd, const void* buf, u64 count);
virtual s64 PWrite(const s32 fd, const void* buf, u64 count, u64 offset);
virtual s64 PWrite(const s32 fd, const void* buf, u64 count, s64 offset);
virtual s64 WriteV(const s32 fd, const OrbisKernelIovec* iov, u32 iovcnt);
virtual s64 PWriteV(const s32 fd, const OrbisKernelIovec* iov, u32 iovcnt, s64 offset);

View File

@ -68,9 +68,9 @@ s32 HostIO_POSIX::FSync(const s32 fd) {
return 0 == status ? status : -errno;
}
s64 HostIO_POSIX::LSeek(const s32 fd, u64 offset, QuasiFS::SeekOrigin origin) {
s64 HostIO_POSIX::LSeek(const s32 fd, s64 offset, s32 whence) {
errno = 0;
s32 status = lseek(fd, offset, ToPOSIXSeekOrigin(origin));
s32 status = lseek(fd, offset, ToPOSIXSeekOrigin(whence));
return status >= 0 ? status : -errno;
}
@ -96,7 +96,7 @@ s64 HostIO_POSIX::Read(const s32 fd, void* buf, u64 count) {
return status >= 0 ? status : -errno;
}
s64 HostIO_POSIX::PRead(const s32 fd, void* buf, u64 count, u64 offset) {
s64 HostIO_POSIX::PRead(const s32 fd, void* buf, u64 count, s64 offset) {
errno = 0;
s32 status = pread(fd, buf, count, offset);
return status >= 0 ? status : -errno;
@ -134,7 +134,7 @@ s64 HostIO_POSIX::Write(const s32 fd, const void* buf, u64 count) {
return status >= 0 ? status : -errno;
}
s64 HostIO_POSIX::PWrite(const s32 fd, const void* buf, u64 count, u64 offset) {
s64 HostIO_POSIX::PWrite(const s32 fd, const void* buf, u64 count, s64 offset) {
errno = 0;
s32 status = pwrite(fd, buf, count, offset);
return status >= 0 ? status : -errno;

View File

@ -43,8 +43,7 @@ s32 HostIO_Virtual::Open(const fs::path& path, s32 flags, u16 mode) {
if ((flags & QUASI_O_CREAT) == 0)
return -QUASI_ENOENT;
target =
this->host_bound ? QuasiFile::Create<RegularFile>() : QuasiFile::Create<VirtualFile>();
target = this->host_bound ? RegularFile::Create() : VirtualFile::Create();
target->chmod(mode);
if (0 != part->touch(parent, this->res->leaf, target))
// touch failed in target directory, issue with resolve() is most likely
@ -147,7 +146,7 @@ s32 HostIO_Virtual::FSync(const s32 fd) {
return handle->node->fsync();
}
s64 HostIO_Virtual::LSeek(const s32 fd, u64 offset, QuasiFS::SeekOrigin origin) {
s64 HostIO_Virtual::LSeek(const s32 fd, s64 offset, s32 whence) {
if (nullptr == handle)
return -QUASI_EINVAL;
@ -156,17 +155,13 @@ s64 HostIO_Virtual::LSeek(const s32 fd, u64 offset, QuasiFS::SeekOrigin origin)
if (nullptr == node)
return -QUASI_EBADF;
auto ptr = &handle->pos;
s64 new_position = node->lseek(handle->pos, offset, whence);
u64 new_ptr = ((SeekOrigin::ORIGIN == origin) * offset) +
((SeekOrigin::CURRENT == origin) * (*(ptr) + offset)) +
((SeekOrigin::END == origin) * (node->st.st_size + offset));
if (new_ptr < 0)
if (new_position < 0)
return -QUASI_EINVAL;
*ptr = new_ptr;
return *ptr;
handle->pos = new_position;
return handle->pos;
}
s64 HostIO_Virtual::Tell(const s32 fd) {
@ -212,7 +207,7 @@ s64 HostIO_Virtual::Read(const s32 fd, void* buf, u64 count) {
return br;
}
s64 HostIO_Virtual::PRead(const s32 fd, void* buf, u64 count, u64 offset) {
s64 HostIO_Virtual::PRead(const s32 fd, void* buf, u64 count, s64 offset) {
if (nullptr == handle)
return -QUASI_EINVAL;
@ -257,7 +252,7 @@ s64 HostIO_Virtual::Write(const s32 fd, const void* buf, u64 count) {
return bw;
}
s64 HostIO_Virtual::PWrite(const s32 fd, const void* buf, u64 count, u64 offset) {
s64 HostIO_Virtual::PWrite(const s32 fd, const void* buf, u64 count, s64 offset) {
if (nullptr == handle)
return -QUASI_EBADF;
@ -300,8 +295,7 @@ s32 HostIO_Virtual::MKDir(const fs::path& path, u16 mode) {
if (nullptr == this->res)
return -QUASI_EINVAL;
dir_ptr new_dir = Directory::Create<Directory>();
return this->res->mountpoint->mkdir(this->res->parent, this->res->leaf, new_dir);
return this->res->mountpoint->mkdir(this->res->parent, this->res->leaf);
}
s32 HostIO_Virtual::RMDir(const fs::path& path) {

View File

@ -58,7 +58,7 @@ s32 HostIO_Win32::FSync(const s32 fd) {
return 0 == status ? 0 : -errno;
}
s64 HostIO_Win32::LSeek(const s32 fd, u64 offset, QuasiFS::SeekOrigin origin) {
s64 HostIO_Win32::LSeek(const s32 fd, u64 offset, s32 whence) {
errno = 0;
s32 status = _lseeki64(fd, offset, ToWIN32SeekOrigin(origin));
return status >= 0 ? status : -errno;

View File

@ -78,7 +78,9 @@ struct File {
}
};
enum class SeekOrigin : uint8_t { ORIGIN, CURRENT, END };
namespace SeekOrigin {
enum { ORIGIN = 0, CURRENT, END };
}
//
// Access

View File

@ -71,17 +71,17 @@ private:
s32 FSync(const s32 fd) override;
s32 Truncate(const fs::path& path, u64 size) override;
s32 FTruncate(const s32 fd, u64 size) override;
s64 LSeek(const s32 fd, u64 offset, SeekOrigin origin) override;
s64 LSeek(const s32 fd, s64 offset, s32 whence) override;
s64 Tell(const s32 fd) override;
s64 Read(const s32 fd, void* buf, u64 count) override;
s64 PRead(const s32 fd, void* buf, u64 count, u64 offset) override;
s64 PRead(const s32 fd, void* buf, u64 count, s64 offset) override;
s64 ReadV(const s32 fd, Libraries::Kernel::OrbisKernelIovec* iov, u32 iovcnt) override;
s64 PReadV(const s32 fd, Libraries::Kernel::OrbisKernelIovec* iov, u32 iovcnt,
s64 offset) override;
s64 Write(const s32 fd, const void* buf, u64 count) override;
s64 PWrite(const s32 fd, const void* buf, u64 count, u64 offset) override;
s64 PWrite(const s32 fd, const void* buf, u64 count, s64 offset) override;
s64 WriteV(const s32 fd, const Libraries::Kernel::OrbisKernelIovec* iov,
u32 iovcnt) override;
s64 PWriteV(const s32 fd, const Libraries::Kernel::OrbisKernelIovec* iov, u32 iovcnt,

View File

@ -72,7 +72,7 @@ public:
return tb;
}
virtual s64 lseek(s64 offset, int whence) {
virtual s64 lseek(s64 current, s64 offset, s32 whence) {
return -QUASI_EBADF;
}

View File

@ -15,11 +15,8 @@ public:
Device();
~Device();
template <typename T, typename... Args>
static dev_ptr Create(Args&&... args) {
if constexpr (std::is_base_of_v<Device, T>)
return std::make_shared<T>(std::forward<Args>(args)...);
UNREACHABLE();
static dev_ptr Create() {
return std::make_shared<Device>();
}
virtual s64 read(void* buf, u64 count);

View File

@ -46,24 +46,27 @@ public:
return out;
}
template <typename T, typename... Args>
static dir_ptr Create(Args&&... args) {
if constexpr (std::is_base_of_v<QuasiDirectory, T>)
return std::make_shared<T>(std::forward<Args>(args)...);
UNREACHABLE();
// Create out of thin air
static dir_ptr Create() {
return std::make_shared<QuasiDirectory>();
}
// Allow "inheriting" type of directory
virtual dir_ptr Spawn() const {
return std::make_shared<QuasiDirectory>();
}
//
// Inode overrides
//
s64 pread(void* buf, u64 count, s64 offset) override;
virtual 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;
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
s32 ftruncate(s64 length) final override;
s64 getdents(void* buf, u32 count, s64 offset, s64* basep) override;
virtual s64 getdents(void* buf, u32 count, s64 offset, s64* basep) override;
//
// Dir-specific

View File

@ -29,16 +29,25 @@ protected:
#pragma pack(pop)
private:
// void RebuildDirents(void);
void RebuildDirents(void);
s64 dirents_size{};
public:
DirectoryPFS();
~DirectoryPFS();
s64 pread(void* buf, u64 count, s64 offset) override;
s64 getdents(void* buf, u32 count, s64 offset, s64* basep) override;
static dir_ptr Create() {
return std::make_shared<DirectoryPFS>();
}
virtual dir_ptr Spawn() const override {
return std::make_shared<DirectoryPFS>();
}
s64 pread(void* buf, u64 count, s64 offset) override;
s64 lseek(s64 current, s64 offset, s32 whence) override;
s64 getdents(void* buf, u32 count, s64 offset, s64* basep) override;
};
} // namespace QuasiFS

View File

@ -18,16 +18,15 @@ public:
};
~QuasiFile() = default;
template <typename T, typename... Args>
static file_ptr Create(Args&&... args) {
if constexpr (std::is_base_of_v<QuasiFile, T>)
return std::make_shared<T>(std::forward<Args>(args)...);
UNREACHABLE();
static file_ptr Create() {
return std::make_shared<QuasiFile>();
}
s64 pread(void* buf, size_t count, s64 offset) override;
s64 pwrite(const void* buf, size_t count, s64 offset) override;
s64 lseek(s64 current, s64 offset, s32 whence) override;
s32 ftruncate(s64 length) override;
};

View File

@ -16,6 +16,10 @@ public:
VirtualFile() = default;
~VirtualFile() = default;
static std::shared_ptr<VirtualFile> Create() {
return std::make_shared<VirtualFile>();
}
//
// Working functions
//

View File

@ -15,11 +15,8 @@ public:
Socket();
~Socket();
template <typename T, typename... Args>
static dev_ptr Create(Args&&... args) {
if constexpr (std::is_base_of_v<Socket, T>)
return std::make_shared<T>(std::forward<Args>(args)...);
UNREACHABLE();
static socket_ptr Create() {
return std::make_shared<Socket>();
}
};

View File

@ -40,14 +40,21 @@ public:
Partition();
Partition(const fs::path& host_root = "", const int root_permissions = 0755,
const u32 ioblock_size = 4096);
Partition(dir_ptr root_directory = Directory::Create<Directory>(),
const fs::path& host_root = "", const int root_permissions = 0755,
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 <typename... Args>
static partition_ptr Create(Args&&... args) {
return std::make_shared<Partition>(std::forward<Args>(args)...);
static partition_ptr Create(const fs::path& host_root = "", const int root_permissions = 0755,
const u32 ioblock_size = 4096) {
return std::make_shared<Partition>(Directory::Create(), host_root, root_permissions,
ioblock_size);
}
static partition_ptr Create(dir_ptr root_directory = Directory::Create(),
const fs::path& host_root = "", const int root_permissions = 0755,
const u32 ioblock_size = 4096) {
return std::make_shared<Partition>(root_directory, host_root, root_permissions,
ioblock_size);
}
// empty - invalid, not empty - safe
@ -71,17 +78,16 @@ public:
// create file at path (creates entry in parent dir). returns 0 or negative errno
template <typename T>
int touch(dir_ptr parent, const std::string& name) {
if constexpr (!std::is_base_of_v<Inode, T>)
if constexpr (std::is_base_of_v<Inode, T>)
return touch(parent, name, T::Create());
UNREACHABLE_MSG(" QuasiFS:Partition:Touch Created element must derive from Inode");
static_assert(std::is_base_of_v<Inode, T>,
" QuasiFS:Partition:Touch Created element must derive from Inode");
return -666;
}
int touch(dir_ptr parent, const std::string& name, inode_ptr child);
int mkdir(dir_ptr parent, const std::string& name);
int mkdir(dir_ptr parent, const std::string& name, dir_ptr child);
int rmdir(fs::path path);
int rmdir(dir_ptr parent, const std::string& name);
int link(inode_ptr source, dir_ptr destination_parent, const std::string& name);

View File

@ -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(Directory::Create<Directory>(), host_path);
this->rootfs = Partition::Create(Directory::Create(), host_path);
this->root = rootfs->GetRoot();
mount_t mount_options = {
@ -416,19 +416,18 @@ void QFS::SyncHostImpl(partition_ptr part) {
dir_ptr parent_dir =
res.node->is_dir() ? std::static_pointer_cast<Directory>(res.node) : nullptr;
inode_ptr new_inode{};
if (entry->is_directory()) {
new_inode = Directory::Create<Directory>();
part->mkdir(parent_dir, leaf, std::static_pointer_cast<Directory>(new_inode));
part->mkdir(parent_dir, leaf);
} else if (entry->is_regular_file()) {
new_inode = QuasiFile::Create<RegularFile>();
part->touch(parent_dir, leaf, std::static_pointer_cast<RegularFile>(new_inode));
part->touch<RegularFile>(parent_dir, leaf);
} else {
LOG_ERROR(Kernel_Fs, "Unsupported host file type: {}", entry_path.string());
continue;
}
inode_ptr new_inode = parent_dir->lookup(leaf);
// this should populate **everything** immediately
// this is a note to self TODO:
if (0 != this->hio_driver.Stat(entry_path, &new_inode->st)) {

View File

@ -17,16 +17,16 @@ 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;
}
s32 QuasiDirectory::ftruncate(s64 length) {
return -QUASI_EISDIR;
}
s64 QuasiDirectory::getdents(void* buf, u32 count, s64 offset, s64* basep) {
RebuildDirents();
@ -123,6 +123,9 @@ void QuasiDirectory::RebuildDirents(void) {
return;
this->last_dirent_rebuild_time = this->st.st_mtim.tv_sec;
constexpr u32 dirent_meta_size = sizeof(dirent_t::d_fileno) + sizeof(dirent_t::d_type) +
sizeof(dirent_t::d_namlen) + sizeof(dirent_t::d_reclen);
u64 dirent_size = 0;
this->dirent_cache.clear();
@ -135,10 +138,7 @@ void QuasiDirectory::RebuildDirents(void) {
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);
tmp.d_reclen = Common::AlignUp(dirent_meta_size + tmp.d_namlen + 1, 4);
dirent_cache[dirent_size] = tmp;
dirent_size += tmp.d_reclen;

View File

@ -23,30 +23,57 @@ s64 DirectoryPFS::pread(void* buf, u64 count, s64 offset) {
if (it == dirent_cache.end())
return 0;
memset(buf, 0, count);
u64 cumulative_offset = 0;
u16 last_reclen = 0;
u32* reclen_location = nullptr;
for (; it != dirent_cache.end(); it++) {
auto dirent = it->second;
if (dirent.d_reclen + cumulative_offset > count)
break;
memcpy(static_cast<u8*>(buf) + cumulative_offset, &dirent, dirent.d_reclen);
last_reclen = dirent.d_reclen;
cumulative_offset += last_reclen;
auto partial_base = static_cast<u8*>(buf) + cumulative_offset;
auto _fileno = reinterpret_cast<u32*>(partial_base);
auto _type = reinterpret_cast<u32*>(partial_base) + 1;
auto _namlen = reinterpret_cast<u32*>(partial_base) + 2;
reclen_location = reinterpret_cast<u32*>(partial_base) + 3;
*_fileno = dirent.d_fileno;
*_type = dirent.d_type;
*_namlen = dirent.d_namlen;
*reclen_location = dirent.d_reclen;
memcpy(reinterpret_cast<u8*>(reclen_location + 1), &dirent.d_name, dirent.d_namlen);
cumulative_offset += dirent.d_reclen;
}
// offset of the last reclen
auto _val = cumulative_offset - last_reclen + 4;
// pointer to last reclen in buffer
u8* placement = static_cast<u8*>(buf) + _val;
// casting buffer to u16 to write whole value at once (2 bytes)
u16* pos = reinterpret_cast<u16*>(placement);
*pos += Common::AlignUp(cumulative_offset, count) - cumulative_offset;
*reclen_location += Common::AlignUp(cumulative_offset, count) - cumulative_offset;
return count;
}
s64 DirectoryPFS::lseek(s64 current, s64 offset, s32 whence) {
switch (whence) {
case 0:
return offset;
break;
case 1:
if ((current + offset) >= dirents_size)
return current + offset;
{
auto _tmp = dirent_cache.lower_bound(current + offset);
if (_tmp == dirent_cache.end())
return -QUASI_EINVAL;
return _tmp->first;
}
break;
case 2:
return dirents_size + offset;
break;
}
UNREACHABLE_MSG("lseek with unknown whence {}", whence);
return -QUASI_ENOSYS;
}
s64 DirectoryPFS::getdents(void* buf, u32 count, s64 offset, s64* basep) {
RebuildDirents();
@ -56,7 +83,6 @@ s64 DirectoryPFS::getdents(void* buf, u32 count, s64 offset, s64* basep) {
return 0;
u64 cumulative_offset = 0;
u16 last_reclen = 0;
for (; it != dirent_cache.end(); it++) {
auto dirent = it->second;
@ -64,21 +90,46 @@ s64 DirectoryPFS::getdents(void* buf, u32 count, s64 offset, s64* basep) {
break;
memcpy(static_cast<u8*>(buf) + cumulative_offset, &dirent, dirent.d_reclen);
last_reclen = dirent.d_reclen;
cumulative_offset += last_reclen;
cumulative_offset += dirent.d_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<u8*>(buf) + _val;
// casting buffer to u16 to write whole value at once (2 bytes)
u16* pos = reinterpret_cast<u16*>(placement);
*pos += Common::AlignUp(cumulative_offset, count) - cumulative_offset;
return count;
return cumulative_offset;
}
void DirectoryPFS::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;
constexpr u32 dirent_meta_size = sizeof(dirent_pfs_t::d_fileno) + sizeof(dirent_pfs_t::d_type) +
sizeof(dirent_pfs_t::d_namlen) +
sizeof(dirent_pfs_t::d_reclen);
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(dirent_meta_size + tmp.d_namlen + 1, 8);
dirent_cache[dirent_size] = tmp;
dirent_size += tmp.d_reclen;
}
this->dirents_size = dirent_size;
return;
}
} // namespace QuasiFS

View File

@ -25,6 +25,12 @@ s64 QuasiFile::pwrite(const void* buf, size_t count, s64 offset) {
return count;
}
s64 QuasiFile::lseek(s64 current, s64 offset, s32 whence) {
return ((SeekOrigin::ORIGIN == whence) * offset) +
((SeekOrigin::CURRENT == whence) * (current + offset)) +
((SeekOrigin::END == whence) * (this->st.st_size + offset));
}
s32 QuasiFile::ftruncate(s64 length) {
if (length < 0)
return -QUASI_EINVAL;

View File

@ -12,16 +12,15 @@
namespace QuasiFS {
Partition::Partition() : Partition(Directory::Create<Directory>(), "", 0755, 4096) {}
Partition::Partition() : Partition(Directory::Create(), "", 0755, 4096) {}
Partition::Partition(const fs::path& host_root, const int root_permissions, const u32 ioblock_size)
: Partition(Directory::Create<Directory>(), host_root, root_permissions, ioblock_size) {}
: Partition(dir_ptr(), 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()),
: root(root_directory), block_id(next_block_id++), host_root(host_root.lexically_normal()),
ioblock_size(ioblock_size) {
this->root = Directory::Create<Directory>();
// clear defaults, write
chmod(this->root, root_permissions);
IndexInode(this->root);
@ -233,35 +232,23 @@ int Partition::touch(dir_ptr parent, const std::string& name, inode_ptr child) {
}
int Partition::mkdir(dir_ptr parent, const std::string& name) {
return mkdir(parent, name, Directory::Create<Directory>());
}
int Partition::mkdir(dir_ptr parent, const std::string& name, dir_ptr child) {
if (nullptr == parent)
return -QUASI_ENOENT;
dir_ptr real_parent = parent->mounted_root ? parent->mounted_root : parent;
dir_ptr child = real_parent->Spawn();
child->st.st_blksize = ioblock_size;
int ret = parent->link(name, child);
if (ret == 0)
IndexInode(child);
auto real_parent = parent->mounted_root ? parent->mounted_root : parent;
mkrelative(real_parent, child);
return ret;
}
int Partition::rmdir(fs::path path) {
Resolved res;
int resolve_status = this->Resolve(path, res);
if (resolve_status != 0)
return resolve_status;
return this->rmdir(res.parent, res.leaf);
}
int Partition::rmdir(dir_ptr parent, const std::string& name) {
if (nullptr == parent)
return -QUASI_ENOENT;

View File

@ -109,6 +109,7 @@ s32 QFS::OperationImpl::Open(const fs::path& path, int flags, u16 mode) {
handle->read = request_read;
handle->write = request_write;
handle->append = request_append;
handle->pos = 0;
auto next_free_handle = qfs.GetFreeHandleNo();
qfs.open_fd[next_free_handle] = handle;
return next_free_handle;
@ -471,7 +472,7 @@ s32 QFS::OperationImpl::FTruncate(const s32 fd, u64 length) {
return vio_status;
}
s64 QFS::OperationImpl::LSeek(const s32 fd, u64 offset, SeekOrigin origin) {
s64 QFS::OperationImpl::LSeek(const s32 fd, s64 offset, s32 whence) {
if (fd < 0)
return -QUASI_EBADF;
@ -485,14 +486,14 @@ s64 QFS::OperationImpl::LSeek(const s32 fd, u64 offset, SeekOrigin origin) {
if (handle->IsHostBound()) {
int host_fd = handle->host_fd;
if (hio_status = qfs.hio_driver.LSeek(host_fd, offset, origin); hio_status < 0)
if (hio_status = qfs.hio_driver.LSeek(host_fd, offset, whence); hio_status < 0)
// hosts operation must succeed in order to continue
return hio_status;
host_used = true;
}
qfs.vio_driver.SetCtx(nullptr, host_used, handle);
vio_status = qfs.vio_driver.LSeek(fd, offset, origin);
vio_status = qfs.vio_driver.LSeek(fd, offset, whence);
qfs.vio_driver.ClearCtx();
if (host_used && (hio_status != vio_status))
@ -550,7 +551,7 @@ s64 QFS::OperationImpl::Read(const s32 fd, void* buf, u64 count) {
return vio_status;
}
s64 QFS::OperationImpl::PRead(const s32 fd, void* buf, u64 count, u64 offset) {
s64 QFS::OperationImpl::PRead(const s32 fd, void* buf, u64 count, s64 offset) {
if (fd < 0)
return -QUASI_EBADF;
@ -687,7 +688,7 @@ s64 QFS::OperationImpl::Write(const s32 fd, const void* buf, u64 count) {
return vio_status;
}
s64 QFS::OperationImpl::PWrite(const s32 fd, const void* buf, u64 count, u64 offset) {
s64 QFS::OperationImpl::PWrite(const s32 fd, const void* buf, u64 count, s64 offset) {
if (fd < 0)
return -QUASI_EBADF;

View File

@ -112,8 +112,9 @@ 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*/);
qfs::partition_ptr partition_dlc = qfs::Partition::Create(
qfs::Directory::Create<qfs::DirectoryPFS>(), entry.path(), 0555);
// Didn't verify FS type
qfs::partition_ptr partition_dlc =
qfs::Partition::Create(qfs::DirectoryPFS::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,

View File

@ -151,7 +151,7 @@ s64 PS4_SYSV_ABI sceKernelWritev(s32 fd, const OrbisKernelIovec* iov, s32 iovcnt
}
s64 PS4_SYSV_ABI posix_lseek(s32 fd, s64 offset, s32 whence) {
qfs::SeekOrigin origin{};
s32 origin{};
if (whence == 0) {
origin = qfs::SeekOrigin::ORIGIN;
} else if (whence == 1) {

View File

@ -192,7 +192,8 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor
max_blocks = static_cast<int>(GetMaxBlockFromSFO(param_sfo));
g_qfs->Operation.MKDir(mount_point);
auto part = qfs::Partition::Create(qfs::Directory::Create<qfs::Directory>(), save_path);
// Didn't verify FS type
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);

View File

@ -94,9 +94,8 @@ Emulator::~Emulator() {}
void Emulator::LoadFilesystem(const std::filesystem::path& game_folder) {
auto* qfs = Common::Singleton<qfs::QFS>::Instance();
qfs::partition_ptr partition_app0 = qfs::Partition::Create(game_folder, 0555, 65536);
// qfs::partition_ptr partition_app0 = qfs::Partition::Create(
// qfs::Directory::Create<qfs::DirectoryPFS>(), game_folder, 0555, 512, 65536);
qfs::partition_ptr partition_app0 =
qfs::Partition::Create(qfs::DirectoryPFS::Create(), game_folder, 0555, 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);
@ -127,9 +126,9 @@ void Emulator::LoadFilesystem(const std::filesystem::path& game_folder) {
//
qfs->Operation.MKDir("/dev/fd");
qfs->ForceInsert("/dev/fd", "0", qfs::Device::Create<Devices::ZeroDevice>());
qfs->ForceInsert("/dev/fd", "1", qfs::Device::Create<Devices::Logger>("stdout", false));
qfs->ForceInsert("/dev/fd", "2", qfs::Device::Create<Devices::Logger>("stderr", true));
qfs->ForceInsert("/dev/fd", "0", Devices::ZeroDevice::Create());
qfs->ForceInsert("/dev/fd", "1", Devices::Logger::Create("stdout", false));
qfs->ForceInsert("/dev/fd", "2", Devices::Logger::Create("stderr", true));
// std* is unavailable from within the app???
qfs->Operation.LinkSymbolic("/dev/fd/0", "/dev/stdin");
qfs->Operation.LinkSymbolic("/dev/fd/1", "/dev/stdout");
@ -138,14 +137,14 @@ void Emulator::LoadFilesystem(const std::filesystem::path& game_folder) {
qfs->Operation.LinkSymbolic("/dev/fd/1", "/dev/deci_stdout");
qfs->Operation.LinkSymbolic("/dev/fd/2", "/dev/deci_stderr");
qfs->ForceInsert("/dev", "console", qfs::Device::Create<Devices::ConsoleDevice>());
qfs->ForceInsert("/dev", "deci_tty6", qfs::Device::Create<Devices::DeciTty6Device>());
qfs->ForceInsert("/dev", "random", qfs::Device::Create<Devices::RandomDevice>());
qfs->ForceInsert("/dev", "urandom", qfs::Device::Create<Devices::RandomDevice>());
qfs->ForceInsert("/dev", "srandom", qfs::Device::Create<Devices::SRandomDevice>());
qfs->ForceInsert("/dev", "zero", qfs::Device::Create<Devices::ZeroDevice>());
qfs->ForceInsert("/dev", "null", qfs::Device::Create<Devices::NullDevice>());
qfs->ForceInsert("/dev", "rng", qfs::Device::Create<Devices::RngDevice>());
qfs->ForceInsert("/dev", "console", Devices::ConsoleDevice::Create());
qfs->ForceInsert("/dev", "deci_tty6", Devices::DeciTty6Device::Create());
qfs->ForceInsert("/dev", "random", Devices::RandomDevice::Create());
qfs->ForceInsert("/dev", "urandom", Devices::RandomDevice::Create());
qfs->ForceInsert("/dev", "srandom", Devices::SRandomDevice::Create());
qfs->ForceInsert("/dev", "zero", Devices::ZeroDevice::Create());
qfs->ForceInsert("/dev", "null", Devices::NullDevice::Create());
qfs->ForceInsert("/dev", "rng", Devices::RngDevice::Create());
qfs->Operation.Chmod("/dev/deci_stderr", 0666);
qfs->Operation.Chmod("/dev/deci_stdout", 0666);