xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/Wim/WimHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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