mirror of
https://github.com/reactos/reactos.git
synced 2024-08-03 10:00:56 +00:00
[fastfat_new]
- Cleanup the source code from parts which aren't needed anymore (after switching to FullFAT library usage). About 40kb of source code removed. svn path=/trunk/; revision=43641
This commit is contained in:
parent
87f0ae4ac5
commit
4f4bec88a5
|
@ -1,447 +0,0 @@
|
||||||
/*
|
|
||||||
* PROJECT: ReactOS FAT file system driver
|
|
||||||
* LICENSE: GPL - See COPYING in the top level directory
|
|
||||||
* FILE: drivers/filesystems/fastfat/blockdev.c
|
|
||||||
* PURPOSE: Temporary sector reading support
|
|
||||||
* PROGRAMMERS: Alexey Vlasov
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* INCLUDES *****************************************************************/
|
|
||||||
|
|
||||||
#define NDEBUG
|
|
||||||
#include "fastfat.h"
|
|
||||||
|
|
||||||
/* FUNCTIONS ***************************************************************/
|
|
||||||
//FIXME: There is a conflicting function FatPerformDevIoCtrl doing same thing!
|
|
||||||
NTSTATUS
|
|
||||||
FatDiskIoControl_(
|
|
||||||
IN PDEVICE_OBJECT DeviceObject,
|
|
||||||
IN ULONG IoCtlCode,
|
|
||||||
IN PVOID InputBuffer,
|
|
||||||
IN ULONG InputBufferSize,
|
|
||||||
IN OUT PVOID OutputBuffer,
|
|
||||||
IN OUT PULONG OutputBufferSize OPTIONAL)
|
|
||||||
{
|
|
||||||
PIRP Irp;
|
|
||||||
KEVENT Event;
|
|
||||||
NTSTATUS Status;
|
|
||||||
ULONG OutBufferSize;
|
|
||||||
IO_STATUS_BLOCK IoStatus;
|
|
||||||
|
|
||||||
/* Set out buffer size if it was supplied. */
|
|
||||||
OutBufferSize = (ARGUMENT_PRESENT(OutputBufferSize)
|
|
||||||
? 0 : *OutputBufferSize);
|
|
||||||
|
|
||||||
/* Initialize event if the operation will be pended. */
|
|
||||||
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
||||||
|
|
||||||
/* Build the Irp. */
|
|
||||||
Irp = IoBuildDeviceIoControlRequest(IoCtlCode, DeviceObject,
|
|
||||||
InputBuffer, InputBufferSize, OutputBuffer, OutBufferSize,
|
|
||||||
FALSE, &Event, &IoStatus);
|
|
||||||
if (Irp == NULL)
|
|
||||||
return STATUS_INSUFFICIENT_RESOURCES;
|
|
||||||
|
|
||||||
/* Send IRP to Disk Device */
|
|
||||||
Status = IoCallDriver(DeviceObject, Irp);
|
|
||||||
if (Status == STATUS_PENDING)
|
|
||||||
{
|
|
||||||
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
||||||
Status = IoStatus.Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return output buffer length if required. */
|
|
||||||
if (ARGUMENT_PRESENT(OutputBufferSize))
|
|
||||||
*OutputBufferSize = IoStatus.Information;
|
|
||||||
return Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
FatLockUserBuffer (
|
|
||||||
IN PFAT_IRP_CONTEXT IrpContext,
|
|
||||||
IN LOCK_OPERATION Operation,
|
|
||||||
IN ULONG BufferLength)
|
|
||||||
/*
|
|
||||||
* FUNCTION:
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* ARGUMENTS:
|
|
||||||
* IrpContext = Pointer to FCB structure for the file.
|
|
||||||
* Operation = Type of lock operation.
|
|
||||||
* BufferLength = Buffer length to be locked.
|
|
||||||
* RETURNS: Status Value.
|
|
||||||
* NOTES:
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
PMDL Mdl;
|
|
||||||
PIRP Irp;
|
|
||||||
NTSTATUS Status;
|
|
||||||
|
|
||||||
Mdl = NULL;
|
|
||||||
Irp = IrpContext->Irp;
|
|
||||||
Status = STATUS_SUCCESS;
|
|
||||||
if (Irp->MdlAddress == NULL)
|
|
||||||
{
|
|
||||||
NTSTATUS Status;
|
|
||||||
Mdl = IoAllocateMdl(Irp->UserBuffer,
|
|
||||||
BufferLength, FALSE, FALSE, Irp);
|
|
||||||
if (Mdl == NULL)
|
|
||||||
return STATUS_INSUFFICIENT_RESOURCES;
|
|
||||||
_SEH2_TRY
|
|
||||||
{
|
|
||||||
MmProbeAndLockPages(Mdl,
|
|
||||||
Irp->RequestorMode, Operation);
|
|
||||||
}
|
|
||||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
||||||
{
|
|
||||||
Status = _SEH2_GetExceptionCode();
|
|
||||||
IoFreeMdl( Mdl );
|
|
||||||
Irp->MdlAddress = NULL;
|
|
||||||
} _SEH2_END
|
|
||||||
}
|
|
||||||
return Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
FatIoSyncCompletionRoutine(
|
|
||||||
IN PDEVICE_OBJECT DeviceObject,
|
|
||||||
IN PIRP Irp,
|
|
||||||
IN PVOID Context)
|
|
||||||
{
|
|
||||||
PFAT_IO_CONTEXT IoContext;
|
|
||||||
|
|
||||||
IoContext = (PFAT_IO_CONTEXT) Context;
|
|
||||||
|
|
||||||
/* Check if this is an associated irp. */
|
|
||||||
if (Irp != IoContext->Irp)
|
|
||||||
{
|
|
||||||
if (!NT_SUCCESS(Irp->IoStatus.Status))
|
|
||||||
IoContext->Irp->IoStatus = Irp->IoStatus;
|
|
||||||
IoFreeMdl(Irp->MdlAddress);
|
|
||||||
IoFreeIrp(Irp);
|
|
||||||
if (InterlockedDecrement(&IoContext->RunCount) != 0)
|
|
||||||
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
||||||
/* This was the last run, update master irp. */
|
|
||||||
if (NT_SUCCESS(IoContext->Irp->IoStatus.Status))
|
|
||||||
IoContext->Irp->IoStatus.Information = IoContext->Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is the last associated irp or a single irp IO. */
|
|
||||||
if (NT_SUCCESS(IoContext->Irp->IoStatus.Status) &&
|
|
||||||
!FlagOn(IoContext->Irp->Flags, IRP_PAGING_IO))
|
|
||||||
{
|
|
||||||
/* Maintain FileObject CurrentByteOffset */
|
|
||||||
IoContext->FileObject->CurrentByteOffset.QuadPart =
|
|
||||||
IoContext->Offset + IoContext->Irp->IoStatus.Information;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Signal about completion. */
|
|
||||||
KeSetEvent(&IoContext->Wait.SyncEvent, 0, FALSE);
|
|
||||||
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
FatIoAsyncCompletionRoutine(
|
|
||||||
IN PDEVICE_OBJECT DeviceObject,
|
|
||||||
IN PIRP Irp,
|
|
||||||
IN PVOID Context)
|
|
||||||
{
|
|
||||||
PFAT_IO_CONTEXT IoContext;
|
|
||||||
|
|
||||||
IoContext = (PFAT_IO_CONTEXT) Context;
|
|
||||||
|
|
||||||
/* Check if this is an associated irp. */
|
|
||||||
if (Irp != IoContext->Irp)
|
|
||||||
{
|
|
||||||
if (!NT_SUCCESS(Irp->IoStatus.Status))
|
|
||||||
IoContext->Irp->IoStatus = Irp->IoStatus;
|
|
||||||
IoFreeMdl(Irp->MdlAddress);
|
|
||||||
IoFreeIrp(Irp);
|
|
||||||
if (InterlockedDecrement(&IoContext->RunCount) != 0)
|
|
||||||
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
||||||
/* This was the last run, update master irp. */
|
|
||||||
if (NT_SUCCESS(IoContext->Irp->IoStatus.Status))
|
|
||||||
IoContext->Irp->IoStatus.Information = IoContext->Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* This is the last associated irp or a single irp IO. */
|
|
||||||
if (NT_SUCCESS(IoContext->Irp->IoStatus.Status) &&
|
|
||||||
!FlagOn(IoContext->Irp->Flags, IRP_PAGING_IO))
|
|
||||||
{
|
|
||||||
/* Maintain FileObject Flags */
|
|
||||||
if (IoGetCurrentIrpStackLocation(IoContext->Irp)->MajorFunction
|
|
||||||
== IRP_MJ_READ)
|
|
||||||
{
|
|
||||||
SetFlag(IoContext->FileObject->Flags, FO_FILE_FAST_IO_READ);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetFlag(IoContext->FileObject->Flags, FO_FILE_MODIFIED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (IoContext->Wait.Async.Resource != NULL)
|
|
||||||
ExReleaseResourceForThreadLite(
|
|
||||||
IoContext->Wait.Async.Resource,
|
|
||||||
IoContext->Wait.Async.ResourceThreadId);
|
|
||||||
|
|
||||||
if (IoContext->Wait.Async.PagingIoResource != NULL)
|
|
||||||
ExReleaseResourceForThreadLite(
|
|
||||||
IoContext->Wait.Async.PagingIoResource,
|
|
||||||
IoContext->Wait.Async.ResourceThreadId);
|
|
||||||
|
|
||||||
IoMarkIrpPending(Irp);
|
|
||||||
ExFreePool(Context);
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
FatPerformLboIo(
|
|
||||||
IN PFAT_IRP_CONTEXT IrpContext,
|
|
||||||
IN PLARGE_INTEGER Offset,
|
|
||||||
IN SIZE_T Length)
|
|
||||||
{
|
|
||||||
BOOLEAN CanWait, ReadOperation;
|
|
||||||
PIO_STACK_LOCATION IoStack;
|
|
||||||
|
|
||||||
CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
|
|
||||||
ReadOperation = (IrpContext->Stack->MajorFunction == IRP_MJ_READ);
|
|
||||||
|
|
||||||
/* Allocate completion context */
|
|
||||||
IrpContext->FatIoContext = FsRtlAllocatePoolWithTag(
|
|
||||||
NonPagedPool, sizeof(FAT_IO_CONTEXT), (ULONG) 'xCoI');
|
|
||||||
|
|
||||||
if (IrpContext->FatIoContext == NULL)
|
|
||||||
return STATUS_INSUFFICIENT_RESOURCES;
|
|
||||||
|
|
||||||
RtlZeroMemory(IrpContext->FatIoContext,
|
|
||||||
sizeof(FAT_IO_CONTEXT));
|
|
||||||
|
|
||||||
/* Initialize event if we are supposed to wait. */
|
|
||||||
if (CanWait)
|
|
||||||
{
|
|
||||||
KeInitializeEvent(
|
|
||||||
&IrpContext->FatIoContext->Wait.SyncEvent,
|
|
||||||
NotificationEvent, FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the completion routine depending on wait semantics. */
|
|
||||||
IoSetCompletionRoutine(IrpContext->Irp,
|
|
||||||
(CanWait
|
|
||||||
? FatIoSyncCompletionRoutine
|
|
||||||
: FatIoAsyncCompletionRoutine),
|
|
||||||
IrpContext->FatIoContext, TRUE, TRUE, TRUE);
|
|
||||||
|
|
||||||
/* Setup stack location. */
|
|
||||||
IoStack = IoGetNextIrpStackLocation(IrpContext->Irp);
|
|
||||||
IoStack->MajorFunction = IrpContext->MajorFunction;
|
|
||||||
IoStack->Parameters.Read.Length = (ULONG) Length;
|
|
||||||
IoStack->Parameters.Read.ByteOffset = *Offset;
|
|
||||||
if (FlagOn(IrpContext->Flags, IRPCONTEXT_WRITETHROUGH))
|
|
||||||
SetFlag(IoStack->Flags, SL_WRITE_THROUGH);
|
|
||||||
|
|
||||||
IoCallDriver(
|
|
||||||
IrpContext->Vcb->TargetDeviceObject,
|
|
||||||
IrpContext->Irp);
|
|
||||||
if (CanWait)
|
|
||||||
{
|
|
||||||
KeWaitForSingleObject(
|
|
||||||
&IrpContext->FatIoContext->Wait.SyncEvent,
|
|
||||||
Executive, KernelMode, FALSE, NULL);
|
|
||||||
return IrpContext->Irp->IoStatus.Status;
|
|
||||||
}
|
|
||||||
SetFlag(IrpContext->Flags, IRPCONTEXT_STACK_IO_CONTEXT);
|
|
||||||
return STATUS_PENDING;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
FatPerformVirtualNonCachedIo(
|
|
||||||
IN PFAT_IRP_CONTEXT IrpContext,
|
|
||||||
IN PFCB Fcb,
|
|
||||||
IN PLARGE_INTEGER Offset,
|
|
||||||
IN SIZE_T Length)
|
|
||||||
{
|
|
||||||
NTSTATUS Status;
|
|
||||||
PIO_STACK_LOCATION IoStack;
|
|
||||||
LONGLONG Vbo, Lbo, RunLength;
|
|
||||||
ULONG RunCount, CleanupIndex, FirstIndex, BeyoundLastIndex;
|
|
||||||
BOOLEAN CanWait, ReadOperation;
|
|
||||||
PIRP* RunIrp;
|
|
||||||
PMDL Mdl;
|
|
||||||
|
|
||||||
ASSERT(IrpContext->FatIoContext == NULL);
|
|
||||||
|
|
||||||
|
|
||||||
FirstIndex = CleanupIndex = 0;
|
|
||||||
CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
|
|
||||||
ReadOperation = (IrpContext->Stack->MajorFunction == IRP_MJ_READ);
|
|
||||||
Status = FatLockUserBuffer(IrpContext,
|
|
||||||
(ReadOperation ? IoWriteAccess : IoReadAccess),
|
|
||||||
Length);
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
goto FatIoPerformNonCachedCleanup;
|
|
||||||
Vbo = Offset->QuadPart;
|
|
||||||
RunLength = Length;
|
|
||||||
_SEH2_TRY
|
|
||||||
{
|
|
||||||
BeyoundLastIndex = FatScanFat(Fcb, Vbo,
|
|
||||||
&Lbo, &RunLength, &FirstIndex, CanWait);
|
|
||||||
}
|
|
||||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
||||||
{
|
|
||||||
Status = _SEH2_GetExceptionCode();
|
|
||||||
_SEH2_YIELD(goto FatIoPerformNonCachedCleanup;)
|
|
||||||
}_SEH2_END
|
|
||||||
RunCount = BeyoundLastIndex - FirstIndex;
|
|
||||||
if (RunCount == 0)
|
|
||||||
{
|
|
||||||
Status = STATUS_END_OF_FILE;
|
|
||||||
goto FatIoPerformNonCachedCleanup;
|
|
||||||
}
|
|
||||||
Length = sizeof(FAT_IO_CONTEXT);
|
|
||||||
if (RunCount > 0x1)
|
|
||||||
Length += RunCount * sizeof(PIRP);
|
|
||||||
IrpContext->FatIoContext = FsRtlAllocatePoolWithTag(
|
|
||||||
NonPagedPool, Length, (ULONG) 'xCoI');
|
|
||||||
if (IrpContext->FatIoContext == NULL)
|
|
||||||
{
|
|
||||||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
||||||
goto FatIoPerformNonCachedCleanup;
|
|
||||||
}
|
|
||||||
RtlZeroMemory(IrpContext->FatIoContext, Length);
|
|
||||||
if (CanWait)
|
|
||||||
{
|
|
||||||
KeInitializeEvent(
|
|
||||||
&IrpContext->FatIoContext->Wait.SyncEvent,
|
|
||||||
NotificationEvent, FALSE);
|
|
||||||
}
|
|
||||||
if (RunCount == 0x1)
|
|
||||||
{
|
|
||||||
IoSetCompletionRoutine(IrpContext->Irp,
|
|
||||||
(CanWait ? FatIoSyncCompletionRoutine
|
|
||||||
: FatIoAsyncCompletionRoutine),
|
|
||||||
IrpContext->FatIoContext, TRUE, TRUE, TRUE);
|
|
||||||
IoStack = IoGetNextIrpStackLocation(IrpContext->Irp);
|
|
||||||
IoStack->MajorFunction = IrpContext->Stack->MajorFunction;
|
|
||||||
IoStack->Parameters.Read.Length = (ULONG) RunLength;
|
|
||||||
IoStack->Parameters.Read.ByteOffset.QuadPart = Lbo;
|
|
||||||
IoStack->Flags = FlagOn(
|
|
||||||
IrpContext->Stack->Flags,
|
|
||||||
SL_WRITE_THROUGH);
|
|
||||||
Status = IoCallDriver(
|
|
||||||
IrpContext->Vcb->TargetDeviceObject,
|
|
||||||
IrpContext->Irp);
|
|
||||||
goto FatIoPerformNonCachedComplete;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* We already have the first run retrieved by FatiScanFat.
|
|
||||||
*/
|
|
||||||
for (RunIrp = &IrpContext->FatIoContext->Irp,
|
|
||||||
CleanupIndex = FirstIndex;
|
|
||||||
CleanupIndex < BeyoundLastIndex;
|
|
||||||
CleanupIndex ++, RunIrp ++)
|
|
||||||
{
|
|
||||||
#if DBG
|
|
||||||
LONGLONG NextVbo = Vbo + RunLength;
|
|
||||||
BOOLEAN RunExists;
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
* Allocate Irp for the run.
|
|
||||||
*/
|
|
||||||
*RunIrp = IoMakeAssociatedIrp(IrpContext->Irp,
|
|
||||||
(CCHAR)(IrpContext->Vcb->TargetDeviceObject->StackSize + 1));
|
|
||||||
if (*RunIrp == NULL)
|
|
||||||
{
|
|
||||||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
||||||
goto FatIoPerformNonCachedCleanup;
|
|
||||||
}
|
|
||||||
CleanupIndex ++;
|
|
||||||
/*
|
|
||||||
* Build Mdl for the run range.
|
|
||||||
*/
|
|
||||||
Mdl = IoAllocateMdl(
|
|
||||||
Add2Ptr(IrpContext->Irp->UserBuffer, Vbo, PVOID),
|
|
||||||
(ULONG) RunLength, FALSE, FALSE, *RunIrp);
|
|
||||||
if (Mdl == NULL)
|
|
||||||
{
|
|
||||||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
||||||
goto FatIoPerformNonCachedCleanup;
|
|
||||||
}
|
|
||||||
IoBuildPartialMdl(IrpContext->Irp->MdlAddress, Mdl,
|
|
||||||
Add2Ptr(IrpContext->Irp->UserBuffer, Vbo, PVOID),
|
|
||||||
(ULONG) RunLength);
|
|
||||||
/*
|
|
||||||
* Setup IRP for each run.
|
|
||||||
*/
|
|
||||||
IoSetCompletionRoutine(IrpContext->Irp,
|
|
||||||
(CanWait ? FatIoSyncCompletionRoutine
|
|
||||||
: FatIoAsyncCompletionRoutine),
|
|
||||||
IrpContext->FatIoContext, TRUE, TRUE, TRUE);
|
|
||||||
IoStack = IoGetNextIrpStackLocation(*RunIrp);
|
|
||||||
IoStack->MajorFunction = IrpContext->Stack->MajorFunction;
|
|
||||||
IoStack->Parameters.Read.Length = (ULONG) RunLength;
|
|
||||||
IoStack->Parameters.Read.ByteOffset.QuadPart = Lbo;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Propagate write-through to the associated IRPs
|
|
||||||
*/
|
|
||||||
if (FlagOn(IrpContext->Flags, IRPCONTEXT_WRITETHROUGH))
|
|
||||||
SetFlag(IoStack->Flags, SL_WRITE_THROUGH);
|
|
||||||
/*
|
|
||||||
* Prepare for next iteration:
|
|
||||||
*/
|
|
||||||
#if DBG
|
|
||||||
RunExists =
|
|
||||||
#endif
|
|
||||||
FsRtlGetNextLargeMcbEntry(&Fcb->Mcb, CleanupIndex, &Vbo, &Lbo, &RunLength);
|
|
||||||
ASSERT(RunExists);
|
|
||||||
ASSERT(NextVbo == Vbo);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Send all IRPs to the volume device, we don't need to check
|
|
||||||
* status code because cleanup will be done
|
|
||||||
* by the completion routine in any case.
|
|
||||||
*/
|
|
||||||
for (RunIrp = &IrpContext->FatIoContext->Irp,
|
|
||||||
CleanupIndex = FirstIndex;
|
|
||||||
CleanupIndex < BeyoundLastIndex;
|
|
||||||
CleanupIndex ++, RunIrp ++)
|
|
||||||
{
|
|
||||||
IoCallDriver(IrpContext->Vcb->TargetDeviceObject, *RunIrp);
|
|
||||||
}
|
|
||||||
|
|
||||||
FatIoPerformNonCachedComplete:
|
|
||||||
if (CanWait)
|
|
||||||
{
|
|
||||||
KeWaitForSingleObject(
|
|
||||||
&IrpContext->FatIoContext->Wait.SyncEvent,
|
|
||||||
Executive, KernelMode, FALSE, NULL);
|
|
||||||
return IrpContext->Irp->IoStatus.Status;
|
|
||||||
}
|
|
||||||
SetFlag(IrpContext->Flags, IRPCONTEXT_STACK_IO_CONTEXT);
|
|
||||||
return STATUS_PENDING;
|
|
||||||
/*
|
|
||||||
* The following block of code implements unwind logic
|
|
||||||
*/
|
|
||||||
FatIoPerformNonCachedCleanup:
|
|
||||||
if (IrpContext->FatIoContext != NULL)
|
|
||||||
{
|
|
||||||
RunIrp = &IrpContext->FatIoContext->Irp;
|
|
||||||
while (FirstIndex < CleanupIndex)
|
|
||||||
{
|
|
||||||
if ((*RunIrp)->MdlAddress != NULL)
|
|
||||||
IoFreeMdl((*RunIrp)->MdlAddress);
|
|
||||||
IoFreeIrp(*RunIrp);
|
|
||||||
FirstIndex ++;
|
|
||||||
RunIrp ++;
|
|
||||||
}
|
|
||||||
ExFreePool(IrpContext->FatIoContext);
|
|
||||||
IrpContext->FatIoContext = NULL;
|
|
||||||
}
|
|
||||||
return Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* EOF */
|
|
|
@ -1,775 +0,0 @@
|
||||||
/*
|
|
||||||
* PROJECT: ReactOS FAT file system driver
|
|
||||||
* LICENSE: GPL - See COPYING in the top level directory
|
|
||||||
* FILE: drivers/filesystems/fastfat/direntry.c
|
|
||||||
* PURPOSE: Directory entries
|
|
||||||
* PROGRAMMERS: Alexey Vlasov
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* INCLUDES *****************************************************************/
|
|
||||||
|
|
||||||
#define NDEBUG
|
|
||||||
#include "fastfat.h"
|
|
||||||
|
|
||||||
/* PROTOTYPES ***************************************************************/
|
|
||||||
|
|
||||||
typedef enum _FILE_TIME_INDEX
|
|
||||||
{
|
|
||||||
FileCreationTime = 0,
|
|
||||||
FileLastAccessTime,
|
|
||||||
FileLastWriteTime,
|
|
||||||
FileChangeTime
|
|
||||||
} FILE_TIME_INDEX;
|
|
||||||
|
|
||||||
|
|
||||||
VOID
|
|
||||||
Fat8dot3ToUnicodeString(OUT PUNICODE_STRING FileName,
|
|
||||||
IN PUCHAR ShortName,
|
|
||||||
IN UCHAR Flags);
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToDirInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer);
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToFullDirInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer);
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToIdFullDirInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer);
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToBothDirInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer);
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToIdBothDirInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer);
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToNamesInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer);
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToObjectIdInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer);
|
|
||||||
|
|
||||||
/* FUNCTIONS *****************************************************************/
|
|
||||||
|
|
||||||
#define FatQueryFileName(xInfo, xDirent) \
|
|
||||||
{ \
|
|
||||||
UNICODE_STRING FileName; \
|
|
||||||
if (Info->FileNameLength == 0) \
|
|
||||||
{ \
|
|
||||||
FileName.Buffer = Info->FileName; \
|
|
||||||
FileName.MaximumLength = 0x18; \
|
|
||||||
Fat8dot3ToUnicodeString(&FileName, \
|
|
||||||
(xDirent)->FileName, (xDirent)->Case); \
|
|
||||||
Info->FileNameLength = FileName.Length; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define FatQueryBothFileName(xInfo, xDirent) \
|
|
||||||
{ \
|
|
||||||
UNICODE_STRING FileName; \
|
|
||||||
FileName.MaximumLength = 0x18; \
|
|
||||||
if (Info->FileNameLength == 0) \
|
|
||||||
{ \
|
|
||||||
FileName.Buffer = Info->FileName; \
|
|
||||||
Fat8dot3ToUnicodeString(&FileName, \
|
|
||||||
(xDirent)->FileName, (xDirent)->Case); \
|
|
||||||
Info->FileNameLength = FileName.Length; \
|
|
||||||
Info->ShortNameLength = 0; \
|
|
||||||
} \
|
|
||||||
else \
|
|
||||||
{ \
|
|
||||||
FileName.Buffer = Info->ShortName; \
|
|
||||||
Fat8dot3ToUnicodeString(&FileName, \
|
|
||||||
(xDirent)->FileName, (xDirent)->Case); \
|
|
||||||
Info->ShortNameLength = (CCHAR) FileName.Length; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
FORCEINLINE
|
|
||||||
UCHAR
|
|
||||||
FatLfnChecksum(
|
|
||||||
PUCHAR Buffer
|
|
||||||
)
|
|
||||||
{
|
|
||||||
UCHAR Index, Chksum;
|
|
||||||
|
|
||||||
for (Index = 0x1, Chksum = *Buffer;
|
|
||||||
Index < RTL_FIELD_SIZE(DIR_ENTRY, FileName);
|
|
||||||
Index ++)
|
|
||||||
{
|
|
||||||
Chksum = (((Chksum & 0x1) << 0x7) | (Chksum >> 0x1))
|
|
||||||
+ Buffer[Index];
|
|
||||||
}
|
|
||||||
return Chksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
VOID
|
|
||||||
FatFindDirent(IN OUT PFAT_FIND_DIRENT_CONTEXT Context,
|
|
||||||
OUT PDIR_ENTRY* Dirent,
|
|
||||||
OUT PUNICODE_STRING LongFileName OPTIONAL)
|
|
||||||
{
|
|
||||||
PDIR_ENTRY Entry, EndOfPage;
|
|
||||||
UCHAR SeqNum = 0, Checksum = 0;
|
|
||||||
PUNICODE_STRING FileName;
|
|
||||||
|
|
||||||
/* Pin first page. */
|
|
||||||
Entry = (PDIR_ENTRY) FatPinPage(&Context->Page, 0);
|
|
||||||
EndOfPage = FatPinEndOfPage(&Context->Page, PDIR_ENTRY);
|
|
||||||
|
|
||||||
/* Run dirents. */
|
|
||||||
FileName = NULL;
|
|
||||||
while (TRUE)
|
|
||||||
{
|
|
||||||
/* Check if we have entered the area of never used dirents */
|
|
||||||
if (Entry->FileName[0] == FAT_DIRENT_NEVER_USED)
|
|
||||||
ExRaiseStatus(STATUS_OBJECT_NAME_NOT_FOUND);
|
|
||||||
|
|
||||||
/* Just ignore entries marked as deleted */
|
|
||||||
if (Entry->FileName[0] == FAT_DIRENT_DELETED)
|
|
||||||
goto FatFindDirentNext;
|
|
||||||
|
|
||||||
/* Check if it's an lfn */
|
|
||||||
if (Entry->Attributes == FAT_DIRENT_ATTR_LFN)
|
|
||||||
{
|
|
||||||
PLONG_FILE_NAME_ENTRY LfnEntry;
|
|
||||||
|
|
||||||
FileName = LongFileName;
|
|
||||||
LfnEntry = (PLONG_FILE_NAME_ENTRY) Entry;
|
|
||||||
|
|
||||||
/* Run lfns and collect file name if required */
|
|
||||||
do {
|
|
||||||
PWSTR Lfn;
|
|
||||||
|
|
||||||
/* Check if we just running lfn */
|
|
||||||
if (FileName == NULL)
|
|
||||||
goto FatFindDirentRunLfn;
|
|
||||||
|
|
||||||
/* Check for cluster index to be zero. */
|
|
||||||
if (LfnEntry->Reserved != 0)
|
|
||||||
{
|
|
||||||
FileName = NULL;
|
|
||||||
goto FatFindDirentRunLfn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if this is the last lfn entry. */
|
|
||||||
if (FlagOn(LfnEntry->SeqNum, FAT_FN_DIR_ENTRY_LAST))
|
|
||||||
{
|
|
||||||
SeqNum = (LfnEntry->SeqNum & ~FAT_FN_DIR_ENTRY_LAST);
|
|
||||||
Checksum = LfnEntry->Checksum;
|
|
||||||
|
|
||||||
/* Check if we exceed max number of lfns */
|
|
||||||
if (SeqNum > (FAT_FN_MAX_DIR_ENTIES + 1))
|
|
||||||
{
|
|
||||||
FileName = NULL;
|
|
||||||
goto FatFindDirentRunLfn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup maximal expected lfn length */
|
|
||||||
FileName->Length = (SeqNum * FAT_LFN_NAME_LENGTH);
|
|
||||||
|
|
||||||
/* Extend lfn buffer if needed */
|
|
||||||
if (FileName->Length > FileName->MaximumLength)
|
|
||||||
{
|
|
||||||
Lfn = ExAllocatePoolWithTag(PagedPool,
|
|
||||||
LongFileName->Length, TAG_VFAT);
|
|
||||||
if (Lfn == NULL)
|
|
||||||
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
||||||
if (FileName->Buffer != NULL)
|
|
||||||
ExFreePool(FileName->Buffer);
|
|
||||||
FileName->Buffer = Lfn;
|
|
||||||
FileName->MaximumLength = FileName->Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!(LfnEntry->SeqNum == SeqNum
|
|
||||||
&& LfnEntry->Checksum == Checksum))
|
|
||||||
{
|
|
||||||
/* Wrong SeqNum or CheckSum. */
|
|
||||||
FileName = NULL;
|
|
||||||
goto FatFindDirentRunLfn;
|
|
||||||
}
|
|
||||||
/* Gather file name */
|
|
||||||
Lfn = Add2Ptr(FileName->Buffer, (SeqNum * FAT_LFN_NAME_LENGTH)
|
|
||||||
- sizeof(LfnEntry->NameC), PWSTR);
|
|
||||||
RtlCopyMemory(Lfn, LfnEntry->NameC, sizeof(LfnEntry->NameC));
|
|
||||||
Lfn -= (sizeof(LfnEntry->NameB) / sizeof(WCHAR));
|
|
||||||
RtlCopyMemory(Lfn, LfnEntry->NameB, sizeof(LfnEntry->NameB));
|
|
||||||
Lfn -= (sizeof(LfnEntry->NameA) / sizeof(WCHAR));
|
|
||||||
RtlCopyMemory(Lfn, LfnEntry->NameA, sizeof(LfnEntry->NameA));
|
|
||||||
|
|
||||||
/* If last lfn determine exact lfn length. */
|
|
||||||
if (FlagOn(LfnEntry->SeqNum, FAT_FN_DIR_ENTRY_LAST))
|
|
||||||
{
|
|
||||||
PWSTR LfnEnd = Add2Ptr(FileName->Buffer,
|
|
||||||
(FileName->Length - sizeof(WCHAR)), PWSTR);
|
|
||||||
|
|
||||||
/* Trim trailing 0xffff's */
|
|
||||||
while (LfnEnd > Lfn && *LfnEnd == 0xffff) --LfnEnd;
|
|
||||||
|
|
||||||
/* Trim 0 terminator is the one is there. */
|
|
||||||
if (*LfnEnd == 0x0) --LfnEnd;
|
|
||||||
|
|
||||||
/* Set correct lfn size */
|
|
||||||
FileName->Length = (USHORT)PtrOffset(FileName->Buffer, LfnEnd);
|
|
||||||
}
|
|
||||||
/* Setup validation for the next iteration */
|
|
||||||
SeqNum = LfnEntry->SeqNum - 0x1;
|
|
||||||
Checksum = LfnEntry->Checksum;
|
|
||||||
|
|
||||||
FatFindDirentRunLfn:
|
|
||||||
/* Get next dirent */
|
|
||||||
LfnEntry ++;
|
|
||||||
if (LfnEntry > (PLONG_FILE_NAME_ENTRY) EndOfPage)
|
|
||||||
{
|
|
||||||
if (FatPinIsLastPage(&Context->Page))
|
|
||||||
ExRaiseStatus(STATUS_OBJECT_NAME_NOT_FOUND);
|
|
||||||
LfnEntry = (PLONG_FILE_NAME_ENTRY) FatPinNextPage(&Context->Page);
|
|
||||||
EndOfPage = FatPinEndOfPage(&Context->Page, PDIR_ENTRY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (LfnEntry->Attributes == FAT_DIRENT_ATTR_LFN);
|
|
||||||
Entry = (PDIR_ENTRY) LfnEntry;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we've got here then this is a normal dirent */
|
|
||||||
if (FileName != NULL && FileName->Length > 0)
|
|
||||||
{
|
|
||||||
/* Check if we have a correct lfn collected. */
|
|
||||||
if (FatLfnChecksum(Entry->FileName) != Checksum)
|
|
||||||
{
|
|
||||||
FileName = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* See if we were looking for this dirent. */
|
|
||||||
if (!Context->Valid8dot3Name &&
|
|
||||||
FsRtlAreNamesEqual(FileName, Context->FileName, TRUE, NULL))
|
|
||||||
{
|
|
||||||
Fat8dot3ToUnicodeString(&Context->ShortName, Entry->FileName, Entry->Case);
|
|
||||||
*Dirent = Entry;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We surely have a short name, check if we were looking for that. */
|
|
||||||
if (Context->Valid8dot3Name)
|
|
||||||
{
|
|
||||||
Fat8dot3ToUnicodeString(&Context->ShortName,
|
|
||||||
Entry->FileName, Entry->Case);
|
|
||||||
if (FsRtlAreNamesEqual(&Context->ShortName, Context->FileName, TRUE, NULL))
|
|
||||||
{
|
|
||||||
if (ARGUMENT_PRESENT(LongFileName) && FileName == NULL)
|
|
||||||
LongFileName->Length = 0;
|
|
||||||
*Dirent = Entry;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FileName = NULL;
|
|
||||||
|
|
||||||
FatFindDirentNext:
|
|
||||||
Entry ++;
|
|
||||||
if (Entry > EndOfPage)
|
|
||||||
{
|
|
||||||
if (FatPinIsLastPage(&Context->Page))
|
|
||||||
ExRaiseStatus(STATUS_OBJECT_NAME_NOT_FOUND);
|
|
||||||
Entry = (PDIR_ENTRY) FatPinNextPage(&Context->Page);
|
|
||||||
EndOfPage = FatPinEndOfPage(&Context->Page, PDIR_ENTRY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Should never get here! */
|
|
||||||
ASSERT(TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
VOID
|
|
||||||
FatEnumerateDirents(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN SIZE_T Offset)
|
|
||||||
{
|
|
||||||
PUCHAR Entry, EndOfPage;
|
|
||||||
PWSTR FileName;
|
|
||||||
USHORT FileNameLength;
|
|
||||||
USHORT FileNameMaximumLength;
|
|
||||||
PVOID InfoEntry;
|
|
||||||
UCHAR SeqNum = 0;
|
|
||||||
UCHAR Checksum = 0;
|
|
||||||
|
|
||||||
/* Pin first page. */
|
|
||||||
Entry = (PUCHAR)FatPinPage(&Context->Page, Offset);
|
|
||||||
EndOfPage = FatPinEndOfPage(&Context->Page, PUCHAR);
|
|
||||||
|
|
||||||
/* Iterate dirents. */
|
|
||||||
while (TRUE)
|
|
||||||
{
|
|
||||||
/* Check if we have entered the area of never used dirents */
|
|
||||||
if (*Entry == FAT_DIRENT_NEVER_USED)
|
|
||||||
ExRaiseStatus(STATUS_NO_MORE_FILES);
|
|
||||||
|
|
||||||
/* Just ignore entries marked as deleted */
|
|
||||||
if (*Entry == FAT_DIRENT_DELETED)
|
|
||||||
goto FatEnumerateDirentsNext;
|
|
||||||
|
|
||||||
/* Get info pointer. */
|
|
||||||
InfoEntry = Add2Ptr(Context->Buffer, Context->Offset, PVOID);
|
|
||||||
|
|
||||||
/* Check if info class has file name */
|
|
||||||
if (Context->NameOffset == Context->LengthOffset)
|
|
||||||
{
|
|
||||||
FileName = NULL;
|
|
||||||
FileNameMaximumLength = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FileName = Add2Ptr(InfoEntry, Context->NameOffset, PWSTR);
|
|
||||||
FileNameMaximumLength = (USHORT) (Context->Length
|
|
||||||
- Context->Offset + Context->NameOffset);
|
|
||||||
}
|
|
||||||
FileNameLength = 0;
|
|
||||||
|
|
||||||
/* Check if it's an lfn */
|
|
||||||
while (Entry[0xb] == FAT_DIRENT_ATTR_LFN)
|
|
||||||
{
|
|
||||||
PWSTR Lfn;
|
|
||||||
PLONG_FILE_NAME_ENTRY LfnEntry;
|
|
||||||
|
|
||||||
/* Check if we just running lfn */
|
|
||||||
if (FileNameMaximumLength == 0)
|
|
||||||
goto FatEnumerateDirentsRunLfn;
|
|
||||||
|
|
||||||
LfnEntry = (PLONG_FILE_NAME_ENTRY) Entry;
|
|
||||||
|
|
||||||
/* Check for cluster index to be zero. */
|
|
||||||
if (LfnEntry->Reserved != 0)
|
|
||||||
{
|
|
||||||
FileNameMaximumLength = 0;
|
|
||||||
goto FatEnumerateDirentsRunLfn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if this is the last lfn entry. */
|
|
||||||
if (FlagOn(LfnEntry->SeqNum, FAT_FN_DIR_ENTRY_LAST))
|
|
||||||
{
|
|
||||||
SeqNum = (LfnEntry->SeqNum & ~FAT_FN_DIR_ENTRY_LAST);
|
|
||||||
Checksum = LfnEntry->Checksum;
|
|
||||||
|
|
||||||
/* Check if we exceed max number of lfns */
|
|
||||||
if (SeqNum > (FAT_FN_MAX_DIR_ENTIES + 1))
|
|
||||||
{
|
|
||||||
FileNameMaximumLength = 0;
|
|
||||||
goto FatEnumerateDirentsRunLfn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup maximal expected lfn length */
|
|
||||||
FileNameLength = SeqNum * FAT_LFN_NAME_LENGTH;
|
|
||||||
|
|
||||||
/* Validate the maximal expected lfn length */
|
|
||||||
if (FileNameLength > FileNameMaximumLength)
|
|
||||||
goto FatEnumerateDirentsRunLfn;
|
|
||||||
}
|
|
||||||
else if (!(LfnEntry->SeqNum == SeqNum
|
|
||||||
&& LfnEntry->Checksum == Checksum))
|
|
||||||
{
|
|
||||||
/* Wrong SeqNum or CheckSum. */
|
|
||||||
FileNameMaximumLength = 0;
|
|
||||||
goto FatEnumerateDirentsRunLfn;
|
|
||||||
}
|
|
||||||
/* Gather file name */
|
|
||||||
Lfn = Add2Ptr(FileName, (SeqNum * FAT_LFN_NAME_LENGTH)
|
|
||||||
- sizeof(LfnEntry->NameC), PWSTR);
|
|
||||||
RtlCopyMemory(Lfn, LfnEntry->NameC, sizeof(LfnEntry->NameC));
|
|
||||||
Lfn -= (sizeof(LfnEntry->NameB) / sizeof(WCHAR));
|
|
||||||
RtlCopyMemory(Lfn, LfnEntry->NameB, sizeof(LfnEntry->NameB));
|
|
||||||
Lfn -= (sizeof(LfnEntry->NameA) / sizeof(WCHAR));
|
|
||||||
RtlCopyMemory(Lfn, LfnEntry->NameA, sizeof(LfnEntry->NameA));
|
|
||||||
|
|
||||||
/* If last lfn determine exact lfn length. */
|
|
||||||
if (FlagOn(LfnEntry->SeqNum, FAT_FN_DIR_ENTRY_LAST))
|
|
||||||
{
|
|
||||||
PWSTR LfnEnd = Add2Ptr(FileName, FileNameLength
|
|
||||||
- sizeof(WCHAR), PWSTR);
|
|
||||||
|
|
||||||
/* Trim trailing 0xffff's */
|
|
||||||
while (LfnEnd > Lfn && *LfnEnd == 0xffff) --LfnEnd;
|
|
||||||
|
|
||||||
/* Trim 0 terminator is the one is there. */
|
|
||||||
if (*LfnEnd == 0x0) --LfnEnd;
|
|
||||||
|
|
||||||
/* Set correct lfn size */
|
|
||||||
FileNameLength = (USHORT)PtrOffset(FileName, LfnEnd);
|
|
||||||
}
|
|
||||||
/* Setup vaidation for the next iteration */
|
|
||||||
SeqNum = LfnEntry->SeqNum - 0x1;
|
|
||||||
Checksum = LfnEntry->Checksum;
|
|
||||||
FatEnumerateDirentsRunLfn:
|
|
||||||
Entry = Add2Ptr(Entry, sizeof(DIR_ENTRY), PUCHAR);
|
|
||||||
if (Entry > EndOfPage)
|
|
||||||
{
|
|
||||||
if (FatPinIsLastPage(&Context->Page))
|
|
||||||
ExRaiseStatus(STATUS_NO_MORE_FILES);
|
|
||||||
Entry = (PUCHAR) FatPinNextPage(&Context->Page);
|
|
||||||
EndOfPage = FatPinEndOfPage(&Context->Page, PUCHAR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if lfn was found, validate and commit. */
|
|
||||||
if (FileNameLength > 0 && FatLfnChecksum(Entry) == Checksum)
|
|
||||||
{
|
|
||||||
*Add2Ptr(InfoEntry, Context->LengthOffset, PULONG) = FileNameLength;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*Add2Ptr(InfoEntry, Context->LengthOffset, PULONG) = 0;
|
|
||||||
}
|
|
||||||
/* TODO: Implement Filtering using Context->FileName & Context->CcbFlags. */
|
|
||||||
|
|
||||||
/* Copy the entry values. */
|
|
||||||
Context->Offset += Context->CopyDirent((PFAT_ENUM_DIR_CONTEXT)Context, (PDIR_ENTRY) Entry, InfoEntry);
|
|
||||||
|
|
||||||
FatEnumerateDirentsNext:
|
|
||||||
/* Get next entry */
|
|
||||||
Entry = Add2Ptr(Entry, sizeof(DIR_ENTRY), PUCHAR);
|
|
||||||
if (Entry > EndOfPage)
|
|
||||||
{
|
|
||||||
if (FatPinIsLastPage(&Context->Page))
|
|
||||||
ExRaiseStatus(STATUS_NO_MORE_FILES);
|
|
||||||
Entry = (PUCHAR) FatPinNextPage(&Context->Page);
|
|
||||||
EndOfPage = FatPinEndOfPage(&Context->Page, PUCHAR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FORCEINLINE
|
|
||||||
VOID
|
|
||||||
FatDateTimeToSystemTime(OUT PLARGE_INTEGER SystemTime,
|
|
||||||
IN PFAT_DATETIME FatDateTime,
|
|
||||||
IN UCHAR TenMs OPTIONAL)
|
|
||||||
{
|
|
||||||
TIME_FIELDS TimeFields;
|
|
||||||
|
|
||||||
/* Setup time fields */
|
|
||||||
TimeFields.Year = FatDateTime->Date.Year + 1980;
|
|
||||||
TimeFields.Month = FatDateTime->Date.Month;
|
|
||||||
TimeFields.Day = FatDateTime->Date.Day;
|
|
||||||
TimeFields.Hour = FatDateTime->Time.Hour;
|
|
||||||
TimeFields.Minute = FatDateTime->Time.Minute;
|
|
||||||
TimeFields.Second = (FatDateTime->Time.DoubleSeconds << 1);
|
|
||||||
|
|
||||||
/* Adjust up to 10 milliseconds
|
|
||||||
* if the parameter was supplied
|
|
||||||
*/
|
|
||||||
if (ARGUMENT_PRESENT(TenMs))
|
|
||||||
{
|
|
||||||
TimeFields.Second += TenMs / 100;
|
|
||||||
TimeFields.Milliseconds = (TenMs % 100) * 10;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TimeFields.Milliseconds = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fix seconds value that might get beyoud the bound */
|
|
||||||
if (TimeFields.Second > 59) TimeFields.Second = 0;
|
|
||||||
|
|
||||||
/* Perform ceonversion to system time if possible */
|
|
||||||
if (RtlTimeFieldsToTime(&TimeFields, SystemTime))
|
|
||||||
{
|
|
||||||
/* Convert to system time */
|
|
||||||
ExLocalTimeToSystemTime(SystemTime, SystemTime);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Set to default time if conversion failed */
|
|
||||||
*SystemTime = FatGlobalData.DefaultFileTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VOID
|
|
||||||
FatQueryFileTimes(OUT PLARGE_INTEGER FileTimes,
|
|
||||||
IN PDIR_ENTRY Dirent)
|
|
||||||
{
|
|
||||||
/* Convert LastWriteTime */
|
|
||||||
FatDateTimeToSystemTime(&FileTimes[FileLastWriteTime],
|
|
||||||
&Dirent->LastWriteDateTime,
|
|
||||||
0);
|
|
||||||
/* All other time fileds are valid (according to MS)
|
|
||||||
* only if Win31 compatability mode is set.
|
|
||||||
*/
|
|
||||||
if (FatGlobalData.Win31FileSystem)
|
|
||||||
{
|
|
||||||
/* We can avoid calling conversion routine
|
|
||||||
* if time in dirent is 0 or equals to already
|
|
||||||
* known time (LastWriteTime).
|
|
||||||
*/
|
|
||||||
if (Dirent->CreationDateTime.Value == 0)
|
|
||||||
{
|
|
||||||
/* Set it to default time */
|
|
||||||
FileTimes[FileCreationTime] = FatGlobalData.DefaultFileTime;
|
|
||||||
}
|
|
||||||
else if (Dirent->CreationDateTime.Value
|
|
||||||
== Dirent->LastWriteDateTime.Value)
|
|
||||||
{
|
|
||||||
/* Assign the already known time */
|
|
||||||
FileTimes[FileCreationTime] = FileTimes[FileLastWriteTime];
|
|
||||||
/* Adjust milliseconds from extra dirent field */
|
|
||||||
FileTimes[FileCreationTime].QuadPart
|
|
||||||
+= (ULONG) Dirent->CreationTimeTenMs * 100000;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Perform conversion */
|
|
||||||
FatDateTimeToSystemTime(&FileTimes[FileCreationTime],
|
|
||||||
&Dirent->CreationDateTime,
|
|
||||||
Dirent->CreationTimeTenMs);
|
|
||||||
}
|
|
||||||
if (Dirent->LastAccessDate.Value == 0)
|
|
||||||
{
|
|
||||||
/* Set it to default time */
|
|
||||||
FileTimes[FileLastAccessTime] = FatGlobalData.DefaultFileTime;
|
|
||||||
}
|
|
||||||
else if (Dirent->LastAccessDate.Value
|
|
||||||
== Dirent->LastWriteDateTime.Date.Value)
|
|
||||||
{
|
|
||||||
/* Assign the already known time */
|
|
||||||
FileTimes[FileLastAccessTime] = FileTimes[FileLastWriteTime];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Perform conversion */
|
|
||||||
FAT_DATETIME LastAccessDateTime;
|
|
||||||
|
|
||||||
LastAccessDateTime.Date.Value = Dirent->LastAccessDate.Value;
|
|
||||||
LastAccessDateTime.Time.Value = 0;
|
|
||||||
FatDateTimeToSystemTime(&FileTimes[FileLastAccessTime],
|
|
||||||
&LastAccessDateTime,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VOID
|
|
||||||
Fat8dot3ToUnicodeString(OUT PUNICODE_STRING FileName,
|
|
||||||
IN PUCHAR ShortName,
|
|
||||||
IN UCHAR Flags)
|
|
||||||
{
|
|
||||||
PCHAR Name;
|
|
||||||
UCHAR Index, Ext = 0;
|
|
||||||
OEM_STRING Oem;
|
|
||||||
|
|
||||||
Name = Add2Ptr(FileName->Buffer, 0x0c, PCHAR);
|
|
||||||
RtlCopyMemory(Name, ShortName, 0x0b);
|
|
||||||
|
|
||||||
/* Restore the name byte used to mark deleted entries */
|
|
||||||
if (Name[0] == 0x05)
|
|
||||||
Name[0] |= 0xe0;
|
|
||||||
|
|
||||||
/* Locate the end of name part */
|
|
||||||
for (Index = 0; Index < 0x08
|
|
||||||
&& Name[Index] != 0x20; Index++);
|
|
||||||
|
|
||||||
/* Locate the end of extension part */
|
|
||||||
if (Name[0x08] != 0x20)
|
|
||||||
{
|
|
||||||
Ext = 0x2;
|
|
||||||
Name[Index++] = 0x2e;
|
|
||||||
Name[Index++] = Name[0x08];
|
|
||||||
if ((Name[Index] = Name[0x09]) != 0x20)
|
|
||||||
{
|
|
||||||
Index ++; Ext ++;
|
|
||||||
}
|
|
||||||
if ((Name[Index] = Name[0x0a]) != 0x20)
|
|
||||||
{
|
|
||||||
Index ++; Ext ++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform Oem to Unicode conversion. */
|
|
||||||
Oem.Buffer = Name;
|
|
||||||
Oem.Length = Index;
|
|
||||||
Oem.MaximumLength = Index;
|
|
||||||
RtlOemStringToUnicodeString(FileName, &Oem, FALSE);
|
|
||||||
Index = FlagOn(Flags, FAT_CASE_LOWER_BASE|FAT_CASE_LOWER_EXT);
|
|
||||||
if (Index > 0)
|
|
||||||
{
|
|
||||||
/* Downcase the whole name */
|
|
||||||
if (Index == (FAT_CASE_LOWER_BASE|FAT_CASE_LOWER_EXT))
|
|
||||||
{
|
|
||||||
RtlUpcaseUnicodeString(FileName, FileName, FALSE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Index == FAT_CASE_LOWER_EXT)
|
|
||||||
{
|
|
||||||
/* Set extension for downcase */
|
|
||||||
Oem.Length = Ext * sizeof(WCHAR);
|
|
||||||
Oem.Buffer = Add2Ptr(FileName->Buffer,
|
|
||||||
FileName->Length - Oem.Length,
|
|
||||||
PSTR);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Set base name for downcase */
|
|
||||||
Oem.Buffer = (PSTR) FileName->Buffer;
|
|
||||||
Oem.Length = FileName->Length - Ext * sizeof(WCHAR);
|
|
||||||
}
|
|
||||||
Oem.MaximumLength = Oem.Length;
|
|
||||||
RtlUpcaseUnicodeString((PUNICODE_STRING)&Oem,
|
|
||||||
(PUNICODE_STRING)&Oem,
|
|
||||||
FALSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToDirInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer)
|
|
||||||
{
|
|
||||||
PFILE_DIRECTORY_INFORMATION Info;
|
|
||||||
Info = (PFILE_DIRECTORY_INFORMATION) Buffer;
|
|
||||||
Info->FileIndex = 0;
|
|
||||||
/* Setup Attributes */
|
|
||||||
Info->FileAttributes = Dirent->Attributes;
|
|
||||||
/* Setup times */
|
|
||||||
FatQueryFileTimes(&Info->CreationTime, Dirent);
|
|
||||||
/* Setup sizes */
|
|
||||||
Info->EndOfFile.QuadPart = Dirent->FileSize;
|
|
||||||
Info->AllocationSize.QuadPart =
|
|
||||||
(Context->BytesPerClusterMask + Dirent->FileSize)
|
|
||||||
& ~(Context->BytesPerClusterMask);
|
|
||||||
FatQueryFileName(Info, Dirent);
|
|
||||||
Info->NextEntryOffset = sizeof(*Info);
|
|
||||||
return Info->NextEntryOffset + Info->FileNameLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToFullDirInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer)
|
|
||||||
{
|
|
||||||
PFILE_FULL_DIR_INFORMATION Info;
|
|
||||||
Info = (PFILE_FULL_DIR_INFORMATION) Buffer;
|
|
||||||
Info->FileIndex = 0;
|
|
||||||
Info->EaSize = 0;
|
|
||||||
/* Setup Attributes */
|
|
||||||
Info->FileAttributes = Dirent->Attributes;
|
|
||||||
/* Setup times */
|
|
||||||
FatQueryFileTimes(&Info->CreationTime, Dirent);
|
|
||||||
/* Setup sizes */
|
|
||||||
Info->EndOfFile.QuadPart = Dirent->FileSize;
|
|
||||||
Info->AllocationSize.QuadPart =
|
|
||||||
(Context->BytesPerClusterMask + Dirent->FileSize)
|
|
||||||
& ~(Context->BytesPerClusterMask);
|
|
||||||
FatQueryFileName(Info, Dirent);
|
|
||||||
Info->NextEntryOffset = sizeof(*Info);
|
|
||||||
return Info->NextEntryOffset + Info->FileNameLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToIdFullDirInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer)
|
|
||||||
{
|
|
||||||
PFILE_ID_FULL_DIR_INFORMATION Info;
|
|
||||||
Info = (PFILE_ID_FULL_DIR_INFORMATION) Buffer;
|
|
||||||
Info->FileId.QuadPart = 0;
|
|
||||||
Info->FileIndex = 0;
|
|
||||||
Info->EaSize = 0;
|
|
||||||
/* Setup Attributes */
|
|
||||||
Info->FileAttributes = Dirent->Attributes;
|
|
||||||
/* Setup times */
|
|
||||||
FatQueryFileTimes(&Info->CreationTime, Dirent);
|
|
||||||
/* Setup sizes */
|
|
||||||
Info->EndOfFile.QuadPart = Dirent->FileSize;
|
|
||||||
Info->AllocationSize.QuadPart =
|
|
||||||
(Context->BytesPerClusterMask + Dirent->FileSize)
|
|
||||||
& ~(Context->BytesPerClusterMask);
|
|
||||||
FatQueryFileName(Info, Dirent);
|
|
||||||
Info->NextEntryOffset = sizeof(*Info);
|
|
||||||
return Info->NextEntryOffset + Info->FileNameLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToBothDirInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer)
|
|
||||||
{
|
|
||||||
PFILE_BOTH_DIR_INFORMATION Info;
|
|
||||||
Info = (PFILE_BOTH_DIR_INFORMATION) Buffer;
|
|
||||||
Info->FileIndex = 0;
|
|
||||||
Info->EaSize = 0;
|
|
||||||
/* Setup Attributes */
|
|
||||||
Info->FileAttributes = Dirent->Attributes;
|
|
||||||
/* Setup times */
|
|
||||||
FatQueryFileTimes(&Info->CreationTime, Dirent);
|
|
||||||
/* Setup sizes */
|
|
||||||
Info->EndOfFile.QuadPart = Dirent->FileSize;
|
|
||||||
Info->AllocationSize.QuadPart =
|
|
||||||
(Context->BytesPerClusterMask + Dirent->FileSize)
|
|
||||||
& ~(Context->BytesPerClusterMask);
|
|
||||||
FatQueryBothFileName(Info, Dirent);
|
|
||||||
Info->NextEntryOffset = sizeof(*Info);
|
|
||||||
return Info->NextEntryOffset + Info->FileNameLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToIdBothDirInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer)
|
|
||||||
{
|
|
||||||
PFILE_ID_BOTH_DIR_INFORMATION Info;
|
|
||||||
Info = (PFILE_ID_BOTH_DIR_INFORMATION) Buffer;
|
|
||||||
Info->FileId.QuadPart = 0;
|
|
||||||
Info->FileIndex = 0;
|
|
||||||
Info->EaSize = 0;
|
|
||||||
/* Setup Attributes */
|
|
||||||
Info->FileAttributes = Dirent->Attributes;
|
|
||||||
/* Setup times */
|
|
||||||
FatQueryFileTimes(&Info->CreationTime, Dirent);
|
|
||||||
/* Setup sizes */
|
|
||||||
Info->EndOfFile.QuadPart = Dirent->FileSize;
|
|
||||||
Info->AllocationSize.QuadPart =
|
|
||||||
(Context->BytesPerClusterMask + Dirent->FileSize)
|
|
||||||
& ~(Context->BytesPerClusterMask);
|
|
||||||
FatQueryBothFileName(Info, Dirent);
|
|
||||||
Info->NextEntryOffset = sizeof(*Info);
|
|
||||||
return Info->NextEntryOffset + Info->FileNameLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToNamesInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer)
|
|
||||||
{
|
|
||||||
PFILE_NAMES_INFORMATION Info;
|
|
||||||
Info = (PFILE_NAMES_INFORMATION) Buffer;
|
|
||||||
Info->FileIndex = 0;
|
|
||||||
FatQueryFileName(Info, Dirent);
|
|
||||||
Info->NextEntryOffset = sizeof(*Info);
|
|
||||||
return Info->NextEntryOffset + Info->FileNameLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatDirentToObjectIdInfo(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN PDIR_ENTRY Dirent,
|
|
||||||
IN PVOID Buffer)
|
|
||||||
{
|
|
||||||
PFILE_OBJECTID_INFORMATION Info;
|
|
||||||
Info = (PFILE_OBJECTID_INFORMATION) Buffer;
|
|
||||||
Info->FileReference = 0;
|
|
||||||
((PLONGLONG)Info->ObjectId)[0] = 0LL;
|
|
||||||
((PLONGLONG)Info->ObjectId)[1] = 0LL;
|
|
||||||
return sizeof(*Info);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* EOF */
|
|
|
@ -66,21 +66,6 @@ FatReadStreamFile(PVCB Vcb,
|
||||||
PBCB *Bcb,
|
PBCB *Bcb,
|
||||||
PVOID *Buffer);
|
PVOID *Buffer);
|
||||||
|
|
||||||
/* ------------------------------------------------------ blockdev.c */
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
FatPerformLboIo(
|
|
||||||
IN PFAT_IRP_CONTEXT IrpContext,
|
|
||||||
IN PLARGE_INTEGER Offset,
|
|
||||||
IN SIZE_T Length);
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
FatPerformVirtualNonCachedIo(
|
|
||||||
IN PFAT_IRP_CONTEXT IrpContext,
|
|
||||||
IN PFCB Fcb,
|
|
||||||
IN PLARGE_INTEGER Offset,
|
|
||||||
IN SIZE_T Length);
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------- dir.c */
|
/* ----------------------------------------------------------- dir.c */
|
||||||
|
|
||||||
NTSTATUS NTAPI
|
NTSTATUS NTAPI
|
||||||
|
@ -215,6 +200,10 @@ FatWriteBlocks(FF_T_UINT8 *pBuffer, FF_T_UINT32 SectorAddress, FF_T_UINT32 Count
|
||||||
FF_T_SINT32
|
FF_T_SINT32
|
||||||
FatReadBlocks(FF_T_UINT8 *pBuffer, FF_T_UINT32 SectorAddress, FF_T_UINT32 Count, void *pParam);
|
FatReadBlocks(FF_T_UINT8 *pBuffer, FF_T_UINT32 SectorAddress, FF_T_UINT32 Count, void *pParam);
|
||||||
|
|
||||||
|
VOID NTAPI
|
||||||
|
FatQueryFileTimes(OUT PLARGE_INTEGER FileTimes,
|
||||||
|
IN PDIR_ENTRY Dirent);
|
||||||
|
|
||||||
/* --------------------------------------------------------- lock.c */
|
/* --------------------------------------------------------- lock.c */
|
||||||
|
|
||||||
NTSTATUS NTAPI
|
NTSTATUS NTAPI
|
||||||
|
@ -249,34 +238,17 @@ NTAPI
|
||||||
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);
|
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);
|
||||||
|
|
||||||
/* ----------------------------------------------------------- fat.c */
|
/* ----------------------------------------------------------- fat.c */
|
||||||
PVOID
|
NTSTATUS NTAPI
|
||||||
FatPinPage(
|
|
||||||
PFAT_PAGE_CONTEXT Context,
|
|
||||||
LONGLONG ByteOffset);
|
|
||||||
|
|
||||||
PVOID
|
|
||||||
FatPinNextPage(
|
|
||||||
PFAT_PAGE_CONTEXT Context);
|
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
FatInitializeVcb(
|
FatInitializeVcb(
|
||||||
IN PFAT_IRP_CONTEXT IrpContext,
|
IN PFAT_IRP_CONTEXT IrpContext,
|
||||||
IN PVCB Vcb,
|
IN PVCB Vcb,
|
||||||
IN PDEVICE_OBJECT TargetDeviceObject,
|
IN PDEVICE_OBJECT TargetDeviceObject,
|
||||||
IN PVPB Vpb);
|
IN PVPB Vpb);
|
||||||
|
|
||||||
VOID
|
VOID NTAPI
|
||||||
FatUninitializeVcb(
|
FatUninitializeVcb(
|
||||||
IN PVCB Vcb);
|
IN PVCB Vcb);
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatScanFat(
|
|
||||||
IN PFCB Fcb,
|
|
||||||
IN LONGLONG Vbo, OUT PLONGLONG Lbo,
|
|
||||||
IN OUT PLONGLONG Length,
|
|
||||||
OUT PULONG Index,
|
|
||||||
IN BOOLEAN CanWait);
|
|
||||||
|
|
||||||
/* ------------------------------------------------------ device.c */
|
/* ------------------------------------------------------ device.c */
|
||||||
|
|
||||||
NTSTATUS NTAPI
|
NTSTATUS NTAPI
|
||||||
|
@ -291,36 +263,7 @@ FatPerformDevIoCtrl(PDEVICE_OBJECT DeviceObject,
|
||||||
ULONG OutputBufferSize,
|
ULONG OutputBufferSize,
|
||||||
BOOLEAN Override);
|
BOOLEAN Override);
|
||||||
|
|
||||||
/* ------------------------------------------------------ direntry.c */
|
|
||||||
VOID
|
|
||||||
FatFindDirent(IN OUT PFAT_FIND_DIRENT_CONTEXT Context,
|
|
||||||
OUT PDIR_ENTRY* Dirent,
|
|
||||||
OUT PUNICODE_STRING LongFileName OPTIONAL);
|
|
||||||
|
|
||||||
VOID
|
|
||||||
FatEnumerateDirents(IN OUT PFAT_ENUM_DIRENT_CONTEXT Context,
|
|
||||||
IN SIZE_T Offset);
|
|
||||||
|
|
||||||
VOID
|
|
||||||
FatQueryFileTimes(OUT PLARGE_INTEGER FileTimes,
|
|
||||||
IN PDIR_ENTRY Dirent);
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------- fcb.c */
|
/* ----------------------------------------------------------- fcb.c */
|
||||||
PFCB
|
|
||||||
FatLookupFcbByName(
|
|
||||||
IN PFCB ParentFcb,
|
|
||||||
IN PUNICODE_STRING Name);
|
|
||||||
|
|
||||||
BOOLEAN
|
|
||||||
FatLinkFcbNames(
|
|
||||||
IN PFCB ParentFcb,
|
|
||||||
IN PFCB Fcb);
|
|
||||||
|
|
||||||
VOID
|
|
||||||
FatUnlinkFcbNames(
|
|
||||||
IN PFCB ParentFcb,
|
|
||||||
IN PFCB Fcb);
|
|
||||||
|
|
||||||
PFCB NTAPI
|
PFCB NTAPI
|
||||||
FatCreateFcb(
|
FatCreateFcb(
|
||||||
IN PFAT_IRP_CONTEXT IrpContext,
|
IN PFAT_IRP_CONTEXT IrpContext,
|
||||||
|
|
|
@ -8,13 +8,11 @@
|
||||||
<library>ntoskrnl</library>
|
<library>ntoskrnl</library>
|
||||||
<library>hal</library>
|
<library>hal</library>
|
||||||
<library>pseh</library>
|
<library>pseh</library>
|
||||||
<file>blockdev.c</file>
|
|
||||||
<file>cleanup.c</file>
|
<file>cleanup.c</file>
|
||||||
<file>close.c</file>
|
<file>close.c</file>
|
||||||
<file>create.c</file>
|
<file>create.c</file>
|
||||||
<file>device.c</file>
|
<file>device.c</file>
|
||||||
<file>dir.c</file>
|
<file>dir.c</file>
|
||||||
<file>direntry.c</file>
|
|
||||||
<file>ea.c</file>
|
<file>ea.c</file>
|
||||||
<file>fastfat.c</file>
|
<file>fastfat.c</file>
|
||||||
<file>fat.c</file>
|
<file>fat.c</file>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* LICENSE: GPL - See COPYING in the top level directory
|
* LICENSE: GPL - See COPYING in the top level directory
|
||||||
* FILE: drivers/filesystems/fastfat/fat.c
|
* FILE: drivers/filesystems/fastfat/fat.c
|
||||||
* PURPOSE: FAT support routines
|
* PURPOSE: FAT support routines
|
||||||
* PROGRAMMERS: Alexey Vlasov
|
* PROGRAMMERS: Aleksey Bragin <aleksey@reactos.org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* INCLUDES *****************************************************************/
|
/* INCLUDES *****************************************************************/
|
||||||
|
@ -12,370 +12,12 @@
|
||||||
#include "fastfat.h"
|
#include "fastfat.h"
|
||||||
|
|
||||||
/* PROTOTYPES ***************************************************************/
|
/* PROTOTYPES ***************************************************************/
|
||||||
typedef struct _FAT_SCAN_CONTEXT
|
|
||||||
{
|
|
||||||
PFILE_OBJECT FileObject;
|
|
||||||
LARGE_INTEGER PageOffset;
|
|
||||||
LONGLONG BeyondLastEntryOffset;
|
|
||||||
PVOID PageBuffer;
|
|
||||||
PBCB PageBcb;
|
|
||||||
} FAT_SCAN_CONTEXT;
|
|
||||||
|
|
||||||
#define FatEntryToDataOffset(xEntry, xVcb) \
|
|
||||||
((xVcb)->DataArea + (((LONGLONG) ((xEntry) - 0x02)) << (xVcb)->BytesPerClusterLog))
|
|
||||||
|
|
||||||
#define FatDataOffsetToEntry(xOffset, xVcb) \
|
|
||||||
((ULONG) ((xOffset - (xVcb)->DataArea) >> (xVcb)->BytesPerClusterLog) + 0x02)
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatScanFat32ForContinousRun(IN OUT PFAT_PAGE_CONTEXT Context,
|
|
||||||
IN OUT PULONG Index,
|
|
||||||
IN BOOLEAN CanWait);
|
|
||||||
|
|
||||||
BOOLEAN
|
BOOLEAN
|
||||||
NTAPI
|
NTAPI
|
||||||
FatValidBpb(IN PBIOS_PARAMETER_BLOCK Bpb);
|
FatValidBpb(IN PBIOS_PARAMETER_BLOCK Bpb);
|
||||||
|
|
||||||
/* VARIABLES ****************************************************************/
|
/* VARIABLES ****************************************************************/
|
||||||
FAT_METHODS Fat12Methods = {
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
FAT_METHODS Fat16Methods = {
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
FAT_METHODS Fat32Methods = {
|
|
||||||
FatScanFat32ForContinousRun,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
/* FUNCTIONS ****************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pins the page containing ByteOffset byte.
|
|
||||||
*
|
|
||||||
* @param Context
|
|
||||||
* Keeps current BCB, Buffer pointer
|
|
||||||
* and maintains current and next page offset.
|
|
||||||
*
|
|
||||||
* @param ByteOffset
|
|
||||||
* Offset from the beginning of the data stream to be pinned.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* Pointer to the buffer starting with the specified ByteOffset.
|
|
||||||
*/
|
|
||||||
PVOID
|
|
||||||
FatPinPage(
|
|
||||||
PFAT_PAGE_CONTEXT Context,
|
|
||||||
LONGLONG ByteOffset)
|
|
||||||
{
|
|
||||||
SIZE_T OffsetWithinPage;
|
|
||||||
|
|
||||||
OffsetWithinPage = (SIZE_T) (ByteOffset & (PAGE_SIZE - 1));
|
|
||||||
ByteOffset -= OffsetWithinPage;
|
|
||||||
if (ByteOffset != Context->Offset.QuadPart)
|
|
||||||
{
|
|
||||||
Context->Offset.QuadPart = ByteOffset;
|
|
||||||
if (Context->Bcb != NULL)
|
|
||||||
{
|
|
||||||
CcUnpinData(Context->Bcb);
|
|
||||||
Context->Bcb = NULL;
|
|
||||||
}
|
|
||||||
if (!CcMapData(Context->FileObject,
|
|
||||||
&Context->Offset,
|
|
||||||
PAGE_SIZE,
|
|
||||||
Context->CanWait,
|
|
||||||
&Context->Bcb,
|
|
||||||
&Context->Buffer))
|
|
||||||
{
|
|
||||||
Context->Offset.QuadPart = 0LL;
|
|
||||||
ExRaiseStatus(STATUS_CANT_WAIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Context->EndOfPage.QuadPart =
|
|
||||||
Context->Offset.QuadPart + PAGE_SIZE;
|
|
||||||
if (Context->EndOfPage.QuadPart
|
|
||||||
> Context->EndOfData.QuadPart)
|
|
||||||
{
|
|
||||||
Context->ValidLength = (SIZE_T)
|
|
||||||
(Context->EndOfData.QuadPart
|
|
||||||
- Context->Offset.QuadPart);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Context->ValidLength = PAGE_SIZE;
|
|
||||||
}
|
|
||||||
return Add2Ptr(Context->Buffer, OffsetWithinPage, PVOID);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pins the next page of data stream.
|
|
||||||
*
|
|
||||||
* @param Context
|
|
||||||
* Keeps current BCB, Buffer pointer
|
|
||||||
* and maintains current and next page offset.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* Pointer to the buffer starting with the beginning of the next page.
|
|
||||||
*/
|
|
||||||
PVOID
|
|
||||||
FatPinNextPage(
|
|
||||||
PFAT_PAGE_CONTEXT Context)
|
|
||||||
{
|
|
||||||
ASSERT ((Context->Offset.QuadPart % PAGE_SIZE)
|
|
||||||
!= (Context->EndOfPage.QuadPart % PAGE_SIZE)
|
|
||||||
&& Context->Bcb != NULL);
|
|
||||||
|
|
||||||
ASSERT (Context->ValidLength == PAGE_SIZE);
|
|
||||||
|
|
||||||
Context->Offset = Context->EndOfPage;
|
|
||||||
CcUnpinData(Context->Bcb);
|
|
||||||
if (!CcMapData(Context->FileObject,
|
|
||||||
&Context->Offset,
|
|
||||||
PAGE_SIZE,
|
|
||||||
Context->CanWait,
|
|
||||||
&Context->Bcb,
|
|
||||||
&Context->Buffer))
|
|
||||||
{
|
|
||||||
Context->Bcb = NULL;
|
|
||||||
Context->Offset.QuadPart = 0LL;
|
|
||||||
ExRaiseStatus(STATUS_CANT_WAIT);
|
|
||||||
}
|
|
||||||
Context->EndOfPage.QuadPart =
|
|
||||||
Context->Offset.QuadPart + PAGE_SIZE;
|
|
||||||
return Context->Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the index of the set bit.
|
|
||||||
*
|
|
||||||
* @param Number
|
|
||||||
* Number having a single bit set.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* Index of the set bit.
|
|
||||||
*/
|
|
||||||
FORCEINLINE
|
|
||||||
ULONG
|
|
||||||
FatPowerOfTwo(
|
|
||||||
ULONG Number)
|
|
||||||
{
|
|
||||||
ULONG Temp;
|
|
||||||
Temp = Number
|
|
||||||
- ((Number >> 1) & 033333333333)
|
|
||||||
- ((Number >> 2) & 011111111111);
|
|
||||||
return (((Temp + (Temp >> 3)) & 030707070707) % 63);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scans FAT32 for continous chain of clusters
|
|
||||||
*
|
|
||||||
* @param Context
|
|
||||||
* Pointer to FAT_PAGE_CONTEXT.
|
|
||||||
*
|
|
||||||
* @param Index
|
|
||||||
* Supplies the Index of the first cluster
|
|
||||||
* and receves the last index after the last
|
|
||||||
* cluster in the chain.
|
|
||||||
*
|
|
||||||
* @param CanWait
|
|
||||||
* Indicates if the context allows blocking.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* Value of the last claster terminated the scan.
|
|
||||||
*
|
|
||||||
* @note
|
|
||||||
* Raises STATUS_CANT_WAIT race condition.
|
|
||||||
*/
|
|
||||||
ULONG
|
|
||||||
FatScanFat32ForContinousRun(IN OUT PFAT_PAGE_CONTEXT Context,
|
|
||||||
IN OUT PULONG Index,
|
|
||||||
IN BOOLEAN CanWait)
|
|
||||||
{
|
|
||||||
PULONG Entry, EndOfPage;
|
|
||||||
|
|
||||||
Entry = FatPinPage(Context, ((LONGLONG) *Index) << 0x2);
|
|
||||||
EndOfPage = FatPinEndOfPage(Context, PULONG);
|
|
||||||
while (TRUE)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if ((*Entry & FAT_CLUSTER_LAST) != ++(*Index))
|
|
||||||
return (*Entry & FAT_CLUSTER_LAST);
|
|
||||||
} while (++Entry < EndOfPage);
|
|
||||||
/* Check if this is the last available entry */
|
|
||||||
if (FatPinIsLastPage(Context))
|
|
||||||
break;
|
|
||||||
Entry = (PULONG) FatPinNextPage(Context);
|
|
||||||
EndOfPage = FatPinEndOfPage(Context, PULONG);
|
|
||||||
}
|
|
||||||
return (*Index - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatSetFat32ContinousRun(IN OUT PFAT_SCAN_CONTEXT Context,
|
|
||||||
IN ULONG Index,
|
|
||||||
IN ULONG Length,
|
|
||||||
IN BOOLEAN CanWait)
|
|
||||||
{
|
|
||||||
ExRaiseStatus(STATUS_NOT_IMPLEMENTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatScanFat32ForValueRun(IN OUT PFAT_SCAN_CONTEXT Context,
|
|
||||||
IN OUT PULONG Index,
|
|
||||||
IN ULONG IndexValue,
|
|
||||||
IN BOOLEAN CanWait)
|
|
||||||
{
|
|
||||||
ExRaiseStatus(STATUS_NOT_IMPLEMENTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG
|
|
||||||
FatSetFat32ValueRun(IN OUT PFAT_SCAN_CONTEXT Context,
|
|
||||||
IN ULONG Index,
|
|
||||||
IN ULONG Length,
|
|
||||||
IN ULONG IndexValue,
|
|
||||||
IN BOOLEAN CanWait)
|
|
||||||
{
|
|
||||||
ExRaiseStatus(STATUS_NOT_IMPLEMENTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queries file MCB for the specified region [Vbo, Vbo + Length],
|
|
||||||
* returns the number of runs in the region as well as the first
|
|
||||||
* run of the range itself.
|
|
||||||
* If the specified region is not fully cached in MCB the routine
|
|
||||||
* scans FAT for the file and fills the MCB until the file offset
|
|
||||||
* (defined as Vbo + Length) is reached.
|
|
||||||
*
|
|
||||||
* @param Fcb
|
|
||||||
* Pointer to FCB structure for the file.
|
|
||||||
*
|
|
||||||
* @param Vbo
|
|
||||||
* Virtual Byte Offset in the file.
|
|
||||||
*
|
|
||||||
* @param Lbo
|
|
||||||
* Receives the Value of Logical Byte offset corresponding
|
|
||||||
* to supplied Vbo Value.
|
|
||||||
*
|
|
||||||
* @param Length
|
|
||||||
* Supplies file range length to be examined and receives
|
|
||||||
* the length of first run.
|
|
||||||
*
|
|
||||||
* @param OutIndex
|
|
||||||
* Receives the index (in MCB cache) of first run.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* Incremented index of the last run (+1).
|
|
||||||
*
|
|
||||||
* @note
|
|
||||||
* Should be called by I/O routines to split the I/O operation
|
|
||||||
* into sequential or parallel I/O operations.
|
|
||||||
*/
|
|
||||||
ULONG
|
|
||||||
FatScanFat(IN PFCB Fcb,
|
|
||||||
IN LONGLONG Vbo,
|
|
||||||
OUT PLONGLONG Lbo,
|
|
||||||
IN OUT PLONGLONG Length,
|
|
||||||
OUT PULONG Index,
|
|
||||||
IN BOOLEAN CanWait)
|
|
||||||
{
|
|
||||||
LONGLONG CurrentLbo, CurrentVbo, BeyondLastVbo, CurrentLength;
|
|
||||||
ULONG Entry, NextEntry, NumberOfEntries, CurrentIndex;
|
|
||||||
FAT_PAGE_CONTEXT Context;
|
|
||||||
PVCB Vcb;
|
|
||||||
|
|
||||||
/* Some often used values */
|
|
||||||
Vcb = Fcb->Vcb;
|
|
||||||
CurrentIndex = 0;
|
|
||||||
BeyondLastVbo = Vbo + *Length;
|
|
||||||
CurrentLength = ((LONGLONG) Vcb->Clusters) << Vcb->BytesPerClusterLog;
|
|
||||||
if (BeyondLastVbo > CurrentLength)
|
|
||||||
BeyondLastVbo = CurrentLength;
|
|
||||||
/* Try to locate first run */
|
|
||||||
if (FsRtlLookupLargeMcbEntry(&Fcb->Mcb, Vbo, Lbo, Length, NULL, NULL, Index))
|
|
||||||
{
|
|
||||||
/* Check if we have a single mapped run */
|
|
||||||
if (Vbo >= BeyondLastVbo)
|
|
||||||
goto FatScanFcbFatExit;
|
|
||||||
} else {
|
|
||||||
*Length = 0L;
|
|
||||||
}
|
|
||||||
/* Get the first scan startup values */
|
|
||||||
if (FsRtlLookupLastLargeMcbEntryAndIndex(
|
|
||||||
&Fcb->Mcb, &CurrentVbo, &CurrentLbo, &CurrentIndex))
|
|
||||||
{
|
|
||||||
Entry = FatDataOffsetToEntry(CurrentLbo, Vcb);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Map is empty, set up initial values */
|
|
||||||
Entry = Fcb->FirstCluster;
|
|
||||||
if (Entry <= 0x2)
|
|
||||||
ExRaiseStatus(STATUS_FILE_CORRUPT_ERROR);
|
|
||||||
if (Entry >= Vcb->Clusters)
|
|
||||||
{
|
|
||||||
if (Entry < FAT_CLUSTER_LAST)
|
|
||||||
ExRaiseStatus(STATUS_FILE_CORRUPT_ERROR);
|
|
||||||
BeyondLastVbo = 0LL;
|
|
||||||
}
|
|
||||||
CurrentIndex = 0L;
|
|
||||||
CurrentVbo = 0LL;
|
|
||||||
}
|
|
||||||
/* Initialize Context */
|
|
||||||
RtlZeroMemory(&Context, sizeof(Context));
|
|
||||||
Context.FileObject = Vcb->StreamFileObject;
|
|
||||||
Context.EndOfData.QuadPart = Vcb->BeyondLastClusterInFat;
|
|
||||||
|
|
||||||
while (CurrentVbo < BeyondLastVbo)
|
|
||||||
{
|
|
||||||
/* Locate Continous run starting with the current entry */
|
|
||||||
NumberOfEntries = Entry;
|
|
||||||
NextEntry = Vcb->Methods.ScanContinousRun(
|
|
||||||
&Context, &NumberOfEntries, CanWait);
|
|
||||||
NumberOfEntries -= Entry;
|
|
||||||
/* Check value that terminated the for being valid for FAT */
|
|
||||||
if (NextEntry <= 0x2)
|
|
||||||
ExRaiseStatus(STATUS_FILE_CORRUPT_ERROR);
|
|
||||||
if (NextEntry >= Vcb->Clusters)
|
|
||||||
{
|
|
||||||
if (NextEntry < FAT_CLUSTER_LAST)
|
|
||||||
ExRaiseStatus(STATUS_FILE_CORRUPT_ERROR);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* Add new run */
|
|
||||||
CurrentLength = ((LONGLONG) NumberOfEntries)
|
|
||||||
<< Vcb->BytesPerClusterLog;
|
|
||||||
FsRtlAddLargeMcbEntry(&Fcb->Mcb,
|
|
||||||
CurrentVbo,
|
|
||||||
FatEntryToDataOffset(Entry, Vcb),
|
|
||||||
CurrentLength);
|
|
||||||
/* Setup next iteration */
|
|
||||||
Entry = NextEntry;
|
|
||||||
CurrentVbo += CurrentLength;
|
|
||||||
CurrentIndex ++;
|
|
||||||
}
|
|
||||||
if (*Length == 0LL && CurrentIndex > 0)
|
|
||||||
{
|
|
||||||
if (!FsRtlLookupLargeMcbEntry(&Fcb->Mcb,
|
|
||||||
Vbo, Lbo, Length, NULL, NULL, Index))
|
|
||||||
{
|
|
||||||
*Index = 0L;
|
|
||||||
*Lbo = 0LL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FatScanFcbFatExit:
|
|
||||||
return CurrentIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOLEAN
|
BOOLEAN
|
||||||
NTAPI
|
NTAPI
|
||||||
|
@ -400,7 +42,29 @@ FatValidBpb(IN PBIOS_PARAMETER_BLOCK Bpb)
|
||||||
&& (Bpb->SectorsPerFat > 0 || !Bpb->MirrorDisabled));
|
&& (Bpb->SectorsPerFat > 0 || !Bpb->MirrorDisabled));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the index of the set bit.
|
||||||
|
*
|
||||||
|
* @param Number
|
||||||
|
* Number having a single bit set.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Index of the set bit.
|
||||||
|
*/
|
||||||
|
FORCEINLINE
|
||||||
|
ULONG
|
||||||
|
FatPowerOfTwo(
|
||||||
|
ULONG Number)
|
||||||
|
{
|
||||||
|
ULONG Temp;
|
||||||
|
Temp = Number
|
||||||
|
- ((Number >> 1) & 033333333333)
|
||||||
|
- ((Number >> 2) & 011111111111);
|
||||||
|
return (((Temp + (Temp >> 3)) & 030707070707) % 63);
|
||||||
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
|
NTAPI
|
||||||
FatiInitializeVcb(PVCB Vcb)
|
FatiInitializeVcb(PVCB Vcb)
|
||||||
{
|
{
|
||||||
ULONG ClustersCapacity;
|
ULONG ClustersCapacity;
|
||||||
|
@ -420,17 +84,17 @@ FatiInitializeVcb(PVCB Vcb)
|
||||||
if (Vcb->BytesPerClusterLog < 4087)
|
if (Vcb->BytesPerClusterLog < 4087)
|
||||||
{
|
{
|
||||||
Vcb->IndexDepth = 0x0c;
|
Vcb->IndexDepth = 0x0c;
|
||||||
Vcb->Methods = Fat12Methods;
|
//Vcb->Methods = Fat12Methods;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Vcb->IndexDepth = 0x10;
|
Vcb->IndexDepth = 0x10;
|
||||||
Vcb->Methods = Fat16Methods;
|
//Vcb->Methods = Fat16Methods;
|
||||||
}
|
}
|
||||||
/* Large Sectors are used for FAT32 */
|
/* Large Sectors are used for FAT32 */
|
||||||
if (Vcb->Bpb.Sectors == 0) {
|
if (Vcb->Bpb.Sectors == 0) {
|
||||||
Vcb->IndexDepth = 0x20;
|
Vcb->IndexDepth = 0x20;
|
||||||
Vcb->Methods = Fat32Methods;
|
//Vcb->Methods = Fat32Methods;
|
||||||
}
|
}
|
||||||
ClustersCapacity = (SectorsToBytes(Vcb, Vcb->Sectors) * 0x8 / Vcb->IndexDepth) - 1;
|
ClustersCapacity = (SectorsToBytes(Vcb, Vcb->Sectors) * 0x8 / Vcb->IndexDepth) - 1;
|
||||||
if (Vcb->Clusters > ClustersCapacity)
|
if (Vcb->Clusters > ClustersCapacity)
|
||||||
|
@ -445,6 +109,7 @@ FatiInitializeVcb(PVCB Vcb)
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
|
NTAPI
|
||||||
FatInitializeVcb(IN PFAT_IRP_CONTEXT IrpContext,
|
FatInitializeVcb(IN PFAT_IRP_CONTEXT IrpContext,
|
||||||
IN PVCB Vcb,
|
IN PVCB Vcb,
|
||||||
IN PDEVICE_OBJECT TargetDeviceObject,
|
IN PDEVICE_OBJECT TargetDeviceObject,
|
||||||
|
@ -558,6 +223,7 @@ FatInitializeVcbCleanup:
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
|
NTAPI
|
||||||
FatUninitializeVcb(IN PVCB Vcb)
|
FatUninitializeVcb(IN PVCB Vcb)
|
||||||
{
|
{
|
||||||
LARGE_INTEGER ZeroSize;
|
LARGE_INTEGER ZeroSize;
|
||||||
|
|
|
@ -388,6 +388,14 @@ typedef enum _TYPE_OF_OPEN
|
||||||
EaFile
|
EaFile
|
||||||
} TYPE_OF_OPEN;
|
} TYPE_OF_OPEN;
|
||||||
|
|
||||||
|
typedef enum _FILE_TIME_INDEX
|
||||||
|
{
|
||||||
|
FileCreationTime = 0,
|
||||||
|
FileLastAccessTime,
|
||||||
|
FileLastWriteTime,
|
||||||
|
FileChangeTime
|
||||||
|
} FILE_TIME_INDEX;
|
||||||
|
|
||||||
#define CCB_SEARCH_RETURN_SINGLE_ENTRY 0x01
|
#define CCB_SEARCH_RETURN_SINGLE_ENTRY 0x01
|
||||||
#define CCB_SEARCH_PATTERN_LEGAL_8DOT3 0x02
|
#define CCB_SEARCH_PATTERN_LEGAL_8DOT3 0x02
|
||||||
#define CCB_SEARCH_PATTERN_HAS_WILD_CARD 0x04
|
#define CCB_SEARCH_PATTERN_HAS_WILD_CARD 0x04
|
||||||
|
|
|
@ -100,4 +100,114 @@ FF_FILE *FF_OpenW(FF_IOMAN *pIoman, PUNICODE_STRING pathW, FF_T_UINT8 Mode, FF_E
|
||||||
return FF_Open(pIoman, AnsiName.Buffer, Mode, pError);
|
return FF_Open(pIoman, AnsiName.Buffer, Mode, pError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORCEINLINE
|
||||||
|
VOID
|
||||||
|
FatDateTimeToSystemTime(OUT PLARGE_INTEGER SystemTime,
|
||||||
|
IN PFAT_DATETIME FatDateTime,
|
||||||
|
IN UCHAR TenMs OPTIONAL)
|
||||||
|
{
|
||||||
|
TIME_FIELDS TimeFields;
|
||||||
|
|
||||||
|
/* Setup time fields */
|
||||||
|
TimeFields.Year = FatDateTime->Date.Year + 1980;
|
||||||
|
TimeFields.Month = FatDateTime->Date.Month;
|
||||||
|
TimeFields.Day = FatDateTime->Date.Day;
|
||||||
|
TimeFields.Hour = FatDateTime->Time.Hour;
|
||||||
|
TimeFields.Minute = FatDateTime->Time.Minute;
|
||||||
|
TimeFields.Second = (FatDateTime->Time.DoubleSeconds << 1);
|
||||||
|
|
||||||
|
/* Adjust up to 10 milliseconds
|
||||||
|
* if the parameter was supplied
|
||||||
|
*/
|
||||||
|
if (ARGUMENT_PRESENT(TenMs))
|
||||||
|
{
|
||||||
|
TimeFields.Second += TenMs / 100;
|
||||||
|
TimeFields.Milliseconds = (TenMs % 100) * 10;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TimeFields.Milliseconds = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix seconds value that might get beyoud the bound */
|
||||||
|
if (TimeFields.Second > 59) TimeFields.Second = 0;
|
||||||
|
|
||||||
|
/* Perform ceonversion to system time if possible */
|
||||||
|
if (RtlTimeFieldsToTime(&TimeFields, SystemTime))
|
||||||
|
{
|
||||||
|
/* Convert to system time */
|
||||||
|
ExLocalTimeToSystemTime(SystemTime, SystemTime);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Set to default time if conversion failed */
|
||||||
|
*SystemTime = FatGlobalData.DefaultFileTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make it a helper around FullFAT library
|
||||||
|
VOID
|
||||||
|
NTAPI
|
||||||
|
FatQueryFileTimes(OUT PLARGE_INTEGER FileTimes,
|
||||||
|
IN PDIR_ENTRY Dirent)
|
||||||
|
{
|
||||||
|
/* Convert LastWriteTime */
|
||||||
|
FatDateTimeToSystemTime(&FileTimes[FileLastWriteTime],
|
||||||
|
&Dirent->LastWriteDateTime,
|
||||||
|
0);
|
||||||
|
/* All other time fileds are valid (according to MS)
|
||||||
|
* only if Win31 compatability mode is set.
|
||||||
|
*/
|
||||||
|
if (FatGlobalData.Win31FileSystem)
|
||||||
|
{
|
||||||
|
/* We can avoid calling conversion routine
|
||||||
|
* if time in dirent is 0 or equals to already
|
||||||
|
* known time (LastWriteTime).
|
||||||
|
*/
|
||||||
|
if (Dirent->CreationDateTime.Value == 0)
|
||||||
|
{
|
||||||
|
/* Set it to default time */
|
||||||
|
FileTimes[FileCreationTime] = FatGlobalData.DefaultFileTime;
|
||||||
|
}
|
||||||
|
else if (Dirent->CreationDateTime.Value
|
||||||
|
== Dirent->LastWriteDateTime.Value)
|
||||||
|
{
|
||||||
|
/* Assign the already known time */
|
||||||
|
FileTimes[FileCreationTime] = FileTimes[FileLastWriteTime];
|
||||||
|
/* Adjust milliseconds from extra dirent field */
|
||||||
|
FileTimes[FileCreationTime].QuadPart
|
||||||
|
+= (ULONG) Dirent->CreationTimeTenMs * 100000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Perform conversion */
|
||||||
|
FatDateTimeToSystemTime(&FileTimes[FileCreationTime],
|
||||||
|
&Dirent->CreationDateTime,
|
||||||
|
Dirent->CreationTimeTenMs);
|
||||||
|
}
|
||||||
|
if (Dirent->LastAccessDate.Value == 0)
|
||||||
|
{
|
||||||
|
/* Set it to default time */
|
||||||
|
FileTimes[FileLastAccessTime] = FatGlobalData.DefaultFileTime;
|
||||||
|
}
|
||||||
|
else if (Dirent->LastAccessDate.Value
|
||||||
|
== Dirent->LastWriteDateTime.Date.Value)
|
||||||
|
{
|
||||||
|
/* Assign the already known time */
|
||||||
|
FileTimes[FileLastAccessTime] = FileTimes[FileLastWriteTime];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Perform conversion */
|
||||||
|
FAT_DATETIME LastAccessDateTime;
|
||||||
|
|
||||||
|
LastAccessDateTime.Date.Value = Dirent->LastAccessDate.Value;
|
||||||
|
LastAccessDateTime.Time.Value = 0;
|
||||||
|
FatDateTimeToSystemTime(&FileTimes[FileLastAccessTime],
|
||||||
|
&LastAccessDateTime,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
* FILE: drivers/filesystems/fastfat/rw.c
|
* FILE: drivers/filesystems/fastfat/rw.c
|
||||||
* PURPOSE: Read/write support
|
* PURPOSE: Read/write support
|
||||||
* PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)
|
* PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)
|
||||||
* Alexey Vlasov
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* INCLUDES *****************************************************************/
|
/* INCLUDES *****************************************************************/
|
||||||
|
|
Loading…
Reference in a new issue