次の方法で共有


ディスパッチ ルーチンに関する制約

Note

最適な信頼性とパフォーマンスを実現するには、レガシ ファイル システム フィルター ドライバーではなく、フィルター マネージャーのサポートがあるファイル システム ミニフィルター ドライバーを使用します。 レガシ ドライバーをミニフィルター ドライバーに移植するには、「レガシ フィルター ドライバーの移植ガイドライン」を参照してください。

次のガイドラインでは、レガシ ファイル システム フィルター ドライバーのディスパッチ ルーチンでの一般的なプログラミング エラーを回避する方法について簡単に説明します。

ページング I/O で使われる IRP の種類については、「ディスパッチ ルーチン IRQL とスレッド コンテキスト」を参照してください。

  • ページング I/O パス内のディスパッチ ルーチンでは、APC_LEVEL より高い IRQL で IoCallDriver を呼び出さないようにする必要があります。 DISPATCH_LEVEL でページング I/O (または任意の I/O) を実行することはできません。システムは APC を使って I/O の完了を処理するので、操作が完了したことを確認できないためです。 ディスパッチ ルーチンで IRQL を上げる場合は、IoCallDriver を呼び出す前にそれを下げる必要があります。

  • どのような状況でも、ディスパッチ ルーチンが APC_LEVEL で IoCallDriver を呼び出すのは安全であるとは限りません。 APC_LEVEL でもかまわないか、それとも PASSIVE_LEVEL にする必要があるかを確認するには、「ディスパッチ ルーチン IRQL とスレッド コンテキスト」を参照してください。

  • 読み取りや書き込みなど、ページング I/O パス内のディスパッチ ルーチンは、呼び出し元が IRQL PASSIVE_LEVEL で実行している必要があるカーネル モード ルーチンを、安全に呼び出すことはできません。

  • ページング ファイルの I/O パス内のディスパッチ ルーチンは、呼び出し元が IRQL < DISPATCH_LEVEL で実行している必要があるカーネル モード ルーチンを、安全に呼び出すことはできません。

  • ページング I/O パス内にないディスパッチ ルーチンは、PASSIVE_LEVEL より高いすべての IRQL で、IoCallDriver を呼び出さないようにする必要があります。 ディスパッチ ルーチンで IRQL を上げる場合は、IoCallDriver を呼び出す前にそれを下げる必要があります。

IRP の処理に関する制約

  • IRP パラメーターにユーザー空間アドレスが含まれている場合、これらのアドレスを使う前に検証する必要があります。 詳しくは、バッファー I/O でのエラーに関する記事をご覧ください。

  • さらに、32 ビット プラットフォームから 64 ビット プラットフォームに送信された IOCTL または FSCTL バッファーが IRP に含まれている場合は、バッファーの内容のサンクが必要になる場合があります。 詳細については、「64 ビット ドライバーでの 32 ビット I/O のサポート」を参照してください。

  • ファイル システムとは異なり、ファイル システム フィルター ドライバーでは、ExAcquireFastMutexUnsafe または ExAcquireResourceExclusiveLite を呼び出す前を除き、FsRtlEnterFileSystem または FsRtlExitFileSystem を呼び出さないでください。 FsRtlEnterFileSystemFsRtlExitFileSystem を呼び出すと、ほとんどのファイル システムで必要な通常のカーネル API が無効になります。

  • ページング I/O パスから他の IRP を発行することはできません。 I/O を発行するワーカー スレッドをキューに入れることはできますが、そのワーカー スレッドの完了を同期的に待機しないでください。待機するとデッドロックが発生します。

IRP の完了に関する制約

  • ファイル システム フィルター ドライバーでは、IRP を完了するときは、成功とエラーの状態値のみを使う必要があります。

  • STATUS_PENDING は成功の NTSTATUS 値ですが、STATUS_PENDINGで IRP を完了するとプログラミング エラーになります。

  • ディスパッチ ルーチンが IoCompleteRequest を呼び出した後では、IRP ポインターは無効になり、安全に逆参照することはできません。

完了ルーチンの設定に関する制約

完了ルーチンの設定については、完了ルーチンの使用に関する記事をご覧ください。

  • ディスパッチ ルーチンでは、IoSetCompletionRoutine を呼び出すときに、必要に応じて、完了ルーチンで特定の IRP を処理するときに使う構造体への Context ポインターを渡すことができます。 完了ルーチンは IRQL DISPATCH_LEVEL で呼び出すことができるため、この構造体は非ページ プールから割り当てる必要があります。

  • ディスパッチ ルーチンでは、STATUS_MORE_PROCESSING_REQUIRED を返す可能性がある完了ルーチンを設定する場合は、I/O マネージャーが IRP を途中で完了しないように、次のいずれかのことを行う必要があります。

    • IRP を保留中としてマークし、IoCallDriver を呼び出して、STATUS_PENDING を返します。
    • KeWaitForSingleObject を呼び出して完了ルーチンが実行されるのを待ってから、IoCompleteRequest を呼び出して IRP を完了します。

IRP を下方に渡すときの制約

  • ディスパッチ ルーチンで IoCallDriver を呼び出した後、IRP ポインターは無効になるので、呼び出されたことを完了ルーチンが通知するのをディスパッチ ルーチンで待ってからでないと、安全に逆参照することはできません。

  • ファイル システム フィルター ドライバーから PoCallDriver を呼び出すとプログラミング エラーになります。 (PoCallDriver は、下位レベルのドライバーに IRP_MJ_POWER 要求を渡すために使われます。ファイル システム フィルター ドライバーが IRP_MJ_POWER 要求を受け取ることはありません。)

状態を返す場合の制約

  • IRP を完了する場合を除き、完了ルーチンを設定しないディスパッチ ルーチンは常に、IoCallDriver によって返される NTSTATUS 値を返す必要があります。 この値が STATUS_PENDING でない限り、IRP を完了したドライバーによって設定された Irp->IoStatus.Status の値と一致する必要があります。

  • IoCallDriver から STATUS_PENDING が返された場合、ディスパッチ ルーチンでは、完了ルーチンからのイベントの通知を待つのでない限り、やはり STATUS_PENDING を返す必要があります。

  • ディスパッチ ルーチンでは、後で処理するためにワーカー キューに IRP をポストするときは、IRP を保留中としてマークし、STATUS_PENDING を返す必要があります。

  • ディスパッチ ルーチンでは、後で処理するために IRP をワーカー キューにポストする可能性がある完了ルーチンを設定するときは、IRP を保留中としてマークし、STATUS_PENDING を返す必要があります。

  • IRP を保留中としてマークするディスパッチ ルーチンは、STATUS_PENDING を返す必要があります。

  • 便宜的ロック操作を保留する (ワーカー キューにポストする) ことはできず、ディスパッチ ルーチンでそれらに対して STATUS_PENDING を返すことはできません。

作業キューへの IRP のポストに関する制約

  • ディスパッチ ルーチンでは、IRP を作業キューにポストする場合は、各 IRP をワーカー キューにポストする前に IoMarkIrpPending を呼び出す必要があります。 そうしないと、IRP がデキューされ、別のドライバー ルーチンによって完了されて、IoMarkIrpPending の呼び出しが発生する前にシステムによって解放されることで、クラッシュが発生する可能性があります。