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