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