• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)(UInt64 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(UInt64 newSize)207 STDMETHODIMP COutMultiVolStream::SetSize(UInt64 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   if (!stdOutMode)
391   {
392     UString resultPath;
393     int pos;
394     if (!NFile::NDirectory::MyGetFullPathName(archivePath.GetFinalPath(), resultPath, pos))
395       throw 1417161;
396     NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos));
397   }
398 
399   COutFileStream *outStreamSpec = NULL;
400   COutMultiVolStream *volStreamSpec = NULL;
401 
402   if (volumesSizes.Size() == 0)
403   {
404     if (stdOutMode)
405       outStream = new CStdOutFileStream;
406     else
407     {
408       outStreamSpec = new COutFileStream;
409       outStream = outStreamSpec;
410       bool isOK = false;
411       UString realPath;
412       for (int i = 0; i < (1 << 16); i++)
413       {
414         if (archivePath.Temp)
415         {
416           if (i > 0)
417           {
418             wchar_t s[16];
419             ConvertUInt32ToString(i, s);
420             archivePath.TempPostfix = s;
421           }
422           realPath = archivePath.GetTempPath();
423         }
424         else
425           realPath = archivePath.GetFinalPath();
426         if (outStreamSpec->Create(realPath, false))
427         {
428           tempFiles.Paths.Add(realPath);
429           isOK = true;
430           break;
431         }
432         if (::GetLastError() != ERROR_FILE_EXISTS)
433           break;
434         if (!archivePath.Temp)
435           break;
436       }
437       if (!isOK)
438       {
439         errorInfo.SystemError = ::GetLastError();
440         errorInfo.FileName = realPath;
441         errorInfo.Message = L"7-Zip cannot open file";
442         return E_FAIL;
443       }
444     }
445   }
446   else
447   {
448     if (stdOutMode)
449       return E_FAIL;
450     volStreamSpec = new COutMultiVolStream;
451     outStream = volStreamSpec;
452     volStreamSpec->Sizes = volumesSizes;
453     volStreamSpec->Prefix = archivePath.GetFinalPath() + UString(L".");
454     volStreamSpec->TempFiles = &tempFiles;
455     volStreamSpec->Init();
456 
457     /*
458     updateCallbackSpec->VolumesSizes = volumesSizes;
459     updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
460     if (!archivePath.VolExtension.IsEmpty())
461       updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension;
462     */
463   }
464 
465   RINOK(SetProperties(outArchive, compressionMethod.Properties));
466 
467   if (sfxMode)
468   {
469     CInFileStream *sfxStreamSpec = new CInFileStream;
470     CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
471     if (!sfxStreamSpec->Open(sfxModule))
472     {
473       errorInfo.SystemError = ::GetLastError();
474       errorInfo.Message = L"7-Zip cannot open SFX module";
475       errorInfo.FileName = sfxModule;
476       return E_FAIL;
477     }
478 
479     CMyComPtr<ISequentialOutStream> sfxOutStream;
480     COutFileStream *outStreamSpec = NULL;
481     if (volumesSizes.Size() == 0)
482       sfxOutStream = outStream;
483     else
484     {
485       outStreamSpec = new COutFileStream;
486       sfxOutStream = outStreamSpec;
487       UString realPath = archivePath.GetFinalPath();
488       if (!outStreamSpec->Create(realPath, false))
489       {
490         errorInfo.SystemError = ::GetLastError();
491         errorInfo.FileName = realPath;
492         errorInfo.Message = L"7-Zip cannot open file";
493         return E_FAIL;
494       }
495     }
496     RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL));
497     if (outStreamSpec)
498     {
499       RINOK(outStreamSpec->Close());
500     }
501   }
502 
503   HRESULT result = outArchive->UpdateItems(outStream, updatePairs2.Size(), updateCallback);
504   callback->Finilize();
505   RINOK(result);
506   if (outStreamSpec)
507     result = outStreamSpec->Close();
508   else if (volStreamSpec)
509     result = volStreamSpec->Close();
510   return result;
511 }
512 
EnumerateInArchiveItems(const NWildcard::CCensor & censor,const CArc & arc,CObjectVector<CArcItem> & arcItems)513 HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor,
514     const CArc &arc,
515     CObjectVector<CArcItem> &arcItems)
516 {
517   arcItems.Clear();
518   UInt32 numItems;
519   IInArchive *archive = arc.Archive;
520   RINOK(archive->GetNumberOfItems(&numItems));
521   arcItems.Reserve(numItems);
522   for (UInt32 i = 0; i < numItems; i++)
523   {
524     CArcItem ai;
525 
526     RINOK(arc.GetItemPath(i, ai.Name));
527     RINOK(IsArchiveItemFolder(archive, i, ai.IsDir));
528     ai.Censored = censor.CheckPath(ai.Name, !ai.IsDir);
529     RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined));
530 
531     {
532       CPropVariant prop;
533       RINOK(archive->GetProperty(i, kpidSize, &prop));
534       ai.SizeDefined = (prop.vt != VT_EMPTY);
535       if (ai.SizeDefined)
536         ai.Size = ConvertPropVariantToUInt64(prop);
537     }
538 
539     {
540       CPropVariant prop;
541       RINOK(archive->GetProperty(i, kpidTimeType, &prop));
542       if (prop.vt == VT_UI4)
543       {
544         ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal;
545         switch(ai.TimeType)
546         {
547           case NFileTimeType::kWindows:
548           case NFileTimeType::kUnix:
549           case NFileTimeType::kDOS:
550             break;
551           default:
552             return E_FAIL;
553         }
554       }
555     }
556 
557     ai.IndexInServer = i;
558     arcItems.Add(ai);
559   }
560   return S_OK;
561 }
562 
563 
UpdateWithItemLists(CCodecs * codecs,CUpdateOptions & options,IInArchive * archive,const CObjectVector<CArcItem> & arcItems,CDirItems & dirItems,CTempFiles & tempFiles,CUpdateErrorInfo & errorInfo,IUpdateCallbackUI2 * callback)564 static HRESULT UpdateWithItemLists(
565     CCodecs *codecs,
566     CUpdateOptions &options,
567     IInArchive *archive,
568     const CObjectVector<CArcItem> &arcItems,
569     CDirItems &dirItems,
570     CTempFiles &tempFiles,
571     CUpdateErrorInfo &errorInfo,
572     IUpdateCallbackUI2 *callback)
573 {
574   for(int i = 0; i < options.Commands.Size(); i++)
575   {
576     CUpdateArchiveCommand &command = options.Commands[i];
577     if (options.StdOutMode)
578     {
579       RINOK(callback->StartArchive(L"stdout", archive != 0));
580     }
581     else
582     {
583       RINOK(callback->StartArchive(command.ArchivePath.GetFinalPath(),
584           i == 0 && options.UpdateArchiveItself && archive != 0));
585     }
586 
587     RINOK(Compress(
588         codecs,
589         command.ActionSet, archive,
590         options.MethodMode,
591         command.ArchivePath,
592         arcItems,
593         options.OpenShareForWrite,
594         options.StdInMode,
595         /* options.StdInFileName, */
596         options.StdOutMode,
597         dirItems,
598         options.SfxMode, options.SfxModule,
599         options.VolumesSizes,
600         tempFiles,
601         errorInfo, callback));
602 
603     RINOK(callback->FinishArchive());
604   }
605   return S_OK;
606 }
607 
608 #if defined(_WIN32) && !defined(UNDER_CE)
609 class CCurrentDirRestorer
610 {
611   UString _path;
612 public:
CCurrentDirRestorer()613   CCurrentDirRestorer() { NFile::NDirectory::MyGetCurrentDirectory(_path); }
~CCurrentDirRestorer()614   ~CCurrentDirRestorer() { RestoreDirectory();}
RestoreDirectory()615   bool RestoreDirectory() { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(_path)); }
616 };
617 #endif
618 
619 struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback
620 {
621   IUpdateCallbackUI2 *Callback;
ScanProgressCEnumDirItemUpdateCallback622   HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, const wchar_t *path)
623   {
624     return Callback->ScanProgress(numFolders, numFiles, path);
625   }
626 };
627 
628 #ifdef _WIN32
629 typedef ULONG (FAR PASCAL MY_MAPISENDDOCUMENTS)(
630   ULONG_PTR ulUIParam,
631   LPSTR lpszDelimChar,
632   LPSTR lpszFilePaths,
633   LPSTR lpszFileNames,
634   ULONG ulReserved
635 );
636 typedef MY_MAPISENDDOCUMENTS FAR *MY_LPMAPISENDDOCUMENTS;
637 #endif
638 
UpdateArchive(CCodecs * codecs,const NWildcard::CCensor & censor,CUpdateOptions & options,CUpdateErrorInfo & errorInfo,IOpenCallbackUI * openCallback,IUpdateCallbackUI2 * callback)639 HRESULT UpdateArchive(
640     CCodecs *codecs,
641     const NWildcard::CCensor &censor,
642     CUpdateOptions &options,
643     CUpdateErrorInfo &errorInfo,
644     IOpenCallbackUI *openCallback,
645     IUpdateCallbackUI2 *callback)
646 {
647   if (options.StdOutMode && options.EMailMode)
648     return E_FAIL;
649 
650   if (options.VolumesSizes.Size() > 0 && (options.EMailMode || options.SfxMode))
651     return E_NOTIMPL;
652 
653   if (options.SfxMode)
654   {
655     CProperty property;
656     property.Name = L"rsfx";
657     property.Value = L"on";
658     options.MethodMode.Properties.Add(property);
659     if (options.SfxModule.IsEmpty())
660     {
661       errorInfo.Message = L"SFX file is not specified";
662       return E_FAIL;
663     }
664     UString name = options.SfxModule;
665     #ifdef UNDER_CE
666     if (!NFind::DoesFileExist(name))
667     #else
668     if (!NDirectory::MySearchPath(NULL, name, NULL, options.SfxModule))
669     #endif
670     {
671       errorInfo.SystemError = ::GetLastError();
672       errorInfo.Message = L"7-Zip cannot find specified SFX module";
673       errorInfo.FileName = name;
674       return E_FAIL;
675     }
676   }
677 
678 
679   CArchiveLink arcLink;
680   const UString arcPath = options.ArchivePath.GetFinalPath();
681 
682   if (!options.ArchivePath.OriginalPath.IsEmpty())
683   {
684     NFind::CFileInfoW fi;
685     if (fi.Find(arcPath))
686     {
687       if (fi.IsDir())
688         throw "there is no such archive";
689       if (options.VolumesSizes.Size() > 0)
690         return E_NOTIMPL;
691       CIntVector formatIndices;
692       if (options.MethodMode.FormatIndex >= 0)
693         formatIndices.Add(options.MethodMode.FormatIndex);
694       HRESULT result = arcLink.Open2(codecs, formatIndices, false, NULL, arcPath, openCallback);
695       if (result == E_ABORT)
696         return result;
697       RINOK(callback->OpenResult(arcPath, result));
698       RINOK(result);
699       if (arcLink.VolumePaths.Size() > 1)
700       {
701         errorInfo.SystemError = (DWORD)E_NOTIMPL;
702         errorInfo.Message = L"Updating for multivolume archives is not implemented";
703         return E_NOTIMPL;
704       }
705 
706       CArc &arc = arcLink.Arcs.Back();
707       arc.MTimeDefined = !fi.IsDevice;
708       arc.MTime = fi.MTime;
709     }
710   }
711   else
712   {
713     /*
714     if (archiveType.IsEmpty())
715       throw "type of archive is not specified";
716     */
717   }
718 
719   CDirItems dirItems;
720   if (options.StdInMode)
721   {
722     CDirItem di;
723     di.Name = options.StdInFileName;
724     di.Size = (UInt64)(Int64)-1;
725     di.Attrib = 0;
726     NTime::GetCurUtcFileTime(di.MTime);
727     di.CTime = di.ATime = di.MTime;
728     dirItems.Items.Add(di);
729   }
730   else
731   {
732     bool needScanning = false;
733     for(int i = 0; i < options.Commands.Size(); i++)
734       if (options.Commands[i].ActionSet.NeedScanning())
735         needScanning = true;
736     if (needScanning)
737     {
738       CEnumDirItemUpdateCallback enumCallback;
739       enumCallback.Callback = callback;
740       RINOK(callback->StartScanning());
741       UStringVector errorPaths;
742       CRecordVector<DWORD> errorCodes;
743       HRESULT res = EnumerateItems(censor, dirItems, &enumCallback, errorPaths, errorCodes);
744       for (int i = 0; i < errorPaths.Size(); i++)
745       {
746         RINOK(callback->CanNotFindError(errorPaths[i], errorCodes[i]));
747       }
748       if (res != S_OK)
749       {
750         if (res != E_ABORT)
751           errorInfo.Message = L"Scanning error";
752         return res;
753       }
754       RINOK(callback->FinishScanning());
755     }
756   }
757 
758   UString tempDirPrefix;
759   bool usesTempDir = false;
760 
761   #ifdef _WIN32
762   NDirectory::CTempDirectoryW tempDirectory;
763   if (options.EMailMode && options.EMailRemoveAfter)
764   {
765     tempDirectory.Create(kTempFolderPrefix);
766     tempDirPrefix = tempDirectory.GetPath();
767     NormalizeDirPathPrefix(tempDirPrefix);
768     usesTempDir = true;
769   }
770   #endif
771 
772   CTempFiles tempFiles;
773 
774   bool createTempFile = false;
775 
776   bool thereIsInArchive = arcLink.IsOpen;
777 
778   if (!options.StdOutMode && options.UpdateArchiveItself)
779   {
780     CArchivePath &ap = options.Commands[0].ArchivePath;
781     ap = options.ArchivePath;
782     // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
783     if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
784     {
785       createTempFile = true;
786       ap.Temp = true;
787       if (!options.WorkingDir.IsEmpty())
788       {
789         ap.TempPrefix = options.WorkingDir;
790         NormalizeDirPathPrefix(ap.TempPrefix);
791       }
792     }
793   }
794 
795   for(int i = 0; i < options.Commands.Size(); i++)
796   {
797     CArchivePath &ap = options.Commands[i].ArchivePath;
798     if (usesTempDir)
799     {
800       // Check it
801       ap.Prefix = tempDirPrefix;
802       // ap.Temp = true;
803       // ap.TempPrefix = tempDirPrefix;
804     }
805     if (!options.StdOutMode &&
806         (i > 0 || !createTempFile))
807     {
808       const UString &path = ap.GetFinalPath();
809       if (NFind::DoesFileOrDirExist(path))
810       {
811         errorInfo.SystemError = 0;
812         errorInfo.Message = L"The file already exists";
813         errorInfo.FileName = path;
814         return E_FAIL;
815       }
816     }
817   }
818 
819   CObjectVector<CArcItem> arcItems;
820   if (thereIsInArchive)
821   {
822     RINOK(EnumerateInArchiveItems(censor, arcLink.Arcs.Back(), arcItems));
823   }
824 
825   RINOK(UpdateWithItemLists(codecs, options,
826       thereIsInArchive ? arcLink.GetArchive() : 0,
827       arcItems, dirItems,
828       tempFiles, errorInfo, callback));
829 
830   if (thereIsInArchive)
831   {
832     RINOK(arcLink.Close());
833     arcLink.Release();
834   }
835 
836   tempFiles.Paths.Clear();
837   if (createTempFile)
838   {
839     try
840     {
841       CArchivePath &ap = options.Commands[0].ArchivePath;
842       const UString &tempPath = ap.GetTempPath();
843       if (thereIsInArchive)
844         if (!NDirectory::DeleteFileAlways(arcPath))
845         {
846           errorInfo.SystemError = ::GetLastError();
847           errorInfo.Message = L"7-Zip cannot delete the file";
848           errorInfo.FileName = arcPath;
849           return E_FAIL;
850         }
851       if (!NDirectory::MyMoveFile(tempPath, arcPath))
852       {
853         errorInfo.SystemError = ::GetLastError();
854         errorInfo.Message = L"7-Zip cannot move the file";
855         errorInfo.FileName = tempPath;
856         errorInfo.FileName2 = arcPath;
857         return E_FAIL;
858       }
859     }
860     catch(...)
861     {
862       throw;
863     }
864   }
865 
866   #if defined(_WIN32) && !defined(UNDER_CE)
867   if (options.EMailMode)
868   {
869     NDLL::CLibrary mapiLib;
870     if (!mapiLib.Load(TEXT("Mapi32.dll")))
871     {
872       errorInfo.SystemError = ::GetLastError();
873       errorInfo.Message = L"7-Zip cannot load Mapi32.dll";
874       return E_FAIL;
875     }
876     MY_LPMAPISENDDOCUMENTS fnSend = (MY_LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments");
877     if (fnSend == 0)
878     {
879       errorInfo.SystemError = ::GetLastError();
880       errorInfo.Message = L"7-Zip cannot find MAPISendDocuments function";
881       return E_FAIL;
882     }
883     UStringVector fullPaths;
884     int i;
885     for(i = 0; i < options.Commands.Size(); i++)
886     {
887       CArchivePath &ap = options.Commands[i].ArchivePath;
888       UString arcPath;
889       if (!NFile::NDirectory::MyGetFullPathName(ap.GetFinalPath(), arcPath))
890       {
891         errorInfo.SystemError = ::GetLastError();
892         errorInfo.Message = L"GetFullPathName error";
893         return E_FAIL;
894       }
895       fullPaths.Add(arcPath);
896     }
897     CCurrentDirRestorer curDirRestorer;
898     for(i = 0; i < fullPaths.Size(); i++)
899     {
900       UString arcPath = fullPaths[i];
901       UString fileName = ExtractFileNameFromPath(arcPath);
902       AString path = GetAnsiString(arcPath);
903       AString name = GetAnsiString(fileName);
904       // Warning!!! MAPISendDocuments function changes Current directory
905       fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
906     }
907   }
908   #endif
909   return S_OK;
910 }
911