1 #include "persistent_json_storage.hpp" 2 3 #include <phosphor-logging/log.hpp> 4 5 #include <fstream> 6 #include <stdexcept> 7 8 using namespace std::literals::string_literals; 9 10 PersistentJsonStorage::PersistentJsonStorage(const DirectoryPath& directory) : 11 directory(directory) 12 {} 13 14 void PersistentJsonStorage::store(const FilePath& filePath, 15 const nlohmann::json& data) 16 { 17 try 18 { 19 const auto path = join(directory, filePath); 20 std::error_code ec; 21 22 phosphor::logging::log<phosphor::logging::level::DEBUG>( 23 "Store to file", phosphor::logging::entry("PATH=%s", path.c_str())); 24 25 std::filesystem::create_directories(path.parent_path(), ec); 26 if (ec) 27 { 28 throw std::runtime_error( 29 "Unable to create directory for file: " + path.string() + 30 ", ec=" + std::to_string(ec.value()) + ": " + ec.message()); 31 } 32 33 assertThatPathIsNotSymlink(path); 34 35 std::ofstream file(path); 36 file << data; 37 if (!file) 38 { 39 throw std::runtime_error("Unable to create file: " + path.string()); 40 } 41 42 limitPermissions(path.parent_path()); 43 limitPermissions(path); 44 } 45 catch (...) 46 { 47 remove(filePath); 48 throw; 49 } 50 } 51 52 bool PersistentJsonStorage::remove(const FilePath& filePath) 53 { 54 const auto path = join(directory, filePath); 55 56 if (std::filesystem::is_symlink(path)) 57 { 58 return false; 59 } 60 61 std::error_code ec; 62 63 auto removed = std::filesystem::remove(path, ec); 64 if (!removed) 65 { 66 phosphor::logging::log<phosphor::logging::level::ERR>( 67 "Unable to remove file", 68 phosphor::logging::entry("PATH=%s", path.c_str()), 69 phosphor::logging::entry("ERROR_CODE=%d", ec.value())); 70 return false; 71 } 72 73 /* removes directory only if it is empty */ 74 std::filesystem::remove(path.parent_path(), ec); 75 76 return true; 77 } 78 79 std::optional<nlohmann::json> 80 PersistentJsonStorage::load(const FilePath& filePath) const 81 { 82 const auto path = join(directory, filePath); 83 if (!std::filesystem::exists(path)) 84 { 85 return std::nullopt; 86 } 87 88 nlohmann::json result; 89 90 try 91 { 92 assertThatPathIsNotSymlink(path); 93 std::ifstream file(path); 94 file >> result; 95 } 96 catch (const std::exception& e) 97 { 98 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 99 return std::nullopt; 100 } 101 102 return result; 103 } 104 105 std::vector<interfaces::JsonStorage::FilePath> 106 PersistentJsonStorage::list() const 107 { 108 std::vector<interfaces::JsonStorage::FilePath> result; 109 if (!std::filesystem::exists(directory)) 110 { 111 return result; 112 } 113 114 for (const auto& p : 115 std::filesystem::recursive_directory_iterator(directory)) 116 { 117 if (p.is_regular_file() && !p.is_symlink()) 118 { 119 auto item = std::filesystem::relative(p.path(), directory); 120 result.emplace_back(std::move(item)); 121 } 122 } 123 124 return result; 125 } 126 127 std::filesystem::path 128 PersistentJsonStorage::join(const std::filesystem::path& left, 129 const std::filesystem::path& right) 130 { 131 return left / right; 132 } 133 134 void PersistentJsonStorage::limitPermissions(const std::filesystem::path& path) 135 { 136 constexpr auto filePerms = std::filesystem::perms::owner_read | 137 std::filesystem::perms::owner_write; 138 constexpr auto dirPerms = filePerms | std::filesystem::perms::owner_exec; 139 std::filesystem::permissions( 140 path, std::filesystem::is_directory(path) ? dirPerms : filePerms, 141 std::filesystem::perm_options::replace); 142 } 143 144 bool PersistentJsonStorage::exist(const FilePath& subPath) const 145 { 146 return std::filesystem::exists(join(directory, subPath)); 147 } 148 149 void PersistentJsonStorage::assertThatPathIsNotSymlink( 150 const std::filesystem::path& path) 151 { 152 if (std::filesystem::is_symlink(path)) 153 { 154 throw std::runtime_error( 155 "Source/Target file is a symlink! Operation canceled on path "s + 156 path.c_str()); 157 } 158 } 159