Dokan文件系统开发指南

在现代的软件开发中,文件系统是一个不可或缺的组成部分。从传统的NTFS、FAT32到现在流行的GDocs等云存储,文件系统的形态和功能越来越多样化。Dokan作为一种创新的文件系统开发工具,为开发者提供了一个强大的平台,使得他们能够开发出各种新颖的文件系统。本文将介绍Dokan的基本理念、开发步骤以及一些关键点,帮助读者更好地理解和使用Dokan。

Dokan是什么?

Dokan是一个设备驱动程序,它封装了文件系统调用,使得开发者可以通过编程方式响应核心的I/O操作,如ReadFile、WriteFile、FindFiles等。Dokan支持.NET,甚至提供了Dokan.NET接口,使得使用C#语言开发文件系统成为可能。通过Dokan,开发者可以创建自己的文件系统,或者从其他使用Dokan的应用程序中获得灵感。例如,可以使用内存作为ramdisk的文件系统,或者使用加密文件作为数据存储的系统。此外,还可以实现对某些磁盘或目录的镜像,通过文件系统访问注册表,或者将进程列表作为文件显示在附加的磁盘上。Dokan的潜力无限,等待去发掘。

开发前的准备

要开始使用Dokan,首先需要安装Dokan。可以从Dokan的官方网站下载最新版本(0.53版本),并进行安装。本文的代码是在0.52版本下测试的。

其次,需要准备一个Microsoft SQL数据库。对于测试目的,可以在个人电脑上安装Microsoft SQL Express版本。但是,为了模拟网络流量和网络事故,作者使用了位于虚拟机上的SQL服务器。对于生产环境,建议创建一个特殊的用户账户,并允许该账户运行存储过程。这是一个良好的安全策略。

在SQL服务器上,需要运行元数据脚本来创建C#应用程序使用的存储过程,并创建一个名为"DOKANFS"的表。这是应用程序使用的唯一表。如果希望使用其他表,则需要在所有存储过程中重命名所有出现的表名。

此外,需要安装Visual Studio 2008 Standard版本。如果使用Express版本,则必须在本地机器上使用Microsoft SQL Express。Express版本不允许远程连接到Microsoft SQL服务器。

代码探索

让深入代码,寻找一些有用的信息。在代码中,文件被"预加载"到一个简单的Dictionary对象中。当第一次调用ReadFile方法以获取数据时,文件从SQL数据库中加载。如果文件在SQL服务器上被gzip压缩,则在下载时会进行"解压缩"。为了减少网络流量,当文件写入SQL服务器时会被gzip压缩。但是,如果文件具有列出的扩展名,则不会被压缩。

文件的读写操作是通过FileCache对象进行的。当文件关闭时,会调用由Dokan.NET调用的Cleanup过程。Cleanup过程会对数据进行gzip压缩,并调用SP WriteFile将数据存储到Microsoft SQL中。

public int Cleanup(string filename, DokanFileInfo info) { lock(FileCache) { if((FileCache.ContainsKey(filename) == true) && (FileCache[filename].MemStream.Length > 0)) { using(SqlConnection conn = new SqlConnection(ConnectionString)) { using(SqlCommand Cmd = new SqlCommand()) { MemoryStream mem = ((FileCaching)FileCache[filename]).MemStream; Cmd.CommandText = "WriteFile"; Cmd.Parameters.Add("@iszipped", SqlDbType.Bit, 1); Cmd.Parameters["@iszipped"].Value = 0; Cmd.Parameters.Add("@OriginalSize", SqlDbType.BigInt); Cmd.Parameters["@OriginalSize"].Value = mem.Length; if(this.ZippedExtension.ToLower().IndexOf(Path.GetExtension(Regex.Split(filename.ToLower(), ".version")[0])) == -1) { if(FileCache[filename].MemStream.Length > 256) { Cmd.Parameters["@iszipped"].Value = 1; MemoryStream dummy = new MemoryStream(); Compress(mem, dummy); mem.SetLength(0); dummy.WriteTo(mem); } } mem.Seek(0, SeekOrigin.Begin); Cmd.Parameters.Add("@data", SqlDbType.VarBinary, (int)mem.Length); Cmd.Parameters["@data"].SqlValue = mem.ToArray(); Cmd.Parameters.AddWithValue("@filename", filename); Cmd.CommandType = CommandType.StoredProcedure; Cmd.Connection = conn; conn.Open(); Cmd.ExecuteNonQuery(); FileCache.Remove(filename); } } } } return DokanNet.DOKAN_SUCCESS; }

在Microsoft SQL中编写的T-SQL代码并不复杂。最复杂的存储过程是FindFiles,因为它使用了版本。设置驱动器以显示旧版本的文件或不显示的选项是设置根目录"\"的内容字段中的第一个位。如果第一个位设置为1,则程序会显示扩展名中的旧版本。

ALTER PROCEDURE [dbo].[FindFiles](@filename varchar(255)) AS BEGIN SET NOCOUNT ON if @filename = '\' set @filename = '\' else set @filename = @filename+'\' select filename, isdirectory, IsNull(OriginalSize, DATALENGTH([CONTENT])) as size, filename as fullfilename, LastAccessTime,LastWriteTime,CreationTime into #TEMP from DOKANFS where (filename like @filename+'%' and FILENAME_ not like @filename+'%\%' and Version is null) -- all versions declare @allVersion int select @allVersion = (isnull(cast(content as int), 0) & 1) _ from DOKANFS where FILENAME = '\' if @allVersion = 1 begin select filename + '.'; + cast(ISNULL(version, '0') as varchar(10)) as filename, isdirectory, IsNull(OriginalSize,DATALENGTH([CONTENT])) as size, filename+ '.'; + cast(ISNULL(version, '0') as varchar(10)) as fullfilename, LastAccessTime,LastWriteTime,CreationTime into #TEMP2 from DOKANFS where (filename like @filename+'%' and FILENAME_ not like @filename+'%\%' and Version is not null) update #TEMP2 set filename = SUBSTRING(filename, CHARINDEX(@filename, filename)+LEN(@filename), 255) end update #TEMP set filename = SUBSTRING(filename, CHARINDEX(@filename, filename)+LEN(@filename), 255) insert into #TEMP (filename, isdirectory,size,fullfilename, LastAccessTime,LastWriteTime,CreationTime) values ('.', 1, 0, '.', GETDATE(), GETDATE(), GETDATE()) if @filename <> '\' insert into #TEMP (filename,isdirectory,size,fullfilename, LastAccessTime,LastWriteTime,CreationTime) values ('..', 1, 0, '..', GETDATE(), GETDATE(), GETDATE()) if @allVersion = 1 begin select * from #TEMP union select * from #TEMP2 order by filename end else begin select * from #TEMP order by filename end RETURN END

关键点

Dokan 0.52版本运行良好,但在Notepad++打开文件时会出现一些意外行为。作者仍在等待Dokan像TrueCrypt一样,按需加载驱动程序。

需要进行大量的网络测试。作者在1Mbit VPN上测试了这个概念,没有遇到任何问题。这只是一个概念验证,没有应用任何代码"美化器"。此外,还可以实现一些"注册"功能,例如,通过创建所有存储过程、创建表等来准备数据库。也可以使用Azure、PostgreSQL、MySQL、Firebird、Oracle或任何想要存储数据的数据库。

所有代码都是原样发布的。作者为可能带来的不便表示歉意。时间过得太快,作者还有其他事情要做,不仅仅是这个项目。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485