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