diff --git a/src/libs/libNet.cpp b/src/libs/libNet.cpp index 7f5cc120c..84733440a 100644 --- a/src/libs/libNet.cpp +++ b/src/libs/libNet.cpp @@ -3138,9 +3138,21 @@ namespace LibNpWebApi2 { LIB_VERSION("NpWebApi2", 1, "NpWebApi2", 1, 1); -constexpr int NP_WEBAPI2_ERROR_INVALID_ARGUMENT = -2141899774; /* 0x80553402 */ -constexpr int NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND = -2141899770; /* 0x80553406 */ -constexpr int NP_WEBAPI2_ERROR_NOT_SIGNED_IN = -2141899769; /* 0x80553407 */ +constexpr int NP_WEBAPI2_ERROR_INVALID_ARGUMENT = -2141899774; /* 0x80553402 */ +constexpr int NP_WEBAPI2_ERROR_INVALID_LIB_CONTEXT_ID = -2141899773; /* 0x80553403 */ +constexpr int NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND = -2141899772; /* 0x80553404 */ +constexpr int NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND = -2141899770; /* 0x80553406 */ +constexpr int NP_WEBAPI2_ERROR_NOT_SIGNED_IN = -2141899769; /* 0x80553407 */ + +struct NpWebApi2MemoryPoolStats { + size_t pool_size; + size_t max_inuse_size; + size_t current_inuse_size; + int32_t reserved; +}; + +static std::mutex g_np_webapi2_context_mutex; +static std::map g_np_webapi2_contexts; struct NpWebApi2ResponseInformationOption { int32_t http_status; @@ -3171,7 +3183,30 @@ static int KYTY_SYSV_ABI NpWebApi2Initialize(int lib_http_ctx_id, size_t pool_si static int id = 0; - return ++id; + if (pool_size > std::numeric_limits::max() - 0x3fff) { + return NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + std::lock_guard lock(g_np_webapi2_context_mutex); + g_np_webapi2_contexts[++id] = (pool_size + 0x3fff) & ~size_t {0x3fff}; + return id; +} + +static int KYTY_SYSV_ABI NpWebApi2GetMemoryPoolStats(int lib_ctx_id, + NpWebApi2MemoryPoolStats* stats) { + if (stats == nullptr) { + return NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + if (lib_ctx_id <= 0) { + return NP_WEBAPI2_ERROR_INVALID_LIB_CONTEXT_ID; + } + std::lock_guard lock(g_np_webapi2_context_mutex); + const auto context = g_np_webapi2_contexts.find(lib_ctx_id); + if (context == g_np_webapi2_contexts.end()) { + return NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + // Requests currently use host storage, not allocations from the library pool. + *stats = {context->second, 0, 0, 0}; + return OK; } static int KYTY_SYSV_ABI NpWebApi2PushEventCreateHandle(int lib_ctx_id) { @@ -3438,11 +3473,16 @@ static int KYTY_SYSV_ABI NpWebApi2Terminate(int lib_ctx_id) { LOGF("\t lib_ctx_id = %d\n", lib_ctx_id); - return 0; + if (lib_ctx_id <= 0) { + return NP_WEBAPI2_ERROR_INVALID_LIB_CONTEXT_ID; + } + std::lock_guard lock(g_np_webapi2_context_mutex); + return g_np_webapi2_contexts.erase(lib_ctx_id) != 0 ? OK : NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; } LIB_DEFINE(InitNet_1_NpWebApi2) { LIB_FUNC("+o9816YQhqQ", LibNpWebApi2::NpWebApi2Initialize); + LIB_FUNC("Xweb+naPZ8Y", LibNpWebApi2::NpWebApi2GetMemoryPoolStats); LIB_FUNC("WV1GwM32NgY", LibNpWebApi2::NpWebApi2PushEventCreateHandle); LIB_FUNC("sk54bi6FtYM", LibNpWebApi2::NpWebApi2CreateUserContext); LIB_FUNC("9X9+cneTGUU", LibNpWebApi2::NpWebApi2DeleteUserContext); diff --git a/tests/KernelFileSystemTests.cpp b/tests/KernelFileSystemTests.cpp index 6dd315239..1faf2ecd9 100644 --- a/tests/KernelFileSystemTests.cpp +++ b/tests/KernelFileSystemTests.cpp @@ -47,6 +47,10 @@ namespace Libs::LibNet { void InitNet_1_Net(Loader::SymbolDatabase *symbols); } +namespace Libs::LibNpWebApi2 { +void InitNet_1_NpWebApi2(Loader::SymbolDatabase *symbols); +} + namespace { namespace FileSystem = Libs::LibKernel::FileSystem; @@ -212,6 +216,43 @@ void TestAioBatches() { Check(FileSystem::KernelClose(fd) == OK, "close AIO read fixture"); } +void TestNpWebApi2Memory() { + Loader::SymbolDatabase symbols; + Libs::LibNpWebApi2::InitNet_1_NpWebApi2(&symbols); + const auto find = [&](const char *nid) { + const auto *symbol = symbols.FindByNid(nid, Loader::SymbolType::Func); + Check(symbol != nullptr, "NpWebApi2 library and memory exports resolve"); + return symbol->vaddr; + }; + struct Stats { size_t pool, maximum, current; int32_t reserved; }; + static_assert(sizeof(Stats) == 32 && offsetof(Stats, reserved) == 24); + using Initialize = int (KYTY_SYSV_ABI *)(int, size_t); + using GetStats = int (KYTY_SYSV_ABI *)(int, Stats *); + using Terminate = int (KYTY_SYSV_ABI *)(int); + const auto initialize = reinterpret_cast(find("+o9816YQhqQ")); + const auto get_stats = reinterpret_cast(find("Xweb+naPZ8Y")); + const auto terminate = reinterpret_cast(find("bEvXpcEk200")); + const int first = initialize(1, 65537); + const int second = initialize(1, 16384); + Stats stats {1, 2, 3, 4}; + Check(first > 0 && second > 0 && first != second && + get_stats(first, &stats) == OK && stats.pool == 81920 && + stats.maximum == 0 && stats.current == 0 && stats.reserved == 0 && + get_stats(second, &stats) == OK && stats.pool == 16384, + "NpWebApi2 statistics retain each context's rounded pool capacity"); + Check(get_stats(first, nullptr) == static_cast(0x80553402) && + get_stats(0, &stats) == static_cast(0x80553403) && + initialize(1, std::numeric_limits::max()) == static_cast(0x80553402), + "NpWebApi2 statistics validate output, context ID, and pool rounding"); + Check(terminate(first) == OK && + get_stats(first, &stats) == static_cast(0x80553404) && + terminate(first) == static_cast(0x80553404) && + terminate(-1) == static_cast(0x80553403) && + get_stats(second, &stats) == OK && stats.pool == 16384 && + terminate(second) == OK, + "NpWebApi2 termination removes only the selected library context"); +} + void CheckMountRoot(const std::filesystem::path &root) { Common::File cache; Check(cache.Create(root / "rpf.cache"), "create directory listing fixture"); @@ -921,6 +962,7 @@ int main(int, char**) { CheckSaveRename(temporary.Path(), "replacement-save"); FileSystem::Shutdown(); CheckSocketWakeup(); + TestNpWebApi2Memory(); graphics.reset(); subsystems.Destroy();