/*! * * Copyright (c) 2023 Diality Inc. - All Rights Reserved. * * \copyright * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * \file IDataProvider.h * \author (last) Phil Braica * \date (last) 23-Jan-2023 * \author (original) Phil Braica * \date (original) 23-Jan-2023 * */ #ifndef DATA_PROVIDER_H_ #define DATA_PROVIDER_H_ #include "HalStdTypes.h" #include "UpdateProtocol.h" #include #include namespace SwUpdate { /*! * \brief Provides a call to fetch data for update. */ class IDataProvider { public: /*! * \brief Constructor. * * \param trgtName Target name: LINUX, UI, DG, HD, DGFPGA, HDGFPGA * \param totalSize Total size in bytes. * \param verifyToken Verification token value. * \param versionToken Version token value. * \param destination File destination. */ IDataProvider( SwUpdateTargetEnum trgtName, uint32 totalSize, uint32 verifyToken, uint32 versionToken, const std::string & destination) : targetName(trgtName), fileDestination(destination), totalSize(totalSize), verifyData(verifyToken), versionData(versionToken) { } /*! \brief Virtual destructor. */ virtual ~IDataProvider() { ; // NOP. } /*! * \brief Read data for an update. * * Calls into a class that has a call like: * std::size_t read(FILE* pFile, std::size_t cat_index, std::size_t offset, * unsigned char* pBuffer, std::size_t size_bytes); * but wraps it to only what the Protocol needs. * * Fetches can be any arbitrary byte offset and do not need to be in order. * * \param offset Offset into the image. * \param pBuffer Buffer to use. * \param size_bytes Max bytes to return. * * \return The amount of bytes fetched / ran out of data to fill the buffer. */ virtual std::size_t read(std::size_t offset, unsigned char* pBuffer, std::size_t size_bytes) { (void)offset; (void)pBuffer; (void)size_bytes; return 0; // NOP. } /*! * \brief Copy a file to a given destination. * * \param destination File path to copy the stream to. * * \return True on success. */ virtual bool copyFile(const std::string& destination = "") { // Use whole pages for fast allocation and copies and such. constexpr int MEMORY_PAGE_SIZE = 4096; unsigned char pBuffer[MEMORY_PAGE_SIZE]; // First two error checks are done in sequence, is our source invalid? // Then create the dest and check if that's invalid. bool rv = true; // Use our fileDestination set at constructor if we don't override. FILE* pDest = fopen( destination.size() != 0 ? destination.c_str() : fileDestination.c_str(), "wb"); std::size_t copied = 0; if (pDest != nullptr) { rv = true; while (rv) { try { std::size_t sz_r = this->read(copied, pBuffer, MEMORY_PAGE_SIZE); if (sz_r == 0) { break; } else { //IDataProvider::decrypt(pBuffer, sz_r); std::size_t sz_w = fwrite(pBuffer, 1, sz_r, pDest); if (sz_w != sz_r) { rv = false; } copied += sz_w; } } catch (...) { rv = false; } } // Close it. rv &= (fclose(pDest) == 0); } // Make sure we copied all of it. rv &= copied == totalSize; return rv; } SwUpdateTargetEnum targetName; ///< Either LINUX, UI, DG, HD, DGFPGA, HDGFPGA std::string fileDestination; ///< File target destination. uint32 totalSize; ///< Total size in bytes. uint32 verifyData; ///< Verify security token. uint32 versionData; ///< Version security token. protected: /*! * \brief Decrypt data in place. * * \param pBuffer Buffer to use. * \param size_bytes Max bytes to return. */ static void decrypt(unsigned char* pBuffer, std::size_t size_bytes) { const unsigned char obs = 71; // Randomly picked value. for (uint32 ii = 0; ii < size_bytes; ii++) { pBuffer[ii] -= obs; } } }; /*! * \brief IDataProvider from a file. */ class DataProvider_File : public IDataProvider { public: /*! * \brief Constructor. * * \param pFile File pointer. * \param trgtName Target name: LINUX, UI, DG, HD, DGFPGA, HDGFPGA * \param fileOffset Offset to where this data starts in the file. * \param totalSize Total size in bytes. * \param verifyToken Verification token value. * \param versionToken Version token value. */ DataProvider_File( FILE* pFile, SwUpdateTargetEnum trgtName, uint32 fileOffset, uint32 totalSize, uint32 verifyToken, uint32 versionToken, const std::string& destination) : IDataProvider(trgtName, totalSize, verifyToken, versionToken, destination), _pFile(pFile), _fileOffset(fileOffset) { ; // NOP. } /*! \brief Virtual destructor. */ virtual ~DataProvider_File() { ; // NOP. } /*! * \brief Read data for an update. * * \param offset Offset into the image. * \param pBuffer Buffer to use. * \param size_bytes Max bytes to return. * * \return The amount of bytes fetched / ran out of data to fill the buffer. */ virtual std::size_t read(std::size_t offset, unsigned char* pBuffer, std::size_t size_bytes) { // Only let ONE thing use the file at once, that way seek and read can't interfer. static std::mutex mutex; std::lock_guard lock(mutex); // Arg check. if ((_pFile == nullptr) || (pBuffer == nullptr)) { return 0; } // If total_size <= offset then asking for something past the end // of this stream in the file. // // Otherwise limit the size to no more than max_bytes - offset. size_bytes = totalSize <= offset ? 0 : totalSize - offset > size_bytes ? size_bytes : totalSize - offset; // Fold in the offset from startData of this file and the startData position. offset += _fileOffset; // Seek and read what we can. fseek(_pFile, (long)offset, 0); std::size_t rd_sz = fread(pBuffer, 1, size_bytes, _pFile); decrypt(pBuffer, rd_sz); return rd_sz; } protected: FILE * _pFile; ///< File pointer. uint32 _fileOffset; ///< Offset to start in that file. }; /*! * \brief IDataProvider from a buffer. */ class DataProvider_Buffer : public IDataProvider { public: /*! * \brief Constructor. * * \param pData Buffer pointer. * \param trgtName Target name: LINUX, UI, DG, HD, DGFPGA, HDGFPGA * \param totalSize Total size in bytes. * \param verifyToken Verification token value. * \param versionToken Version token value. */ DataProvider_Buffer( unsigned char* pData, SwUpdateTargetEnum trgtName, uint32 totalSize, uint32 verifyToken, uint32 versionToken, const std::string& destination) : IDataProvider(trgtName, totalSize, verifyToken, versionToken, destination), _pData(pData) { ; // NOP. } /*! \brief Virtual destructor. */ virtual ~DataProvider_Buffer() { ; // NOP. } /*! * \brief Read data for an update. * * \param offset Offset into the image. * \param pBuffer Buffer to use. * \param size_bytes Max bytes to return. * * \return The amount of bytes fetched / ran out of data to fill the buffer. */ virtual std::size_t read(std::size_t offset, unsigned char* pBuffer, std::size_t size_bytes) { // If total_size <= offset then asking for something past the end // of this stream in the file. // // Otherwise limit the size to no more than max_bytes - offset. size_bytes = totalSize <= offset ? 0 : totalSize - offset > size_bytes ? size_bytes : totalSize - offset; // Fold in the offset from startData of this file and the startData position. // Note that for internal tests we don't encrypt/decrypt the byte stream. memcpy(pBuffer, &_pData[offset], size_bytes); return size_bytes; } protected: unsigned char* _pData; ///< Data pointer. }; } // namespace SwUpdate #endif // DATA_PROVIDER_H_