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