xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/Wim/WimHandlerOut.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // WimHandlerOut.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/IntToString.h"
7 #include "../../../Common/MyBuffer2.h"
8 #include "../../../Common/StringToInt.h"
9 #include "../../../Common/UTFConvert.h"
10 #include "../../../Common/Wildcard.h"
11 
12 #include "../../../Windows/PropVariant.h"
13 #include "../../../Windows/TimeUtils.h"
14 
15 #include "../../Common/LimitedStreams.h"
16 #include "../../Common/ProgressUtils.h"
17 #include "../../Common/StreamUtils.h"
18 #include "../../Common/UniqBlocks.h"
19 
20 #include "../../Crypto/RandGen.h"
21 #include "../../Crypto/Sha1Cls.h"
22 
23 #include "../Common/OutStreamWithSha1.h"
24 
25 #include "WimHandler.h"
26 
27 using namespace NWindows;
28 
29 namespace NArchive {
30 namespace NWim {
31 
32 static const unsigned k_NumSubVectors_Bits = 12; // must be <= 16
33 
34 struct CSortedIndex
35 {
36   CObjectVector<CUIntVector> Vectors;
37 
CSortedIndexNArchive::NWim::CSortedIndex38   CSortedIndex()
39   {
40     const unsigned k_NumSubVectors = 1 << k_NumSubVectors_Bits;
41     Vectors.ClearAndReserve(k_NumSubVectors);
42     for (unsigned i = 0; i < k_NumSubVectors; i++)
43       Vectors.AddNew();
44   }
45 };
46 
AddUniqHash(const CStreamInfo * streams,CSortedIndex & sorted2,const Byte * h,int streamIndexForInsert)47 static int AddUniqHash(const CStreamInfo *streams, CSortedIndex &sorted2, const Byte *h, int streamIndexForInsert)
48 {
49   const unsigned hash = (((unsigned)h[0] << 8) | (unsigned)h[1]) >> (16 - k_NumSubVectors_Bits);
50   CUIntVector &sorted = sorted2.Vectors[hash];
51   unsigned left = 0, right = sorted.Size();
52   while (left != right)
53   {
54     const unsigned mid = (unsigned)(((size_t)left + (size_t)right) / 2);
55     const unsigned index = sorted[mid];
56     const Byte *hash2 = streams[index].Hash;
57 
58     unsigned i;
59     for (i = 0; i < kHashSize; i++)
60       if (h[i] != hash2[i])
61         break;
62 
63     if (i == kHashSize)
64       return (int)index;
65 
66     if (h[i] < hash2[i])
67       right = mid;
68     else
69       left = mid + 1;
70   }
71 
72   if (streamIndexForInsert != -1)
73     sorted.Insert(left, (unsigned)streamIndexForInsert);
74 
75   return -1;
76 }
77 
78 
79 struct CAltStream
80 {
81   int UpdateIndex;
82   int HashIndex;
83   UInt64 Size;
84   UString Name;
85   bool Skip;
86 
CAltStreamNArchive::NWim::CAltStream87   CAltStream(): UpdateIndex(-1), HashIndex(-1), Skip(false) {}
88 };
89 
90 
91 struct CMetaItem
92 {
93   int UpdateIndex;
94   int HashIndex;
95 
96   UInt64 Size;
97   FILETIME CTime;
98   FILETIME ATime;
99   FILETIME MTime;
100   UInt64 FileID;
101   UInt64 VolID;
102 
103   UString Name;
104   UString ShortName;
105 
106   UInt32 Attrib;
107   int SecurityId;       // -1: means no secutity ID
108   bool IsDir;
109   bool Skip;
110   unsigned NumSkipAltStreams;
111   CObjectVector<CAltStream> AltStreams;
112 
113   CByteBuffer Reparse;
114 
GetNumAltStreamsNArchive::NWim::CMetaItem115   unsigned GetNumAltStreams() const { return AltStreams.Size() - NumSkipAltStreams; }
CMetaItemNArchive::NWim::CMetaItem116   CMetaItem():
117         UpdateIndex(-1)
118       , HashIndex(-1)
119       , Size(0)
120       , FileID(0)
121       , VolID(0)
122       , Attrib(0)
123       , SecurityId(-1)
124       , IsDir(false)
125       , Skip(false)
126       , NumSkipAltStreams(0)
127   {
128     FILETIME_Clear(CTime);
129     FILETIME_Clear(ATime);
130     FILETIME_Clear(MTime);
131   }
132 };
133 
134 
Compare_HardLink_MetaItems(const CMetaItem & a1,const CMetaItem & a2)135 static int Compare_HardLink_MetaItems(const CMetaItem &a1, const CMetaItem &a2)
136 {
137   if (a1.VolID < a2.VolID) return -1;
138   if (a1.VolID > a2.VolID) return 1;
139   if (a1.FileID < a2.FileID) return -1;
140   if (a1.FileID > a2.FileID) return 1;
141   if (a1.Size < a2.Size) return -1;
142   if (a1.Size > a2.Size) return 1;
143   return ::CompareFileTime(&a1.MTime, &a2.MTime);
144 }
145 
146 
AddToHardLinkList(const CObjectVector<CMetaItem> & metaItems,unsigned indexOfItem,CUIntVector & indexes)147 static int AddToHardLinkList(const CObjectVector<CMetaItem> &metaItems, unsigned indexOfItem, CUIntVector &indexes)
148 {
149   const CMetaItem &mi = metaItems[indexOfItem];
150   unsigned left = 0, right = indexes.Size();
151   while (left != right)
152   {
153     const unsigned mid = (unsigned)(((size_t)left + (size_t)right) / 2);
154     const unsigned index = indexes[mid];
155     const int comp = Compare_HardLink_MetaItems(mi, metaItems[index]);
156     if (comp == 0)
157       return (int)index;
158     if (comp < 0)
159       right = mid;
160     else
161       left = mid + 1;
162   }
163   indexes.Insert(left, indexOfItem);
164   return -1;
165 }
166 
167 
168 struct CUpdateItem
169 {
170   unsigned CallbackIndex; // index in callback
171 
172   int MetaIndex;          // index in in MetaItems[]
173 
174   int AltStreamIndex;     // index in CMetaItem::AltStreams vector
175                           // -1: if not alt stream?
176 
177   int InArcIndex;         // >= 0, if we use OLD Data
178                           //   -1, if we use NEW Data
179 
CUpdateItemNArchive::NWim::CUpdateItem180   CUpdateItem(): MetaIndex(-1), AltStreamIndex(-1), InArcIndex(-1) {}
181 };
182 
183 
184 struct CDir
185 {
186   int MetaIndex;
187   CObjectVector<CDir> Dirs;
188   CUIntVector Files; // indexes in MetaItems[]
189 
CDirNArchive::NWim::CDir190   CDir(): MetaIndex(-1) {}
191   unsigned GetNumDirs() const;
192   unsigned GetNumFiles() const;
193   UInt64 GetTotalSize(const CObjectVector<CMetaItem> &metaItems) const;
194   bool FindDir(const CObjectVector<CMetaItem> &items, const UString &name, unsigned &index);
195 };
196 
197 /* imagex counts Junctions as files (not as dirs).
198    We suppose that it's not correct */
199 
GetNumDirs() const200 unsigned CDir::GetNumDirs() const
201 {
202   unsigned num = Dirs.Size();
203   FOR_VECTOR (i, Dirs)
204     num += Dirs[i].GetNumDirs();
205   return num;
206 }
207 
GetNumFiles() const208 unsigned CDir::GetNumFiles() const
209 {
210   unsigned num = Files.Size();
211   FOR_VECTOR (i, Dirs)
212     num += Dirs[i].GetNumFiles();
213   return num;
214 }
215 
GetTotalSize(const CObjectVector<CMetaItem> & metaItems) const216 UInt64 CDir::GetTotalSize(const CObjectVector<CMetaItem> &metaItems) const
217 {
218   UInt64 sum = 0;
219   unsigned i;
220   for (i = 0; i < Files.Size(); i++)
221     sum += metaItems[Files[i]].Size;
222   for (i = 0; i < Dirs.Size(); i++)
223     sum += Dirs[i].GetTotalSize(metaItems);
224   return sum;
225 }
226 
FindDir(const CObjectVector<CMetaItem> & items,const UString & name,unsigned & index)227 bool CDir::FindDir(const CObjectVector<CMetaItem> &items, const UString &name, unsigned &index)
228 {
229   unsigned left = 0, right = Dirs.Size();
230   while (left != right)
231   {
232     const unsigned mid = (unsigned)(((size_t)left + (size_t)right) / 2);
233     const int comp = CompareFileNames(name, items[Dirs[mid].MetaIndex].Name);
234     if (comp == 0)
235     {
236       index = mid;
237       return true;
238     }
239     if (comp < 0)
240       right = mid;
241     else
242       left = mid + 1;
243   }
244   index = left;
245   return false;
246 }
247 
248 
Z7_COM7F_IMF(CHandler::GetFileTimeType (UInt32 * type))249 Z7_COM7F_IMF(CHandler::GetFileTimeType(UInt32 *type))
250 {
251   *type = NFileTimeType::kWindows;
252   return S_OK;
253 }
254 
255 
GetOutProperty(IArchiveUpdateCallback * callback,UInt32 callbackIndex,Int32 arcIndex,PROPID propID,PROPVARIANT * value)256 HRESULT CHandler::GetOutProperty(IArchiveUpdateCallback *callback, UInt32 callbackIndex, Int32 arcIndex, PROPID propID, PROPVARIANT *value)
257 {
258   if (arcIndex != -1)
259     return GetProperty((UInt32)arcIndex, propID, value);
260   return callback->GetProperty(callbackIndex, propID, value);
261 }
262 
263 
GetTime(IArchiveUpdateCallback * callback,UInt32 callbackIndex,Int32 arcIndex,PROPID propID,FILETIME & ft)264 HRESULT CHandler::GetTime(IArchiveUpdateCallback *callback, UInt32 callbackIndex, Int32 arcIndex, PROPID propID, FILETIME &ft)
265 {
266   ft.dwLowDateTime = ft.dwHighDateTime = 0;
267   NCOM::CPropVariant prop;
268   RINOK(GetOutProperty(callback, callbackIndex, arcIndex, propID, &prop))
269   if (prop.vt == VT_FILETIME)
270     ft = prop.filetime;
271   else if (prop.vt != VT_EMPTY)
272     return E_INVALIDARG;
273   return S_OK;
274 }
275 
276 
GetRootTime(IArchiveGetRootProps * callback,IArchiveGetRootProps * arcRoot,PROPID propID,FILETIME & ft)277 static HRESULT GetRootTime(
278     IArchiveGetRootProps *callback,
279     IArchiveGetRootProps *arcRoot,
280     PROPID propID, FILETIME &ft)
281 {
282   NCOM::CPropVariant prop;
283   if (callback)
284   {
285     RINOK(callback->GetRootProp(propID, &prop))
286     if (prop.vt == VT_FILETIME)
287     {
288       ft = prop.filetime;
289       return S_OK;
290     }
291     if (prop.vt != VT_EMPTY)
292       return E_INVALIDARG;
293   }
294   if (arcRoot)
295   {
296     RINOK(arcRoot->GetRootProp(propID, &prop))
297     if (prop.vt == VT_FILETIME)
298     {
299       ft = prop.filetime;
300       return S_OK;
301     }
302     if (prop.vt != VT_EMPTY)
303       return E_INVALIDARG;
304   }
305   return S_OK;
306 }
307 
308 #define Set16(p, d) SetUi16(p, d)
309 #define Set32(p, d) SetUi32(p, d)
310 #define Set64(p, d) SetUi64(p, d)
311 
WriteTo(Byte * p) const312 void CResource::WriteTo(Byte *p) const
313 {
314   Set64(p, PackSize)
315   p[7] = Flags;
316   Set64(p + 8, Offset)
317   Set64(p + 16, UnpackSize)
318 }
319 
320 
WriteTo(Byte * p) const321 void CHeader::WriteTo(Byte *p) const
322 {
323   memcpy(p, kSignature, kSignatureSize);
324   Set32(p + 8, kHeaderSizeMax)
325   Set32(p + 0xC, Version)
326   Set32(p + 0x10, Flags)
327   Set32(p + 0x14, ChunkSize)
328   memcpy(p + 0x18, Guid, 16);
329   Set16(p + 0x28, PartNumber)
330   Set16(p + 0x2A, NumParts)
331   Set32(p + 0x2C, NumImages)
332   OffsetResource.WriteTo(p + 0x30);
333   XmlResource.WriteTo(p + 0x48);
334   MetadataResource.WriteTo(p + 0x60);
335   IntegrityResource.WriteTo(p + 0x7C);
336   Set32(p + 0x78, BootIndex)
337   memset(p + 0x94, 0, 60);
338 }
339 
340 
WriteTo(Byte * p) const341 void CStreamInfo::WriteTo(Byte *p) const
342 {
343   Resource.WriteTo(p);
344   Set16(p + 0x18, PartNumber)
345   Set32(p + 0x1A, RefCount)
346   memcpy(p + 0x1E, Hash, kHashSize);
347 }
348 
349 
SetFileTimeToMem(Byte * p,const FILETIME & ft)350 static void SetFileTimeToMem(Byte *p, const FILETIME &ft)
351 {
352   Set32(p, ft.dwLowDateTime)
353   Set32(p + 4, ft.dwHighDateTime)
354 }
355 
WriteItem_Dummy(const CMetaItem & item)356 static size_t WriteItem_Dummy(const CMetaItem &item)
357 {
358   if (item.Skip)
359     return 0;
360   unsigned fileNameLen = item.Name.Len() * 2;
361   // we write fileNameLen + 2 + 2 to be same as original WIM.
362   unsigned fileNameLen2 = (fileNameLen == 0 ? 0 : fileNameLen + 2);
363 
364   const unsigned shortNameLen = item.ShortName.Len() * 2;
365   const unsigned shortNameLen2 = (shortNameLen == 0 ? 2 : shortNameLen + 4);
366 
367   size_t totalLen = ((kDirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~(unsigned)7);
368   if (item.GetNumAltStreams() != 0)
369   {
370     if (!item.IsDir)
371     {
372       const UInt32 curLen = (((0x26 + 0) + 6) & ~(unsigned)7);
373       totalLen += curLen;
374     }
375     FOR_VECTOR (i, item.AltStreams)
376     {
377       const CAltStream &ss = item.AltStreams[i];
378       if (ss.Skip)
379         continue;
380       fileNameLen = ss.Name.Len() * 2;
381       fileNameLen2 = (fileNameLen == 0 ? 0 : fileNameLen + 2 + 2);
382       const UInt32 curLen = (((0x26 + fileNameLen2) + 6) & ~(unsigned)7);
383       totalLen += curLen;
384     }
385   }
386   return totalLen;
387 }
388 
389 
WriteItem(const CStreamInfo * streams,const CMetaItem & item,Byte * p)390 static size_t WriteItem(const CStreamInfo *streams, const CMetaItem &item, Byte *p)
391 {
392   if (item.Skip)
393     return 0;
394   unsigned fileNameLen = item.Name.Len() * 2;
395   unsigned fileNameLen2 = (fileNameLen == 0 ? 0 : fileNameLen + 2);
396   unsigned shortNameLen = item.ShortName.Len() * 2;
397   unsigned shortNameLen2 = (shortNameLen == 0 ? 2 : shortNameLen + 4);
398 
399   size_t totalLen = ((kDirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~(unsigned)7);
400 
401   memset(p, 0, totalLen);
402   Set64(p, totalLen)
403   Set64(p + 8, item.Attrib)
404   Set32(p + 0xC, (UInt32)(Int32)item.SecurityId)
405   SetFileTimeToMem(p + 0x28, item.CTime);
406   SetFileTimeToMem(p + 0x30, item.ATime);
407   SetFileTimeToMem(p + 0x38, item.MTime);
408 
409   /* WIM format probably doesn't support hard links to symbolic links.
410      In these cases it just stores symbolic links (REPARSE TAGS).
411      Check it in new versions of WIM software form MS !!!
412      We also follow that scheme */
413 
414   if (item.Reparse.Size() != 0)
415   {
416     UInt32 tag = GetUi32(item.Reparse);
417     Set32(p + 0x58, tag)
418     // Set32(p + 0x5C, 0); // probably it's always ZERO
419   }
420   else if (item.FileID != 0)
421   {
422     Set64(p + 0x58, item.FileID)
423   }
424 
425   Set16(p + 0x62, (UInt16)shortNameLen)
426   Set16(p + 0x64, (UInt16)fileNameLen)
427   unsigned i;
428   for (i = 0; i * 2 < fileNameLen; i++)
429     Set16(p + kDirRecordSize + i * 2, (UInt16)item.Name[i])
430   for (i = 0; i * 2 < shortNameLen; i++)
431     Set16(p + kDirRecordSize + fileNameLen2 + i * 2, (UInt16)item.ShortName[i])
432 
433   if (item.GetNumAltStreams() == 0)
434   {
435     if (item.HashIndex >= 0)
436       memcpy(p + 0x40, streams[item.HashIndex].Hash, kHashSize);
437   }
438   else
439   {
440     Set16(p + 0x60, (UInt16)(item.GetNumAltStreams() + (item.IsDir ? 0 : 1)))
441     p += totalLen;
442 
443     if (!item.IsDir)
444     {
445       const UInt32 curLen = (((0x26 + 0) + 6) & ~(unsigned)7);
446       memset(p, 0, curLen);
447       Set64(p, curLen)
448       if (item.HashIndex >= 0)
449         memcpy(p + 0x10, streams[item.HashIndex].Hash, kHashSize);
450       totalLen += curLen;
451       p += curLen;
452     }
453 
454     FOR_VECTOR (si, item.AltStreams)
455     {
456       const CAltStream &ss = item.AltStreams[si];
457       if (ss.Skip)
458         continue;
459 
460       fileNameLen = ss.Name.Len() * 2;
461       fileNameLen2 = (fileNameLen == 0 ? 0 : fileNameLen + 2 + 2);
462       UInt32 curLen = (((0x26 + fileNameLen2) + 6) & ~(unsigned)7);
463       memset(p, 0, curLen);
464 
465       Set64(p, curLen)
466       if (ss.HashIndex >= 0)
467         memcpy(p + 0x10, streams[ss.HashIndex].Hash, kHashSize);
468       Set16(p + 0x24, (UInt16)fileNameLen)
469       for (i = 0; i * 2 < fileNameLen; i++)
470         Set16(p + 0x26 + i * 2, (UInt16)ss.Name[i])
471       totalLen += curLen;
472       p += curLen;
473     }
474   }
475 
476   return totalLen;
477 }
478 
479 
480 struct CDb
481 {
482   CMetaItem DefaultDirItem;
483   const CStreamInfo *Hashes;
484   CObjectVector<CMetaItem> MetaItems;
485   CRecordVector<CUpdateItem> UpdateItems;
486   CUIntVector UpdateIndexes; /* indexes in UpdateItems in order of writing data streams
487                                 to disk (the order of tree items). */
488 
489   size_t WriteTree_Dummy(const CDir &tree) const;
490   void WriteTree(const CDir &tree, Byte *dest, size_t &pos)  const;
491   void WriteOrderList(const CDir &tree);
492 };
493 
494 
WriteTree_Dummy(const CDir & tree) const495 size_t CDb::WriteTree_Dummy(const CDir &tree) const
496 {
497   unsigned i;
498   size_t pos = 0;
499   for (i = 0; i < tree.Files.Size(); i++)
500     pos += WriteItem_Dummy(MetaItems[tree.Files[i]]);
501   for (i = 0; i < tree.Dirs.Size(); i++)
502   {
503     const CDir &subDir = tree.Dirs[i];
504     pos += WriteItem_Dummy(MetaItems[subDir.MetaIndex]);
505     pos += WriteTree_Dummy(subDir);
506   }
507   return pos + 8;
508 }
509 
510 
WriteTree(const CDir & tree,Byte * dest,size_t & pos) const511 void CDb::WriteTree(const CDir &tree, Byte *dest, size_t &pos) const
512 {
513   unsigned i;
514   for (i = 0; i < tree.Files.Size(); i++)
515     pos += WriteItem(Hashes, MetaItems[tree.Files[i]], dest + pos);
516 
517   size_t posStart = pos;
518   for (i = 0; i < tree.Dirs.Size(); i++)
519     pos += WriteItem_Dummy(MetaItems[tree.Dirs[i].MetaIndex]);
520 
521   Set64(dest + pos, 0)
522 
523   pos += 8;
524 
525   for (i = 0; i < tree.Dirs.Size(); i++)
526   {
527     const CDir &subDir = tree.Dirs[i];
528     const CMetaItem &metaItem = MetaItems[subDir.MetaIndex];
529     bool needCreateTree = (metaItem.Reparse.Size() == 0)
530         || !subDir.Files.IsEmpty()
531         || !subDir.Dirs.IsEmpty();
532     size_t len = WriteItem(Hashes, metaItem, dest + posStart);
533     posStart += len;
534     if (needCreateTree)
535     {
536       Set64(dest + posStart - len + 0x10, pos) // subdirOffset
537       WriteTree(subDir, dest, pos);
538     }
539   }
540 }
541 
542 
WriteOrderList(const CDir & tree)543 void CDb::WriteOrderList(const CDir &tree)
544 {
545   if (tree.MetaIndex >= 0)
546   {
547     const CMetaItem &mi = MetaItems[tree.MetaIndex];
548     if (mi.UpdateIndex >= 0)
549       UpdateIndexes.Add((unsigned)mi.UpdateIndex);
550     FOR_VECTOR (si, mi.AltStreams)
551       UpdateIndexes.Add((unsigned)mi.AltStreams[si].UpdateIndex);
552   }
553 
554   unsigned i;
555   for (i = 0; i < tree.Files.Size(); i++)
556   {
557     const CMetaItem &mi = MetaItems[tree.Files[i]];
558     UpdateIndexes.Add((unsigned)mi.UpdateIndex);
559     FOR_VECTOR (si, mi.AltStreams)
560       UpdateIndexes.Add((unsigned)mi.AltStreams[si].UpdateIndex);
561   }
562 
563   for (i = 0; i < tree.Dirs.Size(); i++)
564     WriteOrderList(tree.Dirs[i]);
565 }
566 
567 
AddTag_ToString(AString & s,const char * name,const char * value)568 static void AddTag_ToString(AString &s, const char *name, const char *value)
569 {
570   s.Add_Char('<');
571   s += name;
572   s.Add_Char('>');
573   s += value;
574   s.Add_Char('<');
575   s.Add_Slash();
576   s += name;
577   s.Add_Char('>');
578 }
579 
580 
AddTagUInt64_ToString(AString & s,const char * name,UInt64 value)581 static void AddTagUInt64_ToString(AString &s, const char *name, UInt64 value)
582 {
583   char temp[32];
584   ConvertUInt64ToString(value, temp);
585   AddTag_ToString(s, name, temp);
586 }
587 
588 
AddUniqueTag(CXmlItem & parentItem,const char * name)589 static CXmlItem &AddUniqueTag(CXmlItem &parentItem, const char *name)
590 {
591   const int index = parentItem.FindSubTag(name);
592   if (index < 0)
593   {
594     CXmlItem &subItem = parentItem.SubItems.AddNew();
595     subItem.IsTag = true;
596     subItem.Name = name;
597     return subItem;
598   }
599   CXmlItem &subItem = parentItem.SubItems[index];
600   subItem.SubItems.Clear();
601   return subItem;
602 }
603 
604 
AddTag_UInt64_2(CXmlItem & item,UInt64 value)605 static void AddTag_UInt64_2(CXmlItem &item, UInt64 value)
606 {
607   CXmlItem &subItem = item.SubItems.AddNew();
608   subItem.IsTag = false;
609   char temp[32];
610   ConvertUInt64ToString(value, temp);
611   subItem.Name = temp;
612 }
613 
614 
AddTag_UInt64(CXmlItem & parentItem,const char * name,UInt64 value)615 static void AddTag_UInt64(CXmlItem &parentItem, const char *name, UInt64 value)
616 {
617   AddTag_UInt64_2(AddUniqueTag(parentItem, name), value);
618 }
619 
620 
AddTag_Hex(CXmlItem & item,const char * name,UInt32 value)621 static void AddTag_Hex(CXmlItem &item, const char *name, UInt32 value)
622 {
623   item.IsTag = true;
624   item.Name = name;
625   char temp[16];
626   temp[0] = '0';
627   temp[1] = 'x';
628   ConvertUInt32ToHex8Digits(value, temp + 2);
629   CXmlItem &subItem = item.SubItems.AddNew();
630   subItem.IsTag = false;
631   subItem.Name = temp;
632 }
633 
634 
AddTag_Time_2(CXmlItem & item,const FILETIME & ft)635 static void AddTag_Time_2(CXmlItem &item, const FILETIME &ft)
636 {
637   AddTag_Hex(item.SubItems.AddNew(), "HIGHPART", ft.dwHighDateTime);
638   AddTag_Hex(item.SubItems.AddNew(), "LOWPART", ft.dwLowDateTime);
639 }
640 
641 
AddTag_Time(CXmlItem & parentItem,const char * name,const FILETIME & ft)642 static void AddTag_Time(CXmlItem &parentItem, const char *name, const FILETIME &ft)
643 {
644   AddTag_Time_2(AddUniqueTag(parentItem, name), ft);
645 }
646 
647 
AddTag_String_IfEmpty(CXmlItem & parentItem,const char * name,const char * value)648 static void AddTag_String_IfEmpty(CXmlItem &parentItem, const char *name, const char *value)
649 {
650   if (parentItem.FindSubTag(name) >= 0)
651     return;
652   CXmlItem &tag = parentItem.SubItems.AddNew();
653   tag.IsTag = true;
654   tag.Name = name;
655   CXmlItem &subItem = tag.SubItems.AddNew();
656   subItem.IsTag = false;
657   subItem.Name = value;
658 }
659 
660 
SetDefaultFields(bool useLZX)661 void CHeader::SetDefaultFields(bool useLZX)
662 {
663   Version = k_Version_NonSolid;
664   Flags = NHeaderFlags::kReparsePointFixup;
665   ChunkSize = 0;
666   if (useLZX)
667   {
668     Flags |= NHeaderFlags::kCompression | NHeaderFlags::kLZX;
669     ChunkSize = kChunkSize;
670     ChunkSizeBits = kChunkSizeBits;
671   }
672   MY_RAND_GEN(Guid, 16);
673   PartNumber = 1;
674   NumParts = 1;
675   NumImages = 1;
676   BootIndex = 0;
677   OffsetResource.Clear();
678   XmlResource.Clear();
679   MetadataResource.Clear();
680   IntegrityResource.Clear();
681 }
682 
683 
AddTrees(CObjectVector<CDir> & trees,CObjectVector<CMetaItem> & metaItems,const CMetaItem & ri,int curTreeIndex)684 static void AddTrees(CObjectVector<CDir> &trees, CObjectVector<CMetaItem> &metaItems, const CMetaItem &ri, int curTreeIndex)
685 {
686   while (curTreeIndex >= (int)trees.Size())
687     trees.AddNew().Dirs.AddNew().MetaIndex = (int)metaItems.Add(ri);
688 }
689 
690 
691 #define IS_LETTER_CHAR(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
692 
693 
694 
Z7_COM7F_IMF(CHandler::UpdateItems (ISequentialOutStream * outSeqStream,UInt32 numItems,IArchiveUpdateCallback * callback))695 Z7_COM7F_IMF(CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 numItems, IArchiveUpdateCallback *callback))
696 {
697   COM_TRY_BEGIN
698 
699   if (!IsUpdateSupported())
700     return E_NOTIMPL;
701 
702   bool isUpdate = (_volumes.Size() != 0);
703   int defaultImageIndex = _defaultImageNumber - 1;
704   bool showImageNumber;
705 
706   if (isUpdate)
707   {
708     showImageNumber = _showImageNumber;
709     if (!showImageNumber)
710       defaultImageIndex = _db.IndexOfUserImage;
711   }
712   else
713   {
714     showImageNumber = (_set_use_ShowImageNumber && _set_showImageNumber);
715     if (!showImageNumber)
716       defaultImageIndex = 0;
717   }
718 
719   if (defaultImageIndex >= kNumImagesMaxUpdate)
720     return E_NOTIMPL;
721 
722   CMyComPtr<IOutStream> outStream;
723   RINOK(outSeqStream->QueryInterface(IID_IOutStream, (void **)&outStream))
724   if (!outStream)
725     return E_NOTIMPL;
726   if (!callback)
727     return E_FAIL;
728 
729   CDb db;
730   CObjectVector<CDir> trees;
731 
732   CMetaItem ri; // default DIR item
733   FILETIME ftCur;
734   NTime::GetCurUtcFileTime(ftCur);
735   // ri.MTime = ri.ATime = ri.CTime = ftCur;
736   ri.Attrib = FILE_ATTRIBUTE_DIRECTORY;
737   ri.IsDir = true;
738 
739 
740   // ---------- Detect changed images ----------
741 
742   unsigned i;
743   CBoolVector isChangedImage;
744   {
745     CUIntVector numUnchangedItemsInImage;
746     for (i = 0; i < _db.Images.Size(); i++)
747     {
748       numUnchangedItemsInImage.Add(0);
749       isChangedImage.Add(false);
750     }
751 
752     for (i = 0; i < numItems; i++)
753     {
754       UInt32 indexInArchive;
755       Int32 newData, newProps;
756       RINOK(callback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive))
757       if (newProps == 0)
758       {
759         if (indexInArchive >= _db.SortedItems.Size())
760           continue;
761         const CItem &item = _db.Items[_db.SortedItems[indexInArchive]];
762         if (newData == 0)
763         {
764           if (item.ImageIndex >= 0)
765             numUnchangedItemsInImage[item.ImageIndex]++;
766         }
767         else
768         {
769           // oldProps & newData. Current version of 7-Zip doesn't use it
770           if (item.ImageIndex >= 0)
771             isChangedImage[item.ImageIndex] = true;
772         }
773       }
774       else if (!showImageNumber)
775       {
776         if (defaultImageIndex >= 0 && defaultImageIndex < (int)isChangedImage.Size())
777           isChangedImage[defaultImageIndex] = true;
778       }
779       else
780       {
781         NCOM::CPropVariant prop;
782         RINOK(callback->GetProperty(i, kpidPath, &prop))
783 
784         if (prop.vt != VT_BSTR)
785           return E_INVALIDARG;
786         const wchar_t *path = prop.bstrVal;
787         if (!path)
788           return E_INVALIDARG;
789 
790         const wchar_t *end;
791         UInt64 val = ConvertStringToUInt64(path, &end);
792         if (end == path)
793           return E_INVALIDARG;
794         if (val == 0 || val > kNumImagesMaxUpdate)
795           return E_INVALIDARG;
796         wchar_t c = *end;
797         if (c != 0 && c != ':' && c != L'/' && c != WCHAR_PATH_SEPARATOR)
798           return E_INVALIDARG;
799         unsigned imageIndex = (unsigned)val - 1;
800         if (imageIndex < _db.Images.Size())
801           isChangedImage[imageIndex] = true;
802         if (_defaultImageNumber > 0 && val != (unsigned)_defaultImageNumber)
803           return E_INVALIDARG;
804       }
805     }
806 
807     for (i = 0; i < _db.Images.Size(); i++)
808       if (!isChangedImage[i])
809         isChangedImage[i] = _db.GetNumUserItemsInImage(i) != numUnchangedItemsInImage[i];
810   }
811 
812   if (defaultImageIndex >= 0)
813   {
814     for (i = 0; i < _db.Images.Size(); i++)
815       if ((int)i != defaultImageIndex)
816         isChangedImage[i] = false;
817   }
818 
819   CMyComPtr<IArchiveGetRawProps> getRawProps;
820   callback->QueryInterface(IID_IArchiveGetRawProps, (void **)&getRawProps);
821 
822   CMyComPtr<IArchiveGetRootProps> getRootProps;
823   callback->QueryInterface(IID_IArchiveGetRootProps, (void **)&getRootProps);
824 
825   CObjectVector<CUniqBlocks> secureBlocks;
826 
827   if (!showImageNumber && (getRootProps || isUpdate) &&
828       (
829         defaultImageIndex >= (int)isChangedImage.Size()
830         || defaultImageIndex < 0 // test it
831         || isChangedImage[defaultImageIndex]
832       ))
833   {
834     // Fill Root Item: Metadata and security
835     CMetaItem rootItem = ri;
836     {
837       const void *data = NULL;
838       UInt32 dataSize = 0;
839       UInt32 propType = 0;
840       if (getRootProps)
841       {
842         RINOK(getRootProps->GetRootRawProp(kpidNtSecure, &data, &dataSize, &propType))
843       }
844       if (dataSize == 0 && isUpdate)
845       {
846         RINOK(GetRootRawProp(kpidNtSecure, &data, &dataSize, &propType))
847       }
848       if (dataSize != 0)
849       {
850         if (propType != NPropDataType::kRaw)
851           return E_FAIL;
852         while (defaultImageIndex >= (int)secureBlocks.Size())
853           secureBlocks.AddNew();
854         CUniqBlocks &secUniqBlocks = secureBlocks[defaultImageIndex];
855         rootItem.SecurityId = (int)secUniqBlocks.AddUniq((const Byte *)data, dataSize);
856       }
857     }
858 
859     IArchiveGetRootProps *thisGetRoot = isUpdate ? this : NULL;
860 
861     if (_timeOptions.Write_CTime.Val) RINOK(GetRootTime(getRootProps, thisGetRoot, kpidCTime, rootItem.CTime))
862     if (_timeOptions.Write_ATime.Val) RINOK(GetRootTime(getRootProps, thisGetRoot, kpidATime, rootItem.ATime))
863     if (_timeOptions.Write_MTime.Val) RINOK(GetRootTime(getRootProps, thisGetRoot, kpidMTime, rootItem.MTime))
864 
865     {
866       NCOM::CPropVariant prop;
867       if (getRootProps)
868       {
869         RINOK(getRootProps->GetRootProp(kpidAttrib, &prop))
870         if (prop.vt == VT_UI4)
871           rootItem.Attrib = prop.ulVal;
872         else if (prop.vt != VT_EMPTY)
873           return E_INVALIDARG;
874       }
875       if (prop.vt == VT_EMPTY && thisGetRoot)
876       {
877         RINOK(GetRootProp(kpidAttrib, &prop))
878         if (prop.vt == VT_UI4)
879           rootItem.Attrib = prop.ulVal;
880         else if (prop.vt != VT_EMPTY)
881           return E_INVALIDARG;
882       }
883       rootItem.Attrib |= FILE_ATTRIBUTE_DIRECTORY;
884     }
885 
886     AddTrees(trees, db.MetaItems, ri, defaultImageIndex);
887     db.MetaItems[trees[defaultImageIndex].Dirs[0].MetaIndex] = rootItem;
888   }
889 
890   // ---------- Request Metadata for changed items ----------
891 
892   UString fileName;
893 
894   for (i = 0; i < numItems; i++)
895   {
896     CUpdateItem ui;
897     UInt32 indexInArchive;
898     Int32 newData, newProps;
899     RINOK(callback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive))
900 
901     if (newData == 0 || newProps == 0)
902     {
903       if (indexInArchive >= _db.SortedItems.Size())
904         continue;
905 
906       const CItem &item = _db.Items[_db.SortedItems[indexInArchive]];
907 
908       if (item.ImageIndex >= 0)
909       {
910         if (!isChangedImage[item.ImageIndex])
911         {
912           if (newData == 0 && newProps == 0)
913             continue;
914           return E_FAIL;
915         }
916       }
917       else
918       {
919         // if deleted item was not renamed, we just skip it
920         if (newProps == 0)
921           continue;
922         if (item.StreamIndex >= 0)
923         {
924           // we don't support property change for SolidBig streams
925           if (_db.DataStreams[item.StreamIndex].Resource.IsSolidBig())
926             return E_NOTIMPL;
927         }
928       }
929 
930       if (newData == 0)
931         ui.InArcIndex = (Int32)indexInArchive;
932     }
933 
934     // we set arcIndex only if we must use old props
935     const Int32 arcIndex = (newProps ? -1 : (Int32)indexInArchive);
936 
937     bool isDir = false;
938     {
939       NCOM::CPropVariant prop;
940       RINOK(GetOutProperty(callback, i, arcIndex, kpidIsDir, &prop))
941       if (prop.vt == VT_BOOL)
942         isDir = (prop.boolVal != VARIANT_FALSE);
943       else if (prop.vt != VT_EMPTY)
944         return E_INVALIDARG;
945     }
946 
947     bool isAltStream = false;
948     {
949       NCOM::CPropVariant prop;
950       RINOK(GetOutProperty(callback, i, arcIndex, kpidIsAltStream, &prop))
951       if (prop.vt == VT_BOOL)
952         isAltStream = (prop.boolVal != VARIANT_FALSE);
953       else if (prop.vt != VT_EMPTY)
954         return E_INVALIDARG;
955     }
956 
957     if (isDir && isAltStream)
958       return E_INVALIDARG;
959 
960     UInt64 size = 0;
961     UInt64 iNode = 0;
962 
963     if (!isDir)
964     {
965       if (!newData)
966       {
967         NCOM::CPropVariant prop;
968         GetProperty(indexInArchive, kpidINode, &prop);
969         if (prop.vt == VT_UI8)
970           iNode = prop.uhVal.QuadPart;
971       }
972 
973       NCOM::CPropVariant prop;
974 
975       if (newData)
976       {
977         RINOK(callback->GetProperty(i, kpidSize, &prop))
978       }
979       else
980       {
981         RINOK(GetProperty(indexInArchive, kpidSize, &prop))
982       }
983 
984       if (prop.vt == VT_UI8)
985         size = prop.uhVal.QuadPart;
986       else if (prop.vt != VT_EMPTY)
987         return E_INVALIDARG;
988     }
989 
990     {
991       NCOM::CPropVariant propPath;
992       const wchar_t *path = NULL;
993       RINOK(GetOutProperty(callback, i, arcIndex, kpidPath, &propPath))
994       if (propPath.vt == VT_BSTR)
995         path = propPath.bstrVal;
996       else if (propPath.vt != VT_EMPTY)
997         return E_INVALIDARG;
998 
999     if (!path)
1000       return E_INVALIDARG;
1001 
1002     CDir *curItem = NULL;
1003     bool isRootImageDir = false;
1004     fileName.Empty();
1005 
1006     int imageIndex;
1007 
1008     if (!showImageNumber)
1009     {
1010       imageIndex = defaultImageIndex;
1011       AddTrees(trees, db.MetaItems, ri, imageIndex);
1012       curItem = &trees[imageIndex].Dirs[0];
1013     }
1014     else
1015     {
1016       const wchar_t *end;
1017       UInt64 val = ConvertStringToUInt64(path, &end);
1018       if (end == path)
1019         return E_INVALIDARG;
1020       if (val == 0 || val > kNumImagesMaxUpdate)
1021         return E_INVALIDARG;
1022 
1023       imageIndex = (int)val - 1;
1024       if (imageIndex < (int)isChangedImage.Size())
1025        if (!isChangedImage[imageIndex])
1026           return E_FAIL;
1027 
1028       AddTrees(trees, db.MetaItems, ri, imageIndex);
1029       curItem = &trees[imageIndex].Dirs[0];
1030       wchar_t c = *end;
1031 
1032       if (c == 0)
1033       {
1034         if (!isDir || isAltStream)
1035           return E_INVALIDARG;
1036         ui.MetaIndex = curItem->MetaIndex;
1037         isRootImageDir = true;
1038       }
1039       else if (c == ':')
1040       {
1041         if (isDir || !isAltStream)
1042           return E_INVALIDARG;
1043         ui.MetaIndex = curItem->MetaIndex;
1044         CAltStream ss;
1045         ss.Size = size;
1046         ss.Name = end + 1;
1047         ss.UpdateIndex = (int)db.UpdateItems.Size();
1048         ui.AltStreamIndex = (int)db.MetaItems[ui.MetaIndex].AltStreams.Add(ss);
1049       }
1050       else if (c == WCHAR_PATH_SEPARATOR || c == L'/')
1051       {
1052         path = end + 1;
1053         if (*path == 0)
1054           return E_INVALIDARG;
1055       }
1056       else
1057         return E_INVALIDARG;
1058     }
1059 
1060     if (ui.MetaIndex < 0)
1061     {
1062       for (;;)
1063       {
1064         const wchar_t c = *path++;
1065         if (c == 0)
1066           break;
1067         if (c == WCHAR_PATH_SEPARATOR || c == L'/')
1068         {
1069           unsigned indexOfDir;
1070           if (!curItem->FindDir(db.MetaItems, fileName, indexOfDir))
1071           {
1072             CDir &dir = curItem->Dirs.InsertNew(indexOfDir);
1073             dir.MetaIndex = (int)db.MetaItems.Add(ri);
1074             db.MetaItems.Back().Name = fileName;
1075           }
1076           curItem = &curItem->Dirs[indexOfDir];
1077           fileName.Empty();
1078         }
1079         else
1080         {
1081           /*
1082           #if WCHAR_MAX > 0xffff
1083           if (c >= 0x10000)
1084           {
1085             c -= 0x10000;
1086 
1087             if (c < (1 << 20))
1088             {
1089               wchar_t c0 = 0xd800 + ((c >> 10) & 0x3FF);
1090               fileName += c0;
1091               c = 0xdc00 + (c & 0x3FF);
1092             }
1093             else
1094               c = '_'; // we change character unsupported by UTF16
1095           }
1096           #endif
1097           */
1098 
1099           fileName += c;
1100         }
1101       }
1102 
1103       if (isAltStream)
1104       {
1105         int colonPos = fileName.Find(L':');
1106         if (colonPos < 0)
1107           return E_INVALIDARG;
1108 
1109         // we want to support cases of c::substream, where c: is drive name
1110         if (colonPos == 1 && fileName[2] == L':' && IS_LETTER_CHAR(fileName[0]))
1111           colonPos = 2;
1112         const UString mainName = fileName.Left((unsigned)colonPos);
1113         unsigned indexOfDir;
1114 
1115         if (mainName.IsEmpty())
1116           ui.MetaIndex = curItem->MetaIndex;
1117         else if (curItem->FindDir(db.MetaItems, mainName, indexOfDir))
1118           ui.MetaIndex = curItem->Dirs[indexOfDir].MetaIndex;
1119         else
1120         {
1121           for (int j = (int)curItem->Files.Size() - 1; j >= 0; j--)
1122           {
1123             const unsigned metaIndex = curItem->Files[j];
1124             const CMetaItem &mi = db.MetaItems[metaIndex];
1125             if (CompareFileNames(mainName, mi.Name) == 0)
1126             {
1127               ui.MetaIndex = (int)metaIndex;
1128               break;
1129             }
1130           }
1131         }
1132 
1133         if (ui.MetaIndex >= 0)
1134         {
1135           CAltStream ss;
1136           ss.Size = size;
1137           ss.Name = fileName.Ptr(colonPos + 1);
1138           ss.UpdateIndex = (int)db.UpdateItems.Size();
1139           ui.AltStreamIndex = (int)db.MetaItems[ui.MetaIndex].AltStreams.Add(ss);
1140         }
1141       }
1142     }
1143 
1144 
1145     if (ui.MetaIndex < 0 || isRootImageDir)
1146     {
1147       if (!isRootImageDir)
1148       {
1149         ui.MetaIndex = (int)db.MetaItems.Size();
1150         db.MetaItems.AddNew();
1151       }
1152 
1153       CMetaItem &mi = db.MetaItems[ui.MetaIndex];
1154       mi.Size = size;
1155       mi.IsDir = isDir;
1156       mi.Name = fileName;
1157       mi.UpdateIndex = (int)db.UpdateItems.Size();
1158       {
1159         NCOM::CPropVariant prop;
1160         RINOK(GetOutProperty(callback, i, arcIndex, kpidAttrib, &prop))
1161         if (prop.vt == VT_EMPTY)
1162           mi.Attrib = 0;
1163         else if (prop.vt == VT_UI4)
1164           mi.Attrib = prop.ulVal;
1165         else
1166           return E_INVALIDARG;
1167         if (isDir)
1168           mi.Attrib |= FILE_ATTRIBUTE_DIRECTORY;
1169       }
1170 
1171       if (arcIndex != -1 || _timeOptions.Write_CTime.Val)
1172         RINOK(GetTime(callback, i, arcIndex, kpidCTime, mi.CTime))
1173       if (arcIndex != -1 || _timeOptions.Write_ATime.Val)
1174         RINOK(GetTime(callback, i, arcIndex, kpidATime, mi.ATime))
1175       if (arcIndex != -1 || _timeOptions.Write_MTime.Val)
1176         RINOK(GetTime(callback, i, arcIndex, kpidMTime, mi.MTime))
1177 
1178       {
1179         NCOM::CPropVariant prop;
1180         RINOK(GetOutProperty(callback, i, arcIndex, kpidShortName, &prop))
1181         if (prop.vt == VT_BSTR)
1182           mi.ShortName.SetFromBstr(prop.bstrVal);
1183         else if (prop.vt != VT_EMPTY)
1184           return E_INVALIDARG;
1185       }
1186 
1187       while (imageIndex >= (int)secureBlocks.Size())
1188         secureBlocks.AddNew();
1189 
1190       if (!isAltStream && (getRawProps || arcIndex >= 0))
1191       {
1192         CUniqBlocks &secUniqBlocks = secureBlocks[imageIndex];
1193         const void *data;
1194         UInt32 dataSize;
1195         UInt32 propType;
1196 
1197         data = NULL;
1198         dataSize = 0;
1199         propType = 0;
1200 
1201         if (arcIndex >= 0)
1202         {
1203           GetRawProp((UInt32)arcIndex, kpidNtSecure, &data, &dataSize, &propType);
1204         }
1205         else
1206         {
1207           getRawProps->GetRawProp(i, kpidNtSecure, &data, &dataSize, &propType);
1208         }
1209 
1210         if (dataSize != 0)
1211         {
1212           if (propType != NPropDataType::kRaw)
1213             return E_FAIL;
1214           mi.SecurityId = (int)secUniqBlocks.AddUniq((const Byte *)data, dataSize);
1215         }
1216 
1217         data = NULL;
1218         dataSize = 0;
1219         propType = 0;
1220 
1221         if (arcIndex >= 0)
1222         {
1223           GetRawProp((UInt32)arcIndex, kpidNtReparse, &data, &dataSize, &propType);
1224         }
1225         else
1226         {
1227           getRawProps->GetRawProp(i, kpidNtReparse, &data, &dataSize, &propType);
1228         }
1229 
1230         if (dataSize != 0)
1231         {
1232           if (propType != NPropDataType::kRaw)
1233             return E_FAIL;
1234           mi.Reparse.CopyFrom((const Byte *)data, dataSize);
1235         }
1236       }
1237 
1238       if (!isRootImageDir)
1239       {
1240         if (isDir)
1241         {
1242           unsigned indexOfDir;
1243           if (curItem->FindDir(db.MetaItems, fileName, indexOfDir))
1244             curItem->Dirs[indexOfDir].MetaIndex = ui.MetaIndex;
1245           else
1246             curItem->Dirs.InsertNew(indexOfDir).MetaIndex = ui.MetaIndex;
1247         }
1248         else
1249           curItem->Files.Add((unsigned)ui.MetaIndex);
1250       }
1251     }
1252 
1253     }
1254 
1255     if (iNode != 0 && ui.MetaIndex >= 0 && ui.AltStreamIndex < 0)
1256       db.MetaItems[ui.MetaIndex].FileID = iNode;
1257 
1258     ui.CallbackIndex = i;
1259     db.UpdateItems.Add(ui);
1260   }
1261 
1262   unsigned numNewImages = trees.Size();
1263   for (i = numNewImages; i < isChangedImage.Size(); i++)
1264     if (!isChangedImage[i])
1265       numNewImages = i + 1;
1266 
1267   AddTrees(trees, db.MetaItems, ri, (int)numNewImages - 1);
1268 
1269   for (i = 0; i < trees.Size(); i++)
1270     if (i >= isChangedImage.Size() || isChangedImage[i])
1271       db.WriteOrderList(trees[i]);
1272 
1273 
1274   UInt64 complexity = 0;
1275 
1276   unsigned numDataStreams = _db.DataStreams.Size();
1277   CUIntArr streamsRefs(numDataStreams);
1278   for (i = 0; i < numDataStreams; i++)
1279     streamsRefs[i] = 0;
1280 
1281   // ---------- Calculate Streams Refs Counts in unchanged images
1282 
1283   for (i = 0; i < _db.Images.Size(); i++)
1284   {
1285     if (isChangedImage[i])
1286       continue;
1287     complexity += _db.MetaStreams[i].Resource.PackSize;
1288     const CImage &image = _db.Images[i];
1289     unsigned endItem = image.StartItem + image.NumItems;
1290     for (unsigned k = image.StartItem; k < endItem; k++)
1291     {
1292       const CItem &item = _db.Items[k];
1293       if (item.StreamIndex >= 0)
1294         streamsRefs[(unsigned)item.StreamIndex]++;
1295     }
1296   }
1297 
1298 
1299   // ---------- Update Streams Refs Counts in changed images
1300 
1301   for (i = 0; i < db.UpdateIndexes.Size(); i++)
1302   {
1303     const CUpdateItem &ui = db.UpdateItems[db.UpdateIndexes[i]];
1304 
1305     if (ui.InArcIndex >= 0)
1306     {
1307       if ((unsigned)ui.InArcIndex >= _db.SortedItems.Size())
1308         continue;
1309       const CItem &item = _db.Items[_db.SortedItems[ui.InArcIndex]];
1310       if (item.StreamIndex >= 0)
1311         streamsRefs[(unsigned)item.StreamIndex]++;
1312     }
1313     else
1314     {
1315       const CMetaItem &mi = db.MetaItems[ui.MetaIndex];
1316       UInt64 size;
1317       if (ui.AltStreamIndex < 0)
1318         size = mi.Size;
1319       else
1320         size = mi.AltStreams[ui.AltStreamIndex].Size;
1321       complexity += size;
1322     }
1323   }
1324 
1325   // Clear ref counts for SolidBig streams
1326 
1327   for (i = 0; i < _db.DataStreams.Size(); i++)
1328     if (_db.DataStreams[i].Resource.IsSolidBig())
1329       streamsRefs[i] = 0;
1330 
1331   // Set ref counts for SolidBig streams
1332 
1333   for (i = 0; i < _db.DataStreams.Size(); i++)
1334     if (streamsRefs[i] != 0)
1335     {
1336       const CResource &rs = _db.DataStreams[i].Resource;
1337       if (rs.IsSolidSmall())
1338         streamsRefs[_db.Solids[rs.SolidIndex].StreamIndex] = 1;
1339     }
1340 
1341   for (i = 0; i < _db.DataStreams.Size(); i++)
1342     if (streamsRefs[i] != 0)
1343     {
1344       const CResource &rs = _db.DataStreams[i].Resource;
1345       if (!rs.IsSolidSmall())
1346         complexity += rs.PackSize;
1347     }
1348 
1349   RINOK(callback->SetTotal(complexity))
1350   UInt64 totalComplexity = complexity;
1351 
1352   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
1353   lps->Init(callback, true);
1354   CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
1355 
1356   complexity = 0;
1357 
1358   // bool useResourceCompression = false;
1359   // use useResourceCompression only if CHeader::Flags compression is also set
1360 
1361   CHeader header;
1362   header.SetDefaultFields(false);
1363 
1364   if (isUpdate)
1365   {
1366     const CHeader &srcHeader = _volumes[1].Header;
1367     header.Flags = srcHeader.Flags;
1368     header.Version = srcHeader.Version;
1369     header.ChunkSize = srcHeader.ChunkSize;
1370     header.ChunkSizeBits = srcHeader.ChunkSizeBits;
1371   }
1372 
1373   CMyComPtr<IStreamSetRestriction> setRestriction;
1374   outSeqStream->QueryInterface(IID_IStreamSetRestriction, (void **)&setRestriction);
1375   if (setRestriction)
1376     RINOK(setRestriction->SetRestriction(0, kHeaderSizeMax))
1377 
1378   {
1379     Byte buf[kHeaderSizeMax];
1380     header.WriteTo(buf);
1381     RINOK(WriteStream(outStream, buf, kHeaderSizeMax))
1382   }
1383 
1384   UInt64 curPos = kHeaderSizeMax;
1385 
1386   CMyComPtr2_Create<ISequentialInStream, CInStreamWithSha1> inShaStream;
1387 
1388   CLimitedSequentialInStream *inStreamLimitedSpec = NULL;
1389   CMyComPtr<ISequentialInStream> inStreamLimited;
1390   if (_volumes.Size() == 2)
1391   {
1392     inStreamLimitedSpec = new CLimitedSequentialInStream;
1393     inStreamLimited = inStreamLimitedSpec;
1394     inStreamLimitedSpec->SetStream(_volumes[1].Stream);
1395   }
1396 
1397 
1398   CRecordVector<CStreamInfo> streams;
1399   CSortedIndex sortedHashes; // indexes to streams, sorted by SHA1
1400 
1401   // ---------- Copy unchanged data streams ----------
1402 
1403   UInt64 solidRunOffset = 0;
1404   UInt64 curSolidSize = 0;
1405 
1406   for (i = 0; i < _db.DataStreams.Size(); i++)
1407   {
1408     const CStreamInfo &siOld = _db.DataStreams[i];
1409     const CResource &rs = siOld.Resource;
1410 
1411     const unsigned numRefs = streamsRefs[i];
1412 
1413     if (numRefs == 0)
1414     {
1415       if (!rs.IsSolidSmall())
1416         continue;
1417       if (streamsRefs[_db.Solids[rs.SolidIndex].StreamIndex] == 0)
1418         continue;
1419     }
1420 
1421     lps->InSize = lps->OutSize = complexity;
1422     RINOK(lps->SetCur())
1423 
1424     const unsigned streamIndex = streams.Size();
1425     CStreamInfo s;
1426     s.Resource = rs;
1427     s.PartNumber = 1;
1428     s.RefCount = numRefs;
1429 
1430     memcpy(s.Hash, siOld.Hash, kHashSize);
1431 
1432     if (rs.IsSolid())
1433     {
1434       CSolid &ss = _db.Solids[rs.SolidIndex];
1435       if (rs.IsSolidSmall())
1436       {
1437         UInt64 oldOffset = ss.SolidOffset;
1438         if (rs.Offset < oldOffset)
1439           return E_FAIL;
1440         UInt64 relatOffset = rs.Offset - oldOffset;
1441         s.Resource.Offset = solidRunOffset + relatOffset;
1442       }
1443       else
1444       {
1445         // IsSolidBig
1446         solidRunOffset += curSolidSize;
1447         curSolidSize = ss.UnpackSize;
1448       }
1449     }
1450     else
1451     {
1452       solidRunOffset = 0;
1453       curSolidSize = 0;
1454     }
1455 
1456     if (!rs.IsSolid() || rs.IsSolidSmall())
1457     {
1458       const int find = AddUniqHash(streams.ConstData(), sortedHashes, siOld.Hash, (int)streamIndex);
1459       if (find != -1)
1460         return E_FAIL; // two streams with same SHA-1
1461     }
1462 
1463     if (!rs.IsSolid() || rs.IsSolidBig())
1464     {
1465       RINOK(InStream_SeekSet(_volumes[siOld.PartNumber].Stream, rs.Offset))
1466       inStreamLimitedSpec->Init(rs.PackSize);
1467       RINOK(copyCoder.Interface()->Code(inStreamLimited, outStream, NULL, NULL, lps))
1468       if (copyCoder->TotalSize != rs.PackSize)
1469         return E_FAIL;
1470       s.Resource.Offset = curPos;
1471       curPos += rs.PackSize;
1472       lps->ProgressOffset += rs.PackSize;
1473     }
1474 
1475     streams.Add(s);
1476   }
1477 
1478 
1479   // ---------- Write new items ----------
1480 
1481   CUIntVector hlIndexes; // sorted indexes for hard link items
1482 
1483   for (i = 0; i < db.UpdateIndexes.Size(); i++)
1484   {
1485     lps->InSize = lps->OutSize = complexity;
1486     RINOK(lps->SetCur())
1487     const CUpdateItem &ui = db.UpdateItems[db.UpdateIndexes[i]];
1488     CMetaItem &mi = db.MetaItems[ui.MetaIndex];
1489     UInt64 size = 0;
1490 
1491     if (ui.AltStreamIndex >= 0)
1492     {
1493       if (mi.Skip)
1494         continue;
1495       size = mi.AltStreams[ui.AltStreamIndex].Size;
1496     }
1497     else
1498     {
1499       size = mi.Size;
1500       if (mi.IsDir)
1501       {
1502         // we support LINK files here
1503         if (mi.Reparse.Size() == 0)
1504           continue;
1505       }
1506     }
1507 
1508     if (ui.InArcIndex >= 0)
1509     {
1510       // data streams with OLD Data were written already
1511       // we just need to find HashIndex in hashes.
1512 
1513       if ((unsigned)ui.InArcIndex >= _db.SortedItems.Size())
1514         return E_FAIL;
1515 
1516       const CItem &item = _db.Items[_db.SortedItems[ui.InArcIndex]];
1517 
1518       if (item.StreamIndex < 0)
1519       {
1520         if (size == 0)
1521           continue;
1522         // if (_db.ItemHasStream(item))
1523         return E_FAIL;
1524       }
1525 
1526       // We support empty file (size = 0, but with stream and SHA-1) from old archive
1527 
1528       const CStreamInfo &siOld = _db.DataStreams[item.StreamIndex];
1529 
1530       const int index = AddUniqHash(streams.ConstData(), sortedHashes, siOld.Hash, -1);
1531       // we must have written that stream already
1532       if (index == -1)
1533         return E_FAIL;
1534 
1535       if (ui.AltStreamIndex < 0)
1536         mi.HashIndex = index;
1537       else
1538         mi.AltStreams[ui.AltStreamIndex].HashIndex = index;
1539 
1540       continue;
1541     }
1542 
1543     CMyComPtr<ISequentialInStream> fileInStream;
1544     HRESULT res = callback->GetStream(ui.CallbackIndex, &fileInStream);
1545 
1546     if (res == S_FALSE)
1547     {
1548       if (ui.AltStreamIndex >= 0)
1549       {
1550         mi.NumSkipAltStreams++;
1551         mi.AltStreams[ui.AltStreamIndex].Skip = true;
1552       }
1553       else
1554         mi.Skip = true;
1555     }
1556     else
1557     {
1558       RINOK(res)
1559 
1560       int miIndex = -1;
1561 
1562       if (!fileInStream)
1563       {
1564         if (!mi.IsDir)
1565           return E_INVALIDARG;
1566       }
1567       else if (ui.AltStreamIndex < 0)
1568       {
1569         CMyComPtr<IStreamGetProps2> getProps2;
1570         fileInStream->QueryInterface(IID_IStreamGetProps2, (void **)&getProps2);
1571         if (getProps2)
1572         {
1573           CStreamFileProps props;
1574           if (getProps2->GetProps2(&props) == S_OK)
1575           {
1576             mi.Attrib = props.Attrib;
1577             if (_timeOptions.Write_CTime.Val) mi.CTime = props.CTime;
1578             if (_timeOptions.Write_ATime.Val) mi.ATime = props.ATime;
1579             if (_timeOptions.Write_MTime.Val) mi.MTime = props.MTime;
1580             mi.FileID = props.FileID_Low;
1581             if (props.NumLinks <= 1)
1582               mi.FileID = 0;
1583             mi.VolID = props.VolID;
1584             if (mi.FileID != 0)
1585               miIndex = AddToHardLinkList(db.MetaItems, (unsigned)ui.MetaIndex, hlIndexes);
1586 
1587             if (props.Size != size && props.Size != (UInt64)(Int64)-1)
1588             {
1589               const Int64 delta = (Int64)props.Size - (Int64)size;
1590               const Int64 newComplexity = (Int64)totalComplexity + delta;
1591               if (newComplexity > 0)
1592               {
1593                 totalComplexity = (UInt64)newComplexity;
1594                 callback->SetTotal(totalComplexity);
1595               }
1596               mi.Size = props.Size;
1597               size = props.Size;
1598             }
1599           }
1600         }
1601       }
1602 
1603       if (miIndex >= 0)
1604       {
1605         mi.HashIndex = db.MetaItems[miIndex].HashIndex;
1606         if (mi.HashIndex >= 0)
1607           streams[mi.HashIndex].RefCount++;
1608         // fix for future: maybe we need to check also that real size is equal to size from IStreamGetProps2
1609       }
1610       else if (ui.AltStreamIndex < 0 && mi.Reparse.Size() != 0)
1611       {
1612         if (mi.Reparse.Size() < 8)
1613           return E_FAIL;
1614         NCrypto::NSha1::CContext sha1;
1615         sha1.Init();
1616         const size_t packSize = mi.Reparse.Size() - 8;
1617         sha1.Update((const Byte *)mi.Reparse + 8, packSize);
1618         Byte hash[kHashSize];
1619         sha1.Final(hash);
1620 
1621         int index = AddUniqHash(streams.ConstData(), sortedHashes, hash, (int)streams.Size());
1622 
1623         if (index != -1)
1624           streams[index].RefCount++;
1625         else
1626         {
1627           index = (int)streams.Size();
1628           RINOK(WriteStream(outStream, (const Byte *)mi.Reparse + 8, packSize))
1629           CStreamInfo s;
1630           s.Resource.PackSize = packSize;
1631           s.Resource.Offset = curPos;
1632           s.Resource.UnpackSize = packSize;
1633           s.Resource.Flags = 0; // check it
1634           /*
1635             if (useResourceCompression)
1636               s.Resource.Flags = NResourceFlags::Compressed;
1637           */
1638           s.PartNumber = 1;
1639           s.RefCount = 1;
1640           memcpy(s.Hash, hash, kHashSize);
1641           curPos += packSize;
1642 
1643           streams.Add(s);
1644         }
1645 
1646         mi.HashIndex = index;
1647       }
1648       else
1649       {
1650         inShaStream->SetStream(fileInStream);
1651 
1652         CMyComPtr<IInStream> inSeekStream;
1653         fileInStream.QueryInterface(IID_IInStream, (void **)&inSeekStream);
1654 
1655         fileInStream.Release();
1656         inShaStream->Init();
1657         UInt64 offsetBlockSize = 0;
1658         /*
1659         if (useResourceCompression)
1660         {
1661           for (UInt64 t = kChunkSize; t < size; t += kChunkSize)
1662           {
1663             Byte buf[8];
1664             SetUi32(buf, (UInt32)t);
1665             RINOK(WriteStream(outStream, buf, 4));
1666             offsetBlockSize += 4;
1667           }
1668         }
1669         */
1670 
1671         // 22.02: we use additional read-only pass to calculate SHA-1
1672         bool needWritePass = true;
1673         int index = -1;
1674 
1675         if (inSeekStream /* && !sortedHashes.IsEmpty() */)
1676         {
1677           RINOK(copyCoder.Interface()->Code(inShaStream, NULL, NULL, NULL, lps))
1678           size = copyCoder->TotalSize;
1679           if (size == 0)
1680             needWritePass = false;
1681           else
1682           {
1683             Byte hash[kHashSize];
1684             inShaStream->Final(hash);
1685 
1686             index = AddUniqHash(streams.ConstData(), sortedHashes, hash, -1);
1687             if (index != -1)
1688             {
1689               streams[index].RefCount++;
1690               needWritePass = false;
1691             }
1692             else
1693             {
1694               RINOK(InStream_SeekToBegin(inSeekStream))
1695               inShaStream->Init();
1696             }
1697           }
1698         }
1699 
1700         if (needWritePass)
1701         {
1702           RINOK(copyCoder.Interface()->Code(inShaStream, outStream, NULL, NULL, lps))
1703           size = copyCoder->TotalSize;
1704         }
1705 
1706         if (size != 0)
1707         {
1708           if (needWritePass)
1709           {
1710             Byte hash[kHashSize];
1711             const UInt64 packSize = offsetBlockSize + size;
1712             inShaStream->Final(hash);
1713 
1714             index = AddUniqHash(streams.ConstData(), sortedHashes, hash, (int)streams.Size());
1715 
1716             if (index != -1)
1717             {
1718               streams[index].RefCount++;
1719               outStream->Seek(-(Int64)packSize, STREAM_SEEK_CUR, &curPos);
1720               outStream->SetSize(curPos);
1721             }
1722             else
1723             {
1724               index = (int)streams.Size();
1725               CStreamInfo s;
1726               s.Resource.PackSize = packSize;
1727               s.Resource.Offset = curPos;
1728               s.Resource.UnpackSize = size;
1729               s.Resource.Flags = 0;
1730               /*
1731               if (useResourceCompression)
1732               s.Resource.Flags = NResourceFlags::Compressed;
1733               */
1734               s.PartNumber = 1;
1735               s.RefCount = 1;
1736               memcpy(s.Hash, hash, kHashSize);
1737               curPos += packSize;
1738 
1739               streams.Add(s);
1740             }
1741           } // needWritePass
1742           if (ui.AltStreamIndex < 0)
1743             mi.HashIndex = index;
1744           else
1745             mi.AltStreams[ui.AltStreamIndex].HashIndex = index;
1746         } // (size != 0)
1747       }
1748     }
1749     fileInStream.Release();
1750     complexity += size;
1751     RINOK(callback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK))
1752   }
1753 
1754   while (secureBlocks.Size() < numNewImages)
1755     secureBlocks.AddNew();
1756 
1757 
1758 
1759   // ---------- Write Images ----------
1760 
1761   for (i = 0; i < numNewImages; i++)
1762   {
1763     lps->InSize = lps->OutSize = complexity;
1764     RINOK(lps->SetCur())
1765     if (i < isChangedImage.Size() && !isChangedImage[i])
1766     {
1767       CStreamInfo s = _db.MetaStreams[i];
1768 
1769       RINOK(InStream_SeekSet(_volumes[1].Stream, s.Resource.Offset))
1770       inStreamLimitedSpec->Init(s.Resource.PackSize);
1771       RINOK(copyCoder.Interface()->Code(inStreamLimited, outStream, NULL, NULL, lps))
1772       if (copyCoder->TotalSize != s.Resource.PackSize)
1773         return E_FAIL;
1774 
1775       s.Resource.Offset = curPos;
1776       s.PartNumber = 1;
1777       s.RefCount = 1;
1778       streams.Add(s);
1779 
1780       if (_bootIndex != 0 && _bootIndex == (UInt32)i + 1)
1781       {
1782         header.MetadataResource = s.Resource;
1783         header.BootIndex = _bootIndex;
1784       }
1785 
1786       lps->ProgressOffset += s.Resource.PackSize;
1787       curPos += s.Resource.PackSize;
1788       // printf("\nWrite old image %x\n", i + 1);
1789       continue;
1790     }
1791 
1792     const CDir &tree = trees[i];
1793     const UInt32 kSecuritySize = 8;
1794 
1795     size_t pos = kSecuritySize;
1796 
1797     const CUniqBlocks &secUniqBlocks = secureBlocks[i];
1798     const CObjectVector<CByteBuffer> &secBufs = secUniqBlocks.Bufs;
1799     pos += (size_t)secUniqBlocks.GetTotalSizeInBytes();
1800     pos += secBufs.Size() * 8;
1801     pos = (pos + 7) & ~(size_t)7;
1802 
1803     db.DefaultDirItem = ri;
1804     pos += db.WriteTree_Dummy(tree);
1805 
1806     CByteArr meta(pos);
1807 
1808     Set32((Byte *)meta + 4, secBufs.Size()) // num security entries
1809     pos = kSecuritySize;
1810 
1811     if (secBufs.Size() == 0)
1812     {
1813       // we can write 0 here only if there is no security data, imageX does it,
1814       // but some programs expect size = 8
1815       Set32((Byte *)meta, 8) // size of security data
1816       // Set32((Byte *)meta, 0);
1817     }
1818     else
1819     {
1820       unsigned k;
1821       for (k = 0; k < secBufs.Size(); k++, pos += 8)
1822       {
1823         Set64(meta + pos, secBufs[k].Size())
1824       }
1825       for (k = 0; k < secBufs.Size(); k++)
1826       {
1827         const CByteBuffer &buf = secBufs[k];
1828         size_t size = buf.Size();
1829         if (size != 0)
1830         {
1831           memcpy(meta + pos, buf, size);
1832           pos += size;
1833         }
1834       }
1835       while ((pos & 7) != 0)
1836         meta[pos++] = 0;
1837       Set32((Byte *)meta, (UInt32)pos) // size of security data
1838     }
1839 
1840     db.Hashes = streams.ConstData();
1841     db.WriteTree(tree, (Byte *)meta, pos);
1842 
1843     {
1844       NCrypto::NSha1::CContext sha;
1845       sha.Init();
1846       sha.Update((const Byte *)meta, pos);
1847 
1848       Byte digest[kHashSize];
1849       sha.Final(digest);
1850 
1851       CStreamInfo s;
1852       s.Resource.PackSize = pos;
1853       s.Resource.Offset = curPos;
1854       s.Resource.UnpackSize = pos;
1855       s.Resource.Flags = NResourceFlags::kMetadata;
1856       s.PartNumber = 1;
1857       s.RefCount = 1;
1858       memcpy(s.Hash, digest, kHashSize);
1859       streams.Add(s);
1860 
1861       if (_bootIndex != 0 && _bootIndex == (UInt32)i + 1)
1862       {
1863         header.MetadataResource = s.Resource;
1864         header.BootIndex = _bootIndex;
1865       }
1866 
1867       RINOK(WriteStream(outStream, (const Byte *)meta, pos))
1868       meta.Free();
1869       curPos += pos;
1870     }
1871   }
1872 
1873   lps->InSize = lps->OutSize = complexity;
1874   RINOK(lps->SetCur())
1875 
1876   header.OffsetResource.UnpackSize = header.OffsetResource.PackSize = (UInt64)streams.Size() * kStreamInfoSize;
1877   header.OffsetResource.Offset = curPos;
1878   header.OffsetResource.Flags = NResourceFlags::kMetadata;
1879 
1880 
1881 
1882   // ---------- Write Streams Info Tables ----------
1883 
1884   for (i = 0; i < streams.Size(); i++)
1885   {
1886     Byte buf[kStreamInfoSize];
1887     streams[i].WriteTo(buf);
1888     RINOK(WriteStream(outStream, buf, kStreamInfoSize))
1889     curPos += kStreamInfoSize;
1890   }
1891 
1892   AString xml ("<WIM>");
1893   AddTagUInt64_ToString(xml, "TOTALBYTES", curPos);
1894   for (i = 0; i < trees.Size(); i++)
1895   {
1896     const CDir &tree = trees[i];
1897 
1898     CXmlItem item;
1899     if (_xmls.Size() == 1)
1900     {
1901       const CWimXml &_oldXml = _xmls[0];
1902       if (i < _oldXml.Images.Size())
1903       {
1904         // int ttt = _oldXml.Images[i].ItemIndexInXml;
1905         item = _oldXml.Xml.Root.SubItems[_oldXml.Images[i].ItemIndexInXml];
1906       }
1907     }
1908     if (i >= isChangedImage.Size() || isChangedImage[i])
1909     {
1910       char temp[16];
1911       if (item.Name.IsEmpty())
1912       {
1913         ConvertUInt32ToString(i + 1, temp);
1914         item.Name = "IMAGE";
1915         item.IsTag = true;
1916         CXmlProp &prop = item.Props.AddNew();
1917         prop.Name = "INDEX";
1918         prop.Value = temp;
1919       }
1920 
1921       AddTag_String_IfEmpty(item, "NAME", temp);
1922       AddTag_UInt64(item, "DIRCOUNT", tree.GetNumDirs() - 1);
1923       AddTag_UInt64(item, "FILECOUNT", tree.GetNumFiles());
1924       AddTag_UInt64(item, "TOTALBYTES", tree.GetTotalSize(db.MetaItems));
1925 
1926       AddTag_Time(item, "CREATIONTIME", ftCur);
1927       AddTag_Time(item, "LASTMODIFICATIONTIME", ftCur);
1928     }
1929 
1930     item.AppendTo(xml);
1931   }
1932   xml += "</WIM>";
1933 
1934   size_t xmlSize;
1935   {
1936     UString utf16;
1937     if (!ConvertUTF8ToUnicode(xml, utf16))
1938       return S_FALSE;
1939     xmlSize = ((size_t)utf16.Len() + 1) * 2;
1940 
1941     CByteArr xmlBuf(xmlSize);
1942     Set16((Byte *)xmlBuf, 0xFEFF)
1943     for (i = 0; i < (unsigned)utf16.Len(); i++)
1944     {
1945       Set16((Byte *)xmlBuf + 2 + (size_t)i * 2, (UInt16)utf16[i])
1946     }
1947     RINOK(WriteStream(outStream, (const Byte *)xmlBuf, xmlSize))
1948   }
1949 
1950   header.XmlResource.UnpackSize =
1951   header.XmlResource.PackSize = xmlSize;
1952   header.XmlResource.Offset = curPos;
1953   header.XmlResource.Flags = NResourceFlags::kMetadata;
1954 
1955   outStream->Seek(0, STREAM_SEEK_SET, NULL);
1956   header.NumImages = trees.Size();
1957   {
1958     Byte buf[kHeaderSizeMax];
1959     header.WriteTo(buf);
1960     RINOK(WriteStream(outStream, buf, kHeaderSizeMax))
1961   }
1962 
1963   if (setRestriction)
1964     RINOK(setRestriction->SetRestriction(0, 0))
1965 
1966   return S_OK;
1967 
1968   COM_TRY_END
1969 }
1970 
1971 }}
1972