1// EnumDirItems.cpp 2 3#include "StdAfx.h" 4 5#include <wchar.h> 6// #include <stdio.h> 7 8#ifndef _WIN32 9#include <grp.h> 10#include <pwd.h> 11#include "../../../Common/UTFConvert.h" 12#endif 13 14#include "../../../Common/Wildcard.h" 15 16#include "../../../Windows/FileDir.h" 17#include "../../../Windows/FileIO.h" 18#include "../../../Windows/FileName.h" 19 20#if defined(_WIN32) && !defined(UNDER_CE) 21#define Z7_USE_SECURITY_CODE 22#include "../../../Windows/SecurityUtils.h" 23#endif 24 25#include "EnumDirItems.h" 26#include "SortUtils.h" 27 28using namespace NWindows; 29using namespace NFile; 30using namespace NName; 31 32 33static bool FindFile_KeepDots(NFile::NFind::CFileInfo &fi, const FString &path, bool followLink) 34{ 35 const bool res = fi.Find(path, followLink); 36 if (!res) 37 return res; 38 if (path.IsEmpty()) 39 return res; 40 // we keep name "." and "..", if it's without tail slash 41 const FChar *p = path.RightPtr(1); 42 if (*p != '.') 43 return res; 44 if (p != path.Ptr()) 45 { 46 FChar c = p[-1]; 47 if (!IS_PATH_SEPAR(c)) 48 { 49 if (c != '.') 50 return res; 51 p--; 52 if (p != path.Ptr()) 53 { 54 c = p[-1]; 55 if (!IS_PATH_SEPAR(c)) 56 return res; 57 } 58 } 59 } 60 fi.Name = p; 61 return res; 62} 63 64 65void CDirItems::AddDirFileInfo(int phyParent, int logParent, int secureIndex, 66 const NFind::CFileInfo &fi) 67{ 68 /* 69 CDirItem di(fi); 70 di.PhyParent = phyParent; 71 di.LogParent = logParent; 72 di.SecureIndex = secureIndex; 73 Items.Add(di); 74 */ 75 VECTOR_ADD_NEW_OBJECT (Items, CDirItem(fi, phyParent, logParent, secureIndex)) 76 77 if (fi.IsDir()) 78 Stat.NumDirs++; 79 #ifdef _WIN32 80 else if (fi.IsAltStream) 81 { 82 Stat.NumAltStreams++; 83 Stat.AltStreamsSize += fi.Size; 84 } 85 #endif 86 else 87 { 88 Stat.NumFiles++; 89 Stat.FilesSize += fi.Size; 90 } 91} 92 93// (DWORD)E_FAIL 94#define DI_DEFAULT_ERROR ERROR_INVALID_FUNCTION 95 96HRESULT CDirItems::AddError(const FString &path, DWORD errorCode) 97{ 98 if (errorCode == 0) 99 errorCode = DI_DEFAULT_ERROR; 100 Stat.NumErrors++; 101 if (Callback) 102 return Callback->ScanError(path, errorCode); 103 return S_OK; 104} 105 106HRESULT CDirItems::AddError(const FString &path) 107{ 108 return AddError(path, ::GetLastError()); 109} 110 111static const unsigned kScanProgressStepMask = (1 << 12) - 1; 112 113HRESULT CDirItems::ScanProgress(const FString &dirPath) 114{ 115 if (Callback) 116 return Callback->ScanProgress(Stat, dirPath, true); 117 return S_OK; 118} 119 120UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const 121{ 122 UString path; 123 unsigned len = name.Len(); 124 125 int i; 126 for (i = index; i >= 0; i = parents[(unsigned)i]) 127 len += Prefixes[(unsigned)i].Len(); 128 129 wchar_t *p = path.GetBuf_SetEnd(len) + len; 130 131 p -= name.Len(); 132 wmemcpy(p, (const wchar_t *)name, name.Len()); 133 134 for (i = index; i >= 0; i = parents[(unsigned)i]) 135 { 136 const UString &s = Prefixes[(unsigned)i]; 137 p -= s.Len(); 138 wmemcpy(p, (const wchar_t *)s, s.Len()); 139 } 140 141 return path; 142} 143 144FString CDirItems::GetPhyPath(unsigned index) const 145{ 146 const CDirItem &di = Items[index]; 147 return us2fs(GetPrefixesPath(PhyParents, di.PhyParent, di.Name)); 148} 149 150UString CDirItems::GetLogPath(unsigned index) const 151{ 152 const CDirItem &di = Items[index]; 153 return GetPrefixesPath(LogParents, di.LogParent, di.Name); 154} 155 156void CDirItems::ReserveDown() 157{ 158 Prefixes.ReserveDown(); 159 PhyParents.ReserveDown(); 160 LogParents.ReserveDown(); 161 Items.ReserveDown(); 162} 163 164unsigned CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix) 165{ 166 PhyParents.Add(phyParent); 167 LogParents.Add(logParent); 168 return Prefixes.Add(prefix); 169} 170 171void CDirItems::DeleteLastPrefix() 172{ 173 PhyParents.DeleteBack(); 174 LogParents.DeleteBack(); 175 Prefixes.DeleteBack(); 176} 177 178bool InitLocalPrivileges(); 179 180CDirItems::CDirItems(): 181 SymLinks(false), 182 ScanAltStreams(false) 183 , ExcludeDirItems(false) 184 , ExcludeFileItems(false) 185 , ShareForWrite(false) 186 #ifdef Z7_USE_SECURITY_CODE 187 , ReadSecure(false) 188 #endif 189 #ifndef _WIN32 190 , StoreOwnerName(false) 191 #endif 192 , Callback(NULL) 193{ 194 #ifdef Z7_USE_SECURITY_CODE 195 _saclEnabled = InitLocalPrivileges(); 196 #endif 197} 198 199 200#ifdef Z7_USE_SECURITY_CODE 201 202HRESULT CDirItems::AddSecurityItem(const FString &path, int &secureIndex) 203{ 204 secureIndex = -1; 205 206 SECURITY_INFORMATION securInfo = 207 DACL_SECURITY_INFORMATION | 208 GROUP_SECURITY_INFORMATION | 209 OWNER_SECURITY_INFORMATION; 210 if (_saclEnabled) 211 securInfo |= SACL_SECURITY_INFORMATION; 212 213 DWORD errorCode = 0; 214 DWORD secureSize; 215 216 BOOL res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(void *)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); 217 218 if (res) 219 { 220 if (secureSize == 0) 221 return S_OK; 222 if (secureSize > TempSecureBuf.Size()) 223 errorCode = ERROR_INVALID_FUNCTION; 224 } 225 else 226 { 227 errorCode = GetLastError(); 228 if (errorCode == ERROR_INSUFFICIENT_BUFFER) 229 { 230 if (secureSize <= TempSecureBuf.Size()) 231 errorCode = ERROR_INVALID_FUNCTION; 232 else 233 { 234 TempSecureBuf.Alloc(secureSize); 235 res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(void *)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); 236 if (res) 237 { 238 if (secureSize != TempSecureBuf.Size()) 239 errorCode = ERROR_INVALID_FUNCTION; 240 } 241 else 242 errorCode = GetLastError(); 243 } 244 } 245 } 246 247 if (res) 248 { 249 secureIndex = (int)SecureBlocks.AddUniq(TempSecureBuf, secureSize); 250 return S_OK; 251 } 252 253 return AddError(path, errorCode); 254} 255 256#endif // Z7_USE_SECURITY_CODE 257 258 259HRESULT CDirItems::EnumerateOneDir(const FString &phyPrefix, CObjectVector<NFind::CFileInfo> &files) 260{ 261 NFind::CEnumerator enumerator; 262 // printf("\n enumerator.SetDirPrefix(phyPrefix) \n"); 263 264 enumerator.SetDirPrefix(phyPrefix); 265 266 #ifdef _WIN32 267 268 NFind::CFileInfo fi; 269 270 for (unsigned ttt = 0; ; ttt++) 271 { 272 bool found; 273 if (!enumerator.Next(fi, found)) 274 return AddError(phyPrefix); 275 if (!found) 276 return S_OK; 277 files.Add(fi); 278 if (Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask) 279 { 280 RINOK(ScanProgress(phyPrefix)) 281 } 282 } 283 284 #else // _WIN32 285 286 // enumerator.SolveLinks = !SymLinks; 287 288 CObjectVector<NFind::CDirEntry> entries; 289 290 for (unsigned ttt = 0; ; ttt++) 291 { 292 bool found; 293 NFind::CDirEntry de; 294 if (!enumerator.Next(de, found)) 295 { 296 return AddError(phyPrefix); 297 } 298 if (!found) 299 break; 300 entries.Add(de); 301 } 302 303 FOR_VECTOR(i, entries) 304 { 305 const NFind::CDirEntry &de = entries[i]; 306 NFind::CFileInfo fi; 307 if (!enumerator.Fill_FileInfo(de, fi, !SymLinks)) 308 // if (!fi.Find_AfterEnumerator(path)) 309 { 310 const FString path = phyPrefix + de.Name; 311 { 312 RINOK(AddError(path)) 313 continue; 314 } 315 } 316 317 files.Add(fi); 318 319 if (Callback && (i & kScanProgressStepMask) == kScanProgressStepMask) 320 { 321 RINOK(ScanProgress(phyPrefix)) 322 } 323 } 324 325 return S_OK; 326 327 #endif // _WIN32 328} 329 330 331 332 333HRESULT CDirItems::EnumerateDir(int phyParent, int logParent, const FString &phyPrefix) 334{ 335 RINOK(ScanProgress(phyPrefix)) 336 337 CObjectVector<NFind::CFileInfo> files; 338 RINOK(EnumerateOneDir(phyPrefix, files)) 339 340 FOR_VECTOR (i, files) 341 { 342 #ifdef _WIN32 343 const NFind::CFileInfo &fi = files[i]; 344 #else 345 const NFind::CFileInfo &fi = files[i]; 346 /* 347 NFind::CFileInfo fi; 348 { 349 const NFind::CDirEntry &di = files[i]; 350 const FString path = phyPrefix + di.Name; 351 if (!fi.Find_AfterEnumerator(path)) 352 { 353 RINOK(AddError(path)); 354 continue; 355 } 356 fi.Name = di.Name; 357 } 358 */ 359 #endif 360 361 if (CanIncludeItem(fi.IsDir())) 362 { 363 int secureIndex = -1; 364 #ifdef Z7_USE_SECURITY_CODE 365 if (ReadSecure) 366 { 367 RINOK(AddSecurityItem(phyPrefix + fi.Name, secureIndex)) 368 } 369 #endif 370 AddDirFileInfo(phyParent, logParent, secureIndex, fi); 371 } 372 373 if (Callback && (i & kScanProgressStepMask) == kScanProgressStepMask) 374 { 375 RINOK(ScanProgress(phyPrefix)) 376 } 377 378 if (fi.IsDir()) 379 { 380 const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; 381 unsigned parent = AddPrefix(phyParent, logParent, fs2us(name2)); 382 RINOK(EnumerateDir((int)parent, (int)parent, phyPrefix + name2)) 383 } 384 } 385 return S_OK; 386} 387 388 389/* 390EnumerateItems2() 391 const FStringVector &filePaths - are path without tail slashes. 392 All dir prefixes of filePaths will be not stores in logical paths 393fix it: we can scan AltStream also. 394*/ 395 396#ifdef _WIN32 397// #define FOLLOW_LINK_PARAM 398// #define FOLLOW_LINK_PARAM2 399#define FOLLOW_LINK_PARAM , (!SymLinks) 400#define FOLLOW_LINK_PARAM2 , (!dirItems.SymLinks) 401#else 402#define FOLLOW_LINK_PARAM , (!SymLinks) 403#define FOLLOW_LINK_PARAM2 , (!dirItems.SymLinks) 404#endif 405 406HRESULT CDirItems::EnumerateItems2( 407 const FString &phyPrefix, 408 const UString &logPrefix, 409 const FStringVector &filePaths, 410 FStringVector *requestedPaths) 411{ 412 const int phyParent = phyPrefix.IsEmpty() ? -1 : (int)AddPrefix(-1, -1, fs2us(phyPrefix)); 413 const int logParent = logPrefix.IsEmpty() ? -1 : (int)AddPrefix(-1, -1, logPrefix); 414 415 #ifdef _WIN32 416 const bool phyPrefix_isAltStreamPrefix = 417 NFile::NName::IsAltStreamPrefixWithColon(fs2us(phyPrefix)); 418 #endif 419 420 FOR_VECTOR (i, filePaths) 421 { 422 const FString &filePath = filePaths[i]; 423 NFind::CFileInfo fi; 424 const FString phyPath = phyPrefix + filePath; 425 if (!FindFile_KeepDots(fi, phyPath FOLLOW_LINK_PARAM)) 426 { 427 RINOK(AddError(phyPath)) 428 continue; 429 } 430 if (requestedPaths) 431 requestedPaths->Add(phyPath); 432 433 const int delimiter = filePath.ReverseFind_PathSepar(); 434 FString phyPrefixCur; 435 int phyParentCur = phyParent; 436 if (delimiter >= 0) 437 { 438 phyPrefixCur.SetFrom(filePath, (unsigned)(delimiter + 1)); 439 phyParentCur = (int)AddPrefix(phyParent, logParent, fs2us(phyPrefixCur)); 440 } 441 442 if (CanIncludeItem(fi.IsDir())) 443 { 444 int secureIndex = -1; 445 #ifdef Z7_USE_SECURITY_CODE 446 if (ReadSecure) 447 { 448 RINOK(AddSecurityItem(phyPath, secureIndex)) 449 } 450 #endif 451 #ifdef _WIN32 452 if (phyPrefix_isAltStreamPrefix && fi.IsAltStream) 453 { 454 const int pos = fi.Name.Find(FChar(':')); 455 if (pos >= 0) 456 fi.Name.DeleteFrontal((unsigned)pos + 1); 457 } 458 #endif 459 AddDirFileInfo(phyParentCur, logParent, secureIndex, fi); 460 } 461 462 if (fi.IsDir()) 463 { 464 const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; 465 const unsigned parent = AddPrefix(phyParentCur, logParent, fs2us(name2)); 466 RINOK(EnumerateDir((int)parent, (int)parent, phyPrefix + phyPrefixCur + name2)) 467 } 468 } 469 470 ReserveDown(); 471 return S_OK; 472} 473 474 475 476 477static HRESULT EnumerateDirItems( 478 const NWildcard::CCensorNode &curNode, 479 const int phyParent, const int logParent, 480 const FString &phyPrefix, 481 const UStringVector &addParts, // additional parts from curNode 482 CDirItems &dirItems, 483 bool enterToSubFolders); 484 485 486/* EnumerateDirItems_Spec() 487 adds new Dir item prefix, and enumerates dir items, 488 then it can remove that Dir item prefix, if there are no items in that dir. 489*/ 490 491 492/* 493 EnumerateDirItems_Spec() 494 it's similar to EnumerateDirItems, but phyPrefix doesn't include (curFolderName) 495*/ 496 497static HRESULT EnumerateDirItems_Spec( 498 const NWildcard::CCensorNode &curNode, 499 const int phyParent, const int logParent, const FString &curFolderName, 500 const FString &phyPrefix, // without (curFolderName) 501 const UStringVector &addParts, // (curNode + addParts) includes (curFolderName) 502 CDirItems &dirItems, 503 bool enterToSubFolders) 504{ 505 const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR; 506 const unsigned parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2)); 507 const unsigned numItems = dirItems.Items.Size(); 508 HRESULT res = EnumerateDirItems( 509 curNode, (int)parent, (int)parent, phyPrefix + name2, 510 addParts, dirItems, enterToSubFolders); 511 if (numItems == dirItems.Items.Size()) 512 dirItems.DeleteLastPrefix(); 513 return res; 514} 515 516 517#ifndef UNDER_CE 518 519#ifdef _WIN32 520 521static HRESULT EnumerateAltStreams( 522 const NFind::CFileInfo &fi, 523 const NWildcard::CCensorNode &curNode, 524 const int phyParent, const int logParent, 525 const FString &phyPath, // with (fi.Name), without tail slash for folders 526 const UStringVector &addParts, // with (fi.Name), prefix parts from curNode 527 bool addAllSubStreams, 528 CDirItems &dirItems) 529{ 530 // we don't use (ExcludeFileItems) rules for AltStreams 531 // if (dirItems.ExcludeFileItems) return S_OK; 532 533 NFind::CStreamEnumerator enumerator(phyPath); 534 for (;;) 535 { 536 NFind::CStreamInfo si; 537 bool found; 538 if (!enumerator.Next(si, found)) 539 { 540 return dirItems.AddError(phyPath + FTEXT(":*")); // , (DWORD)E_FAIL 541 } 542 if (!found) 543 return S_OK; 544 if (si.IsMainStream()) 545 continue; 546 UStringVector parts = addParts; 547 const UString reducedName = si.GetReducedName(); 548 parts.Back() += reducedName; 549 if (curNode.CheckPathToRoot(false, parts, true)) 550 continue; 551 if (!addAllSubStreams) 552 if (!curNode.CheckPathToRoot(true, parts, true)) 553 continue; 554 555 NFind::CFileInfo fi2 = fi; 556 fi2.Name += us2fs(reducedName); 557 fi2.Size = si.Size; 558 fi2.Attrib &= ~(DWORD)(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT); 559 fi2.IsAltStream = true; 560 dirItems.AddDirFileInfo(phyParent, logParent, -1, fi2); 561 } 562} 563 564#endif // _WIN32 565 566 567/* We get Reparse data and parse it. 568 If there is Reparse error, we free dirItem.Reparse data. 569 Do we need to work with empty reparse data? 570*/ 571 572HRESULT CDirItems::SetLinkInfo(CDirItem &dirItem, const NFind::CFileInfo &fi, 573 const FString &phyPrefix) 574{ 575 if (!SymLinks) 576 return S_OK; 577 578 #ifdef _WIN32 579 if (!fi.HasReparsePoint() || fi.IsAltStream) 580 #else // _WIN32 581 if (!fi.IsPosixLink()) 582 #endif // _WIN32 583 return S_OK; 584 585 const FString path = phyPrefix + fi.Name; 586 CByteBuffer &buf = dirItem.ReparseData; 587 if (NIO::GetReparseData(path, buf)) 588 { 589 // if (dirItem.ReparseData.Size() != 0) 590 Stat.FilesSize -= fi.Size; 591 return S_OK; 592 } 593 594 DWORD res = ::GetLastError(); 595 buf.Free(); 596 return AddError(path, res); 597} 598 599#endif // UNDER_CE 600 601 602 603static HRESULT EnumerateForItem( 604 const NFind::CFileInfo &fi, 605 const NWildcard::CCensorNode &curNode, 606 const int phyParent, const int logParent, const FString &phyPrefix, 607 const UStringVector &addParts, // additional parts from curNode, without (fi.Name) 608 CDirItems &dirItems, 609 bool enterToSubFolders) 610{ 611 const UString name = fs2us(fi.Name); 612 UStringVector newParts = addParts; 613 newParts.Add(name); 614 615 // check the path in exclude rules 616 if (curNode.CheckPathToRoot(false, newParts, !fi.IsDir())) 617 return S_OK; 618 619 #if !defined(UNDER_CE) 620 int dirItemIndex = -1; 621 #if defined(_WIN32) 622 bool addAllSubStreams = false; 623 bool needAltStreams = true; 624 #endif // _WIN32 625 #endif // !defined(UNDER_CE) 626 627 // check the path in inlcude rules 628 if (curNode.CheckPathToRoot(true, newParts, !fi.IsDir())) 629 { 630 #if !defined(UNDER_CE) 631 // dirItemIndex = (int)dirItems.Items.Size(); 632 #if defined(_WIN32) 633 // we will not check include rules for substreams. 634 addAllSubStreams = true; 635 #endif // _WIN32 636 #endif // !defined(UNDER_CE) 637 638 if (dirItems.CanIncludeItem(fi.IsDir())) 639 { 640 int secureIndex = -1; 641 #ifdef Z7_USE_SECURITY_CODE 642 if (dirItems.ReadSecure) 643 { 644 RINOK(dirItems.AddSecurityItem(phyPrefix + fi.Name, secureIndex)) 645 } 646 #endif 647 #if !defined(UNDER_CE) 648 dirItemIndex = (int)dirItems.Items.Size(); 649 #endif // !defined(UNDER_CE) 650 dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi); 651 } 652 else 653 { 654 #if defined(_WIN32) && !defined(UNDER_CE) 655 needAltStreams = false; 656 #endif 657 } 658 659 if (fi.IsDir()) 660 enterToSubFolders = true; 661 } 662 663 #if !defined(UNDER_CE) 664 665 // we don't scan AltStreams for link files 666 667 if (dirItemIndex >= 0) 668 { 669 CDirItem &dirItem = dirItems.Items[(unsigned)dirItemIndex]; 670 RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix)) 671 if (dirItem.ReparseData.Size() != 0) 672 return S_OK; 673 } 674 675 #if defined(_WIN32) 676 if (needAltStreams && dirItems.ScanAltStreams) 677 { 678 RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent, 679 phyPrefix + fi.Name, // with (fi.Name) 680 newParts, // with (fi.Name) 681 addAllSubStreams, 682 dirItems)) 683 } 684 #endif 685 686 #endif // !defined(UNDER_CE) 687 688 689 #ifndef _WIN32 690 if (!fi.IsPosixLink()) // posix link can follow to dir 691 #endif 692 if (!fi.IsDir()) 693 return S_OK; 694 695 const NWildcard::CCensorNode *nextNode = NULL; 696 697 if (addParts.IsEmpty()) 698 { 699 int index = curNode.FindSubNode(name); 700 if (index >= 0) 701 { 702 nextNode = &curNode.SubNodes[(unsigned)index]; 703 newParts.Clear(); 704 } 705 } 706 707 if (!nextNode) 708 { 709 if (!enterToSubFolders) 710 return S_OK; 711 712 #ifndef _WIN32 713 if (fi.IsPosixLink()) 714 { 715 // here we can try to resolve posix link 716 // if the link to dir, then can we follow it 717 return S_OK; // we don't follow posix link 718 } 719 #else 720 if (dirItems.SymLinks && fi.HasReparsePoint()) 721 { 722 /* 20.03: in SymLinks mode: we don't enter to directory that 723 has reparse point and has no CCensorNode 724 NOTE: (curNode and parent nodes) still can have wildcard rules 725 to include some items of target directory (of reparse point), 726 but we ignore these rules here. 727 */ 728 return S_OK; 729 } 730 #endif 731 nextNode = &curNode; 732 } 733 734 return EnumerateDirItems_Spec( 735 *nextNode, phyParent, logParent, fi.Name, 736 phyPrefix, // without (fi.Name) 737 newParts, // relative to (*nextNode). (*nextNode + newParts) includes (fi.Name) 738 dirItems, 739 enterToSubFolders); 740} 741 742 743static bool CanUseFsDirect(const NWildcard::CCensorNode &curNode) 744{ 745 FOR_VECTOR (i, curNode.IncludeItems) 746 { 747 const NWildcard::CItem &item = curNode.IncludeItems[i]; 748 if (item.Recursive || item.PathParts.Size() != 1) 749 return false; 750 const UString &name = item.PathParts.Front(); 751 /* 752 if (name.IsEmpty()) 753 return false; 754 */ 755 756 /* Windows doesn't support file name with wildcard 757 But if another system supports file name with wildcard, 758 and wildcard mode is disabled, we can ignore wildcard in name 759 */ 760 /* 761 #ifndef _WIN32 762 if (!item.WildcardParsing) 763 continue; 764 #endif 765 */ 766 if (DoesNameContainWildcard(name)) 767 return false; 768 } 769 return true; 770} 771 772 773#if defined(_WIN32) && !defined(UNDER_CE) 774 775static bool IsVirtualFsFolder(const FString &prefix, const UString &name) 776{ 777 UString s = fs2us(prefix); 778 s += name; 779 s.Add_PathSepar(); 780 // it returns (true) for non real FS folder path like - "\\SERVER\" 781 return IsPathSepar(s[0]) && GetRootPrefixSize(s) == 0; 782} 783 784#endif 785 786 787 788static HRESULT EnumerateDirItems( 789 const NWildcard::CCensorNode &curNode, 790 const int phyParent, const int logParent, const FString &phyPrefix, 791 const UStringVector &addParts, // prefix from curNode including 792 CDirItems &dirItems, 793 bool enterToSubFolders) 794{ 795 if (!enterToSubFolders) 796 { 797 /* if there are IncludeItems censor rules that affect items in subdirs, 798 then we will enter to all subfolders */ 799 if (curNode.NeedCheckSubDirs()) 800 enterToSubFolders = true; 801 } 802 803 RINOK(dirItems.ScanProgress(phyPrefix)) 804 805 // try direct_names case at first 806 if (addParts.IsEmpty() && !enterToSubFolders) 807 { 808 if (CanUseFsDirect(curNode)) 809 { 810 // all names are direct (no wildcards) 811 // so we don't need file_system's dir enumerator 812 CRecordVector<bool> needEnterVector; 813 unsigned i; 814 815 for (i = 0; i < curNode.IncludeItems.Size(); i++) 816 { 817 const NWildcard::CItem &item = curNode.IncludeItems[i]; 818 const UString &name = item.PathParts.Front(); 819 FString fullPath = phyPrefix + us2fs(name); 820 821 /* 822 // not possible now 823 if (!item.ForDir && !item.ForFile) 824 { 825 RINOK(dirItems.AddError(fullPath, ERROR_INVALID_PARAMETER)); 826 continue; 827 } 828 */ 829 830 #if defined(_WIN32) && !defined(UNDER_CE) 831 bool needAltStreams = true; 832 #endif 833 834 #ifdef Z7_USE_SECURITY_CODE 835 bool needSecurity = true; 836 #endif 837 838 if (phyPrefix.IsEmpty()) 839 { 840 if (!item.ForFile) 841 { 842 /* we don't like some names for alt streams inside archive: 843 ":sname" for "\" 844 "c:::sname" for "C:\" 845 So we ignore alt streams for these cases */ 846 if (name.IsEmpty()) 847 { 848 #if defined(_WIN32) && !defined(UNDER_CE) 849 needAltStreams = false; 850 #endif 851 852 /* 853 // do we need to ignore security info for "\\" folder ? 854 #ifdef Z7_USE_SECURITY_CODE 855 needSecurity = false; 856 #endif 857 */ 858 859 fullPath = CHAR_PATH_SEPARATOR; 860 } 861 #if defined(_WIN32) && !defined(UNDER_CE) 862 else if (item.IsDriveItem()) 863 { 864 needAltStreams = false; 865 fullPath.Add_PathSepar(); 866 } 867 #endif 868 } 869 } 870 871 NFind::CFileInfo fi; 872 #if defined(_WIN32) && !defined(UNDER_CE) 873 if (IsVirtualFsFolder(phyPrefix, name)) 874 { 875 fi.SetAsDir(); 876 fi.Name = us2fs(name); 877 } 878 else 879 #endif 880 if (!FindFile_KeepDots(fi, fullPath FOLLOW_LINK_PARAM2)) 881 { 882 RINOK(dirItems.AddError(fullPath)) 883 continue; 884 } 885 886 /* 887 #ifdef _WIN32 888 #define MY_ERROR_IS_DIR ERROR_FILE_NOT_FOUND 889 #define MY_ERROR_NOT_DIR DI_DEFAULT_ERROR 890 #else 891 #define MY_ERROR_IS_DIR EISDIR 892 #define MY_ERROR_NOT_DIR ENOTDIR 893 #endif 894 */ 895 896 const bool isDir = fi.IsDir(); 897 if (isDir ? !item.ForDir : !item.ForFile) 898 { 899 // RINOK(dirItems.AddError(fullPath, isDir ? MY_ERROR_IS_DIR: MY_ERROR_NOT_DIR)); 900 RINOK(dirItems.AddError(fullPath, DI_DEFAULT_ERROR)) 901 continue; 902 } 903 { 904 UStringVector pathParts; 905 pathParts.Add(fs2us(fi.Name)); 906 if (curNode.CheckPathToRoot(false, pathParts, !isDir)) 907 continue; 908 } 909 910 911 if (dirItems.CanIncludeItem(fi.IsDir())) 912 { 913 int secureIndex = -1; 914 #ifdef Z7_USE_SECURITY_CODE 915 if (needSecurity && dirItems.ReadSecure) 916 { 917 RINOK(dirItems.AddSecurityItem(fullPath, secureIndex)) 918 } 919 #endif 920 921 dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi); 922 923 // we don't scan AltStreams for link files 924 925 #if !defined(UNDER_CE) 926 { 927 CDirItem &dirItem = dirItems.Items.Back(); 928 RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix)) 929 if (dirItem.ReparseData.Size() != 0) 930 continue; 931 } 932 933 #if defined(_WIN32) 934 if (needAltStreams && dirItems.ScanAltStreams) 935 { 936 UStringVector pathParts; 937 pathParts.Add(fs2us(fi.Name)); 938 RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent, 939 fullPath, // including (name) 940 pathParts, // including (fi.Name) 941 true, /* addAllSubStreams */ 942 dirItems)) 943 } 944 #endif // defined(_WIN32) 945 946 #endif // !defined(UNDER_CE) 947 } 948 949 950 #ifndef _WIN32 951 if (!fi.IsPosixLink()) // posix link can follow to dir 952 #endif 953 if (!isDir) 954 continue; 955 956 UStringVector newParts; 957 const NWildcard::CCensorNode *nextNode = NULL; 958 int index = curNode.FindSubNode(name); 959 if (index >= 0) 960 { 961 for (int t = (int)needEnterVector.Size(); t <= index; t++) 962 needEnterVector.Add(true); 963 needEnterVector[(unsigned)index] = false; 964 nextNode = &curNode.SubNodes[(unsigned)index]; 965 } 966 else 967 { 968 #ifndef _WIN32 969 if (fi.IsPosixLink()) 970 { 971 // here we can try to resolve posix link 972 // if the link to dir, then can we follow it 973 continue; // we don't follow posix link 974 } 975 #else 976 if (dirItems.SymLinks) 977 { 978 if (fi.HasReparsePoint()) 979 { 980 /* 20.03: in SymLinks mode: we don't enter to directory that 981 has reparse point and has no CCensorNode */ 982 continue; 983 } 984 } 985 #endif 986 nextNode = &curNode; 987 newParts.Add(name); // don't change it to fi.Name. It's for shortnames support 988 } 989 990 RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix, 991 newParts, dirItems, true)) 992 } 993 994 for (i = 0; i < curNode.SubNodes.Size(); i++) 995 { 996 if (i < needEnterVector.Size()) 997 if (!needEnterVector[i]) 998 continue; 999 const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i]; 1000 FString fullPath = phyPrefix + us2fs(nextNode.Name); 1001 NFind::CFileInfo fi; 1002 1003 if (nextNode.Name.IsEmpty()) 1004 { 1005 if (phyPrefix.IsEmpty()) 1006 fullPath = CHAR_PATH_SEPARATOR; 1007 } 1008 #ifdef _WIN32 1009 else if(phyPrefix.IsEmpty() 1010 || (phyPrefix.Len() == NName::kSuperPathPrefixSize 1011 && IsSuperPath(phyPrefix))) 1012 { 1013 if (NWildcard::IsDriveColonName(nextNode.Name)) 1014 fullPath.Add_PathSepar(); 1015 } 1016 #endif 1017 1018 // we don't want to call fi.Find() for root folder or virtual folder 1019 if ((phyPrefix.IsEmpty() && nextNode.Name.IsEmpty()) 1020 #if defined(_WIN32) && !defined(UNDER_CE) 1021 || IsVirtualFsFolder(phyPrefix, nextNode.Name) 1022 #endif 1023 ) 1024 { 1025 fi.SetAsDir(); 1026 fi.Name = us2fs(nextNode.Name); 1027 } 1028 else 1029 { 1030 if (!FindFile_KeepDots(fi, fullPath FOLLOW_LINK_PARAM2)) 1031 { 1032 if (!nextNode.AreThereIncludeItems()) 1033 continue; 1034 RINOK(dirItems.AddError(fullPath)) 1035 continue; 1036 } 1037 1038 if (!fi.IsDir()) 1039 { 1040 RINOK(dirItems.AddError(fullPath, DI_DEFAULT_ERROR)) 1041 continue; 1042 } 1043 } 1044 1045 RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix, 1046 UStringVector(), dirItems, false)) 1047 } 1048 1049 return S_OK; 1050 } 1051 } 1052 1053 #ifdef _WIN32 1054 #ifndef UNDER_CE 1055 1056 // scan drives, if wildcard is "*:\" 1057 1058 if (phyPrefix.IsEmpty() && curNode.IncludeItems.Size() > 0) 1059 { 1060 unsigned i; 1061 for (i = 0; i < curNode.IncludeItems.Size(); i++) 1062 { 1063 const NWildcard::CItem &item = curNode.IncludeItems[i]; 1064 if (item.PathParts.Size() < 1) 1065 break; 1066 const UString &name = item.PathParts.Front(); 1067 if (name.Len() != 2 || name[1] != ':') 1068 break; 1069 if (item.PathParts.Size() == 1) 1070 if (item.ForFile || !item.ForDir) 1071 break; 1072 if (NWildcard::IsDriveColonName(name)) 1073 continue; 1074 if (name[0] != '*' && name[0] != '?') 1075 break; 1076 } 1077 if (i == curNode.IncludeItems.Size()) 1078 { 1079 FStringVector driveStrings; 1080 NFind::MyGetLogicalDriveStrings(driveStrings); 1081 for (i = 0; i < driveStrings.Size(); i++) 1082 { 1083 FString driveName = driveStrings[i]; 1084 if (driveName.Len() < 3 || driveName.Back() != '\\') 1085 return E_FAIL; 1086 driveName.DeleteBack(); 1087 NFind::CFileInfo fi; 1088 fi.SetAsDir(); 1089 fi.Name = driveName; 1090 1091 RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, 1092 addParts, dirItems, enterToSubFolders)) 1093 } 1094 return S_OK; 1095 } 1096 } 1097 1098 #endif 1099 #endif 1100 1101 1102 CObjectVector<NFind::CFileInfo> files; 1103 1104 // for (int y = 0; y < 1; y++) 1105 { 1106 // files.Clear(); 1107 RINOK(dirItems.EnumerateOneDir(phyPrefix, files)) 1108 /* 1109 FOR_VECTOR (i, files) 1110 { 1111 #ifdef _WIN32 1112 // const NFind::CFileInfo &fi = files[i]; 1113 #else 1114 NFind::CFileInfo &fi = files[i]; 1115 { 1116 const NFind::CFileInfo &di = files[i]; 1117 const FString path = phyPrefix + di.Name; 1118 if (!fi.Find_AfterEnumerator(path)) 1119 { 1120 RINOK(dirItems.AddError(path)); 1121 continue; 1122 } 1123 fi.Name = di.Name; 1124 } 1125 #endif 1126 1127 } 1128 */ 1129 } 1130 1131 FOR_VECTOR (i, files) 1132 { 1133 #ifdef _WIN32 1134 const NFind::CFileInfo &fi = files[i]; 1135 #else 1136 const NFind::CFileInfo &fi = files[i]; 1137 /* 1138 NFind::CFileInfo fi; 1139 { 1140 const NFind::CDirEntry &di = files[i]; 1141 const FString path = phyPrefix + di.Name; 1142 if (!fi.Find_AfterEnumerator(path)) 1143 { 1144 RINOK(dirItems.AddError(path)); 1145 continue; 1146 } 1147 fi.Name = di.Name; 1148 } 1149 */ 1150 #endif 1151 1152 RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, 1153 addParts, dirItems, enterToSubFolders)) 1154 if (dirItems.Callback && (i & kScanProgressStepMask) == kScanProgressStepMask) 1155 { 1156 RINOK(dirItems.ScanProgress(phyPrefix)) 1157 } 1158 } 1159 1160 return S_OK; 1161} 1162 1163 1164 1165 1166HRESULT EnumerateItems( 1167 const NWildcard::CCensor &censor, 1168 const NWildcard::ECensorPathMode pathMode, 1169 const UString &addPathPrefix, // prefix that will be added to Logical Path 1170 CDirItems &dirItems) 1171{ 1172 FOR_VECTOR (i, censor.Pairs) 1173 { 1174 const NWildcard::CPair &pair = censor.Pairs[i]; 1175 const int phyParent = pair.Prefix.IsEmpty() ? -1 : (int)dirItems.AddPrefix(-1, -1, pair.Prefix); 1176 int logParent = -1; 1177 1178 if (pathMode == NWildcard::k_AbsPath) 1179 logParent = phyParent; 1180 else 1181 { 1182 if (!addPathPrefix.IsEmpty()) 1183 logParent = (int)dirItems.AddPrefix(-1, -1, addPathPrefix); 1184 } 1185 1186 RINOK(EnumerateDirItems(pair.Head, phyParent, logParent, us2fs(pair.Prefix), UStringVector(), 1187 dirItems, 1188 false // enterToSubFolders 1189 )) 1190 } 1191 dirItems.ReserveDown(); 1192 1193 #if defined(_WIN32) && !defined(UNDER_CE) 1194 RINOK(dirItems.FillFixedReparse()) 1195 #endif 1196 1197 #ifndef _WIN32 1198 RINOK(dirItems.FillDeviceSizes()) 1199 #endif 1200 1201 return S_OK; 1202} 1203 1204 1205#if defined(_WIN32) && !defined(UNDER_CE) 1206 1207HRESULT CDirItems::FillFixedReparse() 1208{ 1209 FOR_VECTOR(i, Items) 1210 { 1211 CDirItem &item = Items[i]; 1212 1213 if (!SymLinks) 1214 { 1215 // continue; // for debug 1216 if (!item.Has_Attrib_ReparsePoint()) 1217 continue; 1218 1219 // if (item.IsDir()) continue; 1220 1221 const FString phyPath = GetPhyPath(i); 1222 1223 NFind::CFileInfo fi; 1224 if (fi.Fill_From_ByHandleFileInfo(phyPath)) // item.IsDir() 1225 { 1226 item.Size = fi.Size; 1227 item.CTime = fi.CTime; 1228 item.ATime = fi.ATime; 1229 item.MTime = fi.MTime; 1230 item.Attrib = fi.Attrib; 1231 continue; 1232 } 1233 1234 /* 1235 // we request properties of target file instead of properies of symbolic link 1236 // here we also can manually parse unsupported links (like WSL links) 1237 NIO::CInFile inFile; 1238 if (inFile.Open(phyPath)) 1239 { 1240 BY_HANDLE_FILE_INFORMATION info; 1241 if (inFile.GetFileInformation(&info)) 1242 { 1243 // Stat.FilesSize doesn't contain item.Size already 1244 // Stat.FilesSize -= item.Size; 1245 item.Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; 1246 Stat.FilesSize += item.Size; 1247 item.CTime = info.ftCreationTime; 1248 item.ATime = info.ftLastAccessTime; 1249 item.MTime = info.ftLastWriteTime; 1250 item.Attrib = info.dwFileAttributes; 1251 continue; 1252 } 1253 } 1254 */ 1255 1256 RINOK(AddError(phyPath)) 1257 continue; 1258 } 1259 1260 // (SymLinks == true) here 1261 1262 if (item.ReparseData.Size() == 0) 1263 continue; 1264 1265 // if (item.Size == 0) 1266 { 1267 // 20.03: we use Reparse Data instead of real data 1268 item.Size = item.ReparseData.Size(); 1269 } 1270 1271 CReparseAttr attr; 1272 if (!attr.Parse(item.ReparseData, item.ReparseData.Size())) 1273 { 1274 const FString phyPath = GetPhyPath(i); 1275 AddError(phyPath, attr.ErrorCode); 1276 continue; 1277 } 1278 1279 /* imagex/WIM reduces absolute paths in links (raparse data), 1280 if we archive non root folder. We do same thing here */ 1281 1282 bool isWSL = false; 1283 if (attr.IsSymLink_WSL()) 1284 { 1285 // isWSL = true; 1286 // we don't change WSL symlinks 1287 continue; 1288 } 1289 else 1290 { 1291 if (attr.IsRelative_Win()) 1292 continue; 1293 } 1294 1295 const UString &link = attr.GetPath(); 1296 if (!IsDrivePath(link)) 1297 continue; 1298 // maybe we need to support networks paths also ? 1299 1300 FString fullPathF; 1301 if (!NDir::MyGetFullPathName(GetPhyPath(i), fullPathF)) 1302 continue; 1303 const UString fullPath = fs2us(fullPathF); 1304 const UString logPath = GetLogPath(i); 1305 if (logPath.Len() >= fullPath.Len()) 1306 continue; 1307 if (CompareFileNames(logPath, fullPath.RightPtr(logPath.Len())) != 0) 1308 continue; 1309 1310 const UString prefix = fullPath.Left(fullPath.Len() - logPath.Len()); 1311 if (!IsPathSepar(prefix.Back())) 1312 continue; 1313 1314 const unsigned rootPrefixSize = GetRootPrefixSize(prefix); 1315 if (rootPrefixSize == 0) 1316 continue; 1317 if (rootPrefixSize == prefix.Len()) 1318 continue; // simple case: paths are from root 1319 1320 if (link.Len() <= prefix.Len()) 1321 continue; 1322 1323 if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0) 1324 continue; 1325 1326 UString newLink = prefix.Left(rootPrefixSize); 1327 newLink += link.Ptr(prefix.Len()); 1328 1329 CByteBuffer data; 1330 bool isSymLink = !attr.IsMountPoint(); 1331 if (!FillLinkData(data, newLink, isSymLink, isWSL)) 1332 continue; 1333 item.ReparseData2 = data; 1334 } 1335 return S_OK; 1336} 1337 1338#endif 1339 1340 1341#ifndef _WIN32 1342 1343HRESULT CDirItems::FillDeviceSizes() 1344{ 1345 { 1346 FOR_VECTOR (i, Items) 1347 { 1348 CDirItem &item = Items[i]; 1349 1350 if (S_ISBLK(item.mode) && item.Size == 0) 1351 { 1352 const FString phyPath = GetPhyPath(i); 1353 NIO::CInFile inFile; 1354 inFile.PreserveATime = true; 1355 if (inFile.OpenShared(phyPath, ShareForWrite)) // fixme: OpenShared ?? 1356 { 1357 UInt64 size = 0; 1358 if (inFile.GetLength(size)) 1359 item.Size = size; 1360 } 1361 } 1362 if (StoreOwnerName) 1363 { 1364 OwnerNameMap.Add_UInt32(item.uid); 1365 OwnerGroupMap.Add_UInt32(item.gid); 1366 } 1367 } 1368 } 1369 1370 if (StoreOwnerName) 1371 { 1372 UString u; 1373 AString a; 1374 { 1375 FOR_VECTOR (i, OwnerNameMap.Numbers) 1376 { 1377 // 200K/sec speed 1378 u.Empty(); 1379 const passwd *pw = getpwuid(OwnerNameMap.Numbers[i]); 1380 // printf("\ngetpwuid=%s\n", pw->pw_name); 1381 if (pw) 1382 { 1383 a = pw->pw_name; 1384 ConvertUTF8ToUnicode(a, u); 1385 } 1386 OwnerNameMap.Strings.Add(u); 1387 } 1388 } 1389 { 1390 FOR_VECTOR (i, OwnerGroupMap.Numbers) 1391 { 1392 u.Empty(); 1393 const group *gr = getgrgid(OwnerGroupMap.Numbers[i]); 1394 if (gr) 1395 { 1396 // printf("\ngetgrgid %d %s\n", OwnerGroupMap.Numbers[i], gr->gr_name); 1397 a = gr->gr_name; 1398 ConvertUTF8ToUnicode(a, u); 1399 } 1400 OwnerGroupMap.Strings.Add(u); 1401 } 1402 } 1403 1404 FOR_VECTOR (i, Items) 1405 { 1406 CDirItem &item = Items[i]; 1407 { 1408 const int index = OwnerNameMap.Find(item.uid); 1409 if (index < 0) throw 1; 1410 item.OwnerNameIndex = index; 1411 } 1412 { 1413 const int index = OwnerGroupMap.Find(item.gid); 1414 if (index < 0) throw 1; 1415 item.OwnerGroupIndex = index; 1416 } 1417 } 1418 } 1419 1420 1421 // if (NeedOwnerNames) 1422 { 1423 /* 1424 { 1425 for (unsigned i = 0 ; i < 10000; i++) 1426 { 1427 const passwd *pw = getpwuid(i); 1428 if (pw) 1429 { 1430 UString u; 1431 ConvertUTF8ToUnicode(AString(pw->pw_name), u); 1432 OwnerNameMap.Add(i, u); 1433 OwnerNameMap.Add(i, u); 1434 OwnerNameMap.Add(i, u); 1435 } 1436 const group *gr = getgrgid(i); 1437 if (gr) 1438 { 1439 // we can use utf-8 here. 1440 UString u; 1441 ConvertUTF8ToUnicode(AString(gr->gr_name), u); 1442 OwnerGroupMap.Add(i, u); 1443 } 1444 } 1445 } 1446 */ 1447 /* 1448 { 1449 FOR_VECTOR (i, OwnerNameMap.Strings) 1450 { 1451 AString s; 1452 ConvertUnicodeToUTF8(OwnerNameMap.Strings[i], s); 1453 printf("\n%5d %s", (unsigned)OwnerNameMap.Numbers[i], s.Ptr()); 1454 } 1455 } 1456 { 1457 printf("\n\n=========Groups\n"); 1458 FOR_VECTOR (i, OwnerGroupMap.Strings) 1459 { 1460 AString s; 1461 ConvertUnicodeToUTF8(OwnerGroupMap.Strings[i], s); 1462 printf("\n%5d %s", (unsigned)OwnerGroupMap.Numbers[i], s.Ptr()); 1463 } 1464 } 1465 */ 1466 } 1467 /* 1468 for (unsigned i = 0 ; i < 100000000; i++) 1469 { 1470 // const passwd *pw = getpwuid(1000); 1471 // pw = pw; 1472 int pos = OwnerNameMap.Find(1000); 1473 if (pos < 0 - (int)i) 1474 throw 1; 1475 } 1476 */ 1477 1478 return S_OK; 1479} 1480 1481#endif 1482 1483 1484 1485static const char * const kCannotFindArchive = "Cannot find archive"; 1486 1487HRESULT EnumerateDirItemsAndSort( 1488 NWildcard::CCensor &censor, 1489 NWildcard::ECensorPathMode censorPathMode, 1490 const UString &addPathPrefix, 1491 UStringVector &sortedPaths, 1492 UStringVector &sortedFullPaths, 1493 CDirItemsStat &st, 1494 IDirItemsCallback *callback) 1495{ 1496 FStringVector paths; 1497 1498 { 1499 CDirItems dirItems; 1500 dirItems.Callback = callback; 1501 { 1502 HRESULT res = EnumerateItems(censor, censorPathMode, addPathPrefix, dirItems); 1503 st = dirItems.Stat; 1504 RINOK(res) 1505 } 1506 1507 FOR_VECTOR (i, dirItems.Items) 1508 { 1509 const CDirItem &dirItem = dirItems.Items[i]; 1510 if (!dirItem.IsDir()) 1511 paths.Add(dirItems.GetPhyPath(i)); 1512 } 1513 } 1514 1515 if (paths.Size() == 0) 1516 { 1517 // return S_OK; 1518 throw CMessagePathException(kCannotFindArchive); 1519 } 1520 1521 UStringVector fullPaths; 1522 1523 unsigned i; 1524 1525 for (i = 0; i < paths.Size(); i++) 1526 { 1527 FString fullPath; 1528 NFile::NDir::MyGetFullPathName(paths[i], fullPath); 1529 fullPaths.Add(fs2us(fullPath)); 1530 } 1531 1532 CUIntVector indices; 1533 SortFileNames(fullPaths, indices); 1534 sortedPaths.ClearAndReserve(indices.Size()); 1535 sortedFullPaths.ClearAndReserve(indices.Size()); 1536 1537 for (i = 0; i < indices.Size(); i++) 1538 { 1539 unsigned index = indices[i]; 1540 sortedPaths.AddInReserved(fs2us(paths[index])); 1541 sortedFullPaths.AddInReserved(fullPaths[index]); 1542 if (i > 0 && CompareFileNames(sortedFullPaths[i], sortedFullPaths[i - 1]) == 0) 1543 throw CMessagePathException("Duplicate archive path:", sortedFullPaths[i]); 1544 } 1545 1546 return S_OK; 1547} 1548 1549 1550 1551 1552#ifdef _WIN32 1553 1554static bool IsDotsName(const wchar_t *s) 1555{ 1556 return s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0)); 1557} 1558 1559// This code converts all short file names to long file names. 1560 1561static void ConvertToLongName(const UString &prefix, UString &name) 1562{ 1563 if (name.IsEmpty() 1564 || DoesNameContainWildcard(name) 1565 || IsDotsName(name)) 1566 return; 1567 NFind::CFileInfo fi; 1568 const FString path (us2fs(prefix + name)); 1569 #ifndef UNDER_CE 1570 if (NFile::NName::IsDevicePath(path)) 1571 return; 1572 #endif 1573 if (fi.Find(path)) 1574 name = fs2us(fi.Name); 1575} 1576 1577static void ConvertToLongNames(const UString &prefix, CObjectVector<NWildcard::CItem> &items) 1578{ 1579 FOR_VECTOR (i, items) 1580 { 1581 NWildcard::CItem &item = items[i]; 1582 if (item.Recursive || item.PathParts.Size() != 1) 1583 continue; 1584 if (prefix.IsEmpty() && item.IsDriveItem()) 1585 continue; 1586 ConvertToLongName(prefix, item.PathParts.Front()); 1587 } 1588} 1589 1590static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node) 1591{ 1592 ConvertToLongNames(prefix, node.IncludeItems); 1593 ConvertToLongNames(prefix, node.ExcludeItems); 1594 unsigned i; 1595 for (i = 0; i < node.SubNodes.Size(); i++) 1596 { 1597 UString &name = node.SubNodes[i].Name; 1598 if (prefix.IsEmpty() && NWildcard::IsDriveColonName(name)) 1599 continue; 1600 ConvertToLongName(prefix, name); 1601 } 1602 // mix folders with same name 1603 for (i = 0; i < node.SubNodes.Size(); i++) 1604 { 1605 NWildcard::CCensorNode &nextNode1 = node.SubNodes[i]; 1606 for (unsigned j = i + 1; j < node.SubNodes.Size();) 1607 { 1608 const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j]; 1609 if (nextNode1.Name.IsEqualTo_NoCase(nextNode2.Name)) 1610 { 1611 nextNode1.IncludeItems += nextNode2.IncludeItems; 1612 nextNode1.ExcludeItems += nextNode2.ExcludeItems; 1613 node.SubNodes.Delete(j); 1614 } 1615 else 1616 j++; 1617 } 1618 } 1619 for (i = 0; i < node.SubNodes.Size(); i++) 1620 { 1621 NWildcard::CCensorNode &nextNode = node.SubNodes[i]; 1622 ConvertToLongNames(prefix + nextNode.Name + WCHAR_PATH_SEPARATOR, nextNode); 1623 } 1624} 1625 1626void ConvertToLongNames(NWildcard::CCensor &censor) 1627{ 1628 FOR_VECTOR (i, censor.Pairs) 1629 { 1630 NWildcard::CPair &pair = censor.Pairs[i]; 1631 ConvertToLongNames(pair.Prefix, pair.Head); 1632 } 1633} 1634 1635#endif 1636 1637 1638CMessagePathException::CMessagePathException(const char *a, const wchar_t *u) 1639{ 1640 (*this) += a; 1641 if (u) 1642 { 1643 Add_LF(); 1644 (*this) += u; 1645 } 1646} 1647 1648CMessagePathException::CMessagePathException(const wchar_t *a, const wchar_t *u) 1649{ 1650 (*this) += a; 1651 if (u) 1652 { 1653 Add_LF(); 1654 (*this) += u; 1655 } 1656} 1657