Home | History | Annotate | Download | only in Common
      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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:
    613   CCurrentDirRestorer() { NFile::NDirectory::MyGetCurrentDirectory(_path); }
    614   ~CCurrentDirRestorer() { RestoreDirectory();}
    615   bool RestoreDirectory() { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(_path)); }
    616 };
    617 #endif
    618 
    619 struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback
    620 {
    621   IUpdateCallbackUI2 *Callback;
    622   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 
    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