在现代的软件开发中,文件系统是一个不可或缺的组成部分。从传统的NTFS、FAT32到现在流行的GDocs等云存储,文件系统的形态和功能越来越多样化。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或任何想要存储数据的数据库。
所有代码都是原样发布的。作者为可能带来的不便表示歉意。时间过得太快,作者还有其他事情要做,不仅仅是这个项目。