reactos/drivers/filesystems/npfs/datasup.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 */