void EventManager::update() { while(m_bufferCurPos != m_bufferAddPos) { const IEvent* evt = (IEvent*)(m_eventBuffer+m_bufferCurPos); FLT_ASSERT(evt!=0); EventQueues::iterator map_iter = m_events.find(evt->getUID()); if(map_iter!=m_events.end()) { const ReceiverList& receivers = map_iter->second; for(ReceiverList::const_iterator list_iter=receivers.begin(); list_iter!=receivers.end(); ++list_iter) { FLT_ASSERT((*list_iter)!=0); if((*list_iter)->onEvent(*evt)) break; } } m_bufferCurPos += k_max_event_size; if(m_bufferCurPos >= k_event_buffer_size) { m_bufferCurPos = 0; } } }
UCHAR TxNotificationToMinorCode ( _In_ ULONG TxNotification ) /*++ Routine Description: This routine has been written to convert a transaction notification code to an Irp minor code. This function is needed because RECORD_DATA has a UCHAR field for the Irp minor code whereas TxNotification is ULONG. As of now all this function does is compute log_base_2(TxNotification) + 1. That fits our need for now but might have to be evolved later. This function is intricately tied with the enumeration TRANSACTION_NOTIFICATION_CODES in mspyLog.h and the case statements related to transactions in the function PrintIrpCode (Minispy\User\mspyLog.c). Arguments: TxNotification - The transaction notification received. Return Value: 0 if TxNotification is 0; log_base_2(TxNotification) + 1 otherwise. --*/ { UCHAR count = 0; if (TxNotification == 0) return 0; // // This assert verifies if no more than one flag is set // in the TxNotification variable. TxNotification flags are // supposed to be mutually exclusive. The assert below verifies // if the value of TxNotification is a power of 2. If it is not // then we will break. // FLT_ASSERT( !(( TxNotification ) & ( TxNotification - 1 )) ); while (TxNotification) { count++; TxNotification >>= 1; // // If we hit this assert then we have more notification codes than // can fit in a UCHAR. We need to revaluate our approach for // storing minor codes now. // FLT_ASSERT( count != 0 ); } return ( count ); }
void Phy2dWorld::selectedActor(Phy2dActor* actor) { FLT_ASSERT(actor!=0); FLT_ASSERT(!actor->isSelected()); if(actor!=0) { actor->setSelected(true); m_editorSelectedActors.push_back(actor); } }
void Phy2dWorld::deselectedActor(Phy2dActor* actor) { FLT_ASSERT(actor!=0); FLT_ASSERT(actor->isSelected()); if(actor!=0) { actor->setSelected(false); array_t<Phy2dActor*>::iterator iter = find(m_editorSelectedActors.begin(), m_editorSelectedActors.end(), actor); m_editorSelectedActors.erase(iter); } }
int Reader::ReadLine(char* cbuf, int buflen) { int lineLength = 0; char c = 0; while(true) { int count = m_stream.Read(&c, sizeof(char)); // End of stream and no character read? if(count <= 0 && lineLength == 0) return -1; FLT_ASSERT(buflen > lineLength); if(count == 0 || buflen < lineLength + 1) break; if(c == '\r') { // Also skip the following '\n' ? count = m_stream.Read(&c, sizeof(char)); // Oups, need to go back by one char if(c != '\n' && count > 0) { // int before = m_stream.Tell(); m_stream.Seek(-1, io::cur); // int after = m_stream.Tell(); //int diff = after - before; } break; } else if(c == '\n') { break; } else { cbuf[lineLength++] = c; } } FLT_ASSERT(buflen > lineLength); cbuf[lineLength] = 0; return lineLength; }
void Phy2dWorld::setCameraZoomLimit(f32 minZoom, f32 maxZoom) { FLT_ASSERT(minZoom<=maxZoom && minZoom>0); m_cameraZoomMin = minZoom; m_cameraZoomMax = maxZoom; }
NTSTATUS FltpDeviceControlHandler(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp) { PIO_STACK_LOCATION StackPtr; ULONG ControlCode; NTSTATUS Status; StackPtr = IoGetCurrentIrpStackLocation(Irp); FLT_ASSERT(StackPtr->MajorFunction == IRP_MJ_DEVICE_CONTROL); ControlCode = StackPtr->Parameters.DeviceIoControl.IoControlCode; switch (ControlCode) { case IOCTL_LOAD_FILTER: Status = HandleLoadUnloadIoctl(DeviceObject, Irp); break; case IOCTL_UNLOAD_FILTER: Status = HandleLoadUnloadIoctl(DeviceObject, Irp); break; case IOCTL_FIND_FIRST_FILTER: Status = HandleFindFirstIoctl(DeviceObject, Irp); break; default: Status = STATUS_INVALID_PARAMETER; break; } return Status; }
NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) /*++ Routine Description: This is the initialization routine for this miniFilter driver. This registers the miniFilter with FltMgr and initializes all its global data structures. Arguments: DriverObject - Pointer to driver object created by the system to represent this driver. RegistryPath - Unicode string identifying where the parameters for this driver are located in the registry. Return Value: Returns STATUS_SUCCESS. --*/ { NTSTATUS status; UNREFERENCED_PARAMETER( RegistryPath ); // // Register with FltMgr // status = FltRegisterFilter( DriverObject, &FilterRegistration, &NullFilterData.FilterHandle ); FLT_ASSERT( NT_SUCCESS( status ) ); if (NT_SUCCESS( status )) { // // Start filtering i/o // status = FltStartFiltering( NullFilterData.FilterHandle ); if (!NT_SUCCESS( status )) { FltUnregisterFilter( NullFilterData.FilterHandle ); } } return status; }
VOID FLTAPI UcaInstanceContextCleanupCallback(_In_ PUCA_INSTANCE_CONTEXT InstanceContext, _In_ FLT_CONTEXT_TYPE ContextType) { UNREFERENCED_PARAMETER(ContextType); PAGED_CODE(); __debugbreak(); FLT_ASSERT(ContextType == FLT_INSTANCE_CONTEXT); ExFreePoolWithTag(&InstanceContext->VolumeGuidName, UCA_POOL_TAG); }
static VOID LcWriteCallback ( _In_ PFLT_CALLBACK_DATA CallbackData, _In_ PFLT_CONTEXT Context ) /*++ Summary: This function is a completion callback for the asynchronous I/O write operation. Arguments: CallbackData - Pointer to the callback data structure for the I/O operation. Context - Custom context pointer. Return Value: None. --*/ { PWRITE_CALLBACK_CONTEXT context = (PWRITE_CALLBACK_CONTEXT)Context; FLT_ASSERT(CallbackData != NULL); FLT_ASSERT(Context != NULL); PAGED_CODE(); context->Status = CallbackData->IoStatus.Status; context->BytesWritten = NT_SUCCESS(context->Status) ? (ULONG)CallbackData->IoStatus.Information : 0; // Notify listeners that write operation has completed for the current chunk. KeSetEvent(context->Event, IO_NO_INCREMENT, FALSE); }
void EventManager::raiseEvent(const IEvent& event) { int size = ((IEvent*)(&event))->getSize(); FLT_ASSERT(size<k_max_event_size); memcpy( (m_eventBuffer+m_bufferAddPos), &event, size); m_bufferAddPos += k_max_event_size; if(m_bufferAddPos >= k_event_buffer_size) { m_bufferAddPos = 0; } }
VOID FLTAPI UcaStreamContextCleanupCallback(_In_ PUCA_STREAM_CONTEXT StreamContext, _In_ FLT_CONTEXT_TYPE ContextType) { PAGED_CODE(); //__debugbreak(); FLT_ASSERT(ContextType == FLT_STREAM_CONTEXT || ContextType == FLT_FILE_CONTEXT); if (StreamContext->FileNameInfo) { FltReleaseFileNameInformation(StreamContext->FileNameInfo); StreamContext->FileNameInfo = NULL; } }
s32 StubDevice::getRandS32(s32 min, s32 max) const { if (min > max) { int t = min; min = max; max = t; } long long rnd = getRandS32() >> 2; s32 range = max - min; s32 res = (s32)(min + (rnd % range)); FLT_ASSERT(res >= min && res < max); return res; }
VOID NcSetShortName ( _In_ PVOID Entry, _In_ PWSTR NewShortName, _In_ USHORT Length, _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets ) /*++ Routine Description: Sets a new short name into an entry if the information class supports short names. Arguments: Entry - Pointer to the start of an entry. NewShortName - Pointer to the new shortname. Length - The length of the short name in bytes. Offsets - Offsets structure for the information class. Return Value: None. --*/ { PWSTR NamePtr; PCCHAR NameLength; PAGED_CODE(); if( Offsets->ShortNamePresent ) { NamePtr = NcGetShortName( Entry, Offsets ); NameLength = Add2Ptr( Entry, Offsets->ShortNameLengthDist ); FLT_ASSERT( Length <= (12 * sizeof(WCHAR)) ); RtlCopyMemory(NamePtr, NewShortName, Length ); *NameLength = (UCHAR) Length; } }
NTSTATUS HandleLoadUnloadIoctl(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp) { PIO_STACK_LOCATION StackPtr; UNICODE_STRING Name; PFILTER_NAME FilterName; ULONG BufferLength; ULONG ControlCode; /* Get the IOCTL data from the stack pointer */ StackPtr = IoGetCurrentIrpStackLocation(Irp); BufferLength = StackPtr->Parameters.DeviceIoControl.InputBufferLength; ControlCode = StackPtr->Parameters.DeviceIoControl.IoControlCode; FLT_ASSERT(ControlCode == IOCTL_LOAD_FILTER || ControlCode == IOCTL_UNLOAD_FILTER); /* Make sure the buffer is valid */ if (BufferLength < sizeof(FILTER_NAME)) return STATUS_INVALID_PARAMETER; /* Convert the file name buffer into a string */ FilterName = (PFILTER_NAME)Irp->AssociatedIrp.SystemBuffer; Name.Length = FilterName->Length; Name.MaximumLength = FilterName->Length; Name.Buffer = (PWCH)((PCHAR)FilterName + FIELD_OFFSET(FILTER_NAME, FilterName[0])); /* Forward the request to our Flt routines */ if (ControlCode == IOCTL_LOAD_FILTER) { return FltLoadFilter(&Name); } else { return FltUnloadFilter(&Name); } }
VOID SpyFreeRecord ( _In_ PRECORD_LIST Record ) /*++ Routine Description: Free the given buffer NOTE: This code must be NON-PAGED because it can be called on the paging path or at DPC level. Arguments: Record - the buffer to free Return Value: None. --*/ { if (FlagOn(Record->LogRecord.RecordType,RECORD_TYPE_FLAG_STATIC)) { // // This was our static buffer, mark it available. // FLT_ASSERT(MiniFSWatcherData.StaticBufferInUse); MiniFSWatcherData.StaticBufferInUse = FALSE; } else { SpyFreeBuffer( Record ); } }
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) { NTSTATUS status; UNREFERENCED_PARAMETER(RegistryPath); KdPrint(("Passthrough!DriverEntry: entered\n")); status = FltRegisterFilter(DriverObject, &FilterRegistration, &gFilterHandle); FLT_ASSERT(NT_SUCCESS(status)); if (NT_SUCCESS(status)) { status = FltStartFiltering(gFilterHandle); if (!NT_SUCCESS(status)) { FltUnregisterFilter(gFilterHandle); } KdPrint(("Passthrough!DriverEntry: start mini filter \n")); } return status; }
NTSTATUS NcGenerateFileName ( _In_ PFLT_INSTANCE Instance, _In_ PFILE_OBJECT FileObject, _In_opt_ PFLT_CALLBACK_DATA Data, _In_ FLT_FILE_NAME_OPTIONS NameOptions, _Out_ PBOOLEAN CacheFileNameInformation, _Inout_ PFLT_NAME_CONTROL OutputNameControl ) { // // Status vars // NTSTATUS Status; // // State lookup vars // BOOLEAN Opened = (BOOLEAN)(FileObject->FsContext != NULL); // True if file object is opened. BOOLEAN ReturnShortName = (BOOLEAN)(FltGetFileNameFormat(NameOptions) == FLT_FILE_NAME_SHORT); // True if the user is requesting short name BOOLEAN ReturnOpenedName = (BOOLEAN)(FltGetFileNameFormat(NameOptions) == FLT_FILE_NAME_OPENED); // True if user is requesting opened name. BOOLEAN ReturnNormalizedName = (BOOLEAN)(FltGetFileNameFormat(NameOptions) == FLT_FILE_NAME_NORMALIZED); // True if user is requesting normalized name. BOOLEAN IgnoreCase; FLT_FILE_NAME_OPTIONS NameQueryMethod = FltGetFileNameQueryMethod( NameOptions ); FLT_FILE_NAME_OPTIONS NameFlags = FLT_VALID_FILE_NAME_FLAGS & NameOptions; // // File name information // PFLT_FILE_NAME_INFORMATION LowerNameInfo = NULL; // File name as reported by lower name provider. Will always be down real mapping. PFLT_FILE_NAME_INFORMATION ShortInfo = NULL; // We will use ShortInfo to store the short name if needed. // // Contexts // PNC_INSTANCE_CONTEXT InstanceContext = NULL; // // Overlap // NC_PATH_OVERLAP RealOverlap; UNICODE_STRING RealRemainder = EMPTY_UNICODE_STRING; // // Temp storage // UNICODE_STRING MungedName = EMPTY_UNICODE_STRING; // // Temp pointer // PUNICODE_STRING Name = NULL; // Pointer to the name we are going to use. PAGED_CODE(); FLT_ASSERT( IoGetTopLevelIrp() == NULL ); // // This should never happen, but let's be safe. // if (!ReturnShortName && !ReturnOpenedName && !ReturnNormalizedName) { FLT_ASSERT( FALSE ); Status = STATUS_NOT_SUPPORTED; goto NcGenerateFileNameCleanup; } RealOverlap.EntireFlags = 0; // // To prevent infinite recursion, calls to FltGetFileNameInformation // from generate file name callbacks should not target current provider. // ClearFlag( NameFlags, FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER ); // // Fetch the instance context. // Status = FltGetInstanceContext( Instance, &InstanceContext ); if (!NT_SUCCESS( Status )) { goto NcGenerateFileNameCleanup; } // // We need to know what the name provider under us thinks the file is called. // If the caller wants the normalized name we query that, otherwise we query // the opened name because we have to compare the full path of the file vs. // the real mapping to determine if the file is mapped. // Status = NcGetFileNameInformation( Data, FileObject, Instance, (ReturnNormalizedName ? FLT_FILE_NAME_NORMALIZED : FLT_FILE_NAME_OPENED) | NameQueryMethod | NameFlags, &LowerNameInfo ); if (!NT_SUCCESS( Status )) { goto NcGenerateFileNameCleanup; } Status = FltParseFileNameInformation( LowerNameInfo ); if (!NT_SUCCESS( Status )) { goto NcGenerateFileNameCleanup; } // // Issues With Pre-open path: // // 1) Poison name cache below name provider: // If a filter above a name provider calls FltGetFileNameInformation on a // file object in his precreate callback, fltmgr will call the name // provider's generate name callback before the name provider's pre create // callback is invoked. Name providers by their nature change names in their // pre-create. Because the name provider has not had the opportunity to // modify the name yet, we need to make sure that fltmgr does not cache the name we // return below us, so we set the FLT_FILE_NAME_DO_NOT_CACHE flag. // //TODO: TRY TO GET ACROSS THAT THIS IS A NAME CHANGER PROBLEM, NOT ALL NAME PROVIDERS NEED TO. // if (!Opened) { SetFlag( NameFlags, FLT_FILE_NAME_DO_NOT_CACHE ); if (Data) { // // NT supports case sensitive and non-case sensitive naming in file systems. // This is handled on a per-open basis. Weather an open is case senstive is // determined by the FO_OPENED_CASE_SENSITIVE flag on the file object. // In pre-create the SL_CASE_SENSITIVE flag on the create IRP specifies the mode. // // If this is on an unopened FileObject, it had better be pre-create so we know // how to process the operation. If we are queried on an unopened FileObject // at any other time we have no way to handle the request. // FLT_ASSERT( Data->Iopb->MajorFunction == IRP_MJ_CREATE || Data->Iopb->MajorFunction == IRP_MJ_NETWORK_QUERY_OPEN ); IgnoreCase = !BooleanFlagOn( Data->Iopb->OperationFlags, SL_CASE_SENSITIVE ); } else { // // If people do unsafe queries on preopened IOs, we cannot // determine if the open is case sensitive or not. // So we cannot determine if this open is down the mapping. // fail. // FLT_ASSERT( FALSE ); Status = STATUS_INVALID_PARAMETER; goto NcGenerateFileNameCleanup; } } else { // // After a file has been opened, the case sensitivity is stored in the file object. // IgnoreCase = !BooleanFlagOn( FileObject->Flags, FO_OPENED_CASE_SENSITIVE ); } // // Calculate the overlap with the real mapping. // NcComparePath( &LowerNameInfo->Name, &InstanceContext->Mapping.RealMapping, &RealRemainder, IgnoreCase, TRUE, &RealOverlap ); // // Whether we munge depends on what name is requested. // if (ReturnOpenedName || ReturnNormalizedName) { if (Opened && RealOverlap.InMapping) { // // We munge the opened name if it overlaps with the real mapping. // The returned path will be down the user mapping. // Status = NcConstructPath( &InstanceContext->Mapping.UserMapping, &RealRemainder, TRUE, &MungedName); if (!NT_SUCCESS( Status )) { goto NcGenerateFileNameCleanup; } Name = &MungedName; } else { // // We return the queried result if the path is not in the // mapping. // Name = &LowerNameInfo->Name; } } else if (ReturnShortName) { // // Note that unlike opened names, a query for a shortname only returns // the final component, not the full path. // // TODO: Assert not preopen if (RealOverlap.Match) { // // The opened path is the mapping path. // This means that if we queried the filesystem // he would return the wrong path. // // Luckily, we can just use the mapping. // Name = &InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName; } else { // // We have to query below us to get the short name. // Status = NcGetFileNameInformation( Data, FileObject, Instance, FLT_FILE_NAME_SHORT | NameQueryMethod | NameFlags, &ShortInfo ); if (!NT_SUCCESS( Status )) { goto NcGenerateFileNameCleanup; } Status = FltParseFileNameInformation( ShortInfo ); if (!NT_SUCCESS(Status)) { goto NcGenerateFileNameCleanup; } // // Set name to returned name. // Name = &ShortInfo->Name; } } FLT_ASSERT( Name != NULL ); // // Try to grow the namechanger's record to accommodate the result. // Status = FltCheckAndGrowNameControl( OutputNameControl, Name->Length ); if (NT_SUCCESS( Status )) { // // Copy the new name into the buffer. // RtlCopyUnicodeString( &OutputNameControl->Name, Name ); *CacheFileNameInformation = TRUE; } NcGenerateFileNameCleanup: if (LowerNameInfo != NULL) { FltReleaseFileNameInformation( LowerNameInfo ); } if (ShortInfo != NULL) { FltReleaseFileNameInformation( ShortInfo ); } if (InstanceContext != NULL) { FltReleaseContext( InstanceContext ); } if (MungedName.Buffer != NULL) { ExFreePoolWithTag( MungedName.Buffer, NC_GENERATE_NAME_TAG ); } return Status; }
NTSTATUS FmmIsImplicitVolumeLock( _In_ PFLT_CALLBACK_DATA Cbd, _Out_ PBOOLEAN IsLock ) /*++ Routine Description: This routine determines if an open is a implcit volume lock. Arguments Cbd - Supplies a pointer to the callbackData which declares the requested operation. IsLock - Supplies a pointer to a user allocated boolean which is used to tell the user wheather the operation is an implied volume lock. Return Value: Returns STATUS_SUCCESS if the the function determined wheather or not the operation was a volume lock. On STATUS_SUCCESS it is safe to check IsLock to get the answer. Otherwise, the check failed and we dont know if it is a lock or not. STATUS_INVALID_PARAMETER indicates that the volume's file system type is unrecognized by the check function. This is an error code. --*/ { NTSTATUS status = STATUS_SUCCESS; PFMM_INSTANCE_CONTEXT instanceContext = NULL; USHORT shareAccess; ACCESS_MASK prevAccess; PAGED_CODE(); // // Get the instance context so we know // which file system we are attached to. // status = FltGetInstanceContext( Cbd->Iopb->TargetInstance, &instanceContext ); if (!NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, ("[Fmm]: FmmIsImplicitVolumeLock -> Failed to get instance context.\n") ); goto FmmIsImplicitVolumeLockCleanup; } FLT_ASSERT( instanceContext != NULL ); // // Now check to see if the open is an implied volume lock // on this filesystem. // shareAccess = Cbd->Iopb->Parameters.Create.ShareAccess; prevAccess = Cbd->Iopb->Parameters.Create.SecurityContext->DesiredAccess; switch (instanceContext->FilesystemType) { case FLT_FSTYPE_REFS: *IsLock = ((!BooleanFlagOn( shareAccess, FILE_SHARE_WRITE | FILE_SHARE_DELETE)) && (BooleanFlagOn( prevAccess,(FILE_WRITE_DATA | FILE_APPEND_DATA) ))); status = STATUS_SUCCESS; break; case FLT_FSTYPE_NTFS: *IsLock = ((!BooleanFlagOn( shareAccess, FILE_SHARE_WRITE | FILE_SHARE_DELETE)) && (BooleanFlagOn( prevAccess,(FILE_WRITE_DATA | FILE_APPEND_DATA) ))); status = STATUS_SUCCESS; break; case FLT_FSTYPE_FAT: *IsLock = (!BooleanFlagOn( shareAccess, FILE_SHARE_WRITE | FILE_SHARE_DELETE)); status = STATUS_SUCCESS; break; default: status = STATUS_INVALID_PARAMETER; break; } FmmIsImplicitVolumeLockCleanup: if (instanceContext != NULL ) { FltReleaseContext( instanceContext ); } return status; }
NTSTATUS NcGetFileNameInformation( _In_opt_ PFLT_CALLBACK_DATA Data, _In_opt_ PFILE_OBJECT FileObject, _In_opt_ PFLT_INSTANCE Instance, _In_ FLT_FILE_NAME_OPTIONS NameOptions, _Outptr_ PFLT_FILE_NAME_INFORMATION *FileNameInformation ) /*++ Routine Description: This function is a wrapper to call the correct variant of FltGetFileNameInformation depending on the information we happen to have available. Arguments: Data - Pointer to the callback data structure associated with a request. This is optional, but if not specified, FileObject and Instance must be supplied. FileObject - Pointer to the file object to query a name on. Optional, but if not supplied, Data must be supplied. Instance - Pointer to the instance of our filter to query the name on. Optional, but if not supplied, Data must be supplied. NameOptions - FLT_FILE_NAME_* flags for this request. FileNameInformation - On output, contains the file name information resulting from this query. On failure, contents are undefined. On success, caller is responsible for releasing this with FltReleaseFileNameInformation. Return Value: Returns the status of the operation. --*/ { NTSTATUS Status; PAGED_CODE(); FLT_ASSERT( Data || FileObject ); *FileNameInformation = NULL; if (ARGUMENT_PRESENT( Data )) { Status = FltGetFileNameInformation( Data, NameOptions, FileNameInformation ); } else if (ARGUMENT_PRESENT( FileObject )) { Status = FltGetFileNameInformationUnsafe( FileObject, Instance, NameOptions, FileNameInformation ); // // This should never happen, as either Data or FileObject must be non-NULL. // } else { FLT_ASSERT( FALSE ); Status = STATUS_INVALID_PARAMETER; } return Status; }
NTSTATUS NcEnumerateDirectorySetupInjection ( _Inout_ PNC_DIR_QRY_CONTEXT DirQryCtx, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ PNC_INSTANCE_CONTEXT InstanceContext, _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, _In_ FILE_INFORMATION_CLASS InformationClass ) /*++ Routine Description: Sets up directory enumeration context cache so that we are ready to perform injection. Arguments: DirQryCtx - Pointer to directory query context (on the stream handle.) FltObjects - FltObjects structure for this operation. InstanceContext - Instance Context for this operation. Offsets - Offsets structure for this information class. InformationClass - The information class for this operation. Return Value: Returns STATUS_SUCCESS on success, otherwise an appropriate error code. --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES RealParentAttributes; HANDLE RealParentHandle = 0; //close always PFILE_OBJECT RealParentFileObj = NULL; IO_STATUS_BLOCK RealParentStatusBlock; char * QueryBuffer = NULL; //free on error, when no injection ULONG QueryBufferLength = 0; USHORT NameLength; ULONG QueryBufferLengthRead; BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, FO_OPENED_CASE_SENSITIVE ); PAGED_CODE(); // // If the user has specified a search string, and if our user mapping // should not be returned in this search string, return success. We // don't need to inject anything. // if (DirQryCtx->SearchString.Length > 0 && !FsRtlIsNameInExpression( &DirQryCtx->SearchString, &InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName, IgnoreCase, NULL ) && !FsRtlIsNameInExpression( &DirQryCtx->SearchString, &InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName, IgnoreCase, NULL )) { Status = STATUS_SUCCESS; goto NcEnumerateDirectorySetupCleanup; } // // Initialize insertion info. // // We have to insert the final component of the real mapping // as the final component of the user mapping. To do this we // will open the parent of the real mapping, and query the real // mapping. // Then we will overwrite the real mapping's name with // the final component of the user mapping. This data will be // stored in the DirQryCtx for later injection. // // // Open parent of real mapping. // InitializeObjectAttributes( &RealParentAttributes, &InstanceContext->Mapping.RealMapping.LongNamePath.ParentPath, OBJ_KERNEL_HANDLE, NULL, NULL); Status = NcCreateFileHelper( NcGlobalData.FilterHandle, // Filter FltObjects->Instance, // InstanceOffsets &RealParentHandle, // Returned Handle &RealParentFileObj, // Returned FileObject FILE_LIST_DIRECTORY|FILE_TRAVERSE, // Desired Access &RealParentAttributes, // object attributes &RealParentStatusBlock, // Returned IOStatusBlock 0, // Allocation Size FILE_ATTRIBUTE_NORMAL, // File Attributes 0, // Share Access FILE_OPEN, // Create Disposition FILE_DIRECTORY_FILE, // Create Options NULL, // Ea Buffer 0, // EA Length IO_IGNORE_SHARE_ACCESS_CHECK, // Flags FltObjects->FileObject ); // Transaction state if (!NT_SUCCESS( Status )) { goto NcEnumerateDirectorySetupCleanup; } // // Allocate Buffer to store mapping data. // NameLength = Max( InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName.Length, InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length ); QueryBufferLength = Offsets->FileNameDist + NameLength; QueryBuffer = ExAllocatePoolWithTag( PagedPool, QueryBufferLength, NC_DIR_QRY_CACHE_TAG ); if (QueryBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto NcEnumerateDirectorySetupCleanup; } // // Query the information from the parent of the real mapping. // Status = NcQueryDirectoryFile( FltObjects->Instance, RealParentFileObj, QueryBuffer, QueryBufferLength, InformationClass, TRUE,//Return single entry &InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName, FALSE,//restart scan &QueryBufferLengthRead); if (Status == STATUS_NO_SUCH_FILE) { // // The user mapping does not exist, this is allowed. It means we // have nothing to inject. // DirQryCtx->InjectionEntry.Buffer = NULL; DirQryCtx->InjectionEntry.CurrentOffset = 0; ExFreePoolWithTag( QueryBuffer, NC_DIR_QRY_CACHE_TAG ); QueryBuffer = NULL; Status = STATUS_SUCCESS; } else if (!NT_SUCCESS( Status )) { // // An unexpected error occurred, return code. // goto NcEnumerateDirectorySetupCleanup; } else { // // Now we have to munge the real mapping directory entry into a // user mapping directory entry. // NcSetFileName( QueryBuffer, InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Buffer, InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length, Offsets, TRUE ); NcSetShortName( QueryBuffer, InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName.Buffer, InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName.Length, Offsets ); FLT_ASSERT( DirQryCtx->InjectionEntry.Buffer == NULL ); // // Set the injection entry up in the cache. // DirQryCtx->InjectionEntry.Buffer = QueryBuffer; DirQryCtx->InjectionEntry.CurrentOffset = 0; } NcEnumerateDirectorySetupCleanup: if (!NT_SUCCESS( Status )) { if(QueryBuffer != NULL) { ExFreePoolWithTag( QueryBuffer, NC_DIR_QRY_CACHE_TAG ); } } if (RealParentHandle != NULL) { FltClose( RealParentHandle ); } if (RealParentFileObj != NULL) { ObDereferenceObject( RealParentFileObj ); } return Status; }
VOID FmmInstanceTeardownComplete ( _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags ) /*++ Routine Description: This routine is called at the end of instance teardown. Arguments: FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing opaque handles to this filter, instance and its associated volume. Flags - Reason why this instance is been deleted. Return Value: None. --*/ { PFMM_INSTANCE_CONTEXT instanceContext; NTSTATUS status; UNREFERENCED_PARAMETER( Flags ); PAGED_CODE(); DebugTrace( DEBUG_TRACE_INSTANCES, ("[Fmm]: Instance teardown complete started (Instance = %p)\n", FltObjects->Instance) ); status = FltGetInstanceContext( FltObjects->Instance, &instanceContext ); if (NT_SUCCESS( status )) { // // Acquire exclusive access to the instance context // FmmAcquireResourceExclusive( &instanceContext->MetadataResource ); // // Sanity - the instance context cannot be in a transition state during instance teardown complete // FLT_ASSERT( !FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION) ); if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) { // // Close the metadata file // FmmCloseMetadata( instanceContext ); } // // Relinquish exclusive access to the instance context // FmmReleaseResource( &instanceContext->MetadataResource ); FltReleaseContext( instanceContext ); } DebugTrace( DEBUG_TRACE_INSTANCES, ("[Fmm]: Instance teardown complete ended (Instance = %p)\n", FltObjects->Instance) ); }
static _Check_return_ NTSTATUS LcFetchFileByChunks ( _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ HANDLE SourceFileHandle, _In_ PLARGE_INTEGER SourceFileSize, _Out_ PLARGE_INTEGER BytesCopied ) /*++ Summary: This function copies the original file from the 'SourceFileHandle' to the currently opened file ('FltObjects->FileObject') by chunks. It maintains its own list of chunks, and extends it, if there are no chunks available to read into. Write operation goes from one chunk to another in a sequential order. If the next chunk is empty, write waits for the read to be completed, and proceeds. There are simple rules for chunks allocation: 1. Up to two chunks are initially allocated: a) If the file is smaller than the 'ChunkSize', only one chunk is allocated with buffer that is equal to the file size. b) If the file is larger than the 'ChunkSize', two chunks are allocated, taking the file size into account for the second chunk size. 2. If all chunks currently allocated are full and awaiting to be written to a disk, and the current amount of chunks is lesser than 'MaxChunks', an additional chunk is allocated. There is a corner case, when the actual file size differs from the reported one. In this case one of the chunks in the list will be smaller than the 'ChunkSize'. For example, 'MaxChunks' is 3, ChunkSize is '10', file size reported is '12', and actual file size is '25'. Two chunks will be initially allocated: [1] 10b; [2] (12-10)=2b. Later on, when all of them will be filled in with the data, and EOF will not be received, because the actual size is 25b, another chunk [3] of size 10b (ChunkSize) will be allocated. In total, there will be three chunks: 10b, 2b, and 10b. We don't reallocate the 2nd chunk, because this driver is supposed to be used with a proper filesystems, but making this modification might be a valuable TODO item. Let's look at how chunks work. All chunks are stored in the doubly-linked list. [Head] node doesn't contain any buffer to store data in. Refer to the 'FILE_CHUNK' structure for details. MSDN about lists: http://msdn.microsoft.com/en-us/library/windows/hardware/ff563802(v=vs.85).aspx For large files we will have two chunks from the beginning. [Head] <-> [1] <-> [2] <-> [Head] There are pointers for [R]ead and [W]rite. When the first chunk is being read, the list will look like the following: [Head] <-> [1] <-> [2] <-> [Head] [W] [R] [W] is awaiting for the [1] to be filled in with the data before writing it to the disk. When the [1] chunk is filled with the data: [Head] <-> [1*] <-> [2] <-> [Head] [W] [R] [1] is full and is being written to a disk, and we're reading into chunk [2]. Let's also assume that the reads are faster then writes. When the [2] chunk is full, there are no free chunks available: [Head] <-> [1*] <-> [2*] <-> [Head] [W] [R] If the current amount of chunks is lesser than the 'MaxChunks' value, a new chunk will be allocated before the next [R] node. In this case it will be added before [Head] to the end of the list, and read will continue: [Head] <-> [1*] <-> [2*] <-> [3] <-> [Head] [W] [R] Then [W] and [R] finish, and [W] moves to the [2] chunk. [Head] <-> [1] <-> [2*] <-> [3*] <-> [Head] [W] [R] [R] sees that [1] chunk is available, and reads into it: [Head] <-> [1] <-> [2*] <-> [3*] <-> [Head] [R] [W] After [R] finishes reading, there are no free chunks again: [Head] <-> [1*] <-> [2*] <-> [3*] <-> [Head] [R] [W] A new chunk can be allocated again before the [2]: [Head] <-> [1*] <-> [4] <-> [2*] <-> [3*] <-> [Head] [R] [W] With this approach, [R] will fill chunks [1]->[2]->[3]->[1]->[4], and write will write them in the same order. I.e. allocating a new chunk before the next filled chunk (if the amount of chunks is lesser than the 'MaxChunks') makes sure that the data is written sequentially, and there is no need to constantly seek in the file. Arguments: FltObjects - Pointer to the 'FLT_RELATED_OBJECTS' data structure containing opaque handles to this filter, instance, its associated volume and file object. SourceFileHandle - Handle to the source file to copy content from. SourceFileSize - Size of the source file. BytesCopied - Pointer to the LARGE_INTEGER structure that receives the amount of bytes copied. Return Value: The return value is the status of the operation. --*/ { NTSTATUS status = STATUS_SUCCESS; LIST_ENTRY chunksListHead = { 0 }; ULONG chunkListLength = 0; // State of the R/W operations. BOOLEAN readComplete = FALSE; BOOLEAN writeComplete = FALSE; PFILE_CHUNK readChunk = NULL; PFILE_CHUNK writeChunk = NULL; BOOLEAN eof = FALSE; BOOLEAN waitingForRead = FALSE; KEVENT writeEvent = { 0 }; WRITE_CALLBACK_CONTEXT writeCallbackContext = { 0 }; LARGE_INTEGER waitTimeout = { 0 }; LARGE_INTEGER zeroTimeout = { 0 }; IO_STATUS_BLOCK statusBlock = { 0 }; LARGE_INTEGER remainingBytes = { 0 }; LARGE_INTEGER totalBytesRead = { 0 }; LARGE_INTEGER totalBytesWritten = { 0 }; LARGE_INTEGER sourceFileOffset = { 0 }; LARGE_INTEGER destinationFileOffset = { 0 }; PAGED_CODE(); FLT_ASSERT(FltObjects != NULL); FLT_ASSERT(SourceFileHandle != NULL); FLT_ASSERT(SourceFileSize != NULL); FLT_ASSERT(SourceFileSize->QuadPart > 0); FLT_ASSERT(BytesCopied != NULL); FLT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); *BytesCopied = RtlConvertLongToLargeInteger(0); __try { // Set the relative timeout (1 stands for 100 nanoseconds). waitTimeout = RtlConvertLongToLargeInteger(-10000); waitTimeout.QuadPart *= TimeoutMilliseconds; KeInitializeEvent(&writeEvent, NotificationEvent, TRUE); writeCallbackContext.Event = &writeEvent; remainingBytes.QuadPart = SourceFileSize->QuadPart; NT_IF_FAIL_LEAVE(LcInitializeChunksList(FltObjects->Instance, &chunksListHead, remainingBytes, &chunkListLength)); for (;;) { if (waitingForRead) { // Wait for the read operation to finish. NT_IF_FAIL_LEAVE(ZwWaitForSingleObject(SourceFileHandle, FALSE, &waitTimeout)); readComplete = TRUE; } else { readComplete = ZwWaitForSingleObject(SourceFileHandle, FALSE, &zeroTimeout) == STATUS_SUCCESS; } writeComplete = KeReadStateEvent(&writeEvent) != 0; if (!eof && readComplete) { // If it's not the first read, update status of the current chunk. if (readChunk != NULL) { status = statusBlock.Status; if (NT_SUCCESS(status) || status == STATUS_END_OF_FILE) { ULONG bytesRead = (ULONG)statusBlock.Information; readChunk->BytesInBuffer = bytesRead; remainingBytes.QuadPart -= bytesRead; totalBytesRead.QuadPart += bytesRead; sourceFileOffset.QuadPart += bytesRead; if (status == STATUS_END_OF_FILE || bytesRead < readChunk->BufferSize) { eof = TRUE; status = STATUS_SUCCESS; // Will not be used later in this case, only to have the proper data here. remainingBytes.QuadPart = 0; } } NT_IF_FAIL_LEAVE(status); } // Move to the next available chunk and schedule read. if (!eof) { // If the remote file system returned an invalid file size, when we started reading it, // this value might be negative. Set it to the default, so the newly allocated chunk // will have the maximum allowed size. if (remainingBytes.QuadPart <= 0) { remainingBytes.QuadPart = ChunkSize; } NT_IF_FAIL_LEAVE(LcGetNextAvailableChunk( FltObjects->Instance, &chunksListHead, &readChunk, &chunkListLength, TRUE, // Read operation. &remainingBytes, &writeEvent, &waitTimeout)); // Schedule read operation for the current chunk. status = ZwReadFile( SourceFileHandle, NULL, NULL, NULL, &statusBlock, readChunk->Buffer, readChunk->BufferSize, &sourceFileOffset, NULL); NT_IF_FALSE_LEAVE(status == STATUS_PENDING || status == STATUS_SUCCESS, status); } } if (writeComplete) { if (!waitingForRead) { // If it's not the first write, update status of the current chunk. if (writeChunk != NULL) { NT_IF_FAIL_LEAVE(writeCallbackContext.Status); writeChunk->BytesInBuffer = 0; totalBytesWritten.QuadPart += writeCallbackContext.BytesWritten; destinationFileOffset.QuadPart += writeCallbackContext.BytesWritten; } NT_IF_FAIL_LEAVE(LcGetNextAvailableChunk( FltObjects->Instance, &chunksListHead, &writeChunk, &chunkListLength, FALSE, // Write operation. NULL, NULL, NULL)); } waitingForRead = FALSE; // If we don't have any data in the current chunk, restart from the beginning of the loop. if (writeChunk->BytesInBuffer == 0) { if (eof) { // We're done! break; } else { // Since we're waiting for the read to complete for the current chunk, // don't change the chunk position on next iteration. waitingForRead = TRUE; continue; } } KeClearEvent(&writeEvent); NT_IF_FAIL_LEAVE(FltWriteFile( FltObjects->Instance, FltObjects->FileObject, &destinationFileOffset, writeChunk->BytesInBuffer, writeChunk->Buffer, FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET, NULL, (PFLT_COMPLETED_ASYNC_IO_CALLBACK)&LcWriteCallback, &writeCallbackContext)); } } *BytesCopied = totalBytesWritten; } __finally { LcClearChunksList(FltObjects->Instance, &chunksListHead); } return status; }
USHORT SpyAddRecordName( _Inout_ PLOG_RECORD LogRecord, _In_ PUNICODE_STRING Name, _In_ USHORT ByteOffset ) /*++ Routine Description: Sets the given file name in the LogRecord. NOTE: This code must be NON-PAGED because it can be called on the paging path. Arguments: LogRecord - The record in which to set the name. Name - The name to insert Return Value: None. --*/ { PWCHAR printPointer = (PWCHAR)LogRecord->Names; SHORT charsToSkip = ByteOffset / sizeof(WCHAR); SHORT wcharsCopied; USHORT stringLength; if (Name == NULL || charsToSkip >= MAX_NAME_WCHARS_LESS_NULL) { return 0; } #pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") wcharsCopied = (SHORT)_snwprintf(printPointer + charsToSkip, MAX_NAME_WCHARS_LESS_NULL - charsToSkip, L"%wZ", Name); if (wcharsCopied >= 0) { stringLength = ByteOffset + (wcharsCopied * sizeof(WCHAR)); } else { // // There wasn't enough buffer space, so manually truncate in a NULL // because we can't trust _snwprintf to do so in that case. // stringLength = MAX_NAME_SPACE_LESS_NULL; printPointer[MAX_NAME_WCHARS_LESS_NULL] = UNICODE_NULL; } // // We will always round up log-record length to sizeof(PVOID) so that // the next log record starts on the right PVOID boundary to prevent // IA64 alignment faults. The length of the record of course // includes the additional NULL at the end. // LogRecord->Length = ROUND_TO_SIZE((sizeof(LOG_RECORD) + stringLength + sizeof(UNICODE_NULL)), sizeof(PVOID)); FLT_ASSERT(LogRecord->Length <= MAX_LOG_RECORD_LENGTH); return stringLength + sizeof(UNICODE_NULL); }
NTSTATUS LcFetchRemoteFile ( _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ PUNICODE_STRING SourceFile, _In_ PUNICODE_STRING TargetFile, _In_ BOOLEAN UseCustomHandler, _Out_ PLARGE_INTEGER BytesCopied ) /*++ Summary: This function copies the remote file content to the current file object. In order for the remote file to be fetched, make sure that the network redirector device is used, i.e. the 'SourceFile' root points to the '\Device\Mup\<path>'. Arguments: FltObjects - Pointer to the 'FLT_RELATED_OBJECTS' data structure containing opaque handles to this filter, instance, its associated volume and file object. SourceFile - Path to the file to fetch content from. TargetFile - Path to the file to store content to. UseCustomHandler - Whether the file should be fetched by the user-mode client. BytesCopied - The amount of bytes copied. Return Value: The return value is the status of the operation. --*/ { NTSTATUS status = STATUS_SUCCESS; HANDLE sourceFileHandle = NULL; IO_STATUS_BLOCK statusBlock = { 0 }; FILE_STANDARD_INFORMATION standardInfo = { 0 }; FILE_END_OF_FILE_INFORMATION eofInfo = { 0 }; PAGED_CODE(); IF_FALSE_RETURN_RESULT(FltObjects != NULL, STATUS_INVALID_PARAMETER_1); IF_FALSE_RETURN_RESULT(SourceFile != NULL, STATUS_INVALID_PARAMETER_2); IF_FALSE_RETURN_RESULT(TargetFile != NULL, STATUS_INVALID_PARAMETER_3); IF_FALSE_RETURN_RESULT(BytesCopied != NULL, STATUS_INVALID_PARAMETER_5); FLT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); *BytesCopied = RtlConvertLongToLargeInteger(0); __try { LOG((DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, "[LazyCopy] Fetching content from: '%wZ' -> '%wZ'\n", SourceFile, TargetFile)); if (UseCustomHandler) { NT_IF_FAIL_LEAVE(LcFetchFileInUserMode(SourceFile, TargetFile, BytesCopied)); } else { // // Open the source file and make sure it's not empty. // NT_IF_FAIL_LEAVE(LcOpenFile(SourceFile, TargetFile, &sourceFileHandle)); NT_IF_FAIL_LEAVE(ZwQueryInformationFile(sourceFileHandle, &statusBlock, &standardInfo, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation)); if (standardInfo.EndOfFile.QuadPart == 0) { // No need to copy an empty file. __leave; } // Extend the target file, so all readers that wait for the content to be copied will get the actual file size information. // Remote file system may return incorrect information, but we are doing it only for the cases, when multiple threads // try to access the same file, while we are fetching it. eofInfo.EndOfFile.QuadPart = standardInfo.EndOfFile.QuadPart; NT_IF_FAIL_LEAVE(FltSetInformationFile(FltObjects->Instance, FltObjects->FileObject, &eofInfo, sizeof(eofInfo), FileEndOfFileInformation)); // // Copy source file contents into the local (target) file. // NT_IF_FAIL_LEAVE(LcFetchFileByChunks( FltObjects, sourceFileHandle, &standardInfo.EndOfFile, BytesCopied)); } } __finally { if (sourceFileHandle != NULL) { ZwClose(sourceFileHandle); } } return status; }
static _Check_return_ NTSTATUS LcOpenFile ( _In_ PUNICODE_STRING SourceFile, _In_ PUNICODE_STRING TargetFile, _Out_ PHANDLE Handle ) /*++ Summary: This function tries to open the 'FilePath' given. If open operation fails in the current context, it asks for the user-mode service to open that file. Arguments: SourceFile - Path to the file to open. TargetFile - Path to the file the content should be stored to. Handle - Receives handle to the opened file. Return Value: The return value is the status of the operation. --*/ { NTSTATUS status = STATUS_SUCCESS; IO_STATUS_BLOCK statusBlock = { 0 }; OBJECT_ATTRIBUTES objectAttributes = { 0 }; HANDLE fileHandle = NULL; PAGED_CODE(); FLT_ASSERT(SourceFile != NULL); FLT_ASSERT(TargetFile != NULL); FLT_ASSERT(Handle != NULL); EventWriteFile_Open_Start(NULL, SourceFile->Buffer); __try { // The current minifilter instance may not be attached to the target volume, // so the ZwOpenFile/ZwReadFile functions should be used here instead of the FltCreateFile/FltReadFile. InitializeObjectAttributes(&objectAttributes, SourceFile, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); // Open file for asynchronous reads. status = ZwOpenFile( &fileHandle, GENERIC_READ, &objectAttributes, &statusBlock, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE | FILE_SEQUENTIAL_ONLY); // Open operation may fail, if a remote file is opened by a system, which does not have access to the remote share. // If this happens, we want the user-mode client to open it for us. if (!NT_SUCCESS(status) && status == STATUS_ACCESS_DENIED) { NTSTATUS notificationStatus = STATUS_SUCCESS; LOG((DPFLTR_IHVDRIVER_ID, DPFLTR_WARNING_LEVEL, "[LazyCopy] '%wZ' cannot be accessed by the system, trying to use user-mode service instead.\n", FilePath)); notificationStatus = LcOpenFileInUserMode(SourceFile, TargetFile, &fileHandle); // Return original status, if error occurred while sending notification to the user-mode client. status = notificationStatus == STATUS_PORT_DISCONNECTED || notificationStatus == STATUS_TIMEOUT ? status : notificationStatus; } } __finally { if (NT_SUCCESS(status)) { *Handle = fileHandle; // Make sure it's not closed by the code below. fileHandle = NULL; } if (fileHandle != NULL) { ZwClose(fileHandle); } } EventWriteFile_Open_Stop(NULL); return status; }
FLT_PREOP_CALLBACK_STATUS NcEnumerateDirectory ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ) /*++ Routine Description: Routine is invoked when a directory enumeration is issued by the user. Arguments: Data - Pointer to the filter CallbackData that is passed to us. FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing opaque handles to this filter, instance, its associated volume and file object. CompletionContext - The context for the completion routine for this operation. Return Value: The return value is the Status of the operation. --*/ { //TODO WE SHOULD CONSIDER MOVING THIS TO POST BECAUSE NTFS WILL TAKE CARE // OF SYNC. FLT_PREOP_CALLBACK_STATUS ReturnValue; NTSTATUS Status; PNC_INSTANCE_CONTEXT InstanceContext = NULL; PNC_STREAM_HANDLE_CONTEXT HandleContext = NULL; PNC_DIR_QRY_CONTEXT DirCtx = NULL; PFLT_FILE_NAME_INFORMATION FileNameInformation = NULL; NC_PATH_OVERLAP RealOverlap; NC_PATH_OVERLAP UserOverlap; BOOLEAN Reset = BooleanFlagOn( Data->Iopb->OperationFlags, SL_RESTART_SCAN ); BOOLEAN FirstQuery; BOOLEAN Single = BooleanFlagOn( Data->Iopb->OperationFlags, SL_RETURN_SINGLE_ENTRY ); BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, FO_OPENED_CASE_SENSITIVE ); FILE_INFORMATION_CLASS InformationClass = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass; PVOID UserBuffer; ULONG BufferSize; //size for user and system buffers. BOOLEAN Unlock = FALSE; //Vars for moving data into user buffer. ULONG NumEntriesCopied; ULONG UserBufferOffset; ULONG LastEntryStart; BOOLEAN MoreRoom; PNC_CACHE_ENTRY NextEntry; DIRECTORY_CONTROL_OFFSETS Offsets; BOOLEAN FoundStructureOffsets; UNREFERENCED_PARAMETER( CompletionContext ); PAGED_CODE(); FLT_ASSERT( IoGetTopLevelIrp() == NULL ); FoundStructureOffsets = NcDetermineStructureOffsets( &Offsets, InformationClass ); if (!FoundStructureOffsets) { Status = STATUS_INVALID_PARAMETER; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // Get our instance context. // Status = FltGetInstanceContext( FltObjects->Instance, &InstanceContext ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // Get the directory's name. // Status = NcGetFileNameInformation( Data, NULL, NULL, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, &FileNameInformation ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } Status = FltParseFileNameInformation( FileNameInformation ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // See if the directory is parent of either mapping. // NcComparePath( &FileNameInformation->Name, &InstanceContext->Mapping.UserMapping, NULL, IgnoreCase, TRUE, &UserOverlap ); NcComparePath( &FileNameInformation->Name, &InstanceContext->Mapping.RealMapping, NULL, IgnoreCase, TRUE, &RealOverlap ); if (!(UserOverlap.Parent || RealOverlap.Parent )) { // // We are not interested in this directory // because it is not the parent of either // mapping. This means we can just passthrough. // Status = STATUS_SUCCESS; ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; goto NcEnumerateDirectoryCleanup; } Status = NcStreamHandleContextAllocAndAttach( FltObjects->Filter, FltObjects->Instance, FltObjects->FileObject, &HandleContext ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } FLT_ASSERT( HandleContext != NULL ); DirCtx = &HandleContext->DirectoryQueryContext; _Analysis_assume_( DirCtx != NULL ); // // Before looking at the context, we have to acquire the lock. // NcLockStreamHandleContext( HandleContext ); Unlock = TRUE; // // We don't allow multiple outstanding enumeration requests on // a single handle. // // TODO: This needs to change. // if (DirCtx->EnumerationOutstanding) { Status = STATUS_UNSUCCESSFUL; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } DirCtx->EnumerationOutstanding = TRUE; // // Now drop the lock. We're protected by the EnumerationOutstanding // flag; nobody else can muck with the enumeration context structure. // NcUnlockStreamHandleContext( HandleContext ); Unlock = FALSE; // // Now we need to initialize or clear the cache and query options. // Status = NcStreamHandleContextEnumSetup( DirCtx, InstanceContext, &Offsets, Data, FltObjects, UserOverlap, &FirstQuery ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // Prepare to populate the user buffer. // UserBuffer = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer; BufferSize = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length; // // Lets copy data into the user buffer. // NumEntriesCopied = 0; UserBufferOffset = 0; do { // // If there is no cache entry, populate it. // if (DirCtx->Cache.Buffer == NULL) { Status = NcPopulateCacheEntry( FltObjects->Instance, FltObjects->FileObject, Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length, Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass, Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileName, Reset, &DirCtx->Cache); // // We only want to reset the cache once. // Reset = FALSE; // // There was a problem populating cache, pass up to user. // if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } } NextEntry = NcDirEnumSelectNextEntry( DirCtx, &Offsets, IgnoreCase ); if (NextEntry == NULL) { // // There are no more entries. // break; } if (NcSkipName( &Offsets, DirCtx, RealOverlap, &InstanceContext->Mapping, IgnoreCase )) { // // This entry is the real mapping path. That means we have to mask it... // We will say there is more room and continue. // MoreRoom = TRUE; } else { // // We are keeping this entry! // try { LastEntryStart = UserBufferOffset; UserBufferOffset = NcCopyDirEnumEntry( UserBuffer, UserBufferOffset, BufferSize, NextEntry, &Offsets, &MoreRoom ); } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { Status = STATUS_INVALID_USER_BUFFER; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } if (MoreRoom) { NumEntriesCopied++; } }// end of "we are copying entry" } while (MoreRoom && (Single ? (NumEntriesCopied < 1) : TRUE)); if (NumEntriesCopied > 0) { // // Now we know what the last entry in the user buffer is going to be. // Set its NextEntryOffset to 0, so that the user knows its the last element. // try { NcSetNextEntryOffset( Add2Ptr(UserBuffer, LastEntryStart), &Offsets, TRUE ); } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { Status = STATUS_INVALID_USER_BUFFER; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } } // // We finished copying data. // ReturnValue = FLT_PREOP_COMPLETE; if (NumEntriesCopied == 0) { if (FirstQuery) { Status = STATUS_NO_SUCH_FILE; } else { Status = STATUS_NO_MORE_FILES; } } else { Status = STATUS_SUCCESS; } ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; NcEnumerateDirectoryCleanup: if (ReturnValue == FLT_PREOP_COMPLETE) { // // We need to write back results of query. // Data->IoStatus.Status = Status; if (NT_SUCCESS( Status )) { //success Data->IoStatus.Information = UserBufferOffset; } else { //failure Data->IoStatus.Information = 0; } } if (InstanceContext != NULL) { FltReleaseContext( InstanceContext ); } if (DirCtx != NULL) { if (!Unlock) { NcLockStreamHandleContext( HandleContext ); Unlock = TRUE; } FLT_ASSERT( DirCtx->EnumerationOutstanding ); DirCtx->EnumerationOutstanding = FALSE; NcUnlockStreamHandleContext( HandleContext ); Unlock = FALSE; FltReleaseContext( HandleContext ); } FLT_ASSERT( !Unlock ); if (FileNameInformation != NULL) { FltReleaseFileNameInformation( FileNameInformation ); } return ReturnValue; }
bool AnimationTrack::loadFromXML(TiXmlElement *xmlNodeTrack) { xmlNodeTrack->QueryStringAttribute("name",&m_name); stringc dataType; xmlNodeTrack->QueryStringAttribute("dataType",&dataType); m_dataType = stringToDataType(dataType); TiXmlHandle hXmlNode(xmlNodeTrack); //load keyframes data TiXmlElement* xmlNodeTimes = hXmlNode.FirstChild( "times" ).Element(); if(xmlNodeTimes!=0) { int kfNum = 0; //times const char* timesStr = xmlNodeTimes->GetText(); if(timesStr!=0) { float *times=0; kfNum = StringUtil::splitOutFloats(times, timesStr); for(int i=0; i<kfNum; ++i) { KeyFrame* kf = createKeyFrame(); if(kf!=0) { kf->setTime(times[i]); m_keyFrames.push_back(kf); } } delete times; } //at least has 1 kf if(kfNum>0) { //KF datas float *kfDatas=0; TiXmlElement* xmlNodeKFs = hXmlNode.FirstChild( "keyframes" ).Element(); if(xmlNodeKFs!=0) { int valueNum = StringUtil::splitOutFloats(kfDatas, xmlNodeKFs->GetText()); u32 dataElemNum = m_keyFrames[0]->getDataElementNum(); FLT_ASSERT(dataElemNum>0 && valueNum/dataElemNum==kfNum); for(int i=0; i<kfNum; ++i) { m_keyFrames[i]->loadValue(kfDatas, i); } delete kfDatas; } //interp datas TiXmlElement* xmlNodeInterps = hXmlNode.FirstChild( "interps" ).Element(); if(xmlNodeInterps!=0) { array_t<stringc> strs; strs = StringUtil::split(xmlNodeInterps->GetText(), ",", kfNum); FLT_ASSERT(strs.size()==kfNum); for(int i=0; i<kfNum; ++i) { m_keyFrames[i]->setInterpTypeByString(strs[i]); } } } //create a computed frame, for interpolation m_computedFrame = createKeyFrame(); } return true; }
NTSTATUS FmmInstanceSetup ( _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ FLT_INSTANCE_SETUP_FLAGS Flags, _In_ DEVICE_TYPE VolumeDeviceType, _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType ) /*++ Routine Description: This routine is called whenever a new instance is created on a volume. This gives us a chance to decide if we need to attach to this volume or not. Arguments: FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing opaque handles to this filter, instance and its associated volume. Flags - Flags describing the reason for this attach request. Return Value: STATUS_SUCCESS - attach STATUS_FLT_DO_NOT_ATTACH - do not attach --*/ { PFMM_INSTANCE_CONTEXT instanceContext = NULL; PDEVICE_OBJECT diskDeviceObject; NTSTATUS status = STATUS_SUCCESS; UNREFERENCED_PARAMETER( VolumeDeviceType ); PAGED_CODE(); DebugTrace( DEBUG_TRACE_INSTANCES, ("[Fmm]: Instance setup started (Volume = %p, Instance = %p)\n", FltObjects->Volume, FltObjects->Instance) ); // // Check if the file system mounted is ntfs or fat // // The sample picks NTFS, FAT and ReFS as examples. The metadata // handling demostrated in the sample can be applied // to any file system // if (VolumeFilesystemType != FLT_FSTYPE_NTFS && VolumeFilesystemType != FLT_FSTYPE_FAT && VolumeFilesystemType != FLT_FSTYPE_REFS) { // // An unknown file system is mounted which we do not care // DebugTrace( DEBUG_TRACE_INSTANCES, ("[Fmm]: Unsupported file system mounted (Volume = %p, Instance = %p)\n", FltObjects->Volume, FltObjects->Instance) ); status = STATUS_NOT_SUPPORTED; goto FmmInstanceSetupCleanup; } // // Get the disk device object and make sure it is a disk device type and does not // have any of the device characteristics we do not support. // // The sample picks the device characteristics to demonstrate how to access and // check the device characteristics in order to make a decision to attach. The // metadata handling demostrated in the sample is not limited to the // characteristics we have used in the sample. // status = FltGetDiskDeviceObject( FltObjects->Volume, &diskDeviceObject ); if (!NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_INSTANCES | DEBUG_TRACE_ERROR, ("[Fmm]: Failed to get device object (Volume = %p, Status = 0x%08X)\n", FltObjects->Volume, status) ); goto FmmInstanceSetupCleanup; } if (diskDeviceObject->DeviceType != FILE_DEVICE_DISK || FlagOn( diskDeviceObject->Characteristics, FMM_UNSUPPORTED_DEVICE_CHARACS )) { DebugTrace( DEBUG_TRACE_INSTANCES, ("[Fmm]: Unsupported device type or device characteristics (Volume = %p, Instance = %p DiskDeviceObjectDeviceTYpe = 0x%x, DiskDeviceObjectCharacteristics = 0x%x)\n", FltObjects->Volume, FltObjects->Instance, diskDeviceObject->DeviceType, diskDeviceObject->Characteristics) ); ObDereferenceObject( diskDeviceObject ); status = STATUS_NOT_SUPPORTED; goto FmmInstanceSetupCleanup; } ObDereferenceObject( diskDeviceObject ); // // Allocate and initialize the context for this volume // status = FltAllocateContext( FltObjects->Filter, FLT_INSTANCE_CONTEXT, FMM_INSTANCE_CONTEXT_SIZE, NonPagedPool, &instanceContext ); if( !NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_INSTANCES | DEBUG_TRACE_ERROR, ("[Fmm]: Failed to allocate instance context (Volume = %p, Instance = %p, Status = 0x%08X)\n", FltObjects->Volume, FltObjects->Instance, status) ); goto FmmInstanceSetupCleanup; } FLT_ASSERT( instanceContext != NULL ); RtlZeroMemory( instanceContext, FMM_INSTANCE_CONTEXT_SIZE ); instanceContext->Flags = 0; instanceContext->Instance = FltObjects->Instance; instanceContext->FilesystemType = VolumeFilesystemType; instanceContext->Volume = FltObjects->Volume; ExInitializeResourceLite( &instanceContext->MetadataResource ); // // Set the instance context. // status = FltSetInstanceContext( FltObjects->Instance, FLT_SET_CONTEXT_KEEP_IF_EXISTS, instanceContext, NULL ); if( !NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_INSTANCES | DEBUG_TRACE_ERROR, ("[Fmm]: Failed to set instance context (Volume = %p, Instance = %p, Status = 0x%08X)\n", FltObjects->Volume, FltObjects->Instance, status) ); goto FmmInstanceSetupCleanup; } // // Acquire exclusive access to the instance context // FmmAcquireResourceExclusive( &instanceContext->MetadataResource ); // // Sanity - the instance context cannot be in a transition state during instance setup // FLT_ASSERT( !FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION) ); // // Open the filter metadata on disk // // The sample will attach to volume if it finds its metadata file on the volume. // If this is a manual attachment then the sample filter will create its metadata // file and attach to the volume. // status = FmmOpenMetadata( instanceContext, BooleanFlagOn( Flags, FLTFL_INSTANCE_SETUP_MANUAL_ATTACHMENT ) ); // // Relinquish exclusive access to the instance context // FmmReleaseResource( &instanceContext->MetadataResource ); if (!NT_SUCCESS( status )) { goto FmmInstanceSetupCleanup; } FmmInstanceSetupCleanup: // // If FltAllocateContext suceeded then we MUST release the context, // irrespective of whether FltSetInstanceContext suceeded or not. // // FltAllocateContext increments the ref count by one. // A successful FltSetInstanceContext increments the ref count by one // and also associates the context with the file system object // // FltReleaseContext decrements the ref count by one. // // When FltSetInstanceContext succeeds, calling FltReleaseContext will // leave the context with a ref count of 1 corresponding to the internal // reference to the context from the file system structures // // When FltSetInstanceContext fails, calling FltReleaseContext will // leave the context with a ref count of 0 which is correct since // there is no reference to the context from the file system structures // if ( instanceContext != NULL ) { FltReleaseContext( instanceContext ); } if (NT_SUCCESS( status )) { DebugTrace( DEBUG_TRACE_INSTANCES, ("[Fmm]: Instance setup complete (Volume = %p, Instance = %p). Filter will attach to the volume.\n", FltObjects->Volume, FltObjects->Instance) ); } else { DebugTrace( DEBUG_TRACE_INSTANCES, ("[Fmm]: Instance setup complete (Volume = %p, Instance = %p). Filter will not attach to the volume.\n", FltObjects->Volume, FltObjects->Instance) ); } // // If this is an automatic attachment (mount, load, etc) and we are not // attaching to this volume because we do not support attaching to this // volume, then simply return STATUS_FLT_DO_NOT_ATTACH. If we return // anything else fltmgr logs an event log indicating failure to attach. // Since this failure to attach is not really an error, we do not want // this failure to be logged as an error in the event log. For all other // error codes besides the ones we consider "normal", if is ok for fltmgr // to actually log the failure to attach. // // If this is a manual attach attempt that we have failed then we want to // give the user a clear indication of why the attachment failed. Hence in // this case, we will not override the error status with STATUS_FLT_DO_NOT_ATTACH // irrespective of the cause of the failure to attach // if (status == STATUS_NOT_SUPPORTED && !FlagOn( Flags, FLTFL_INSTANCE_SETUP_MANUAL_ATTACHMENT )) { status = STATUS_FLT_DO_NOT_ATTACH; } return status; }