1
0

recvol5.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. static const uint MaxVolumes=65535;
  2. // We select this limit arbitrarily, to prevent user creating too many
  3. // rev files by mistake.
  4. #define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files.
  5. RecVolumes5::RecVolumes5(RAROptions *Cmd,bool TestOnly)
  6. {
  7. RealBuf=NULL;
  8. RealReadBuffer=NULL;
  9. DataCount=0;
  10. RecCount=0;
  11. TotalCount=0;
  12. RecBufferSize=0;
  13. #ifdef RAR_SMP
  14. MaxUserThreads=Cmd->Threads;
  15. #else
  16. MaxUserThreads=1;
  17. #endif
  18. ThreadData=new RecRSThreadData[MaxUserThreads];
  19. for (uint I=0;I<MaxUserThreads;I++)
  20. {
  21. ThreadData[I].RecRSPtr=this;
  22. ThreadData[I].RS=NULL;
  23. }
  24. if (TestOnly)
  25. {
  26. #ifdef RAR_SMP
  27. RecThreadPool=NULL;
  28. #endif
  29. }
  30. else
  31. {
  32. #ifdef RAR_SMP
  33. RecThreadPool=new ThreadPool(MaxUserThreads);
  34. #endif
  35. RealBuf=new byte[TotalBufferSize+SSE_ALIGNMENT];
  36. Buf=(byte *)ALIGN_VALUE(RealBuf,SSE_ALIGNMENT);
  37. }
  38. }
  39. RecVolumes5::~RecVolumes5()
  40. {
  41. delete[] RealBuf;
  42. delete[] RealReadBuffer;
  43. for (uint I=0;I<RecItems.Size();I++)
  44. delete RecItems[I].f;
  45. for (uint I=0;I<MaxUserThreads;I++)
  46. delete ThreadData[I].RS;
  47. delete[] ThreadData;
  48. #ifdef RAR_SMP
  49. delete RecThreadPool;
  50. #endif
  51. }
  52. #ifdef RAR_SMP
  53. THREAD_PROC(RecThreadRS)
  54. {
  55. RecRSThreadData *td=(RecRSThreadData *)Data;
  56. td->RecRSPtr->ProcessAreaRS(td);
  57. }
  58. #endif
  59. void RecVolumes5::ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode)
  60. {
  61. /*
  62. RSCoder16 RS;
  63. RS.Init(DataCount,RecCount,Encode ? NULL:ValidFlags);
  64. uint Count=Encode ? RecCount : MissingVolumes;
  65. for (uint I=0;I<Count;I++)
  66. RS.UpdateECC(DataNum, I, Data, Buf+I*RecBufferSize, MaxRead);
  67. */
  68. uint ThreadNumber=MaxUserThreads;
  69. const uint MinThreadBlock=0x1000;
  70. ThreadNumber=Min(ThreadNumber,MaxRead/MinThreadBlock);
  71. if (ThreadNumber<1)
  72. ThreadNumber=1;
  73. uint ThreadDataSize=MaxRead/ThreadNumber;
  74. ThreadDataSize+=(ThreadDataSize&1); // Must be even for 16-bit RS coder.
  75. #ifdef USE_SSE
  76. ThreadDataSize=ALIGN_VALUE(ThreadDataSize,SSE_ALIGNMENT); // Alignment for SSE operations.
  77. #endif
  78. if (ThreadDataSize<MinThreadBlock)
  79. ThreadDataSize=MinThreadBlock;
  80. for (size_t I=0,CurPos=0;I<ThreadNumber && CurPos<MaxRead;I++)
  81. {
  82. RecRSThreadData *td=ThreadData+I;
  83. if (td->RS==NULL)
  84. {
  85. td->RS=new RSCoder16;
  86. td->RS->Init(DataCount,RecCount,Encode ? NULL:ValidFlags);
  87. }
  88. td->DataNum=DataNum;
  89. td->Data=Data;
  90. td->Encode=Encode;
  91. td->StartPos=CurPos;
  92. size_t EndPos=CurPos+ThreadDataSize;
  93. if (EndPos>MaxRead || I==ThreadNumber-1)
  94. EndPos=MaxRead;
  95. td->Size=EndPos-CurPos;
  96. CurPos=EndPos;
  97. #ifdef RAR_SMP
  98. if (ThreadNumber>1)
  99. RecThreadPool->AddTask(RecThreadRS,(void*)td);
  100. else
  101. ProcessAreaRS(td);
  102. #else
  103. ProcessAreaRS(td);
  104. #endif
  105. }
  106. #ifdef RAR_SMP
  107. RecThreadPool->WaitDone();
  108. #endif // RAR_SMP
  109. }
  110. void RecVolumes5::ProcessAreaRS(RecRSThreadData *td)
  111. {
  112. uint Count=td->Encode ? RecCount : MissingVolumes;
  113. for (uint I=0;I<Count;I++)
  114. td->RS->UpdateECC(td->DataNum, I, td->Data+td->StartPos, Buf+I*RecBufferSize+td->StartPos, td->Size);
  115. }
  116. bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
  117. {
  118. wchar ArcName[NM];
  119. wcsncpyz(ArcName,Name,ASIZE(ArcName));
  120. wchar *Num=GetVolNumPart(ArcName);
  121. while (Num>ArcName && IsDigit(*(Num-1)))
  122. Num--;
  123. if (Num<=PointToName(ArcName))
  124. return false; // Numeric part is missing or entire volume name is numeric, not possible for RAR or REV volume.
  125. wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName));
  126. wchar FirstVolName[NM];
  127. *FirstVolName=0;
  128. wchar LongestRevName[NM];
  129. *LongestRevName=0;
  130. int64 RecFileSize=0;
  131. FindFile VolFind;
  132. VolFind.SetMask(ArcName);
  133. FindData fd;
  134. uint FoundRecVolumes=0;
  135. while (VolFind.Next(&fd))
  136. {
  137. Wait();
  138. Archive *Vol=new Archive(Cmd);
  139. int ItemPos=-1;
  140. if (!fd.IsDir && Vol->WOpen(fd.Name))
  141. {
  142. if (CmpExt(fd.Name,L"rev"))
  143. {
  144. uint RecNum=ReadHeader(Vol,FoundRecVolumes==0);
  145. if (RecNum!=0)
  146. {
  147. if (FoundRecVolumes==0)
  148. RecFileSize=Vol->FileLength();
  149. ItemPos=RecNum;
  150. FoundRecVolumes++;
  151. if (wcslen(fd.Name)>wcslen(LongestRevName))
  152. wcsncpyz(LongestRevName,fd.Name,ASIZE(LongestRevName));
  153. }
  154. }
  155. else
  156. if (Vol->IsArchive(true) && (Vol->SFXSize>0 || CmpExt(fd.Name,L"rar")))
  157. {
  158. if (!Vol->Volume && !Vol->BrokenHeader)
  159. {
  160. uiMsg(UIERROR_NOTVOLUME,ArcName);
  161. return false;
  162. }
  163. // We work with archive as with raw data file, so we do not want
  164. // to spend time to QOpen I/O redirection.
  165. Vol->QOpenUnload();
  166. Vol->Seek(0,SEEK_SET);
  167. // RAR volume found. Get its number, store the handle in appropriate
  168. // array slot, clean slots in between if we had to grow the array.
  169. wchar *Num=GetVolNumPart(fd.Name);
  170. uint VolNum=0;
  171. for (uint K=1;Num>=fd.Name && IsDigit(*Num);K*=10,Num--)
  172. VolNum+=(*Num-'0')*K;
  173. if (VolNum==0 || VolNum>MaxVolumes)
  174. continue;
  175. size_t CurSize=RecItems.Size();
  176. if (VolNum>CurSize)
  177. {
  178. RecItems.Alloc(VolNum);
  179. for (size_t I=CurSize;I<VolNum;I++)
  180. RecItems[I].f=NULL;
  181. }
  182. ItemPos=VolNum-1;
  183. if (*FirstVolName==0)
  184. VolNameToFirstName(fd.Name,FirstVolName,ASIZE(FirstVolName),true);
  185. }
  186. }
  187. if (ItemPos==-1)
  188. delete Vol; // Skip found file, it is not RAR or REV volume.
  189. else
  190. if ((uint)ItemPos<RecItems.Size()) // Check if found more REV than needed.
  191. {
  192. // Store found RAR or REV volume.
  193. RecVolItem *Item=RecItems+ItemPos;
  194. Item->f=Vol;
  195. Item->New=false;
  196. wcsncpyz(Item->Name,fd.Name,ASIZE(Item->Name));
  197. }
  198. }
  199. if (!Silent || FoundRecVolumes!=0)
  200. uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes);
  201. if (FoundRecVolumes==0)
  202. return false;
  203. // If we did not find even a single .rar volume, create .rar volume name
  204. // based on the longest .rev file name. Use longest .rev, so we have
  205. // enough space for volume number.
  206. if (*FirstVolName==0)
  207. {
  208. SetExt(LongestRevName,L"rar",ASIZE(LongestRevName));
  209. VolNameToFirstName(LongestRevName,FirstVolName,ASIZE(FirstVolName),true);
  210. }
  211. uiMsg(UIMSG_RECVOLCALCCHECKSUM);
  212. MissingVolumes=0;
  213. for (uint I=0;I<TotalCount;I++)
  214. {
  215. RecVolItem *Item=&RecItems[I];
  216. if (Item->f!=NULL)
  217. {
  218. uiMsg(UIMSG_STRING,Item->Name);
  219. uint RevCRC;
  220. CalcFileSum(Item->f,&RevCRC,NULL,MaxUserThreads,INT64NDF,CALCFSUM_CURPOS);
  221. Item->Valid=RevCRC==Item->CRC;
  222. if (!Item->Valid)
  223. {
  224. uiMsg(UIMSG_CHECKSUM,Item->Name);
  225. // Close only corrupt REV volumes here. We'll close and rename corrupt
  226. // RAR volumes later, if we'll know that recovery is possible.
  227. if (I>=DataCount)
  228. {
  229. Item->f->Close();
  230. Item->f=NULL;
  231. FoundRecVolumes--;
  232. }
  233. }
  234. }
  235. if (I<DataCount && (Item->f==NULL || !Item->Valid))
  236. MissingVolumes++;
  237. }
  238. uiMsg(UIMSG_RECVOLMISSING,MissingVolumes);
  239. if (MissingVolumes==0)
  240. {
  241. uiMsg(UIERROR_RECVOLALLEXIST);
  242. return false;
  243. }
  244. if (MissingVolumes>FoundRecVolumes)
  245. {
  246. uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode.
  247. uiMsg(UIERROR_RECVOLCANNOTFIX);
  248. return false;
  249. }
  250. uiMsg(UIMSG_RECONSTRUCTING);
  251. // Create missing and rename bad volumes.
  252. uint64 MaxVolSize=0;
  253. for (uint I=0;I<DataCount;I++)
  254. {
  255. RecVolItem *Item=&RecItems[I];
  256. if (Item->FileSize>MaxVolSize)
  257. MaxVolSize=Item->FileSize;
  258. if (Item->f!=NULL && !Item->Valid)
  259. {
  260. Item->f->Close();
  261. wchar NewName[NM];
  262. wcsncpyz(NewName,Item->Name,ASIZE(NewName));
  263. wcsncatz(NewName,L".bad",ASIZE(NewName));
  264. uiMsg(UIMSG_BADARCHIVE,Item->Name);
  265. uiMsg(UIMSG_RENAMING,Item->Name,NewName);
  266. RenameFile(Item->Name,NewName);
  267. delete Item->f;
  268. Item->f=NULL;
  269. }
  270. if ((Item->New=(Item->f==NULL))==true)
  271. {
  272. wcsncpyz(Item->Name,FirstVolName,ASIZE(Item->Name));
  273. uiMsg(UIMSG_CREATING,Item->Name);
  274. uiMsg(UIEVENT_NEWARCHIVE,Item->Name);
  275. File *NewVol=new File;
  276. bool UserReject;
  277. if (!FileCreate(Cmd,NewVol,Item->Name,ASIZE(Item->Name),&UserReject))
  278. {
  279. if (!UserReject)
  280. ErrHandler.CreateErrorMsg(Item->Name);
  281. ErrHandler.Exit(UserReject ? RARX_USERBREAK:RARX_CREATE);
  282. }
  283. NewVol->Prealloc(Item->FileSize);
  284. Item->f=NewVol;
  285. }
  286. NextVolumeName(FirstVolName,ASIZE(FirstVolName),false);
  287. }
  288. int64 ProcessedSize=0;
  289. int LastPercent=-1;
  290. mprintf(L" ");
  291. // Even though we already preliminary calculated missing volume number,
  292. // let's do it again now, when we have the final and exact information.
  293. MissingVolumes=0;
  294. ValidFlags=new bool[TotalCount];
  295. for (uint I=0;I<TotalCount;I++)
  296. {
  297. ValidFlags[I]=RecItems[I].f!=NULL && !RecItems[I].New;
  298. if (I<DataCount && !ValidFlags[I])
  299. MissingVolumes++;
  300. }
  301. // Size of per file buffer.
  302. RecBufferSize=TotalBufferSize/MissingVolumes;
  303. if ((RecBufferSize&1)==1) // Must be even for our RS16 codec.
  304. RecBufferSize--;
  305. #ifdef USE_SSE
  306. RecBufferSize&=~(SSE_ALIGNMENT-1); // Align for SSE.
  307. #endif
  308. RSCoder16 RS;
  309. if (!RS.Init(DataCount,RecCount,ValidFlags))
  310. {
  311. uiMsg(UIERROR_OPFAILED);
  312. delete[] ValidFlags;
  313. return false; // Should not happen, we check parameter validity above.
  314. }
  315. RealReadBuffer=new byte[RecBufferSize+SSE_ALIGNMENT];
  316. byte *ReadBuf=(byte *)ALIGN_VALUE(RealReadBuffer,SSE_ALIGNMENT);
  317. while (true)
  318. {
  319. Wait();
  320. int MaxRead=0;
  321. for (uint I=0,J=DataCount;I<DataCount;I++)
  322. {
  323. uint VolNum=I;
  324. if (!ValidFlags[I]) // If next RAR volume is missing or invalid.
  325. {
  326. while (!ValidFlags[J]) // Find next valid REV volume.
  327. J++;
  328. VolNum=J++; // Use next valid REV volume data instead of RAR.
  329. }
  330. RecVolItem *Item=RecItems+VolNum;
  331. byte *B=&ReadBuf[0];
  332. int ReadSize=0;
  333. if (Item->f!=NULL && !Item->New)
  334. ReadSize=Item->f->Read(B,RecBufferSize);
  335. if (ReadSize!=RecBufferSize)
  336. memset(B+ReadSize,0,RecBufferSize-ReadSize);
  337. if (ReadSize>MaxRead)
  338. MaxRead=ReadSize;
  339. // We can have volumes of different size. Let's use data chunk
  340. // for largest volume size.
  341. uint DataToProcess=(uint)Min(RecBufferSize,MaxVolSize-ProcessedSize);
  342. ProcessRS(Cmd,I,B,DataToProcess,false);
  343. }
  344. if (MaxRead==0)
  345. break;
  346. for (uint I=0,J=0;I<DataCount;I++)
  347. if (!ValidFlags[I])
  348. {
  349. RecVolItem *Item=RecItems+I;
  350. size_t WriteSize=(size_t)Min(MaxRead,Item->FileSize);
  351. Item->f->Write(Buf+(J++)*RecBufferSize,WriteSize);
  352. Item->FileSize-=WriteSize;
  353. }
  354. int CurPercent=ToPercent(ProcessedSize,RecFileSize);
  355. if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
  356. {
  357. uiProcessProgress("RV",ProcessedSize,RecFileSize);
  358. LastPercent=CurPercent;
  359. }
  360. ProcessedSize+=MaxRead;
  361. }
  362. for (uint I=0;I<TotalCount;I++)
  363. if (RecItems[I].f!=NULL)
  364. RecItems[I].f->Close();
  365. delete[] ValidFlags;
  366. #if !defined(SILENT)
  367. if (!Cmd->DisablePercentage)
  368. mprintf(L"\b\b\b\b100%%");
  369. if (!Silent && !Cmd->DisableDone)
  370. mprintf(St(MDone));
  371. #endif
  372. return true;
  373. }
  374. uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev)
  375. {
  376. const size_t FirstReadSize=REV5_SIGN_SIZE+8;
  377. byte ShortBuf[FirstReadSize];
  378. if (RecFile->Read(ShortBuf,FirstReadSize)!=FirstReadSize)
  379. return 0;
  380. if (memcmp(ShortBuf,REV5_SIGN,REV5_SIGN_SIZE)!=0)
  381. return 0;
  382. uint HeaderSize=RawGet4(ShortBuf+REV5_SIGN_SIZE+4);
  383. if (HeaderSize>0x100000 || HeaderSize<=5)
  384. return 0;
  385. uint BlockCRC=RawGet4(ShortBuf+REV5_SIGN_SIZE);
  386. RawRead Raw(RecFile);
  387. if (Raw.Read(HeaderSize)!=HeaderSize)
  388. return 0;
  389. // Calculate CRC32 of entire header including 4 byte size field.
  390. uint CalcCRC=CRC32(0xffffffff,ShortBuf+REV5_SIGN_SIZE+4,4);
  391. if ((CRC32(CalcCRC,Raw.GetDataPtr(),HeaderSize)^0xffffffff)!=BlockCRC)
  392. return 0;
  393. if (Raw.Get1()!=1) // Version check.
  394. return 0;
  395. DataCount=Raw.Get2();
  396. RecCount=Raw.Get2();
  397. TotalCount=DataCount+RecCount;
  398. uint RecNum=Raw.Get2(); // Number of recovery volume.
  399. if (RecNum>=TotalCount || TotalCount>MaxVolumes)
  400. return 0;
  401. uint RevCRC=Raw.Get4(); // CRC of current REV volume.
  402. if (FirstRev)
  403. {
  404. // If we have read the first valid REV file, init data structures
  405. // using information from REV header.
  406. size_t CurSize=RecItems.Size();
  407. RecItems.Alloc(TotalCount);
  408. for (size_t I=CurSize;I<TotalCount;I++)
  409. RecItems[I].f=NULL;
  410. for (uint I=0;I<DataCount;I++)
  411. {
  412. RecItems[I].FileSize=Raw.Get8();
  413. RecItems[I].CRC=Raw.Get4();
  414. }
  415. }
  416. RecItems[RecNum].CRC=RevCRC; // Assign it here, after allocating RecItems.
  417. return RecNum;
  418. }
  419. void RecVolumes5::Test(RAROptions *Cmd,const wchar *Name)
  420. {
  421. wchar VolName[NM];
  422. wcsncpyz(VolName,Name,ASIZE(VolName));
  423. uint FoundRecVolumes=0;
  424. while (FileExist(VolName))
  425. {
  426. File CurFile;
  427. if (!CurFile.Open(VolName))
  428. {
  429. ErrHandler.OpenErrorMsg(VolName); // It also sets RARX_OPEN.
  430. continue;
  431. }
  432. if (!uiStartFileExtract(VolName,false,true,false))
  433. return;
  434. mprintf(St(MExtrTestFile),VolName);
  435. mprintf(L" ");
  436. bool Valid=false;
  437. uint RecNum=ReadHeader(&CurFile,FoundRecVolumes==0);
  438. if (RecNum!=0)
  439. {
  440. FoundRecVolumes++;
  441. uint RevCRC;
  442. CalcFileSum(&CurFile,&RevCRC,NULL,1,INT64NDF,CALCFSUM_CURPOS|(Cmd->DisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS));
  443. Valid=RevCRC==RecItems[RecNum].CRC;
  444. }
  445. if (Valid)
  446. {
  447. mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk));
  448. }
  449. else
  450. {
  451. uiMsg(UIERROR_CHECKSUM,VolName,VolName);
  452. ErrHandler.SetErrorCode(RARX_CRC);
  453. }
  454. NextVolumeName(VolName,ASIZE(VolName),false);
  455. }
  456. }