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