1 #include "dump_entry.hpp"
2 
3 #include "dump_manager.hpp"
4 
5 #include <fcntl.h>
6 
7 #include <nlohmann/json.hpp>
8 #include <phosphor-logging/elog-errors.hpp>
9 #include <phosphor-logging/elog.hpp>
10 #include <phosphor-logging/lg2.hpp>
11 #include <sdeventplus/event.hpp>
12 #include <sdeventplus/source/event.hpp>
13 #include <xyz/openbmc_project/Common/File/error.hpp>
14 #include <xyz/openbmc_project/Common/error.hpp>
15 
16 #include <cstring>
17 
18 namespace phosphor
19 {
20 namespace dump
21 {
22 
23 using namespace phosphor::logging;
24 
25 void Entry::delete_()
26 {
27     // Remove Dump entry D-bus object
28     parent.erase(id);
29 }
30 
31 sdbusplus::message::unix_fd Entry::getFileHandle()
32 {
33     using namespace sdbusplus::xyz::openbmc_project::Common::File::Error;
34     using metadata = xyz::openbmc_project::Common::File::Open;
35     if (file.empty())
36     {
37         lg2::error("Failed to get file handle: File path is empty.");
38         elog<sdbusplus::xyz::openbmc_project::Common::Error::Unavailable>();
39     }
40 
41     if (fdCloseEventSource)
42     {
43         // Return the existing file descriptor
44         return fdCloseEventSource->first;
45     }
46 
47     int fd = open(file.c_str(), O_RDONLY | O_NONBLOCK);
48     if (fd == -1)
49     {
50         auto err = errno;
51         lg2::error("Failed to open dump file: id: {ID} error: {ERRNO}", "ID",
52                    id, "ERRNO", std::strerror(errno));
53         elog<Open>(metadata::ERRNO(err), metadata::PATH(file.c_str()));
54     }
55 
56     // Create a new Defer event source for closing this fd
57     sdeventplus::Event event = sdeventplus::Event::get_default();
58     auto eventSource = std::make_unique<sdeventplus::source::Defer>(
59         event, [this](auto& /*source*/) { closeFD(); });
60 
61     // Store the file descriptor and event source in the optional pair
62     fdCloseEventSource = std::make_pair(fd, std::move(eventSource));
63 
64     return fd;
65 }
66 
67 void Entry::serialize()
68 {
69     // Folder for serialized entry
70     std::filesystem::path dir = file.parent_path() / PRESERVE;
71 
72     // Serialized entry file
73     std::filesystem::path serializePath = dir / SERIAL_FILE;
74     try
75     {
76         if (!std::filesystem::exists(dir))
77         {
78             std::filesystem::create_directories(dir);
79         }
80 
81         std::ofstream os(serializePath, std::ios::binary);
82         if (!os.is_open())
83         {
84             lg2::error("Failed to open file for serialization: {PATH} ", "PATH",
85                        serializePath);
86             return;
87         }
88         nlohmann::json j;
89         j["version"] = CLASS_SERIALIZATION_VERSION;
90         j["dumpId"] = id;
91         j["originatorId"] = originatorId();
92         j["originatorType"] = originatorType();
93         j["startTime"] = startTime();
94 
95         os << j.dump();
96     }
97     catch (const std::exception& e)
98     {
99         lg2::error("Serialization error: {PATH} {ERROR} ", "PATH",
100                    serializePath, "ERROR", e);
101 
102         // Remove the serialization folder if that got created
103         // Ignore the error since folder may not be created
104         std::error_code ec;
105         std::filesystem::remove_all(dir, ec);
106     }
107 }
108 
109 void Entry::deserialize(const std::filesystem::path& dumpPath)
110 {
111     try
112     {
113         // .preserve folder
114         std::filesystem::path dir = dumpPath / PRESERVE;
115         if (!std::filesystem::exists(dir))
116         {
117             lg2::info("Serialization directory: {SERIAL_DIR} doesnt exist, "
118                       "skip deserialization",
119                       "SERIAL_DIR", dir);
120             return;
121         }
122 
123         // Serialized entry
124         std::filesystem::path serializePath = dir / SERIAL_FILE;
125         std::ifstream is(serializePath, std::ios::binary);
126         if (!is.is_open())
127         {
128             lg2::error("Failed to open file for deserialization: {PATH}",
129                        "PATH", serializePath);
130             return;
131         }
132         nlohmann::json j;
133         is >> j;
134 
135         uint32_t version;
136         j.at("version").get_to(version);
137         if (version == CLASS_SERIALIZATION_VERSION)
138         {
139             uint32_t storedId;
140             j.at("dumpId").get_to(storedId);
141             if (storedId == id)
142             {
143                 originatorId(j["originatorId"].get<std::string>());
144                 originatorType(j["originatorType"].get<originatorTypes>());
145                 startTime(j["startTime"].get<uint64_t>());
146             }
147             else
148             {
149                 lg2::error("The id ({ID_IN_FILE}) is not matching the dump id "
150                            "({DUMPID}); skipping deserialization.",
151                            "ID_IN_FILE", storedId, "DUMPID", id);
152 
153                 // Id is not matching, this could be due to file corruption
154                 // deleting the .preserve folder.
155                 // Attempt to delete the folder and ignore any error.
156                 std::error_code ec;
157                 std::filesystem::remove_all(dir, ec);
158             }
159         }
160         else
161         {
162             lg2::error("The serialized file version and current class version"
163                        "doesnt match, skip deserialization {VERSION}",
164                        "VERSION", version);
165         }
166     }
167     catch (const std::exception& e)
168     {
169         lg2::error("Deserialization error: {PATH}, {ERROR}", "PATH", dumpPath,
170                    "ERROR", e);
171     }
172 }
173 
174 } // namespace dump
175 } // namespace phosphor
176