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
42 changes: 42 additions & 0 deletions core/libs/prx/libkernel/File/src/Stdio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <optional>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

struct KernelIovec {
Expand Down Expand Up @@ -270,6 +271,7 @@ static const struct iovec* NativeIovecs(const KernelIovec* iov) {
}
#endif

static constexpr int GUEST_EPERM = 1;
static constexpr int GUEST_ENOENT = 2;
static constexpr int GUEST_EIO = 5;
static constexpr int GUEST_EBADF = 9;
Expand All @@ -281,6 +283,7 @@ static constexpr int GUEST_ENAMETOOLONG = 63;
static constexpr int GUEST_ENOTDIR = 20;
static constexpr int GUEST_EISDIR = 21;
static constexpr int GUEST_EWOULDBLOCK = 35;
static constexpr int GUEST_EOPNOTSUPP = 45;
static constexpr int GUEST_ENOTEMPTY = 66;

static int SceErrorFromErrno(int error);
Expand Down Expand Up @@ -432,6 +435,33 @@ static bool HasNonDirectoryPrefix(const std::filesystem::path& native) {
return false;
}

static int LookupError(const std::filesystem::path& native) {
std::error_code error;
const auto status = std::filesystem::status(native, error);
if (std::filesystem::exists(status)) return native.has_filename() || std::filesystem::is_directory(status) ? 0 : GUEST_ENOTDIR;
if (status.type() != std::filesystem::file_type::not_found) return SceErrorFromErrno(error.default_error_condition().value()) & 0xffff;
const bool fileWithSeparator = !native.has_filename() && std::filesystem::exists(WithoutTrailingSeparator(native), error);
return fileWithSeparator || HasNonDirectoryPrefix(native) ? GUEST_ENOTDIR : GUEST_ENOENT;
}

static constexpr std::pair<std::uint64_t, const char*> GUEST_FILE_FLAGS[] = {
{0x00000001, "UF_NODUMP"}, {0x00000002, "UF_IMMUTABLE"}, {0x00000004, "UF_APPEND"}, {0x00000008, "UF_OPAQUE"},
{0x00000010, "UF_NOUNLINK"}, {0x00000080, "UF_SYSTEM"}, {0x00000100, "UF_SPARSE"}, {0x00000200, "UF_OFFLINE"},
{0x00000400, "UF_REPARSE"}, {0x00000800, "UF_ARCHIVE"}, {0x00001000, "UF_READONLY"}, {0x00008000, "UF_HIDDEN"},
{0x00010000, "SF_ARCHIVED"}, {0x00020000, "SF_IMMUTABLE"}, {0x00040000, "SF_APPEND"}, {0x00100000, "SF_NOUNLINK"},
{0x00200000, "SF_SNAPSHOT"}};
static constexpr std::uint64_t GUEST_SF_SNAPSHOT = 0x00200000;

static std::string FileFlagNames(std::uint64_t flags) {
std::string names;
for (const auto& [flag, name] : GUEST_FILE_FLAGS) {
if ((flags & flag) == 0) continue;
if (!names.empty()) names += '|';
names += name;
}
return names;
}

extern "C" {

int APS5_VABI chmod_nid_postfix(const char* path, int mode) {
Expand All @@ -441,6 +471,18 @@ int APS5_VABI chmod_nid_postfix(const char* path, int mode) {
return 0;
}

int APS5_VABI chflags_nid_postfix(const char* path, std::uint64_t flags) {
if (const int error = PathError(path)) return PosixFailure(error);
if (const int error = LookupError(ResolvePath_nid_no_patch(path))) return PosixFailure(error);
std::uint64_t known = 0;
for (const auto& entry : GUEST_FILE_FLAGS) known |= entry.first;
if ((flags & ~known) != 0) return PosixFailure(GUEST_EOPNOTSUPP);
if ((flags & GUEST_SF_SNAPSHOT) != 0) return PosixFailure(GUEST_EPERM);
if (flags != 0)
throw std::runtime_error("chflags: setting " + FileFlagNames(flags) + " on " + path + " is not supported, the host keeps no BSD file flags");
return 0;
}

int APS5_VABI close_nid_postfix(int d) {
if (d >= GuestSockets::FirstDescriptor) return GuestSockets::Close(d);
File::ForgetRandomDevice(d);
Expand Down
19 changes: 19 additions & 0 deletions core/libs/prx/libkernel/System/src/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,13 @@ void validateSchedulingPolicy(int policy) {
throw std::invalid_argument("Unsupported guest scheduling policy");
}

constexpr int guestNoProcess = 3;
constexpr int guestFault = 14;
constexpr int guestInvalid = 22;
constexpr int guestPriorityProcess = 0;
constexpr int guestPriorityGroup = 1;
constexpr int guestPriorityUser = 2;
constexpr int guestNice = 0;
constexpr int guestRlimitData = 2;
constexpr int guestRlimitCount = 15;
constexpr std::int64_t guestRlimitInfinity = std::numeric_limits<std::int64_t>::max();
Expand Down Expand Up @@ -358,6 +363,20 @@ int APS5_VABI sched_get_priority_min_nid_postfix(int policy) {
return 767;
}

int APS5_VABI getpriority_nid_postfix(int which, int who) {
if (which != guestPriorityProcess && which != guestPriorityGroup && which != guestPriorityUser) {
*__error_nid_postfix() = guestInvalid;
return -1;
}
const bool found = which == guestPriorityUser ? (who == 0 ? getuid_nid_postfix() : who) == getuid_nid_postfix()
: who == 0 || who == getpid_nid_postfix();
if (!found) {
*__error_nid_postfix() = guestNoProcess;
return -1;
}
return guestNice;
}

int APS5_VABI getrusage_nid_postfix(int who, GuestResourceUsage* usage) {
if (usage == nullptr)
throw std::invalid_argument("getrusage: usage is null");
Expand Down
16 changes: 16 additions & 0 deletions core/libs/prx/libkernel/Time/Time.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@

extern "C" int* APS5_VABI __error_nid_postfix();

static constexpr int GUEST_EFAULT = 14;
static constexpr int GUEST_EINVAL = 22;
static constexpr int GUEST_CLOCK_REALTIME = 0;
static constexpr int GUEST_CLOCK_THREAD_CPUTIME_ID = 14;

static bool IsCpuClock(int clockId) {
Expand Down Expand Up @@ -555,6 +557,20 @@ int APS5_VABI clock_getres_nid_postfix(int clockId, KernelTimespec* res) {
#endif
}

int APS5_VABI clock_settime_nid_postfix(int clockId, const KernelTimespec* tp) {
if (tp == nullptr) {
*__error_nid_postfix() = GUEST_EFAULT;
return -1;
}
if (clockId != GUEST_CLOCK_REALTIME || tp->tv_nsec < 0 || tp->tv_nsec >= 1000000000 || tp->tv_sec < 0) {
*__error_nid_postfix() = GUEST_EINVAL;
return -1;
}
char requested[48];
std::snprintf(requested, sizeof(requested), "%lld.%09lld", static_cast<long long>(tp->tv_sec), static_cast<long long>(tp->tv_nsec));
throw std::runtime_error(std::string("clock_settime: setting CLOCK_REALTIME to ") + requested + " is not supported, the host clock is not changed");
}

// ---------------------------------------------------------------------------
// Moved as-is (not yet implemented) from the monolithic libkernel/Export.cpp.
// ---------------------------------------------------------------------------
Expand Down
22 changes: 22 additions & 0 deletions core/libs/prx/libkernel_sys/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ constexpr int GuestEnoent = 2;
constexpr int GuestEperm = 1;
constexpr int GuestEio = 5;
constexpr int GuestEfault = 14;
constexpr int GuestEnotdir = 20;
constexpr int GuestEinval = 22;
constexpr int GuestEexist = 17;
constexpr int GuestExdev = 18;
Expand All @@ -42,6 +43,15 @@ int GuestError(const std::error_code& error) {
const int value = error.default_error_condition().value();
return value > 0 && value <= 34 ? value : GuestEio;
}

bool HasFileAncestor(const std::filesystem::path& native) {
std::error_code error;
for (auto prefix = native.parent_path(); prefix.has_relative_path(); prefix = prefix.parent_path()) {
const auto status = std::filesystem::status(prefix, error);
if (std::filesystem::exists(status)) return !std::filesystem::is_directory(status);
}
return false;
}
}

extern "C" {
Expand Down Expand Up @@ -114,6 +124,18 @@ int APS5_VABI fchdir_nid_postfix(int fd) {
return 0;
}

int APS5_VABI chroot_nid_postfix(const char* path) {
if (path == nullptr) return static_cast<int>(Fail(GuestEfault));
if (*path == '\0') return static_cast<int>(Fail(GuestEnoent));
const auto native = ResolvePath_nid_no_patch(path);
std::error_code error;
const auto status = std::filesystem::status(native, error);
if (status.type() == std::filesystem::file_type::not_found) return static_cast<int>(Fail(HasFileAncestor(native) ? GuestEnotdir : GuestEnoent));
if (error) return static_cast<int>(Fail(GuestError(error)));
if (!std::filesystem::is_directory(status)) return static_cast<int>(Fail(GuestEnotdir));
throw std::runtime_error(std::string("chroot: changing the root directory to ") + path + " is not supported, the guest path resolver keeps a fixed root");
}

int APS5_VABI symlink_nid_postfix(const char* target, const char* created) {
if (target == nullptr || created == nullptr) return static_cast<int>(Fail(GuestEfault));
if (*target == '\0' || *created == '\0') return static_cast<int>(Fail(GuestEnoent));
Expand Down
24 changes: 24 additions & 0 deletions core/libs/tests/GuestFilesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ int APS5_VABI sceKernelChmod_nid_postfix(const char*, unsigned short);
int APS5_VABI sceKernelFchmod(int, unsigned short);
int APS5_VABI fchmod_nid_postfix(int, int);
int APS5_VABI chmod_nid_postfix(const char*, int);
int APS5_VABI chflags_nid_postfix(const char*, std::uint64_t);
int APS5_VABI futimes_nid_postfix(int, const KernelTimeval*);
int APS5_VABI sceKernelFutimes(int, const KernelTimeval*);
int APS5_VABI socket_nid_postfix(int, int, int);
Expand Down Expand Up @@ -76,6 +77,14 @@ static void Check(bool value, int line) {
}
}
#define Require(value) Check((value), __LINE__)
static bool ChflagsThrows(const std::filesystem::path& path, std::uint64_t flags, const char* names) {
try {
chflags_nid_postfix(path.string().c_str(), flags);
} catch (const std::runtime_error& error) {
return std::strstr(error.what(), names) != nullptr;
}
return false;
}
int main() {
Require(chmod_nid_postfix(nullptr, 0600) == -1 && *__error_nid_postfix() == 14);
Require(sceKernelChmod_nid_postfix(nullptr, 0600) == static_cast<int>(0x8002000eu));
Expand Down Expand Up @@ -146,6 +155,21 @@ int main() {
{ std::ofstream stream(readOnly); stream << "removable"; }
Require(sceKernelChmod_nid_postfix(readOnly.string().c_str(), 0400) == 0);
Require(remove_nid_postfix(readOnly.string().c_str()) == 0 && !std::filesystem::exists(readOnly));
Require(chflags_nid_postfix(nullptr, 0) == -1 && *__error_nid_postfix() == 14);
Require(chflags_nid_postfix("", 0) == -1 && *__error_nid_postfix() == 2);
Require(chflags_nid_postfix((root / "missing").string().c_str(), 0) == -1 && *__error_nid_postfix() == 2);
Require(chflags_nid_postfix((root / "missing").string().c_str(), ~0ULL) == -1 && *__error_nid_postfix() == 2);
Require(chflags_nid_postfix((file / "child").string().c_str(), 0) == -1 && *__error_nid_postfix() == 20);
Require(chflags_nid_postfix((file.string() + "/").c_str(), 0) == -1 && *__error_nid_postfix() == 20);
Require(chflags_nid_postfix(file.string().c_str(), 0) == 0);
Require(chflags_nid_postfix(root.string().c_str(), 0) == 0);
Require(chflags_nid_postfix(file.string().c_str(), 0x20) == -1 && *__error_nid_postfix() == 45);
Require(chflags_nid_postfix(file.string().c_str(), 1ULL << 32) == -1 && *__error_nid_postfix() == 45);
Require(chflags_nid_postfix(file.string().c_str(), ~0ULL) == -1 && *__error_nid_postfix() == 45);
Require(chflags_nid_postfix(file.string().c_str(), 0x200000) == -1 && *__error_nid_postfix() == 1);
Require(ChflagsThrows(file, 0x2, "UF_IMMUTABLE"));
Require(ChflagsThrows(file, 0x1000 | 0x40000, "UF_READONLY|SF_APPEND"));
Require(std::filesystem::is_regular_file(file));
const auto renamed = root / "renamed.txt";
{ std::ofstream stream(renamed); stream << "old contents"; }
Require(rename_nid_postfix(file.string().c_str(), renamed.string().c_str()) == 0);
Expand Down
40 changes: 40 additions & 0 deletions core/libs/tests/GuestFreebsdClocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
#include <array>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <stdexcept>

extern "C" {
int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp);
int APS5_VABI clock_getres_nid_postfix(int clockId, KernelTimespec* res);
int APS5_VABI clock_settime_nid_postfix(int clockId, const KernelTimespec* tp);
int* APS5_VABI __error_nid_postfix();
int APS5_VABI sceKernelClockGettime(KernelClockid clockId, KernelTimespec* tp);
int APS5_VABI sceKernelClockGetres(KernelClockid clockId, KernelTimespec* tp);
int APS5_VABI sceKernelUsleep_nid_postfix(KernelUseconds microseconds);
Expand All @@ -21,7 +25,9 @@ static constexpr int GUEST_CLOCK_REALTIME = 0;
static constexpr int GUEST_CLOCK_VIRTUAL = 1;
static constexpr int GUEST_CLOCK_PROF = 2;
static constexpr int GUEST_CLOCK_MONOTONIC = 4;
static constexpr int GUEST_CLOCK_REALTIME_PRECISE = 9;
static constexpr int GUEST_CLOCK_SECOND = 13;
static constexpr int GUEST_CLOCK_THREAD_CPUTIME_ID = 14;
static constexpr int GUEST_CLOCK_PROCESS_CPUTIME_ID = 15;

static constexpr std::int64_t NANOS_PER_SECOND = 1000000000LL;
Expand Down Expand Up @@ -167,7 +173,41 @@ static void CalendarConversionsInitializeTheirState() {
Require(sceKernelConvertUtcToLocaltime(0, nullptr, nullptr, nullptr) == SCE_OK);
}

static void RequireSettimeError(int clockId, const KernelTimespec* time, int error) {
*__error_nid_postfix() = 0;
Require(clock_settime_nid_postfix(clockId, time) == -1 && *__error_nid_postfix() == error);
}

static bool SettimeThrows(const KernelTimespec& time, const char* requested) {
try {
clock_settime_nid_postfix(GUEST_CLOCK_REALTIME, &time);
} catch (const std::runtime_error& error) {
return std::strstr(error.what(), requested) != nullptr;
}
return false;
}

static void SettimeValidatesLikeFreebsd() {
const KernelTimespec valid{1767225600, 5};
RequireSettimeError(GUEST_CLOCK_REALTIME, nullptr, 14);
RequireSettimeError(GUEST_CLOCK_MONOTONIC, nullptr, 14);
for (const int clockId : {GUEST_CLOCK_VIRTUAL, GUEST_CLOCK_PROF, GUEST_CLOCK_MONOTONIC, GUEST_CLOCK_REALTIME_PRECISE,
GUEST_CLOCK_SECOND, GUEST_CLOCK_THREAD_CPUTIME_ID, GUEST_CLOCK_PROCESS_CPUTIME_ID, -1, 16})
RequireSettimeError(clockId, &valid, 22);
const KernelTimespec negativeNanoseconds{0, -1};
const KernelTimespec wholeSecondOfNanoseconds{0, NANOS_PER_SECOND};
const KernelTimespec negativeSeconds{-1, 0};
RequireSettimeError(GUEST_CLOCK_REALTIME, &negativeNanoseconds, 22);
RequireSettimeError(GUEST_CLOCK_REALTIME, &wholeSecondOfNanoseconds, 22);
RequireSettimeError(GUEST_CLOCK_REALTIME, &negativeSeconds, 22);
RequireSettimeError(GUEST_CLOCK_MONOTONIC, &negativeSeconds, 22);
Require(SettimeThrows(valid, "1767225600.000000005"));
Require(SettimeThrows(KernelTimespec{0, 0}, "0.000000000"));
Require(SettimeThrows(KernelTimespec{1, NANOS_PER_SECOND - 1}, "1.999999999"));
}

int main() {
SettimeValidatesLikeFreebsd();
CalendarConversionsInitializeTheirState();
SecondClockReportsWholeSeconds();
KernelTimezone zone{-1, -1};
Expand Down
21 changes: 21 additions & 0 deletions core/libs/tests/GuestKernelSys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <filesystem>
#include <fstream>
#include <source_location>
#include <stdexcept>
#include <string>
#ifdef __linux__
#include <cerrno>
Expand All @@ -22,6 +23,7 @@ int APS5_VABI close_nid_postfix(int);
std::int64_t APS5_VABI readlink_nid_postfix(const char*, char*, std::size_t);
int APS5_VABI fchdir_nid_postfix(int);
int APS5_VABI chdir_nid_postfix(const char*);
int APS5_VABI chroot_nid_postfix(const char*);
char* APS5_VABI getcwd_nid_postfix(char*, std::size_t);
int APS5_VABI sceKernelOpen(const char*, int, std::uint16_t);
int APS5_VABI sceKernelClose(int);
Expand All @@ -43,6 +45,15 @@ int Error() {
return *__error_nid_postfix();
}

bool ChrootThrows(const std::string& path) {
try {
chroot_nid_postfix(path.c_str());
} catch (const std::runtime_error& error) {
return std::strstr(error.what(), path.c_str()) != nullptr;
}
return false;
}

int main() {
Require(setsid_nid_postfix() == -1 && Error() == 1);
Require(umask_nid_postfix(0) == 022);
Expand Down Expand Up @@ -127,6 +138,16 @@ int main() {
Require(sceKernelClose(directory) == 0);
Require(fchdir_nid_postfix(directory) == -1 && Error() == 9);
Require(chdir_nid_postfix("/") == 0);
Require(chroot_nid_postfix(nullptr) == -1 && Error() == 14);
Require(chroot_nid_postfix("") == -1 && Error() == 2);
Require(chroot_nid_postfix((root / "missing").string().c_str()) == -1 && Error() == 2);
Require(chroot_nid_postfix(file.c_str()) == -1 && Error() == 20);
Require(chroot_nid_postfix((root / "file.txt" / "child").string().c_str()) == -1 && Error() == 20);
Require(chroot_nid_postfix((file + "/").c_str()) == -1 && Error() == 20);
Require(ChrootThrows(root.string()));
Require(ChrootThrows("/"));
Require(getcwd_nid_postfix(directoryPath, sizeof(directoryPath)) == directoryPath && std::strcmp(directoryPath, "/") == 0);
Require(readlink_nid_postfix(file.c_str(), buffer, sizeof(buffer)) == -1 && Error() == 22);
const auto symbolic = (root / "symbolic").string();
const int created = symlink_nid_postfix("original.bin", symbolic.c_str());
if (created == 0) {
Expand Down
26 changes: 26 additions & 0 deletions core/libs/tests/GuestSchedPriority.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,42 @@
extern "C" {
int APS5_VABI sched_get_priority_max_nid_postfix(int policy);
int APS5_VABI sched_get_priority_min_nid_postfix(int policy);
int APS5_VABI getpriority_nid_postfix(int which, int who);
int APS5_VABI getpid_nid_postfix(void);
int* APS5_VABI __error_nid_postfix();
}
static constexpr int PrioProcess = 0;
static constexpr int PrioGroup = 1;
static constexpr int PrioUser = 2;
static void Require(bool value) { if (!value) std::abort(); }
static bool Rejects(int policy) {
try { sched_get_priority_max_nid_postfix(policy); } catch (const std::invalid_argument&) { return true; }
return false;
}
static void RequireNice(int which, int who) {
*__error_nid_postfix() = 13;
Require(getpriority_nid_postfix(which, who) == 0 && *__error_nid_postfix() == 13);
}
static void RequireError(int which, int who, int error) {
*__error_nid_postfix() = 0;
Require(getpriority_nid_postfix(which, who) == -1 && *__error_nid_postfix() == error);
}
int main() {
for (int policy : {1, 2, 3}) {
Require(sched_get_priority_max_nid_postfix(policy) == 256);
Require(sched_get_priority_min_nid_postfix(policy) == 767);
}
Require(Rejects(0) && Rejects(4));
const int self = getpid_nid_postfix();
RequireNice(PrioProcess, 0);
RequireNice(PrioProcess, self);
RequireNice(PrioGroup, 0);
RequireNice(PrioGroup, self);
RequireNice(PrioUser, 0);
RequireError(PrioProcess, self + 1, 3);
RequireError(PrioProcess, -1, 3);
RequireError(PrioGroup, self + 1, 3);
RequireError(PrioUser, 1000, 3);
RequireError(3, 0, 22);
RequireError(-1, self, 22);
}
Loading
Loading