IRP(I/O Request Packet)はアプリケーションからI/O要求が発生した際に、I/Oマネージャが作成する構造体です。IRPは適当なドライバのディスパッチルーチンを呼び出す時に、引数として渡されます。IRP構造体は図1のようにヘッダとスタックロケーションの二つの部分が存在します。スタックロケーションの数は、I/O要求に関わるドライバの階層と関係しています。例えば、上位、中間、物理の3つのドライバを介して一つのI/O要求を処理する場合、3つのスタックロケーションが存在します。
IRPはオブジェクトではなく、データのパケットで、その構造体は非常に複雑な形をしています。IRPは非ページプールメモリに作成されます。IRPのヘッダの値、大きさはドライバの階層に関係なく固定しています。次にIRPの簡略した構造体と、よく使うフィールドについて示します。
typedef struct _IRP{
PMDL MdlAddress;
ULONG Flags;
union{
struct _IRP *MasterIrp;
PVOID SystemBuffer;
}AssocitatedIrp;
IO_STATUS_BLOCK IoStatus;
KPROCESSOR_MODE RequestorMode;
BOOLEAN Cancel;
KIRQL CancelIrql;
PDRIVER_CANCEL CnacelRoutine;
uion{
struct{
union{
KDEVICE_QUERY_ENTRY DeviceQueueEntry;
Struct{
PVOID DriverContext[4];
};
};
PETHRED Thread;
LIST_ENTRY ListEntry;
}Overlay;
}Tail;
}IRP, *PIRP;
IRPのスタックロケーションは、階層ドライバのそれぞれのドライバに対応しています。スタックロケーションはIO_STACK_LOCATION構造体により定義されています。次にスタックロケーションの構造体の一部を示します。
typedef struct _IO_STACK_LOCATION {
UCHAR MajorFunction;
UCHAR MinorFunction;
UCHAR Flags;
UCHAR Control;
union {
//IRP_MJ_CREATEのパラメータ
struct {
PIO_SECURITY_CONTEXT SecurityContext;
ULONG Options;
USHORT POINTER_ALIGNMENT FileAttributes;
USHORT ShareAccess;
ULONG POINTER_ALIGNMENT EaLength;
} Create;
//IRP_MN_START_DEVICEのパラメータ
struct {
PCM_RESOURCE_LIST AllocatedResources;
PCM_RESOURCE_LIST AllocatedResourcesTranslated;
} StartDevice;
} Parameters;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;
IRPのヘッダとスタックロケーションの構造を図2に示します。
IRPがどのように処理されるか図3に簡単に示します。
階層ドライバにおいて、I/OマネージャはユーザーモードからI/O要求を受けると、ドライバと同じ数のスタックロケーションを非ページプールメモリに確保します。そして、I/Oマネージャはヘッダと最初のスタックロケーションのみを初期化します。階層ドライバがどのようにIRP処理を行うか図4に示します。
階層ドライバにおいて、IRPはスタックを下方向に通過する際に各ドライバによって処理されます。また、スタックを上方向に戻る際にも、各ドライバによって処理されます。図4において、Driver1はDriver2のドライバのためにスタックロケーションを初期化します。ドライバは自分自身のスタックロケーションとその直下のスタックロケーションにのみ安全にアクセスすることが可能です。
IRPが上位のドライバに渡されたとき、ドライバは現在のスタックロケーションへのポインタを取り出し、IRPのパラメータを調べます。その際、上位ドライバは次の3つの処理を選択して実行することができます。
IRPは非ページプールメモリに割り当てられます。ドライバは安全にIRPのフィールドにアクセスするためにDDKに用意された関数を使用しなければなりません。次にIRPにアクセスするための関数を示します。
この関数はスタックロケーションのポインタを取得します。
PIO_STACK_LOCATION IoGetCurrentIrpStackLocation
(IN PIRP Irp); //現在のIRPのポインタ
この関数は次の階層ドライバにIRPを渡します。
NTSTATUS IoCallDriver (
IN PDEVICE_OBJECT DeviceObject, //デバイスオブジェクト
IN OUT PIRP Irp); //次に低いドライバに渡すIRPのポインタ
この関数は現在のスタックロケーションの内容を次のスタックロケーションにコピーします。この関数では、完了ルーチンを設定できません。
VOID IoSkipCurrentIrpStackLocation(IN PIRP Irp); /現在のIRPのポインタ
この関数は現在のスタックロケーションの内容を次のスタックロケーションにコピーします。この関数では、完了ルーチンを設定できます。
VOID IoCopyCurrentIrpStackLocationToNext (
IN PIRP Irp); //現在のIRPのポインタ
この関数はIRPの完了をI/Oマネージャに通知します。
VOID IoCompleteRequest(
IN PIRP Irp, //現在のIRPのポインタ
IN CCHAR PriorityBoost); //一時的にスレッドの優先順位を引き上げる
この関数は完了ルーチンを設定します。
VOID IoSetCompletionRoutine(
IN PIRP Irp, //IRP処理完了時、 IRPへのポインタ
IN PIO_COMPLETION_ROUTINE CompletionRoutine,
//IRPが完了した時に呼ばれる関数のポインタ
IN PVOID Context, // CompletionRoutineに渡される引数のポインタ
IN BOOLEAN InvokeOnSuccess,
BOOLEAN InvokeOnError,
IN BOOLEAN InvokeOnCancel);
この関数は現在のIRPをペンディングさせます。
VOID IoMarkIrpPending (
IN OUT PIRP Irp); //ペンディングしようとするIRPのポインタ
この関数は次に低い階層のIRPスタックロケーションのポインタを取得します。
PIO_STACK_LOCATION IoGetNextIrpStackLocation (
IN PIRP Irp); //次に低い階層IRPのポインタ
この関数は次に低い階層のIRPスタックロケーションにアクセス可能にします。
VOID IoSetNextIrpStackLocation(
IN OUT PIRP Irp); //次に低い階層IRPのポインタ