同样的是老歌,苏联的老歌曲显得更耐听一些,反观我们的,曲子很好,旋律也美,但词却怎么也唱不出口了,动不动就党啊毛啊的,太露骨。跟时代跟得太紧,必然会被时代抛弃!
时钟在敲,心儿在跳,这正时时候,送去欢笑;时钟在敲,心儿在跳,这正是时候,将错误抛掉 ……
同样的是老歌,苏联的老歌曲显得更耐听一些,反观我们的,曲子很好,旋律也美,但词却怎么也唱不出口了,动不动就党啊毛啊的,太露骨。跟时代跟得太紧,必然会被时代抛弃!
时钟在敲,心儿在跳,这正时时候,送去欢笑;时钟在敲,心儿在跳,这正是时候,将错误抛掉 ……
凡是不确定的东西,你都会觉得难,但其实它可能根本就是无厘头!今天就遇到两件无厘头的事:
第一件是个很无厘头的数据损坏的bug。说它是无厘头,是因为还没搞清楚它为什么会发生,虽然找到了解决方案并且解决方案非常简单。问题大体如下:针对cached i/o,如果写数据超出文件大小,我们会为此文件再分配空间,然后直接调用CcCopyWrite交给Cache Manager来处理就行。这里会涉及文件大小的更改,问题就出在这里。以前的版本是在CcCopyWrite之前就直接将文件大小改好了,数据写入一切正常。后来觉得不妥当,遂改到调用CcCopyWrite之后根据返回结果来更改文件大小,结果就发生了数据的损坏。但研究来研究去,并没有发现CcCopyWrite函数有对文件大小有什么判断。
第二件是一个数独游戏。以前也摆过这道题,但因为不确定性太多,无法下子,然后就不了了之。今天又摆上了,到后面只剩下了1,3,5和8,可能的摆法 很多,就随便摆了摆,结果就通了。呵呵,原来是怎么摆都成,而不是就只有唯一的一个确定的方案!这道题实际上是第一题,应该是最容易的。但我当初玩时却在想,为什么第一题这么难,倒是后面的比较容易,难就难在太纠结于不确定上面了。
针对FastIoRead及FastIoWrite的实现,很多文件系统驱动都简单地调用系统提供的File System Runtime Routine: FsRtlCopyRead和FsRtlCopyWrite。
但这样做会有一个隐患,以Ext2Fsd来说,实际的文件大小是存储在inode.i_size中的,不是在FSRTL_COMMON_FCB_HEADER中(或FSRTL_ADVANCED_FCB_HEADER,前者超集)。Cache Manager内部只会处理FSRTL_COMMON_FCB_HEADER中的FileSize,从而导致Fcb中的FileSize和内部的inode.i_size大小不一致。这个 bug还是测试在EXT2卷上用DDK编译/build程序时发现的。
解决办法就是实现自己的FastIoWrite,如果写请求超出文件大小则拒绝此请求,然后走正常的IRP流程。其实,结合上篇blog: AdvanceOnly- FileEndOfFileInformation,还有另外一种方案,即在处理FileEndOfFileInformation w/ AdvanceOnly 时来更新内部的inode.i_size至最新大小,但此方法有待验证。
IRP_MJ_SET_INFORMATION有个特殊的参数:AdvanceOnly,DDK中说明如下:
实际实现中,此参数只针对FileEndOfFileInformation才有意义。FileEndOfFileInformation case是用来处理文件数据大小的,即更改文件数据长度。但当AdvanceOnly为TRUE的时候,情形并不如此,此时的真实目的是更新ValidDataLength (VDL),并不是EndOfFile,这是最容易让人误解的地方。
下面是一个典型的调用实例,堆栈分析如下 (XP, i386):
0: kd> kb
ChildEBP RetAddr Args to Child
ba4e3bbc b16dfd58 89a462c0 899f8ad8 ba4e3c0c Ext2Fsd!Ext2SetFileInformation+0x2a2
ba4e3bcc b16dfe67 89a462c0 0b2b27d2 899f8ad8 Ext2Fsd!Ext2DispatchRequest+0x9a
ba4e3c0c 804f0095 898d4af8 88b66c40 88b66c40 Ext2Fsd!Ext2BuildRequest+0x89
ba4e3c1c b9ed709e 8988b008 899de848 00000000 nt!IopfCallDriver+0x31
ba4e3c48 804f0095 899f8ad8 88b66c40 88b66c40 fltMgr!FltpDispatch+0x152
……
ba4e3cb4 804f0095 89a8ead8 88b66c40 89a8ead8 fltMgr!FltpDispatch+0x11f
ba4e3cc4 804e2f43 00000000 88cbd008 806e59f0 nt!IopfCallDriver+0x31
ba4e3cf8 804e59aa 88ccd4f8 ba4e3d20 80559690 nt!CcSetValidData+0xa5
ba4e3d34 804e8081 89e31098 80564720 89e31998 nt!CcWriteBehind+0x258
ba4e3d7c 805389bd 89e31098 00000000 89e31998 nt!CcWorkerThread+0x12f
ba4e3dac 805cf84c 89e31098 00000000 00000000 nt!ExpWorkerThread+0xef
ba4e3ddc 8054632e 805388ce 00000000 00000000 nt!PspSystemThreadStartup+0x34
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
可以看出,调用者是Lazy-writer线程。CcWriteBehind将dirty cache写入磁盘后会调用CcStValidData来更新当前文件的ValidDataLength。Cache Manager在每个文件的SHARED_CACHE_MAP中在保存着此文件最大的ValidDataLength (变量ValidDataGoal,由脏页的最大偏移计算出),并在每次写入cache后告知文件系统。
NTFS文件系统中有VDL的概念,但FAT及EXT2都没有,只有AllocationSize及FileSize描述。Ext2Fsd是直接将VDL等同于FileSize来处理的。
上篇blog里说,怎样开始以及从哪里开始比开始更重要,但转念一想,觉得最重要的还是坚持。如果当初坚持把影片《The Time Traveler’s Wife 》看下去,肯定也会觉得好看,毕竟是金子总会发光的。
不得不说,现在网络盗版太方便了,以至于大家都不再珍惜。如果是去电影院,一旦进去以后,很少人会看到一半就出来的。就我来说,就只有一次看到一半就出来的经历。
就像是做事情,不管大小,只有做了才清楚其中难易,无论计划得多详细,总会有想不到的地方,可能开头顺利但过程却曲折,也可能相反,只有真正去做了才知道,之前的计划都只时揣测。
如果想知道一本书精彩不精彩,只有读了才知道;想知道一个故事有没有趣,也只有听了才知晓。