Filesystem: Directory-related fixes (#3416)

* Various GetDents fixes

Fixed return and parameter types, and made the function return all the entries that will fit in nbytes during one call.

* Fstat dir stub changes

Changes the returned statistics to match what my PS4 tests generally show.

* Stat dir stub changes

To match my fstat changes
This commit is contained in:
Stephen Miller
2025-08-14 06:50:20 -05:00
committed by GitHub
parent 2c906dc280
commit a285543134

View File

@@ -606,9 +606,9 @@ s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) {
} }
if (std::filesystem::is_directory(path_name)) { if (std::filesystem::is_directory(path_name)) {
sb->st_mode = 0000777u | 0040000u; sb->st_mode = 0000777u | 0040000u;
sb->st_size = 0; sb->st_size = 65536;
sb->st_blksize = 512; sb->st_blksize = 65536;
sb->st_blocks = 0; sb->st_blocks = 128;
// TODO incomplete // TODO incomplete
} else { } else {
sb->st_mode = 0000777u | 0100000u; sb->st_mode = 0000777u | 0100000u;
@@ -678,9 +678,9 @@ s32 PS4_SYSV_ABI fstat(s32 fd, OrbisKernelStat* sb) {
} }
case Core::FileSys::FileType::Directory: { case Core::FileSys::FileType::Directory: {
sb->st_mode = 0000777u | 0040000u; sb->st_mode = 0000777u | 0040000u;
sb->st_size = 0; sb->st_size = 65536;
sb->st_blksize = 512; sb->st_blksize = 65536;
sb->st_blocks = 0; sb->st_blocks = 128;
// TODO incomplete // TODO incomplete
break; break;
} }
@@ -890,7 +890,7 @@ s32 PS4_SYSV_ABI sceKernelFsync(s32 fd) {
return result; return result;
} }
static s32 GetDents(s32 fd, char* buf, s32 nbytes, s64* basep) { static s64 GetDents(s32 fd, char* buf, u64 nbytes, s64* basep) {
if (buf == nullptr) { if (buf == nullptr) {
*__Error() = POSIX_EFAULT; *__Error() = POSIX_EFAULT;
return -1; return -1;
@@ -901,6 +901,7 @@ static s32 GetDents(s32 fd, char* buf, s32 nbytes, s64* basep) {
*__Error() = POSIX_EBADF; *__Error() = POSIX_EBADF;
return -1; return -1;
} }
if (file->type == Core::FileSys::FileType::Device) { if (file->type == Core::FileSys::FileType::Device) {
s32 result = file->device->getdents(buf, nbytes, basep); s32 result = file->device->getdents(buf, nbytes, basep);
if (result < 0) { if (result < 0) {
@@ -910,39 +911,49 @@ static s32 GetDents(s32 fd, char* buf, s32 nbytes, s64* basep) {
return result; return result;
} }
if (file->dirents_index == file->dirents.size()) {
return ORBIS_OK;
}
if (file->type != Core::FileSys::FileType::Directory || nbytes < 512 || if (file->type != Core::FileSys::FileType::Directory || nbytes < 512 ||
file->dirents_index > file->dirents.size()) { file->dirents_index > file->dirents.size()) {
*__Error() = POSIX_EINVAL; *__Error() = POSIX_EINVAL;
return -1; return -1;
} }
s64 bytes_to_write = nbytes;
char* buf_to_write = buf;
u64 bytes_written = 0;
while (bytes_to_write >= sizeof(OrbisKernelDirent)) {
if (file->dirents_index == file->dirents.size()) {
break;
}
const auto& entry = file->dirents.at(file->dirents_index++); const auto& entry = file->dirents.at(file->dirents_index++);
auto str = entry.name; auto str = entry.name;
static int fileno = 1000; // random static int fileno = 1000; // random
OrbisKernelDirent* sce_ent = (OrbisKernelDirent*)buf; OrbisKernelDirent* sce_ent = (OrbisKernelDirent*)buf_to_write;
sce_ent->d_fileno = fileno++; // TODO this should be unique but atm it changes maybe switch to a // TODO this should be unique, maybe switch to a hash or something?
// hash or something? sce_ent->d_fileno = fileno++;
sce_ent->d_reclen = sizeof(OrbisKernelDirent); sce_ent->d_reclen = sizeof(OrbisKernelDirent);
sce_ent->d_type = (entry.isFile ? 8 : 4); sce_ent->d_type = (entry.isFile ? 8 : 4);
sce_ent->d_namlen = str.size(); sce_ent->d_namlen = str.size();
strncpy(sce_ent->d_name, str.c_str(), ORBIS_MAX_PATH); strncpy(sce_ent->d_name, str.c_str(), ORBIS_MAX_PATH);
sce_ent->d_name[ORBIS_MAX_PATH] = '\0'; sce_ent->d_name[ORBIS_MAX_PATH] = '\0';
buf_to_write += sizeof(OrbisKernelDirent);
bytes_to_write -= sizeof(OrbisKernelDirent);
bytes_written += sizeof(OrbisKernelDirent);
}
if (basep != nullptr) { if (basep != nullptr) {
*basep = file->dirents_index; *basep = file->dirents_index;
} }
return sizeof(OrbisKernelDirent); return bytes_written;
} }
s32 PS4_SYSV_ABI posix_getdents(s32 fd, char* buf, s32 nbytes) { s64 PS4_SYSV_ABI posix_getdents(s32 fd, char* buf, u64 nbytes) {
return GetDents(fd, buf, nbytes, nullptr); return GetDents(fd, buf, nbytes, nullptr);
} }
s32 PS4_SYSV_ABI sceKernelGetdents(s32 fd, char* buf, s32 nbytes) { s64 PS4_SYSV_ABI sceKernelGetdents(s32 fd, char* buf, u64 nbytes) {
s32 result = GetDents(fd, buf, nbytes, nullptr); s64 result = posix_getdents(fd, buf, nbytes);
if (result < 0) { if (result < 0) {
LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); LOG_ERROR(Kernel_Fs, "error = {}", *__Error());
return ErrnoToSceKernelError(*__Error()); return ErrnoToSceKernelError(*__Error());
@@ -950,16 +961,16 @@ s32 PS4_SYSV_ABI sceKernelGetdents(s32 fd, char* buf, s32 nbytes) {
return result; return result;
} }
s32 PS4_SYSV_ABI getdirentries(s32 fd, char* buf, s32 nbytes, s64* basep) { s64 PS4_SYSV_ABI getdirentries(s32 fd, char* buf, u64 nbytes, s64* basep) {
return GetDents(fd, buf, nbytes, basep); return GetDents(fd, buf, nbytes, basep);
} }
s32 PS4_SYSV_ABI posix_getdirentries(s32 fd, char* buf, s32 nbytes, s64* basep) { s64 PS4_SYSV_ABI posix_getdirentries(s32 fd, char* buf, u64 nbytes, s64* basep) {
return GetDents(fd, buf, nbytes, basep); return GetDents(fd, buf, nbytes, basep);
} }
s32 PS4_SYSV_ABI sceKernelGetdirentries(s32 fd, char* buf, s32 nbytes, s64* basep) { s64 PS4_SYSV_ABI sceKernelGetdirentries(s32 fd, char* buf, u64 nbytes, s64* basep) {
s32 result = GetDents(fd, buf, nbytes, basep); s64 result = GetDents(fd, buf, nbytes, basep);
if (result < 0) { if (result < 0) {
LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); LOG_ERROR(Kernel_Fs, "error = {}", *__Error());
return ErrnoToSceKernelError(*__Error()); return ErrnoToSceKernelError(*__Error());