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