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