mirror of
https://github.com/reactos/reactos.git
synced 2024-08-14 23:49:49 +00:00
485 lines
13 KiB
C
485 lines
13 KiB
C
/*
|
|
* PROJECT: ReactOS Named Pipe FileSystem
|
|
* LICENSE: BSD - See COPYING.ARM in the top level directory
|
|
* FILE: drivers/filesystems/npfs/datasup.c
|
|
* PURPOSE: Data Queues Support
|
|
* PROGRAMMERS: ReactOS Portable Systems Group
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "npfs.h"
|
|
|
|
// File ID number for NPFS bugchecking support
|
|
#define NPFS_BUGCHECK_FILE_ID (NPFS_BUGCHECK_DATASUP)
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NpUninitializeDataQueue(IN PNP_DATA_QUEUE DataQueue)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DataQueue->QueueState == Empty);
|
|
|
|
RtlZeroMemory(DataQueue, sizeof(*DataQueue));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NpInitializeDataQueue(IN PNP_DATA_QUEUE DataQueue,
|
|
IN ULONG Quota)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DataQueue->BytesInQueue = 0;
|
|
DataQueue->EntriesInQueue = 0;
|
|
DataQueue->QuotaUsed = 0;
|
|
DataQueue->ByteOffset = 0;
|
|
DataQueue->QueueState = Empty;
|
|
DataQueue->Quota = Quota;
|
|
InitializeListHead(&DataQueue->Queue);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
NpCompleteStalledWrites(IN PNP_DATA_QUEUE DataQueue,
|
|
IN PLIST_ENTRY List)
|
|
{
|
|
ULONG QuotaLeft, ByteOffset, DataLeft, NewQuotaLeft;
|
|
PNP_DATA_QUEUE_ENTRY DataQueueEntry;
|
|
PIRP Irp;
|
|
PLIST_ENTRY NextEntry;
|
|
|
|
QuotaLeft = DataQueue->Quota - DataQueue->QuotaUsed;
|
|
ByteOffset = DataQueue->ByteOffset;
|
|
|
|
NextEntry = DataQueue->Queue.Flink;
|
|
while (NextEntry != &DataQueue->Queue)
|
|
{
|
|
if (!QuotaLeft) break;
|
|
|
|
DataQueueEntry = CONTAINING_RECORD(NextEntry,
|
|
NP_DATA_QUEUE_ENTRY,
|
|
QueueEntry);
|
|
|
|
Irp = DataQueueEntry->Irp;
|
|
|
|
if ((DataQueueEntry->DataEntryType == Buffered) && (Irp))
|
|
{
|
|
DataLeft = DataQueueEntry->DataSize - ByteOffset;
|
|
|
|
if (DataQueueEntry->QuotaInEntry < DataLeft)
|
|
{
|
|
NewQuotaLeft = DataLeft - DataQueueEntry->QuotaInEntry;
|
|
if (NewQuotaLeft > QuotaLeft) NewQuotaLeft = QuotaLeft;
|
|
|
|
QuotaLeft -= NewQuotaLeft;
|
|
DataQueueEntry->QuotaInEntry += NewQuotaLeft;
|
|
|
|
if (DataQueueEntry->QuotaInEntry == DataLeft &&
|
|
IoSetCancelRoutine(Irp, NULL))
|
|
{
|
|
DataQueueEntry->Irp = NULL;
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = DataQueueEntry->DataSize;
|
|
|
|
InsertTailList(List, &Irp->Tail.Overlay.ListEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
NextEntry = NextEntry->Flink;
|
|
ByteOffset = 0;
|
|
}
|
|
|
|
DataQueue->QuotaUsed = DataQueue->Quota - QuotaLeft;
|
|
}
|
|
|
|
PIRP
|
|
NTAPI
|
|
NpRemoveDataQueueEntry(IN PNP_DATA_QUEUE DataQueue,
|
|
IN BOOLEAN Flag,
|
|
IN PLIST_ENTRY List)
|
|
{
|
|
PIRP Irp;
|
|
PNP_DATA_QUEUE_ENTRY QueueEntry;
|
|
BOOLEAN HasWrites;
|
|
|
|
if (DataQueue->QueueState == Empty)
|
|
{
|
|
Irp = NULL;
|
|
ASSERT(IsListEmpty(&DataQueue->Queue));
|
|
ASSERT(DataQueue->EntriesInQueue == 0);
|
|
ASSERT(DataQueue->BytesInQueue == 0);
|
|
ASSERT(DataQueue->QuotaUsed == 0);
|
|
}
|
|
else
|
|
{
|
|
QueueEntry = CONTAINING_RECORD(RemoveHeadList(&DataQueue->Queue),
|
|
NP_DATA_QUEUE_ENTRY,
|
|
QueueEntry);
|
|
|
|
DataQueue->BytesInQueue -= QueueEntry->DataSize;
|
|
--DataQueue->EntriesInQueue;
|
|
|
|
HasWrites = TRUE;
|
|
if (DataQueue->QueueState != WriteEntries ||
|
|
DataQueue->QuotaUsed < DataQueue->Quota ||
|
|
!QueueEntry->QuotaInEntry)
|
|
{
|
|
HasWrites = FALSE;
|
|
}
|
|
|
|
DataQueue->QuotaUsed -= QueueEntry->QuotaInEntry;
|
|
|
|
if (IsListEmpty(&DataQueue->Queue))
|
|
{
|
|
DataQueue->QueueState = Empty;
|
|
HasWrites = FALSE;
|
|
}
|
|
|
|
Irp = QueueEntry->Irp;
|
|
NpFreeClientSecurityContext(QueueEntry->ClientSecurityContext);
|
|
|
|
if (Irp && !IoSetCancelRoutine(Irp, NULL))
|
|
{
|
|
Irp->Tail.Overlay.DriverContext[3] = NULL;
|
|
Irp = NULL;
|
|
}
|
|
|
|
ExFreePool(QueueEntry);
|
|
|
|
if (Flag)
|
|
{
|
|
NpGetNextRealDataQueueEntry(DataQueue, List);
|
|
}
|
|
|
|
if (HasWrites)
|
|
{
|
|
NpCompleteStalledWrites(DataQueue, List);
|
|
}
|
|
}
|
|
|
|
DataQueue->ByteOffset = 0;
|
|
return Irp;
|
|
}
|
|
|
|
PLIST_ENTRY
|
|
NTAPI
|
|
NpGetNextRealDataQueueEntry(IN PNP_DATA_QUEUE DataQueue,
|
|
IN PLIST_ENTRY List)
|
|
{
|
|
PNP_DATA_QUEUE_ENTRY DataEntry;
|
|
ULONG Type;
|
|
PIRP Irp;
|
|
PLIST_ENTRY NextEntry;
|
|
PAGED_CODE();
|
|
|
|
for (NextEntry = DataQueue->Queue.Flink;
|
|
NextEntry != &DataQueue->Queue;
|
|
NextEntry = DataQueue->Queue.Flink)
|
|
{
|
|
DataEntry = CONTAINING_RECORD(NextEntry,
|
|
NP_DATA_QUEUE_ENTRY,
|
|
QueueEntry);
|
|
|
|
Type = DataEntry->DataEntryType;
|
|
if (Type == Buffered || Type == Unbuffered) break;
|
|
|
|
Irp = NpRemoveDataQueueEntry(DataQueue, FALSE, List);
|
|
if (Irp)
|
|
{
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
InsertTailList(List, &Irp->Tail.Overlay.ListEntry);
|
|
}
|
|
}
|
|
|
|
return NextEntry;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
NpCancelDataQueueIrp(IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
PNP_DATA_QUEUE DataQueue;
|
|
PNP_DATA_QUEUE_ENTRY DataEntry;
|
|
LIST_ENTRY DeferredList;
|
|
PSECURITY_CLIENT_CONTEXT ClientSecurityContext;
|
|
BOOLEAN CompleteWrites, FirstEntry;
|
|
|
|
if (DeviceObject) IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
InitializeListHead(&DeferredList);
|
|
|
|
DataQueue = Irp->Tail.Overlay.DriverContext[2];
|
|
ClientSecurityContext = NULL;
|
|
|
|
if (DeviceObject)
|
|
{
|
|
FsRtlEnterFileSystem();
|
|
NpAcquireExclusiveVcb();
|
|
}
|
|
|
|
DataEntry = Irp->Tail.Overlay.DriverContext[3];
|
|
if (DataEntry)
|
|
{
|
|
if (DataEntry->QueueEntry.Blink == &DataQueue->Queue)
|
|
{
|
|
DataQueue->ByteOffset = 0;
|
|
FirstEntry = TRUE;
|
|
}
|
|
else
|
|
{
|
|
FirstEntry = FALSE;
|
|
}
|
|
|
|
RemoveEntryList(&DataEntry->QueueEntry);
|
|
|
|
ClientSecurityContext = DataEntry->ClientSecurityContext;
|
|
|
|
CompleteWrites = TRUE;
|
|
if (DataQueue->QueueState != WriteEntries ||
|
|
DataQueue->QuotaUsed < DataQueue->Quota ||
|
|
!DataEntry->QuotaInEntry)
|
|
{
|
|
CompleteWrites = FALSE;
|
|
}
|
|
|
|
DataQueue->BytesInQueue -= DataEntry->DataSize;
|
|
DataQueue->QuotaUsed -= DataEntry->QuotaInEntry;
|
|
--DataQueue->EntriesInQueue;
|
|
|
|
if (IsListEmpty(&DataQueue->Queue))
|
|
{
|
|
DataQueue->QueueState = Empty;
|
|
ASSERT(DataQueue->BytesInQueue == 0);
|
|
ASSERT(DataQueue->EntriesInQueue == 0);
|
|
ASSERT(DataQueue->QuotaUsed == 0);
|
|
}
|
|
else
|
|
{
|
|
if (FirstEntry)
|
|
{
|
|
NpGetNextRealDataQueueEntry(DataQueue, &DeferredList);
|
|
}
|
|
if (CompleteWrites)
|
|
{
|
|
NpCompleteStalledWrites(DataQueue, &DeferredList);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DeviceObject)
|
|
{
|
|
NpReleaseVcb();
|
|
FsRtlExitFileSystem();
|
|
}
|
|
|
|
if (DataEntry) ExFreePool(DataEntry);
|
|
|
|
NpFreeClientSecurityContext(ClientSecurityContext);
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Irp, IO_NAMED_PIPE_INCREMENT);
|
|
|
|
NpCompleteDeferredIrps(&DeferredList);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NpAddDataQueueEntry(IN ULONG NamedPipeEnd,
|
|
IN PNP_CCB Ccb,
|
|
IN PNP_DATA_QUEUE DataQueue,
|
|
IN ULONG Who,
|
|
IN ULONG Type,
|
|
IN ULONG DataSize,
|
|
IN PIRP Irp,
|
|
IN PVOID Buffer,
|
|
IN ULONG ByteOffset)
|
|
{
|
|
NTSTATUS Status;
|
|
PNP_DATA_QUEUE_ENTRY DataEntry;
|
|
SIZE_T EntrySize;
|
|
ULONG QuotaInEntry;
|
|
PSECURITY_CLIENT_CONTEXT ClientContext;
|
|
BOOLEAN HasSpace;
|
|
|
|
ClientContext = NULL;
|
|
ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who));
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if ((Type != 2) && (Who == WriteEntries))
|
|
{
|
|
Status = NpGetClientSecurityContext(NamedPipeEnd,
|
|
Ccb,
|
|
Irp ? Irp->Tail.Overlay.Thread :
|
|
PsGetCurrentThread(),
|
|
&ClientContext);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
switch (Type)
|
|
{
|
|
case Unbuffered:
|
|
case 2:
|
|
case 3:
|
|
|
|
ASSERT(Irp != NULL);
|
|
DataEntry = ExAllocatePoolWithQuotaTag(NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
|
|
sizeof(*DataEntry),
|
|
NPFS_DATA_ENTRY_TAG);
|
|
if (!DataEntry)
|
|
{
|
|
NpFreeClientSecurityContext(ClientContext);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
DataEntry->DataEntryType = Type;
|
|
DataEntry->QuotaInEntry = 0;
|
|
DataEntry->Irp = Irp;
|
|
DataEntry->DataSize = DataSize;
|
|
DataEntry->ClientSecurityContext = ClientContext;
|
|
ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who));
|
|
Status = STATUS_PENDING;
|
|
break;
|
|
|
|
case Buffered:
|
|
|
|
EntrySize = sizeof(*DataEntry);
|
|
if (Who != ReadEntries)
|
|
{
|
|
EntrySize += DataSize;
|
|
if (EntrySize < DataSize)
|
|
{
|
|
NpFreeClientSecurityContext(ClientContext);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
QuotaInEntry = DataSize - ByteOffset;
|
|
if (DataQueue->Quota - DataQueue->QuotaUsed < QuotaInEntry)
|
|
{
|
|
QuotaInEntry = DataQueue->Quota - DataQueue->QuotaUsed;
|
|
HasSpace = TRUE;
|
|
}
|
|
else
|
|
{
|
|
HasSpace = FALSE;
|
|
}
|
|
|
|
DataEntry = ExAllocatePoolWithQuotaTag(NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
|
|
EntrySize,
|
|
NPFS_DATA_ENTRY_TAG);
|
|
if (!DataEntry)
|
|
{
|
|
NpFreeClientSecurityContext(ClientContext);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
DataEntry->QuotaInEntry = QuotaInEntry;
|
|
DataEntry->Irp = Irp;
|
|
DataEntry->DataEntryType = Buffered;
|
|
DataEntry->ClientSecurityContext = ClientContext;
|
|
DataEntry->DataSize = DataSize;
|
|
|
|
if (Who == ReadEntries)
|
|
{
|
|
ASSERT(Irp);
|
|
|
|
Status = STATUS_PENDING;
|
|
ASSERT((DataQueue->QueueState == Empty) ||
|
|
(DataQueue->QueueState == Who));
|
|
}
|
|
else
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
RtlCopyMemory(DataEntry + 1,
|
|
Irp ? Irp->UserBuffer: Buffer,
|
|
DataSize);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
NpFreeClientSecurityContext(ClientContext);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (HasSpace && Irp)
|
|
{
|
|
Status = STATUS_PENDING;
|
|
}
|
|
else
|
|
{
|
|
DataEntry->Irp = NULL;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
ASSERT((DataQueue->QueueState == Empty) ||
|
|
(DataQueue->QueueState == Who));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
NpFreeClientSecurityContext(ClientContext);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who));
|
|
if (DataQueue->QueueState == Empty)
|
|
{
|
|
ASSERT(DataQueue->BytesInQueue == 0);
|
|
ASSERT(DataQueue->EntriesInQueue == 0);
|
|
ASSERT(IsListEmpty(&DataQueue->Queue));
|
|
}
|
|
else
|
|
{
|
|
ASSERT(DataQueue->QueueState == Who);
|
|
ASSERT(DataQueue->QueueState != Empty);
|
|
ASSERT(DataQueue->EntriesInQueue != 0);
|
|
}
|
|
|
|
DataQueue->QuotaUsed += DataEntry->QuotaInEntry;
|
|
DataQueue->QueueState = Who;
|
|
DataQueue->BytesInQueue += DataEntry->DataSize;
|
|
DataQueue->EntriesInQueue++;
|
|
|
|
if (ByteOffset)
|
|
{
|
|
DataQueue->ByteOffset = ByteOffset;
|
|
ASSERT(Who == WriteEntries);
|
|
ASSERT(ByteOffset < DataEntry->DataSize);
|
|
ASSERT(DataQueue->EntriesInQueue == 1);
|
|
}
|
|
|
|
InsertTailList(&DataQueue->Queue, &DataEntry->QueueEntry);
|
|
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
IoMarkIrpPending(Irp);
|
|
Irp->Tail.Overlay.DriverContext[2] = DataQueue;
|
|
Irp->Tail.Overlay.DriverContext[3] = DataEntry;
|
|
|
|
IoSetCancelRoutine(Irp, NpCancelDataQueueIrp);
|
|
|
|
if ((Irp->Cancel) && (IoSetCancelRoutine(Irp, NULL)))
|
|
{
|
|
NpCancelDataQueueIrp(NULL, Irp);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|