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