1 // 7zHandlerOut.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/StringToInt.h"
7 #include "../../../Common/Wildcard.h"
8
9 #include "../Common/ItemNameUtils.h"
10 #include "../Common/ParseProperties.h"
11
12 #include "7zHandler.h"
13 #include "7zOut.h"
14 #include "7zUpdate.h"
15
16 #ifndef EXTRACT_ONLY
17
18 using namespace NWindows;
19
20 namespace NArchive {
21 namespace N7z {
22
23 #define k_LZMA_Name "LZMA"
24 #define kDefaultMethodName "LZMA2"
25 #define k_Copy_Name "Copy"
26
27 #define k_MatchFinder_ForHeaders "BT2"
28
29 static const UInt32 k_NumFastBytes_ForHeaders = 273;
30 static const UInt32 k_Level_ForHeaders = 5;
31 static const UInt32 k_Dictionary_ForHeaders =
32 #ifdef UNDER_CE
33 1 << 18;
34 #else
35 1 << 20;
36 #endif
37
GetFileTimeType(UInt32 * type)38 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
39 {
40 *type = NFileTimeType::kWindows;
41 return S_OK;
42 }
43
PropsMethod_To_FullMethod(CMethodFull & dest,const COneMethodInfo & m)44 HRESULT CHandler::PropsMethod_To_FullMethod(CMethodFull &dest, const COneMethodInfo &m)
45 {
46 dest.CodecIndex = FindMethod_Index(
47 EXTERNAL_CODECS_VARS
48 m.MethodName, true,
49 dest.Id, dest.NumStreams);
50 if (dest.CodecIndex < 0)
51 return E_INVALIDARG;
52 (CProps &)dest = (CProps &)m;
53 return S_OK;
54 }
55
SetHeaderMethod(CCompressionMethodMode & headerMethod)56 HRESULT CHandler::SetHeaderMethod(CCompressionMethodMode &headerMethod)
57 {
58 if (!_compressHeaders)
59 return S_OK;
60 COneMethodInfo m;
61 m.MethodName = k_LZMA_Name;
62 m.AddProp_Ascii(NCoderPropID::kMatchFinder, k_MatchFinder_ForHeaders);
63 m.AddProp_Level(k_Level_ForHeaders);
64 m.AddProp32(NCoderPropID::kNumFastBytes, k_NumFastBytes_ForHeaders);
65 m.AddProp32(NCoderPropID::kDictionarySize, k_Dictionary_ForHeaders);
66 m.AddProp_NumThreads(1);
67
68 CMethodFull &methodFull = headerMethod.Methods.AddNew();
69 return PropsMethod_To_FullMethod(methodFull, m);
70 }
71
72
SetMainMethod(CCompressionMethodMode & methodMode)73 HRESULT CHandler::SetMainMethod(CCompressionMethodMode &methodMode)
74 {
75 methodMode.Bonds = _bonds;
76
77 // we create local copy of _methods. So we can modify it.
78 CObjectVector<COneMethodInfo> methods = _methods;
79
80 {
81 FOR_VECTOR (i, methods)
82 {
83 AString &methodName = methods[i].MethodName;
84 if (methodName.IsEmpty())
85 methodName = kDefaultMethodName;
86 }
87 if (methods.IsEmpty())
88 {
89 COneMethodInfo &m = methods.AddNew();
90 m.MethodName = (GetLevel() == 0 ? k_Copy_Name : kDefaultMethodName);
91 methodMode.DefaultMethod_was_Inserted = true;
92 }
93 }
94
95 if (!_filterMethod.MethodName.IsEmpty())
96 {
97 // if (methodMode.Bonds.IsEmpty())
98 {
99 FOR_VECTOR (k, methodMode.Bonds)
100 {
101 CBond2 &bond = methodMode.Bonds[k];
102 bond.InCoder++;
103 bond.OutCoder++;
104 }
105 methods.Insert(0, _filterMethod);
106 methodMode.Filter_was_Inserted = true;
107 }
108 }
109
110 const UInt64 kSolidBytes_Min = (1 << 24);
111 const UInt64 kSolidBytes_Max = ((UInt64)1 << 32);
112
113 bool needSolid = false;
114
115 FOR_VECTOR (i, methods)
116 {
117 COneMethodInfo &oneMethodInfo = methods[i];
118
119 SetGlobalLevelTo(oneMethodInfo);
120
121 #ifndef _7ZIP_ST
122 const bool numThreads_WasSpecifiedInMethod = (oneMethodInfo.Get_NumThreads() >= 0);
123 if (!numThreads_WasSpecifiedInMethod)
124 {
125 // here we set the (NCoderPropID::kNumThreads) property in each method, only if there is no such property already
126 CMultiMethodProps::SetMethodThreadsTo_IfNotFinded(oneMethodInfo, methodMode.NumThreads);
127 }
128 #endif
129
130 CMethodFull &methodFull = methodMode.Methods.AddNew();
131 RINOK(PropsMethod_To_FullMethod(methodFull, oneMethodInfo));
132
133 #ifndef _7ZIP_ST
134 methodFull.Set_NumThreads = true;
135 methodFull.NumThreads = methodMode.NumThreads;
136 #endif
137
138 if (methodFull.Id != k_Copy)
139 needSolid = true;
140
141 UInt64 dicSize;
142 switch (methodFull.Id)
143 {
144 case k_LZMA:
145 case k_LZMA2: dicSize = oneMethodInfo.Get_Lzma_DicSize(); break;
146 case k_PPMD: dicSize = oneMethodInfo.Get_Ppmd_MemSize(); break;
147 case k_Deflate: dicSize = (UInt32)1 << 15; break;
148 case k_Deflate64: dicSize = (UInt32)1 << 16; break;
149 case k_BZip2: dicSize = oneMethodInfo.Get_BZip2_BlockSize(); break;
150 default: continue;
151 }
152
153 UInt64 numSolidBytes;
154
155 if (methodFull.Id == k_LZMA2)
156 {
157 // he we calculate default chunk Size for LZMA2 as defined in LZMA2 encoder code
158 /* lzma2 code use dictionary upo to fake 4 GiB to calculate ChunkSize.
159 So we do same */
160 UInt64 cs = (UInt64)dicSize << 2;
161 const UInt32 kMinSize = (UInt32)1 << 20;
162 const UInt32 kMaxSize = (UInt32)1 << 28;
163 if (cs < kMinSize) cs = kMinSize;
164 if (cs > kMaxSize) cs = kMaxSize;
165 if (cs < dicSize) cs = dicSize;
166 cs += (kMinSize - 1);
167 cs &= ~(UInt64)(kMinSize - 1);
168 // we want to use at least 64 chunks (threads) per one solid block.
169
170 // here we don't use chunckSize property
171 numSolidBytes = cs << 6;
172
173 // here we get real chunckSize
174 cs = oneMethodInfo.Get_Xz_BlockSize();
175 if (dicSize > cs)
176 dicSize = cs;
177
178 const UInt64 kSolidBytes_Lzma2_Max = ((UInt64)1 << 34);
179 if (numSolidBytes > kSolidBytes_Lzma2_Max)
180 numSolidBytes = kSolidBytes_Lzma2_Max;
181
182 methodFull.Set_NumThreads = false; // we don't use ICompressSetCoderMt::SetNumberOfThreads() for LZMA2 encoder
183
184 #ifndef _7ZIP_ST
185 if (!numThreads_WasSpecifiedInMethod
186 && !methodMode.NumThreads_WasForced
187 && methodMode.MemoryUsageLimit_WasSet
188 )
189 {
190 const UInt32 lzmaThreads = oneMethodInfo.Get_Lzma_NumThreads();
191 const UInt32 numBlockThreads_Original = methodMode.NumThreads / lzmaThreads;
192
193 if (numBlockThreads_Original > 1)
194 {
195 /*
196 const UInt32 kNumThreads_Max = 1024;
197 if (numBlockThreads > kNumMaxThreads)
198 numBlockThreads = kNumMaxThreads;
199 */
200
201 UInt32 numBlockThreads = numBlockThreads_Original;
202 const UInt64 lzmaMemUsage = oneMethodInfo.Get_Lzma_MemUsage(false); // solid
203
204 for (; numBlockThreads > 1; numBlockThreads--)
205 {
206 UInt64 size = numBlockThreads * (lzmaMemUsage + cs);
207 UInt32 numPackChunks = numBlockThreads + (numBlockThreads / 8) + 1;
208 if (cs < ((UInt32)1 << 26)) numPackChunks++;
209 if (cs < ((UInt32)1 << 24)) numPackChunks++;
210 if (cs < ((UInt32)1 << 22)) numPackChunks++;
211 size += numPackChunks * cs;
212 // printf("\nnumBlockThreads = %d, size = %d\n", (unsigned)(numBlockThreads), (unsigned)(size >> 20));
213 if (size <= methodMode.MemoryUsageLimit)
214 break;
215 }
216
217 if (numBlockThreads == 0)
218 numBlockThreads = 1;
219 if (numBlockThreads != numBlockThreads_Original)
220 {
221 const UInt32 numThreads_New = numBlockThreads * lzmaThreads;
222 CMultiMethodProps::SetMethodThreadsTo_Replace(methodFull, numThreads_New);
223 }
224 }
225 }
226 #endif
227 }
228 else
229 {
230 numSolidBytes = (UInt64)dicSize << 7;
231 if (numSolidBytes > kSolidBytes_Max)
232 numSolidBytes = kSolidBytes_Max;
233 }
234
235 if (_numSolidBytesDefined)
236 continue;
237
238 if (numSolidBytes < kSolidBytes_Min)
239 numSolidBytes = kSolidBytes_Min;
240 _numSolidBytes = numSolidBytes;
241 _numSolidBytesDefined = true;
242 }
243
244 if (!_numSolidBytesDefined)
245 {
246 if (needSolid)
247 _numSolidBytes = kSolidBytes_Max;
248 else
249 _numSolidBytes = 0;
250 }
251 _numSolidBytesDefined = true;
252
253
254 return S_OK;
255 }
256
257
258
GetTime(IArchiveUpdateCallback * updateCallback,unsigned index,PROPID propID,UInt64 & ft,bool & ftDefined)259 static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, unsigned index, PROPID propID, UInt64 &ft, bool &ftDefined)
260 {
261 // ft = 0;
262 // ftDefined = false;
263 NCOM::CPropVariant prop;
264 RINOK(updateCallback->GetProperty(index, propID, &prop));
265 if (prop.vt == VT_FILETIME)
266 {
267 ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32);
268 ftDefined = true;
269 }
270 else if (prop.vt != VT_EMPTY)
271 return E_INVALIDARG;
272 else
273 {
274 ft = 0;
275 ftDefined = false;
276 }
277 return S_OK;
278 }
279
280 /*
281
282 #ifdef _WIN32
283 static const wchar_t kDirDelimiter1 = L'\\';
284 #endif
285 static const wchar_t kDirDelimiter2 = L'/';
286
287 static inline bool IsCharDirLimiter(wchar_t c)
288 {
289 return (
290 #ifdef _WIN32
291 c == kDirDelimiter1 ||
292 #endif
293 c == kDirDelimiter2);
294 }
295
296 static int FillSortIndex(CObjectVector<CTreeFolder> &treeFolders, int cur, int curSortIndex)
297 {
298 CTreeFolder &tf = treeFolders[cur];
299 tf.SortIndex = curSortIndex++;
300 for (int i = 0; i < tf.SubFolders.Size(); i++)
301 curSortIndex = FillSortIndex(treeFolders, tf.SubFolders[i], curSortIndex);
302 tf.SortIndexEnd = curSortIndex;
303 return curSortIndex;
304 }
305
306 static int FindSubFolder(const CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name, int &insertPos)
307 {
308 const CIntVector &subFolders = treeFolders[cur].SubFolders;
309 int left = 0, right = subFolders.Size();
310 insertPos = -1;
311 for (;;)
312 {
313 if (left == right)
314 {
315 insertPos = left;
316 return -1;
317 }
318 int mid = (left + right) / 2;
319 int midFolder = subFolders[mid];
320 int compare = CompareFileNames(name, treeFolders[midFolder].Name);
321 if (compare == 0)
322 return midFolder;
323 if (compare < 0)
324 right = mid;
325 else
326 left = mid + 1;
327 }
328 }
329
330 static int AddFolder(CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name)
331 {
332 int insertPos;
333 int folderIndex = FindSubFolder(treeFolders, cur, name, insertPos);
334 if (folderIndex < 0)
335 {
336 folderIndex = treeFolders.Size();
337 CTreeFolder &newFolder = treeFolders.AddNew();
338 newFolder.Parent = cur;
339 newFolder.Name = name;
340 treeFolders[cur].SubFolders.Insert(insertPos, folderIndex);
341 }
342 // else if (treeFolders[folderIndex].IsAltStreamFolder != isAltStreamFolder) throw 1123234234;
343 return folderIndex;
344 }
345 */
346
UpdateItems(ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback)347 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
348 IArchiveUpdateCallback *updateCallback)
349 {
350 COM_TRY_BEGIN
351
352 const CDbEx *db = 0;
353 #ifdef _7Z_VOL
354 if (_volumes.Size() > 1)
355 return E_FAIL;
356 const CVolume *volume = 0;
357 if (_volumes.Size() == 1)
358 {
359 volume = &_volumes.Front();
360 db = &volume->Database;
361 }
362 #else
363 if (_inStream != 0)
364 db = &_db;
365 #endif
366
367 if (db && !db->CanUpdate())
368 return E_NOTIMPL;
369
370 /*
371 CMyComPtr<IArchiveGetRawProps> getRawProps;
372 updateCallback->QueryInterface(IID_IArchiveGetRawProps, (void **)&getRawProps);
373
374 CUniqBlocks secureBlocks;
375 secureBlocks.AddUniq(NULL, 0);
376
377 CObjectVector<CTreeFolder> treeFolders;
378 {
379 CTreeFolder folder;
380 folder.Parent = -1;
381 treeFolders.Add(folder);
382 }
383 */
384
385 CObjectVector<CUpdateItem> updateItems;
386
387 bool need_CTime = (Write_CTime.Def && Write_CTime.Val);
388 bool need_ATime = (Write_ATime.Def && Write_ATime.Val);
389 bool need_MTime = (Write_MTime.Def ? Write_MTime.Val : true);
390 bool need_Attrib = (Write_Attrib.Def ? Write_Attrib.Val : true);
391
392 if (db && !db->Files.IsEmpty())
393 {
394 if (!Write_CTime.Def) need_CTime = !db->CTime.Defs.IsEmpty();
395 if (!Write_ATime.Def) need_ATime = !db->ATime.Defs.IsEmpty();
396 if (!Write_MTime.Def) need_MTime = !db->MTime.Defs.IsEmpty();
397 if (!Write_Attrib.Def) need_Attrib = !db->Attrib.Defs.IsEmpty();
398 }
399
400 // UString s;
401 UString name;
402
403 for (UInt32 i = 0; i < numItems; i++)
404 {
405 Int32 newData, newProps;
406 UInt32 indexInArchive;
407 if (!updateCallback)
408 return E_FAIL;
409 RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive));
410 CUpdateItem ui;
411 ui.NewProps = IntToBool(newProps);
412 ui.NewData = IntToBool(newData);
413 ui.IndexInArchive = (int)indexInArchive;
414 ui.IndexInClient = i;
415 ui.IsAnti = false;
416 ui.Size = 0;
417
418 name.Empty();
419 // bool isAltStream = false;
420 if (ui.IndexInArchive != -1)
421 {
422 if (!db || (unsigned)ui.IndexInArchive >= db->Files.Size())
423 return E_INVALIDARG;
424 const CFileItem &fi = db->Files[(unsigned)ui.IndexInArchive];
425 if (!ui.NewProps)
426 {
427 _db.GetPath((unsigned)ui.IndexInArchive, name);
428 }
429 ui.IsDir = fi.IsDir;
430 ui.Size = fi.Size;
431 // isAltStream = fi.IsAltStream;
432 ui.IsAnti = db->IsItemAnti((unsigned)ui.IndexInArchive);
433
434 if (!ui.NewProps)
435 {
436 ui.CTimeDefined = db->CTime.GetItem((unsigned)ui.IndexInArchive, ui.CTime);
437 ui.ATimeDefined = db->ATime.GetItem((unsigned)ui.IndexInArchive, ui.ATime);
438 ui.MTimeDefined = db->MTime.GetItem((unsigned)ui.IndexInArchive, ui.MTime);
439 }
440 }
441
442 if (ui.NewProps)
443 {
444 bool folderStatusIsDefined;
445 if (need_Attrib)
446 {
447 NCOM::CPropVariant prop;
448 RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop));
449 if (prop.vt == VT_EMPTY)
450 ui.AttribDefined = false;
451 else if (prop.vt != VT_UI4)
452 return E_INVALIDARG;
453 else
454 {
455 ui.Attrib = prop.ulVal;
456 ui.AttribDefined = true;
457 }
458 }
459
460 // we need MTime to sort files.
461 if (need_CTime) RINOK(GetTime(updateCallback, i, kpidCTime, ui.CTime, ui.CTimeDefined));
462 if (need_ATime) RINOK(GetTime(updateCallback, i, kpidATime, ui.ATime, ui.ATimeDefined));
463 if (need_MTime) RINOK(GetTime(updateCallback, i, kpidMTime, ui.MTime, ui.MTimeDefined));
464
465 /*
466 if (getRawProps)
467 {
468 const void *data;
469 UInt32 dataSize;
470 UInt32 propType;
471
472 getRawProps->GetRawProp(i, kpidNtSecure, &data, &dataSize, &propType);
473 if (dataSize != 0 && propType != NPropDataType::kRaw)
474 return E_FAIL;
475 ui.SecureIndex = secureBlocks.AddUniq((const Byte *)data, dataSize);
476 }
477 */
478
479 {
480 NCOM::CPropVariant prop;
481 RINOK(updateCallback->GetProperty(i, kpidPath, &prop));
482 if (prop.vt == VT_EMPTY)
483 {
484 }
485 else if (prop.vt != VT_BSTR)
486 return E_INVALIDARG;
487 else
488 {
489 name = prop.bstrVal;
490 NItemName::ReplaceSlashes_OsToUnix(name);
491 }
492 }
493 {
494 NCOM::CPropVariant prop;
495 RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop));
496 if (prop.vt == VT_EMPTY)
497 folderStatusIsDefined = false;
498 else if (prop.vt != VT_BOOL)
499 return E_INVALIDARG;
500 else
501 {
502 ui.IsDir = (prop.boolVal != VARIANT_FALSE);
503 folderStatusIsDefined = true;
504 }
505 }
506
507 {
508 NCOM::CPropVariant prop;
509 RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop));
510 if (prop.vt == VT_EMPTY)
511 ui.IsAnti = false;
512 else if (prop.vt != VT_BOOL)
513 return E_INVALIDARG;
514 else
515 ui.IsAnti = (prop.boolVal != VARIANT_FALSE);
516 }
517
518 /*
519 {
520 NCOM::CPropVariant prop;
521 RINOK(updateCallback->GetProperty(i, kpidIsAltStream, &prop));
522 if (prop.vt == VT_EMPTY)
523 isAltStream = false;
524 else if (prop.vt != VT_BOOL)
525 return E_INVALIDARG;
526 else
527 isAltStream = (prop.boolVal != VARIANT_FALSE);
528 }
529 */
530
531 if (ui.IsAnti)
532 {
533 ui.AttribDefined = false;
534
535 ui.CTimeDefined = false;
536 ui.ATimeDefined = false;
537 ui.MTimeDefined = false;
538
539 ui.Size = 0;
540 }
541
542 if (!folderStatusIsDefined && ui.AttribDefined)
543 ui.SetDirStatusFromAttrib();
544 }
545 else
546 {
547 /*
548 if (_db.SecureIDs.IsEmpty())
549 ui.SecureIndex = secureBlocks.AddUniq(NULL, 0);
550 else
551 {
552 int id = _db.SecureIDs[ui.IndexInArchive];
553 size_t offs = _db.SecureOffsets[id];
554 size_t size = _db.SecureOffsets[id + 1] - offs;
555 ui.SecureIndex = secureBlocks.AddUniq(_db.SecureBuf + offs, size);
556 }
557 */
558 }
559
560 /*
561 {
562 int folderIndex = 0;
563 if (_useParents)
564 {
565 int j;
566 s.Empty();
567 for (j = 0; j < name.Len(); j++)
568 {
569 wchar_t c = name[j];
570 if (IsCharDirLimiter(c))
571 {
572 folderIndex = AddFolder(treeFolders, folderIndex, s);
573 s.Empty();
574 continue;
575 }
576 s += c;
577 }
578 if (isAltStream)
579 {
580 int colonPos = s.Find(':');
581 if (colonPos < 0)
582 {
583 // isAltStream = false;
584 return E_INVALIDARG;
585 }
586 UString mainName = s.Left(colonPos);
587 int newFolderIndex = AddFolder(treeFolders, folderIndex, mainName);
588 if (treeFolders[newFolderIndex].UpdateItemIndex < 0)
589 {
590 for (int j = updateItems.Size() - 1; j >= 0; j--)
591 {
592 CUpdateItem &ui2 = updateItems[j];
593 if (ui2.ParentFolderIndex == folderIndex
594 && ui2.Name == mainName)
595 {
596 ui2.TreeFolderIndex = newFolderIndex;
597 treeFolders[newFolderIndex].UpdateItemIndex = j;
598 }
599 }
600 }
601 folderIndex = newFolderIndex;
602 s.Delete(0, colonPos + 1);
603 }
604 ui.Name = s;
605 }
606 else
607 ui.Name = name;
608 ui.IsAltStream = isAltStream;
609 ui.ParentFolderIndex = folderIndex;
610 ui.TreeFolderIndex = -1;
611 if (ui.IsDir && !s.IsEmpty())
612 {
613 ui.TreeFolderIndex = AddFolder(treeFolders, folderIndex, s);
614 treeFolders[ui.TreeFolderIndex].UpdateItemIndex = updateItems.Size();
615 }
616 }
617 */
618 ui.Name = name;
619
620 if (ui.NewData)
621 {
622 ui.Size = 0;
623 if (!ui.IsDir)
624 {
625 NCOM::CPropVariant prop;
626 RINOK(updateCallback->GetProperty(i, kpidSize, &prop));
627 if (prop.vt != VT_UI8)
628 return E_INVALIDARG;
629 ui.Size = (UInt64)prop.uhVal.QuadPart;
630 if (ui.Size != 0 && ui.IsAnti)
631 return E_INVALIDARG;
632 }
633 }
634
635 updateItems.Add(ui);
636 }
637
638 /*
639 FillSortIndex(treeFolders, 0, 0);
640 for (i = 0; i < (UInt32)updateItems.Size(); i++)
641 {
642 CUpdateItem &ui = updateItems[i];
643 ui.ParentSortIndex = treeFolders[ui.ParentFolderIndex].SortIndex;
644 ui.ParentSortIndexEnd = treeFolders[ui.ParentFolderIndex].SortIndexEnd;
645 }
646 */
647
648 CCompressionMethodMode methodMode, headerMethod;
649
650 methodMode.MemoryUsageLimit = _memUsage_Compress;
651 methodMode.MemoryUsageLimit_WasSet = _memUsage_WasSet;
652
653 #ifndef _7ZIP_ST
654 {
655 UInt32 numThreads = _numThreads;
656 const UInt32 kNumThreads_Max = 1024;
657 if (numThreads > kNumThreads_Max)
658 numThreads = kNumThreads_Max;
659 methodMode.NumThreads = numThreads;
660 methodMode.NumThreads_WasForced = _numThreads_WasForced;
661 methodMode.MultiThreadMixer = _useMultiThreadMixer;
662 // headerMethod.NumThreads = 1;
663 headerMethod.MultiThreadMixer = _useMultiThreadMixer;
664 }
665 #endif
666
667 HRESULT res = SetMainMethod(methodMode);
668 RINOK(res);
669
670 RINOK(SetHeaderMethod(headerMethod));
671
672 CMyComPtr<ICryptoGetTextPassword2> getPassword2;
673 updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2);
674
675 methodMode.PasswordIsDefined = false;
676 methodMode.Password.Wipe_and_Empty();
677 if (getPassword2)
678 {
679 CMyComBSTR_Wipe password;
680 Int32 passwordIsDefined;
681 RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password));
682 methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
683 if (methodMode.PasswordIsDefined && password)
684 methodMode.Password = password;
685 }
686
687 bool compressMainHeader = _compressHeaders; // check it
688
689 bool encryptHeaders = false;
690
691 #ifndef _NO_CRYPTO
692 if (!methodMode.PasswordIsDefined && _passwordIsDefined)
693 {
694 // if header is compressed, we use that password for updated archive
695 methodMode.PasswordIsDefined = true;
696 methodMode.Password = _password;
697 }
698 #endif
699
700 if (methodMode.PasswordIsDefined)
701 {
702 if (_encryptHeadersSpecified)
703 encryptHeaders = _encryptHeaders;
704 #ifndef _NO_CRYPTO
705 else
706 encryptHeaders = _passwordIsDefined;
707 #endif
708 compressMainHeader = true;
709 if (encryptHeaders)
710 {
711 headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined;
712 headerMethod.Password = methodMode.Password;
713 }
714 }
715
716 if (numItems < 2)
717 compressMainHeader = false;
718
719 int level = GetLevel();
720
721 CUpdateOptions options;
722 options.Method = &methodMode;
723 options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : NULL;
724 options.UseFilters = (level != 0 && _autoFilter && !methodMode.Filter_was_Inserted);
725 options.MaxFilter = (level >= 8);
726 options.AnalysisLevel = GetAnalysisLevel();
727
728 options.HeaderOptions.CompressMainHeader = compressMainHeader;
729 /*
730 options.HeaderOptions.WriteCTime = Write_CTime;
731 options.HeaderOptions.WriteATime = Write_ATime;
732 options.HeaderOptions.WriteMTime = Write_MTime;
733 options.HeaderOptions.WriteAttrib = Write_Attrib;
734 */
735
736 options.NumSolidFiles = _numSolidFiles;
737 options.NumSolidBytes = _numSolidBytes;
738 options.SolidExtension = _solidExtension;
739 options.UseTypeSorting = _useTypeSorting;
740
741 options.RemoveSfxBlock = _removeSfxBlock;
742 // options.VolumeMode = _volumeMode;
743
744 options.MultiThreadMixer = _useMultiThreadMixer;
745
746 COutArchive archive;
747 CArchiveDatabaseOut newDatabase;
748
749 CMyComPtr<ICryptoGetTextPassword> getPassword;
750 updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword);
751
752 /*
753 if (secureBlocks.Sorted.Size() > 1)
754 {
755 secureBlocks.GetReverseMap();
756 for (int i = 0; i < updateItems.Size(); i++)
757 {
758 int &secureIndex = updateItems[i].SecureIndex;
759 secureIndex = secureBlocks.BufIndexToSortedIndex[secureIndex];
760 }
761 }
762 */
763
764 res = Update(
765 EXTERNAL_CODECS_VARS
766 #ifdef _7Z_VOL
767 volume ? volume->Stream: 0,
768 volume ? db : 0,
769 #else
770 _inStream,
771 db,
772 #endif
773 updateItems,
774 // treeFolders,
775 // secureBlocks,
776 archive, newDatabase, outStream, updateCallback, options
777 #ifndef _NO_CRYPTO
778 , getPassword
779 #endif
780 );
781
782 RINOK(res);
783
784 updateItems.ClearAndFree();
785
786 return archive.WriteDatabase(EXTERNAL_CODECS_VARS
787 newDatabase, options.HeaderMethod, options.HeaderOptions);
788
789 COM_TRY_END
790 }
791
ParseBond(UString & srcString,UInt32 & coder,UInt32 & stream)792 static HRESULT ParseBond(UString &srcString, UInt32 &coder, UInt32 &stream)
793 {
794 stream = 0;
795 {
796 unsigned index = ParseStringToUInt32(srcString, coder);
797 if (index == 0)
798 return E_INVALIDARG;
799 srcString.DeleteFrontal(index);
800 }
801 if (srcString[0] == 's')
802 {
803 srcString.Delete(0);
804 unsigned index = ParseStringToUInt32(srcString, stream);
805 if (index == 0)
806 return E_INVALIDARG;
807 srcString.DeleteFrontal(index);
808 }
809 return S_OK;
810 }
811
InitProps7z()812 void COutHandler::InitProps7z()
813 {
814 _removeSfxBlock = false;
815 _compressHeaders = true;
816 _encryptHeadersSpecified = false;
817 _encryptHeaders = false;
818 // _useParents = false;
819
820 Write_CTime.Init();
821 Write_ATime.Init();
822 Write_MTime.Init();
823 Write_Attrib.Init();
824
825 _useMultiThreadMixer = true;
826
827 // _volumeMode = false;
828
829 InitSolid();
830 _useTypeSorting = false;
831 }
832
InitProps()833 void COutHandler::InitProps()
834 {
835 CMultiMethodProps::Init();
836 InitProps7z();
837 }
838
839
840
SetSolidFromString(const UString & s)841 HRESULT COutHandler::SetSolidFromString(const UString &s)
842 {
843 UString s2 = s;
844 s2.MakeLower_Ascii();
845 for (unsigned i = 0; i < s2.Len();)
846 {
847 const wchar_t *start = ((const wchar_t *)s2) + i;
848 const wchar_t *end;
849 UInt64 v = ConvertStringToUInt64(start, &end);
850 if (start == end)
851 {
852 if (s2[i++] != 'e')
853 return E_INVALIDARG;
854 _solidExtension = true;
855 continue;
856 }
857 i += (unsigned)(end - start);
858 if (i == s2.Len())
859 return E_INVALIDARG;
860 wchar_t c = s2[i++];
861 if (c == 'f')
862 {
863 if (v < 1)
864 v = 1;
865 _numSolidFiles = v;
866 }
867 else
868 {
869 unsigned numBits;
870 switch (c)
871 {
872 case 'b': numBits = 0; break;
873 case 'k': numBits = 10; break;
874 case 'm': numBits = 20; break;
875 case 'g': numBits = 30; break;
876 case 't': numBits = 40; break;
877 default: return E_INVALIDARG;
878 }
879 _numSolidBytes = (v << numBits);
880 _numSolidBytesDefined = true;
881 /*
882 if (_numSolidBytes == 0)
883 _numSolidFiles = 1;
884 */
885 }
886 }
887 return S_OK;
888 }
889
SetSolidFromPROPVARIANT(const PROPVARIANT & value)890 HRESULT COutHandler::SetSolidFromPROPVARIANT(const PROPVARIANT &value)
891 {
892 bool isSolid;
893 switch (value.vt)
894 {
895 case VT_EMPTY: isSolid = true; break;
896 case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break;
897 case VT_BSTR:
898 if (StringToBool(value.bstrVal, isSolid))
899 break;
900 return SetSolidFromString(value.bstrVal);
901 default: return E_INVALIDARG;
902 }
903 if (isSolid)
904 InitSolid();
905 else
906 _numSolidFiles = 1;
907 return S_OK;
908 }
909
PROPVARIANT_to_BoolPair(const PROPVARIANT & prop,CBoolPair & dest)910 static HRESULT PROPVARIANT_to_BoolPair(const PROPVARIANT &prop, CBoolPair &dest)
911 {
912 RINOK(PROPVARIANT_to_bool(prop, dest.Val));
913 dest.Def = true;
914 return S_OK;
915 }
916
SetProperty(const wchar_t * nameSpec,const PROPVARIANT & value)917 HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
918 {
919 UString name = nameSpec;
920 name.MakeLower_Ascii();
921 if (name.IsEmpty())
922 return E_INVALIDARG;
923
924 if (name[0] == L's')
925 {
926 name.Delete(0);
927 if (name.IsEmpty())
928 return SetSolidFromPROPVARIANT(value);
929 if (value.vt != VT_EMPTY)
930 return E_INVALIDARG;
931 return SetSolidFromString(name);
932 }
933
934 UInt32 number;
935 unsigned index = ParseStringToUInt32(name, number);
936 // UString realName = name.Ptr(index);
937 if (index == 0)
938 {
939 if (name.IsEqualTo("rsfx")) return PROPVARIANT_to_bool(value, _removeSfxBlock);
940 if (name.IsEqualTo("hc")) return PROPVARIANT_to_bool(value, _compressHeaders);
941 // if (name.IsEqualToNoCase(L"HS")) return PROPVARIANT_to_bool(value, _useParents);
942
943 if (name.IsEqualTo("hcf"))
944 {
945 bool compressHeadersFull = true;
946 RINOK(PROPVARIANT_to_bool(value, compressHeadersFull));
947 return compressHeadersFull ? S_OK: E_INVALIDARG;
948 }
949
950 if (name.IsEqualTo("he"))
951 {
952 RINOK(PROPVARIANT_to_bool(value, _encryptHeaders));
953 _encryptHeadersSpecified = true;
954 return S_OK;
955 }
956
957 if (name.IsEqualTo("tc")) return PROPVARIANT_to_BoolPair(value, Write_CTime);
958 if (name.IsEqualTo("ta")) return PROPVARIANT_to_BoolPair(value, Write_ATime);
959 if (name.IsEqualTo("tm")) return PROPVARIANT_to_BoolPair(value, Write_MTime);
960
961 if (name.IsEqualTo("tr")) return PROPVARIANT_to_BoolPair(value, Write_Attrib);
962
963 if (name.IsEqualTo("mtf")) return PROPVARIANT_to_bool(value, _useMultiThreadMixer);
964
965 if (name.IsEqualTo("qs")) return PROPVARIANT_to_bool(value, _useTypeSorting);
966
967 // if (name.IsEqualTo("v")) return PROPVARIANT_to_bool(value, _volumeMode);
968 }
969 return CMultiMethodProps::SetProperty(name, value);
970 }
971
SetProperties(const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps)972 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
973 {
974 COM_TRY_BEGIN
975 _bonds.Clear();
976 InitProps();
977
978 for (UInt32 i = 0; i < numProps; i++)
979 {
980 UString name = names[i];
981 name.MakeLower_Ascii();
982 if (name.IsEmpty())
983 return E_INVALIDARG;
984
985 const PROPVARIANT &value = values[i];
986
987 if (name[0] == 'b')
988 {
989 if (value.vt != VT_EMPTY)
990 return E_INVALIDARG;
991 name.Delete(0);
992
993 CBond2 bond;
994 RINOK(ParseBond(name, bond.OutCoder, bond.OutStream));
995 if (name[0] != ':')
996 return E_INVALIDARG;
997 name.Delete(0);
998 UInt32 inStream = 0;
999 RINOK(ParseBond(name, bond.InCoder, inStream));
1000 if (inStream != 0)
1001 return E_INVALIDARG;
1002 if (!name.IsEmpty())
1003 return E_INVALIDARG;
1004 _bonds.Add(bond);
1005 continue;
1006 }
1007
1008 RINOK(SetProperty(name, value));
1009 }
1010
1011 unsigned numEmptyMethods = GetNumEmptyMethods();
1012 if (numEmptyMethods > 0)
1013 {
1014 unsigned k;
1015 for (k = 0; k < _bonds.Size(); k++)
1016 {
1017 const CBond2 &bond = _bonds[k];
1018 if (bond.InCoder < (UInt32)numEmptyMethods ||
1019 bond.OutCoder < (UInt32)numEmptyMethods)
1020 return E_INVALIDARG;
1021 }
1022 for (k = 0; k < _bonds.Size(); k++)
1023 {
1024 CBond2 &bond = _bonds[k];
1025 bond.InCoder -= (UInt32)numEmptyMethods;
1026 bond.OutCoder -= (UInt32)numEmptyMethods;
1027 }
1028 _methods.DeleteFrontal(numEmptyMethods);
1029 }
1030
1031 FOR_VECTOR (k, _bonds)
1032 {
1033 const CBond2 &bond = _bonds[k];
1034 if (bond.InCoder >= (UInt32)_methods.Size() ||
1035 bond.OutCoder >= (UInt32)_methods.Size())
1036 return E_INVALIDARG;
1037 }
1038
1039 return S_OK;
1040 COM_TRY_END
1041 }
1042
1043 }}
1044
1045 #endif
1046