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