blob: 53bbba6615eb953bfe920581465dcb277f22c95d [file]
/*
* Copyright (c) 2025 The Khronos Group Inc.
* Copyright (c) 2025 Valve Corporation
* Copyright (c) 2025 LunarG, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and/or associated documentation files (the "Materials"), to
* deal in the Materials without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Materials, and to permit persons to whom the Materials are
* furnished to do so, subject to the following conditions:
*
* The above copyright notice(s) and this permission notice shall be included in
* all copies or substantial portions of the Materials.
*
* THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
* USE OR OTHER DEALINGS IN THE MATERIALS.
*
*/
#include "folder_manager.h"
#include <algorithm>
#include <fstream>
#include "gtest/gtest.h"
#include "util/json_writer.h"
#include "util/env_var_wrapper.h"
namespace fs {
Folder::Folder(std::filesystem::path root_path, std::string name) noexcept : folder((root_path / name).lexically_normal()) {
clear();
// Don't actually create the folder yet, as we will do it on demand
}
Folder::~Folder() noexcept { clear(); }
Folder::Folder(Folder&& other) noexcept : actually_created(other.actually_created), folder(other.folder) { other.folder.clear(); }
Folder& Folder::operator=(Folder&& other) noexcept {
folder = other.folder;
actually_created = other.actually_created;
other.folder.clear();
return *this;
}
void Folder::check_if_first_use() {
if (!actually_created) {
if (!::testing::internal::InDeathTestChild()) {
std::error_code err;
if (!std::filesystem::create_directories(folder, err)) {
std::cerr << "Failed to create folder " << folder << " because " << err.message() << "\n";
}
}
actually_created = true;
}
}
std::filesystem::path Folder::write_manifest(std::filesystem::path const& name, std::string const& contents) {
check_if_first_use();
std::filesystem::path out_path = (folder / name).lexically_normal();
if (!::testing::internal::InDeathTestChild()) {
auto file = std::ofstream(out_path, std::ios_base::trunc | std::ios_base::out);
if (!file) {
std::cerr << "Failed to create manifest " << name << " at " << out_path << "\n";
return out_path;
}
file << contents << std::endl;
}
insert_file_to_tracking(name);
return out_path;
}
// close file handle, delete file, remove `name` from managed file list.
void Folder::remove(std::filesystem::path const& name) {
check_if_first_use();
std::filesystem::path out_path = folder / name;
if (!::testing::internal::InDeathTestChild()) {
std::error_code err;
if (!std::filesystem::remove(out_path, err)) {
std::cerr << "Failed to remove file " << name << " at " << out_path << " because " << err.message() << "\n";
}
}
auto found = std::find(added_files.begin(), added_files.end(), name);
if (found != added_files.end()) {
added_files.erase(found);
} else {
std::cout << "File " << name << " not in tracked files of folder " << folder << ".\n";
}
}
// copy file into this folder
std::filesystem::path Folder::copy_file(std::filesystem::path const& file, std::filesystem::path const& new_name) {
check_if_first_use();
insert_file_to_tracking(new_name);
auto new_filepath = folder / new_name;
if (!::testing::internal::InDeathTestChild()) {
std::error_code err;
if (!std::filesystem::copy_file(file, new_filepath, err)) {
std::cerr << "Failed to copy file " << file << " to " << new_filepath << " because " << err.message() << "\n";
}
}
return new_filepath;
}
std::vector<std::filesystem::path> Folder::get_files() const { return added_files; }
std::filesystem::path Folder::add_symlink(std::filesystem::path const& target, std::filesystem::path const& link_name) {
check_if_first_use();
if (!::testing::internal::InDeathTestChild()) {
std::error_code err;
std::filesystem::create_symlink(target, folder / link_name, err);
if (err.value() != 0) {
std::cerr << "Failed to create symlink with target" << target << " with name " << folder / link_name << " because "
<< err.message() << "\n";
}
}
insert_file_to_tracking(link_name);
return folder / link_name;
}
void Folder::insert_file_to_tracking(std::filesystem::path const& name) {
auto found = std::find(added_files.begin(), added_files.end(), name);
if (found != added_files.end()) {
std::cout << "Overwriting manifest " << name << ". Was this intended?\n";
} else {
added_files.emplace_back(name);
}
}
void Folder::clear() const noexcept {
if (!::testing::internal::InDeathTestChild()) {
std::error_code err;
std::filesystem::remove_all(folder, err);
if (err.value() != 0) {
std::cerr << "Failed to remove folder " << folder << " because " << err.message() << "\n";
}
}
}
std::filesystem::path category_path_name(ManifestCategory category) {
if (category == ManifestCategory::settings) return "settings.d";
if (category == ManifestCategory::implicit_layer) return "implicit_layer.d";
if (category == ManifestCategory::explicit_layer)
return "explicit_layer.d";
else
return "icd.d";
}
FileSystemManager::FileSystemManager()
: root_folder(std::filesystem::path(TEST_EXECUTION_DIRECTORY),
std::string(::testing::UnitTest::GetInstance()->GetInstance()->current_test_suite()->name()) + "_" +
::testing::UnitTest::GetInstance()->current_test_info()->name()) {
// Clean out test folder in case a previous run's files are still around
root_folder.clear();
auto const& root = root_folder.location();
folders.try_emplace(ManifestLocation::null, root, std::string("null_dir"));
folders.try_emplace(ManifestLocation::driver, root, std::string("icd_manifests"));
folders.try_emplace(ManifestLocation::driver_env_var, root, std::string("icd_env_vars_manifests"));
folders.try_emplace(ManifestLocation::explicit_layer, root, std::string("explicit_layer_manifests"));
folders.try_emplace(ManifestLocation::explicit_layer_env_var, root, std::string("explicit_env_var_layer_folder"));
folders.try_emplace(ManifestLocation::explicit_layer_add_env_var, root, std::string("explicit_add_env_var_layer_folder"));
folders.try_emplace(ManifestLocation::implicit_layer, root, std::string("implicit_layer_manifests"));
folders.try_emplace(ManifestLocation::implicit_layer_env_var, root, std::string("implicit_env_var_layer_manifests"));
folders.try_emplace(ManifestLocation::implicit_layer_add_env_var, root, std::string("implicit_add_env_var_layer_manifests"));
folders.try_emplace(ManifestLocation::override_layer, root, std::string("override_layer_manifests"));
#if defined(_WIN32)
folders.try_emplace(ManifestLocation::windows_app_package, root, std::string("app_package_manifests"));
#endif
#if defined(__APPLE__)
folders.try_emplace(ManifestLocation::macos_bundle, root, std::string("macos_bundle"));
#endif
folders.try_emplace(ManifestLocation::settings_location, root, std::string("settings_location"));
folders.try_emplace(ManifestLocation::unsecured_driver, root, std::string("unsecured_driver"));
folders.try_emplace(ManifestLocation::unsecured_explicit_layer, root, std::string("unsecured_explicit_layer"));
folders.try_emplace(ManifestLocation::unsecured_implicit_layer, root, std::string("unsecured_implicit_layer"));
folders.try_emplace(ManifestLocation::unsecured_settings, root, std::string("unsecured_settings"));
}
fs::Folder& FileSystemManager::get_folder(ManifestLocation location) noexcept { return folders.at(location); }
fs::Folder const& FileSystemManager::get_folder(ManifestLocation location) const noexcept { return folders.at(location); }
Folder* FileSystemManager::get_folder_for_given_path(std::string_view given_path) noexcept {
for (auto const& [manifest_location, folder] : folders) {
if (folder.location() == given_path) {
return &folders.at(manifest_location);
}
}
for (auto const& [redirected_path, redirected_location] : redirected_paths) {
if (redirected_path == given_path) {
return &folders.at(redirected_location);
}
const auto mismatch_pair =
std::mismatch(given_path.begin(), given_path.end(), redirected_path.begin(), redirected_path.end());
if (mismatch_pair.second == redirected_path.end()) return &folders.at(redirected_location);
}
return nullptr;
}
bool FileSystemManager::is_folder_path(std::filesystem::path const& path) const noexcept {
for (auto const& [location, folder] : folders) {
if (path == folder.location()) {
return true;
}
}
return false;
}
void FileSystemManager::add_path_redirect(std::filesystem::path const& redirected_path, ManifestLocation location) {
redirected_paths[redirected_path.string()] = location;
}
std::filesystem::path FileSystemManager::get_real_path_of_redirected_path(std::filesystem::path const& path) const {
if (redirected_paths.count(path.string()) > 0) {
return folders.at(redirected_paths.at(path.lexically_normal().string())).location();
}
for (auto const& [redirected_path_str, redirected_location] : redirected_paths) {
std::filesystem::path redirected_path = redirected_path_str;
const auto mismatch_pair = std::mismatch(path.begin(), path.end(), redirected_path.begin(), redirected_path.end());
if (mismatch_pair.second == redirected_path.end()) return folders.at(redirected_location).location();
}
return std::filesystem::path{};
}
std::filesystem::path FileSystemManager::get_path_redirect_by_manifest_location(ManifestLocation location) const {
for (auto const& [path, redirected_location] : redirected_paths) {
if (redirected_location == location) {
return path;
}
}
return folders.at(ManifestLocation::null).location();
}
} // namespace fs