Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion core/libs/prx/libkernel/File/src/NativeStat.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#include "prx/libkernel/File/include/NativeStat.hpp"
#include "prx/libkernel/File/include/DirectoryDescriptor.hpp"

#include <cerrno>
#include <cstdint>
#include <stdexcept>
#include <string>

#ifdef _WIN32
#include <windows.h>
#include <io.h>
#include <sys/stat.h>
#include <sys/types.h>
using NativeStat = struct __stat64;
Expand All @@ -15,6 +19,39 @@ static int DoFstat(int fd, NativeStat* st) {
if (const auto directory = File::DirectoryDescriptorPath(fd)) return DoStat(*directory, st);
return _fstat64(fd, st);
}
static bool CopyHandleIdentity(HANDLE handle, FileStat* sb) {
BY_HANDLE_FILE_INFORMATION info{};
if (!::GetFileInformationByHandle(handle, &info)) {
errno = EIO;
return false;
}
const auto index = (static_cast<std::uint64_t>(info.nFileIndexHigh) << 32) | info.nFileIndexLow;
sb->st_dev = static_cast<std::uint32_t>(info.dwVolumeSerialNumber);
sb->st_ino = static_cast<std::uint32_t>(index ^ (index >> 32));
sb->st_nlink = static_cast<std::uint16_t>(info.nNumberOfLinks > 0xffff ? 0xffff : info.nNumberOfLinks);
return true;
}
static bool CopyPathIdentity(const std::filesystem::path& p, FileStat* sb) {
const auto handle = ::CreateFileW(p.wstring().c_str(), FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
errno = EIO;
return false;
}
const bool copied = CopyHandleIdentity(handle, sb);
::CloseHandle(handle);
return copied;
}
static bool CopyDescriptorIdentity(int fd, FileStat* sb) {
if (const auto directory = File::DirectoryDescriptorPath(fd)) return CopyPathIdentity(*directory, sb);
const auto handle = reinterpret_cast<HANDLE>(::_get_osfhandle(fd));
if (handle == INVALID_HANDLE_VALUE) {
errno = EBADF;
return false;
}
if (::GetFileType(handle) != FILE_TYPE_DISK) return true;
return CopyHandleIdentity(handle, sb);
}
#else
#include <sys/stat.h>
using NativeStat = struct stat;
Expand All @@ -24,6 +61,12 @@ static int DoStat(const std::filesystem::path& p, NativeStat* st) {
static int DoFstat(int fd, NativeStat* st) {
return ::fstat(fd, st);
}
static bool CopyPathIdentity(const std::filesystem::path&, FileStat*) {
return true;
}
static bool CopyDescriptorIdentity(int, FileStat*) {
return true;
}
#endif

static void CopyNativeStat(const NativeStat& st, FileStat* sb) {
Expand Down Expand Up @@ -82,6 +125,9 @@ void FillFileStat(const std::filesystem::path& nativePath, FileStat* sb) {
throw std::runtime_error(std::string("FillFileStat: stat failed for ") + nativePath.string());
}
CopyNativeStat(st, sb);
if (!CopyPathIdentity(nativePath, sb)) {
throw std::runtime_error(std::string("FillFileStat: file identity failed for ") + nativePath.string());
}
}

void FillFileStat(int nativeDescriptor, FileStat* sb) {
Expand All @@ -90,13 +136,16 @@ void FillFileStat(int nativeDescriptor, FileStat* sb) {
throw std::runtime_error(std::string("FillFileStat: fstat failed for fd ") + std::to_string(nativeDescriptor));
}
CopyNativeStat(st, sb);
if (!CopyDescriptorIdentity(nativeDescriptor, sb)) {
throw std::runtime_error(std::string("FillFileStat: file identity failed for fd ") + std::to_string(nativeDescriptor));
}
}

bool FillFileStatFromDescriptor(int fd, FileStat* sb) {
NativeStat st{};
if (DoFstat(fd, &st) != 0) return false;
CopyNativeStat(st, sb);
return true;
return CopyDescriptorIdentity(fd, sb);
}

}
13 changes: 13 additions & 0 deletions core/libs/tests/GuestFilesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ int APS5_VABI mkdir_nid_postfix(const char*, unsigned short);
int APS5_VABI sceKernelOpen(const char*, int, unsigned short);
int APS5_VABI sceKernelClose(int);
int APS5_VABI sceKernelStat(const char*, FileStat*);
int APS5_VABI sceKernelFstat(int, FileStat*);
int APS5_VABI sceKernelUnlink(const char*);
int APS5_VABI sceKernelRmdir(const char*);
int* APS5_VABI __error_nid_postfix();
Expand Down Expand Up @@ -202,6 +203,18 @@ int main() {
Require(closable >= 0 && sceKernelClose(closable) == 0);
Require(sceKernelClose(closable) == static_cast<int>(0x80020009u));
Require(sceKernelClose(-1) == static_cast<int>(0x80020009u));
const auto otherName = (root / "other.txt").string();
{ std::ofstream stream(otherName); stream << "other"; }
FileStat byPath{};
FileStat otherByPath{};
FileStat byDescriptor{};
Require(sceKernelStat(presentName.c_str(), &byPath) == 0);
Require(sceKernelStat(otherName.c_str(), &otherByPath) == 0);
Require(byPath.st_dev == otherByPath.st_dev && byPath.st_ino != otherByPath.st_ino);
const int identified = sceKernelOpen(presentName.c_str(), 0, 0);
Require(identified >= 0 && sceKernelFstat(identified, &byDescriptor) == 0 && sceKernelClose(identified) == 0);
Require(byDescriptor.st_dev == byPath.st_dev && byDescriptor.st_ino == byPath.st_ino);
Require(unlink_nid_postfix(otherName.c_str()) == 0);
Require(unlink_nid_postfix(presentName.c_str()) == 0 && !std::filesystem::exists(present));
const auto empty = root / "empty";
Require(std::filesystem::create_directory(empty));
Expand Down
Loading