diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/cntfs/ea.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/cntfs/ea.c')
-rw-r--r-- | private/ntos/cntfs/ea.c | 2748 |
1 files changed, 2748 insertions, 0 deletions
diff --git a/private/ntos/cntfs/ea.c b/private/ntos/cntfs/ea.c new file mode 100644 index 000000000..7ddc18c4c --- /dev/null +++ b/private/ntos/cntfs/ea.c @@ -0,0 +1,2748 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + Ea.c + +Abstract: + + This module implements the File set and query Ea routines for Ntfs called + by the dispatch driver. + +Author: + + Your Name [Email] dd-Mon-Year + +Revision History: + +--*/ + +#include "NtfsProc.h" + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_EA) + +// +// Define a tag for general pool allocations from this module +// + +#undef MODULE_POOL_TAG +#define MODULE_POOL_TAG ('EFtN') + +// +// Local definitions +// + +// +// The following gives us an empty name string. +// + +UNICODE_STRING AttrNoName = CONSTANT_UNICODE_STRING( L"" ); + +#define MAXIMUM_EA_SIZE 0x0000ffff + +// +// The following macros compute the packed and unpacked size of the EAs. +// We use the 1 char defined in the structure for the NULL terminator of +// the name. +// + +#define SizeOfEaInformation \ + (sizeof( ULONG ) + sizeof( USHORT ) + 3 * sizeof( UCHAR )) + +#define PackedEaSize(EA) \ + ((SizeOfEaInformation - 4) \ + + ((PFILE_FULL_EA_INFORMATION) EA)->EaNameLength \ + + ((PFILE_FULL_EA_INFORMATION) EA)->EaValueLength) + +#define RawUnpackedEaSize(EA) \ + (SizeOfEaInformation \ + + ((PFILE_FULL_EA_INFORMATION) EA)->EaNameLength \ + + ((PFILE_FULL_EA_INFORMATION) EA)->EaValueLength) \ + +#define AlignedUnpackedEaSize(EA) \ + (((PFILE_FULL_EA_INFORMATION) EA)->NextEntryOffset != 0 \ + ? ((PFILE_FULL_EA_INFORMATION) EA)->NextEntryOffset \ + : (LongAlign( RawUnpackedEaSize( EA )))) \ + +// +// BOOLEAN +// NtfsAreEaNamesEqual ( +// IN PIRP_CONTEXT IrpContext, +// IN PSTRING NameA, +// IN PSTRING NameB +// ); +// + +#define NtfsAreEaNamesEqual(NAMEA, NAMEB ) ((BOOLEAN) \ + ((NAMEA)->Length == (NAMEB)->Length \ + && RtlEqualMemory( (NAMEA)->Buffer, \ + (NAMEB)->Buffer, \ + (NAMEA)->Length ) ) \ +) + +// +// VOID +// NtfsUpcaseEaName ( +// IN PSTRING EaName, +// OUT PSTRING UpcasedEaName +// ); +// + +#define NtfsUpcaseEaName( NAME, UPCASEDNAME ) \ + RtlUpperString( UPCASEDNAME, NAME ) + +BOOLEAN +NtfsIsEaNameValid ( + IN STRING Name + ); + +// +// Local procedure prototypes +// + +VOID +NtfsAppendEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_LIST_HEADER EaListHeader, + IN PFILE_FULL_EA_INFORMATION FullEa, + IN PVCB Vcb + ); + +VOID +NtfsDeleteEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_LIST_HEADER EaListHeader, + IN ULONG Offset + ); + +BOOLEAN +NtfsLocateEaByName ( + IN PFILE_FULL_EA_INFORMATION FullEa, + IN ULONG EaBufferLength, + IN PSTRING EaName, + OUT PULONG Offset + ); + +IO_STATUS_BLOCK +NtfsQueryEaUserEaList ( + IN PFILE_FULL_EA_INFORMATION CurrentEas, + IN PEA_INFORMATION EaInformation, + OUT PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG UserBufferLength, + IN PFILE_GET_EA_INFORMATION UserEaList, + IN BOOLEAN ReturnSingleEntry + ); + +IO_STATUS_BLOCK +NtfsQueryEaIndexSpecified ( + OUT PCCB Ccb, + IN PFILE_FULL_EA_INFORMATION CurrentEas, + IN PEA_INFORMATION EaInformation, + OUT PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG UserBufferLength, + IN ULONG UserEaIndex, + IN BOOLEAN ReturnSingleEntry + ); + +IO_STATUS_BLOCK +NtfsQueryEaSimpleScan ( + OUT PCCB Ccb, + IN PFILE_FULL_EA_INFORMATION CurrentEas, + IN PEA_INFORMATION EaInformation, + OUT PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG UserBufferLength, + IN BOOLEAN ReturnSingleEntry, + IN ULONG StartingOffset + ); + +BOOLEAN +NtfsIsDuplicateGeaName ( + IN PFILE_GET_EA_INFORMATION CurrentGea, + IN PFILE_GET_EA_INFORMATION UserGeaBuffer + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NtfsAppendEa) +#pragma alloc_text(PAGE, NtfsBuildEaList) +#pragma alloc_text(PAGE, NtfsCommonQueryEa) +#pragma alloc_text(PAGE, NtfsCommonSetEa) +#pragma alloc_text(PAGE, NtfsDeleteEa) +#pragma alloc_text(PAGE, NtfsFsdQueryEa) +#pragma alloc_text(PAGE, NtfsFsdSetEa) +#pragma alloc_text(PAGE, NtfsIsDuplicateGeaName) +#pragma alloc_text(PAGE, NtfsIsEaNameValid) +#pragma alloc_text(PAGE, NtfsLocateEaByName) +#pragma alloc_text(PAGE, NtfsMapExistingEas) +#pragma alloc_text(PAGE, NtfsQueryEaIndexSpecified) +#pragma alloc_text(PAGE, NtfsQueryEaSimpleScan) +#pragma alloc_text(PAGE, NtfsQueryEaUserEaList) +#pragma alloc_text(PAGE, NtfsReplaceFileEas) +#endif + + +NTSTATUS +NtfsFsdQueryEa ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of query Ea. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + TOP_LEVEL_CONTEXT TopLevelContext; + PTOP_LEVEL_CONTEXT ThreadTopLevelContext; + + NTSTATUS Status = STATUS_SUCCESS; + PIRP_CONTEXT IrpContext = NULL; + + ASSERT_IRP( Irp ); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsFsdQueryEa\n") ); + + // + // Call the common query Ea routine + // + + FsRtlEnterFileSystem(); + + ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, FALSE, FALSE ); + + do { + + try { + + // + // We are either initiating this request or retrying it. + // + + if (IrpContext == NULL) { + + IrpContext = NtfsCreateIrpContext( Irp, CanFsdWait( Irp ) ); + NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext ); + + } else if (Status == STATUS_LOG_FILE_FULL) { + + NtfsCheckpointForLogFileFull( IrpContext ); + } + + Status = NtfsCommonQueryEa( IrpContext, Irp ); + break; + + } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + } while (Status == STATUS_CANT_WAIT || + Status == STATUS_LOG_FILE_FULL); + + if (ThreadTopLevelContext == &TopLevelContext) { + NtfsRestoreTopLevelIrp( ThreadTopLevelContext ); + } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace( -1, Dbg, ("NtfsFsdQueryEa -> %08lx\n", Status) ); + + return Status; +} + + +NTSTATUS +NtfsFsdSetEa ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of set Ea. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + TOP_LEVEL_CONTEXT TopLevelContext; + PTOP_LEVEL_CONTEXT ThreadTopLevelContext; + + NTSTATUS Status = STATUS_SUCCESS; + PIRP_CONTEXT IrpContext = NULL; + + ASSERT_IRP( Irp ); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsFsdSetEa\n") ); + + // + // Call the common set Ea routine + // + + FsRtlEnterFileSystem(); + + ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, FALSE, FALSE ); + + do { + + try { + + // + // We are either initiating this request or retrying it. + // + + if (IrpContext == NULL) { + + IrpContext = NtfsCreateIrpContext( Irp, CanFsdWait( Irp ) ); + NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext ); + + } else if (Status == STATUS_LOG_FILE_FULL) { + + NtfsCheckpointForLogFileFull( IrpContext ); + } + + Status = NtfsCommonSetEa( IrpContext, Irp ); + break; + + } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + } while (Status == STATUS_CANT_WAIT || + Status == STATUS_LOG_FILE_FULL); + + if (ThreadTopLevelContext == &TopLevelContext) { + NtfsRestoreTopLevelIrp( ThreadTopLevelContext ); + } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace( -1, Dbg, ("NtfsFsdSetEa -> %08lx\n", Status) ); + + return Status; +} + + +NTSTATUS +NtfsCommonQueryEa ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for query Ea called by both the fsd and fsp + threads. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + PFILE_OBJECT FileObject; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PSCB Scb; + PCCB Ccb; + + PFILE_FULL_EA_INFORMATION EaBuffer; + ULONG UserBufferLength; + PFILE_GET_EA_INFORMATION UserEaList; + ULONG UserEaListLength; + ULONG UserEaIndex; + ULONG EaLength; + BOOLEAN RestartScan; + BOOLEAN ReturnSingleEntry; + BOOLEAN IndexSpecified; + + PFILE_FULL_EA_INFORMATION CurrentEas; + PBCB EaBcb; + + ATTRIBUTE_ENUMERATION_CONTEXT EaInfoAttr; + BOOLEAN CleanupEaInfoAttr; + PEA_INFORMATION EaInformation; + EA_INFORMATION DummyEaInformation; + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + PAGED_CODE(); + + // + // Get the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace( +1, Dbg, ("NtfsCommonQueryEa\n") ); + DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); + DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) ); + DebugTrace( 0, Dbg, ("SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) ); + DebugTrace( 0, Dbg, ("Length = %08lx\n", IrpSp->Parameters.QueryEa.Length) ); + DebugTrace( 0, Dbg, ("EaList = %08lx\n", IrpSp->Parameters.QueryEa.EaList) ); + DebugTrace( 0, Dbg, ("EaListLength = %08lx\n", IrpSp->Parameters.QueryEa.EaListLength) ); + DebugTrace( 0, Dbg, ("EaIndex = %08lx\n", IrpSp->Parameters.QueryEa.EaIndex) ); + DebugTrace( 0, Dbg, ("RestartScan = %08lx\n", FlagOn(IrpSp->Flags, SL_RESTART_SCAN)) ); + DebugTrace( 0, Dbg, ("ReturnSingleEntry = %08lx\n", FlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY)) ); + DebugTrace( 0, Dbg, ("IndexSpecified = %08lx\n", FlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED)) ); + + // + // Extract and decode the file object + // + + FileObject = IrpSp->FileObject; + TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE ); + + // + // This must be a user file or directory and the Ccb must indicate that + // the caller opened the entire file. + // + + if (Ccb == NULL + || !FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE ) + || (TypeOfOpen != UserFileOpen + && TypeOfOpen != UserDirectoryOpen)) { + + NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace( -1, Dbg, ("NtfsCommonQueryEa -> %08lx\n", STATUS_INVALID_PARAMETER) ); + + return STATUS_INVALID_PARAMETER; + } + + // + // Acquire the Fcb exclusively. + // + + NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, FALSE, FALSE ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Reference our input parameters to make things easier + // + + UserBufferLength = IrpSp->Parameters.QueryEa.Length; + UserEaList = (PFILE_GET_EA_INFORMATION) IrpSp->Parameters.QueryEa.EaList; + UserEaListLength = IrpSp->Parameters.QueryEa.EaListLength; + UserEaIndex = IrpSp->Parameters.QueryEa.EaIndex; + RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN); + ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY); + IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED); + + // + // Initialize our local variables. + // + + Status = STATUS_SUCCESS; + CleanupEaInfoAttr = FALSE; + EaBcb = NULL; + + // + // Map the user's buffer. + // + + EaBuffer = NtfsMapUserBuffer( Irp ); + + // + // Verify that the Ea file is in a consistant state. If the + // Ea modification count in the Fcb doesn't match that in + // the CCB, then the Ea file has been changed from under + // us. If we are not starting the search from the beginning + // of the Ea set, we return an error. + // + + if (UserEaList == NULL + && Ccb->NextEaOffset != 0 + && !IndexSpecified + && !RestartScan + && Fcb->EaModificationCount != Ccb->EaModificationCount) { + + DebugTrace( 0, Dbg, ("NtfsCommonQueryEa: Ea file in unknown state\n") ); + + Status = STATUS_EA_CORRUPT_ERROR; + + try_return( Status ); + } + + // + // Show that the Ea's for this file are consistant for this + // file handle. + // + + Ccb->EaModificationCount = Fcb->EaModificationCount; + + // + // We need to look up the attribute for the Ea information. + // If we don't find the attribute, then there are no EA's for + // this file. In that case we dummy up an ea list to use below. + // + + NtfsInitializeAttributeContext( &EaInfoAttr ); + + CleanupEaInfoAttr = TRUE; + + { + BOOLEAN EasOnFile; + + EasOnFile = FALSE; + + if (NtfsLookupAttributeByCode( IrpContext, + Fcb, + &Fcb->FileReference, + $EA_INFORMATION, + &EaInfoAttr)) { + + // + // As a sanity check we will check that the unpacked length is + // non-zero. It should always be so. + // + + EaInformation = (PEA_INFORMATION) NtfsAttributeValue( NtfsFoundAttribute( &EaInfoAttr )); + + if (EaInformation->UnpackedEaSize != 0) { + + EasOnFile = TRUE; + } + } + + if (EasOnFile) { + + // + // We obtain a pointer to the start of the existing Ea's for the file. + // + + CurrentEas = NtfsMapExistingEas( IrpContext, + Fcb, + &EaBcb, + &EaLength ); + + } else { + + CurrentEas = NULL; + EaLength = 0; + + DummyEaInformation.PackedEaSize = 0; + DummyEaInformation.NeedEaCount = 0; + DummyEaInformation.UnpackedEaSize = 0; + + EaInformation = &DummyEaInformation; + } + } + + // + // Let's clear the output buffer. + // + + RtlZeroMemory( EaBuffer, UserBufferLength ); + + // + // We now satisfy the user's request depending on whether he + // specified an Ea name list, an Ea index or restarting the + // search. + // + + // + // The user has supplied a list of Ea names. + // + + if (UserEaList != NULL) { + + Irp->IoStatus = NtfsQueryEaUserEaList( CurrentEas, + EaInformation, + EaBuffer, + UserBufferLength, + UserEaList, + ReturnSingleEntry ); + + // + // The user supplied an index into the Ea list. + // + + } else if (IndexSpecified) { + + Irp->IoStatus = NtfsQueryEaIndexSpecified( Ccb, + CurrentEas, + EaInformation, + EaBuffer, + UserBufferLength, + UserEaIndex, + ReturnSingleEntry ); + + // + // Else perform a simple scan, taking into account the restart + // flag and the position of the next Ea stored in the Ccb. + // + + } else { + + Irp->IoStatus = NtfsQueryEaSimpleScan( Ccb, + CurrentEas, + EaInformation, + EaBuffer, + UserBufferLength, + ReturnSingleEntry, + RestartScan + ? 0 + : Ccb->NextEaOffset ); + } + + Status = Irp->IoStatus.Status; + + try_exit: NOTHING; + } finally { + + DebugUnwind( NtfsCommonQueryEa ); + + // + // We cleanup any attribute contexts. + // + + if (CleanupEaInfoAttr) { + + NtfsCleanupAttributeContext( &EaInfoAttr ); + } + + // + // Unpin the stream file if pinned. + // + + NtfsUnpinBcb( &EaBcb ); + + // + // Release the Fcb. + // + + NtfsReleaseFcb( IrpContext, Fcb ); + + if (!AbnormalTermination()) { + + NtfsCompleteRequest( &IrpContext, &Irp, Status ); + } + + // + // And return to our caller + // + + DebugTrace( -1, Dbg, ("NtfsCommonQueryEa -> %08lx\n", Status) ); + } + + return Status; +} + + +NTSTATUS +NtfsCommonSetEa ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for set Ea called by both the fsd and fsp + threads. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + PFILE_OBJECT FileObject; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PSCB Scb; + PCCB Ccb; + + ULONG Offset; + + ATTRIBUTE_ENUMERATION_CONTEXT EaInfoAttr; + PEA_INFORMATION EaInformation; + + BOOLEAN PreviousEas; + + EA_LIST_HEADER EaList; + + PBCB EaBcb; + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + PAGED_CODE(); + + NtfsInitializeAttributeContext( &EaInfoAttr ); + + // + // Get the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace( +1, Dbg, ("NtfsCommonSetEa\n") ); + DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); + DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) ); + + // + // Extract and decode the file object + // + + FileObject = IrpSp->FileObject; + TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE ); + + // + // Initialize the IoStatus values. + // + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // Check that the file object is associated with either a user file or + // user directory open or an open by file ID. + // + + if ((Ccb == NULL) || + !FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE ) || + ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen))) { + + DebugTrace( 0, Dbg, ("Invalid file object\n") ); + NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace( -1, Dbg, ("NtfsCommonQueryEa -> %08lx\n", STATUS_INVALID_PARAMETER) ); + + return STATUS_INVALID_PARAMETER; + } + + // + // We must be waitable. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + Status = NtfsPostRequest( IrpContext, Irp ); + + DebugTrace( -1, Dbg, ("NtfsCommonSetEa -> %08lx\n", Status) ); + return Status; + } + + // + // Acquire exclusive access to the Fcb. + // + + NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, FALSE, FALSE ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + ULONG UserBufferLength; + PFILE_FULL_EA_INFORMATION Buffer; + + PFILE_FULL_EA_INFORMATION CurrentEas; + + // + // Reference the input parameters and initialize our local variables. + // + + UserBufferLength = IrpSp->Parameters.SetEa.Length; + + EaBcb = NULL; + Offset = 0; + + EaList.FullEa = NULL; + + // + // Map the user's Ea buffer. + // + + Buffer = NtfsMapUserBuffer( Irp ); + + // + // Check the user's buffer for validity. + // + + { + ULONG ErrorOffset; + + Status = IoCheckEaBufferValidity( Buffer, + UserBufferLength, + &ErrorOffset ); + + if (!NT_SUCCESS( Status )) { + + Irp->IoStatus.Information = ErrorOffset; + try_return( Status ); + } + } + + // + // Check if the file has existing Ea's. + // + + if (NtfsLookupAttributeByCode( IrpContext, + Fcb, + &Fcb->FileReference, + $EA_INFORMATION, + &EaInfoAttr)) { + + PreviousEas = TRUE; + + EaInformation = (PEA_INFORMATION) NtfsAttributeValue( NtfsFoundAttribute( &EaInfoAttr )); + + } else { + + PreviousEas = FALSE; + } + + // + // Sanity check. + // + + ASSERT( !PreviousEas || EaInformation->UnpackedEaSize != 0 ); + + // + // Initialize our Ea list structure depending on whether there + // were previous Ea's or not. + // + + if (PreviousEas) { + + // + // Copy the information out of the Ea information attribute. + // + + EaList.PackedEaSize = (ULONG) EaInformation->PackedEaSize; + EaList.NeedEaCount = EaInformation->NeedEaCount; + EaList.UnpackedEaSize = EaInformation->UnpackedEaSize; + + CurrentEas = NtfsMapExistingEas( IrpContext, + Fcb, + &EaBcb, + &EaList.BufferSize ); + + // + // The allocated size of the Ea buffer is the Unpacked length. + // + + EaList.FullEa = NtfsAllocatePool(PagedPool, EaList.BufferSize ); + + // + // Now copy the mapped Eas. + // + + RtlCopyMemory( EaList.FullEa, + CurrentEas, + EaList.BufferSize ); + + // + // Upin the stream file. + // + + NtfsUnpinBcb( &EaBcb ); + + } else { + + // + // Set this up as an empty list. + // + + EaList.PackedEaSize = 0; + EaList.NeedEaCount = 0; + EaList.UnpackedEaSize = 0; + EaList.BufferSize = 0; + EaList.FullEa = NULL; + } + + // + // Build the new ea list. + // + + Status = NtfsBuildEaList( IrpContext, + Vcb, + &EaList, + Buffer, + &Irp->IoStatus.Information ); + + if (!NT_SUCCESS( Status )) { + + try_return( Status ); + } + + // + // Replace the existing Eas. + // + + NtfsReplaceFileEas( IrpContext, Fcb, &EaList ); + + // + // Increment the Modification count for the Eas. + // + + Fcb->EaModificationCount++; + + // + // Update the information in the duplicate information and mark + // the Fcb as info modified. + // + + if (EaList.UnpackedEaSize == 0) { + + Fcb->Info.PackedEaSize = 0; + + } else { + + Fcb->Info.PackedEaSize = (USHORT) EaList.PackedEaSize; + } + + // + // Update the caller's Iosb. + // + + Irp->IoStatus.Information = 0; + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + // + // Check if there are transactions to cleanup. + // + + NtfsCleanupTransaction( IrpContext, Status, FALSE ); + + // + // Show that we changed the Ea's and also set the Ccb flag so we will + // update the time stamps. + // + + SetFlag( Ccb->Flags, + CCB_FLAG_UPDATE_LAST_CHANGE | CCB_FLAG_SET_ARCHIVE ); + + } finally { + + DebugUnwind( NtfsCommonSetEa ); + + // + // Free the in-memory copy of the Eas. + // + + if (EaList.FullEa != NULL) { + + NtfsFreePool( EaList.FullEa ); + } + + // + // Unpin the Bcb. + // + + NtfsUnpinBcb( &EaBcb ); + + // + // Cleanup any attribute contexts used. + // + + NtfsCleanupAttributeContext( &EaInfoAttr ); + + // + // Release the Fcb. + // + + NtfsReleaseFcb( IrpContext, Fcb ); + + // + // Complete the Irp. + // + + if (!AbnormalTermination()) { + + NtfsCompleteRequest( &IrpContext, &Irp, Status ); + } + + DebugTrace( -1, Dbg, ("NtfsCommonSetEa -> %08lx\n", Status) ); + } + + return Status; +} + + +// +// Local support routine +// + +VOID +NtfsAppendEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_LIST_HEADER EaListHeader, + IN PFILE_FULL_EA_INFORMATION FullEa, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine appends a new packed ea onto an existing ea list, + it also will allocate/dealloate pool as necessary to hold the ea list. + +Arguments: + + EaListHeader - Supplies a pointer the Ea list header structure. + + FullEa - Supplies a pointer to the new full ea that is to be appended + to the ea list. + + Vcb - Vcb for this volume. + +Return Value: + + None. + +--*/ + +{ + ULONG UnpackedEaLength; + STRING EaName; + PFILE_FULL_EA_INFORMATION ThisEa; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsAppendEa...\n") ); + + UnpackedEaLength = AlignedUnpackedEaSize( FullEa ); + + // + // As a quick check see if the computed packed ea size plus the + // current ea list size will overflow the buffer. + // + + if (UnpackedEaLength + EaListHeader->UnpackedEaSize > EaListHeader->BufferSize) { + + // + // We will overflow our current work buffer so allocate a larger + // one and copy over the current buffer + // + + PVOID Temp; + ULONG NewAllocationSize; + + DebugTrace( 0, Dbg, ("Allocate a new ea list buffer\n") ); + + // + // Compute a new size and allocate space. Always increase the + // allocation in cluster increments. + // + + NewAllocationSize = ClusterAlign( Vcb, + UnpackedEaLength + + EaListHeader->UnpackedEaSize ); + + Temp = NtfsAllocatePool(PagedPool, NewAllocationSize ); + + // + // Move over the existing ea list and zero the remaining space. + // + + RtlCopyMemory( Temp, + EaListHeader->FullEa, + EaListHeader->BufferSize ); + + RtlZeroMemory( Add2Ptr( Temp, EaListHeader->BufferSize ), + NewAllocationSize - EaListHeader->BufferSize ); + + // + // Deallocate the current Ea list and use the freshly allocated list. + // + + if (EaListHeader->FullEa != NULL) { + + NtfsFreePool( EaListHeader->FullEa ); + } + + EaListHeader->FullEa = Temp; + + EaListHeader->BufferSize = NewAllocationSize; + } + + // + // Determine if we need to increment our need ea changes count + // + + if (FlagOn( FullEa->Flags, FILE_NEED_EA )) { + + EaListHeader->NeedEaCount += 1; + } + + // + // Now copy over the ea. + // + // Before: + // UsedSize Allocated + // | | + // V V + // +xxxxxxxx+-----------------------------+ + // + // After: + // UsedSize Allocated + // | | + // V V + // +xxxxxxxx+yyyyyyyyyyyyyyyy+------------+ + // + + ThisEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( EaListHeader->FullEa, + EaListHeader->UnpackedEaSize ); + + RtlCopyMemory( ThisEa, + FullEa, + UnpackedEaLength ); + + // + // We always store the offset of this Ea in the next entry offset field. + // + + ThisEa->NextEntryOffset = UnpackedEaLength; + + // + // Upcase the name. + // + + EaName.MaximumLength = EaName.Length = ThisEa->EaNameLength; + EaName.Buffer = &ThisEa->EaName[0]; + + NtfsUpcaseEaName( &EaName, &EaName ); + + // + // Increment the used size in the ea list structure + // + + EaListHeader->UnpackedEaSize += UnpackedEaLength; + EaListHeader->PackedEaSize += PackedEaSize( FullEa ); + + // + // And return to our caller + // + + DebugTrace( -1, Dbg, ("NtfsAppendEa -> VOID\n") ); + + return; + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +// +// Local support routine +// + +VOID +NtfsDeleteEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_LIST_HEADER EaListHeader, + IN ULONG Offset + ) + +/*++ + +Routine Description: + + This routine deletes an individual packed ea from the supplied + ea list. + +Arguments: + + EaListHeader - Supplies a pointer to the Ea list header structure. + + Offset - Supplies the offset to the individual ea in the list to delete + +Return Value: + + None. + +--*/ + +{ + PFILE_FULL_EA_INFORMATION ThisEa; + ULONG UnpackedEaLength; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsDeletePackedEa, Offset = %08lx\n", Offset) ); + + // + // Get a reference to the Ea to delete. + // + + ThisEa = Add2Ptr( EaListHeader->FullEa, Offset ); + + // + // Determine if we need to decrement our need ea changes count + // + + if (FlagOn( ThisEa->Flags, FILE_NEED_EA )) { + + EaListHeader->NeedEaCount--; + } + + // + // Decrement the Ea size values. + // + + EaListHeader->PackedEaSize -= PackedEaSize( ThisEa ); + + UnpackedEaLength = AlignedUnpackedEaSize( ThisEa ); + EaListHeader->UnpackedEaSize -= UnpackedEaLength; + + // + // 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+UnpackedEaLength UsedSize Allocated + // | | | | + // V V V V + // +xxxxxxxx+yyyyyyyyyyyyyyyy+zzzzzzzzzzzzzzzzzz+------------+ + // + // After + // Offset UsedSize Allocated + // | | | + // V V V + // +xxxxxxxx+zzzzzzzzzzzzzzzzzz+-----------------------------+ + // + + RtlMoveMemory( ThisEa, + Add2Ptr( ThisEa, ThisEa->NextEntryOffset ), + EaListHeader->UnpackedEaSize - Offset ); + + // + // And zero out the remaing part of the ea list, to make things + // nice and more robust + // + + RtlZeroMemory( Add2Ptr( EaListHeader->FullEa, EaListHeader->UnpackedEaSize ), + UnpackedEaLength ); + + // + // And return to our caller + // + + DebugTrace( -1, Dbg, ("NtfsDeleteEa -> VOID\n") ); + + return; + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +// +// Local support routine +// + +BOOLEAN +NtfsLocateEaByName ( + IN PFILE_FULL_EA_INFORMATION FullEa, + IN ULONG EaBufferLength, + IN PSTRING EaName, + OUT PULONG Offset + ) + +/*++ + +Routine Description: + + This routine locates the offset for the next individual packed ea + inside of a ea list, given the name of the ea to locate. + +Arguments: + + FullEa - Pointer to the first Ea to look at. + + EaBufferLength - This is the ulong-aligned size of the Ea buffer. + + 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 ea exists in the list and FALSE + otherwise. + +--*/ + +{ + PFILE_FULL_EA_INFORMATION ThisEa; + STRING Name; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsLocateEaByName, EaName = %Z\n", EaName) ); + + // + // If the Ea list is NULL, there is nothing to do. + // + + if (FullEa == NULL) { + + DebugTrace( -1, Dbg, ("NtfsLocateEaByName: No work to do\n") ); + return FALSE; + } + + // + // For each ea in the list check its name against the + // ea name we're searching for + // + + *Offset = 0; + + // + // We assume there is at least one Ea in the list. + // + + do { + + ThisEa = Add2Ptr( FullEa, *Offset ); + + // + // Make a string out of the name in the Ea and compare it to the + // given string. + // + + RtlInitString( &Name, &ThisEa->EaName[0] ); + + if ( RtlCompareString( EaName, &Name, TRUE ) == 0 ) { + + DebugTrace( -1, Dbg, ("NtfsLocateEaByName -> TRUE, *Offset = %08lx\n", *Offset) ); + return TRUE; + } + + // + // Update the offset to get to the next Ea. + // + + *Offset += AlignedUnpackedEaSize( ThisEa ); + + } while ( *Offset < EaBufferLength ); + + // + // We've exhausted the ea list without finding a match so return false + // + + DebugTrace( -1, Dbg, ("NtfsLocateEaByName -> FALSE\n") ); + return FALSE; +} + + +// +// Local support routine. +// + +PFILE_FULL_EA_INFORMATION +NtfsMapExistingEas ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + OUT PBCB *EaBcb, + OUT PULONG EaLength + ) + +/*++ + +Routine Description: + + This routine maps the current Eas for the file, either through the + Mft record for the file if resident or the Scb for the non-resident + Eas. + +Arguments: + + Fcb - Pointer to the Fcb for the file whose Ea's are being queried. + + EaBcb - Pointer to the Bcb to use if we are mapping data in the + Ea attribute stream file. + + EaLength - Returns the length of the packed Eas in bytes. + +Return Value: + + PFILE_FULL_EA_INFORMATION - Pointer to the mapped attributes. + +--*/ + +{ + ATTRIBUTE_ENUMERATION_CONTEXT Context; + PFILE_FULL_EA_INFORMATION CurrentEas; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsMapExistingEas: Entered\n") ); + + // + // We start by looking up the Ea attribute. It better be there. + // + + NtfsInitializeAttributeContext( &Context ); + + if (!NtfsLookupAttributeByCode( IrpContext, + Fcb, + &Fcb->FileReference, + $EA, + &Context )) { + + // + // This is a disk corrupt error. + // + + DebugTrace( -1, Dbg, ("NtfsMapExistingEas: Corrupt disk\n") ); + + NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb ); + } + + try { + + NtfsMapAttributeValue( IrpContext, + Fcb, + (PVOID *)&CurrentEas, + EaLength, + EaBcb, + &Context ); + + } finally { + + NtfsCleanupAttributeContext( &Context ); + } + + DebugTrace( -1, Dbg, ("NtfsMapExistingEas: Exit\n") ); + + return CurrentEas; +} + + +// +// Local support routine. +// + +IO_STATUS_BLOCK +NtfsQueryEaUserEaList ( + IN PFILE_FULL_EA_INFORMATION CurrentEas, + IN PEA_INFORMATION EaInformation, + OUT PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG UserBufferLength, + IN PFILE_GET_EA_INFORMATION UserEaList, + IN BOOLEAN ReturnSingleEntry + ) + +/*++ + +Routine Description: + + This routine is the work routine for querying EAs given a list + of Ea's to search for. + +Arguments: + + CurrentEas - This is a pointer to the current Eas for the file + + EaInformation - This is a pointer to an Ea information attribute. + + EaBuffer - Supplies the buffer to receive the full eas + + UserBufferLength - Supplies the length, in bytes, of the user buffer + + UserEaList - Supplies the user specified ea name list + + ReturnSingleEntry - Indicates if we are to return a single entry or not + +Return Value: + + IO_STATUS_BLOCK - Receives the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + ULONG GeaOffset; + ULONG FeaOffset; + ULONG Offset; + + PFILE_FULL_EA_INFORMATION LastFullEa; + PFILE_FULL_EA_INFORMATION NextFullEa; + + PFILE_GET_EA_INFORMATION GetEa; + + BOOLEAN Overflow; + ULONG PrevEaPadding; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsQueryEaUserEaList: Entered\n") ); + + // + // Setup pointer in the output buffer so we can track the Ea being + // written to it and the last Ea written. + // + + LastFullEa = NULL; + + Overflow = FALSE; + + // + // Initialize our next offset value. + // + + GeaOffset = 0; + Offset = 0; + PrevEaPadding = 0; + + // + // Loop through all the entries in the user's ea list. + // + + while (TRUE) { + + STRING GeaName; + STRING OutputEaName; + ULONG RawEaSize; + + // + // Get the next entry in the user's list. + // + + GetEa = (PFILE_GET_EA_INFORMATION) Add2Ptr( UserEaList, GeaOffset ); + + // + // Make a string reference to the name and see if we can locate + // the ea by name. + // + + GeaName.MaximumLength = GeaName.Length = GetEa->EaNameLength; + GeaName.Buffer = &GetEa->EaName[0]; + + // + // Upcase the name so we can do a case-insensitive compare. + // + + NtfsUpcaseEaName( &GeaName, &GeaName ); + + // + // Check for a valid name. + // + + if (!NtfsIsEaNameValid( GeaName )) { + + DebugTrace( -1, Dbg, ("NtfsQueryEaUserEaList: Invalid Ea Name\n") ); + + Iosb.Information = GeaOffset; + Iosb.Status = STATUS_INVALID_EA_NAME; + return Iosb; + } + + GeaOffset += GetEa->NextEntryOffset; + + // + // If this is a duplicate name, then step over this entry. + // + + if (NtfsIsDuplicateGeaName( GetEa, UserEaList )) { + + continue; + } + + // + // Generate a pointer in the Ea buffer. + // + + NextFullEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( EaBuffer, Offset + PrevEaPadding ); + + // + // Try to find a matching Ea. + // If we couldn't, let's dummy up an Ea to give to the user. + // + + if (!NtfsLocateEaByName( CurrentEas, + EaInformation->UnpackedEaSize, + &GeaName, + &FeaOffset )) { + + // + // We were not able to locate the name therefore we must + // dummy up a entry for the query. The needed Ea size is + // the size of the name + 4 (next entry offset) + 1 (flags) + // + 1 (name length) + 2 (value length) + the name length + + // 1 (null byte). + // + + RawEaSize = 4+1+1+2+GetEa->EaNameLength+1; + + if ((RawEaSize + PrevEaPadding) > UserBufferLength) { + + Overflow = TRUE; + break; + } + + // + // Everything is going to work fine, so copy over the name, + // set the name length and zero out the rest of the ea. + // + + NextFullEa->NextEntryOffset = 0; + NextFullEa->Flags = 0; + NextFullEa->EaNameLength = GetEa->EaNameLength; + NextFullEa->EaValueLength = 0; + RtlCopyMemory( &NextFullEa->EaName[0], + &GetEa->EaName[0], + GetEa->EaNameLength ); + + // + // Upcase the name in the buffer. + // + + OutputEaName.MaximumLength = OutputEaName.Length = GeaName.Length; + OutputEaName.Buffer = NextFullEa->EaName; + + NtfsUpcaseEaName( &OutputEaName, &OutputEaName ); + + NextFullEa->EaName[GetEa->EaNameLength] = 0; + + // + // Otherwise return the Ea we found back to the user. + // + + } else { + + PFILE_FULL_EA_INFORMATION ThisEa; + + // + // Reference this ea. + // + + ThisEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( CurrentEas, FeaOffset ); + + // + // Check if this Ea can fit in the user's buffer. + // + + RawEaSize = RawUnpackedEaSize( ThisEa ); + + if (RawEaSize > (UserBufferLength - PrevEaPadding)) { + + Overflow = TRUE; + break; + } + + // + // Copy this ea to the user's buffer. + // + + RtlCopyMemory( NextFullEa, + ThisEa, + RawEaSize); + + NextFullEa->NextEntryOffset = 0; + } + + // + // Compute the next offset in the user's buffer. + // + + Offset += (RawEaSize + PrevEaPadding); + + // + // If we were to return a single entry then break out of our loop + // now + // + + if (ReturnSingleEntry) { + + break; + } + + // + // If we have a new Ea entry, go back and update the offset field + // of the previous Ea entry. + // + + if (LastFullEa != NULL) { + + LastFullEa->NextEntryOffset = PtrOffset( LastFullEa, NextFullEa ); + } + + // + // If we've exhausted the entries in the Get Ea list, then we are + // done. + // + + if (GetEa->NextEntryOffset == 0) { + + break; + } + + // + // Remember this as the previous ea value. Also update the buffer + // length values and the buffer offset values. + // + + LastFullEa = NextFullEa; + UserBufferLength -= (RawEaSize + PrevEaPadding); + + // + // Now remember the padding bytes needed for this call. + // + + PrevEaPadding = LongAlign( RawEaSize ) - RawEaSize; + } + + // + // If the Ea information won't fit in the user's buffer, then return + // an overflow status. + // + + if (Overflow) { + + Iosb.Information = 0; + Iosb.Status = STATUS_BUFFER_OVERFLOW; + + // + // Otherwise return the length of the data returned. + // + + } else { + + // + // Return the length of the buffer filled and a success + // status. + // + + Iosb.Information = Offset; + Iosb.Status = STATUS_SUCCESS; + } + + DebugTrace( 0, Dbg, ("Status -> %08lx\n", Iosb.Status) ); + DebugTrace( 0, Dbg, ("Information -> %08lx\n", Iosb.Information) ); + DebugTrace( -1, Dbg, ("NtfsQueryEaUserEaList: Exit\n") ); + + return Iosb; +} + + +// +// Local support routine +// + +IO_STATUS_BLOCK +NtfsQueryEaIndexSpecified ( + OUT PCCB Ccb, + IN PFILE_FULL_EA_INFORMATION CurrentEas, + IN PEA_INFORMATION EaInformation, + OUT PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG UserBufferLength, + IN ULONG UserEaIndex, + IN BOOLEAN ReturnSingleEntry + ) + +/*++ + +Routine Description: + + This routine is the work routine for querying EAs given an ea index + +Arguments: + + Ccb - This is the Ccb for the caller. + + CurrentEas - This is a pointer to the current Eas for the file. + + EaInformation - This is a pointer to an Ea information attribute. + + EaBuffer - Supplies the buffer to receive the full eas + + UserBufferLength - Supplies the length, in bytes, of the user buffer + + UserEaIndex - This is the Index for the first ea to return. The value + 1 indicates the first ea of the file. + + ReturnSingleEntry - Indicates if we are to return a single entry or not + +Return Value: + + IO_STATUS_BLOCK - Receives the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + ULONG i; + ULONG Offset; + PFILE_FULL_EA_INFORMATION ThisEa; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsQueryEaIndexSpecified: Entered\n") ); + + i = 1; + Offset = 0; + ThisEa = NULL; + + // + // If the index value is zero, there are no Eas to return. + // + + if (UserEaIndex == 0 + || EaInformation->UnpackedEaSize == 0) { + + DebugTrace( -1, Dbg, ("NtfsQueryEaIndexSpecified: Non-existant entry\n") ); + + Iosb.Information = 0; + Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY; + + return Iosb; + } + + // + // Walk through the CurrentEas until we find the starting Ea offset. + // + + while (i < UserEaIndex + && Offset < EaInformation->UnpackedEaSize) { + + ThisEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( CurrentEas, Offset ); + + Offset += AlignedUnpackedEaSize( ThisEa ); + + i += 1; + } + + if (Offset >= EaInformation->UnpackedEaSize) { + + // + // If we just passed the last Ea, we will return STATUS_NO_MORE_EAS. + // This is for the caller who may be enumerating the Eas. + // + + if (i == UserEaIndex) { + + Iosb.Status = STATUS_NO_MORE_EAS; + + // + // Otherwise we report that this is a bad ea index. + // + + } else { + + Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY; + } + + DebugTrace( -1, Dbg, ("NtfsQueryEaIndexSpecified -> %08lx\n", Iosb.Status) ); + return Iosb; + } + + // + // We now have the offset of the first Ea to return to the user. + // We simply call our EaSimpleScan routine to do the actual work. + // + + Iosb = NtfsQueryEaSimpleScan( Ccb, + CurrentEas, + EaInformation, + EaBuffer, + UserBufferLength, + ReturnSingleEntry, + Offset ); + + DebugTrace( -1, Dbg, ("NtfsQueryEaIndexSpecified: Exit\n") ); + + return Iosb; +} + + +// +// Local support routine +// + +IO_STATUS_BLOCK +NtfsQueryEaSimpleScan ( + OUT PCCB Ccb, + IN PFILE_FULL_EA_INFORMATION CurrentEas, + IN PEA_INFORMATION EaInformation, + OUT PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG UserBufferLength, + IN BOOLEAN ReturnSingleEntry, + IN ULONG StartingOffset + ) + +/*++ + +Routine Description: + + This routine is the work routine for querying EAs starting from a given + offset within the Ea attribute. + +Arguments: + + Ccb - This is the Ccb for the caller. + + CurrentEas - This is a pointer to the current Eas for the file. + + EaInformation - This is a pointer to an Ea information attribute. + + EaBuffer - Supplies the buffer to receive the full eas + + UserBufferLength - Supplies the length, in bytes, of the user buffer + + ReturnSingleEntry - Indicates if we are to return a single entry or not + + StartingOffset - Supplies the offset of the first Ea to return + +Return Value: + + IO_STATUS_BLOCK - Receives the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + PFILE_FULL_EA_INFORMATION LastFullEa; + PFILE_FULL_EA_INFORMATION NextFullEa; + PFILE_FULL_EA_INFORMATION ThisEa; + + BOOLEAN BufferOverflow = FALSE; + + ULONG BufferOffset; + ULONG PrevEaPadding; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsQueryEaSimpleScan: Entered\n") ); + + // + // Initialize our Ea pointers and the offsets into the user buffer + // and our Ea buffer. + // + + LastFullEa = NULL; + BufferOffset = 0; + PrevEaPadding = 0; + + // + // Loop until the Ea offset is beyond the valid range of Eas. + // + + while (StartingOffset < EaInformation->UnpackedEaSize) { + + ULONG EaSize; + + // + // Reference the next EA to return. + // + + ThisEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( CurrentEas, StartingOffset); + + // + // If the size of this Ea is greater than the remaining buffer size, + // we exit the loop. We need to remember to include any padding bytes + // from the previous Eas. + // + + EaSize = RawUnpackedEaSize( ThisEa ); + + if ((EaSize + PrevEaPadding) > UserBufferLength) { + + BufferOverflow = TRUE; + break; + } + + // + // Copy the Ea into the user's buffer. + // + + BufferOffset += PrevEaPadding; + + NextFullEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( EaBuffer, BufferOffset ); + + RtlCopyMemory( NextFullEa, ThisEa, EaSize ); + + // + // Move to the next Ea. + // + + LastFullEa = NextFullEa; + UserBufferLength -= (EaSize + PrevEaPadding); + BufferOffset += EaSize; + + StartingOffset += LongAlign( EaSize ); + + // + // Remember the padding needed for this entry. + // + + PrevEaPadding = LongAlign( EaSize ) - EaSize; + + // + // If the user only wanted one entry, exit now. + // + + if (ReturnSingleEntry) { + + break; + } + } + + // + // If we didn't find any entries, it could be because there were no + // more to find or that we ran out of buffer space. + // + + if (LastFullEa == NULL) { + + Iosb.Information = 0; + + // + // We were not able to return a single ea entry, now we need to find + // out if it is because we didn't have an entry to return or the + // buffer is too small. If the Offset variable is less than + // the size of the Ea attribute, then the user buffer is too small. + // + + if (EaInformation->UnpackedEaSize == 0) { + + Iosb.Status = STATUS_NO_EAS_ON_FILE; + + } else if (StartingOffset >= EaInformation->UnpackedEaSize) { + + Iosb.Status = STATUS_NO_MORE_EAS; + + } else { + + Iosb.Status = STATUS_BUFFER_TOO_SMALL; + } + + // + // Otherwise we have returned some Ea's. Update the Iosb to return. + // + + } else { + + // + // Update the Ccb to show where to start the next search. + // + + Ccb->NextEaOffset = StartingOffset; + + // + // Zero the next entry field of the last Ea. + // + + LastFullEa->NextEntryOffset = 0; + + // + // Now update the Iosb. + // + + Iosb.Information = BufferOffset; + + // + // If there are more to return, report the buffer was too small. + // Otherwise return STATUS_SUCCESS. + // + + if (BufferOverflow) { + + Iosb.Status = STATUS_BUFFER_OVERFLOW; + + } else { + + Iosb.Status = STATUS_SUCCESS; + } + } + + DebugTrace( -1, Dbg, ("NtfsQueryEaSimpleScan: Exit\n") ); + + return Iosb; +} + + +// +// Local support routine +// + +BOOLEAN +NtfsIsDuplicateGeaName ( + IN PFILE_GET_EA_INFORMATION GetEa, + IN PFILE_GET_EA_INFORMATION UserGeaBuffer + ) + +/*++ + +Routine Description: + + This routine walks through a list of Gea names to find a duplicate name. + 'GetEa' is an actual position in the list, 'UserGeaBuffer' is the beginning + of the list. We are only interested in + previous matching ea names, as the ea information for that ea name + would have been returned with the previous instance. + +Arguments: + + GetEa - Supplies the Ea name structure for the ea name to match. + + UserGeaBuffer - Supplies a pointer to the user buffer with the list + of ea names to search for. + +Return Value: + + BOOLEAN - TRUE if a previous match is found, FALSE otherwise. + +--*/ + +{ + BOOLEAN DuplicateFound; + STRING GeaString; + + PFILE_GET_EA_INFORMATION ThisGetEa; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsIsDuplicateGeaName: Entered\n") ); + + // + // Set up the string structure. + // + + GeaString.MaximumLength = GeaString.Length = GetEa->EaNameLength; + GeaString.Buffer = &GetEa->EaName[0]; + + DuplicateFound = FALSE; + + ThisGetEa = UserGeaBuffer; + + // + // We loop until we reach the given Gea or a match is found. + // + + while (ThisGetEa != GetEa) { + + STRING ThisGea; + + // + // Create a string structure for the current Gea. + // + + ThisGea.MaximumLength = ThisGea.Length = ThisGetEa->EaNameLength; + ThisGea.Buffer = &ThisGetEa->EaName[0]; + + // + // Check if the Gea names match, exit if they do. + // + + if (NtfsAreEaNamesEqual( &GeaString, + &ThisGea )) { + + DuplicateFound = TRUE; + break; + } + + // + // Move to the next Gea entry. + // + + ThisGetEa = (PFILE_GET_EA_INFORMATION) Add2Ptr( ThisGetEa, + ThisGetEa->NextEntryOffset ); + } + + DebugTrace( -1, Dbg, ("NtfsIsDuplicateGeaName: Exit\n") ); + + return DuplicateFound; +} + + +// +// Local support routine +// + +NTSTATUS +NtfsBuildEaList ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN OUT PEA_LIST_HEADER EaListHeader, + IN PFILE_FULL_EA_INFORMATION UserEaList, + OUT PULONG ErrorOffset + ) + +/*++ + +Routine Description: + + This routine is called to build an up-to-date Ea list based on the + given existing Ea list and the user-specified Ea list. + +Arguments: + + Vcb - The Vcb for the volume. + + EaListHeader - This is the Ea list to modify. + + UserEaList - This is the user specified Ea list. + + ErrorOffset - Supplies the address to store the offset of an invalid + Ea in the user's list. + +Return Value: + + NTSTATUS - The result of modifying the Ea list. + +--*/ + +{ + NTSTATUS Status; + BOOLEAN MoreEas; + ULONG Offset; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsBuildEaList: Entered\n") ); + + Status = STATUS_SUCCESS; + Offset = 0; + + // + // Now for each full ea in the input user buffer we do the specified operation + // on the ea. + // + + do { + + STRING EaName; + ULONG EaOffset; + + PFILE_FULL_EA_INFORMATION ThisEa; + + ThisEa = (PFILE_FULL_EA_INFORMATION) Add2Ptr( UserEaList, Offset ); + + // + // Create a string out of the name in the user's Ea. + // + + EaName.MaximumLength = EaName.Length = ThisEa->EaNameLength; + EaName.Buffer = &ThisEa->EaName[0]; + + // + // If the Ea isn't valid, return error offset to caller. + // + + if (!NtfsIsEaNameValid( EaName )) { + + *ErrorOffset = Offset; + Status = STATUS_INVALID_EA_NAME; + + break; + } + + // + // Verify that no invalid ea flags are set. + // + + if (ThisEa->Flags != 0 + && ThisEa->Flags != FILE_NEED_EA) { + + *ErrorOffset = Offset; + Status = STATUS_INVALID_EA_NAME; + + break; + } + + // + // If we can find the name in the Ea set, we remove it. + // + + if (NtfsLocateEaByName( EaListHeader->FullEa, + EaListHeader->UnpackedEaSize, + &EaName, + &EaOffset )) { + + NtfsDeleteEa( IrpContext, + EaListHeader, + EaOffset ); + } + + // + // If the user specified a non-zero value length, we add this + // ea to the in memory Ea list. + // + + if (ThisEa->EaValueLength != 0) { + + NtfsAppendEa( IrpContext, + EaListHeader, + ThisEa, + Vcb ); + } + + // + // Move to the next Ea in the list. + // + + Offset += AlignedUnpackedEaSize( ThisEa ); + + MoreEas = (BOOLEAN) (ThisEa->NextEntryOffset != 0); + + } while( MoreEas ); + + // + // First we check that the packed size of the Eas does not exceed the + // maximum value. We have to reserve the 4 bytes for the OS/2 list + // header. + // + + if (NT_SUCCESS( Status )) { + + if (EaListHeader->PackedEaSize > (MAXIMUM_EA_SIZE - 4)) { + + Status = STATUS_EA_TOO_LARGE; + } + } + + DebugTrace( -1, Dbg, ("NtfsBuildEaList: Exit\n") ); + + return Status; +} + + +// +// Local support routine +// + +VOID +NtfsReplaceFileEas ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PEA_LIST_HEADER EaList + ) + +/*++ + +Routine Description: + + This routine will replace an existing Ea list with a new Ea list. It + correctly handles the case where there was no previous Eas and where we + are removing all of the previous EAs. + +Arguments: + + Fcb - Fcb for the file with the EAs + + EaList - This contains the modified Ea list. + +Return Value: + + None. + +--*/ + +{ + EA_INFORMATION ThisEaInformation; + ATTRIBUTE_ENUMERATION_CONTEXT Context; + PSCB EaScb; + BOOLEAN EaScbAcquired = FALSE; + + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsReplaceFileEas: Entered\n") ); + + ThisEaInformation.PackedEaSize = (USHORT) EaList->PackedEaSize; + ThisEaInformation.UnpackedEaSize = EaList->UnpackedEaSize; + ThisEaInformation.NeedEaCount = EaList->NeedEaCount; + + NtfsInitializeAttributeContext( &Context ); + + // + // First we handle $EA_INFORMATION and then the $EA attribute in the + // same fashion. + // + + try { + + // + // Lookup the $EA_INFORMATION attribute. If it does not exist then we + // will need to create one. + // + + if (!NtfsLookupAttributeByCode( IrpContext, + Fcb, + &Fcb->FileReference, + $EA_INFORMATION, + &Context )) { + + if (EaList->UnpackedEaSize != 0) { + + DebugTrace( 0, Dbg, ("Create a new $EA_INFORMATION attribute\n") ); + + NtfsCleanupAttributeContext( &Context ); + NtfsInitializeAttributeContext( &Context ); + + NtfsCreateAttributeWithValue( IrpContext, + Fcb, + $EA_INFORMATION, + NULL, // attribute name + &ThisEaInformation, + sizeof(EA_INFORMATION), + 0, // attribute flags + NULL, // where indexed + TRUE, // logit + &Context ); + } + + } else { + + // + // If it exists, and we are writing an EA, then we have to update it. + // + + if (EaList->UnpackedEaSize != 0) { + + DebugTrace( 0, Dbg, ("Change an existing $EA_INFORMATION attribute\n") ); + + NtfsChangeAttributeValue( IrpContext, + Fcb, + 0, // Value offset + &ThisEaInformation, + sizeof(EA_INFORMATION), + TRUE, // SetNewLength + FALSE, // LogNonResidentToo + FALSE, // CreateSectionUnderway + FALSE, + &Context ); + + // + // If it exists, but our new length is zero, then delete it. + // + + } else { + + DebugTrace( 0, Dbg, ("Delete existing $EA_INFORMATION attribute\n") ); + + NtfsDeleteAttributeRecord( IrpContext, + Fcb, + TRUE, + FALSE, + &Context ); + } + } + + // + // Now we will cleanup and reinitialize the context for reuse. + // + + NtfsCleanupAttributeContext( &Context ); + NtfsInitializeAttributeContext( &Context ); + + // + // Lookup the $EA attribute. If it does not exist then we will need to create + // one. + // + + if (!NtfsLookupAttributeByCode( IrpContext, + Fcb, + &Fcb->FileReference, + $EA, + &Context )) { + + if (EaList->UnpackedEaSize != 0) { + + DebugTrace( 0, Dbg, ("Create a new $EA attribute\n") ); + + NtfsCleanupAttributeContext( &Context ); + NtfsInitializeAttributeContext( &Context ); + + NtfsCreateAttributeWithValue( IrpContext, + Fcb, + $EA, + NULL, // attribute name + EaList->FullEa, + EaList->UnpackedEaSize, + 0, // attribute flags + NULL, // where indexed + TRUE, // logit + &Context ); + } + + } else { + + // + // If it exists, and we are writing an EA, then we have to update it. + // + + if (EaList->UnpackedEaSize != 0) { + + DebugTrace( 0, Dbg, ("Change an existing $EA attribute\n") ); + + NtfsChangeAttributeValue( IrpContext, + Fcb, + 0, // Value offset + EaList->FullEa, + EaList->UnpackedEaSize, + TRUE, // SetNewLength + FALSE, // LogNonResidentToo + FALSE, // CreateSectionUnderway + FALSE, + &Context ); + + // + // If it exists, but our new length is zero, then delete it. + // + + } else { + + DebugTrace( 0, Dbg, ("Delete existing $EA attribute\n") ); + + // + // If the stream is non-resident then get hold of an + // Scb for this. + // + + if (!NtfsIsAttributeResident( NtfsFoundAttribute( &Context ))) { + + EaScb = NtfsCreateScb( IrpContext, + Fcb, + $EA, + &NtfsEmptyString, + FALSE, + NULL ); + + NtfsAcquireExclusiveScb( IrpContext, EaScb ); + EaScbAcquired = TRUE; + } + + NtfsDeleteAttributeRecord( IrpContext, + Fcb, + TRUE, + FALSE, + &Context ); + + // + // If we have acquired the Scb then knock the sizes back + // to zero. + // + + if (EaScbAcquired) { + + EaScb->Header.FileSize = + EaScb->Header.ValidDataLength = + EaScb->Header.AllocationSize = Li0; + + SetFlag( EaScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED ); + } + } + } + + // + // Increment the Modification count for the Eas. + // + + Fcb->EaModificationCount++; + + if (EaList->UnpackedEaSize == 0) { + + Fcb->Info.PackedEaSize = 0; + + } else { + + Fcb->Info.PackedEaSize = (USHORT) EaList->PackedEaSize; + } + + SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_EA_SIZE ); + + } finally { + + DebugUnwind( NtfsReplaceFileEas ); + + if (EaScbAcquired) { + + NtfsReleaseScb( IrpContext, EaScb ); + } + + // + // Cleanup our attribute enumeration context + // + + NtfsCleanupAttributeContext( &Context ); + } + + DebugTrace( -1, Dbg, ("NtfsReplaceFileEas: Exit\n") ); + + return; +} + + +BOOLEAN +NtfsIsEaNameValid ( + IN 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(); + + // + // 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; +} + |