diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index ff49dba..d4f7a0c 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v4 - name: Install dependencies - run: sudo apt-get update && sudo apt-get --no-install-recommends install -y bmap-tools libssl-dev libxml2-dev libarchive-dev tar bzip2 gzip lz4 lzop xz-utils zstd cppcheck + run: sudo apt-get update && sudo apt-get --no-install-recommends install -y bmap-tools libssl-dev libtinyxml2-dev libarchive-dev tar bzip2 gzip lz4 lzop xz-utils zstd cppcheck - name: Cppcheck run: cppcheck --enable=all --suppress=missingIncludeSystem *.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d1715a..16c69e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,12 +7,15 @@ project(bmap-writer) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) -# Find libxml2 -find_package(LibXml2 REQUIRED) -if (LIBXML2_FOUND) - include_directories(${LIBXML2_INCLUDE_DIR}) +# Find pkgconfig +find_package(PkgConfig REQUIRED) + +# Find TinyXML-2 +pkg_check_modules(TINYXML2 REQUIRED tinyxml2) +if (TINYXML2_FOUND) + include_directories(${TINYXML2_INCLUDE_DIR}) else() - message(FATAL_ERROR "libxml2 not found") + message(FATAL_ERROR "tinyxml2 library not found") endif() @@ -44,7 +47,7 @@ add_executable(bmap-writer bmap-writer.cpp sha256.cpp) target_compile_options(bmap-writer PUBLIC -Wformat -Wformat-security -Wconversion -Wsign-conversion -pedantic -Werror) # Link the libraries -target_link_libraries(bmap-writer ${LIBXML2_LIBRARIES} ${LibArchive_LIBRARIES}) +target_link_libraries(bmap-writer ${TINYXML2_LIBRARIES} ${LibArchive_LIBRARIES}) # Specify the install rules install(TARGETS bmap-writer DESTINATION bin) diff --git a/README.md b/README.md index b385655..432f7a0 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Unlike the Yocto BMAP tool, `bmap-writer` is C++ based does not require Python a - C++ compiler - CMake - Libarchive -- Libxml2 +- TinyXML-2 ## Build and Installation @@ -37,7 +37,7 @@ Unlike the Yocto BMAP tool, `bmap-writer` is C++ based does not require Python a ```sh sudo apt-get update -sudo apt-get install -y libarchive-dev libxml2-dev +sudo apt-get install -y libarchive-dev libtinyxml2-dev ``` ## Build diff --git a/bmap-writer.cpp b/bmap-writer.cpp index 9ee281f..5f835b0 100644 --- a/bmap-writer.cpp +++ b/bmap-writer.cpp @@ -36,9 +36,8 @@ #include #include -#include -#include #include +#include #include "sha256.h" @@ -53,50 +52,82 @@ struct bmap_t { size_t blockSize; }; -bmap_t parseBMap(const std::string &filename) { - bmap_t bmapData = {}; - bmapData.blockSize = 0; +int parseBMap(const std::string &filename, bmap_t& bmapData) { + int ret = 0; - xmlDocPtr doc = xmlReadFile(filename.c_str(), NULL, 0); - if (doc == NULL) { - return bmapData; - } + try { + tinyxml2::XMLDocument doc; + tinyxml2::XMLError err; - xmlNodePtr root_element = xmlDocGetRootElement(doc); - for (xmlNodePtr node = root_element->children; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE) { - if (strcmp(reinterpret_cast(node->name), "BlockSize") == 0) { - xmlChar *blockSizeStr = xmlNodeGetContent(node); - bmapData.blockSize = static_cast(std::stoul(reinterpret_cast(blockSizeStr))); - xmlFree(blockSizeStr); - //std::cout << "BlockSize: " << bmapData.blockSize << std::endl; - } else if (strcmp(reinterpret_cast(node->name), "BlockMap") == 0) { - for (xmlNodePtr rangeNode = node->children; rangeNode; rangeNode = rangeNode->next) { - if (rangeNode->type == XML_ELEMENT_NODE && strcmp(reinterpret_cast(rangeNode->name), "Range") == 0) { - xmlChar *checksum = xmlGetProp(rangeNode, reinterpret_cast("chksum")); - xmlChar *range = xmlNodeGetContent(rangeNode); - - range_t r; - r.checksum = reinterpret_cast(checksum); - - if (sscanf(reinterpret_cast(range), "%zu-%zu", &r.startBlock, &r.endBlock) == 1) { - r.endBlock = r.startBlock; // Handle single block range - } - - bmapData.ranges.push_back(r); - //std::cout << "Parsed Range: checksum=" << r.checksum << ", range=" << r.range << std::endl; - xmlFree(checksum); - xmlFree(range); - } + err = doc.LoadFile(filename.c_str()); + if (err != tinyxml2::XML_SUCCESS) { + throw std::string("Failed to load BMAP file"); + } + + tinyxml2::XMLElement * p_root = doc.RootElement(); + + // Check if the provided file is a valid BMAP + if (strcmp(reinterpret_cast(p_root->Name()), "bmap") != 0) { + throw std::string("BMAP file is invalid"); + } + + // Parse image information + tinyxml2::XMLElement * p_data; + + p_data = p_root->FirstChildElement("BlockSize"); + if (p_data == nullptr) { + throw std::string("BMAP: BlockSize not found"); + } else { + bmapData.blockSize = static_cast(std::stoul(p_data->GetText())); + //std::cout << "BlockSize: " << bmapData.blockSize << std::endl; + } + + p_data = p_root->FirstChildElement("BlockMap"); + if (p_data == nullptr) { + throw std::string("BMAP: BlockMap not found"); + } else { + tinyxml2::XMLElement * p_range = p_data->FirstChildElement("Range"); + while (p_range != nullptr) { + range_t r; + + const char *val = p_range->GetText(); + if (val == nullptr) { + throw std::string("BMAP: found an empty range"); } + + const char *chksum = p_range->Attribute("chksum"); + if (chksum == nullptr) { + throw std::string("BMAP: following range has no checksum: ") + std::string(val); + } + + int parseResult = std::sscanf(val, "%zu-%zu", &r.startBlock, &r.endBlock); + switch (parseResult) { + case 2: + // Multiple blocks range, nothing to do + break; + case 1: + // Handle single block range + r.endBlock = r.startBlock; + break; + default: + throw std::string("BMAP: invalid range: ") + std::string(val); + } + + r.checksum = std::string(chksum); + + //std::cout << "Parsed Range: checksum=" << r.checksum << ", range=" << r.startBlock << "-" << r.endBlock << std::endl; + + bmapData.ranges.push_back(r); + + p_range = p_range->NextSiblingElement("Range"); } } + } catch (const std::string& err) { + std::cerr << err << std::endl; + ret = -1; } - xmlFreeDoc(doc); - xmlCleanupParser(); - - return bmapData; + return ret; } bool isDeviceMounted(const std::string &device) { @@ -371,12 +402,15 @@ int main(int argc, char *argv[]) { std::cerr << "Error device: " << device << " is mounted. Please unmount it before proceeding." << std::endl; return -1; } - bmap_t bmap = parseBMap(bmapFile); - if (bmap.blockSize == 0 || bmap.ranges.empty()) { - std::cerr << "Failed to parse file: " << bmapFile << std::endl; + + bmap_t bmap; + int ret = parseBMap(bmapFile, bmap); + if (ret != 0) { + std::cerr << "Failed to parse BMAP file: " << bmapFile << std::endl; return 1; } - int ret = BmapWriteImage(imageFile, bmap, device, noVerify); + + ret = BmapWriteImage(imageFile, bmap, device, noVerify); if (ret != 0) { std::cerr << "Failed to write image to device: " << device << std::endl; return ret;