Dokan Library 幫助程序員在windows系統下輕松建立用戶級文件系統,不需要寫設備驅動,其與FUSE(Linux user mode file system)類似。
2. Dokan原理Dokan Library包含以下部分:
- user-mode library (dokan.dll) LGPL
- driver (dokan.sys) LGPL
- control program (dokanctl.exe) MIT
- mount service (mouter.exe) MIT
- samples (mirror.c) MIT
dokan.sys是內核態文件系統驅動,當此驅動安裝之后,就可以創建自己的文件系統了。而我們創建文件系統(實現Dokan庫的API)的程序稱為文件系統程序。這時,程序提出的文件操作請求(例如CreateFile, ReadFile, WriteFile)就會送到Windows I/O subsystem (內核中運行),后者會繼續將請求送到dokan.sys。文件系統程序可以利用dokan.dll提供的函數將我們的回調函數(操作實現函數)注冊到dokan.sys中,后者會在收到請求之后調用這些我們提供的函數。回調函數的結果會送回提出請求的程序。(可參見圖一)
dokan.sys 相當於一個運行在內核態的代理,作為提出請求的程序和我們實現各種操作的文件系統程序的橋梁。至此,我們程序員就可以在用戶態輕松的創建調試文件系統程序,大大提高了開發效率。
3. 創建文件系統
與FUSE類似,我們的文件系統程序需要實現一個結構體中的各個操作DOKAN_OPERATIONS(聲明在dokan.h中),然后該結構體作為參數調用DokanMain掛載文件系統。這些函數的參數與Windows APIs 一致,但必須做到線程安全,因為有可能有多個線程調用。
這些函數有個典型的調用順序:
1. CreateFile(OpenDirectory, CreateFile)
2. Other functions
3. Cleanup
4. CloseFile
file creation functions (OpenDirectory, CreateFile,…)總是在file access operations (listing directory, reading file attributes, …)之前調用。當文件被CloseFile Windows API關閉時,Cleanup程序總是被dokan.sys調用。
返回值為0時表示操作成功。
每個函數的最后一個參數是DOKAN_FILE_INFO structure
typedef struct _DOKAN_FILE_INFO {
ULONG64 Context; //文件系統程序維護,可作為文件句柄
ULONG64 DokanContext; //Dokan Library維護
ULONG ProcessId; //操作ID
BOOL IsDirectory; //目錄= TRUE
} DOKAN_FILE_INFO, *PDOKAN_FILE_INFO;
每一個文件句柄都與一個DOKAN_FILE_INFO struct對應。該結構創建在文件被CreateFile系統調用打開時,回收在文件被CloseFile系統調用關閉時。
下面是幾個操作的聲明:
int (*CreateFile) (
LPCWSTR, // FileName
DWORD, // DesiredAccess
DWORD, // ShareMode
DWORD, // CreationDisposition
DWORD, // FlagsAndAttributes
PDOKAN_FILE_INFO);
int (*OpenDirectory) (
LPCWSTR, // FileName
PDOKAN_FILE_INFO);
int (*CreateDirectory) (
LPCWSTR, // FileName
PDOKAN_FILE_INFO); //注意設置IsDirectory = TRUE
//CloseHandle ( Windows API)執行之后調用,如果文件句柄在//CreateFile中創建,應該在此釋放,而不是在CloseFile。
//如果用戶在內存中緩存了文件,調用Cleanup之后還有可能調用讀寫//操作。
int (*Cleanup) (
LPCWSTR, // FileName
PDOKAN_FILE_INFO);
//如果用戶調用CloseHandle后再打開相同文件,CreateFile之前可能//不會再調用CloseFile,這可能會出共享錯誤。
int (*CloseFile) (
LPCWSTR, // FileName
PDOKAN_FILE_INFO);
int (*FindFiles) (
LPCWSTR, // PathName
PFillFindData, // call this function with PWIN32_FIND_DATAW
PDOKAN_FILE_INFO); // (see PFillFindData definition)
// You should implement either FindFires or FindFilesWithPattern
int (*FindFilesWithPattern) (
LPCWSTR, // PathName
LPCWSTR, // SearchPattern
PFillFindData, // call this function with PWIN32_FIND_DATAW
PDOKAN_FILE_INFO);
上面兩個函數是回應列目錄項操作請求的。對每一個目錄項,文件系統程序都會調用函數FillFindData( &win32FindDataw, DokanFileInfo )。由於Windows的shell對於模式匹配不支持,文件系統程序就要執行通配模式。當文件系統程序實現FindFiles,DokanLibrary會自動添加通配模式,我們也可以自己實現FindFilesWithPattern來加以控制。DokanIsNameInExpression (dokan.dll)函數就是用來實現模式匹配的。
4. 掛載前面已經提到,可以調用DokanMain函數來掛載文件系統。該程序會阻塞到文件系統被卸載。
我們的文件系統要做兩件事,一是為Dokan運行庫填寫DokanOptions,二是填寫帶各個操作函數指針的DokanOperations作為DokanMain的參數。
int DOKANAPI DokanMain(
PDOKAN_OPTIONS DokanOptions,
PDOKAN_OPERATIONS DokanOperations);
typedef struct _DOKAN_OPTIONS {
USHORT Version; // Supported Dokan Version, ex. "530" (Dokan ver 0.5.3)
ULONG ThreadCount; // number of threads to be used
ULONG Options; // combination of DOKAN_OPTIONS_*
ULONG64 GlobalContext; // FileSystem can use this variable
LPCWSTR MountPoint; // mount point "M:\" (drive letter) or
// "C:\mount\dokan" (path in NTFS)
} DOKAN_OPTIONS, *PDOKAN_OPTIONS;
5. 卸載調用 DokanUnmount進行文件系統的卸載。用戶也可以使用 DokanCtl 像這樣進行卸載:
> dokanctl.exe /u DriveLetter