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