recvol3.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. // Buffer size for all volumes involved.
  2. static const size_t TotalBufferSize=0x4000000;
  3. class RSEncode // Encode or decode data area, one object per one thread.
  4. {
  5. private:
  6. RSCoder RSC;
  7. public:
  8. void EncodeBuf();
  9. void DecodeBuf();
  10. void Init(int RecVolNumber) {RSC.Init(RecVolNumber);}
  11. byte *Buf;
  12. byte *OutBuf;
  13. int BufStart;
  14. int BufEnd;
  15. int FileNumber;
  16. int RecVolNumber;
  17. size_t RecBufferSize;
  18. int *Erasures;
  19. int EraSize;
  20. };
  21. #ifdef RAR_SMP
  22. THREAD_PROC(RSEncodeThread)
  23. {
  24. RSEncode *rs=(RSEncode *)Data;
  25. rs->EncodeBuf();
  26. }
  27. THREAD_PROC(RSDecodeThread)
  28. {
  29. RSEncode *rs=(RSEncode *)Data;
  30. rs->DecodeBuf();
  31. }
  32. #endif
  33. RecVolumes3::RecVolumes3(RAROptions *Cmd,bool TestOnly)
  34. {
  35. memset(SrcFile,0,sizeof(SrcFile));
  36. if (TestOnly)
  37. {
  38. #ifdef RAR_SMP
  39. RSThreadPool=NULL;
  40. #endif
  41. }
  42. else
  43. {
  44. Buf.Alloc(TotalBufferSize);
  45. memset(SrcFile,0,sizeof(SrcFile));
  46. #ifdef RAR_SMP
  47. RSThreadPool=new ThreadPool(Cmd->Threads);
  48. #endif
  49. }
  50. }
  51. RecVolumes3::~RecVolumes3()
  52. {
  53. for (size_t I=0;I<ASIZE(SrcFile);I++)
  54. delete SrcFile[I];
  55. #ifdef RAR_SMP
  56. delete RSThreadPool;
  57. #endif
  58. }
  59. void RSEncode::EncodeBuf()
  60. {
  61. for (int BufPos=BufStart;BufPos<BufEnd;BufPos++)
  62. {
  63. byte Data[256],Code[256];
  64. for (int I=0;I<FileNumber;I++)
  65. Data[I]=Buf[I*RecBufferSize+BufPos];
  66. RSC.Encode(Data,FileNumber,Code);
  67. for (int I=0;I<RecVolNumber;I++)
  68. OutBuf[I*RecBufferSize+BufPos]=Code[I];
  69. }
  70. }
  71. // Check for names like arc5_3_1.rev created by RAR 3.0.
  72. static bool IsNewStyleRev(const wchar *Name)
  73. {
  74. wchar *Ext=GetExt(Name);
  75. if (Ext==NULL)
  76. return true;
  77. int DigitGroup=0;
  78. for (Ext--;Ext>Name;Ext--)
  79. if (!IsDigit(*Ext))
  80. if (*Ext=='_' && IsDigit(*(Ext-1)))
  81. DigitGroup++;
  82. else
  83. break;
  84. return DigitGroup<2;
  85. }
  86. bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
  87. {
  88. wchar ArcName[NM];
  89. wcsncpyz(ArcName,Name,ASIZE(ArcName));
  90. wchar *Ext=GetExt(ArcName);
  91. bool NewStyle=false; // New style .rev volumes are supported since RAR 3.10.
  92. bool RevName=Ext!=NULL && wcsicomp(Ext,L".rev")==0;
  93. if (RevName)
  94. {
  95. NewStyle=IsNewStyleRev(ArcName);
  96. while (Ext>ArcName+1 && (IsDigit(*(Ext-1)) || *(Ext-1)=='_'))
  97. Ext--;
  98. wcsncpyz(Ext,L"*.*",ASIZE(ArcName)-(Ext-ArcName));
  99. FindFile Find;
  100. Find.SetMask(ArcName);
  101. FindData fd;
  102. while (Find.Next(&fd))
  103. {
  104. Archive Arc(Cmd);
  105. if (Arc.WOpen(fd.Name) && Arc.IsArchive(true))
  106. {
  107. wcsncpyz(ArcName,fd.Name,ASIZE(ArcName));
  108. break;
  109. }
  110. }
  111. }
  112. Archive Arc(Cmd);
  113. if (!Arc.WCheckOpen(ArcName))
  114. return false;
  115. if (!Arc.Volume)
  116. {
  117. uiMsg(UIERROR_NOTVOLUME,ArcName);
  118. return false;
  119. }
  120. bool NewNumbering=Arc.NewNumbering;
  121. Arc.Close();
  122. wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering);
  123. wchar RecVolMask[NM];
  124. wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask));
  125. size_t BaseNamePartLength=VolNumStart-ArcName;
  126. wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength);
  127. int64 RecFileSize=0;
  128. // We cannot display "Calculating CRC..." message here, because we do not
  129. // know if we'll find any recovery volumes. We'll display it after finding
  130. // the first recovery volume.
  131. bool CalcCRCMessageDone=false;
  132. FindFile Find;
  133. Find.SetMask(RecVolMask);
  134. FindData RecData;
  135. int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0;
  136. wchar PrevName[NM];
  137. while (Find.Next(&RecData))
  138. {
  139. wchar *CurName=RecData.Name;
  140. int P[3];
  141. if (!RevName && !NewStyle)
  142. {
  143. NewStyle=true;
  144. wchar *Dot=GetExt(CurName);
  145. if (Dot!=NULL)
  146. {
  147. int LineCount=0;
  148. Dot--;
  149. while (Dot>CurName && *Dot!='.')
  150. {
  151. if (*Dot=='_')
  152. LineCount++;
  153. Dot--;
  154. }
  155. if (LineCount==2)
  156. NewStyle=false;
  157. }
  158. }
  159. if (NewStyle)
  160. {
  161. if (!CalcCRCMessageDone)
  162. {
  163. uiMsg(UIMSG_RECVOLCALCCHECKSUM);
  164. CalcCRCMessageDone=true;
  165. }
  166. uiMsg(UIMSG_STRING,CurName);
  167. File CurFile;
  168. CurFile.TOpen(CurName);
  169. CurFile.Seek(0,SEEK_END);
  170. int64 Length=CurFile.Tell();
  171. CurFile.Seek(Length-7,SEEK_SET);
  172. for (int I=0;I<3;I++)
  173. P[2-I]=CurFile.GetByte()+1;
  174. uint FileCRC=0;
  175. for (int I=0;I<4;I++)
  176. FileCRC|=CurFile.GetByte()<<(I*8);
  177. uint CalcCRC;
  178. CalcFileSum(&CurFile,&CalcCRC,NULL,Cmd->Threads,Length-4);
  179. if (FileCRC!=CalcCRC)
  180. {
  181. uiMsg(UIMSG_CHECKSUM,CurName);
  182. continue;
  183. }
  184. }
  185. else
  186. {
  187. wchar *Dot=GetExt(CurName);
  188. if (Dot==NULL)
  189. continue;
  190. bool WrongParam=false;
  191. for (size_t I=0;I<ASIZE(P);I++)
  192. {
  193. do
  194. {
  195. Dot--;
  196. } while (IsDigit(*Dot) && Dot>=CurName+BaseNamePartLength);
  197. P[I]=atoiw(Dot+1);
  198. if (P[I]==0 || P[I]>255)
  199. WrongParam=true;
  200. }
  201. if (WrongParam)
  202. continue;
  203. }
  204. if (P[1]+P[2]>255)
  205. continue;
  206. if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2])
  207. {
  208. uiMsg(UIERROR_RECVOLDIFFSETS,CurName,PrevName);
  209. return false;
  210. }
  211. RecVolNumber=P[1];
  212. FileNumber=P[2];
  213. wcsncpyz(PrevName,CurName,ASIZE(PrevName));
  214. File *NewFile=new File;
  215. NewFile->TOpen(CurName);
  216. SrcFile[FileNumber+P[0]-1]=NewFile;
  217. FoundRecVolumes++;
  218. if (RecFileSize==0)
  219. RecFileSize=NewFile->FileLength();
  220. }
  221. if (!Silent || FoundRecVolumes!=0)
  222. uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes);
  223. if (FoundRecVolumes==0)
  224. return false;
  225. bool WriteFlags[256];
  226. memset(WriteFlags,0,sizeof(WriteFlags));
  227. wchar LastVolName[NM];
  228. *LastVolName=0;
  229. for (int CurArcNum=0;CurArcNum<FileNumber;CurArcNum++)
  230. {
  231. Archive *NewFile=new Archive(Cmd);
  232. bool ValidVolume=FileExist(ArcName);
  233. if (ValidVolume)
  234. {
  235. NewFile->TOpen(ArcName);
  236. ValidVolume=NewFile->IsArchive(false);
  237. if (ValidVolume)
  238. {
  239. while (NewFile->ReadHeader()!=0)
  240. {
  241. if (NewFile->GetHeaderType()==HEAD_ENDARC)
  242. {
  243. uiMsg(UIMSG_STRING,ArcName);
  244. if (NewFile->EndArcHead.DataCRC)
  245. {
  246. uint CalcCRC;
  247. CalcFileSum(NewFile,&CalcCRC,NULL,Cmd->Threads,NewFile->CurBlockPos);
  248. if (NewFile->EndArcHead.ArcDataCRC!=CalcCRC)
  249. {
  250. ValidVolume=false;
  251. uiMsg(UIMSG_CHECKSUM,ArcName);
  252. }
  253. }
  254. break;
  255. }
  256. NewFile->SeekToNext();
  257. }
  258. }
  259. if (!ValidVolume)
  260. {
  261. NewFile->Close();
  262. wchar NewName[NM];
  263. wcsncpyz(NewName,ArcName,ASIZE(NewName));
  264. wcsncatz(NewName,L".bad",ASIZE(NewName));
  265. uiMsg(UIMSG_BADARCHIVE,ArcName);
  266. uiMsg(UIMSG_RENAMING,ArcName,NewName);
  267. RenameFile(ArcName,NewName);
  268. }
  269. NewFile->Seek(0,SEEK_SET);
  270. }
  271. if (!ValidVolume)
  272. {
  273. // It is important to return 'false' instead of aborting here,
  274. // so if we are called from extraction, we will be able to continue
  275. // extracting. It may happen if .rar and .rev are on read-only disks
  276. // like CDs.
  277. if (!NewFile->Create(ArcName,FMF_WRITE|FMF_SHAREREAD))
  278. {
  279. // We need to display the title of operation before the error message,
  280. // to make clear for user that create error is related to recovery
  281. // volumes. This is why we cannot use WCreate call here. Title must be
  282. // before create error, not after that.
  283. uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode.
  284. uiMsg(UIERROR_RECONSTRUCTING);
  285. ErrHandler.CreateErrorMsg(ArcName);
  286. return false;
  287. }
  288. WriteFlags[CurArcNum]=true;
  289. MissingVolumes++;
  290. if (CurArcNum==FileNumber-1)
  291. wcsncpyz(LastVolName,ArcName,ASIZE(LastVolName));
  292. uiMsg(UIMSG_MISSINGVOL,ArcName);
  293. uiMsg(UIEVENT_NEWARCHIVE,ArcName);
  294. }
  295. SrcFile[CurArcNum]=(File*)NewFile;
  296. NextVolumeName(ArcName,ASIZE(ArcName),!NewNumbering);
  297. }
  298. uiMsg(UIMSG_RECVOLMISSING,MissingVolumes);
  299. if (MissingVolumes==0)
  300. {
  301. uiMsg(UIERROR_RECVOLALLEXIST);
  302. return false;
  303. }
  304. if (MissingVolumes>FoundRecVolumes)
  305. {
  306. uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode.
  307. uiMsg(UIERROR_RECVOLCANNOTFIX);
  308. return false;
  309. }
  310. uiMsg(UIMSG_RECONSTRUCTING);
  311. int TotalFiles=FileNumber+RecVolNumber;
  312. int Erasures[256],EraSize=0;
  313. for (int I=0;I<TotalFiles;I++)
  314. if (WriteFlags[I] || SrcFile[I]==NULL)
  315. Erasures[EraSize++]=I;
  316. int64 ProcessedSize=0;
  317. int LastPercent=-1;
  318. mprintf(L" ");
  319. // Size of per file buffer.
  320. size_t RecBufferSize=TotalBufferSize/TotalFiles;
  321. #ifdef RAR_SMP
  322. uint ThreadNumber=Cmd->Threads;
  323. #else
  324. uint ThreadNumber=1;
  325. #endif
  326. RSEncode *rse=new RSEncode[ThreadNumber];
  327. for (uint I=0;I<ThreadNumber;I++)
  328. rse[I].Init(RecVolNumber);
  329. while (true)
  330. {
  331. Wait();
  332. int MaxRead=0;
  333. for (int I=0;I<TotalFiles;I++)
  334. if (WriteFlags[I] || SrcFile[I]==NULL)
  335. memset(&Buf[I*RecBufferSize],0,RecBufferSize);
  336. else
  337. {
  338. int ReadSize=SrcFile[I]->Read(&Buf[I*RecBufferSize],RecBufferSize);
  339. if ((size_t)ReadSize!=RecBufferSize)
  340. memset(&Buf[I*RecBufferSize+ReadSize],0,RecBufferSize-ReadSize);
  341. if (ReadSize>MaxRead)
  342. MaxRead=ReadSize;
  343. }
  344. if (MaxRead==0)
  345. break;
  346. int CurPercent=ToPercent(ProcessedSize,RecFileSize);
  347. if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
  348. {
  349. uiProcessProgress("RC",ProcessedSize,RecFileSize);
  350. LastPercent=CurPercent;
  351. }
  352. ProcessedSize+=MaxRead;
  353. int BlockStart=0;
  354. int BlockSize=MaxRead/ThreadNumber;
  355. if (BlockSize<0x100)
  356. BlockSize=MaxRead;
  357. for (uint CurThread=0;BlockStart<MaxRead;CurThread++)
  358. {
  359. // Last thread processes all left data including increasement
  360. // from rounding error.
  361. if (CurThread==ThreadNumber-1)
  362. BlockSize=MaxRead-BlockStart;
  363. RSEncode *curenc=rse+CurThread;
  364. curenc->Buf=&Buf[0];
  365. curenc->BufStart=BlockStart;
  366. curenc->BufEnd=BlockStart+BlockSize;
  367. curenc->FileNumber=TotalFiles;
  368. curenc->RecBufferSize=RecBufferSize;
  369. curenc->Erasures=Erasures;
  370. curenc->EraSize=EraSize;
  371. #ifdef RAR_SMP
  372. if (ThreadNumber>1)
  373. RSThreadPool->AddTask(RSDecodeThread,(void*)curenc);
  374. else
  375. curenc->DecodeBuf();
  376. #else
  377. curenc->DecodeBuf();
  378. #endif
  379. BlockStart+=BlockSize;
  380. }
  381. #ifdef RAR_SMP
  382. RSThreadPool->WaitDone();
  383. #endif // RAR_SMP
  384. for (int I=0;I<FileNumber;I++)
  385. if (WriteFlags[I])
  386. SrcFile[I]->Write(&Buf[I*RecBufferSize],MaxRead);
  387. }
  388. delete[] rse;
  389. for (int I=0;I<RecVolNumber+FileNumber;I++)
  390. if (SrcFile[I]!=NULL)
  391. {
  392. File *CurFile=SrcFile[I];
  393. if (NewStyle && WriteFlags[I])
  394. {
  395. int64 Length=CurFile->Tell();
  396. CurFile->Seek(Length-7,SEEK_SET);
  397. for (int J=0;J<7;J++)
  398. CurFile->PutByte(0);
  399. }
  400. CurFile->Close();
  401. SrcFile[I]=NULL;
  402. }
  403. if (*LastVolName!=0)
  404. {
  405. // Truncate the last volume to its real size.
  406. Archive Arc(Cmd);
  407. if (Arc.Open(LastVolName,FMF_UPDATE) && Arc.IsArchive(true) &&
  408. Arc.SearchBlock(HEAD_ENDARC))
  409. {
  410. Arc.Seek(Arc.NextBlockPos,SEEK_SET);
  411. char Buf[8192];
  412. int ReadSize=Arc.Read(Buf,sizeof(Buf));
  413. int ZeroCount=0;
  414. while (ZeroCount<ReadSize && Buf[ZeroCount]==0)
  415. ZeroCount++;
  416. if (ZeroCount==ReadSize)
  417. {
  418. Arc.Seek(Arc.NextBlockPos,SEEK_SET);
  419. Arc.Truncate();
  420. }
  421. }
  422. }
  423. #if !defined(SILENT)
  424. if (!Cmd->DisablePercentage)
  425. mprintf(L"\b\b\b\b100%%");
  426. if (!Silent && !Cmd->DisableDone)
  427. mprintf(St(MDone));
  428. #endif
  429. return true;
  430. }
  431. void RSEncode::DecodeBuf()
  432. {
  433. for (int BufPos=BufStart;BufPos<BufEnd;BufPos++)
  434. {
  435. byte Data[256];
  436. for (int I=0;I<FileNumber;I++)
  437. Data[I]=Buf[I*RecBufferSize+BufPos];
  438. RSC.Decode(Data,FileNumber,Erasures,EraSize);
  439. for (int I=0;I<EraSize;I++)
  440. Buf[Erasures[I]*RecBufferSize+BufPos]=Data[Erasures[I]];
  441. }
  442. }
  443. void RecVolumes3::Test(RAROptions *Cmd,const wchar *Name)
  444. {
  445. if (!IsNewStyleRev(Name)) // RAR 3.0 name#_#_#.rev do not include CRC32.
  446. {
  447. ErrHandler.UnknownMethodMsg(Name,Name);
  448. return;
  449. }
  450. wchar VolName[NM];
  451. wcsncpyz(VolName,Name,ASIZE(VolName));
  452. while (FileExist(VolName))
  453. {
  454. File CurFile;
  455. if (!CurFile.Open(VolName))
  456. {
  457. ErrHandler.OpenErrorMsg(VolName); // It also sets RARX_OPEN.
  458. continue;
  459. }
  460. if (!uiStartFileExtract(VolName,false,true,false))
  461. return;
  462. mprintf(St(MExtrTestFile),VolName);
  463. mprintf(L" ");
  464. CurFile.Seek(0,SEEK_END);
  465. int64 Length=CurFile.Tell();
  466. CurFile.Seek(Length-4,SEEK_SET);
  467. uint FileCRC=0;
  468. for (int I=0;I<4;I++)
  469. FileCRC|=CurFile.GetByte()<<(I*8);
  470. uint CalcCRC;
  471. CalcFileSum(&CurFile,&CalcCRC,NULL,1,Length-4,Cmd->DisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS);
  472. if (FileCRC==CalcCRC)
  473. {
  474. mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk));
  475. }
  476. else
  477. {
  478. uiMsg(UIERROR_CHECKSUM,VolName,VolName);
  479. ErrHandler.SetErrorCode(RARX_CRC);
  480. }
  481. NextVolumeName(VolName,ASIZE(VolName),false);
  482. }
  483. }