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