123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- #include "rar.hpp"
- QuickOpen::QuickOpen()
- {
- Buf=NULL;
- Init(NULL,false);
- }
- QuickOpen::~QuickOpen()
- {
- Close();
- delete[] Buf;
- }
- void QuickOpen::Init(Archive *Arc,bool WriteMode)
- {
- if (Arc!=NULL) // Unless called from constructor.
- Close();
- QuickOpen::Arc=Arc;
- QuickOpen::WriteMode=WriteMode;
- ListStart=NULL;
- ListEnd=NULL;
- if (Buf==NULL)
- Buf=new byte[MaxBufSize];
- CurBufSize=0; // Current size of buffered data in write mode.
- Loaded=false;
- }
- void QuickOpen::Close()
- {
- QuickOpenItem *Item=ListStart;
- while (Item!=NULL)
- {
- QuickOpenItem *Next=Item->Next;
- delete[] Item->Header;
- delete Item;
- Item=Next;
- }
- }
- void QuickOpen::Load(uint64 BlockPos)
- {
- if (!Loaded)
- {
- // If loading for the first time, perform additional intialization.
- SeekPos=Arc->Tell();
- UnsyncSeekPos=false;
- int64 SavePos=SeekPos;
- Arc->Seek(BlockPos,SEEK_SET);
- // If BlockPos points to original main header, we'll have the infinite
- // recursion, because ReadHeader() for main header will attempt to load
- // QOpen and call QuickOpen::Load again. If BlockPos points to long chain
- // of other main headers, we'll have multiple recursive calls of this
- // function wasting resources. So we prohibit QOpen temporarily to
- // prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator
- // and QOpenOffset fields, so we cannot use them to prohibit QOpen.
- Arc->SetProhibitQOpen(true);
- size_t ReadSize=Arc->ReadHeader();
- Arc->SetProhibitQOpen(false);
- if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE ||
- !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN))
- {
- Arc->Seek(SavePos,SEEK_SET);
- return;
- }
- QOHeaderPos=Arc->CurBlockPos;
- RawDataStart=Arc->Tell();
- RawDataSize=Arc->SubHead.UnpSize;
- Arc->Seek(SavePos,SEEK_SET);
- Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader.
- }
- if (Arc->SubHead.Encrypted)
- {
- RAROptions *Cmd=Arc->GetRAROptions();
- #ifndef RAR_NOCRYPT
- if (Cmd->Password.IsSet())
- Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt,
- Arc->SubHead.InitV,Arc->SubHead.Lg2Count,
- Arc->SubHead.HashKey,Arc->SubHead.PswCheck);
- else
- #endif
- {
- Loaded=false;
- return;
- }
- }
- RawDataPos=0;
- ReadBufSize=0;
- ReadBufPos=0;
- LastReadHeader.Reset();
- LastReadHeaderPos=0;
- ReadBuffer();
- }
- bool QuickOpen::Read(void *Data,size_t Size,size_t &Result)
- {
- if (!Loaded)
- return false;
- // Find next suitable cached block.
- while (LastReadHeaderPos+LastReadHeader.Size()<=SeekPos)
- if (!ReadNext())
- break;
- if (!Loaded)
- {
- // If something wrong happened, let's set the correct file pointer
- // and stop further quick open processing.
- if (UnsyncSeekPos)
- Arc->File::Seek(SeekPos,SEEK_SET);
- return false;
- }
- if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.Size())
- {
- memcpy(Data,LastReadHeader+size_t(SeekPos-LastReadHeaderPos),Size);
- Result=Size;
- SeekPos+=Size;
- UnsyncSeekPos=true;
- }
- else
- {
- if (UnsyncSeekPos)
- {
- Arc->File::Seek(SeekPos,SEEK_SET);
- UnsyncSeekPos=false;
- }
- int ReadSize=Arc->File::Read(Data,Size);
- if (ReadSize<0)
- {
- Loaded=false;
- return false;
- }
- Result=ReadSize;
- SeekPos+=ReadSize;
- }
-
- return true;
- }
- bool QuickOpen::Seek(int64 Offset,int Method)
- {
- if (!Loaded)
- return false;
- // Normally we process an archive sequentially from beginning to end,
- // so we read quick open data sequentially. But some operations like
- // archive updating involve several passes. So if we detect that file
- // pointer is moved back, we reload quick open data from beginning.
- if (Method==SEEK_SET && (uint64)Offset<SeekPos && (uint64)Offset<LastReadHeaderPos)
- Load(QOHeaderPos);
- if (Method==SEEK_SET)
- SeekPos=Offset;
- if (Method==SEEK_CUR)
- SeekPos+=Offset;
- UnsyncSeekPos=true;
- if (Method==SEEK_END)
- {
- Arc->File::Seek(Offset,SEEK_END);
- SeekPos=Arc->File::Tell();
- UnsyncSeekPos=false;
- }
- return true;
- }
- bool QuickOpen::Tell(int64 *Pos)
- {
- if (!Loaded)
- return false;
- *Pos=SeekPos;
- return true;
- }
- uint QuickOpen::ReadBuffer()
- {
- int64 SavePos=Arc->Tell();
- Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET);
- size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize);
- if (Arc->SubHead.Encrypted)
- SizeToRead &= ~CRYPT_BLOCK_MASK;
- int ReadSize=0;
- if (SizeToRead!=0)
- {
- ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead);
- if (ReadSize<=0)
- ReadSize=0;
- else
- {
- #ifndef RAR_NOCRYPT
- if (Arc->SubHead.Encrypted)
- Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK);
- #endif
- RawDataPos+=ReadSize;
- ReadBufSize+=ReadSize;
- }
- }
- Arc->Seek(SavePos,SEEK_SET);
- return ReadSize;
- }
- // Fill RawRead object from buffer.
- bool QuickOpen::ReadRaw(RawRead &Raw)
- {
- if (MaxBufSize-ReadBufPos<0x100) // We are close to end of buffer.
- {
- // Ensure that we have enough data to read CRC and header size.
- size_t DataLeft=ReadBufSize-ReadBufPos;
- memcpy(Buf,Buf+ReadBufPos,DataLeft);
- ReadBufPos=0;
- ReadBufSize=DataLeft;
- ReadBuffer();
- }
- const size_t FirstReadSize=7;
- if (ReadBufPos+FirstReadSize>ReadBufSize)
- return false;
- Raw.Read(Buf+ReadBufPos,FirstReadSize);
- ReadBufPos+=FirstReadSize;
- uint SavedCRC=Raw.Get4();
- uint SizeBytes=Raw.GetVSize(4);
- uint64 BlockSize=Raw.GetV();
- int SizeToRead=int(BlockSize);
- SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
- if (SizeToRead<0 || SizeBytes==0 || BlockSize==0)
- {
- Loaded=false; // Invalid data.
- return false;
- }
- // If rest of block data crosses Buf boundary, read it in loop.
- while (SizeToRead>0)
- {
- size_t DataLeft=ReadBufSize-ReadBufPos;
- size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead);
- Raw.Read(Buf+ReadBufPos,CurSizeToRead);
- ReadBufPos+=CurSizeToRead;
- SizeToRead-=int(CurSizeToRead);
- if (SizeToRead>0) // We read the entire buffer and still need more data.
- {
- ReadBufPos=0;
- ReadBufSize=0;
- if (ReadBuffer()==0)
- return false;
- }
- }
- return SavedCRC==Raw.GetCRC50();
- }
- // Read next cached header.
- bool QuickOpen::ReadNext()
- {
- RawRead Raw(NULL);
- if (!ReadRaw(Raw)) // Read internal quick open header preceding stored block.
- return false;
- uint Flags=(uint)Raw.GetV();
- uint64 Offset=Raw.GetV();
- size_t HeaderSize=(size_t)Raw.GetV();
- if (HeaderSize>MAX_HEADER_SIZE_RAR5)
- return false;
- LastReadHeader.Alloc(HeaderSize);
- Raw.GetB(&LastReadHeader[0],HeaderSize);
- // Calculate the absolute position as offset from quick open service header.
- LastReadHeaderPos=QOHeaderPos-Offset;
- return true;
- }
|