檔案重定向(採用檔案過濾驅動實現)
Windows的I/O管理器提供了一個方便的方法來重定向一個檔案物件。通常使用檔案過濾驅動(在檔案開啟和檔案建立的操作中)實現該方法。操作方法如下:
1、在IRP_MJ_CREATE的分發函式中,獲得FILE_OBJET的FileName屬性。
2、用目標檔案的完整路徑替換原有的檔名字。
這個全名,包括卷裝置物件的名字(例如,Device/HardDiskVolume0/Directory/MyFile.txt)。可以釋放掉原有的FileName.Buffer,同時用自己定義的緩衝區(buffer,以NonPagedPool方式申請)替換它。
3、設定IoStatus的status欄位為STATUS_REPARSE,然後設定Information欄位為IO_REPARSE.
4、完成該IRP請求。
5、返回STATUS_REPARSE
I/O管理器接收到該返回後,便會觸發另一個檔案開啟操作,併發送一個IRP_MJ_CREATE的請求。
而目標檔案可以是本地或遠端計算機。而若要重定向遠端檔案開啟操作,檔名要遵循以下語法:
"/??/UNC/HostName/Share/File"
或
"/Device/Mup/HostName/Share/File"
或
"/Device/LanmanagerRedirector/HostName/Share/File"(在你的目標檔案是CIFS/SMB/LanManager的情況下)
在你的首次開啟/建立檔案操作是相對於另一個檔案物件的時候,沒有必有修改FILE_OBJECT的RelatedFileObject域。在重定向時,I/O管理器只考慮FileName域,而不考慮RelatedFileObject域(在I/O管理器收到STATUS_REPARSE後,它便會釋放該域)。
I/O管理器為了避免重定向的無限迴圈,在巢狀迴圈中加了一些限制:重定向操作的最大巢狀次數是32.
程式碼:
在IRP_MJ_CREATE例程裡新增如下程式碼:
irpSp = IoGetCurrentIrpStackLocation(Irp);
RtlInitUnicodeString(&cmpFileName, L"//hello.txt");
KdPrint((">>> Create/Open FileName:%ws/n", irpSp->FileObject->FileName.Buffer));
if (RtlCompareUnicodeString(&cmpFileName, &irpSp->FileObject->FileName, FALSE) == 0)
{
pusFileName = &(irpSp->FileObject->FileName);
// 方法很簡單
// 就是把FileObject->FileName.Buffer釋放掉
// 然後自己ExAllocatePool...分配一個緩衝區用於儲存重定向的檔名 這裡需要是全檔名
// FileObject->FileName指向新分配的緩衝區
// 把新檔名拷貝到FileObject->FileName裡
// 設定Irp->IoStatus的值如下
// Irp->IoStatus.Status = STATUS_REPARSE;
// Irp->IoStatus.Information = IO_REPARSE;
// 返回STATUS_REPARSE
// 經測試跨卷訪問也可以
RtlInitUnicodeString(&usNewFileName, L"//??//E://123//hello.txt");
pwNewNameBuffer = ExAllocatePool(PagedPool, usNewFileName.MaximumLength);
if (pwNewNameBuffer == NULL)
{
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_INSUFFICIENT_RESOURCES;
}
ExFreePool( pusFileName->Buffer );
pusFileName->Buffer = pwNewNameBuffer;
pusFileName->MaximumLength = usNewFileName.MaximumLength;
RtlCopyUnicodeString(pusFileName, &usNewFileName);
Irp->IoStatus.Status = STATUS_REPARSE;
Irp->IoStatus.Information = IO_REPARSE;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_REPARSE;
}