1 // WimHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../../C/CpuArch.h"
6
7 #include "../../../Common/ComTry.h"
8 #include "../../../Common/IntToString.h"
9
10 #include "../../Common/MethodProps.h"
11 #include "../../Common/ProgressUtils.h"
12 #include "../../Common/StreamUtils.h"
13
14 #include "WimHandler.h"
15
16 #define Get16(p) GetUi16(p)
17 #define Get32(p) GetUi32(p)
18 #define Get64(p) GetUi64(p)
19
20 using namespace NWindows;
21
22 namespace NArchive {
23 namespace NWim {
24
25 #define FILES_DIR_NAME "[DELETED]"
26
27 // #define WIM_DETAILS
28
29 static const Byte kProps[] =
30 {
31 kpidPath,
32 kpidIsDir,
33 kpidSize,
34 kpidPackSize,
35 kpidMTime,
36 kpidCTime,
37 kpidATime,
38 kpidAttrib,
39 kpidMethod,
40 kpidSolid,
41 kpidShortName,
42 kpidINode,
43 kpidLinks,
44 kpidIsAltStream,
45 kpidNumAltStreams,
46
47 #ifdef WIM_DETAILS
48 , kpidVolume
49 , kpidOffset
50 #endif
51 };
52
53 enum
54 {
55 kpidNumImages = kpidUserDefined,
56 kpidBootImage
57 };
58
59 static const CStatProp kArcProps[] =
60 {
61 { NULL, kpidSize, VT_UI8},
62 { NULL, kpidPackSize, VT_UI8},
63 { NULL, kpidMethod, VT_BSTR},
64 { NULL, kpidClusterSize, VT_UI4},
65 { NULL, kpidCTime, VT_FILETIME},
66 { NULL, kpidMTime, VT_FILETIME},
67 { NULL, kpidComment, VT_BSTR},
68 { NULL, kpidUnpackVer, VT_BSTR},
69 { NULL, kpidIsVolume, VT_BOOL},
70 { NULL, kpidVolume, VT_UI4},
71 { NULL, kpidNumVolumes, VT_UI4},
72 { "Images", kpidNumImages, VT_UI4},
73 { "Boot Image", kpidBootImage, VT_UI4}
74 };
75
76
77 static const char * const k_Methods[] =
78 {
79 "Copy"
80 , "XPress"
81 , "LZX"
82 , "LZMS"
83 };
84
85
86
87 IMP_IInArchive_Props
88 IMP_IInArchive_ArcProps_WITH_NAME
89
AddErrorMessage(AString & s,const char * message)90 static void AddErrorMessage(AString &s, const char *message)
91 {
92 if (!s.IsEmpty())
93 s += ". ";
94 s += message;
95 }
96
97
GetArchiveProperty(PROPID propID,PROPVARIANT * value)98 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
99 {
100 COM_TRY_BEGIN
101 NCOM::CPropVariant prop;
102
103 const CImageInfo *image = NULL;
104 if (_xmls.Size() == 1)
105 {
106 const CWimXml &xml = _xmls[0];
107 if (xml.Images.Size() == 1)
108 image = &xml.Images[0];
109 }
110
111 switch (propID)
112 {
113 case kpidPhySize: prop = _phySize; break;
114 case kpidSize: prop = _db.GetUnpackSize(); break;
115 case kpidPackSize: prop = _db.GetPackSize(); break;
116
117 case kpidCTime:
118 if (_xmls.Size() == 1)
119 {
120 const CWimXml &xml = _xmls[0];
121 int index = -1;
122 FOR_VECTOR (i, xml.Images)
123 {
124 const CImageInfo &image2 = xml.Images[i];
125 if (image2.CTimeDefined)
126 if (index < 0 || ::CompareFileTime(&image2.CTime, &xml.Images[index].CTime) < 0)
127 index = i;
128 }
129 if (index >= 0)
130 prop = xml.Images[index].CTime;
131 }
132 break;
133
134 case kpidMTime:
135 if (_xmls.Size() == 1)
136 {
137 const CWimXml &xml = _xmls[0];
138 int index = -1;
139 FOR_VECTOR (i, xml.Images)
140 {
141 const CImageInfo &image2 = xml.Images[i];
142 if (image2.MTimeDefined)
143 if (index < 0 || ::CompareFileTime(&image2.MTime, &xml.Images[index].MTime) > 0)
144 index = i;
145 }
146 if (index >= 0)
147 prop = xml.Images[index].MTime;
148 }
149 break;
150
151 case kpidComment:
152 if (image)
153 {
154 if (_xmlInComments)
155 {
156 UString s;
157 _xmls[0].ToUnicode(s);
158 prop = s;
159 }
160 else if (image->NameDefined)
161 prop = image->Name;
162 }
163 break;
164
165 case kpidUnpackVer:
166 {
167 UInt32 ver1 = _version >> 16;
168 UInt32 ver2 = (_version >> 8) & 0xFF;
169 UInt32 ver3 = (_version) & 0xFF;
170
171 AString res;
172 res.Add_UInt32(ver1);
173 res += '.';
174 res.Add_UInt32(ver2);
175 if (ver3 != 0)
176 {
177 res += '.';
178 res.Add_UInt32(ver3);
179 }
180 prop = res;
181 break;
182 }
183
184 case kpidIsVolume:
185 if (_xmls.Size() > 0)
186 {
187 UInt16 volIndex = _xmls[0].VolIndex;
188 if (volIndex < _volumes.Size())
189 prop = (_volumes[volIndex].Header.NumParts > 1);
190 }
191 break;
192 case kpidVolume:
193 if (_xmls.Size() > 0)
194 {
195 UInt16 volIndex = _xmls[0].VolIndex;
196 if (volIndex < _volumes.Size())
197 prop = (UInt32)_volumes[volIndex].Header.PartNumber;
198 }
199 break;
200 case kpidNumVolumes: if (_volumes.Size() > 0) prop = (UInt32)(_volumes.Size() - 1); break;
201
202 case kpidClusterSize:
203 if (_xmls.Size() > 0)
204 {
205 UInt16 volIndex = _xmls[0].VolIndex;
206 if (volIndex < _volumes.Size())
207 {
208 const CHeader &h = _volumes[volIndex].Header;
209 prop = (UInt32)1 << h.ChunkSizeBits;
210 }
211 }
212 break;
213
214 case kpidName:
215 if (_firstVolumeIndex >= 0)
216 {
217 const CHeader &h = _volumes[_firstVolumeIndex].Header;
218 if (GetUi32(h.Guid) != 0)
219 {
220 char temp[64];
221 RawLeGuidToString(h.Guid, temp);
222 temp[8] = 0; // for reduced GUID
223 AString s (temp);
224 const char *ext = ".wim";
225 if (h.NumParts != 1)
226 {
227 s += '_';
228 if (h.PartNumber != 1)
229 s.Add_UInt32(h.PartNumber);
230 ext = ".swm";
231 }
232 s += ext;
233 prop = s;
234 }
235 }
236 break;
237
238 case kpidExtension:
239 if (_firstVolumeIndex >= 0)
240 {
241 const CHeader &h = _volumes[_firstVolumeIndex].Header;
242 if (h.NumParts > 1)
243 {
244 AString s;
245 if (h.PartNumber != 1)
246 {
247 s.Add_UInt32(h.PartNumber);
248 s += '.';
249 }
250 s += "swm";
251 prop = s;
252 }
253 }
254 break;
255
256 case kpidNumImages: prop = (UInt32)_db.Images.Size(); break;
257 case kpidBootImage: if (_bootIndex != 0) prop = (UInt32)_bootIndex; break;
258
259 case kpidMethod:
260 {
261 UInt32 methodUnknown = 0;
262 UInt32 methodMask = 0;
263 unsigned chunkSizeBits = 0;
264
265 {
266 FOR_VECTOR (i, _xmls)
267 {
268 const CHeader &header = _volumes[_xmls[i].VolIndex].Header;
269 unsigned method = header.GetMethod();
270 if (method < ARRAY_SIZE(k_Methods))
271 methodMask |= ((UInt32)1 << method);
272 else
273 methodUnknown = method;
274 if (chunkSizeBits < header.ChunkSizeBits)
275 chunkSizeBits = header.ChunkSizeBits;
276 }
277 }
278
279 AString res;
280
281 unsigned numMethods = 0;
282
283 for (unsigned i = 0; i < ARRAY_SIZE(k_Methods); i++)
284 {
285 if (methodMask & ((UInt32)1 << i))
286 {
287 res.Add_Space_if_NotEmpty();
288 res += k_Methods[i];
289 numMethods++;
290 }
291 }
292
293 if (methodUnknown != 0)
294 {
295 res.Add_Space_if_NotEmpty();
296 res.Add_UInt32(methodUnknown);
297 numMethods++;
298 }
299
300 if (numMethods == 1 && chunkSizeBits != 0)
301 {
302 res += ':';
303 res.Add_UInt32((UInt32)chunkSizeBits);
304 }
305
306 prop = res;
307 break;
308 }
309
310 case kpidIsTree: prop = true; break;
311 case kpidIsAltStream: prop = _db.ThereAreAltStreams; break;
312 case kpidIsAux: prop = true; break;
313 // WIM uses special prefix to represent deleted items
314 // case kpidIsDeleted: prop = _db.ThereAreDeletedStreams; break;
315 case kpidINode: prop = true; break;
316
317 case kpidErrorFlags:
318 {
319 UInt32 flags = 0;
320 if (!_isArc) flags |= kpv_ErrorFlags_IsNotArc;
321 if (_db.HeadersError) flags |= kpv_ErrorFlags_HeadersError;
322 if (_unsupported) flags |= kpv_ErrorFlags_UnsupportedMethod;
323 prop = flags;
324 break;
325 }
326
327 case kpidWarning:
328 {
329 AString s;
330 if (_xmlError)
331 AddErrorMessage(s, "XML error");
332 if (_db.RefCountError)
333 AddErrorMessage(s, "Some files have incorrect reference count");
334 if (!s.IsEmpty())
335 prop = s;
336 break;
337 }
338
339 case kpidReadOnly:
340 {
341 bool readOnly = !IsUpdateSupported();
342 if (readOnly)
343 prop = readOnly;
344 break;
345 }
346 }
347
348 prop.Detach(value);
349 return S_OK;
350 COM_TRY_END
351 }
352
GetFileTime(const Byte * p,NCOM::CPropVariant & prop)353 void GetFileTime(const Byte *p, NCOM::CPropVariant &prop)
354 {
355 prop.vt = VT_FILETIME;
356 prop.filetime.dwLowDateTime = Get32(p);
357 prop.filetime.dwHighDateTime = Get32(p + 4);
358 }
359
360
MethodToProp(int method,int chunksSizeBits,NCOM::CPropVariant & prop)361 static void MethodToProp(int method, int chunksSizeBits, NCOM::CPropVariant &prop)
362 {
363 if (method >= 0)
364 {
365 char temp[32];
366
367 if ((unsigned)method < ARRAY_SIZE(k_Methods))
368 strcpy(temp, k_Methods[(unsigned)method]);
369 else
370 ConvertUInt32ToString((UInt32)(unsigned)method, temp);
371
372 if (chunksSizeBits >= 0)
373 {
374 size_t pos = strlen(temp);
375 temp[pos++] = ':';
376 ConvertUInt32ToString((unsigned)chunksSizeBits, temp + pos);
377 }
378
379 prop = temp;
380 }
381 }
382
383
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)384 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
385 {
386 COM_TRY_BEGIN
387 NCOM::CPropVariant prop;
388
389 if (index < _db.SortedItems.Size())
390 {
391 unsigned realIndex = _db.SortedItems[index];
392 const CItem &item = _db.Items[realIndex];
393 const CStreamInfo *si = NULL;
394 const CVolume *vol = NULL;
395 if (item.StreamIndex >= 0)
396 {
397 si = &_db.DataStreams[item.StreamIndex];
398 vol = &_volumes[si->PartNumber];
399 }
400
401 const CItem *mainItem = &item;
402 if (item.IsAltStream)
403 mainItem = &_db.Items[item.Parent];
404 const Byte *metadata = NULL;
405 if (mainItem->ImageIndex >= 0)
406 metadata = _db.Images[mainItem->ImageIndex].Meta + mainItem->Offset;
407
408 switch (propID)
409 {
410 case kpidPath:
411 if (item.ImageIndex >= 0)
412 _db.GetItemPath(realIndex, _showImageNumber, prop);
413 else
414 {
415 /*
416 while (s.Len() < _nameLenForStreams)
417 s = '0' + s;
418 */
419 /*
420 if (si->Resource.IsFree())
421 s = (AString)("[Free]" STRING_PATH_SEPARATOR) + sz;
422 else
423 */
424 AString s (FILES_DIR_NAME STRING_PATH_SEPARATOR);
425 s.Add_UInt32(item.StreamIndex);
426 prop = s;
427 }
428 break;
429
430 case kpidName:
431 if (item.ImageIndex >= 0)
432 _db.GetItemName(realIndex, prop);
433 else
434 {
435 char sz[16];
436 ConvertUInt32ToString(item.StreamIndex, sz);
437 /*
438 AString s = sz;
439 while (s.Len() < _nameLenForStreams)
440 s = '0' + s;
441 */
442 prop = sz;
443 }
444 break;
445
446 case kpidShortName:
447 if (item.ImageIndex >= 0 && !item.IsAltStream)
448 _db.GetShortName(realIndex, prop);
449 break;
450
451 case kpidPackSize:
452 {
453 if (si)
454 {
455 if (!si->Resource.IsSolidSmall())
456 prop = si->Resource.PackSize;
457 else
458 {
459 if (si->Resource.SolidIndex >= 0)
460 {
461 const CSolid &ss = _db.Solids[(unsigned)si->Resource.SolidIndex];
462 if (ss.FirstSmallStream == item.StreamIndex)
463 prop = _db.DataStreams[ss.StreamIndex].Resource.PackSize;
464 }
465 }
466 }
467 else if (!item.IsDir)
468 prop = (UInt64)0;
469
470 break;
471 }
472
473 case kpidSize:
474 {
475 if (si)
476 {
477 if (si->Resource.IsSolid())
478 {
479 if (si->Resource.IsSolidBig())
480 {
481 if (si->Resource.SolidIndex >= 0)
482 {
483 const CSolid &ss = _db.Solids[(unsigned)si->Resource.SolidIndex];
484 prop = ss.UnpackSize;
485 }
486 }
487 else
488 prop = si->Resource.PackSize;
489 }
490 else
491 prop = si->Resource.UnpackSize;
492 }
493 else if (!item.IsDir)
494 prop = (UInt64)0;
495
496 break;
497 }
498
499 case kpidIsDir: prop = item.IsDir; break;
500 case kpidIsAltStream: prop = item.IsAltStream; break;
501 case kpidNumAltStreams:
502 {
503 if (!item.IsAltStream && mainItem->HasMetadata())
504 {
505 UInt32 dirRecordSize = _db.IsOldVersion ? kDirRecordSizeOld : kDirRecordSize;
506 UInt32 numAltStreams = Get16(metadata + dirRecordSize - 6);
507 if (numAltStreams != 0)
508 {
509 if (!item.IsDir)
510 numAltStreams--;
511 prop = numAltStreams;
512 }
513 }
514 break;
515 }
516
517 case kpidAttrib:
518 if (!item.IsAltStream && mainItem->ImageIndex >= 0)
519 {
520 /*
521 if (fileNameLen == 0 && isDir && !item.HasStream())
522 item.Attrib = 0x10; // some swm archives have system/hidden attributes for root
523 */
524 prop = (UInt32)Get32(metadata + 8);
525 }
526 break;
527 case kpidCTime: if (mainItem->HasMetadata()) GetFileTime(metadata + (_db.IsOldVersion ? 0x18: 0x28), prop); break;
528 case kpidATime: if (mainItem->HasMetadata()) GetFileTime(metadata + (_db.IsOldVersion ? 0x20: 0x30), prop); break;
529 case kpidMTime: if (mainItem->HasMetadata()) GetFileTime(metadata + (_db.IsOldVersion ? 0x28: 0x38), prop); break;
530
531 case kpidINode:
532 if (mainItem->HasMetadata() && !_isOldVersion)
533 {
534 UInt32 attrib = (UInt32)Get32(metadata + 8);
535 if ((attrib & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
536 {
537 // we don't know about that field in OLD WIM format
538 unsigned offset = 0x58; // (_db.IsOldVersion ? 0x30: 0x58);
539 UInt64 val = Get64(metadata + offset);
540 if (val != 0)
541 prop = val;
542 }
543 }
544 break;
545
546 case kpidStreamId:
547 if (item.StreamIndex >= 0)
548 prop = (UInt32)item.StreamIndex;
549 break;
550
551 case kpidMethod:
552 if (si)
553 {
554 const CResource &r = si->Resource;
555 if (r.IsSolid())
556 {
557 if (r.SolidIndex >= 0)
558 {
559 CSolid &ss = _db.Solids[r.SolidIndex];
560 MethodToProp(ss.Method, ss.ChunkSizeBits, prop);
561 }
562 }
563 else
564 {
565 int method = 0;
566 int chunkSizeBits = -1;
567 if (r.IsCompressed())
568 {
569 method = vol->Header.GetMethod();
570 chunkSizeBits = vol->Header.ChunkSizeBits;
571 }
572 MethodToProp(method, chunkSizeBits, prop);
573 }
574 }
575 break;
576
577 case kpidSolid: if (si) prop = si->Resource.IsSolid(); break;
578 case kpidLinks: if (si) prop = (UInt32)si->RefCount; break;
579 #ifdef WIM_DETAILS
580 case kpidVolume: if (si) prop = (UInt32)si->PartNumber; break;
581 case kpidOffset: if (si) prop = (UInt64)si->Resource.Offset; break;
582 #endif
583 }
584 }
585 else
586 {
587 index -= _db.SortedItems.Size();
588 if (index < _numXmlItems)
589 {
590 switch (propID)
591 {
592 case kpidPath:
593 case kpidName: prop = _xmls[index].FileName; break;
594 case kpidIsDir: prop = false; break;
595 case kpidPackSize:
596 case kpidSize: prop = (UInt64)_xmls[index].Data.Size(); break;
597 case kpidMethod: /* prop = k_Method_Copy; */ break;
598 }
599 }
600 else
601 {
602 index -= _numXmlItems;
603 switch (propID)
604 {
605 case kpidPath:
606 case kpidName:
607 if (index < (UInt32)_db.VirtualRoots.Size())
608 prop = _db.Images[_db.VirtualRoots[index]].RootName;
609 else
610 prop = FILES_DIR_NAME;
611 break;
612 case kpidIsDir: prop = true; break;
613 case kpidIsAux: prop = true; break;
614 }
615 }
616 }
617 prop.Detach(value);
618 return S_OK;
619 COM_TRY_END
620 }
621
GetRootProp(PROPID propID,PROPVARIANT * value)622 STDMETHODIMP CHandler::GetRootProp(PROPID propID, PROPVARIANT *value)
623 {
624 // COM_TRY_BEGIN
625 NCOM::CPropVariant prop;
626 if (_db.Images.Size() != 0 && _db.NumExcludededItems != 0)
627 {
628 const CImage &image = _db.Images[_db.IndexOfUserImage];
629 const CItem &item = _db.Items[image.StartItem];
630 if (!item.IsDir || item.ImageIndex != _db.IndexOfUserImage)
631 return E_FAIL;
632 const Byte *metadata = image.Meta + item.Offset;
633
634 switch (propID)
635 {
636 case kpidIsDir: prop = true; break;
637 case kpidAttrib: prop = (UInt32)Get32(metadata + 8); break;
638 case kpidCTime: GetFileTime(metadata + (_db.IsOldVersion ? 0x18: 0x28), prop); break;
639 case kpidATime: GetFileTime(metadata + (_db.IsOldVersion ? 0x20: 0x30), prop); break;
640 case kpidMTime: GetFileTime(metadata + (_db.IsOldVersion ? 0x28: 0x38), prop); break;
641 }
642 }
643 prop.Detach(value);
644 return S_OK;
645 // COM_TRY_END
646 }
647
GetSecurity(UInt32 realIndex,const void ** data,UInt32 * dataSize,UInt32 * propType)648 HRESULT CHandler::GetSecurity(UInt32 realIndex, const void **data, UInt32 *dataSize, UInt32 *propType)
649 {
650 const CItem &item = _db.Items[realIndex];
651 if (item.IsAltStream || item.ImageIndex < 0)
652 return S_OK;
653 const CImage &image = _db.Images[item.ImageIndex];
654 const Byte *metadata = image.Meta + item.Offset;
655 UInt32 securityId = Get32(metadata + 0xC);
656 if (securityId == (UInt32)(Int32)-1)
657 return S_OK;
658 if (securityId >= (UInt32)image.SecurOffsets.Size())
659 return E_FAIL;
660 UInt32 offs = image.SecurOffsets[securityId];
661 UInt32 len = image.SecurOffsets[securityId + 1] - offs;
662 const CByteBuffer &buf = image.Meta;
663 if (offs <= buf.Size() && buf.Size() - offs >= len)
664 {
665 *data = buf + offs;
666 *dataSize = len;
667 *propType = NPropDataType::kRaw;
668 }
669 return S_OK;
670 }
671
GetRootRawProp(PROPID propID,const void ** data,UInt32 * dataSize,UInt32 * propType)672 STDMETHODIMP CHandler::GetRootRawProp(PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
673 {
674 *data = 0;
675 *dataSize = 0;
676 *propType = 0;
677 if (propID == kpidNtSecure && _db.Images.Size() != 0 && _db.NumExcludededItems != 0)
678 {
679 const CImage &image = _db.Images[_db.IndexOfUserImage];
680 const CItem &item = _db.Items[image.StartItem];
681 if (!item.IsDir || item.ImageIndex != _db.IndexOfUserImage)
682 return E_FAIL;
683 return GetSecurity(image.StartItem, data, dataSize, propType);
684 }
685 return S_OK;
686 }
687
688 static const Byte kRawProps[] =
689 {
690 kpidSha1,
691 kpidNtReparse,
692 kpidNtSecure
693 };
694
695
GetNumRawProps(UInt32 * numProps)696 STDMETHODIMP CHandler::GetNumRawProps(UInt32 *numProps)
697 {
698 *numProps = ARRAY_SIZE(kRawProps);
699 return S_OK;
700 }
701
GetRawPropInfo(UInt32 index,BSTR * name,PROPID * propID)702 STDMETHODIMP CHandler::GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID)
703 {
704 *propID = kRawProps[index];
705 *name = 0;
706 return S_OK;
707 }
708
GetParent(UInt32 index,UInt32 * parent,UInt32 * parentType)709 STDMETHODIMP CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType)
710 {
711 *parentType = NParentType::kDir;
712 *parent = (UInt32)(Int32)-1;
713 if (index >= _db.SortedItems.Size())
714 return S_OK;
715
716 const CItem &item = _db.Items[_db.SortedItems[index]];
717
718 if (item.ImageIndex >= 0)
719 {
720 *parentType = item.IsAltStream ? NParentType::kAltStream : NParentType::kDir;
721 if (item.Parent >= 0)
722 {
723 if (_db.ExludedItem != item.Parent)
724 *parent = _db.Items[item.Parent].IndexInSorted;
725 }
726 else
727 {
728 CImage &image = _db.Images[item.ImageIndex];
729 if (image.VirtualRootIndex >= 0)
730 *parent = _db.SortedItems.Size() + _numXmlItems + image.VirtualRootIndex;
731 }
732 }
733 else
734 *parent = _db.SortedItems.Size() + _numXmlItems + _db.VirtualRoots.Size();
735 return S_OK;
736 }
737
GetRawProp(UInt32 index,PROPID propID,const void ** data,UInt32 * dataSize,UInt32 * propType)738 STDMETHODIMP CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
739 {
740 *data = NULL;
741 *dataSize = 0;
742 *propType = 0;
743
744 if (propID == kpidName)
745 {
746 if (index < _db.SortedItems.Size())
747 {
748 const CItem &item = _db.Items[_db.SortedItems[index]];
749 if (item.ImageIndex < 0)
750 return S_OK;
751 const CImage &image = _db.Images[item.ImageIndex];
752 *propType = NPropDataType::kUtf16z;
753 if (image.NumEmptyRootItems != 0 && item.Parent < 0)
754 {
755 const CByteBuffer &buf = _db.Images[item.ImageIndex].RootNameBuf;
756 *data = (void *)(const Byte *)buf;
757 *dataSize = (UInt32)buf.Size();
758 return S_OK;
759 }
760 const Byte *meta = image.Meta + item.Offset +
761 (item.IsAltStream ?
762 (_isOldVersion ? 0x10 : 0x24) :
763 (_isOldVersion ? kDirRecordSizeOld - 2 : kDirRecordSize - 2));
764 *data = (const void *)(meta + 2);
765 *dataSize = (UInt32)Get16(meta) + 2;
766 return S_OK;
767 }
768 {
769 index -= _db.SortedItems.Size();
770 if (index < _numXmlItems)
771 return S_OK;
772 index -= _numXmlItems;
773 if (index >= (UInt32)_db.VirtualRoots.Size())
774 return S_OK;
775 const CByteBuffer &buf = _db.Images[_db.VirtualRoots[index]].RootNameBuf;
776 *data = (void *)(const Byte *)buf;
777 *dataSize = (UInt32)buf.Size();
778 *propType = NPropDataType::kUtf16z;
779 return S_OK;
780 }
781 }
782
783 if (index >= _db.SortedItems.Size())
784 return S_OK;
785
786 unsigned index2 = _db.SortedItems[index];
787
788 if (propID == kpidNtSecure)
789 {
790 return GetSecurity(index2, data, dataSize, propType);
791 }
792
793 const CItem &item = _db.Items[index2];
794 if (propID == kpidSha1)
795 {
796 if (item.StreamIndex >= 0)
797 *data = _db.DataStreams[item.StreamIndex].Hash;
798 else
799 {
800 if (_isOldVersion)
801 return S_OK;
802 const Byte *sha1 = _db.Images[item.ImageIndex].Meta + item.Offset + (item.IsAltStream ? 0x10 : 0x40);
803 if (IsEmptySha(sha1))
804 return S_OK;
805 *data = sha1;
806 }
807 *dataSize = kHashSize;
808 *propType = NPropDataType::kRaw;
809 return S_OK;
810 }
811
812 if (propID == kpidNtReparse && !_isOldVersion)
813 {
814 // we don't know about Reparse field in OLD WIM format
815
816 if (item.StreamIndex < 0)
817 return S_OK;
818 if (index2 >= _db.ItemToReparse.Size())
819 return S_OK;
820 int reparseIndex = _db.ItemToReparse[index2];
821 if (reparseIndex < 0)
822 return S_OK;
823 const CByteBuffer &buf = _db.ReparseItems[reparseIndex];
824 if (buf.Size() == 0)
825 return S_OK;
826 *data = buf;
827 *dataSize = (UInt32)buf.Size();
828 *propType = NPropDataType::kRaw;
829 return S_OK;
830 }
831
832 return S_OK;
833 }
834
835 class CVolumeName
836 {
837 UString _before;
838 UString _after;
839 public:
InitName(const UString & name)840 void InitName(const UString &name)
841 {
842 int dotPos = name.ReverseFind_Dot();
843 if (dotPos < 0)
844 dotPos = name.Len();
845 _before = name.Left(dotPos);
846 _after = name.Ptr(dotPos);
847 }
848
GetNextName(UInt32 index) const849 UString GetNextName(UInt32 index) const
850 {
851 UString s = _before;
852 s.Add_UInt32(index);
853 s += _after;
854 return s;
855 }
856 };
857
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback * callback)858 STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback)
859 {
860 COM_TRY_BEGIN
861
862 Close();
863 {
864 CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
865
866 CVolumeName seqName;
867 if (callback)
868 callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback);
869
870 UInt32 numVolumes = 1;
871
872 for (UInt32 i = 1; i <= numVolumes; i++)
873 {
874 CMyComPtr<IInStream> curStream;
875
876 if (i == 1)
877 curStream = inStream;
878 else
879 {
880 UString fullName = seqName.GetNextName(i);
881 HRESULT result = openVolumeCallback->GetStream(fullName, &curStream);
882 if (result == S_FALSE)
883 continue;
884 if (result != S_OK)
885 return result;
886 if (!curStream)
887 break;
888 }
889
890 CHeader header;
891 HRESULT res = NWim::ReadHeader(curStream, header, _phySize);
892
893 if (res != S_OK)
894 {
895 if (i != 1 && res == S_FALSE)
896 continue;
897 return res;
898 }
899
900 _isArc = true;
901 _bootIndex = header.BootIndex;
902 _version = header.Version;
903 _isOldVersion = header.IsOldVersion();
904 if (_firstVolumeIndex >= 0)
905 if (!header.AreFromOnArchive(_volumes[_firstVolumeIndex].Header))
906 break;
907 if (_volumes.Size() > header.PartNumber && _volumes[header.PartNumber].Stream)
908 break;
909 CWimXml xml;
910 xml.VolIndex = header.PartNumber;
911 res = _db.OpenXml(curStream, header, xml.Data);
912
913 if (res == S_OK)
914 {
915 if (!xml.Parse())
916 _xmlError = true;
917
918 if (xml.IsEncrypted)
919 {
920 _unsupported = true;
921 return S_FALSE;
922 }
923
924 UInt64 totalFiles = xml.GetTotalFilesAndDirs() + xml.Images.Size();
925 totalFiles += 16 + xml.Images.Size() * 4; // we reserve some additional items
926 if (totalFiles >= ((UInt32)1 << 30))
927 totalFiles = 0;
928 res = _db.Open(curStream, header, (unsigned)totalFiles, callback);
929 }
930
931 if (res != S_OK)
932 {
933 if (i != 1 && res == S_FALSE)
934 continue;
935 return res;
936 }
937
938 while (_volumes.Size() <= header.PartNumber)
939 _volumes.AddNew();
940 CVolume &volume = _volumes[header.PartNumber];
941 volume.Header = header;
942 volume.Stream = curStream;
943
944 _firstVolumeIndex = header.PartNumber;
945
946 if (_xmls.IsEmpty() || xml.Data != _xmls[0].Data)
947 {
948 xml.FileName = '[';
949 xml.FileName.Add_UInt32(xml.VolIndex);
950 xml.FileName += "].xml";
951 _xmls.Add(xml);
952 }
953
954 if (i == 1)
955 {
956 if (header.PartNumber != 1)
957 break;
958 if (!openVolumeCallback)
959 break;
960 numVolumes = header.NumParts;
961 {
962 NCOM::CPropVariant prop;
963 RINOK(openVolumeCallback->GetProperty(kpidName, &prop));
964 if (prop.vt != VT_BSTR)
965 break;
966 seqName.InitName(prop.bstrVal);
967 }
968 }
969 }
970
971 RINOK(_db.FillAndCheck(_volumes));
972 int defaultImageIndex = (int)_defaultImageNumber - 1;
973
974 bool showImageNumber = (_db.Images.Size() != 1 && defaultImageIndex < 0);
975 if (!showImageNumber && _set_use_ShowImageNumber)
976 showImageNumber = _set_showImageNumber;
977
978 if (!showImageNumber && _keepMode_ShowImageNumber)
979 showImageNumber = true;
980
981 _showImageNumber = showImageNumber;
982
983 RINOK(_db.GenerateSortedItems(defaultImageIndex, showImageNumber));
984 RINOK(_db.ExtractReparseStreams(_volumes, callback));
985
986 /*
987 wchar_t sz[16];
988 ConvertUInt32ToString(_db.DataStreams.Size(), sz);
989 _nameLenForStreams = MyStringLen(sz);
990 */
991
992 _xmlInComments = !_showImageNumber;
993 _numXmlItems = (_xmlInComments ? 0 : _xmls.Size());
994 _numIgnoreItems = _db.ThereAreDeletedStreams ? 1 : 0;
995 }
996 return S_OK;
997 COM_TRY_END
998 }
999
1000
Close()1001 STDMETHODIMP CHandler::Close()
1002 {
1003 _firstVolumeIndex = -1;
1004 _phySize = 0;
1005 _db.Clear();
1006 _volumes.Clear();
1007 _xmls.Clear();
1008 // _nameLenForStreams = 0;
1009 _xmlInComments = false;
1010 _numXmlItems = 0;
1011 _numIgnoreItems = 0;
1012 _xmlError = false;
1013 _isArc = false;
1014 _unsupported = false;
1015 return S_OK;
1016 }
1017
1018
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)1019 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1020 Int32 testMode, IArchiveExtractCallback *extractCallback)
1021 {
1022 COM_TRY_BEGIN
1023 bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1024
1025 if (allFilesMode)
1026 numItems = _db.SortedItems.Size() + _numXmlItems + _db.VirtualRoots.Size() + _numIgnoreItems;
1027 if (numItems == 0)
1028 return S_OK;
1029
1030 UInt32 i;
1031 UInt64 totalSize = 0;
1032
1033 for (i = 0; i < numItems; i++)
1034 {
1035 UInt32 index = allFilesMode ? i : indices[i];
1036 if (index < _db.SortedItems.Size())
1037 {
1038 int streamIndex = _db.Items[_db.SortedItems[index]].StreamIndex;
1039 if (streamIndex >= 0)
1040 {
1041 const CStreamInfo &si = _db.DataStreams[streamIndex];
1042 totalSize += _db.Get_UnpackSize_of_Resource(si.Resource);
1043 }
1044 }
1045 else
1046 {
1047 index -= _db.SortedItems.Size();
1048 if (index < (UInt32)_numXmlItems)
1049 totalSize += _xmls[index].Data.Size();
1050 }
1051 }
1052
1053 RINOK(extractCallback->SetTotal(totalSize));
1054
1055 UInt64 currentTotalUnPacked = 0;
1056 UInt64 currentItemUnPacked;
1057
1058 int prevSuccessStreamIndex = -1;
1059
1060 CUnpacker unpacker;
1061
1062 CLocalProgress *lps = new CLocalProgress;
1063 CMyComPtr<ICompressProgressInfo> progress = lps;
1064 lps->Init(extractCallback, false);
1065
1066 for (i = 0;; i++,
1067 currentTotalUnPacked += currentItemUnPacked)
1068 {
1069 currentItemUnPacked = 0;
1070
1071 lps->InSize = unpacker.TotalPacked;
1072 lps->OutSize = currentTotalUnPacked;
1073
1074 RINOK(lps->SetCur());
1075
1076 if (i >= numItems)
1077 break;
1078
1079 UInt32 index = allFilesMode ? i : indices[i];
1080 Int32 askMode = testMode ?
1081 NExtract::NAskMode::kTest :
1082 NExtract::NAskMode::kExtract;
1083
1084 CMyComPtr<ISequentialOutStream> realOutStream;
1085 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
1086
1087 if (index >= _db.SortedItems.Size())
1088 {
1089 if (!testMode && !realOutStream)
1090 continue;
1091 RINOK(extractCallback->PrepareOperation(askMode));
1092 index -= _db.SortedItems.Size();
1093 if (index < (UInt32)_numXmlItems)
1094 {
1095 const CByteBuffer &data = _xmls[index].Data;
1096 currentItemUnPacked = data.Size();
1097 if (realOutStream)
1098 {
1099 RINOK(WriteStream(realOutStream, (const Byte *)data, data.Size()));
1100 realOutStream.Release();
1101 }
1102 }
1103 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
1104 continue;
1105 }
1106
1107 const CItem &item = _db.Items[_db.SortedItems[index]];
1108 int streamIndex = item.StreamIndex;
1109 if (streamIndex < 0)
1110 {
1111 if (!item.IsDir)
1112 if (!testMode && !realOutStream)
1113 continue;
1114 RINOK(extractCallback->PrepareOperation(askMode));
1115 realOutStream.Release();
1116 RINOK(extractCallback->SetOperationResult(!item.IsDir && _db.ItemHasStream(item) ?
1117 NExtract::NOperationResult::kDataError :
1118 NExtract::NOperationResult::kOK));
1119 continue;
1120 }
1121
1122 const CStreamInfo &si = _db.DataStreams[streamIndex];
1123 currentItemUnPacked = _db.Get_UnpackSize_of_Resource(si.Resource);
1124 // currentItemPacked = _db.Get_PackSize_of_Resource(streamIndex);
1125
1126 if (!testMode && !realOutStream)
1127 continue;
1128 RINOK(extractCallback->PrepareOperation(askMode));
1129 Int32 opRes = NExtract::NOperationResult::kOK;
1130
1131 if (streamIndex != prevSuccessStreamIndex || realOutStream)
1132 {
1133 Byte digest[kHashSize];
1134 const CVolume &vol = _volumes[si.PartNumber];
1135 bool needDigest = !si.IsEmptyHash();
1136
1137 HRESULT res = unpacker.Unpack(vol.Stream, si.Resource, vol.Header, &_db,
1138 realOutStream, progress, needDigest ? digest : NULL);
1139
1140 if (res == S_OK)
1141 {
1142 if (!needDigest || memcmp(digest, si.Hash, kHashSize) == 0)
1143 prevSuccessStreamIndex = streamIndex;
1144 else
1145 opRes = NExtract::NOperationResult::kCRCError;
1146 }
1147 else if (res == S_FALSE)
1148 opRes = NExtract::NOperationResult::kDataError;
1149 else if (res == E_NOTIMPL)
1150 opRes = NExtract::NOperationResult::kUnsupportedMethod;
1151 else
1152 return res;
1153 }
1154
1155 realOutStream.Release();
1156 RINOK(extractCallback->SetOperationResult(opRes));
1157 }
1158
1159 return S_OK;
1160 COM_TRY_END
1161 }
1162
1163
GetNumberOfItems(UInt32 * numItems)1164 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
1165 {
1166 *numItems = _db.SortedItems.Size() +
1167 _numXmlItems +
1168 _db.VirtualRoots.Size() +
1169 _numIgnoreItems;
1170 return S_OK;
1171 }
1172
CHandler()1173 CHandler::CHandler()
1174 {
1175 _keepMode_ShowImageNumber = false;
1176 InitDefaults();
1177 _xmlError = false;
1178 }
1179
SetProperties(const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps)1180 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
1181 {
1182 InitDefaults();
1183
1184 for (UInt32 i = 0; i < numProps; i++)
1185 {
1186 UString name = names[i];
1187 name.MakeLower_Ascii();
1188 if (name.IsEmpty())
1189 return E_INVALIDARG;
1190
1191 const PROPVARIANT &prop = values[i];
1192
1193 if (name[0] == L'x')
1194 {
1195 // some clients write 'x' property. So we support it
1196 UInt32 level = 0;
1197 RINOK(ParsePropToUInt32(name.Ptr(1), prop, level));
1198 }
1199 else if (name.IsEqualTo("is"))
1200 {
1201 RINOK(PROPVARIANT_to_bool(prop, _set_showImageNumber));
1202 _set_use_ShowImageNumber = true;
1203 }
1204 else if (name.IsEqualTo("im"))
1205 {
1206 UInt32 image = 9;
1207 RINOK(ParsePropToUInt32(L"", prop, image));
1208 _defaultImageNumber = image;
1209 }
1210 else
1211 return E_INVALIDARG;
1212 }
1213 return S_OK;
1214 }
1215
KeepModeForNextOpen()1216 STDMETHODIMP CHandler::KeepModeForNextOpen()
1217 {
1218 _keepMode_ShowImageNumber = _showImageNumber;
1219 return S_OK;
1220 }
1221
1222 }}
1223