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