summaryrefslogtreecommitdiffstats
path: root/private/ntos/cntfs/ea.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/cntfs/ea.c
downloadNT4.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.c2748
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;
+}
+