static const uint MaxVolumes=65535; // We select this limit arbitrarily, to prevent user creating too many // rev files by mistake. #define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files. RecVolumes5::RecVolumes5(RAROptions *Cmd,bool TestOnly) { RealBuf=NULL; RealReadBuffer=NULL; DataCount=0; RecCount=0; TotalCount=0; RecBufferSize=0; #ifdef RAR_SMP MaxUserThreads=Cmd->Threads; #else MaxUserThreads=1; #endif ThreadData=new RecRSThreadData[MaxUserThreads]; for (uint I=0;IRecRSPtr->ProcessAreaRS(td); } #endif void RecVolumes5::ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode) { /* RSCoder16 RS; RS.Init(DataCount,RecCount,Encode ? NULL:ValidFlags); uint Count=Encode ? RecCount : MissingVolumes; for (uint I=0;IRS==NULL) { td->RS=new RSCoder16; td->RS->Init(DataCount,RecCount,Encode ? NULL:ValidFlags); } td->DataNum=DataNum; td->Data=Data; td->Encode=Encode; td->StartPos=CurPos; size_t EndPos=CurPos+ThreadDataSize; if (EndPos>MaxRead || I==ThreadNumber-1) EndPos=MaxRead; td->Size=EndPos-CurPos; CurPos=EndPos; #ifdef RAR_SMP if (ThreadNumber>1) RecThreadPool->AddTask(RecThreadRS,(void*)td); else ProcessAreaRS(td); #else ProcessAreaRS(td); #endif } #ifdef RAR_SMP RecThreadPool->WaitDone(); #endif // RAR_SMP } void RecVolumes5::ProcessAreaRS(RecRSThreadData *td) { uint Count=td->Encode ? RecCount : MissingVolumes; for (uint I=0;IRS->UpdateECC(td->DataNum, I, td->Data+td->StartPos, Buf+I*RecBufferSize+td->StartPos, td->Size); } bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) { wchar ArcName[NM]; wcsncpyz(ArcName,Name,ASIZE(ArcName)); wchar *Num=GetVolNumPart(ArcName); while (Num>ArcName && IsDigit(*(Num-1))) Num--; if (Num<=PointToName(ArcName)) return false; // Numeric part is missing or entire volume name is numeric, not possible for RAR or REV volume. wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName)); wchar FirstVolName[NM]; *FirstVolName=0; wchar LongestRevName[NM]; *LongestRevName=0; int64 RecFileSize=0; FindFile VolFind; VolFind.SetMask(ArcName); FindData fd; uint FoundRecVolumes=0; while (VolFind.Next(&fd)) { Wait(); Archive *Vol=new Archive(Cmd); int ItemPos=-1; if (!fd.IsDir && Vol->WOpen(fd.Name)) { if (CmpExt(fd.Name,L"rev")) { uint RecNum=ReadHeader(Vol,FoundRecVolumes==0); if (RecNum!=0) { if (FoundRecVolumes==0) RecFileSize=Vol->FileLength(); ItemPos=RecNum; FoundRecVolumes++; if (wcslen(fd.Name)>wcslen(LongestRevName)) wcsncpyz(LongestRevName,fd.Name,ASIZE(LongestRevName)); } } else if (Vol->IsArchive(true) && (Vol->SFXSize>0 || CmpExt(fd.Name,L"rar"))) { if (!Vol->Volume && !Vol->BrokenHeader) { uiMsg(UIERROR_NOTVOLUME,ArcName); return false; } // We work with archive as with raw data file, so we do not want // to spend time to QOpen I/O redirection. Vol->QOpenUnload(); Vol->Seek(0,SEEK_SET); // RAR volume found. Get its number, store the handle in appropriate // array slot, clean slots in between if we had to grow the array. wchar *Num=GetVolNumPart(fd.Name); uint VolNum=0; for (uint K=1;Num>=fd.Name && IsDigit(*Num);K*=10,Num--) VolNum+=(*Num-'0')*K; if (VolNum==0 || VolNum>MaxVolumes) continue; size_t CurSize=RecItems.Size(); if (VolNum>CurSize) { RecItems.Alloc(VolNum); for (size_t I=CurSize;If=Vol; Item->New=false; wcsncpyz(Item->Name,fd.Name,ASIZE(Item->Name)); } } if (!Silent || FoundRecVolumes!=0) uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes); if (FoundRecVolumes==0) return false; // If we did not find even a single .rar volume, create .rar volume name // based on the longest .rev file name. Use longest .rev, so we have // enough space for volume number. if (*FirstVolName==0) { SetExt(LongestRevName,L"rar",ASIZE(LongestRevName)); VolNameToFirstName(LongestRevName,FirstVolName,ASIZE(FirstVolName),true); } uiMsg(UIMSG_RECVOLCALCCHECKSUM); MissingVolumes=0; for (uint I=0;If!=NULL) { uiMsg(UIMSG_STRING,Item->Name); uint RevCRC; CalcFileSum(Item->f,&RevCRC,NULL,MaxUserThreads,INT64NDF,CALCFSUM_CURPOS); Item->Valid=RevCRC==Item->CRC; if (!Item->Valid) { uiMsg(UIMSG_CHECKSUM,Item->Name); // Close only corrupt REV volumes here. We'll close and rename corrupt // RAR volumes later, if we'll know that recovery is possible. if (I>=DataCount) { Item->f->Close(); Item->f=NULL; FoundRecVolumes--; } } } if (If==NULL || !Item->Valid)) MissingVolumes++; } uiMsg(UIMSG_RECVOLMISSING,MissingVolumes); if (MissingVolumes==0) { uiMsg(UIERROR_RECVOLALLEXIST); return false; } if (MissingVolumes>FoundRecVolumes) { uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode. uiMsg(UIERROR_RECVOLCANNOTFIX); return false; } uiMsg(UIMSG_RECONSTRUCTING); // Create missing and rename bad volumes. uint64 MaxVolSize=0; for (uint I=0;IFileSize>MaxVolSize) MaxVolSize=Item->FileSize; if (Item->f!=NULL && !Item->Valid) { Item->f->Close(); wchar NewName[NM]; wcsncpyz(NewName,Item->Name,ASIZE(NewName)); wcsncatz(NewName,L".bad",ASIZE(NewName)); uiMsg(UIMSG_BADARCHIVE,Item->Name); uiMsg(UIMSG_RENAMING,Item->Name,NewName); RenameFile(Item->Name,NewName); delete Item->f; Item->f=NULL; } if ((Item->New=(Item->f==NULL))==true) { wcsncpyz(Item->Name,FirstVolName,ASIZE(Item->Name)); uiMsg(UIMSG_CREATING,Item->Name); uiMsg(UIEVENT_NEWARCHIVE,Item->Name); File *NewVol=new File; bool UserReject; if (!FileCreate(Cmd,NewVol,Item->Name,ASIZE(Item->Name),&UserReject)) { if (!UserReject) ErrHandler.CreateErrorMsg(Item->Name); ErrHandler.Exit(UserReject ? RARX_USERBREAK:RARX_CREATE); } NewVol->Prealloc(Item->FileSize); Item->f=NewVol; } NextVolumeName(FirstVolName,ASIZE(FirstVolName),false); } int64 ProcessedSize=0; int LastPercent=-1; mprintf(L" "); // Even though we already preliminary calculated missing volume number, // let's do it again now, when we have the final and exact information. MissingVolumes=0; ValidFlags=new bool[TotalCount]; for (uint I=0;If!=NULL && !Item->New) ReadSize=Item->f->Read(B,RecBufferSize); if (ReadSize!=RecBufferSize) memset(B+ReadSize,0,RecBufferSize-ReadSize); if (ReadSize>MaxRead) MaxRead=ReadSize; // We can have volumes of different size. Let's use data chunk // for largest volume size. uint DataToProcess=(uint)Min(RecBufferSize,MaxVolSize-ProcessedSize); ProcessRS(Cmd,I,B,DataToProcess,false); } if (MaxRead==0) break; for (uint I=0,J=0;IFileSize); Item->f->Write(Buf+(J++)*RecBufferSize,WriteSize); Item->FileSize-=WriteSize; } int CurPercent=ToPercent(ProcessedSize,RecFileSize); if (!Cmd->DisablePercentage && CurPercent!=LastPercent) { uiProcessProgress("RV",ProcessedSize,RecFileSize); LastPercent=CurPercent; } ProcessedSize+=MaxRead; } for (uint I=0;IClose(); delete[] ValidFlags; #if !defined(SILENT) if (!Cmd->DisablePercentage) mprintf(L"\b\b\b\b100%%"); if (!Silent && !Cmd->DisableDone) mprintf(St(MDone)); #endif return true; } uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev) { const size_t FirstReadSize=REV5_SIGN_SIZE+8; byte ShortBuf[FirstReadSize]; if (RecFile->Read(ShortBuf,FirstReadSize)!=FirstReadSize) return 0; if (memcmp(ShortBuf,REV5_SIGN,REV5_SIGN_SIZE)!=0) return 0; uint HeaderSize=RawGet4(ShortBuf+REV5_SIGN_SIZE+4); if (HeaderSize>0x100000 || HeaderSize<=5) return 0; uint BlockCRC=RawGet4(ShortBuf+REV5_SIGN_SIZE); RawRead Raw(RecFile); if (Raw.Read(HeaderSize)!=HeaderSize) return 0; // Calculate CRC32 of entire header including 4 byte size field. uint CalcCRC=CRC32(0xffffffff,ShortBuf+REV5_SIGN_SIZE+4,4); if ((CRC32(CalcCRC,Raw.GetDataPtr(),HeaderSize)^0xffffffff)!=BlockCRC) return 0; if (Raw.Get1()!=1) // Version check. return 0; DataCount=Raw.Get2(); RecCount=Raw.Get2(); TotalCount=DataCount+RecCount; uint RecNum=Raw.Get2(); // Number of recovery volume. if (RecNum>=TotalCount || TotalCount>MaxVolumes) return 0; uint RevCRC=Raw.Get4(); // CRC of current REV volume. if (FirstRev) { // If we have read the first valid REV file, init data structures // using information from REV header. size_t CurSize=RecItems.Size(); RecItems.Alloc(TotalCount); for (size_t I=CurSize;IDisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS)); Valid=RevCRC==RecItems[RecNum].CRC; } if (Valid) { mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk)); } else { uiMsg(UIERROR_CHECKSUM,VolName,VolName); ErrHandler.SetErrorCode(RARX_CRC); } NextVolumeName(VolName,ASIZE(VolName),false); } }