windows平台xFsRedir程序更新(虚拟磁盘镜像存储方式改进和xFsRedir缓存原理等)
by fanxiushu 2018-05-12 转载或引用请注明原始作者。
https://download.csdn.net/download/fanxiushu/10416862 更新的内容包括如下几个方面: 1,重写了虚拟磁盘镜像文件存储方式,以前的版本只是简单的利用稀疏文件来存储虚拟磁盘镜像。 2,在第一条的基础上,扩展实现了内存虚拟磁盘,从而给xFsRedir实现内存目录提供基础。 同时也实现了重定向到本地任意一个目录。 3,删除了百度网盘的接口,替代成微软的OneDrive云盘接口。 国内这些云盘接口像昙花一样, 花了时间查了他们的接口资料好不容易实现了,没多久结果又不能用了,因此懒得去折腾, 还不如实现能提供稳定公开接口的比如OneDrive等云盘, 本想再实现Google,DropBox,亚马逊等云盘接口,可惜在中国境内要么被墙,要么速度慢的要死。 xFsRedir本身并不是做这些网盘的客户端,xFsRedir鼓励你建立自己的私有云存储。 xFsRedir提供了尽量多的网络文件传输协议帮助你建立私有云存储。 4,软件配置界面做了些修改,配置界面对高分屏的电脑屏幕提供了支持。 5,修改了其他一些BUG。 xFsRedir是在windows平台下实现目录重定向, 也就是把多个异构的网络文件系统集中到windows的目录中进行访问,如同访问windows的本地文件系统一样。 xFsRedir运行效果如下图(WIN10平台): 如上图所述,非常热闹。 各种系统的文件目录都被重定向到 FXS-VDISK的虚拟磁盘中。 FXS-VDISK是xFsRedir创建的一个虚拟磁盘,磁盘被格式化成NTFS文件系统,然后在FXS-VDISK中实现目录重定向。 CentOS目录指向CentOS系统(linux的一个分支),github是我的github账户目录,iPhone是iPhone手机共享目录, LocalDir是本地目录,就是把随意的某个本地目录重定向到另一个本地目录。 memdir是内存目录,就是文件目录放到内存中。 openwrt是嵌入式路由操作系统openwrt对应的系统目录。 macOS是macOS系统的目录,OneDrive和OneDrive2是OneDrive云盘注册的两个账号。 w-FTP,PC1,SMB,坚果云等目录是使用通用协议的重定向。缺了Andriod系统,因为手上没Andriod手机。 这些重定向的目录都等同于本地文件系统,不是资源管理器的插件扩展,也不是同步软件的同步目录, 所有的文件操作都被实时的发送给远端的文件目录处理,xFsRedir原理可查阅我之前写的“过滤驱动实现目录重定向”的文章。 一,重写虚拟磁盘镜像存储方式: xFsRedir提供了创建新盘符的功能,以便把重定向目录扩展到新盘符的某个目录中。 如上图所示,FXS-VDISK(K:) 就是xFsRedir驱动创建的新的K盘符。 新建的虚拟磁盘是采用类似FileDisk驱动框架实现的, 在 https://blog.csdn.net/fanxiushu/article/details/9903123 (磁盘驱动与虚拟磁盘Miniport驱动之一) 等链接简单提到了filedisk的功能。 filedisk提供了以镜像文件方式代替真正的磁盘扇区,简单的说虚拟磁盘最终存储的是一个镜像文件而不是真实的磁盘。当把这个虚拟磁盘格式化成NTFS,FAT等文件系统的时候,格式化等数据全部被写入到镜像文件中。 最初设计xFsRedir创建新盘符的功能的时候,采用的是自定义的文件系统,根据目录重定向的特点,所有请求实际都被重定向了。 也就是没有任何实际数据写入到虚拟磁盘中,因此在自定义文件系统中也就没必要引入镜像文件的概念了。 但是开发完成之后,发现某些奇怪的软件,尤其是win10平台下的UWP软件,比如视频播放器,压根就不认这个自定义文件系统, (当然这其中的原因可能是没处理好某些特殊磁盘命令造成的。) 而且xFsRedir的驱动核心部分就是过滤驱动拦截某个目录的文件请求, 定义了新文件系统,结果还是要做新文件系统下的某个目录重定向。也就是这样做显得比较罗嗦了。 因此放弃了自定义文件系统(其实xfs_redir.sys驱动中依然保留了自定义文件系统,只是我没把它导出到应用层而已), 对虚拟磁盘格式化为NTFS文件系统,这个时候就必须引入镜像文件。 已经发布的xFsRedir版本对镜像文件只是简单的利用稀疏文件来保存虚拟磁盘数据。 所谓稀疏文件,就是文件可以很大,但是不一定都需要占用文件大小这么大的空间, 因为可能只有文件很少一部分写入了真实数据,其他都是空的。 比如分配一个100GB的虚拟磁盘空间,需要的镜像文件大小必须是100G,这个时候,采用稀疏文件格式, 镜像文件虽然100G,但是实际只占用磁盘上了其中几十MB字节的数据。 本来这种简单使用稀疏文件方式,在之前的多台电脑上运行得比较良好, 自从换了台新电脑MacBookPro,而且安装Windows10之后,利用xFsRedir创建新盘符,机器就蓝屏死机, 调试发现是镜像读写文件问题,也不想去找具体原因了,打算修改存储算法,采用其他办法来替代稀疏文件方式。 这也是本次发布新版本xFsRedir的原动力。 至于如何修改,达到怎样的效果。 想到了vmware,它生成的vmdk磁盘文件,属于动态增长方式,也就是开始创建一个虚拟机的时候, vmdk磁盘文件很小,随着虚拟机的使用,vmdk磁盘文件慢慢增长,当然增大了之后就不会变小了。 我们的的虚拟磁盘的镜像文件也要达到这种效果。 我们采用尽量简单的数据结构来达到这种效果。 在镜像文件的前4*512K字节以4字节(也就是一个int长度)为单位存储真正虚拟磁盘的磁盘数据块的偏移值, 以4M字节为一个磁盘数据块。 然后接下来开辟4K字节存储其他头信息, 再接下来就是以4M字节为单位,存储真正的数据内容。 我们可以简单算一下,这种存储方式能最大达到多大虚拟磁盘容量。 前4*512K字节以4字节为单位存储偏移,也就是可以存储 512K个偏移,而每个偏移数据块是4M大小,因此总容量是: 512KB * 4MB = 2TB 也就是最大 2TB 的虚拟磁盘容量,这正好是使用MBR存储方式单个分区能达到的最大容量, 因为我在虚拟磁盘使用的是MBR分区而不是GPT分区。 接下来要解决虚拟磁盘的数据块与镜像文件这种存储结构的数据块的映射关系。 镜像文件头部 4*512K + 4K(以下简称IMG_HEADER)之后是 4M 数据块(以下简称IMG_BLOCK) 组成的数组。 IMG_BLOCK在本镜像中的偏移很好定位,比如第一个IMG_BLOCK偏移就是0,第二个是1,以此类推。 IMG_BLOCK在真正的虚拟磁盘的偏移(也就是这个4M数据块在虚拟磁盘处于哪个位置), 则是根据 IMG_HEADER的4*512K数据区给出。 比如在本地镜像中第一个IMG_BLOCK,对应在虚拟磁盘中的偏移根据IMG_HEADER的 0-4字节给出,第二个IMG_BLOCK则是4-8字节给出。 而在内存中,使用MAP结构来保存这种映射关系,以便能迅速定位。 MAP的key是在IMG_BLOCK在虚拟磁盘中的真实偏移,value对应在本镜像中的偏移。 MAP结构在初始化虚拟磁盘的时候,从本地镜像的IMG_HEADER的前4*512K字节读取映射关系并存储到MAP结构中。 当虚拟磁盘驱动发起IRP_MJ_READ/IRP_MJ_WRITE请求的时候,根据请求的offset和length计算出 这个请求的数据块属于哪个IMG_BLOCK内或者包含哪些IMG_BLOCK的偏移值,然后从MAP结构查找对应在镜像文件中的位置, 接着就是ZwReadFile/ZwWriteFile读写镜像文件内容了。 大致伪代码如下: //计算出偏移属于哪个IMG_BLOCK中,i_virtual_block对应起始值 ULONG i_virtual_block = (ULONG)(offset.QuadPart /IMG_BLOCK_SIZE); LONG in_off = (LONG)(offset.QuadPart % IMG_BLOCK_SIZE); //计算在起始IMG_BLOCK内的偏移,字节为单位。 LONG in_len = min( (IMG_BLOCK_SIZE - in_off),length); //计算在起始IMG_BLOCK内长度,字节为单位。 // vdisk_image_read_write_block执行真正的数据块读写,其中i_virtual_block 是在虚拟磁盘中的IMG_BLOCK偏移。 // len 作为返回值,真正读写的字节数。in_off是在当前IMG_BLOCK中的字节偏移。 status = vdisk_image_readwrite_block(img,is_read,buf,i_virtual_block,in_off,&len);// 。。。。 length -= in_len; //length是读写总长度 buf += in_len; //buf是读写缓冲 while (length > 0) { len = min(IMG_BLOCK_SIZE,length); i_virtual_block++; //next block, 下一个数据块 // DPT("2-- read/write block.n"); status = vdisk_image_readwrite_block(img,&len); // 。。。// 其他处理 ///继续执行下一个IMG_BLOCK读写,直到出错或者读写完成。 length -= len; buf += len; } 上面代码片段中vdisk_image_readwrite_block函数的大致流程如下: 首先根据i_virtual_block从MAP结构中查询对应的本地镜像的偏移值,如果没找到,说明没有建立本地镜像和虚拟磁盘对应块的关系, 这个时候就新建一个对应块关系。 如果找到,则根据查找到的本地镜像的IMG_BLOCK偏移, 计算出在镜像文件中的实际位置,调用ZwReadFile/ZwWriteFile发起文件读写操作。 经过这样设计之后的虚拟磁盘镜像,已经就可以达到vmware中的那样的效果,一开始格式化NTFS文件系统的虚拟磁盘, 占100M左右的镜像文件大小,之后慢慢朝这个虚拟磁盘添加文件,镜像的大小才会慢慢变大。 当然变大了就不会缩小,除非删除虚拟磁盘和对应的镜像文件,重新格式化。 采用如上的数据结构存储虚拟磁盘数据,发现很容易扩展到虚拟内存磁盘,因此也就顺便实现了虚拟内存磁盘。 虚拟内存磁盘和写到镜像文件的虚拟磁盘除了数据存储位置不同(一个存储到内存,一个存储到文件),其他没什么区别。 跟镜像文件虚拟磁盘一样,首先固定一个数据块大小,我们使用的是16M为一个块大小(以下简称MEM_BLOCK)。 我们依然使用MAP结构来保存虚拟磁盘的MEM_BLOCK数据块偏移,不过MAP的value是对应的内存地址, 因为我们使用的是 MmAllocatePagesForMdl 系统函数来分配内核内存,因此实际上MAP的Value保存的是MDL指针。 跟上面的文件镜像磁盘一样, 当内存虚拟磁盘驱动发起IRP_MJ_READ/IRP_MJ_WRITE请求的时候,根据请求的offset和length计算出哪些MEM_BLOCK, 然后根据MEM_BLOCK偏移,从 MAP结构查找对应的数据块的MDL, 如果找到则从MDL读写对应的数据,其实就是计算出具体位置,从MDL链中获取对应内存位置,调用RtlCopyMemory复制数据。 如果没找到,说明对应偏移值的MEM_BLOCK没在内存中存在, 我们必须创建一个16M的MEM_BLOCK并且保存到MAP结构中。 系统函数 MmAllocatePagesForMdl 有个特点,就是如果分配16M大小内存,不一定就能获得16M的内存, 可能只获取到一个很小内存的MDL,为了一次分配达到16M,代码按照如下方式实现: static NTSTATUS AllocateFixedBufferSizeMDL(vdisk_image_t* img,image_block_t* block) { const PHYSICAL_ADDRESS physical_address_zero = { 0,0 }; const PHYSICAL_ADDRESS physical_address_max64 = { ULONG_MAX,ULONG_MAX }; //// LONG length = img->fixed_block_size; // 16M, MEM_BLOCK_SIZE while (length > 0) { PMDL mdl = MmAllocatePagesForMdl(physical_address_zero, physical_address_max64,physical_address_zero,length ); if (!mdl) { DPT("*** MmAllocatePagesForMdl error NULL.n"); FreeMdlChain(block->mdl);//释放 mdl 数据链。 return STATUS_INSUFFICIENT_RESOURCES; } ////// mdl->Next = block->mdl; block->mdl = mdl; LONG cc = MmGetMdlByteCount(mdl); length -= cc; } return STATUS_SUCCESS; } 其中 image_block_t就是MAP存储单元,mdl对应的就是MDL,如上代码,mdl是个链,包含多个MDL描述符。 所有这些MDL组成的内存总长度就是16M。 实现了虚拟内存磁盘,我们用他来做什么呢? 其实就是文章开头的更新部分提到的,用来实现内存目录。 内存目录是什么呢?我们大部分目录都是存储在硬盘上,电脑关机之后,数据也不会丢失。 内存目录是保存到内存中,关机之后,数据就会丢失。 就像linux平台下的/tmp这类的目录,是把数据保存到内存的目录。 根据windows的特点,是很难实现内存方式的目录的,顶多模拟出一个虚拟内存磁盘,然后挂载出一个新的盘符来使用内存文件系统。 而不能像linux那样可以挂载到任意目录。 这个时候就需要借用xFsRedir程序的功能,实现原理也不复杂。 首先实现一个虚拟内存磁盘,格式化为NTFS系统,然后把某个需要重定向作为内存目录的比如 d:memdir,经过xfsRedir驱动, 全部重定向到这个虚拟内存磁盘上。这就达到要求了。有兴趣可下载最新版本的xFsRedir程序来使用它的内存目录。 二, 添加 OneDrive 云盘。 把早已没用的百度云盘接口删除了,想着得要再实现一个类似的公网云盘,找来找去, 国内没一个像样的公开接口的云盘。甚至都没找到有公开接口的云盘。 因此只好想到了微软的OneDrive,OneDrive的服务器不在中国境内,访问起来速度还是比较慢。 但基本上能接受,而且再看windows10自带的OneDrive客户端同步软件,有些时候,同步起来速度还是挺快。 OneDrive 的开发接口文档请查阅如下链接: https://docs.microsoft.com/zh-cn/onedrive/developer/rest-api/ 这是 REST-API 方式的接口,看他们的文档应该能很好理解。 有兴趣可以在GITHUB下载我实现的OneDrive接口,链接如下: https://github.com/fanxiushu/onedrive-xfsredir (编辑:商洛站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- window python2.7 ImportError: No module named MySQLdb
- wpf – xaml Scrollviewer – 禁用整个窗口的过度滚动/橡皮
- 在windows平台下采用electron-packager打包electron程序为.
- windows – JScript:如何运行外部命令并获得输出?
- Windows Azure SDK for C
- 值为NULL的列是否会影响Microsoft SQL Server的性能?
- ms-office – Microsoft Office 2010功能区自定义UI中的Pha
- 标签为Windows的SSH工具?
- windows – 为什么模拟会话中定义的DOS设备不会出现在资源管
- 10053 您的主机中的软件中止了一个已建立的连接