xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/Common/Update.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // Update.cpp
2 
3 #include "StdAfx.h"
4 
5 // #include  <stdio.h>
6 
7 #include "Update.h"
8 
9 #include "../../../Common/StringConvert.h"
10 
11 #include "../../../Windows/DLL.h"
12 #include "../../../Windows/FileDir.h"
13 #include "../../../Windows/FileFind.h"
14 #include "../../../Windows/FileName.h"
15 #include "../../../Windows/PropVariant.h"
16 #include "../../../Windows/PropVariantConv.h"
17 #include "../../../Windows/TimeUtils.h"
18 
19 #include "../../Common/FileStreams.h"
20 #include "../../Common/LimitedStreams.h"
21 #include "../../Common/MultiOutStream.h"
22 #include "../../Common/StreamUtils.h"
23 
24 #include "../../Compress/CopyCoder.h"
25 
26 #include "../Common/DirItem.h"
27 #include "../Common/EnumDirItems.h"
28 #include "../Common/OpenArchive.h"
29 #include "../Common/UpdateProduce.h"
30 
31 #include "EnumDirItems.h"
32 #include "SetProperties.h"
33 #include "TempFiles.h"
34 #include "UpdateCallback.h"
35 
36 static const char * const kUpdateIsNotSupoorted =
37   "update operations are not supported for this archive";
38 
39 static const char * const kUpdateIsNotSupported_MultiVol =
40   "Updating for multivolume archives is not implemented";
41 
42 using namespace NWindows;
43 using namespace NCOM;
44 using namespace NFile;
45 using namespace NDir;
46 using namespace NName;
47 
48 #ifdef _WIN32
49 static CFSTR const kTempFolderPrefix = FTEXT("7zE");
50 #endif
51 
SetFromLastError(const char * message)52 void CUpdateErrorInfo::SetFromLastError(const char *message)
53 {
54   SystemError = ::GetLastError();
55   Message = message;
56 }
57 
SetFromLastError(const char * message,const FString & fileName)58 HRESULT CUpdateErrorInfo::SetFromLastError(const char *message, const FString &fileName)
59 {
60   SetFromLastError(message);
61   FileNames.Add(fileName);
62   return Get_HRESULT_Error();
63 }
64 
SetFromError_DWORD(const char * message,const FString & fileName,DWORD error)65 HRESULT CUpdateErrorInfo::SetFromError_DWORD(const char *message, const FString &fileName, DWORD error)
66 {
67   Message = message;
68   FileNames.Add(fileName);
69   SystemError = error;
70   return Get_HRESULT_Error();
71 }
72 
73 
74 using namespace NUpdateArchive;
75 
76 struct CMultiOutStream_Rec
77 {
78   CMultiOutStream *Spec;
79   CMyComPtr<IOutStream> Ref;
80 };
81 
82 struct CMultiOutStream_Bunch
83 {
84   CObjectVector<CMultiOutStream_Rec> Items;
85 
DestructCMultiOutStream_Bunch86   HRESULT Destruct()
87   {
88     HRESULT hres = S_OK;
89     FOR_VECTOR (i, Items)
90     {
91       CMultiOutStream_Rec &rec = Items[i];
92       if (rec.Ref)
93       {
94         const HRESULT hres2 = rec.Spec->Destruct();
95         if (hres == S_OK)
96           hres = hres2;
97       }
98     }
99     Items.Clear();
100     return hres;
101   }
102 
DisableDeletionCMultiOutStream_Bunch103   void DisableDeletion()
104   {
105     FOR_VECTOR (i, Items)
106     {
107       CMultiOutStream_Rec &rec = Items[i];
108       if (rec.Ref)
109         rec.Spec->NeedDelete = false;
110     }
111   }
112 };
113 
114 
ParseFromPath(const UString & path,EArcNameMode mode)115 void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode)
116 {
117   OriginalPath = path;
118 
119   SplitPathToParts_2(path, Prefix, Name);
120 
121   if (mode == k_ArcNameMode_Add)
122     return;
123 
124   if (mode != k_ArcNameMode_Exact)
125   {
126     int dotPos = Name.ReverseFind_Dot();
127     if (dotPos < 0)
128       return;
129     if ((unsigned)dotPos == Name.Len() - 1)
130       Name.DeleteBack();
131     else
132     {
133       const UString ext = Name.Ptr((unsigned)(dotPos + 1));
134       if (BaseExtension.IsEqualTo_NoCase(ext))
135       {
136         BaseExtension = ext;
137         Name.DeleteFrom((unsigned)dotPos);
138         return;
139       }
140     }
141   }
142 
143   BaseExtension.Empty();
144 }
145 
GetFinalPath() const146 UString CArchivePath::GetFinalPath() const
147 {
148   UString path = GetPathWithoutExt();
149   if (!BaseExtension.IsEmpty())
150   {
151     path.Add_Dot();
152     path += BaseExtension;
153   }
154   return path;
155 }
156 
GetFinalVolPath() const157 UString CArchivePath::GetFinalVolPath() const
158 {
159   UString path = GetPathWithoutExt();
160   // if BaseExtension is empty, we must ignore VolExtension also.
161   if (!BaseExtension.IsEmpty())
162   {
163     path.Add_Dot();
164     path += VolExtension;
165   }
166   return path;
167 }
168 
GetTempPath() const169 FString CArchivePath::GetTempPath() const
170 {
171   FString path = TempPrefix;
172   path += us2fs(Name);
173   if (!BaseExtension.IsEmpty())
174   {
175     path.Add_Dot();
176     path += us2fs(BaseExtension);
177   }
178   path += ".tmp";
179   path += TempPostfix;
180   return path;
181 }
182 
183 static const char * const kDefaultArcType = "7z";
184 static const char * const kDefaultArcExt = "7z";
185 static const char * const kSFXExtension =
186   #ifdef _WIN32
187     "exe";
188   #else
189     "";
190   #endif
191 
InitFormatIndex(const CCodecs * codecs,const CObjectVector<COpenType> & types,const UString & arcPath)192 bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs,
193     const CObjectVector<COpenType> &types, const UString &arcPath)
194 {
195   if (types.Size() > 1)
196     return false;
197   // int arcTypeIndex = -1;
198   if (types.Size() != 0)
199   {
200     MethodMode.Type = types[0];
201     MethodMode.Type_Defined = true;
202   }
203   if (MethodMode.Type.FormatIndex < 0)
204   {
205     // MethodMode.Type = -1;
206     MethodMode.Type = COpenType();
207     if (ArcNameMode != k_ArcNameMode_Add)
208     {
209       MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
210       if (MethodMode.Type.FormatIndex >= 0)
211         MethodMode.Type_Defined = true;
212     }
213   }
214   return true;
215 }
216 
SetArcPath(const CCodecs * codecs,const UString & arcPath)217 bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath)
218 {
219   UString typeExt;
220   int formatIndex = MethodMode.Type.FormatIndex;
221   if (formatIndex < 0)
222   {
223     typeExt = kDefaultArcExt;
224   }
225   else
226   {
227     const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex];
228     if (!arcInfo.UpdateEnabled)
229       return false;
230     typeExt = arcInfo.GetMainExt();
231   }
232   UString ext = typeExt;
233   if (SfxMode)
234     ext = kSFXExtension;
235   ArchivePath.BaseExtension = ext;
236   ArchivePath.VolExtension = typeExt;
237   ArchivePath.ParseFromPath(arcPath, ArcNameMode);
238   FOR_VECTOR (i, Commands)
239   {
240     CUpdateArchiveCommand &uc = Commands[i];
241     uc.ArchivePath.BaseExtension = ext;
242     uc.ArchivePath.VolExtension = typeExt;
243     uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode);
244   }
245   return true;
246 }
247 
248 
249 struct CUpdateProduceCallbackImp Z7_final: public IUpdateProduceCallback
250 {
251   const CObjectVector<CArcItem> *_arcItems;
252   CDirItemsStat *_stat;
253   IUpdateCallbackUI *_callback;
254 
CUpdateProduceCallbackImpZ7_final255   CUpdateProduceCallbackImp(
256       const CObjectVector<CArcItem> *a,
257       CDirItemsStat *stat,
258       IUpdateCallbackUI *callback):
259     _arcItems(a),
260     _stat(stat),
261     _callback(callback) {}
262 
263   virtual HRESULT ShowDeleteFile(unsigned arcIndex) Z7_override;
264 };
265 
266 
ShowDeleteFile(unsigned arcIndex)267 HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(unsigned arcIndex)
268 {
269   const CArcItem &ai = (*_arcItems)[arcIndex];
270   {
271     CDirItemsStat &stat = *_stat;
272     if (ai.IsDir)
273       stat.NumDirs++;
274     else if (ai.IsAltStream)
275     {
276       stat.NumAltStreams++;
277       stat.AltStreamsSize += ai.Size;
278     }
279     else
280     {
281       stat.NumFiles++;
282       stat.FilesSize += ai.Size;
283     }
284   }
285   return _callback->ShowDeleteFile(ai.Name, ai.IsDir);
286 }
287 
Prepare()288 bool CRenamePair::Prepare()
289 {
290   if (RecursedType != NRecursedType::kNonRecursed)
291     return false;
292   if (!WildcardParsing)
293     return true;
294   return !DoesNameContainWildcard(OldName);
295 }
296 
297 extern bool g_CaseSensitive;
298 
CompareTwoNames(const wchar_t * s1,const wchar_t * s2)299 static unsigned CompareTwoNames(const wchar_t *s1, const wchar_t *s2)
300 {
301   for (unsigned i = 0;; i++)
302   {
303     wchar_t c1 = s1[i];
304     wchar_t c2 = s2[i];
305     if (c1 == 0 || c2 == 0)
306       return i;
307     if (c1 == c2)
308       continue;
309     if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2)))
310       continue;
311     if (IsPathSepar(c1) && IsPathSepar(c2))
312       continue;
313     return i;
314   }
315 }
316 
GetNewPath(bool isFolder,const UString & src,UString & dest) const317 bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const
318 {
319   unsigned num = CompareTwoNames(OldName, src);
320   if (OldName[num] == 0)
321   {
322     if (src[num] != 0 && !IsPathSepar(src[num]) && num != 0 && !IsPathSepar(src[num - 1]))
323       return false;
324   }
325   else
326   {
327     // OldName[num] != 0
328     // OldName = "1\1a.txt"
329     // src = "1"
330 
331     if (!isFolder
332         || src[num] != 0
333         || !IsPathSepar(OldName[num])
334         || OldName[num + 1] != 0)
335       return false;
336   }
337   dest = NewName + src.Ptr(num);
338   return true;
339 }
340 
341 #ifdef SUPPORT_ALT_STREAMS
342 int FindAltStreamColon_in_Path(const wchar_t *path);
343 #endif
344 
345 
346 
Compress(const CUpdateOptions & options,bool isUpdatingItself,CCodecs * codecs,const CActionSet & actionSet,const CArc * arc,CArchivePath & archivePath,const CObjectVector<CArcItem> & arcItems,Byte * processedItemsStatuses,const CDirItems & dirItems,const CDirItem * parentDirItem,CTempFiles & tempFiles,CMultiOutStream_Bunch & multiStreams,CUpdateErrorInfo & errorInfo,IUpdateCallbackUI * callback,CFinishArchiveStat & st)347 static HRESULT Compress(
348     const CUpdateOptions &options,
349     bool isUpdatingItself,
350     CCodecs *codecs,
351     const CActionSet &actionSet,
352     const CArc *arc,
353     CArchivePath &archivePath,
354     const CObjectVector<CArcItem> &arcItems,
355     Byte *processedItemsStatuses,
356     const CDirItems &dirItems,
357     const CDirItem *parentDirItem,
358     CTempFiles &tempFiles,
359     CMultiOutStream_Bunch &multiStreams,
360     CUpdateErrorInfo &errorInfo,
361     IUpdateCallbackUI *callback,
362     CFinishArchiveStat &st)
363 {
364   CMyComPtr<IOutArchive> outArchive;
365   int formatIndex = options.MethodMode.Type.FormatIndex;
366 
367   if (arc)
368   {
369     formatIndex = arc->FormatIndex;
370     if (formatIndex < 0)
371       return E_NOTIMPL;
372     CMyComPtr<IInArchive> archive2 = arc->Archive;
373     HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
374     if (result != S_OK)
375       throw kUpdateIsNotSupoorted;
376   }
377   else
378   {
379     RINOK(codecs->CreateOutArchive((unsigned)formatIndex, outArchive))
380 
381     #ifdef Z7_EXTERNAL_CODECS
382     {
383       CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
384       outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
385       if (setCompressCodecsInfo)
386       {
387         RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs))
388       }
389     }
390     #endif
391   }
392 
393   if (!outArchive)
394     throw kUpdateIsNotSupoorted;
395 
396   // we need to set properties to get fileTimeType.
397   RINOK(SetProperties(outArchive, options.MethodMode.Properties))
398 
399   NFileTimeType::EEnum fileTimeType;
400   {
401     /*
402     how we compare file_in_archive::MTime with dirItem.MTime
403     for GetUpdatePairInfoList():
404 
405     if (kpidMTime is not defined), external MTime of archive is used.
406 
407     before 22.00:
408       if (kpidTimeType is defined)
409       {
410         kpidTimeType is used as precision.
411         (kpidTimeType > kDOS) is not allowed.
412       }
413       else GetFileTimeType() value is used as precision.
414 
415     22.00:
416       if (kpidMTime is defined)
417       {
418         if (kpidMTime::precision != 0), then kpidMTime::precision is used as precision.
419         else
420         {
421           if (kpidTimeType is defined), kpidTimeType is used as precision.
422           else GetFileTimeType() value is used as precision.
423         }
424       }
425       else external MTime of archive is used as precision.
426     */
427 
428     UInt32 value;
429     RINOK(outArchive->GetFileTimeType(&value))
430 
431     // we support any future fileType here.
432     fileTimeType = (NFileTimeType::EEnum)value;
433 
434     /*
435     old 21.07 code:
436     switch (value)
437     {
438       case NFileTimeType::kWindows:
439       case NFileTimeType::kUnix:
440       case NFileTimeType::kDOS:
441         fileTimeType = (NFileTimeType::EEnum)value;
442         break;
443       default:
444         return E_FAIL;
445     }
446     */
447   }
448 
449   // bool noTimestampExpected = false;
450   {
451     const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex];
452 
453     // if (arcInfo.Flags_KeepName()) noTimestampExpected = true;
454     if (arcInfo.Is_Xz() ||
455         arcInfo.Is_BZip2())
456     {
457       /* 7-zip before 22.00 returns NFileTimeType::kUnix for xz and bzip2,
458          but we want to set timestamp without reduction to unix. */
459       // noTimestampExpected = true;
460       fileTimeType = NFileTimeType::kNotDefined; // it means not defined
461     }
462 
463     if (options.AltStreams.Val && !arcInfo.Flags_AltStreams())
464       return E_NOTIMPL;
465     if (options.NtSecurity.Val && !arcInfo.Flags_NtSecurity())
466       return E_NOTIMPL;
467     if (options.DeleteAfterCompressing && arcInfo.Flags_HashHandler())
468       return E_NOTIMPL;
469   }
470 
471   CRecordVector<CUpdatePair2> updatePairs2;
472 
473   UStringVector newNames;
474 
475   CArcToDoStat stat2;
476 
477   if (options.RenamePairs.Size() != 0)
478   {
479     FOR_VECTOR (i, arcItems)
480     {
481       const CArcItem &ai = arcItems[i];
482       bool needRename = false;
483       UString dest;
484 
485       if (ai.Censored)
486       {
487         FOR_VECTOR (j, options.RenamePairs)
488         {
489           const CRenamePair &rp = options.RenamePairs[j];
490           if (rp.GetNewPath(ai.IsDir, ai.Name, dest))
491           {
492             needRename = true;
493             break;
494           }
495 
496           #ifdef SUPPORT_ALT_STREAMS
497           if (ai.IsAltStream)
498           {
499             int colonPos = FindAltStreamColon_in_Path(ai.Name);
500             if (colonPos >= 0)
501             {
502               UString mainName = ai.Name.Left((unsigned)colonPos);
503               /*
504               actually we must improve that code to support cases
505               with folder renaming like: rn arc dir1\ dir2\
506               */
507               if (rp.GetNewPath(false, mainName, dest))
508               {
509                 needRename = true;
510                 dest.Add_Colon();
511                 dest += ai.Name.Ptr((unsigned)(colonPos + 1));
512                 break;
513               }
514             }
515           }
516           #endif
517         }
518       }
519 
520       CUpdatePair2 up2;
521       up2.SetAs_NoChangeArcItem(ai.IndexInServer);
522       if (needRename)
523       {
524         up2.NewProps = true;
525         RINOK(arc->IsItem_Anti(i, up2.IsAnti))
526         up2.NewNameIndex = (int)newNames.Add(dest);
527       }
528       updatePairs2.Add(up2);
529     }
530   }
531   else
532   {
533     CRecordVector<CUpdatePair> updatePairs;
534     GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!!
535     CUpdateProduceCallbackImp upCallback(&arcItems, &stat2.DeleteData, callback);
536 
537     UpdateProduce(updatePairs, actionSet, updatePairs2, isUpdatingItself ? &upCallback : NULL);
538   }
539 
540   {
541     FOR_VECTOR (i, updatePairs2)
542     {
543       const CUpdatePair2 &up = updatePairs2[i];
544 
545       // 17.01: anti-item is (up.NewData && (p.UseArcProps in most cases))
546 
547       if (up.NewData && !up.UseArcProps)
548       {
549         if (up.ExistOnDisk())
550         {
551           CDirItemsStat2 &stat = stat2.NewData;
552           const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex];
553           if (di.IsDir())
554           {
555             if (up.IsAnti)
556               stat.Anti_NumDirs++;
557             else
558               stat.NumDirs++;
559           }
560          #ifdef _WIN32
561           else if (di.IsAltStream)
562           {
563             if (up.IsAnti)
564               stat.Anti_NumAltStreams++;
565             else
566             {
567               stat.NumAltStreams++;
568               stat.AltStreamsSize += di.Size;
569             }
570           }
571          #endif
572           else
573           {
574             if (up.IsAnti)
575               stat.Anti_NumFiles++;
576             else
577             {
578               stat.NumFiles++;
579               stat.FilesSize += di.Size;
580             }
581           }
582         }
583       }
584       else if (up.ArcIndex >= 0)
585       {
586         CDirItemsStat2 &stat = *(up.NewData ? &stat2.NewData : &stat2.OldData);
587         const CArcItem &ai = arcItems[(unsigned)up.ArcIndex];
588         if (ai.IsDir)
589         {
590           if (up.IsAnti)
591             stat.Anti_NumDirs++;
592           else
593             stat.NumDirs++;
594         }
595         else if (ai.IsAltStream)
596         {
597           if (up.IsAnti)
598             stat.Anti_NumAltStreams++;
599           else
600           {
601             stat.NumAltStreams++;
602             stat.AltStreamsSize += ai.Size;
603           }
604         }
605         else
606         {
607           if (up.IsAnti)
608             stat.Anti_NumFiles++;
609           else
610           {
611             stat.NumFiles++;
612             stat.FilesSize += ai.Size;
613           }
614         }
615       }
616     }
617     RINOK(callback->SetNumItems(stat2))
618   }
619 
620   CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
621   CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
622 
623   updateCallbackSpec->PreserveATime = options.PreserveATime;
624   updateCallbackSpec->ShareForWrite = options.OpenShareForWrite;
625   updateCallbackSpec->StopAfterOpenError = options.StopAfterOpenError;
626   updateCallbackSpec->StdInMode = options.StdInMode;
627   updateCallbackSpec->Callback = callback;
628 
629   if (arc)
630   {
631     // we set Archive to allow to transfer GetProperty requests back to DLL.
632     updateCallbackSpec->Archive = arc->Archive;
633   }
634 
635   updateCallbackSpec->DirItems = &dirItems;
636   updateCallbackSpec->ParentDirItem = parentDirItem;
637 
638   updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val;
639   updateCallbackSpec->StoreHardLinks = options.HardLinks.Val;
640   updateCallbackSpec->StoreSymLinks = options.SymLinks.Val;
641   updateCallbackSpec->StoreOwnerName = options.StoreOwnerName.Val;
642   updateCallbackSpec->StoreOwnerId = options.StoreOwnerId.Val;
643 
644   updateCallbackSpec->Arc = arc;
645   updateCallbackSpec->ArcItems = &arcItems;
646   updateCallbackSpec->UpdatePairs = &updatePairs2;
647 
648   updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses;
649 
650   {
651     const UString arcPath = archivePath.GetFinalPath();
652     updateCallbackSpec->ArcFileName = ExtractFileNameFromPath(arcPath);
653   }
654 
655   if (options.RenamePairs.Size() != 0)
656     updateCallbackSpec->NewNames = &newNames;
657 
658   if (options.SetArcMTime)
659   {
660     // updateCallbackSpec->Need_ArcMTime_Report = true;
661     updateCallbackSpec->Need_LatestMTime = true;
662   }
663 
664   CMyComPtr<IOutStream> outSeekStream;
665   CMyComPtr<ISequentialOutStream> outStream;
666 
667   if (!options.StdOutMode)
668   {
669     FString dirPrefix;
670     if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix))
671       throw 1417161;
672     CreateComplexDir(dirPrefix);
673   }
674 
675   COutFileStream *outStreamSpec = NULL;
676   CStdOutFileStream *stdOutFileStreamSpec = NULL;
677   CMultiOutStream *volStreamSpec = NULL;
678 
679   if (options.VolumesSizes.Size() == 0)
680   {
681     if (options.StdOutMode)
682     {
683       stdOutFileStreamSpec = new CStdOutFileStream;
684       outStream = stdOutFileStreamSpec;
685     }
686     else
687     {
688       outStreamSpec = new COutFileStream;
689       outSeekStream = outStreamSpec;
690       outStream = outSeekStream;
691       bool isOK = false;
692       FString realPath;
693 
694       for (unsigned i = 0; i < (1 << 16); i++)
695       {
696         if (archivePath.Temp)
697         {
698           if (i > 0)
699           {
700             archivePath.TempPostfix.Empty();
701             archivePath.TempPostfix.Add_UInt32(i);
702           }
703           realPath = archivePath.GetTempPath();
704         }
705         else
706           realPath = us2fs(archivePath.GetFinalPath());
707         if (outStreamSpec->Create_NEW(realPath))
708         {
709           tempFiles.Paths.Add(realPath);
710           isOK = true;
711           break;
712         }
713         if (::GetLastError() != ERROR_FILE_EXISTS)
714           break;
715         if (!archivePath.Temp)
716           break;
717       }
718 
719       if (!isOK)
720         return errorInfo.SetFromLastError("cannot open file", realPath);
721     }
722   }
723   else
724   {
725     if (options.StdOutMode)
726       return E_FAIL;
727     if (arc && arc->GetGlobalOffset() > 0)
728       return E_NOTIMPL;
729 
730     volStreamSpec = new CMultiOutStream();
731     outSeekStream = volStreamSpec;
732     outStream = outSeekStream;
733     volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath());
734     volStreamSpec->Prefix.Add_Dot();
735     volStreamSpec->Init(options.VolumesSizes);
736     {
737       CMultiOutStream_Rec &rec = multiStreams.Items.AddNew();
738       rec.Spec = volStreamSpec;
739       rec.Ref = rec.Spec;
740     }
741 
742     /*
743     updateCallbackSpec->VolumesSizes = volumesSizes;
744     updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
745     if (!archivePath.VolExtension.IsEmpty())
746       updateCallbackSpec->VolExt = UString('.') + archivePath.VolExtension;
747     */
748   }
749 
750   if (options.SfxMode)
751   {
752     CInFileStream *sfxStreamSpec = new CInFileStream;
753     CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
754     if (!sfxStreamSpec->Open(options.SfxModule))
755       return errorInfo.SetFromLastError("cannot open SFX module", options.SfxModule);
756 
757     CMyComPtr<ISequentialOutStream> sfxOutStream;
758     COutFileStream *outStreamSpec2 = NULL;
759     if (options.VolumesSizes.Size() == 0)
760       sfxOutStream = outStream;
761     else
762     {
763       outStreamSpec2 = new COutFileStream;
764       sfxOutStream = outStreamSpec2;
765       const FString realPath = us2fs(archivePath.GetFinalPath());
766       if (!outStreamSpec2->Create_NEW(realPath))
767         return errorInfo.SetFromLastError("cannot open file", realPath);
768     }
769 
770     {
771       UInt64 sfxSize;
772       RINOK(sfxStreamSpec->GetSize(&sfxSize))
773       RINOK(callback->WriteSfx(fs2us(options.SfxModule), sfxSize))
774     }
775 
776     RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL))
777 
778     if (outStreamSpec2)
779     {
780       RINOK(outStreamSpec2->Close())
781     }
782   }
783 
784   CMyComPtr<ISequentialOutStream> tailStream;
785 
786   if (options.SfxMode || !arc || arc->ArcStreamOffset == 0)
787     tailStream = outStream;
788   else
789   {
790     // Int64 globalOffset = arc->GetGlobalOffset();
791     RINOK(InStream_SeekToBegin(arc->InStream))
792     RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL))
793     if (options.StdOutMode)
794       tailStream = outStream;
795     else
796     {
797       CTailOutStream *tailStreamSpec = new CTailOutStream;
798       tailStream = tailStreamSpec;
799       tailStreamSpec->Stream = outSeekStream;
800       tailStreamSpec->Offset = arc->ArcStreamOffset;
801       tailStreamSpec->Init();
802     }
803   }
804 
805   CFiTime ft;
806   FiTime_Clear(ft);
807   bool ft_Defined = false;
808   {
809     FOR_VECTOR (i, updatePairs2)
810     {
811       const CUpdatePair2 &pair2 = updatePairs2[i];
812       CFiTime ft2;
813       FiTime_Clear(ft2);
814       bool ft2_Defined = false;
815       /* we use full precision of dirItem, if dirItem is defined
816          and (dirItem will be used or dirItem is sameTime in dir and arc */
817       if (pair2.DirIndex >= 0 &&
818           (pair2.NewProps || pair2.IsSameTime))
819       {
820         ft2 = dirItems.Items[(unsigned)pair2.DirIndex].MTime;
821         ft2_Defined = true;
822       }
823       else if (pair2.UseArcProps && pair2.ArcIndex >= 0)
824       {
825         const CArcItem &arcItem = arcItems[(unsigned)pair2.ArcIndex];
826         if (arcItem.MTime.Def)
827         {
828           arcItem.MTime.Write_To_FiTime(ft2);
829           ft2_Defined = true;
830         }
831       }
832       if (ft2_Defined)
833       {
834         if (!ft_Defined || Compare_FiTime(&ft, &ft2) < 0)
835         {
836           ft = ft2;
837           ft_Defined = true;
838         }
839       }
840     }
841     /*
842     if (fileTimeType != NFileTimeType::kNotDefined)
843     FiTime_Normalize_With_Prec(ft, fileTimeType);
844     */
845   }
846 
847   if (volStreamSpec && options.SetArcMTime && ft_Defined)
848   {
849     volStreamSpec->MTime = ft;
850     volStreamSpec->MTime_Defined = true;
851   }
852 
853   HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback);
854   // callback->Finalize();
855   RINOK(result)
856 
857   if (!updateCallbackSpec->AreAllFilesClosed())
858   {
859     errorInfo.Message = "There are unclosed input files:";
860     errorInfo.FileNames = updateCallbackSpec->_openFiles_Paths;
861     return E_FAIL;
862   }
863 
864   if (options.SetArcMTime)
865   {
866     // bool needNormalizeAfterStream;
867     // needParse;
868     /*
869     if (updateCallbackSpec->ArcMTime_WasReported)
870     {
871       isDefined = updateCallbackSpec->Reported_ArcMTime.Def;
872       if (isDefined)
873         updateCallbackSpec->Reported_ArcMTime.Write_To_FiTime(ft);
874       else
875         fileTimeType = NFileTimeType::kNotDefined;
876     }
877     if (!isDefined)
878     */
879     {
880       if (updateCallbackSpec->LatestMTime_Defined)
881       {
882         // CArcTime at = StreamCallback_ArcMTime;
883         // updateCallbackSpec->StreamCallback_ArcMTime.Write_To_FiTime(ft);
884         // we must normalize with precision from archive;
885         if (!ft_Defined || Compare_FiTime(&ft, &updateCallbackSpec->LatestMTime) < 0)
886           ft = updateCallbackSpec->LatestMTime;
887         ft_Defined = true;
888       }
889       /*
890       if (fileTimeType != NFileTimeType::kNotDefined)
891         FiTime_Normalize_With_Prec(ft, fileTimeType);
892       */
893     }
894     // if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0)
895     if (ft_Defined)
896     {
897       // we ignore set time errors here.
898       // note that user could move some finished volumes to another folder.
899       if (outStreamSpec)
900         outStreamSpec->SetMTime(&ft);
901       else if (volStreamSpec)
902         volStreamSpec->SetMTime_Final(ft);
903     }
904   }
905 
906   if (callback)
907   {
908     UInt64 size = 0;
909     if (outStreamSpec)
910       outStreamSpec->GetSize(&size);
911     else if (stdOutFileStreamSpec)
912       size = stdOutFileStreamSpec->GetSize();
913     else
914       size = volStreamSpec->GetSize();
915 
916     st.OutArcFileSize = size;
917   }
918 
919   if (outStreamSpec)
920     result = outStreamSpec->Close();
921   else if (volStreamSpec)
922   {
923     result = volStreamSpec->FinalFlush_and_CloseFiles(st.NumVolumes);
924     st.IsMultiVolMode = true;
925   }
926 
927   RINOK(result)
928 
929   if (processedItemsStatuses)
930   {
931     FOR_VECTOR (i, updatePairs2)
932     {
933       const CUpdatePair2 &up = updatePairs2[i];
934       if (up.NewData && up.DirIndex >= 0)
935       {
936         const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex];
937         if (di.AreReparseData() || (!di.IsDir() && di.Size == 0))
938           processedItemsStatuses[(unsigned)up.DirIndex] = 1;
939       }
940     }
941   }
942 
943   return result;
944 }
945 
946 
947 
Censor_AreAllAllowed(const NWildcard::CCensor & censor)948 static bool Censor_AreAllAllowed(const NWildcard::CCensor &censor)
949 {
950   if (censor.Pairs.Size() != 1)
951     return false;
952   const NWildcard::CPair &pair = censor.Pairs[0];
953   /* Censor_CheckPath() ignores (CPair::Prefix).
954      So we also ignore (CPair::Prefix) here */
955   // if (!pair.Prefix.IsEmpty()) return false;
956   return pair.Head.AreAllAllowed();
957 }
958 
959 bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include);
960 
Censor_CheckPath(const NWildcard::CCensor & censor,const CReadArcItem & item)961 static bool Censor_CheckPath(const NWildcard::CCensor &censor, const CReadArcItem &item)
962 {
963   bool finded = false;
964   FOR_VECTOR (i, censor.Pairs)
965   {
966     /* (CPair::Prefix) in not used for matching items in archive.
967        So we ignore (CPair::Prefix) here */
968     bool include;
969     if (CensorNode_CheckPath2(censor.Pairs[i].Head, item, include))
970     {
971       // Check it and FIXME !!!!
972       // here we can exclude item via some Pair, that is still allowed by another Pair
973       if (!include)
974         return false;
975       finded = true;
976     }
977   }
978   return finded;
979 }
980 
EnumerateInArchiveItems(const NWildcard::CCensor & censor,const CArc & arc,CObjectVector<CArcItem> & arcItems)981 static HRESULT EnumerateInArchiveItems(
982     // bool storeStreamsMode,
983     const NWildcard::CCensor &censor,
984     const CArc &arc,
985     CObjectVector<CArcItem> &arcItems)
986 {
987   arcItems.Clear();
988   UInt32 numItems;
989   IInArchive *archive = arc.Archive;
990   RINOK(archive->GetNumberOfItems(&numItems))
991   arcItems.ClearAndReserve(numItems);
992 
993   CReadArcItem item;
994 
995   const bool allFilesAreAllowed = Censor_AreAllAllowed(censor);
996 
997   for (UInt32 i = 0; i < numItems; i++)
998   {
999     CArcItem ai;
1000 
1001     RINOK(arc.GetItem(i, item))
1002     ai.Name = item.Path;
1003     ai.IsDir = item.IsDir;
1004     ai.IsAltStream =
1005         #ifdef SUPPORT_ALT_STREAMS
1006           item.IsAltStream;
1007         #else
1008           false;
1009         #endif
1010 
1011     /*
1012     if (!storeStreamsMode && ai.IsAltStream)
1013       continue;
1014     */
1015     if (allFilesAreAllowed)
1016       ai.Censored = true;
1017     else
1018       ai.Censored = Censor_CheckPath(censor, item);
1019 
1020     // ai.MTime will be set to archive MTime, if not present in archive item
1021     RINOK(arc.GetItem_MTime(i, ai.MTime))
1022     RINOK(arc.GetItem_Size(i, ai.Size, ai.Size_Defined))
1023 
1024     ai.IndexInServer = i;
1025     arcItems.AddInReserved(ai);
1026   }
1027   return S_OK;
1028 }
1029 
1030 #if defined(_WIN32) && !defined(UNDER_CE)
1031 
1032 #if defined(__MINGW32__) || defined(__MINGW64__)
1033 #include <mapi.h>
1034 #else
1035 #include <MAPI.h>
1036 #endif
1037 
1038 extern "C" {
1039 
1040 #ifdef MAPI_FORCE_UNICODE
1041 
1042 #define Z7_WIN_LPMAPISENDMAILW  LPMAPISENDMAILW
1043 #define Z7_WIN_MapiFileDescW    MapiFileDescW
1044 #define Z7_WIN_MapiMessageW     MapiMessageW
1045 #define Z7_WIN_MapiRecipDescW   MapiRecipDescW
1046 
1047 #else
1048 
1049 typedef struct
1050 {
1051     ULONG ulReserved;
1052     ULONG ulRecipClass;
1053     PWSTR lpszName;
1054     PWSTR lpszAddress;
1055     ULONG ulEIDSize;
1056     PVOID lpEntryID;
1057 } Z7_WIN_MapiRecipDescW, *Z7_WIN_lpMapiRecipDescW;
1058 
1059 typedef struct
1060 {
1061     ULONG ulReserved;
1062     ULONG flFlags;
1063     ULONG nPosition;
1064     PWSTR lpszPathName;
1065     PWSTR lpszFileName;
1066     PVOID lpFileType;
1067 } Z7_WIN_MapiFileDescW, *Z7_WIN_lpMapiFileDescW;
1068 
1069 typedef struct
1070 {
1071   ULONG ulReserved;
1072   PWSTR lpszSubject;
1073   PWSTR lpszNoteText;
1074   PWSTR lpszMessageType;
1075   PWSTR lpszDateReceived;
1076   PWSTR lpszConversationID;
1077   FLAGS flFlags;
1078   Z7_WIN_lpMapiRecipDescW lpOriginator;
1079   ULONG nRecipCount;
1080   Z7_WIN_lpMapiRecipDescW lpRecips;
1081   ULONG nFileCount;
1082   Z7_WIN_lpMapiFileDescW lpFiles;
1083 } Z7_WIN_MapiMessageW, *Z7_WIN_lpMapiMessageW;
1084 
1085 typedef ULONG (FAR PASCAL Z7_WIN_MAPISENDMAILW)(
1086   LHANDLE lhSession,
1087   ULONG_PTR ulUIParam,
1088   Z7_WIN_lpMapiMessageW lpMessage,
1089   FLAGS flFlags,
1090   ULONG ulReserved
1091 );
1092 typedef Z7_WIN_MAPISENDMAILW FAR *Z7_WIN_LPMAPISENDMAILW;
1093 
1094 #endif // MAPI_FORCE_UNICODE
1095 }
1096 #endif // _WIN32
1097 
1098 
1099 struct C_CopyFileProgress_to_IUpdateCallbackUI2 Z7_final:
1100   public ICopyFileProgress
1101 {
1102   IUpdateCallbackUI2 *Callback;
1103   HRESULT CallbackResult;
1104   // bool Disable_Break;
1105 
CopyFileProgressZ7_final1106   virtual DWORD CopyFileProgress(UInt64 total, UInt64 current) Z7_override
1107   {
1108     const HRESULT res = Callback->MoveArc_Progress(total, current);
1109     CallbackResult = res;
1110     // if (Disable_Break && res == E_ABORT) res = S_OK;
1111     return res == S_OK ? PROGRESS_CONTINUE : PROGRESS_CANCEL;
1112   }
1113 
C_CopyFileProgress_to_IUpdateCallbackUI2Z7_final1114   C_CopyFileProgress_to_IUpdateCallbackUI2(
1115       IUpdateCallbackUI2 *callback) :
1116     Callback(callback),
1117     CallbackResult(S_OK)
1118     // , Disable_Break(false)
1119     {}
1120 };
1121 
1122 
UpdateArchive(CCodecs * codecs,const CObjectVector<COpenType> & types,const UString & cmdArcPath2,NWildcard::CCensor & censor,CUpdateOptions & options,CUpdateErrorInfo & errorInfo,IOpenCallbackUI * openCallback,IUpdateCallbackUI2 * callback,bool needSetPath)1123 HRESULT UpdateArchive(
1124     CCodecs *codecs,
1125     const CObjectVector<COpenType> &types,
1126     const UString &cmdArcPath2,
1127     NWildcard::CCensor &censor,
1128     CUpdateOptions &options,
1129     CUpdateErrorInfo &errorInfo,
1130     IOpenCallbackUI *openCallback,
1131     IUpdateCallbackUI2 *callback,
1132     bool needSetPath)
1133 {
1134   if (options.StdOutMode && options.EMailMode)
1135     return E_FAIL;
1136 
1137   if (types.Size() > 1)
1138     return E_NOTIMPL;
1139 
1140   bool renameMode = !options.RenamePairs.IsEmpty();
1141   if (renameMode)
1142   {
1143     if (options.Commands.Size() != 1)
1144       return E_FAIL;
1145   }
1146 
1147   if (options.DeleteAfterCompressing)
1148   {
1149     if (options.Commands.Size() != 1)
1150       return E_NOTIMPL;
1151     const CActionSet &as = options.Commands[0].ActionSet;
1152     for (unsigned i = 2; i < NPairState::kNumValues; i++)
1153       if (as.StateActions[i] != NPairAction::kCompress)
1154         return E_NOTIMPL;
1155   }
1156 
1157   censor.AddPathsToCensor(options.PathMode);
1158   #ifdef _WIN32
1159   ConvertToLongNames(censor);
1160   #endif
1161   censor.ExtendExclude();
1162 
1163 
1164   if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */))
1165     return E_NOTIMPL;
1166 
1167   if (options.SfxMode)
1168   {
1169     CProperty property;
1170     property.Name = "rsfx";
1171     options.MethodMode.Properties.Add(property);
1172     if (options.SfxModule.IsEmpty())
1173     {
1174       errorInfo.Message = "SFX file is not specified";
1175       return E_FAIL;
1176     }
1177     bool found = false;
1178     if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0)
1179     {
1180       const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule;
1181       if (NFind::DoesFileExist_FollowLink(fullName))
1182       {
1183         options.SfxModule = fullName;
1184         found = true;
1185       }
1186     }
1187     if (!found)
1188     {
1189       if (!NFind::DoesFileExist_FollowLink(options.SfxModule))
1190         return errorInfo.SetFromLastError("cannot find specified SFX module", options.SfxModule);
1191     }
1192   }
1193 
1194   CArchiveLink arcLink;
1195 
1196 
1197   if (needSetPath)
1198   {
1199     if (!options.InitFormatIndex(codecs, types, cmdArcPath2) ||
1200         !options.SetArcPath(codecs, cmdArcPath2))
1201       return E_NOTIMPL;
1202   }
1203 
1204   UString arcPath = options.ArchivePath.GetFinalPath();
1205 
1206   if (!options.VolumesSizes.IsEmpty())
1207   {
1208     arcPath = options.ArchivePath.GetFinalVolPath();
1209     arcPath += ".001";
1210   }
1211 
1212   if (cmdArcPath2.IsEmpty())
1213   {
1214     if (options.MethodMode.Type.FormatIndex < 0)
1215       throw "type of archive is not specified";
1216   }
1217   else
1218   {
1219     NFind::CFileInfo fi;
1220     if (!fi.Find_FollowLink(us2fs(arcPath)))
1221     {
1222       if (renameMode)
1223         throw "can't find archive";
1224       if (options.MethodMode.Type.FormatIndex < 0)
1225       {
1226         if (!options.SetArcPath(codecs, cmdArcPath2))
1227           return E_NOTIMPL;
1228       }
1229     }
1230     else
1231     {
1232       if (fi.IsDir())
1233         return errorInfo.SetFromError_DWORD("There is a folder with the name of archive",
1234             us2fs(arcPath),
1235             #ifdef _WIN32
1236               ERROR_ACCESS_DENIED
1237             #else
1238               EISDIR
1239             #endif
1240             );
1241      #ifdef _WIN32
1242       if (fi.IsDevice)
1243         return E_NOTIMPL;
1244      #endif
1245 
1246       if (!options.StdOutMode && options.UpdateArchiveItself)
1247         if (fi.IsReadOnly())
1248         {
1249           return errorInfo.SetFromError_DWORD("The file is read-only",
1250               us2fs(arcPath),
1251               #ifdef _WIN32
1252                 ERROR_ACCESS_DENIED
1253               #else
1254                 EACCES
1255               #endif
1256               );
1257         }
1258 
1259       if (options.VolumesSizes.Size() > 0)
1260       {
1261         errorInfo.FileNames.Add(us2fs(arcPath));
1262         // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1263         errorInfo.Message = kUpdateIsNotSupported_MultiVol;
1264         return E_NOTIMPL;
1265       }
1266       CObjectVector<COpenType> types2;
1267       // change it.
1268       if (options.MethodMode.Type_Defined)
1269         types2.Add(options.MethodMode.Type);
1270       // We need to set Properties to open archive only in some cases (WIM archives).
1271 
1272       CIntVector excl;
1273       COpenOptions op;
1274       #ifndef Z7_SFX
1275       op.props = &options.MethodMode.Properties;
1276       #endif
1277       op.codecs = codecs;
1278       op.types = &types2;
1279       op.excludedFormats = &excl;
1280       op.stdInMode = false;
1281       op.stream = NULL;
1282       op.filePath = arcPath;
1283 
1284       RINOK(callback->StartOpenArchive(arcPath))
1285 
1286       HRESULT result = arcLink.Open_Strict(op, openCallback);
1287 
1288       if (result == E_ABORT)
1289         return result;
1290 
1291       HRESULT res2 = callback->OpenResult(codecs, arcLink, arcPath, result);
1292       /*
1293       if (result == S_FALSE)
1294         return E_FAIL;
1295       */
1296       RINOK(res2)
1297       RINOK(result)
1298 
1299       if (arcLink.VolumePaths.Size() > 1)
1300       {
1301         // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1302         errorInfo.Message = kUpdateIsNotSupported_MultiVol;
1303         return E_NOTIMPL;
1304       }
1305 
1306       CArc &arc = arcLink.Arcs.Back();
1307       arc.MTime.Def =
1308         #ifdef _WIN32
1309           !fi.IsDevice;
1310         #else
1311           true;
1312         #endif
1313       if (arc.MTime.Def)
1314         arc.MTime.Set_From_FiTime(fi.MTime);
1315 
1316       if (arc.ErrorInfo.ThereIsTail)
1317       {
1318         // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1319         errorInfo.Message = "There is some data block after the end of the archive";
1320         return E_NOTIMPL;
1321       }
1322       if (options.MethodMode.Type.FormatIndex < 0)
1323       {
1324         options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex;
1325         if (!options.SetArcPath(codecs, cmdArcPath2))
1326           return E_NOTIMPL;
1327       }
1328     }
1329   }
1330 
1331   if (options.MethodMode.Type.FormatIndex < 0)
1332   {
1333     options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType((UString)kDefaultArcType);
1334     if (options.MethodMode.Type.FormatIndex < 0)
1335       return E_NOTIMPL;
1336   }
1337 
1338   const bool thereIsInArchive = arcLink.IsOpen;
1339   if (!thereIsInArchive && renameMode)
1340     return E_FAIL;
1341 
1342   CDirItems dirItems;
1343   dirItems.Callback = callback;
1344 
1345   CDirItem parentDirItem;
1346   CDirItem *parentDirItem_Ptr = NULL;
1347 
1348   /*
1349   FStringVector requestedPaths;
1350   FStringVector *requestedPaths_Ptr = NULL;
1351   if (options.DeleteAfterCompressing)
1352     requestedPaths_Ptr = &requestedPaths;
1353   */
1354 
1355   if (options.StdInMode)
1356   {
1357     CDirItem di;
1358     // di.ClearBase();
1359     // di.Size = (UInt64)(Int64)-1;
1360     if (!di.SetAs_StdInFile())
1361       return GetLastError_noZero_HRESULT();
1362     di.Name = options.StdInFileName;
1363     // di.Attrib_IsDefined = false;
1364     // NTime::GetCurUtc_FiTime(di.MTime);
1365     // di.CTime = di.ATime = di.MTime;
1366     dirItems.Items.Add(di);
1367   }
1368   else
1369   {
1370     bool needScanning = false;
1371 
1372     if (!renameMode)
1373     FOR_VECTOR (i, options.Commands)
1374       if (options.Commands[i].ActionSet.NeedScanning())
1375         needScanning = true;
1376 
1377     if (needScanning)
1378     {
1379       RINOK(callback->StartScanning())
1380 
1381       dirItems.SymLinks = options.SymLinks.Val;
1382 
1383       #if defined(_WIN32) && !defined(UNDER_CE)
1384       dirItems.ReadSecure = options.NtSecurity.Val;
1385       #endif
1386 
1387       dirItems.ScanAltStreams = options.AltStreams.Val;
1388       dirItems.ExcludeDirItems = censor.ExcludeDirItems;
1389       dirItems.ExcludeFileItems = censor.ExcludeFileItems;
1390 
1391       dirItems.ShareForWrite = options.OpenShareForWrite;
1392 
1393      #ifndef _WIN32
1394       dirItems.StoreOwnerName = options.StoreOwnerName.Val;
1395      #endif
1396 
1397       const HRESULT res = EnumerateItems(censor,
1398           options.PathMode,
1399           UString(), // options.AddPathPrefix,
1400           dirItems);
1401 
1402       if (res != S_OK)
1403       {
1404         if (res != E_ABORT)
1405           errorInfo.Message = "Scanning error";
1406         return res;
1407       }
1408 
1409       RINOK(callback->FinishScanning(dirItems.Stat))
1410 
1411       // 22.00: we don't need parent folder, if absolute path mode
1412       if (options.PathMode != NWildcard::k_AbsPath)
1413       if (censor.Pairs.Size() == 1)
1414       {
1415         NFind::CFileInfo fi;
1416         FString prefix = us2fs(censor.Pairs[0].Prefix);
1417         prefix.Add_Dot();
1418         // UString prefix = censor.Pairs[0].Prefix;
1419         /*
1420         if (prefix.Back() == WCHAR_PATH_SEPARATOR)
1421         {
1422           prefix.DeleteBack();
1423         }
1424         */
1425         if (fi.Find(prefix))
1426           if (fi.IsDir())
1427           {
1428             parentDirItem.Copy_From_FileInfoBase(fi);
1429             parentDirItem_Ptr = &parentDirItem;
1430 
1431             int secureIndex = -1;
1432             #if defined(_WIN32) && !defined(UNDER_CE)
1433             if (options.NtSecurity.Val)
1434               dirItems.AddSecurityItem(prefix, secureIndex);
1435             #endif
1436             parentDirItem.SecureIndex = secureIndex;
1437           }
1438       }
1439     }
1440   }
1441 
1442   FString tempDirPrefix;
1443   bool usesTempDir = false;
1444 
1445   #ifdef _WIN32
1446   CTempDir tempDirectory;
1447   if (options.EMailMode && options.EMailRemoveAfter)
1448   {
1449     tempDirectory.Create(kTempFolderPrefix);
1450     tempDirPrefix = tempDirectory.GetPath();
1451     NormalizeDirPathPrefix(tempDirPrefix);
1452     usesTempDir = true;
1453   }
1454   #endif
1455 
1456   CTempFiles tempFiles;
1457 
1458   bool createTempFile = false;
1459 
1460   if (!options.StdOutMode && options.UpdateArchiveItself)
1461   {
1462     CArchivePath &ap = options.Commands[0].ArchivePath;
1463     ap = options.ArchivePath;
1464     // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
1465     if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
1466     {
1467       createTempFile = true;
1468       ap.Temp = true;
1469       if (!options.WorkingDir.IsEmpty())
1470         ap.TempPrefix = options.WorkingDir;
1471       else
1472         ap.TempPrefix = us2fs(ap.Prefix);
1473       NormalizeDirPathPrefix(ap.TempPrefix);
1474     }
1475   }
1476 
1477   unsigned ci;
1478 
1479 
1480   // self including protection
1481   if (options.DeleteAfterCompressing)
1482   {
1483     for (ci = 0; ci < options.Commands.Size(); ci++)
1484     {
1485       CArchivePath &ap = options.Commands[ci].ArchivePath;
1486       const FString path = us2fs(ap.GetFinalPath());
1487       // maybe we must compare absolute paths path here
1488       FOR_VECTOR (i, dirItems.Items)
1489       {
1490         const FString phyPath = dirItems.GetPhyPath(i);
1491         if (phyPath == path)
1492         {
1493           UString s;
1494           s = "It is not allowed to include archive to itself";
1495           s.Add_LF();
1496           s += fs2us(path);
1497           throw s;
1498         }
1499       }
1500     }
1501   }
1502 
1503 
1504   for (ci = 0; ci < options.Commands.Size(); ci++)
1505   {
1506     CArchivePath &ap = options.Commands[ci].ArchivePath;
1507     if (usesTempDir)
1508     {
1509       // Check it
1510       ap.Prefix = fs2us(tempDirPrefix);
1511       // ap.Temp = true;
1512       // ap.TempPrefix = tempDirPrefix;
1513     }
1514     if (!options.StdOutMode &&
1515         (ci > 0 || !createTempFile))
1516     {
1517       const FString path = us2fs(ap.GetFinalPath());
1518       if (NFind::DoesFileOrDirExist(path))
1519       {
1520         errorInfo.SystemError = ERROR_FILE_EXISTS;
1521         errorInfo.Message = "The file already exists";
1522         errorInfo.FileNames.Add(path);
1523         return errorInfo.Get_HRESULT_Error();
1524       }
1525     }
1526   }
1527 
1528   CObjectVector<CArcItem> arcItems;
1529   if (thereIsInArchive)
1530   {
1531     RINOK(EnumerateInArchiveItems(
1532       // options.StoreAltStreams,
1533       censor, arcLink.Arcs.Back(), arcItems))
1534   }
1535 
1536   /*
1537   FStringVector processedFilePaths;
1538   FStringVector *processedFilePaths_Ptr = NULL;
1539   if (options.DeleteAfterCompressing)
1540     processedFilePaths_Ptr = &processedFilePaths;
1541   */
1542 
1543   CByteBuffer processedItems;
1544   if (options.DeleteAfterCompressing)
1545   {
1546     const unsigned num = dirItems.Items.Size();
1547     processedItems.Alloc(num);
1548     for (unsigned i = 0; i < num; i++)
1549       processedItems[i] = 0;
1550   }
1551 
1552   CMultiOutStream_Bunch multiStreams;
1553 
1554   /*
1555   #ifndef Z7_NO_CRYPTO
1556   if (arcLink.PasswordWasAsked)
1557   {
1558     // We set password, if open have requested password
1559     RINOK(callback->SetPassword(arcLink.Password));
1560   }
1561   #endif
1562   */
1563 
1564   for (ci = 0; ci < options.Commands.Size(); ci++)
1565   {
1566     const CArc *arc = thereIsInArchive ? arcLink.GetArc() : NULL;
1567     CUpdateArchiveCommand &command = options.Commands[ci];
1568     UString name;
1569     bool isUpdating;
1570 
1571     if (options.StdOutMode)
1572     {
1573       name = "stdout";
1574       isUpdating = thereIsInArchive;
1575     }
1576     else
1577     {
1578       name = command.ArchivePath.GetFinalPath();
1579       isUpdating = (ci == 0 && options.UpdateArchiveItself && thereIsInArchive);
1580     }
1581 
1582     RINOK(callback->StartArchive(name, isUpdating))
1583 
1584     CFinishArchiveStat st;
1585 
1586     RINOK(Compress(options,
1587         isUpdating,
1588         codecs,
1589         command.ActionSet,
1590         arc,
1591         command.ArchivePath,
1592         arcItems,
1593         options.DeleteAfterCompressing ? (Byte *)processedItems : NULL,
1594 
1595         dirItems,
1596         parentDirItem_Ptr,
1597 
1598         tempFiles,
1599         multiStreams,
1600         errorInfo, callback, st))
1601 
1602     RINOK(callback->FinishArchive(st))
1603   }
1604 
1605 
1606   if (thereIsInArchive)
1607   {
1608     RINOK(arcLink.Close())
1609     arcLink.Release();
1610   }
1611 
1612   multiStreams.DisableDeletion();
1613   RINOK(multiStreams.Destruct())
1614 
1615   // here we disable deleting of temp archives.
1616   // note: archive moving can fail, or it can be interrupted,
1617   // if we move new temp update from another volume.
1618   // And we still want to keep temp archive in that case,
1619   // because we will have deleted original archive.
1620   tempFiles.NeedDeleteFiles = false;
1621   // tempFiles.Paths.Clear();
1622 
1623   if (createTempFile)
1624   {
1625     try
1626     {
1627       CArchivePath &ap = options.Commands[0].ArchivePath;
1628       const FString &tempPath = ap.GetTempPath();
1629 
1630       // DWORD attrib = 0;
1631       if (thereIsInArchive)
1632       {
1633         // attrib = NFind::GetFileAttrib(us2fs(arcPath));
1634         if (!DeleteFileAlways(us2fs(arcPath)))
1635           return errorInfo.SetFromLastError("cannot delete the file", us2fs(arcPath));
1636       }
1637 
1638       UInt64 totalArcSize = 0;
1639       {
1640         NFind::CFileInfo fi;
1641         if (fi.Find(tempPath))
1642           totalArcSize = fi.Size;
1643       }
1644       RINOK(callback->MoveArc_Start(fs2us(tempPath), arcPath,
1645           totalArcSize, BoolToInt(thereIsInArchive)))
1646 
1647       C_CopyFileProgress_to_IUpdateCallbackUI2 prox(callback);
1648       // if we update archive, we have removed original archive.
1649       // So if we break archive moving, we will have only temporary archive.
1650       // We can disable breaking here:
1651       // prox.Disable_Break = thereIsInArchive;
1652 
1653       if (!MyMoveFile_with_Progress(tempPath, us2fs(arcPath), &prox))
1654       {
1655         errorInfo.SystemError = ::GetLastError();
1656         errorInfo.Message = "cannot move the file";
1657         if (errorInfo.SystemError == ERROR_INVALID_PARAMETER)
1658         {
1659           if (totalArcSize > (UInt32)(Int32)-1)
1660           {
1661             // bool isFsDetected = false;
1662             // if (NSystem::Is_File_LimitedBy_4GB(us2fs(arcPath), isFsDetected) || !isFsDetected)
1663             {
1664               errorInfo.Message.Add_LF();
1665               errorInfo.Message += "Archive file size exceeds 4 GB";
1666             }
1667           }
1668         }
1669         // if there was no input archive, and we have operation breaking.
1670         // then we can remove temporary archive, because we still have original uncompressed files.
1671         if (!thereIsInArchive
1672             && prox.CallbackResult == E_ABORT)
1673           tempFiles.NeedDeleteFiles = true;
1674         errorInfo.FileNames.Add(tempPath);
1675         errorInfo.FileNames.Add(us2fs(arcPath));
1676         RINOK(prox.CallbackResult)
1677         return errorInfo.Get_HRESULT_Error();
1678       }
1679 
1680       // MoveArc_Finish() can return delayed user break (E_ABORT) status,
1681       // if callback callee ignored interruption to finish archive creation operation.
1682       RINOK(callback->MoveArc_Finish())
1683 
1684       /*
1685       if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))
1686       {
1687         DWORD attrib2 = NFind::GetFileAttrib(us2fs(arcPath));
1688         if (attrib2 != INVALID_FILE_ATTRIBUTES)
1689           NDir::SetFileAttrib(us2fs(arcPath), attrib2 | FILE_ATTRIBUTE_READONLY);
1690       }
1691       */
1692     }
1693     catch(...)
1694     {
1695       throw;
1696     }
1697   }
1698 
1699 
1700   #if defined(_WIN32) && !defined(UNDER_CE)
1701 
1702 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
1703 
1704   if (options.EMailMode)
1705   {
1706     NDLL::CLibrary mapiLib;
1707     if (!mapiLib.Load(FTEXT("Mapi32.dll")))
1708     {
1709       errorInfo.SetFromLastError("cannot load Mapi32.dll");
1710       return errorInfo.Get_HRESULT_Error();
1711     }
1712 
1713     FStringVector fullPaths;
1714     unsigned i;
1715 
1716     for (i = 0; i < options.Commands.Size(); i++)
1717     {
1718       CArchivePath &ap = options.Commands[i].ArchivePath;
1719       const FString finalPath = us2fs(ap.GetFinalPath());
1720       FString arcPath2;
1721       if (!MyGetFullPathName(finalPath, arcPath2))
1722         return errorInfo.SetFromLastError("GetFullPathName error", finalPath);
1723       fullPaths.Add(arcPath2);
1724     }
1725 
1726     /*
1727     LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments");
1728     if (fnSend == 0)
1729     {
1730       errorInfo.SetFromLastError)("7-Zip cannot find MAPISendDocuments function");
1731       return errorInfo.Get_HRESULT_Error();
1732     }
1733     */
1734     const
1735     Z7_WIN_LPMAPISENDMAILW sendMailW = Z7_GET_PROC_ADDRESS(
1736     Z7_WIN_LPMAPISENDMAILW, mapiLib.Get_HMODULE(),
1737             "MAPISendMailW");
1738    if (sendMailW)
1739    {
1740 
1741     CCurrentDirRestorer curDirRestorer;
1742 
1743     UStringVector paths;
1744     UStringVector names;
1745 
1746     for (i = 0; i < fullPaths.Size(); i++)
1747     {
1748       const UString arcPath2 = fs2us(fullPaths[i]);
1749       const UString fileName = ExtractFileNameFromPath(arcPath2);
1750       paths.Add(arcPath2);
1751       names.Add(fileName);
1752       // Warning!!! MAPISendDocuments function changes Current directory
1753       // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
1754     }
1755 
1756     CRecordVector<Z7_WIN_MapiFileDescW> files;
1757     files.ClearAndSetSize(paths.Size());
1758 
1759     for (i = 0; i < paths.Size(); i++)
1760     {
1761       Z7_WIN_MapiFileDescW &f = files[i];
1762       memset(&f, 0, sizeof(f));
1763       f.nPosition = 0xFFFFFFFF;
1764       f.lpszPathName = paths[i].Ptr_non_const();
1765       f.lpszFileName = names[i].Ptr_non_const();
1766     }
1767 
1768     {
1769       Z7_WIN_MapiMessageW m;
1770       memset(&m, 0, sizeof(m));
1771       m.nFileCount = files.Size();
1772       m.lpFiles = files.NonConstData();
1773 
1774       const UString addr (options.EMailAddress);
1775       Z7_WIN_MapiRecipDescW rec;
1776       if (!addr.IsEmpty())
1777       {
1778         memset(&rec, 0, sizeof(rec));
1779         rec.ulRecipClass = MAPI_TO;
1780         rec.lpszAddress = addr.Ptr_non_const();
1781         m.nRecipCount = 1;
1782         m.lpRecips = &rec;
1783       }
1784 
1785       sendMailW((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
1786     }
1787    }
1788    else
1789    {
1790     const
1791     LPMAPISENDMAIL sendMail = Z7_GET_PROC_ADDRESS(
1792     LPMAPISENDMAIL, mapiLib.Get_HMODULE(),
1793      "MAPISendMail");
1794     if (!sendMail)
1795     {
1796       errorInfo.SetFromLastError("7-Zip cannot find MAPISendMail function");
1797       return errorInfo.Get_HRESULT_Error();
1798     }
1799 
1800     CCurrentDirRestorer curDirRestorer;
1801 
1802     AStringVector paths;
1803     AStringVector names;
1804 
1805     for (i = 0; i < fullPaths.Size(); i++)
1806     {
1807       const UString arcPath2 = fs2us(fullPaths[i]);
1808       const UString fileName = ExtractFileNameFromPath(arcPath2);
1809       paths.Add(GetAnsiString(arcPath2));
1810       names.Add(GetAnsiString(fileName));
1811       // const AString path (GetAnsiString(arcPath2));
1812       // const AString name (GetAnsiString(fileName));
1813       // Warning!!! MAPISendDocuments function changes Current directory
1814       // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
1815     }
1816 
1817     CRecordVector<MapiFileDesc> files;
1818     files.ClearAndSetSize(paths.Size());
1819 
1820     for (i = 0; i < paths.Size(); i++)
1821     {
1822       MapiFileDesc &f = files[i];
1823       memset(&f, 0, sizeof(f));
1824       f.nPosition = 0xFFFFFFFF;
1825       f.lpszPathName = paths[i].Ptr_non_const();
1826       f.lpszFileName = names[i].Ptr_non_const();
1827     }
1828 
1829     {
1830       MapiMessage m;
1831       memset(&m, 0, sizeof(m));
1832       m.nFileCount = files.Size();
1833       m.lpFiles = files.NonConstData();
1834 
1835       const AString addr (GetAnsiString(options.EMailAddress));
1836       MapiRecipDesc rec;
1837       if (!addr.IsEmpty())
1838       {
1839         memset(&rec, 0, sizeof(rec));
1840         rec.ulRecipClass = MAPI_TO;
1841         rec.lpszAddress = addr.Ptr_non_const();
1842         m.nRecipCount = 1;
1843         m.lpRecips = &rec;
1844       }
1845 
1846       sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
1847     }
1848    }
1849   }
1850 
1851   #endif
1852 
1853   if (options.DeleteAfterCompressing)
1854   {
1855     CRecordVector<CDirPathSortPair> pairs;
1856     FStringVector foldersNames;
1857 
1858     unsigned i;
1859 
1860     for (i = 0; i < dirItems.Items.Size(); i++)
1861     {
1862       const CDirItem &dirItem = dirItems.Items[i];
1863       const FString phyPath = dirItems.GetPhyPath(i);
1864       if (dirItem.IsDir())
1865       {
1866         CDirPathSortPair pair;
1867         pair.Index = i;
1868         pair.SetNumSlashes(phyPath);
1869         pairs.Add(pair);
1870       }
1871       else
1872       {
1873         // 21.04: we have set processedItems[*] before for all required items
1874         if (processedItems[i] != 0
1875             // || dirItem.Size == 0
1876             // || dirItem.AreReparseData()
1877             )
1878         {
1879           NFind::CFileInfo fileInfo;
1880           /* if (!SymLinks), we follow link here, similar to (dirItem) filling */
1881           if (fileInfo.Find(phyPath, !options.SymLinks.Val))
1882           {
1883             bool is_SameSize = false;
1884             if (options.SymLinks.Val && dirItem.AreReparseData())
1885             {
1886               /* (dirItem.Size = dirItem.ReparseData.Size()) was set before.
1887                  So we don't compare sizes for that case here */
1888               is_SameSize = fileInfo.IsOsSymLink();
1889             }
1890             else
1891               is_SameSize = (fileInfo.Size == dirItem.Size);
1892 
1893             if (is_SameSize
1894                 && Compare_FiTime(&fileInfo.MTime, &dirItem.MTime) == 0
1895                 && Compare_FiTime(&fileInfo.CTime, &dirItem.CTime) == 0)
1896             {
1897               RINOK(callback->DeletingAfterArchiving(phyPath, false))
1898               DeleteFileAlways(phyPath);
1899             }
1900           }
1901         }
1902         else
1903         {
1904           // file was skipped by some reason. We can throw error for debug:
1905           /*
1906           errorInfo.SystemError = 0;
1907           errorInfo.Message = "file was not processed";
1908           errorInfo.FileNames.Add(phyPath);
1909           return E_FAIL;
1910           */
1911         }
1912       }
1913     }
1914 
1915     pairs.Sort2();
1916 
1917     for (i = 0; i < pairs.Size(); i++)
1918     {
1919       const FString phyPath = dirItems.GetPhyPath(pairs[i].Index);
1920       if (NFind::DoesDirExist(phyPath))
1921       {
1922         RINOK(callback->DeletingAfterArchiving(phyPath, true))
1923         RemoveDir(phyPath);
1924       }
1925     }
1926 
1927     RINOK(callback->FinishDeletingAfterArchiving())
1928   }
1929 
1930   return S_OK;
1931 }
1932