implement generic I/O async request object to replace file-specific async requests, refactor lwIP network stack to dispatch requests through IRPs with cancellation support, add cancellation polling to lwIP accept and receive operations using LwipSocketWaitRecvNetbuf and LwipSocketWaitAccept, introduce NetSubmitSocketIrp for routing socket operations via device IRPs, track network IRP statistics with SYS_NET_IRP_STATS, add process module lock spinlock and reference counting for process modules, implement dynamic module loading and unloading via user module handles, add POSIX dynamic loader APIs including dlopen, dlsym, dlerror, and dlclose, introduce PosixStart entry point for posix applications, add new native smoke tests for net IRPs.

This commit is contained in:
maksw committed 2026-06-27 10:37:53 +02:00
1 parent ed3d065abd
commit 1f5477cf66
32 files changed
+3713 -551

No files matched your search

+77 -166
View File
@@ -32,31 +32,12 @@ typedef enum IO_ASYNC_FILE_OP {
IoAsyncFileWrite = 1,
} IO_ASYNC_FILE_OP;
typedef enum IO_ASYNC_FILE_STATE {
IoAsyncStateQueued = 0,
IoAsyncStateRunning = 1,
IoAsyncStateCompleted = 2,
IoAsyncStateCancelled = 3,
} IO_ASYNC_FILE_STATE;
struct IO_ASYNC_FILE_REQUEST_OBJECT {
OBJECT Header; // ObjectTypeAsyncIo
LIST_ENTRY Link; // protected by gAsyncFileQueueLock
IO_ASYNC_REQUEST_OBJECT Async;
IO_ASYNC_FILE_OP Op;
IO_ASYNC_FILE_STATE State;
uint8_t CancelRequested;
FILE_OBJECT* File; // referenced until object destruction
EVENT* WaitEvent; // internal completion event
EVENT* CompletionEvent; // optional, referenced until object destruction
KAPC* CompletionApc; // caller-owned APC storage
IO_STATUS_BLOCK* Iosb; // caller/user-owned completion block
IO_STATUS_BLOCK IosbSnapshot;
MM_LOCKED_USER_BUFFER* LockedBuffer;
MM_LOCKED_USER_BUFFER* LockedIosb;
void* Buffer; // caller-owned; user buffers must be locked until completion/cancel
FILE_OBJECT* File;
void* Buffer;
size_t Size;
uint64_t Offset;
};
@@ -228,72 +209,45 @@ static void KiIoSignalFileLockWaitersLocked(const IO_FILE_IDENTITY* identity) {
}
}
// Implements the io Async File Request Destroy operation.
static void KiIoAsyncFileRequestDestroy(OBJECT* obj) {
// Releases file-specific references owned by an async file request.
static void KiIoAsyncFileRequestCleanup(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
(void)context;
IO_ASYNC_FILE_REQUEST_OBJECT* req =
CONTAINING_RECORD(obj, IO_ASYNC_FILE_REQUEST_OBJECT, Header);
(IO_ASYNC_FILE_REQUEST_OBJECT*)async;
if (req->WaitEvent) {
ObDereferenceObject(&req->WaitEvent->Header);
req->WaitEvent = (EVENT*)0;
}
if (req->CompletionEvent) {
ObDereferenceObject(&req->CompletionEvent->Header);
req->CompletionEvent = (EVENT*)0;
}
if (req->File) {
ObDereferenceObject(&req->File->Header);
req->File = (FILE_OBJECT*)0;
}
if (req->LockedBuffer) {
MmUnlockUserBuffer(req->LockedBuffer);
req->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedIosb) {
MmUnlockUserBuffer(req->LockedIosb);
req->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
}
static void KiIoCompleteAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
KSTATUS status,
size_t information) {
if (!req) return;
req->IosbSnapshot.Status = status;
req->IosbSnapshot.Information = information;
// Cancels an async file request from the common async request surface.
static KSTATUS KiIoCancelAsyncFileRequest(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
(void)context;
if (!async) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
req->State = (status == STATUS_CANCELLED) ? IoAsyncStateCancelled : IoAsyncStateCompleted;
IO_ASYNC_REQUEST_STATE state = IoAsyncRequestGetState(async);
if (state == IoAsyncRequestQueued) {
ListRemove(&async->Link);
IoAsyncRequestRequestCancel(async);
IoAsyncRequestSetState(async, IoAsyncRequestCancelled);
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
IoCompleteAsyncRequest(async, STATUS_CANCELLED, 0);
ObDereferenceObject(&async->Header);
return STATUS_SUCCESS;
}
if (state == IoAsyncRequestRunning) {
IoAsyncRequestRequestCancel(async);
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return STATUS_PENDING;
}
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
if (req->Iosb) {
req->Iosb->Status = status;
req->Iosb->Information = information;
}
// Match IRP completion ordering: queue APC first, then signal the event.
if (req->CompletionApc) {
if (!KeInsertQueueApc(req->CompletionApc, req->Iosb, (void*)(uintptr_t)status)) {
if (req->CompletionApc->RundownRoutine)
req->CompletionApc->RundownRoutine(req->CompletionApc);
}
}
if (req->CompletionEvent)
KeSignalEvent(req->CompletionEvent);
if (req->WaitEvent)
KeSignalEvent(req->WaitEvent);
if (req->LockedBuffer) {
MmUnlockUserBuffer(req->LockedBuffer);
req->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedIosb) {
MmUnlockUserBuffer(req->LockedIosb);
req->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
return STATUS_OBJECT_DEAD;
}
// Implements the io Async File Dequeue operation.
@@ -306,12 +260,12 @@ static IO_ASYNC_FILE_REQUEST_OBJECT* KiIoAsyncFileDequeue(void) {
}
IO_ASYNC_FILE_REQUEST_OBJECT* req =
CONTAINING_RECORD(node, IO_ASYNC_FILE_REQUEST_OBJECT, Link);
(IO_ASYNC_FILE_REQUEST_OBJECT*)CONTAINING_RECORD(node, IO_ASYNC_REQUEST_OBJECT, Link);
if (req->CancelRequested) {
req->State = IoAsyncStateCancelled;
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
IoAsyncRequestSetState(&req->Async, IoAsyncRequestCancelled);
} else {
req->State = IoAsyncStateRunning;
IoAsyncRequestSetState(&req->Async, IoAsyncRequestRunning);
}
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return req;
@@ -331,9 +285,9 @@ static void KiIoAsyncFileWorker(void* arg) {
IO_ASYNC_FILE_REQUEST_OBJECT* req = KiIoAsyncFileDequeue();
if (!req) continue;
if (req->CancelRequested || req->State == IoAsyncStateCancelled) {
KiIoCompleteAsyncFileRequest(req, STATUS_CANCELLED, 0);
ObDereferenceObject(&req->Header); // queue/worker reference
if (IoAsyncRequestIsCancelRequested(&req->Async) || IoAsyncRequestGetState(&req->Async) == IoAsyncRequestCancelled) {
IoCompleteAsyncRequest(&req->Async, STATUS_CANCELLED, 0);
ObDereferenceObject(&req->Async.Header); // queue/worker reference
continue;
}
@@ -346,7 +300,7 @@ static void KiIoAsyncFileWorker(void* arg) {
req->Size, &done);
}
if (req->CancelRequested) {
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
// The operation may already have reached the filesystem. The
// completion contract still reports cancellation so waiters/APCs do
// not mistake a cancelled request for useful data.
@@ -354,8 +308,8 @@ static void KiIoAsyncFileWorker(void* arg) {
done = 0;
}
KiIoCompleteAsyncFileRequest(req, s, done);
ObDereferenceObject(&req->Header); // queue/worker reference
IoCompleteAsyncRequest(&req->Async, s, done);
ObDereferenceObject(&req->Async.Header); // queue/worker reference
}
}
@@ -404,13 +358,13 @@ static void KiIoNativeFsAsyncComplete(void* completeContext,
(IO_ASYNC_FILE_REQUEST_OBJECT*)completeContext;
if (!req) return;
if (req->CancelRequested) {
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
status = STATUS_CANCELLED;
information = 0;
}
KiIoCompleteAsyncFileRequest(req, status, information);
ObDereferenceObject(&req->Header); // native filesystem provider reference
IoCompleteAsyncRequest(&req->Async, status, information);
ObDereferenceObject(&req->Async.Header); // native filesystem provider reference
}
static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
@@ -443,45 +397,30 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
iosb->Status = STATUS_PENDING;
iosb->Information = 0;
IO_ASYNC_FILE_REQUEST_OBJECT* req =
(IO_ASYNC_FILE_REQUEST_OBJECT*)ObAllocateObject(ObjectTypeAsyncIo,
sizeof(IO_ASYNC_FILE_REQUEST_OBJECT));
if (!req) {
iosb->Status = STATUS_NO_MEMORY;
IO_ASYNC_REQUEST_OBJECT* async = (IO_ASYNC_REQUEST_OBJECT*)0;
KSTATUS createStatus = IoCreateAsyncRequest(sizeof(IO_ASYNC_FILE_REQUEST_OBJECT),
KiIoAsyncFileRequestCleanup,
(void*)0,
KiIoCancelAsyncFileRequest,
(void*)0,
&async);
if (!KSUCCESS(createStatus)) {
iosb->Status = createStatus;
MmUnlockUserBuffer(lockedBuffer);
MmUnlockUserBuffer(lockedIosb);
return STATUS_NO_MEMORY;
return createStatus;
}
req->Header.Destroy = KiIoAsyncFileRequestDestroy;
LIST_INIT(&req->Link);
IO_ASYNC_FILE_REQUEST_OBJECT* req = (IO_ASYNC_FILE_REQUEST_OBJECT*)async;
req->Op = op;
req->State = IoAsyncStateQueued;
req->CancelRequested = 0;
KSTATUS waitStatus = KeEventCreate(EventManualReset, 0, &req->WaitEvent);
if (!KSUCCESS(waitStatus)) {
iosb->Status = waitStatus;
MmUnlockUserBuffer(lockedBuffer);
MmUnlockUserBuffer(lockedIosb);
ObDereferenceObject(&req->Header);
return waitStatus;
}
req->File = file;
req->CompletionEvent = completionEvent;
req->CompletionApc = completionApc;
req->Iosb = iosb;
req->IosbSnapshot.Status = STATUS_PENDING;
req->IosbSnapshot.Information = 0;
req->LockedBuffer = lockedBuffer;
req->LockedIosb = lockedIosb;
req->Buffer = buffer;
req->Size = size;
req->Offset = offset;
IoInitializeAsyncRequestCompletion(&req->Async, iosb, completionEvent,
completionApc, lockedBuffer, lockedIosb);
ObReferenceObject(&file->Header);
if (completionEvent)
ObReferenceObject(&completionEvent->Header);
IO_FS_ASYNC_COMPLETE complete = KiIoNativeFsAsyncComplete;
KSTATUS ns = STATUS_NOT_SUPPORTED;
@@ -490,10 +429,10 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
if (hasNative) {
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
req->State = IoAsyncStateRunning;
IoAsyncRequestSetState(&req->Async, IoAsyncRequestRunning);
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
ObReferenceObject(&req->Header); // native filesystem provider reference
ObReferenceObject(&req->Async.Header); // native filesystem provider reference
if (op == IoAsyncFileRead) {
ns = file->Fs->ReadAsync(file->Fs, file->FileCtx, offset, buffer,
size, complete, req);
@@ -505,34 +444,34 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
if (requestOut) {
*requestOut = req;
} else {
ObDereferenceObject(&req->Header);
ObDereferenceObject(&req->Async.Header);
}
return STATUS_PENDING;
}
ObDereferenceObject(&req->Header); // provider rejected synchronously
ObDereferenceObject(&req->Async.Header); // provider rejected synchronously
iosb->Status = ns;
req->IosbSnapshot.Status = ns;
req->IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Header); // creator reference
req->Async.IosbSnapshot.Status = ns;
req->Async.IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Async.Header); // creator reference
return ns;
}
KSTATUS s = KiIoEnsureAsyncFileIoStarted();
if (!KSUCCESS(s)) {
iosb->Status = s;
req->IosbSnapshot.Status = s;
req->IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Header);
req->Async.IosbSnapshot.Status = s;
req->Async.IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Async.Header);
return s;
}
// Queue ownership keeps the request alive until the worker completes or a
// successful queued cancellation completes it from IoCancelAsyncFileRequest.
ObReferenceObject(&req->Header);
ObReferenceObject(&req->Async.Header);
uintptr_t qflags = KeAcquireSpinlock(&gAsyncFileQueueLock);
ListInsertTail(&gAsyncFileQueue, &req->Link);
ListInsertTail(&gAsyncFileQueue, &req->Async.Link);
KeReleaseSpinlock(&gAsyncFileQueueLock, qflags);
KeSignalSemaphore(gAsyncFileSemaphore);
@@ -540,7 +479,7 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
if (requestOut) {
*requestOut = req; // creator reference returned to caller
} else {
ObDereferenceObject(&req->Header);
ObDereferenceObject(&req->Async.Header);
}
return STATUS_PENDING;
@@ -574,56 +513,28 @@ __declspec(dllexport) KSTATUS IoWriteFileAsyncKernel(FILE_OBJECT* file, const vo
// Implements the async File Request Object operation.
__declspec(dllexport) OBJECT* IoAsyncFileRequestObject(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
return req ? &req->Header : (OBJECT*)0;
return req ? &req->Async.Header : (OBJECT*)0;
}
// Returns the wait event owned by an async file request.
__declspec(dllexport) EVENT* IoGetAsyncFileRequestWaitEvent(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
return req ? req->WaitEvent : (EVENT*)0;
return req ? IoGetAsyncRequestWaitEvent(&req->Async) : (EVENT*)0;
}
// Waits for an async file request to complete according to wait flags.
__declspec(dllexport) KSTATUS IoWaitAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req, uint32_t flags) {
if (!req || !req->WaitEvent) return STATUS_INVALID_PARAMETER;
if (flags & ~SYS_WAIT_FLAG_ALERTABLE) return STATUS_INVALID_PARAMETER;
return KeWaitForEvent(req->WaitEvent, (flags & SYS_WAIT_FLAG_ALERTABLE) ? 1 : 0);
return IoWaitAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0, flags);
}
// Cancels async File Request.
// Cancels an async file request through the common async I/O object.
__declspec(dllexport) KSTATUS IoCancelAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
if (!req) return STATUS_INVALID_PARAMETER;
IO_ASYNC_FILE_STATE state;
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
state = req->State;
if (state == IoAsyncStateQueued) {
ListRemove(&req->Link);
req->CancelRequested = 1;
req->State = IoAsyncStateCancelled;
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
KiIoCompleteAsyncFileRequest(req, STATUS_CANCELLED, 0);
ObDereferenceObject(&req->Header); // queue reference removed above
return STATUS_SUCCESS;
}
if (state == IoAsyncStateRunning) {
req->CancelRequested = 1;
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return STATUS_PENDING;
}
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return STATUS_OBJECT_DEAD;
return IoCancelAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0);
}
__declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
IO_STATUS_BLOCK* iosbOut) {
if (!req || !iosbOut) return STATUS_INVALID_PARAMETER;
iosbOut->Status = req->IosbSnapshot.Status;
iosbOut->Information = req->IosbSnapshot.Information;
return STATUS_SUCCESS;
return IoQueryAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0,
iosbOut);
}
// Current Working Directory