diff options
Diffstat (limited to 'CPP/7zip/Archive/Wim/WimHandlerOut.cpp')
-rw-r--r-- | CPP/7zip/Archive/Wim/WimHandlerOut.cpp | 1932 |
1 files changed, 1932 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/Wim/WimHandlerOut.cpp b/CPP/7zip/Archive/Wim/WimHandlerOut.cpp new file mode 100644 index 0000000..6b4497f --- /dev/null +++ b/CPP/7zip/Archive/Wim/WimHandlerOut.cpp | |||
@@ -0,0 +1,1932 @@ | |||
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 | |||
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 | |||
68 | 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 | |||
96 | unsigned GetNumAltStreams() const { return AltStreams.Size() - NumSkipAltStreams; } | ||
97 | 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 | |||
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 | |||
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 | |||
154 | 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 | |||
164 | 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 | |||
174 | 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 | |||
182 | 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 | |||
190 | 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 | |||
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 | |||
223 | STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) | ||
224 | { | ||
225 | *type = NFileTimeType::kWindows; | ||
226 | return S_OK; | ||
227 | } | ||
228 | |||
229 | |||
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 | |||
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 | |||
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 | |||
286 | 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 | |||
295 | 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 | |||
315 | 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; | ||
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 | |||
337 | CInStreamWithSha1(): _sha(sizeof(CSha1)) {} | ||
338 | void SetStream(ISequentialInStream *stream) { _stream = stream; } | ||
339 | void Init() | ||
340 | { | ||
341 | _size = 0; | ||
342 | Sha1_Init(Sha()); | ||
343 | } | ||
344 | void ReleaseStream() { _stream.Release(); } | ||
345 | UInt64 GetSize() const { return _size; } | ||
346 | void Final(Byte *digest) { Sha1_Final(Sha(), digest); } | ||
347 | }; | ||
348 | |||
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 | |||
361 | static void SetFileTimeToMem(Byte *p, const FILETIME &ft) | ||
362 | { | ||
363 | Set32(p, ft.dwLowDateTime); | ||
364 | Set32(p + 4, ft.dwHighDateTime); | ||
365 | } | ||
366 | |||
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 | |||
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 | |||
506 | 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 | |||
522 | 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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
626 | static void AddTag_UInt64(CXmlItem &parentItem, const char *name, UInt64 value) | ||
627 | { | ||
628 | AddTag_UInt64_2(AddUniqueTag(parentItem, name), value); | ||
629 | } | ||
630 | |||
631 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | }} | ||