Browse Source

Merge pull request #124 from timoreo22/fix/wine-sdk

Removed checks against symlinks causing errors when running on Wine
stan 2 days ago
parent
commit
5f9b9895e5

+ 2 - 3
Sunrise/src/client/content/activity/activity_sdk_generation_worker.cpp

@@ -163,7 +163,7 @@ void log_stage_duration(std::string_view stage,
     return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
     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 {
 [[nodiscard]] bool isolated_output_ready(std::wstring_view path) noexcept {
     if (path.empty()) {
     if (path.empty()) {
         return false;
         return false;
@@ -175,8 +175,7 @@ void log_stage_duration(std::string_view stage,
             const DWORD error = GetLastError();
             const DWORD error = GetLastError();
             return error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND;
             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;
             return false;
         }
         }
         WIN32_FIND_DATAW entry{};
         WIN32_FIND_DATAW entry{};

+ 25 - 49
Sunrise/src/client/content/activity/activity_sdk_live_publication.cpp

@@ -10,7 +10,6 @@
 #include <string_view>
 #include <string_view>
 
 
 #include "activity_sdk_tree_publication.h"
 #include "activity_sdk_tree_publication.h"
-#include "core/runtime/host_environment.h"
 
 
 namespace sunrise::client::content::activity::sdk_generation::live_publication {
 namespace sunrise::client::content::activity::sdk_generation::live_publication {
 namespace {
 namespace {
@@ -43,7 +42,7 @@ struct Marker final {
     std::array<std::uint8_t, 3> reserved{};
     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. */
 /** Resolves one null-terminated lexical path into normalized absolute storage. */
 [[nodiscard]] bool full_path(const wchar_t* input, std::wstring& output) noexcept {
 [[nodiscard]] bool full_path(const wchar_t* input, std::wstring& output) noexcept {
@@ -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()) {
     while (cursor < directory.size()) {
         const std::size_t separator = directory.find(L'\\', cursor);
         const std::size_t separator = directory.find(L'\\', cursor);
         const std::size_t end = separator == std::wstring::npos ? directory.size() : separator;
         const std::size_t end = separator == std::wstring::npos ? directory.size() : separator;
         const std::wstring prefix = directory.substr(0, end);
         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) {
         if (separator == std::wstring::npos) {
             break;
             break;
         }
         }
         cursor = separator + 1U;
         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. */
 /** Resolves one existing ordinary directory and rejects reparse points in every ancestor. */
 [[nodiscard]] bool canonical_directory(const wchar_t* input, std::wstring& output) noexcept {
 [[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. */
 /** Compares two complete Windows path components without locale-sensitive folding. */
@@ -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;
     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') {
     if (path == nullptr || path[0] == L'\0') {
         return false;
         return false;
     }
     }
     const DWORD attributes = GetFileAttributesW(path);
     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. */
 /** 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') {
     if (path == nullptr || path[0] == L'\0') {
         return false;
         return false;
     }
     }
     const DWORD attributes = GetFileAttributesW(path);
     const DWORD attributes = GetFileAttributesW(path);
     return attributes != INVALID_FILE_ATTRIBUTES
     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. */
 /** Accepts a missing final path or checks its exact expected ordinary kind. */
@@ -191,8 +169,7 @@ split(std::wstring_view path, std::wstring_view& parent, std::wstring_view& leaf
         return error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND;
         return error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND;
     }
     }
     exists = true;
     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. */
 /** Creates one empty ordinary directory or accepts an existing ordinary directory. */
@@ -200,7 +177,7 @@ split(std::wstring_view path, std::wstring_view& parent, std::wstring_view& leaf
     if (CreateDirectoryW(path.c_str(), nullptr) != FALSE) {
     if (CreateDirectoryW(path.c_str(), nullptr) != FALSE) {
         return true;
         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. */
 /** Builds one collision-resistant sibling backup path without consulting global artifact state. */
@@ -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. */
 /** Reads one exact marker without accepting extra bytes or alternate state. */
 [[nodiscard]] bool read_marker(const std::wstring& path, Marker& output) noexcept {
 [[nodiscard]] bool read_marker(const std::wstring& path, Marker& output) noexcept {
     output = {};
     output = {};
-    if (!ordinary_file(path.c_str())) {
+    if (!is_file(path.c_str())) {
         return false;
         return false;
     }
     }
     const HANDLE file = CreateFileW(path.c_str(),
     const HANDLE file = CreateFileW(path.c_str(),
@@ -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);
         const std::wstring path = parent + L"\\" + std::wstring(name);
         if ((entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0
         if ((entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0
-            || (entry.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
             || !tree_publication::discard(path.c_str())) {
             || !tree_publication::discard(path.c_str())) {
             complete = false;
             complete = false;
         }
         }
@@ -522,8 +498,8 @@ Status allocate(const wchar_t* finalPackPath, Stage& output) noexcept {
         }
         }
         const std::wstring finalSdkDirectory = parentPath + L"\\sdk";
         const std::wstring finalSdkDirectory = parentPath + L"\\sdk";
         const std::wstring finalCatalogPath = finalSdkDirectory + L"\\catalog.bin";
         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())
             || !recover(finalSdkDirectory.c_str(), normalizedPack.c_str(), finalCatalogPath.c_str())
             || !discard_stale_stages(parentPath)) {
             || !discard_stale_stages(parentPath)) {
             return Status::invalidInput;
             return Status::invalidInput;
@@ -600,8 +576,8 @@ Status publish(const Stage& stage,
         || !full_path(stage.catalogPath.c_str(), canonicalStageCatalog)
         || !full_path(stage.catalogPath.c_str(), canonicalStageCatalog)
         || !full_path(finalPackPath, canonicalFinalPack)
         || !full_path(finalPackPath, canonicalFinalPack)
         || !full_path(finalCatalogPath, canonicalFinalCatalog)
         || !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;
         return Status::invalidInput;
     }
     }
     const std::wstring expectedStageSdk = canonicalStageRoot + L"\\sdk";
     const std::wstring expectedStageSdk = canonicalStageRoot + L"\\sdk";

+ 1 - 1
Sunrise/src/client/content/activity/activity_sdk_lua_publish.cpp

@@ -158,7 +158,7 @@ private:
     }
     }
     const DWORD attributes = GetFileAttributesW(path);
     const DWORD attributes = GetFileAttributesW(path);
     if (attributes == INVALID_FILE_ATTRIBUTES
     if (attributes == INVALID_FILE_ATTRIBUTES
-        || (attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) != 0) {
+        || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
         return false;
         return false;
     }
     }
     const HANDLE file = CreateFileW(path,
     const HANDLE file = CreateFileW(path,

+ 3 - 6
Sunrise/src/client/content/activity/activity_sdk_one_shot.cpp

@@ -118,8 +118,7 @@ void report(OfflineProgressSink sink,
             const std::wstring prefix = directory.substr(0, end);
             const std::wstring prefix = directory.substr(0, end);
             const DWORD attributes = GetFileAttributesW(prefix.c_str());
             const DWORD attributes = GetFileAttributesW(prefix.c_str());
             if (attributes == INVALID_FILE_ATTRIBUTES
             if (attributes == INVALID_FILE_ATTRIBUTES
-                || (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0
-                || (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
+                || (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
                 return false;
                 return false;
             }
             }
             if (separator == std::wstring::npos) {
             if (separator == std::wstring::npos) {
@@ -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 {
 [[nodiscard]] bool final_directory_path(std::wstring_view input, std::wstring& output) noexcept {
     std::wstring lexical;
     std::wstring lexical;
     if (!full_path(input, lexical) || !ordinary_ancestry(lexical)) {
     if (!full_path(input, lexical) || !ordinary_ancestry(lexical)) {
         return false;
         return false;
     }
     }
     const DWORD attributes = GetFileAttributesW(lexical.c_str());
     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;
         return false;
     }
     }
     const HANDLE directory = CreateFileW(lexical.c_str(),
     const HANDLE directory = CreateFileW(lexical.c_str(),
@@ -191,7 +189,6 @@ void report(OfflineProgressSink sink,
     const DWORD attributes = GetFileAttributesW(lexical.c_str());
     const DWORD attributes = GetFileAttributesW(lexical.c_str());
     if (attributes != INVALID_FILE_ATTRIBUTES) {
     if (attributes != INVALID_FILE_ATTRIBUTES) {
         return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0
         return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0
-               && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0
                && final_directory_path(lexical, output);
                && final_directory_path(lexical, output);
     }
     }
     const std::size_t separator = lexical.find_last_of(L"\\/");
     const std::size_t separator = lexical.find_last_of(L"\\/");

+ 3 - 7
Sunrise/src/client/content/activity/activity_sdk_one_shot_export.cpp

@@ -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 std::wstring prefix = directory.substr(0, end);
             const DWORD attributes = GetFileAttributesW(prefix.c_str());
             const DWORD attributes = GetFileAttributesW(prefix.c_str());
             if (attributes == INVALID_FILE_ATTRIBUTES
             if (attributes == INVALID_FILE_ATTRIBUTES
-                || (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0
-                || (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
+                || (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
                 return false;
                 return false;
             }
             }
             if (separator == std::wstring::npos) {
             if (separator == std::wstring::npos) {
@@ -155,10 +154,8 @@ void copy_result(Status status, const Result& source, public_abi::Result& output
     const DWORD estateAttributes = GetFileAttributesW(output.c_str());
     const DWORD estateAttributes = GetFileAttributesW(output.c_str());
     return ordinary_ancestry(output) && artifactAttributes != INVALID_FILE_ATTRIBUTES
     return ordinary_ancestry(output) && artifactAttributes != INVALID_FILE_ATTRIBUTES
            && (artifactAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
            && (artifactAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
-           && (artifactAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0
            && estateAttributes != INVALID_FILE_ATTRIBUTES
            && estateAttributes != INVALID_FILE_ATTRIBUTES
-           && (estateAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
-           && (estateAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0;
+           && (estateAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
 }
 }
 
 
 /**
 /**
@@ -197,8 +194,7 @@ void copy_result(Status status, const Result& source, public_abi::Result& output
         }
         }
         const DWORD attributes = GetFileAttributesW(requestedPath.c_str());
         const DWORD attributes = GetFileAttributesW(requestedPath.c_str());
         return attributes == INVALID_FILE_ATTRIBUTES
         return attributes == INVALID_FILE_ATTRIBUTES
-               || ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0
-                   && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0);
+               || ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
     } catch (...) {
     } catch (...) {
         return false;
         return false;
     }
     }

+ 2 - 7
Sunrise/src/client/content/activity/activity_sdk_shard_cleanup.cpp

@@ -13,7 +13,7 @@ namespace {
 namespace generated = state::activity_sdk::generated_world;
 namespace generated = state::activity_sdk::generated_world;
 namespace manifest = state::activity_sdk::generated_world::manifest;
 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,
 [[nodiscard]] bool ordinary_directory_path(const std::wstring& input,
                                            std::wstring& output) noexcept {
                                            std::wstring& output) noexcept {
     output.clear();
     output.clear();
@@ -40,8 +40,7 @@ namespace manifest = state::activity_sdk::generated_world::manifest;
             const std::wstring prefix = output.substr(0, end);
             const std::wstring prefix = output.substr(0, end);
             const DWORD attributes = GetFileAttributesW(prefix.c_str());
             const DWORD attributes = GetFileAttributesW(prefix.c_str());
             if (attributes == INVALID_FILE_ATTRIBUTES
             if (attributes == INVALID_FILE_ATTRIBUTES
-                || (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0
-                || (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
+                || (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
                 output.clear();
                 output.clear();
                 return false;
                 return false;
             }
             }
@@ -148,10 +147,6 @@ bool clean_stale_shards(const std::wstring& directory,
         std::uint32_t scenarioTag = 0;
         std::uint32_t scenarioTag = 0;
         generated::Digest digest{};
         generated::Digest digest{};
         if (parse_shard_leaf(leaf, scenarioTag, digest)) {
         if (parse_shard_leaf(leaf, scenarioTag, digest)) {
-            if ((found.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
-                complete = false;
-                continue;
-            }
             const auto record =
             const auto record =
                 std::lower_bound(active.begin(),
                 std::lower_bound(active.begin(),
                                  active.end(),
                                  active.end(),

+ 11 - 13
Sunrise/src/client/content/activity/activity_sdk_tree_publication.cpp

@@ -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);
     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);
     const DWORD attributes = GetFileAttributesW(path);
     return attributes != INVALID_FILE_ATTRIBUTES
     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. */
 /** Requires every existing drive-path directory component to be ordinary. */
 [[nodiscard]] bool ordinary_ancestry(const std::wstring& directory) noexcept {
 [[nodiscard]] bool ordinary_ancestry(const std::wstring& directory) noexcept {
     if (directory.size() < 3U || directory[1] != L':' || directory[2] != L'\\'
     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;
         return false;
     }
     }
     std::size_t cursor = 3U;
     std::size_t cursor = 3U;
     while (cursor < directory.size()) {
     while (cursor < directory.size()) {
         const std::size_t separator = directory.find(L'\\', cursor);
         const std::size_t separator = directory.find(L'\\', cursor);
         const std::size_t end = separator == std::wstring::npos ? directory.size() : separator;
         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;
             return false;
         }
         }
         if (separator == std::wstring::npos) {
         if (separator == std::wstring::npos) {
@@ -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. */
 /** Reads one exact marker without accepting trailing bytes or noncanonical fields. */
 [[nodiscard]] bool read_marker(const std::wstring& path, Marker& output) noexcept {
 [[nodiscard]] bool read_marker(const std::wstring& path, Marker& output) noexcept {
     output = {};
     output = {};
-    if (!ordinary_file(path.c_str())) {
+    if (!is_file(path.c_str())) {
         return false;
         return false;
     }
     }
     const HANDLE file = CreateFileW(path.c_str(),
     const HANDLE file = CreateFileW(path.c_str(),
@@ -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;
         return error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND;
     }
     }
     exists = true;
     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. */
 /** Restores or finalizes one interrupted transaction before any new publication begins. */
@@ -324,7 +322,7 @@ Status publish(const wchar_t* stage,
     } catch (...) {
     } catch (...) {
         return Status::invalidInput;
         return Status::invalidInput;
     }
     }
-    if (!ordinary_ancestry(parent) || !ordinary_directory(canonicalStage.c_str())) {
+    if (!ordinary_ancestry(parent) || !is_directory(canonicalStage.c_str())) {
         return Status::invalidInput;
         return Status::invalidInput;
     }
     }
     std::wstring backup;
     std::wstring backup;