src/CacheAndPersist.cpp (672 lines of code) (raw):
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#include "aws/iotfleetwise/CacheAndPersist.h"
#include "aws/iotfleetwise/Assert.h"
#include "aws/iotfleetwise/LoggingModule.h"
#include <algorithm>
#include <boost/filesystem.hpp>
#include <boost/uuid/detail/sha1.hpp>
#include <boost/version.hpp>
#include <cstdint>
#include <cstdio>
#include <fstream> // IWYU pragma: keep
#include <iomanip>
#include <ios> // IWYU pragma: keep
#include <iostream> // IWYU pragma: keep
#include <memory>
#include <stdexcept>
#include <vector>
namespace Aws
{
namespace IoTFleetWise
{
constexpr auto SHA1_DIGEST_SIZE_BYTES = sizeof( boost::uuids::detail::sha1::digest_type );
namespace
{
std::string
sha1DigestAsString( boost::uuids::detail::sha1::digest_type &digest )
{
std::stringstream hashString;
#if BOOST_VERSION >= 108600
for ( unsigned int i = 0; i < SHA1_DIGEST_SIZE_BYTES; i++ )
{
hashString << std::hex << std::setw( 2 ) << std::setfill( '0' ) << static_cast<unsigned int>( digest[i] );
}
#else
constexpr auto SHA1_DIGEST_SIZE_WORDS = SHA1_DIGEST_SIZE_BYTES / sizeof( uint32_t );
for ( unsigned int i = 0; i < SHA1_DIGEST_SIZE_WORDS; i++ )
{
hashString << std::hex << std::setw( sizeof( uint32_t ) * 2 ) << std::setfill( '0' ) << digest[i];
}
#endif
return hashString.str();
}
std::string
calculateSha1( const uint8_t *buf, size_t size )
{
boost::uuids::detail::sha1 sha1;
try
{
sha1.process_bytes( buf, size );
}
catch ( const std::runtime_error &e )
{
// An exception is only thrown in case the input is too large. The max length for the input when calculating
// SHA1 is 2^64 bits, so it should never happen in our case.
FWE_FATAL_ASSERT( false, "Exception while calculating SHA1: " + std::string( e.what() ) );
}
boost::uuids::detail::sha1::digest_type digest{};
sha1.get_digest( digest );
return sha1DigestAsString( digest );
}
} // namespace
CacheAndPersist::CacheAndPersist( const std::string &partitionPath, size_t maxPartitionSize )
: mPersistencyPath{ partitionPath }
, mPersistencyWorkspace{ partitionPath +
( ( ( partitionPath.empty() ) || ( partitionPath.back() == '/' ) ) ? "" : "/" ) +
PERSISTENCY_WORKSPACE }
, mDecoderManifestFile( mPersistencyWorkspace + DECODER_MANIFEST_FILE )
, mCollectionSchemeListFile( mPersistencyWorkspace + COLLECTION_SCHEME_LIST_FILE )
#ifdef FWE_FEATURE_LAST_KNOWN_STATE
, mStateTemplateListFile( mPersistencyWorkspace + STATE_TEMPLATE_LIST_FILE )
, mStateTemplateListMetadataFile( mPersistencyWorkspace + STATE_TEMPLATE_LIST_METADATA_FILE )
#endif
, mPayloadMetadataFile( mPersistencyWorkspace + PAYLOAD_METADATA_FILE )
, mCollectedDataPath( mPersistencyWorkspace + COLLECTED_DATA_FOLDER )
, mMaxPersistencePartitionSize( maxPartitionSize )
{
}
bool
CacheAndPersist::init()
{
cleanupDeprecatedFiles();
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and and
// non-template function
if ( ( !boost::filesystem::exists( mPayloadMetadataFile ) ) ||
( readMetadata( mPersistedMetadata ) != ErrorCode::SUCCESS ) )
{
mPersistedMetadata["version"] = METADATA_SCHEME_VERSION;
mPersistedMetadata["files"] = Json::arrayValue;
}
else if ( mPersistedMetadata["version"] != METADATA_SCHEME_VERSION )
{
FWE_LOG_ERROR( "Metadata scheme version is not supported. Ignoring persisted files." )
mPersistedMetadata["version"] = METADATA_SCHEME_VERSION;
mPersistedMetadata["files"] = Json::arrayValue;
static_cast<void>( erase( mPayloadMetadataFile ) );
}
else
{
FWE_LOG_TRACE( "Successfully read persisted metadata" );
}
// Create directory for persisted data if it doesn't exist
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and and
// non-template function
if ( !boost::filesystem::exists( mPersistencyWorkspace ) )
{
try
{
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and
// and non-template function
boost::filesystem::create_directory( mPersistencyWorkspace );
}
catch ( const boost::filesystem::filesystem_error &err )
{
FWE_LOG_ERROR( "Failed to create directory for persistency workspace: " + std::string( err.what() ) );
return false;
}
}
// Create directory for persisted data if it doesn't exist
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and and
// non-template function
if ( !boost::filesystem::exists( mCollectedDataPath ) )
{
try
{
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and
// and non-template function
boost::filesystem::create_directory( mCollectedDataPath );
}
catch ( const boost::filesystem::filesystem_error &err )
{
FWE_LOG_ERROR( "Failed to create directory for collected data: " + std::string( err.what() ) );
return false;
}
}
// Clean directory from files without metadata at startup
cleanupPersistedData();
// Write the metadata to ensure the file is created in case it doesn't exist yet
writeMetadata( mPersistedMetadata );
FWE_LOG_INFO( "Persistency library successfully initialised" );
return true;
}
ErrorCode
CacheAndPersist::write( const uint8_t *bufPtr, size_t size, DataType dataType, const std::string &filename )
{
std::string path = getFileName( dataType );
if ( dataType == DataType::EDGE_TO_CLOUD_PAYLOAD )
{
if ( filename.empty() )
{
FWE_LOG_ERROR( "Failed to persist data: filename for the payload is empty " );
return ErrorCode::INVALID_DATATYPE;
}
else
{
path += filename;
}
}
return write( bufPtr, size, path );
}
ErrorCode
CacheAndPersist::write( const uint8_t *bufPtr, size_t size, const std::string &path )
{
if ( bufPtr == nullptr )
{
FWE_LOG_ERROR( "Failed to persist data: buffer is empty" );
return ErrorCode::INVALID_DATA;
}
if ( path.empty() )
{
FWE_LOG_ERROR( "Failed to persist data: path is empty" );
return ErrorCode::INVALID_DATATYPE;
}
if ( getTotalSize() + size >= mMaxPersistencePartitionSize )
{
FWE_LOG_ERROR( "Failed to persist data: memory limit achieved" );
return ErrorCode::MEMORY_FULL;
}
auto checksumStatus = writeChecksumForFile( path, calculateSha1( bufPtr, size ) );
if ( checksumStatus != ErrorCode::SUCCESS )
{
return checksumStatus;
}
std::ofstream file( path.c_str(), std::ios_base::out | std::ios_base::binary );
file.write( reinterpret_cast<const char *>( bufPtr ), static_cast<std::streamsize>( size ) );
if ( !file.good() )
{
FWE_LOG_ERROR( "Failed to persist data: write to the file failed" );
return ErrorCode::FILESYSTEM_ERROR;
}
return ErrorCode::SUCCESS;
}
ErrorCode
CacheAndPersist::write( std::streambuf &streambuf, DataType dataType, const std::string &filename )
{
if ( filename.empty() )
{
FWE_LOG_ERROR( "Failed to persist data: filename is empty" );
return ErrorCode::INVALID_DATATYPE;
}
if ( dataType != DataType::EDGE_TO_CLOUD_PAYLOAD )
{
FWE_LOG_ERROR( "Failed to persist data: wrong datatype provided" );
return ErrorCode::INVALID_DATATYPE;
}
boost::filesystem::path path{ mCollectedDataPath + filename };
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and
// and non-template function
if ( !boost::filesystem::exists( path.parent_path().string() ) )
{
try
{
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and
// and non-template function
boost::filesystem::create_directories( path.parent_path().string() );
}
catch ( const boost::filesystem::filesystem_error &err )
{
FWE_LOG_ERROR( "Failed to create directory for persistency of streamed vision system data: " +
std::string( err.what() ) );
return ErrorCode::FILESYSTEM_ERROR;
}
}
std::ofstream file( path.string(), std::ios::binary );
boost::uuids::detail::sha1 sha1;
while ( streambuf.sgetc() != std::char_traits<char>::eof() )
{
static const std::size_t CHUNK_SIZE = 4024;
char chunk[CHUNK_SIZE];
char *chunkPtr = chunk;
auto count = streambuf.sgetn( chunkPtr, CHUNK_SIZE );
try
{
sha1.process_bytes( chunkPtr, static_cast<size_t>( count ) );
}
catch ( const std::runtime_error &e )
{
// An exception is only thrown in case the input is too large. The max length for the input when calculating
// SHA1 is 2^64 bits, so it should never happen in our case.
FWE_FATAL_ASSERT( false, "Exception while calculating SHA1: " + std::string( e.what() ) );
}
file.write( chunkPtr, count );
}
boost::uuids::detail::sha1::digest_type digest{};
sha1.get_digest( digest );
auto checksumStatus = writeChecksumForFile( path.string(), sha1DigestAsString( digest ) );
if ( checksumStatus != ErrorCode::SUCCESS )
{
return checksumStatus;
}
file.close();
if ( !file.good() )
{
FWE_LOG_ERROR( "Failed to persist data: write to the file failed" );
return ErrorCode::FILESYSTEM_ERROR;
}
return ErrorCode::SUCCESS;
}
void
CacheAndPersist::addMetadata( const Json::Value &metadata )
{
mPersistedMetadata["files"].append( metadata );
}
size_t
CacheAndPersist::getSize( DataType dataType, const std::string &filename )
{
std::string path = getFileName( dataType );
if ( dataType == DataType::EDGE_TO_CLOUD_PAYLOAD )
{
if ( filename.empty() )
{
FWE_LOG_ERROR( "Could not get filesize: filename for the payload is empty " );
return INVALID_FILE_SIZE;
}
else
{
path += filename;
}
}
return getSize( path );
}
size_t
CacheAndPersist::getSize( const std::string &path )
{
if ( path.empty() )
{
FWE_LOG_ERROR( "Could not get filesize, path is empty" );
return INVALID_FILE_SIZE;
}
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and and
// non-template function
if ( !boost::filesystem::exists( path ) )
{
FWE_LOG_TRACE( "File " + path + " does not exist" );
return 0;
}
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and and
// non-template function
std::uintmax_t fileSize = boost::filesystem::file_size( path );
// In theory this should never happen as we only read the files created in this system, but it could
// happen if someone copies persisted files from a different architecture (e.g. from arm64 to armhf).
if ( fileSize > SIZE_MAX )
{
FWE_LOG_ERROR( "Filesize is larger than SIZE_MAX. File content can't be fully stored in memory." );
return INVALID_FILE_SIZE;
}
return static_cast<size_t>( fileSize );
}
size_t
CacheAndPersist::getMetadataSize()
{
// Estimate metadata size if one more file will be added
return ( mPersistedMetadata["files"].size() + 1 ) * ESTIMATED_METADATA_SIZE_PER_FILE;
}
ErrorCode
CacheAndPersist::read( uint8_t *const readBufPtr, size_t size, DataType dataType, const std::string &filename )
{
std::string path = getFileName( dataType );
if ( dataType == DataType::EDGE_TO_CLOUD_PAYLOAD )
{
if ( filename.empty() )
{
FWE_LOG_ERROR( "Failed to read persisted data: filename for the payload is empty " );
return ErrorCode::INVALID_DATATYPE;
}
else
{
path += filename;
}
}
return read( readBufPtr, size, path );
}
ErrorCode
CacheAndPersist::read( uint8_t *const readBufPtr, size_t size, const std::string &path ) const
{
if ( readBufPtr == nullptr )
{
FWE_LOG_ERROR( "Failed to read persisted data: buffer is empty " );
return ErrorCode::INVALID_DATA;
}
if ( size >= mMaxPersistencePartitionSize )
{
FWE_LOG_ERROR( "Failed to read persisted data: size is bigger than memory limit " );
return ErrorCode::MEMORY_FULL;
}
if ( path.empty() )
{
FWE_LOG_ERROR( "Failed to read persisted data: path is empty " );
return ErrorCode::INVALID_DATATYPE;
}
size_t filesize = getSize( path );
if ( ( filesize == 0 ) || ( filesize == INVALID_FILE_SIZE ) )
{
FWE_LOG_ERROR( "Failed to read persisted data: file " + path + " is empty " );
return ErrorCode::EMPTY;
}
if ( size != filesize )
{
FWE_LOG_ERROR( "Failed to read persisted data: requested size " + std::to_string( size ) +
" Bytes and actual size " + std::to_string( filesize ) + " Bytes differ" );
return ErrorCode::INVALID_DATA;
}
std::ifstream file( path.c_str(), std::ios_base::binary | std::ios_base::in );
file.clear();
file.read( reinterpret_cast<char *>( readBufPtr ), static_cast<std::streamsize>( size ) );
if ( file.fail() )
{
FWE_LOG_ERROR( "Error reading file" );
return ErrorCode::FILESYSTEM_ERROR;
}
std::string expectedChecksum;
auto checksumStatus = readChecksumForFile( path, expectedChecksum );
if ( checksumStatus == ErrorCode::EMPTY )
{
// Skip the check as this is likely data that was persisted before checksum support was added.
return ErrorCode::SUCCESS;
}
else if ( checksumStatus != ErrorCode::SUCCESS )
{
return checksumStatus;
}
std::string actualChecksum = calculateSha1( readBufPtr, size );
if ( expectedChecksum != actualChecksum )
{
FWE_LOG_ERROR( "Checksum mismatch for file '" + path + "'. Expected SHA1: " + expectedChecksum +
", actual SHA1: " + actualChecksum );
erase( path );
erase( getChecksumFilename( path ) );
return ErrorCode::INVALID_DATA;
}
return ErrorCode::SUCCESS;
}
ErrorCode
CacheAndPersist::readChecksumForFile( const std::string &path, std::string &result )
{
auto checksumFilename = getChecksumFilename( path );
// SHA1_DIGEST_SIZE is multiplied by 2 because we write it as a hex string
if ( !boost::filesystem::exists( checksumFilename ) )
{
FWE_LOG_WARN( "Checksum file " + checksumFilename +
" doesn't exist. It won't be possible to detect if data is corrupted." );
return ErrorCode::EMPTY;
}
constexpr auto checksumFileSize = static_cast<size_t>( SHA1_DIGEST_SIZE_BYTES * 2 );
if ( getSize( checksumFilename ) != checksumFileSize )
{
// Don't do anything. The caller will eventually compare the values and detect the mismatch.
FWE_LOG_WARN( "Invalid checksum file: " + checksumFilename );
result = "";
return ErrorCode::SUCCESS;
}
char checksumBuf[checksumFileSize];
char *checksumBufPtr = checksumBuf;
std::ifstream checksumFile( checksumFilename.c_str(), std::ios_base::in );
checksumFile.clear();
checksumFile.read( checksumBufPtr, static_cast<std::streamsize>( checksumFileSize ) );
if ( checksumFile.fail() )
{
FWE_LOG_ERROR( "Error reading file: " + checksumFilename );
return ErrorCode::FILESYSTEM_ERROR;
}
result = std::string( checksumBufPtr, checksumBufPtr + checksumFileSize );
return ErrorCode::SUCCESS;
}
ErrorCode
CacheAndPersist::writeChecksumForFile( const std::string &path, const std::string &checksum )
{
auto checksumFilename = getChecksumFilename( path );
std::ofstream checksumFile( checksumFilename.c_str(), std::ios_base::out );
checksumFile << checksum;
if ( !checksumFile.good() )
{
FWE_LOG_ERROR( "Failed to write file: " + checksumFilename );
return ErrorCode::FILESYSTEM_ERROR;
}
return ErrorCode::SUCCESS;
}
std::string
CacheAndPersist::getChecksumFilename( const std::string &path )
{
return path + ".sha1";
}
ErrorCode
CacheAndPersist::readMetadata( Json::Value &metadata )
{
try
{
std::ifstream jsonStream( mPayloadMetadataFile );
jsonStream >> metadata;
}
catch ( ... )
{
FWE_LOG_ERROR( "Error reading JSON file with metadata" );
static_cast<void>( erase( mPayloadMetadataFile ) );
return ErrorCode::FILESYSTEM_ERROR;
}
return ErrorCode::SUCCESS;
}
ErrorCode
CacheAndPersist::erase( DataType dataType, const std::string &filename )
{
std::string path = getFileName( dataType );
if ( dataType == DataType::EDGE_TO_CLOUD_PAYLOAD )
{
if ( filename.empty() )
{
FWE_LOG_ERROR( "Failed to erase persisted data: filename for the edge to cloud payload is empty" );
return ErrorCode::INVALID_DATATYPE;
}
else
{
path += filename;
}
}
return erase( path );
}
ErrorCode
CacheAndPersist::erase( const std::string &path )
{
if ( path.empty() )
{
FWE_LOG_ERROR( "Failed to delete persisted file: path is empty " );
return ErrorCode::INVALID_DATATYPE;
}
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and and
// non-template function
if ( !boost::filesystem::exists( path ) )
{
FWE_LOG_INFO( "File does not exist, nothing to delete: " + path );
return ErrorCode::SUCCESS;
}
// Delete the file
if ( std::remove( path.c_str() ) != 0 )
{
FWE_LOG_ERROR( "Failed to delete persisted file: remove failed" );
return ErrorCode::FILESYSTEM_ERROR;
}
return ErrorCode::SUCCESS;
}
const char *
CacheAndPersist::getErrorString( ErrorCode err )
{
switch ( err )
{
case ErrorCode::SUCCESS:
return "ErrorCode::SUCCESS";
case ErrorCode::MEMORY_FULL:
return "MEMORY_FULL";
case ErrorCode::EMPTY:
return "EMPTY";
case ErrorCode::FILESYSTEM_ERROR:
return "FILESYSTEM_ERROR";
case ErrorCode::INVALID_DATATYPE:
return "INVALID_DATATYPE";
case ErrorCode::INVALID_DATA:
return "INVALID_DATA";
default:
return "UNKNOWN";
}
}
std::string
CacheAndPersist::getFileName( DataType dataType )
{
switch ( dataType )
{
case DataType::COLLECTION_SCHEME_LIST:
return mCollectionSchemeListFile;
case DataType::DECODER_MANIFEST:
return mDecoderManifestFile;
case DataType::PAYLOAD_METADATA:
return mPayloadMetadataFile;
case DataType::EDGE_TO_CLOUD_PAYLOAD:
return mCollectedDataPath;
#ifdef FWE_FEATURE_LAST_KNOWN_STATE
case DataType::STATE_TEMPLATE_LIST:
return mStateTemplateListFile;
case DataType::STATE_TEMPLATE_LIST_METADATA:
return mStateTemplateListMetadataFile;
#endif
default:
FWE_LOG_ERROR( "Invalid data type specified" );
return "";
}
}
std::uintmax_t
CacheAndPersist::getTotalSize()
{
std::uintmax_t size = getMetadataSize();
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and and
// non-template function
if ( !boost::filesystem::exists( mPersistencyWorkspace ) )
{
FWE_LOG_ERROR( "Directory " + mPersistencyWorkspace + " for persisted data does not exist" );
}
else
{
try
{
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and
// and non-template function
for ( boost::filesystem::recursive_directory_iterator it( mPersistencyWorkspace );
it != boost::filesystem::recursive_directory_iterator();
++it )
{
if ( !boost::filesystem::is_directory( *it ) )
{
// actual metadata is stored in memory
if ( it->path().string() != mPayloadMetadataFile )
{
size += boost::filesystem::file_size( *it );
}
}
}
}
catch ( const boost::filesystem::filesystem_error &err )
{
FWE_LOG_ERROR( "Error getting file size: " + std::string( err.what() ) );
}
}
return size;
}
void
CacheAndPersist::clearMetadata()
{
mPersistedMetadata["files"] = Json::arrayValue;
}
ErrorCode
CacheAndPersist::cleanupPersistedData()
{
FWE_LOG_TRACE( "Cleaning up persistency workspace" );
std::vector<std::string> filenames;
for ( const auto &file : mPersistedMetadata["files"] )
{
// Handle the legacy metadata. file["filename"] is the old one and file["payload"]["filename"] the new one
auto filename =
file["filename"].asString().empty() ? file["payload"]["filename"].asString() : file["filename"].asString();
filenames.push_back( mCollectedDataPath + filename );
}
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and and
// non-template function
if ( !boost::filesystem::exists( mPersistencyWorkspace ) )
{
FWE_LOG_ERROR( "Persistency directory " + mPersistencyWorkspace + " does not exist" );
return ErrorCode::FILESYSTEM_ERROR;
}
std::vector<std::string> filesToDelete;
try
{
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and and
// non-template function
for ( boost::filesystem::recursive_directory_iterator it( mPersistencyWorkspace );
it != boost::filesystem::recursive_directory_iterator();
++it )
{
if ( !boost::filesystem::is_directory( *it ) )
{
std::string filename = it->path().string();
if ( filename != mDecoderManifestFile && filename != mCollectionSchemeListFile &&
filename != mPayloadMetadataFile &&
#ifdef FWE_FEATURE_LAST_KNOWN_STATE
filename != mStateTemplateListFile && filename != mStateTemplateListMetadataFile &&
#endif
( std::find( filenames.begin(), filenames.end(), filename ) == filenames.end() ) )
{
// Delete files after iterating over directory
// TODO: do not skip ion files but add the metadata for them so they don't get deleted
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both
// template and and non-template function
if ( it->path().extension() != ".10n" ) // skip ion files
{
filesToDelete.push_back( filename );
}
}
}
}
}
catch ( const boost::filesystem::filesystem_error &err )
{
FWE_LOG_ERROR( "Error during clean up: " + std::string( err.what() ) );
return ErrorCode::FILESYSTEM_ERROR;
}
for ( auto &fileToDelete : filesToDelete )
{
FWE_LOG_TRACE( "Deleting file " + fileToDelete );
static_cast<void>( erase( fileToDelete ) );
}
if ( !filesToDelete.empty() )
{
FWE_LOG_TRACE( "Persistency folder was cleaned up successfully" );
}
return ErrorCode::SUCCESS;
}
ErrorCode
CacheAndPersist::cleanupDeprecatedFiles()
{
FWE_LOG_TRACE( "Cleaning up persistency folder from old and deprecated files" );
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and and
// non-template function
if ( !boost::filesystem::exists( mPersistencyPath ) )
{
FWE_LOG_ERROR( "Persistency directory " + mPersistencyPath + " does not exist" );
return ErrorCode::FILESYSTEM_ERROR;
}
std::vector<std::string> filesToDelete;
try
{
// coverity[misra_cpp_2008_rule_14_8_2_violation] - boost filesystem path header defines both template and and
// non-template function
for ( boost::filesystem::directory_iterator it( mPersistencyPath );
it != boost::filesystem::directory_iterator();
++it )
{
// Deleted all old files from the persistency directory. FWE is supposed to work only in
// COLLECTED_DATA_FOLDER.
if ( !boost::filesystem::is_directory( *it ) )
{
std::string filename = it->path().string();
if ( ( filename == ( mPersistencyPath + DECODER_MANIFEST_FILE ) ) ||
( filename == ( mPersistencyPath + COLLECTION_SCHEME_LIST_FILE ) ) ||
( filename == ( mPersistencyPath + PAYLOAD_METADATA_FILE ) ) ||
( filename == ( mPersistencyPath + DEPRECATED_COLLECTED_DATA_FILE ) ) )
{
// Delete files after iterating over directory
filesToDelete.push_back( filename );
}
}
}
}
catch ( const boost::filesystem::filesystem_error &err )
{
FWE_LOG_ERROR( "Error during clean up: " + std::string( err.what() ) );
return ErrorCode::FILESYSTEM_ERROR;
}
for ( auto &fileToDelete : filesToDelete )
{
static_cast<void>( erase( fileToDelete ) );
}
if ( !filesToDelete.empty() )
{
FWE_LOG_TRACE( "Deprecated files were successfully deleted from the persistency folder" );
}
return ErrorCode::SUCCESS;
}
bool
CacheAndPersist::writeMetadata( Json::Value &metadata )
{
Json::StreamWriterBuilder builder;
builder["indentation"] = "";
// coverity[autosar_cpp14_a20_8_5_violation] Calling newStreamWriter() is a recommended usage from boost
// documentation
std::unique_ptr<Json::StreamWriter> writer( builder.newStreamWriter() );
std::ofstream outputFileStream( mPayloadMetadataFile );
writer->write( metadata, &outputFileStream );
if ( !outputFileStream.good() )
{
FWE_LOG_ERROR( "Error writing metadata to the JSON file" );
return false;
}
return true;
}
Json::Value
CacheAndPersist::getMetadata()
{
return mPersistedMetadata["files"];
}
CacheAndPersist::~CacheAndPersist()
{
writeMetadata( mPersistedMetadata );
cleanupPersistedData();
}
} // namespace IoTFleetWise
} // namespace Aws