1. Dokan Library 简介
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