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