Home | History | Annotate | Download | only in Common
      1 // UpdateCallback.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #ifndef _7ZIP_ST
      6 #include "../../../Windows/Synchronization.h"
      7 #endif
      8 
      9 #include "../../../Common/ComTry.h"
     10 #include "../../../Common/IntToString.h"
     11 #include "../../../Common/StringConvert.h"
     12 #include "../../../Common/Wildcard.h"
     13 
     14 #include "../../../Windows/FileDir.h"
     15 #include "../../../Windows/FileName.h"
     16 #include "../../../Windows/PropVariant.h"
     17 
     18 #include "../../Common/StreamObjects.h"
     19 
     20 #include "UpdateCallback.h"
     21 
     22 #if defined(_WIN32) && !defined(UNDER_CE)
     23 #define _USE_SECURITY_CODE
     24 #include "../../../Windows/SecurityUtils.h"
     25 #endif
     26 
     27 using namespace NWindows;
     28 using namespace NFile;
     29 
     30 #ifndef _7ZIP_ST
     31 static NSynchronization::CCriticalSection g_CriticalSection;
     32 #define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection);
     33 #else
     34 #define MT_LOCK
     35 #endif
     36 
     37 
     38 #ifdef _USE_SECURITY_CODE
     39 bool InitLocalPrivileges();
     40 #endif
     41 
     42 CArchiveUpdateCallback::CArchiveUpdateCallback():
     43     _hardIndex_From((UInt32)(Int32)-1),
     44 
     45     Callback(NULL),
     46 
     47     DirItems(NULL),
     48     ParentDirItem(NULL),
     49 
     50     Arc(NULL),
     51     ArcItems(NULL),
     52     UpdatePairs(NULL),
     53     NewNames(NULL),
     54     CommentIndex(-1),
     55     Comment(NULL),
     56 
     57     ShareForWrite(false),
     58     StopAfterOpenError(false),
     59     StdInMode(false),
     60 
     61     KeepOriginalItemNames(false),
     62     StoreNtSecurity(false),
     63     StoreHardLinks(false),
     64     StoreSymLinks(false),
     65 
     66     ProcessedItemsStatuses(NULL)
     67 {
     68   #ifdef _USE_SECURITY_CODE
     69   _saclEnabled = InitLocalPrivileges();
     70   #endif
     71 }
     72 
     73 
     74 STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size)
     75 {
     76   COM_TRY_BEGIN
     77   return Callback->SetTotal(size);
     78   COM_TRY_END
     79 }
     80 
     81 STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue)
     82 {
     83   COM_TRY_BEGIN
     84   return Callback->SetCompleted(completeValue);
     85   COM_TRY_END
     86 }
     87 
     88 STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
     89 {
     90   COM_TRY_BEGIN
     91   return Callback->SetRatioInfo(inSize, outSize);
     92   COM_TRY_END
     93 }
     94 
     95 
     96 /*
     97 static const CStatProp kProps[] =
     98 {
     99   { NULL, kpidPath, VT_BSTR},
    100   { NULL, kpidIsDir, VT_BOOL},
    101   { NULL, kpidSize, VT_UI8},
    102   { NULL, kpidCTime, VT_FILETIME},
    103   { NULL, kpidATime, VT_FILETIME},
    104   { NULL, kpidMTime, VT_FILETIME},
    105   { NULL, kpidAttrib, VT_UI4},
    106   { NULL, kpidIsAnti, VT_BOOL}
    107 };
    108 
    109 STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **)
    110 {
    111   return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator);
    112 }
    113 */
    114 
    115 STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index,
    116       Int32 *newData, Int32 *newProps, UInt32 *indexInArchive)
    117 {
    118   COM_TRY_BEGIN
    119   RINOK(Callback->CheckBreak());
    120   const CUpdatePair2 &up = (*UpdatePairs)[index];
    121   if (newData) *newData = BoolToInt(up.NewData);
    122   if (newProps) *newProps = BoolToInt(up.NewProps);
    123   if (indexInArchive)
    124   {
    125     *indexInArchive = (UInt32)(Int32)-1;
    126     if (up.ExistInArchive())
    127       *indexInArchive = (ArcItems == 0) ? up.ArcIndex : (*ArcItems)[up.ArcIndex].IndexInServer;
    128   }
    129   return S_OK;
    130   COM_TRY_END
    131 }
    132 
    133 STDMETHODIMP CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *value)
    134 {
    135   NCOM::CPropVariant prop;
    136   switch (propID)
    137   {
    138     case kpidIsDir:  prop = true; break;
    139     case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->Attrib; break;
    140     case kpidCTime:  if (ParentDirItem) prop = ParentDirItem->CTime; break;
    141     case kpidATime:  if (ParentDirItem) prop = ParentDirItem->ATime; break;
    142     case kpidMTime:  if (ParentDirItem) prop = ParentDirItem->MTime; break;
    143   }
    144   prop.Detach(value);
    145   return S_OK;
    146 }
    147 
    148 STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType)
    149 {
    150   *parentType = NParentType::kDir;
    151   *parent = (UInt32)(Int32)-1;
    152   return S_OK;
    153 }
    154 
    155 STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps)
    156 {
    157   *numProps = 0;
    158   if (StoreNtSecurity)
    159     *numProps = 1;
    160   return S_OK;
    161 }
    162 
    163 STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID)
    164 {
    165   *name = NULL;
    166   *propID = kpidNtSecure;
    167   return S_OK;
    168 }
    169 
    170 STDMETHODIMP CArchiveUpdateCallback::GetRootRawProp(PROPID
    171     #ifdef _USE_SECURITY_CODE
    172     propID
    173     #endif
    174     , const void **data, UInt32 *dataSize, UInt32 *propType)
    175 {
    176   *data = 0;
    177   *dataSize = 0;
    178   *propType = 0;
    179   if (!StoreNtSecurity)
    180     return S_OK;
    181   #ifdef _USE_SECURITY_CODE
    182   if (propID == kpidNtSecure)
    183   {
    184     if (StdInMode)
    185       return S_OK;
    186 
    187     if (ParentDirItem)
    188     {
    189       if (ParentDirItem->SecureIndex < 0)
    190         return S_OK;
    191       const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[ParentDirItem->SecureIndex];
    192       *data = buf;
    193       *dataSize = (UInt32)buf.Size();
    194       *propType = NPropDataType::kRaw;
    195       return S_OK;
    196     }
    197 
    198     if (Arc && Arc->GetRootProps)
    199       return Arc->GetRootProps->GetRootRawProp(propID, data, dataSize, propType);
    200   }
    201   #endif
    202   return S_OK;
    203 }
    204 
    205 //    #ifdef _USE_SECURITY_CODE
    206 //    #endif
    207 
    208 STDMETHODIMP CArchiveUpdateCallback::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
    209 {
    210   *data = 0;
    211   *dataSize = 0;
    212   *propType = 0;
    213 
    214   if (propID == kpidNtSecure ||
    215       propID == kpidNtReparse)
    216   {
    217     if (StdInMode)
    218       return S_OK;
    219 
    220     const CUpdatePair2 &up = (*UpdatePairs)[index];
    221     if (up.UseArcProps && up.ExistInArchive() && Arc->GetRawProps)
    222       return Arc->GetRawProps->GetRawProp(
    223           ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex,
    224           propID, data, dataSize, propType);
    225     {
    226       /*
    227       if (!up.NewData)
    228         return E_FAIL;
    229       */
    230       if (up.IsAnti)
    231         return S_OK;
    232 
    233       #ifndef UNDER_CE
    234       const CDirItem &di = DirItems->Items[up.DirIndex];
    235       #endif
    236 
    237       #ifdef _USE_SECURITY_CODE
    238       if (propID == kpidNtSecure)
    239       {
    240         if (!StoreNtSecurity)
    241           return S_OK;
    242         if (di.SecureIndex < 0)
    243           return S_OK;
    244         const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[di.SecureIndex];
    245         *data = buf;
    246         *dataSize = (UInt32)buf.Size();
    247         *propType = NPropDataType::kRaw;
    248       }
    249       else
    250       #endif
    251       {
    252         // propID == kpidNtReparse
    253         if (!StoreSymLinks)
    254           return S_OK;
    255         #ifndef UNDER_CE
    256         const CByteBuffer *buf = &di.ReparseData2;
    257         if (buf->Size() == 0)
    258           buf = &di.ReparseData;
    259         if (buf->Size() != 0)
    260         {
    261           *data = *buf;
    262           *dataSize = (UInt32)buf->Size();
    263           *propType = NPropDataType::kRaw;
    264         }
    265         #endif
    266       }
    267 
    268       return S_OK;
    269     }
    270   }
    271 
    272   return S_OK;
    273 }
    274 
    275 #ifndef UNDER_CE
    276 
    277 static UString GetRelativePath(const UString &to, const UString &from)
    278 {
    279   UStringVector partsTo, partsFrom;
    280   SplitPathToParts(to, partsTo);
    281   SplitPathToParts(from, partsFrom);
    282 
    283   unsigned i;
    284   for (i = 0;; i++)
    285   {
    286     if (i + 1 >= partsFrom.Size() ||
    287         i + 1 >= partsTo.Size())
    288       break;
    289     if (CompareFileNames(partsFrom[i], partsTo[i]) != 0)
    290       break;
    291   }
    292 
    293   if (i == 0)
    294   {
    295     #ifdef _WIN32
    296     if (NName::IsDrivePath(to) ||
    297         NName::IsDrivePath(from))
    298       return to;
    299     #endif
    300   }
    301 
    302   UString s;
    303   unsigned k;
    304 
    305   for (k = i + 1; k < partsFrom.Size(); k++)
    306     s += ".." STRING_PATH_SEPARATOR;
    307 
    308   for (k = i; k < partsTo.Size(); k++)
    309   {
    310     if (k != i)
    311       s.Add_PathSepar();
    312     s += partsTo[k];
    313   }
    314 
    315   return s;
    316 }
    317 
    318 #endif
    319 
    320 STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
    321 {
    322   COM_TRY_BEGIN
    323   const CUpdatePair2 &up = (*UpdatePairs)[index];
    324   NCOM::CPropVariant prop;
    325 
    326   if (up.NewData)
    327   {
    328     /*
    329     if (propID == kpidIsHardLink)
    330     {
    331       prop = _isHardLink;
    332       prop.Detach(value);
    333       return S_OK;
    334     }
    335     */
    336     if (propID == kpidSymLink)
    337     {
    338       if (index == _hardIndex_From)
    339       {
    340         prop.Detach(value);
    341         return S_OK;
    342       }
    343       if (up.DirIndex >= 0)
    344       {
    345         #ifndef UNDER_CE
    346         const CDirItem &di = DirItems->Items[up.DirIndex];
    347         // if (di.IsDir())
    348         {
    349           CReparseAttr attr;
    350           DWORD errorCode = 0;
    351           if (attr.Parse(di.ReparseData, di.ReparseData.Size(), errorCode))
    352           {
    353             UString simpleName = attr.GetPath();
    354             if (attr.IsRelative())
    355               prop = simpleName;
    356             else
    357             {
    358               const FString phyPath = DirItems->GetPhyPath(up.DirIndex);
    359               FString fullPath;
    360               if (NDir::MyGetFullPathName(phyPath, fullPath))
    361               {
    362                 prop = GetRelativePath(simpleName, fs2us(fullPath));
    363               }
    364             }
    365             prop.Detach(value);
    366             return S_OK;
    367           }
    368         }
    369         #endif
    370       }
    371     }
    372     else if (propID == kpidHardLink)
    373     {
    374       if (index == _hardIndex_From)
    375       {
    376         const CKeyKeyValPair &pair = _map[_hardIndex_To];
    377         const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value];
    378         prop = DirItems->GetLogPath(up2.DirIndex);
    379         prop.Detach(value);
    380         return S_OK;
    381       }
    382       if (up.DirIndex >= 0)
    383       {
    384         prop.Detach(value);
    385         return S_OK;
    386       }
    387     }
    388   }
    389 
    390   if (up.IsAnti
    391       && propID != kpidIsDir
    392       && propID != kpidPath
    393       && propID != kpidIsAltStream)
    394   {
    395     switch (propID)
    396     {
    397       case kpidSize:  prop = (UInt64)0; break;
    398       case kpidIsAnti:  prop = true; break;
    399     }
    400   }
    401   else if (propID == kpidPath && up.NewNameIndex >= 0)
    402     prop = (*NewNames)[up.NewNameIndex];
    403   else if (propID == kpidComment
    404       && CommentIndex >= 0
    405       && (unsigned)CommentIndex == index
    406       && Comment)
    407     prop = *Comment;
    408   else if (propID == kpidShortName && up.NewNameIndex >= 0 && up.IsMainRenameItem)
    409   {
    410     // we can generate new ShortName here;
    411   }
    412   else if ((up.UseArcProps || (KeepOriginalItemNames && (propID == kpidPath || propID == kpidIsAltStream)))
    413       && up.ExistInArchive() && Archive)
    414     return Archive->GetProperty(ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, value);
    415   else if (up.ExistOnDisk())
    416   {
    417     const CDirItem &di = DirItems->Items[up.DirIndex];
    418     switch (propID)
    419     {
    420       case kpidPath:  prop = DirItems->GetLogPath(up.DirIndex); break;
    421       case kpidIsDir:  prop = di.IsDir(); break;
    422       case kpidSize:  prop = di.IsDir() ? (UInt64)0 : di.Size; break;
    423       case kpidAttrib:  prop = di.Attrib; break;
    424       case kpidCTime:  prop = di.CTime; break;
    425       case kpidATime:  prop = di.ATime; break;
    426       case kpidMTime:  prop = di.MTime; break;
    427       case kpidIsAltStream:  prop = di.IsAltStream; break;
    428       #if defined(_WIN32) && !defined(UNDER_CE)
    429       // case kpidShortName:  prop = di.ShortName; break;
    430       #endif
    431     }
    432   }
    433   prop.Detach(value);
    434   return S_OK;
    435   COM_TRY_END
    436 }
    437 
    438 #ifndef _7ZIP_ST
    439 static NSynchronization::CCriticalSection CS;
    440 #endif
    441 
    442 STDMETHODIMP CArchiveUpdateCallback::GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 mode)
    443 {
    444   COM_TRY_BEGIN
    445   *inStream = NULL;
    446   const CUpdatePair2 &up = (*UpdatePairs)[index];
    447   if (!up.NewData)
    448     return E_FAIL;
    449 
    450   RINOK(Callback->CheckBreak());
    451   // RINOK(Callback->Finalize());
    452 
    453   bool isDir = IsDir(up);
    454 
    455   if (up.IsAnti)
    456   {
    457     UString name;
    458     if (up.ArcIndex >= 0)
    459       name = (*ArcItems)[up.ArcIndex].Name;
    460     else if (up.DirIndex >= 0)
    461       name = DirItems->GetLogPath(up.DirIndex);
    462     RINOK(Callback->GetStream(name, isDir, true, mode));
    463 
    464     /* 9.33: fixed. Handlers expect real stream object for files, even for anti-file.
    465        so we return empty stream */
    466 
    467     if (!isDir)
    468     {
    469       CBufInStream *inStreamSpec = new CBufInStream();
    470       CMyComPtr<ISequentialInStream> inStreamLoc = inStreamSpec;
    471       inStreamSpec->Init(NULL, 0);
    472       *inStream = inStreamLoc.Detach();
    473     }
    474     return S_OK;
    475   }
    476 
    477   RINOK(Callback->GetStream(DirItems->GetLogPath(up.DirIndex), isDir, false, mode));
    478 
    479   if (isDir)
    480     return S_OK;
    481 
    482   if (StdInMode)
    483   {
    484     if (mode != NUpdateNotifyOp::kAdd &&
    485         mode != NUpdateNotifyOp::kUpdate)
    486       return S_OK;
    487 
    488     CStdInFileStream *inStreamSpec = new CStdInFileStream;
    489     CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
    490     *inStream = inStreamLoc.Detach();
    491   }
    492   else
    493   {
    494     CInFileStream *inStreamSpec = new CInFileStream;
    495     CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
    496 
    497     inStreamSpec->SupportHardLinks = StoreHardLinks;
    498     inStreamSpec->Callback = this;
    499     inStreamSpec->CallbackRef = index;
    500 
    501     const FString path = DirItems->GetPhyPath(up.DirIndex);
    502     _openFiles_Indexes.Add(index);
    503     _openFiles_Paths.Add(path);
    504 
    505     #if defined(_WIN32) && !defined(UNDER_CE)
    506     if (DirItems->Items[up.DirIndex].AreReparseData())
    507     {
    508       if (!inStreamSpec->File.OpenReparse(path))
    509       {
    510         return Callback->OpenFileError(path, ::GetLastError());
    511       }
    512     }
    513     else
    514     #endif
    515     if (!inStreamSpec->OpenShared(path, ShareForWrite))
    516     {
    517       DWORD error = ::GetLastError();
    518       HRESULT hres = Callback->OpenFileError(path, error);
    519       if (StopAfterOpenError)
    520         if (hres == S_OK || hres == S_FALSE)
    521           return HRESULT_FROM_WIN32(error);
    522       return hres;
    523     }
    524 
    525     if (StoreHardLinks)
    526     {
    527       CStreamFileProps props;
    528       if (inStreamSpec->GetProps2(&props) == S_OK)
    529       {
    530         if (props.NumLinks > 1)
    531         {
    532           CKeyKeyValPair pair;
    533           pair.Key1 = props.VolID;
    534           pair.Key2 = props.FileID_Low;
    535           pair.Value = index;
    536           unsigned numItems = _map.Size();
    537           unsigned pairIndex = _map.AddToUniqueSorted2(pair);
    538           if (numItems == _map.Size())
    539           {
    540             // const CKeyKeyValPair &pair2 = _map.Pairs[pairIndex];
    541             _hardIndex_From = index;
    542             _hardIndex_To = pairIndex;
    543             // we could return NULL as stream, but it's better to return real stream
    544             // return S_OK;
    545           }
    546         }
    547       }
    548     }
    549 
    550     if (ProcessedItemsStatuses)
    551     {
    552       #ifndef _7ZIP_ST
    553       NSynchronization::CCriticalSectionLock lock(CS);
    554       #endif
    555       ProcessedItemsStatuses[(unsigned)up.DirIndex] = 1;
    556     }
    557     *inStream = inStreamLoc.Detach();
    558   }
    559 
    560   return S_OK;
    561   COM_TRY_END
    562 }
    563 
    564 STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 opRes)
    565 {
    566   COM_TRY_BEGIN
    567   return Callback->SetOperationResult(opRes);
    568   COM_TRY_END
    569 }
    570 
    571 STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
    572 {
    573   COM_TRY_BEGIN
    574   return GetStream2(index, inStream,
    575       (*UpdatePairs)[index].ArcIndex < 0 ?
    576           NUpdateNotifyOp::kAdd :
    577           NUpdateNotifyOp::kUpdate);
    578   COM_TRY_END
    579 }
    580 
    581 STDMETHODIMP CArchiveUpdateCallback::ReportOperation(UInt32 indexType, UInt32 index, UInt32 op)
    582 {
    583   COM_TRY_BEGIN
    584 
    585   bool isDir = false;
    586 
    587   if (indexType == NArchive::NEventIndexType::kOutArcIndex)
    588   {
    589     UString name;
    590     if (index != (UInt32)(Int32)-1)
    591     {
    592       const CUpdatePair2 &up = (*UpdatePairs)[index];
    593       if (up.ExistOnDisk())
    594       {
    595         name = DirItems->GetLogPath(up.DirIndex);
    596         isDir = DirItems->Items[up.DirIndex].IsDir();
    597       }
    598     }
    599     return Callback->ReportUpdateOpeartion(op, name.IsEmpty() ? NULL : name.Ptr(), isDir);
    600   }
    601 
    602   wchar_t temp[16];
    603   UString s2;
    604   const wchar_t *s = NULL;
    605 
    606   if (indexType == NArchive::NEventIndexType::kInArcIndex)
    607   {
    608     if (index != (UInt32)(Int32)-1)
    609     {
    610       if (ArcItems)
    611       {
    612         const CArcItem &ai = (*ArcItems)[index];
    613         s = ai.Name;
    614         isDir = ai.IsDir;
    615       }
    616       else if (Arc)
    617       {
    618         RINOK(Arc->GetItemPath(index, s2));
    619         s = s2;
    620         RINOK(Archive_IsItem_Dir(Arc->Archive, index, isDir));
    621       }
    622     }
    623   }
    624   else if (indexType == NArchive::NEventIndexType::kBlockIndex)
    625   {
    626     temp[0] = '#';
    627     ConvertUInt32ToString(index, temp + 1);
    628     s = temp;
    629   }
    630 
    631   if (!s)
    632     s = L"";
    633 
    634   return Callback->ReportUpdateOpeartion(op, s, isDir);
    635 
    636   COM_TRY_END
    637 }
    638 
    639 STDMETHODIMP CArchiveUpdateCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes)
    640 {
    641   COM_TRY_BEGIN
    642 
    643   bool isEncrypted = false;
    644   wchar_t temp[16];
    645   UString s2;
    646   const wchar_t *s = NULL;
    647 
    648   if (indexType == NArchive::NEventIndexType::kOutArcIndex)
    649   {
    650     /*
    651     UString name;
    652     if (index != (UInt32)(Int32)-1)
    653     {
    654       const CUpdatePair2 &up = (*UpdatePairs)[index];
    655       if (up.ExistOnDisk())
    656       {
    657         s2 = DirItems->GetLogPath(up.DirIndex);
    658         s = s2;
    659       }
    660     }
    661     */
    662     return E_FAIL;
    663   }
    664 
    665   if (indexType == NArchive::NEventIndexType::kInArcIndex)
    666   {
    667     if (index != (UInt32)(Int32)-1)
    668     {
    669       if (ArcItems)
    670         s = (*ArcItems)[index].Name;
    671       else if (Arc)
    672       {
    673         RINOK(Arc->GetItemPath(index, s2));
    674         s = s2;
    675       }
    676       if (Archive)
    677       {
    678         RINOK(Archive_GetItemBoolProp(Archive, index, kpidEncrypted, isEncrypted));
    679       }
    680     }
    681   }
    682   else if (indexType == NArchive::NEventIndexType::kBlockIndex)
    683   {
    684     temp[0] = '#';
    685     ConvertUInt32ToString(index, temp + 1);
    686     s = temp;
    687   }
    688 
    689   return Callback->ReportExtractResult(opRes, BoolToInt(isEncrypted), s);
    690 
    691   COM_TRY_END
    692 }
    693 
    694 STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
    695 {
    696   if (VolumesSizes.Size() == 0)
    697     return S_FALSE;
    698   if (index >= (UInt32)VolumesSizes.Size())
    699     index = VolumesSizes.Size() - 1;
    700   *size = VolumesSizes[index];
    701   return S_OK;
    702 }
    703 
    704 STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
    705 {
    706   COM_TRY_BEGIN
    707   char temp[16];
    708   ConvertUInt32ToString(index + 1, temp);
    709   FString res (temp);
    710   while (res.Len() < 2)
    711     res.InsertAtFront(FTEXT('0'));
    712   FString fileName = VolName;
    713   fileName += '.';
    714   fileName += res;
    715   fileName += VolExt;
    716   COutFileStream *streamSpec = new COutFileStream;
    717   CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
    718   if (!streamSpec->Create(fileName, false))
    719     return ::GetLastError();
    720   *volumeStream = streamLoc.Detach();
    721   return S_OK;
    722   COM_TRY_END
    723 }
    724 
    725 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
    726 {
    727   COM_TRY_BEGIN
    728   return Callback->CryptoGetTextPassword2(passwordIsDefined, password);
    729   COM_TRY_END
    730 }
    731 
    732 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password)
    733 {
    734   COM_TRY_BEGIN
    735   return Callback->CryptoGetTextPassword(password);
    736   COM_TRY_END
    737 }
    738 
    739 HRESULT CArchiveUpdateCallback::InFileStream_On_Error(UINT_PTR val, DWORD error)
    740 {
    741   if (error == ERROR_LOCK_VIOLATION)
    742   {
    743     MT_LOCK
    744     UInt32 index = (UInt32)val;
    745     FOR_VECTOR(i, _openFiles_Indexes)
    746     {
    747       if (_openFiles_Indexes[i] == index)
    748       {
    749         RINOK(Callback->ReadingFileError(_openFiles_Paths[i], error));
    750         break;
    751       }
    752     }
    753   }
    754   return HRESULT_FROM_WIN32(error);
    755 }
    756 
    757 void CArchiveUpdateCallback::InFileStream_On_Destroy(UINT_PTR val)
    758 {
    759   MT_LOCK
    760   UInt32 index = (UInt32)val;
    761   FOR_VECTOR(i, _openFiles_Indexes)
    762   {
    763     if (_openFiles_Indexes[i] == index)
    764     {
    765       _openFiles_Indexes.Delete(i);
    766       _openFiles_Paths.Delete(i);
    767       return;
    768     }
    769   }
    770   throw 20141125;
    771 }
    772