0

I writing a Windows Minifilter Driver which needs to read the entire file (only files with size up to a specific threshold) on IRP_MJ_CLEANUP. As FltReadFile may not be called from the preop callback I queued the job to a work queue and did it there. When I finish reading the file I call FltCompletePendedPreOperation and invoke the post-cleanup callback which also handles the post operation as deferred work. Here are snippets of my code:

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 },
};

This proves to work for a while but the system seems to hang (deadlock?) after a while. The problem seems to be with the call to FltReadFile since the hang does not occur when removing this call. Any ideas on why this might happen or how to debug it further?

4

1 回答 1

0

好吧,显然问题在于 FltReadFile 被赋予了一个与卷扇区大小不一致的 FileOffset。如果为非缓存 IO 创建 FileObject,这显然是一个问题(请参阅https://msdn.microsoft.com/en-us/library/windows/hardware/ff544286(v=vs.85).aspx中的备注部分)。由于我无法控制相关 FileObject 的创建方式,因此在某些情况下它确实是为非缓存 IO 创建的。为了解决这个问题,我在 for(;;) 循环的末尾添加了以下检查:

if (BytesRead < FILE_CHUNK_SIZE) {
        break;
}

如果 FILE_CHUNK_SIZE 是卷扇区大小的倍数,这应该可以工作。

于 2016-04-01T12:00:48.360 回答