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
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))98 Z7_COM7F_IMF(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 = (int)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 = (int)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.Add_Dot();
174 res.Add_UInt32(ver2);
175 if (ver3 != 0)
176 {
177 res.Add_Dot();
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.Add_Dot();
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 < Z7_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 < Z7_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.Add_Colon();
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 static 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 prop.Set_FtPrec(k_PropVar_TimePrec_100ns);
359 }
360
361
MethodToProp(int method,int chunksSizeBits,NCOM::CPropVariant & prop)362 static void MethodToProp(int method, int chunksSizeBits, NCOM::CPropVariant &prop)
363 {
364 if (method >= 0)
365 {
366 char temp[32];
367
368 if ((unsigned)method < Z7_ARRAY_SIZE(k_Methods))
369 MyStringCopy(temp, k_Methods[(unsigned)method]);
370 else
371 ConvertUInt32ToString((UInt32)(unsigned)method, temp);
372
373 if (chunksSizeBits >= 0)
374 {
375 size_t pos = strlen(temp);
376 temp[pos++] = ':';
377 ConvertUInt32ToString((unsigned)chunksSizeBits, temp + pos);
378 }
379
380 prop = temp;
381 }
382 }
383
384
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))385 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
386 {
387 COM_TRY_BEGIN
388 NCOM::CPropVariant prop;
389
390 if (index < _db.SortedItems.Size())
391 {
392 unsigned realIndex = _db.SortedItems[index];
393 const CItem &item = _db.Items[realIndex];
394 const CStreamInfo *si = NULL;
395 const CVolume *vol = NULL;
396 if (item.StreamIndex >= 0)
397 {
398 si = &_db.DataStreams[item.StreamIndex];
399 vol = &_volumes[si->PartNumber];
400 }
401
402 const CItem *mainItem = &item;
403 if (item.IsAltStream)
404 mainItem = &_db.Items[item.Parent];
405 const Byte *metadata = NULL;
406 if (mainItem->ImageIndex >= 0)
407 metadata = _db.Images[mainItem->ImageIndex].Meta + mainItem->Offset;
408
409 switch (propID)
410 {
411 case kpidPath:
412 if (item.ImageIndex >= 0)
413 _db.GetItemPath(realIndex, _showImageNumber, prop);
414 else
415 {
416 /*
417 while (s.Len() < _nameLenForStreams)
418 s = '0' + s;
419 */
420 /*
421 if (si->Resource.IsFree())
422 s = (AString)("[Free]" STRING_PATH_SEPARATOR) + sz;
423 else
424 */
425 AString s (FILES_DIR_NAME STRING_PATH_SEPARATOR);
426 s.Add_UInt32((UInt32)(Int32)item.StreamIndex);
427 prop = s;
428 }
429 break;
430
431 case kpidName:
432 if (item.ImageIndex >= 0)
433 _db.GetItemName(realIndex, prop);
434 else
435 {
436 char sz[16];
437 ConvertUInt32ToString((UInt32)(Int32)item.StreamIndex, sz);
438 /*
439 AString s = sz;
440 while (s.Len() < _nameLenForStreams)
441 s = '0' + s;
442 */
443 prop = sz;
444 }
445 break;
446
447 case kpidShortName:
448 if (item.ImageIndex >= 0 && !item.IsAltStream)
449 _db.GetShortName(realIndex, prop);
450 break;
451
452 case kpidPackSize:
453 {
454 if (si)
455 {
456 if (!si->Resource.IsSolidSmall())
457 prop = si->Resource.PackSize;
458 else
459 {
460 if (si->Resource.SolidIndex >= 0)
461 {
462 const CSolid &ss = _db.Solids[(unsigned)si->Resource.SolidIndex];
463 if (ss.FirstSmallStream == item.StreamIndex)
464 prop = _db.DataStreams[ss.StreamIndex].Resource.PackSize;
465 }
466 }
467 }
468 else if (!item.IsDir)
469 prop = (UInt64)0;
470
471 break;
472 }
473
474 case kpidSize:
475 {
476 if (si)
477 {
478 if (si->Resource.IsSolid())
479 {
480 if (si->Resource.IsSolidBig())
481 {
482 if (si->Resource.SolidIndex >= 0)
483 {
484 const CSolid &ss = _db.Solids[(unsigned)si->Resource.SolidIndex];
485 prop = ss.UnpackSize;
486 }
487 }
488 else
489 prop = si->Resource.PackSize;
490 }
491 else
492 prop = si->Resource.UnpackSize;
493 }
494 else if (!item.IsDir)
495 prop = (UInt64)0;
496
497 break;
498 }
499
500 case kpidIsDir: prop = item.IsDir; break;
501 case kpidIsAltStream: prop = item.IsAltStream; break;
502 case kpidNumAltStreams:
503 {
504 if (!item.IsAltStream && mainItem->HasMetadata())
505 {
506 UInt32 dirRecordSize = _db.IsOldVersion ? kDirRecordSizeOld : kDirRecordSize;
507 UInt32 numAltStreams = Get16(metadata + dirRecordSize - 6);
508 if (numAltStreams != 0)
509 {
510 if (!item.IsDir)
511 numAltStreams--;
512 prop = numAltStreams;
513 }
514 }
515 break;
516 }
517
518 case kpidAttrib:
519 if (!item.IsAltStream && mainItem->ImageIndex >= 0)
520 {
521 /*
522 if (fileNameLen == 0 && isDir && !item.HasStream())
523 item.Attrib = 0x10; // some swm archives have system/hidden attributes for root
524 */
525 prop = (UInt32)Get32(metadata + 8);
526 }
527 break;
528 case kpidCTime: if (mainItem->HasMetadata()) GetFileTime(metadata + (_db.IsOldVersion ? 0x18: 0x28), prop); break;
529 case kpidATime: if (mainItem->HasMetadata()) GetFileTime(metadata + (_db.IsOldVersion ? 0x20: 0x30), prop); break;
530 case kpidMTime: if (mainItem->HasMetadata()) GetFileTime(metadata + (_db.IsOldVersion ? 0x28: 0x38), prop); break;
531
532 case kpidINode:
533 if (mainItem->HasMetadata() && !_isOldVersion)
534 {
535 UInt32 attrib = (UInt32)Get32(metadata + 8);
536 if ((attrib & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
537 {
538 // we don't know about that field in OLD WIM format
539 unsigned offset = 0x58; // (_db.IsOldVersion ? 0x30: 0x58);
540 UInt64 val = Get64(metadata + offset);
541 if (val != 0)
542 prop = val;
543 }
544 }
545 break;
546
547 case kpidStreamId:
548 if (item.StreamIndex >= 0)
549 prop = (UInt32)item.StreamIndex;
550 break;
551
552 case kpidMethod:
553 if (si)
554 {
555 const CResource &r = si->Resource;
556 if (r.IsSolid())
557 {
558 if (r.SolidIndex >= 0)
559 {
560 CSolid &ss = _db.Solids[r.SolidIndex];
561 MethodToProp(ss.Method, (int)ss.ChunkSizeBits, prop);
562 }
563 }
564 else
565 {
566 int method = 0;
567 int chunkSizeBits = -1;
568 if (r.IsCompressed())
569 {
570 method = (int)vol->Header.GetMethod();
571 chunkSizeBits = (int)vol->Header.ChunkSizeBits;
572 }
573 MethodToProp(method, chunkSizeBits, prop);
574 }
575 }
576 break;
577
578 case kpidSolid: if (si) prop = si->Resource.IsSolid(); break;
579 case kpidLinks: if (si) prop = (UInt32)si->RefCount; break;
580 #ifdef WIM_DETAILS
581 case kpidVolume: if (si) prop = (UInt32)si->PartNumber; break;
582 case kpidOffset: if (si) prop = (UInt64)si->Resource.Offset; break;
583 #endif
584 }
585 }
586 else
587 {
588 index -= _db.SortedItems.Size();
589 if (index < _numXmlItems)
590 {
591 switch (propID)
592 {
593 case kpidPath:
594 case kpidName: prop = _xmls[index].FileName; break;
595 case kpidIsDir: prop = false; break;
596 case kpidPackSize:
597 case kpidSize: prop = (UInt64)_xmls[index].Data.Size(); break;
598 case kpidMethod: /* prop = k_Method_Copy; */ break;
599 }
600 }
601 else
602 {
603 index -= _numXmlItems;
604 switch (propID)
605 {
606 case kpidPath:
607 case kpidName:
608 if (index < (UInt32)_db.VirtualRoots.Size())
609 prop = _db.Images[_db.VirtualRoots[index]].RootName;
610 else
611 prop = FILES_DIR_NAME;
612 break;
613 case kpidIsDir: prop = true; break;
614 case kpidIsAux: prop = true; break;
615 }
616 }
617 }
618 prop.Detach(value);
619 return S_OK;
620 COM_TRY_END
621 }
622
Z7_COM7F_IMF(CHandler::GetRootProp (PROPID propID,PROPVARIANT * value))623 Z7_COM7F_IMF(CHandler::GetRootProp(PROPID propID, PROPVARIANT *value))
624 {
625 // COM_TRY_BEGIN
626 NCOM::CPropVariant prop;
627 if (_db.Images.Size() != 0 && _db.NumExcludededItems != 0)
628 {
629 const CImage &image = _db.Images[_db.IndexOfUserImage];
630 const CItem &item = _db.Items[image.StartItem];
631 if (!item.IsDir || item.ImageIndex != _db.IndexOfUserImage)
632 return E_FAIL;
633 const Byte *metadata = image.Meta + item.Offset;
634
635 switch (propID)
636 {
637 case kpidIsDir: prop = true; break;
638 case kpidAttrib: prop = (UInt32)Get32(metadata + 8); break;
639 case kpidCTime: GetFileTime(metadata + (_db.IsOldVersion ? 0x18: 0x28), prop); break;
640 case kpidATime: GetFileTime(metadata + (_db.IsOldVersion ? 0x20: 0x30), prop); break;
641 case kpidMTime: GetFileTime(metadata + (_db.IsOldVersion ? 0x28: 0x38), prop); break;
642 }
643 }
644 prop.Detach(value);
645 return S_OK;
646 // COM_TRY_END
647 }
648
GetSecurity(UInt32 realIndex,const void ** data,UInt32 * dataSize,UInt32 * propType)649 HRESULT CHandler::GetSecurity(UInt32 realIndex, const void **data, UInt32 *dataSize, UInt32 *propType)
650 {
651 const CItem &item = _db.Items[realIndex];
652 if (item.IsAltStream || item.ImageIndex < 0)
653 return S_OK;
654 const CImage &image = _db.Images[item.ImageIndex];
655 const Byte *metadata = image.Meta + item.Offset;
656 UInt32 securityId = Get32(metadata + 0xC);
657 if (securityId == (UInt32)(Int32)-1)
658 return S_OK;
659 if (securityId >= (UInt32)image.SecurOffsets.Size())
660 return E_FAIL;
661 UInt32 offs = image.SecurOffsets[securityId];
662 UInt32 len = image.SecurOffsets[securityId + 1] - offs;
663 const CByteBuffer &buf = image.Meta;
664 if (offs <= buf.Size() && buf.Size() - offs >= len)
665 {
666 *data = buf + offs;
667 *dataSize = len;
668 *propType = NPropDataType::kRaw;
669 }
670 return S_OK;
671 }
672
Z7_COM7F_IMF(CHandler::GetRootRawProp (PROPID propID,const void ** data,UInt32 * dataSize,UInt32 * propType))673 Z7_COM7F_IMF(CHandler::GetRootRawProp(PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType))
674 {
675 *data = NULL;
676 *dataSize = 0;
677 *propType = 0;
678 if (propID == kpidNtSecure && _db.Images.Size() != 0 && _db.NumExcludededItems != 0)
679 {
680 const CImage &image = _db.Images[_db.IndexOfUserImage];
681 const CItem &item = _db.Items[image.StartItem];
682 if (!item.IsDir || item.ImageIndex != _db.IndexOfUserImage)
683 return E_FAIL;
684 return GetSecurity(image.StartItem, data, dataSize, propType);
685 }
686 return S_OK;
687 }
688
689 static const Byte kRawProps[] =
690 {
691 kpidSha1,
692 kpidNtReparse,
693 kpidNtSecure
694 };
695
696
Z7_COM7F_IMF(CHandler::GetNumRawProps (UInt32 * numProps))697 Z7_COM7F_IMF(CHandler::GetNumRawProps(UInt32 *numProps))
698 {
699 *numProps = Z7_ARRAY_SIZE(kRawProps);
700 return S_OK;
701 }
702
Z7_COM7F_IMF(CHandler::GetRawPropInfo (UInt32 index,BSTR * name,PROPID * propID))703 Z7_COM7F_IMF(CHandler::GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID))
704 {
705 *propID = kRawProps[index];
706 *name = NULL;
707 return S_OK;
708 }
709
Z7_COM7F_IMF(CHandler::GetParent (UInt32 index,UInt32 * parent,UInt32 * parentType))710 Z7_COM7F_IMF(CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType))
711 {
712 *parentType = NParentType::kDir;
713 *parent = (UInt32)(Int32)-1;
714 if (index >= _db.SortedItems.Size())
715 return S_OK;
716
717 const CItem &item = _db.Items[_db.SortedItems[index]];
718
719 if (item.ImageIndex >= 0)
720 {
721 *parentType = item.IsAltStream ? NParentType::kAltStream : NParentType::kDir;
722 if (item.Parent >= 0)
723 {
724 if (_db.ExludedItem != item.Parent)
725 *parent = (unsigned)_db.Items[item.Parent].IndexInSorted;
726 }
727 else
728 {
729 CImage &image = _db.Images[item.ImageIndex];
730 if (image.VirtualRootIndex >= 0)
731 *parent = _db.SortedItems.Size() + _numXmlItems + (unsigned)image.VirtualRootIndex;
732 }
733 }
734 else
735 *parent = _db.SortedItems.Size() + _numXmlItems + _db.VirtualRoots.Size();
736 return S_OK;
737 }
738
Z7_COM7F_IMF(CHandler::GetRawProp (UInt32 index,PROPID propID,const void ** data,UInt32 * dataSize,UInt32 * propType))739 Z7_COM7F_IMF(CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType))
740 {
741 *data = NULL;
742 *dataSize = 0;
743 *propType = 0;
744
745 if (propID == kpidName)
746 {
747 if (index < _db.SortedItems.Size())
748 {
749 const CItem &item = _db.Items[_db.SortedItems[index]];
750 if (item.ImageIndex < 0)
751 return S_OK;
752 const CImage &image = _db.Images[item.ImageIndex];
753 *propType = NPropDataType::kUtf16z;
754 if (image.NumEmptyRootItems != 0 && item.Parent < 0)
755 {
756 const CByteBuffer &buf = _db.Images[item.ImageIndex].RootNameBuf;
757 *data = (void *)(const Byte *)buf;
758 *dataSize = (UInt32)buf.Size();
759 return S_OK;
760 }
761 const Byte *meta = image.Meta + item.Offset +
762 (item.IsAltStream ?
763 (_isOldVersion ? 0x10 : 0x24) :
764 (_isOldVersion ? kDirRecordSizeOld - 2 : kDirRecordSize - 2));
765 *data = (const void *)(meta + 2);
766 *dataSize = (UInt32)Get16(meta) + 2;
767 return S_OK;
768 }
769 {
770 index -= _db.SortedItems.Size();
771 if (index < _numXmlItems)
772 return S_OK;
773 index -= _numXmlItems;
774 if (index >= (UInt32)_db.VirtualRoots.Size())
775 return S_OK;
776 const CByteBuffer &buf = _db.Images[_db.VirtualRoots[index]].RootNameBuf;
777 *data = (void *)(const Byte *)buf;
778 *dataSize = (UInt32)buf.Size();
779 *propType = NPropDataType::kUtf16z;
780 return S_OK;
781 }
782 }
783
784 if (index >= _db.SortedItems.Size())
785 return S_OK;
786
787 unsigned index2 = _db.SortedItems[index];
788
789 if (propID == kpidNtSecure)
790 {
791 return GetSecurity(index2, data, dataSize, propType);
792 }
793
794 const CItem &item = _db.Items[index2];
795 if (propID == kpidSha1)
796 {
797 if (item.StreamIndex >= 0)
798 *data = _db.DataStreams[item.StreamIndex].Hash;
799 else
800 {
801 if (_isOldVersion)
802 return S_OK;
803 const Byte *sha1 = _db.Images[item.ImageIndex].Meta + item.Offset + (item.IsAltStream ? 0x10 : 0x40);
804 if (IsEmptySha(sha1))
805 return S_OK;
806 *data = sha1;
807 }
808 *dataSize = kHashSize;
809 *propType = NPropDataType::kRaw;
810 return S_OK;
811 }
812
813 if (propID == kpidNtReparse && !_isOldVersion)
814 {
815 // we don't know about Reparse field in OLD WIM format
816
817 if (item.StreamIndex < 0)
818 return S_OK;
819 if (index2 >= _db.ItemToReparse.Size())
820 return S_OK;
821 int reparseIndex = _db.ItemToReparse[index2];
822 if (reparseIndex < 0)
823 return S_OK;
824 const CByteBuffer &buf = _db.ReparseItems[reparseIndex];
825 if (buf.Size() == 0)
826 return S_OK;
827 *data = buf;
828 *dataSize = (UInt32)buf.Size();
829 *propType = NPropDataType::kRaw;
830 return S_OK;
831 }
832
833 return S_OK;
834 }
835
836 class CVolumeName
837 {
838 UString _before;
839 UString _after;
840 public:
InitName(const UString & name)841 void InitName(const UString &name)
842 {
843 int dotPos = name.ReverseFind_Dot();
844 if (dotPos < 0)
845 dotPos = (int)name.Len();
846 _before.SetFrom(name.Ptr(), (unsigned)dotPos);
847 _after = name.Ptr(dotPos);
848 }
849
GetNextName(UInt32 index) const850 UString GetNextName(UInt32 index) const
851 {
852 UString s = _before;
853 s.Add_UInt32(index);
854 s += _after;
855 return s;
856 }
857 };
858
Z7_COM7F_IMF(CHandler::Open (IInStream * inStream,const UInt64 *,IArchiveOpenCallback * callback))859 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback))
860 {
861 COM_TRY_BEGIN
862
863 Close();
864 {
865 CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
866
867 CVolumeName seqName;
868 if (callback)
869 callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback);
870
871 UInt32 numVolumes = 1;
872
873 for (UInt32 i = 1; i <= numVolumes; i++)
874 {
875 CMyComPtr<IInStream> curStream;
876
877 if (i == 1)
878 curStream = inStream;
879 else
880 {
881 if (!openVolumeCallback)
882 continue;
883 const UString fullName = seqName.GetNextName(i);
884 const HRESULT result = openVolumeCallback->GetStream(fullName, &curStream);
885 if (result == S_FALSE)
886 continue;
887 if (result != S_OK)
888 return result;
889 if (!curStream)
890 break;
891 }
892
893 CHeader header;
894 HRESULT res = NWim::ReadHeader(curStream, header, _phySize);
895
896 if (res != S_OK)
897 {
898 if (i != 1 && res == S_FALSE)
899 continue;
900 return res;
901 }
902
903 _isArc = true;
904 _bootIndex = header.BootIndex;
905 _version = header.Version;
906 _isOldVersion = header.IsOldVersion();
907 if (_firstVolumeIndex >= 0)
908 if (!header.AreFromOnArchive(_volumes[_firstVolumeIndex].Header))
909 break;
910 if (_volumes.Size() > header.PartNumber && _volumes[header.PartNumber].Stream)
911 break;
912 CWimXml xml;
913 xml.VolIndex = header.PartNumber;
914 res = _db.OpenXml(curStream, header, xml.Data);
915
916 if (res == S_OK)
917 {
918 if (!xml.Parse())
919 _xmlError = true;
920
921 if (xml.IsEncrypted)
922 {
923 _unsupported = true;
924 return S_FALSE;
925 }
926
927 UInt64 totalFiles = xml.GetTotalFilesAndDirs() + xml.Images.Size();
928 totalFiles += 16 + xml.Images.Size() * 4; // we reserve some additional items
929 if (totalFiles >= ((UInt32)1 << 30))
930 totalFiles = 0;
931 res = _db.Open(curStream, header, (unsigned)totalFiles, callback);
932 }
933
934 if (res != S_OK)
935 {
936 if (i != 1 && res == S_FALSE)
937 continue;
938 return res;
939 }
940
941 while (_volumes.Size() <= header.PartNumber)
942 _volumes.AddNew();
943 CVolume &volume = _volumes[header.PartNumber];
944 volume.Header = header;
945 volume.Stream = curStream;
946
947 _firstVolumeIndex = header.PartNumber;
948
949 if (_xmls.IsEmpty() || xml.Data != _xmls[0].Data)
950 {
951 xml.FileName = '[';
952 xml.FileName.Add_UInt32(xml.VolIndex);
953 xml.FileName += "].xml";
954 _xmls.Add(xml);
955 }
956
957 if (i == 1)
958 {
959 if (header.PartNumber != 1)
960 break;
961 if (!openVolumeCallback)
962 break;
963 numVolumes = header.NumParts;
964 {
965 NCOM::CPropVariant prop;
966 RINOK(openVolumeCallback->GetProperty(kpidName, &prop))
967 if (prop.vt != VT_BSTR)
968 break;
969 seqName.InitName(prop.bstrVal);
970 }
971 }
972 }
973
974 RINOK(_db.FillAndCheck(_volumes))
975 int defaultImageIndex = (int)_defaultImageNumber - 1;
976
977 bool showImageNumber = (_db.Images.Size() != 1 && defaultImageIndex < 0);
978 if (!showImageNumber && _set_use_ShowImageNumber)
979 showImageNumber = _set_showImageNumber;
980
981 if (!showImageNumber && _keepMode_ShowImageNumber)
982 showImageNumber = true;
983
984 _showImageNumber = showImageNumber;
985
986 RINOK(_db.GenerateSortedItems(defaultImageIndex, showImageNumber))
987 RINOK(_db.ExtractReparseStreams(_volumes, callback))
988
989 /*
990 wchar_t sz[16];
991 ConvertUInt32ToString(_db.DataStreams.Size(), sz);
992 _nameLenForStreams = MyStringLen(sz);
993 */
994
995 _xmlInComments = !_showImageNumber;
996 _numXmlItems = (_xmlInComments ? 0 : _xmls.Size());
997 _numIgnoreItems = _db.ThereAreDeletedStreams ? 1 : 0;
998 }
999 return S_OK;
1000 COM_TRY_END
1001 }
1002
1003
Z7_COM7F_IMF(CHandler::Close ())1004 Z7_COM7F_IMF(CHandler::Close())
1005 {
1006 _firstVolumeIndex = -1;
1007 _phySize = 0;
1008 _db.Clear();
1009 _volumes.Clear();
1010 _xmls.Clear();
1011 // _nameLenForStreams = 0;
1012 _xmlInComments = false;
1013 _numXmlItems = 0;
1014 _numIgnoreItems = 0;
1015 _xmlError = false;
1016 _isArc = false;
1017 _unsupported = false;
1018 return S_OK;
1019 }
1020
1021
Z7_COM7F_IMF(CHandler::Extract (const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback))1022 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1023 Int32 testMode, IArchiveExtractCallback *extractCallback))
1024 {
1025 COM_TRY_BEGIN
1026 const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1027
1028 if (allFilesMode)
1029 numItems = _db.SortedItems.Size() + _numXmlItems + _db.VirtualRoots.Size() + _numIgnoreItems;
1030 if (numItems == 0)
1031 return S_OK;
1032
1033 UInt32 i;
1034 UInt64 totalSize = 0;
1035
1036 for (i = 0; i < numItems; i++)
1037 {
1038 UInt32 index = allFilesMode ? i : indices[i];
1039 if (index < _db.SortedItems.Size())
1040 {
1041 int streamIndex = _db.Items[_db.SortedItems[index]].StreamIndex;
1042 if (streamIndex >= 0)
1043 {
1044 const CStreamInfo &si = _db.DataStreams[streamIndex];
1045 totalSize += _db.Get_UnpackSize_of_Resource(si.Resource);
1046 }
1047 }
1048 else
1049 {
1050 index -= _db.SortedItems.Size();
1051 if (index < _numXmlItems)
1052 totalSize += _xmls[index].Data.Size();
1053 }
1054 }
1055
1056 RINOK(extractCallback->SetTotal(totalSize))
1057
1058 totalSize = 0;
1059 UInt64 currentItemUnPacked;
1060
1061 int prevSuccessStreamIndex = -1;
1062
1063 CUnpacker unpacker;
1064
1065 CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
1066 lps->Init(extractCallback, false);
1067
1068 for (i = 0;; i++,
1069 totalSize += currentItemUnPacked)
1070 {
1071 currentItemUnPacked = 0;
1072 lps->InSize = unpacker.TotalPacked;
1073 lps->OutSize = totalSize;
1074 RINOK(lps->SetCur())
1075 if (i >= numItems)
1076 break;
1077
1078 UInt32 index = allFilesMode ? i : indices[i];
1079 const Int32 askMode = testMode ?
1080 NExtract::NAskMode::kTest :
1081 NExtract::NAskMode::kExtract;
1082
1083 CMyComPtr<ISequentialOutStream> realOutStream;
1084 RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
1085
1086 if (index >= _db.SortedItems.Size())
1087 {
1088 if (!testMode && !realOutStream)
1089 continue;
1090 RINOK(extractCallback->PrepareOperation(askMode))
1091 index -= _db.SortedItems.Size();
1092 if (index < _numXmlItems)
1093 {
1094 const CByteBuffer &data = _xmls[index].Data;
1095 currentItemUnPacked = data.Size();
1096 if (realOutStream)
1097 {
1098 RINOK(WriteStream(realOutStream, (const Byte *)data, data.Size()))
1099 realOutStream.Release();
1100 }
1101 }
1102 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
1103 continue;
1104 }
1105
1106 const CItem &item = _db.Items[_db.SortedItems[index]];
1107 const int streamIndex = item.StreamIndex;
1108 if (streamIndex < 0)
1109 {
1110 if (!item.IsDir)
1111 if (!testMode && !realOutStream)
1112 continue;
1113 RINOK(extractCallback->PrepareOperation(askMode))
1114 realOutStream.Release();
1115 RINOK(extractCallback->SetOperationResult(!item.IsDir && _db.ItemHasStream(item) ?
1116 NExtract::NOperationResult::kDataError :
1117 NExtract::NOperationResult::kOK))
1118 continue;
1119 }
1120
1121 const CStreamInfo &si = _db.DataStreams[streamIndex];
1122 currentItemUnPacked = _db.Get_UnpackSize_of_Resource(si.Resource);
1123 // currentItemPacked = _db.Get_PackSize_of_Resource(streamIndex);
1124
1125 if (!testMode && !realOutStream)
1126 continue;
1127 RINOK(extractCallback->PrepareOperation(askMode))
1128 Int32 opRes = NExtract::NOperationResult::kOK;
1129
1130 if (streamIndex != prevSuccessStreamIndex || realOutStream)
1131 {
1132 Byte digest[kHashSize];
1133 const CVolume &vol = _volumes[si.PartNumber];
1134 const bool needDigest = !si.IsEmptyHash() && !_disable_Sha1Check;
1135 const HRESULT res = unpacker.Unpack(vol.Stream, si.Resource, vol.Header, &_db,
1136 realOutStream, lps, needDigest ? digest : NULL);
1137
1138 if (res == S_OK)
1139 {
1140 if (!needDigest || memcmp(digest, si.Hash, kHashSize) == 0)
1141 prevSuccessStreamIndex = streamIndex;
1142 else
1143 opRes = NExtract::NOperationResult::kCRCError;
1144 }
1145 else if (res == S_FALSE)
1146 opRes = NExtract::NOperationResult::kDataError;
1147 else if (res == E_NOTIMPL)
1148 opRes = NExtract::NOperationResult::kUnsupportedMethod;
1149 else
1150 return res;
1151 }
1152
1153 realOutStream.Release();
1154 RINOK(extractCallback->SetOperationResult(opRes))
1155 }
1156
1157 return S_OK;
1158 COM_TRY_END
1159 }
1160
1161
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))1162 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
1163 {
1164 *numItems = _db.SortedItems.Size() +
1165 _numXmlItems +
1166 _db.VirtualRoots.Size() +
1167 _numIgnoreItems;
1168 return S_OK;
1169 }
1170
CHandler()1171 CHandler::CHandler()
1172 {
1173 _keepMode_ShowImageNumber = false;
1174 InitDefaults();
1175 _xmlError = false;
1176 }
1177
Z7_COM7F_IMF(CHandler::SetProperties (const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps))1178 Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
1179 {
1180 InitDefaults();
1181
1182 for (UInt32 i = 0; i < numProps; i++)
1183 {
1184 UString name = names[i];
1185 name.MakeLower_Ascii();
1186 if (name.IsEmpty())
1187 return E_INVALIDARG;
1188
1189 const PROPVARIANT &prop = values[i];
1190
1191 if (name[0] == L'x')
1192 {
1193 // some clients write 'x' property. So we support it
1194 UInt32 level = 0;
1195 RINOK(ParsePropToUInt32(name.Ptr(1), prop, level))
1196 }
1197 else if (name.IsEqualTo("is"))
1198 {
1199 RINOK(PROPVARIANT_to_bool(prop, _set_showImageNumber))
1200 _set_use_ShowImageNumber = true;
1201 }
1202 else if (name.IsEqualTo("im"))
1203 {
1204 UInt32 image = 9;
1205 RINOK(ParsePropToUInt32(L"", prop, image))
1206 _defaultImageNumber = (int)image;
1207 }
1208 else if (name.IsPrefixedBy_Ascii_NoCase("mt"))
1209 {
1210 }
1211 else if (name.IsPrefixedBy_Ascii_NoCase("memuse"))
1212 {
1213 }
1214 else if (name.IsPrefixedBy_Ascii_NoCase("crc"))
1215 {
1216 name.Delete(0, 3);
1217 UInt32 crcSize = 1;
1218 RINOK(ParsePropToUInt32(name, prop, crcSize))
1219 _disable_Sha1Check = (crcSize == 0);
1220 }
1221 else
1222 {
1223 bool processed = false;
1224 RINOK(_timeOptions.Parse(name, prop, processed))
1225 if (!processed)
1226 return E_INVALIDARG;
1227 }
1228 }
1229 return S_OK;
1230 }
1231
Z7_COM7F_IMF(CHandler::KeepModeForNextOpen ())1232 Z7_COM7F_IMF(CHandler::KeepModeForNextOpen())
1233 {
1234 _keepMode_ShowImageNumber = _showImageNumber;
1235 return S_OK;
1236 }
1237
1238 }}
1239