/*++ Copyright (c) 1990-2000 Microsoft Corporation Module Name: EaSup.c Abstract: This module implements the cluster operations on the EA file for Fat. // @@BEGIN_DDKSPLIT Author: Brian Andrew [BrianAn] 07-Nov-1990 // @@END_DDKSPLIT --*/ #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 #define Add2Ptr(P,I) ((PVOID)((PUCHAR)(P) + (I))) // // Any access to the Ea file must recognize when a section boundary is being // crossed. // #define EA_SECTION_SIZE (0x00040000) 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; BOOLEAN LockedEaFcb; EA_RANGE EaSetRange; 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. // 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 = 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); } 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); } return; } 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; BOOLEAN LockedEaFcb; EA_RANGE EaSetRange; 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. // 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 = 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; } 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); } return; } 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; EA_RANGE EaSetRange; DebugTrace(+1, Dbg, "FatCreateEa...\n", 0); EaBcb = NULL; LockedEaFcb = FALSE; RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); // // Use 'try' to facilitate cleanup. // 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; } } 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); } return; } 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; DebugTrace(+1, Dbg, "FatDeleteEa...\n", 0); // // Initialize local values. // EaBcb = NULL; LockedEaFcb = FALSE; // // Use a try statement to facilitate cleanup. // 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 ); } 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); } return; } 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; DebugTrace(+1, Dbg, "FatGetEaFile ...\n", 0); RtlZeroMemory( &EaFileRange, sizeof( EA_RANGE )); // // Use a try to facilitate cleanup // 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, EaDirent, EaBcb ); try_return( NOTHING ); } else { VBO ByteOffset; // // 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; 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 ); FatPrepareWriteDirectoryFile( IrpContext, Vcb->EaFcb->ParentDcb, Vcb->EaFcb->DirentOffsetWithinDirectory, sizeof(DIRENT), EaBcb, EaDirent, FALSE, TRUE, &Status ); 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; } finally { DebugUnwind( FatGetEaFile ); // // If this is abnormal termination and disk space has been // allocated. We deallocate it now. // if (AbnormalTermination()) { // // Deallocate the Ea file // if (UnwindAllocatedDiskSpace) { FatDeallocateDiskSpace( IrpContext, Vcb, &Vcb->EaFcb->Mcb ); } // // 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); } 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; DebugTrace(+1, Dbg, "FatReadEaSet\n", 0); DebugTrace( 0, Dbg, " Vcb = %8lx\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 ) + ((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. // 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 ); } } 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); } return; } 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; ULONG EaSetLength; EA_RANGE EaSetRange; PEA_SET_HEADER EaSet; USHORT EaSetClusterCount; // // 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 ) + ((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. // 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 ); 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 ); } 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 (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); } return; } 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; USHORT EaOffsetIndex; ULONG EaOffsetTableSize; PUSHORT EaOffsetTable; ULONG EaSetClusterOffset; ULONG EaSetVbo; 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; DebugTrace(+1, Dbg, "FatAddEaSet\n", 0); DebugTrace( 0, Dbg, " Vcb = %8lx\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. // 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 ); 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 ) + ((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); } finally { DebugUnwind( FatAddEaSet ); // // Handle cleanup for abnormal termination only if we allocated // disk space for the new ea set. // if (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 ); } // // Deallocate the disk space. // FatDeallocateDiskSpace( IrpContext, Vcb, &EaSetMcb ); } // // 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); } 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; 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; 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; 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; 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; // // 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 ) ) { 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; BOOLEAN FirstPage = TRUE; // // 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. // 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; // // 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; // // 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; }