Files
nt-lite/code/user/tests/posix_service_smoke/posix_service_smoke.c
T
maksw 73abd6f90d isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
2026-09-16 10:33:20 +02:00

515 lines
25 KiB
C

// Native smoke test for posixsrv discovery, stable PID registration, and bounded transactions.
#include <stdint.h>
#include <string.h>
#include <ntlite/module_loader.h>
#include <ntlite/posix_service.h>
#include <ntlite/process_loader.h>
#include <ntlite/path.h>
#include <ntlite/resource.h>
#include <ntlite/security.h>
#include <ntlite/syscall.h>
#define CHILD_ARGUMENT "--pid-child"
// Writes one test diagnostic through the native console.
static void print(const char* text) {
SYS_WRITE_CONSOLE_REQUEST request = { sizeof(request), SysSvFromCstr(text) };
(void)SysWriteConsole(&request);
}
// Terminates the smoke test with one visible failure reason.
static void fail(const char* text) {
print("posix_service_smoke: ");
print(text);
print("\n");
for (;;) SysExitProcess(1);
}
// Returns whether the current command line contains one ASCII token.
static int command_contains(const char* token) {
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION || !token)
return 0;
size_t tokenLength = strlen(token);
if (tokenLength == 0 || tokenLength > peb->CommandLine.Length) return 0;
for (size_t i = 0; i + tokenLength <= peb->CommandLine.Length; ++i) {
if (memcmp(peb->CommandLine.Data + i, token, tokenLength) == 0)
return 1;
}
return 0;
}
// Resolves /Services/posix through RSSRV and connects one independent client endpoint.
static KSTATUS connect_posixsrv(SYS_HANDLE* endpointOut) {
if (!endpointOut) return NTLSTATUS_INVALID_PARAMETER;
*endpointOut = SYS_HANDLE_INVALID;
SYS_HANDLE resource = SYS_HANDLE_INVALID;
KSTATUS status = NtlResourceConnect(&resource);
if (!KSUCCESS(status)) return status;
SYS_HANDLE listener = SYS_HANDLE_INVALID;
status = NtlResourceLookup(resource, SYS_SV_LITERAL(NTL_POSIX_SERVICE_PATH),
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT, &listener);
(void)NtlResourceClose(resource);
if (!KSUCCESS(status)) return status;
SYS_CONNECT_PORT_REQUEST connect;
memset(&connect, 0, sizeof(connect));
connect.Size = sizeof(connect);
connect.ListenerHandle = listener;
connect.Flags = SYS_PORT_CONNECT_FLAG_NONE;
connect.EndpointHandleOut = endpointOut;
status = SysConnectPort(&connect);
(void)SysCloseHandle(listener);
return status;
}
// Performs one fixed-size request with optional capability transfer and validates reply framing.
static KSTATUS call_posixsrv_exchange(SYS_HANDLE endpoint,
const NTL_POSIX_REQUEST* request,
const SYS_PORT_HANDLE_TRANSFER* requestHandles,
uint32_t requestHandleCount,
SYS_HANDLE* replyHandlesOut,
uint32_t replyHandleCapacity,
uint32_t expectedReplyHandleCount,
NTL_POSIX_REPLY* replyOut) {
if (endpoint == SYS_HANDLE_INVALID || !request || !replyOut ||
expectedReplyHandleCount > replyHandleCapacity ||
(replyHandleCapacity != 0 && !replyHandlesOut))
return NTLSTATUS_INVALID_PARAMETER;
memset(replyOut, 0, sizeof(*replyOut));
if (replyHandlesOut) {
for (uint32_t i = 0; i < replyHandleCapacity; ++i)
replyHandlesOut[i] = SYS_HANDLE_INVALID;
}
SYS_PORT_MESSAGE_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_CALL_PORT_REQUEST call;
memset(&call, 0, sizeof(call));
call.Size = sizeof(call);
call.EndpointHandle = endpoint;
call.Flags = SYS_PORT_FLAG_NONE;
call.RequestPayload = request;
call.RequestLength = sizeof(*request);
call.RequestHandles = requestHandles;
call.RequestHandleCount = requestHandleCount;
call.ReplyPayload = replyOut;
call.ReplyCapacity = sizeof(*replyOut);
call.ReplyHandlesOut = replyHandlesOut;
call.ReplyHandleCapacity = replyHandleCapacity;
call.ReplyInfoOut = &info;
call.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysCallPort(&call);
if (!KSUCCESS(status)) return status;
uint32_t expectedHandles = KSUCCESS(replyOut->Status) ? expectedReplyHandleCount : 0u;
if (info.Type != SYS_PORT_MESSAGE_REPLY || info.HandleCount != expectedHandles ||
info.PayloadLength != sizeof(*replyOut) || replyOut->Size != sizeof(*replyOut) ||
replyOut->Version != NTL_POSIX_PROTOCOL_VERSION ||
replyOut->Operation != request->Operation ||
replyOut->Flags != NTL_POSIX_REQUEST_FLAG_NONE) {
if (replyHandlesOut) {
uint32_t closeCount = info.HandleCount < replyHandleCapacity ?
info.HandleCount : replyHandleCapacity;
for (uint32_t i = 0; i < closeCount; ++i) {
if (replyHandlesOut[i] != SYS_HANDLE_INVALID)
(void)SysCloseHandle(replyHandlesOut[i]);
replyHandlesOut[i] = SYS_HANDLE_INVALID;
}
}
return NTLSTATUS_FAILURE;
}
return NTLSTATUS_SUCCESS;
}
// Performs one request that expects no capabilities in the reply.
static KSTATUS call_posixsrv(SYS_HANDLE endpoint,
const NTL_POSIX_REQUEST* request,
const SYS_PORT_HANDLE_TRANSFER* handles,
uint32_t handleCount,
NTL_POSIX_REPLY* replyOut) {
return call_posixsrv_exchange(endpoint, request, handles, handleCount,
(SYS_HANDLE*)0, 0, 0, replyOut);
}
// Constructs the common fixed-size request header used by the service protocol.
static NTL_POSIX_REQUEST make_request(uint32_t operation) {
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = operation;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
return request;
}
// Registers the current native process by transferring a restricted self process capability.
static KSTATUS register_current_process(SYS_HANDLE endpoint, NTL_POSIX_REPLY* replyOut) {
SYS_HANDLE self = SYS_HANDLE_INVALID;
KSTATUS status = SysOpenProcess(SysGetCurrentPid(),
SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_RIGHT_TRANSFER,
&self);
if (!KSUCCESS(status)) return status;
SYS_PORT_HANDLE_TRANSFER transfer;
memset(&transfer, 0, sizeof(transfer));
transfer.Handle = self;
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
NTL_POSIX_REQUEST request = make_request(NTL_POSIX_OP_REGISTER_PROCESS);
size_t cwdRequired = 0;
status = NtlGetCurrentDirectory(request.Path, sizeof(request.Path), &cwdRequired);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(self);
return status;
}
request.PathLength = (uint32_t)strlen(request.Path);
status = call_posixsrv(endpoint, &request, &transfer, 1u, replyOut);
(void)SysCloseHandle(self);
return status;
}
// Creates one handle-less server-owned description and returns its stable identity.
static uint64_t create_synthetic_description(SYS_HANDLE endpoint,
uint32_t type,
uint32_t statusFlags,
NTL_POSIX_REPLY* replyOut) {
NTL_POSIX_REQUEST request = make_request(NTL_POSIX_OP_CREATE_OPEN_DESCRIPTION);
request.OpenDescriptionType = type;
request.OpenStatusFlags = statusFlags;
if (!KSUCCESS(call_posixsrv(endpoint, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
replyOut)) || !KSUCCESS(replyOut->Status) ||
replyOut->OpenDescriptionId == 0 || replyOut->SharedOpenDescription == 0)
fail("shared description creation failed");
return replyOut->OpenDescriptionId;
}
// Releases one server-owned description held by the current registered process.
static KSTATUS release_description(SYS_HANDLE endpoint, uint64_t descriptionId,
NTL_POSIX_REPLY* replyOut) {
NTL_POSIX_REQUEST request = make_request(NTL_POSIX_OP_RELEASE_OPEN_DESCRIPTION);
request.OpenDescriptionId = descriptionId;
KSTATUS status = call_posixsrv(endpoint, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
replyOut);
if (!KSUCCESS(status)) return status;
return replyOut->Status;
}
// Waits for the process monitor to retire one process and all of its shared holders.
static void wait_registry_counts(SYS_HANDLE endpoint,
uint32_t expectedProcesses,
uint32_t expectedDescriptions) {
for (uint32_t attempt = 0; attempt < 4096u; ++attempt) {
NTL_POSIX_REQUEST request = make_request(NTL_POSIX_OP_QUERY);
NTL_POSIX_REPLY reply;
if (!KSUCCESS(call_posixsrv(endpoint, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status))
fail("registry query failed");
if (reply.RegisteredProcesses == expectedProcesses &&
reply.RegisteredOpenDescriptions == expectedDescriptions)
return;
(void)SysYield();
}
fail("exited process registry state was not retired");
}
// Runs a second native process so the server must allocate a distinct stable POSIX PID.
static uint32_t spawn_child_posix_pid(uint32_t* nativePidOut) {
if (nativePidOut) *nativePidOut = 0;
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION ||
!peb->ImagePath.Data || peb->ImagePath.Length == 0 || peb->ImagePath.Length > SYS_PATH_MAX)
fail("invalid self image path");
char path[SYS_PATH_MAX + 1u];
memcpy(path, peb->ImagePath.Data, peb->ImagePath.Length);
path[peb->ImagePath.Length] = '\0';
char command[SYS_PATH_MAX + 32u];
size_t pathLength = strlen(path);
size_t argumentLength = strlen(CHILD_ARGUMENT);
if (pathLength + 1u + argumentLength + 1u > sizeof(command))
fail("child command line too long");
memcpy(command, path, pathLength);
command[pathLength] = ' ';
memcpy(command + pathLength + 1u, CHILD_ARGUMENT, argumentLength + 1u);
SYS_HANDLE process = SYS_HANDLE_INVALID;
NTL_CREATE_SYSTEM_PROCESS_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
create.ImageFile = SYS_HANDLE_INVALID;
create.Path = SysSvFromCstr(path);
create.CommandLine = SysSvFromCstr(command);
create.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT;
create.ResolveModuleName = NtlResolveSystemModuleName;
NTL_POSIX_BOOTSTRAP_DATA bootstrap;
memset(&bootstrap, 0, sizeof(bootstrap));
bootstrap.Magic = NTL_POSIX_BOOTSTRAP_MAGIC;
bootstrap.Version = NTL_POSIX_BOOTSTRAP_VERSION;
bootstrap.Size = sizeof(bootstrap);
create.StartupData = &bootstrap;
create.StartupDataSize = sizeof(bootstrap);
create.ProcessHandleOut = &process;
KSTATUS status = NtlCreateSystemProcess(&create);
if (!KSUCCESS(status)) fail("child process creation failed");
uint32_t nativePid = 0;
if (!KSUCCESS(SysGetProcessPid(process, &nativePid)))
fail("child native pid query failed");
if (nativePidOut) *nativePidOut = nativePid;
if (!KSUCCESS(SysWait(process, SYS_WAIT_FLAG_NONE)))
fail("child process wait failed");
int32_t exitCode = 0;
if (!KSUCCESS(SysGetProcessExitCode(process, &exitCode)))
fail("child exit query failed");
(void)SysCloseHandle(process);
if (exitCode <= 0) fail("child returned invalid POSIX pid");
return (uint32_t)exitCode;
}
// Registers and returns the stable PID when running as the self-spawned child.
static void run_child_mode(void) {
NTL_POSIX_BOOTSTRAP_DATA bootstrap;
size_t bootstrapSize = 0;
if (!KSUCCESS(NtlGetProcessStartupData(&bootstrap, sizeof(bootstrap), &bootstrapSize)) ||
bootstrapSize != sizeof(bootstrap) || bootstrap.Magic != NTL_POSIX_BOOTSTRAP_MAGIC ||
bootstrap.Version != NTL_POSIX_BOOTSTRAP_VERSION || bootstrap.Size != sizeof(bootstrap) ||
bootstrap.Reserved != 0 || bootstrap.Token != 0 || bootstrap.PosixPid != 0 ||
bootstrap.Generation != 0 || bootstrap.FdInheritanceToken != 0)
for (;;) SysExitProcess(4);
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
if (!KSUCCESS(connect_posixsrv(&endpoint)))
for (;;) SysExitProcess(2);
NTL_POSIX_REPLY reply;
if (!KSUCCESS(register_current_process(endpoint, &reply)) || !KSUCCESS(reply.Status) ||
reply.PosixPid == 0 || reply.PosixPid > 0x7FFFFFFFu ||
reply.ProcessState != NTL_POSIX_PROCESS_STATE_RUNNING)
for (;;) SysExitProcess(3);
uint32_t pid = reply.PosixPid;
NTL_POSIX_REPLY descriptionReply;
uint64_t leakedDescription = create_synthetic_description(
endpoint, NTL_POSIX_OPEN_TYPE_DEV_NULL, NTL_POSIX_OPEN_STATUS_WRONLY,
&descriptionReply);
NTL_POSIX_SHARED_OPEN_DESCRIPTION* shared =
(NTL_POSIX_SHARED_OPEN_DESCRIPTION*)(uintptr_t)descriptionReply.SharedOpenDescription;
if (leakedDescription == 0 || !shared || shared->DescriptionId != leakedDescription ||
shared->Magic != NTL_POSIX_SHARED_OPEN_MAGIC ||
shared->Version != NTL_POSIX_SHARED_OPEN_VERSION)
for (;;) SysExitProcess(5);
// Intentionally leak the holder; the server's native-process monitor must reclaim it.
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
for (;;) SysExitProcess((int32_t)pid);
}
// Exercises service discovery, stable identity, protocol versions, and transaction boundaries.
int main(void) {
if (command_contains(CHILD_ARGUMENT)) run_child_mode();
print("posix_service_smoke: start\n");
SYS_HANDLE first = SYS_HANDLE_INVALID;
if (!KSUCCESS(connect_posixsrv(&first))) fail("first connect failed");
SYS_SECURITY_CONTEXT_INFO security;
memset(&security, 0, sizeof(security));
security.Size = sizeof(security);
if (!KSUCCESS(SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &security)))
fail("security query failed");
NTL_POSIX_REQUEST request = make_request(NTL_POSIX_OP_QUERY);
NTL_POSIX_REPLY reply;
if (!KSUCCESS(call_posixsrv(first, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status))
fail("query failed");
if (reply.ClientProcessId != SysGetCurrentPid() || reply.ClientSecurityId != security.SecurityId ||
reply.ActiveTransactions != 0 || reply.TransactionCapacity != NTL_POSIX_TRANSACTION_CAPACITY ||
reply.PosixPid != 0)
fail("pre-registration identity mismatch");
if (!KSUCCESS(register_current_process(first, &reply)) || !KSUCCESS(reply.Status))
fail("process registration failed");
uint32_t stablePid = reply.PosixPid;
if (stablePid == 0 || stablePid == SysGetCurrentPid() || reply.NativeGeneration != 1u ||
reply.ProcessState != NTL_POSIX_PROCESS_STATE_RUNNING)
fail("stable process identity mismatch");
if (reply.UserId != 0 || reply.EffectiveUserId != 0 || reply.GroupId != 0 ||
reply.EffectiveGroupId != 0 || reply.ProcessGroupId != stablePid ||
reply.SessionId != stablePid || reply.SupplementaryGroupCount != 0 ||
reply.NameLength != 4u || strcmp(reply.Name, "root") != 0)
fail("initial shared process attributes mismatch");
char registeredCwd[SYS_PATH_MAX + 1u];
size_t registeredCwdRequired = 0;
if (!KSUCCESS(NtlGetCurrentDirectory(registeredCwd, sizeof(registeredCwd), &registeredCwdRequired)) ||
reply.CurrentDirectoryLength != strlen(registeredCwd) ||
strcmp(reply.CurrentDirectory, registeredCwd) != 0)
fail("registered cwd mismatch");
uint32_t baselineDescriptions = reply.RegisteredOpenDescriptions;
NTL_POSIX_REPLY descriptionReply;
uint64_t descriptionId = create_synthetic_description(
first, NTL_POSIX_OPEN_TYPE_DEV_NULL, NTL_POSIX_OPEN_STATUS_WRONLY,
&descriptionReply);
if (descriptionReply.RegisteredOpenDescriptions != baselineDescriptions + 1u ||
descriptionReply.OpenDescriptionType != NTL_POSIX_OPEN_TYPE_DEV_NULL ||
descriptionReply.OpenStatusFlags != NTL_POSIX_OPEN_STATUS_WRONLY)
fail("shared description registry mismatch");
NTL_POSIX_SHARED_OPEN_DESCRIPTION* shared =
(NTL_POSIX_SHARED_OPEN_DESCRIPTION*)(uintptr_t)descriptionReply.SharedOpenDescription;
if (!shared || shared->Magic != NTL_POSIX_SHARED_OPEN_MAGIC ||
shared->Version != NTL_POSIX_SHARED_OPEN_VERSION ||
shared->Type != NTL_POSIX_OPEN_TYPE_DEV_NULL ||
shared->DescriptionId != descriptionId ||
(uint32_t)shared->StatusFlags != NTL_POSIX_OPEN_STATUS_WRONLY)
fail("shared description pointer mismatch");
request = make_request(NTL_POSIX_OP_SET_OPEN_STATUS);
request.OpenDescriptionId = descriptionId;
request.OpenStatusFlags = NTL_POSIX_OPEN_STATUS_WRONLY | NTL_POSIX_OPEN_STATUS_NONBLOCK;
if (!KSUCCESS(call_posixsrv(first, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &descriptionReply)) ||
!KSUCCESS(descriptionReply.Status) || descriptionReply.OpenDescriptionId != descriptionId ||
descriptionReply.OpenStatusFlags != request.OpenStatusFlags ||
(uint32_t)shared->StatusFlags != request.OpenStatusFlags)
fail("server-serialized status update failed");
if (!KSUCCESS(release_description(first, descriptionId, &descriptionReply)) ||
descriptionReply.RegisteredOpenDescriptions != baselineDescriptions)
fail("shared description release failed");
if (release_description(first, descriptionId, &descriptionReply) != NTLSTATUS_NOT_FOUND)
fail("duplicate shared description release was accepted");
request = make_request(NTL_POSIX_OP_CREATE_PIPE);
SYS_HANDLE pipeHandles[2] = { SYS_HANDLE_INVALID, SYS_HANDLE_INVALID };
if (!KSUCCESS(call_posixsrv_exchange(first, &request,
(const SYS_PORT_HANDLE_TRANSFER*)0, 0,
pipeHandles, 2u, 2u, &descriptionReply)) ||
!KSUCCESS(descriptionReply.Status) ||
descriptionReply.RegisteredOpenDescriptions != baselineDescriptions + 2u ||
descriptionReply.OpenDescriptionType != NTL_POSIX_OPEN_TYPE_PIPE_READ ||
descriptionReply.OpenDescriptionType2 != NTL_POSIX_OPEN_TYPE_PIPE_WRITE ||
descriptionReply.OpenStatusFlags != NTL_POSIX_OPEN_STATUS_RDONLY ||
descriptionReply.OpenStatusFlags2 != NTL_POSIX_OPEN_STATUS_WRONLY ||
descriptionReply.OpenDescriptionId == 0 || descriptionReply.OpenDescriptionId2 == 0 ||
descriptionReply.OpenDescriptionId == descriptionReply.OpenDescriptionId2 ||
pipeHandles[0] == SYS_HANDLE_INVALID || pipeHandles[1] == SYS_HANDLE_INVALID)
fail("server-owned pipe creation failed");
NTL_POSIX_SHARED_OPEN_DESCRIPTION* pipeReadShared =
(NTL_POSIX_SHARED_OPEN_DESCRIPTION*)(uintptr_t)descriptionReply.SharedOpenDescription;
NTL_POSIX_SHARED_OPEN_DESCRIPTION* pipeWriteShared =
(NTL_POSIX_SHARED_OPEN_DESCRIPTION*)(uintptr_t)descriptionReply.SharedOpenDescription2;
SYS_PIPE_SHARED_STATE* pipeShared = pipeReadShared ?
(SYS_PIPE_SHARED_STATE*)(uintptr_t)pipeReadShared->SharedObjectData :
(SYS_PIPE_SHARED_STATE*)0;
if (!pipeReadShared || !pipeWriteShared || !pipeShared ||
pipeReadShared->Magic != NTL_POSIX_SHARED_OPEN_MAGIC ||
pipeWriteShared->Magic != NTL_POSIX_SHARED_OPEN_MAGIC ||
pipeReadShared->Version != NTL_POSIX_SHARED_OPEN_VERSION ||
pipeWriteShared->Version != NTL_POSIX_SHARED_OPEN_VERSION ||
pipeReadShared->DescriptionId != descriptionReply.OpenDescriptionId ||
pipeWriteShared->DescriptionId != descriptionReply.OpenDescriptionId2 ||
pipeReadShared->SharedObjectData != pipeWriteShared->SharedObjectData ||
pipeShared->Magic != SYS_PIPE_SHARED_MAGIC || pipeShared->Version != SYS_PIPE_SHARED_VERSION ||
pipeShared->Capacity != SYS_PIPE_BUFFER_CAPACITY ||
pipeShared->HeaderBytes != SYS_PIPE_SHARED_HEADER_BYTES ||
pipeShared->ReaderOpen == 0 || pipeShared->WriterOpen == 0)
fail("server-owned pipe shared state mismatch");
if (!KSUCCESS(release_description(first, descriptionReply.OpenDescriptionId, &reply)) ||
reply.RegisteredOpenDescriptions != baselineDescriptions + 1u ||
pipeShared->ReaderOpen == 0)
fail("pipe read master release invalidated live alias");
if (!KSUCCESS(SysCloseHandle(pipeHandles[0])) || pipeShared->ReaderOpen != 0)
fail("pipe read alias close did not publish reader shutdown");
pipeHandles[0] = SYS_HANDLE_INVALID;
if (!KSUCCESS(SysCloseHandle(pipeHandles[1])))
fail("pipe write alias close failed");
pipeHandles[1] = SYS_HANDLE_INVALID;
if (!KSUCCESS(release_description(first, descriptionReply.OpenDescriptionId2, &reply)) ||
reply.RegisteredOpenDescriptions != baselineDescriptions)
fail("server-owned pipe description release failed");
request = make_request(NTL_POSIX_OP_LOOKUP_USER);
request.LookupKind = NTL_POSIX_LOOKUP_BY_NAME;
memcpy(request.Name, "user", 5u);
request.NameLength = 4u;
if (!KSUCCESS(call_posixsrv(first, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status) || reply.UserId != 1000u || reply.GroupId != 1000u ||
strcmp(reply.Name, "user") != 0)
fail("identity database lookup failed");
uint32_t childNativePid = 0;
uint32_t childPosixPid = spawn_child_posix_pid(&childNativePid);
if (childPosixPid == stablePid || childPosixPid == childNativePid)
fail("child POSIX identity was not independent");
wait_registry_counts(first, 1u, baselineDescriptions);
request = make_request(NTL_POSIX_OP_BEGIN_TRANSACTION);
request.TransactionKind = NTL_POSIX_TRANSACTION_SPAWN;
if (!KSUCCESS(call_posixsrv(first, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status) || reply.TransactionId == 0 || reply.ActiveTransactions != 1)
fail("transaction begin failed");
uint32_t transactionId = reply.TransactionId;
SYS_HANDLE second = SYS_HANDLE_INVALID;
if (!KSUCCESS(connect_posixsrv(&second))) fail("second connect failed");
if (!KSUCCESS(register_current_process(second, &reply)) || !KSUCCESS(reply.Status) ||
reply.PosixPid != stablePid || reply.NativeGeneration != 1u)
fail("same-process reconnect changed POSIX identity");
request = make_request(NTL_POSIX_OP_QUERY);
if (!KSUCCESS(call_posixsrv(second, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status) || reply.ActiveTransactions != 0 || reply.PosixPid != stablePid)
fail("connection transaction isolation failed");
request = make_request(NTL_POSIX_OP_QUERY);
request.Version = NTL_POSIX_PROTOCOL_VERSION + 1u;
if (!KSUCCESS(call_posixsrv(second, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
reply.Status != NTLSTATUS_NOT_SUPPORTED)
fail("version rejection failed");
request = make_request(NTL_POSIX_OP_CANCEL_TRANSACTION);
request.TransactionId = transactionId;
if (!KSUCCESS(call_posixsrv(first, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status) || reply.TransactionId != transactionId || reply.ActiveTransactions != 0)
fail("transaction cancel failed");
request = make_request(NTL_POSIX_OP_CANCEL_TRANSACTION);
request.TransactionId = transactionId;
if (!KSUCCESS(call_posixsrv(first, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
reply.Status != NTLSTATUS_NOT_FOUND)
fail("unknown transaction rejection failed");
request = make_request(NTL_POSIX_OP_BEGIN_TRANSACTION);
request.TransactionKind = NTL_POSIX_TRANSACTION_EXEC;
if (!KSUCCESS(call_posixsrv(first, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status))
fail("exec transaction begin failed");
(void)SysDisconnectPort(first);
(void)SysCloseHandle(first);
(void)SysDisconnectPort(second);
(void)SysCloseHandle(second);
SYS_HANDLE third = SYS_HANDLE_INVALID;
if (!KSUCCESS(connect_posixsrv(&third))) fail("reconnect failed");
if (!KSUCCESS(register_current_process(third, &reply)) || !KSUCCESS(reply.Status) ||
reply.PosixPid != stablePid || reply.NativeGeneration != 1u || reply.ActiveTransactions != 0)
fail("live process lost identity after disconnect");
(void)SysDisconnectPort(third);
(void)SysCloseHandle(third);
print("posix_service_smoke: PASS\n");
return 0;
}