Finished dirents (hopefully)

This commit is contained in:
Marek Ledworowski 2025-11-15 23:52:32 +01:00
parent 9eed4385f2
commit 26fc2a54ea
4 changed files with 95 additions and 22 deletions

View File

@ -62,6 +62,7 @@ public:
virtual s64 pread(void* buf, u64 count, s64 offset) override;
// s64 pwrite(const void* buf, size_t count, u64 offset) override;
s64 lseek(s64 current, s64 offset, s32 whence) override;
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
s32 ftruncate(s64 length) final override;

View File

@ -45,8 +45,8 @@ public:
}
s64 pread(void* buf, u64 count, s64 offset) override;
s64 lseek(s64 current, s64 offset, s32 whence) override;
// s64 lseek(s64 current, s64 offset, s32 whence) override;
s64 getdents(void* buf, u32 count, s64 offset, s64* basep) override;
};

View File

@ -24,6 +24,11 @@ s64 QuasiDirectory::pread(void* buf, u64 count, s64 offset) {
return getdents(buf, count, offset, nullptr);
}
s64 QuasiDirectory::lseek(s64 current, s64 offset, s32 whence) {
RebuildDirents();
return Inode::lseek(current, offset, whence);
}
s32 QuasiDirectory::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
RebuildDirents();
*sb = st;
@ -42,20 +47,15 @@ s64 QuasiDirectory::getdents(void* buf, u32 count, s64 offset, s64* basep) {
// return always alignd final fptr to 512 bytes
// doesn't zero-out remaining space in buffer
// we're assuming this is always aligned, no check here
s64 bytes_available = this->dirent_cache_bin.size() - offset;
if (bytes_available <= 0)
return 0;
if (count > 512)
count = 512;
u64 apparent_end = count + offset;
// offset might push it too far so read count becomes misaligned
u64 apparent_end = offset + count;
u64 minimum_read = Common::AlignDown(apparent_end, 512) - offset;
u64 maximum_read = Common::AlignUp(apparent_end, 512) - offset;
u64 to_read = std::min(minimum_read, maximum_read);
if (to_read > bytes_available)
UNREACHABLE_MSG("Buffer read/write misaligned from 512 bytes");
u64 to_read = bytes_available > minimum_read ? minimum_read : bytes_available;
std::copy(dirent_cache_bin.data() + offset, dirent_cache_bin.data() + offset + to_read,
static_cast<u8*>(buf));

View File

@ -7,41 +7,65 @@
#include "core/file_sys/quasifs/quasi_errno.h"
#include "core/file_sys/quasifs/quasifs_inode_quasi_directory_pfs.h"
// PFS is a bit different from regular dirents, see comments below
// Although it's pretty simple, every tested game (sample size: 1) reads it exclusively with
// count=65536, so it doesn't need much mumbo-jambo like regular dirents
// I'll worry if a game uses something different than that count and offset=0
namespace QuasiFS {
DirectoryPFS::DirectoryPFS() {
this->st.st_size = 65536;
this->st.st_size = 65536; // pro forma, gets erased on sync. TODO: fixme
}
DirectoryPFS::~DirectoryPFS() = default;
s64 DirectoryPFS::pread(void* buf, u64 count, s64 offset) {
RebuildDirents();
memset(buf, 0, count);
// data is contiguous. i think that's how it's said
// anyway, everything goes raw
// always returns count
// always zeroes buffer
// no cut-off, will just spew out data whenever you ask for it
// aligned to 65536 bytes, everything is zeroed-out in that range
// currently not testing for anything higher than that
if (offset >= this->dirent_cache_bin.size())
return count;
s64 apparent_end = offset + count;
if (apparent_end > 65536) {
// i really don't want to do this yet
LOG_CRITICAL(Kernel_Fs,
"PFS directory size larger than 65536 bytes is not implemented yet");
if (offset > 65536)
return 0;
count = 65536 - offset;
}
s64 bytes_available = this->dirent_cache_bin.size() - offset;
// bytes_to_read but retains the same variable lmao
if (bytes_available >= count)
bytes_available = count;
if (0 >= bytes_available)
return 0;
bytes_available = bytes_available > count ? count : bytes_available;
// data
memcpy(buf, this->dirent_cache_bin.data() + offset, bytes_available);
// remainders
s64 filler = count - bytes_available;
if (filler > 0)
memset(static_cast<u8*>(buf) + bytes_available, 0, filler);
// always returns count
return count;
}
s64 DirectoryPFS::lseek(s64 current, s64 offset, s32 whence) {
RebuildDirents();
return Inode::lseek(current, offset, whence);
}
s64 DirectoryPFS::getdents(void* buf, u32 count, s64 offset, s64* basep) {
RebuildDirents();
memset(buf, 0, count);
if (count != 65536)
LOG_CRITICAL(Kernel_Fs, "PFS dirents read with count={} (which is not 65536) (report this)",
count);
// honestly i have no idea on how to implement this
// buffer behaves like a regular directory
@ -50,7 +74,52 @@ s64 DirectoryPFS::getdents(void* buf, u32 count, s64 offset, s64* basep) {
// for some reason, subsequent calls return 0
// doesn't zero-out memory after last dirent
return count;
s64 apparent_end = offset + count;
if (apparent_end > 65536) {
// i really don't want to do this yet
LOG_CRITICAL(Kernel_Fs,
"PFS directory size larger than 65536 bytes is not implemented yet");
if (offset > 65536)
return 0;
count = 65536 - offset;
}
s64 bytes_available = this->dirent_cache_bin.size() - offset;
if (0 >= bytes_available)
return 0;
bytes_available = bytes_available > count ? count : bytes_available;
u64 bytes_written = 0;
u64 dirent_offset = 0;
while (dirent_offset < bytes_available) {
const dirent_pfs_t* pfs_dirent =
reinterpret_cast<dirent_pfs_t*>(this->dirent_cache_bin.data() + bytes_written);
dirent_t normal_dirent{};
// we're transposing u32 into smaller types, so there miiight be some issues
normal_dirent.d_fileno = pfs_dirent->d_fileno;
normal_dirent.d_reclen = pfs_dirent->d_reclen;
normal_dirent.d_type = pfs_dirent->d_type;
normal_dirent.d_namlen = pfs_dirent->d_namlen;
memcpy(normal_dirent.d_name, pfs_dirent->d_name, pfs_dirent->d_namlen);
s64 bytes_remaining = bytes_available - bytes_written;
u64 to_write = 0;
if (bytes_remaining > normal_dirent.d_reclen)
to_write = normal_dirent.d_reclen;
else if (bytes_remaining > 0) {
to_write = bytes_remaining;
}
memcpy(static_cast<u8*>(buf) + bytes_written, &normal_dirent, to_write);
dirent_offset += pfs_dirent->d_reclen;
bytes_written += to_write;
}
// always returns count
return bytes_written;
}
void DirectoryPFS::RebuildDirents(void) {
@ -84,6 +153,9 @@ void DirectoryPFS::RebuildDirents(void) {
}
// directory size is always 65536 bytes
// it gets erased on FS sync in QFS, but this fn is rarely called,
// especially for PFS which is RO
this->st.st_size = 65536;
return;
}