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
96 {
97     std::vector<interfaces::JsonStorage::FilePath> result;
98     if (!std::filesystem::exists(directory))
99     {
100         return result;
101     }
102 
103     for (const auto& p :
104          std::filesystem::recursive_directory_iterator(directory))
105     {
106         if (p.is_regular_file())
107         {
108             auto item = std::filesystem::relative(p.path(), directory);
109             result.emplace_back(std::move(item));
110         }
111     }
112 
113     return result;
114 }
115 
116 std::filesystem::path
117     PersistentJsonStorage::join(const std::filesystem::path& left,
118                                 const std::filesystem::path& right)
119 {
120     return left / right;
121 }
122 
123 void PersistentJsonStorage::limitPermissions(const std::filesystem::path& path)
124 {
125     constexpr auto filePerms = std::filesystem::perms::owner_read |
126                                std::filesystem::perms::owner_write;
127     constexpr auto dirPerms = filePerms | std::filesystem::perms::owner_exec;
128     std::filesystem::permissions(
129         path, std::filesystem::is_directory(path) ? dirPerms : filePerms,
130         std::filesystem::perm_options::replace);
131 }
132 
133 bool PersistentJsonStorage::exist(const FilePath& subPath) const
134 {
135     return std::filesystem::exists(join(directory, subPath));
136 }
137