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