Something went wrong. Try again.
Reactos
Something went wrong. Try again.
49 kB · 1613 lines
C
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614/*++
Copyright (c) 1989-2000 Microsoft Corporation
Module Name:
DirCtrl.c
Abstract:
This module implements the File Directory Control routines for Fat called by the dispatch driver.
--*/
#include "fatprocs.h"
//// The Bug check file id for this module//
#define BugCheckFileId (FAT_BUG_CHECK_DIRCTRL)
//// The local debug trace level//
#define Dbg (DEBUG_TRACE_DIRCTRL)
WCHAR Fat8QMdot3QM[12] = { DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, L'.', DOS_QM, DOS_QM, DOS_QM};
//// Local procedure prototypes//
_Requires_lock_held_(_Global_critical_region_)NTSTATUSFatQueryDirectory ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp );
VOIDFatGetDirTimes( PIRP_CONTEXT IrpContext, PDIRENT Dirent, PFILE_DIRECTORY_INFORMATION DirInfo );
_Requires_lock_held_(_Global_critical_region_)NTSTATUSFatNotifyChangeDirectory ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp );
#ifdef ALLOC_PRAGMA#pragma alloc_text(PAGE, FatCommonDirectoryControl)#pragma alloc_text(PAGE, FatFsdDirectoryControl)#pragma alloc_text(PAGE, FatNotifyChangeDirectory)#pragma alloc_text(PAGE, FatQueryDirectory)#pragma alloc_text(PAGE, FatGetDirTimes)
#endif
_Function_class_(IRP_MJ_DIRECTORY_CONTROL)_Function_class_(DRIVER_DISPATCH)NTSTATUSNTAPIFatFsdDirectoryControl ( _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, _Inout_ PIRP Irp )
/*++
Routine Description:
This routine implements the FSD part of directory control
Arguments:
VolumeDeviceObject - Supplies the volume device object where the file exists
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The FSD status for the IRP
--*/
{ NTSTATUS Status; PIRP_CONTEXT IrpContext = NULL;
BOOLEAN TopLevel;
PAGED_CODE();
DebugTrace(+1, Dbg, "FatFsdDirectoryControl\n", 0);
// // Call the common directory Control routine, with blocking allowed if // synchronous //
FsRtlEnterFileSystem();
TopLevel = FatIsIrpTopLevel( Irp );
_SEH2_TRY {
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
Status = FatCommonDirectoryControl( IrpContext, Irp );
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_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, _SEH2_GetExceptionCode() ); } _SEH2_END;
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
FsRtlExitFileSystem();
// // And return to our caller //
DebugTrace(-1, Dbg, "FatFsdDirectoryControl -> %08lx\n", Status);
UNREFERENCED_PARAMETER( VolumeDeviceObject );
return Status;}
_Requires_lock_held_(_Global_critical_region_)NTSTATUSFatCommonDirectoryControl ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp )
/*++
Routine Description:
This is the common routine for doing directory control operations called by both the fsd and fsp threads
Arguments:
Irp - Supplies the Irp to process
Return Value:
NTSTATUS - The return status for the operation
--*/
{ NTSTATUS Status; PIO_STACK_LOCATION IrpSp;
PAGED_CODE();
// // Get a pointer to the current Irp stack location //
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "FatCommonDirectoryControl\n", 0); DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction );
// // We know this is a directory control so we'll case on the // minor function, and call a internal worker routine to complete // the irp. //
switch ( IrpSp->MinorFunction ) {
case IRP_MN_QUERY_DIRECTORY:
Status = FatQueryDirectory( IrpContext, Irp ); break;
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
Status = FatNotifyChangeDirectory( IrpContext, Irp ); break;
default:
DebugTrace(0, Dbg, "Invalid Directory Control Minor Function %08lx\n", IrpSp->MinorFunction);
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); Status = STATUS_INVALID_DEVICE_REQUEST; break; }
DebugTrace(-1, Dbg, "FatCommonDirectoryControl -> %08lx\n", Status);
return Status;}
//// Local Support Routine//
_Requires_lock_held_(_Global_critical_region_)NTSTATUSFatQueryDirectory ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp )
/*++
Routine Description:
This routine performs the query directory operation. It is responsible for either completing of enqueuing the input Irp.
Arguments:
Irp - Supplies the Irp to process
Return Value:
NTSTATUS - The return status for the operation
--*/
{ NTSTATUS Status; PIO_STACK_LOCATION IrpSp;
PVCB Vcb; PDCB Dcb; PCCB Ccb; PBCB Bcb;
ULONG i; PUCHAR Buffer; CLONG UserBufferLength;
PUNICODE_STRING UniArgFileName; WCHAR LongFileNameBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE]; UNICODE_STRING LongFileName; UNICODE_STRING OrigFileName; FILE_INFORMATION_CLASS FileInformationClass; ULONG FileIndex; ULONG MatchFlags = 0; BOOLEAN RestartScan; BOOLEAN ReturnSingleEntry; BOOLEAN IndexSpecified;
BOOLEAN InitialQuery; VBO CurrentVbo = 0; BOOLEAN UpdateCcb; PDIRENT Dirent; UCHAR Fat8Dot3Buffer[12]; OEM_STRING Fat8Dot3String; ULONG DiskAllocSize;
ULONG NextEntry; ULONG LastEntry;
PFILE_DIRECTORY_INFORMATION DirInfo; PFILE_FULL_DIR_INFORMATION FullDirInfo; PFILE_BOTH_DIR_INFORMATION BothDirInfo; PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo; PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo; PFILE_NAMES_INFORMATION NamesInfo;
PAGED_CODE();
// // Get the current Stack location //
IrpSp = IoGetCurrentIrpStackLocation( Irp );
// // Display the input values. // DebugTrace(+1, Dbg, "FatQueryDirectory...\n", 0); DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); DebugTrace( 0, Dbg, " Irp = %p\n", Irp); DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.QueryDirectory.Length); DebugTrace( 0, Dbg, " ->FileName = %wZ\n", IrpSp->Parameters.QueryDirectory.FileName); DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryDirectory.FileInformationClass); DebugTrace( 0, Dbg, " ->FileIndex = %08lx\n", IrpSp->Parameters.QueryDirectory.FileIndex); DebugTrace( 0, Dbg, " ->UserBuffer = %p\n", Irp->AssociatedIrp.SystemBuffer); 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 ));
// // Reference our input parameters to make things easier //
UserBufferLength = IrpSp->Parameters.QueryDirectory.Length;
FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass; FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex;
UniArgFileName = IrpSp->Parameters.QueryDirectory.FileName;
RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN); ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY); IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED);
// // Check on the type of open. We return invalid parameter for all // but UserDirectoryOpens. Also check that the filename is a valid // UNICODE string. //
if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Dcb, &Ccb) != UserDirectoryOpen || (UniArgFileName && UniArgFileName->Length % sizeof(WCHAR))) {
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0);
return STATUS_INVALID_PARAMETER; }
// // Initialize the local variables. //
Bcb = NULL; UpdateCcb = TRUE; Dirent = NULL;
Fat8Dot3String.MaximumLength = 12; Fat8Dot3String.Buffer = (PCHAR)Fat8Dot3Buffer;
LongFileName.Length = 0; LongFileName.MaximumLength = sizeof( LongFileNameBuffer); LongFileName.Buffer = LongFileNameBuffer;
InitialQuery = (BOOLEAN)((Ccb->UnicodeQueryTemplate.Buffer == NULL) && !FlagOn(Ccb->Flags, CCB_FLAG_MATCH_ALL)); Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0;
DiskAllocSize = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
// // If this is the initial query, then grab exclusive access in // order to update the search string in the Ccb. We may // discover that we are not the initial query once we grab the Fcb // and downgrade our status. // // If restartscan is set, we may be replacing the query template, // so take the FCB exclusive to protect against multiple people // changing the CCB at once. //
if (InitialQuery || RestartScan) {
if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) {
DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0); Status = FatFsdPostRequest( IrpContext, Irp ); DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status);
return Status; }
if (!RestartScan && (Ccb->UnicodeQueryTemplate.Buffer != NULL)) {
InitialQuery = FALSE;
FatConvertToSharedFcb( IrpContext, Dcb ); }
} else {
if (!FatAcquireSharedFcb( IrpContext, Dcb )) {
DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0); Status = FatFsdPostRequest( IrpContext, Irp ); DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status);
return Status;
} }
_SEH2_TRY {
ULONG BaseLength; ULONG BytesConverted;
// // If we are in the Fsp now because we had to wait earlier, // we must map the user buffer, otherwise we can use the // user's buffer directly. //
Buffer = FatMapUserBuffer( IrpContext, Irp );
// // Make sure the Dcb is still good. //
FatVerifyFcb( IrpContext, Dcb );
// // Determine where to start the scan. Highest priority is given // to the file index. Lower priority is the restart flag. If // neither of these is specified, then the Vbo offset field in the // Ccb is used. //
if (IndexSpecified) {
CurrentVbo = FileIndex + sizeof( DIRENT );
} else if (RestartScan) {
CurrentVbo = 0; Ccb->OffsetToStartSearchFrom = 0;
} else {
CurrentVbo = Ccb->OffsetToStartSearchFrom; }
// // If this is the first try then allocate a buffer for the file // name. //
if (InitialQuery || (RestartScan && UniArgFileName != NULL && UniArgFileName->Length != 0)) {
// // If we're restarting the scan, clear out the pattern in the Ccb and regenerate it, //
if (RestartScan) {
if (Ccb->UnicodeQueryTemplate.Buffer) {
if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_UNICODE)) {
ExFreePoolWithTag(Ccb->UnicodeQueryTemplate.Buffer, TAG_FILENAME_BUFFER); ClearFlag(Ccb->Flags, CCB_FLAG_FREE_UNICODE); }
Ccb->UnicodeQueryTemplate.Buffer = NULL; Ccb->UnicodeQueryTemplate.Length = 0; Ccb->UnicodeQueryTemplate.MaximumLength = 0; }
if (Ccb->OemQueryTemplate.Wild.Buffer) {
if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) {
RtlFreeOemString( &Ccb->OemQueryTemplate.Wild ); ClearFlag(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT); }
Ccb->OemQueryTemplate.Wild.Buffer = NULL; Ccb->OemQueryTemplate.Wild.Length = 0; Ccb->OemQueryTemplate.Wild.MaximumLength = 0; }
Ccb->ContainsWildCards = FALSE; ClearFlag(Ccb->Flags, CCB_FLAG_MATCH_ALL); ClearFlag(Ccb->Flags, CCB_FLAG_FREE_UNICODE); ClearFlag(Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE); ClearFlag(Ccb->Flags, CCB_FLAG_QUERY_TEMPLATE_MIXED);
}
// // If either: // // - No name was specified // - An empty name was specified // - We received a '*' // - The user specified the DOS equivolent of ????????.??? // // then match all names. //
if ((UniArgFileName == NULL) || (UniArgFileName->Length == 0) || (UniArgFileName->Buffer == NULL) || ((UniArgFileName->Length == sizeof(WCHAR)) && (UniArgFileName->Buffer[0] == L'*')) || ((UniArgFileName->Length == 12*sizeof(WCHAR)) && (RtlEqualMemory( UniArgFileName->Buffer, Fat8QMdot3QM, 12*sizeof(WCHAR) )))) {
Ccb->ContainsWildCards = TRUE;
SetFlag( Ccb->Flags, CCB_FLAG_MATCH_ALL );
} else {
BOOLEAN ExtendedName = FALSE; OEM_STRING LocalBestFit;
// // First and formost, see if the name has wild cards. //
Ccb->ContainsWildCards = FsRtlDoesNameContainWildCards( UniArgFileName );
// // Now check to see if the name contains any extended // characters //
for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) {
if (UniArgFileName->Buffer[i] >= 0x80) {
ExtendedName = TRUE; break; } }
// // OK, now do the conversions we need. //
if (ExtendedName) {
Status = RtlUpcaseUnicodeString( &Ccb->UnicodeQueryTemplate, UniArgFileName, TRUE );
if (!NT_SUCCESS(Status)) {
try_return( Status ); }
SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE );
// // Upcase the name and convert it to the Oem code page. //
Status = RtlUpcaseUnicodeStringToCountedOemString( &LocalBestFit, UniArgFileName, TRUE );
// // If this conversion failed for any reason other than // an unmappable character fail the request. //
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_UNMAPPABLE_CHARACTER) {
SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE );
} else {
try_return( Status ); }
} else {
SetFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); }
} else {
PVOID Buffers;
// // This case is optimized because I know I only have to // worry about a-z. //
Buffers = FsRtlAllocatePoolWithTag( PagedPool, UniArgFileName->Length + UniArgFileName->Length / sizeof(WCHAR), TAG_FILENAME_BUFFER );
Ccb->UnicodeQueryTemplate.Buffer = Buffers; Ccb->UnicodeQueryTemplate.Length = UniArgFileName->Length; Ccb->UnicodeQueryTemplate.MaximumLength = UniArgFileName->Length;
LocalBestFit.Buffer = (PCHAR)Buffers + UniArgFileName->Length; LocalBestFit.Length = UniArgFileName->Length / sizeof(WCHAR); LocalBestFit.MaximumLength = LocalBestFit.Length;
SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE );
for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) {
WCHAR c = UniArgFileName->Buffer[i];
LocalBestFit.Buffer[i] = (UCHAR) (Ccb->UnicodeQueryTemplate.Buffer[i] = (c < 'a' ? c : c <= 'z' ? c - ('a' - 'A') : c)); } }
// // At this point we now have the upcased unicode name, // and the two Oem names if they could be represented in // this code page. // // Now determine if the Oem names are legal for what we // going to try and do. Mark them as not usable is they // are not legal. Note that we can optimize extended names // since they are actually both the same string. //
if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ) && !FatIsNameShortOemValid( IrpContext, LocalBestFit, Ccb->ContainsWildCards, FALSE, FALSE )) {
if (ExtendedName) {
RtlFreeOemString( &LocalBestFit ); ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); }
SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ); }
// // OK, now both locals oem strings correctly reflect their // usability. Now we want to load up the Ccb structure. // // Now we will branch on two paths of wheather the name // is wild or not. //
if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE )) {
if (Ccb->ContainsWildCards) {
Ccb->OemQueryTemplate.Wild = LocalBestFit;
} else {
FatStringTo8dot3( IrpContext, LocalBestFit, &Ccb->OemQueryTemplate.Constant );
if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) {
RtlFreeOemString( &LocalBestFit ); ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); } } } }
// // We convert to shared access. //
FatConvertToSharedFcb( IrpContext, Dcb ); }
LastEntry = 0; NextEntry = 0;
switch (FileInformationClass) {
case FileDirectoryInformation:
BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[0] ); break;
case FileFullDirectoryInformation:
BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName[0] ); break;
case FileIdFullDirectoryInformation:
BaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION, FileName[0] ); break;
case FileNamesInformation:
BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName[0] ); break;
case FileBothDirectoryInformation:
BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] ); break;
case FileIdBothDirectoryInformation:
BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, FileName[0] ); break;
default:
try_return( Status = STATUS_INVALID_INFO_CLASS ); }
// // At this point we are about to enter our query loop. We have // determined the index into the directory file to begin the // search. LastEntry and NextEntry are used to index into the user // buffer. LastEntry is the last entry we've added, NextEntry is // current one we're working on. If NextEntry is non-zero, then // at least one entry was added. //
while ( TRUE ) {
VBO NextVbo; ULONG FileNameLength; ULONG BytesRemainingInBuffer; BOOLEAN FileNameDos;
DebugTrace(0, Dbg, "FatQueryDirectory -> Top of loop\n", 0);
// // If the user had requested only a single match and we have // returned that, then we stop at this point. //
if (ReturnSingleEntry && NextEntry != 0) {
try_return( Status ); }
// // We call FatLocateDirent to lock down the next matching dirent. //
FatLocateDirent( IrpContext, Dcb, Ccb, CurrentVbo, &MatchFlags, &Dirent, &Bcb, &NextVbo, &FileNameDos, &LongFileName, &OrigFileName );
// // If we didn't receive a dirent, then we are at the end of the // directory. If we have returned any files, we exit with // success, otherwise we return STATUS_NO_MORE_FILES. //
if (!Dirent) {
DebugTrace(0, Dbg, "FatQueryDirectory -> No dirent\n", 0);
if (NextEntry == 0) {
UpdateCcb = FALSE;
if (InitialQuery) {
Status = STATUS_NO_SUCH_FILE;
} else {
Status = STATUS_NO_MORE_FILES; } }
try_return( Status ); }
// // Protect access to the user buffer with an exception handler. // Since (at our request) IO doesn't buffer these requests, we have // to guard against a user messing with the page protection and other // such trickery. //
_SEH2_TRY {
Fat8dot3ToString( IrpContext, Dirent, TRUE, &Fat8Dot3String );
if (LongFileName.Length == 0) {
// // Now we have an entry to return to our caller. We'll convert // the name from the form in the dirent to a <name>.<ext> form. // We'll case on the type of information requested and fill up // the user buffer if everything fits. //
// // Determine the UNICODE length of the file name. //
FileNameLength = RtlOemStringToCountedUnicodeSize(&Fat8Dot3String);
// // Here are the rules concerning filling up the buffer: // // 1. The Io system garentees that there will always be // enough room for at least one base record. // // 2. If the full first record (including file name) cannot // fit, as much of the name as possible is copied and // STATUS_BUFFER_OVERFLOW is returned. // // 3. If a subsequent record cannot completely fit into the // buffer, none of it (as in 0 bytes) is copied, and // STATUS_SUCCESS is returned. A subsequent query will // pick up with this record. //
BytesRemainingInBuffer = UserBufferLength - NextEntry;
if ( (NextEntry != 0) && ( (BaseLength + FileNameLength > BytesRemainingInBuffer) || (UserBufferLength < NextEntry) ) ) {
DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
try_return( Status = STATUS_SUCCESS ); }
NT_ASSERT( BytesRemainingInBuffer >= BaseLength );
// // Zero the base part of the structure. //
RtlZeroMemory( &Buffer[NextEntry], BaseLength );
switch ( FileInformationClass ) {
// // Now fill the base parts of the strucure that are applicable. //
case FileBothDirectoryInformation: case FileFullDirectoryInformation: case FileIdBothDirectoryInformation: case FileIdFullDirectoryInformation:
DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0);
// // Get the Ea file length. //
FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry];
// // If the EAs are corrupt, ignore the error. We don't want // to abort the directory query. //
_SEH2_TRY {
FatGetEaLength( IrpContext, Vcb, Dirent, &FullDirInfo->EaSize );
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
FatResetExceptionState( IrpContext ); FullDirInfo->EaSize = 0; } _SEH2_END;
case FileDirectoryInformation:
DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry];
FatGetDirTimes( IrpContext, Dirent, DirInfo );
DirInfo->EndOfFile.QuadPart = Dirent->FileSize;
if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {
DirInfo->AllocationSize.QuadPart = (((Dirent->FileSize + DiskAllocSize - 1) / DiskAllocSize) * DiskAllocSize ); }
if (Dirent->Attributes != 0) { DirInfo->FileAttributes = Dirent->Attributes;
} else {
DirInfo->FileAttributes = 0;
DirInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL; }
DirInfo->FileIndex = NextVbo;
DirInfo->FileNameLength = FileNameLength;
DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String);
break;
case FileNamesInformation:
DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0);
NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry];
NamesInfo->FileIndex = NextVbo;
NamesInfo->FileNameLength = FileNameLength;
DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String );
break;
default:
#ifdef _MSC_VER#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )#endif FatBugCheck( FileInformationClass, 0, 0 ); }
BytesConverted = 0;
Status = RtlOemToUnicodeN( (PWCH)&Buffer[NextEntry + BaseLength], BytesRemainingInBuffer - BaseLength, &BytesConverted, Fat8Dot3String.Buffer, Fat8Dot3String.Length );
// // Check for the case that a single entry doesn't fit. // This should only get this far on the first entry //
if (BytesConverted < FileNameLength) {
NT_ASSERT( NextEntry == 0 ); Status = STATUS_BUFFER_OVERFLOW; }
// // Set up the previous next entry offset //
*((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry;
// // And indicate how much of the user buffer we have currently // used up. We must compute this value before we long align // ourselves for the next entry //
Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information ) + BaseLength + BytesConverted;
// // If something happened with the conversion, bail here. //
if ( !NT_SUCCESS( Status ) ) {
try_return( NOTHING ); }
} else {
ULONG ShortNameLength;
FileNameLength = LongFileName.Length;
// // Here are the rules concerning filling up the buffer: // // 1. The Io system garentees that there will always be // enough room for at least one base record. // // 2. If the full first record (including file name) cannot // fit, as much of the name as possible is copied and // STATUS_BUFFER_OVERFLOW is returned. // // 3. If a subsequent record cannot completely fit into the // buffer, none of it (as in 0 bytes) is copied, and // STATUS_SUCCESS is returned. A subsequent query will // pick up with this record. //
BytesRemainingInBuffer = UserBufferLength - NextEntry;
if ( (NextEntry != 0) && ( (BaseLength + FileNameLength > BytesRemainingInBuffer) || (UserBufferLength < NextEntry) ) ) {
DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
try_return( Status = STATUS_SUCCESS ); }
NT_ASSERT( BytesRemainingInBuffer >= BaseLength );
// // Zero the base part of the structure. //
RtlZeroMemory( &Buffer[NextEntry], BaseLength );
switch ( FileInformationClass ) {
// // Now fill the base parts of the strucure that are applicable. //
case FileBothDirectoryInformation: case FileIdBothDirectoryInformation:
BothDirInfo = (PFILE_BOTH_DIR_INFORMATION)&Buffer[NextEntry];
// // Now we have an entry to return to our caller. We'll convert // the name from the form in the dirent to a <name>.<ext> form. // We'll case on the type of information requested and fill up // the user buffer if everything fits. //
Fat8dot3ToString( IrpContext, Dirent, FALSE, &Fat8Dot3String );
NT_ASSERT( Fat8Dot3String.Length <= 12 );
Status = RtlOemToUnicodeN( &BothDirInfo->ShortName[0], 12*sizeof(WCHAR), &ShortNameLength, Fat8Dot3String.Buffer, Fat8Dot3String.Length );
NT_ASSERT( Status != STATUS_BUFFER_OVERFLOW ); NT_ASSERT( ShortNameLength <= 12*sizeof(WCHAR) );
// // Copy the length into the dirinfo structure. Note // that the LHS below is a USHORT, so it can not // be specificed as the OUT parameter above. //
BothDirInfo->ShortNameLength = (UCHAR)ShortNameLength;
// // If something happened with the conversion, bail here. //
if ( !NT_SUCCESS( Status ) ) {
try_return( NOTHING ); }
case FileFullDirectoryInformation: case FileIdFullDirectoryInformation:
DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0);
// // Get the Ea file length. //
FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry];
// // If the EAs are corrupt, ignore the error. We don't want // to abort the directory query. //
_SEH2_TRY {
FatGetEaLength( IrpContext, Vcb, Dirent, &FullDirInfo->EaSize );
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
FatResetExceptionState( IrpContext ); FullDirInfo->EaSize = 0; } _SEH2_END;
case FileDirectoryInformation:
DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry];
FatGetDirTimes( IrpContext, Dirent, DirInfo );
DirInfo->EndOfFile.QuadPart = Dirent->FileSize;
if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {
DirInfo->AllocationSize.QuadPart = ( (( Dirent->FileSize + DiskAllocSize - 1 ) / DiskAllocSize ) * DiskAllocSize ); }
if (Dirent->Attributes != 0) { DirInfo->FileAttributes = Dirent->Attributes;
} else {
DirInfo->FileAttributes = 0;
DirInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL; }
DirInfo->FileIndex = NextVbo;
DirInfo->FileNameLength = FileNameLength;
DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String);
break;
case FileNamesInformation:
DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0);
NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry];
NamesInfo->FileIndex = NextVbo;
NamesInfo->FileNameLength = FileNameLength;
DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String );
break;
default:
#ifdef _MSC_VER#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )#endif FatBugCheck( FileInformationClass, 0, 0 ); }
BytesConverted = BytesRemainingInBuffer - BaseLength >= FileNameLength ? FileNameLength : BytesRemainingInBuffer - BaseLength;
RtlCopyMemory( &Buffer[NextEntry + BaseLength], &LongFileName.Buffer[0], BytesConverted );
// // Set up the previous next entry offset //
*((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry;
// // And indicate how much of the user buffer we have currently // used up. We must compute this value before we long align // ourselves for the next entry //
Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information ) + BaseLength + BytesConverted;
// // Check for the case that a single entry doesn't fit. // This should only get this far on the first entry. //
if (BytesConverted < FileNameLength) {
NT_ASSERT( NextEntry == 0 );
try_return( Status = STATUS_BUFFER_OVERFLOW ); } }
// // Finish up by filling in the FileId //
switch ( FileInformationClass ) {
case FileIdBothDirectoryInformation:
IdBothDirInfo = (PFILE_ID_BOTH_DIR_INFORMATION)&Buffer[NextEntry]; IdBothDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo ); break;
case FileIdFullDirectoryInformation:
IdFullDirInfo = (PFILE_ID_FULL_DIR_INFORMATION)&Buffer[NextEntry]; IdFullDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo ); break;
default: break; }
} _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
// // We had a problem filling in the user's buffer, so stop and // fail this request. This is the only reason any exception // would have occured at this level. //
Irp->IoStatus.Information = 0; UpdateCcb = FALSE; try_return( Status = _SEH2_GetExceptionCode()); } _SEH2_END;
// // Set ourselves up for the next iteration //
LastEntry = NextEntry; NextEntry += (ULONG)QuadAlign(BaseLength + BytesConverted);
CurrentVbo = NextVbo + sizeof( DIRENT ); }
try_exit: NOTHING; } _SEH2_FINALLY {
DebugUnwind( FatQueryDirectory );
FatReleaseFcb( IrpContext, Dcb );
// // Unpin data in cache if still held. //
FatUnpinBcb( IrpContext, Bcb );
// // Free any dynamically allocated string buffer //
FatFreeStringBuffer( &LongFileName);
// // Perform any cleanup. If this is the first query, then store // the filename in the Ccb if successful. Also update the // VBO index for the next search. This is done by transferring // from shared access to exclusive access and copying the // data from the local copies. //
if (!_SEH2_AbnormalTermination()) {
if (UpdateCcb) {
// // Store the most recent VBO to use as a starting point for // the next search. //
Ccb->OffsetToStartSearchFrom = CurrentVbo; }
FatCompleteRequest( IrpContext, Irp, Status ); }
DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status);
} _SEH2_END;
return Status;}
//// Local Support Routine//
VOIDFatGetDirTimes( PIRP_CONTEXT IrpContext, PDIRENT Dirent, PFILE_DIRECTORY_INFORMATION DirInfo )
/*++
Routine Description:
This routine pulls the date/time information from a dirent and fills in the DirInfo structure.
Arguments:
Dirent - Supplies the dirent DirInfo - Supplies the target structure
Return Value:
VOID
--*/
{ PAGED_CODE();
// // Start with the Last Write Time. //
DirInfo->LastWriteTime = FatFatTimeToNtTime( IrpContext, Dirent->LastWriteTime, 0 );
// // These fields are only non-zero when in Chicago mode. //
if (FatData.ChicagoMode) {
// // Do a quick check here for Creation and LastAccess // times that are the same as the LastWriteTime. //
if (*((UNALIGNED LONG *)&Dirent->CreationTime) == *((UNALIGNED LONG *)&Dirent->LastWriteTime)) {
DirInfo->CreationTime.QuadPart =
DirInfo->LastWriteTime.QuadPart + Dirent->CreationMSec * 10 * 1000 * 10;
} else {
// // Only do the really hard work if this field is non-zero. //
if (((PUSHORT)Dirent)[8] != 0) {
DirInfo->CreationTime = FatFatTimeToNtTime( IrpContext, Dirent->CreationTime, Dirent->CreationMSec );
} else {
ExLocalTimeToSystemTime( &FatJanOne1980, &DirInfo->CreationTime ); } }
// // Do a quick check for LastAccessDate. //
if (*((PUSHORT)&Dirent->LastAccessDate) == *((PUSHORT)&Dirent->LastWriteTime.Date)) {
PFAT_TIME WriteTime;
WriteTime = &Dirent->LastWriteTime.Time;
DirInfo->LastAccessTime.QuadPart = DirInfo->LastWriteTime.QuadPart - UInt32x32To64(((WriteTime->DoubleSeconds * 2) + (WriteTime->Minute * 60) + (WriteTime->Hour * 60 * 60)), 1000 * 1000 * 10);
} else {
// // Only do the really hard work if this field is non-zero. //
if (((PUSHORT)Dirent)[9] != 0) {
DirInfo->LastAccessTime = FatFatDateToNtTime( IrpContext, Dirent->LastAccessDate );
} else {
ExLocalTimeToSystemTime( &FatJanOne1980, &DirInfo->LastAccessTime ); } } }}
//// Local Support Routine//
_Requires_lock_held_(_Global_critical_region_)NTSTATUSFatNotifyChangeDirectory ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp )
/*++
Routine Description:
This routine performs the notify change directory operation. It is responsible for either completing of enqueuing the input Irp.
Arguments:
Irp - Supplies the Irp to process
Return Value:
NTSTATUS - The return status for the operation
--*/
{ NTSTATUS Status = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp; PVCB Vcb; PDCB Dcb; PCCB Ccb; ULONG CompletionFilter; BOOLEAN WatchTree;
BOOLEAN CompleteRequest;
PAGED_CODE();
// // Get the current Stack location //
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace(+1, Dbg, "FatNotifyChangeDirectory...\n", 0); DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); DebugTrace( 0, Dbg, " Irp = %p\n", Irp); DebugTrace( 0, Dbg, " ->CompletionFilter = %08lx\n", IrpSp->Parameters.NotifyDirectory.CompletionFilter);
// // Always set the wait flag in the Irp context for the original request. //
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
// // Assume we don't complete request. //
CompleteRequest = FALSE;
// // Check on the type of open. We return invalid parameter for all // but UserDirectoryOpens. //
if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Dcb, &Ccb ) != UserDirectoryOpen) {
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0);
return STATUS_INVALID_PARAMETER;
}
// // Reference our input parameter to make things easier //
CompletionFilter = IrpSp->Parameters.NotifyDirectory.CompletionFilter; WatchTree = BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE );
// // Try to acquire exclusive access to the Dcb and enqueue the Irp to the // Fsp if we didn't get access //
if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) {
DebugTrace(0, Dbg, "FatNotifyChangeDirectory -> Cannot Acquire Fcb\n", 0);
Status = FatFsdPostRequest( IrpContext, Irp );
DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status); return Status; }
_SEH2_TRY {
// // Make sure the Fcb is still good //
FatVerifyFcb( IrpContext, Dcb );
// // We need the full name. //
FatSetFullFileNameInFcb( IrpContext, Dcb );
// // If the file is marked as DELETE_PENDING then complete this // request immediately. //
if (FlagOn( Dcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) {
FatRaiseStatus( IrpContext, STATUS_DELETE_PENDING ); }
// // Call the Fsrtl package to process the request. //
FsRtlNotifyFullChangeDirectory( Vcb->NotifySync, &Vcb->DirNotifyList, Ccb, (PSTRING)&Dcb->FullFileName, WatchTree, FALSE, CompletionFilter, Irp, NULL, NULL );
Status = STATUS_PENDING;
CompleteRequest = TRUE;
} _SEH2_FINALLY {
DebugUnwind( FatNotifyChangeDirectory );
FatReleaseFcb( IrpContext, Dcb );
// // If the dir notify package is holding the Irp, we discard the // the IrpContext. //
if (CompleteRequest) {
FatCompleteRequest( IrpContext, FatNull, 0 ); }
DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status); } _SEH2_END;
return Status;}