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