xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/Wim/WimIn.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // Archive/WimIn.cpp
2 
3 #include "StdAfx.h"
4 
5 // #define SHOW_DEBUG_INFO
6 
7 #ifdef SHOW_DEBUG_INFO
8 #include <stdio.h>
9 #define PRF(x) x
10 #else
11 #define PRF(x)
12 #endif
13 
14 #include "../../../../C/CpuArch.h"
15 
16 #include "../../../Common/IntToString.h"
17 #include "../../../Common/StringToInt.h"
18 #include "../../../Common/UTFConvert.h"
19 
20 #include "../../Common/LimitedStreams.h"
21 #include "../../Common/StreamObjects.h"
22 #include "../../Common/StreamUtils.h"
23 
24 #include "../../Compress/XpressDecoder.h"
25 
26 #include "../Common/OutStreamWithSha1.h"
27 
28 #include "WimIn.h"
29 
30 #define Get16(p) GetUi16(p)
31 #define Get32(p) GetUi32(p)
32 #define Get64(p) GetUi64(p)
33 
34 namespace NArchive {
35 namespace NWim {
36 
GetLog_val_min_dest(const UInt32 val,unsigned i,unsigned & dest)37 static bool inline GetLog_val_min_dest(const UInt32 val, unsigned i, unsigned &dest)
38 {
39   UInt32 v = (UInt32)1 << i;
40   for (; i < 32; i++)
41   {
42     if (v == val)
43     {
44       dest = i;
45       return true;
46     }
47     v += v;
48   }
49   return false;
50 }
51 
52 
UnpackChunk(ISequentialInStream * inStream,unsigned method,unsigned chunkSizeBits,size_t inSize,size_t outSize,ISequentialOutStream * outStream)53 HRESULT CUnpacker::UnpackChunk(
54     ISequentialInStream *inStream,
55     unsigned method, unsigned chunkSizeBits,
56     size_t inSize, size_t outSize,
57     ISequentialOutStream *outStream)
58 {
59   if (inSize == outSize)
60   {
61   }
62   else if (method == NMethod::kXPRESS)
63   {
64   }
65   else if (method == NMethod::kLZX)
66   {
67     lzxDecoder.Create_if_Empty();
68     lzxDecoder->Set_WimMode(true);
69   }
70   else if (method == NMethod::kLZMS)
71   {
72     lzmsDecoder.Create_if_Empty();
73   }
74   else
75     return E_NOTIMPL;
76 
77   const size_t chunkSize = (size_t)1 << chunkSizeBits;
78 
79   {
80     const unsigned
81         kAdditionalOutputBufSize = MyMax(NCompress::NLzx::
82         kAdditionalOutputBufSize,        NCompress::NXpress::
83         kAdditionalOutputBufSize);
84     unpackBuf.EnsureCapacity(chunkSize + kAdditionalOutputBufSize);
85     if (!unpackBuf.Data)
86       return E_OUTOFMEMORY;
87   }
88 
89   HRESULT res = S_FALSE;
90   size_t unpackedSize = 0;
91 
92   if (inSize == outSize)
93   {
94     unpackedSize = outSize;
95     res = ReadStream(inStream, unpackBuf.Data, &unpackedSize);
96     TotalPacked += unpackedSize;
97   }
98   else if (inSize < chunkSize)
99   {
100     const unsigned kAdditionalInputSize = 32;
101     packBuf.EnsureCapacity(chunkSize + kAdditionalInputSize);
102     if (!packBuf.Data)
103       return E_OUTOFMEMORY;
104 
105     RINOK(ReadStream_FALSE(inStream, packBuf.Data, inSize))
106     memset(packBuf.Data + inSize, 0xff, kAdditionalInputSize);
107 
108     TotalPacked += inSize;
109 
110     if (method == NMethod::kXPRESS)
111     {
112       res = NCompress::NXpress::Decode_WithExceedWrite(packBuf.Data, inSize, unpackBuf.Data, outSize);
113       if (res == S_OK)
114         unpackedSize = outSize;
115     }
116     else if (method == NMethod::kLZX)
117     {
118       res = lzxDecoder->Set_ExternalWindow_DictBits(unpackBuf.Data, chunkSizeBits);
119       if (res != S_OK)
120         return E_NOTIMPL;
121       lzxDecoder->Set_KeepHistoryForNext(false);
122       lzxDecoder->Set_KeepHistory(false);
123       res = lzxDecoder->Code_WithExceedReadWrite(packBuf.Data, inSize, (UInt32)outSize);
124       unpackedSize = lzxDecoder->GetUnpackSize();
125       if (res == S_OK && !lzxDecoder->WasBlockFinished())
126         res = S_FALSE;
127     }
128     else
129     {
130       res = lzmsDecoder->Code(packBuf.Data, inSize, unpackBuf.Data, outSize);
131       unpackedSize = lzmsDecoder->GetUnpackSize();
132     }
133   }
134 
135   if (unpackedSize != outSize)
136   {
137     if (res == S_OK)
138       res = S_FALSE;
139 
140     if (unpackedSize > outSize)
141       res = S_FALSE;
142     else
143       memset(unpackBuf.Data + unpackedSize, 0, outSize - unpackedSize);
144   }
145 
146   if (outStream)
147   {
148     RINOK(WriteStream(outStream, unpackBuf.Data, outSize))
149   }
150 
151   return res;
152 }
153 
154 
Unpack2(IInStream * inStream,const CResource & resource,const CHeader & header,const CDatabase * db,ISequentialOutStream * outStream,ICompressProgressInfo * progress)155 HRESULT CUnpacker::Unpack2(
156     IInStream *inStream,
157     const CResource &resource,
158     const CHeader &header,
159     const CDatabase *db,
160     ISequentialOutStream *outStream,
161     ICompressProgressInfo *progress)
162 {
163   if (!resource.IsCompressed() && !resource.IsSolid())
164   {
165     copyCoder.Create_if_Empty();
166 
167     CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> limitedStream;
168     limitedStream->SetStream(inStream);
169 
170     RINOK(InStream_SeekSet(inStream, resource.Offset))
171     if (resource.PackSize != resource.UnpackSize)
172       return S_FALSE;
173 
174     limitedStream->Init(resource.PackSize);
175     TotalPacked += resource.PackSize;
176 
177     HRESULT res = copyCoder.Interface()->Code(limitedStream, outStream, NULL, NULL, progress);
178 
179     if (res == S_OK && copyCoder->TotalSize != resource.UnpackSize)
180       res = S_FALSE;
181     return res;
182   }
183 
184   if (resource.IsSolid())
185   {
186     if (!db || resource.SolidIndex < 0)
187       return E_NOTIMPL;
188     if (resource.IsCompressed())
189       return E_NOTIMPL;
190 
191     const CSolid &ss = db->Solids[resource.SolidIndex];
192 
193     const unsigned chunkSizeBits = ss.ChunkSizeBits;
194     const size_t chunkSize = (size_t)1 << chunkSizeBits;
195 
196     size_t chunkIndex = 0;
197     UInt64 rem = ss.UnpackSize;
198     size_t offsetInChunk = 0;
199 
200     if (resource.IsSolidSmall())
201     {
202       UInt64 offs = resource.Offset;
203       if (offs < ss.SolidOffset)
204         return E_NOTIMPL;
205       offs -= ss.SolidOffset;
206       if (offs > ss.UnpackSize)
207         return E_NOTIMPL;
208       rem = resource.PackSize;
209       if (rem > ss.UnpackSize - offs)
210         return E_NOTIMPL;
211       chunkIndex = (size_t)(offs >> chunkSizeBits);
212       offsetInChunk = (size_t)offs & (chunkSize - 1);
213     }
214 
215     UInt64 packProcessed = 0;
216     UInt64 outProcessed = 0;
217 
218     if (_solidIndex == resource.SolidIndex && _unpackedChunkIndex == chunkIndex)
219     {
220       size_t cur = chunkSize - offsetInChunk;
221       if (cur > rem)
222         cur = (size_t)rem;
223       RINOK(WriteStream(outStream, unpackBuf.Data + offsetInChunk, cur))
224       outProcessed += cur;
225       rem -= cur;
226       offsetInChunk = 0;
227       chunkIndex++;
228     }
229 
230     for (;;)
231     {
232       if (rem == 0)
233         return S_OK;
234 
235       const UInt64 offset = ss.Chunks[chunkIndex];
236       const UInt64 packSize = ss.GetChunkPackSize(chunkIndex);
237       const CResource &rs = db->DataStreams[ss.StreamIndex].Resource;
238       RINOK(InStream_SeekSet(inStream, rs.Offset + ss.HeadersSize + offset))
239 
240       size_t cur = chunkSize;
241       const UInt64 unpackRem = ss.UnpackSize - ((UInt64)chunkIndex << chunkSizeBits);
242       if (cur > unpackRem)
243         cur = (size_t)unpackRem;
244 
245       _solidIndex = -1;
246       _unpackedChunkIndex = 0;
247 
248       const HRESULT res = UnpackChunk(inStream, (unsigned)ss.Method, chunkSizeBits, (size_t)packSize, cur, NULL);
249 
250       if (res != S_OK)
251       {
252         // We ignore data errors in solid stream. SHA will show what files are bad.
253         if (res != S_FALSE)
254           return res;
255       }
256 
257       _solidIndex = resource.SolidIndex;
258       _unpackedChunkIndex = chunkIndex;
259 
260       if (cur < offsetInChunk)
261         return E_FAIL;
262 
263       cur -= offsetInChunk;
264 
265       if (cur > rem)
266         cur = (size_t)rem;
267 
268       RINOK(WriteStream(outStream, unpackBuf.Data + offsetInChunk, cur))
269 
270       if (progress)
271       {
272         RINOK(progress->SetRatioInfo(&packProcessed, &outProcessed))
273         packProcessed += packSize;
274         outProcessed += cur;
275       }
276 
277       rem -= cur;
278       offsetInChunk = 0;
279       chunkIndex++;
280     }
281   }
282 
283 
284   // ---------- NON Solid ----------
285 
286   const UInt64 unpackSize = resource.UnpackSize;
287   if (unpackSize == 0)
288   {
289     if (resource.PackSize == 0)
290       return S_OK;
291     return S_FALSE;
292   }
293 
294   if (unpackSize > ((UInt64)1 << 63))
295     return E_NOTIMPL;
296 
297   const unsigned chunkSizeBits = header.ChunkSizeBits;
298   const unsigned entrySizeShifts = (resource.UnpackSize < ((UInt64)1 << 32) ? 2 : 3);
299 
300   UInt64 baseOffset = resource.Offset;
301   UInt64 packDataSize;
302   size_t numChunks;
303   {
304     const UInt64 numChunks64 = (unpackSize + (((UInt32)1 << chunkSizeBits) - 1)) >> chunkSizeBits;
305     const UInt64 sizesBufSize64 = (numChunks64 - 1) << entrySizeShifts;
306     if (sizesBufSize64 > resource.PackSize)
307       return S_FALSE;
308     packDataSize = resource.PackSize - sizesBufSize64;
309     const size_t sizesBufSize = (size_t)sizesBufSize64;
310     if (sizesBufSize != sizesBufSize64)
311       return E_OUTOFMEMORY;
312     sizesBuf.AllocAtLeast(sizesBufSize);
313     RINOK(InStream_SeekSet(inStream, baseOffset))
314     RINOK(ReadStream_FALSE(inStream, sizesBuf, sizesBufSize))
315     baseOffset += sizesBufSize64;
316     numChunks = (size_t)numChunks64;
317   }
318 
319   _solidIndex = -1;
320   _unpackedChunkIndex = 0;
321 
322   UInt64 outProcessed = 0;
323   UInt64 offset = 0;
324 
325   for (size_t i = 0; i < numChunks; i++)
326   {
327     UInt64 nextOffset = packDataSize;
328 
329     if (i + 1 < numChunks)
330     {
331       const Byte *p = (const Byte *)sizesBuf + (i << entrySizeShifts);
332       nextOffset = (entrySizeShifts == 2) ? Get32(p): Get64(p);
333     }
334 
335     if (nextOffset < offset)
336       return S_FALSE;
337 
338     UInt64 inSize64 = nextOffset - offset;
339     size_t inSize = (size_t)inSize64;
340     if (inSize != inSize64)
341       return S_FALSE;
342 
343     RINOK(InStream_SeekSet(inStream, baseOffset + offset))
344 
345     if (progress)
346     {
347       RINOK(progress->SetRatioInfo(&offset, &outProcessed))
348     }
349 
350     size_t outSize = (size_t)1 << chunkSizeBits;
351     const UInt64 rem = unpackSize - outProcessed;
352     if (outSize > rem)
353       outSize = (size_t)rem;
354 
355     RINOK(UnpackChunk(inStream, header.GetMethod(), chunkSizeBits, inSize, outSize, outStream))
356 
357     outProcessed += outSize;
358     offset = nextOffset;
359   }
360 
361   return S_OK;
362 }
363 
364 
Unpack(IInStream * inStream,const CResource & resource,const CHeader & header,const CDatabase * db,ISequentialOutStream * outStream,ICompressProgressInfo * progress,Byte * digest)365 HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, const CHeader &header, const CDatabase *db,
366     ISequentialOutStream *outStream, ICompressProgressInfo *progress, Byte *digest)
367 {
368   CMyComPtr2_Create<ISequentialOutStream, COutStreamWithSha1> shaStream;
369   // outStream can be NULL, so we use COutStreamWithSha1 even if sha1 is not required
370   shaStream->SetStream(outStream);
371   shaStream->Init(digest != NULL);
372   const HRESULT res = Unpack2(inStream, resource, header, db, shaStream, progress);
373   if (digest)
374     shaStream->Final(digest);
375   return res;
376 }
377 
378 
UnpackData(IInStream * inStream,const CResource & resource,const CHeader & header,const CDatabase * db,CByteBuffer & buf,Byte * digest)379 HRESULT CUnpacker::UnpackData(IInStream *inStream,
380     const CResource &resource, const CHeader &header,
381     const CDatabase *db,
382     CByteBuffer &buf, Byte *digest)
383 {
384   // if (resource.IsSolid()) return E_NOTIMPL;
385   UInt64 unpackSize64 = resource.UnpackSize;
386   if (db)
387     unpackSize64 = db->Get_UnpackSize_of_Resource(resource);
388   const size_t size = (size_t)unpackSize64;
389   if (size != unpackSize64)
390     return E_OUTOFMEMORY;
391   buf.Alloc(size);
392 
393   CMyComPtr2_Create<ISequentialOutStream, CBufPtrSeqOutStream> outStream;
394   outStream->Init((Byte *)buf, size);
395   return Unpack(inStream, resource, header, db, outStream, NULL, digest);
396 }
397 
398 
Parse(const Byte * p)399 void CResource::Parse(const Byte *p)
400 {
401   Flags = p[7];
402   PackSize = Get64(p) & (((UInt64)1 << 56) - 1);
403   Offset = Get64(p + 8);
404   UnpackSize = Get64(p + 16);
405   KeepSolid = false;
406   SolidIndex = -1;
407 }
408 
409 #define GET_RESOURCE(_p_, res) res.ParseAndUpdatePhySize(_p_, phySize)
410 
ParseStream(bool oldVersion,const Byte * p,CStreamInfo & s)411 static inline void ParseStream(bool oldVersion, const Byte *p, CStreamInfo &s)
412 {
413   s.Resource.Parse(p);
414   if (oldVersion)
415   {
416     s.PartNumber = 1;
417     s.Id = Get32(p + 24);
418     p += 28;
419   }
420   else
421   {
422     s.PartNumber = Get16(p + 24);
423     p += 26;
424   }
425   s.RefCount = Get32(p);
426   memcpy(s.Hash, p + 4, kHashSize);
427 }
428 
429 
430 #define kLongPath "[LongPath]"
431 
GetShortName(unsigned index,NWindows::NCOM::CPropVariant & name) const432 void CDatabase::GetShortName(unsigned index, NWindows::NCOM::CPropVariant &name) const
433 {
434   const CItem &item = Items[index];
435   const CImage &image = Images[item.ImageIndex];
436   if (item.Parent < 0 && image.NumEmptyRootItems != 0)
437   {
438     name.Clear();
439     return;
440   }
441   const Byte *meta = image.Meta + item.Offset +
442       (IsOldVersion ? kDirRecordSizeOld : kDirRecordSize);
443   UInt32 fileNameLen = Get16(meta - 2);
444   UInt32 shortLen = Get16(meta - 4) / 2;
445   wchar_t *s = name.AllocBstr(shortLen);
446   if (fileNameLen != 0)
447     meta += fileNameLen + 2;
448   for (UInt32 i = 0; i < shortLen; i++)
449     s[i] = Get16(meta + i * 2);
450   s[shortLen] = 0;
451   // empty shortName has no ZERO at the end ?
452 }
453 
454 
GetItemName(unsigned index,NWindows::NCOM::CPropVariant & name) const455 void CDatabase::GetItemName(unsigned index, NWindows::NCOM::CPropVariant &name) const
456 {
457   const CItem &item = Items[index];
458   const CImage &image = Images[item.ImageIndex];
459   if (item.Parent < 0 && image.NumEmptyRootItems != 0)
460   {
461     name = image.RootName;
462     return;
463   }
464   const Byte *meta = image.Meta + item.Offset +
465       (item.IsAltStream ?
466       (IsOldVersion ? 0x10 : 0x24) :
467       (IsOldVersion ? kDirRecordSizeOld - 2 : kDirRecordSize - 2));
468   UInt32 len = Get16(meta) / 2;
469   wchar_t *s = name.AllocBstr(len);
470   meta += 2;
471   len++;
472   for (UInt32 i = 0; i < len; i++)
473     s[i] = Get16(meta + i * 2);
474 }
475 
476 
GetItemPath(unsigned index1,bool showImageNumber,NWindows::NCOM::CPropVariant & path) const477 void CDatabase::GetItemPath(unsigned index1, bool showImageNumber, NWindows::NCOM::CPropVariant &path) const
478 {
479   unsigned size = 0;
480   int index = (int)index1;
481   const int imageIndex = Items[index].ImageIndex;
482   const CImage &image = Images[imageIndex];
483 
484   unsigned newLevel = 0;
485   bool needColon = false;
486 
487   for (;;)
488   {
489     const CItem &item = Items[index];
490     index = item.Parent;
491     if (index >= 0 || image.NumEmptyRootItems == 0)
492     {
493       const Byte *meta = image.Meta + item.Offset;
494       meta += item.IsAltStream ?
495           (IsOldVersion ? 0x10 : 0x24) :
496           (IsOldVersion ? kDirRecordSizeOld - 2 : kDirRecordSize - 2);
497       needColon = item.IsAltStream;
498       size += Get16(meta) / 2;
499       size += newLevel;
500       newLevel = 1;
501       if (size >= ((UInt32)1 << 15))
502       {
503         path = kLongPath;
504         return;
505       }
506     }
507     if (index < 0)
508       break;
509   }
510 
511   if (showImageNumber)
512   {
513     size += image.RootName.Len();
514     size += newLevel;
515   }
516   else if (needColon)
517     size++;
518 
519   wchar_t *s = path.AllocBstr(size);
520   s[size] = 0;
521 
522   if (showImageNumber)
523   {
524     MyStringCopy(s, (const wchar_t *)image.RootName);
525     if (newLevel)
526       s[image.RootName.Len()] = (wchar_t)(needColon ? L':' : WCHAR_PATH_SEPARATOR);
527   }
528   else if (needColon)
529     s[0] = L':';
530 
531   index = (int)index1;
532   wchar_t separator = 0;
533 
534   for (;;)
535   {
536     const CItem &item = Items[index];
537     index = item.Parent;
538     if (index >= 0 || image.NumEmptyRootItems == 0)
539     {
540       if (separator != 0)
541         s[--size] = separator;
542       const Byte *meta = image.Meta + item.Offset;
543       meta += (item.IsAltStream) ?
544           (IsOldVersion ? 0x10: 0x24) :
545           (IsOldVersion ? kDirRecordSizeOld - 2 : kDirRecordSize - 2);
546       unsigned len = Get16(meta) / 2;
547       size -= len;
548       wchar_t *dest = s + size;
549       meta += 2;
550       for (unsigned i = 0; i < len; i++)
551       {
552         wchar_t c = Get16(meta + i * 2);
553         if (c == L'/')
554           c = L'_';
555         #if WCHAR_PATH_SEPARATOR != L'/'
556         else if (c == L'\\')
557           c = WCHAR_IN_FILE_NAME_BACKSLASH_REPLACEMENT; // 22.00 : WSL scheme
558         #endif
559         dest[i] = c;
560       }
561     }
562     if (index < 0)
563       return;
564     separator = item.IsAltStream ? L':' : WCHAR_PATH_SEPARATOR;
565   }
566 }
567 
568 
569 // if (ver <= 1.10), root folder contains real items.
570 // if (ver >= 1.12), root folder contains only one folder with empty name.
571 
ParseDirItem(size_t pos,int parent)572 HRESULT CDatabase::ParseDirItem(size_t pos, int parent)
573 {
574   const unsigned align = GetDirAlignMask();
575   if ((pos & align) != 0)
576     return S_FALSE;
577 
578   for (unsigned numItems = 0;; numItems++)
579   {
580     if (OpenCallback && (Items.Size() & 0xFFFF) == 0)
581     {
582       UInt64 numFiles = Items.Size();
583       RINOK(OpenCallback->SetCompleted(&numFiles, NULL))
584     }
585 
586     const size_t rem = DirSize - pos;
587     if (pos < DirStartOffset || pos > DirSize || rem < 8)
588       return S_FALSE;
589 
590     const Byte *p = DirData + pos;
591 
592     UInt64 len = Get64(p);
593     if (len == 0)
594     {
595       DirProcessed += 8;
596       return S_OK;
597     }
598 
599     if ((len & align) != 0 || rem < len)
600       return S_FALSE;
601 
602     DirProcessed += (size_t)len;
603     if (DirProcessed > DirSize)
604       return S_FALSE;
605 
606     const unsigned dirRecordSize = IsOldVersion ? kDirRecordSizeOld : kDirRecordSize;
607     if (len < dirRecordSize)
608       return S_FALSE;
609 
610     CItem item;
611     UInt32 attrib = Get32(p + 8);
612     item.IsDir = ((attrib & 0x10) != 0);
613     UInt64 subdirOffset = Get64(p + 0x10);
614 
615     const UInt32 numAltStreams = Get16(p + dirRecordSize - 6);
616     const UInt32 shortNameLen = Get16(p + dirRecordSize - 4);
617     const UInt32 fileNameLen = Get16(p + dirRecordSize - 2);
618     if ((shortNameLen & 1) != 0 || (fileNameLen & 1) != 0)
619       return S_FALSE;
620     const UInt32 shortNameLen2 = (shortNameLen == 0 ? shortNameLen : shortNameLen + 2);
621     const UInt32 fileNameLen2 = (fileNameLen == 0 ? fileNameLen : fileNameLen + 2);
622     if (((dirRecordSize + fileNameLen2 + shortNameLen2 + align) & ~align) > len)
623       return S_FALSE;
624 
625     p += dirRecordSize;
626 
627     {
628       if (*(const UInt16 *)(const void *)(p + fileNameLen) != 0)
629         return S_FALSE;
630       for (UInt32 j = 0; j < fileNameLen; j += 2)
631         if (*(const UInt16 *)(const void *)(p + j) == 0)
632           return S_FALSE;
633     }
634 
635     // PRF(printf("\n%S", p));
636 
637     if (shortNameLen != 0)
638     {
639       // empty shortName has no ZERO at the end ?
640       const Byte *p2 = p + fileNameLen2;
641       if (*(const UInt16 *)(const void *)(p2 + shortNameLen) != 0)
642         return S_FALSE;
643       for (UInt32 j = 0; j < shortNameLen; j += 2)
644         if (*(const UInt16 *)(const void *)(p2 + j) == 0)
645           return S_FALSE;
646     }
647 
648     item.Offset = pos;
649     item.Parent = parent;
650     item.ImageIndex = (int)Images.Size() - 1;
651 
652     const unsigned prevIndex = Items.Add(item);
653 
654     pos += (size_t)len;
655 
656     for (UInt32 i = 0; i < numAltStreams; i++)
657     {
658       const size_t rem2 = DirSize - pos;
659       if (pos < DirStartOffset || pos > DirSize || rem2 < 8)
660         return S_FALSE;
661       const Byte *p2 = DirData + pos;
662       const UInt64 len2 = Get64(p2);
663       if ((len2 & align) != 0 || rem2 < len2
664           || len2 < (unsigned)(IsOldVersion ? 0x18 : 0x28))
665         return S_FALSE;
666 
667       DirProcessed += (size_t)len2;
668       if (DirProcessed > DirSize)
669         return S_FALSE;
670 
671       unsigned extraOffset = 0;
672 
673       if (IsOldVersion)
674         extraOffset = 0x10;
675       else
676       {
677         if (Get64(p2 + 8) != 0)
678           return S_FALSE;
679         extraOffset = 0x24;
680       }
681 
682       const UInt32 fileNameLen111 = Get16(p2 + extraOffset);
683       if ((fileNameLen111 & 1) != 0)
684         return S_FALSE;
685       /* Probably different versions of ImageX can use different number of
686          additional ZEROs. So we don't use exact check. */
687       const UInt32 fileNameLen222 = (fileNameLen111 == 0 ? fileNameLen111 : fileNameLen111 + 2);
688       if (((extraOffset + 2 + fileNameLen222 + align) & ~align) > len2)
689         return S_FALSE;
690 
691       {
692         const Byte *p3 = p2 + extraOffset + 2;
693         if (*(const UInt16 *)(const void *)(p3 + fileNameLen111) != 0)
694           return S_FALSE;
695         for (UInt32 j = 0; j < fileNameLen111; j += 2)
696           if (*(const UInt16 *)(const void *)(p3 + j) == 0)
697             return S_FALSE;
698 
699         // PRF(printf("\n  %S", p3));
700       }
701 
702 
703       /* wim uses alt sreams list, if there is at least one alt stream.
704          And alt stream without name is main stream. */
705 
706       // Why wimlib writes two alt streams for REPARSE_POINT, with empty second alt stream?
707 
708       Byte *prevMeta = DirData + item.Offset;
709 
710       if (fileNameLen111 == 0 &&
711           ((attrib & FILE_ATTRIBUTE_REPARSE_POINT) || !item.IsDir)
712           && (IsOldVersion || IsEmptySha(prevMeta + 0x40)))
713       {
714         if (IsOldVersion)
715           memcpy(prevMeta + 0x10, p2 + 8, 4); // It's 32-bit Id
716         else if (!IsEmptySha(p2 + 0x10))
717         {
718           // if (IsEmptySha(prevMeta + 0x40))
719             memcpy(prevMeta + 0x40, p2 + 0x10, kHashSize);
720           // else HeadersError = true;
721         }
722       }
723       else
724       {
725         ThereAreAltStreams = true;
726         CItem item2;
727         item2.Offset = pos;
728         item2.IsAltStream = true;
729         item2.Parent = (int)prevIndex;
730         item2.ImageIndex = (int)Images.Size() - 1;
731         Items.Add(item2);
732       }
733 
734       pos += (size_t)len2;
735     }
736 
737     if (parent < 0 && numItems == 0 && shortNameLen == 0 && fileNameLen == 0 && item.IsDir)
738     {
739       const Byte *p2 = DirData + pos;
740       if (DirSize - pos >= 8 && Get64(p2) == 0)
741       {
742         CImage &image = Images.Back();
743         image.NumEmptyRootItems = 1;
744 
745         if (subdirOffset != 0
746             && DirSize - pos >= 16
747             && Get64(p2 + 8) != 0
748             && pos + 8 < subdirOffset)
749         {
750           // Longhorn.4093 contains hidden files after empty root folder and before items of next folder. Why?
751           // That code shows them. If we want to ignore them, we need to update DirProcessed.
752           // DirProcessed += (size_t)(subdirOffset - (pos + 8));
753           // printf("\ndirOffset = %5d hiddenOffset = %5d\n", (int)subdirOffset, (int)pos + 8);
754           subdirOffset = pos + 8;
755           // return S_FALSE;
756         }
757       }
758     }
759 
760     if (item.IsDir && subdirOffset != 0)
761     {
762       RINOK(ParseDirItem((size_t)subdirOffset, (int)prevIndex))
763     }
764   }
765 }
766 
767 
ParseImageDirs(CByteBuffer & buf,int parent)768 HRESULT CDatabase::ParseImageDirs(CByteBuffer &buf, int parent)
769 {
770   DirData = buf;
771   DirSize = buf.Size();
772   if (DirSize < 8)
773     return S_FALSE;
774   const Byte *p = DirData;
775   size_t pos = 0;
776   CImage &image = Images.Back();
777 
778   if (IsOldVersion)
779   {
780     UInt32 numEntries = Get32(p + 4);
781 
782     if (numEntries > (1 << 28) ||
783         numEntries > (DirSize >> 3))
784       return S_FALSE;
785 
786     UInt32 sum = 8;
787     if (numEntries != 0)
788       sum = numEntries * 8;
789 
790     image.SecurOffsets.ClearAndReserve(numEntries + 1);
791     image.SecurOffsets.AddInReserved(sum);
792 
793     for (UInt32 i = 0; i < numEntries; i++)
794     {
795       const Byte *pp = p + (size_t)i * 8;
796       UInt32 len = Get32(pp);
797       if (i != 0 && Get32(pp + 4) != 0)
798         return S_FALSE;
799       if (len > DirSize - sum)
800         return S_FALSE;
801       sum += len;
802       if (sum < len)
803         return S_FALSE;
804       image.SecurOffsets.AddInReserved(sum);
805     }
806 
807     pos = sum;
808 
809     const size_t align = GetDirAlignMask();
810     pos = (pos + align) & ~(size_t)align;
811   }
812   else
813   {
814     UInt32 totalLen = Get32(p);
815     if (totalLen == 0)
816       pos = 8;
817     else
818     {
819       if (totalLen < 8)
820         return S_FALSE;
821       UInt32 numEntries = Get32(p + 4);
822       pos = 8;
823       if (totalLen > DirSize || numEntries > ((totalLen - 8) >> 3))
824         return S_FALSE;
825       UInt32 sum = (UInt32)pos + numEntries * 8;
826       image.SecurOffsets.ClearAndReserve(numEntries + 1);
827       image.SecurOffsets.AddInReserved(sum);
828 
829       for (UInt32 i = 0; i < numEntries; i++, pos += 8)
830       {
831         UInt64 len = Get64(p + pos);
832         if (len > totalLen - sum)
833           return S_FALSE;
834         sum += (UInt32)len;
835         image.SecurOffsets.AddInReserved(sum);
836       }
837 
838       pos = sum;
839       pos = (pos + 7) & ~(size_t)7;
840       if (pos != (((size_t)totalLen + 7) & ~(size_t)7))
841         return S_FALSE;
842     }
843   }
844 
845   if (pos > DirSize)
846     return S_FALSE;
847 
848   DirStartOffset = DirProcessed = pos;
849   image.StartItem = Items.Size();
850 
851   RINOK(ParseDirItem(pos, parent))
852 
853   image.NumItems = Items.Size() - image.StartItem;
854   if (DirProcessed == DirSize)
855     return S_OK;
856 
857   /* Original program writes additional 8 bytes (END_OF_ROOT_FOLDER),
858      but the reference to that folder is empty */
859 
860   // we can't use DirProcessed - DirStartOffset == 112 check if there is alt stream in root
861   if (DirProcessed == DirSize - 8 && Get64(p + DirSize - 8) != 0)
862     return S_OK;
863 
864   // 18.06: we support cases, when some old dism can capture images
865   // where DirProcessed much smaller than DirSize
866   HeadersError = true;
867   return S_OK;
868   // return S_FALSE;
869 }
870 
871 
Parse(const Byte * p,UInt64 & phySize)872 HRESULT CHeader::Parse(const Byte *p, UInt64 &phySize)
873 {
874   UInt32 headerSize = Get32(p + 8);
875   phySize = headerSize;
876   Version = Get32(p + 0x0C);
877   Flags = Get32(p + 0x10);
878   if (!IsSupported())
879     return S_FALSE;
880 
881   {
882     ChunkSize = Get32(p + 0x14);
883     ChunkSizeBits = kChunkSizeBits;
884     if (ChunkSize != 0)
885     {
886       if (!GetLog_val_min_dest(ChunkSize, 12, ChunkSizeBits))
887         return S_FALSE;
888     }
889   }
890 
891   _isOldVersion = false;
892   _isNewVersion = false;
893 
894   if (IsSolidVersion())
895     _isNewVersion = true;
896   else
897   {
898     if (Version < 0x010900)
899       return S_FALSE;
900     _isOldVersion = (Version <= 0x010A00);
901     // We don't know details about 1.11 version. So we use headerSize to guess exact features.
902     if (Version == 0x010B00 && headerSize == 0x60)
903       _isOldVersion = true;
904     _isNewVersion = (Version >= 0x010D00);
905   }
906 
907   unsigned offset;
908 
909   if (IsOldVersion())
910   {
911     if (headerSize != 0x60)
912       return S_FALSE;
913     memset(Guid, 0, 16);
914     offset = 0x18;
915     PartNumber = 1;
916     NumParts = 1;
917   }
918   else
919   {
920     if (headerSize < 0x74)
921       return S_FALSE;
922     memcpy(Guid, p + 0x18, 16);
923     PartNumber = Get16(p + 0x28);
924     NumParts = Get16(p + 0x2A);
925     if (PartNumber == 0 || PartNumber > NumParts)
926       return S_FALSE;
927     offset = 0x2C;
928     if (IsNewVersion())
929     {
930       // if (headerSize < 0xD0)
931       if (headerSize != 0xD0)
932         return S_FALSE;
933       NumImages = Get32(p + offset);
934       offset += 4;
935     }
936   }
937 
938   GET_RESOURCE(p + offset       , OffsetResource);
939   GET_RESOURCE(p + offset + 0x18, XmlResource);
940   GET_RESOURCE(p + offset + 0x30, MetadataResource);
941   BootIndex = 0;
942 
943   if (IsNewVersion())
944   {
945     BootIndex = Get32(p + offset + 0x48);
946     GET_RESOURCE(p + offset + 0x4C, IntegrityResource);
947   }
948 
949   return S_OK;
950 }
951 
952 
953 const Byte kSignature[kSignatureSize] = { 'M', 'S', 'W', 'I', 'M', 0, 0, 0 };
954 
ReadHeader(IInStream * inStream,CHeader & h,UInt64 & phySize)955 HRESULT ReadHeader(IInStream *inStream, CHeader &h, UInt64 &phySize)
956 {
957   Byte p[kHeaderSizeMax];
958   RINOK(ReadStream_FALSE(inStream, p, kHeaderSizeMax))
959   if (memcmp(p, kSignature, kSignatureSize) != 0)
960     return S_FALSE;
961   return h.Parse(p, phySize);
962 }
963 
964 
ReadStreams(IInStream * inStream,const CHeader & h,CDatabase & db)965 static HRESULT ReadStreams(IInStream *inStream, const CHeader &h, CDatabase &db)
966 {
967   CByteBuffer offsetBuf;
968 
969   CUnpacker unpacker;
970   RINOK(unpacker.UnpackData(inStream, h.OffsetResource, h, NULL, offsetBuf, NULL))
971 
972   const size_t streamInfoSize = h.IsOldVersion() ? kStreamInfoSize + 2 : kStreamInfoSize;
973   {
974     const unsigned numItems = (unsigned)(offsetBuf.Size() / streamInfoSize);
975     if ((size_t)numItems * streamInfoSize != offsetBuf.Size())
976       return S_FALSE;
977     const unsigned numItems2 = db.DataStreams.Size() + numItems;
978     if (numItems2 < numItems)
979       return S_FALSE;
980     db.DataStreams.Reserve(numItems2);
981   }
982 
983   bool keepSolid = false;
984 
985   for (size_t i = 0; i < offsetBuf.Size(); i += streamInfoSize)
986   {
987     CStreamInfo s;
988     ParseStream(h.IsOldVersion(), (const Byte *)offsetBuf + i, s);
989 
990     PRF(printf("\n"));
991     PRF(printf(s.Resource.IsMetadata() ? "### META" : "    DATA"));
992     PRF(printf(" %2X", s.Resource.Flags));
993     PRF(printf(" %9I64X", s.Resource.Offset));
994     PRF(printf(" %9I64X", s.Resource.PackSize));
995     PRF(printf(" %9I64X", s.Resource.UnpackSize));
996     PRF(printf(" %d", s.RefCount));
997 
998     if (s.PartNumber != h.PartNumber)
999       continue;
1000 
1001     if (s.Resource.IsSolid())
1002     {
1003       s.Resource.KeepSolid = keepSolid;
1004       keepSolid = true;
1005     }
1006     else
1007     {
1008       s.Resource.KeepSolid = false;
1009       keepSolid = false;
1010     }
1011 
1012     if (!s.Resource.IsMetadata())
1013       db.DataStreams.AddInReserved(s);
1014     else
1015     {
1016       if (s.Resource.IsSolid())
1017         return E_NOTIMPL;
1018       if (s.RefCount == 0)
1019       {
1020         // some wims have such (deleted?) metadata stream.
1021         // examples: boot.wim in VistaBeta2, WinPE.wim from WAIK.
1022         // db.DataStreams.Add(s);
1023         // we can show these delete images, if we comment "continue" command;
1024         continue;
1025       }
1026 
1027       if (s.RefCount > 1)
1028       {
1029         return S_FALSE;
1030         // s.RefCount--;
1031         // db.DataStreams.Add(s);
1032       }
1033 
1034       db.MetaStreams.Add(s);
1035     }
1036   }
1037 
1038   PRF(printf("\n"));
1039 
1040   return S_OK;
1041 }
1042 
1043 
OpenXml(IInStream * inStream,const CHeader & h,CByteBuffer & xml)1044 HRESULT CDatabase::OpenXml(IInStream *inStream, const CHeader &h, CByteBuffer &xml)
1045 {
1046   CUnpacker unpacker;
1047   return unpacker.UnpackData(inStream, h.XmlResource, h, this, xml, NULL);
1048 }
1049 
SetRootNames(CImage & image,unsigned value)1050 static void SetRootNames(CImage &image, unsigned value)
1051 {
1052   wchar_t temp[16];
1053   ConvertUInt32ToString(value, temp);
1054   image.RootName = temp;
1055   image.RootNameBuf.Alloc(image.RootName.Len() * 2 + 2);
1056   Byte *p = image.RootNameBuf;
1057   unsigned len = image.RootName.Len() + 1;
1058   for (unsigned k = 0; k < len; k++)
1059   {
1060     p[k * 2] = (Byte)temp[k];
1061     p[k * 2 + 1] = 0;
1062   }
1063 }
1064 
1065 
Open(IInStream * inStream,const CHeader & h,unsigned numItemsReserve,IArchiveOpenCallback * openCallback)1066 HRESULT CDatabase::Open(IInStream *inStream, const CHeader &h, unsigned numItemsReserve, IArchiveOpenCallback *openCallback)
1067 {
1068   OpenCallback = openCallback;
1069   IsOldVersion = h.IsOldVersion();
1070   IsOldVersion9 = (h.Version == 0x10900);
1071 
1072   RINOK(ReadStreams(inStream, h, *this))
1073 
1074   bool needBootMetadata = !h.MetadataResource.IsEmpty();
1075   unsigned numNonDeletedImages = 0;
1076 
1077   CUnpacker unpacker;
1078 
1079   FOR_VECTOR (i, MetaStreams)
1080   {
1081     const CStreamInfo &si = MetaStreams[i];
1082 
1083     if (h.PartNumber != 1 || si.PartNumber != h.PartNumber)
1084       continue;
1085 
1086     const unsigned userImage = Images.Size() + GetStartImageIndex();
1087     CImage &image = Images.AddNew();
1088     SetRootNames(image, userImage);
1089 
1090     CByteBuffer &metadata = image.Meta;
1091     Byte hash[kHashSize];
1092 
1093     RINOK(unpacker.UnpackData(inStream, si.Resource, h, this, metadata, hash))
1094 
1095     if (memcmp(hash, si.Hash, kHashSize) != 0 &&
1096         !(h.IsOldVersion() && IsEmptySha(si.Hash)))
1097       return S_FALSE;
1098 
1099     image.NumEmptyRootItems = 0;
1100 
1101     if (Items.IsEmpty())
1102       Items.ClearAndReserve(numItemsReserve);
1103 
1104     RINOK(ParseImageDirs(metadata, -1))
1105 
1106     if (needBootMetadata)
1107     {
1108       bool sameRes = (h.MetadataResource.Offset == si.Resource.Offset);
1109       if (sameRes)
1110         needBootMetadata = false;
1111       if (h.IsNewVersion())
1112       {
1113         if (si.RefCount == 1)
1114         {
1115           numNonDeletedImages++;
1116           bool isBootIndex = (h.BootIndex == numNonDeletedImages);
1117           if (sameRes && !isBootIndex)
1118             return S_FALSE;
1119           if (isBootIndex && !sameRes)
1120             return S_FALSE;
1121         }
1122       }
1123     }
1124   }
1125 
1126   if (needBootMetadata)
1127     return S_FALSE;
1128   return S_OK;
1129 }
1130 
1131 
ItemHasStream(const CItem & item) const1132 bool CDatabase::ItemHasStream(const CItem &item) const
1133 {
1134   if (item.ImageIndex < 0)
1135     return true;
1136   const Byte *meta = Images[item.ImageIndex].Meta + item.Offset;
1137   if (IsOldVersion)
1138   {
1139     // old wim use same field for file_id and dir_offset;
1140     if (item.IsDir)
1141       return false;
1142     meta += (item.IsAltStream ? 0x8 : 0x10);
1143     UInt32 id = GetUi32(meta);
1144     return id != 0;
1145   }
1146   meta += (item.IsAltStream ? 0x10 : 0x40);
1147   return !IsEmptySha(meta);
1148 }
1149 
1150 
1151 #define RINOZ(x) { int _tt_ = (x); if (_tt_ != 0) return _tt_; }
1152 
CompareStreamsByPos(const CStreamInfo * p1,const CStreamInfo * p2,void *)1153 static int CompareStreamsByPos(const CStreamInfo *p1, const CStreamInfo *p2, void * /* param */)
1154 {
1155   RINOZ(MyCompare(p1->PartNumber, p2->PartNumber))
1156   RINOZ(MyCompare(p1->Resource.Offset, p2->Resource.Offset))
1157   return MyCompare(p1->Resource.PackSize, p2->Resource.PackSize);
1158 }
1159 
CompareIDs(const unsigned * p1,const unsigned * p2,void * param)1160 static int CompareIDs(const unsigned *p1, const unsigned *p2, void *param)
1161 {
1162   const CStreamInfo *streams = (const CStreamInfo *)param;
1163   return MyCompare(streams[*p1].Id, streams[*p2].Id);
1164 }
1165 
CompareHashRefs(const unsigned * p1,const unsigned * p2,void * param)1166 static int CompareHashRefs(const unsigned *p1, const unsigned *p2, void *param)
1167 {
1168   const CStreamInfo *streams = (const CStreamInfo *)param;
1169   return memcmp(streams[*p1].Hash, streams[*p2].Hash, kHashSize);
1170 }
1171 
FindId(const CStreamInfo * streams,const CUIntVector & sorted,UInt32 id)1172 static int FindId(const CStreamInfo *streams, const CUIntVector &sorted, UInt32 id)
1173 {
1174   unsigned left = 0, right = sorted.Size();
1175   while (left != right)
1176   {
1177     const unsigned mid = (left + right) / 2;
1178     const unsigned streamIndex = sorted[mid];
1179     const UInt32 id2 = streams[streamIndex].Id;
1180     if (id == id2)
1181       return (int)streamIndex;
1182     if (id < id2)
1183       right = mid;
1184     else
1185       left = mid + 1;
1186   }
1187   return -1;
1188 }
1189 
FindHash(const CStreamInfo * streams,const CUIntVector & sorted,const Byte * hash)1190 static int FindHash(const CStreamInfo *streams, const CUIntVector &sorted, const Byte *hash)
1191 {
1192   unsigned left = 0, right = sorted.Size();
1193   while (left != right)
1194   {
1195     const unsigned mid = (left + right) / 2;
1196     const unsigned streamIndex = sorted[mid];
1197     const Byte *hash2 = streams[streamIndex].Hash;
1198     unsigned i;
1199     for (i = 0; i < kHashSize; i++)
1200       if (hash[i] != hash2[i])
1201         break;
1202     if (i == kHashSize)
1203       return (int)streamIndex;
1204     if (hash[i] < hash2[i])
1205       right = mid;
1206     else
1207       left = mid + 1;
1208   }
1209   return -1;
1210 }
1211 
CompareItems(const unsigned * a1,const unsigned * a2,void * param)1212 static int CompareItems(const unsigned *a1, const unsigned *a2, void *param)
1213 {
1214   const CRecordVector<CItem> &items = ((CDatabase *)param)->Items;
1215   const CItem &i1 = items[*a1];
1216   const CItem &i2 = items[*a2];
1217 
1218   if (i1.IsDir != i2.IsDir)
1219     return i1.IsDir ? -1 : 1;
1220   if (i1.IsAltStream != i2.IsAltStream)
1221     return i1.IsAltStream ? 1 : -1;
1222   RINOZ(MyCompare(i1.StreamIndex, i2.StreamIndex))
1223   RINOZ(MyCompare(i1.ImageIndex, i2.ImageIndex))
1224   return MyCompare(i1.Offset, i2.Offset);
1225 }
1226 
1227 
FillAndCheck(const CObjectVector<CVolume> & volumes)1228 HRESULT CDatabase::FillAndCheck(const CObjectVector<CVolume> &volumes)
1229 {
1230   CUIntVector sortedByHash;
1231   sortedByHash.Reserve(DataStreams.Size());
1232   {
1233     CByteBuffer sizesBuf;
1234 
1235     for (unsigned iii = 0; iii < DataStreams.Size();)
1236     {
1237       {
1238         const CResource &r = DataStreams[iii].Resource;
1239         if (!r.IsSolid())
1240         {
1241           sortedByHash.AddInReserved(iii++);
1242           continue;
1243         }
1244       }
1245 
1246       UInt64 solidRunOffset = 0;
1247       unsigned k;
1248       unsigned numSolidsStart = Solids.Size();
1249 
1250       for (k = iii; k < DataStreams.Size(); k++)
1251       {
1252         CStreamInfo &si = DataStreams[k];
1253         CResource &r = si.Resource;
1254 
1255         if (!r.IsSolid())
1256           break;
1257         if (!r.KeepSolid && k != iii)
1258           break;
1259 
1260         if (r.Flags != NResourceFlags::kSolid)
1261           return S_FALSE;
1262 
1263         if (!r.IsSolidBig())
1264           continue;
1265 
1266         if (!si.IsEmptyHash())
1267           return S_FALSE;
1268         if (si.RefCount != 1)
1269           return S_FALSE;
1270 
1271         r.SolidIndex = (int)Solids.Size();
1272 
1273         CSolid &ss = Solids.AddNew();
1274         ss.StreamIndex = k;
1275         ss.SolidOffset = solidRunOffset;
1276         {
1277           const size_t kSolidHeaderSize = 8 + 4 + 4;
1278           Byte header[kSolidHeaderSize];
1279 
1280           if (si.PartNumber >= volumes.Size())
1281             return S_FALSE;
1282 
1283           const CVolume &vol = volumes[si.PartNumber];
1284           IInStream *inStream = vol.Stream;
1285           RINOK(InStream_SeekSet(inStream, r.Offset))
1286           RINOK(ReadStream_FALSE(inStream, (Byte *)header, kSolidHeaderSize))
1287 
1288           ss.UnpackSize = GetUi64(header);
1289 
1290           if (ss.UnpackSize > ((UInt64)1 << 63))
1291             return S_FALSE;
1292 
1293           solidRunOffset += ss.UnpackSize;
1294           if (solidRunOffset < ss.UnpackSize)
1295             return S_FALSE;
1296 
1297           const UInt32 solidChunkSize = GetUi32(header + 8);
1298           if (!GetLog_val_min_dest(solidChunkSize, 8, ss.ChunkSizeBits))
1299             return S_FALSE;
1300           ss.Method = (Int32)GetUi32(header + 12);
1301 
1302           const UInt64 numChunks64 = (ss.UnpackSize + (((UInt32)1 << ss.ChunkSizeBits) - 1)) >> ss.ChunkSizeBits;
1303           const UInt64 sizesBufSize64 = 4 * numChunks64;
1304           ss.HeadersSize = kSolidHeaderSize + sizesBufSize64;
1305           const size_t sizesBufSize = (size_t)sizesBufSize64;
1306           if (sizesBufSize != sizesBufSize64)
1307             return E_OUTOFMEMORY;
1308           sizesBuf.AllocAtLeast(sizesBufSize);
1309 
1310           RINOK(ReadStream_FALSE(inStream, sizesBuf, sizesBufSize))
1311 
1312           const size_t numChunks = (size_t)numChunks64;
1313           ss.Chunks.Alloc(numChunks + 1);
1314 
1315           UInt64 offset = 0;
1316 
1317           size_t c;
1318           for (c = 0; c < numChunks; c++)
1319           {
1320             ss.Chunks[c] = offset;
1321             UInt32 packSize = GetUi32((const Byte *)sizesBuf + c * 4);
1322             offset += packSize;
1323             if (offset < packSize)
1324               return S_FALSE;
1325           }
1326           ss.Chunks[c] = offset;
1327 
1328           if (ss.Chunks[0] != 0)
1329             return S_FALSE;
1330           if (ss.HeadersSize + offset != r.PackSize)
1331             return S_FALSE;
1332         }
1333       }
1334 
1335       unsigned solidLim = k;
1336 
1337       for (k = iii; k < solidLim; k++)
1338       {
1339         CStreamInfo &si = DataStreams[k];
1340         CResource &r = si.Resource;
1341 
1342         if (!r.IsSolidSmall())
1343           continue;
1344 
1345         if (si.IsEmptyHash())
1346           return S_FALSE;
1347 
1348         unsigned solidIndex;
1349         {
1350           UInt64 offset = r.Offset;
1351           for (solidIndex = numSolidsStart;; solidIndex++)
1352           {
1353             if (solidIndex == Solids.Size())
1354               return S_FALSE;
1355             UInt64 unpackSize = Solids[solidIndex].UnpackSize;
1356             if (offset < unpackSize)
1357               break;
1358             offset -= unpackSize;
1359           }
1360         }
1361         CSolid &ss = Solids[solidIndex];
1362         if (r.Offset < ss.SolidOffset)
1363           return S_FALSE;
1364         const UInt64 relat = r.Offset - ss.SolidOffset;
1365         if (relat > ss.UnpackSize)
1366           return S_FALSE;
1367         if (r.PackSize > ss.UnpackSize - relat)
1368           return S_FALSE;
1369         r.SolidIndex = (int)solidIndex;
1370         if (ss.FirstSmallStream < 0)
1371           ss.FirstSmallStream = (int)k;
1372 
1373         sortedByHash.AddInReserved(k);
1374         // ss.NumRefs++;
1375       }
1376 
1377       iii = solidLim;
1378     }
1379   }
1380 
1381   if (Solids.IsEmpty())
1382   {
1383     /* We want to check that streams layout is OK.
1384        So we need resources sorted by offset.
1385        Another code can work with non-sorted streams.
1386        NOTE: all WIM programs probably create wim archives with
1387          sorted data streams. So it doesn't call Sort() here. */
1388 
1389     {
1390       unsigned i;
1391       for (i = 1; i < DataStreams.Size(); i++)
1392       {
1393         const CStreamInfo &s0 = DataStreams[i - 1];
1394         const CStreamInfo &s1 = DataStreams[i];
1395         if (s0.PartNumber < s1.PartNumber) continue;
1396         if (s0.PartNumber > s1.PartNumber) break;
1397         if (s0.Resource.Offset < s1.Resource.Offset) continue;
1398         if (s0.Resource.Offset > s1.Resource.Offset) break;
1399         if (s0.Resource.PackSize > s1.Resource.PackSize) break;
1400       }
1401 
1402       if (i < DataStreams.Size())
1403       {
1404         // return E_FAIL;
1405         DataStreams.Sort(CompareStreamsByPos, NULL);
1406       }
1407     }
1408 
1409     for (unsigned i = 1; i < DataStreams.Size(); i++)
1410     {
1411       const CStreamInfo &s0 = DataStreams[i - 1];
1412       const CStreamInfo &s1 = DataStreams[i];
1413       if (s0.PartNumber == s1.PartNumber)
1414         if (s0.Resource.GetEndLimit() > s1.Resource.Offset)
1415           return S_FALSE;
1416     }
1417   }
1418 
1419   {
1420     {
1421       const CStreamInfo *streams = DataStreams.ConstData();
1422 
1423       if (IsOldVersion)
1424       {
1425         sortedByHash.Sort(CompareIDs, (void *)streams);
1426 
1427         for (unsigned i = 1; i < sortedByHash.Size(); i++)
1428           if (streams[sortedByHash[i - 1]].Id >=
1429               streams[sortedByHash[i]].Id)
1430             return S_FALSE;
1431       }
1432       else
1433       {
1434         sortedByHash.Sort(CompareHashRefs, (void *)streams);
1435 
1436         if (!sortedByHash.IsEmpty())
1437         {
1438           if (IsEmptySha(streams[sortedByHash[0]].Hash))
1439             HeadersError = true;
1440 
1441           for (unsigned i = 1; i < sortedByHash.Size(); i++)
1442             if (memcmp(
1443                 streams[sortedByHash[i - 1]].Hash,
1444                 streams[sortedByHash[i]].Hash,
1445                 kHashSize) >= 0)
1446               return S_FALSE;
1447         }
1448       }
1449     }
1450 
1451     FOR_VECTOR (i, Items)
1452     {
1453       CItem &item = Items[i];
1454       item.StreamIndex = -1;
1455       const Byte *hash = Images[item.ImageIndex].Meta + item.Offset;
1456       if (IsOldVersion)
1457       {
1458         if (!item.IsDir)
1459         {
1460           hash += (item.IsAltStream ? 0x8 : 0x10);
1461           UInt32 id = GetUi32(hash);
1462           if (id != 0)
1463             item.StreamIndex = FindId(DataStreams.ConstData(), sortedByHash, id);
1464         }
1465       }
1466       /*
1467       else if (item.IsDir)
1468       {
1469         // reparse points can have dirs some dir
1470       }
1471       */
1472       else
1473       {
1474         hash += (item.IsAltStream ? 0x10 : 0x40);
1475         if (!IsEmptySha(hash))
1476         {
1477           item.StreamIndex = FindHash(DataStreams.ConstData(), sortedByHash, hash);
1478         }
1479       }
1480     }
1481   }
1482   {
1483     CUIntVector refCounts;
1484     refCounts.ClearAndSetSize(DataStreams.Size());
1485     unsigned i;
1486 
1487     for (i = 0; i < DataStreams.Size(); i++)
1488     {
1489       UInt32 startVal = 0;
1490       // const CStreamInfo &s = DataStreams[i];
1491       /*
1492       if (s.Resource.IsMetadata() && s.PartNumber == 1)
1493         startVal = 1;
1494       */
1495       refCounts[i] = startVal;
1496     }
1497 
1498     for (i = 0; i < Items.Size(); i++)
1499     {
1500       const int streamIndex = Items[i].StreamIndex;
1501       if (streamIndex >= 0)
1502         refCounts[streamIndex]++;
1503     }
1504 
1505     for (i = 0; i < DataStreams.Size(); i++)
1506     {
1507       const CStreamInfo &s = DataStreams[i];
1508       if (s.RefCount != refCounts[i]
1509           && !s.Resource.IsSolidBig())
1510       {
1511         /*
1512         printf("\ni=%5d  si.Ref=%2d  realRefs=%2d size=%8d offset=%8x id=%4d ",
1513           i, s.RefCount, refCounts[i], (unsigned)s.Resource.UnpackSize, (unsigned)s.Resource.Offset, s.Id);
1514         */
1515         RefCountError = true;
1516       }
1517 
1518       if (refCounts[i] == 0)
1519       {
1520         const CResource &r = DataStreams[i].Resource;
1521         if (!r.IsSolidBig() || Solids[r.SolidIndex].FirstSmallStream < 0)
1522         {
1523           CItem item;
1524           item.Offset = 0;
1525           item.StreamIndex = (int)i;
1526           item.ImageIndex = -1;
1527           Items.Add(item);
1528           ThereAreDeletedStreams = true;
1529         }
1530       }
1531     }
1532   }
1533 
1534   return S_OK;
1535 }
1536 
1537 
GenerateSortedItems(int imageIndex,bool showImageNumber)1538 HRESULT CDatabase::GenerateSortedItems(int imageIndex, bool showImageNumber)
1539 {
1540   SortedItems.Clear();
1541   VirtualRoots.Clear();
1542   IndexOfUserImage = imageIndex;
1543   NumExcludededItems = 0;
1544   ExludedItem = -1;
1545 
1546   if (Images.Size() != 1 && imageIndex < 0)
1547     showImageNumber = true;
1548 
1549   unsigned startItem = 0;
1550   unsigned endItem = 0;
1551 
1552   if (imageIndex < 0)
1553   {
1554     endItem = Items.Size();
1555     if (Images.Size() == 1)
1556     {
1557       IndexOfUserImage = 0;
1558       const CImage &image = Images[0];
1559       if (!showImageNumber)
1560         NumExcludededItems = image.NumEmptyRootItems;
1561     }
1562   }
1563   else if ((unsigned)imageIndex < Images.Size())
1564   {
1565     const CImage &image = Images[imageIndex];
1566     startItem = image.StartItem;
1567     endItem = startItem + image.NumItems;
1568     if (!showImageNumber)
1569       NumExcludededItems = image.NumEmptyRootItems;
1570   }
1571 
1572   if (NumExcludededItems != 0)
1573   {
1574     ExludedItem = (int)startItem;
1575     startItem += NumExcludededItems;
1576   }
1577 
1578   unsigned num = endItem - startItem;
1579   SortedItems.ClearAndSetSize(num);
1580   unsigned i;
1581   for (i = 0; i < num; i++)
1582     SortedItems[i] = startItem + i;
1583 
1584   SortedItems.Sort(CompareItems, this);
1585   for (i = 0; i < SortedItems.Size(); i++)
1586     Items[SortedItems[i]].IndexInSorted = (int)i;
1587 
1588   if (showImageNumber)
1589     for (i = 0; i < Images.Size(); i++)
1590     {
1591       CImage &image = Images[i];
1592       if (image.NumEmptyRootItems != 0)
1593         continue;
1594       image.VirtualRootIndex = (int)VirtualRoots.Size();
1595       VirtualRoots.Add(i);
1596     }
1597 
1598   return S_OK;
1599 }
1600 
1601 
IntVector_SetMinusOne_IfNeed(CIntVector & v,unsigned size)1602 static void IntVector_SetMinusOne_IfNeed(CIntVector &v, unsigned size)
1603 {
1604   if (v.Size() == size)
1605     return;
1606   v.ClearAndSetSize(size);
1607   int *vals = &v[0];
1608   for (unsigned i = 0; i < size; i++)
1609     vals[i] = -1;
1610 }
1611 
1612 
ExtractReparseStreams(const CObjectVector<CVolume> & volumes,IArchiveOpenCallback * openCallback)1613 HRESULT CDatabase::ExtractReparseStreams(const CObjectVector<CVolume> &volumes, IArchiveOpenCallback *openCallback)
1614 {
1615   ItemToReparse.Clear();
1616   ReparseItems.Clear();
1617 
1618   // we don't know about Reparse field for OLD WIM format
1619   if (IsOldVersion)
1620     return S_OK;
1621 
1622   CIntVector streamToReparse;
1623   CUnpacker unpacker;
1624   UInt64 totalPackedPrev = 0;
1625 
1626   FOR_VECTOR(indexInSorted, SortedItems)
1627   {
1628     // we use sorted items for faster access
1629     unsigned itemIndex = SortedItems[indexInSorted];
1630     const CItem &item = Items[itemIndex];
1631 
1632     if (!item.HasMetadata() || item.IsAltStream)
1633       continue;
1634 
1635     if (item.ImageIndex < 0)
1636       continue;
1637 
1638     const Byte *metadata = Images[item.ImageIndex].Meta + item.Offset;
1639 
1640     const UInt32 attrib = Get32(metadata + 8);
1641     if ((attrib & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
1642       continue;
1643 
1644     if (item.StreamIndex < 0)
1645       continue; // it's ERROR
1646 
1647     const CStreamInfo &si = DataStreams[item.StreamIndex];
1648     if (si.Resource.UnpackSize >= (1 << 16))
1649       continue; // reparse data can not be larger than 64 KB
1650 
1651     IntVector_SetMinusOne_IfNeed(streamToReparse, DataStreams.Size());
1652     IntVector_SetMinusOne_IfNeed(ItemToReparse, Items.Size());
1653 
1654     const unsigned offset = 0x58; // we don't know about Reparse field for OLD WIM format
1655     UInt32 tag = Get32(metadata + offset);
1656     int reparseIndex = streamToReparse[item.StreamIndex];
1657     CByteBuffer buf;
1658 
1659     if (openCallback)
1660     {
1661       if ((unpacker.TotalPacked - totalPackedPrev) >= ((UInt32)1 << 16))
1662       {
1663         UInt64 numFiles = Items.Size();
1664         RINOK(openCallback->SetCompleted(&numFiles, &unpacker.TotalPacked))
1665         totalPackedPrev = unpacker.TotalPacked;
1666       }
1667     }
1668 
1669     if (reparseIndex >= 0)
1670     {
1671       const CByteBuffer &reparse = ReparseItems[reparseIndex];
1672       if (tag == Get32(reparse))
1673       {
1674         ItemToReparse[itemIndex] = reparseIndex;
1675         continue;
1676       }
1677       buf = reparse;
1678       // we support that strange and unusual situation with different tags and same reparse data.
1679     }
1680     else
1681     {
1682       /*
1683       if (si.PartNumber >= volumes.Size())
1684         continue;
1685       */
1686       const CVolume &vol = volumes[si.PartNumber];
1687       /*
1688       if (!vol.Stream)
1689         continue;
1690       */
1691 
1692       Byte digest[kHashSize];
1693       HRESULT res = unpacker.UnpackData(vol.Stream, si.Resource, vol.Header, this, buf, digest);
1694 
1695       if (res == S_FALSE)
1696         continue;
1697 
1698       RINOK(res)
1699 
1700       if (memcmp(digest, si.Hash, kHashSize) != 0
1701         // && !(h.IsOldVersion() && IsEmptySha(si.Hash))
1702         )
1703       {
1704         // setErrorStatus;
1705         continue;
1706       }
1707     }
1708 
1709     CByteBuffer &reparse = ReparseItems.AddNew();
1710     reparse.Alloc(8 + buf.Size());
1711     Byte *dest = (Byte *)reparse;
1712     SetUi32(dest, tag)
1713     SetUi32(dest + 4, (UInt32)buf.Size())
1714     if (buf.Size() != 0)
1715       memcpy(dest + 8, buf, buf.Size());
1716     ItemToReparse[itemIndex] = (int)ReparseItems.Size() - 1;
1717   }
1718 
1719   return S_OK;
1720 }
1721 
1722 
1723 
ParseNumber64(const AString & s,UInt64 & res)1724 static bool ParseNumber64(const AString &s, UInt64 &res)
1725 {
1726   const char *end;
1727   if (s.IsPrefixedBy("0x"))
1728   {
1729     if (s.Len() == 2)
1730       return false;
1731     res = ConvertHexStringToUInt64(s.Ptr(2), &end);
1732   }
1733   else
1734   {
1735     if (s.IsEmpty())
1736       return false;
1737     res = ConvertStringToUInt64(s, &end);
1738   }
1739   return *end == 0;
1740 }
1741 
1742 
ParseNumber32(const AString & s,UInt32 & res)1743 static bool ParseNumber32(const AString &s, UInt32 &res)
1744 {
1745   UInt64 res64;
1746   if (!ParseNumber64(s, res64) || res64 >= ((UInt64)1 << 32))
1747     return false;
1748   res = (UInt32)res64;
1749   return true;
1750 }
1751 
1752 
ParseTime(const CXmlItem & item,FILETIME & ft,const char * tag)1753 static bool ParseTime(const CXmlItem &item, FILETIME &ft, const char *tag)
1754 {
1755   const CXmlItem *timeItem = item.FindSubTag_GetPtr(tag);
1756   if (timeItem)
1757   {
1758     UInt32 low = 0, high = 0;
1759     if (ParseNumber32(timeItem->GetSubStringForTag("LOWPART"), low) &&
1760         ParseNumber32(timeItem->GetSubStringForTag("HIGHPART"), high))
1761     {
1762       ft.dwLowDateTime = low;
1763       ft.dwHighDateTime = high;
1764       return true;
1765     }
1766   }
1767   return false;
1768 }
1769 
1770 
Parse(const CXmlItem & item)1771 void CImageInfo::Parse(const CXmlItem &item)
1772 {
1773   CTimeDefined = ParseTime(item, CTime, "CREATIONTIME");
1774   MTimeDefined = ParseTime(item, MTime, "LASTMODIFICATIONTIME");
1775   NameDefined = true;
1776   ConvertUTF8ToUnicode(item.GetSubStringForTag("NAME"), Name);
1777 
1778   ParseNumber64(item.GetSubStringForTag("DIRCOUNT"), DirCount);
1779   ParseNumber64(item.GetSubStringForTag("FILECOUNT"), FileCount);
1780   IndexDefined = ParseNumber32(item.GetPropVal("INDEX"), Index);
1781 }
1782 
ToUnicode(UString & s)1783 void CWimXml::ToUnicode(UString &s)
1784 {
1785   size_t size = Data.Size();
1786   if (size < 2 || (size & 1) != 0 || size > (1 << 24))
1787     return;
1788   const Byte *p = Data;
1789   if (Get16(p) != 0xFEFF)
1790     return;
1791   wchar_t *chars = s.GetBuf((unsigned)(size / 2));
1792   for (size_t i = 2; i < size; i += 2)
1793   {
1794     wchar_t c = Get16(p + i);
1795     if (c == 0)
1796       break;
1797     *chars++ = c;
1798   }
1799   *chars = 0;
1800   s.ReleaseBuf_SetLen((unsigned)(chars - (const wchar_t *)s));
1801 }
1802 
1803 
Parse()1804 bool CWimXml::Parse()
1805 {
1806   IsEncrypted = false;
1807   AString utf;
1808   {
1809     UString s;
1810     ToUnicode(s);
1811     // if (!ConvertUnicodeToUTF8(s, utf)) return false;
1812     ConvertUnicodeToUTF8(s, utf);
1813   }
1814 
1815   if (!Xml.Parse(utf))
1816     return false;
1817   if (Xml.Root.Name != "WIM")
1818     return false;
1819 
1820   FOR_VECTOR (i, Xml.Root.SubItems)
1821   {
1822     const CXmlItem &item = Xml.Root.SubItems[i];
1823 
1824     if (item.IsTagged("IMAGE"))
1825     {
1826       CImageInfo imageInfo;
1827       imageInfo.Parse(item);
1828       if (!imageInfo.IndexDefined)
1829         return false;
1830 
1831       if (imageInfo.Index != (UInt32)Images.Size() + 1)
1832       {
1833         // old wim (1.09) uses zero based image index
1834         if (imageInfo.Index != (UInt32)Images.Size())
1835           return false;
1836       }
1837 
1838       imageInfo.ItemIndexInXml = (int)i;
1839       Images.Add(imageInfo);
1840     }
1841 
1842     if (item.IsTagged("ESD"))
1843     {
1844       FOR_VECTOR (k, item.SubItems)
1845       {
1846         const CXmlItem &item2 = item.SubItems[k];
1847         if (item2.IsTagged("ENCRYPTED"))
1848           IsEncrypted = true;
1849       }
1850     }
1851   }
1852 
1853   return true;
1854 }
1855 
1856 }}
1857