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