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