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