summaryrefslogtreecommitdiffstats
path: root/other-licenses/7zstub/src/CPP/7zip/UI/Common/EnumDirItems.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'other-licenses/7zstub/src/CPP/7zip/UI/Common/EnumDirItems.cpp')
-rw-r--r--other-licenses/7zstub/src/CPP/7zip/UI/Common/EnumDirItems.cpp1086
1 files changed, 1086 insertions, 0 deletions
diff --git a/other-licenses/7zstub/src/CPP/7zip/UI/Common/EnumDirItems.cpp b/other-licenses/7zstub/src/CPP/7zip/UI/Common/EnumDirItems.cpp
new file mode 100644
index 000000000..032e2fffe
--- /dev/null
+++ b/other-licenses/7zstub/src/CPP/7zip/UI/Common/EnumDirItems.cpp
@@ -0,0 +1,1086 @@
+// EnumDirItems.cpp
+
+#include "StdAfx.h"
+
+#include <wchar.h>
+
+#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<bool> 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<NWildcard::CItem> &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;
+ }
+}