BOOLEAN NtfsFileContainsWildcards ( IN PVOID Value ) /*++ RoutineDescription: This routine is called to see if a file name attribute contains wildcards. Arguments: Value - Pointer to the value expression, which is a FILE_NAME. ReturnValue: TRUE - if the file name contains a wild card. --*/ { PFILE_NAME ValueName; UNICODE_STRING ValueString; PAGED_CODE(); // // Point to the file name attribute records. // ValueName = (PFILE_NAME)Value; // // Build the unicode strings and call namesup. // ValueString.Length = ValueString.MaximumLength = (USHORT)ValueName->FileNameLength << 1; ValueString.Buffer = &ValueName->FileName[0]; return FsRtlDoesNameContainWildCards( &ValueString ); }
NTSTATUS NdasFatSecondaryQueryDirectory ( 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; FILE_INFORMATION_CLASS FileInformationClass; ULONG FileIndex; BOOLEAN RestartScan; BOOLEAN ReturnSingleEntry; BOOLEAN IndexSpecified; BOOLEAN InitialQuery; VBO CurrentVbo; 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; #if 1 PVOLUME_DEVICE_OBJECT volDo; BOOLEAN secondarySessionResourceAcquired = FALSE; PSECONDARY_REQUEST secondaryRequest = NULL; PNDFS_REQUEST_HEADER ndfsRequestHeader; PNDFS_WINXP_REQUEST_HEADER ndfsWinxpRequestHeader; PNDFS_WINXP_REPLY_HEADER ndfsWinxpReplytHeader; _U8 *ndfsWinxpRequestData; LARGE_INTEGER timeOut; struct QueryDirectory queryDirectory; PVOID inputBuffer; ULONG inputBufferLength; ULONG returnedDataSize; #endif 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 = %08lx\n", Irp); DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.QueryDirectory.Length); DebugTrace( 0, Dbg, " ->FileName = %08lx\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 = %08lx\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 = (PUNICODE_STRING) 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; } #if 1 if (FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_UNOPENED)) { ASSERT( FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_CORRUPTED) ); FatCompleteRequest( IrpContext, Irp, STATUS_FILE_CORRUPT_ERROR ); DebugTrace2( -1, Dbg, ("NtfsCommonDirectoryControl -> STATUS_FILE_CORRUPT_ERROR\n") ); return STATUS_FILE_CORRUPT_ERROR; } #endif // // Initialize the local variables. // Bcb = NULL; UpdateCcb = TRUE; Dirent = NULL; Fat8Dot3String.MaximumLength = 12; Fat8Dot3String.Buffer = 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 (InitialQuery) { 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 (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; } } 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 ); #if 1 volDo = CONTAINING_RECORD( Vcb, VOLUME_DEVICE_OBJECT, Vcb ); secondarySessionResourceAcquired = SecondaryAcquireResourceExclusiveLite( IrpContext, &volDo->SessionResource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); if (FlagOn(volDo->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED)) { NDASFAT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); SetFlag( IrpContext->NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_DONT_POST_REQUEST ); FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); } queryDirectory.FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex; queryDirectory.FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass; queryDirectory.FileName = IrpSp->Parameters.QueryDirectory.FileName; queryDirectory.Length = IrpSp->Parameters.QueryDirectory.Length; inputBuffer = (queryDirectory.FileName) ? (queryDirectory.FileName->Buffer) : NULL; inputBufferLength = (queryDirectory.FileName) ? (queryDirectory.FileName->Length) : 0; if (queryDirectory.FileName) { DebugTrace2( 0, Dbg, ("NdNtfsSecondaryQueryDirectory: queryFileName = %wZ\n", queryDirectory.FileName) ); } ASSERT( inputBufferLength <= volDo->Secondary->Thread.SessionContext.PrimaryMaxDataSize ); ASSERT( UserBufferLength <= volDo->Secondary->Thread.SessionContext.SecondaryMaxDataSize ); secondaryRequest = AllocateWinxpSecondaryRequest( volDo->Secondary, IRP_MJ_DIRECTORY_CONTROL, ((inputBufferLength > UserBufferLength) ? inputBufferLength : UserBufferLength) ); if (secondaryRequest == NULL) { try_return( Status = STATUS_INSUFFICIENT_RESOURCES ); } ndfsRequestHeader = &secondaryRequest->NdfsRequestHeader; INITIALIZE_NDFS_REQUEST_HEADER( ndfsRequestHeader, NDFS_COMMAND_EXECUTE, volDo->Secondary, IRP_MJ_DIRECTORY_CONTROL, inputBufferLength ); ndfsWinxpRequestHeader = (PNDFS_WINXP_REQUEST_HEADER)(ndfsRequestHeader+1); ASSERT( ndfsWinxpRequestHeader == (PNDFS_WINXP_REQUEST_HEADER)secondaryRequest->NdfsRequestData ); INITIALIZE_NDFS_WINXP_REQUEST_HEADER( ndfsWinxpRequestHeader, Irp, IrpSp, Ccb->PrimaryFileHandle ); ndfsWinxpRequestHeader->QueryDirectory.Length = UserBufferLength; ndfsWinxpRequestHeader->QueryDirectory.FileInformationClass = queryDirectory.FileInformationClass; ndfsWinxpRequestHeader->QueryDirectory.FileIndex = queryDirectory.FileIndex; ndfsWinxpRequestData = (_U8 *)(ndfsWinxpRequestHeader+1); if (inputBufferLength) RtlCopyMemory( ndfsWinxpRequestData, inputBuffer, inputBufferLength ); secondaryRequest->RequestType = SECONDARY_REQ_SEND_MESSAGE; QueueingSecondaryRequest( volDo->Secondary, secondaryRequest ); timeOut.QuadPart = -NDASFAT_TIME_OUT; Status = KeWaitForSingleObject( &secondaryRequest->CompleteEvent, Executive, KernelMode, FALSE, &timeOut ); KeClearEvent( &secondaryRequest->CompleteEvent ); if (Status != STATUS_SUCCESS) { secondaryRequest = NULL; try_return( Status = STATUS_IO_DEVICE_ERROR ); } SecondaryReleaseResourceLite( IrpContext, &volDo->SessionResource ); secondarySessionResourceAcquired = FALSE; if (secondaryRequest->ExecuteStatus != STATUS_SUCCESS) { if (IrpContext->OriginatingIrp) PrintIrp( Dbg2, "secondaryRequest->ExecuteStatus != STATUS_SUCCESS", NULL, IrpContext->OriginatingIrp ); DebugTrace2( 0, Dbg2, ("secondaryRequest->ExecuteStatus != STATUS_SUCCESS file = %s, line = %d\n", __FILE__, __LINE__) ); NDASFAT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); SetFlag( IrpContext->NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_DONT_POST_REQUEST ); FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); } ndfsWinxpReplytHeader = (PNDFS_WINXP_REPLY_HEADER)secondaryRequest->NdfsReplyData; Status = Irp->IoStatus.Status = ndfsWinxpReplytHeader->Status; Irp->IoStatus.Information = ndfsWinxpReplytHeader->Information; returnedDataSize = secondaryRequest->NdfsReplyHeader.MessageSize - sizeof(NDFS_REPLY_HEADER) - sizeof(NDFS_WINXP_REPLY_HEADER); if (returnedDataSize) { ASSERT( ndfsWinxpReplytHeader->Information != 0 ); ASSERT(returnedDataSize <= ADD_ALIGN8(queryDirectory.Length)); ASSERT( Buffer ); RtlCopyMemory( Buffer, (_U8 *)(ndfsWinxpReplytHeader+1), (returnedDataSize < queryDirectory.Length) ? returnedDataSize : queryDirectory.Length ); } #endif #if 0 // // 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; } else { CurrentVbo = Ccb->OffsetToStartSearchFrom; } // // If this is the first try then allocate a buffer for the file // name. // if (InitialQuery) { // // 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 = (PUCHAR)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; 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, &Dirent, &Bcb, &NextVbo, NULL, &LongFileName); // // 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. // try { 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. // Fat8dot3ToString( IrpContext, Dirent, TRUE, &Fat8Dot3String ); // // 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 ); } 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. // try { FatGetEaLength( IrpContext, Vcb, Dirent, &FullDirInfo->EaSize ); } except(EXCEPTION_EXECUTE_HANDLER) { FatResetExceptionState( IrpContext ); FullDirInfo->EaSize = 0; } 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 ); } DirInfo->FileAttributes = Dirent->Attributes != 0 ? Dirent->Attributes : 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: 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) { 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 ); } 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 ); ASSERT( Fat8Dot3String.Length <= 12 ); Status = RtlOemToUnicodeN( &BothDirInfo->ShortName[0], 12*sizeof(WCHAR), &ShortNameLength, Fat8Dot3String.Buffer, Fat8Dot3String.Length ); ASSERT( Status != STATUS_BUFFER_OVERFLOW ); 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. // try { FatGetEaLength( IrpContext, Vcb, Dirent, &FullDirInfo->EaSize ); } except(EXCEPTION_EXECUTE_HANDLER) { FatResetExceptionState( IrpContext ); FullDirInfo->EaSize = 0; } 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 ); } DirInfo->FileAttributes = Dirent->Attributes != 0 ? Dirent->Attributes : 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: 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) { 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; } } 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 = GetExceptionCode()); } // // Set ourselves up for the next iteration // LastEntry = NextEntry; NextEntry += (ULONG)QuadAlign(BaseLength + BytesConverted); CurrentVbo = NextVbo + sizeof( DIRENT ); } #endif try_exit: NOTHING; } finally {
VOID CdInitializeEnumeration ( __in PIRP_CONTEXT IrpContext, __in PIO_STACK_LOCATION IrpSp, __in PFCB Fcb, __inout PCCB Ccb, __inout PFILE_ENUM_CONTEXT FileContext, __out PBOOLEAN ReturnNextEntry, __out PBOOLEAN ReturnSingleEntry, __out PBOOLEAN InitialQuery ) /*++ Routine Description: This routine is called to initialize the enumeration variables and structures. We look at the state of a previous enumeration from the Ccb as well as any input values from the user. On exit we will position the FileContext at a file in the directory and let the caller know whether this entry or the next entry should be returned. Arguments: IrpSp - Irp stack location for this request. Fcb - Fcb for this directory. Ccb - Ccb for the directory handle. FileContext - FileContext to use for this enumeration. ReturnNextEntry - Address to store whether we should return the entry at the FileContext position or the next entry. ReturnSingleEntry - Address to store whether we should only return a single entry. InitialQuery - Address to store whether this is the first enumeration query on this handle. Return Value: None. --*/ { NTSTATUS Status; PUNICODE_STRING FileName; CD_NAME WildCardName; CD_NAME SearchExpression; ULONG CcbFlags; ULONG DirentOffset; ULONG LastDirentOffset; BOOLEAN KnownOffset; BOOLEAN Found; PAGED_CODE(); // // If this is the initial query then build a search expression from the input // file name. // if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) { FileName = IrpSp->Parameters.QueryDirectory.FileName; CcbFlags = 0; // // If the filename is not specified or is a single '*' then we will // match all names. // if ((FileName == NULL) || (FileName->Buffer == NULL) || (FileName->Length == 0) || ((FileName->Length == sizeof( WCHAR )) && (FileName->Buffer[0] == L'*'))) { SetFlag( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL ); RtlZeroMemory( &SearchExpression, sizeof( SearchExpression )); // // Otherwise build the CdName from the name in the stack location. // This involves building both the name and version portions and // checking for wild card characters. We also upcase the string if // this is a case-insensitive search. // } else { // // Create a CdName to check for wild cards. // WildCardName.FileName = *FileName; CdConvertNameToCdName( IrpContext, &WildCardName ); // // The name better have at least one character. // if (WildCardName.FileName.Length == 0) { CdRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER ); } // // Check for wildcards in the separate components. // if (FsRtlDoesNameContainWildCards( &WildCardName.FileName)) { SetFlag( CcbFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD ); } if ((WildCardName.VersionString.Length != 0) && (FsRtlDoesNameContainWildCards( &WildCardName.VersionString ))) { SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD ); // // Check if this is a wild card only and match all version // strings. // if ((WildCardName.VersionString.Length == sizeof( WCHAR )) && (WildCardName.VersionString.Buffer[0] == L'*')) { SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL ); } } // // Now create the search expression to store in the Ccb. // SearchExpression.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, FileName->Length, TAG_ENUM_EXPRESSION ); SearchExpression.FileName.MaximumLength = FileName->Length; // // Either copy the name directly or perform the upcase. // if (FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )) { Status = RtlUpcaseUnicodeString( (PUNICODE_STRING) &SearchExpression.FileName, FileName, FALSE ); // // This should never fail. // __analysis_assert( Status == STATUS_SUCCESS ); ASSERT( Status == STATUS_SUCCESS ); } else { RtlCopyMemory( SearchExpression.FileName.Buffer, FileName->Buffer, FileName->Length ); } // // Now split into the separate name and version components. // SearchExpression.FileName.Length = WildCardName.FileName.Length; SearchExpression.VersionString.Length = WildCardName.VersionString.Length; SearchExpression.VersionString.MaximumLength = WildCardName.VersionString.MaximumLength; SearchExpression.VersionString.Buffer = Add2Ptr( SearchExpression.FileName.Buffer, SearchExpression.FileName.Length + sizeof( WCHAR ), PWCHAR ); } // // But we do not want to return the constant "." and ".." entries for // the root directory, for consistency with the rest of Microsoft's // filesystems. // if (Fcb == Fcb->Vcb->RootIndexFcb) { SetFlag( CcbFlags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY ); } // // Now lock the Fcb in order to update the Ccb with the inital // enumeration values. // CdLockFcb( IrpContext, Fcb ); // // Check again that this is the initial search. // if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) { // // Update the values in the Ccb. // Ccb->CurrentDirentOffset = Fcb->StreamOffset; Ccb->SearchExpression = SearchExpression; // // Set the appropriate flags in the Ccb. // SetFlag( Ccb->Flags, CcbFlags | CCB_FLAG_ENUM_INITIALIZED ); // // Otherwise cleanup any buffer allocated here. // } else { if (!FlagOn( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL )) { CdFreePool( &SearchExpression.FileName.Buffer ); } } // // Otherwise lock the Fcb so we can read the current enumeration values. // } else { CdLockFcb( IrpContext, Fcb ); } // // Capture the current state of the enumeration. // // If the user specified an index then use his offset. We always // return the next entry in this case. // if (FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED )) { KnownOffset = FALSE; DirentOffset = IrpSp->Parameters.QueryDirectory.FileIndex; *ReturnNextEntry = TRUE; // // If we are restarting the scan then go from the self entry. // } else if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) { KnownOffset = TRUE; DirentOffset = Fcb->StreamOffset; *ReturnNextEntry = FALSE; // // Otherwise use the values from the Ccb. // } else { KnownOffset = TRUE; DirentOffset = Ccb->CurrentDirentOffset; *ReturnNextEntry = BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT ); } // // Unlock the Fcb. // CdUnlockFcb( IrpContext, Fcb ); // // We have the starting offset in the directory and whether to return // that entry or the next. If we are at the beginning of the directory // and are returning that entry, then tell our caller this is the // initial query. // *InitialQuery = FALSE; if ((DirentOffset == Fcb->StreamOffset) && !(*ReturnNextEntry)) { *InitialQuery = TRUE; } // // If there is no file object then create it now. // CdVerifyOrCreateDirStreamFile( IrpContext, Fcb); // // Determine the offset in the stream to position the FileContext and // whether this offset is known to be a file offset. // // If this offset is known to be safe then go ahead and position the // file context. This handles the cases where the offset is the beginning // of the stream, the offset is from a previous search or this is the // initial query. // if (KnownOffset) { CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, DirentOffset ); // // Otherwise we walk through the directory from the beginning until // we reach the entry which contains this offset. // } else { LastDirentOffset = Fcb->StreamOffset; Found = TRUE; CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset ); // // If the requested offset is prior to the beginning offset in the stream // then don't return the next entry. // if (DirentOffset < LastDirentOffset) { *ReturnNextEntry = FALSE; // // Else look for the last entry which ends past the desired index. // } else { // // Keep walking through the directory until we run out of // entries or we find an entry which ends beyond the input // index value. // do { // // If we have passed the index value then exit. // if (FileContext->InitialDirent->Dirent.DirentOffset > DirentOffset) { Found = FALSE; break; } // // Remember the current position in case we need to go back. // LastDirentOffset = FileContext->InitialDirent->Dirent.DirentOffset; // // Exit if the next entry is beyond the desired index value. // if (LastDirentOffset + FileContext->InitialDirent->Dirent.DirentLength > DirentOffset) { break; } Found = CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext ); } while (Found); // // If we didn't find the entry then go back to the last known entry. // This can happen if the index lies in the unused range at the // end of a sector. // if (!Found) { CdCleanupFileContext( IrpContext, FileContext ); CdInitializeFileContext( IrpContext, FileContext ); CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset ); } } } // // Only update the dirent name if we will need it for some reason. // Don't update this name if we are returning the next entry and // the search string has a version component. // FileContext->ShortName.FileName.Length = 0; if (!(*ReturnNextEntry) || (Ccb->SearchExpression.VersionString.Length == 0)) { // // Update the name in the dirent into filename and version components. // CdUpdateDirentName( IrpContext, &FileContext->InitialDirent->Dirent, FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )); } // // Look at the flag in the IrpSp indicating whether to return just // one entry. // *ReturnSingleEntry = FALSE; if (FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )) { *ReturnSingleEntry = TRUE; } return; }
static NTSTATUS query_directory(PIRP Irp) { PIO_STACK_LOCATION IrpSp; NTSTATUS Status, status2; fcb* fcb; ccb* ccb; file_ref* fileref; device_extension* Vcb; void* buf; UINT8 *curitem, *lastitem; LONG length; ULONG count; BOOL has_wildcard = FALSE, specific_file = FALSE, initial; dir_entry de; UINT64 newoffset; dir_child* dc = NULL; TRACE("query directory\n"); IrpSp = IoGetCurrentIrpStackLocation(Irp); fcb = IrpSp->FileObject->FsContext; ccb = IrpSp->FileObject->FsContext2; fileref = ccb ? ccb->fileref : NULL; if (!fileref) return STATUS_INVALID_PARAMETER; if (!ccb) { ERR("ccb was NULL\n"); return STATUS_INVALID_PARAMETER; } if (!fcb) { ERR("fcb was NULL\n"); return STATUS_INVALID_PARAMETER; } if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_LIST_DIRECTORY)) { WARN("insufficient privileges\n"); return STATUS_ACCESS_DENIED; } Vcb = fcb->Vcb; if (!Vcb) { ERR("Vcb was NULL\n"); return STATUS_INVALID_PARAMETER; } if (fileref->fcb == Vcb->dummy_fcb) return STATUS_NO_MORE_FILES; if (IrpSp->Flags == 0) { TRACE("QD flags: (none)\n"); } else { ULONG flags = IrpSp->Flags; TRACE("QD flags:\n"); if (flags & SL_INDEX_SPECIFIED) { TRACE(" SL_INDEX_SPECIFIED\n"); flags &= ~SL_INDEX_SPECIFIED; } if (flags & SL_RESTART_SCAN) { TRACE(" SL_RESTART_SCAN\n"); flags &= ~SL_RESTART_SCAN; } if (flags & SL_RETURN_SINGLE_ENTRY) { TRACE(" SL_RETURN_SINGLE_ENTRY\n"); flags &= ~SL_RETURN_SINGLE_ENTRY; } if (flags != 0) TRACE(" unknown flags: %u\n", flags); } if (IrpSp->Flags & SL_RESTART_SCAN) { ccb->query_dir_offset = 0; if (ccb->query_string.Buffer) { RtlFreeUnicodeString(&ccb->query_string); ccb->query_string.Buffer = NULL; } ccb->has_wildcard = FALSE; ccb->specific_file = FALSE; } initial = !ccb->query_string.Buffer; if (IrpSp->Parameters.QueryDirectory.FileName && IrpSp->Parameters.QueryDirectory.FileName->Length > 1) { TRACE("QD filename: %.*S\n", IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR), IrpSp->Parameters.QueryDirectory.FileName->Buffer); if (IrpSp->Parameters.QueryDirectory.FileName->Length > sizeof(WCHAR) || IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != L'*') { specific_file = TRUE; if (FsRtlDoesNameContainWildCards(IrpSp->Parameters.QueryDirectory.FileName)) { has_wildcard = TRUE; specific_file = FALSE; } } if (ccb->query_string.Buffer) RtlFreeUnicodeString(&ccb->query_string); if (has_wildcard) RtlUpcaseUnicodeString(&ccb->query_string, IrpSp->Parameters.QueryDirectory.FileName, TRUE); else { ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG); if (!ccb->query_string.Buffer) { ERR("out of memory\n"); return STATUS_INSUFFICIENT_RESOURCES; } ccb->query_string.Length = ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length; RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length); } ccb->has_wildcard = has_wildcard; ccb->specific_file = specific_file; } else { has_wildcard = ccb->has_wildcard; specific_file = ccb->specific_file; if (!(IrpSp->Flags & SL_RESTART_SCAN)) { initial = FALSE; if (specific_file) return STATUS_NO_MORE_FILES; } } if (ccb->query_string.Buffer) { TRACE("query string = %.*S\n", ccb->query_string.Length / sizeof(WCHAR), ccb->query_string.Buffer); } newoffset = ccb->query_dir_offset; ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE); Status = next_dir_entry(fileref, &newoffset, &de, &dc); if (!NT_SUCCESS(Status)) { if (Status == STATUS_NO_MORE_FILES && initial) Status = STATUS_NO_SUCH_FILE; goto end; } ccb->query_dir_offset = newoffset; buf = map_user_buffer(Irp, NormalPagePriority); if (Irp->MdlAddress && !buf) { ERR("MmGetSystemAddressForMdlSafe returned NULL\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } length = IrpSp->Parameters.QueryDirectory.Length; if (specific_file) { BOOL found = FALSE; UNICODE_STRING us; LIST_ENTRY* le; UINT32 hash; UINT8 c; us.Buffer = NULL; if (!ccb->case_sensitive) { Status = RtlUpcaseUnicodeString(&us, &ccb->query_string, TRUE); if (!NT_SUCCESS(Status)) { ERR("RtlUpcaseUnicodeString returned %08x\n", Status); goto end; } hash = calc_crc32c(0xffffffff, (UINT8*)us.Buffer, us.Length); } else hash = calc_crc32c(0xffffffff, (UINT8*)ccb->query_string.Buffer, ccb->query_string.Length); c = hash >> 24; if (ccb->case_sensitive) { if (fileref->fcb->hash_ptrs[c]) { le = fileref->fcb->hash_ptrs[c]; while (le != &fileref->fcb->dir_children_hash) { dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash); if (dc2->hash == hash) { if (dc2->name.Length == ccb->query_string.Length && RtlCompareMemory(dc2->name.Buffer, ccb->query_string.Buffer, ccb->query_string.Length) == ccb->query_string.Length) { found = TRUE; de.key = dc2->key; de.name = dc2->name; de.type = dc2->type; de.dir_entry_type = DirEntryType_File; de.dc = dc2; break; } } else if (dc2->hash > hash) break; le = le->Flink; } } } else { if (fileref->fcb->hash_ptrs_uc[c]) { le = fileref->fcb->hash_ptrs_uc[c]; while (le != &fileref->fcb->dir_children_hash_uc) { dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc); if (dc2->hash_uc == hash) { if (dc2->name_uc.Length == us.Length && RtlCompareMemory(dc2->name_uc.Buffer, us.Buffer, us.Length) == us.Length) { found = TRUE; de.key = dc2->key; de.name = dc2->name; de.type = dc2->type; de.dir_entry_type = DirEntryType_File; de.dc = dc2; break; } } else if (dc2->hash_uc > hash) break; le = le->Flink; } } } if (us.Buffer) ExFreePool(us.Buffer); if (!found) { Status = STATUS_NO_SUCH_FILE; goto end; } } else if (has_wildcard) {
__drv_mustHoldCriticalRegion NTSTATUS FFSQueryDirectory( IN PFFS_IRP_CONTEXT IrpContext) { PDEVICE_OBJECT DeviceObject; NTSTATUS Status = STATUS_UNSUCCESSFUL; PFFS_VCB Vcb = 0; PFILE_OBJECT FileObject; PFFS_FCB Fcb = 0; PFFS_CCB Ccb; PIRP Irp; PIO_STACK_LOCATION IoStackLocation; FILE_INFORMATION_CLASS FileInformationClass; ULONG Length; PUNICODE_STRING FileName; ULONG FileIndex; BOOLEAN RestartScan; BOOLEAN ReturnSingleEntry; BOOLEAN IndexSpecified; PUCHAR Buffer; BOOLEAN FirstQuery; PFFSv1_INODE dinode1 = NULL; PFFSv2_INODE dinode2 = NULL; BOOLEAN FcbResourceAcquired = FALSE; ULONG UsedLength = 0; USHORT InodeFileNameLength; UNICODE_STRING InodeFileName; PFFS_DIR_ENTRY pDir = NULL; ULONG dwBytes; ULONG dwTemp = 0; ULONG dwSize = 0; ULONG dwReturn = 0; BOOLEAN bRun = TRUE; ULONG ByteOffset; PAGED_CODE(); InodeFileName.Buffer = NULL; _SEH2_TRY { ASSERT(IrpContext); ASSERT((IrpContext->Identifier.Type == FFSICX) && (IrpContext->Identifier.Size == sizeof(FFS_IRP_CONTEXT))); DeviceObject = IrpContext->DeviceObject; // // This request is not allowed on the main device object // if (DeviceObject == FFSGlobal->DeviceObject) { Status = STATUS_INVALID_DEVICE_REQUEST; _SEH2_LEAVE; } Vcb = (PFFS_VCB)DeviceObject->DeviceExtension; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == FFSVCB) && (Vcb->Identifier.Size == sizeof(FFS_VCB))); ASSERT(IsMounted(Vcb)); FileObject = IrpContext->FileObject; Fcb = (PFFS_FCB)FileObject->FsContext; ASSERT(Fcb); // // This request is not allowed on volumes // if (Fcb->Identifier.Type == FFSVCB) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } ASSERT((Fcb->Identifier.Type == FFSFCB) && (Fcb->Identifier.Size == sizeof(FFS_FCB))); if (!IsDirectory(Fcb)) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } Ccb = (PFFS_CCB)FileObject->FsContext2; ASSERT(Ccb); ASSERT((Ccb->Identifier.Type == FFSCCB) && (Ccb->Identifier.Size == sizeof(FFS_CCB))); Irp = IrpContext->Irp; IoStackLocation = IoGetCurrentIrpStackLocation(Irp); #if !defined(_GNU_NTIFS_) || defined(__REACTOS__) FileInformationClass = IoStackLocation->Parameters.QueryDirectory.FileInformationClass; Length = IoStackLocation->Parameters.QueryDirectory.Length; FileName = IoStackLocation->Parameters.QueryDirectory.FileName; FileIndex = IoStackLocation->Parameters.QueryDirectory.FileIndex; #else // _GNU_NTIFS_ FileInformationClass = ((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Parameters.QueryDirectory.FileInformationClass; Length = ((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Parameters.QueryDirectory.Length; FileName = ((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Parameters.QueryDirectory.FileName; FileIndex = ((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Parameters.QueryDirectory.FileIndex; #endif // _GNU_NTIFS_ RestartScan = FlagOn(IoStackLocation->Flags, SL_RESTART_SCAN); ReturnSingleEntry = FlagOn(IoStackLocation->Flags, SL_RETURN_SINGLE_ENTRY); IndexSpecified = FlagOn(IoStackLocation->Flags, SL_INDEX_SPECIFIED); /* if (!Irp->MdlAddress && Irp->UserBuffer) { ProbeForWrite(Irp->UserBuffer, Length, 1); } */ Buffer = FFSGetUserBuffer(Irp); if (Buffer == NULL) { FFSBreakPoint(); Status = STATUS_INVALID_USER_BUFFER; _SEH2_LEAVE; } if (!IrpContext->IsSynchronous) { Status = STATUS_PENDING; _SEH2_LEAVE; } if (!ExAcquireResourceSharedLite( &Fcb->MainResource, IrpContext->IsSynchronous)) { Status = STATUS_PENDING; _SEH2_LEAVE; } FcbResourceAcquired = TRUE; if (FileName != NULL) { if (Ccb->DirectorySearchPattern.Buffer != NULL) { FirstQuery = FALSE; } else { FirstQuery = TRUE; Ccb->DirectorySearchPattern.Length = Ccb->DirectorySearchPattern.MaximumLength = FileName->Length; Ccb->DirectorySearchPattern.Buffer = ExAllocatePoolWithTag(PagedPool, FileName->Length, FFS_POOL_TAG); if (Ccb->DirectorySearchPattern.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } Status = RtlUpcaseUnicodeString( &(Ccb->DirectorySearchPattern), FileName, FALSE); if (!NT_SUCCESS(Status)) _SEH2_LEAVE; } } else if (Ccb->DirectorySearchPattern.Buffer != NULL) { FirstQuery = FALSE; FileName = &Ccb->DirectorySearchPattern; } else { FirstQuery = TRUE; Ccb->DirectorySearchPattern.Length = Ccb->DirectorySearchPattern.MaximumLength = 2; Ccb->DirectorySearchPattern.Buffer = ExAllocatePoolWithTag(PagedPool, 2, FFS_POOL_TAG); if (Ccb->DirectorySearchPattern.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } RtlCopyMemory( Ccb->DirectorySearchPattern.Buffer, L"*\0", 2); } if (!IndexSpecified) { if (RestartScan || FirstQuery) { FileIndex = Fcb->FFSMcb->DeOffset = 0; } else { FileIndex = Ccb->CurrentByteOffset; } } if (FS_VERSION == 1) { dinode1 = (PFFSv1_INODE)ExAllocatePoolWithTag( PagedPool, DINODE1_SIZE, FFS_POOL_TAG); if (dinode1 == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } RtlZeroMemory(Buffer, Length); if (Fcb->dinode1->di_size <= FileIndex) { Status = STATUS_NO_MORE_FILES; _SEH2_LEAVE; } } else { dinode2 = (PFFSv2_INODE)ExAllocatePoolWithTag( PagedPool, DINODE2_SIZE, FFS_POOL_TAG); if (dinode2 == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } RtlZeroMemory(Buffer, Length); if (Fcb->dinode2->di_size <= FileIndex) { Status = STATUS_NO_MORE_FILES; _SEH2_LEAVE; } } pDir = ExAllocatePoolWithTag(PagedPool, sizeof(FFS_DIR_ENTRY), FFS_POOL_TAG); if (!pDir) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } if (FS_VERSION == 1) { dwBytes = 0; dwSize = (ULONG)Fcb->dinode1->di_size - FileIndex - (sizeof(FFS_DIR_ENTRY) - FFS_NAME_LEN + 1); ByteOffset = FileIndex; dwTemp = 0; while (bRun && UsedLength < Length && dwBytes < dwSize) { OEM_STRING OemName; RtlZeroMemory(pDir, sizeof(FFS_DIR_ENTRY)); Status = FFSv1ReadInode( NULL, Vcb, Fcb->dinode1, ByteOffset, (PVOID)pDir, sizeof(FFS_DIR_ENTRY), &dwReturn); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } if (!pDir->d_ino) { if (pDir->d_reclen == 0) { FFSBreakPoint(); _SEH2_LEAVE; } goto ProcessNextEntryv1; } OemName.Buffer = pDir->d_name; OemName.Length = (pDir->d_namlen & 0xff); OemName.MaximumLength = OemName.Length; #if 0 /* // // We could not filter the files: "." and ".." // if ((OemName.Length >) 1 && OemName.Buffer[0] == '.') { if ( OemName.Length == 2 && OemName.Buffer[1] == '.') { } else { goto ProcessNextEntry1; } } */ #endif InodeFileNameLength = (USHORT) RtlOemStringToUnicodeSize(&OemName); InodeFileName.Length = 0; InodeFileName.MaximumLength = InodeFileNameLength + 2; if (InodeFileNameLength <= 0) { break; } InodeFileName.Buffer = ExAllocatePoolWithTag( PagedPool, InodeFileNameLength + 2, FFS_POOL_TAG); if (!InodeFileName.Buffer) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } RtlZeroMemory( InodeFileName.Buffer, InodeFileNameLength + 2); Status = FFSOEMToUnicode(&InodeFileName, &OemName); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } if (FsRtlDoesNameContainWildCards( &(Ccb->DirectorySearchPattern)) ? FsRtlIsNameInExpression( &(Ccb->DirectorySearchPattern), &InodeFileName, TRUE, NULL) : !RtlCompareUnicodeString( &(Ccb->DirectorySearchPattern), &InodeFileName, TRUE)) { dwReturn = FFSProcessDirEntry( Vcb, FileInformationClass, pDir->d_ino, Buffer, UsedLength, Length - UsedLength, (FileIndex + dwBytes), &InodeFileName, ReturnSingleEntry); if (dwReturn <= 0) { bRun = FALSE; } else { dwTemp = UsedLength; UsedLength += dwReturn; } } if (InodeFileName.Buffer != NULL) { ExFreePool(InodeFileName.Buffer); InodeFileName.Buffer = NULL; } ProcessNextEntryv1: if (bRun) { dwBytes +=pDir->d_reclen; Ccb->CurrentByteOffset = FileIndex + dwBytes; } if (UsedLength && ReturnSingleEntry) { Status = STATUS_SUCCESS; _SEH2_LEAVE; } ByteOffset = FileIndex + dwBytes; } } else { dwBytes = 0; dwSize = (ULONG)Fcb->dinode2->di_size - FileIndex - (sizeof(FFS_DIR_ENTRY) - FFS_NAME_LEN + 1); ByteOffset = FileIndex; dwTemp = 0; while (bRun && UsedLength < Length && dwBytes < dwSize) { OEM_STRING OemName; RtlZeroMemory(pDir, sizeof(FFS_DIR_ENTRY)); Status = FFSv2ReadInode( NULL, Vcb, Fcb->dinode2, ByteOffset, (PVOID)pDir, sizeof(FFS_DIR_ENTRY), &dwReturn); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } if (!pDir->d_ino) { if (pDir->d_reclen == 0) { FFSBreakPoint(); _SEH2_LEAVE; } goto ProcessNextEntryv2; } OemName.Buffer = pDir->d_name; OemName.Length = (pDir->d_namlen & 0xff); OemName.MaximumLength = OemName.Length; #if 0 /* // // We could not filter the files: "." and ".." // if ((OemName.Length >) 1 && OemName.Buffer[0] == '.') { if ( OemName.Length == 2 && OemName.Buffer[1] == '.') { } else { goto ProcessNextEntry2; } } */ #endif InodeFileNameLength = (USHORT) RtlOemStringToUnicodeSize(&OemName); InodeFileName.Length = 0; InodeFileName.MaximumLength = InodeFileNameLength + 2; if (InodeFileNameLength <= 0) { break; } InodeFileName.Buffer = ExAllocatePoolWithTag( PagedPool, InodeFileNameLength + 2, FFS_POOL_TAG); if (!InodeFileName.Buffer) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } RtlZeroMemory( InodeFileName.Buffer, InodeFileNameLength + 2); Status = FFSOEMToUnicode(&InodeFileName, &OemName); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } if (FsRtlDoesNameContainWildCards( &(Ccb->DirectorySearchPattern)) ? FsRtlIsNameInExpression( &(Ccb->DirectorySearchPattern), &InodeFileName, TRUE, NULL) : !RtlCompareUnicodeString( &(Ccb->DirectorySearchPattern), &InodeFileName, TRUE)) { dwReturn = FFSProcessDirEntry( Vcb, FileInformationClass, pDir->d_ino, Buffer, UsedLength, Length - UsedLength, (FileIndex + dwBytes), &InodeFileName, ReturnSingleEntry); if (dwReturn <= 0) { bRun = FALSE; } else { dwTemp = UsedLength; UsedLength += dwReturn; } } if (InodeFileName.Buffer != NULL) { ExFreePool(InodeFileName.Buffer); InodeFileName.Buffer = NULL; } ProcessNextEntryv2: if (bRun) { dwBytes +=pDir->d_reclen; Ccb->CurrentByteOffset = FileIndex + dwBytes; } if (UsedLength && ReturnSingleEntry) { Status = STATUS_SUCCESS; _SEH2_LEAVE; } ByteOffset = FileIndex + dwBytes; } } FileIndex += dwBytes; ((PULONG)((PUCHAR)Buffer + dwTemp)) [0] = 0; if (!UsedLength) { if (FirstQuery) { Status = STATUS_NO_SUCH_FILE; } else { Status = STATUS_NO_MORE_FILES; } } else { Status = STATUS_SUCCESS; } } _SEH2_FINALLY { if (FcbResourceAcquired) { ExReleaseResourceForThreadLite( &Fcb->MainResource, ExGetCurrentResourceThread()); } if (FS_VERSION == 1) { if (dinode1 != NULL) { ExFreePool(dinode1); } } else { if (dinode2 != NULL) { ExFreePool(dinode2); } } if (pDir != NULL) { ExFreePool(pDir); pDir = NULL; } if (InodeFileName.Buffer != NULL) { ExFreePool(InodeFileName.Buffer); } if (!IrpContext->ExceptionInProgress) { if (Status == STATUS_PENDING) { Status = FFSLockUserBuffer( IrpContext->Irp, Length, IoWriteAccess); if (NT_SUCCESS(Status)) { Status = FFSQueueRequest(IrpContext); } else { FFSCompleteIrpContext(IrpContext, Status); } } else { IrpContext->Irp->IoStatus.Information = UsedLength; FFSCompleteIrpContext(IrpContext, Status); } } } _SEH2_END; return Status; }
/************************************************************************* * * Function: UDFQueryDirectory() * * Description: * Query directory request. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: STATUS_SUCCESS/Error * *************************************************************************/ NTSTATUS NTAPI UDFQueryDirectory( PtrUDFIrpContext PtrIrpContext, PIRP Irp, PIO_STACK_LOCATION IrpSp, PFILE_OBJECT FileObject, PtrUDFFCB Fcb, PtrUDFCCB Ccb ) { NTSTATUS RC = STATUS_SUCCESS; BOOLEAN PostRequest = FALSE; PtrUDFNTRequiredFCB NtReqFcb = NULL; BOOLEAN CanWait = FALSE; PVCB Vcb = NULL; BOOLEAN AcquiredFCB = FALSE; unsigned long BufferLength = 0; UNICODE_STRING SearchPattern; PUNICODE_STRING PtrSearchPattern; FILE_INFORMATION_CLASS FileInformationClass; BOOLEAN ReturnSingleEntry = FALSE; PUCHAR Buffer = NULL; BOOLEAN FirstTimeQuery = FALSE; LONG NextMatch; LONG PrevMatch = -1; ULONG CurrentOffset; ULONG BaseLength; ULONG FileNameBytes; ULONG Information = 0; ULONG LastOffset = 0; BOOLEAN AtLeastOneFound = FALSE; PEXTENDED_IO_STACK_LOCATION pStackLocation = (PEXTENDED_IO_STACK_LOCATION) IrpSp; PUDF_FILE_INFO DirFileInfo = NULL; PDIR_INDEX_HDR hDirIndex = NULL; PFILE_BOTH_DIR_INFORMATION DirInformation = NULL; // Returned from udf_info module PFILE_BOTH_DIR_INFORMATION BothDirInformation = NULL; // Pointer in callers buffer PFILE_NAMES_INFORMATION NamesInfo; ULONG BytesRemainingInBuffer; UCHAR FNM_Flags = 0; PHASH_ENTRY cur_hashes = NULL; PDIR_INDEX_ITEM DirNdx; // do some pre-init... SearchPattern.Buffer = NULL; UDFPrint(("UDFQueryDirectory: @=%#x\n", &PtrIrpContext)); #define CanBe8dot3 (FNM_Flags & UDF_FNM_FLAG_CAN_BE_8D3) #define IgnoreCase (FNM_Flags & UDF_FNM_FLAG_IGNORE_CASE) #define ContainsWC (FNM_Flags & UDF_FNM_FLAG_CONTAINS_WC) _SEH2_TRY { // Validate the sent-in FCB if ((Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) || !(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) { // We will only allow notify requests on directories. try_return(RC = STATUS_INVALID_PARAMETER); } // Obtain the callers parameters NtReqFcb = Fcb->NTRequiredFCB; CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; Vcb = Fcb->Vcb; //Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_CASE_SENSETIVE) ? 0 : UDF_FNM_FLAG_IGNORE_CASE; DirFileInfo = Fcb->FileInfo; BufferLength = pStackLocation->Parameters.QueryDirectory.Length; // If the caller does not want to block, it would be easier to // simply post the request now. if (!CanWait) { PostRequest = TRUE; try_return(RC = STATUS_PENDING); } // Continue obtaining the callers parameters... if(IgnoreCase && pStackLocation->Parameters.QueryDirectory.FileName) { PtrSearchPattern = &SearchPattern; if(!NT_SUCCESS(RC = RtlUpcaseUnicodeString(PtrSearchPattern, (PUNICODE_STRING)(pStackLocation->Parameters.QueryDirectory.FileName), TRUE))) try_return(RC); } else { PtrSearchPattern = (PUNICODE_STRING)(pStackLocation->Parameters.QueryDirectory.FileName); } FileInformationClass = pStackLocation->Parameters.QueryDirectory.FileInformationClass; // Calculate baselength (without name) for each InfoClass 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 FileNamesInformation: BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName[0] ); break; case FileBothDirectoryInformation: BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] ); break; default: try_return(RC = STATUS_INVALID_INFO_CLASS); } // Some additional arguments that affect the FSD behavior ReturnSingleEntry = (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) ? TRUE : FALSE; UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFAcquireResourceShared(&(NtReqFcb->MainResource), TRUE); AcquiredFCB = TRUE; // We must determine the buffer pointer to be used. Since this // routine could either be invoked directly in the context of the // calling thread, or in the context of a worker thread, here is // a general way of determining what we should use. if(Irp->MdlAddress) { Buffer = (PUCHAR) MmGetSystemAddressForMdlSafer(Irp->MdlAddress); if(!Buffer) try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } else { Buffer = (PUCHAR) Irp->UserBuffer; if(!Buffer) try_return(RC = STATUS_INVALID_USER_BUFFER); } // The method of determining where to look from and what to look for is // unfortunately extremely confusing. However, here is a methodology // we broadly adopt: // (a) We have to maintain a search buffer per CCB structure. // (b) This search buffer is initialized the very first time // a query directory operation is performed using the file object. // (For the UDF FSD, the search buffer is stored in the // DirectorySearchPattern field) // However, the caller still has the option of "overriding" this stored // search pattern by supplying a new one in a query directory operation. if(PtrSearchPattern && PtrSearchPattern->Buffer && !(PtrSearchPattern->Buffer[PtrSearchPattern->Length/sizeof(WCHAR) - 1])) { PtrSearchPattern->Length -= sizeof(WCHAR); } if(IrpSp->Flags & SL_INDEX_SPECIFIED) { // Good idea from M$: we should continue search from NEXT item // when FileIndex specified... // Strange idea from M$: we should do it with EMPTY pattern... PtrSearchPattern = NULL; Ccb->CCBFlags |= UDF_CCB_MATCH_ALL; } else if(PtrSearchPattern && PtrSearchPattern->Buffer && !UDFIsMatchAllMask(PtrSearchPattern, NULL) ) { Ccb->CCBFlags &= ~(UDF_CCB_MATCH_ALL | UDF_CCB_WILDCARD_PRESENT | UDF_CCB_CAN_BE_8_DOT_3); // Once we have validated the search pattern, we must // check whether we need to store this search pattern in // the CCB. if(Ccb->DirectorySearchPattern) { MyFreePool__(Ccb->DirectorySearchPattern->Buffer); MyFreePool__(Ccb->DirectorySearchPattern); Ccb->DirectorySearchPattern = NULL; } // This must be the very first query request. FirstTimeQuery = TRUE; // Now, allocate enough memory to contain the caller // supplied search pattern and fill in the DirectorySearchPattern // field in the CCB Ccb->DirectorySearchPattern = (PUNICODE_STRING)MyAllocatePool__(NonPagedPool,sizeof(UNICODE_STRING)); if(!(Ccb->DirectorySearchPattern)) { try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } Ccb->DirectorySearchPattern->Length = PtrSearchPattern->Length; Ccb->DirectorySearchPattern->MaximumLength = PtrSearchPattern->MaximumLength; Ccb->DirectorySearchPattern->Buffer = (PWCHAR)MyAllocatePool__(NonPagedPool,PtrSearchPattern->MaximumLength); if(!(Ccb->DirectorySearchPattern->Buffer)) { try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } RtlCopyMemory(Ccb->DirectorySearchPattern->Buffer,PtrSearchPattern->Buffer, PtrSearchPattern->MaximumLength); if(FsRtlDoesNameContainWildCards(PtrSearchPattern)) { Ccb->CCBFlags |= UDF_CCB_WILDCARD_PRESENT; } else { UDFBuildHashEntry(Vcb, PtrSearchPattern, cur_hashes = &(Ccb->hashes), HASH_POSIX | HASH_ULFN); } if(UDFCanNameBeA8dot3(PtrSearchPattern)) Ccb->CCBFlags |= UDF_CCB_CAN_BE_8_DOT_3; } else if(!Ccb->DirectorySearchPattern && !(Ccb->CCBFlags & UDF_CCB_MATCH_ALL) ) { // If the filename is not specified or is a single '*' then we will // match all names. FirstTimeQuery = TRUE; PtrSearchPattern = NULL; Ccb->CCBFlags |= UDF_CCB_MATCH_ALL; } else { // The caller has not supplied any search pattern that we are // forced to use. However, the caller had previously supplied // a pattern (or we must have invented one) and we will use it. // This is definitely not the first query operation on this // directory using this particular file object. if(Ccb->CCBFlags & UDF_CCB_MATCH_ALL) { PtrSearchPattern = NULL; /* if(Ccb->CurrentIndex) Ccb->CurrentIndex++;*/ } else { PtrSearchPattern = Ccb->DirectorySearchPattern; if(!(Ccb->CCBFlags & UDF_CCB_WILDCARD_PRESENT)) { cur_hashes = &(Ccb->hashes); } } } if(IrpSp->Flags & SL_INDEX_SPECIFIED) { // Caller has told us wherefrom to begin. // We may need to round this to an appropriate directory entry // entry alignment value. NextMatch = pStackLocation->Parameters.QueryDirectory.FileIndex + 1; } else if(IrpSp->Flags & SL_RESTART_SCAN) { NextMatch = 0; } else { // Get the starting offset from the CCB. // Remember to update this value on our way out from this function. // But, do not update the CCB CurrentByteOffset field if our reach // the end of the directory (or get an error reading the directory) // while performing the search. NextMatch = Ccb->CurrentIndex + 1; // Last good index } FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_WILDCARD_PRESENT) ? UDF_FNM_FLAG_CONTAINS_WC : 0; // this is used only when mask is supplied FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_CAN_BE_8_DOT_3) ? UDF_FNM_FLAG_CAN_BE_8D3 : 0; // This is an additional verifying if(!UDFIsADirectory(DirFileInfo)) { try_return(RC = STATUS_INVALID_PARAMETER); } hDirIndex = DirFileInfo->Dloc->DirIndex; if(!hDirIndex) { try_return(RC = STATUS_INVALID_PARAMETER); } RC = STATUS_SUCCESS; // Allocate buffer enough to save both DirInformation and FileName DirInformation = (PFILE_BOTH_DIR_INFORMATION)MyAllocatePool__(NonPagedPool, sizeof(FILE_BOTH_DIR_INFORMATION)+((ULONG)UDF_NAME_LEN*sizeof(WCHAR)) ); if(!DirInformation) { try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } CurrentOffset=0; BytesRemainingInBuffer = pStackLocation->Parameters.QueryDirectory.Length; RtlZeroMemory(Buffer,BytesRemainingInBuffer); if((!FirstTimeQuery) && !UDFDirIndex(hDirIndex, (uint_di)NextMatch) ) { try_return( RC = STATUS_NO_MORE_FILES); } // One final note though: // If we do not find a directory entry OR while searching we reach the // end of the directory, then the return code should be set as follows: // (a) If any files have been returned (i.e. ReturnSingleEntry was FALSE // and we did find at least one match), then return STATUS_SUCCESS // (b) If no entry is being returned then: // (i) If this is the first query i.e. FirstTimeQuery is TRUE // then return STATUS_NO_SUCH_FILE // (ii) Otherwise, return STATUS_NO_MORE_FILES while(TRUE) { // If the user had requested only a single match and we have // returned that, then we stop at this point. if(ReturnSingleEntry && AtLeastOneFound) { try_return(RC); } // We call UDFFindNextMatch to look down the next matching dirent. RC = UDFFindNextMatch(Vcb, hDirIndex,&NextMatch,PtrSearchPattern, FNM_Flags, cur_hashes, &DirNdx); // If we didn't receive next match, 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(!NT_SUCCESS(RC)) { RC = AtLeastOneFound ? STATUS_SUCCESS : (FirstTimeQuery ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES); try_return(RC); } // We found at least one matching file entry AtLeastOneFound = TRUE; if(!NT_SUCCESS(RC = UDFFileDirInfoToNT(Vcb, DirNdx, DirInformation))) { // this happends when we can't allocate tmp buffers try_return(RC); } DirInformation->FileIndex = NextMatch; FileNameBytes = DirInformation->FileNameLength; if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) { // We haven't successfully transfered current data & // later NextMatch will be incremented. Thus we should // prevent loosing information in such a way: if(NextMatch) NextMatch --; // If this won't fit and we have returned a previous entry then just // return STATUS_SUCCESS. Otherwise // use a status code of STATUS_BUFFER_OVERFLOW. if(CurrentOffset) { try_return(RC = STATUS_SUCCESS); } // strange policy... ReturnSingleEntry = TRUE; FileNameBytes = BaseLength + FileNameBytes - BytesRemainingInBuffer; RC = STATUS_BUFFER_OVERFLOW; } // Now we have an entry to return to our caller. // We'll case on the type of information requested and fill up // the user buffer if everything fits. switch (FileInformationClass) { case FileBothDirectoryInformation: case FileFullDirectoryInformation: case FileDirectoryInformation: BothDirInformation = (PFILE_BOTH_DIR_INFORMATION)(Buffer + CurrentOffset); RtlCopyMemory(BothDirInformation,DirInformation,BaseLength); BothDirInformation->FileIndex = NextMatch; BothDirInformation->FileNameLength = FileNameBytes; break; case FileNamesInformation: NamesInfo = (PFILE_NAMES_INFORMATION)(Buffer + CurrentOffset); NamesInfo->FileIndex = NextMatch; NamesInfo->FileNameLength = FileNameBytes; break; default: break; } if (FileNameBytes) { // This is a Unicode name, we can copy the bytes directly. RtlCopyMemory( (PVOID)(Buffer + CurrentOffset + BaseLength), DirInformation->FileName, FileNameBytes ); } Information = CurrentOffset + BaseLength + FileNameBytes; // ((..._INFORMATION)(PointerToPreviousEntryInBuffer))->NextEntryOffset = CurrentOffset - LastOffset; *((PULONG)(Buffer+LastOffset)) = CurrentOffset - LastOffset; // Set up our variables for the next dirent. FirstTimeQuery = FALSE; LastOffset = CurrentOffset; PrevMatch = NextMatch; NextMatch++; CurrentOffset = UDFQuadAlign(Information); BytesRemainingInBuffer = BufferLength - CurrentOffset; } try_exit: NOTHING; } _SEH2_FINALLY { if (PostRequest) { if (AcquiredFCB) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); } // Map the users buffer and then post the request. RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, BufferLength); ASSERT(NT_SUCCESS(RC)); RC = UDFPostRequest(PtrIrpContext, Irp); } else { #ifdef UDF_DBG if(!NT_SUCCESS(RC)) { UDFPrint((" Not found\n")); } #endif // UDF_DBG // Remember to update the CurrentByteOffset field in the CCB if required. if(Ccb) Ccb->CurrentIndex = PrevMatch; if (AcquiredFCB) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); } if (!_SEH2_AbnormalTermination()) { // complete the IRP Irp->IoStatus.Status = RC; Irp->IoStatus.Information = Information; IoCompleteRequest(Irp, IO_DISK_INCREMENT); // Free up the Irp Context UDFReleaseIrpContext(PtrIrpContext); } } if(SearchPattern.Buffer) RtlFreeUnicodeString(&SearchPattern); if(DirInformation) MyFreePool__(DirInformation); } _SEH2_END; return(RC); } // end UDFQueryDirectory()
/************************************************************************* * * Function: Ext2QueryDirectory() * * Description: * Query directory request. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: STATUS_SUCCESS/Error * *************************************************************************/ NTSTATUS NTAPI Ext2QueryDirectory( PtrExt2IrpContext PtrIrpContext, PIRP PtrIrp, #ifdef _GNU_NTIFS_ PEXTENDED_IO_STACK_LOCATION PtrIoStackLocation, #else PIO_STACK_LOCATION PtrIoStackLocation, #endif PFILE_OBJECT PtrFileObject, PtrExt2FCB PtrFCB, PtrExt2CCB PtrCCB) { NTSTATUS RC = STATUS_SUCCESS; BOOLEAN PostRequest = FALSE; PtrExt2NTRequiredFCB PtrReqdFCB = NULL; BOOLEAN CanWait = FALSE; PtrExt2VCB PtrVCB = NULL; BOOLEAN AcquiredFCB = FALSE; unsigned long BufferLength = 0; unsigned long BufferIndex = 0; unsigned long FileIndex = 0; PUNICODE_STRING PtrSearchPattern = NULL; FILE_INFORMATION_CLASS FileInformationClass; BOOLEAN RestartScan = FALSE; BOOLEAN ReturnSingleEntry = FALSE; BOOLEAN IndexSpecified = FALSE; unsigned char *Buffer = NULL; BOOLEAN FirstTimeQuery = FALSE; unsigned long StartingIndexForSearch = 0; unsigned long BytesReturned = 0; BOOLEAN BufferUsedup = FALSE; BOOLEAN SearchWithWildCards = FALSE; PFILE_BOTH_DIR_INFORMATION BothDirInformation = NULL; PFILE_DIRECTORY_INFORMATION DirectoryInformation = NULL; PEXT2_DIR_ENTRY PtrDirEntry = NULL; PEXT2_INODE PtrInode = NULL; unsigned long LogicalBlockSize; unsigned long ThisBlock; // The starting Physical Block No... //LARGE_INTEGER StartPhysicalBlock; LARGE_INTEGER StartBufferOffset ; ULONG PinBufferLength; // Buffer Control Block PBCB PtrBCB = NULL; BYTE * PtrPinnedBlockBuffer = NULL; unsigned int j; DebugTrace(DEBUG_TRACE_MISC, " === Querying Directory %S", PtrFCB->FCBName->ObjectName.Buffer ); try { // Validate the sent-in FCB if ((PtrFCB->NodeIdentifier.NodeType == EXT2_NODE_TYPE_VCB) || !(PtrFCB->FCBFlags & EXT2_FCB_DIRECTORY)) { // We will only allow notify requests on directories. RC = STATUS_INVALID_PARAMETER; } PtrReqdFCB = &(PtrFCB->NTRequiredFCB); CanWait = ((PtrIrpContext->IrpContextFlags & EXT2_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE); PtrVCB = PtrFCB->PtrVCB; // // Asynchronous IO requested // Posting request... // /* * This is incorrect because posted IRP_MJ_DIRECTORY_CONTROL * requests aren't handled in the worker thread yet. I tried * adding handling of them to the worked routine, but there * were problems with accessing the PtrIoStackLocation-> * Parameters.QueryDirectory.FileName variable. * -- Filip Navara, 18/08/2004 */ #if 0 if (!CanWait) { PostRequest = TRUE; try_return(RC = STATUS_PENDING); } #endif // Obtain the callers parameters BufferLength = PtrIoStackLocation->Parameters.QueryDirectory.Length; PtrSearchPattern = ( PUNICODE_STRING ) PtrIoStackLocation->Parameters.QueryDirectory.FileName; FileInformationClass = PtrIoStackLocation->Parameters.QueryDirectory.FileInformationClass; FileIndex = PtrIoStackLocation->Parameters.QueryDirectory.FileIndex; // Some additional arguments that affect the FSD behavior RestartScan = (PtrIoStackLocation->Flags & SL_RESTART_SCAN); ReturnSingleEntry = (PtrIoStackLocation->Flags & SL_RETURN_SINGLE_ENTRY); IndexSpecified = (PtrIoStackLocation->Flags & SL_INDEX_SPECIFIED); // // Acquiring exclusive access to the FCB. // This is not mandatory // DebugTrace(DEBUG_TRACE_MISC, "*** Going into a block to acquire FCB Exclusively[DirCtrl]", 0); DebugTraceState( "FCBMain AC:0x%LX SW:0x%LX EX:0x%LX [DirCtrl]", PtrReqdFCB->MainResource.ActiveCount, PtrReqdFCB->MainResource.NumberOfExclusiveWaiters, PtrReqdFCB->MainResource.NumberOfSharedWaiters ); ExAcquireResourceExclusiveLite(&(PtrReqdFCB->MainResource), TRUE); DebugTrace(DEBUG_TRACE_MISC, "*** FCB acquired [DirCtrl]", 0); AcquiredFCB = TRUE; // We must determine the buffer pointer to be used. Since this // routine could either be invoked directly in the context of the // calling thread, or in the context of a worker thread, here is // a general way of determining what we should use. Buffer = Ext2GetCallersBuffer ( PtrIrp ); // The method of determining where to look from and what to look for is // unfortunately extremely confusing. However, here is a methodology you // you can broadly adopt: // (a) You have to maintain a search buffer per CCB structure. // (b) This search buffer is initialized the very first time // a query directory operation is performed using the file object. // (For the sample FSD, the search buffer is stored in the // DirectorySearchPattern field) // However, the caller still has the option of "overriding" this stored // search pattern by supplying a new one in a query directory operation. // if( PtrCCB->DirectorySearchPattern.Length ) { if( PtrCCB->DirectorySearchPattern.Buffer[PtrCCB->DirectorySearchPattern.Length/2] != 0 ) { DebugTrace(DEBUG_TRACE_MISC, "&&&&&&&&& PtrCCB->DirectorySearchPattern not NULL terminated!", 0); } DebugTrace(DEBUG_TRACE_MISC, " === Old Search pattern %S", PtrCCB->DirectorySearchPattern.Buffer ); } if (PtrSearchPattern != NULL) { // User has supplied a search pattern // Now validate that the search pattern is legitimate if ( PtrCCB->DirectorySearchPattern.Length == 0 ) { // This must be the very first query request. FirstTimeQuery = TRUE; } else { // We should ignore the search pattern in the CCB and instead, // use the user-supplied pattern for this particular query // directory request. Ext2DeallocateUnicodeString( &PtrCCB->DirectorySearchPattern ); } // Now, allocate enough memory to contain the caller // supplied search pattern and fill in the DirectorySearchPattern // field in the CCB Ext2CopyUnicodeString( &PtrCCB->DirectorySearchPattern, PtrSearchPattern ); /* PtrCCB->DirectorySearchPattern = Ext2AllocatePool(PagedPool, sizeof( PtrSearchPattern ) ); ASSERT(PtrCCB->DirectorySearchPattern); RtlCopyMemory( PtrCCB->DirectorySearchPattern, PtrSearchPattern, sizeof( PtrSearchPattern ) ); */ } else if ( PtrCCB->DirectorySearchPattern.Length == 0 ) { // This MUST be the first directory query operation (else the // DirectorySearchPattern field would never be empty. Also, the caller // has neglected to provide a pattern so we MUST invent one. // Use "*" (following NT conventions) as your search pattern // and store it in the PtrCCB->DirectorySearchPattern field. /* PtrCCB->DirectorySearchPattern = Ext2AllocatePool(PagedPool, sizeof(L"*") ); ASSERT(PtrCCB->DirectorySearchPattern); RtlCopyMemory( PtrCCB->DirectorySearchPattern, L"*", 4 );*/ Ext2CopyWideCharToUnicodeString( &PtrCCB->DirectorySearchPattern, L"*" ); FirstTimeQuery = TRUE; } else { // The caller has not supplied any search pattern... // Using previously supplied pattern PtrSearchPattern = &PtrCCB->DirectorySearchPattern; } if( PtrCCB->DirectorySearchPattern.Buffer[PtrCCB->DirectorySearchPattern.Length/2] != 0 ) { DebugTrace(DEBUG_TRACE_MISC, "&&&&&&&&& PtrCCB->DirectorySearchPattern not NULL terminated!", 0 ); } DebugTrace(DEBUG_TRACE_MISC, " === Search pattern %S", PtrCCB->DirectorySearchPattern.Buffer ); SearchWithWildCards = FsRtlDoesNameContainWildCards( PtrSearchPattern ); // There is one other piece of information that your FSD must store // in the CCB structure for query directory support. This is the index // value (i.e. the offset in your on-disk directory structure) from // which you should start searching. // However, the flags supplied with the IRP can make us override this // as well. if (FileIndex) { // Caller has told us wherefrom to begin. // You may need to round this to an appropriate directory entry // entry alignment value. StartingIndexForSearch = FileIndex; } else if (RestartScan) { StartingIndexForSearch = 0; } else { // Get the starting offset from the CCB. StartingIndexForSearch = PtrCCB->CurrentByteOffset.LowPart; } // Read in the file inode if it hasn't already been read... Ext2InitializeFCBInodeInfo( PtrFCB ); if (PtrFileObject->PrivateCacheMap == NULL) { CcInitializeCacheMap(PtrFileObject, (PCC_FILE_SIZES)(&(PtrReqdFCB->CommonFCBHeader.AllocationSize)), TRUE, // We will utilize pin access for directories &(Ext2GlobalData.CacheMgrCallBacks), // callbacks PtrCCB); // The context used in callbacks } // // Read in the next Data Block of this directory // LogicalBlockSize = EXT2_MIN_BLOCK_SIZE << PtrVCB->LogBlockSize; StartBufferOffset.QuadPart = ( StartingIndexForSearch / LogicalBlockSize ); StartBufferOffset.QuadPart *= LogicalBlockSize; // This should be the StartBufferOffset alaigned to LBlock boundary... PinBufferLength = PtrReqdFCB->CommonFCBHeader.FileSize.LowPart - StartBufferOffset.LowPart; if ( !CcMapData( PtrFileObject, &StartBufferOffset, PinBufferLength, TRUE, &PtrBCB, (PVOID*)&PtrPinnedBlockBuffer ) ) { // Read Failure DebugTrace(DEBUG_TRACE_MISC, "Cache read failiure while reading in volume meta data", 0); try_return(); } else { DebugTrace(DEBUG_TRACE_MISC, "Cache hit while reading in volume meta data", 0); } PtrInode = Ext2AllocatePool( PagedPool, sizeof( EXT2_INODE ) ); // // Walking through the directory entries... for( BufferUsedup = FALSE, BufferIndex = 0; !BufferUsedup && StartingIndexForSearch < ( PtrFCB->NTRequiredFCB.CommonFCBHeader.FileSize.QuadPart - 1) ; ) { PtrDirEntry = (PEXT2_DIR_ENTRY) &PtrPinnedBlockBuffer[ StartingIndexForSearch - StartBufferOffset.LowPart ]; StartingIndexForSearch += PtrDirEntry->rec_len; PtrCCB->CurrentByteOffset.LowPart = StartingIndexForSearch; if( PtrDirEntry->inode == 0 ) { continue; } if( PtrDirEntry->name_len == 0 || PtrDirEntry->rec_len == 0 ) { // // This should not happen // Hqw can this be so!!! // Ext2BreakPoint(); if( BothDirInformation ) { BothDirInformation->NextEntryOffset = 0; } if( !BytesReturned ) { if( FirstTimeQuery ) RC = STATUS_NO_SUCH_FILE; else RC = STATUS_NO_MORE_FILES; } break; } // Does this entry match the search criterian? // Checking // { UNICODE_STRING FileName; LONG Matched = 0; // Constructing a counted Unicode string out of PtrDirEntry Ext2CopyCharToUnicodeString( &FileName, PtrDirEntry->name, PtrDirEntry->name_len ); if ( SearchWithWildCards ) { Matched = FsRtlIsNameInExpression ( PtrSearchPattern, &FileName, FALSE, NULL ); } else { Matched = ! RtlCompareUnicodeString( PtrSearchPattern, &FileName, FALSE ); } Ext2DeallocateUnicodeString( &FileName ); if( !Matched ) { continue; } } switch( FileInformationClass ) { case FileBothDirectoryInformation: DebugTrace(DEBUG_TRACE_DIRINFO, " === FileBothDirectoryInformation", 0 ); ThisBlock = sizeof( FILE_BOTH_DIR_INFORMATION ); ThisBlock += PtrDirEntry->name_len*2; ThisBlock = Ext2QuadAlign( ThisBlock ); if( ( BufferIndex + ThisBlock ) > BufferLength ) { // // Next entry won't fit into the buffer... // will have to return... // :( // if( BothDirInformation ) BothDirInformation->NextEntryOffset = 0; if( !BytesReturned ) RC = STATUS_NO_MORE_FILES; BufferUsedup = TRUE; break; } Ext2ReadInode( PtrVCB, PtrDirEntry->inode, PtrInode ); if( !PtrInode ) { try_return( RC = STATUS_UNSUCCESSFUL ); } BothDirInformation = ( PFILE_BOTH_DIR_INFORMATION ) ( Buffer + ( BufferIndex ) ); BothDirInformation->EaSize = 0; BothDirInformation->AllocationSize.QuadPart = PtrInode->i_blocks * 512; BothDirInformation->EndOfFile.QuadPart = PtrInode->i_size; BothDirInformation->ChangeTime.QuadPart = 0; BothDirInformation->CreationTime.QuadPart = ( __int64 ) PtrInode->i_ctime * 10000000; BothDirInformation->CreationTime.QuadPart += Ext2GlobalData.TimeDiff.QuadPart; BothDirInformation->LastAccessTime.QuadPart = Ext2GlobalData.TimeDiff.QuadPart + ( ( __int64 ) PtrInode->i_atime * 10000000 ); BothDirInformation->LastWriteTime.QuadPart = Ext2GlobalData.TimeDiff.QuadPart + ( ( __int64 )PtrInode->i_mtime * 10000000 ); // Getting the file type... BothDirInformation->FileAttributes = FILE_ATTRIBUTE_NORMAL; if( ! Ext2IsModeRegularFile( PtrInode->i_mode ) ) { // Not a reqular file... if( Ext2IsModeDirectory( PtrInode->i_mode) ) { // Directory... BothDirInformation->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; } else { // Special File... // Treated with respect... ;) // BothDirInformation->FileAttributes |= ( FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY); // FILE_ATTRIBUTE_DEVICE } if ( Ext2IsModeHidden( PtrInode->i_mode ) ) { BothDirInformation->FileAttributes |= FILE_ATTRIBUTE_HIDDEN; } if ( Ext2IsModeReadOnly( PtrInode->i_mode ) ) { BothDirInformation->FileAttributes |= FILE_ATTRIBUTE_READONLY; } } BothDirInformation->FileIndex = StartingIndexForSearch; BothDirInformation->FileNameLength = PtrDirEntry->name_len*2 + 2; BothDirInformation->ShortNameLength = 0; BothDirInformation->ShortName[0] = 0; // Copying out the name as WCHAR null terminated strings for( j = 0; j< PtrDirEntry->name_len ; j ++ ) { // BothDirInformation->ShortName[ j ] = PtrDirEntry->name[j]; BothDirInformation->FileName[ j ] = PtrDirEntry->name[j]; // if( j < 11 ) // BothDirInformation->ShortName[j] = PtrDirEntry->name[j]; } /* if( j < 11 ) { BothDirInformation->ShortNameLength = j * 2 + 2; BothDirInformation->ShortName[ j ] = 0; } else { BothDirInformation->ShortNameLength = 24; BothDirInformation->ShortName[ 11 ] = 0; }*/ BothDirInformation->FileName[ j ] = 0; BytesReturned += ThisBlock; BufferIndex += ThisBlock; if( !ReturnSingleEntry && ( StartingIndexForSearch < ( PtrFCB->NTRequiredFCB.CommonFCBHeader.FileSize.QuadPart - 1) )) BothDirInformation->NextEntryOffset = ThisBlock; else BothDirInformation->NextEntryOffset = 0; break; case FileDirectoryInformation: // DirectoryInformation DebugTrace(DEBUG_TRACE_DIRINFO, " === FileDirectoryInformation", 0 ); ThisBlock = sizeof( FILE_DIRECTORY_INFORMATION ); ThisBlock += PtrDirEntry->name_len*2; ThisBlock = Ext2QuadAlign( ThisBlock ); if( ( BufferIndex + ThisBlock ) > BufferLength ) { // // Next entry won't fit into the buffer... // will have to return... // :( // if( DirectoryInformation ) DirectoryInformation->NextEntryOffset = 0; if( !BytesReturned ) RC = STATUS_NO_MORE_FILES; BufferUsedup = TRUE; break; } Ext2ReadInode( PtrVCB, PtrDirEntry->inode, PtrInode ); if( !PtrInode ) { try_return( RC = STATUS_UNSUCCESSFUL ); } DirectoryInformation = ( PFILE_DIRECTORY_INFORMATION ) ( Buffer + ( BufferIndex ) ); DirectoryInformation->AllocationSize.QuadPart = PtrInode->i_blocks * 512; DirectoryInformation->EndOfFile.QuadPart = PtrInode->i_size; DirectoryInformation->ChangeTime.QuadPart = 0; DirectoryInformation->CreationTime.QuadPart = ( __int64 ) PtrInode->i_ctime * 10000000; DirectoryInformation->CreationTime.QuadPart += Ext2GlobalData.TimeDiff.QuadPart; DirectoryInformation->LastAccessTime.QuadPart = Ext2GlobalData.TimeDiff.QuadPart + ( ( __int64 ) PtrInode->i_atime * 10000000 ); DirectoryInformation->LastWriteTime.QuadPart = Ext2GlobalData.TimeDiff.QuadPart + ( ( __int64 )PtrInode->i_mtime * 10000000 ); // Getting the file type... DirectoryInformation->FileAttributes = FILE_ATTRIBUTE_NORMAL; if( ! Ext2IsModeRegularFile( PtrInode->i_mode ) ) { // Not a reqular file... if( Ext2IsModeDirectory( PtrInode->i_mode) ) { // Directory... DirectoryInformation->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; } else { // Special File... // Treated with respect... ;) // DirectoryInformation->FileAttributes |= ( FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY); // FILE_ATTRIBUTE_DEVICE } if ( Ext2IsModeHidden( PtrInode->i_mode ) ) { DirectoryInformation->FileAttributes |= FILE_ATTRIBUTE_HIDDEN; } if ( Ext2IsModeReadOnly( PtrInode->i_mode ) ) { DirectoryInformation->FileAttributes |= FILE_ATTRIBUTE_READONLY; } } DirectoryInformation->FileIndex = StartingIndexForSearch; DirectoryInformation->FileNameLength = PtrDirEntry->name_len*2 + 2; // Copying out the name as WCHAR null terminated strings for( j = 0; j< PtrDirEntry->name_len ; j ++ ) { DirectoryInformation->FileName[ j ] = PtrDirEntry->name[j]; } DirectoryInformation->FileName[ j ] = 0; BytesReturned += ThisBlock; BufferIndex += ThisBlock; if( !ReturnSingleEntry && ( StartingIndexForSearch < ( PtrFCB->NTRequiredFCB.CommonFCBHeader.FileSize.QuadPart - 1) )) DirectoryInformation->NextEntryOffset = ThisBlock; else DirectoryInformation->NextEntryOffset = 0; break; case FileFullDirectoryInformation: // FullDirInformation-> DebugTrace(DEBUG_TRACE_DIRINFO, " === FileFullDirectoryInformation - Not handled", 0 ); try_return(); case FileNamesInformation: // NamesInformation-> DebugTrace(DEBUG_TRACE_DIRINFO, " === FileNamesInformation - Not handled", 0 ); try_return(); default: DebugTrace(DEBUG_TRACE_DIRINFO, " === Invalid Dir Info class - Not handled", 0 ); try_return( RC = STATUS_INVALID_INFO_CLASS ); } if( ReturnSingleEntry ) { break; } }// end of for... if( !BytesReturned && StartingIndexForSearch >= ( PtrFCB->NTRequiredFCB.CommonFCBHeader.FileSize.QuadPart) ) { Ext2DeallocateUnicodeString( &PtrCCB->DirectorySearchPattern ); PtrCCB->CurrentByteOffset.QuadPart = 0; if( FirstTimeQuery ) RC = STATUS_NO_SUCH_FILE; else RC = STATUS_NO_MORE_FILES; try_return(); } else if( BytesReturned ) { BothDirInformation->NextEntryOffset = 0; } try_exit: NOTHING; } finally { if( PtrInode ) { DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [DirCtrl]", PtrInode ); ExFreePool( PtrInode ); } if( PtrBCB ) { CcUnpinData( PtrBCB ); PtrBCB = NULL; } if (PostRequest) { if (AcquiredFCB) { Ext2ReleaseResource(&(PtrReqdFCB->MainResource)); DebugTrace(DEBUG_TRACE_MISC, "*** FCB Released in [DirCtrl]", 0); DebugTraceState( "FCBMain AC:0x%LX SW:0x%LX EX:0x%LX [DirCtrl]", PtrReqdFCB->MainResource.ActiveCount, PtrReqdFCB->MainResource.NumberOfExclusiveWaiters, PtrReqdFCB->MainResource.NumberOfSharedWaiters ); } // Map the users buffer and then post the request. RC = Ext2LockCallersBuffer(PtrIrp, TRUE, BufferLength); ASSERT(NT_SUCCESS(RC)); RC = Ext2PostRequest(PtrIrpContext, PtrIrp); } else if (!(PtrIrpContext->IrpContextFlags & EXT2_IRP_CONTEXT_EXCEPTION)) { if (AcquiredFCB) { Ext2ReleaseResource(&(PtrReqdFCB->MainResource)); DebugTrace(DEBUG_TRACE_MISC, "*** FCB Released [DirCtrl]", 0); DebugTraceState( "FCBMain AC:0x%LX SW:0x%LX EX:0x%LX [DirCtrl]", PtrReqdFCB->MainResource.ActiveCount, PtrReqdFCB->MainResource.NumberOfExclusiveWaiters, PtrReqdFCB->MainResource.NumberOfSharedWaiters ); } // Complete the request. PtrIrp->IoStatus.Status = RC; PtrIrp->IoStatus.Information = BytesReturned; // Free up the Irp Context Ext2ReleaseIrpContext(PtrIrpContext); // complete the IRP IoCompleteRequest(PtrIrp, IO_DISK_INCREMENT); } // Flush the saved BCBs... // Ext2FlushSavedBCBs ( PtrIrpContext ); } return(RC); }