blob: 0dc66ad37067b50790720cd1561d274301008428 [file] [log] [blame]
Krzysztof Grobelny73da6902020-09-24 13:42:04 +02001#include "persistent_json_storage.hpp"
2
3#include <phosphor-logging/log.hpp>
4
5#include <fstream>
6#include <stdexcept>
7
Krzysztof Grobelnya06626d2022-11-24 15:22:40 +00008using namespace std::literals::string_literals;
9
Krzysztof Grobelny73da6902020-09-24 13:42:04 +020010PersistentJsonStorage::PersistentJsonStorage(const DirectoryPath& directory) :
11 directory(directory)
12{}
13
14void 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>(
Wludzik, Jozef982c5b52021-01-02 12:05:21 +010023 "Store to file", phosphor::logging::entry("PATH=%s", path.c_str()));
Krzysztof Grobelny73da6902020-09-24 13:42:04 +020024
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
Krzysztof Grobelnya06626d2022-11-24 15:22:40 +000033 assertThatPathIsNotSymlink(path);
34
Krzysztof Grobelny73da6902020-09-24 13:42:04 +020035 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
52bool PersistentJsonStorage::remove(const FilePath& filePath)
53{
54 const auto path = join(directory, filePath);
Krzysztof Grobelnya06626d2022-11-24 15:22:40 +000055
56 if (std::filesystem::is_symlink(path))
57 {
58 return false;
59 }
60
Krzysztof Grobelny73da6902020-09-24 13:42:04 +020061 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",
Wludzik, Jozef982c5b52021-01-02 12:05:21 +010068 phosphor::logging::entry("PATH=%s", path.c_str()),
69 phosphor::logging::entry("ERROR_CODE=%d", ec.value()));
Krzysztof Grobelny73da6902020-09-24 13:42:04 +020070 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
79std::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 {
Krzysztof Grobelnya06626d2022-11-24 15:22:40 +000092 assertThatPathIsNotSymlink(path);
Krzysztof Grobelny73da6902020-09-24 13:42:04 +020093 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
105std::vector<interfaces::JsonStorage::FilePath>
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100106 PersistentJsonStorage::list() const
Krzysztof Grobelny73da6902020-09-24 13:42:04 +0200107{
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100108 std::vector<interfaces::JsonStorage::FilePath> result;
109 if (!std::filesystem::exists(directory))
Krzysztof Grobelny73da6902020-09-24 13:42:04 +0200110 {
111 return result;
112 }
113
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100114 for (const auto& p :
115 std::filesystem::recursive_directory_iterator(directory))
Krzysztof Grobelny73da6902020-09-24 13:42:04 +0200116 {
Krzysztof Grobelnya06626d2022-11-24 15:22:40 +0000117 if (p.is_regular_file() && !p.is_symlink())
Krzysztof Grobelny73da6902020-09-24 13:42:04 +0200118 {
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100119 auto item = std::filesystem::relative(p.path(), directory);
120 result.emplace_back(std::move(item));
Krzysztof Grobelny73da6902020-09-24 13:42:04 +0200121 }
122 }
123
124 return result;
125}
126
127std::filesystem::path
128 PersistentJsonStorage::join(const std::filesystem::path& left,
129 const std::filesystem::path& right)
130{
131 return left / right;
132}
133
134void 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}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100143
144bool PersistentJsonStorage::exist(const FilePath& subPath) const
145{
146 return std::filesystem::exists(join(directory, subPath));
147}
Krzysztof Grobelnya06626d2022-11-24 15:22:40 +0000148
149void 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}