2018 lines
54 KiB
C
2018 lines
54 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Ea.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the EA routines for Fat called by
|
||
the dispatch driver.
|
||
|
||
// @@BEGIN_DDKSPLIT
|
||
|
||
Author:
|
||
|
||
Gary Kimura [GaryKi] 12-Apr-1990
|
||
|
||
Revision History:
|
||
|
||
// @@END_DDKSPLIT
|
||
|
||
--*/
|
||
|
||
#include "FatProcs.h"
|
||
|
||
//
|
||
// The local debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_EA)
|
||
|
||
//
|
||
// Local procedure prototypes
|
||
//
|
||
|
||
IO_STATUS_BLOCK
|
||
FatQueryEaUserEaList (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
OUT PCCB Ccb,
|
||
IN PPACKED_EA FirstPackedEa,
|
||
IN ULONG PackedEasLength,
|
||
OUT PUCHAR UserBuffer,
|
||
IN ULONG UserBufferLength,
|
||
IN PUCHAR UserEaList,
|
||
IN ULONG UserEaListLength,
|
||
IN BOOLEAN ReturnSingleEntry
|
||
);
|
||
|
||
IO_STATUS_BLOCK
|
||
FatQueryEaIndexSpecified (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
OUT PCCB Ccb,
|
||
IN PPACKED_EA FirstPackedEa,
|
||
IN ULONG PackedEasLength,
|
||
OUT PUCHAR UserBuffer,
|
||
IN ULONG UserBufferLength,
|
||
IN ULONG UserEaIndex,
|
||
IN BOOLEAN ReturnSingleEntry
|
||
);
|
||
|
||
IO_STATUS_BLOCK
|
||
FatQueryEaSimpleScan (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
OUT PCCB Ccb,
|
||
IN PPACKED_EA FirstPackedEa,
|
||
IN ULONG PackedEasLength,
|
||
OUT PUCHAR UserBuffer,
|
||
IN ULONG UserBufferLength,
|
||
IN BOOLEAN ReturnSingleEntry,
|
||
ULONG StartOffset
|
||
);
|
||
|
||
BOOLEAN
|
||
FatIsDuplicateEaName (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFILE_GET_EA_INFORMATION GetEa,
|
||
IN PUCHAR UserBuffer
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, FatCommonQueryEa)
|
||
#pragma alloc_text(PAGE, FatCommonSetEa)
|
||
#pragma alloc_text(PAGE, FatFsdQueryEa)
|
||
#pragma alloc_text(PAGE, FatFsdSetEa)
|
||
#pragma alloc_text(PAGE, FatIsDuplicateEaName)
|
||
#pragma alloc_text(PAGE, FatQueryEaIndexSpecified)
|
||
#pragma alloc_text(PAGE, FatQueryEaSimpleScan)
|
||
#pragma alloc_text(PAGE, FatQueryEaUserEaList)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
FatFsdQueryEa (
|
||
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the Fsd part of the NtQueryEa API
|
||
call.
|
||
|
||
Arguments:
|
||
|
||
VolumeDeviceObject - Supplies the volume device object where the file
|
||
being queried exists.
|
||
|
||
Irp - Supplies the Irp being processed.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The FSD status for the Irp.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PIRP_CONTEXT IrpContext = NULL;
|
||
|
||
BOOLEAN TopLevel;
|
||
|
||
DebugTrace(+1, Dbg, "FatFsdQueryEa\n", 0);
|
||
|
||
//
|
||
// Call the common query routine, with blocking allowed if synchronous
|
||
//
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
TopLevel = FatIsIrpTopLevel( Irp );
|
||
|
||
try {
|
||
|
||
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
|
||
|
||
Status = FatCommonQueryEa( IrpContext, Irp );
|
||
|
||
} except(FatExceptionFilter( 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 = FatProcessException( IrpContext, Irp, GetExceptionCode() );
|
||
}
|
||
|
||
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
|
||
|
||
FsRtlExitFileSystem();
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "FatFsdQueryEa -> %08lx\n", Status);
|
||
|
||
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
FatFsdSetEa (
|
||
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the FSD part of the NtSetEa API
|
||
call.
|
||
|
||
Arguments:
|
||
|
||
VolumeDeviceObject - Supplies the volume device object where the file
|
||
being set exists.
|
||
|
||
Irp - Supplies the Irp being processed.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The FSD status for the Irp.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PIRP_CONTEXT IrpContext = NULL;
|
||
|
||
BOOLEAN TopLevel;
|
||
|
||
DebugTrace(+1, Dbg, "FatFsdSetEa\n", 0);
|
||
|
||
//
|
||
// Call the common set routine, with blocking allowed if synchronous
|
||
//
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
TopLevel = FatIsIrpTopLevel( Irp );
|
||
|
||
try {
|
||
|
||
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
|
||
|
||
Status = FatCommonSetEa( IrpContext, Irp );
|
||
|
||
} except(FatExceptionFilter( 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 = FatProcessException( IrpContext, Irp, GetExceptionCode() );
|
||
}
|
||
|
||
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
|
||
|
||
FsRtlExitFileSystem();
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "FatFsdSetEa -> %08lx\n", Status);
|
||
|
||
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
FatCommonQueryEa (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common routine for querying File ea called by both
|
||
the fsd and fsp threads.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
NTSTATUS Status;
|
||
|
||
PUCHAR Buffer;
|
||
ULONG UserBufferLength;
|
||
|
||
PUCHAR UserEaList;
|
||
ULONG UserEaListLength;
|
||
ULONG UserEaIndex;
|
||
BOOLEAN RestartScan;
|
||
BOOLEAN ReturnSingleEntry;
|
||
BOOLEAN IndexSpecified;
|
||
|
||
PVCB Vcb;
|
||
PCCB Ccb;
|
||
|
||
PFCB Fcb;
|
||
PDIRENT Dirent;
|
||
PBCB Bcb;
|
||
|
||
PDIRENT EaDirent;
|
||
PBCB EaBcb;
|
||
BOOLEAN LockedEaFcb;
|
||
|
||
PEA_SET_HEADER EaSetHeader;
|
||
EA_RANGE EaSetRange;
|
||
|
||
USHORT ExtendedAttributes;
|
||
|
||
//
|
||
// Get the current Irp stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatCommonQueryEa...\n", 0);
|
||
DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
|
||
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));
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
//
|
||
// Check that the file object is associated with either a user file
|
||
// or directory open. We don't allow Ea operations on the root
|
||
// directory.
|
||
//
|
||
|
||
{
|
||
TYPE_OF_OPEN OpenType;
|
||
|
||
if (((OpenType = FatDecodeFileObject( IrpSp->FileObject,
|
||
&Vcb,
|
||
&Fcb,
|
||
&Ccb )) != UserFileOpen
|
||
&& OpenType != UserDirectoryOpen) ||
|
||
|
||
(NodeType( Fcb )) == FAT_NTC_ROOT_DCB) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg,
|
||
"FatCommonQueryEa -> %08lx\n",
|
||
STATUS_INVALID_PARAMETER);
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Fat32 does not support ea's.
|
||
//
|
||
|
||
if (FatIsFat32(Vcb)) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_EAS_NOT_SUPPORTED );
|
||
DebugTrace(-1, Dbg,
|
||
"FatCommonQueryEa -> %08lx\n",
|
||
STATUS_EAS_NOT_SUPPORTED);
|
||
return STATUS_EAS_NOT_SUPPORTED;
|
||
}
|
||
|
||
//
|
||
// Acquire shared access to the Fcb and enqueue the Irp if we didn't
|
||
// get access.
|
||
//
|
||
|
||
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
|
||
|
||
DebugTrace(0, Dbg, "FatCommonQueryEa: Thread can't wait\n", 0);
|
||
|
||
Status = FatFsdPostRequest( IrpContext, Irp );
|
||
|
||
DebugTrace(-1, Dbg, "FatCommonQueryEa -> %08lx\n", Status );
|
||
|
||
return Status;
|
||
}
|
||
|
||
FatAcquireSharedFcb( IrpContext, Fcb );
|
||
|
||
//
|
||
// Reference our input parameters to make things easier
|
||
//
|
||
|
||
UserBufferLength = IrpSp->Parameters.QueryEa.Length;
|
||
UserEaList = 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 values.
|
||
//
|
||
|
||
LockedEaFcb = FALSE;
|
||
Bcb = NULL;
|
||
EaBcb = NULL;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
|
||
|
||
try {
|
||
|
||
PPACKED_EA FirstPackedEa;
|
||
ULONG PackedEasLength;
|
||
|
||
Buffer = FatMapUserBuffer( IrpContext, Irp );
|
||
|
||
//
|
||
// We verify that the Fcb is still valid.
|
||
//
|
||
|
||
FatVerifyFcb( IrpContext, Fcb );
|
||
|
||
//
|
||
// We need to get the dirent for the Fcb to recover the Ea handle.
|
||
//
|
||
|
||
FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
|
||
|
||
//
|
||
// 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->OffsetOfNextEaToReturn != 0
|
||
&& !IndexSpecified
|
||
&& !RestartScan
|
||
&& Fcb->EaModificationCount != Ccb->EaModificationCount) {
|
||
|
||
DebugTrace(0, Dbg,
|
||
"FatCommonQueryEa: Ea file in unknown state\n", 0);
|
||
|
||
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;
|
||
|
||
//
|
||
// If the handle value is 0, then the file has no Eas. We dummy up
|
||
// an ea list to use below.
|
||
//
|
||
|
||
ExtendedAttributes = Dirent->ExtendedAttributes;
|
||
|
||
FatUnpinBcb( IrpContext, Bcb );
|
||
|
||
if (ExtendedAttributes == 0) {
|
||
|
||
DebugTrace(0, Dbg,
|
||
"FatCommonQueryEa: Zero handle, no Ea's for this file\n", 0);
|
||
|
||
FirstPackedEa = (PPACKED_EA) NULL;
|
||
|
||
PackedEasLength = 0;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We need to get the Ea file for this volume. If the
|
||
// operation doesn't complete due to blocking, then queue the
|
||
// Irp to the Fsp.
|
||
//
|
||
|
||
FatGetEaFile( IrpContext,
|
||
Vcb,
|
||
&EaDirent,
|
||
&EaBcb,
|
||
FALSE,
|
||
FALSE );
|
||
|
||
LockedEaFcb = TRUE;
|
||
|
||
//
|
||
// If the above operation completed and the Ea file did not exist,
|
||
// the disk has been corrupted. There is an existing Ea handle
|
||
// without any Ea data.
|
||
//
|
||
|
||
if (Vcb->VirtualEaFile == NULL) {
|
||
|
||
DebugTrace(0, Dbg,
|
||
"FatCommonQueryEa: No Ea file found when expected\n", 0);
|
||
|
||
Status = STATUS_NO_EAS_ON_FILE;
|
||
|
||
try_return( Status );
|
||
}
|
||
|
||
//
|
||
// We need to try to get the Ea set for the desired file. If
|
||
// blocking is necessary then we'll post the request to the Fsp.
|
||
//
|
||
|
||
FatReadEaSet( IrpContext,
|
||
Vcb,
|
||
ExtendedAttributes,
|
||
&Fcb->ShortName.Name.Oem,
|
||
TRUE,
|
||
&EaSetRange );
|
||
|
||
EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
|
||
|
||
//
|
||
// Find the start and length of the Eas.
|
||
//
|
||
|
||
FirstPackedEa = (PPACKED_EA) EaSetHeader->PackedEas;
|
||
|
||
PackedEasLength = GetcbList( EaSetHeader ) - 4;
|
||
}
|
||
|
||
//
|
||
// Protect our access to the user buffer since IO dosn't do this
|
||
// for us in this path unless we had specified that our driver
|
||
// requires buffering for these large requests. We don't, so ...
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Let's clear the output buffer.
|
||
//
|
||
|
||
RtlZeroMemory( Buffer, 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 = FatQueryEaUserEaList( IrpContext,
|
||
Ccb,
|
||
FirstPackedEa,
|
||
PackedEasLength,
|
||
Buffer,
|
||
UserBufferLength,
|
||
UserEaList,
|
||
UserEaListLength,
|
||
ReturnSingleEntry );
|
||
|
||
//
|
||
// The user supplied an index into the Ea list.
|
||
//
|
||
|
||
} else if (IndexSpecified) {
|
||
|
||
Irp->IoStatus = FatQueryEaIndexSpecified( IrpContext,
|
||
Ccb,
|
||
FirstPackedEa,
|
||
PackedEasLength,
|
||
Buffer,
|
||
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 = FatQueryEaSimpleScan( IrpContext,
|
||
Ccb,
|
||
FirstPackedEa,
|
||
PackedEasLength,
|
||
Buffer,
|
||
UserBufferLength,
|
||
ReturnSingleEntry,
|
||
RestartScan
|
||
? 0
|
||
: Ccb->OffsetOfNextEaToReturn );
|
||
}
|
||
|
||
} except (!FsRtlIsNtstatusExpected(GetExceptionCode()) ?
|
||
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
|
||
|
||
//
|
||
// We must have had a problem filling in the user's buffer, so fail.
|
||
//
|
||
|
||
Irp->IoStatus.Status = GetExceptionCode();
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
|
||
Status = Irp->IoStatus.Status;
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
DebugUnwind( FatCommonQueryEa );
|
||
|
||
//
|
||
// Release the Fcb for the file object, and the Ea Fcb if
|
||
// successfully locked.
|
||
//
|
||
|
||
FatReleaseFcb( IrpContext, Fcb );
|
||
|
||
if (LockedEaFcb) {
|
||
|
||
FatReleaseFcb( IrpContext, Vcb->EaFcb );
|
||
}
|
||
|
||
//
|
||
// Unpin the dirents for the Fcb, EaFcb and EaSetFcb if necessary.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, Bcb );
|
||
FatUnpinBcb( IrpContext, EaBcb );
|
||
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
|
||
if (!AbnormalTermination()) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatCommonQueryEa -> %08lx\n", Status);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
FatCommonSetEa (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the common Set Ea File Api called by the
|
||
the Fsd and Fsp threads
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The appropriate status for the Irp
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
NTSTATUS Status;
|
||
|
||
USHORT ExtendedAttributes;
|
||
|
||
PUCHAR Buffer;
|
||
ULONG UserBufferLength;
|
||
|
||
PVCB Vcb;
|
||
PCCB Ccb;
|
||
|
||
PFCB Fcb;
|
||
PDIRENT Dirent;
|
||
PBCB Bcb = NULL;
|
||
|
||
PDIRENT EaDirent = NULL;
|
||
PBCB EaBcb = NULL;
|
||
|
||
PEA_SET_HEADER EaSetHeader = NULL;
|
||
|
||
PEA_SET_HEADER PrevEaSetHeader;
|
||
PEA_SET_HEADER NewEaSetHeader;
|
||
EA_RANGE EaSetRange;
|
||
|
||
BOOLEAN AcquiredVcb = FALSE;
|
||
BOOLEAN AcquiredFcb = FALSE;
|
||
BOOLEAN AcquiredParentDcb = FALSE;
|
||
BOOLEAN AcquiredRootDcb = FALSE;
|
||
BOOLEAN AcquiredEaFcb = FALSE;
|
||
|
||
//
|
||
// The following booleans are used in the unwind process.
|
||
//
|
||
|
||
//
|
||
// Get the current Irp stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FatCommonSetEa...\n", 0);
|
||
DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
|
||
DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp );
|
||
DebugTrace( 0, Dbg, " ->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer );
|
||
DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.SetEa.Length );
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
//
|
||
// Check that the file object is associated with either a user file
|
||
// or directory open.
|
||
//
|
||
|
||
{
|
||
TYPE_OF_OPEN OpenType;
|
||
|
||
if (((OpenType = FatDecodeFileObject( IrpSp->FileObject,
|
||
&Vcb,
|
||
&Fcb,
|
||
&Ccb )) != UserFileOpen
|
||
&& OpenType != UserDirectoryOpen) ||
|
||
|
||
(NodeType( Fcb )) == FAT_NTC_ROOT_DCB) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
|
||
DebugTrace(-1, Dbg,
|
||
"FatCommonSetEa -> %08lx\n",
|
||
STATUS_INVALID_PARAMETER);
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Fat32 does not support ea's.
|
||
//
|
||
|
||
if (FatIsFat32(Vcb)) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, STATUS_EAS_NOT_SUPPORTED );
|
||
DebugTrace(-1, Dbg,
|
||
"FatCommonSetEa -> %08lx\n",
|
||
STATUS_EAS_NOT_SUPPORTED);
|
||
return STATUS_EAS_NOT_SUPPORTED;
|
||
}
|
||
|
||
//
|
||
// Reference our input parameters to make things easier
|
||
//
|
||
|
||
UserBufferLength = IrpSp->Parameters.SetEa.Length;
|
||
|
||
//
|
||
// Since we ask for no outside help (direct or buffered IO), it
|
||
// is our responsibility to insulate ourselves from the
|
||
// deviousness of the user above. Now, buffer and validate the
|
||
// contents.
|
||
//
|
||
|
||
Buffer = FatBufferUserBuffer( IrpContext, Irp, UserBufferLength );
|
||
|
||
//
|
||
// Check the validity of the buffer with the new eas. We really
|
||
// need to do this always since we don't know, if it was already
|
||
// buffered, that we buffered and checked it or some overlying
|
||
// filter buffered without checking.
|
||
//
|
||
|
||
Status = IoCheckEaBufferValidity( (PFILE_FULL_EA_INFORMATION) Buffer,
|
||
UserBufferLength,
|
||
(PULONG)&Irp->IoStatus.Information );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
DebugTrace(-1, Dbg,
|
||
"FatCommonSetEa -> %08lx\n",
|
||
Status);
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Acquire exclusive access to the Fcb. If this is a write-through operation
|
||
// we will need to pick up the other possible streams that can be modified in
|
||
// this operation so that the locking order is preserved - the root directory
|
||
// (dirent addition if EA database doesn't already exist) and the parent
|
||
// directory (addition of the EA handle to the object's dirent).
|
||
//
|
||
// We are primarily synchronizing with directory enumeration here.
|
||
//
|
||
// If we cannot wait need to send things off to the fsp.
|
||
//
|
||
|
||
if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
|
||
|
||
DebugTrace(0, Dbg, "FatCommonSetEa: Set Ea must be waitable\n", 0);
|
||
|
||
Status = FatFsdPostRequest( IrpContext, Irp );
|
||
|
||
DebugTrace(-1, Dbg, "FatCommonSetEa -> %08lx\n", Status );
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Set this handle as having modified the file
|
||
//
|
||
|
||
IrpSp->FileObject->Flags |= FO_FILE_MODIFIED;
|
||
|
||
RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
|
||
|
||
try {
|
||
|
||
ULONG PackedEasLength;
|
||
BOOLEAN PreviousEas;
|
||
ULONG AllocationLength;
|
||
ULONG BytesPerCluster;
|
||
USHORT EaHandle;
|
||
|
||
PFILE_FULL_EA_INFORMATION FullEa;
|
||
|
||
//
|
||
// Now go pick up everything
|
||
//
|
||
|
||
FatAcquireSharedVcb( IrpContext, Fcb->Vcb );
|
||
AcquiredVcb = TRUE;
|
||
FatAcquireExclusiveFcb( IrpContext, Fcb );
|
||
AcquiredFcb = TRUE;
|
||
|
||
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) {
|
||
|
||
if (Fcb->ParentDcb) {
|
||
|
||
FatAcquireExclusiveFcb( IrpContext, Fcb->ParentDcb );
|
||
AcquiredParentDcb = TRUE;
|
||
}
|
||
|
||
FatAcquireExclusiveFcb( IrpContext, Fcb->Vcb->RootDcb );
|
||
AcquiredRootDcb = TRUE;
|
||
}
|
||
|
||
//
|
||
// We verify that the Fcb is still valid.
|
||
//
|
||
|
||
FatVerifyFcb( IrpContext, Fcb );
|
||
|
||
//
|
||
// We need to get the dirent for the Fcb to recover the Ea handle.
|
||
//
|
||
|
||
FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
|
||
|
||
DebugTrace(0, Dbg, "FatCommonSetEa: Dirent Address -> %08lx\n",
|
||
Dirent );
|
||
DebugTrace(0, Dbg, "FatCommonSetEa: Dirent Bcb -> %08lx\n",
|
||
Bcb);
|
||
|
||
//
|
||
// If the handle value is 0, then the file has no Eas. In that
|
||
// case we allocate memory to hold the Eas to be added. If there
|
||
// are existing Eas for the file, then we must read from the
|
||
// file and copy the Eas.
|
||
//
|
||
|
||
ExtendedAttributes = Dirent->ExtendedAttributes;
|
||
|
||
FatUnpinBcb( IrpContext, Bcb );
|
||
|
||
if (ExtendedAttributes == 0) {
|
||
|
||
PreviousEas = FALSE;
|
||
|
||
DebugTrace(0, Dbg,
|
||
"FatCommonSetEa: File has no current Eas\n", 0 );
|
||
|
||
} else {
|
||
|
||
PreviousEas = TRUE;
|
||
|
||
DebugTrace(0, Dbg, "FatCommonSetEa: File has previous Eas\n", 0 );
|
||
|
||
FatGetEaFile( IrpContext,
|
||
Vcb,
|
||
&EaDirent,
|
||
&EaBcb,
|
||
FALSE,
|
||
TRUE );
|
||
|
||
AcquiredEaFcb = TRUE;
|
||
|
||
//
|
||
// If we didn't get the file then there is an error on
|
||
// the disk.
|
||
//
|
||
|
||
if (Vcb->VirtualEaFile == NULL) {
|
||
|
||
Status = STATUS_NO_EAS_ON_FILE;
|
||
try_return( Status );
|
||
}
|
||
}
|
||
|
||
DebugTrace(0, Dbg, "FatCommonSetEa: EaBcb -> %08lx\n", EaBcb);
|
||
|
||
DebugTrace(0, Dbg, "FatCommonSetEa: EaDirent -> %08lx\n", EaDirent);
|
||
|
||
//
|
||
// If the file has existing ea's, we need to read them to
|
||
// determine the size of the buffer allocation.
|
||
//
|
||
|
||
if (PreviousEas) {
|
||
|
||
//
|
||
// We need to try to get the Ea set for the desired file.
|
||
//
|
||
|
||
FatReadEaSet( IrpContext,
|
||
Vcb,
|
||
ExtendedAttributes,
|
||
&Fcb->ShortName.Name.Oem,
|
||
TRUE,
|
||
&EaSetRange );
|
||
|
||
PrevEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
|
||
|
||
//
|
||
// We now must allocate pool memory for our copy of the
|
||
// EaSetHeader and then copy the Ea data into it. At that
|
||
// time we can unpin the EaSet.
|
||
//
|
||
|
||
PackedEasLength = GetcbList( PrevEaSetHeader ) - 4;
|
||
|
||
//
|
||
// Else we will create a dummy EaSetHeader.
|
||
//
|
||
|
||
} else {
|
||
|
||
PackedEasLength = 0;
|
||
}
|
||
|
||
BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
|
||
|
||
AllocationLength = (PackedEasLength
|
||
+ SIZE_OF_EA_SET_HEADER
|
||
+ BytesPerCluster - 1)
|
||
& ~(BytesPerCluster - 1);
|
||
|
||
EaSetHeader = FsRtlAllocatePoolWithTag( PagedPool,
|
||
AllocationLength,
|
||
TAG_EA_SET_HEADER );
|
||
|
||
//
|
||
// Copy the existing Eas over to pool memory.
|
||
//
|
||
|
||
if (PreviousEas) {
|
||
|
||
RtlCopyMemory( EaSetHeader, PrevEaSetHeader, AllocationLength );
|
||
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
|
||
} else {
|
||
|
||
RtlZeroMemory( EaSetHeader, AllocationLength );
|
||
|
||
RtlCopyMemory( EaSetHeader->OwnerFileName,
|
||
Fcb->ShortName.Name.Oem.Buffer,
|
||
Fcb->ShortName.Name.Oem.Length );
|
||
}
|
||
|
||
|
||
AllocationLength -= SIZE_OF_EA_SET_HEADER;
|
||
|
||
DebugTrace(0, Dbg, "FatCommonSetEa: Initial Ea set -> %08lx\n",
|
||
EaSetHeader);
|
||
|
||
//
|
||
// At this point we have either read in the current eas for the file
|
||
// or we have initialized a new empty buffer for the eas. Now for
|
||
// each full ea in the input user buffer we do the specified operation
|
||
// on the ea
|
||
//
|
||
|
||
for (FullEa = (PFILE_FULL_EA_INFORMATION) Buffer;
|
||
FullEa < (PFILE_FULL_EA_INFORMATION) &Buffer[UserBufferLength];
|
||
FullEa = (PFILE_FULL_EA_INFORMATION) (FullEa->NextEntryOffset == 0 ?
|
||
&Buffer[UserBufferLength] :
|
||
(PUCHAR) FullEa + FullEa->NextEntryOffset)) {
|
||
|
||
OEM_STRING EaName;
|
||
ULONG Offset;
|
||
|
||
EaName.MaximumLength = EaName.Length = FullEa->EaNameLength;
|
||
EaName.Buffer = &FullEa->EaName[0];
|
||
|
||
DebugTrace(0, Dbg, "FatCommonSetEa: Next Ea name -> %Z\n",
|
||
&EaName);
|
||
|
||
//
|
||
// Make sure the ea name is valid
|
||
//
|
||
|
||
if (!FatIsEaNameValid( IrpContext,EaName )) {
|
||
|
||
Irp->IoStatus.Information = (PUCHAR)FullEa - Buffer;
|
||
Status = STATUS_INVALID_EA_NAME;
|
||
try_return( Status );
|
||
}
|
||
|
||
//
|
||
// Check that no invalid ea flags are set.
|
||
//
|
||
|
||
//
|
||
// TEMPCODE We are returning STATUS_INVALID_EA_NAME
|
||
// until a more appropriate error code exists.
|
||
//
|
||
|
||
if (FullEa->Flags != 0
|
||
&& FullEa->Flags != FILE_NEED_EA) {
|
||
|
||
Irp->IoStatus.Information = (PUCHAR)FullEa - (PUCHAR)Buffer;
|
||
try_return( Status = STATUS_INVALID_EA_NAME );
|
||
}
|
||
|
||
//
|
||
// See if we can locate the ea name in the ea set
|
||
//
|
||
|
||
if (FatLocateEaByName( IrpContext,
|
||
(PPACKED_EA) EaSetHeader->PackedEas,
|
||
PackedEasLength,
|
||
&EaName,
|
||
&Offset )) {
|
||
|
||
DebugTrace(0, Dbg, "FatCommonSetEa: Found Ea name\n", 0);
|
||
|
||
//
|
||
// We found the ea name so now delete the current entry,
|
||
// and if the new ea value length is not zero then we
|
||
// replace if with the new ea
|
||
//
|
||
|
||
FatDeletePackedEa( IrpContext,
|
||
EaSetHeader,
|
||
&PackedEasLength,
|
||
Offset );
|
||
}
|
||
|
||
if (FullEa->EaValueLength != 0) {
|
||
|
||
FatAppendPackedEa( IrpContext,
|
||
&EaSetHeader,
|
||
&PackedEasLength,
|
||
&AllocationLength,
|
||
FullEa,
|
||
BytesPerCluster );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If there are any ea's not removed, we
|
||
// call 'AddEaSet' to insert them into the Fat chain.
|
||
//
|
||
|
||
if (PackedEasLength != 0) {
|
||
|
||
LARGE_INTEGER EaOffset;
|
||
|
||
EaOffset.HighPart = 0;
|
||
|
||
//
|
||
// If the packed eas length (plus 4 bytes) is greater
|
||
// than the maximum allowed ea size, we return an error.
|
||
//
|
||
|
||
if (PackedEasLength + 4 > MAXIMUM_EA_SIZE) {
|
||
|
||
DebugTrace( 0, Dbg, "Ea length is greater than maximum\n", 0 );
|
||
|
||
try_return( Status = STATUS_EA_TOO_LARGE );
|
||
}
|
||
|
||
//
|
||
// We need to now read the ea file if we haven't already.
|
||
//
|
||
|
||
if (EaDirent == NULL) {
|
||
|
||
FatGetEaFile( IrpContext,
|
||
Vcb,
|
||
&EaDirent,
|
||
&EaBcb,
|
||
TRUE,
|
||
TRUE );
|
||
|
||
AcquiredEaFcb = TRUE;
|
||
}
|
||
|
||
FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
|
||
|
||
RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
|
||
|
||
FatAddEaSet( IrpContext,
|
||
Vcb,
|
||
PackedEasLength + SIZE_OF_EA_SET_HEADER,
|
||
EaBcb,
|
||
EaDirent,
|
||
&EaHandle,
|
||
&EaSetRange );
|
||
|
||
NewEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
|
||
|
||
DebugTrace(0, Dbg, "FatCommonSetEa: Adding an ea set\n", 0);
|
||
|
||
//
|
||
// Store the length of the new Ea's into the EaSetHeader.
|
||
// This is the PackedEasLength + 4.
|
||
//
|
||
|
||
PackedEasLength += 4;
|
||
|
||
CopyU4char( EaSetHeader->cbList, &PackedEasLength );
|
||
|
||
//
|
||
// Copy all but the first four bytes of EaSetHeader into
|
||
// NewEaSetHeader. The signature and index fields have
|
||
// already been filled in.
|
||
//
|
||
|
||
RtlCopyMemory( &NewEaSetHeader->NeedEaCount,
|
||
&EaSetHeader->NeedEaCount,
|
||
PackedEasLength + SIZE_OF_EA_SET_HEADER - 8 );
|
||
|
||
FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange );
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
|
||
CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
|
||
|
||
} else {
|
||
|
||
FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb );
|
||
|
||
EaHandle = 0;
|
||
}
|
||
|
||
//
|
||
// Now we do a wholesale replacement of the ea for the file
|
||
//
|
||
|
||
if (PreviousEas) {
|
||
|
||
FatDeleteEaSet( IrpContext,
|
||
Vcb,
|
||
EaBcb,
|
||
EaDirent,
|
||
ExtendedAttributes,
|
||
&Fcb->ShortName.Name.Oem );
|
||
|
||
CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
|
||
}
|
||
|
||
if (PackedEasLength != 0 ) {
|
||
|
||
Fcb->EaModificationCount++;
|
||
}
|
||
|
||
//
|
||
// Mark the dirent with the new ea's
|
||
//
|
||
|
||
Dirent->ExtendedAttributes = EaHandle;
|
||
|
||
FatSetDirtyBcb( IrpContext, Bcb, Vcb, TRUE );
|
||
|
||
//
|
||
// We call the notify package to report that the ea's were
|
||
// modified.
|
||
//
|
||
|
||
FatNotifyReportChange( IrpContext,
|
||
Vcb,
|
||
Fcb,
|
||
FILE_NOTIFY_CHANGE_EA,
|
||
FILE_ACTION_MODIFIED );
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
try_exit: NOTHING;
|
||
|
||
//
|
||
// Unpin the dirents for the Fcb and EaFcb if necessary.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, Bcb );
|
||
FatUnpinBcb( IrpContext, EaBcb );
|
||
|
||
FatUnpinRepinnedBcbs( IrpContext );
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( FatCommonSetEa );
|
||
|
||
//
|
||
// If this is an abnormal termination, we need to clean up
|
||
// any locked resources.
|
||
//
|
||
|
||
if (AbnormalTermination()) {
|
||
|
||
//
|
||
// Unpin the dirents for the Fcb, EaFcb and EaSetFcb if necessary.
|
||
//
|
||
|
||
FatUnpinBcb( IrpContext, Bcb );
|
||
FatUnpinBcb( IrpContext, EaBcb );
|
||
|
||
FatUnpinEaRange( IrpContext, &EaSetRange );
|
||
}
|
||
|
||
//
|
||
// Release the Fcbs/Vcb acquired.
|
||
//
|
||
|
||
if (AcquiredEaFcb) {
|
||
FatReleaseFcb( IrpContext, Vcb->EaFcb );
|
||
}
|
||
|
||
if (AcquiredFcb) {
|
||
FatReleaseFcb( IrpContext, Fcb );
|
||
}
|
||
|
||
if (AcquiredParentDcb) {
|
||
FatReleaseFcb( IrpContext, Fcb->ParentDcb );
|
||
}
|
||
|
||
if (AcquiredRootDcb) {
|
||
FatReleaseFcb( IrpContext, Fcb->Vcb->RootDcb );
|
||
}
|
||
|
||
if (AcquiredVcb) {
|
||
FatReleaseVcb( IrpContext, Fcb->Vcb );
|
||
}
|
||
|
||
//
|
||
// Deallocate our Ea buffer.
|
||
//
|
||
|
||
if (EaSetHeader != NULL) {
|
||
|
||
ExFreePool( EaSetHeader );
|
||
}
|
||
|
||
//
|
||
// Complete the irp.
|
||
//
|
||
|
||
if (!AbnormalTermination()) {
|
||
|
||
FatCompleteRequest( IrpContext, Irp, Status );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatCommonSetEa -> %08lx\n", Status);
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
IO_STATUS_BLOCK
|
||
FatQueryEaUserEaList (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
OUT PCCB Ccb,
|
||
IN PPACKED_EA FirstPackedEa,
|
||
IN ULONG PackedEasLength,
|
||
OUT PUCHAR UserBuffer,
|
||
IN ULONG UserBufferLength,
|
||
IN PUCHAR UserEaList,
|
||
IN ULONG UserEaListLength,
|
||
IN BOOLEAN ReturnSingleEntry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the work routine for querying EAs given an ea index
|
||
|
||
Arguments:
|
||
|
||
Ccb - Supplies the Ccb for the query
|
||
|
||
FirstPackedEa - Supplies the first ea for the file being queried
|
||
|
||
PackedEasLength - Supplies the length of the ea data
|
||
|
||
UserBuffer - 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
|
||
|
||
UserEaListLength - Supplies the length, in bytes, of the user ea 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 Offset;
|
||
ULONG RemainingUserBufferLength;
|
||
|
||
PPACKED_EA PackedEa;
|
||
ULONG PackedEaSize;
|
||
|
||
PFILE_FULL_EA_INFORMATION LastFullEa;
|
||
ULONG LastFullEaSize;
|
||
PFILE_FULL_EA_INFORMATION NextFullEa;
|
||
|
||
PFILE_GET_EA_INFORMATION GetEa;
|
||
|
||
BOOLEAN Overflow;
|
||
|
||
DebugTrace(+1, Dbg, "FatQueryEaUserEaList...\n", 0);
|
||
|
||
LastFullEa = NULL;
|
||
NextFullEa = (PFILE_FULL_EA_INFORMATION) UserBuffer;
|
||
RemainingUserBufferLength = UserBufferLength;
|
||
|
||
Overflow = FALSE;
|
||
|
||
for (GetEa = (PFILE_GET_EA_INFORMATION) &UserEaList[0];
|
||
GetEa < (PFILE_GET_EA_INFORMATION) ((PUCHAR) UserEaList
|
||
+ UserEaListLength);
|
||
GetEa = (GetEa->NextEntryOffset == 0
|
||
? (PFILE_GET_EA_INFORMATION) MAXUINT_PTR
|
||
: (PFILE_GET_EA_INFORMATION) ((PUCHAR) GetEa
|
||
+ GetEa->NextEntryOffset))) {
|
||
|
||
OEM_STRING Str;
|
||
OEM_STRING OutputEaName;
|
||
|
||
DebugTrace(0, Dbg, "Top of loop, GetEa = %08lx\n", GetEa);
|
||
DebugTrace(0, Dbg, "LastFullEa = %08lx\n", LastFullEa);
|
||
DebugTrace(0, Dbg, "NextFullEa = %08lx\n", NextFullEa);
|
||
DebugTrace(0, Dbg, "RemainingUserBufferLength = %08lx\n", RemainingUserBufferLength);
|
||
|
||
//
|
||
// Make a string reference to the GetEa and see if we can
|
||
// locate the ea by name
|
||
//
|
||
|
||
Str.MaximumLength = Str.Length = GetEa->EaNameLength;
|
||
Str.Buffer = &GetEa->EaName[0];
|
||
|
||
//
|
||
// Check for a valid name.
|
||
//
|
||
|
||
if (!FatIsEaNameValid( IrpContext, Str )) {
|
||
|
||
DebugTrace(-1, Dbg,
|
||
"FatQueryEaUserEaList: Invalid Ea Name -> %Z\n",
|
||
&Str);
|
||
|
||
Iosb.Information = (PUCHAR)GetEa - UserEaList;
|
||
Iosb.Status = STATUS_INVALID_EA_NAME;
|
||
return Iosb;
|
||
}
|
||
|
||
//
|
||
// If this is a duplicate name, we skip to the next.
|
||
//
|
||
|
||
if (FatIsDuplicateEaName( IrpContext, GetEa, UserEaList )) {
|
||
|
||
DebugTrace(0, Dbg, "FatQueryEaUserEaList: Duplicate name\n", 0);
|
||
continue;
|
||
}
|
||
|
||
if (!FatLocateEaByName( IrpContext,
|
||
FirstPackedEa,
|
||
PackedEasLength,
|
||
&Str,
|
||
&Offset )) {
|
||
|
||
Offset = 0xffffffff;
|
||
|
||
DebugTrace(0, Dbg, "Need to dummy up an ea\n", 0);
|
||
|
||
//
|
||
// 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).
|
||
//
|
||
|
||
if ((ULONG)(4+1+1+2+GetEa->EaNameLength+1)
|
||
> RemainingUserBufferLength) {
|
||
|
||
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 = Str.Length;
|
||
OutputEaName.Buffer = NextFullEa->EaName;
|
||
|
||
FatUpcaseEaName( IrpContext, &OutputEaName, &OutputEaName );
|
||
|
||
NextFullEa->EaName[GetEa->EaNameLength] = 0;
|
||
|
||
} else {
|
||
|
||
DebugTrace(0, Dbg, "Located the ea, Offset = %08lx\n", Offset);
|
||
|
||
//
|
||
// We were able to locate the packed ea
|
||
// Reference the packed ea
|
||
//
|
||
|
||
PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + Offset);
|
||
SizeOfPackedEa( PackedEa, &PackedEaSize );
|
||
|
||
DebugTrace(0, Dbg, "PackedEaSize = %08lx\n", PackedEaSize);
|
||
|
||
//
|
||
// We know that the packed ea is 4 bytes smaller than its
|
||
// equivalent full ea so we need to check the remaining
|
||
// user buffer length against the computed full ea size.
|
||
//
|
||
|
||
if (PackedEaSize + 4 > RemainingUserBufferLength) {
|
||
|
||
Overflow = TRUE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Everything is going to work fine, so copy over the packed
|
||
// ea to the full ea and zero out the next entry offset field.
|
||
//
|
||
|
||
RtlCopyMemory( &NextFullEa->Flags,
|
||
&PackedEa->Flags,
|
||
PackedEaSize );
|
||
|
||
NextFullEa->NextEntryOffset = 0;
|
||
}
|
||
|
||
//
|
||
// At this point we've copied a new full ea into the next full ea
|
||
// location. So now go back and set the set full eas entry offset
|
||
// field to be the difference between out two pointers.
|
||
//
|
||
|
||
if (LastFullEa != NULL) {
|
||
|
||
LastFullEa->NextEntryOffset = (ULONG)((PUCHAR) NextFullEa
|
||
- (PUCHAR) LastFullEa);
|
||
}
|
||
|
||
//
|
||
// Set the last full ea to the next full ea, compute
|
||
// where the next full should be, and decrement the remaining user
|
||
// buffer length appropriately
|
||
//
|
||
|
||
LastFullEa = NextFullEa;
|
||
LastFullEaSize = LongAlign( SizeOfFullEa( LastFullEa ));
|
||
RemainingUserBufferLength -= LastFullEaSize;
|
||
NextFullEa = (PFILE_FULL_EA_INFORMATION) ((PUCHAR) NextFullEa
|
||
+ LastFullEaSize);
|
||
|
||
//
|
||
// Remember the offset of the next ea in case we're asked to
|
||
// resume the iteration
|
||
//
|
||
|
||
Ccb->OffsetOfNextEaToReturn = FatLocateNextEa( IrpContext,
|
||
FirstPackedEa,
|
||
PackedEasLength,
|
||
Offset );
|
||
|
||
//
|
||
// If we were to return a single entry then break out of our loop
|
||
// now
|
||
//
|
||
|
||
if (ReturnSingleEntry) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now we've iterated all that can and we've exited the preceding loop
|
||
// with either all, some or no information stored in the return buffer.
|
||
// We can decide if we got everything to fit by checking the local
|
||
// Overflow variable
|
||
//
|
||
|
||
if (Overflow) {
|
||
|
||
Iosb.Information = 0;
|
||
Iosb.Status = STATUS_BUFFER_OVERFLOW;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Otherwise we've been successful in returing at least one
|
||
// ea so we'll compute the number of bytes used to store the
|
||
// full ea information. The number of bytes used is the difference
|
||
// between the LastFullEa and the start of the buffer, and the
|
||
// non-aligned size of the last full ea.
|
||
//
|
||
|
||
Iosb.Information = ((PUCHAR) LastFullEa - UserBuffer)
|
||
+ SizeOfFullEa(LastFullEa);
|
||
|
||
Iosb.Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatQueryEaUserEaList -> Iosb.Status = %08lx\n",
|
||
Iosb.Status);
|
||
|
||
return Iosb;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
IO_STATUS_BLOCK
|
||
FatQueryEaIndexSpecified (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
OUT PCCB Ccb,
|
||
IN PPACKED_EA FirstPackedEa,
|
||
IN ULONG PackedEasLength,
|
||
OUT PUCHAR UserBuffer,
|
||
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 - Supplies the Ccb for the query
|
||
|
||
FirstPackedEa - Supplies the first ea for the file being queried
|
||
|
||
PackedEasLength - Supplies the length of the ea data
|
||
|
||
UserBuffer - Supplies the buffer to receive the full eas
|
||
|
||
UserBufferLength - Supplies the length, in bytes, of the user buffer
|
||
|
||
UserEaIndex - Supplies the index of the first ea to return.
|
||
|
||
RestartScan - Indicates if the first item to return is at the
|
||
beginning of the packed ea list or if we should resume our
|
||
previous iteration
|
||
|
||
Return Value:
|
||
|
||
IO_STATUS_BLOCK - Receives the completion status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
IO_STATUS_BLOCK Iosb;
|
||
|
||
ULONG i;
|
||
ULONG Offset;
|
||
|
||
DebugTrace(+1, Dbg, "FatQueryEaIndexSpecified...\n", 0);
|
||
|
||
//
|
||
// Zero out the information field of the iosb
|
||
//
|
||
|
||
Iosb.Information = 0;
|
||
|
||
//
|
||
// If the index value is zero or there are no Eas on the file, then
|
||
// the specified index can't be returned.
|
||
//
|
||
|
||
if (UserEaIndex == 0
|
||
|| PackedEasLength == 0) {
|
||
|
||
DebugTrace( -1, Dbg, "FatQueryEaIndexSpecified: Non-existant entry\n", 0 );
|
||
|
||
Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY;
|
||
|
||
return Iosb;
|
||
}
|
||
|
||
//
|
||
// Iterate the eas until we find the index we're after.
|
||
//
|
||
|
||
for (i = 1, Offset = 0;
|
||
(i < UserEaIndex) && (Offset < PackedEasLength);
|
||
i += 1, Offset = FatLocateNextEa( IrpContext,
|
||
FirstPackedEa,
|
||
PackedEasLength, Offset )) {
|
||
|
||
NOTHING;
|
||
}
|
||
|
||
//
|
||
// Make sure the offset we're given to the ea is a real offset otherwise
|
||
// the ea doesn't exist
|
||
//
|
||
|
||
if (Offset >= PackedEasLength) {
|
||
|
||
//
|
||
// 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, "FatQueryEaIndexSpecified -> %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 = FatQueryEaSimpleScan( IrpContext,
|
||
Ccb,
|
||
FirstPackedEa,
|
||
PackedEasLength,
|
||
UserBuffer,
|
||
UserBufferLength,
|
||
ReturnSingleEntry,
|
||
Offset );
|
||
|
||
DebugTrace(-1, Dbg, "FatQueryEaIndexSpecified -> %08lx\n", Iosb.Status);
|
||
|
||
return Iosb;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
IO_STATUS_BLOCK
|
||
FatQueryEaSimpleScan (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
OUT PCCB Ccb,
|
||
IN PPACKED_EA FirstPackedEa,
|
||
IN ULONG PackedEasLength,
|
||
OUT PUCHAR UserBuffer,
|
||
IN ULONG UserBufferLength,
|
||
IN BOOLEAN ReturnSingleEntry,
|
||
ULONG StartOffset
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the work routine for querying EAs from the beginning of
|
||
the ea list.
|
||
|
||
Arguments:
|
||
|
||
Ccb - Supplies the Ccb for the query
|
||
|
||
FirstPackedEa - Supplies the first ea for the file being queried
|
||
|
||
PackedEasLength - Supplies the length of the ea data
|
||
|
||
UserBuffer - 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
|
||
|
||
StartOffset - Indicates the offset within the Ea data to return the
|
||
first block of data.
|
||
|
||
Return Value:
|
||
|
||
IO_STATUS_BLOCK - Receives the completion status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
IO_STATUS_BLOCK Iosb;
|
||
|
||
ULONG RemainingUserBufferLength;
|
||
|
||
PPACKED_EA PackedEa;
|
||
ULONG PackedEaSize;
|
||
|
||
PFILE_FULL_EA_INFORMATION LastFullEa;
|
||
ULONG LastFullEaSize;
|
||
PFILE_FULL_EA_INFORMATION NextFullEa;
|
||
BOOLEAN BufferOverflow = FALSE;
|
||
|
||
|
||
DebugTrace(+1, Dbg, "FatQueryEaSimpleScan...\n", 0);
|
||
|
||
//
|
||
// Zero out the information field in the Iosb
|
||
//
|
||
|
||
Iosb.Information = 0;
|
||
|
||
LastFullEa = NULL;
|
||
NextFullEa = (PFILE_FULL_EA_INFORMATION) UserBuffer;
|
||
RemainingUserBufferLength = UserBufferLength;
|
||
|
||
while (StartOffset < PackedEasLength) {
|
||
|
||
DebugTrace(0, Dbg, "Top of loop, Offset = %08lx\n", StartOffset);
|
||
DebugTrace(0, Dbg, "LastFullEa = %08lx\n", LastFullEa);
|
||
DebugTrace(0, Dbg, "NextFullEa = %08lx\n", NextFullEa);
|
||
DebugTrace(0, Dbg, "RemainingUserBufferLength = %08lx\n", RemainingUserBufferLength);
|
||
|
||
//
|
||
// Reference the packed ea of interest.
|
||
//
|
||
|
||
PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + StartOffset);
|
||
|
||
SizeOfPackedEa( PackedEa, &PackedEaSize );
|
||
|
||
DebugTrace(0, Dbg, "PackedEaSize = %08lx\n", PackedEaSize);
|
||
|
||
//
|
||
// We know that the packed ea is 4 bytes smaller than its
|
||
// equivalent full ea so we need to check the remaining
|
||
// user buffer length against the computed full ea size.
|
||
//
|
||
|
||
if (PackedEaSize + 4 > RemainingUserBufferLength) {
|
||
|
||
BufferOverflow = TRUE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Everything is going to work fine, so copy over the packed
|
||
// ea to the full ea and zero out the next entry offset field.
|
||
// Then go back and set the last full eas entry offset field
|
||
// to be the difference between the two pointers.
|
||
//
|
||
|
||
RtlCopyMemory( &NextFullEa->Flags, &PackedEa->Flags, PackedEaSize );
|
||
NextFullEa->NextEntryOffset = 0;
|
||
|
||
if (LastFullEa != NULL) {
|
||
|
||
LastFullEa->NextEntryOffset = (ULONG)((PUCHAR) NextFullEa
|
||
- (PUCHAR) LastFullEa);
|
||
}
|
||
|
||
//
|
||
// Set the last full ea to the next full ea, compute
|
||
// where the next full should be, and decrement the remaining user
|
||
// buffer length appropriately
|
||
//
|
||
|
||
LastFullEa = NextFullEa;
|
||
LastFullEaSize = LongAlign( SizeOfFullEa( LastFullEa ));
|
||
RemainingUserBufferLength -= LastFullEaSize;
|
||
NextFullEa = (PFILE_FULL_EA_INFORMATION) ((PUCHAR) NextFullEa
|
||
+ LastFullEaSize);
|
||
|
||
//
|
||
// Remember the offset of the next ea in case we're asked to
|
||
// resume the teration
|
||
//
|
||
|
||
StartOffset = FatLocateNextEa( IrpContext,
|
||
FirstPackedEa,
|
||
PackedEasLength,
|
||
StartOffset );
|
||
|
||
Ccb->OffsetOfNextEaToReturn = StartOffset;
|
||
|
||
//
|
||
// If we were to return a single entry then break out of our loop
|
||
// now
|
||
//
|
||
|
||
if (ReturnSingleEntry) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now we've iterated all that can and we've exited the preceding loop
|
||
// with either some or no information stored in the return buffer.
|
||
// We can decide which it is by checking if the last full ea is null
|
||
//
|
||
|
||
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
|
||
// PackedEaList->UsedSize then the user buffer is too small
|
||
//
|
||
|
||
if (PackedEasLength == 0) {
|
||
|
||
Iosb.Status = STATUS_NO_EAS_ON_FILE;
|
||
|
||
} else if (StartOffset >= PackedEasLength) {
|
||
|
||
Iosb.Status = STATUS_NO_MORE_EAS;
|
||
|
||
} else {
|
||
|
||
Iosb.Status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Otherwise we've been successful in returing at least one
|
||
// ea so we'll compute the number of bytes used to store the
|
||
// full ea information. The number of bytes used is the difference
|
||
// between the LastFullEa and the start of the buffer, and the
|
||
// non-aligned size of the last full ea.
|
||
//
|
||
|
||
Iosb.Information = ((PUCHAR) LastFullEa - UserBuffer)
|
||
+ SizeOfFullEa( LastFullEa );
|
||
|
||
//
|
||
// 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, "FatQueryEaSimpleScan -> Iosb.Status = %08lx\n",
|
||
Iosb.Status);
|
||
|
||
return Iosb;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support Routine
|
||
//
|
||
|
||
BOOLEAN
|
||
FatIsDuplicateEaName (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFILE_GET_EA_INFORMATION GetEa,
|
||
IN PUCHAR UserBuffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine walks through a list of ea names to find a duplicate name.
|
||
'GetEa' is an actual position in 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.
|
||
|
||
UserBuffer - 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.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFILE_GET_EA_INFORMATION ThisGetEa;
|
||
|
||
BOOLEAN DuplicateFound;
|
||
OEM_STRING EaString;
|
||
|
||
DebugTrace(+1, Dbg, "FatIsDuplicateEaName...\n", 0);
|
||
|
||
EaString.MaximumLength = EaString.Length = GetEa->EaNameLength;
|
||
EaString.Buffer = &GetEa->EaName[0];
|
||
|
||
FatUpcaseEaName( IrpContext, &EaString, &EaString );
|
||
|
||
DuplicateFound = FALSE;
|
||
|
||
for (ThisGetEa = (PFILE_GET_EA_INFORMATION) &UserBuffer[0];
|
||
ThisGetEa < GetEa
|
||
&& ThisGetEa->NextEntryOffset != 0;
|
||
ThisGetEa = (PFILE_GET_EA_INFORMATION) ((PUCHAR) ThisGetEa
|
||
+ ThisGetEa->NextEntryOffset)) {
|
||
|
||
OEM_STRING Str;
|
||
|
||
DebugTrace(0, Dbg, "Top of loop, ThisGetEa = %08lx\n", ThisGetEa);
|
||
|
||
//
|
||
// Make a string reference to the GetEa and see if we can
|
||
// locate the ea by name
|
||
//
|
||
|
||
Str.MaximumLength = Str.Length = ThisGetEa->EaNameLength;
|
||
Str.Buffer = &ThisGetEa->EaName[0];
|
||
|
||
DebugTrace(0, Dbg, "FatIsDuplicateEaName: Next Name -> %Z\n", &Str);
|
||
|
||
if ( FatAreNamesEqual(IrpContext, Str, EaString) ) {
|
||
|
||
DebugTrace(0, Dbg, "FatIsDuplicateEaName: Duplicate found\n", 0);
|
||
DuplicateFound = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatIsDuplicateEaName: Exit -> %04x\n", DuplicateFound);
|
||
|
||
return DuplicateFound;
|
||
}
|