1 // Archive/CabIn.cpp
2
3 #include "StdAfx.h"
4
5 // #include <stdio.h>
6
7 #include "../../../../C/CpuArch.h"
8
9 #include "../../Common/LimitedStreams.h"
10 #include "../../Common/StreamUtils.h"
11
12 #include "CabIn.h"
13
14 #define Get16(p) GetUi16(p)
15 #define Get32(p) GetUi32(p)
16
17 namespace NArchive {
18 namespace NCab {
19
20 struct CUnexpectedEndException {};
21
Skip(unsigned size)22 void CInArchive::Skip(unsigned size)
23 {
24 if (_inBuffer.Skip(size) != size)
25 throw CUnexpectedEndException();
26 }
27
Read(Byte * data,unsigned size)28 void CInArchive::Read(Byte *data, unsigned size)
29 {
30 if (_inBuffer.ReadBytes(data, size) != size)
31 throw CUnexpectedEndException();
32 }
33
ReadName(AString & s)34 void CInArchive::ReadName(AString &s)
35 {
36 for (size_t i = 0; i < ((size_t)1 << 13); i++)
37 {
38 Byte b;
39 if (!_inBuffer.ReadByte(b))
40 throw CUnexpectedEndException();
41 if (b == 0)
42 {
43 s.SetFrom((const char *)(const Byte *)_tempBuf, (unsigned)i);
44 return;
45 }
46 if (_tempBuf.Size() == i)
47 _tempBuf.ChangeSize_KeepData(i * 2, i);
48 _tempBuf[i] = b;
49 }
50
51 for (;;)
52 {
53 Byte b;
54 if (!_inBuffer.ReadByte(b))
55 throw CUnexpectedEndException();
56 if (b == 0)
57 break;
58 }
59
60 ErrorInNames = true;
61 s = "[ERROR-LONG-PATH]";
62 }
63
ReadOtherArc(COtherArc & oa)64 void CInArchive::ReadOtherArc(COtherArc &oa)
65 {
66 ReadName(oa.FileName);
67 ReadName(oa.DiskName);
68 }
69
70
71 struct CSignatureFinder
72 {
73 Byte *Buf;
74 UInt32 Pos;
75 UInt32 End;
76 const Byte *Signature;
77 UInt32 SignatureSize;
78
79 UInt32 _headerSize;
80 UInt32 _alignSize;
81 UInt32 _bufUseCapacity;
82
83 const UInt64 *SearchLimit;
84 ISequentialInStream *Stream;
85 UInt64 Processed; // Global offset of start of Buf
86
GetTotalCapacityNArchive::NCab::CSignatureFinder87 UInt32 GetTotalCapacity(UInt32 basicSize, UInt32 headerSize)
88 {
89 _headerSize = headerSize;
90 for (_alignSize = (1 << 5); _alignSize < _headerSize; _alignSize <<= 1);
91 _bufUseCapacity = basicSize + _alignSize;
92 return _bufUseCapacity + 16;
93 }
94
95 /*
96 returns:
97 S_OK - signature found (at Pos)
98 S_FALSE - signature not found
99 */
100 HRESULT Find();
101 };
102
103
Find()104 HRESULT CSignatureFinder::Find()
105 {
106 for (;;)
107 {
108 Buf[End] = Signature[0]; // it's for fast search;
109
110 while (End - Pos >= _headerSize)
111 {
112 const Byte *p = Buf + Pos;
113 const Byte b = Signature[0];
114 for (;;)
115 {
116 if (*p == b) { break; } p++;
117 if (*p == b) { break; } p++;
118 }
119 Pos = (UInt32)(p - Buf);
120 if (End - Pos < _headerSize)
121 {
122 Pos = End - _headerSize + 1;
123 break;
124 }
125 UInt32 i;
126 for (i = 1; i < SignatureSize && p[i] == Signature[i]; i++);
127 if (i == SignatureSize)
128 return S_OK;
129 Pos++;
130 }
131
132 if (Pos >= _alignSize)
133 {
134 const UInt32 num = (Pos & ~(_alignSize - 1));
135 Processed += num;
136 Pos -= num;
137 End -= num;
138 memmove(Buf, Buf + num, End);
139 }
140 UInt32 rem = _bufUseCapacity - End;
141 if (SearchLimit)
142 {
143 if (Processed + Pos > *SearchLimit)
144 return S_FALSE;
145 const UInt64 rem2 = *SearchLimit - (Processed + End) + _headerSize;
146 if (rem > rem2)
147 rem = (UInt32)rem2;
148 }
149
150 UInt32 processedSize;
151 if (Processed == 0 && rem == _bufUseCapacity - _headerSize)
152 rem -= _alignSize; // to make reads more aligned.
153 RINOK(Stream->Read(Buf + End, rem, &processedSize))
154 if (processedSize == 0)
155 return S_FALSE;
156 End += processedSize;
157 }
158 }
159
160
Parse(const Byte * p)161 bool CInArcInfo::Parse(const Byte *p)
162 {
163 if (Get32(p + 0x0C) != 0 ||
164 Get32(p + 0x14) != 0)
165 return false;
166 Size = Get32(p + 8);
167 if (Size < 36)
168 return false;
169 Flags = Get16(p + 0x1E);
170 if (Flags > 7)
171 return false;
172 FileHeadersOffset = Get32(p + 0x10);
173 if (FileHeadersOffset != 0 && FileHeadersOffset > Size)
174 return false;
175 VersionMinor = p[0x18];
176 VersionMajor = p[0x19];
177 NumFolders = Get16(p + 0x1A);
178 NumFiles = Get16(p + 0x1C);
179 return true;
180 }
181
182
Open2(CDatabaseEx & db,const UInt64 * searchHeaderSizeLimit)183 HRESULT CInArchive::Open2(CDatabaseEx &db, const UInt64 *searchHeaderSizeLimit)
184 {
185 IsArc = false;
186 ErrorInNames = false;
187 UnexpectedEnd = false;
188 HeaderError = false;
189
190 db.Clear();
191 RINOK(InStream_GetPos(db.Stream, db.StartPosition))
192 // UInt64 temp = db.StartPosition;
193
194 CByteBuffer buffer;
195 CInArcInfo &ai = db.ArcInfo;
196 UInt64 startInBuf = 0;
197
198 CLimitedSequentialInStream *limitedStreamSpec = NULL;
199 CMyComPtr<ISequentialInStream> limitedStream;
200
201 // for (int iii = 0; iii < 10000; iii++)
202 {
203 // db.StartPosition = temp; RINOK(InStream_SeekSet(db.Stream, db.StartPosition))
204
205 const UInt32 kMainHeaderSize = 32;
206 Byte header[kMainHeaderSize];
207 const UInt32 kBufSize = 1 << 15;
208 RINOK(ReadStream_FALSE(db.Stream, header, kMainHeaderSize))
209 if (memcmp(header, NHeader::kMarker, NHeader::kMarkerSize) == 0 && ai.Parse(header))
210 {
211 limitedStreamSpec = new CLimitedSequentialInStream;
212 limitedStream = limitedStreamSpec;
213 limitedStreamSpec->SetStream(db.Stream);
214 limitedStreamSpec->Init(ai.Size - NHeader::kMarkerSize);
215 buffer.Alloc(kBufSize);
216 memcpy(buffer, header, kMainHeaderSize);
217 UInt32 numProcessedBytes;
218 RINOK(limitedStream->Read(buffer + kMainHeaderSize, kBufSize - kMainHeaderSize, &numProcessedBytes))
219 _inBuffer.SetBuf(buffer, (UInt32)kBufSize, kMainHeaderSize + numProcessedBytes, kMainHeaderSize);
220 }
221 else
222 {
223 if (searchHeaderSizeLimit && *searchHeaderSizeLimit == 0)
224 return S_FALSE;
225
226 CSignatureFinder finder;
227
228 finder.Stream = db.Stream;
229 finder.Signature = NHeader::kMarker;
230 finder.SignatureSize = NHeader::kMarkerSize;
231 finder.SearchLimit = searchHeaderSizeLimit;
232
233 buffer.Alloc(finder.GetTotalCapacity(kBufSize, kMainHeaderSize));
234 finder.Buf = buffer;
235
236 memcpy(buffer, header, kMainHeaderSize);
237 finder.Processed = db.StartPosition;
238 finder.End = kMainHeaderSize;
239 finder.Pos = 1;
240
241 for (;;)
242 {
243 RINOK(finder.Find())
244 if (ai.Parse(finder.Buf + finder.Pos))
245 {
246 db.StartPosition = finder.Processed + finder.Pos;
247 limitedStreamSpec = new CLimitedSequentialInStream;
248 limitedStreamSpec->SetStream(db.Stream);
249 limitedStream = limitedStreamSpec;
250 const UInt32 remInFinder = finder.End - finder.Pos;
251 if (ai.Size <= remInFinder)
252 {
253 limitedStreamSpec->Init(0);
254 finder.End = finder.Pos + ai.Size;
255 }
256 else
257 limitedStreamSpec->Init(ai.Size - remInFinder);
258
259 startInBuf = finder.Pos;
260 _inBuffer.SetBuf(buffer, (UInt32)kBufSize, finder.End, finder.Pos + kMainHeaderSize);
261 break;
262 }
263 finder.Pos++;
264 }
265 }
266 }
267
268 IsArc = true;
269
270 _inBuffer.SetStream(limitedStream);
271 if (_tempBuf.Size() == 0)
272 _tempBuf.Alloc(1 << 12);
273
274 Byte p[16];
275 const unsigned nextSize = 4 + (ai.ReserveBlockPresent() ? 4 : 0);
276 Read(p, nextSize);
277 ai.SetID = Get16(p);
278 ai.CabinetNumber = Get16(p + 2);
279
280 if (ai.ReserveBlockPresent())
281 {
282 ai.PerCabinet_AreaSize = Get16(p + 4);
283 ai.PerFolder_AreaSize = p[6];
284 ai.PerDataBlock_AreaSize = p[7];
285 Skip(ai.PerCabinet_AreaSize);
286 }
287
288 if (ai.IsTherePrev()) ReadOtherArc(ai.PrevArc);
289 if (ai.IsThereNext()) ReadOtherArc(ai.NextArc);
290
291 UInt32 i;
292
293 db.Folders.ClearAndReserve(ai.NumFolders);
294
295 for (i = 0; i < ai.NumFolders; i++)
296 {
297 Read(p, 8);
298 CFolder folder;
299 folder.DataStart = Get32(p);
300 folder.NumDataBlocks = Get16(p + 4);
301 folder.MethodMajor = p[6];
302 folder.MethodMinor = p[7];
303 Skip(ai.PerFolder_AreaSize);
304 db.Folders.AddInReserved(folder);
305 }
306
307 // for (int iii = 0; iii < 10000; iii++) {
308
309 if (_inBuffer.GetProcessedSize() - startInBuf != ai.FileHeadersOffset)
310 {
311 // printf("\n!!! Seek Error !!!!\n");
312 // fflush(stdout);
313 RINOK(InStream_SeekSet(db.Stream, db.StartPosition + ai.FileHeadersOffset))
314 limitedStreamSpec->Init(ai.Size - ai.FileHeadersOffset);
315 _inBuffer.Init();
316 }
317
318 db.Items.ClearAndReserve(ai.NumFiles);
319
320 for (i = 0; i < ai.NumFiles; i++)
321 {
322 Read(p, 16);
323 CItem &item = db.Items.AddNewInReserved();
324 item.Size = Get32(p);
325 item.Offset = Get32(p + 4);
326 item.FolderIndex = Get16(p + 8);
327 const UInt16 pureDate = Get16(p + 10);
328 const UInt16 pureTime = Get16(p + 12);
329 item.Time = (((UInt32)pureDate << 16)) | pureTime;
330 item.Attributes = Get16(p + 14);
331
332 ReadName(item.Name);
333
334 if (item.GetFolderIndex(db.Folders.Size()) >= (int)db.Folders.Size())
335 {
336 HeaderError = true;
337 return S_FALSE;
338 }
339 }
340
341 // }
342
343 return S_OK;
344 }
345
346
Open(CDatabaseEx & db,const UInt64 * searchHeaderSizeLimit)347 HRESULT CInArchive::Open(CDatabaseEx &db, const UInt64 *searchHeaderSizeLimit)
348 {
349 try
350 {
351 return Open2(db, searchHeaderSizeLimit);
352 }
353 catch(const CInBufferException &e) { return e.ErrorCode; }
354 catch(CUnexpectedEndException &) { UnexpectedEnd = true; return S_FALSE; }
355 }
356
357
358
359 #define RINOZ(x) { int _tt_ = (x); if (_tt_ != 0) return _tt_; }
360
CompareMvItems(const CMvItem * p1,const CMvItem * p2,void * param)361 static int CompareMvItems(const CMvItem *p1, const CMvItem *p2, void *param)
362 {
363 const CMvDatabaseEx &mvDb = *(const CMvDatabaseEx *)param;
364 const CDatabaseEx &db1 = mvDb.Volumes[p1->VolumeIndex];
365 const CDatabaseEx &db2 = mvDb.Volumes[p2->VolumeIndex];
366 const CItem &item1 = db1.Items[p1->ItemIndex];
367 const CItem &item2 = db2.Items[p2->ItemIndex];
368 const bool isDir1 = item1.IsDir();
369 const bool isDir2 = item2.IsDir();
370 if (isDir1 && !isDir2) return -1;
371 if (isDir2 && !isDir1) return 1;
372 const int f1 = mvDb.GetFolderIndex(p1);
373 const int f2 = mvDb.GetFolderIndex(p2);
374 RINOZ(MyCompare(f1, f2))
375 RINOZ(MyCompare(item1.Offset, item2.Offset))
376 RINOZ(MyCompare(item1.Size, item2.Size))
377 RINOZ(MyCompare(p1->VolumeIndex, p2->VolumeIndex))
378 return MyCompare(p1->ItemIndex, p2->ItemIndex);
379 }
380
381
AreItemsEqual(unsigned i1,unsigned i2)382 bool CMvDatabaseEx::AreItemsEqual(unsigned i1, unsigned i2)
383 {
384 const CMvItem *p1 = &Items[i1];
385 const CMvItem *p2 = &Items[i2];
386 const CDatabaseEx &db1 = Volumes[p1->VolumeIndex];
387 const CDatabaseEx &db2 = Volumes[p2->VolumeIndex];
388 const CItem &item1 = db1.Items[p1->ItemIndex];
389 const CItem &item2 = db2.Items[p2->ItemIndex];
390 return GetFolderIndex(p1) == GetFolderIndex(p2)
391 && item1.Offset == item2.Offset
392 && item1.Size == item2.Size
393 && item1.Name == item2.Name;
394 }
395
396
FillSortAndShrink()397 void CMvDatabaseEx::FillSortAndShrink()
398 {
399 Items.Clear();
400 StartFolderOfVol.Clear();
401 FolderStartFileIndex.Clear();
402
403 int offset = 0;
404
405 FOR_VECTOR (v, Volumes)
406 {
407 const CDatabaseEx &db = Volumes[v];
408 int curOffset = offset;
409 if (db.IsTherePrevFolder())
410 curOffset--;
411 StartFolderOfVol.Add(curOffset);
412 offset += db.GetNumberOfNewFolders();
413
414 CMvItem mvItem;
415 mvItem.VolumeIndex = v;
416 FOR_VECTOR (i, db.Items)
417 {
418 mvItem.ItemIndex = i;
419 Items.Add(mvItem);
420 }
421 }
422
423 if (Items.Size() > 1)
424 {
425 Items.Sort(CompareMvItems, (void *)this);
426 unsigned j = 1;
427 unsigned i = 1;
428 for (; i < Items.Size(); i++)
429 if (!AreItemsEqual(i, i - 1))
430 Items[j++] = Items[i];
431 Items.DeleteFrom(j);
432 }
433
434 FOR_VECTOR (i, Items)
435 {
436 const int folderIndex = GetFolderIndex(&Items[i]);
437 while (folderIndex >= (int)FolderStartFileIndex.Size())
438 FolderStartFileIndex.Add(i);
439 }
440 }
441
442
Check()443 bool CMvDatabaseEx::Check()
444 {
445 for (unsigned v = 1; v < Volumes.Size(); v++)
446 {
447 const CDatabaseEx &db1 = Volumes[v];
448 if (db1.IsTherePrevFolder())
449 {
450 const CDatabaseEx &db0 = Volumes[v - 1];
451 if (db0.Folders.IsEmpty() || db1.Folders.IsEmpty())
452 return false;
453 const CFolder &f0 = db0.Folders.Back();
454 const CFolder &f1 = db1.Folders.FrontItem();
455 if (f0.MethodMajor != f1.MethodMajor ||
456 f0.MethodMinor != f1.MethodMinor)
457 return false;
458 }
459 }
460
461 UInt32 beginPos = 0;
462 UInt64 endPos = 0;
463 int prevFolder = -2;
464
465 FOR_VECTOR (i, Items)
466 {
467 const CMvItem &mvItem = Items[i];
468 const int fIndex = GetFolderIndex(&mvItem);
469 if (fIndex >= (int)FolderStartFileIndex.Size())
470 return false;
471 const CItem &item = Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex];
472 if (item.IsDir())
473 continue;
474
475 const int folderIndex = GetFolderIndex(&mvItem);
476
477 if (folderIndex != prevFolder)
478 prevFolder = folderIndex;
479 else if (item.Offset < endPos &&
480 (item.Offset != beginPos || item.GetEndOffset() != endPos))
481 return false;
482
483 beginPos = item.Offset;
484 endPos = item.GetEndOffset();
485 }
486
487 return true;
488 }
489
490 }}
491