mirror of
https://github.com/reactos/reactos.git
synced 2024-08-14 15:16:27 +00:00
![Pierre Schweitzer](/assets/img/avatar_default.png)
This implies that a sample for W10. It has been backported to NT5.2; not sure how it would work on a W2K3 (feel free to test!)
3857 lines
102 KiB
C
3857 lines
102 KiB
C
/*++
|
||
|
||
Copyright (c) 1990-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
EaSup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the cluster operations on the EA file for Fat.
|
||
|
||
|
||
--*/
|
||
|
||
#include "fatprocs.h"
|
||
|
||
//
|
||
// Local debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_EA)
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, FatAddEaSet)
|
||
#pragma alloc_text(PAGE, FatAppendPackedEa)
|
||
#pragma alloc_text(PAGE, FatCreateEa)
|
||
#pragma alloc_text(PAGE, FatDeleteEa)
|
||
#pragma alloc_text(PAGE, FatDeleteEaSet)
|
||
#pragma alloc_text(PAGE, FatDeletePackedEa)
|
||
#pragma alloc_text(PAGE, FatGetEaFile)
|
||
#pragma alloc_text(PAGE, FatGetEaLength)
|
||
#pragma alloc_text(PAGE, FatGetNeedEaCount)
|
||
#pragma alloc_text(PAGE, FatIsEaNameValid)
|
||
#pragma alloc_text(PAGE, FatLocateEaByName)
|
||
#pragma alloc_text(PAGE, FatLocateNextEa)
|
||
#pragma alloc_text(PAGE, FatReadEaSet)
|
||
#pragma alloc_text(PAGE, FatPinEaRange)
|
||
#pragma alloc_text(PAGE, FatMarkEaRangeDirty)
|
||
#pragma alloc_text(PAGE, FatUnpinEaRange)
|
||
#endif
|
||
|
||
|
||
//
|
||
// Any access to the Ea file must recognize when a section boundary is being
|
||
// crossed.
|
||
//
|
||
|
||
#define EA_SECTION_SIZE (0x00040000)
|
||
|
||
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
VOID
|
||
FatGetEaLength (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN PDIRENT Dirent,
|
||
OUT PULONG EaLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks up the Ea length for the Eas of the file. This
|
||
length is the length of the packed eas, including the 4 bytes which
|
||
contain the Ea length.
|
||
|
||
This routine pins down the Ea set for the desired file and copies
|
||
this field from the Ea set header.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Vcb for the volume containing the Eas.
|
||
|
||
Dirent - Supplies a pointer to the dirent for the file in question.
|
||
|
||
EaLength - Supplies the address to store the length of the Eas.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PBCB EaBcb = NULL;
|
||
BOOLEAN LockedEaFcb = FALSE;
|
||
EA_RANGE EaSetRange;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FatGetEaLength ...\n", 0);
|
||
|
||
//
|
||
// If this is Fat32 volume, or if the handle is 0 then the Ea length is 0.
|
||
//
|
||
|
||
if (FatIsFat32( Vcb ) ||
|
||
Dirent->ExtendedAttributes == 0) {
|
||
|
||
*EaLength = 0;
|
||
DebugTrace(-1, Dbg, "FatGetEaLength -> %08lx\n", TRUE);
|
||
return;
|
||
}
|
||
|
||
RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
|
||
|
||
//
|
||
// Use a try to facilitate cleanup.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
PDIRENT EaDirent;
|
||
OEM_STRING ThisFilename;
|
||
UCHAR Buffer[12];
|
||
PEA_SET_HEADER EaSetHeader;
|
||
|
||
//
|
||
// Initial the local values.
|
||
//
|
||
|
||
EaBcb = NULL;
|
||
LockedEaFcb = FALSE;
|
||
|
||
//
|
||
// Try to get the Ea file object. Return FALSE on failure.
|
||
//
|
||
|
||
FatGetEaFile( IrpContext,
|
||
Vcb,
|
||
&EaDirent,
|
||
&EaBcb,
|
||
FALSE,
|
||
FALSE );
|
||
|
||
LockedEaFcb = TRUE;
|
||
|
||
//
|
||
// If we didn't get the file because it doesn't exist, then the
|
||
// disk is corrupted.
|
||
//
|
||
|
||
if (Vcb->VirtualEaFile == NULL) {
|
||
|
||
DebugTrace(0, Dbg, "FatGetEaLength: Ea file doesn't exist\n", 0);
|
||
FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE );
|
||
}
|
||
|
||
//
|
||
// Try to pin down the Ea set header for the index in the
|
||
// dirent. If the operation doesn't complete, return FALSE
|
||
// from this routine.
|
||
//
|
||
|
||
ThisFilename.Buffer = (PCHAR)Buffer;
|
||
Fat8dot3ToString( IrpContext, Dirent, FALSE, &ThisFilename );
|
||
|
||
FatReadEaSet( IrpContext,
|
||
Vcb,
|
||
Dirent->ExtendedAttributes,
|
||
&ThisFilename,
|
||
FALSE,
|
||
&EaSetRange );
|
||
|
||
EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
|
||
|
||
//
|
||
// We now have the Ea set header for this file. We simply copy
|
||
// the Ea length field.
|
||
//
|
||
|
||
CopyUchar4( EaLength, EaSetHeader->cbList );
|
||
DebugTrace(0, Dbg, "FatGetEaLength: Length of Ea is -> %08lx\n",
|
||
*EaLength);
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatGetEaLength );
|
||
|
||
//
|
||
// Unpin the EaDirent and the EaSetHeader if pinned.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, EaBcb );
|
||
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
|
||
//
|
||
// Release the Fcb for the Ea file if locked.
|
||
//
|
||
|
||
if (LockedEaFcb) {
|
||
|
||
FatReleaseFcb( IrpContext, Vcb->EaFcb );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatGetEaLength: Ea length -> %08lx\n", *EaLength);
|
||
} _SEH2_END;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
VOID
|
||
FatGetNeedEaCount (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN PDIRENT Dirent,
|
||
OUT PULONG NeedEaCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks up the Need Ea count for the file. The value is the
|
||
in the ea header for the file.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Vcb for the volume containing the Eas.
|
||
|
||
Dirent - Supplies a pointer to the dirent for the file in question.
|
||
|
||
NeedEaCount - Supplies the address to store the Need Ea count.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PBCB EaBcb = NULL;
|
||
BOOLEAN LockedEaFcb = FALSE;
|
||
EA_RANGE EaSetRange;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FatGetNeedEaCount ...\n", 0);
|
||
|
||
//
|
||
// If the handle is 0 then the Need Ea count is 0.
|
||
//
|
||
|
||
if (Dirent->ExtendedAttributes == 0) {
|
||
|
||
*NeedEaCount = 0;
|
||
DebugTrace(-1, Dbg, "FatGetNeedEaCount -> %08lx\n", TRUE);
|
||
return;
|
||
}
|
||
|
||
RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
|
||
|
||
//
|
||
// Use a try to facilitate cleanup.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
PDIRENT EaDirent;
|
||
OEM_STRING ThisFilename;
|
||
UCHAR Buffer[12];
|
||
PEA_SET_HEADER EaSetHeader;
|
||
|
||
//
|
||
// Initial the local values.
|
||
//
|
||
|
||
EaBcb = NULL;
|
||
LockedEaFcb = FALSE;
|
||
|
||
//
|
||
// Try to get the Ea file object. Return FALSE on failure.
|
||
//
|
||
|
||
FatGetEaFile( IrpContext,
|
||
Vcb,
|
||
&EaDirent,
|
||
&EaBcb,
|
||
FALSE,
|
||
FALSE );
|
||
|
||
LockedEaFcb = TRUE;
|
||
|
||
//
|
||
// If we didn't get the file because it doesn't exist, then the
|
||
// disk is corrupted.
|
||
//
|
||
|
||
if (Vcb->VirtualEaFile == NULL) {
|
||
|
||
DebugTrace(0, Dbg, "FatGetNeedEaCount: Ea file doesn't exist\n", 0);
|
||
FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE );
|
||
}
|
||
|
||
//
|
||
// Try to pin down the Ea set header for the index in the
|
||
// dirent. If the operation doesn't complete, return FALSE
|
||
// from this routine.
|
||
//
|
||
|
||
ThisFilename.Buffer = (PCHAR)Buffer;
|
||
Fat8dot3ToString( IrpContext, Dirent, FALSE, &ThisFilename );
|
||
|
||
FatReadEaSet( IrpContext,
|
||
Vcb,
|
||
Dirent->ExtendedAttributes,
|
||
&ThisFilename,
|
||
FALSE,
|
||
&EaSetRange );
|
||
|
||
EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
|
||
|
||
//
|
||
// We now have the Ea set header for this file. We simply copy
|
||
// the Need Ea field.
|
||
//
|
||
|
||
*NeedEaCount = EaSetHeader->NeedEaCount;
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatGetNeedEaCount );
|
||
|
||
//
|
||
// Unpin the EaDirent and the EaSetHeader if pinned.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, EaBcb );
|
||
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
|
||
//
|
||
// Release the Fcb for the Ea file if locked.
|
||
//
|
||
|
||
if (LockedEaFcb) {
|
||
|
||
FatReleaseFcb( IrpContext, Vcb->EaFcb );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatGetNeedEaCount: NeedEaCount -> %08lx\n", *NeedEaCount);
|
||
} _SEH2_END;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
VOID
|
||
FatCreateEa (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN PUCHAR Buffer,
|
||
IN ULONG Length,
|
||
IN POEM_STRING FileName,
|
||
OUT PUSHORT EaHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine adds an entire ea set to the Ea file. The owning file
|
||
is specified in 'FileName'. This is used to replace the Ea set attached
|
||
to an existing file during a supersede operation.
|
||
|
||
NOTE: This routine may block, it should not be called unless the
|
||
thread is waitable.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the Vcb for the volume.
|
||
|
||
Buffer - Buffer with the Ea list to add.
|
||
|
||
Length - Length of the buffer.
|
||
|
||
FileName - The Ea's will be attached to this file.
|
||
|
||
EaHandle - The new ea handle will be assigned to this address.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PBCB EaBcb;
|
||
BOOLEAN LockedEaFcb;
|
||
|
||
PEA_SET_HEADER EaSetHeader = NULL;
|
||
EA_RANGE EaSetRange;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FatCreateEa...\n", 0);
|
||
|
||
EaBcb = NULL;
|
||
LockedEaFcb = FALSE;
|
||
|
||
RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
|
||
|
||
//
|
||
// Use 'try' to facilitate cleanup.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
PDIRENT EaDirent;
|
||
|
||
ULONG PackedEasLength;
|
||
ULONG AllocationLength;
|
||
ULONG BytesPerCluster;
|
||
|
||
PFILE_FULL_EA_INFORMATION FullEa;
|
||
|
||
//
|
||
// We will allocate a buffer and copy the Ea list from the user's
|
||
// buffer to a FAT packed Ea list. Initial allocation is one
|
||
// cluster, our starting offset into the packed Ea list is 0.
|
||
//
|
||
|
||
PackedEasLength = 0;
|
||
|
||
BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
AllocationLength = (PackedEasLength
|
||
+ SIZE_OF_EA_SET_HEADER
|
||
+ BytesPerCluster - 1)
|
||
& ~(BytesPerCluster - 1);
|
||
|
||
//
|
||
// Allocate the memory and store the file name into it.
|
||
//
|
||
|
||
EaSetHeader = FsRtlAllocatePoolWithTag( PagedPool,
|
||
AllocationLength,
|
||
TAG_EA_SET_HEADER );
|
||
|
||
RtlZeroMemory( EaSetHeader, AllocationLength );
|
||
|
||
RtlCopyMemory( EaSetHeader->OwnerFileName,
|
||
FileName->Buffer,
|
||
FileName->Length );
|
||
|
||
AllocationLength -= SIZE_OF_EA_SET_HEADER;
|
||
|
||
//
|
||
// Loop through the user's Ea list. Catch any error for invalid
|
||
// name or non-existent Ea value.
|
||
//
|
||
|
||
for ( FullEa = (PFILE_FULL_EA_INFORMATION) Buffer;
|
||
FullEa < (PFILE_FULL_EA_INFORMATION) &Buffer[Length];
|
||
FullEa = (PFILE_FULL_EA_INFORMATION) (FullEa->NextEntryOffset == 0 ?
|
||
&Buffer[Length] :
|
||
(PUCHAR) FullEa + FullEa->NextEntryOffset)) {
|
||
|
||
OEM_STRING EaName;
|
||
ULONG EaOffset;
|
||
|
||
EaName.Length = FullEa->EaNameLength;
|
||
EaName.Buffer = &FullEa->EaName[0];
|
||
|
||
//
|
||
// Make sure the ea name is valid
|
||
//
|
||
|
||
if (!FatIsEaNameValid( IrpContext, EaName )) {
|
||
|
||
DebugTrace(0, Dbg,
|
||
"FatCreateEa: Invalid Ea Name -> %Z\n",
|
||
EaName);
|
||
|
||
IrpContext->OriginatingIrp->IoStatus.Information = (PUCHAR)FullEa - Buffer;
|
||
IrpContext->OriginatingIrp->IoStatus.Status = STATUS_INVALID_EA_NAME;
|
||
FatRaiseStatus( IrpContext, STATUS_INVALID_EA_NAME );
|
||
}
|
||
|
||
//
|
||
// Check that no invalid ea flags are set.
|
||
//
|
||
|
||
//
|
||
// TEMPCODE We are returning STATUS_INVALID_EA_NAME
|
||
// until a more appropriate error code exists.
|
||
//
|
||
|
||
if (FullEa->Flags != 0
|
||
&& FullEa->Flags != FILE_NEED_EA) {
|
||
|
||
IrpContext->OriginatingIrp->IoStatus.Information = (PUCHAR)FullEa - Buffer;
|
||
IrpContext->OriginatingIrp->IoStatus.Status = STATUS_INVALID_EA_NAME;
|
||
FatRaiseStatus( IrpContext, STATUS_INVALID_EA_NAME );
|
||
}
|
||
|
||
//
|
||
// If this is a duplicate name then delete the current ea
|
||
// value.
|
||
//
|
||
|
||
if (FatLocateEaByName( IrpContext,
|
||
(PPACKED_EA) EaSetHeader->PackedEas,
|
||
PackedEasLength,
|
||
&EaName,
|
||
&EaOffset )) {
|
||
|
||
DebugTrace(0, Dbg, "FatCreateEa: Duplicate name found\n", 0);
|
||
|
||
FatDeletePackedEa( IrpContext,
|
||
EaSetHeader,
|
||
&PackedEasLength,
|
||
EaOffset );
|
||
}
|
||
|
||
//
|
||
// We ignore this value if the eavalue length is zero.
|
||
//
|
||
|
||
if (FullEa->EaValueLength == 0) {
|
||
|
||
DebugTrace(0, Dbg,
|
||
"FatCreateEa: Empty ea\n",
|
||
0);
|
||
|
||
continue;
|
||
}
|
||
|
||
FatAppendPackedEa( IrpContext,
|
||
&EaSetHeader,
|
||
&PackedEasLength,
|
||
&AllocationLength,
|
||
FullEa,
|
||
BytesPerCluster );
|
||
}
|
||
|
||
//
|
||
// If the resulting length isn't zero, then allocate a FAT cluster
|
||
// to store the data.
|
||
//
|
||
|
||
if (PackedEasLength != 0) {
|
||
|
||
PEA_SET_HEADER NewEaSetHeader;
|
||
|
||
//
|
||
// If the packed eas length (plus 4 bytes) is greater
|
||
// than the maximum allowed ea size, we return an error.
|
||
//
|
||
|
||
if (PackedEasLength + 4 > MAXIMUM_EA_SIZE) {
|
||
|
||
DebugTrace( 0, Dbg, "Ea length is greater than maximum\n", 0 );
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_EA_TOO_LARGE );
|
||
}
|
||
|
||
//
|
||
// Get the Ea file.
|
||
//
|
||
|
||
FatGetEaFile( IrpContext,
|
||
Vcb,
|
||
&EaDirent,
|
||
&EaBcb,
|
||
TRUE,
|
||
TRUE );
|
||
|
||
LockedEaFcb = TRUE;
|
||
|
||
FatAddEaSet( IrpContext,
|
||
Vcb,
|
||
PackedEasLength + SIZE_OF_EA_SET_HEADER,
|
||
EaBcb,
|
||
EaDirent,
|
||
EaHandle,
|
||
&EaSetRange );
|
||
|
||
NewEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
|
||
|
||
//
|
||
// Store the length of the new Ea's into the NewEaSetHeader.
|
||
// This is the PackedEasLength + 4.
|
||
//
|
||
|
||
PackedEasLength += 4;
|
||
|
||
CopyU4char( EaSetHeader->cbList, &PackedEasLength );
|
||
|
||
//
|
||
// Copy all but the first four bytes of EaSetHeader into
|
||
// the new ea. The signature and index fields have
|
||
// already been filled in.
|
||
//
|
||
|
||
RtlCopyMemory( &NewEaSetHeader->NeedEaCount,
|
||
&EaSetHeader->NeedEaCount,
|
||
PackedEasLength + SIZE_OF_EA_SET_HEADER - 8 );
|
||
|
||
FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange );
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
|
||
CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
|
||
|
||
//
|
||
// There was no data added to the Ea file. Return a handle
|
||
// of 0.
|
||
//
|
||
|
||
} else {
|
||
|
||
*EaHandle = 0;
|
||
}
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatCreateEa );
|
||
|
||
//
|
||
// Deallocate the EaSetHeader if present.
|
||
//
|
||
|
||
if (EaSetHeader) {
|
||
|
||
ExFreePool( EaSetHeader );
|
||
}
|
||
|
||
//
|
||
// Release the EaFcb if held.
|
||
//
|
||
|
||
if (LockedEaFcb) {
|
||
|
||
FatReleaseFcb( IrpContext, Vcb->EaFcb );
|
||
}
|
||
|
||
//
|
||
// Unpin the dirents for the EaFcb and EaSetFcb if necessary.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, EaBcb );
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
|
||
DebugTrace(-1, Dbg, "FatCreateEa -> Exit\n", 0);
|
||
} _SEH2_END;
|
||
|
||
return;
|
||
}
|
||
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
VOID
|
||
FatDeleteEa (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN USHORT EaHandle,
|
||
IN POEM_STRING FileName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to remove an entire ea set. Most of the work
|
||
is done in the call to 'FatDeleteEaSet'. This routine opens the
|
||
Ea file and then calls the support routine.
|
||
|
||
NOTE: This routine may block, it should not be called unless the
|
||
thread is waitable.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Vcb for the volume
|
||
|
||
EaHandle - The handle for the Ea's to remove. This handle will be
|
||
verified during this operation.
|
||
|
||
FileName - The name of the file whose Ea's are being removed. This
|
||
name is compared against the Ea owner's name in the Ea set.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PBCB EaBcb;
|
||
BOOLEAN LockedEaFcb;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FatDeleteEa...\n", 0);
|
||
|
||
//
|
||
// Initialize local values.
|
||
//
|
||
|
||
EaBcb = NULL;
|
||
LockedEaFcb = FALSE;
|
||
|
||
//
|
||
// Use a try statement to facilitate cleanup.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
PDIRENT EaDirent;
|
||
|
||
//
|
||
// Get the Ea stream file. If the file doesn't exist on the disk
|
||
// then the disk has been corrupted.
|
||
//
|
||
|
||
FatGetEaFile( IrpContext,
|
||
Vcb,
|
||
&EaDirent,
|
||
&EaBcb,
|
||
FALSE,
|
||
TRUE );
|
||
|
||
LockedEaFcb = TRUE;
|
||
|
||
//
|
||
// If we didn't get the Ea file, then the disk is corrupt.
|
||
//
|
||
|
||
if ( EaBcb == NULL ) {
|
||
|
||
|
||
DebugTrace(0, Dbg,
|
||
"FatDeleteEa: No Ea file exists\n",
|
||
0);
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE );
|
||
}
|
||
|
||
//
|
||
// We now have everything we need to delete the ea set. Call the
|
||
// support routine to do this.
|
||
//
|
||
|
||
FatDeleteEaSet( IrpContext,
|
||
Vcb,
|
||
EaBcb,
|
||
EaDirent,
|
||
EaHandle,
|
||
FileName );
|
||
|
||
CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatDeleteEa );
|
||
|
||
//
|
||
// Release the EaFcb if held.
|
||
//
|
||
|
||
if (LockedEaFcb) {
|
||
|
||
FatReleaseFcb( IrpContext, Vcb->EaFcb );
|
||
}
|
||
|
||
//
|
||
// Unpin the dirent for the Ea file if pinned.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, EaBcb );
|
||
|
||
DebugTrace(-1, Dbg, "FatDeleteEa -> Exit\n", 0);
|
||
} _SEH2_END;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
VOID
|
||
FatGetEaFile (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OUT PVCB Vcb,
|
||
OUT PDIRENT *EaDirent,
|
||
OUT PBCB *EaBcb,
|
||
IN BOOLEAN CreateFile,
|
||
IN BOOLEAN ExclusiveFcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to completely initialize the Vcb and
|
||
the Ea file for the Vcb.
|
||
|
||
If the Vcb doesn't have the Ea file object, then we first try to
|
||
lookup the Ea data file in the root directory and if that fails
|
||
we try to create the file. The 'CreateFile' flag is used to check
|
||
whether it is necessary to create the Ea file.
|
||
|
||
This routine will lock down the Fcb for exclusive or shared access before
|
||
performing any operations. If the operation does not complete due
|
||
to blocking, exclusive or shared access will be given up before returning.
|
||
|
||
If we are creating the Ea file and marking sections of it dirty,
|
||
we can't use the repin feature through the cache map. In that case
|
||
we use a local IrpContext and then unpin all of the Bcb's before
|
||
continuing.
|
||
|
||
Note: If this routine will be creating the Ea file, we are guaranteed
|
||
to be waitable.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Vcb for the volume
|
||
|
||
EaDirent - Location to store the address of the pinned dirent for the
|
||
Ea file.
|
||
|
||
EaBcb - Location to store the address of the Bcb for the pinned dirent.
|
||
|
||
CreateFile - Boolean indicating whether we should create the Ea file
|
||
on the disk.
|
||
|
||
ExclusiveFcb - Indicates whether shared or exclusive access is desired
|
||
for the EaFcb.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFILE_OBJECT EaStreamFile = NULL;
|
||
EA_RANGE EaFileRange;
|
||
|
||
BOOLEAN UnwindLockedEaFcb = FALSE;
|
||
BOOLEAN UnwindLockedRootDcb = FALSE;
|
||
BOOLEAN UnwindAllocatedDiskSpace = FALSE;
|
||
BOOLEAN UnwindEaDirentCreated = FALSE;
|
||
BOOLEAN UnwindUpdatedSizes = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FatGetEaFile ...\n", 0);
|
||
|
||
RtlZeroMemory( &EaFileRange, sizeof( EA_RANGE ));
|
||
|
||
//
|
||
// Use a try to facilitate cleanup
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
OEM_STRING EaFileName;
|
||
LARGE_INTEGER SectionSize;
|
||
|
||
//
|
||
// Check if the Vcb already has the file object. If it doesn't, then
|
||
// we need to search the root directory for the Ea data file.
|
||
//
|
||
|
||
if (Vcb->VirtualEaFile == NULL) {
|
||
|
||
//
|
||
// Always lock the Ea file exclusively if we have to create the file.
|
||
//
|
||
|
||
if ( !FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) {
|
||
|
||
DebugTrace(0, Dbg, "FatGetEaFile: Can't grab exclusive\n", 0);
|
||
FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
|
||
}
|
||
|
||
UnwindLockedEaFcb = TRUE;
|
||
|
||
//
|
||
// Otherwise we acquire the Fcb as the caller requested.
|
||
//
|
||
|
||
} else {
|
||
|
||
if ((ExclusiveFcb && !FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb ))
|
||
|| (!ExclusiveFcb && !FatAcquireSharedFcb( IrpContext, Vcb->EaFcb))) {
|
||
|
||
DebugTrace(0, Dbg, "FatGetEaFile: Can't grab EaFcb\n", 0);
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
|
||
}
|
||
|
||
UnwindLockedEaFcb = TRUE;
|
||
|
||
//
|
||
// If the file now does not exist we need to release the Fcb and
|
||
// reacquire exclusive if we acquired shared.
|
||
//
|
||
|
||
if ((Vcb->VirtualEaFile == NULL) && !ExclusiveFcb) {
|
||
|
||
FatReleaseFcb( IrpContext, Vcb->EaFcb );
|
||
UnwindLockedEaFcb = FALSE;
|
||
|
||
if (!FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) {
|
||
|
||
DebugTrace(0, Dbg, "FatGetEaFile: Can't grab EaFcb\n", 0);
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
|
||
}
|
||
|
||
UnwindLockedEaFcb = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the file object is now there we only need to get the
|
||
// dirent for the Ea file.
|
||
//
|
||
|
||
if (Vcb->VirtualEaFile != NULL) {
|
||
|
||
FatVerifyFcb( IrpContext, Vcb->EaFcb );
|
||
|
||
FatGetDirentFromFcbOrDcb( IrpContext,
|
||
Vcb->EaFcb,
|
||
FALSE,
|
||
EaDirent,
|
||
EaBcb );
|
||
|
||
try_return( NOTHING );
|
||
|
||
} else {
|
||
|
||
VBO ByteOffset = 0;
|
||
|
||
//
|
||
// Always mark the ea fcb as good.
|
||
//
|
||
|
||
Vcb->EaFcb->FcbCondition = FcbGood;
|
||
|
||
//
|
||
// We try to lookup the dirent for the Ea Fcb.
|
||
//
|
||
|
||
EaFileName.Buffer = "EA DATA. SF";
|
||
EaFileName.Length = 11;
|
||
EaFileName.MaximumLength = 12;
|
||
|
||
//
|
||
// Now pick up the root directory to be synchronized with
|
||
// deletion/creation of entries. If we may create the file,
|
||
// get it exclusive right now.
|
||
//
|
||
// Again, note how we are relying on bottom-up lockorder. We
|
||
// already got the EaFcb.
|
||
//
|
||
|
||
if (CreateFile) {
|
||
ExAcquireResourceExclusiveLite( Vcb->RootDcb->Header.Resource, TRUE );
|
||
} else {
|
||
ExAcquireResourceSharedLite( Vcb->RootDcb->Header.Resource, TRUE );
|
||
}
|
||
UnwindLockedRootDcb = TRUE;
|
||
|
||
FatLocateSimpleOemDirent( IrpContext,
|
||
Vcb->EaFcb->ParentDcb,
|
||
&EaFileName,
|
||
EaDirent,
|
||
EaBcb,
|
||
&ByteOffset );
|
||
|
||
//
|
||
// If the file exists, we need to create the virtual file
|
||
// object for it.
|
||
//
|
||
|
||
if (*EaDirent != NULL) {
|
||
|
||
//
|
||
// Since we may be modifying the dirent, pin the data now.
|
||
//
|
||
|
||
FatPinMappedData( IrpContext,
|
||
Vcb->EaFcb->ParentDcb,
|
||
ByteOffset,
|
||
sizeof(DIRENT),
|
||
EaBcb );
|
||
|
||
//
|
||
// Update the Fcb with information on the file size
|
||
// and disk location. Also increment the open/unclean
|
||
// counts in the EaFcb and the open count in the
|
||
// Vcb.
|
||
//
|
||
|
||
Vcb->EaFcb->FirstClusterOfFile = (*EaDirent)->FirstClusterOfFile;
|
||
Vcb->EaFcb->DirentOffsetWithinDirectory = ByteOffset;
|
||
|
||
//
|
||
// Find the allocation size. The purpose here is
|
||
// really to completely fill in the Mcb for the
|
||
// file.
|
||
//
|
||
|
||
Vcb->EaFcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT;
|
||
|
||
FatLookupFileAllocationSize( IrpContext, Vcb->EaFcb );
|
||
|
||
//
|
||
// Start by computing the section size for the cache
|
||
// manager.
|
||
//
|
||
|
||
SectionSize.QuadPart = (*EaDirent)->FileSize;
|
||
Vcb->EaFcb->Header.AllocationSize = SectionSize;
|
||
Vcb->EaFcb->Header.FileSize = SectionSize;
|
||
|
||
//
|
||
// Create and initialize the file object for the
|
||
// Ea virtual file.
|
||
//
|
||
|
||
EaStreamFile = FatOpenEaFile( IrpContext, Vcb->EaFcb );
|
||
|
||
Vcb->VirtualEaFile = EaStreamFile;
|
||
|
||
//
|
||
// Else there was no dirent. If we were instructed to
|
||
// create the file object, we will try to create the dirent,
|
||
// allocate disk space, initialize the Ea file header and
|
||
// return this information to the user.
|
||
//
|
||
|
||
} else if (CreateFile) {
|
||
|
||
ULONG BytesPerCluster;
|
||
ULONG OffsetTableSize;
|
||
ULONG AllocationSize;
|
||
PEA_FILE_HEADER FileHeader;
|
||
USHORT AllocatedClusters;
|
||
PUSHORT CurrentIndex;
|
||
ULONG Index;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
DebugTrace(0, Dbg, "FatGetEaFile: Creating local IrpContext\n", 0);
|
||
|
||
BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
AllocationSize = (((ULONG) sizeof( EA_FILE_HEADER ) << 1) + BytesPerCluster - 1)
|
||
& ~(BytesPerCluster - 1);
|
||
|
||
AllocatedClusters = (USHORT) (AllocationSize
|
||
>> Vcb->AllocationSupport.LogOfBytesPerCluster);
|
||
|
||
OffsetTableSize = AllocationSize - sizeof( EA_FILE_HEADER );
|
||
|
||
//
|
||
// Allocate disk space, the space allocated is 1024 bytes
|
||
// rounded up to the nearest cluster size.
|
||
//
|
||
|
||
FatAllocateDiskSpace( IrpContext,
|
||
Vcb,
|
||
0,
|
||
&AllocationSize,
|
||
FALSE,
|
||
&Vcb->EaFcb->Mcb );
|
||
|
||
UnwindAllocatedDiskSpace = TRUE;
|
||
|
||
//
|
||
// Allocate and initialize a dirent in the root directory
|
||
// to describe this new file.
|
||
//
|
||
|
||
Vcb->EaFcb->DirentOffsetWithinDirectory =
|
||
FatCreateNewDirent( IrpContext,
|
||
Vcb->EaFcb->ParentDcb,
|
||
1,
|
||
FALSE );
|
||
|
||
FatPrepareWriteDirectoryFile( IrpContext,
|
||
Vcb->EaFcb->ParentDcb,
|
||
Vcb->EaFcb->DirentOffsetWithinDirectory,
|
||
sizeof(DIRENT),
|
||
EaBcb,
|
||
#ifndef __REACTOS__
|
||
EaDirent,
|
||
#else
|
||
(PVOID *)EaDirent,
|
||
#endif
|
||
FALSE,
|
||
TRUE,
|
||
&Status );
|
||
|
||
NT_ASSERT( NT_SUCCESS( Status ));
|
||
|
||
UnwindEaDirentCreated = TRUE;
|
||
|
||
FatConstructDirent( IrpContext,
|
||
*EaDirent,
|
||
&EaFileName,
|
||
FALSE,
|
||
FALSE,
|
||
NULL,
|
||
FAT_DIRENT_ATTR_READ_ONLY
|
||
| FAT_DIRENT_ATTR_HIDDEN
|
||
| FAT_DIRENT_ATTR_SYSTEM
|
||
| FAT_DIRENT_ATTR_ARCHIVE,
|
||
TRUE,
|
||
NULL );
|
||
|
||
(*EaDirent)->FileSize = AllocationSize;
|
||
|
||
//
|
||
// Initialize the Fcb for this file and initialize the
|
||
// cache map as well.
|
||
//
|
||
|
||
//
|
||
// Start by computing the section size for the cache
|
||
// manager.
|
||
//
|
||
|
||
SectionSize.QuadPart = (*EaDirent)->FileSize;
|
||
Vcb->EaFcb->Header.AllocationSize = SectionSize;
|
||
Vcb->EaFcb->Header.FileSize = SectionSize;
|
||
UnwindUpdatedSizes = TRUE;
|
||
|
||
//
|
||
// Create and initialize the file object for the
|
||
// Ea virtual file.
|
||
//
|
||
|
||
EaStreamFile = FatOpenEaFile( IrpContext, Vcb->EaFcb );
|
||
|
||
//
|
||
// Update the Fcb with information on the file size
|
||
// and disk location. Also increment the open/unclean
|
||
// counts in the EaFcb and the open count in the
|
||
// Vcb.
|
||
//
|
||
|
||
{
|
||
LBO FirstLboOfFile;
|
||
|
||
FatLookupMcbEntry( Vcb, &Vcb->EaFcb->Mcb,
|
||
0,
|
||
&FirstLboOfFile,
|
||
NULL,
|
||
NULL );
|
||
|
||
//
|
||
// The discerning reader will note that this doesn't take
|
||
// FAT32 into account, which is of course intentional.
|
||
//
|
||
|
||
(*EaDirent)->FirstClusterOfFile =
|
||
(USHORT) FatGetIndexFromLbo( Vcb, FirstLboOfFile );
|
||
}
|
||
|
||
Vcb->EaFcb->FirstClusterOfFile = (*EaDirent)->FirstClusterOfFile;
|
||
|
||
//
|
||
// Initialize the Ea file header and mark the Bcb as dirty.
|
||
//
|
||
|
||
FatPinEaRange( IrpContext,
|
||
EaStreamFile,
|
||
Vcb->EaFcb,
|
||
&EaFileRange,
|
||
0,
|
||
AllocationSize,
|
||
STATUS_DATA_ERROR );
|
||
|
||
FileHeader = (PEA_FILE_HEADER) EaFileRange.Data;
|
||
|
||
RtlZeroMemory( FileHeader, AllocationSize );
|
||
FileHeader->Signature = EA_FILE_SIGNATURE;
|
||
|
||
for (Index = MAX_EA_BASE_INDEX, CurrentIndex = FileHeader->EaBaseTable;
|
||
Index;
|
||
Index--, CurrentIndex++) {
|
||
|
||
*CurrentIndex = AllocatedClusters;
|
||
}
|
||
|
||
//
|
||
// Initialize the offset table with the offset set to
|
||
// after the just allocated clusters.
|
||
//
|
||
|
||
for (Index = OffsetTableSize >> 1,
|
||
CurrentIndex = (PUSHORT) ((PUCHAR) FileHeader + sizeof( EA_FILE_HEADER ));
|
||
Index;
|
||
Index--, CurrentIndex++) {
|
||
|
||
*CurrentIndex = UNUSED_EA_HANDLE;
|
||
}
|
||
|
||
//
|
||
// Unpin the file header and offset table.
|
||
//
|
||
|
||
FatMarkEaRangeDirty( IrpContext, EaStreamFile, &EaFileRange );
|
||
FatUnpinEaRange( IrpContext, &EaFileRange );
|
||
|
||
CcFlushCache( EaStreamFile->SectionObjectPointer, NULL, 0, NULL );
|
||
|
||
//
|
||
// Return the Ea file object to the user.
|
||
//
|
||
|
||
Vcb->VirtualEaFile = EaStreamFile;
|
||
}
|
||
}
|
||
try_exit: NOTHING;
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatGetEaFile );
|
||
|
||
//
|
||
// If this is abnormal termination and disk space has been
|
||
// allocated. We deallocate it now.
|
||
//
|
||
|
||
if (_SEH2_AbnormalTermination()) {
|
||
|
||
//
|
||
// Deallocate the Ea file
|
||
//
|
||
|
||
if (UnwindAllocatedDiskSpace) {
|
||
|
||
FatDeallocateDiskSpace( IrpContext,
|
||
Vcb,
|
||
&Vcb->EaFcb->Mcb,
|
||
FALSE );
|
||
}
|
||
|
||
//
|
||
// Delete the dirent for the Ea file, if created.
|
||
//
|
||
|
||
if (UnwindEaDirentCreated) {
|
||
|
||
if (UnwindUpdatedSizes) {
|
||
|
||
Vcb->EaFcb->Header.AllocationSize.QuadPart = 0;
|
||
Vcb->EaFcb->Header.FileSize.QuadPart = 0;
|
||
}
|
||
|
||
FatUnpinBcb( IrpContext, *EaBcb );
|
||
FatDeleteDirent( IrpContext, Vcb->EaFcb, NULL, TRUE );
|
||
}
|
||
|
||
//
|
||
// Release the EA Fcb if held
|
||
//
|
||
|
||
if (UnwindLockedEaFcb) {
|
||
|
||
FatReleaseFcb( IrpContext, Vcb->EaFcb );
|
||
}
|
||
|
||
//
|
||
// Dereference the Ea stream file if created.
|
||
//
|
||
|
||
if (EaStreamFile != NULL) {
|
||
|
||
ObDereferenceObject( EaStreamFile );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Always release the root Dcb (our caller releases the EA Fcb if we
|
||
// do not raise).
|
||
//
|
||
|
||
if (UnwindLockedRootDcb) {
|
||
|
||
FatReleaseFcb( IrpContext, Vcb->RootDcb );
|
||
}
|
||
|
||
//
|
||
// If the Ea file header is locked down. We unpin it now.
|
||
//
|
||
|
||
FatUnpinEaRange( IrpContext, &EaFileRange );
|
||
|
||
DebugTrace(-1, Dbg, "FatGetEaFile: Exit\n", 0);
|
||
} _SEH2_END;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FatReadEaSet (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN USHORT EaHandle,
|
||
IN POEM_STRING FileName,
|
||
IN BOOLEAN ReturnEntireSet,
|
||
OUT PEA_RANGE EaSetRange
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine pins the Ea set for the given ea handle within the
|
||
Ea stream file. The EaHandle, after first comparing against valid
|
||
index values, is used to compute the cluster offset for this
|
||
this Ea set. The Ea set is then verified as belonging to this
|
||
index and lying within the Ea data file.
|
||
|
||
The caller of this function will have verified that the Ea file
|
||
exists and that the Vcb field points to an initialized cache file.
|
||
The caller will already have gained exclusive access to the
|
||
EaFcb.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the Vcb for the volume.
|
||
|
||
EaHandle - Supplies the handle for the Ea's to read.
|
||
|
||
FileName - Name of the file whose Ea's are being read.
|
||
|
||
ReturnEntireSet - Indicates if the caller needs the entire set
|
||
as opposed to just the header.
|
||
|
||
EaSetRange - Pointer to the EaRange structure which will describe the Ea
|
||
on return.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
ULONG EaOffsetVbo;
|
||
EA_RANGE EaOffsetRange;
|
||
USHORT EaOffsetCluster;
|
||
|
||
EA_RANGE EaHeaderRange;
|
||
PEA_FILE_HEADER EaHeader;
|
||
|
||
ULONG EaSetVbo;
|
||
PEA_SET_HEADER EaSet;
|
||
|
||
ULONG CbList;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( FileName );
|
||
|
||
DebugTrace(+1, Dbg, "FatReadEaSet\n", 0);
|
||
DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb);
|
||
|
||
//
|
||
// Verify that the Ea index has a legal value. Raise status
|
||
// STATUS_NONEXISTENT_EA_ENTRY if illegal.
|
||
//
|
||
|
||
if (EaHandle < MIN_EA_HANDLE
|
||
|| EaHandle > MAX_EA_HANDLE) {
|
||
|
||
DebugTrace(-1, Dbg, "FatReadEaSet: Illegal handle value\n", 0);
|
||
FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY );
|
||
}
|
||
|
||
//
|
||
// Verify that the virtual Ea file is large enough for us to read
|
||
// the EaOffet table for this index.
|
||
//
|
||
|
||
EaOffsetVbo = sizeof( EA_FILE_HEADER ) + (((ULONGLONG)EaHandle >> 7) << 8);
|
||
|
||
//
|
||
// Zero the Ea range structures.
|
||
//
|
||
|
||
RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE ));
|
||
RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE ));
|
||
|
||
//
|
||
// Use a try statement to clean up on exit.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// Pin down the EA file header.
|
||
//
|
||
|
||
FatPinEaRange( IrpContext,
|
||
Vcb->VirtualEaFile,
|
||
Vcb->EaFcb,
|
||
&EaHeaderRange,
|
||
0,
|
||
sizeof( EA_FILE_HEADER ),
|
||
STATUS_NONEXISTENT_EA_ENTRY );
|
||
|
||
EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data;
|
||
|
||
//
|
||
// Pin down the Ea offset table for the particular index.
|
||
//
|
||
|
||
FatPinEaRange( IrpContext,
|
||
Vcb->VirtualEaFile,
|
||
Vcb->EaFcb,
|
||
&EaOffsetRange,
|
||
EaOffsetVbo,
|
||
sizeof( EA_OFF_TABLE ),
|
||
STATUS_NONEXISTENT_EA_ENTRY );
|
||
|
||
//
|
||
// Check if the specifific handle is currently being used.
|
||
//
|
||
|
||
EaOffsetCluster = *((PUSHORT) EaOffsetRange.Data
|
||
+ (EaHandle & (MAX_EA_OFFSET_INDEX - 1)));
|
||
|
||
if (EaOffsetCluster == UNUSED_EA_HANDLE) {
|
||
|
||
DebugTrace(0, Dbg, "FatReadEaSet: Ea handle is unused\n", 0);
|
||
FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY );
|
||
}
|
||
|
||
//
|
||
// Compute the file offset for the Ea data.
|
||
//
|
||
|
||
EaSetVbo = (EaHeader->EaBaseTable[EaHandle >> 7] + EaOffsetCluster)
|
||
<< Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
//
|
||
// Unpin the file header and offset table.
|
||
//
|
||
|
||
FatUnpinEaRange( IrpContext, &EaHeaderRange );
|
||
FatUnpinEaRange( IrpContext, &EaOffsetRange );
|
||
|
||
//
|
||
// Pin the ea set.
|
||
//
|
||
|
||
FatPinEaRange( IrpContext,
|
||
Vcb->VirtualEaFile,
|
||
Vcb->EaFcb,
|
||
EaSetRange,
|
||
EaSetVbo,
|
||
BytesPerCluster,
|
||
STATUS_DATA_ERROR );
|
||
|
||
//
|
||
// Verify that the Ea set is valid and belongs to this index.
|
||
// Raise STATUS_DATA_ERROR if there is a data conflict.
|
||
//
|
||
|
||
EaSet = (PEA_SET_HEADER) EaSetRange->Data;
|
||
|
||
if (EaSet->Signature != EA_SET_SIGNATURE
|
||
|| EaSet->OwnEaHandle != EaHandle ) {
|
||
|
||
DebugTrace(0, Dbg, "FatReadEaSet: Ea set header is corrupt\n", 0);
|
||
FatRaiseStatus( IrpContext, STATUS_DATA_ERROR );
|
||
}
|
||
|
||
//
|
||
// At this point we have pinned a single cluster of Ea data. If
|
||
// this represents the entire Ea data for the Ea index, we are
|
||
// done. Otherwise we need to check on the entire size of
|
||
// of the Ea set header and whether it is contained in the allocated
|
||
// size of the Ea virtual file. At that point we can unpin
|
||
// the partial Ea set header and repin the entire header.
|
||
//
|
||
|
||
CbList = GetcbList( EaSet );
|
||
|
||
if (ReturnEntireSet
|
||
&& CbList > BytesPerCluster ) {
|
||
|
||
//
|
||
// Round up to the cluster size.
|
||
//
|
||
|
||
CbList = (CbList + EA_CBLIST_OFFSET + BytesPerCluster - 1)
|
||
& ~(BytesPerCluster - 1);
|
||
|
||
FatUnpinEaRange( IrpContext, EaSetRange );
|
||
|
||
RtlZeroMemory( EaSetRange, sizeof( EA_RANGE ));
|
||
|
||
FatPinEaRange( IrpContext,
|
||
Vcb->VirtualEaFile,
|
||
Vcb->EaFcb,
|
||
EaSetRange,
|
||
EaSetVbo,
|
||
CbList,
|
||
STATUS_DATA_ERROR );
|
||
}
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatReadEaSet );
|
||
|
||
//
|
||
// Unpin the Ea base and offset tables if locked down.
|
||
//
|
||
|
||
FatUnpinEaRange( IrpContext, &EaHeaderRange );
|
||
FatUnpinEaRange( IrpContext, &EaOffsetRange );
|
||
|
||
DebugTrace(-1, Dbg, "FatReadEaSet: Exit\n", 0);
|
||
} _SEH2_END;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
VOID
|
||
FatDeleteEaSet (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN PBCB EaBcb,
|
||
OUT PDIRENT EaDirent,
|
||
IN USHORT EaHandle,
|
||
IN POEM_STRING FileName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routines clips the Ea set for a particular index out of the
|
||
Ea file for a volume. The index is verified as belonging to a valid
|
||
handle. The clusters are removed and the Ea stream file along with
|
||
the Ea base and offset files are updated.
|
||
|
||
The caller of this function will have verified that the Ea file
|
||
exists and that the Vcb field points to an initialized cache file.
|
||
The caller will already have gained exclusive access to the
|
||
EaFcb.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the Vcb for the volume.
|
||
|
||
VirtualEeFile - Pointer to the file object for the virtual Ea file.
|
||
|
||
EaFcb - Supplies the pointer to the Fcb for the Ea file.
|
||
|
||
EaBcb - Supplies a pointer to the Bcb for the Ea dirent.
|
||
|
||
EaDirent - Supplies a pointer to the dirent for the Ea file.
|
||
|
||
EaHandle - Supplies the handle for the Ea's to read.
|
||
|
||
FileName - Name of the file whose Ea's are being read.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
ULONG CbList;
|
||
LARGE_INTEGER FileOffset;
|
||
|
||
LARGE_MCB DataMcb;
|
||
BOOLEAN UnwindInitializeDataMcb = FALSE;
|
||
BOOLEAN UnwindSplitData = FALSE;
|
||
|
||
LARGE_MCB TailMcb;
|
||
BOOLEAN UnwindInitializeTailMcb = FALSE;
|
||
BOOLEAN UnwindSplitTail = FALSE;
|
||
BOOLEAN UnwindMergeTail = FALSE;
|
||
|
||
BOOLEAN UnwindModifiedEaHeader = FALSE;
|
||
BOOLEAN UnwindCacheValues = FALSE;
|
||
ULONG UnwindPrevFileSize = 0;
|
||
|
||
ULONG EaOffsetVbo;
|
||
USHORT EaOffsetIndex;
|
||
EA_RANGE EaOffsetRange;
|
||
USHORT EaOffsetCluster;
|
||
|
||
PFILE_OBJECT VirtualEaFile = Vcb->VirtualEaFile;
|
||
PFCB EaFcb = Vcb->EaFcb;
|
||
|
||
EA_RANGE EaHeaderRange;
|
||
PEA_FILE_HEADER EaHeader;
|
||
USHORT EaHeaderBaseIndex;
|
||
|
||
ULONG EaSetVbo = 0;
|
||
ULONG EaSetLength;
|
||
EA_RANGE EaSetRange;
|
||
PEA_SET_HEADER EaSet;
|
||
USHORT EaSetClusterCount;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( FileName );
|
||
|
||
//
|
||
// Verify that the Ea index has a legal value. Raise status
|
||
// STATUS_INVALID_HANDLE if illegal.
|
||
//
|
||
|
||
if (EaHandle < MIN_EA_HANDLE
|
||
|| EaHandle > MAX_EA_HANDLE) {
|
||
|
||
DebugTrace(-1, Dbg, "FatDeleteEaSet: Illegal handle value\n", 0);
|
||
FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY );
|
||
}
|
||
|
||
//
|
||
// Verify that the virtual Ea file is large enough for us to read
|
||
// the EaOffet table for this index.
|
||
//
|
||
|
||
EaOffsetVbo = sizeof( EA_FILE_HEADER ) + (((ULONGLONG)EaHandle >> 7) << 8);
|
||
|
||
//
|
||
// Zero the Ea range structures.
|
||
//
|
||
|
||
RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE ));
|
||
RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE ));
|
||
RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
|
||
|
||
//
|
||
// Use a try to facilitate cleanup.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// Pin down the EA file header.
|
||
//
|
||
|
||
FatPinEaRange( IrpContext,
|
||
VirtualEaFile,
|
||
EaFcb,
|
||
&EaHeaderRange,
|
||
0,
|
||
sizeof( EA_FILE_HEADER ),
|
||
STATUS_NONEXISTENT_EA_ENTRY );
|
||
|
||
EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data;
|
||
|
||
//
|
||
// Pin down the Ea offset table for the particular index.
|
||
//
|
||
|
||
FatPinEaRange( IrpContext,
|
||
VirtualEaFile,
|
||
EaFcb,
|
||
&EaOffsetRange,
|
||
EaOffsetVbo,
|
||
sizeof( EA_OFF_TABLE ),
|
||
STATUS_NONEXISTENT_EA_ENTRY );
|
||
|
||
//
|
||
// Check if the specifific handle is currently being used.
|
||
//
|
||
|
||
EaOffsetIndex = EaHandle & (MAX_EA_OFFSET_INDEX - 1);
|
||
EaOffsetCluster = *((PUSHORT) EaOffsetRange.Data + EaOffsetIndex);
|
||
|
||
if (EaOffsetCluster == UNUSED_EA_HANDLE) {
|
||
|
||
DebugTrace(0, Dbg, "FatReadEaSet: Ea handle is unused\n", 0);
|
||
FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY );
|
||
}
|
||
|
||
//
|
||
// Compute the file offset for the Ea data.
|
||
//
|
||
|
||
EaHeaderBaseIndex = EaHandle >> 7;
|
||
EaSetVbo = (EaHeader->EaBaseTable[EaHeaderBaseIndex] + EaOffsetCluster)
|
||
<< Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
//
|
||
// Unpin the file header and offset table.
|
||
//
|
||
|
||
FatUnpinEaRange( IrpContext, &EaHeaderRange );
|
||
FatUnpinEaRange( IrpContext, &EaOffsetRange );
|
||
|
||
//
|
||
// Try to pin the requested Ea set.
|
||
//
|
||
|
||
FatPinEaRange( IrpContext,
|
||
VirtualEaFile,
|
||
EaFcb,
|
||
&EaSetRange,
|
||
EaSetVbo,
|
||
BytesPerCluster,
|
||
STATUS_DATA_ERROR );
|
||
|
||
EaSet = (PEA_SET_HEADER) EaSetRange.Data;
|
||
|
||
if (EaSet->Signature != EA_SET_SIGNATURE
|
||
|| EaSet->OwnEaHandle != EaHandle ) {
|
||
|
||
DebugTrace(0, Dbg, "FatReadEaSet: Ea set header is corrupt\n", 0);
|
||
FatRaiseStatus( IrpContext, STATUS_DATA_ERROR );
|
||
}
|
||
|
||
//
|
||
// At this point we have pinned a single cluster of Ea data. If
|
||
// this represents the entire Ea data for the Ea index, we know
|
||
// the number of clusters to remove. Otherwise we need to check
|
||
// on the entire size of the Ea set header and whether it is
|
||
// contained in the allocated size of the Ea virtual file. At
|
||
// that point we unpin the partial Ea set header and remember the
|
||
// starting cluster offset and number of clusters in both cluster
|
||
// and Vbo formats.
|
||
//
|
||
// At that point the following variables have the described
|
||
// values.
|
||
//
|
||
// EaSetVbo - Vbo to start splice at.
|
||
// EaSetLength - Number of bytes to splice.
|
||
// EaSetClusterCount - Number of clusters to splice.
|
||
//
|
||
|
||
CbList = GetcbList( EaSet );
|
||
|
||
EaSetClusterCount = (USHORT) ((CbList + EA_CBLIST_OFFSET + BytesPerCluster - 1)
|
||
>> Vcb->AllocationSupport.LogOfBytesPerCluster);
|
||
|
||
EaSetLength = EaSetClusterCount << Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
if (EaSetLength > BytesPerCluster) {
|
||
|
||
if (EaFcb->Header.FileSize.LowPart - EaSetVbo < EaSetLength) {
|
||
|
||
DebugTrace(0, Dbg, "FatDeleteEaSet: Full Ea set not contained in file\n", 0);
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_DATA_ERROR );
|
||
}
|
||
}
|
||
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
|
||
//
|
||
// Update the cache manager for this file. This is done by
|
||
// truncating to the point where the data was spliced and
|
||
// reinitializing with the modified size of the file.
|
||
//
|
||
// NOTE: Even if the all the EA's are removed the Ea file will
|
||
// always exist and the header area will never shrink.
|
||
//
|
||
|
||
FileOffset.LowPart = EaSetVbo;
|
||
FileOffset.HighPart = 0;
|
||
|
||
//
|
||
// Round the cache map down to a system page boundary.
|
||
//
|
||
|
||
FileOffset.LowPart &= ~(PAGE_SIZE - 1);
|
||
|
||
//
|
||
// Make sure all the data gets out to the disk.
|
||
//
|
||
|
||
{
|
||
IO_STATUS_BLOCK Iosb;
|
||
ULONG PurgeCount = 5;
|
||
|
||
while (--PurgeCount) {
|
||
|
||
Iosb.Status = STATUS_SUCCESS;
|
||
|
||
CcFlushCache( VirtualEaFile->SectionObjectPointer,
|
||
NULL,
|
||
0,
|
||
&Iosb );
|
||
|
||
NT_ASSERT( Iosb.Status == STATUS_SUCCESS );
|
||
|
||
//
|
||
// We do not have to worry about a lazy writer firing in parallel
|
||
// with our CcFlushCache since we have the EaFcb exclusive. Thus
|
||
// we know all data is out.
|
||
//
|
||
|
||
//
|
||
// We throw the unwanted pages out of the cache and then
|
||
// truncate the Ea File for the new size.
|
||
//
|
||
|
||
if (CcPurgeCacheSection( VirtualEaFile->SectionObjectPointer,
|
||
&FileOffset,
|
||
0,
|
||
FALSE )) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!PurgeCount) {
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_UNABLE_TO_DELETE_SECTION );
|
||
}
|
||
}
|
||
|
||
FileOffset.LowPart = EaFcb->Header.FileSize.LowPart - EaSetLength;
|
||
|
||
//
|
||
// Perform the splice operation on the FAT chain. This is done
|
||
// by splitting the target clusters out and merging the remaining
|
||
// clusters around them. We can ignore the return value from
|
||
// the merge and splice functions because we are guaranteed
|
||
// to be able to block.
|
||
//
|
||
|
||
{
|
||
FsRtlInitializeLargeMcb( &DataMcb, PagedPool );
|
||
|
||
UnwindInitializeDataMcb = TRUE;
|
||
|
||
FatSplitAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
EaSetVbo,
|
||
&DataMcb );
|
||
|
||
UnwindSplitData = TRUE;
|
||
|
||
if (EaSetLength + EaSetVbo != EaFcb->Header.FileSize.LowPart) {
|
||
|
||
FsRtlInitializeLargeMcb( &TailMcb, PagedPool );
|
||
|
||
UnwindInitializeTailMcb = TRUE;
|
||
|
||
FatSplitAllocation( IrpContext,
|
||
Vcb,
|
||
&DataMcb,
|
||
EaSetLength,
|
||
&TailMcb );
|
||
|
||
UnwindSplitTail = TRUE;
|
||
|
||
FatMergeAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
&TailMcb );
|
||
|
||
UnwindMergeTail = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Update the Fcb for the Ea file
|
||
//
|
||
|
||
UnwindPrevFileSize = EaFcb->Header.FileSize.LowPart;
|
||
|
||
(VOID)ExAcquireResourceExclusiveLite( EaFcb->Header.PagingIoResource,
|
||
TRUE );
|
||
|
||
EaFcb->Header.FileSize.LowPart = EaFcb->Header.FileSize.LowPart - EaSetLength;
|
||
EaFcb->Header.AllocationSize = EaFcb->Header.FileSize;
|
||
|
||
|
||
CcSetFileSizes( VirtualEaFile,
|
||
(PCC_FILE_SIZES)&EaFcb->Header.AllocationSize );
|
||
|
||
ExReleaseResourceLite( EaFcb->Header.PagingIoResource );
|
||
|
||
UnwindCacheValues = TRUE;
|
||
|
||
EaDirent->FileSize = EaFcb->Header.FileSize.LowPart;
|
||
|
||
FatSetDirtyBcb( IrpContext, EaBcb, Vcb, TRUE );
|
||
|
||
//
|
||
// Update the Ea base and offset tables. For the Ea base table,
|
||
// all subsequent index values must be decremented by the number
|
||
// of clusters removed.
|
||
//
|
||
// For the entries in the relevant Ea offset table, all entries
|
||
// after this index must also be decreased by the number of
|
||
// clusters removed.
|
||
//
|
||
|
||
//
|
||
// Pin down the EA file header.
|
||
//
|
||
|
||
RtlZeroMemory( &EaHeaderRange,
|
||
sizeof( EA_RANGE ));
|
||
|
||
FatPinEaRange( IrpContext,
|
||
VirtualEaFile,
|
||
EaFcb,
|
||
&EaHeaderRange,
|
||
0,
|
||
sizeof( EA_FILE_HEADER ),
|
||
STATUS_NONEXISTENT_EA_ENTRY );
|
||
|
||
EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data;
|
||
|
||
//
|
||
// Pin down the Ea offset table for the particular index.
|
||
//
|
||
|
||
RtlZeroMemory( &EaOffsetRange,
|
||
sizeof( EA_RANGE ));
|
||
|
||
FatPinEaRange( IrpContext,
|
||
VirtualEaFile,
|
||
EaFcb,
|
||
&EaOffsetRange,
|
||
EaOffsetVbo,
|
||
sizeof( EA_OFF_TABLE ),
|
||
STATUS_NONEXISTENT_EA_ENTRY );
|
||
|
||
{
|
||
ULONG Count;
|
||
PUSHORT NextEaIndex;
|
||
|
||
Count = MAX_EA_BASE_INDEX - EaHeaderBaseIndex - 1;
|
||
|
||
NextEaIndex = &EaHeader->EaBaseTable[EaHeaderBaseIndex + 1];
|
||
|
||
while (Count--) {
|
||
|
||
*(NextEaIndex++) -= EaSetClusterCount;
|
||
}
|
||
|
||
FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaHeaderRange );
|
||
|
||
Count = MAX_EA_OFFSET_INDEX - EaOffsetIndex - 1;
|
||
NextEaIndex = (PUSHORT) EaOffsetRange.Data + EaOffsetIndex;
|
||
|
||
*(NextEaIndex++) = UNUSED_EA_HANDLE;
|
||
|
||
while (Count--) {
|
||
|
||
if (*NextEaIndex != UNUSED_EA_HANDLE) {
|
||
|
||
*NextEaIndex -= EaSetClusterCount;
|
||
}
|
||
|
||
NextEaIndex++;
|
||
}
|
||
|
||
FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaOffsetRange );
|
||
}
|
||
|
||
UnwindModifiedEaHeader = TRUE;
|
||
|
||
//
|
||
// Deallocate the ea set removed
|
||
//
|
||
|
||
FatDeallocateDiskSpace( IrpContext,
|
||
Vcb,
|
||
&DataMcb,
|
||
FALSE );
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatDeleteEaSet );
|
||
|
||
//
|
||
// Restore file if abnormal termination.
|
||
//
|
||
// If we have modified the ea file header we ignore this
|
||
// error. Otherwise we walk through the state variables.
|
||
//
|
||
|
||
if (_SEH2_AbnormalTermination()
|
||
&& !UnwindModifiedEaHeader) {
|
||
|
||
//
|
||
// If we modified the Ea dirent or Fcb, recover the previous
|
||
// values.
|
||
//
|
||
|
||
if (UnwindPrevFileSize) {
|
||
|
||
EaFcb->Header.FileSize.LowPart = UnwindPrevFileSize;
|
||
EaFcb->Header.AllocationSize.LowPart = UnwindPrevFileSize;
|
||
EaDirent->FileSize = UnwindPrevFileSize;
|
||
|
||
if (UnwindCacheValues) {
|
||
|
||
CcSetFileSizes( VirtualEaFile,
|
||
(PCC_FILE_SIZES)&EaFcb->Header.AllocationSize );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we merged the tail with the
|
||
// ea file header. We split it out
|
||
// again.
|
||
//
|
||
|
||
if (UnwindMergeTail) {
|
||
|
||
FatSplitAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
EaSetVbo,
|
||
&TailMcb );
|
||
}
|
||
|
||
//
|
||
// If we split the tail off we merge the tail back
|
||
// with the ea data to remove.
|
||
//
|
||
|
||
if (UnwindSplitTail) {
|
||
|
||
FatMergeAllocation( IrpContext,
|
||
Vcb,
|
||
&DataMcb,
|
||
&TailMcb );
|
||
}
|
||
|
||
//
|
||
// If the ea set has been split out, we merge that
|
||
// cluster string back in the file. Otherwise we
|
||
// simply uninitialize the local Mcb.
|
||
//
|
||
|
||
if (UnwindSplitData) {
|
||
|
||
FatMergeAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
&DataMcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Unpin any Bcb's still active.
|
||
//
|
||
|
||
FatUnpinEaRange( IrpContext, &EaHeaderRange );
|
||
FatUnpinEaRange( IrpContext, &EaOffsetRange );
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
|
||
//
|
||
// Uninitialize any initialized Mcbs
|
||
//
|
||
|
||
if (UnwindInitializeDataMcb) {
|
||
|
||
FsRtlUninitializeLargeMcb( &DataMcb );
|
||
}
|
||
|
||
if (UnwindInitializeTailMcb) {
|
||
|
||
FsRtlUninitializeLargeMcb( &TailMcb );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatDeleteEaSet -> Exit\n", 0);
|
||
} _SEH2_END;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
VOID
|
||
FatAddEaSet (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN ULONG EaSetLength,
|
||
IN PBCB EaBcb,
|
||
OUT PDIRENT EaDirent,
|
||
OUT PUSHORT EaHandle,
|
||
OUT PEA_RANGE EaSetRange
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will add the necessary clusters to support a new
|
||
Ea set of the given size. This is done by splicing a chain of
|
||
clusters into the existing Ea file. An Ea index is assigned to
|
||
this new chain and the Ea base and offset tables are updated to
|
||
include this new handle. This routine also pins the added
|
||
clusters and returns their address and a Bcb.
|
||
|
||
The caller of this function will have verified that the Ea file
|
||
exists and that the Vcb field points to an initialized cache file.
|
||
The caller will already have gained exclusive access to the
|
||
EaFcb.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the Vcb to fill in.
|
||
|
||
EaSetLength - The number of bytes needed to contain the Ea set. This
|
||
routine will round this up the next cluster size.
|
||
|
||
EaBcb - Supplies a pointer to the Bcb for the Ea dirent.
|
||
|
||
EaDirent - Supplies a pointer to the dirent for the Ea file.
|
||
|
||
EaHandle - Supplies the address to store the ea index generated here.
|
||
|
||
EaSetRange - This is the structure that describes new range in the Ea file.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
EA_RANGE EaHeaderRange;
|
||
USHORT EaHeaderIndex;
|
||
PEA_FILE_HEADER EaHeader;
|
||
|
||
EA_RANGE EaOffsetRange;
|
||
ULONG EaNewOffsetVbo = 0;
|
||
USHORT EaOffsetIndex;
|
||
ULONG EaOffsetTableSize;
|
||
PUSHORT EaOffsetTable;
|
||
|
||
ULONG EaSetClusterOffset;
|
||
ULONG EaSetVbo = 0;
|
||
USHORT EaSetClusterCount;
|
||
PEA_SET_HEADER EaSet;
|
||
|
||
PFILE_OBJECT VirtualEaFile = Vcb->VirtualEaFile;
|
||
PFCB EaFcb = Vcb->EaFcb;
|
||
|
||
LARGE_MCB EaSetMcb;
|
||
BOOLEAN UnwindInitializedEaSetMcb = FALSE;
|
||
BOOLEAN UnwindAllocatedNewAllocation = FALSE;
|
||
BOOLEAN UnwindMergedNewEaSet = FALSE;
|
||
|
||
LARGE_MCB EaOffsetMcb;
|
||
BOOLEAN UnwindInitializedOffsetMcb = FALSE;
|
||
BOOLEAN UnwindSplitNewAllocation = FALSE;
|
||
BOOLEAN UnwindMergedNewOffset = FALSE;
|
||
|
||
LARGE_MCB EaTailMcb;
|
||
BOOLEAN UnwindInitializedTailMcb = FALSE;
|
||
BOOLEAN UnwindSplitTail = FALSE;
|
||
BOOLEAN UnwindMergedTail = FALSE;
|
||
|
||
LARGE_MCB EaInitialEaMcb;
|
||
BOOLEAN UnwindInitializedInitialEaMcb = FALSE;
|
||
BOOLEAN UnwindSplitInitialEa = FALSE;
|
||
BOOLEAN UnwindMergedInitialEa = FALSE;
|
||
|
||
USHORT NewEaIndex;
|
||
PUSHORT NextEaOffset;
|
||
|
||
ULONG NewAllocation;
|
||
LARGE_INTEGER FileOffset;
|
||
ULONG Count;
|
||
|
||
ULONG UnwindPrevFileSize = 0;
|
||
BOOLEAN UnwindCacheValues = FALSE;
|
||
|
||
BOOLEAN TailExists = FALSE;
|
||
BOOLEAN AddedOffsetTableCluster = FALSE;
|
||
BOOLEAN UnwindPurgeCacheMap = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FatAddEaSet\n", 0);
|
||
DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb);
|
||
DebugTrace( 0, Dbg, " EaSetLength = %ul\n", EaSetLength );
|
||
|
||
//
|
||
// Zero the Ea range structures.
|
||
//
|
||
|
||
RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE ));
|
||
RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE ));
|
||
|
||
//
|
||
// Use a try statement to facilitate cleanup.
|
||
//
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// Pin down the file header.
|
||
//
|
||
|
||
FatPinEaRange( IrpContext,
|
||
VirtualEaFile,
|
||
EaFcb,
|
||
&EaHeaderRange,
|
||
0,
|
||
sizeof( EA_FILE_HEADER ),
|
||
STATUS_DATA_ERROR );
|
||
|
||
EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data;
|
||
|
||
//
|
||
// Compute the size of the offset table.
|
||
//
|
||
|
||
EaNewOffsetVbo = EaHeader->EaBaseTable[0] << Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
EaOffsetTableSize = EaNewOffsetVbo - sizeof( EA_FILE_HEADER );
|
||
|
||
//
|
||
// Pin down the entire offset table.
|
||
//
|
||
|
||
FatPinEaRange( IrpContext,
|
||
VirtualEaFile,
|
||
EaFcb,
|
||
&EaOffsetRange,
|
||
sizeof( EA_FILE_HEADER ),
|
||
EaOffsetTableSize,
|
||
STATUS_DATA_ERROR );
|
||
|
||
//
|
||
// We now look for a valid handle out of the existing offset table.
|
||
// We start at the last entry and walk backwards. We stop at the
|
||
// first unused handle which is preceded by a used handle (or handle
|
||
// 1).
|
||
//
|
||
// As we walk backwards, we need to remember the file offset of the
|
||
// cluster which will follow the clusters we add. We initially
|
||
// remember the end of the file. If the end of the offset table
|
||
// consists of a string of used handles, we remember the offset of
|
||
// the handle prior to the transition from used to unused handles.
|
||
//
|
||
|
||
EaSetClusterOffset = EaFcb->Header.FileSize.LowPart
|
||
>> Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
NewEaIndex = (USHORT) ((EaOffsetTableSize >> 1) - 1);
|
||
|
||
NextEaOffset = (PUSHORT) EaOffsetRange.Data + NewEaIndex;
|
||
|
||
//
|
||
// Walk through the used handles at the end of the offset table.
|
||
//
|
||
|
||
if (*NextEaOffset != UNUSED_EA_HANDLE) {
|
||
|
||
while (NewEaIndex != 0) {
|
||
|
||
if (*(NextEaOffset - 1) == UNUSED_EA_HANDLE) {
|
||
|
||
//
|
||
// If the handle is 1, we take no action. Otherwise
|
||
// we save the cluster offset of the current handle
|
||
// knowing we will use a previous handle and insert
|
||
// a chain of clusters.
|
||
//
|
||
|
||
if (NewEaIndex != 1) {
|
||
|
||
EaSetClusterOffset = *NextEaOffset
|
||
+ EaHeader->EaBaseTable[NewEaIndex >> 7];
|
||
|
||
TailExists = TRUE;
|
||
}
|
||
|
||
NewEaIndex--;
|
||
NextEaOffset--;
|
||
|
||
break;
|
||
}
|
||
|
||
NewEaIndex--;
|
||
NextEaOffset--;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Walk through looking for the first unused handle in a string
|
||
// of unused handles.
|
||
//
|
||
|
||
while (NewEaIndex) {
|
||
|
||
if (*(NextEaOffset - 1) != UNUSED_EA_HANDLE) {
|
||
|
||
break;
|
||
}
|
||
|
||
NextEaOffset--;
|
||
NewEaIndex--;
|
||
}
|
||
|
||
//
|
||
// If the handle is zero, we do a special test to see if handle 1
|
||
// is available. Otherwise we will use the first handle of a new
|
||
// cluster. A non-zero handle now indicates that a handle was found
|
||
// in an existing offset table cluster.
|
||
//
|
||
|
||
if (NewEaIndex == 0) {
|
||
|
||
if (*(NextEaOffset + 1) == UNUSED_EA_HANDLE) {
|
||
|
||
NewEaIndex = 1;
|
||
|
||
} else {
|
||
|
||
NewEaIndex = (USHORT) EaOffsetTableSize >> 1;
|
||
AddedOffsetTableCluster = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the Ea index is outside the legal range then raise an
|
||
// exception.
|
||
//
|
||
|
||
if (NewEaIndex > MAX_EA_HANDLE) {
|
||
|
||
DebugTrace(-1, Dbg,
|
||
"FatAddEaSet: Illegal handle value for new handle\n", 0);
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
//
|
||
// Compute the base and offset indexes.
|
||
//
|
||
|
||
EaHeaderIndex = NewEaIndex >> 7;
|
||
EaOffsetIndex = NewEaIndex & (MAX_EA_OFFSET_INDEX - 1);
|
||
|
||
//
|
||
// Compute the byte offset of the new ea data in the file.
|
||
//
|
||
|
||
EaSetVbo = EaSetClusterOffset << Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
//
|
||
// Allocate all the required disk space together to insure this
|
||
// operation is atomic. We don't want to allocate one block
|
||
// of disk space and then fail on a second allocation.
|
||
//
|
||
|
||
EaSetLength = (EaSetLength + BytesPerCluster - 1)
|
||
& ~(BytesPerCluster - 1);
|
||
|
||
NewAllocation = EaSetLength
|
||
+ (AddedOffsetTableCluster ? BytesPerCluster : 0);
|
||
|
||
//
|
||
// Verify that adding these clusters will not grow the Ea file
|
||
// beyond its legal value. The maximum number of clusters is
|
||
// 2^16 since the Ea sets are referenced by a 16 bit cluster
|
||
// offset value.
|
||
//
|
||
|
||
if ((ULONG) ((0x0000FFFF << Vcb->AllocationSupport.LogOfBytesPerCluster)
|
||
- EaFcb->Header.FileSize.LowPart)
|
||
< NewAllocation) {
|
||
|
||
DebugTrace(-1, Dbg,
|
||
"FatAddEaSet: New Ea file size is too large\n", 0);
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
FsRtlInitializeLargeMcb( &EaSetMcb, PagedPool );
|
||
|
||
UnwindInitializedEaSetMcb = TRUE;
|
||
|
||
FatAllocateDiskSpace( IrpContext,
|
||
Vcb,
|
||
0,
|
||
&NewAllocation,
|
||
FALSE,
|
||
&EaSetMcb );
|
||
|
||
UnwindAllocatedNewAllocation = TRUE;
|
||
|
||
EaSetClusterCount = (USHORT) (EaSetLength >> Vcb->AllocationSupport.LogOfBytesPerCluster);
|
||
|
||
if (AddedOffsetTableCluster) {
|
||
|
||
FsRtlInitializeLargeMcb( &EaOffsetMcb, PagedPool );
|
||
|
||
UnwindInitializedOffsetMcb = TRUE;
|
||
|
||
FatSplitAllocation( IrpContext,
|
||
Vcb,
|
||
&EaSetMcb,
|
||
EaSetLength,
|
||
&EaOffsetMcb );
|
||
|
||
UnwindSplitNewAllocation = TRUE;
|
||
}
|
||
|
||
FatUnpinEaRange( IrpContext, &EaHeaderRange );
|
||
FatUnpinEaRange( IrpContext, &EaOffsetRange );
|
||
|
||
if (AddedOffsetTableCluster) {
|
||
|
||
FileOffset.LowPart = EaNewOffsetVbo;
|
||
|
||
} else {
|
||
|
||
FileOffset.LowPart = EaSetVbo;
|
||
}
|
||
|
||
FileOffset.HighPart = 0;
|
||
|
||
//
|
||
// Round the cache map down to a system page boundary.
|
||
//
|
||
|
||
FileOffset.LowPart &= ~(PAGE_SIZE - 1);
|
||
|
||
{
|
||
IO_STATUS_BLOCK Iosb;
|
||
ULONG PurgeCount = 5;
|
||
|
||
while (--PurgeCount) {
|
||
|
||
Iosb.Status = STATUS_SUCCESS;
|
||
|
||
CcFlushCache( VirtualEaFile->SectionObjectPointer,
|
||
NULL,
|
||
0,
|
||
&Iosb );
|
||
|
||
NT_ASSERT( Iosb.Status == STATUS_SUCCESS );
|
||
|
||
//
|
||
// We do not have to worry about a lazy writer firing in parallel
|
||
// with our CcFlushCache since we have the EaFcb exclusive. Thus
|
||
// we know all data is out.
|
||
//
|
||
|
||
//
|
||
// We throw the unwanted pages out of the cache and then
|
||
// truncate the Ea File for the new size.
|
||
//
|
||
//
|
||
|
||
if (CcPurgeCacheSection( VirtualEaFile->SectionObjectPointer,
|
||
&FileOffset,
|
||
0,
|
||
FALSE )) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!PurgeCount) {
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_UNABLE_TO_DELETE_SECTION );
|
||
}
|
||
}
|
||
|
||
UnwindPurgeCacheMap = TRUE;
|
||
|
||
FileOffset.LowPart = EaFcb->Header.FileSize.LowPart + NewAllocation;
|
||
|
||
//
|
||
// If there is a tail to the file, then we initialize an Mcb
|
||
// for the file section and split the tail from the file.
|
||
//
|
||
|
||
if (TailExists) {
|
||
|
||
FsRtlInitializeLargeMcb( &EaTailMcb, PagedPool );
|
||
|
||
UnwindInitializedTailMcb = TRUE;
|
||
|
||
FatSplitAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
EaSetVbo,
|
||
&EaTailMcb );
|
||
|
||
UnwindSplitTail = TRUE;
|
||
}
|
||
|
||
//
|
||
// If there is an initial section of ea data, we initialize an
|
||
// Mcb for that section.
|
||
//
|
||
|
||
if (AddedOffsetTableCluster
|
||
&& EaSetVbo != EaNewOffsetVbo) {
|
||
|
||
FsRtlInitializeLargeMcb( &EaInitialEaMcb, PagedPool );
|
||
|
||
UnwindInitializedInitialEaMcb = TRUE;
|
||
|
||
FatSplitAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
EaNewOffsetVbo,
|
||
&EaInitialEaMcb );
|
||
|
||
UnwindSplitInitialEa = TRUE;
|
||
}
|
||
|
||
//
|
||
// We have now split the new file allocation into the new
|
||
// ea set and possibly a new offset table.
|
||
//
|
||
// We have also split the existing file data into a file
|
||
// header, an initial section of ea data and the tail of the
|
||
// file. These last 2 may not exist.
|
||
//
|
||
// Each section is described by an Mcb.
|
||
//
|
||
|
||
//
|
||
// Merge the new offset information if it exists.
|
||
//
|
||
|
||
if (AddedOffsetTableCluster) {
|
||
|
||
FatMergeAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
&EaOffsetMcb );
|
||
|
||
FsRtlUninitializeLargeMcb( &EaOffsetMcb );
|
||
FsRtlInitializeLargeMcb( &EaOffsetMcb, PagedPool );
|
||
|
||
UnwindMergedNewOffset = TRUE;
|
||
}
|
||
|
||
//
|
||
// Merge the existing initial ea data if it exists.
|
||
//
|
||
|
||
if (UnwindInitializedInitialEaMcb) {
|
||
|
||
FatMergeAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
&EaInitialEaMcb );
|
||
|
||
FsRtlUninitializeLargeMcb( &EaInitialEaMcb );
|
||
FsRtlInitializeLargeMcb( &EaInitialEaMcb, PagedPool );
|
||
|
||
UnwindMergedInitialEa = TRUE;
|
||
}
|
||
|
||
//
|
||
// We modify the offset of the new ea set by one cluster if
|
||
// we added one to the offset table.
|
||
//
|
||
|
||
if (AddedOffsetTableCluster) {
|
||
|
||
EaSetClusterOffset += 1;
|
||
EaSetVbo += BytesPerCluster;
|
||
}
|
||
|
||
//
|
||
// Merge the new ea set.
|
||
//
|
||
|
||
FatMergeAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
&EaSetMcb );
|
||
|
||
FsRtlUninitializeLargeMcb( &EaSetMcb );
|
||
FsRtlInitializeLargeMcb( &EaSetMcb, PagedPool );
|
||
|
||
UnwindMergedNewEaSet = TRUE;
|
||
|
||
//
|
||
// Merge the tail if it exists.
|
||
//
|
||
|
||
if (UnwindInitializedTailMcb) {
|
||
|
||
FatMergeAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
&EaTailMcb );
|
||
|
||
FsRtlUninitializeLargeMcb( &EaTailMcb );
|
||
FsRtlInitializeLargeMcb( &EaTailMcb, PagedPool );
|
||
|
||
UnwindMergedTail = TRUE;
|
||
}
|
||
|
||
//
|
||
// If we added a new cluster for the offset table, we need to
|
||
// lock the entire cluster down and initialize all the handles to
|
||
// the unused state except the first one.
|
||
//
|
||
|
||
//
|
||
// Update the Fcb information.
|
||
//
|
||
|
||
UnwindPrevFileSize = EaFcb->Header.FileSize.LowPart;
|
||
|
||
EaFcb->Header.FileSize.LowPart += NewAllocation;
|
||
EaFcb->Header.AllocationSize = EaFcb->Header.FileSize;
|
||
EaDirent->FileSize = EaFcb->Header.FileSize.LowPart;
|
||
|
||
FatSetDirtyBcb( IrpContext, EaBcb, Vcb, TRUE );
|
||
|
||
//
|
||
// Let Mm and Cc know the new file sizes.
|
||
//
|
||
|
||
CcSetFileSizes( VirtualEaFile,
|
||
(PCC_FILE_SIZES)&EaFcb->Header.AllocationSize );
|
||
|
||
UnwindCacheValues = TRUE;
|
||
|
||
//
|
||
// Pin down the file header.
|
||
//
|
||
|
||
RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE ));
|
||
|
||
FatPinEaRange( IrpContext,
|
||
VirtualEaFile,
|
||
EaFcb,
|
||
&EaHeaderRange,
|
||
0,
|
||
sizeof( EA_FILE_HEADER ),
|
||
STATUS_DATA_ERROR );
|
||
|
||
EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data;
|
||
|
||
//
|
||
// Pin down the entire offset table.
|
||
//
|
||
|
||
|
||
RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE ));
|
||
|
||
FatPinEaRange( IrpContext,
|
||
VirtualEaFile,
|
||
EaFcb,
|
||
&EaOffsetRange,
|
||
sizeof( EA_FILE_HEADER ) + (((ULONGLONG)NewEaIndex >> 7) << 8),
|
||
sizeof( EA_OFF_TABLE ),
|
||
STATUS_DATA_ERROR );
|
||
|
||
EaOffsetTable = (PUSHORT) EaOffsetRange.Data;
|
||
|
||
//
|
||
// Pin the Ea set header for the added clusters and initialize
|
||
// the fields of interest. These are the signature field, the
|
||
// owning handle field, the need Ea field and the cbList field.
|
||
// Also mark the data as dirty.
|
||
//
|
||
|
||
//
|
||
// Pin the ea set.
|
||
//
|
||
|
||
FatPinEaRange( IrpContext,
|
||
VirtualEaFile,
|
||
EaFcb,
|
||
EaSetRange,
|
||
EaSetVbo,
|
||
EaSetLength,
|
||
STATUS_DATA_ERROR );
|
||
|
||
EaSet = (PEA_SET_HEADER) EaSetRange->Data;
|
||
|
||
EaSet->Signature = EA_SET_SIGNATURE;
|
||
EaSet->OwnEaHandle = NewEaIndex;
|
||
|
||
FatMarkEaRangeDirty( IrpContext, VirtualEaFile, EaSetRange );
|
||
|
||
//
|
||
// Update the Ea base and offset tables. For the Ea base table,
|
||
// all subsequent index values must be incremented by the number
|
||
// of clusters added.
|
||
//
|
||
// For the entries in the relevant Ea offset table, all entries
|
||
// after this index must also be increased by the number of
|
||
// clusters added.
|
||
//
|
||
// If we added another cluster to the offset table, then we increment
|
||
// all the base table values by 1.
|
||
//
|
||
|
||
Count = MAX_EA_BASE_INDEX - EaHeaderIndex - 1;
|
||
|
||
NextEaOffset = &EaHeader->EaBaseTable[EaHeaderIndex + 1];
|
||
|
||
while (Count--) {
|
||
|
||
*(NextEaOffset++) += EaSetClusterCount;
|
||
}
|
||
|
||
if (AddedOffsetTableCluster) {
|
||
|
||
Count = MAX_EA_BASE_INDEX;
|
||
|
||
NextEaOffset = &EaHeader->EaBaseTable[0];
|
||
|
||
while (Count--) {
|
||
|
||
*(NextEaOffset++) += 1;
|
||
}
|
||
}
|
||
|
||
FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaHeaderRange );
|
||
|
||
//
|
||
// If we added an offset table cluster, we need to initialize
|
||
// the handles to unused.
|
||
//
|
||
|
||
if (AddedOffsetTableCluster) {
|
||
|
||
Count = (BytesPerCluster >> 1) - 1;
|
||
NextEaOffset = EaOffsetTable;
|
||
|
||
*NextEaOffset++ = 0;
|
||
|
||
while (Count--) {
|
||
|
||
*NextEaOffset++ = UNUSED_EA_HANDLE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We need to compute the offset of the added Ea set clusters
|
||
// from their base.
|
||
//
|
||
|
||
NextEaOffset = EaOffsetTable + EaOffsetIndex;
|
||
|
||
*NextEaOffset++ = (USHORT) (EaSetClusterOffset
|
||
- EaHeader->EaBaseTable[EaHeaderIndex]);
|
||
|
||
Count = MAX_EA_OFFSET_INDEX - EaOffsetIndex - 1;
|
||
|
||
while (Count--) {
|
||
|
||
if (*NextEaOffset != UNUSED_EA_HANDLE) {
|
||
|
||
*NextEaOffset += EaSetClusterCount;
|
||
}
|
||
|
||
NextEaOffset++;
|
||
}
|
||
|
||
FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaOffsetRange );
|
||
|
||
//
|
||
// Update the callers parameters.
|
||
//
|
||
|
||
*EaHandle = NewEaIndex;
|
||
|
||
DebugTrace(0, Dbg, "FatAddEaSet: Return values\n", 0);
|
||
|
||
DebugTrace(0, Dbg, "FatAddEaSet: New Handle -> %x\n",
|
||
*EaHandle);
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatAddEaSet );
|
||
|
||
//
|
||
// Handle cleanup for abnormal termination only if we allocated
|
||
// disk space for the new ea set.
|
||
//
|
||
|
||
if (_SEH2_AbnormalTermination() && UnwindAllocatedNewAllocation) {
|
||
|
||
//
|
||
// If we modified the Ea dirent or Fcb, recover the previous
|
||
// values. Even though we are decreasing FileSize here, we
|
||
// don't need to synchronize to synchronize with paging Io
|
||
// because there was no dirty data generated in the new allocation.
|
||
//
|
||
|
||
if (UnwindPrevFileSize) {
|
||
|
||
EaFcb->Header.FileSize.LowPart = UnwindPrevFileSize;
|
||
EaFcb->Header.AllocationSize.LowPart = UnwindPrevFileSize;
|
||
EaDirent->FileSize = UnwindPrevFileSize;
|
||
|
||
if (UnwindCacheValues) {
|
||
|
||
CcSetFileSizes( VirtualEaFile,
|
||
(PCC_FILE_SIZES)&EaFcb->Header.AllocationSize );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we merged the tail then split it off.
|
||
//
|
||
|
||
if (UnwindMergedTail) {
|
||
|
||
VBO NewTailPosition;
|
||
|
||
NewTailPosition = EaSetVbo + EaSetLength;
|
||
|
||
FatSplitAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
NewTailPosition,
|
||
&EaTailMcb );
|
||
}
|
||
|
||
//
|
||
// If we merged the new ea data then split it out.
|
||
//
|
||
|
||
if (UnwindMergedNewEaSet) {
|
||
|
||
FatSplitAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
EaSetVbo,
|
||
&EaSetMcb );
|
||
}
|
||
|
||
//
|
||
// If we merged the initial ea data then split it out.
|
||
//
|
||
|
||
if (UnwindMergedInitialEa) {
|
||
|
||
FatSplitAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
EaNewOffsetVbo + BytesPerCluster,
|
||
&EaInitialEaMcb );
|
||
}
|
||
|
||
//
|
||
// If we added a new offset cluster, then split it out.
|
||
//
|
||
|
||
if (UnwindMergedNewOffset) {
|
||
|
||
FatSplitAllocation( IrpContext,
|
||
Vcb,
|
||
&EaFcb->Mcb,
|
||
EaNewOffsetVbo,
|
||
&EaOffsetMcb );
|
||
}
|
||
|
||
//
|
||
// If there is an initial ea section prior to the new section, merge
|
||
// it with the rest of the file.
|
||
//
|
||
|
||
if (UnwindSplitInitialEa) {
|
||
|
||
FatMergeAllocation( IrpContext, Vcb, &EaFcb->Mcb, &EaInitialEaMcb );
|
||
}
|
||
|
||
//
|
||
// If there is a file tail split off, merge it with the
|
||
// rest of the file.
|
||
//
|
||
|
||
if (UnwindSplitTail) {
|
||
|
||
FatMergeAllocation( IrpContext, Vcb, &EaFcb->Mcb, &EaTailMcb );
|
||
}
|
||
|
||
//
|
||
// If we modified the cache initialization for the ea file,
|
||
// then throw away the ea file object.
|
||
//
|
||
|
||
if (UnwindPurgeCacheMap) {
|
||
|
||
Vcb->VirtualEaFile = NULL;
|
||
ObDereferenceObject( VirtualEaFile );
|
||
}
|
||
|
||
//
|
||
// If we split the allocation, then deallocate the block for
|
||
// the new offset information.
|
||
//
|
||
|
||
if (UnwindSplitNewAllocation) {
|
||
|
||
FatDeallocateDiskSpace( IrpContext, Vcb, &EaOffsetMcb, FALSE );
|
||
}
|
||
|
||
//
|
||
// Deallocate the disk space.
|
||
//
|
||
|
||
FatDeallocateDiskSpace( IrpContext, Vcb, &EaSetMcb, FALSE );
|
||
}
|
||
|
||
//
|
||
// Unpin the Ea ranges.
|
||
//
|
||
|
||
FatUnpinEaRange( IrpContext, &EaHeaderRange );
|
||
FatUnpinEaRange( IrpContext, &EaOffsetRange );
|
||
|
||
//
|
||
// Uninitialize any local Mcbs
|
||
//
|
||
|
||
if (UnwindInitializedEaSetMcb) {
|
||
|
||
FsRtlUninitializeLargeMcb( &EaSetMcb );
|
||
}
|
||
|
||
if (UnwindInitializedOffsetMcb) {
|
||
|
||
FsRtlUninitializeLargeMcb( &EaOffsetMcb );
|
||
}
|
||
|
||
if (UnwindInitializedTailMcb) {
|
||
|
||
FsRtlUninitializeLargeMcb( &EaTailMcb );
|
||
}
|
||
|
||
if (UnwindInitializedInitialEaMcb) {
|
||
|
||
FsRtlUninitializeLargeMcb( &EaInitialEaMcb );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatAddEaSet -> Exit\n", 0);
|
||
} _SEH2_END;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FatAppendPackedEa (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OUT PEA_SET_HEADER *EaSetHeader,
|
||
IN OUT PULONG PackedEasLength,
|
||
IN OUT PULONG AllocationLength,
|
||
IN PFILE_FULL_EA_INFORMATION FullEa,
|
||
IN ULONG BytesPerCluster
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine appends a new packed ea onto an existing packed ea list,
|
||
it also will allocate/dealloate pool as necessary to hold the ea list.
|
||
|
||
Arguments:
|
||
|
||
EaSetHeader - Supplies the address to store the pointer to pool memory
|
||
which contains the Ea list for a file.
|
||
|
||
PackedEasLength - Supplies the length of the actual Ea data. The
|
||
new Ea data will be appended at this point.
|
||
|
||
AllocationLength - Supplies the allocated length available for Ea
|
||
data.
|
||
|
||
FullEa - Supplies a pointer to the new full ea that is to be appended
|
||
(in packed form) to the packed ea list.
|
||
|
||
BytesPerCluster - Number of bytes per cluster on this volume.
|
||
|
||
NOTE: The EaSetHeader refers to the entire block of Ea data for a
|
||
file. This includes the Ea's and their values as well as the
|
||
header information. The PackedEasLength and AllocationLength
|
||
parameters refer to the name/value pairs only.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG PackedEaSize;
|
||
PPACKED_EA ThisPackedEa;
|
||
OEM_STRING EaName;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FatAppendPackedEa...\n", 0);
|
||
|
||
//
|
||
// As a quick check see if the computed packed ea size plus the
|
||
// current packed ea list size will overflow the buffer. Full Ea and
|
||
// packed Ea only differ by 4 in their size
|
||
//
|
||
|
||
PackedEaSize = SizeOfFullEa( FullEa ) - 4;
|
||
|
||
if ( PackedEaSize + *PackedEasLength > *AllocationLength ) {
|
||
|
||
//
|
||
// We will overflow our current work buffer so allocate a larger
|
||
// one and copy over the current buffer
|
||
//
|
||
|
||
PVOID Temp;
|
||
ULONG NewAllocationSize;
|
||
ULONG OldAllocationSize;
|
||
|
||
DebugTrace(0, Dbg, "Allocate a new ea list buffer\n", 0);
|
||
|
||
//
|
||
// Compute a new size and allocate space. Always increase the
|
||
// allocation in cluster increments.
|
||
//
|
||
|
||
NewAllocationSize = (SIZE_OF_EA_SET_HEADER
|
||
+ PackedEaSize
|
||
+ *PackedEasLength
|
||
+ BytesPerCluster - 1)
|
||
& ~(BytesPerCluster - 1);
|
||
|
||
Temp = FsRtlAllocatePoolWithTag( PagedPool,
|
||
NewAllocationSize,
|
||
TAG_EA_SET_HEADER );
|
||
|
||
//
|
||
// Move over the existing ea list, and deallocate the old one
|
||
//
|
||
|
||
RtlCopyMemory( Temp,
|
||
*EaSetHeader,
|
||
OldAllocationSize = *AllocationLength
|
||
+ SIZE_OF_EA_SET_HEADER );
|
||
|
||
ExFreePool( *EaSetHeader );
|
||
|
||
//
|
||
// Set up so we will use the new packed ea list
|
||
//
|
||
|
||
*EaSetHeader = Temp;
|
||
|
||
//
|
||
// Zero out the added memory.
|
||
//
|
||
|
||
RtlZeroMemory( &(*EaSetHeader)->PackedEas[*AllocationLength],
|
||
NewAllocationSize - OldAllocationSize );
|
||
|
||
*AllocationLength = NewAllocationSize - SIZE_OF_EA_SET_HEADER;
|
||
}
|
||
|
||
//
|
||
// Determine if we need to increment our need ea changes count
|
||
//
|
||
|
||
if ( FlagOn(FullEa->Flags, FILE_NEED_EA )) {
|
||
|
||
//
|
||
// The NeedEaCount field is long aligned so we will write
|
||
// directly to it.
|
||
//
|
||
|
||
(*EaSetHeader)->NeedEaCount++;
|
||
}
|
||
|
||
//
|
||
// Now copy over the ea, full ea's and packed ea are identical except
|
||
// that full ea also have a next ea offset that we skip over
|
||
//
|
||
// Before:
|
||
// UsedSize Allocated
|
||
// | |
|
||
// V V
|
||
// +xxxxxxxx+-----------------------------+
|
||
//
|
||
// After:
|
||
// UsedSize Allocated
|
||
// | |
|
||
// V V
|
||
// +xxxxxxxx+yyyyyyyyyyyyyyyy+------------+
|
||
//
|
||
|
||
ThisPackedEa = (PPACKED_EA) (RtlOffsetToPointer( (*EaSetHeader)->PackedEas,
|
||
*PackedEasLength ));
|
||
|
||
RtlCopyMemory( ThisPackedEa,
|
||
(PUCHAR) FullEa + 4,
|
||
PackedEaSize );
|
||
|
||
//
|
||
// Now convert the name to uppercase.
|
||
//
|
||
|
||
EaName.MaximumLength = EaName.Length = FullEa->EaNameLength;
|
||
EaName.Buffer = ThisPackedEa->EaName;
|
||
|
||
FatUpcaseEaName( IrpContext, &EaName, &EaName );
|
||
|
||
//
|
||
// Increment the used size in the packed ea list structure
|
||
//
|
||
|
||
*PackedEasLength += PackedEaSize;
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "FatAppendPackedEa -> VOID\n", 0);
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FatDeletePackedEa (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OUT PEA_SET_HEADER EaSetHeader,
|
||
IN OUT PULONG PackedEasLength,
|
||
IN ULONG Offset
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine deletes an individual packed ea from the supplied
|
||
packed ea list.
|
||
|
||
Arguments:
|
||
|
||
EaSetHeader - Supplies the address to store the pointer to pool memory
|
||
which contains the Ea list for a file.
|
||
|
||
PackedEasLength - Supplies the length of the actual Ea data. The
|
||
new Ea data will be appended at this point.
|
||
|
||
Offset - Supplies the offset to the individual ea in the list to delete
|
||
|
||
NOTE: The EaSetHeader refers to the entire block of Ea data for a
|
||
file. This includes the Ea's and their values as well as the
|
||
header information. The PackedEasLength parameter refer to the
|
||
name/value pairs only.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPACKED_EA PackedEa;
|
||
ULONG PackedEaSize;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FatDeletePackedEa, Offset = %08lx\n", Offset);
|
||
|
||
//
|
||
// Get a reference to the packed ea and figure out its size
|
||
//
|
||
|
||
PackedEa = (PPACKED_EA) (&EaSetHeader->PackedEas[Offset]);
|
||
|
||
SizeOfPackedEa( PackedEa, &PackedEaSize );
|
||
|
||
//
|
||
// Determine if we need to decrement our need ea changes count
|
||
//
|
||
|
||
if (FlagOn(PackedEa->Flags, EA_NEED_EA_FLAG)) {
|
||
|
||
EaSetHeader->NeedEaCount--;
|
||
}
|
||
|
||
//
|
||
// Shrink the ea list over the deleted ea. The amount to copy is the
|
||
// total size of the ea list minus the offset to the end of the ea
|
||
// we're deleting.
|
||
//
|
||
// Before:
|
||
// Offset Offset+PackedEaSize UsedSize Allocated
|
||
// | | | |
|
||
// V V V V
|
||
// +xxxxxxxx+yyyyyyyyyyyyyyyy+zzzzzzzzzzzzzzzzzz+------------+
|
||
//
|
||
// After
|
||
// Offset UsedSize Allocated
|
||
// | | |
|
||
// V V V
|
||
// +xxxxxxxx+zzzzzzzzzzzzzzzzzz+-----------------------------+
|
||
//
|
||
|
||
RtlCopyMemory( PackedEa,
|
||
(PUCHAR) PackedEa + PackedEaSize,
|
||
*PackedEasLength - (Offset + PackedEaSize) );
|
||
|
||
//
|
||
// And zero out the remaing part of the ea list, to make things
|
||
// nice and more robust
|
||
//
|
||
|
||
RtlZeroMemory( &EaSetHeader->PackedEas[*PackedEasLength - PackedEaSize],
|
||
PackedEaSize );
|
||
|
||
//
|
||
// Decrement the used size by the amount we just removed
|
||
//
|
||
|
||
*PackedEasLength -= PackedEaSize;
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "FatDeletePackedEa -> VOID\n", 0);
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
ULONG
|
||
FatLocateNextEa (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PPACKED_EA FirstPackedEa,
|
||
IN ULONG PackedEasLength,
|
||
IN ULONG PreviousOffset
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine locates the offset for the next individual packed ea
|
||
inside of a packed ea list, given the offset to a previous Ea.
|
||
Instead of returing boolean to indicate if we've found the next one
|
||
we let the return offset be so large that it overuns the used size
|
||
of the packed ea list, and that way it's an easy construct to use
|
||
in a for loop.
|
||
|
||
Arguments:
|
||
|
||
FirstPackedEa - Supplies a pointer to the packed ea list structure
|
||
|
||
PackedEasLength - Supplies the length of the packed ea list
|
||
|
||
PreviousOffset - Supplies the offset to a individual packed ea in the
|
||
list
|
||
|
||
Return Value:
|
||
|
||
ULONG - The offset to the next ea in the list or 0xffffffff of one
|
||
does not exist.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPACKED_EA PackedEa;
|
||
ULONG PackedEaSize;
|
||
ULONG Offset;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FatLocateNextEa, PreviousOffset = %08lx\n",
|
||
PreviousOffset);
|
||
|
||
//
|
||
// Make sure the previous offset is within the used size range
|
||
//
|
||
|
||
if ( PreviousOffset >= PackedEasLength ) {
|
||
|
||
DebugTrace(-1, Dbg, "FatLocateNextEa -> 0xffffffff\n", 0);
|
||
return 0xffffffff;
|
||
}
|
||
|
||
//
|
||
// Get a reference to the previous packed ea, and compute its size
|
||
//
|
||
|
||
PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + PreviousOffset );
|
||
SizeOfPackedEa( PackedEa, &PackedEaSize );
|
||
|
||
//
|
||
// Compute to the next ea
|
||
//
|
||
|
||
Offset = PreviousOffset + PackedEaSize;
|
||
|
||
//
|
||
// Now, if the new offset is beyond the ea size then we know
|
||
// that there isn't one so, we return an offset of 0xffffffff.
|
||
// otherwise we'll leave the new offset alone.
|
||
//
|
||
|
||
if ( Offset >= PackedEasLength ) {
|
||
|
||
Offset = 0xffffffff;
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatLocateNextEa -> %08lx\n", Offset);
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
return Offset;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
FatLocateEaByName (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PPACKED_EA FirstPackedEa,
|
||
IN ULONG PackedEasLength,
|
||
IN POEM_STRING EaName,
|
||
OUT PULONG Offset
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine locates the offset for the next individual packed ea
|
||
inside of a packed ea list, given the name of the ea to locate
|
||
|
||
Arguments:
|
||
|
||
FirstPackedEa - Supplies a pointer to the packed ea list structure
|
||
|
||
PackedEasLength - Supplies the length of the packed ea list
|
||
|
||
EaName - Supplies the name of the ea search for
|
||
|
||
Offset - Receives the offset to the located individual ea in the list
|
||
if one exists.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the named packed ea exists in the list and FALSE
|
||
otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPACKED_EA PackedEa;
|
||
OEM_STRING Name;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FatLocateEaByName, EaName = %Z\n", EaName);
|
||
|
||
//
|
||
// For each packed ea in the list check its name against the
|
||
// ea name we're searching for
|
||
//
|
||
|
||
for ( *Offset = 0;
|
||
*Offset < PackedEasLength;
|
||
*Offset = FatLocateNextEa( IrpContext,
|
||
FirstPackedEa,
|
||
PackedEasLength,
|
||
*Offset )) {
|
||
|
||
//
|
||
// Reference the packed ea and get a string to its name
|
||
//
|
||
|
||
PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + *Offset);
|
||
|
||
Name.Buffer = &PackedEa->EaName[0];
|
||
Name.Length = PackedEa->EaNameLength;
|
||
Name.MaximumLength = PackedEa->EaNameLength;
|
||
|
||
//
|
||
// Compare the two strings, if they are equal then we've
|
||
// found the caller's ea
|
||
//
|
||
|
||
if ( RtlCompareString( EaName, &Name, TRUE ) == 0 ) {
|
||
|
||
DebugTrace(-1, Dbg, "FatLocateEaByName -> TRUE, *Offset = %08lx\n", *Offset);
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We've exhausted the ea list without finding a match so return false
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "FatLocateEaByName -> FALSE\n", 0);
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
FatIsEaNameValid (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OEM_STRING Name
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine simple returns whether the specified file names conforms
|
||
to the file system specific rules for legal Ea names.
|
||
|
||
For Ea names, the following rules apply:
|
||
|
||
A. An Ea name may not contain any of the following characters:
|
||
|
||
0x0000 - 0x001F \ / : * ? " < > | , + = [ ] ;
|
||
|
||
Arguments:
|
||
|
||
Name - Supllies the name to check.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the name is legal, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Index;
|
||
|
||
UCHAR Char;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// Empty names are not valid.
|
||
//
|
||
|
||
if ( Name.Length == 0 ) { return FALSE; }
|
||
|
||
//
|
||
// At this point we should only have a single name, which can't have
|
||
// more than 254 characters
|
||
//
|
||
|
||
if ( Name.Length > 254 ) { return FALSE; }
|
||
|
||
for ( Index = 0; Index < (ULONG)Name.Length; Index += 1 ) {
|
||
|
||
Char = Name.Buffer[ Index ];
|
||
|
||
//
|
||
// Skip over and Dbcs chacters
|
||
//
|
||
|
||
if ( FsRtlIsLeadDbcsCharacter( Char ) ) {
|
||
|
||
NT_ASSERT( Index != (ULONG)(Name.Length - 1) );
|
||
|
||
Index += 1;
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Make sure this character is legal, and if a wild card, that
|
||
// wild cards are permissible.
|
||
//
|
||
|
||
if ( !FsRtlIsAnsiCharacterLegalFat(Char, FALSE) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
FatPinEaRange (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFILE_OBJECT VirtualEaFile,
|
||
IN PFCB EaFcb,
|
||
IN OUT PEA_RANGE EaRange,
|
||
IN ULONG StartingVbo,
|
||
IN ULONG Length,
|
||
IN NTSTATUS ErrorStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to pin a range within the Ea file. It will follow all the
|
||
rules required by the cache manager so that we don't have overlapping pin operations.
|
||
If the range being pinned spans a section then the desired data will be copied into
|
||
an auxilary buffer. FatMarkEaRangeDirty will know whether to copy the data back
|
||
into the cache or whether to simply mark the pinned data dirty.
|
||
|
||
Arguments:
|
||
|
||
VirtualEaFile - This is the stream file for the Ea file.
|
||
|
||
EaFcb - This is the Fcb for the Ea file.
|
||
|
||
EaRange - This is the Ea range structure for this request.
|
||
|
||
StartingVbo - This is the starting offset in the Ea file to read from.
|
||
|
||
Length - This is the length of the read.
|
||
|
||
ErrorStatus - This is the error status to use if we are reading outside
|
||
of the file.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
LARGE_INTEGER LargeVbo;
|
||
ULONG ByteCount;
|
||
PBCB *NextBcb;
|
||
PVOID Buffer;
|
||
PCHAR DestinationBuffer = NULL;
|
||
BOOLEAN FirstPage = TRUE;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Verify that the entire read is contained within the Ea file.
|
||
//
|
||
|
||
if (Length == 0
|
||
|| StartingVbo >= EaFcb->Header.AllocationSize.LowPart
|
||
|| (EaFcb->Header.AllocationSize.LowPart - StartingVbo) < Length) {
|
||
|
||
FatRaiseStatus( IrpContext, ErrorStatus );
|
||
}
|
||
|
||
//
|
||
// If the read will span a section, the system addresses may not be contiguous.
|
||
// Allocate a separate buffer in this case.
|
||
//
|
||
|
||
if (((StartingVbo & (EA_SECTION_SIZE - 1)) + Length) > EA_SECTION_SIZE) {
|
||
|
||
EaRange->Data = FsRtlAllocatePoolWithTag( PagedPool,
|
||
Length,
|
||
TAG_EA_DATA );
|
||
EaRange->AuxilaryBuffer = TRUE;
|
||
|
||
DestinationBuffer = EaRange->Data;
|
||
|
||
} else {
|
||
|
||
//
|
||
// PREfix correctly notes that if we don't decide here to have an aux buffer
|
||
// and the flag is up in the EaRange, we'll party on random memory since
|
||
// DestinationBuffer won't be set; however, this will never happen due to
|
||
// initialization of ea ranges and the cleanup in UnpinEaRange.
|
||
//
|
||
|
||
NT_ASSERT( EaRange->AuxilaryBuffer == FALSE );
|
||
}
|
||
|
||
|
||
//
|
||
// If the read will require more pages than our structure will hold then
|
||
// allocate an auxilary buffer. We have to figure the number of pages
|
||
// being requested so we have to include the page offset of the first page of
|
||
// the request.
|
||
//
|
||
|
||
EaRange->BcbChainLength = (USHORT) (((StartingVbo & (PAGE_SIZE - 1)) + Length + PAGE_SIZE - 1) / PAGE_SIZE);
|
||
|
||
if (EaRange->BcbChainLength > EA_BCB_ARRAY_SIZE) {
|
||
|
||
EaRange->BcbChain = FsRtlAllocatePoolWithTag( PagedPool,
|
||
sizeof( PBCB ) * EaRange->BcbChainLength,
|
||
TAG_BCB );
|
||
|
||
RtlZeroMemory( EaRange->BcbChain, sizeof( PBCB ) * EaRange->BcbChainLength );
|
||
|
||
} else {
|
||
|
||
EaRange->BcbChain = (PBCB *) &EaRange->BcbArray;
|
||
}
|
||
|
||
//
|
||
// Store the byte range data in the Ea Range structure.
|
||
//
|
||
|
||
EaRange->StartingVbo = StartingVbo;
|
||
EaRange->Length = Length;
|
||
|
||
//
|
||
// Compute the initial pin length.
|
||
//
|
||
|
||
ByteCount = PAGE_SIZE - (StartingVbo & (PAGE_SIZE - 1));
|
||
|
||
//
|
||
// For each page in the range; pin the page and update the Bcb count, copy to
|
||
// the auxiliary buffer.
|
||
//
|
||
|
||
NextBcb = EaRange->BcbChain;
|
||
|
||
while (Length != 0) {
|
||
|
||
//
|
||
// Pin the page and remember the data start.
|
||
//
|
||
|
||
LargeVbo.QuadPart = StartingVbo;
|
||
|
||
if (ByteCount > Length) {
|
||
|
||
ByteCount = Length;
|
||
}
|
||
|
||
if (!CcPinRead( VirtualEaFile,
|
||
&LargeVbo,
|
||
ByteCount,
|
||
BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
|
||
NextBcb,
|
||
&Buffer )) {
|
||
|
||
//
|
||
// Could not read the data without waiting (cache miss).
|
||
//
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
|
||
}
|
||
|
||
//
|
||
// Increment the Bcb pointer and copy to the auxilary buffer if necessary.
|
||
//
|
||
|
||
NextBcb += 1;
|
||
|
||
if (EaRange->AuxilaryBuffer == TRUE) {
|
||
|
||
RtlCopyMemory( DestinationBuffer,
|
||
Buffer,
|
||
ByteCount );
|
||
|
||
DestinationBuffer = (PCHAR) Add2Ptr( DestinationBuffer, ByteCount );
|
||
}
|
||
|
||
StartingVbo += ByteCount;
|
||
Length -= ByteCount;
|
||
|
||
//
|
||
// If this is the first page then update the Ea Range structure.
|
||
//
|
||
|
||
if (FirstPage) {
|
||
|
||
FirstPage = FALSE;
|
||
ByteCount = PAGE_SIZE;
|
||
|
||
if (EaRange->AuxilaryBuffer == FALSE) {
|
||
|
||
EaRange->Data = Buffer;
|
||
}
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FatMarkEaRangeDirty (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFILE_OBJECT EaFileObject,
|
||
IN OUT PEA_RANGE EaRange
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to mark a range of the Ea file as dirty. If the modified
|
||
data is sitting in an auxilary buffer then we will copy it back into the cache.
|
||
In any case we will go through the list of Bcb's and mark them dirty.
|
||
|
||
Arguments:
|
||
|
||
EaFileObject - This is the file object for the Ea file.
|
||
|
||
EaRange - This is the Ea range structure for this request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PBCB *NextBcb;
|
||
ULONG BcbCount;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// If there is an auxilary buffer we need to copy the data back into the cache.
|
||
//
|
||
|
||
if (EaRange->AuxilaryBuffer == TRUE) {
|
||
|
||
LARGE_INTEGER LargeVbo;
|
||
|
||
LargeVbo.QuadPart = EaRange->StartingVbo;
|
||
|
||
CcCopyWrite( EaFileObject,
|
||
&LargeVbo,
|
||
EaRange->Length,
|
||
TRUE,
|
||
EaRange->Data );
|
||
}
|
||
|
||
//
|
||
// Now walk through the Bcb chain and mark everything dirty.
|
||
//
|
||
|
||
BcbCount = EaRange->BcbChainLength;
|
||
NextBcb = EaRange->BcbChain;
|
||
|
||
while (BcbCount--) {
|
||
|
||
if (*NextBcb != NULL) {
|
||
|
||
CcSetDirtyPinnedData( *NextBcb, NULL );
|
||
}
|
||
|
||
NextBcb += 1;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FatUnpinEaRange (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OUT PEA_RANGE EaRange
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to unpin a range in the Ea file. Any structures allocated
|
||
will be deallocated here.
|
||
|
||
Arguments:
|
||
|
||
EaRange - This is the Ea range structure for this request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PBCB *NextBcb;
|
||
ULONG BcbCount;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// If we allocated a auxilary buffer, deallocate it here.
|
||
//
|
||
|
||
if (EaRange->AuxilaryBuffer == TRUE) {
|
||
|
||
ExFreePool( EaRange->Data );
|
||
EaRange->AuxilaryBuffer = FALSE;
|
||
}
|
||
|
||
//
|
||
// Walk through the Bcb chain and unpin the data.
|
||
//
|
||
|
||
if (EaRange->BcbChain != NULL) {
|
||
|
||
BcbCount = EaRange->BcbChainLength;
|
||
NextBcb = EaRange->BcbChain;
|
||
|
||
while (BcbCount--) {
|
||
|
||
if (*NextBcb != NULL) {
|
||
|
||
CcUnpinData( *NextBcb );
|
||
*NextBcb = NULL;
|
||
}
|
||
|
||
NextBcb += 1;
|
||
}
|
||
|
||
//
|
||
// If we allocated a Bcb chain, deallocate it here.
|
||
//
|
||
|
||
if (EaRange->BcbChain != &EaRange->BcbArray[0]) {
|
||
|
||
ExFreePool( EaRange->BcbChain );
|
||
}
|
||
|
||
EaRange->BcbChain = NULL;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|