mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2017 lines
54 KiB
2017 lines
54 KiB
/*++
|
|
|
|
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;
|
|
}
|