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 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 }
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         if (!openVolumeCallback)
881           continue;
882         const UString fullName = seqName.GetNextName(i);
883         const HRESULT result = openVolumeCallback->GetStream(fullName, &curStream);
884         if (result == S_FALSE)
885           continue;
886         if (result != S_OK)
887           return result;
888         if (!curStream)
889           break;
890       }
891 
892       CHeader header;
893       HRESULT res = NWim::ReadHeader(curStream, header, _phySize);
894 
895       if (res != S_OK)
896       {
897         if (i != 1 && res == S_FALSE)
898           continue;
899         return res;
900       }
901 
902       _isArc = true;
903       _bootIndex = header.BootIndex;
904       _version = header.Version;
905       _isOldVersion = header.IsOldVersion();
906       if (_firstVolumeIndex >= 0)
907         if (!header.AreFromOnArchive(_volumes[_firstVolumeIndex].Header))
908           break;
909       if (_volumes.Size() > header.PartNumber && _volumes[header.PartNumber].Stream)
910         break;
911       CWimXml xml;
912       xml.VolIndex = header.PartNumber;
913       res = _db.OpenXml(curStream, header, xml.Data);
914 
915       if (res == S_OK)
916       {
917         if (!xml.Parse())
918           _xmlError = true;
919 
920         if (xml.IsEncrypted)
921         {
922           _unsupported = true;
923           return S_FALSE;
924         }
925 
926         UInt64 totalFiles = xml.GetTotalFilesAndDirs() + xml.Images.Size();
927         totalFiles += 16 + xml.Images.Size() * 4; // we reserve some additional items
928         if (totalFiles >= ((UInt32)1 << 30))
929           totalFiles = 0;
930         res = _db.Open(curStream, header, (unsigned)totalFiles, callback);
931       }
932 
933       if (res != S_OK)
934       {
935         if (i != 1 && res == S_FALSE)
936           continue;
937         return res;
938       }
939 
940       while (_volumes.Size() <= header.PartNumber)
941         _volumes.AddNew();
942       CVolume &volume = _volumes[header.PartNumber];
943       volume.Header = header;
944       volume.Stream = curStream;
945 
946       _firstVolumeIndex = header.PartNumber;
947 
948       if (_xmls.IsEmpty() || xml.Data != _xmls[0].Data)
949       {
950         xml.FileName = '[';
951         xml.FileName.Add_UInt32(xml.VolIndex);
952         xml.FileName += "].xml";
953         _xmls.Add(xml);
954       }
955 
956       if (i == 1)
957       {
958         if (header.PartNumber != 1)
959           break;
960         if (!openVolumeCallback)
961           break;
962         numVolumes = header.NumParts;
963         {
964           NCOM::CPropVariant prop;
965           RINOK(openVolumeCallback->GetProperty(kpidName, &prop));
966           if (prop.vt != VT_BSTR)
967             break;
968           seqName.InitName(prop.bstrVal);
969         }
970       }
971     }
972 
973     RINOK(_db.FillAndCheck(_volumes));
974     int defaultImageIndex = (int)_defaultImageNumber - 1;
975 
976     bool showImageNumber = (_db.Images.Size() != 1 && defaultImageIndex < 0);
977     if (!showImageNumber && _set_use_ShowImageNumber)
978       showImageNumber = _set_showImageNumber;
979 
980     if (!showImageNumber && _keepMode_ShowImageNumber)
981       showImageNumber = true;
982 
983     _showImageNumber = showImageNumber;
984 
985     RINOK(_db.GenerateSortedItems(defaultImageIndex, showImageNumber));
986     RINOK(_db.ExtractReparseStreams(_volumes, callback));
987 
988     /*
989     wchar_t sz[16];
990     ConvertUInt32ToString(_db.DataStreams.Size(), sz);
991     _nameLenForStreams = MyStringLen(sz);
992     */
993 
994     _xmlInComments = !_showImageNumber;
995     _numXmlItems = (_xmlInComments ? 0 : _xmls.Size());
996     _numIgnoreItems = _db.ThereAreDeletedStreams ? 1 : 0;
997   }
998   return S_OK;
999   COM_TRY_END
1000 }
1001 
1002 
Close()1003 STDMETHODIMP CHandler::Close()
1004 {
1005   _firstVolumeIndex = -1;
1006   _phySize = 0;
1007   _db.Clear();
1008   _volumes.Clear();
1009   _xmls.Clear();
1010   // _nameLenForStreams = 0;
1011   _xmlInComments = false;
1012   _numXmlItems = 0;
1013   _numIgnoreItems = 0;
1014   _xmlError = false;
1015   _isArc = false;
1016   _unsupported = false;
1017   return S_OK;
1018 }
1019 
1020 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)1021 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1022     Int32 testMode, IArchiveExtractCallback *extractCallback)
1023 {
1024   COM_TRY_BEGIN
1025   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1026 
1027   if (allFilesMode)
1028     numItems = _db.SortedItems.Size() + _numXmlItems + _db.VirtualRoots.Size() + _numIgnoreItems;
1029   if (numItems == 0)
1030     return S_OK;
1031 
1032   UInt32 i;
1033   UInt64 totalSize = 0;
1034 
1035   for (i = 0; i < numItems; i++)
1036   {
1037     UInt32 index = allFilesMode ? i : indices[i];
1038     if (index < _db.SortedItems.Size())
1039     {
1040       int streamIndex = _db.Items[_db.SortedItems[index]].StreamIndex;
1041       if (streamIndex >= 0)
1042       {
1043         const CStreamInfo &si = _db.DataStreams[streamIndex];
1044         totalSize += _db.Get_UnpackSize_of_Resource(si.Resource);
1045       }
1046     }
1047     else
1048     {
1049       index -= _db.SortedItems.Size();
1050       if (index < (UInt32)_numXmlItems)
1051         totalSize += _xmls[index].Data.Size();
1052     }
1053   }
1054 
1055   RINOK(extractCallback->SetTotal(totalSize));
1056 
1057   UInt64 currentTotalUnPacked = 0;
1058   UInt64 currentItemUnPacked;
1059 
1060   int prevSuccessStreamIndex = -1;
1061 
1062   CUnpacker unpacker;
1063 
1064   CLocalProgress *lps = new CLocalProgress;
1065   CMyComPtr<ICompressProgressInfo> progress = lps;
1066   lps->Init(extractCallback, false);
1067 
1068   for (i = 0;; i++,
1069       currentTotalUnPacked += currentItemUnPacked)
1070   {
1071     currentItemUnPacked = 0;
1072 
1073     lps->InSize = unpacker.TotalPacked;
1074     lps->OutSize = currentTotalUnPacked;
1075 
1076     RINOK(lps->SetCur());
1077 
1078     if (i >= numItems)
1079       break;
1080 
1081     UInt32 index = allFilesMode ? i : indices[i];
1082     Int32 askMode = testMode ?
1083         NExtract::NAskMode::kTest :
1084         NExtract::NAskMode::kExtract;
1085 
1086     CMyComPtr<ISequentialOutStream> realOutStream;
1087     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
1088 
1089     if (index >= _db.SortedItems.Size())
1090     {
1091       if (!testMode && !realOutStream)
1092         continue;
1093       RINOK(extractCallback->PrepareOperation(askMode));
1094       index -= _db.SortedItems.Size();
1095       if (index < (UInt32)_numXmlItems)
1096       {
1097         const CByteBuffer &data = _xmls[index].Data;
1098         currentItemUnPacked = data.Size();
1099         if (realOutStream)
1100         {
1101           RINOK(WriteStream(realOutStream, (const Byte *)data, data.Size()));
1102           realOutStream.Release();
1103         }
1104       }
1105       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
1106       continue;
1107     }
1108 
1109     const CItem &item = _db.Items[_db.SortedItems[index]];
1110     int streamIndex = item.StreamIndex;
1111     if (streamIndex < 0)
1112     {
1113       if (!item.IsDir)
1114         if (!testMode && !realOutStream)
1115           continue;
1116       RINOK(extractCallback->PrepareOperation(askMode));
1117       realOutStream.Release();
1118       RINOK(extractCallback->SetOperationResult(!item.IsDir && _db.ItemHasStream(item) ?
1119           NExtract::NOperationResult::kDataError :
1120           NExtract::NOperationResult::kOK));
1121       continue;
1122     }
1123 
1124     const CStreamInfo &si = _db.DataStreams[streamIndex];
1125     currentItemUnPacked = _db.Get_UnpackSize_of_Resource(si.Resource);
1126     // currentItemPacked = _db.Get_PackSize_of_Resource(streamIndex);
1127 
1128     if (!testMode && !realOutStream)
1129       continue;
1130     RINOK(extractCallback->PrepareOperation(askMode));
1131     Int32 opRes = NExtract::NOperationResult::kOK;
1132 
1133     if (streamIndex != prevSuccessStreamIndex || realOutStream)
1134     {
1135       Byte digest[kHashSize];
1136       const CVolume &vol = _volumes[si.PartNumber];
1137       bool needDigest = !si.IsEmptyHash();
1138 
1139       HRESULT res = unpacker.Unpack(vol.Stream, si.Resource, vol.Header, &_db,
1140           realOutStream, progress, needDigest ? digest : NULL);
1141 
1142       if (res == S_OK)
1143       {
1144         if (!needDigest || memcmp(digest, si.Hash, kHashSize) == 0)
1145           prevSuccessStreamIndex = streamIndex;
1146         else
1147           opRes = NExtract::NOperationResult::kCRCError;
1148       }
1149       else if (res == S_FALSE)
1150         opRes = NExtract::NOperationResult::kDataError;
1151       else if (res == E_NOTIMPL)
1152         opRes = NExtract::NOperationResult::kUnsupportedMethod;
1153       else
1154         return res;
1155     }
1156 
1157     realOutStream.Release();
1158     RINOK(extractCallback->SetOperationResult(opRes));
1159   }
1160 
1161   return S_OK;
1162   COM_TRY_END
1163 }
1164 
1165 
GetNumberOfItems(UInt32 * numItems)1166 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
1167 {
1168   *numItems = _db.SortedItems.Size() +
1169       _numXmlItems +
1170       _db.VirtualRoots.Size() +
1171       _numIgnoreItems;
1172   return S_OK;
1173 }
1174 
CHandler()1175 CHandler::CHandler()
1176 {
1177   _keepMode_ShowImageNumber = false;
1178   InitDefaults();
1179   _xmlError = false;
1180 }
1181 
SetProperties(const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps)1182 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
1183 {
1184   InitDefaults();
1185 
1186   for (UInt32 i = 0; i < numProps; i++)
1187   {
1188     UString name = names[i];
1189     name.MakeLower_Ascii();
1190     if (name.IsEmpty())
1191       return E_INVALIDARG;
1192 
1193     const PROPVARIANT &prop = values[i];
1194 
1195     if (name[0] == L'x')
1196     {
1197       // some clients write 'x' property. So we support it
1198       UInt32 level = 0;
1199       RINOK(ParsePropToUInt32(name.Ptr(1), prop, level));
1200     }
1201     else if (name.IsEqualTo("is"))
1202     {
1203       RINOK(PROPVARIANT_to_bool(prop, _set_showImageNumber));
1204       _set_use_ShowImageNumber = true;
1205     }
1206     else if (name.IsEqualTo("im"))
1207     {
1208       UInt32 image = 9;
1209       RINOK(ParsePropToUInt32(L"", prop, image));
1210       _defaultImageNumber = image;
1211     }
1212     else if (name.IsPrefixedBy_Ascii_NoCase("mt"))
1213     {
1214     }
1215     else if (name.IsPrefixedBy_Ascii_NoCase("memuse"))
1216     {
1217     }
1218     else
1219       return E_INVALIDARG;
1220   }
1221   return S_OK;
1222 }
1223 
KeepModeForNextOpen()1224 STDMETHODIMP CHandler::KeepModeForNextOpen()
1225 {
1226   _keepMode_ShowImageNumber = _showImageNumber;
1227   return S_OK;
1228 }
1229 
1230 }}
1231