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