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