xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/Cab/CabIn.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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