1 #include "version_handler.hpp" 2 3 #include <algorithm> 4 #include <cstring> 5 #include <ios> 6 #include <limits> 7 #include <memory> 8 #include <optional> 9 #include <utility> 10 #include <vector> 11 12 namespace ipmi_flash 13 { 14 15 VersionBlobHandler::VersionBlobHandler( 16 std::vector<HandlerConfig<ActionPack>>&& configs) 17 { 18 for (auto& config : configs) 19 { 20 auto info = std::make_unique<BlobInfo>(); 21 info->blobId = std::move(config.blobId); 22 info->actions = std::move(config.actions); 23 info->handler = std::move(config.handler); 24 info->actions->onOpen->setCallback( 25 [infoP = info.get()](TriggerableActionInterface& tai) { 26 auto data = 27 std::make_shared<std::optional<std::vector<uint8_t>>>(); 28 do 29 { 30 if (tai.status() != ActionStatus::success) 31 { 32 fprintf(stderr, "Version file unit failed for %s\n", 33 infoP->blobId.c_str()); 34 continue; 35 } 36 if (!infoP->handler->open("", std::ios::in)) 37 { 38 fprintf(stderr, "Opening version file failed for %s\n", 39 infoP->blobId.c_str()); 40 continue; 41 } 42 auto d = infoP->handler->read( 43 0, std::numeric_limits<uint32_t>::max()); 44 infoP->handler->close(); 45 if (!d) 46 { 47 fprintf(stderr, "Reading version file failed for %s\n", 48 infoP->blobId.c_str()); 49 continue; 50 } 51 *data = std::move(d); 52 } while (false); 53 for (auto sessionP : infoP->sessionsToUpdate) 54 { 55 sessionP->data = data; 56 } 57 infoP->sessionsToUpdate.clear(); 58 }); 59 if (!blobInfoMap.try_emplace(info->blobId, std::move(info)).second) 60 { 61 fprintf(stderr, "Ignoring duplicate config for %s\n", 62 info->blobId.c_str()); 63 } 64 } 65 } 66 67 bool VersionBlobHandler::canHandleBlob(const std::string& path) 68 { 69 return blobInfoMap.find(path) != blobInfoMap.end(); 70 } 71 72 std::vector<std::string> VersionBlobHandler::getBlobIds() 73 { 74 std::vector<std::string> ret; 75 for (const auto& [key, _] : blobInfoMap) 76 { 77 ret.emplace_back(key); 78 } 79 return ret; 80 } 81 82 /** 83 * deleteBlob - does nothing, always fails 84 */ 85 bool VersionBlobHandler::deleteBlob(const std::string&) 86 { 87 return false; 88 } 89 90 bool VersionBlobHandler::stat(const std::string&, blobs::BlobMeta*) 91 { 92 return false; 93 } 94 95 bool VersionBlobHandler::open(uint16_t session, uint16_t flags, 96 const std::string& path) 97 { 98 /* only reads are supported, check if blob is handled and make sure 99 * the blob isn't already opened 100 */ 101 if (flags != blobs::read) 102 { 103 fprintf(stderr, "open %s fail: unsupported flags(0x%04X.)\n", 104 path.c_str(), flags); 105 return false; 106 } 107 108 auto info = std::make_unique<SessionInfo>(); 109 info->blob = blobInfoMap.at(path).get(); 110 info->blob->sessionsToUpdate.emplace(info.get()); 111 if (info->blob->sessionsToUpdate.size() == 1 && 112 !info->blob->actions->onOpen->trigger()) 113 { 114 fprintf(stderr, "open %s fail: onOpen trigger failed\n", path.c_str()); 115 info->blob->sessionsToUpdate.erase(info.get()); 116 return false; 117 } 118 119 sessionInfoMap[session] = std::move(info); 120 return true; 121 } 122 123 std::vector<uint8_t> VersionBlobHandler::read(uint16_t session, uint32_t offset, 124 uint32_t requestedSize) 125 { 126 auto& data = sessionInfoMap.at(session)->data; 127 if (data == nullptr || !*data) 128 { 129 throw std::runtime_error("Version data not ready for read"); 130 } 131 if ((*data)->size() < offset) 132 { 133 return {}; 134 } 135 std::vector<uint8_t> ret( 136 std::min<size_t>(requestedSize, (*data)->size() - offset)); 137 std::memcpy(&ret[0], &(**data)[offset], ret.size()); 138 return ret; 139 } 140 141 bool VersionBlobHandler::close(uint16_t session) 142 { 143 auto it = sessionInfoMap.find(session); 144 if (it == sessionInfoMap.end()) 145 { 146 return false; 147 } 148 auto& info = *it->second; 149 info.blob->sessionsToUpdate.erase(&info); 150 if (info.blob->sessionsToUpdate.empty()) 151 { 152 info.blob->actions->onOpen->abort(); 153 } 154 sessionInfoMap.erase(it); 155 return true; 156 } 157 158 bool VersionBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta) 159 { 160 const auto& data = sessionInfoMap.at(session)->data; 161 if (data == nullptr) 162 { 163 meta->blobState = blobs::StateFlags::committing; 164 meta->size = 0; 165 } 166 else if (!*data) 167 { 168 meta->blobState = blobs::StateFlags::commit_error; 169 meta->size = 0; 170 } 171 else 172 { 173 meta->blobState = 174 blobs::StateFlags::committed | blobs::StateFlags::open_read; 175 meta->size = (*data)->size(); 176 } 177 return true; 178 } 179 180 bool VersionBlobHandler::expire(uint16_t session) 181 { 182 close(session); 183 return true; 184 } 185 186 } // namespace ipmi_flash 187