NTSTATUS _InitializePrefixTableEntryAllocation(PDFS_PREFIX_TABLE pTable) { NTSTATUS status = STATUS_SUCCESS; #ifdef KERNEL_MODE PVOID pSegment = NULL; pSegment = ExAllocatePool(PagedPool,PREFIX_TABLE_ENTRY_SEGMENT_SIZE); if (pSegment != NULL) { status = ExInitializeZone(&pTable->PrefixTableEntryZone, QuadAlign(sizeof(DFS_PREFIX_TABLE_ENTRY)), pSegment, PREFIX_TABLE_ENTRY_SEGMENT_SIZE); } else status = STATUS_NO_MEMORY; #endif return status; }
NTSTATUS DokanFindStreams(PFILE_STREAM_INFORMATION StreamInfo, PDOKAN_FILE_INFO FileInfo, PEVENT_CONTEXT EventContext, PDOKAN_INSTANCE DokanInstance, PULONG RemainingLength) { PDOKAN_OPEN_INFO openInfo = (PDOKAN_OPEN_INFO)(UINT_PTR)FileInfo->DokanContext; NTSTATUS status = STATUS_SUCCESS; if (!DokanInstance->DokanOperations->FindStreams) { return STATUS_NOT_IMPLEMENTED; } if (openInfo->StreamListHead == NULL) { openInfo->StreamListHead = malloc(sizeof(LIST_ENTRY)); if (openInfo->StreamListHead != NULL) { InitializeListHead(openInfo->StreamListHead); } else { status = STATUS_NO_MEMORY; } } if (status == STATUS_SUCCESS && IsListEmpty(openInfo->StreamListHead)) { status = DokanInstance->DokanOperations->FindStreams( EventContext->Operation.File.FileName, DokanFillFindStreamData, FileInfo); } if (status == STATUS_SUCCESS) { PLIST_ENTRY listHead, entry; ULONG entrySize; listHead = openInfo->StreamListHead; entrySize = 0; for (entry = listHead->Flink; entry != listHead; entry = entry->Flink) { PDOKAN_FIND_STREAM_DATA find = CONTAINING_RECORD(entry, DOKAN_FIND_STREAM_DATA, ListEntry); ULONG nextEntryOffset = entrySize; ULONG streamNameLength = (ULONG)wcslen(find->FindStreamData.cStreamName) * sizeof(WCHAR); entrySize = sizeof(FILE_STREAM_INFORMATION) + streamNameLength; // Must be align on a 8-byte boundary. entrySize = QuadAlign(entrySize); if (*RemainingLength < entrySize) { status = STATUS_BUFFER_OVERFLOW; break; } // Not the first entry, set the offset before filling the new entry if (nextEntryOffset > 0) { StreamInfo->NextEntryOffset = nextEntryOffset; StreamInfo = (PFILE_STREAM_INFORMATION)((LPBYTE)StreamInfo + StreamInfo->NextEntryOffset); } // Fill the new entry StreamInfo->StreamNameLength = streamNameLength; memcpy(StreamInfo->StreamName, find->FindStreamData.cStreamName, streamNameLength); StreamInfo->StreamSize = find->FindStreamData.StreamSize; StreamInfo->StreamAllocationSize = find->FindStreamData.StreamSize; StreamInfo->NextEntryOffset = 0; ALIGN_ALLOCATION_SIZE(&StreamInfo->StreamAllocationSize); *RemainingLength -= entrySize; } if (status != STATUS_BUFFER_OVERFLOW) { ClearFindStreamData(openInfo->StreamListHead); } } else { ClearFindStreamData(openInfo->StreamListHead); } return status; }
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 {
__drv_mustHoldCriticalRegion NTSTATUS CdQueryDirectory ( __inout PIRP_CONTEXT IrpContext, __inout PIRP Irp, __in PIO_STACK_LOCATION IrpSp, __in PFCB Fcb, __in PCCB Ccb ) /*++ Routine Description: This routine performs the query directory operation. It is responsible for either completing of enqueuing the input Irp. We store the state of the search in the Ccb. Arguments: Irp - Supplies the Irp to process IrpSp - Stack location for this Irp. Fcb - Fcb for this directory. Ccb - Ccb for this directory open. Return Value: NTSTATUS - The return status for the operation --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG Information = 0; ULONG LastEntry = 0; ULONG NextEntry = 0; ULONG FileNameBytes; ULONG SeparatorBytes; ULONG VersionStringBytes; FILE_ENUM_CONTEXT FileContext; PDIRENT ThisDirent = NULL; BOOLEAN InitialQuery; BOOLEAN ReturnNextEntry = FALSE; BOOLEAN ReturnSingleEntry; BOOLEAN Found; BOOLEAN DoCcbUpdate = FALSE; PCHAR UserBuffer; ULONG BytesRemainingInBuffer; ULONG BaseLength; PFILE_BOTH_DIR_INFORMATION DirInfo = NULL; PFILE_NAMES_INFORMATION NamesInfo; PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo; PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo; PAGED_CODE(); // // Check if we support this search mode. Also remember the size of the base part of // each of these structures. // switch (IrpSp->Parameters.QueryDirectory.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: CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_INFO_CLASS ); return STATUS_INVALID_INFO_CLASS; } // // Get the user buffer. // CdMapUserBuffer( IrpContext, &UserBuffer); // // Initialize our search context. // CdInitializeFileContext( IrpContext, &FileContext ); // // Acquire the directory. // CdAcquireFileShared( IrpContext, Fcb ); // // Use a try-finally to facilitate cleanup. // try { // // Verify the Fcb is still good. // CdVerifyFcbOperation( IrpContext, Fcb ); // // Start by getting the initial state for the enumeration. This will set up the Ccb with // the initial search parameters and let us know the starting offset in the directory // to search. // CdInitializeEnumeration( IrpContext, IrpSp, Fcb, Ccb, &FileContext, &ReturnNextEntry, &ReturnSingleEntry, &InitialQuery ); // // The current dirent is stored in the InitialDirent field. We capture // this here so that we have a valid restart point even if we don't // find a single entry. // ThisDirent = &FileContext.InitialDirent->Dirent; // // 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) { // // If the user had requested only a single match and we have // returned that, then we stop at this point. We update the Ccb with // the status based on the last entry returned. // if ((NextEntry != 0) && ReturnSingleEntry) { DoCcbUpdate = TRUE; try_leave( Status ); } // // We try to locate the next matching dirent. Our search if based on a starting // dirent offset, whether we should return the current or next entry, whether // we should be doing a short name search and finally whether we should be // checking for a version match. // Found = CdEnumerateIndex( IrpContext, Ccb, &FileContext, ReturnNextEntry ); // // Initialize the value for the next search. // ReturnNextEntry = TRUE; // // 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 (!Found) { if (NextEntry == 0) { Status = STATUS_NO_MORE_FILES; if (InitialQuery) { Status = STATUS_NO_SUCH_FILE; } } DoCcbUpdate = TRUE; try_leave( Status ); } // // Remember the dirent for the file we just found. // ThisDirent = &FileContext.InitialDirent->Dirent; // // 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. // // // Let's compute the number of bytes we need to transfer the current entry. // SeparatorBytes = VersionStringBytes = 0; // // We can look directly at the dirent that we found. // FileNameBytes = ThisDirent->CdFileName.FileName.Length; // // Compute the number of bytes for the version string if // we will return this. Allow directories with illegal ";". // if (((Ccb->SearchExpression.VersionString.Length != 0) || (FlagOn(ThisDirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY))) && (ThisDirent->CdFileName.VersionString.Length != 0)) { SeparatorBytes = 2; VersionStringBytes = ThisDirent->CdFileName.VersionString.Length; } // // If the slot for the next entry would be beyond the length of the // user's buffer just exit (we know we've returned at least one entry // already). This will happen when we align the pointer past the end. // if (NextEntry > IrpSp->Parameters.QueryDirectory.Length) { ReturnNextEntry = FALSE; DoCcbUpdate = TRUE; try_leave( Status = STATUS_SUCCESS ); } // // Compute the number of bytes remaining in the buffer. Round this // down to a WCHAR boundary so we can copy full characters. // BytesRemainingInBuffer = IrpSp->Parameters.QueryDirectory.Length - NextEntry; ClearFlag( BytesRemainingInBuffer, 1 ); // // If this won't fit and we have returned a previous entry then just // return STATUS_SUCCESS. // if ((BaseLength + FileNameBytes + SeparatorBytes + VersionStringBytes) > BytesRemainingInBuffer) { // // If we already found an entry then just exit. // if (NextEntry != 0) { ReturnNextEntry = FALSE; DoCcbUpdate = TRUE; try_leave( Status = STATUS_SUCCESS ); } // // Don't even try to return the version string if it doesn't all fit. // Reduce the FileNameBytes to just fit in the buffer. // if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) { FileNameBytes = BytesRemainingInBuffer - BaseLength; } // // Don't return any version string bytes. // VersionStringBytes = SeparatorBytes = 0; // // Use a status code of STATUS_BUFFER_OVERFLOW. Also set // ReturnSingleEntry so that we will exit the loop at the top. // Status = STATUS_BUFFER_OVERFLOW; ReturnSingleEntry = TRUE; } // // 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 { // // Zero and initialize the base part of the current entry. // RtlZeroMemory( Add2Ptr( UserBuffer, NextEntry, PVOID ), BaseLength ); // // 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 (IrpSp->Parameters.QueryDirectory.FileInformationClass) { case FileBothDirectoryInformation: case FileFullDirectoryInformation: case FileIdBothDirectoryInformation: case FileIdFullDirectoryInformation: case FileDirectoryInformation: DirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_BOTH_DIR_INFORMATION ); // // Use the create time for all the time stamps. // CdConvertCdTimeToNtTime( IrpContext, FileContext.InitialDirent->Dirent.CdTime, &DirInfo->CreationTime ); DirInfo->LastWriteTime = DirInfo->ChangeTime = DirInfo->CreationTime; // // Set the attributes and sizes separately for directories and // files. // if (FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { DirInfo->EndOfFile.QuadPart = DirInfo->AllocationSize.QuadPart = 0; SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY); } else { DirInfo->EndOfFile.QuadPart = FileContext.FileSize; DirInfo->AllocationSize.QuadPart = LlSectorAlign( FileContext.FileSize ); SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_READONLY); } if (FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_HIDDEN )) { SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_HIDDEN ); } DirInfo->FileIndex = ThisDirent->DirentOffset; DirInfo->FileNameLength = FileNameBytes + SeparatorBytes + VersionStringBytes; break; case FileNamesInformation: NamesInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_NAMES_INFORMATION ); NamesInfo->FileIndex = ThisDirent->DirentOffset; NamesInfo->FileNameLength = FileNameBytes + SeparatorBytes + VersionStringBytes; break; } // // Fill in the FileId // switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) { case FileIdBothDirectoryInformation: IdBothDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_BOTH_DIR_INFORMATION ); CdSetFidFromParentAndDirent( IdBothDirInfo->FileId, Fcb, ThisDirent ); break; case FileIdFullDirectoryInformation: IdFullDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_FULL_DIR_INFORMATION ); CdSetFidFromParentAndDirent( IdFullDirInfo->FileId, Fcb, ThisDirent ); break; default: break; } // // Now copy as much of the name as possible. We also may have a version // string to copy. // if (FileNameBytes != 0) { // // This is a Unicode name, we can copy the bytes directly. // RtlCopyMemory( Add2Ptr( UserBuffer, NextEntry + BaseLength, PVOID ), ThisDirent->CdFileName.FileName.Buffer, FileNameBytes ); if (SeparatorBytes != 0) { *(Add2Ptr( UserBuffer, NextEntry + BaseLength + FileNameBytes, PWCHAR )) = L';'; if (VersionStringBytes != 0) { RtlCopyMemory( Add2Ptr( UserBuffer, NextEntry + BaseLength + FileNameBytes + sizeof( WCHAR ), PVOID ), ThisDirent->CdFileName.VersionString.Buffer, VersionStringBytes ); } } } // // Fill in the short name if we got STATUS_SUCCESS. The short name // may already be in the file context. Otherwise we will check // whether the long name is 8.3. Special case the self and parent // directory names. // if ((Status == STATUS_SUCCESS) && (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation || IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation) && (Ccb->SearchExpression.VersionString.Length == 0) && !FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) { // // If we already have the short name then copy into the user's buffer. // if (FileContext.ShortName.FileName.Length != 0) { RtlCopyMemory( DirInfo->ShortName, FileContext.ShortName.FileName.Buffer, FileContext.ShortName.FileName.Length ); DirInfo->ShortNameLength = (CCHAR) FileContext.ShortName.FileName.Length; // // If the short name length is currently zero then check if // the long name is not 8.3. We can copy the short name in // unicode form directly into the caller's buffer. // } else { if (!CdIs8dot3Name( IrpContext, ThisDirent->CdFileName.FileName )) { CdGenerate8dot3Name( IrpContext, &ThisDirent->CdCaseFileName.FileName, ThisDirent->DirentOffset, DirInfo->ShortName, &FileContext.ShortName.FileName.Length ); DirInfo->ShortNameLength = (CCHAR) FileContext.ShortName.FileName.Length; } } } // // Sum the total number of bytes for the information field. // FileNameBytes += SeparatorBytes + VersionStringBytes; // // Update the information with the number of bytes stored in the // buffer. We quad-align the existing buffer to add any necessary // pad bytes. // Information = NextEntry + BaseLength + FileNameBytes; // // Go back to the previous entry and fill in the update to this entry. // *(Add2Ptr( UserBuffer, LastEntry, PULONG )) = NextEntry - LastEntry; // // Set up our variables for the next dirent. // InitialQuery = FALSE; LastEntry = NextEntry; NextEntry = QuadAlign( Information ); } 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. // Information = 0; try_leave( Status = GetExceptionCode()); } } DoCcbUpdate = TRUE; } finally { // // Cleanup our search context - *before* aquiring the FCB mutex exclusive, // else can block on threads in cdcreateinternalstream/purge which // hold the FCB but are waiting for all maps in this stream to be released. // CdCleanupFileContext( IrpContext, &FileContext ); // // Now we can safely aqure the FCB mutex if we need to. // if (DoCcbUpdate && !NT_ERROR( Status )) { // // Update the Ccb to show the current state of the enumeration. // CdLockFcb( IrpContext, Fcb ); Ccb->CurrentDirentOffset = ThisDirent->DirentOffset; ClearFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT ); if (ReturnNextEntry) { SetFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT ); } CdUnlockFcb( IrpContext, Fcb ); } // // Release the Fcb. // CdReleaseFile( IrpContext, Fcb ); } // // Complete the request here. // Irp->IoStatus.Information = Information; CdCompleteRequest( IrpContext, Irp, Status ); return Status; }
ULONG DokanFillDirectoryInformation(FILE_INFORMATION_CLASS DirectoryInfo, PVOID Buffer, PULONG LengthRemaining, PWIN32_FIND_DATAW FindData, ULONG Index, PDOKAN_INSTANCE DokanInstance) { ULONG nameBytes; ULONG thisEntrySize; nameBytes = (ULONG)wcslen(FindData->cFileName) * sizeof(WCHAR); thisEntrySize = nameBytes; switch (DirectoryInfo) { case FileDirectoryInformation: thisEntrySize += sizeof(FILE_DIRECTORY_INFORMATION); break; case FileFullDirectoryInformation: thisEntrySize += sizeof(FILE_FULL_DIR_INFORMATION); break; case FileNamesInformation: thisEntrySize += sizeof(FILE_NAMES_INFORMATION); break; case FileBothDirectoryInformation: thisEntrySize += sizeof(FILE_BOTH_DIR_INFORMATION); break; case FileIdBothDirectoryInformation: thisEntrySize += sizeof(FILE_ID_BOTH_DIR_INFORMATION); break; default: break; } // Must be align on a 8-byte boundary. thisEntrySize = QuadAlign(thisEntrySize); // no more memory, don't fill any more if (*LengthRemaining < thisEntrySize) { DbgPrint(" no memory\n"); return 0; } RtlZeroMemory(Buffer, thisEntrySize); switch (DirectoryInfo) { case FileDirectoryInformation: DokanFillDirInfo(Buffer, FindData, Index, DokanInstance); break; case FileFullDirectoryInformation: DokanFillFullDirInfo(Buffer, FindData, Index, DokanInstance); break; case FileNamesInformation: DokanFillNamesInfo(Buffer, FindData, Index); break; case FileBothDirectoryInformation: DokanFillBothDirInfo(Buffer, FindData, Index, DokanInstance); break; case FileIdBothDirectoryInformation: DokanFillIdBothDirInfo(Buffer, FindData, Index, DokanInstance); break; default: break; } *LengthRemaining -= thisEntrySize; return thisEntrySize; }
NTSTATUS AFSProcessRequest( IN ULONG RequestType, IN ULONG RequestFlags, IN GUID *AuthGroup, IN PUNICODE_STRING FileName, IN AFSFileID *FileId, IN void *Data, IN ULONG DataLength, IN OUT void *ResultBuffer, IN OUT PULONG ResultBufferLength) { NTSTATUS ntStatus = STATUS_SUCCESS; AFSPoolEntry stPoolEntry, *pPoolEntry = NULL; AFSCommSrvcCB *pCommSrvc = NULL; BOOLEAN bReleasePool = FALSE; AFSDeviceExt *pControlDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension; AFSDeviceExt *pRDRDevExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension; BOOLEAN bWait = BooleanFlagOn( RequestFlags, AFS_REQUEST_FLAG_SYNCHRONOUS); ULONG ulPoolEntryLength = 0; BOOLEAN bDecrementCount = FALSE; __try { if( BooleanFlagOn( pRDRDevExt->DeviceFlags, AFS_DEVICE_FLAG_REDIRECTOR_SHUTDOWN)) { try_return( ntStatus = STATUS_DEVICE_NOT_READY); } if( InterlockedIncrement( &pControlDevExt->Specific.Control.OutstandingServiceRequestCount) == 1) { KeClearEvent( &pControlDevExt->Specific.Control.OutstandingServiceRequestEvent); } bDecrementCount = TRUE; pCommSrvc = &pControlDevExt->Specific.Control.CommServiceCB; // // Grab the pool resource and check the state // AFSDbgLogMsg( AFS_SUBSYSTEM_LOCK_PROCESSING, AFS_TRACE_LEVEL_VERBOSE, "AFSProcessRequest Acquiring IrpPoolLock lock %08lX EXCL %08lX\n", &pCommSrvc->IrpPoolLock, PsGetCurrentThread()); AFSAcquireExcl( &pCommSrvc->IrpPoolLock, TRUE); bReleasePool = TRUE; if( pCommSrvc->IrpPoolControlFlag != POOL_ACTIVE) { // // Pool not running so bail. // try_return( ntStatus = STATUS_DEVICE_NOT_READY); } // // If this is an async request we need to allocate a pool entry for the request // pPoolEntry = &stPoolEntry; if( !bWait) { ASSERT( ResultBuffer == NULL); ulPoolEntryLength = sizeof( AFSPoolEntry) + QuadAlign( DataLength); if( FileName != NULL) { ulPoolEntryLength += FileName->Length; } pPoolEntry = (AFSPoolEntry *)AFSExAllocatePoolWithTag( NonPagedPool, ulPoolEntryLength, AFS_POOL_ENTRY_TAG); if( pPoolEntry == NULL) { try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES); } RtlZeroMemory( pPoolEntry, ulPoolEntryLength); pPoolEntry->Data = (void *)((char *)pPoolEntry + sizeof( AFSPoolEntry)); pPoolEntry->FileName.Buffer = (WCHAR *)((char *)pPoolEntry->Data + DataLength); } else { RtlZeroMemory( pPoolEntry, sizeof( AFSPoolEntry)); KeInitializeEvent( &pPoolEntry->Event, NotificationEvent, FALSE); } pPoolEntry->RequestType = RequestType; pPoolEntry->RequestIndex = pCommSrvc->IrpPoolRequestIndex++; pPoolEntry->RequestFlags = RequestFlags; pPoolEntry->ResultBufferLength = 0; if( FileId != NULL) { pPoolEntry->FileId = *FileId; } pPoolEntry->FileName.Length = 0; if( FileName != NULL) { if( bWait) { pPoolEntry->FileName = *FileName; } else { pPoolEntry->FileName.Length = FileName->Length; pPoolEntry->FileName.MaximumLength = pPoolEntry->FileName.Length; RtlCopyMemory( pPoolEntry->FileName.Buffer, FileName->Buffer, pPoolEntry->FileName.Length); } } // // Move in the data if there is some // pPoolEntry->DataLength = DataLength; if( Data != NULL && DataLength > 0) { if( bWait) { pPoolEntry->Data = Data; } else { RtlCopyMemory( pPoolEntry->Data, Data, DataLength); } } pPoolEntry->ResultBuffer = ResultBuffer; pPoolEntry->ResultBufferLength = ResultBufferLength; // // Store off the auth group // if( AuthGroup == NULL) { AFSRetrieveAuthGroup( (ULONGLONG)PsGetCurrentProcessId(), (ULONGLONG)PsGetCurrentThreadId(), &pPoolEntry->AuthGroup); } else { RtlCopyMemory( &pPoolEntry->AuthGroup, AuthGroup, sizeof( GUID)); } if( AFSIsLocalSystemAuthGroup( &pPoolEntry->AuthGroup)) { SetFlag( pPoolEntry->RequestFlags, AFS_REQUEST_LOCAL_SYSTEM_PAG); } if( AFSIsNoPAGAuthGroup( &pPoolEntry->AuthGroup)) { AFSDbgLogMsg( 0, 0, "AFSProcessRequest NoPAG Auth Group %08lX\n", PsGetCurrentThread()); } // // Indicate the type of process // #ifdef AMD64 if( !AFSIs64BitProcess( (ULONGLONG)PsGetCurrentProcessId())) { SetFlag( pPoolEntry->RequestFlags, AFS_REQUEST_FLAG_WOW64); } #endif // // Insert the entry into the request pool // ntStatus = AFSInsertRequest( pCommSrvc, pPoolEntry); if( !NT_SUCCESS( ntStatus)) { if( !bWait) { ExFreePool( pPoolEntry); } try_return( ntStatus); } // // Drop the lock on the pool prior to waiting // AFSReleaseResource( &pCommSrvc->IrpPoolLock); bReleasePool = FALSE; // // Wait for the result if this is NOT an asynchronous request // if( bWait) { // // Wait for the result of the request. We specify no timeout ... // ntStatus = KeWaitForSingleObject( &pPoolEntry->Event, Executive, KernelMode, FALSE, NULL); // // Process the result of the request // if( ntStatus == STATUS_SUCCESS) { ntStatus = pPoolEntry->ResultStatus; } else { ntStatus = STATUS_DEVICE_NOT_READY; } } try_exit: if( bReleasePool) { AFSReleaseResource( &pCommSrvc->IrpPoolLock); } if( bDecrementCount && InterlockedDecrement( &pControlDevExt->Specific.Control.OutstandingServiceRequestCount) == 0) { KeSetEvent( &pControlDevExt->Specific.Control.OutstandingServiceRequestEvent, 0, FALSE); } } __except( AFSExceptionFilter( __FUNCTION__, GetExceptionCode(), GetExceptionInformation())) { AFSDumpTraceFilesFnc(); if( bReleasePool) { AFSReleaseResource( &pCommSrvc->IrpPoolLock); } if( bDecrementCount && InterlockedDecrement( &pControlDevExt->Specific.Control.OutstandingServiceRequestCount) == 0) { KeSetEvent( &pControlDevExt->Specific.Control.OutstandingServiceRequestEvent, 0, FALSE); } if ( ntStatus == STATUS_SUCCESS) { ntStatus = STATUS_UNSUCCESSFUL; } } return ntStatus; }
BOOLEAN NtfsCheckFileRecord ( IN PVCB Vcb, IN PFILE_RECORD_SEGMENT_HEADER FileRecord, IN PFILE_REFERENCE FileReference OPTIONAL, OUT PULONG CorruptionHint ) /*++ Routine Description: Consistency check for file records. Arguments: Vcb - the vcb it belongs to FileRecord - the filerecord to check FileReference - if specified double check the sequence number and self ref. fileref against it CorruptionHint - hint for debugging on where corruption occured; Return Value: FALSE - if the file record is not valid TRUE - if it is --*/ { PATTRIBUTE_RECORD_HEADER Attribute; PFILE_RECORD_SEGMENT_HEADER EndOfFileRecord; ULONG BytesPerFileRecordSegment = Vcb->BytesPerFileRecordSegment; BOOLEAN StandardInformationSeen = FALSE; ULONG BytesInOldHeader; PAGED_CODE(); *CorruptionHint = 0; EndOfFileRecord = Add2Ptr( FileRecord, BytesPerFileRecordSegment ); // // Check the file record header for consistency. // if ((*(PULONG)FileRecord->MultiSectorHeader.Signature != *(PULONG)FileSignature) || ((ULONG)FileRecord->MultiSectorHeader.UpdateSequenceArrayOffset > (SEQUENCE_NUMBER_STRIDE - (PAGE_SIZE / SEQUENCE_NUMBER_STRIDE + 1) * sizeof(USHORT))) || ((ULONG)((FileRecord->MultiSectorHeader.UpdateSequenceArraySize - 1) * SEQUENCE_NUMBER_STRIDE) != BytesPerFileRecordSegment) || !FlagOn(FileRecord->Flags, FILE_RECORD_SEGMENT_IN_USE)) { DebugTrace( 0, 0, ("Invalid file record: %08lx\n", FileRecord) ); *CorruptionHint = 1; #if !__NDAS_NTFS_SECONDARY__ ASSERTMSG( "Invalid resident file record\n", FALSE ); #endif return FALSE; } BytesInOldHeader = QuadAlign( sizeof( FILE_RECORD_SEGMENT_HEADER_V0 ) + (UpdateSequenceArraySize( BytesPerFileRecordSegment ) - 1) * sizeof( USHORT )); // // Offset bounds checks // if ((FileRecord->FirstFreeByte > BytesPerFileRecordSegment) || (FileRecord->FirstFreeByte < BytesInOldHeader) || (FileRecord->BytesAvailable != BytesPerFileRecordSegment) || (((ULONG)FileRecord->FirstAttributeOffset < BytesInOldHeader) || ((ULONG)FileRecord->FirstAttributeOffset > BytesPerFileRecordSegment - SIZEOF_RESIDENT_ATTRIBUTE_HEADER)) || (!IsQuadAligned( FileRecord->FirstAttributeOffset ))) { *CorruptionHint = 2; ASSERTMSG( "Out of bound offset in frs\n", FALSE ); return FALSE; } // // Optional fileref number check // if (ARGUMENT_PRESENT( FileReference )) { if ((FileReference->SequenceNumber != FileRecord->SequenceNumber) || ((FileRecord->FirstAttributeOffset > BytesInOldHeader) && ((FileRecord->SegmentNumberHighPart != FileReference->SegmentNumberHighPart) || (FileRecord->SegmentNumberLowPart != FileReference->SegmentNumberLowPart)))) { *CorruptionHint = 3; ASSERTMSG( "Filerecord fileref doesn't match expected value\n", FALSE ); return FALSE; } } // // Loop to check all of the attributes. // for (Attribute = NtfsFirstAttribute(FileRecord); Attribute->TypeCode != $END; Attribute = NtfsGetNextRecord(Attribute)) { // if (!StandardInformationSeen && // (Attribute->TypeCode != $STANDARD_INFORMATION) && // XxEqlZero(FileRecord->BaseFileRecordSegment)) { // // DebugTrace( 0, 0, ("Standard Information missing: %08lx\n", Attribute) ); // // ASSERTMSG( "Standard Information missing\n", FALSE ); // return FALSE; // } StandardInformationSeen = TRUE; if (!NtfsCheckAttributeRecord( Vcb, FileRecord, Attribute, FALSE, CorruptionHint )) { return FALSE; } } return TRUE; }
ULONG DokanEnumerateNamedStreams( PFILE_STREAM_INFORMATION StreamInfo, PDOKAN_FILE_INFO FileInfo, PEVENT_CONTEXT EventContext, PDOKAN_INSTANCE DokanInstance, PULONG RemainingLength) { WCHAR streamName[SHRT_MAX + 1]; ULONG streamNameLength; LONGLONG streamSize; ULONG entrySize = 0; PVOID enumContext = NULL; int result = 0; if (DokanInstance->DokanOptions->Version < DOKAN_ENUMERATE_STREAMS_SUPPORTED_VERSION || !DokanInstance->DokanOperations->EnumerateNamedStreams) { return STATUS_NOT_IMPLEMENTED; } if (*RemainingLength < sizeof(FILE_STREAM_INFORMATION)) { return STATUS_BUFFER_OVERFLOW; } while (result > -1) { ZeroMemory(streamName, sizeof(streamName)); streamNameLength = 0; streamSize = 0; result = DokanInstance->DokanOperations->EnumerateNamedStreams( EventContext->Operation.File.FileName, &enumContext, streamName, &streamNameLength, &streamSize, FileInfo); if (result > -1) { if (*RemainingLength < sizeof(FILE_STREAM_INFORMATION)) { return STATUS_BUFFER_OVERFLOW; } // Not the first entry, set the offset before filling the new entry if (entrySize > 0) { StreamInfo->NextEntryOffset = entrySize; StreamInfo = (PFILE_STREAM_INFORMATION)((LPBYTE)StreamInfo + StreamInfo->NextEntryOffset); } entrySize = sizeof(FILE_STREAM_INFORMATION) + streamNameLength; // Must be align on a 8-byte boundary. entrySize = QuadAlign(entrySize); if (*RemainingLength < entrySize) { return STATUS_BUFFER_OVERFLOW; } // Fill the new entry StreamInfo->StreamNameLength = streamNameLength; wcscpy_s(StreamInfo->StreamName, streamNameLength + 1, streamName); StreamInfo->StreamSize.QuadPart = streamSize; StreamInfo->StreamAllocationSize.QuadPart = streamSize; ALIGN_ALLOCATION_SIZE(&StreamInfo->StreamAllocationSize); *RemainingLength -= entrySize; } } return STATUS_SUCCESS; }
NTSTATUS NpQueryDirectory ( IN PROOT_DCB RootDcb, IN PROOT_DCB_CCB Ccb, IN PIRP Irp ) /*++ Routine Description: This is the work routine for querying a directory. Arugments: RootDcb - Supplies the dcb being queried Ccb - Supplies the context of the caller Irp - Supplies the Irp being processed Return Value: NTSTATUS - The return status for the operation. --*/ { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; PUCHAR Buffer; CLONG SystemBufferLength; UNICODE_STRING FileName; ULONG FileIndex; FILE_INFORMATION_CLASS FileInformationClass; BOOLEAN RestartScan; BOOLEAN ReturnSingleEntry; BOOLEAN IndexSpecified; static WCHAR Star = L'*'; BOOLEAN CaseInsensitive = TRUE; //*** Make searches case insensitive ULONG CurrentIndex; ULONG LastEntry; ULONG NextEntry; PLIST_ENTRY Links; PFCB Fcb; PFILE_DIRECTORY_INFORMATION DirInfo; PFILE_NAMES_INFORMATION NamesInfo; PAGED_CODE(); // // Get the current stack location // IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace(+1, Dbg, "NpQueryDirectory\n", 0 ); DebugTrace( 0, Dbg, "RootDcb = %08lx\n", RootDcb); DebugTrace( 0, Dbg, "Ccb = %08lx\n", Ccb); DebugTrace( 0, Dbg, "SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer); DebugTrace( 0, Dbg, "Length = %08lx\n", IrpSp->Parameters.QueryDirectory.Length); DebugTrace( 0, Dbg, "FileName = %Z\n", IrpSp->Parameters.QueryDirectory.FileName); DebugTrace( 0, Dbg, "FileIndex = %08lx\n", IrpSp->Parameters.QueryDirectory.FileIndex); DebugTrace( 0, Dbg, "FileInformationClass = %08lx\n", IrpSp->Parameters.QueryDirectory.FileInformationClass); 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)); // // Save references to the input parameters within the Irp // SystemBufferLength = IrpSp->Parameters.QueryDirectory.Length; FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex; FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass; RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN); ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY); IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED); if (IrpSp->Parameters.QueryDirectory.FileName != NULL) { FileName = *(PUNICODE_STRING)IrpSp->Parameters.QueryDirectory.FileName; } else { FileName.Length = 0; FileName.Buffer = NULL; } // // Check if the ccb already has a query template attached. If it // does not already have one then we either use the string we are // given or we attach our own containing "*" // if (Ccb->QueryTemplate == NULL) { // // This is our first time calling query directory so we need // to either set the query template to the user specified string // or to "*" // if (FileName.Buffer == NULL) { DebugTrace(0, Dbg, "Set template to *\n", 0); FileName.Length = 2; FileName.Buffer = ⋆ } DebugTrace(0, Dbg, "Set query template -> %Z\n", &FileName); // // Allocate space for the query template // Ccb->QueryTemplate = FsRtlAllocatePool( PagedPool, sizeof(UNICODE_STRING) + FileName.Length ); // // Initialize the query template and copy over the string // Ccb->QueryTemplate->Length = FileName.Length; Ccb->QueryTemplate->Buffer = (PWCH)Ccb->QueryTemplate + sizeof(UNICODE_STRING) / sizeof(WCHAR); RtlCopyMemory( Ccb->QueryTemplate->Buffer, FileName.Buffer, FileName.Length ); // // Now zero out the FileName so we won't think we're to use it // as a subsearch string. // FileName.Length = 0; FileName.Buffer = NULL; } // // Check if we were given an index to start with or if we need to // restart the scan or if we should use the index that was saved in // the ccb // if (RestartScan) { FileIndex = 0; } else if (!IndexSpecified) { FileIndex = Ccb->IndexOfLastCcbReturned + 1; } // // Now we are committed to completing the Irp, we do that in // the finally clause of the following try. // try { ULONG BaseLength; ULONG LengthAdded; // // Map the user buffer. // Buffer = NpMapUserBuffer( Irp ); // // At this point we are about to enter our query loop. We have // already decided which Fcb index we need to return. The variables // LastEntry and NextEntry are used to index into the user buffer. // LastEntry is the last entry we added to the user buffer, and // NextEntry is the current one we're working on. CurrentIndex // is the Fcb index that we are looking at next. Logically the // way the loop works is as follows. // // Scan all of the Fcb in the directory // // if the Fcb matches the query template then // // if the CurrentIndex is >= the FileIndex then // // process this fcb, and decide if we should // continue the main loop // // end if // // Increment the current index // // end if // // end scan // CurrentIndex = 0; 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 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( Status = STATUS_INVALID_INFO_CLASS ); } for (Links = RootDcb->Specific.Dcb.ParentDcbQueue.Flink; Links != &RootDcb->Specific.Dcb.ParentDcbQueue; Links = Links->Flink) { Fcb = CONTAINING_RECORD(Links, FCB, ParentDcbLinks); ASSERT(Fcb->NodeTypeCode == NPFS_NTC_FCB); DebugTrace(0, Dbg, "Top of Loop\n", 0); DebugTrace(0, Dbg, "Fcb = %08lx\n", Fcb); DebugTrace(0, Dbg, "CurrentIndex = %08lx\n", CurrentIndex); DebugTrace(0, Dbg, "FileIndex = %08lx\n", FileIndex); DebugTrace(0, Dbg, "LastEntry = %08lx\n", LastEntry); DebugTrace(0, Dbg, "NextEntry = %08lx\n", NextEntry); // // Check if the Fcb represents a named pipe that is part of // our query template // if (FsRtlIsNameInExpression( Ccb->QueryTemplate, &Fcb->LastFileName, CaseInsensitive, NULL )) { // // The fcb is in the query template so now check if // this is the index we should start returning // if (CurrentIndex >= FileIndex) { ULONG BytesToCopy; ULONG BytesRemainingInBuffer; // // 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 = SystemBufferLength - NextEntry; if ( (NextEntry != 0) && ( (BaseLength + Fcb->LastFileName.Length > BytesRemainingInBuffer) || (SystemBufferLength < NextEntry) ) ) { DebugTrace(0, Dbg, "Next entry won't fit\n", 0); try_return( Status = STATUS_SUCCESS ); } ASSERT( BytesRemainingInBuffer >= BaseLength ); // // See how much of the name we will be able to copy into // the system buffer. This also dictates out return // value. // if ( BaseLength + Fcb->LastFileName.Length <= BytesRemainingInBuffer ) { BytesToCopy = Fcb->LastFileName.Length; Status = STATUS_SUCCESS; } else { BytesToCopy = BytesRemainingInBuffer - BaseLength; Status = STATUS_BUFFER_OVERFLOW; } // // Note how much of buffer we are consuming and zero // the base part of the structure. // LengthAdded = BaseLength + BytesToCopy; RtlZeroMemory( &Buffer[NextEntry], BaseLength ); // // Now fill the base parts of the strucure that are // applicable. // switch (FileInformationClass) { case FileBothDirectoryInformation: // // We don't need short name // DebugTrace(0, Dbg, "Getting directory full information\n", 0); case FileFullDirectoryInformation: // // We don't use EaLength, so fill in nothing here. // DebugTrace(0, Dbg, "Getting directory full information\n", 0); case FileDirectoryInformation: DebugTrace(0, Dbg, "Getting directory information\n", 0); // // The eof indicates the number of instances and // allocation size is the maximum allowed // DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry]; DirInfo->EndOfFile.QuadPart = Fcb->OpenCount; DirInfo->AllocationSize.QuadPart = Fcb->Specific.Fcb.MaximumInstances; DirInfo->FileAttributes = FILE_ATTRIBUTE_NORMAL; DirInfo->FileNameLength = Fcb->LastFileName.Length; break; case FileNamesInformation: DebugTrace(0, Dbg, "Getting names information\n", 0); NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry]; NamesInfo->FileNameLength = Fcb->LastFileName.Length; break; default: NpBugCheck( FileInformationClass, 0, 0 ); } RtlCopyMemory( &Buffer[NextEntry + BaseLength], Fcb->LastFileName.Buffer, BytesToCopy ); // // Update the ccb to the index we've just used // Ccb->IndexOfLastCcbReturned = CurrentIndex; // // And indicate how much of the system buffer we have // currently used up. We must compute this value before // we long align outselves for the next entry // Irp->IoStatus.Information = NextEntry + LengthAdded; // // Setup the previous next entry offset // *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry; // // Check if the last entry didn't completely fit // if ( Status == STATUS_BUFFER_OVERFLOW ) { try_return( NOTHING ); } // // Check if we are only to return a single entry // if (ReturnSingleEntry) { try_return( Status = STATUS_SUCCESS ); } // // Set ourselves up for the next iteration // LastEntry = NextEntry; NextEntry += (ULONG)QuadAlign( LengthAdded ); } // // Increment the current index by one // CurrentIndex += 1; } } // // At this point we've scanned the entire list of Fcb so if // the NextEntry is zero then we haven't found anything so we // will return no more files, otherwise we return success. // if (NextEntry == 0) { Status = STATUS_NO_MORE_FILES; } else { Status = STATUS_SUCCESS; } try_exit: NOTHING; } finally { if (!AbnormalTermination()) { NpCompleteRequest( Irp, Status ); } DebugTrace(-1, Dbg, "NpQueryDirectory -> %08lx\n", Status); } return Status; }
NTSTATUS DfsDriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { NTSTATUS Status; UNICODE_STRING UnicodeString; PDEVICE_OBJECT DeviceObject; OBJECT_ATTRIBUTES ObjectAttributes; PWSTR p; int i; HANDLE hTemp; HANDLE DirHandle; IO_STATUS_BLOCK iosb; // // See if someone else has already created a File System Device object // with the name we intend to use. If so, we bail. // RtlInitUnicodeString( &UnicodeString, DFS_DRIVER_NAME ); InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, 0, NULL); Status = ZwCreateFile( &hTemp, SYNCHRONIZE, &ObjectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0); if (NT_SUCCESS(Status)) { ZwClose( hTemp ); DfsDbgTrace(0, Dbg, "Dfs driver already loaded!\n", 0); return( STATUS_UNSUCCESSFUL ); } // // Create the filesystem device object. // Status = IoCreateDevice( DriverObject, 0, &UnicodeString, FILE_DEVICE_DFS_FILE_SYSTEM, FILE_REMOTE_DEVICE, FALSE, &DeviceObject ); if ( !NT_SUCCESS( Status ) ) { return Status; } // // Create a permanent object directory in which the logical root // device objects will reside. Make the directory temporary, so // we can just close the handle to make it go away. // UnicodeString.Buffer = p = LogicalRootDevPath; UnicodeString.Length = 0; UnicodeString.MaximumLength = MAX_LOGICAL_ROOT_LEN; while (*p++ != UNICODE_NULL) UnicodeString.Length += sizeof (WCHAR); InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_PERMANENT, NULL, NULL ); Status = ZwCreateDirectoryObject( &DirHandle, DIRECTORY_ALL_ACCESS, &ObjectAttributes); if ( !NT_SUCCESS( Status ) ) { return Status; } ZwMakeTemporaryObject(DirHandle); p[-1] = UNICODE_PATH_SEP; UnicodeString.Length += sizeof (WCHAR); // // Initialize the driver object with this driver's entry points. // Most are simply passed through to some other device driver. // for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { DriverObject->MajorFunction[i] = DfsVolumePassThrough; } DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)DfsFsdCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)DfsFsdClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)DfsFsdCleanup; DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)DfsFsdQueryInformation; DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)DfsFsdSetInformation; DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)DfsFsdFileSystemControl; DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION]= (PDRIVER_DISPATCH)DfsFsdQueryVolumeInformation; DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION]= (PDRIVER_DISPATCH)DfsFsdSetVolumeInformation; DriverObject->FastIoDispatch = &FastIoDispatch; // // Initialize the global data structures // RtlZeroMemory(&DfsData, sizeof (DFS_DATA)); DfsData.NodeTypeCode = DSFS_NTC_DATA_HEADER; DfsData.NodeByteSize = sizeof( DFS_DATA ); InitializeListHead( &DfsData.VcbQueue ); InitializeListHead( &DfsData.DeletedVcbQueue ); InitializeListHead( &DfsData.Credentials ); InitializeListHead( &DfsData.DeletedCredentials ); DfsData.DriverObject = DriverObject; DfsData.FileSysDeviceObject = DeviceObject; DfsData.LogRootDevName = UnicodeString; ExInitializeResource( &DfsData.Resource ); KeInitializeEvent( &DfsData.PktWritePending, NotificationEvent, TRUE ); KeInitializeSemaphore( &DfsData.PktReferralRequests, 1, 1 ); DfsData.MachineState = DFS_CLIENT; // // Allocate Provider structures. // DfsData.pProvider = ExAllocatePool( PagedPool, sizeof ( PROVIDER_DEF ) * MAX_PROVIDERS); for (i = 0; i < MAX_PROVIDERS; i++) { DfsData.pProvider[i].NodeTypeCode = DSFS_NTC_PROVIDER; DfsData.pProvider[i].NodeByteSize = sizeof ( PROVIDER_DEF ); } DfsData.cProvider = 0; DfsData.maxProvider = MAX_PROVIDERS; // // Initialize the system wide PKT // PktInitialize(&DfsData.Pkt); { ULONG SystemSizeMultiplier; ULONG ZoneSegmentSize; switch (MmQuerySystemSize()) { default: case MmSmallSystem: SystemSizeMultiplier = 4; break; case MmMediumSystem: SystemSizeMultiplier = 8; break; case MmLargeSystem: SystemSizeMultiplier = 16; break; } // // Allocate the DFS_FCB hash table structure. The number of hash buckets // will depend upon the memory size of the system. // Status = DfsInitFcbs(SystemSizeMultiplier * 2); // // Now initialize the zone structures for allocating IRP context // records. The size of the zone will depend upon the memory // available in the system. // KeInitializeSpinLock( &DfsData.IrpContextSpinLock ); ZoneSegmentSize = (SystemSizeMultiplier * QuadAlign(sizeof(IRP_CONTEXT))) + sizeof(ZONE_SEGMENT_HEADER); (VOID) ExInitializeZone( &DfsData.IrpContextZone, QuadAlign(sizeof(IRP_CONTEXT)), FsRtlAllocatePool( NonPagedPool, ZoneSegmentSize ), ZoneSegmentSize ); } // // Set up global pointer to the system process. // DfsData.OurProcess = PsGetCurrentProcess(); // // Register the file system with the I/O system // IoRegisterFileSystem( DeviceObject ); // // Initialize the provider definitions from the registry. // if (!NT_SUCCESS( ProviderInit() )) { DfsDbgTrace(0,DEBUG_TRACE_ERROR, "Could not initialize some or all providers!\n", 0); } // // Initialize the logical roots device objects. These are what form the // link between the outside world and the Dfs driver. // Status = DfsInitializeLogicalRoot( DD_DFS_DEVICE_NAME, NULL, NULL, 0); if (!NT_SUCCESS(Status)) { DfsDbgTrace(-1, DEBUG_TRACE_ERROR, "Failed creation of root logical root %08lx\n", Status); return(Status); } // // Let us start off the Timer Routine. // RtlZeroMemory(&DfsTimerContext, sizeof(DFS_TIMER_CONTEXT)); DfsTimerContext.InUse = FALSE; DfsTimerContext.TickCount = 0; IoInitializeTimer(DeviceObject, DfsIoTimerRoutine, &DfsTimerContext); DfsDbgTrace(0, Dbg, "Initialized the Timer routine\n", 0); // // Let us start the timer now. // IoStartTimer(DeviceObject); return STATUS_SUCCESS; }