114 lines
4.6 KiB
C
114 lines
4.6 KiB
C
// Kernel well-known system-service registry. This file intentionally owns only numeric service discovery.
|
|
|
|
#include <kernel/ke_system_service.h>
|
|
#include <kernel/ke_port.h>
|
|
#include <kernel/ke_spinlock.h>
|
|
#include <kernel/ob_object.h>
|
|
#include <kernel/ob_object.h>
|
|
|
|
static SPINLOCK gSystemServiceLock = SPINLOCK_INIT;
|
|
static PORT* gSystemServices[SYS_SERVICE_MAX + 1u];
|
|
|
|
// Returns whether one numeric service identifier is valid for the current ABI.
|
|
static bool KiSystemServiceIdValid(SYS_SERVICE_ID serviceId) {
|
|
return serviceId > SYS_SERVICE_INVALID && serviceId <= SYS_SERVICE_MAX;
|
|
}
|
|
|
|
// Authority is an object capability, never a process privilege or service role.
|
|
typedef struct KE_SERVICE_REGISTRATION_AUTHORITY {
|
|
OBJECT Header;
|
|
SYS_SERVICE_ID ServiceId;
|
|
} KE_SERVICE_REGISTRATION_AUTHORITY;
|
|
|
|
// Creates a capability to register one numeric service identity.
|
|
KSTATUS KeSystemServiceCreateRegistrationAuthority(SYS_SERVICE_ID serviceId, OBJECT** authorityOut) {
|
|
if (!authorityOut) return NTLSTATUS_INVALID_PARAMETER;
|
|
*authorityOut = (OBJECT*)0;
|
|
if (!KiSystemServiceIdValid(serviceId)) return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
KE_SERVICE_REGISTRATION_AUTHORITY* authority =
|
|
(KE_SERVICE_REGISTRATION_AUTHORITY*)ObAllocateObject(ObjectTypeServiceRegistration,
|
|
sizeof(*authority));
|
|
if (!authority) return NTLSTATUS_NO_MEMORY;
|
|
authority->ServiceId = serviceId;
|
|
*authorityOut = &authority->Header;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Resolves an authority's registry identity without consulting process identity.
|
|
KSTATUS KeSystemServiceRegistrationIdentity(const OBJECT* object, SYS_SERVICE_ID* serviceIdOut) {
|
|
if (!object || !serviceIdOut || object->Type != ObjectTypeServiceRegistration)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
const KE_SERVICE_REGISTRATION_AUTHORITY* authority = (const KE_SERVICE_REGISTRATION_AUTHORITY*)object;
|
|
if (!KiSystemServiceIdValid(authority->ServiceId)) return NTLSTATUS_INVALID_PARAMETER;
|
|
*serviceIdOut = authority->ServiceId;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Registers one weak listener reference without creating a string or pathname namespace.
|
|
KSTATUS KeSystemServiceRegister(SYS_SERVICE_ID serviceId, PORT* listener) {
|
|
if (!KiSystemServiceIdValid(serviceId) || !listener || !KePortIsListener(listener))
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gSystemServiceLock);
|
|
if (gSystemServices[serviceId]) {
|
|
KeReleaseSpinlock(&gSystemServiceLock, flags);
|
|
return NTLSTATUS_ALREADY_EXISTS;
|
|
}
|
|
gSystemServices[serviceId] = listener;
|
|
KeReleaseSpinlock(&gSystemServiceLock, flags);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Removes one registration only when the caller still names the registered listener object.
|
|
KSTATUS KeSystemServiceUnregister(SYS_SERVICE_ID serviceId, PORT* listener) {
|
|
if (!KiSystemServiceIdValid(serviceId) || !listener || !KePortIsListener(listener))
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gSystemServiceLock);
|
|
if (!gSystemServices[serviceId]) {
|
|
KeReleaseSpinlock(&gSystemServiceLock, flags);
|
|
return NTLSTATUS_NOT_FOUND;
|
|
}
|
|
if (gSystemServices[serviceId] != listener) {
|
|
KeReleaseSpinlock(&gSystemServiceLock, flags);
|
|
return NTLSTATUS_ACCESS_DENIED;
|
|
}
|
|
gSystemServices[serviceId] = (PORT*)0;
|
|
KeReleaseSpinlock(&gSystemServiceLock, flags);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// References one currently registered listener for a connect operation.
|
|
KSTATUS KeSystemServiceReference(SYS_SERVICE_ID serviceId, PORT** listenerOut) {
|
|
if (!listenerOut)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
*listenerOut = (PORT*)0;
|
|
if (!KiSystemServiceIdValid(serviceId))
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gSystemServiceLock);
|
|
PORT* listener = gSystemServices[serviceId];
|
|
if (!listener || !ObTryReferenceObject((OBJECT*)listener)) {
|
|
KeReleaseSpinlock(&gSystemServiceLock, flags);
|
|
return NTLSTATUS_NOT_FOUND;
|
|
}
|
|
KeReleaseSpinlock(&gSystemServiceLock, flags);
|
|
|
|
*listenerOut = listener;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Removes any weak registration whose listener is entering terminal object destruction.
|
|
void KeSystemServiceUnregisterListener(PORT* listener) {
|
|
if (!listener)
|
|
return;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gSystemServiceLock);
|
|
for (SYS_SERVICE_ID id = 1; id <= SYS_SERVICE_MAX; ++id) {
|
|
if (gSystemServices[id] == listener)
|
|
gSystemServices[id] = (PORT*)0;
|
|
}
|
|
KeReleaseSpinlock(&gSystemServiceLock, flags);
|
|
}
|