一、问题描述
1. clients.met 中保存的数据是一些错误的数据时候,会引起电驴程序启动时出现程序crash或runtime error
(clients.met 的损坏可能是由于程序之前有其它情况引起了意外退出的时候 )
二、问题分析
1. 当程序启动的时候程序加载clients.met数据(该文件中保持了电驴P2P网络积分数据,也即Peer之间上传下载数据比率等)程序执行 CClientCreditsList::LoadList
CClientCreditsList::LoadList( )
{
... UINT count = file.ReadUInt32();
m_mapClients.InitHashTable(count+5000); // TODO: should be prime number... and 20% larger
...
}
由于文件的意外损坏,使得count变成了一个很大的数,我拿到的crash测试样本中读出来是 0x53000000, 这个数是一个非常大的数,使得 m_mapClients.InitHashTable 分配内存失败,直接引起程序crash或跳出runtime error, (卖瓜的!这个时候要向系统申请分配 5,570,035,712 Byte的内存
! )
2. 之前的代码中在 clients.met不存在 或 client.met损坏后都未尝试从 clients.met.bak 中恢复数据
3. 同时发现了另一个bug:按之前的设计,每次成功加载clients.met后, CClientCreditsList::LoadList中的都会尝试把当前的clients.met备份为一个隐藏的clients.met.bak 文件,以避免出现程序意外,但实际的运行结果是:只有第一次能备份成功,以后的备份由于 clients.met.bak 文件有隐藏属性,CopyFile会失败,也就是几乎只有安装运行后的第一次曾经成功备份过,后来的新的clients.met数据都没有成功备份为 clients.met.bak;
(参考MSDN CopyFile 说明: This function fails with ERROR_ACCESS_DENIED if the destination file already exists and has the FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_READONLY attribute set.)
三、问题解决
1. 读取该ClientCreditsList的Count个数超过一定值的时候,可以认为该文件已经损坏,应该尝试从上一次成功加载并且成功备份的 clients.met.bak 文件中加载数据到 ClientCreditsList ;
由于从 clients.met或从clients.met.bak 实际加载数据的代码应该基本是相同的,因此采用“递归”来实现,先默认执行LoadList(bFromBakFile=false)从clients.met加载数据,当clients.met不存在或损坏的情况下尝试递归调用LoadList(bFromBakFile=true),也即从clients.met.bak加载数据;如果clients.met.bak 也不存在或已损坏,那么直接return;
2. 备份 client.met 为 clients.met.bak 之前应该先清除 clients.met.bak 的隐藏属性,CopyFile 成功备份后再重新设置


































