1 // Update.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "Update.h"
6 
7 #include "Common/IntToString.h"
8 #include "Common/StringConvert.h"
9 
10 #ifdef _WIN32
11 #include "Windows/DLL.h"
12 #endif
13 
14 #include "Windows/FileDir.h"
15 #include "Windows/FileFind.h"
16 #include "Windows/FileName.h"
17 #include "Windows/PropVariant.h"
18 #include "Windows/PropVariantConversions.h"
19 #include "Windows/Time.h"
20 
21 #include "../../Common/FileStreams.h"
22 
23 #include "../../Compress/CopyCoder.h"
24 
25 #include "../Common/DirItem.h"
26 #include "../Common/EnumDirItems.h"
27 #include "../Common/OpenArchive.h"
28 #include "../Common/UpdateProduce.h"
29 
30 #include "EnumDirItems.h"
31 #include "SetProperties.h"
32 #include "TempFiles.h"
33 #include "UpdateCallback.h"
34 
35 static const char *kUpdateIsNotSupoorted =
36   "update operations are not supported for this archive";
37 
38 using namespace NWindows;
39 using namespace NCOM;
40 using namespace NFile;
41 using namespace NName;
42 
43 static const wchar_t *kTempFolderPrefix = L"7zE";
44 
45 using namespace NUpdateArchive;
46 
47 class COutMultiVolStream:
48   public IOutStream,
49   public CMyUnknownImp
50 {
51   int _streamIndex; // required stream
52   UInt64 _offsetPos; // offset from start of _streamIndex index
53   UInt64 _absPos;
54   UInt64 _length;
55 
56   struct CSubStreamInfo
57   {
58     COutFileStream *StreamSpec;
59     CMyComPtr<IOutStream> Stream;
60     UString Name;
61     UInt64 Pos;
62     UInt64 RealSize;
63   };
64   CObjectVector<CSubStreamInfo> Streams;
65 public:
66   // CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
67   CRecordVector<UInt64> Sizes;
68   UString Prefix;
69   CTempFiles *TempFiles;
70 
Init()71   void Init()
72   {
73     _streamIndex = 0;
74     _offsetPos = 0;
75     _absPos = 0;
76     _length = 0;
77   }
78 
79   HRESULT Close();
80 
81   MY_UNKNOWN_IMP1(IOutStream)
82 
83   STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
84   STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
85   STDMETHOD(SetSize)(Int64 newSize);
86 };
87 
88 // static NSynchronization::CCriticalSection g_TempPathsCS;
89 
Close()90 HRESULT COutMultiVolStream::Close()
91 {
92   HRESULT res = S_OK;
93   for (int i = 0; i < Streams.Size(); i++)
94   {
95     CSubStreamInfo &s = Streams[i];
96     if (s.StreamSpec)
97     {
98       HRESULT res2 = s.StreamSpec->Close();
99       if (res2 != S_OK)
100         res = res2;
101     }
102   }
103   return res;
104 }
105 
Write(const void * data,UInt32 size,UInt32 * processedSize)106 STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
107 {
108   if (processedSize != NULL)
109     *processedSize = 0;
110   while(size > 0)
111   {
112     if (_streamIndex >= Streams.Size())
113     {
114       CSubStreamInfo subStream;
115 
116       wchar_t temp[16];
117       ConvertUInt32ToString(_streamIndex + 1, temp);
118       UString res = temp;
119       while (res.Length() < 3)
120         res = UString(L'0') + res;
121       UString name = Prefix + res;
122       subStream.StreamSpec = new COutFileStream;
123       subStream.Stream = subStream.StreamSpec;
124       if (!subStream.StreamSpec->Create(name, false))
125         return ::GetLastError();
126       {
127         // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS);
128         TempFiles->Paths.Add(name);
129       }
130 
131       subStream.Pos = 0;
132       subStream.RealSize = 0;
133       subStream.Name = name;
134       Streams.Add(subStream);
135       continue;
136     }
137     CSubStreamInfo &subStream = Streams[_streamIndex];
138 
139     int index = _streamIndex;
140     if (index >= Sizes.Size())
141       index = Sizes.Size() - 1;
142     UInt64 volSize = Sizes[index];
143 
144     if (_offsetPos >= volSize)
145     {
146       _offsetPos -= volSize;
147       _streamIndex++;
148       continue;
149     }
150     if (_offsetPos != subStream.Pos)
151     {
152       // CMyComPtr<IOutStream> outStream;
153       // RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream));
154       RINOK(subStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL));
155       subStream.Pos = _offsetPos;
156     }
157 
158     UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - subStream.Pos);
159     UInt32 realProcessed;
160     RINOK(subStream.Stream->Write(data, curSize, &realProcessed));
161     data = (void *)((Byte *)data + realProcessed);
162     size -= realProcessed;
163     subStream.Pos += realProcessed;
164     _offsetPos += realProcessed;
165     _absPos += realProcessed;
166     if (_absPos > _length)
167       _length = _absPos;
168     if (_offsetPos > subStream.RealSize)
169       subStream.RealSize = _offsetPos;
170     if (processedSize != NULL)
171       *processedSize += realProcessed;
172     if (subStream.Pos == volSize)
173     {
174       _streamIndex++;
175       _offsetPos = 0;
176     }
177     if (realProcessed == 0 && curSize != 0)
178       return E_FAIL;
179     break;
180   }
181   return S_OK;
182 }
183 
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)184 STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
185 {
186   if (seekOrigin >= 3)
187     return STG_E_INVALIDFUNCTION;
188   switch(seekOrigin)
189   {
190     case STREAM_SEEK_SET:
191       _absPos = offset;
192       break;
193     case STREAM_SEEK_CUR:
194       _absPos += offset;
195       break;
196     case STREAM_SEEK_END:
197       _absPos = _length + offset;
198       break;
199   }
200   _offsetPos = _absPos;
201   if (newPosition != NULL)
202     *newPosition = _absPos;
203   _streamIndex = 0;
204   return S_OK;
205 }
206 
SetSize(Int64 newSize)207 STDMETHODIMP COutMultiVolStream::SetSize(Int64 newSize)
208 {
209   if (newSize < 0)
210     return E_INVALIDARG;
211   int i = 0;
212   while (i < Streams.Size())
213   {
214     CSubStreamInfo &subStream = Streams[i++];
215     if ((UInt64)newSize < subStream.RealSize)
216     {
217       RINOK(subStream.Stream->SetSize(newSize));
218       subStream.RealSize = newSize;
219       break;
220     }
221     newSize -= subStream.RealSize;
222   }
223   while (i < Streams.Size())
224   {
225     {
226       CSubStreamInfo &subStream = Streams.Back();
227       subStream.Stream.Release();
228       NDirectory::DeleteFileAlways(subStream.Name);
229     }
230     Streams.DeleteBack();
231   }
232   _offsetPos = _absPos;
233   _streamIndex = 0;
234   _length = newSize;
235   return S_OK;
236 }
237 
238 static const wchar_t *kDefaultArchiveType = L"7z";
239 static const wchar_t *kSFXExtension =
240   #ifdef _WIN32
241     L"exe";
242   #else
243     L"";
244   #endif
245 
Init(const CCodecs * codecs,const CIntVector & formatIndices,const UString & arcPath)246 bool CUpdateOptions::Init(const CCodecs *codecs, const CIntVector &formatIndices, const UString &arcPath)
247 {
248   if (formatIndices.Size() > 1)
249     return false;
250   int arcTypeIndex = -1;
251   if (formatIndices.Size() != 0)
252     arcTypeIndex = formatIndices[0];
253   if (arcTypeIndex >= 0)
254     MethodMode.FormatIndex = arcTypeIndex;
255   else
256   {
257     MethodMode.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
258     // It works incorrectly for update command if archive has some non-default extension!
259     if (MethodMode.FormatIndex < 0)
260       MethodMode.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArchiveType);
261   }
262   if (MethodMode.FormatIndex < 0)
263     return false;
264   const CArcInfoEx &arcInfo = codecs->Formats[MethodMode.FormatIndex];
265   if (!arcInfo.UpdateEnabled)
266     return false;
267   UString typeExt = arcInfo.GetMainExt();
268   UString ext = typeExt;
269   if (SfxMode)
270     ext = kSFXExtension;
271   ArchivePath.BaseExtension = ext;
272   ArchivePath.VolExtension = typeExt;
273   ArchivePath.ParseFromPath(arcPath);
274   for (int i = 0; i < Commands.Size(); i++)
275   {
276     CUpdateArchiveCommand &uc = Commands[i];
277     uc.ArchivePath.BaseExtension = ext;
278     uc.ArchivePath.VolExtension = typeExt;
279     uc.ArchivePath.ParseFromPath(uc.UserArchivePath);
280   }
281   return true;
282 }
283 
284 /*
285 struct CUpdateProduceCallbackImp: public IUpdateProduceCallback
286 {
287   const CObjectVector<CArcItem> *_arcItems;
288   IUpdateCallbackUI *_callback;
289 
290   CUpdateProduceCallbackImp(const CObjectVector<CArcItem> *a,
291       IUpdateCallbackUI *callback): _arcItems(a), _callback(callback) {}
292   virtual HRESULT ShowDeleteFile(int arcIndex);
293 };
294 
295 HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(int arcIndex)
296 {
297   return _callback->ShowDeleteFile((*_arcItems)[arcIndex].Name);
298 }
299 */
300 
Compress(CCodecs * codecs,const CActionSet & actionSet,IInArchive * archive,const CCompressionMethodMode & compressionMethod,CArchivePath & archivePath,const CObjectVector<CArcItem> & arcItems,bool shareForWrite,bool stdInMode,bool stdOutMode,const CDirItems & dirItems,bool sfxMode,const UString & sfxModule,const CRecordVector<UInt64> & volumesSizes,CTempFiles & tempFiles,CUpdateErrorInfo & errorInfo,IUpdateCallbackUI * callback)301 static HRESULT Compress(
302     CCodecs *codecs,
303     const CActionSet &actionSet,
304     IInArchive *archive,
305     const CCompressionMethodMode &compressionMethod,
306     CArchivePath &archivePath,
307     const CObjectVector<CArcItem> &arcItems,
308     bool shareForWrite,
309     bool stdInMode,
310     /* const UString & stdInFileName, */
311     bool stdOutMode,
312     const CDirItems &dirItems,
313     bool sfxMode,
314     const UString &sfxModule,
315     const CRecordVector<UInt64> &volumesSizes,
316     CTempFiles &tempFiles,
317     CUpdateErrorInfo &errorInfo,
318     IUpdateCallbackUI *callback)
319 {
320   CMyComPtr<IOutArchive> outArchive;
321   if (archive != NULL)
322   {
323     CMyComPtr<IInArchive> archive2 = archive;
324     HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
325     if (result != S_OK)
326       throw kUpdateIsNotSupoorted;
327   }
328   else
329   {
330     RINOK(codecs->CreateOutArchive(compressionMethod.FormatIndex, outArchive));
331 
332     #ifdef EXTERNAL_CODECS
333     {
334       CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
335       outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
336       if (setCompressCodecsInfo)
337       {
338         RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
339       }
340     }
341     #endif
342   }
343   if (outArchive == 0)
344     throw kUpdateIsNotSupoorted;
345 
346   NFileTimeType::EEnum fileTimeType;
347   UInt32 value;
348   RINOK(outArchive->GetFileTimeType(&value));
349 
350   switch(value)
351   {
352     case NFileTimeType::kWindows:
353     case NFileTimeType::kUnix:
354     case NFileTimeType::kDOS:
355       fileTimeType = (NFileTimeType::EEnum)value;
356       break;
357     default:
358       return E_FAIL;
359   }
360 
361   CRecordVector<CUpdatePair2> updatePairs2;
362 
363   {
364     CRecordVector<CUpdatePair> updatePairs;
365     GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!!
366     // CUpdateProduceCallbackImp upCallback(&arcItems, callback);
367     UpdateProduce(updatePairs, actionSet, updatePairs2, NULL /* &upCallback */);
368   }
369 
370   UInt32 numFiles = 0;
371   for (int i = 0; i < updatePairs2.Size(); i++)
372     if (updatePairs2[i].NewData)
373       numFiles++;
374 
375   RINOK(callback->SetNumFiles(numFiles));
376 
377 
378   CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
379   CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
380 
381   updateCallbackSpec->ShareForWrite = shareForWrite;
382   updateCallbackSpec->StdInMode = stdInMode;
383   updateCallbackSpec->Callback = callback;
384   updateCallbackSpec->DirItems = &dirItems;
385   updateCallbackSpec->ArcItems = &arcItems;
386   updateCallbackSpec->UpdatePairs = &updatePairs2;
387 
388   CMyComPtr<ISequentialOutStream> outStream;
389 
390   const UString &archiveName = archivePath.GetFinalPath();
391   if (!stdOutMode)
392   {
393     UString resultPath;
394     int pos;
395     if (!NFile::NDirectory::MyGetFullPathName(archiveName, resultPath, pos))
396       throw 1417161;
397     NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos));
398   }
399 
400   COutFileStream *outStreamSpec = NULL;
401   COutMultiVolStream *volStreamSpec = NULL;
402 
403   if (volumesSizes.Size() == 0)
404   {
405     if (stdOutMode)
406       outStream = new CStdOutFileStream;
407     else
408     {
409       outStreamSpec = new COutFileStream;
410       outStream = outStreamSpec;
411       bool isOK = false;
412       UString realPath;
413       for (int i = 0; i < (1 << 16); i++)
414       {
415         if (archivePath.Temp)
416         {
417           if (i > 0)
418           {
419             wchar_t s[16];
420             ConvertUInt32ToString(i, s);
421             archivePath.TempPostfix = s;
422           }
423           realPath = archivePath.GetTempPath();
424         }
425         else
426           realPath = archivePath.GetFinalPath();
427         if (outStreamSpec->Create(realPath, false))
428         {
429           tempFiles.Paths.Add(realPath);
430           isOK = true;
431           break;
432         }
433         if (::GetLastError() != ERROR_FILE_EXISTS)
434           break;
435         if (!archivePath.Temp)
436           break;
437       }
438       if (!isOK)
439       {
440         errorInfo.SystemError = ::GetLastError();
441         errorInfo.FileName = realPath;
442         errorInfo.Message = L"Can not open file";
443         return E_FAIL;
444       }
445     }
446   }
447   else
448   {
449     if (stdOutMode)
450       return E_FAIL;
451     volStreamSpec = new COutMultiVolStream;
452     outStream = volStreamSpec;
453     volStreamSpec->Sizes = volumesSizes;
454     volStreamSpec->Prefix = archivePath.GetFinalPath() + UString(L".");
455     volStreamSpec->TempFiles = &tempFiles;
456     volStreamSpec->Init();
457 
458     /*
459     updateCallbackSpec->VolumesSizes = volumesSizes;
460     updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
461     if (!archivePath.VolExtension.IsEmpty())
462       updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension;
463     */
464   }
465 
466   RINOK(SetProperties(outArchive, compressionMethod.Properties));
467 
468   if (sfxMode)
469   {
470     CInFileStream *sfxStreamSpec = new CInFileStream;
471     CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
472     if (!sfxStreamSpec->Open(sfxModule))
473     {
474       errorInfo.SystemError = ::GetLastError();
475       errorInfo.Message = L"Can't open sfx module";
476       errorInfo.FileName = sfxModule;
477       return E_FAIL;
478     }
479 
480     CMyComPtr<ISequentialOutStream> sfxOutStream;
481     COutFileStream *outStreamSpec = NULL;
482     if (volumesSizes.Size() == 0)
483       sfxOutStream = outStream;
484     else
485     {
486       outStreamSpec = new COutFileStream;
487       sfxOutStream = outStreamSpec;
488       UString realPath = archivePath.GetFinalPath();
489       if (!outStreamSpec->Create(realPath, false))
490       {
491         errorInfo.SystemError = ::GetLastError();
492         errorInfo.FileName = realPath;
493         errorInfo.Message = L"Can not open file";
494         return E_FAIL;
495       }
496     }
497     RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL));
498     if (outStreamSpec)
499     {
500       RINOK(outStreamSpec->Close());
501     }
502   }
503 
504   HRESULT result = outArchive->UpdateItems(outStream, updatePairs2.Size(), updateCallback);
505   callback->Finilize();
506   RINOK(result);
507   if (outStreamSpec)
508     result = outStreamSpec->Close();
509   else if (volStreamSpec)
510     result = volStreamSpec->Close();
511   return result;
512 }
513 
EnumerateInArchiveItems(const NWildcard::CCensor & censor,const CArc & arc,CObjectVector<CArcItem> & arcItems)514 HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor,
515     const CArc &arc,
516     CObjectVector<CArcItem> &arcItems)
517 {
518   arcItems.Clear();
519   UInt32 numItems;
520   IInArchive *archive = arc.Archive;
521   RINOK(archive->GetNumberOfItems(&numItems));
522   arcItems.Reserve(numItems);
523   for (UInt32 i = 0; i < numItems; i++)
524   {
525     CArcItem ai;
526 
527     RINOK(arc.GetItemPath(i, ai.Name));
528     RINOK(IsArchiveItemFolder(archive, i, ai.IsDir));
529     ai.Censored = censor.CheckPath(ai.Name, !ai.IsDir);
530     RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined));
531 
532     {
533       CPropVariant prop;
534       RINOK(archive->GetProperty(i, kpidSize, &prop));
535       ai.SizeDefined = (prop.vt != VT_EMPTY);
536       if (ai.SizeDefined)
537         ai.Size = ConvertPropVariantToUInt64(prop);
538     }
539 
540     {
541       CPropVariant prop;
542       RINOK(archive->GetProperty(i, kpidTimeType, &prop));
543       if (prop.vt == VT_UI4)
544       {
545         ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal;
546         switch(ai.TimeType)
547         {
548           case NFileTimeType::kWindows:
549           case NFileTimeType::kUnix:
550           case NFileTimeType::kDOS:
551             break;
552           default:
553             return E_FAIL;
554         }
555       }
556     }
557 
558     ai.IndexInServer = i;
559     arcItems.Add(ai);
560   }
561   return S_OK;
562 }
563 
564 
UpdateWithItemLists(CCodecs * codecs,CUpdateOptions & options,IInArchive * archive,const CObjectVector<CArcItem> & arcItems,CDirItems & dirItems,CTempFiles & tempFiles,CUpdateErrorInfo & errorInfo,IUpdateCallbackUI2 * callback)565 static HRESULT UpdateWithItemLists(
566     CCodecs *codecs,
567     CUpdateOptions &options,
568     IInArchive *archive,
569     const CObjectVector<CArcItem> &arcItems,
570     CDirItems &dirItems,
571     CTempFiles &tempFiles,
572     CUpdateErrorInfo &errorInfo,
573     IUpdateCallbackUI2 *callback)
574 {
575   for(int i = 0; i < options.Commands.Size(); i++)
576   {
577     CUpdateArchiveCommand &command = options.Commands[i];
578     if (options.StdOutMode)
579     {
580       RINOK(callback->StartArchive(L"stdout", archive != 0));
581     }
582     else
583     {
584       RINOK(callback->StartArchive(command.ArchivePath.GetFinalPath(),
585           i == 0 && options.UpdateArchiveItself && archive != 0));
586     }
587 
588     RINOK(Compress(
589         codecs,
590         command.ActionSet, archive,
591         options.MethodMode,
592         command.ArchivePath,
593         arcItems,
594         options.OpenShareForWrite,
595         options.StdInMode,
596         /* options.StdInFileName, */
597         options.StdOutMode,
598         dirItems,
599         options.SfxMode, options.SfxModule,
600         options.VolumesSizes,
601         tempFiles,
602         errorInfo, callback));
603 
604     RINOK(callback->FinishArchive());
605   }
606   return S_OK;
607 }
608 
609 #ifdef _WIN32
610 class CCurrentDirRestorer
611 {
612   UString m_CurrentDirectory;
613 public:
CCurrentDirRestorer()614   CCurrentDirRestorer()
615     { NFile::NDirectory::MyGetCurrentDirectory(m_CurrentDirectory); }
~CCurrentDirRestorer()616   ~CCurrentDirRestorer()
617     { RestoreDirectory();}
RestoreDirectory()618   bool RestoreDirectory()
619     { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(m_CurrentDirectory)); }
620 };
621 #endif
622 
623 struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback
624 {
625   IUpdateCallbackUI2 *Callback;
ScanProgressCEnumDirItemUpdateCallback626   HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, const wchar_t *path)
627   {
628     return Callback->ScanProgress(numFolders, numFiles, path);
629   }
630 };
631 
632 #ifdef _WIN32
633 typedef ULONG (FAR PASCAL MY_MAPISENDDOCUMENTS)(
634   ULONG_PTR ulUIParam,
635   LPSTR lpszDelimChar,
636   LPSTR lpszFilePaths,
637   LPSTR lpszFileNames,
638   ULONG ulReserved
639 );
640 typedef MY_MAPISENDDOCUMENTS FAR *MY_LPMAPISENDDOCUMENTS;
641 #endif
642 
UpdateArchive(CCodecs * codecs,const NWildcard::CCensor & censor,CUpdateOptions & options,CUpdateErrorInfo & errorInfo,IOpenCallbackUI * openCallback,IUpdateCallbackUI2 * callback)643 HRESULT UpdateArchive(
644     CCodecs *codecs,
645     const NWildcard::CCensor &censor,
646     CUpdateOptions &options,
647     CUpdateErrorInfo &errorInfo,
648     IOpenCallbackUI *openCallback,
649     IUpdateCallbackUI2 *callback)
650 {
651   if (options.StdOutMode && options.EMailMode)
652     return E_FAIL;
653 
654   if (options.VolumesSizes.Size() > 0 && (options.EMailMode || options.SfxMode))
655     return E_NOTIMPL;
656 
657   if (options.SfxMode)
658   {
659     CProperty property;
660     property.Name = L"rsfx";
661     property.Value = L"on";
662     options.MethodMode.Properties.Add(property);
663     if (options.SfxModule.IsEmpty())
664     {
665       errorInfo.Message = L"sfx file is not specified";
666       return E_FAIL;
667     }
668     UString name = options.SfxModule;
669     if (!NDirectory::MySearchPath(NULL, name, NULL, options.SfxModule))
670     {
671       errorInfo.Message = L"can't find specified sfx module";
672       return E_FAIL;
673     }
674   }
675 
676   const UString archiveName = options.ArchivePath.GetFinalPath();
677 
678   CArchiveLink archiveLink;
679   NFind::CFileInfoW archiveFileInfo;
680 
681   if (archiveFileInfo.Find(archiveName))
682   {
683     if (archiveFileInfo.IsDir())
684       throw "there is no such archive";
685     if (options.VolumesSizes.Size() > 0)
686       return E_NOTIMPL;
687     CIntVector formatIndices;
688     if (options.MethodMode.FormatIndex >= 0)
689       formatIndices.Add(options.MethodMode.FormatIndex);
690     HRESULT result = archiveLink.Open2(codecs, formatIndices, false, NULL, archiveName, openCallback);
691     if (result == E_ABORT)
692       return result;
693     RINOK(callback->OpenResult(archiveName, result));
694     RINOK(result);
695     if (archiveLink.VolumePaths.Size() > 1)
696     {
697       errorInfo.SystemError = (DWORD)E_NOTIMPL;
698       errorInfo.Message = L"Updating for multivolume archives is not implemented";
699       return E_NOTIMPL;
700     }
701 
702     CArc &arc = archiveLink.Arcs.Back();
703     arc.MTimeDefined = !archiveFileInfo.IsDevice;
704     arc.MTime = archiveFileInfo.MTime;
705   }
706   else
707   {
708     /*
709     if (archiveType.IsEmpty())
710       throw "type of archive is not specified";
711     */
712   }
713 
714   CDirItems dirItems;
715   if (options.StdInMode)
716   {
717     CDirItem di;
718     di.Name = options.StdInFileName;
719     di.Size = (UInt64)(Int64)-1;
720     di.Attrib = 0;
721     NTime::GetCurUtcFileTime(di.MTime);
722     di.CTime = di.ATime = di.MTime;
723     dirItems.Items.Add(di);
724   }
725   else
726   {
727     bool needScanning = false;
728     for(int i = 0; i < options.Commands.Size(); i++)
729       if (options.Commands[i].ActionSet.NeedScanning())
730         needScanning = true;
731     if (needScanning)
732     {
733       CEnumDirItemUpdateCallback enumCallback;
734       enumCallback.Callback = callback;
735       RINOK(callback->StartScanning());
736       UStringVector errorPaths;
737       CRecordVector<DWORD> errorCodes;
738       HRESULT res = EnumerateItems(censor, dirItems, &enumCallback, errorPaths, errorCodes);
739       for (int i = 0; i < errorPaths.Size(); i++)
740       {
741         RINOK(callback->CanNotFindError(errorPaths[i], errorCodes[i]));
742       }
743       if (res != S_OK)
744       {
745         if (res != E_ABORT)
746           errorInfo.Message = L"Scanning error";
747         // errorInfo.FileName = errorPath;
748         return res;
749       }
750       RINOK(callback->FinishScanning());
751     }
752   }
753 
754   UString tempDirPrefix;
755   bool usesTempDir = false;
756 
757   #ifdef _WIN32
758   NDirectory::CTempDirectoryW tempDirectory;
759   if (options.EMailMode && options.EMailRemoveAfter)
760   {
761     tempDirectory.Create(kTempFolderPrefix);
762     tempDirPrefix = tempDirectory.GetPath();
763     NormalizeDirPathPrefix(tempDirPrefix);
764     usesTempDir = true;
765   }
766   #endif
767 
768   CTempFiles tempFiles;
769 
770   bool createTempFile = false;
771 
772   bool thereIsInArchive = archiveLink.IsOpen;
773 
774   if (!options.StdOutMode && options.UpdateArchiveItself)
775   {
776     CArchivePath &ap = options.Commands[0].ArchivePath;
777     ap = options.ArchivePath;
778     // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
779     if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
780     {
781       createTempFile = true;
782       ap.Temp = true;
783       if (!options.WorkingDir.IsEmpty())
784       {
785         ap.TempPrefix = options.WorkingDir;
786         NormalizeDirPathPrefix(ap.TempPrefix);
787       }
788     }
789   }
790 
791   for(int i = 0; i < options.Commands.Size(); i++)
792   {
793     CArchivePath &ap = options.Commands[i].ArchivePath;
794     if (usesTempDir)
795     {
796       // Check it
797       ap.Prefix = tempDirPrefix;
798       // ap.Temp = true;
799       // ap.TempPrefix = tempDirPrefix;
800     }
801     if (i > 0 || !createTempFile)
802     {
803       const UString &path = ap.GetFinalPath();
804       if (NFind::DoesFileOrDirExist(path))
805       {
806         errorInfo.SystemError = 0;
807         errorInfo.Message = L"File already exists";
808         errorInfo.FileName = path;
809         return E_FAIL;
810       }
811     }
812   }
813 
814   CObjectVector<CArcItem> arcItems;
815   if (thereIsInArchive)
816   {
817     RINOK(EnumerateInArchiveItems(censor, archiveLink.Arcs.Back(), arcItems));
818   }
819 
820   RINOK(UpdateWithItemLists(codecs, options,
821       thereIsInArchive ? archiveLink.GetArchive() : 0,
822       arcItems, dirItems,
823       tempFiles, errorInfo, callback));
824 
825   if (thereIsInArchive)
826   {
827     RINOK(archiveLink.Close());
828     archiveLink.Release();
829   }
830 
831   tempFiles.Paths.Clear();
832   if (createTempFile)
833   {
834     try
835     {
836       CArchivePath &ap = options.Commands[0].ArchivePath;
837       const UString &tempPath = ap.GetTempPath();
838       if (thereIsInArchive)
839         if (!NDirectory::DeleteFileAlways(archiveName))
840         {
841           errorInfo.SystemError = ::GetLastError();
842           errorInfo.Message = L"delete file error";
843           errorInfo.FileName = archiveName;
844           return E_FAIL;
845         }
846       if (!NDirectory::MyMoveFile(tempPath, archiveName))
847       {
848         errorInfo.SystemError = ::GetLastError();
849         errorInfo.Message = L"move file error";
850         errorInfo.FileName = tempPath;
851         errorInfo.FileName2 = archiveName;
852         return E_FAIL;
853       }
854     }
855     catch(...)
856     {
857       throw;
858     }
859   }
860 
861   #ifdef _WIN32
862   if (options.EMailMode)
863   {
864     NDLL::CLibrary mapiLib;
865     if (!mapiLib.Load(TEXT("Mapi32.dll")))
866     {
867       errorInfo.SystemError = ::GetLastError();
868       errorInfo.Message = L"can not load Mapi32.dll";
869       return E_FAIL;
870     }
871     MY_LPMAPISENDDOCUMENTS fnSend = (MY_LPMAPISENDDOCUMENTS)
872         mapiLib.GetProcAddress("MAPISendDocuments");
873     if (fnSend == 0)
874     {
875       errorInfo.SystemError = ::GetLastError();
876       errorInfo.Message = L"can not find MAPISendDocuments function";
877       return E_FAIL;
878     }
879     UStringVector fullPaths;
880     int i;
881     for(i = 0; i < options.Commands.Size(); i++)
882     {
883       CArchivePath &ap = options.Commands[i].ArchivePath;
884       UString arcPath;
885       if (!NFile::NDirectory::MyGetFullPathName(ap.GetFinalPath(), arcPath))
886       {
887         errorInfo.SystemError = ::GetLastError();
888         return E_FAIL;
889       }
890       fullPaths.Add(arcPath);
891     }
892     CCurrentDirRestorer curDirRestorer;
893     for(i = 0; i < fullPaths.Size(); i++)
894     {
895       UString arcPath = fullPaths[i];
896       UString fileName = ExtractFileNameFromPath(arcPath);
897       AString path = GetAnsiString(arcPath);
898       AString name = GetAnsiString(fileName);
899       // Warning!!! MAPISendDocuments function changes Current directory
900       fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
901     }
902   }
903   #endif
904   return S_OK;
905 }
906 
907