EnumDirItems.cpp (28643B)
1 // EnumDirItems.cpp 2 3 #include "StdAfx.h" 4 5 #include <wchar.h> 6 7 #include "../../../Common/Wildcard.h" 8 9 #include "../../../Windows/FileDir.h" 10 #include "../../../Windows/FileIO.h" 11 #include "../../../Windows/FileName.h" 12 13 #if defined(_WIN32) && !defined(UNDER_CE) 14 #define _USE_SECURITY_CODE 15 #include "../../../Windows/SecurityUtils.h" 16 #endif 17 18 #include "EnumDirItems.h" 19 #include "SortUtils.h" 20 21 using namespace NWindows; 22 using namespace NFile; 23 using namespace NName; 24 25 void CDirItems::AddDirFileInfo(int phyParent, int logParent, int secureIndex, 26 const NFind::CFileInfo &fi) 27 { 28 CDirItem di; 29 di.Size = fi.Size; 30 di.CTime = fi.CTime; 31 di.ATime = fi.ATime; 32 di.MTime = fi.MTime; 33 di.Attrib = fi.Attrib; 34 di.IsAltStream = fi.IsAltStream; 35 di.PhyParent = phyParent; 36 di.LogParent = logParent; 37 di.SecureIndex = secureIndex; 38 di.Name = fs2us(fi.Name); 39 #if defined(_WIN32) && !defined(UNDER_CE) 40 // di.ShortName = fs2us(fi.ShortName); 41 #endif 42 Items.Add(di); 43 44 if (fi.IsDir()) 45 Stat.NumDirs++; 46 else if (fi.IsAltStream) 47 { 48 Stat.NumAltStreams++; 49 Stat.AltStreamsSize += fi.Size; 50 } 51 else 52 { 53 Stat.NumFiles++; 54 Stat.FilesSize += fi.Size; 55 } 56 } 57 58 HRESULT CDirItems::AddError(const FString &path, DWORD errorCode) 59 { 60 Stat.NumErrors++; 61 if (Callback) 62 return Callback->ScanError(path, errorCode); 63 return S_OK; 64 } 65 66 HRESULT CDirItems::AddError(const FString &path) 67 { 68 return AddError(path, ::GetLastError()); 69 } 70 71 static const unsigned kScanProgressStepMask = (1 << 12) - 1; 72 73 HRESULT CDirItems::ScanProgress(const FString &dirPath) 74 { 75 if (Callback) 76 return Callback->ScanProgress(Stat, dirPath, true); 77 return S_OK; 78 } 79 80 UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const 81 { 82 UString path; 83 unsigned len = name.Len(); 84 85 int i; 86 for (i = index; i >= 0; i = parents[i]) 87 len += Prefixes[i].Len(); 88 89 wchar_t *p = path.GetBuf_SetEnd(len) + len; 90 91 p -= name.Len(); 92 wmemcpy(p, (const wchar_t *)name, name.Len()); 93 94 for (i = index; i >= 0; i = parents[i]) 95 { 96 const UString &s = Prefixes[i]; 97 p -= s.Len(); 98 wmemcpy(p, (const wchar_t *)s, s.Len()); 99 } 100 101 return path; 102 } 103 104 FString CDirItems::GetPhyPath(unsigned index) const 105 { 106 const CDirItem &di = Items[index]; 107 return us2fs(GetPrefixesPath(PhyParents, di.PhyParent, di.Name)); 108 } 109 110 UString CDirItems::GetLogPath(unsigned index) const 111 { 112 const CDirItem &di = Items[index]; 113 return GetPrefixesPath(LogParents, di.LogParent, di.Name); 114 } 115 116 void CDirItems::ReserveDown() 117 { 118 Prefixes.ReserveDown(); 119 PhyParents.ReserveDown(); 120 LogParents.ReserveDown(); 121 Items.ReserveDown(); 122 } 123 124 unsigned CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix) 125 { 126 PhyParents.Add(phyParent); 127 LogParents.Add(logParent); 128 return Prefixes.Add(prefix); 129 } 130 131 void CDirItems::DeleteLastPrefix() 132 { 133 PhyParents.DeleteBack(); 134 LogParents.DeleteBack(); 135 Prefixes.DeleteBack(); 136 } 137 138 bool InitLocalPrivileges(); 139 140 CDirItems::CDirItems(): 141 SymLinks(false), 142 ScanAltStreams(false) 143 #ifdef _USE_SECURITY_CODE 144 , ReadSecure(false) 145 #endif 146 , Callback(NULL) 147 { 148 #ifdef _USE_SECURITY_CODE 149 _saclEnabled = InitLocalPrivileges(); 150 #endif 151 } 152 153 #ifdef _USE_SECURITY_CODE 154 155 HRESULT CDirItems::AddSecurityItem(const FString &path, int &secureIndex) 156 { 157 secureIndex = -1; 158 159 SECURITY_INFORMATION securInfo = 160 DACL_SECURITY_INFORMATION | 161 GROUP_SECURITY_INFORMATION | 162 OWNER_SECURITY_INFORMATION; 163 if (_saclEnabled) 164 securInfo |= SACL_SECURITY_INFORMATION; 165 166 DWORD errorCode = 0; 167 DWORD secureSize; 168 169 BOOL res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); 170 171 if (res) 172 { 173 if (secureSize == 0) 174 return S_OK; 175 if (secureSize > TempSecureBuf.Size()) 176 errorCode = ERROR_INVALID_FUNCTION; 177 } 178 else 179 { 180 errorCode = GetLastError(); 181 if (errorCode == ERROR_INSUFFICIENT_BUFFER) 182 { 183 if (secureSize <= TempSecureBuf.Size()) 184 errorCode = ERROR_INVALID_FUNCTION; 185 else 186 { 187 TempSecureBuf.Alloc(secureSize); 188 res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); 189 if (res) 190 { 191 if (secureSize != TempSecureBuf.Size()) 192 errorCode = ERROR_INVALID_FUNCTION;; 193 } 194 else 195 errorCode = GetLastError(); 196 } 197 } 198 } 199 200 if (res) 201 { 202 secureIndex = SecureBlocks.AddUniq(TempSecureBuf, secureSize); 203 return S_OK; 204 } 205 206 if (errorCode == 0) 207 errorCode = ERROR_INVALID_FUNCTION; 208 return AddError(path, errorCode); 209 } 210 211 #endif 212 213 HRESULT CDirItems::EnumerateDir(int phyParent, int logParent, const FString &phyPrefix) 214 { 215 RINOK(ScanProgress(phyPrefix)); 216 217 NFind::CEnumerator enumerator; 218 enumerator.SetDirPrefix(phyPrefix); 219 for (unsigned ttt = 0; ; ttt++) 220 { 221 NFind::CFileInfo fi; 222 bool found; 223 if (!enumerator.Next(fi, found)) 224 { 225 return AddError(phyPrefix); 226 } 227 if (!found) 228 return S_OK; 229 230 int secureIndex = -1; 231 #ifdef _USE_SECURITY_CODE 232 if (ReadSecure) 233 { 234 RINOK(AddSecurityItem(phyPrefix + fi.Name, secureIndex)); 235 } 236 #endif 237 238 AddDirFileInfo(phyParent, logParent, secureIndex, fi); 239 240 if (Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask) 241 { 242 RINOK(ScanProgress(phyPrefix)); 243 } 244 245 if (fi.IsDir()) 246 { 247 const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; 248 unsigned parent = AddPrefix(phyParent, logParent, fs2us(name2)); 249 RINOK(EnumerateDir(parent, parent, phyPrefix + name2)); 250 } 251 } 252 } 253 254 HRESULT CDirItems::EnumerateItems2( 255 const FString &phyPrefix, 256 const UString &logPrefix, 257 const FStringVector &filePaths, 258 FStringVector *requestedPaths) 259 { 260 int phyParent = phyPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, fs2us(phyPrefix)); 261 int logParent = logPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, logPrefix); 262 263 FOR_VECTOR (i, filePaths) 264 { 265 const FString &filePath = filePaths[i]; 266 NFind::CFileInfo fi; 267 const FString phyPath = phyPrefix + filePath; 268 if (!fi.Find(phyPath)) 269 { 270 RINOK(AddError(phyPath)); 271 continue; 272 } 273 if (requestedPaths) 274 requestedPaths->Add(phyPath); 275 276 int delimiter = filePath.ReverseFind_PathSepar(); 277 FString phyPrefixCur; 278 int phyParentCur = phyParent; 279 if (delimiter >= 0) 280 { 281 phyPrefixCur.SetFrom(filePath, delimiter + 1); 282 phyParentCur = AddPrefix(phyParent, logParent, fs2us(phyPrefixCur)); 283 } 284 285 int secureIndex = -1; 286 #ifdef _USE_SECURITY_CODE 287 if (ReadSecure) 288 { 289 RINOK(AddSecurityItem(phyPath, secureIndex)); 290 } 291 #endif 292 293 AddDirFileInfo(phyParentCur, logParent, secureIndex, fi); 294 295 if (fi.IsDir()) 296 { 297 const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; 298 unsigned parent = AddPrefix(phyParentCur, logParent, fs2us(name2)); 299 RINOK(EnumerateDir(parent, parent, phyPrefix + phyPrefixCur + name2)); 300 } 301 } 302 303 ReserveDown(); 304 return S_OK; 305 } 306 307 308 309 310 311 312 static HRESULT EnumerateDirItems( 313 const NWildcard::CCensorNode &curNode, 314 int phyParent, int logParent, const FString &phyPrefix, 315 const UStringVector &addArchivePrefix, 316 CDirItems &dirItems, 317 bool enterToSubFolders); 318 319 static HRESULT EnumerateDirItems_Spec( 320 const NWildcard::CCensorNode &curNode, 321 int phyParent, int logParent, const FString &curFolderName, 322 const FString &phyPrefix, 323 const UStringVector &addArchivePrefix, 324 CDirItems &dirItems, 325 bool enterToSubFolders) 326 { 327 const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR; 328 unsigned parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2)); 329 unsigned numItems = dirItems.Items.Size(); 330 HRESULT res = EnumerateDirItems( 331 curNode, parent, parent, phyPrefix + name2, 332 addArchivePrefix, dirItems, enterToSubFolders); 333 if (numItems == dirItems.Items.Size()) 334 dirItems.DeleteLastPrefix(); 335 return res; 336 } 337 338 #ifndef UNDER_CE 339 340 #ifdef _WIN32 341 342 static HRESULT EnumerateAltStreams( 343 const NFind::CFileInfo &fi, 344 const NWildcard::CCensorNode &curNode, 345 int phyParent, int logParent, const FString &fullPath, 346 const UStringVector &addArchivePrefix, // prefix from curNode 347 bool addAllItems, 348 CDirItems &dirItems) 349 { 350 NFind::CStreamEnumerator enumerator(fullPath); 351 for (;;) 352 { 353 NFind::CStreamInfo si; 354 bool found; 355 if (!enumerator.Next(si, found)) 356 { 357 return dirItems.AddError(fullPath + FTEXT(":*")); // , (DWORD)E_FAIL 358 } 359 if (!found) 360 return S_OK; 361 if (si.IsMainStream()) 362 continue; 363 UStringVector addArchivePrefixNew = addArchivePrefix; 364 UString reducedName = si.GetReducedName(); 365 addArchivePrefixNew.Back() += reducedName; 366 if (curNode.CheckPathToRoot(false, addArchivePrefixNew, true)) 367 continue; 368 if (!addAllItems) 369 if (!curNode.CheckPathToRoot(true, addArchivePrefixNew, true)) 370 continue; 371 372 NFind::CFileInfo fi2 = fi; 373 fi2.Name += us2fs(reducedName); 374 fi2.Size = si.Size; 375 fi2.Attrib &= ~FILE_ATTRIBUTE_DIRECTORY; 376 fi2.IsAltStream = true; 377 dirItems.AddDirFileInfo(phyParent, logParent, -1, fi2); 378 } 379 } 380 381 #endif 382 383 HRESULT CDirItems::SetLinkInfo(CDirItem &dirItem, const NFind::CFileInfo &fi, 384 const FString &phyPrefix) 385 { 386 if (!SymLinks || !fi.HasReparsePoint()) 387 return S_OK; 388 const FString path = phyPrefix + fi.Name; 389 CByteBuffer &buf = dirItem.ReparseData; 390 DWORD res = 0; 391 if (NIO::GetReparseData(path, buf)) 392 { 393 CReparseAttr attr; 394 if (attr.Parse(buf, buf.Size(), res)) 395 return S_OK; 396 // we ignore unknown reparse points 397 if (res != ERROR_INVALID_REPARSE_DATA) 398 res = 0; 399 } 400 else 401 { 402 res = ::GetLastError(); 403 if (res == 0) 404 res = ERROR_INVALID_FUNCTION; 405 } 406 407 buf.Free(); 408 if (res == 0) 409 return S_OK; 410 return AddError(path, res); 411 } 412 413 #endif 414 415 static HRESULT EnumerateForItem( 416 NFind::CFileInfo &fi, 417 const NWildcard::CCensorNode &curNode, 418 int phyParent, int logParent, const FString &phyPrefix, 419 const UStringVector &addArchivePrefix, // prefix from curNode 420 CDirItems &dirItems, 421 bool enterToSubFolders) 422 { 423 const UString name = fs2us(fi.Name); 424 bool enterToSubFolders2 = enterToSubFolders; 425 UStringVector addArchivePrefixNew = addArchivePrefix; 426 addArchivePrefixNew.Add(name); 427 { 428 UStringVector addArchivePrefixNewTemp(addArchivePrefixNew); 429 if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fi.IsDir())) 430 return S_OK; 431 } 432 int dirItemIndex = -1; 433 434 bool addAllSubStreams = false; 435 436 if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fi.IsDir())) 437 { 438 int secureIndex = -1; 439 #ifdef _USE_SECURITY_CODE 440 if (dirItems.ReadSecure) 441 { 442 RINOK(dirItems.AddSecurityItem(phyPrefix + fi.Name, secureIndex)); 443 } 444 #endif 445 446 dirItemIndex = dirItems.Items.Size(); 447 dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi); 448 if (fi.IsDir()) 449 enterToSubFolders2 = true; 450 451 addAllSubStreams = true; 452 } 453 454 #ifndef UNDER_CE 455 if (dirItems.ScanAltStreams) 456 { 457 RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent, 458 phyPrefix + fi.Name, 459 addArchivePrefixNew, 460 addAllSubStreams, 461 dirItems)); 462 } 463 464 if (dirItemIndex >= 0) 465 { 466 CDirItem &dirItem = dirItems.Items[dirItemIndex]; 467 RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix)); 468 if (dirItem.ReparseData.Size() != 0) 469 return S_OK; 470 } 471 #endif 472 473 if (!fi.IsDir()) 474 return S_OK; 475 476 const NWildcard::CCensorNode *nextNode = 0; 477 if (addArchivePrefix.IsEmpty()) 478 { 479 int index = curNode.FindSubNode(name); 480 if (index >= 0) 481 nextNode = &curNode.SubNodes[index]; 482 } 483 if (!enterToSubFolders2 && nextNode == 0) 484 return S_OK; 485 486 addArchivePrefixNew = addArchivePrefix; 487 if (nextNode == 0) 488 { 489 nextNode = &curNode; 490 addArchivePrefixNew.Add(name); 491 } 492 493 return EnumerateDirItems_Spec( 494 *nextNode, phyParent, logParent, fi.Name, phyPrefix, 495 addArchivePrefixNew, 496 dirItems, 497 enterToSubFolders2); 498 } 499 500 501 static bool CanUseFsDirect(const NWildcard::CCensorNode &curNode) 502 { 503 FOR_VECTOR (i, curNode.IncludeItems) 504 { 505 const NWildcard::CItem &item = curNode.IncludeItems[i]; 506 if (item.Recursive || item.PathParts.Size() != 1) 507 return false; 508 const UString &name = item.PathParts.Front(); 509 /* 510 if (name.IsEmpty()) 511 return false; 512 */ 513 514 /* Windows doesn't support file name with wildcard 515 But if another system supports file name with wildcard, 516 and wildcard mode is disabled, we can ignore wildcard in name */ 517 /* 518 if (!item.WildcardParsing) 519 continue; 520 */ 521 if (DoesNameContainWildcard(name)) 522 return false; 523 } 524 return true; 525 } 526 527 528 #if defined(_WIN32) && !defined(UNDER_CE) 529 530 static bool IsVirtualFsFolder(const FString &prefix, const UString &name) 531 { 532 UString s = fs2us(prefix); 533 s += name; 534 s.Add_PathSepar(); 535 return IsPathSepar(s[0]) && GetRootPrefixSize(s) == 0; 536 } 537 538 #endif 539 540 static HRESULT EnumerateDirItems( 541 const NWildcard::CCensorNode &curNode, 542 int phyParent, int logParent, const FString &phyPrefix, 543 const UStringVector &addArchivePrefix, // prefix from curNode 544 CDirItems &dirItems, 545 bool enterToSubFolders) 546 { 547 if (!enterToSubFolders) 548 if (curNode.NeedCheckSubDirs()) 549 enterToSubFolders = true; 550 551 RINOK(dirItems.ScanProgress(phyPrefix)); 552 553 // try direct_names case at first 554 if (addArchivePrefix.IsEmpty() && !enterToSubFolders) 555 { 556 if (CanUseFsDirect(curNode)) 557 { 558 // all names are direct (no wildcards) 559 // so we don't need file_system's dir enumerator 560 CRecordVector<bool> needEnterVector; 561 unsigned i; 562 563 for (i = 0; i < curNode.IncludeItems.Size(); i++) 564 { 565 const NWildcard::CItem &item = curNode.IncludeItems[i]; 566 const UString &name = item.PathParts.Front(); 567 FString fullPath = phyPrefix + us2fs(name); 568 569 #if defined(_WIN32) && !defined(UNDER_CE) 570 bool needAltStreams = true; 571 #endif 572 573 #ifdef _USE_SECURITY_CODE 574 bool needSecurity = true; 575 #endif 576 577 if (phyPrefix.IsEmpty()) 578 { 579 if (!item.ForFile) 580 { 581 /* we don't like some names for alt streams inside archive: 582 ":sname" for "\" 583 "c:::sname" for "C:\" 584 So we ignore alt streams for these cases */ 585 if (name.IsEmpty()) 586 { 587 #if defined(_WIN32) && !defined(UNDER_CE) 588 needAltStreams = false; 589 #endif 590 591 /* 592 // do we need to ignore security info for "\\" folder ? 593 #ifdef _USE_SECURITY_CODE 594 needSecurity = false; 595 #endif 596 */ 597 598 fullPath = CHAR_PATH_SEPARATOR; 599 } 600 #if defined(_WIN32) && !defined(UNDER_CE) 601 else if (item.IsDriveItem()) 602 { 603 needAltStreams = false; 604 fullPath.Add_PathSepar(); 605 } 606 #endif 607 } 608 } 609 610 NFind::CFileInfo fi; 611 #if defined(_WIN32) && !defined(UNDER_CE) 612 if (IsVirtualFsFolder(phyPrefix, name)) 613 { 614 fi.SetAsDir(); 615 fi.Name = us2fs(name); 616 } 617 else 618 #endif 619 if (!fi.Find(fullPath)) 620 { 621 RINOK(dirItems.AddError(fullPath)); 622 continue; 623 } 624 625 bool isDir = fi.IsDir(); 626 if (isDir && !item.ForDir || !isDir && !item.ForFile) 627 { 628 RINOK(dirItems.AddError(fullPath, (DWORD)E_FAIL)); 629 continue; 630 } 631 { 632 UStringVector pathParts; 633 pathParts.Add(fs2us(fi.Name)); 634 if (curNode.CheckPathToRoot(false, pathParts, !isDir)) 635 continue; 636 } 637 638 int secureIndex = -1; 639 #ifdef _USE_SECURITY_CODE 640 if (needSecurity && dirItems.ReadSecure) 641 { 642 RINOK(dirItems.AddSecurityItem(fullPath, secureIndex)); 643 } 644 #endif 645 646 dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi); 647 648 #ifndef UNDER_CE 649 { 650 CDirItem &dirItem = dirItems.Items.Back(); 651 RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix)); 652 if (dirItem.ReparseData.Size() != 0) 653 { 654 if (fi.IsAltStream) 655 dirItems.Stat.AltStreamsSize -= fi.Size; 656 else 657 dirItems.Stat.FilesSize -= fi.Size; 658 continue; 659 } 660 } 661 #endif 662 663 664 #ifndef UNDER_CE 665 if (needAltStreams && dirItems.ScanAltStreams) 666 { 667 UStringVector pathParts; 668 pathParts.Add(fs2us(fi.Name)); 669 RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent, 670 fullPath, pathParts, 671 true, /* addAllSubStreams */ 672 dirItems)); 673 } 674 #endif 675 676 if (!isDir) 677 continue; 678 679 UStringVector addArchivePrefixNew; 680 const NWildcard::CCensorNode *nextNode = 0; 681 int index = curNode.FindSubNode(name); 682 if (index >= 0) 683 { 684 for (int t = needEnterVector.Size(); t <= index; t++) 685 needEnterVector.Add(true); 686 needEnterVector[index] = false; 687 nextNode = &curNode.SubNodes[index]; 688 } 689 else 690 { 691 nextNode = &curNode; 692 addArchivePrefixNew.Add(name); // don't change it to fi.Name. It's for shortnames support 693 } 694 695 RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix, 696 addArchivePrefixNew, dirItems, true)); 697 } 698 699 for (i = 0; i < curNode.SubNodes.Size(); i++) 700 { 701 if (i < needEnterVector.Size()) 702 if (!needEnterVector[i]) 703 continue; 704 const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i]; 705 FString fullPath = phyPrefix + us2fs(nextNode.Name); 706 NFind::CFileInfo fi; 707 708 if (phyPrefix.IsEmpty()) 709 { 710 { 711 if (nextNode.Name.IsEmpty()) 712 fullPath = CHAR_PATH_SEPARATOR; 713 #ifdef _WIN32 714 else if (NWildcard::IsDriveColonName(nextNode.Name)) 715 fullPath.Add_PathSepar(); 716 #endif 717 } 718 } 719 720 // we don't want to call fi.Find() for root folder or virtual folder 721 if (phyPrefix.IsEmpty() && nextNode.Name.IsEmpty() 722 #if defined(_WIN32) && !defined(UNDER_CE) 723 || IsVirtualFsFolder(phyPrefix, nextNode.Name) 724 #endif 725 ) 726 { 727 fi.SetAsDir(); 728 fi.Name = us2fs(nextNode.Name); 729 } 730 else 731 { 732 if (!fi.Find(fullPath)) 733 { 734 if (!nextNode.AreThereIncludeItems()) 735 continue; 736 RINOK(dirItems.AddError(fullPath)); 737 continue; 738 } 739 740 if (!fi.IsDir()) 741 { 742 RINOK(dirItems.AddError(fullPath, (DWORD)E_FAIL)); 743 continue; 744 } 745 } 746 747 RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix, 748 UStringVector(), dirItems, false)); 749 } 750 751 return S_OK; 752 } 753 } 754 755 #ifdef _WIN32 756 #ifndef UNDER_CE 757 758 // scan drives, if wildcard is "*:\" 759 760 if (phyPrefix.IsEmpty() && curNode.IncludeItems.Size() > 0) 761 { 762 unsigned i; 763 for (i = 0; i < curNode.IncludeItems.Size(); i++) 764 { 765 const NWildcard::CItem &item = curNode.IncludeItems[i]; 766 if (item.PathParts.Size() < 1) 767 break; 768 const UString &name = item.PathParts.Front(); 769 if (name.Len() != 2 || name[1] != ':') 770 break; 771 if (item.PathParts.Size() == 1) 772 if (item.ForFile || !item.ForDir) 773 break; 774 if (NWildcard::IsDriveColonName(name)) 775 continue; 776 if (name[0] != '*' && name[0] != '?') 777 break; 778 } 779 if (i == curNode.IncludeItems.Size()) 780 { 781 FStringVector driveStrings; 782 NFind::MyGetLogicalDriveStrings(driveStrings); 783 for (i = 0; i < driveStrings.Size(); i++) 784 { 785 FString driveName = driveStrings[i]; 786 if (driveName.Len() < 3 || driveName.Back() != '\\') 787 return E_FAIL; 788 driveName.DeleteBack(); 789 NFind::CFileInfo fi; 790 fi.SetAsDir(); 791 fi.Name = driveName; 792 793 RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, 794 addArchivePrefix, dirItems, enterToSubFolders)); 795 } 796 return S_OK; 797 } 798 } 799 800 #endif 801 #endif 802 803 NFind::CEnumerator enumerator; 804 enumerator.SetDirPrefix(phyPrefix); 805 806 for (unsigned ttt = 0; ; ttt++) 807 { 808 NFind::CFileInfo fi; 809 bool found; 810 if (!enumerator.Next(fi, found)) 811 { 812 RINOK(dirItems.AddError(phyPrefix)); 813 break; 814 } 815 if (!found) 816 break; 817 818 if (dirItems.Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask) 819 { 820 RINOK(dirItems.ScanProgress(phyPrefix)); 821 } 822 823 RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, 824 addArchivePrefix, dirItems, enterToSubFolders)); 825 } 826 827 return S_OK; 828 } 829 830 HRESULT EnumerateItems( 831 const NWildcard::CCensor &censor, 832 const NWildcard::ECensorPathMode pathMode, 833 const UString &addPathPrefix, 834 CDirItems &dirItems) 835 { 836 FOR_VECTOR (i, censor.Pairs) 837 { 838 const NWildcard::CPair &pair = censor.Pairs[i]; 839 int phyParent = pair.Prefix.IsEmpty() ? -1 : dirItems.AddPrefix(-1, -1, pair.Prefix); 840 int logParent = -1; 841 842 if (pathMode == NWildcard::k_AbsPath) 843 logParent = phyParent; 844 else 845 { 846 if (!addPathPrefix.IsEmpty()) 847 logParent = dirItems.AddPrefix(-1, -1, addPathPrefix); 848 } 849 850 RINOK(EnumerateDirItems(pair.Head, phyParent, logParent, us2fs(pair.Prefix), UStringVector(), 851 dirItems, 852 false // enterToSubFolders 853 )); 854 } 855 dirItems.ReserveDown(); 856 857 #if defined(_WIN32) && !defined(UNDER_CE) 858 dirItems.FillFixedReparse(); 859 #endif 860 861 return S_OK; 862 } 863 864 #if defined(_WIN32) && !defined(UNDER_CE) 865 866 void CDirItems::FillFixedReparse() 867 { 868 /* imagex/WIM reduces absolute pathes in links (raparse data), 869 if we archive non root folder. We do same thing here */ 870 871 if (!SymLinks) 872 return; 873 874 FOR_VECTOR(i, Items) 875 { 876 CDirItem &item = Items[i]; 877 if (item.ReparseData.Size() == 0) 878 continue; 879 880 CReparseAttr attr; 881 DWORD errorCode = 0; 882 if (!attr.Parse(item.ReparseData, item.ReparseData.Size(), errorCode)) 883 continue; 884 if (attr.IsRelative()) 885 continue; 886 887 const UString &link = attr.GetPath(); 888 if (!IsDrivePath(link)) 889 continue; 890 // maybe we need to support networks paths also ? 891 892 FString fullPathF; 893 if (!NDir::MyGetFullPathName(GetPhyPath(i), fullPathF)) 894 continue; 895 UString fullPath = fs2us(fullPathF); 896 const UString logPath = GetLogPath(i); 897 if (logPath.Len() >= fullPath.Len()) 898 continue; 899 if (CompareFileNames(logPath, fullPath.RightPtr(logPath.Len())) != 0) 900 continue; 901 902 const UString prefix = fullPath.Left(fullPath.Len() - logPath.Len()); 903 if (!IsPathSepar(prefix.Back())) 904 continue; 905 906 unsigned rootPrefixSize = GetRootPrefixSize(prefix); 907 if (rootPrefixSize == 0) 908 continue; 909 if (rootPrefixSize == prefix.Len()) 910 continue; // simple case: paths are from root 911 912 if (link.Len() <= prefix.Len()) 913 continue; 914 915 if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0) 916 continue; 917 918 UString newLink = prefix.Left(rootPrefixSize); 919 newLink += link.Ptr(prefix.Len()); 920 921 CByteBuffer data; 922 if (!FillLinkData(data, newLink, attr.IsSymLink())) 923 continue; 924 item.ReparseData2 = data; 925 } 926 } 927 928 #endif 929 930 931 932 static const char * const kCannotFindArchive = "Cannot find archive"; 933 934 HRESULT EnumerateDirItemsAndSort( 935 NWildcard::CCensor &censor, 936 NWildcard::ECensorPathMode censorPathMode, 937 const UString &addPathPrefix, 938 UStringVector &sortedPaths, 939 UStringVector &sortedFullPaths, 940 CDirItemsStat &st, 941 IDirItemsCallback *callback) 942 { 943 FStringVector paths; 944 945 { 946 CDirItems dirItems; 947 dirItems.Callback = callback; 948 { 949 HRESULT res = EnumerateItems(censor, censorPathMode, addPathPrefix, dirItems); 950 st = dirItems.Stat; 951 RINOK(res); 952 } 953 954 FOR_VECTOR (i, dirItems.Items) 955 { 956 const CDirItem &dirItem = dirItems.Items[i]; 957 if (!dirItem.IsDir()) 958 paths.Add(dirItems.GetPhyPath(i)); 959 } 960 } 961 962 if (paths.Size() == 0) 963 { 964 // return S_OK; 965 throw CMessagePathException(kCannotFindArchive); 966 } 967 968 UStringVector fullPaths; 969 970 unsigned i; 971 972 for (i = 0; i < paths.Size(); i++) 973 { 974 FString fullPath; 975 NFile::NDir::MyGetFullPathName(paths[i], fullPath); 976 fullPaths.Add(fs2us(fullPath)); 977 } 978 979 CUIntVector indices; 980 SortFileNames(fullPaths, indices); 981 sortedPaths.ClearAndReserve(indices.Size()); 982 sortedFullPaths.ClearAndReserve(indices.Size()); 983 984 for (i = 0; i < indices.Size(); i++) 985 { 986 unsigned index = indices[i]; 987 sortedPaths.AddInReserved(fs2us(paths[index])); 988 sortedFullPaths.AddInReserved(fullPaths[index]); 989 if (i > 0 && CompareFileNames(sortedFullPaths[i], sortedFullPaths[i - 1]) == 0) 990 throw CMessagePathException("Duplicate archive path:", sortedFullPaths[i]); 991 } 992 993 return S_OK; 994 } 995 996 997 998 999 #ifdef _WIN32 1000 1001 // This code converts all short file names to long file names. 1002 1003 static void ConvertToLongName(const UString &prefix, UString &name) 1004 { 1005 if (name.IsEmpty() || DoesNameContainWildcard(name)) 1006 return; 1007 NFind::CFileInfo fi; 1008 const FString path (us2fs(prefix + name)); 1009 #ifndef UNDER_CE 1010 if (NFile::NName::IsDevicePath(path)) 1011 return; 1012 #endif 1013 if (fi.Find(path)) 1014 name = fs2us(fi.Name); 1015 } 1016 1017 static void ConvertToLongNames(const UString &prefix, CObjectVector<NWildcard::CItem> &items) 1018 { 1019 FOR_VECTOR (i, items) 1020 { 1021 NWildcard::CItem &item = items[i]; 1022 if (item.Recursive || item.PathParts.Size() != 1) 1023 continue; 1024 if (prefix.IsEmpty() && item.IsDriveItem()) 1025 continue; 1026 ConvertToLongName(prefix, item.PathParts.Front()); 1027 } 1028 } 1029 1030 static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node) 1031 { 1032 ConvertToLongNames(prefix, node.IncludeItems); 1033 ConvertToLongNames(prefix, node.ExcludeItems); 1034 unsigned i; 1035 for (i = 0; i < node.SubNodes.Size(); i++) 1036 { 1037 UString &name = node.SubNodes[i].Name; 1038 if (prefix.IsEmpty() && NWildcard::IsDriveColonName(name)) 1039 continue; 1040 ConvertToLongName(prefix, name); 1041 } 1042 // mix folders with same name 1043 for (i = 0; i < node.SubNodes.Size(); i++) 1044 { 1045 NWildcard::CCensorNode &nextNode1 = node.SubNodes[i]; 1046 for (unsigned j = i + 1; j < node.SubNodes.Size();) 1047 { 1048 const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j]; 1049 if (nextNode1.Name.IsEqualTo_NoCase(nextNode2.Name)) 1050 { 1051 nextNode1.IncludeItems += nextNode2.IncludeItems; 1052 nextNode1.ExcludeItems += nextNode2.ExcludeItems; 1053 node.SubNodes.Delete(j); 1054 } 1055 else 1056 j++; 1057 } 1058 } 1059 for (i = 0; i < node.SubNodes.Size(); i++) 1060 { 1061 NWildcard::CCensorNode &nextNode = node.SubNodes[i]; 1062 ConvertToLongNames(prefix + nextNode.Name + WCHAR_PATH_SEPARATOR, nextNode); 1063 } 1064 } 1065 1066 void ConvertToLongNames(NWildcard::CCensor &censor) 1067 { 1068 FOR_VECTOR (i, censor.Pairs) 1069 { 1070 NWildcard::CPair &pair = censor.Pairs[i]; 1071 ConvertToLongNames(pair.Prefix, pair.Head); 1072 } 1073 } 1074 1075 #endif 1076 1077 1078 CMessagePathException::CMessagePathException(const char *a, const wchar_t *u) 1079 { 1080 (*this) += a; 1081 if (u) 1082 { 1083 Add_LF(); 1084 (*this) += u; 1085 } 1086 }