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