Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ void log_stage_duration(std::string_view stage,
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

/** Accepts only a missing or empty ordinary output root for the internal offline pass. */
/** Accepts only a missing or empty output root for the internal offline pass. */
[[nodiscard]] bool isolated_output_ready(std::wstring_view path) noexcept {
if (path.empty()) {
return false;
Expand All @@ -175,8 +175,7 @@ void log_stage_duration(std::string_view stage,
const DWORD error = GetLastError();
return error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND;
}
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0
|| (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
return false;
}
WIN32_FIND_DATAW entry{};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <string_view>

#include "activity_sdk_tree_publication.h"
#include "core/runtime/host_environment.h"

namespace sunrise::client::content::activity::sdk_generation::live_publication {
namespace {
Expand Down Expand Up @@ -43,7 +42,7 @@ struct Marker final {
std::array<std::uint8_t, 3> reserved{};
};

[[nodiscard]] bool ordinary_directory(const wchar_t* path) noexcept;
[[nodiscard]] bool is_directory(const wchar_t* path) noexcept;

/** Resolves one null-terminated lexical path into normalized absolute storage. */
[[nodiscard]] bool full_path(const wchar_t* input, std::wstring& output) noexcept {
Expand Down Expand Up @@ -73,53 +72,33 @@ struct Marker final {
}
}

/** Which drive-path component, if any, is not an ordinary directory. */
enum class Ancestry : std::uint8_t {
ready,
pathShape,
driveRoot,
ancestor,
};

/** A drive path is at least a letter, a colon and a separator. */
constexpr std::size_t kDrivePrefixLength = 3;

/** Reports the first drive-path component which is not an ordinary directory. */
[[nodiscard]] Ancestry ordinary_ancestry(const std::wstring& directory) noexcept {
if (directory.size() < kDrivePrefixLength || directory[1] != L':' || directory[2] != L'\\') {
return Ancestry::pathShape;
/** Requires every existing drive-path directory component to be ordinary, never a reparse point. */
[[nodiscard]] bool ordinary_ancestry(const std::wstring& directory) noexcept {
if (directory.size() < 3U || directory[1] != L':' || directory[2] != L'\\') {
return false;
}
const bool rootOrdinary = ordinary_directory(directory.substr(0, kDrivePrefixLength).c_str());
std::size_t cursor = kDrivePrefixLength;
if (!is_directory(directory.substr(0, 3U).c_str())) {
return false;
}
std::size_t cursor = 3U;
while (cursor < directory.size()) {
const std::size_t separator = directory.find(L'\\', cursor);
const std::size_t end = separator == std::wstring::npos ? directory.size() : separator;
const std::wstring prefix = directory.substr(0, end);
if (!ordinary_directory(prefix.c_str())) {
return Ancestry::ancestor;
if (!is_directory(prefix.c_str())) {
return false;
}
if (separator == std::wstring::npos) {
break;
}
cursor = separator + 1U;
}
return rootOrdinary ? Ancestry::ready : Ancestry::driveRoot;
}

/** Wine maps its synthetic drive root as a reparse point; real path components stay checked. */
[[nodiscard]] bool ordinary_ancestry_allowed(Ancestry ancestry) noexcept {
if (ancestry == Ancestry::ready) {
return true;
}
if (ancestry != Ancestry::driveRoot) {
return false;
}
return core::runtime::is_wine();
return true;
}

/** Resolves one existing ordinary directory and rejects reparse points in every ancestor. */
[[nodiscard]] bool canonical_directory(const wchar_t* input, std::wstring& output) noexcept {
return full_path(input, output) && ordinary_ancestry_allowed(ordinary_ancestry(output));
return full_path(input, output) && ordinary_ancestry(output);
}

/** Compares two complete Windows path components without locale-sensitive folding. */
Expand Down Expand Up @@ -162,24 +141,23 @@ split(std::wstring_view path, std::wstring_view& parent, std::wstring_view& leaf
return MoveFileExW(source, target, MOVEFILE_WRITE_THROUGH) != FALSE;
}

/** Requires one ordinary directory and rejects a junction or symbolic-link leaf. */
[[nodiscard]] bool ordinary_directory(const wchar_t* path) noexcept {
/** Requires one ordinary directory. */
[[nodiscard]] bool is_directory(const wchar_t* path) noexcept {
if (path == nullptr || path[0] == L'\0') {
return false;
}
const DWORD attributes = GetFileAttributesW(path);
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0
&& (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0;
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

/** Requires one ordinary file and rejects a reparse-backed leaf. */
[[nodiscard]] bool ordinary_file(const wchar_t* path) noexcept {
[[nodiscard]] bool is_file(const wchar_t* path) noexcept {
if (path == nullptr || path[0] == L'\0') {
return false;
}
const DWORD attributes = GetFileAttributesW(path);
return attributes != INVALID_FILE_ATTRIBUTES
&& (attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) == 0;
&& (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
}

/** Accepts a missing final path or checks its exact expected ordinary kind. */
Expand All @@ -191,16 +169,15 @@ split(std::wstring_view path, std::wstring_view& parent, std::wstring_view& leaf
return error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND;
}
exists = true;
return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0
&& ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) == directory;
return ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) == directory;
}

/** Creates one empty ordinary directory or accepts an existing ordinary directory. */
[[nodiscard]] bool ensure_directory(const std::wstring& path) noexcept {
if (CreateDirectoryW(path.c_str(), nullptr) != FALSE) {
return true;
}
return GetLastError() == ERROR_ALREADY_EXISTS && ordinary_directory(path.c_str());
return GetLastError() == ERROR_ALREADY_EXISTS && is_directory(path.c_str());
}

/** Builds one collision-resistant sibling backup path without consulting global artifact state. */
Expand Down Expand Up @@ -237,7 +214,7 @@ split(std::wstring_view path, std::wstring_view& parent, std::wstring_view& leaf
/** Reads one exact marker without accepting extra bytes or alternate state. */
[[nodiscard]] bool read_marker(const std::wstring& path, Marker& output) noexcept {
output = {};
if (!ordinary_file(path.c_str())) {
if (!is_file(path.c_str())) {
return false;
}
const HANDLE file = CreateFileW(path.c_str(),
Expand Down Expand Up @@ -464,7 +441,6 @@ split(std::wstring_view path, std::wstring_view& parent, std::wstring_view& leaf
}
const std::wstring path = parent + L"\\" + std::wstring(name);
if ((entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0
|| (entry.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
|| !tree_publication::discard(path.c_str())) {
complete = false;
}
Expand Down Expand Up @@ -522,8 +498,8 @@ Status allocate(const wchar_t* finalPackPath, Stage& output) noexcept {
}
const std::wstring finalSdkDirectory = parentPath + L"\\sdk";
const std::wstring finalCatalogPath = finalSdkDirectory + L"\\catalog.bin";
if (!ordinary_directory(parentPath.c_str())
|| !ordinary_directory(finalSdkDirectory.c_str())
if (!is_directory(parentPath.c_str())
|| !is_directory(finalSdkDirectory.c_str())
|| !recover(finalSdkDirectory.c_str(), normalizedPack.c_str(), finalCatalogPath.c_str())
|| !discard_stale_stages(parentPath)) {
return Status::invalidInput;
Expand Down Expand Up @@ -600,8 +576,8 @@ Status publish(const Stage& stage,
|| !full_path(stage.catalogPath.c_str(), canonicalStageCatalog)
|| !full_path(finalPackPath, canonicalFinalPack)
|| !full_path(finalCatalogPath, canonicalFinalCatalog)
|| !ordinary_file(canonicalStagePack.c_str())
|| !ordinary_file(canonicalStageCatalog.c_str())) {
|| !is_file(canonicalStagePack.c_str())
|| !is_file(canonicalStageCatalog.c_str())) {
return Status::invalidInput;
}
const std::wstring expectedStageSdk = canonicalStageRoot + L"\\sdk";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ class FileHandle final {
}
const DWORD attributes = GetFileAttributesW(path);
if (attributes == INVALID_FILE_ATTRIBUTES
|| (attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) != 0) {
|| (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
return false;
}
const HANDLE file = CreateFileW(path,
Expand Down
9 changes: 3 additions & 6 deletions Sunrise/src/client/content/activity/activity_sdk_one_shot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ void report(OfflineProgressSink sink,
const std::wstring prefix = directory.substr(0, end);
const DWORD attributes = GetFileAttributesW(prefix.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES
|| (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0
|| (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
|| (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
return false;
}
if (separator == std::wstring::npos) {
Expand All @@ -132,15 +131,14 @@ void report(OfflineProgressSink sink,
}
}

/** Canonicalizes one existing directory after rejecting reparse points in its ancestry. */
/** Canonicalizes one existing directory. */
[[nodiscard]] bool final_directory_path(std::wstring_view input, std::wstring& output) noexcept {
std::wstring lexical;
if (!full_path(input, lexical) || !ordinary_ancestry(lexical)) {
return false;
}
const DWORD attributes = GetFileAttributesW(lexical.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES || (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0
|| (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
if (attributes == INVALID_FILE_ATTRIBUTES || (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
return false;
}
const HANDLE directory = CreateFileW(lexical.c_str(),
Expand Down Expand Up @@ -191,7 +189,6 @@ void report(OfflineProgressSink sink,
const DWORD attributes = GetFileAttributesW(lexical.c_str());
if (attributes != INVALID_FILE_ATTRIBUTES) {
return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0
&& (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0
&& final_directory_path(lexical, output);
}
const std::size_t separator = lexical.find_last_of(L"\\/");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ void copy_result(Status status, const Result& source, public_abi::Result& output
const std::wstring prefix = directory.substr(0, end);
const DWORD attributes = GetFileAttributesW(prefix.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES
|| (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0
|| (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
|| (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
return false;
}
if (separator == std::wstring::npos) {
Expand Down Expand Up @@ -155,10 +154,8 @@ void copy_result(Status status, const Result& source, public_abi::Result& output
const DWORD estateAttributes = GetFileAttributesW(output.c_str());
return ordinary_ancestry(output) && artifactAttributes != INVALID_FILE_ATTRIBUTES
&& (artifactAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
&& (artifactAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0
&& estateAttributes != INVALID_FILE_ATTRIBUTES
&& (estateAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
&& (estateAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0;
&& (estateAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

/**
Expand Down Expand Up @@ -197,8 +194,7 @@ void copy_result(Status status, const Result& source, public_abi::Result& output
}
const DWORD attributes = GetFileAttributesW(requestedPath.c_str());
return attributes == INVALID_FILE_ATTRIBUTES
|| ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0
&& (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0);
|| ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
} catch (...) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace {
namespace generated = state::activity_sdk::generated_world;
namespace manifest = state::activity_sdk::generated_world::manifest;

/** Resolves one directory and rejects every reparse-backed ancestor component. */
/** Resolves one directory. */
[[nodiscard]] bool ordinary_directory_path(const std::wstring& input,
std::wstring& output) noexcept {
output.clear();
Expand All @@ -40,8 +40,7 @@ namespace manifest = state::activity_sdk::generated_world::manifest;
const std::wstring prefix = output.substr(0, end);
const DWORD attributes = GetFileAttributesW(prefix.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES
|| (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0
|| (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
|| (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
output.clear();
return false;
}
Expand Down Expand Up @@ -148,10 +147,6 @@ bool clean_stale_shards(const std::wstring& directory,
std::uint32_t scenarioTag = 0;
generated::Digest digest{};
if (parse_shard_leaf(leaf, scenarioTag, digest)) {
if ((found.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
complete = false;
continue;
}
const auto record =
std::lower_bound(active.begin(),
active.end(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,31 +53,30 @@ struct Marker final {
}
}

/** Requires one existing ordinary directory and rejects a reparse-backed leaf. */
[[nodiscard]] bool ordinary_directory(const wchar_t* path) noexcept {
/** Requires one existing ordinary directory. */
[[nodiscard]] bool is_directory(const wchar_t* path) noexcept {
const DWORD attributes = GetFileAttributesW(path);
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0
&& (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0;
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

/** Requires one existing ordinary file and rejects a reparse-backed leaf. */
[[nodiscard]] bool ordinary_file(const wchar_t* path) noexcept {
/** Requires one existing ordinary file. */
[[nodiscard]] bool is_file(const wchar_t* path) noexcept {
const DWORD attributes = GetFileAttributesW(path);
return attributes != INVALID_FILE_ATTRIBUTES
&& (attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) == 0;
&& (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
}

/** Requires every existing drive-path directory component to be ordinary. */
[[nodiscard]] bool ordinary_ancestry(const std::wstring& directory) noexcept {
if (directory.size() < 3U || directory[1] != L':' || directory[2] != L'\\'
|| !ordinary_directory(directory.substr(0, 3U).c_str())) {
|| !is_directory(directory.substr(0, 3U).c_str())) {
return false;
}
std::size_t cursor = 3U;
while (cursor < directory.size()) {
const std::size_t separator = directory.find(L'\\', cursor);
const std::size_t end = separator == std::wstring::npos ? directory.size() : separator;
if (!ordinary_directory(directory.substr(0, end).c_str())) {
if (!is_directory(directory.substr(0, end).c_str())) {
return false;
}
if (separator == std::wstring::npos) {
Expand Down Expand Up @@ -192,7 +191,7 @@ sibling_path(std::wstring_view output, std::wstring_view suffix, std::wstring& s
/** Reads one exact marker without accepting trailing bytes or noncanonical fields. */
[[nodiscard]] bool read_marker(const std::wstring& path, Marker& output) noexcept {
output = {};
if (!ordinary_file(path.c_str())) {
if (!is_file(path.c_str())) {
return false;
}
const HANDLE file = CreateFileW(path.c_str(),
Expand Down Expand Up @@ -225,8 +224,7 @@ sibling_path(std::wstring_view output, std::wstring_view suffix, std::wstring& s
return error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND;
}
exists = true;
return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0
&& (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0;
return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

/** Restores or finalizes one interrupted transaction before any new publication begins. */
Expand Down Expand Up @@ -324,7 +322,7 @@ Status publish(const wchar_t* stage,
} catch (...) {
return Status::invalidInput;
}
if (!ordinary_ancestry(parent) || !ordinary_directory(canonicalStage.c_str())) {
if (!ordinary_ancestry(parent) || !is_directory(canonicalStage.c_str())) {
return Status::invalidInput;
}
std::wstring backup;
Expand Down