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