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