Fix - Makemsix pack fails when directory given ends with "\" (#361)
This commit is contained in:
Родитель
da77ac2910
Коммит
ebad9ca20f
|
@ -58,7 +58,7 @@ namespace MSIX {
|
|||
ComPtr<IStream> OpenFile(const std::string& fileName, MSIX::FileStream::Mode mode) override;
|
||||
std::multimap<std::uint64_t, std::string> GetFilesByLastModDate() override;
|
||||
|
||||
static const char* GetPathSeparator();
|
||||
char GetPathSeparator() const;
|
||||
|
||||
protected:
|
||||
std::string m_root;
|
||||
|
|
|
@ -13,38 +13,58 @@
|
|||
#include <dirent.h>
|
||||
#include <map>
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
template<class Lambda>
|
||||
void WalkDirectory(const std::string& root, Lambda& visitor)
|
||||
namespace MSIX
|
||||
{
|
||||
namespace
|
||||
{
|
||||
static std::string dot(".");
|
||||
static std::string dotdot("..");
|
||||
|
||||
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(root.c_str()), closedir);
|
||||
ThrowErrorIf(Error::FileNotFound, dir.get() == nullptr, "Invalid directory");
|
||||
struct dirent* dp;
|
||||
// TODO: handle junction loops
|
||||
while((dp = readdir(dir.get())) != nullptr)
|
||||
template<class Lambda>
|
||||
void WalkDirectory(const std::string& root, Lambda& visitor)
|
||||
{
|
||||
std::string fileName = std::string(dp->d_name);
|
||||
std::string child = root + "/" + fileName;
|
||||
if (dp->d_type == DT_DIR)
|
||||
static std::string dot(".");
|
||||
static std::string dotdot("..");
|
||||
|
||||
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(root.c_str()), closedir);
|
||||
ThrowErrorIf(Error::FileNotFound, dir.get() == nullptr, "Invalid directory");
|
||||
struct dirent* dp;
|
||||
// TODO: handle junction loops
|
||||
while((dp = readdir(dir.get())) != nullptr)
|
||||
{
|
||||
if ((fileName != dot) && (fileName != dotdot))
|
||||
std::string fileName = std::string(dp->d_name);
|
||||
std::string child = root + "/" + fileName;
|
||||
if (dp->d_type == DT_DIR)
|
||||
{
|
||||
WalkDirectory(child, visitor);
|
||||
if ((fileName != dot) && (fileName != dotdot))
|
||||
{
|
||||
WalkDirectory(child, visitor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: ignore .DS_STORE for mac?
|
||||
struct stat sb;
|
||||
ThrowErrorIf(Error::Unexpected, stat(child.c_str(), &sb) == -1, std::string("stat call failed" + std::to_string(errno)).c_str());
|
||||
if (!visitor(root, std::move(fileName), static_cast<std::uint64_t>(sb.st_mtime)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
#define DEFAULT_MODE S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
|
||||
void mkdirp(std::string& path, size_t startPos = 0, mode_t mode = DEFAULT_MODE)
|
||||
{
|
||||
char* p = &path[startPos];
|
||||
if (*p == '/') { p++; }
|
||||
while (*p != '\0')
|
||||
{
|
||||
// TODO: ignore .DS_STORE for mac?
|
||||
struct stat sb;
|
||||
ThrowErrorIf(Error::Unexpected, stat(child.c_str(), &sb) == -1, std::string("stat call failed" + std::to_string(errno)).c_str());
|
||||
if (!visitor(root, std::move(fileName), static_cast<std::uint64_t>(sb.st_mtime)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
while (*p != '\0' && *p != '/') { p++; }
|
||||
|
||||
char v = *p;
|
||||
*p = '\0';
|
||||
ThrowErrorIfNot(Error::FileCreateDirectory,(mkdir(path.c_str(), mode) != -1 || errno == EEXIST), path.c_str());
|
||||
*p = v;
|
||||
if (*p != '\0') {p++;}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,27 +75,15 @@ namespace MSIX {
|
|||
NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
#define DEFAULT_MODE S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
|
||||
void mkdirp(std::string& path, size_t startPos = 0, mode_t mode = DEFAULT_MODE)
|
||||
{
|
||||
char* p = &path[startPos];
|
||||
if (*p == '/') { p++; }
|
||||
while (*p != '\0')
|
||||
{
|
||||
while (*p != '\0' && *p != '/') { p++; }
|
||||
|
||||
char v = *p;
|
||||
*p = '\0';
|
||||
ThrowErrorIfNot(Error::FileCreateDirectory,(mkdir(path.c_str(), mode) != -1 || errno == EEXIST), path.c_str());
|
||||
*p = v;
|
||||
if (*p != '\0') {p++;}
|
||||
}
|
||||
}
|
||||
|
||||
const char* DirectoryObject::GetPathSeparator() { return "/"; }
|
||||
char DirectoryObject::GetPathSeparator() const { return '/'; }
|
||||
|
||||
DirectoryObject::DirectoryObject(const std::string& root, bool createRootIfNecessary) : m_root(root)
|
||||
{
|
||||
if (!m_root.empty() && m_root.back() == GetPathSeparator())
|
||||
{
|
||||
m_root = m_root.substr(0, m_root.length() - 1);
|
||||
}
|
||||
|
||||
if (createRootIfNecessary)
|
||||
{
|
||||
mkdirp(m_root);
|
||||
|
|
|
@ -19,263 +19,277 @@
|
|||
#include <codecvt>
|
||||
#include <queue>
|
||||
|
||||
namespace MSIX {
|
||||
enum class WalkOptions : std::uint16_t
|
||||
namespace MSIX
|
||||
{
|
||||
namespace
|
||||
{
|
||||
Files = 1, // Enumerate files within the specified directory
|
||||
Directories = 2, // Enumerate directories
|
||||
Recursive = 4 // Enumerate recursively
|
||||
};
|
||||
|
||||
inline constexpr WalkOptions operator& (WalkOptions a, WalkOptions b)
|
||||
{
|
||||
return static_cast<WalkOptions>(static_cast<uint16_t>(a) & static_cast<uint16_t>(b));
|
||||
}
|
||||
|
||||
inline constexpr WalkOptions operator| (WalkOptions a, WalkOptions b)
|
||||
{
|
||||
return static_cast<WalkOptions>(static_cast<uint16_t>(a) | static_cast<uint16_t>(b));
|
||||
}
|
||||
|
||||
template <class Lambda>
|
||||
void WalkDirectory(const std::string& root, WalkOptions options, Lambda& visitor)
|
||||
{
|
||||
static std::string dot(".");
|
||||
static std::string dotdot("..");
|
||||
|
||||
std::wstring utf16Name = utf8_to_wstring(root);
|
||||
if ((options & WalkOptions::Files) == WalkOptions::Files)
|
||||
enum class WalkOptions : std::uint16_t
|
||||
{
|
||||
utf16Name += L"\\*";
|
||||
Files = 1, // Enumerate files within the specified directory
|
||||
Directories = 2, // Enumerate directories
|
||||
Recursive = 4 // Enumerate recursively
|
||||
};
|
||||
|
||||
inline constexpr WalkOptions operator& (WalkOptions a, WalkOptions b)
|
||||
{
|
||||
return static_cast<WalkOptions>(static_cast<uint16_t>(a) & static_cast<uint16_t>(b));
|
||||
}
|
||||
|
||||
WIN32_FIND_DATA findFileData = {};
|
||||
std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::FindClose)> find(
|
||||
FindFirstFile(reinterpret_cast<LPCWSTR>(utf16Name.c_str()), &findFileData),
|
||||
&FindClose);
|
||||
|
||||
if (INVALID_HANDLE_VALUE == find.get())
|
||||
inline constexpr WalkOptions operator| (WalkOptions a, WalkOptions b)
|
||||
{
|
||||
DWORD lastError = GetLastError();
|
||||
if (lastError == ERROR_FILE_NOT_FOUND)
|
||||
return static_cast<WalkOptions>(static_cast<uint16_t>(a) | static_cast<uint16_t>(b));
|
||||
}
|
||||
|
||||
template <class Lambda>
|
||||
void WalkDirectory(const std::string& root, WalkOptions options, Lambda& visitor)
|
||||
{
|
||||
static std::string dot(".");
|
||||
static std::string dotdot("..");
|
||||
|
||||
std::wstring utf16Name = utf8_to_wstring(root);
|
||||
if ((options & WalkOptions::Files) == WalkOptions::Files)
|
||||
{
|
||||
return;
|
||||
utf16Name += L"\\*";
|
||||
}
|
||||
ThrowWin32ErrorIfNot(lastError, false, "FindFirstFile failed.");
|
||||
}
|
||||
|
||||
// TODO: handle junction loops
|
||||
do
|
||||
{
|
||||
utf16Name = std::wstring(findFileData.cFileName);
|
||||
auto utf8Name = wstring_to_utf8(utf16Name);
|
||||
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
WIN32_FIND_DATA findFileData = {};
|
||||
std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::FindClose)> find(
|
||||
FindFirstFile(reinterpret_cast<LPCWSTR>(utf16Name.c_str()), &findFileData),
|
||||
&FindClose);
|
||||
|
||||
if (INVALID_HANDLE_VALUE == find.get())
|
||||
{
|
||||
if (dot != utf8Name && dotdot != utf8Name)
|
||||
DWORD lastError = GetLastError();
|
||||
if (lastError == ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
std::string child = root + "\\" + utf8Name;
|
||||
if ((options & WalkOptions::Directories) == WalkOptions::Directories &&
|
||||
!visitor(root, WalkOptions::Directories, std::move(utf8Name), 0))
|
||||
return;
|
||||
}
|
||||
ThrowWin32ErrorIfNot(lastError, false, "FindFirstFile failed.");
|
||||
}
|
||||
|
||||
// TODO: handle junction loops
|
||||
do
|
||||
{
|
||||
utf16Name = std::wstring(findFileData.cFileName);
|
||||
auto utf8Name = wstring_to_utf8(utf16Name);
|
||||
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
if (dot != utf8Name && dotdot != utf8Name)
|
||||
{
|
||||
std::string child = root + "\\" + utf8Name;
|
||||
if ((options & WalkOptions::Directories) == WalkOptions::Directories &&
|
||||
!visitor(root, WalkOptions::Directories, std::move(utf8Name), 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if ((options & WalkOptions::Recursive) == WalkOptions::Recursive)
|
||||
{
|
||||
WalkDirectory(child, options, visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((options & WalkOptions::Files) == WalkOptions::Files)
|
||||
{
|
||||
ULARGE_INTEGER fileTime;
|
||||
fileTime.HighPart = findFileData.ftLastWriteTime.dwHighDateTime;
|
||||
fileTime.LowPart = findFileData.ftLastWriteTime.dwLowDateTime;
|
||||
if (!visitor(root, WalkOptions::Files, std::move(utf8Name), static_cast<std::uint64_t>(fileTime.QuadPart)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if ((options & WalkOptions::Recursive) == WalkOptions::Recursive)
|
||||
{
|
||||
WalkDirectory(child, options, visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((options & WalkOptions::Files) == WalkOptions::Files)
|
||||
while (FindNextFile(find.get(), &findFileData));
|
||||
|
||||
std::uint32_t lastError = static_cast<std::uint32_t>(GetLastError());
|
||||
ThrowWin32ErrorIfNot(lastError,
|
||||
((lastError == ERROR_NO_MORE_FILES) ||
|
||||
(lastError == ERROR_SUCCESS) ||
|
||||
(lastError == ERROR_ALREADY_EXISTS)),
|
||||
"FindNextFile");
|
||||
}
|
||||
|
||||
std::string GetFullPath(const std::string& path)
|
||||
{
|
||||
const std::wstring longPathPrefix = LR"(\\?\)";
|
||||
|
||||
auto pathWide = utf8_to_wstring(path);
|
||||
|
||||
std::wstring result = longPathPrefix;
|
||||
size_t prefixChars = longPathPrefix.size();
|
||||
if (pathWide.substr(0, longPathPrefix.size()) == longPathPrefix)
|
||||
{
|
||||
ULARGE_INTEGER fileTime;
|
||||
fileTime.HighPart = findFileData.ftLastWriteTime.dwHighDateTime;
|
||||
fileTime.LowPart = findFileData.ftLastWriteTime.dwLowDateTime;
|
||||
if (!visitor(root, WalkOptions::Files, std::move(utf8Name), static_cast<std::uint64_t>(fileTime.QuadPart)))
|
||||
// Already begins with long path prefix, so don't add it
|
||||
result = L"";
|
||||
prefixChars = 0;
|
||||
}
|
||||
|
||||
// We aren't going to go out of our way to support crazy incoming paths.
|
||||
// This means that path here is limited to MAX_PATH, but the resulting path won't be.
|
||||
DWORD length = GetFullPathNameW(pathWide.c_str(), 0, nullptr, nullptr);
|
||||
|
||||
// Any errors result in 0
|
||||
ThrowLastErrorIf(length == 0, "Failed to get necessary char count for GetFullPathNameW");
|
||||
|
||||
// When requesting size, length accounts for null char
|
||||
result.resize(prefixChars + length, L' ');
|
||||
|
||||
DWORD newLength = GetFullPathNameW(pathWide.c_str(), length, &result[prefixChars], nullptr);
|
||||
|
||||
// On success, newLength does not account for null char
|
||||
ThrowLastErrorIf(newLength == 0, "Failed to get necessary char count for GetFullPathNameW");
|
||||
|
||||
// The normal scenario is that length - 1 == newLength, but for relative paths, GetFullPathName
|
||||
// doesn't return the correct case size and there's no guarantee it will be always bigger than needed.
|
||||
// If we are in a case that length > newlenght there's no harm, just resize the string. Otherwise, it means
|
||||
// that the first length was not correct and the path didn't get resolved correctly. Try until previous length
|
||||
// is lower than the next call.
|
||||
if (length < newLength)
|
||||
{
|
||||
DWORD retry = 1;
|
||||
DWORD previousLength = 0;
|
||||
|
||||
do
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (FindNextFile(find.get(), &findFileData));
|
||||
|
||||
std::uint32_t lastError = static_cast<std::uint32_t>(GetLastError());
|
||||
ThrowWin32ErrorIfNot(lastError,
|
||||
((lastError == ERROR_NO_MORE_FILES) ||
|
||||
(lastError == ERROR_SUCCESS) ||
|
||||
(lastError == ERROR_ALREADY_EXISTS)),
|
||||
"FindNextFile");
|
||||
}
|
||||
|
||||
std::string GetFullPath(const std::string& path)
|
||||
{
|
||||
const std::wstring longPathPrefix = LR"(\\?\)";
|
||||
|
||||
auto pathWide = utf8_to_wstring(path);
|
||||
|
||||
std::wstring result = longPathPrefix;
|
||||
size_t prefixChars = longPathPrefix.size();
|
||||
if (pathWide.substr(0, longPathPrefix.size()) == longPathPrefix)
|
||||
{
|
||||
// Already begins with long path prefix, so don't add it
|
||||
result = L"";
|
||||
prefixChars = 0;
|
||||
}
|
||||
|
||||
// We aren't going to go out of our way to support crazy incoming paths.
|
||||
// This means that path here is limited to MAX_PATH, but the resulting path won't be.
|
||||
DWORD length = GetFullPathNameW(pathWide.c_str(), 0, nullptr, nullptr);
|
||||
|
||||
// Any errors result in 0
|
||||
ThrowLastErrorIf(length == 0, "Failed to get necessary char count for GetFullPathNameW");
|
||||
|
||||
// When requesting size, length accounts for null char
|
||||
result.resize(prefixChars + length, L' ');
|
||||
|
||||
DWORD newLength = GetFullPathNameW(pathWide.c_str(), length, &result[prefixChars], nullptr);
|
||||
|
||||
// On success, newLength does not account for null char
|
||||
ThrowLastErrorIf(newLength == 0, "Failed to get necessary char count for GetFullPathNameW");
|
||||
|
||||
// The normal scenario is that length - 1 == newLength, but for relative paths, GetFullPathName
|
||||
// doesn't return the correct case size and there's no guarantee it will be always bigger than needed.
|
||||
// If we are in a case that length > newlenght there's no harm, just resize the string. Otherwise, it means
|
||||
// that the first length was not correct and the path didn't get resolved correctly. Try until previous length
|
||||
// is lower than the next call.
|
||||
if (length < newLength)
|
||||
{
|
||||
DWORD retry = 1;
|
||||
DWORD previousLength = 0;
|
||||
|
||||
do
|
||||
{
|
||||
previousLength = newLength + retry;
|
||||
result.resize(prefixChars + previousLength, L' ');
|
||||
newLength = GetFullPathNameW(pathWide.c_str(), previousLength, &result[prefixChars], nullptr);
|
||||
retry++;
|
||||
} while ((previousLength < newLength) && retry <= 10);
|
||||
|
||||
}
|
||||
|
||||
result.resize(prefixChars + newLength);
|
||||
|
||||
return wstring_to_utf8(result);
|
||||
}
|
||||
|
||||
struct DirectoryInfo
|
||||
{
|
||||
std::string Name;
|
||||
bool Create;
|
||||
|
||||
DirectoryInfo(std::string&& name, bool create) : Name(std::move(name)), Create(create) {}
|
||||
};
|
||||
|
||||
static void SplitDirectories(const std::string& path, std::queue<DirectoryInfo>& directories, bool forCreate)
|
||||
{
|
||||
static char const* const Delims = "\\/";
|
||||
const std::string longPathPrefix = R"(\\?\)";
|
||||
|
||||
size_t copyPos = 0;
|
||||
size_t searchPos = 0;
|
||||
size_t lastPos = 0;
|
||||
|
||||
if (path.substr(0, longPathPrefix.size()) == longPathPrefix)
|
||||
{
|
||||
// Absolute path, we need to skip it
|
||||
searchPos = longPathPrefix.size();
|
||||
}
|
||||
|
||||
while (lastPos != std::string::npos)
|
||||
{
|
||||
lastPos = path.find_first_of(Delims, searchPos);
|
||||
|
||||
std::string temp = path.substr(copyPos, lastPos - copyPos);
|
||||
if (!temp.empty())
|
||||
{
|
||||
directories.emplace(std::move(temp), forCreate);
|
||||
previousLength = newLength + retry;
|
||||
result.resize(prefixChars + previousLength, L' ');
|
||||
newLength = GetFullPathNameW(pathWide.c_str(), previousLength, &result[prefixChars], nullptr);
|
||||
retry++;
|
||||
} while ((previousLength < newLength) && retry <= 10);
|
||||
|
||||
}
|
||||
|
||||
copyPos = searchPos = lastPos + 1;
|
||||
result.resize(prefixChars + newLength);
|
||||
|
||||
return wstring_to_utf8(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Destroys directories
|
||||
static void EnsureDirectoryStructureExists(const std::string& root, std::queue<DirectoryInfo>& directories, bool lastIsFile, std::string* resultingPath = nullptr)
|
||||
{
|
||||
ThrowErrorIf(Error::Unexpected, directories.empty(), "Some path must be given");
|
||||
|
||||
auto PopFirst = [&directories]()
|
||||
struct DirectoryInfo
|
||||
{
|
||||
auto result = directories.front();
|
||||
directories.pop();
|
||||
return result;
|
||||
std::string Name;
|
||||
bool Create;
|
||||
|
||||
DirectoryInfo(std::string&& name, bool create) : Name(std::move(name)), Create(create) {}
|
||||
};
|
||||
|
||||
std::string path = root;
|
||||
bool isFirst = true;
|
||||
|
||||
while (!directories.empty())
|
||||
void SplitDirectories(const std::string& path, std::queue<DirectoryInfo>& directories, bool forCreate)
|
||||
{
|
||||
auto dirInfo = PopFirst();
|
||||
if (!path.empty())
|
||||
{
|
||||
path += DirectoryObject::GetPathSeparator();
|
||||
}
|
||||
path += dirInfo.Name;
|
||||
static char const* const Delims = "\\/";
|
||||
const std::string longPathPrefix = R"(\\?\)";
|
||||
|
||||
bool shouldWeCreateDir = dirInfo.Create;
|
||||
size_t copyPos = 0;
|
||||
size_t searchPos = 0;
|
||||
size_t lastPos = 0;
|
||||
|
||||
// When the last entry is a file, and we are on the last entry, never create
|
||||
if (lastIsFile && directories.empty())
|
||||
if (path.substr(0, longPathPrefix.size()) == longPathPrefix)
|
||||
{
|
||||
shouldWeCreateDir = false;
|
||||
}
|
||||
// If this is a rooted list of directories, the first one will be a device
|
||||
else if (root.empty() && isFirst)
|
||||
{
|
||||
shouldWeCreateDir = false;
|
||||
// Absolute path, we need to skip it
|
||||
searchPos = longPathPrefix.size();
|
||||
}
|
||||
|
||||
if (shouldWeCreateDir)
|
||||
while (lastPos != std::string::npos)
|
||||
{
|
||||
bool found = false;
|
||||
lastPos = path.find_first_of(Delims, searchPos);
|
||||
|
||||
std::wstring utf16Name = utf8_to_wstring(path);
|
||||
DWORD attr = GetFileAttributesW(utf16Name.c_str());
|
||||
|
||||
if (attr == INVALID_FILE_ATTRIBUTES)
|
||||
std::string temp = path.substr(copyPos, lastPos - copyPos);
|
||||
if (!temp.empty())
|
||||
{
|
||||
if (!CreateDirectory(utf16Name.c_str(), nullptr))
|
||||
{
|
||||
auto lastError = GetLastError();
|
||||
ThrowWin32ErrorIfNot(lastError, (lastError == ERROR_ALREADY_EXISTS), std::string("Call to CreateDirectory failed creating: " + path).c_str());
|
||||
}
|
||||
directories.emplace(std::move(temp), forCreate);
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowWin32ErrorIfNot(ERROR_ALREADY_EXISTS, attr & FILE_ATTRIBUTE_DIRECTORY, ("A file at this path already exists: " + path).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
isFirst = false;
|
||||
copyPos = searchPos = lastPos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (resultingPath)
|
||||
// Destroys directories
|
||||
void EnsureDirectoryStructureExists(
|
||||
const std::string& root,
|
||||
std::queue<DirectoryInfo>& directories,
|
||||
bool lastIsFile,
|
||||
char pathSeparator,
|
||||
std::string* resultingPath = nullptr)
|
||||
{
|
||||
*resultingPath = std::move(path);
|
||||
ThrowErrorIf(Error::Unexpected, directories.empty(), "Some path must be given");
|
||||
|
||||
auto PopFirst = [&directories]()
|
||||
{
|
||||
auto result = directories.front();
|
||||
directories.pop();
|
||||
return result;
|
||||
};
|
||||
|
||||
std::string path = root;
|
||||
bool isFirst = true;
|
||||
|
||||
while (!directories.empty())
|
||||
{
|
||||
auto dirInfo = PopFirst();
|
||||
if (!path.empty())
|
||||
{
|
||||
path += pathSeparator;
|
||||
}
|
||||
path += dirInfo.Name;
|
||||
|
||||
bool shouldWeCreateDir = dirInfo.Create;
|
||||
|
||||
// When the last entry is a file, and we are on the last entry, never create
|
||||
if (lastIsFile && directories.empty())
|
||||
{
|
||||
shouldWeCreateDir = false;
|
||||
}
|
||||
// If this is a rooted list of directories, the first one will be a device
|
||||
else if (root.empty() && isFirst)
|
||||
{
|
||||
shouldWeCreateDir = false;
|
||||
}
|
||||
|
||||
if (shouldWeCreateDir)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
std::wstring utf16Name = utf8_to_wstring(path);
|
||||
DWORD attr = GetFileAttributesW(utf16Name.c_str());
|
||||
|
||||
if (attr == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
if (!CreateDirectory(utf16Name.c_str(), nullptr))
|
||||
{
|
||||
auto lastError = GetLastError();
|
||||
ThrowWin32ErrorIfNot(lastError, (lastError == ERROR_ALREADY_EXISTS), std::string("Call to CreateDirectory failed creating: " + path).c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowWin32ErrorIfNot(ERROR_ALREADY_EXISTS, attr & FILE_ATTRIBUTE_DIRECTORY, ("A file at this path already exists: " + path).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
if (resultingPath)
|
||||
{
|
||||
*resultingPath = std::move(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* DirectoryObject::GetPathSeparator() { return "\\"; }
|
||||
char DirectoryObject::GetPathSeparator() const { return '\\'; }
|
||||
|
||||
DirectoryObject::DirectoryObject(const std::string& root, bool createRootIfNecessary)
|
||||
{
|
||||
m_root = GetFullPath(root);
|
||||
|
||||
if (!m_root.empty() && m_root.back() == GetPathSeparator())
|
||||
{
|
||||
m_root = m_root.substr(0, m_root.length() - 1);
|
||||
}
|
||||
|
||||
if (createRootIfNecessary)
|
||||
{
|
||||
std::queue<DirectoryInfo> directories;
|
||||
SplitDirectories(m_root, directories, true);
|
||||
EnsureDirectoryStructureExists({}, directories, false);
|
||||
EnsureDirectoryStructureExists({}, directories, false, GetPathSeparator());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,7 +310,7 @@ namespace MSIX {
|
|||
SplitDirectories(fileName, directories, modeWillCreateFile);
|
||||
|
||||
std::string path;
|
||||
EnsureDirectoryStructureExists(m_root, directories, true, &path);
|
||||
EnsureDirectoryStructureExists(m_root, directories, true, GetPathSeparator(), &path);
|
||||
|
||||
auto result = ComPtr<IStream>::Make<FileStream>(std::move(utf8_to_wstring(path)), mode);
|
||||
return result;
|
||||
|
|
|
@ -45,6 +45,22 @@ TEST_CASE("Pack_Good", "[pack]")
|
|||
MsixTest::Pack::ValidatePackageStream(outputPackage);
|
||||
}
|
||||
|
||||
TEST_CASE("Pack_Good_EndsWithSeparator", "[pack]")
|
||||
{
|
||||
HRESULT expected = S_OK;
|
||||
|
||||
#ifdef WIN32
|
||||
std::string directory = "input\\";
|
||||
#else
|
||||
std::string directory = "input/";
|
||||
#endif
|
||||
|
||||
RunPackTest(expected, directory);
|
||||
|
||||
// Verify output package
|
||||
MsixTest::Pack::ValidatePackageStream(outputPackage);
|
||||
}
|
||||
|
||||
// Fail if there's no AppxManifest.xml
|
||||
TEST_CASE("Pack_AppxManifestNotPresent", "[pack]")
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче