1 // UpdateCallback.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../Common/ComTry.h" 6 #include "../../../Common/IntToString.h" 7 #include "../../../Common/StringConvert.h" 8 #include "../../../Common/Wildcard.h" 9 10 #include "../../../Windows/FileDir.h" 11 #include "../../../Windows/FileName.h" 12 #include "../../../Windows/PropVariant.h" 13 #include "../../../Windows/Synchronization.h" 14 15 #include "../../Common/FileStreams.h" 16 #include "../../Common/StreamObjects.h" 17 18 #include "UpdateCallback.h" 19 20 #if defined(_WIN32) && !defined(UNDER_CE) 21 #define _USE_SECURITY_CODE 22 #include "../../../Windows/SecurityUtils.h" 23 #endif 24 25 using namespace NWindows; 26 using namespace NFile; 27 28 #ifdef _USE_SECURITY_CODE 29 bool InitLocalPrivileges(); 30 #endif 31 32 CArchiveUpdateCallback::CArchiveUpdateCallback(): 33 Callback(0), 34 ShareForWrite(false), 35 StdInMode(false), 36 DirItems(0), 37 ArcItems(0), 38 UpdatePairs(0), 39 NewNames(0), 40 KeepOriginalItemNames(false), 41 ProcessedItemsStatuses(NULL), 42 ParentDirItem(NULL), 43 StoreNtSecurity(false), 44 StoreHardLinks(false), 45 StoreSymLinks(false), 46 _hardIndex_From((UInt32)(Int32)-1) 47 { 48 #ifdef _USE_SECURITY_CODE 49 _saclEnabled = InitLocalPrivileges(); 50 #endif 51 } 52 53 54 STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size) 55 { 56 COM_TRY_BEGIN 57 return Callback->SetTotal(size); 58 COM_TRY_END 59 } 60 61 STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue) 62 { 63 COM_TRY_BEGIN 64 return Callback->SetCompleted(completeValue); 65 COM_TRY_END 66 } 67 68 STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) 69 { 70 COM_TRY_BEGIN 71 return Callback->SetRatioInfo(inSize, outSize); 72 COM_TRY_END 73 } 74 75 76 /* 77 static const STATPROPSTG kProps[] = 78 { 79 { NULL, kpidPath, VT_BSTR}, 80 { NULL, kpidIsDir, VT_BOOL}, 81 { NULL, kpidSize, VT_UI8}, 82 { NULL, kpidCTime, VT_FILETIME}, 83 { NULL, kpidATime, VT_FILETIME}, 84 { NULL, kpidMTime, VT_FILETIME}, 85 { NULL, kpidAttrib, VT_UI4}, 86 { NULL, kpidIsAnti, VT_BOOL} 87 }; 88 89 STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **) 90 { 91 return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator); 92 } 93 */ 94 95 STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index, 96 Int32 *newData, Int32 *newProps, UInt32 *indexInArchive) 97 { 98 COM_TRY_BEGIN 99 RINOK(Callback->CheckBreak()); 100 const CUpdatePair2 &up = (*UpdatePairs)[index]; 101 if (newData) *newData = BoolToInt(up.NewData); 102 if (newProps) *newProps = BoolToInt(up.NewProps); 103 if (indexInArchive) 104 { 105 *indexInArchive = (UInt32)(Int32)-1; 106 if (up.ExistInArchive()) 107 *indexInArchive = (ArcItems == 0) ? up.ArcIndex : (*ArcItems)[up.ArcIndex].IndexInServer; 108 } 109 return S_OK; 110 COM_TRY_END 111 } 112 113 STDMETHODIMP CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *value) 114 { 115 NCOM::CPropVariant prop; 116 switch (propID) 117 { 118 case kpidIsDir: prop = true; break; 119 case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->Attrib; break; 120 case kpidCTime: if (ParentDirItem) prop = ParentDirItem->CTime; break; 121 case kpidATime: if (ParentDirItem) prop = ParentDirItem->ATime; break; 122 case kpidMTime: if (ParentDirItem) prop = ParentDirItem->MTime; break; 123 } 124 prop.Detach(value); 125 return S_OK; 126 } 127 128 STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType) 129 { 130 *parentType = NParentType::kDir; 131 *parent = (UInt32)(Int32)-1; 132 return S_OK; 133 } 134 135 STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps) 136 { 137 *numProps = 0; 138 if (StoreNtSecurity) 139 *numProps = 1; 140 return S_OK; 141 } 142 143 STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID) 144 { 145 *name = NULL; 146 *propID = kpidNtSecure; 147 return S_OK; 148 } 149 150 STDMETHODIMP CArchiveUpdateCallback::GetRootRawProp(PROPID 151 #ifdef _USE_SECURITY_CODE 152 propID 153 #endif 154 , const void **data, UInt32 *dataSize, UInt32 *propType) 155 { 156 *data = 0; 157 *dataSize = 0; 158 *propType = 0; 159 if (!StoreNtSecurity) 160 return S_OK; 161 #ifdef _USE_SECURITY_CODE 162 if (propID == kpidNtSecure) 163 { 164 if (StdInMode) 165 return S_OK; 166 167 if (ParentDirItem) 168 { 169 if (ParentDirItem->SecureIndex < 0) 170 return S_OK; 171 const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[ParentDirItem->SecureIndex]; 172 *data = buf; 173 *dataSize = (UInt32)buf.Size(); 174 *propType = NPropDataType::kRaw; 175 return S_OK; 176 } 177 178 if (GetRootProps) 179 return GetRootProps->GetRootRawProp(propID, data, dataSize, propType); 180 } 181 #endif 182 return S_OK; 183 } 184 185 // #ifdef _USE_SECURITY_CODE 186 // #endif 187 188 STDMETHODIMP CArchiveUpdateCallback::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) 189 { 190 *data = 0; 191 *dataSize = 0; 192 *propType = 0; 193 194 if (propID == kpidNtSecure || 195 propID == kpidNtReparse) 196 { 197 if (StdInMode) 198 return S_OK; 199 200 const CUpdatePair2 &up = (*UpdatePairs)[index]; 201 if (up.UseArcProps && up.ExistInArchive() && GetRawProps) 202 return GetRawProps->GetRawProp( 203 ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, 204 propID, data, dataSize, propType); 205 206 { 207 const CUpdatePair2 &up = (*UpdatePairs)[index]; 208 /* 209 if (!up.NewData) 210 return E_FAIL; 211 */ 212 if (up.IsAnti) 213 return S_OK; 214 215 #ifndef UNDER_CE 216 const CDirItem &di = DirItems->Items[up.DirIndex]; 217 #endif 218 219 #ifdef _USE_SECURITY_CODE 220 if (propID == kpidNtSecure) 221 { 222 if (!StoreNtSecurity) 223 return S_OK; 224 if (di.SecureIndex < 0) 225 return S_OK; 226 const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[di.SecureIndex]; 227 *data = buf; 228 *dataSize = (UInt32)buf.Size(); 229 *propType = NPropDataType::kRaw; 230 } 231 else 232 #endif 233 { 234 // propID == kpidNtReparse 235 if (!StoreSymLinks) 236 return S_OK; 237 #ifndef UNDER_CE 238 const CByteBuffer *buf = &di.ReparseData2; 239 if (buf->Size() == 0) 240 buf = &di.ReparseData; 241 if (buf->Size() != 0) 242 { 243 *data = *buf; 244 *dataSize = (UInt32)buf->Size(); 245 *propType = NPropDataType::kRaw; 246 } 247 #endif 248 } 249 250 return S_OK; 251 } 252 } 253 254 return S_OK; 255 } 256 257 #ifndef UNDER_CE 258 259 static UString GetRelativePath(const UString &to, const UString &from) 260 { 261 UStringVector partsTo, partsFrom; 262 SplitPathToParts(to, partsTo); 263 SplitPathToParts(from, partsFrom); 264 265 unsigned i; 266 for (i = 0;; i++) 267 { 268 if (i + 1 >= partsFrom.Size() || 269 i + 1 >= partsTo.Size()) 270 break; 271 if (CompareFileNames(partsFrom[i], partsTo[i]) != 0) 272 break; 273 } 274 275 if (i == 0) 276 { 277 #ifdef _WIN32 278 if (NName::IsDrivePath(to) || 279 NName::IsDrivePath(from)) 280 return to; 281 #endif 282 } 283 284 UString s; 285 unsigned k; 286 287 for (k = i + 1; k < partsFrom.Size(); k++) 288 s += L".." WSTRING_PATH_SEPARATOR; 289 290 for (k = i; k < partsTo.Size(); k++) 291 { 292 if (k != i) 293 s += WCHAR_PATH_SEPARATOR; 294 s += partsTo[k]; 295 } 296 297 return s; 298 } 299 300 #endif 301 302 STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) 303 { 304 COM_TRY_BEGIN 305 const CUpdatePair2 &up = (*UpdatePairs)[index]; 306 NCOM::CPropVariant prop; 307 308 if (up.NewData) 309 { 310 /* 311 if (propID == kpidIsHardLink) 312 { 313 prop = _isHardLink; 314 prop.Detach(value); 315 return S_OK; 316 } 317 */ 318 if (propID == kpidSymLink) 319 { 320 if (index == _hardIndex_From) 321 { 322 prop.Detach(value); 323 return S_OK; 324 } 325 if (up.DirIndex >= 0) 326 { 327 #ifndef UNDER_CE 328 const CDirItem &di = DirItems->Items[up.DirIndex]; 329 // if (di.IsDir()) 330 { 331 CReparseAttr attr; 332 if (attr.Parse(di.ReparseData, di.ReparseData.Size())) 333 { 334 UString simpleName = attr.GetPath(); 335 if (attr.IsRelative()) 336 prop = simpleName; 337 else 338 { 339 const UString phyPath = DirItems->GetPhyPath(up.DirIndex); 340 FString fullPath; 341 if (NDir::MyGetFullPathName(us2fs(phyPath), fullPath)) 342 { 343 prop = GetRelativePath(simpleName, fs2us(fullPath)); 344 } 345 } 346 prop.Detach(value); 347 return S_OK; 348 } 349 } 350 #endif 351 } 352 } 353 else if (propID == kpidHardLink) 354 { 355 if (index == _hardIndex_From) 356 { 357 const CKeyKeyValPair &pair = _map[_hardIndex_To]; 358 const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value]; 359 prop = DirItems->GetLogPath(up2.DirIndex); 360 prop.Detach(value); 361 return S_OK; 362 } 363 if (up.DirIndex >= 0) 364 { 365 prop.Detach(value); 366 return S_OK; 367 } 368 } 369 } 370 371 if (up.IsAnti 372 && propID != kpidIsDir 373 && propID != kpidPath 374 && propID != kpidIsAltStream) 375 { 376 switch (propID) 377 { 378 case kpidSize: prop = (UInt64)0; break; 379 case kpidIsAnti: prop = true; break; 380 } 381 } 382 else if (propID == kpidPath && up.NewNameIndex >= 0) 383 prop = (*NewNames)[up.NewNameIndex]; 384 else if (propID == kpidShortName && up.NewNameIndex >= 0 && up.IsMainRenameItem) 385 { 386 // we can generate new ShortName here; 387 } 388 else if ((up.UseArcProps 389 || (KeepOriginalItemNames && (propID == kpidPath || propID == kpidIsAltStream))) 390 && up.ExistInArchive() && Archive) 391 return Archive->GetProperty(ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, value); 392 else if (up.ExistOnDisk()) 393 { 394 const CDirItem &di = DirItems->Items[up.DirIndex]; 395 switch (propID) 396 { 397 case kpidPath: prop = DirItems->GetLogPath(up.DirIndex); break; 398 case kpidIsDir: prop = di.IsDir(); break; 399 case kpidSize: prop = di.Size; break; 400 case kpidAttrib: prop = di.Attrib; break; 401 case kpidCTime: prop = di.CTime; break; 402 case kpidATime: prop = di.ATime; break; 403 case kpidMTime: prop = di.MTime; break; 404 case kpidIsAltStream: prop = di.IsAltStream; break; 405 #if defined(_WIN32) && !defined(UNDER_CE) 406 // case kpidShortName: prop = di.ShortName; break; 407 #endif 408 } 409 } 410 prop.Detach(value); 411 return S_OK; 412 COM_TRY_END 413 } 414 415 static NSynchronization::CCriticalSection CS; 416 417 STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream) 418 { 419 COM_TRY_BEGIN 420 *inStream = NULL; 421 const CUpdatePair2 &up = (*UpdatePairs)[index]; 422 if (!up.NewData) 423 return E_FAIL; 424 425 RINOK(Callback->CheckBreak()); 426 RINOK(Callback->Finilize()); 427 428 bool isDir = IsDir(up); 429 430 if (up.IsAnti) 431 { 432 UString name; 433 if (up.ArcIndex >= 0) 434 name = (*ArcItems)[up.ArcIndex].Name; 435 else if (up.DirIndex >= 0) 436 name = DirItems->GetLogPath(up.DirIndex); 437 RINOK(Callback->GetStream(name, true)); 438 439 /* 9.33: fixed. Handlers expect real stream object for files, even for anti-file. 440 so we return empty stream */ 441 442 if (!isDir) 443 { 444 CBufInStream *inStreamSpec = new CBufInStream(); 445 CMyComPtr<ISequentialInStream> inStreamLoc = inStreamSpec; 446 inStreamSpec->Init(NULL, 0); 447 *inStream = inStreamLoc.Detach(); 448 } 449 return S_OK; 450 } 451 452 RINOK(Callback->GetStream(DirItems->GetLogPath(up.DirIndex), false)); 453 454 if (isDir) 455 return S_OK; 456 457 if (StdInMode) 458 { 459 CStdInFileStream *inStreamSpec = new CStdInFileStream; 460 CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec); 461 *inStream = inStreamLoc.Detach(); 462 } 463 else 464 { 465 CInFileStream *inStreamSpec = new CInFileStream; 466 CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec); 467 468 inStreamSpec->SupportHardLinks = StoreHardLinks; 469 470 const UString path = DirItems->GetPhyPath(up.DirIndex); 471 472 #if defined(_WIN32) && !defined(UNDER_CE) 473 if (DirItems->Items[up.DirIndex].AreReparseData()) 474 { 475 if (!inStreamSpec->File.OpenReparse(us2fs(path))) 476 { 477 return Callback->OpenFileError(path, ::GetLastError()); 478 } 479 } 480 else 481 #endif 482 if (!inStreamSpec->OpenShared(us2fs(path), ShareForWrite)) 483 { 484 return Callback->OpenFileError(path, ::GetLastError()); 485 } 486 487 if (StoreHardLinks) 488 { 489 CStreamFileProps props; 490 if (inStreamSpec->GetProps2(&props) == S_OK) 491 { 492 if (props.NumLinks > 1) 493 { 494 CKeyKeyValPair pair; 495 pair.Key1 = props.VolID; 496 pair.Key2 = props.FileID_Low; 497 pair.Value = index; 498 unsigned numItems = _map.Size(); 499 unsigned pairIndex = _map.AddToUniqueSorted2(pair); 500 if (numItems == _map.Size()) 501 { 502 // const CKeyKeyValPair &pair2 = _map.Pairs[pairIndex]; 503 _hardIndex_From = index; 504 _hardIndex_To = pairIndex; 505 // we could return NULL as stream, but it's better to return real stream 506 // return S_OK; 507 } 508 } 509 } 510 } 511 512 if (ProcessedItemsStatuses) 513 { 514 NSynchronization::CCriticalSectionLock lock(CS); 515 ProcessedItemsStatuses[up.DirIndex] = 1; 516 } 517 *inStream = inStreamLoc.Detach(); 518 } 519 520 return S_OK; 521 COM_TRY_END 522 } 523 524 STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 operationResult) 525 { 526 COM_TRY_BEGIN 527 return Callback->SetOperationResult(operationResult); 528 COM_TRY_END 529 } 530 531 STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size) 532 { 533 if (VolumesSizes.Size() == 0) 534 return S_FALSE; 535 if (index >= (UInt32)VolumesSizes.Size()) 536 index = VolumesSizes.Size() - 1; 537 *size = VolumesSizes[index]; 538 return S_OK; 539 } 540 541 STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream) 542 { 543 COM_TRY_BEGIN 544 FChar temp[16]; 545 ConvertUInt32ToString(index + 1, temp); 546 FString res = temp; 547 while (res.Len() < 2) 548 res.InsertAtFront(FTEXT('0')); 549 FString fileName = VolName; 550 fileName += L'.'; 551 fileName += res; 552 fileName += VolExt; 553 COutFileStream *streamSpec = new COutFileStream; 554 CMyComPtr<ISequentialOutStream> streamLoc(streamSpec); 555 if (!streamSpec->Create(fileName, false)) 556 return ::GetLastError(); 557 *volumeStream = streamLoc.Detach(); 558 return S_OK; 559 COM_TRY_END 560 } 561 562 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) 563 { 564 COM_TRY_BEGIN 565 return Callback->CryptoGetTextPassword2(passwordIsDefined, password); 566 COM_TRY_END 567 } 568 569 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password) 570 { 571 COM_TRY_BEGIN 572 return Callback->CryptoGetTextPassword(password); 573 COM_TRY_END 574 } 575