Windows文件系統過濾驅動開發教程(11)

11.文件和目錄的生成打開,關閉與刪除

我們已經分析了讀,寫與讀類似。文件系統還有其他的操作。比如文件或目錄的打開(打開已經存在的或者創建新的),關閉。文件或目錄的移動,刪除。

實際上FILE_OBJECT並不僅僅指文件對象。在windows文件系統中,目錄和文件都是用FileObject來抽象的。這裏產生一個問題,對於一個已經有的FileObject,我如何判斷這是一個目錄還是一個文件呢?

對於一個已經存在的FileObject,我沒有找到除了發送IRP來向卷設備詢問這個FileObject的信息之外更好的辦法。自己發送IRP很麻 煩。不是我很樂意做的那種事情。但是FileObject都是在CreateFile的時候誕生的。在誕生的過程中,確實有機會得到這個即將誕生的 FileObject,是一個文件還是一個目錄。

Create的時候,獲得當前IO_STACK_LOCATION,假設爲irpsp,那麼irpsp->Parameters.Create的結構爲:

struct {
PIO_SECURITY_CONTEXT SecurityContext;
ULONG Options;
USHORT FileAttributes;
USHORT ShareAccess;
ULONG EaLength;
};

這個結構中的參數是與CreateFile這個api中的參數對應的,請自己研究。我先寫一些函數包裝來方便讀取irpsp.

_inline wd_ulong wd_irpsp_file_opts(wd_irpsp *irpsp)
{
return irpsp->Parameters.Create.Options;
}

_inline wd_ushort wd_irpsp_file_attrs(wd_irpsp *irpsp)
{
return irpsp->Parameters.Create.FileAttributes;
}

enum {wd_file_opt_dir = FILE_DIRECTORY_FILE};
enum {wd_file_attr_dir = FILE_ATTRIBUTE_DIRECTORY};

然後我們搞清上邊Options和FileAttributes的意思。是不是Options裏邊有FILE_DIRECTORY_FILE標記就表示這 是一個目錄?實際上,CreateOpen是一種嘗試性的動作。無論如何,我們只有當CreateOpen成功的時候,判斷FileObject纔有意 義。否則是空談。

成功有兩種可能,一是已經打開了原有的文件或者目錄,另一種是新建立了文件或者目錄。Options裏邊帶有FILE_DIRECTORY_FILE表示 打開或者生成的對象是一個目錄。那麼,如果在Create的完成函數中,證實生成或者打開是成功的,那麼返回得到的FILE_OBJECT,確實應該是一 個目錄。

當我經常要使用我過濾時得到的文件或者目錄對象的時候,我一般在Create成功的的時候捕獲他們,並把他們記錄在一個“集合”中。這時你得寫一個用來表 示“集合”的數據結構。你可以用鏈表或者數組,只是注意保證多線程安全性。因爲Create的時候已經得到了屬性表示FileObject是否是目錄,你 就沒有必要再發送IRP來詢問FileObject的Attribute了。

對了上邊有FileAttributes。但是這個東西並不可靠。因爲在生成或者打開的時候,你只需要設置Options。我認爲這個字段並無法說明你打開的文件對象是目錄。

這你需要設置一下Create的完成函數。如果設置這裏不再重複,請參考上邊對文件讀操作。

wd_stat my_create_comp(in wd_dev *dev,
in wd_irp *irp,
in wd_void *context)
{
wd_irpsp *irpsp = wd_irp_cur_sp(irp);
wd_file *file = wd_irpsp_file(irpsp);

UNREFERENCED_PARAMETER(dev);

if(wd_suc(wd_irp_status(irp))
{
// 如果成功了,把這個FileObject記錄到集合裏,這是一個
// 剛剛打開或者生成的目錄
if(file &&
(wd_irpsp_file_opts(irpsp) & wd_file_opt_dir))
add_obj_to_set(file);
}
return wd_irp_status(irp);
}

這裏順便解釋一下UNREFERENCED_PARAMETER宏。我曾經不理解這個宏的意思。其實就是因爲本函數傳入了三個參數,這些參數你未必會用 到。如果你不用的話,大家知道c編譯器會發出一條警告。一般認爲驅動應該去掉所有的警告,所以用了這個宏來“使用”一下沒有用到過的參數。你完全可以不用 他們。

現在所有的目錄都被你記錄。那麼得到一個FileObject的時候,判斷一下這個FileObject在不在你的集合裏,如果在,就說明是目錄,反之是文件。

當這個FileObject被關閉的時候你應該把它從你的集合中刪除。你可以捕獲Close的IRP來做這個。記得本教程很早以前,我們已經安裝過 my_close函數的來處理IRP(請回憶或者翻閱第3節的內容),那麼很簡單了,在該函數中從你的集合中刪除該FileObject即可。作爲保險的 做法,應該觀察一下關閉是否成功。如果成功的話,再進行你的從集合中刪除元素工作。

因爲判斷FileObject是文件還是目錄的問題,我們已經見識了文件的打開和關閉工作。現在看一下文件是如何被刪除的。

刪除的操作,第一步是打開文件,打開文件的時候必須設置爲可以刪除。如果打開失敗,則直接導致無法刪除文件。第二步設置文件屬性爲用於刪除,第三步關閉文件即可。關閉的時候,文件被系統刪除。

不過請注意這裏的“刪除”並非把文件刪除到回收站。如果要測試,你必須按住shift徹底刪除文件。文件刪除到回收站只是一種改名操作。改名操作我們留到以後再討論。

第一步是打開文件,我應該可以在文件被打開的時候,捕獲到的irpsp的參數,記得前邊的參數結構,中間有:

PIO_SECURITY_CONTEXT SecurityContext;

相關的結構如下:

typedef struct _IO_SECURITY_CONTEXT {
PSECURITY_QUALITY_OF_SERVICE SecurityQos;
PACCESS_STATE AccessState;
ACCESS_MASK DesiredAccess;
ULONG FullCreateOptions;
} IO_SECURITY_CONTEXT, *PIO_SECURITY_CONTEXT;

注意其中的DesiredAccess,其中必須有DELETE標記,纔可以刪除文件。

第二步是設置爲”關閉時刪除”。這是通過發送一個IRP(Set Information)來設置的。捕獲主功能碼爲IRP_MJ_SET_INFORMATION的IRP後:
首先,IrpSp->Parameters.SetFile.FileInformationClass應該爲FileDispositionInformation。

然後,Irp->AssociatedIrp.SystemBuffer指向一個如下的結構:

typedef struct _FILE_DISPOSITION_INFORMATION {
BOOLEAN DeleteFile;
} FILE_DISPOSITION_INFORMATION;

如果DeleteFile爲TRUE,那麼這是一個刪除文件的操作。文件將在這個FileObject Close的時候被刪除。

以上的我都未實際調試,也不再提供示例的代碼。有興趣的讀者請自己完成。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章