1 #include "rar.hpp"
2 
QuickOpen()3 QuickOpen::QuickOpen()
4 {
5   Buf=NULL;
6   Init(NULL,false);
7 }
8 
9 
~QuickOpen()10 QuickOpen::~QuickOpen()
11 {
12   Close();
13   delete[] Buf;
14 }
15 
16 
Init(Archive * Arc,bool WriteMode)17 void QuickOpen::Init(Archive *Arc,bool WriteMode)
18 {
19   if (Arc!=NULL) // Unless called from constructor.
20     Close();
21 
22   QuickOpen::Arc=Arc;
23   QuickOpen::WriteMode=WriteMode;
24 
25   ListStart=NULL;
26   ListEnd=NULL;
27 
28   if (Buf==NULL)
29     Buf=new byte[MaxBufSize];
30 
31   CurBufSize=0; // Current size of buffered data in write mode.
32 
33   Loaded=false;
34 }
35 
36 
Close()37 void QuickOpen::Close()
38 {
39   QuickOpenItem *Item=ListStart;
40   while (Item!=NULL)
41   {
42     QuickOpenItem *Next=Item->Next;
43     delete[] Item->Header;
44     delete Item;
45     Item=Next;
46   }
47 }
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
Load(uint64 BlockPos)62 void QuickOpen::Load(uint64 BlockPos)
63 {
64   if (!Loaded)
65   {
66     // If loading for the first time, perform additional intialization.
67     SeekPos=Arc->Tell();
68     UnsyncSeekPos=false;
69 
70     int64 SavePos=SeekPos;
71     Arc->Seek(BlockPos,SEEK_SET);
72 
73     // If BlockPos points to original main header, we'll have the infinite
74     // recursion, because ReadHeader() for main header will attempt to load
75     // QOpen and call QuickOpen::Load again. If BlockPos points to long chain
76     // of other main headers, we'll have multiple recursive calls of this
77     // function wasting resources. So we prohibit QOpen temporarily to
78     // prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator
79     // and QOpenOffset fields, so we cannot use them to prohibit QOpen.
80     Arc->SetProhibitQOpen(true);
81     size_t ReadSize=Arc->ReadHeader();
82     Arc->SetProhibitQOpen(false);
83 
84     if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE ||
85         !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN))
86     {
87       Arc->Seek(SavePos,SEEK_SET);
88       return;
89     }
90     QOHeaderPos=Arc->CurBlockPos;
91     RawDataStart=Arc->Tell();
92     RawDataSize=Arc->SubHead.UnpSize;
93     Arc->Seek(SavePos,SEEK_SET);
94 
95     Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader.
96   }
97 
98   if (Arc->SubHead.Encrypted)
99   {
100     RAROptions *Cmd=Arc->GetRAROptions();
101 #ifndef RAR_NOCRYPT
102     if (Cmd->Password.IsSet())
103       Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt,
104                          Arc->SubHead.InitV,Arc->SubHead.Lg2Count,
105                          Arc->SubHead.HashKey,Arc->SubHead.PswCheck);
106     else
107 #endif
108     {
109       Loaded=false;
110       return;
111     }
112   }
113 
114   RawDataPos=0;
115   ReadBufSize=0;
116   ReadBufPos=0;
117   LastReadHeader.Reset();
118   LastReadHeaderPos=0;
119 
120   ReadBuffer();
121 }
122 
123 
Read(void * Data,size_t Size,size_t & Result)124 bool QuickOpen::Read(void *Data,size_t Size,size_t &Result)
125 {
126   if (!Loaded)
127     return false;
128   // Find next suitable cached block.
129   while (LastReadHeaderPos+LastReadHeader.Size()<=SeekPos)
130     if (!ReadNext())
131       break;
132   if (!Loaded)
133   {
134     // If something wrong happened, let's set the correct file pointer
135     // and stop further quick open processing.
136     if (UnsyncSeekPos)
137       Arc->File::Seek(SeekPos,SEEK_SET);
138     return false;
139   }
140 
141   if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.Size())
142   {
143     memcpy(Data,LastReadHeader+size_t(SeekPos-LastReadHeaderPos),Size);
144     Result=Size;
145     SeekPos+=Size;
146     UnsyncSeekPos=true;
147   }
148   else
149   {
150     if (UnsyncSeekPos)
151     {
152       Arc->File::Seek(SeekPos,SEEK_SET);
153       UnsyncSeekPos=false;
154     }
155     int ReadSize=Arc->File::Read(Data,Size);
156     if (ReadSize<0)
157     {
158       Loaded=false;
159       return false;
160     }
161     Result=ReadSize;
162     SeekPos+=ReadSize;
163   }
164 
165   return true;
166 }
167 
168 
Seek(int64 Offset,int Method)169 bool QuickOpen::Seek(int64 Offset,int Method)
170 {
171   if (!Loaded)
172     return false;
173 
174   // Normally we process an archive sequentially from beginning to end,
175   // so we read quick open data sequentially. But some operations like
176   // archive updating involve several passes. So if we detect that file
177   // pointer is moved back, we reload quick open data from beginning.
178   if (Method==SEEK_SET && (uint64)Offset<SeekPos && (uint64)Offset<LastReadHeaderPos)
179     Load(QOHeaderPos);
180 
181   if (Method==SEEK_SET)
182     SeekPos=Offset;
183   if (Method==SEEK_CUR)
184     SeekPos+=Offset;
185   UnsyncSeekPos=true;
186 
187   if (Method==SEEK_END)
188   {
189     Arc->File::Seek(Offset,SEEK_END);
190     SeekPos=Arc->File::Tell();
191     UnsyncSeekPos=false;
192   }
193   return true;
194 }
195 
196 
Tell(int64 * Pos)197 bool QuickOpen::Tell(int64 *Pos)
198 {
199   if (!Loaded)
200     return false;
201   *Pos=SeekPos;
202   return true;
203 }
204 
205 
ReadBuffer()206 uint QuickOpen::ReadBuffer()
207 {
208   int64 SavePos=Arc->Tell();
209   Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET);
210   size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize);
211   if (Arc->SubHead.Encrypted)
212     SizeToRead &= ~CRYPT_BLOCK_MASK;
213   int ReadSize=0;
214   if (SizeToRead!=0)
215   {
216     ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead);
217     if (ReadSize<=0)
218       ReadSize=0;
219     else
220     {
221 #ifndef RAR_NOCRYPT
222       if (Arc->SubHead.Encrypted)
223         Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK);
224 #endif
225       RawDataPos+=ReadSize;
226       ReadBufSize+=ReadSize;
227     }
228   }
229   Arc->Seek(SavePos,SEEK_SET);
230   return ReadSize;
231 }
232 
233 
234 // Fill RawRead object from buffer.
ReadRaw(RawRead & Raw)235 bool QuickOpen::ReadRaw(RawRead &Raw)
236 {
237   if (MaxBufSize-ReadBufPos<0x100) // We are close to end of buffer.
238   {
239     // Ensure that we have enough data to read CRC and header size.
240     size_t DataLeft=ReadBufSize-ReadBufPos;
241     memcpy(Buf,Buf+ReadBufPos,DataLeft);
242     ReadBufPos=0;
243     ReadBufSize=DataLeft;
244     ReadBuffer();
245   }
246   const size_t FirstReadSize=7;
247   if (ReadBufPos+FirstReadSize>ReadBufSize)
248     return false;
249   Raw.Read(Buf+ReadBufPos,FirstReadSize);
250   ReadBufPos+=FirstReadSize;
251 
252   uint SavedCRC=Raw.Get4();
253   uint SizeBytes=Raw.GetVSize(4);
254   uint64 BlockSize=Raw.GetV();
255   int SizeToRead=int(BlockSize);
256   SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
257   if (SizeToRead<0 || SizeBytes==0 || BlockSize==0)
258   {
259     Loaded=false; // Invalid data.
260     return false;
261   }
262 
263   // If rest of block data crosses Buf boundary, read it in loop.
264   while (SizeToRead>0)
265   {
266     size_t DataLeft=ReadBufSize-ReadBufPos;
267     size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead);
268     Raw.Read(Buf+ReadBufPos,CurSizeToRead);
269     ReadBufPos+=CurSizeToRead;
270     SizeToRead-=int(CurSizeToRead);
271     if (SizeToRead>0) // We read the entire buffer and still need more data.
272     {
273       ReadBufPos=0;
274       ReadBufSize=0;
275       if (ReadBuffer()==0)
276         return false;
277     }
278   }
279 
280   return SavedCRC==Raw.GetCRC50();
281 }
282 
283 
284 // Read next cached header.
ReadNext()285 bool QuickOpen::ReadNext()
286 {
287   RawRead Raw(NULL);
288   if (!ReadRaw(Raw)) // Read internal quick open header preceding stored block.
289     return false;
290   uint Flags=(uint)Raw.GetV();
291   uint64 Offset=Raw.GetV();
292   size_t HeaderSize=(size_t)Raw.GetV();
293   if (HeaderSize>MAX_HEADER_SIZE_RAR5)
294     return false;
295   LastReadHeader.Alloc(HeaderSize);
296   Raw.GetB(&LastReadHeader[0],HeaderSize);
297   // Calculate the absolute position as offset from quick open service header.
298   LastReadHeaderPos=QOHeaderPos-Offset;
299   return true;
300 }
301