// EnumDirItems.cpp #include "StdAfx.h" #include #include "../../../Common/Wildcard.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileIO.h" #include "../../../Windows/FileName.h" #if defined(_WIN32) && !defined(UNDER_CE) #define _USE_SECURITY_CODE #include "../../../Windows/SecurityUtils.h" #endif #include "EnumDirItems.h" #include "SortUtils.h" using namespace NWindows; using namespace NFile; using namespace NName; void CDirItems::AddDirFileInfo(int phyParent, int logParent, int secureIndex, const NFind::CFileInfo &fi) { CDirItem di; di.Size = fi.Size; di.CTime = fi.CTime; di.ATime = fi.ATime; di.MTime = fi.MTime; di.Attrib = fi.Attrib; di.IsAltStream = fi.IsAltStream; di.PhyParent = phyParent; di.LogParent = logParent; di.SecureIndex = secureIndex; di.Name = fs2us(fi.Name); #if defined(_WIN32) && !defined(UNDER_CE) // di.ShortName = fs2us(fi.ShortName); #endif Items.Add(di); if (fi.IsDir()) Stat.NumDirs++; else if (fi.IsAltStream) { Stat.NumAltStreams++; Stat.AltStreamsSize += fi.Size; } else { Stat.NumFiles++; Stat.FilesSize += fi.Size; } } HRESULT CDirItems::AddError(const FString &path, DWORD errorCode) { Stat.NumErrors++; if (Callback) return Callback->ScanError(path, errorCode); return S_OK; } HRESULT CDirItems::AddError(const FString &path) { return AddError(path, ::GetLastError()); } static const unsigned kScanProgressStepMask = (1 << 12) - 1; HRESULT CDirItems::ScanProgress(const FString &dirPath) { if (Callback) return Callback->ScanProgress(Stat, dirPath, true); return S_OK; } UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const { UString path; unsigned len = name.Len(); int i; for (i = index; i >= 0; i = parents[i]) len += Prefixes[i].Len(); wchar_t *p = path.GetBuf_SetEnd(len) + len; p -= name.Len(); wmemcpy(p, (const wchar_t *)name, name.Len()); for (i = index; i >= 0; i = parents[i]) { const UString &s = Prefixes[i]; p -= s.Len(); wmemcpy(p, (const wchar_t *)s, s.Len()); } return path; } FString CDirItems::GetPhyPath(unsigned index) const { const CDirItem &di = Items[index]; return us2fs(GetPrefixesPath(PhyParents, di.PhyParent, di.Name)); } UString CDirItems::GetLogPath(unsigned index) const { const CDirItem &di = Items[index]; return GetPrefixesPath(LogParents, di.LogParent, di.Name); } void CDirItems::ReserveDown() { Prefixes.ReserveDown(); PhyParents.ReserveDown(); LogParents.ReserveDown(); Items.ReserveDown(); } unsigned CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix) { PhyParents.Add(phyParent); LogParents.Add(logParent); return Prefixes.Add(prefix); } void CDirItems::DeleteLastPrefix() { PhyParents.DeleteBack(); LogParents.DeleteBack(); Prefixes.DeleteBack(); } bool InitLocalPrivileges(); CDirItems::CDirItems(): SymLinks(false), ScanAltStreams(false) #ifdef _USE_SECURITY_CODE , ReadSecure(false) #endif , Callback(NULL) { #ifdef _USE_SECURITY_CODE _saclEnabled = InitLocalPrivileges(); #endif } #ifdef _USE_SECURITY_CODE HRESULT CDirItems::AddSecurityItem(const FString &path, int &secureIndex) { secureIndex = -1; SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION; if (_saclEnabled) securInfo |= SACL_SECURITY_INFORMATION; DWORD errorCode = 0; DWORD secureSize; BOOL res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); if (res) { if (secureSize == 0) return S_OK; if (secureSize > TempSecureBuf.Size()) errorCode = ERROR_INVALID_FUNCTION; } else { errorCode = GetLastError(); if (errorCode == ERROR_INSUFFICIENT_BUFFER) { if (secureSize <= TempSecureBuf.Size()) errorCode = ERROR_INVALID_FUNCTION; else { TempSecureBuf.Alloc(secureSize); res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); if (res) { if (secureSize != TempSecureBuf.Size()) errorCode = ERROR_INVALID_FUNCTION;; } else errorCode = GetLastError(); } } } if (res) { secureIndex = SecureBlocks.AddUniq(TempSecureBuf, secureSize); return S_OK; } if (errorCode == 0) errorCode = ERROR_INVALID_FUNCTION; return AddError(path, errorCode); } #endif HRESULT CDirItems::EnumerateDir(int phyParent, int logParent, const FString &phyPrefix) { RINOK(ScanProgress(phyPrefix)); NFind::CEnumerator enumerator; enumerator.SetDirPrefix(phyPrefix); for (unsigned ttt = 0; ; ttt++) { NFind::CFileInfo fi; bool found; if (!enumerator.Next(fi, found)) { return AddError(phyPrefix); } if (!found) return S_OK; int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (ReadSecure) { RINOK(AddSecurityItem(phyPrefix + fi.Name, secureIndex)); } #endif AddDirFileInfo(phyParent, logParent, secureIndex, fi); if (Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask) { RINOK(ScanProgress(phyPrefix)); } if (fi.IsDir()) { const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; unsigned parent = AddPrefix(phyParent, logParent, fs2us(name2)); RINOK(EnumerateDir(parent, parent, phyPrefix + name2)); } } } HRESULT CDirItems::EnumerateItems2( const FString &phyPrefix, const UString &logPrefix, const FStringVector &filePaths, FStringVector *requestedPaths) { int phyParent = phyPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, fs2us(phyPrefix)); int logParent = logPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, logPrefix); FOR_VECTOR (i, filePaths) { const FString &filePath = filePaths[i]; NFind::CFileInfo fi; const FString phyPath = phyPrefix + filePath; if (!fi.Find(phyPath)) { RINOK(AddError(phyPath)); continue; } if (requestedPaths) requestedPaths->Add(phyPath); int delimiter = filePath.ReverseFind_PathSepar(); FString phyPrefixCur; int phyParentCur = phyParent; if (delimiter >= 0) { phyPrefixCur.SetFrom(filePath, delimiter + 1); phyParentCur = AddPrefix(phyParent, logParent, fs2us(phyPrefixCur)); } int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (ReadSecure) { RINOK(AddSecurityItem(phyPath, secureIndex)); } #endif AddDirFileInfo(phyParentCur, logParent, secureIndex, fi); if (fi.IsDir()) { const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; unsigned parent = AddPrefix(phyParentCur, logParent, fs2us(name2)); RINOK(EnumerateDir(parent, parent, phyPrefix + phyPrefixCur + name2)); } } ReserveDown(); return S_OK; } static HRESULT EnumerateDirItems( const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, CDirItems &dirItems, bool enterToSubFolders); static HRESULT EnumerateDirItems_Spec( const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &curFolderName, const FString &phyPrefix, const UStringVector &addArchivePrefix, CDirItems &dirItems, bool enterToSubFolders) { const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR; unsigned parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2)); unsigned numItems = dirItems.Items.Size(); HRESULT res = EnumerateDirItems( curNode, parent, parent, phyPrefix + name2, addArchivePrefix, dirItems, enterToSubFolders); if (numItems == dirItems.Items.Size()) dirItems.DeleteLastPrefix(); return res; } #ifndef UNDER_CE #ifdef _WIN32 static HRESULT EnumerateAltStreams( const NFind::CFileInfo &fi, const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &fullPath, const UStringVector &addArchivePrefix, // prefix from curNode bool addAllItems, CDirItems &dirItems) { NFind::CStreamEnumerator enumerator(fullPath); for (;;) { NFind::CStreamInfo si; bool found; if (!enumerator.Next(si, found)) { return dirItems.AddError(fullPath + FTEXT(":*")); // , (DWORD)E_FAIL } if (!found) return S_OK; if (si.IsMainStream()) continue; UStringVector addArchivePrefixNew = addArchivePrefix; UString reducedName = si.GetReducedName(); addArchivePrefixNew.Back() += reducedName; if (curNode.CheckPathToRoot(false, addArchivePrefixNew, true)) continue; if (!addAllItems) if (!curNode.CheckPathToRoot(true, addArchivePrefixNew, true)) continue; NFind::CFileInfo fi2 = fi; fi2.Name += us2fs(reducedName); fi2.Size = si.Size; fi2.Attrib &= ~FILE_ATTRIBUTE_DIRECTORY; fi2.IsAltStream = true; dirItems.AddDirFileInfo(phyParent, logParent, -1, fi2); } } #endif HRESULT CDirItems::SetLinkInfo(CDirItem &dirItem, const NFind::CFileInfo &fi, const FString &phyPrefix) { if (!SymLinks || !fi.HasReparsePoint()) return S_OK; const FString path = phyPrefix + fi.Name; CByteBuffer &buf = dirItem.ReparseData; DWORD res = 0; if (NIO::GetReparseData(path, buf)) { CReparseAttr attr; if (attr.Parse(buf, buf.Size(), res)) return S_OK; // we ignore unknown reparse points if (res != ERROR_INVALID_REPARSE_DATA) res = 0; } else { res = ::GetLastError(); if (res == 0) res = ERROR_INVALID_FUNCTION; } buf.Free(); if (res == 0) return S_OK; return AddError(path, res); } #endif static HRESULT EnumerateForItem( NFind::CFileInfo &fi, const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, // prefix from curNode CDirItems &dirItems, bool enterToSubFolders) { const UString name = fs2us(fi.Name); bool enterToSubFolders2 = enterToSubFolders; UStringVector addArchivePrefixNew = addArchivePrefix; addArchivePrefixNew.Add(name); { UStringVector addArchivePrefixNewTemp(addArchivePrefixNew); if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fi.IsDir())) return S_OK; } int dirItemIndex = -1; bool addAllSubStreams = false; if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fi.IsDir())) { int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (dirItems.ReadSecure) { RINOK(dirItems.AddSecurityItem(phyPrefix + fi.Name, secureIndex)); } #endif dirItemIndex = dirItems.Items.Size(); dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi); if (fi.IsDir()) enterToSubFolders2 = true; addAllSubStreams = true; } #ifndef UNDER_CE if (dirItems.ScanAltStreams) { RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent, phyPrefix + fi.Name, addArchivePrefixNew, addAllSubStreams, dirItems)); } if (dirItemIndex >= 0) { CDirItem &dirItem = dirItems.Items[dirItemIndex]; RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix)); if (dirItem.ReparseData.Size() != 0) return S_OK; } #endif if (!fi.IsDir()) return S_OK; const NWildcard::CCensorNode *nextNode = 0; if (addArchivePrefix.IsEmpty()) { int index = curNode.FindSubNode(name); if (index >= 0) nextNode = &curNode.SubNodes[index]; } if (!enterToSubFolders2 && nextNode == 0) return S_OK; addArchivePrefixNew = addArchivePrefix; if (nextNode == 0) { nextNode = &curNode; addArchivePrefixNew.Add(name); } return EnumerateDirItems_Spec( *nextNode, phyParent, logParent, fi.Name, phyPrefix, addArchivePrefixNew, dirItems, enterToSubFolders2); } static bool CanUseFsDirect(const NWildcard::CCensorNode &curNode) { FOR_VECTOR (i, curNode.IncludeItems) { const NWildcard::CItem &item = curNode.IncludeItems[i]; if (item.Recursive || item.PathParts.Size() != 1) return false; const UString &name = item.PathParts.Front(); /* if (name.IsEmpty()) return false; */ /* Windows doesn't support file name with wildcard But if another system supports file name with wildcard, and wildcard mode is disabled, we can ignore wildcard in name */ /* if (!item.WildcardParsing) continue; */ if (DoesNameContainWildcard(name)) return false; } return true; } #if defined(_WIN32) && !defined(UNDER_CE) static bool IsVirtualFsFolder(const FString &prefix, const UString &name) { UString s = fs2us(prefix); s += name; s.Add_PathSepar(); return IsPathSepar(s[0]) && GetRootPrefixSize(s) == 0; } #endif static HRESULT EnumerateDirItems( const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, // prefix from curNode CDirItems &dirItems, bool enterToSubFolders) { if (!enterToSubFolders) if (curNode.NeedCheckSubDirs()) enterToSubFolders = true; RINOK(dirItems.ScanProgress(phyPrefix)); // try direct_names case at first if (addArchivePrefix.IsEmpty() && !enterToSubFolders) { if (CanUseFsDirect(curNode)) { // all names are direct (no wildcards) // so we don't need file_system's dir enumerator CRecordVector needEnterVector; unsigned i; for (i = 0; i < curNode.IncludeItems.Size(); i++) { const NWildcard::CItem &item = curNode.IncludeItems[i]; const UString &name = item.PathParts.Front(); FString fullPath = phyPrefix + us2fs(name); #if defined(_WIN32) && !defined(UNDER_CE) bool needAltStreams = true; #endif #ifdef _USE_SECURITY_CODE bool needSecurity = true; #endif if (phyPrefix.IsEmpty()) { if (!item.ForFile) { /* we don't like some names for alt streams inside archive: ":sname" for "\" "c:::sname" for "C:\" So we ignore alt streams for these cases */ if (name.IsEmpty()) { #if defined(_WIN32) && !defined(UNDER_CE) needAltStreams = false; #endif /* // do we need to ignore security info for "\\" folder ? #ifdef _USE_SECURITY_CODE needSecurity = false; #endif */ fullPath = CHAR_PATH_SEPARATOR; } #if defined(_WIN32) && !defined(UNDER_CE) else if (item.IsDriveItem()) { needAltStreams = false; fullPath.Add_PathSepar(); } #endif } } NFind::CFileInfo fi; #if defined(_WIN32) && !defined(UNDER_CE) if (IsVirtualFsFolder(phyPrefix, name)) { fi.SetAsDir(); fi.Name = us2fs(name); } else #endif if (!fi.Find(fullPath)) { RINOK(dirItems.AddError(fullPath)); continue; } bool isDir = fi.IsDir(); if (isDir && !item.ForDir || !isDir && !item.ForFile) { RINOK(dirItems.AddError(fullPath, (DWORD)E_FAIL)); continue; } { UStringVector pathParts; pathParts.Add(fs2us(fi.Name)); if (curNode.CheckPathToRoot(false, pathParts, !isDir)) continue; } int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (needSecurity && dirItems.ReadSecure) { RINOK(dirItems.AddSecurityItem(fullPath, secureIndex)); } #endif dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi); #ifndef UNDER_CE { CDirItem &dirItem = dirItems.Items.Back(); RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix)); if (dirItem.ReparseData.Size() != 0) { if (fi.IsAltStream) dirItems.Stat.AltStreamsSize -= fi.Size; else dirItems.Stat.FilesSize -= fi.Size; continue; } } #endif #ifndef UNDER_CE if (needAltStreams && dirItems.ScanAltStreams) { UStringVector pathParts; pathParts.Add(fs2us(fi.Name)); RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent, fullPath, pathParts, true, /* addAllSubStreams */ dirItems)); } #endif if (!isDir) continue; UStringVector addArchivePrefixNew; const NWildcard::CCensorNode *nextNode = 0; int index = curNode.FindSubNode(name); if (index >= 0) { for (int t = needEnterVector.Size(); t <= index; t++) needEnterVector.Add(true); needEnterVector[index] = false; nextNode = &curNode.SubNodes[index]; } else { nextNode = &curNode; addArchivePrefixNew.Add(name); // don't change it to fi.Name. It's for shortnames support } RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix, addArchivePrefixNew, dirItems, true)); } for (i = 0; i < curNode.SubNodes.Size(); i++) { if (i < needEnterVector.Size()) if (!needEnterVector[i]) continue; const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i]; FString fullPath = phyPrefix + us2fs(nextNode.Name); NFind::CFileInfo fi; if (phyPrefix.IsEmpty()) { { if (nextNode.Name.IsEmpty()) fullPath = CHAR_PATH_SEPARATOR; #ifdef _WIN32 else if (NWildcard::IsDriveColonName(nextNode.Name)) fullPath.Add_PathSepar(); #endif } } // we don't want to call fi.Find() for root folder or virtual folder if (phyPrefix.IsEmpty() && nextNode.Name.IsEmpty() #if defined(_WIN32) && !defined(UNDER_CE) || IsVirtualFsFolder(phyPrefix, nextNode.Name) #endif ) { fi.SetAsDir(); fi.Name = us2fs(nextNode.Name); } else { if (!fi.Find(fullPath)) { if (!nextNode.AreThereIncludeItems()) continue; RINOK(dirItems.AddError(fullPath)); continue; } if (!fi.IsDir()) { RINOK(dirItems.AddError(fullPath, (DWORD)E_FAIL)); continue; } } RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix, UStringVector(), dirItems, false)); } return S_OK; } } #ifdef _WIN32 #ifndef UNDER_CE // scan drives, if wildcard is "*:\" if (phyPrefix.IsEmpty() && curNode.IncludeItems.Size() > 0) { unsigned i; for (i = 0; i < curNode.IncludeItems.Size(); i++) { const NWildcard::CItem &item = curNode.IncludeItems[i]; if (item.PathParts.Size() < 1) break; const UString &name = item.PathParts.Front(); if (name.Len() != 2 || name[1] != ':') break; if (item.PathParts.Size() == 1) if (item.ForFile || !item.ForDir) break; if (NWildcard::IsDriveColonName(name)) continue; if (name[0] != '*' && name[0] != '?') break; } if (i == curNode.IncludeItems.Size()) { FStringVector driveStrings; NFind::MyGetLogicalDriveStrings(driveStrings); for (i = 0; i < driveStrings.Size(); i++) { FString driveName = driveStrings[i]; if (driveName.Len() < 3 || driveName.Back() != '\\') return E_FAIL; driveName.DeleteBack(); NFind::CFileInfo fi; fi.SetAsDir(); fi.Name = driveName; RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, addArchivePrefix, dirItems, enterToSubFolders)); } return S_OK; } } #endif #endif NFind::CEnumerator enumerator; enumerator.SetDirPrefix(phyPrefix); for (unsigned ttt = 0; ; ttt++) { NFind::CFileInfo fi; bool found; if (!enumerator.Next(fi, found)) { RINOK(dirItems.AddError(phyPrefix)); break; } if (!found) break; if (dirItems.Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask) { RINOK(dirItems.ScanProgress(phyPrefix)); } RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, addArchivePrefix, dirItems, enterToSubFolders)); } return S_OK; } HRESULT EnumerateItems( const NWildcard::CCensor &censor, const NWildcard::ECensorPathMode pathMode, const UString &addPathPrefix, CDirItems &dirItems) { FOR_VECTOR (i, censor.Pairs) { const NWildcard::CPair &pair = censor.Pairs[i]; int phyParent = pair.Prefix.IsEmpty() ? -1 : dirItems.AddPrefix(-1, -1, pair.Prefix); int logParent = -1; if (pathMode == NWildcard::k_AbsPath) logParent = phyParent; else { if (!addPathPrefix.IsEmpty()) logParent = dirItems.AddPrefix(-1, -1, addPathPrefix); } RINOK(EnumerateDirItems(pair.Head, phyParent, logParent, us2fs(pair.Prefix), UStringVector(), dirItems, false // enterToSubFolders )); } dirItems.ReserveDown(); #if defined(_WIN32) && !defined(UNDER_CE) dirItems.FillFixedReparse(); #endif return S_OK; } #if defined(_WIN32) && !defined(UNDER_CE) void CDirItems::FillFixedReparse() { /* imagex/WIM reduces absolute pathes in links (raparse data), if we archive non root folder. We do same thing here */ if (!SymLinks) return; FOR_VECTOR(i, Items) { CDirItem &item = Items[i]; if (item.ReparseData.Size() == 0) continue; CReparseAttr attr; DWORD errorCode = 0; if (!attr.Parse(item.ReparseData, item.ReparseData.Size(), errorCode)) continue; if (attr.IsRelative()) continue; const UString &link = attr.GetPath(); if (!IsDrivePath(link)) continue; // maybe we need to support networks paths also ? FString fullPathF; if (!NDir::MyGetFullPathName(GetPhyPath(i), fullPathF)) continue; UString fullPath = fs2us(fullPathF); const UString logPath = GetLogPath(i); if (logPath.Len() >= fullPath.Len()) continue; if (CompareFileNames(logPath, fullPath.RightPtr(logPath.Len())) != 0) continue; const UString prefix = fullPath.Left(fullPath.Len() - logPath.Len()); if (!IsPathSepar(prefix.Back())) continue; unsigned rootPrefixSize = GetRootPrefixSize(prefix); if (rootPrefixSize == 0) continue; if (rootPrefixSize == prefix.Len()) continue; // simple case: paths are from root if (link.Len() <= prefix.Len()) continue; if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0) continue; UString newLink = prefix.Left(rootPrefixSize); newLink += link.Ptr(prefix.Len()); CByteBuffer data; if (!FillLinkData(data, newLink, attr.IsSymLink())) continue; item.ReparseData2 = data; } } #endif static const char * const kCannotFindArchive = "Cannot find archive"; HRESULT EnumerateDirItemsAndSort( NWildcard::CCensor &censor, NWildcard::ECensorPathMode censorPathMode, const UString &addPathPrefix, UStringVector &sortedPaths, UStringVector &sortedFullPaths, CDirItemsStat &st, IDirItemsCallback *callback) { FStringVector paths; { CDirItems dirItems; dirItems.Callback = callback; { HRESULT res = EnumerateItems(censor, censorPathMode, addPathPrefix, dirItems); st = dirItems.Stat; RINOK(res); } FOR_VECTOR (i, dirItems.Items) { const CDirItem &dirItem = dirItems.Items[i]; if (!dirItem.IsDir()) paths.Add(dirItems.GetPhyPath(i)); } } if (paths.Size() == 0) { // return S_OK; throw CMessagePathException(kCannotFindArchive); } UStringVector fullPaths; unsigned i; for (i = 0; i < paths.Size(); i++) { FString fullPath; NFile::NDir::MyGetFullPathName(paths[i], fullPath); fullPaths.Add(fs2us(fullPath)); } CUIntVector indices; SortFileNames(fullPaths, indices); sortedPaths.ClearAndReserve(indices.Size()); sortedFullPaths.ClearAndReserve(indices.Size()); for (i = 0; i < indices.Size(); i++) { unsigned index = indices[i]; sortedPaths.AddInReserved(fs2us(paths[index])); sortedFullPaths.AddInReserved(fullPaths[index]); if (i > 0 && CompareFileNames(sortedFullPaths[i], sortedFullPaths[i - 1]) == 0) throw CMessagePathException("Duplicate archive path:", sortedFullPaths[i]); } return S_OK; } #ifdef _WIN32 // This code converts all short file names to long file names. static void ConvertToLongName(const UString &prefix, UString &name) { if (name.IsEmpty() || DoesNameContainWildcard(name)) return; NFind::CFileInfo fi; const FString path (us2fs(prefix + name)); #ifndef UNDER_CE if (NFile::NName::IsDevicePath(path)) return; #endif if (fi.Find(path)) name = fs2us(fi.Name); } static void ConvertToLongNames(const UString &prefix, CObjectVector &items) { FOR_VECTOR (i, items) { NWildcard::CItem &item = items[i]; if (item.Recursive || item.PathParts.Size() != 1) continue; if (prefix.IsEmpty() && item.IsDriveItem()) continue; ConvertToLongName(prefix, item.PathParts.Front()); } } static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node) { ConvertToLongNames(prefix, node.IncludeItems); ConvertToLongNames(prefix, node.ExcludeItems); unsigned i; for (i = 0; i < node.SubNodes.Size(); i++) { UString &name = node.SubNodes[i].Name; if (prefix.IsEmpty() && NWildcard::IsDriveColonName(name)) continue; ConvertToLongName(prefix, name); } // mix folders with same name for (i = 0; i < node.SubNodes.Size(); i++) { NWildcard::CCensorNode &nextNode1 = node.SubNodes[i]; for (unsigned j = i + 1; j < node.SubNodes.Size();) { const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j]; if (nextNode1.Name.IsEqualTo_NoCase(nextNode2.Name)) { nextNode1.IncludeItems += nextNode2.IncludeItems; nextNode1.ExcludeItems += nextNode2.ExcludeItems; node.SubNodes.Delete(j); } else j++; } } for (i = 0; i < node.SubNodes.Size(); i++) { NWildcard::CCensorNode &nextNode = node.SubNodes[i]; ConvertToLongNames(prefix + nextNode.Name + WCHAR_PATH_SEPARATOR, nextNode); } } void ConvertToLongNames(NWildcard::CCensor &censor) { FOR_VECTOR (i, censor.Pairs) { NWildcard::CPair &pair = censor.Pairs[i]; ConvertToLongNames(pair.Prefix, pair.Head); } } #endif CMessagePathException::CMessagePathException(const char *a, const wchar_t *u) { (*this) += a; if (u) { Add_LF(); (*this) += u; } }