2016-03-28 32 views
0

我正在編寫一個Windows Minifilter Driver,它需要在IRP_MJ_CLEANUP上讀取整個文件(只有大小達到特定閾值的文件)。由於FltReadFile可能不會從preop回調中調用,因此我將作業排入工作隊列並在那裏執行。當我讀完文件時,我調用FltCompletePendedPreOperation並調用post-cleanup回調函數,該函數也將post操作作爲延期工作來處理。下面是我的代碼片段:在延遲工作項目中的預清理階段讀取文件

static NTSTATUS HandlePreCleanup(_In_ PFLT_CALLBACK_DATA Data, 
           _Out_ PVOID *Context) 
{ 
    NTSTATUS Status = STATUS_SUCCESS; 
    PFLT_INSTANCE Instance; 
    PFILE_OBJECT FileObject; 
    PVOID Buffer = NULL; 
    LARGE_INTEGER FileOffset; 

    FileObject = Data->Iopb->TargetFileObject; 
    Instance = Data->Iopb->TargetInstance; 

    Buffer = ExAllocatePoolWithTag(PagedPool, 
            (ULONG) FILE_CHUNK_SIZE, 
            PPFILTER_FILE_POOLTAG); 
    if (Buffer == NULL) { 
     PPERROR("Failed allocating file chunk\n"); 
     Status = STATUS_MEMORY_NOT_ALLOCATED; 
     goto out; 
    } 

    FileOffset.QuadPart = 0; 
    for (;;) { 
     ULONG BytesRead; 

     Status = FltReadFile(
      Instance, FileObject, &FileOffset, 
      (ULONG) FILE_CHUNK_SIZE, Buffer, 
      FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET, 
      &BytesRead, NULL, NULL 
     ); 
     if (!NT_SUCCESS(Status)) { 
      if (Status == STATUS_END_OF_FILE) { 
       Status = STATUS_SUCCESS; 
       break; 
      } 

      PPERROR("Failed reading from file %wZ: error %d\n", 
        &FileObject->FileName, Status); 
      goto out; 
     } 

     FileOffset.QuadPart += BytesRead; 
    } 

out: 
    if (Buffer != NULL) { 
     ExFreePoolWithTag(Buffer, PPFILTER_FILE_POOLTAG); 
    } 

    return Status; 
} 

static VOID DeferredPreCallback(_In_ PFLT_DEFERRED_IO_WORKITEM WorkItem, 
           _In_ PFLT_CALLBACK_DATA Data, 
           _In_opt_ PVOID Context) 
{ 
    NTSTATUS Status = STATUS_SUCCESS; 
    PVOID PostContext = NULL; 

    UNREFERENCED_PARAMETER(Context); 

    switch (Data->Iopb->MajorFunction) { 
    case IRP_MJ_CLEANUP: 
     Status = HandlePreCleanup(Data, &PostContext); 
     break; 
    default: 
     NT_ASSERTMSG("Unexpected deferred pre callback operation", 
        FALSE); 
     break; 
    } 

    FltCompletePendedPreOperation(Data, 
            FLT_PREOP_SUCCESS_WITH_CALLBACK, 
            PostContext); 
    FltFreeDeferredIoWorkItem(WorkItem); 
} 


static NTSTATUS QueueWork(_Inout_ PFLT_CALLBACK_DATA Data, 
          _In_ PFLT_DEFERRED_IO_WORKITEM_ROUTINE WorkRoutine, 
          _In_ PVOID Context) 
{ 
    NTSTATUS Status = STATUS_SUCCESS; 
    PFLT_DEFERRED_IO_WORKITEM WorkItem = NULL; 

    WorkItem = FltAllocateDeferredIoWorkItem(); 
    if (WorkItem == NULL) { 
     Status = STATUS_MEMORY_NOT_ALLOCATED; 
     PPERROR("Failed allocating work item\n"); 
     goto failed; 
    } 

    Status = FltQueueDeferredIoWorkItem(WorkItem, Data, WorkRoutine, 
             CriticalWorkQueue, Context); 
    if (!NT_SUCCESS(Status)) { 
     PPERROR("Failed queuing work item to queue: error %d\n", 
       Status); 
     goto failed; 
    } 

    return STATUS_SUCCESS; 

failed: 
    if (WorkItem != NULL) { 
     FltFreeDeferredIoWorkItem(WorkItem); 
    } 

    return Status; 
} 

static FLT_PREOP_CALLBACK_STATUS DeferPreCallback(
    _Inout_ PFLT_CALLBACK_DATA Data, 
    _In_ PCFLT_RELATED_OBJECTS FltObjects, 
    _Out_ PVOID *CompletionContext 
) 
{ 
    NTSTATUS Status = STATUS_SUCCESS; 

    UNREFERENCED_PARAMETER(FltObjects); 
    UNREFERENCED_PARAMETER(CompletionContext); 

    Status = QueueWork(Data, DeferredPreCallback, NULL); 
    if (!NT_SUCCESS(Status)) { 
     return FLT_PREOP_SUCCESS_NO_CALLBACK; 
    } 

    return FLT_PREOP_PENDING; 
} 

CONST FLT_OPERATION_REGISTRATION OperationRegistrations[] = { 
    { 
     IRP_MJ_CLEANUP, 
     0, 
     DeferPreCallback, 
     DeferPostCallback, 
     NULL 
    }, 
    { IRP_MJ_OPERATION_END }, 
}; 

這證明了一會兒工作,但系統似乎一段時間後掛(僵局?)。這個問題似乎與調用FltReadFile,因爲掛起不會發生在刪除此調用。任何想法,爲什麼會發生這種情況或如何進一步調試?

回答

0

好吧,顯然問題是FltReadFile被賦予了與卷扇區大小不一致的FileOffset。如果FileObject是爲非緩存IO創建的(請參閱https://msdn.microsoft.com/en-us/library/windows/hardware/ff544286(v=vs.85).aspx中的備註部分),這顯然是一個問題。由於我無法控制如何創建FileObject,因此可能會出現這種情況,它確實是爲非緩存IO創建的。爲了解決這個問題,我添加了以下檢查時的年底(;;)循環:

if (BytesRead < FILE_CHUNK_SIZE) { 
     break; 
} 

這應該工作,如果FILE_CHUNK_SIZE是卷扇區大小的倍數。