blob: 3bad2f8776004fdf44fe8842db18595bc437f83d [file] [log] [blame]
#include "editor_impl.hpp"
#include "utils.hpp"
#include <fstream>
#include <iostream>
#include <iterator>
#include "vpdecc/vpdecc.h"
namespace openpower
{
namespace vpd
{
namespace manager
{
namespace editor
{
using namespace openpower::vpd::constants;
void EditorImpl::checkPTForRecord(Binary::const_iterator& iterator,
Byte ptLength)
{
// auto iterator = ptRecord.cbegin();
auto end = std::next(iterator, ptLength + 1);
// Look at each entry in the PT keyword for the record name
while (iterator < end)
{
auto stop = std::next(iterator, lengths::RECORD_NAME);
std::string record(iterator, stop);
if (record == thisRecord.recName)
{
// Skip record name and record type
std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
// Get record offset
thisRecord.recOffset = readUInt16LE(iterator);
// pass the record offset length to read record length
std::advance(iterator, lengths::RECORD_OFFSET);
thisRecord.recSize = readUInt16LE(iterator);
std::advance(iterator, lengths::RECORD_LENGTH);
thisRecord.recECCoffset = readUInt16LE(iterator);
ECCLength len;
std::advance(iterator, lengths::RECORD_ECC_OFFSET);
len = readUInt16LE(iterator);
thisRecord.recECCLength = len;
// once we find the record we don't need to look further
return;
}
else
{
// Jump the record
std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType) +
sizeof(RecordOffset) +
sizeof(RecordLength) +
sizeof(ECCOffset) + sizeof(ECCLength));
}
}
// imples the record was not found
throw std::runtime_error("Record not found");
}
void EditorImpl::updateData(Binary kwdData)
{
std::size_t lengthToUpdate = kwdData.size() <= thisRecord.kwdDataLength
? kwdData.size()
: thisRecord.kwdDataLength;
auto iteratorToNewdata = kwdData.cbegin();
auto end = iteratorToNewdata;
std::advance(end, lengthToUpdate);
std::copy(iteratorToNewdata, end,
std::ostreambuf_iterator<char>(vpdFileStream));
}
void EditorImpl::checkRecordForKwd()
{
RecordOffset recOffset = thisRecord.recOffset;
// Jump to record name
auto nameOffset = recOffset + sizeof(RecordId) + sizeof(RecordSize) +
// Skip past the RT keyword, which contains
// the record name.
lengths::KW_NAME + sizeof(KwSize);
vpdFileStream.seekg(nameOffset + lengths::RECORD_NAME, std::ios::beg);
(thisRecord.recData).resize(thisRecord.recSize);
vpdFileStream.read(reinterpret_cast<char*>((thisRecord.recData).data()),
thisRecord.recSize);
auto iterator = (thisRecord.recData).cbegin();
auto end = (thisRecord.recData).cend();
std::size_t dataLength = 0;
while (iterator < end)
{
// Note keyword name
std::string kw(iterator, iterator + lengths::KW_NAME);
// Check if the Keyword starts with '#'
char kwNameStart = *iterator;
std::advance(iterator, lengths::KW_NAME);
// if keyword starts with #
if (POUND_KW == kwNameStart)
{
// Note existing keyword data length
dataLength = readUInt16LE(iterator);
// Jump past 2Byte keyword length + data
std::advance(iterator, sizeof(PoundKwSize));
}
else
{
// Note existing keyword data length
dataLength = *iterator;
// Jump past keyword length and data
std::advance(iterator, sizeof(KwSize));
}
if (thisRecord.recKWd == kw)
{
// We're done
std::size_t kwdOffset =
std::distance((thisRecord.recData).cbegin(), iterator);
vpdFileStream.seekp(nameOffset + lengths::RECORD_NAME + kwdOffset,
std::ios::beg);
thisRecord.kwdDataLength = dataLength;
return;
}
// jump the data of current kwd to point to next kwd name
std::advance(iterator, dataLength);
}
throw std::runtime_error("Keyword not found");
}
void EditorImpl::updateRecordECC()
{
vpdFileStream.seekp(thisRecord.recECCoffset, std::ios::beg);
(thisRecord.recEccData).resize(thisRecord.recECCLength);
vpdFileStream.read(reinterpret_cast<char*>((thisRecord.recEccData).data()),
thisRecord.recECCLength);
auto recPtr = (thisRecord.recData).cbegin();
auto recEccPtr = (thisRecord.recEccData).cbegin();
auto l_status = vpdecc_create_ecc(
const_cast<uint8_t*>(&recPtr[0]), thisRecord.recSize,
const_cast<uint8_t*>(&recEccPtr[0]), &thisRecord.recECCLength);
if (l_status != VPD_ECC_OK)
{
throw std::runtime_error("Ecc update failed");
}
auto end = (thisRecord.recEccData).cbegin();
std::advance(end, thisRecord.recECCLength);
std::copy((thisRecord.recEccData).cbegin(), end,
std::ostreambuf_iterator<char>(vpdFileStream));
}
auto EditorImpl::getValue(offsets::Offsets offset)
{
Byte data = 0;
vpdFileStream.seekg(offset, std::ios::beg)
.get(*(reinterpret_cast<char*>(&data)));
LE2ByteData lowByte = data;
vpdFileStream.seekg(offset + 1, std::ios::beg)
.get(*(reinterpret_cast<char*>(&data)));
LE2ByteData highByte = data;
lowByte |= (highByte << 8);
return lowByte;
}
void EditorImpl::checkECC(const Binary& tocRecData, const Binary& tocECCData,
RecordLength recLength, ECCLength eccLength)
{
auto l_status =
vpdecc_check_data(const_cast<uint8_t*>(&tocRecData[0]), recLength,
const_cast<uint8_t*>(&tocECCData[0]), eccLength);
if (l_status != VPD_ECC_OK)
{
throw std::runtime_error("Ecc check failed for VTOC");
}
}
void EditorImpl::readVTOC()
{
// read VTOC offset
RecordOffset tocOffset = getValue(offsets::VTOC_PTR);
// read VTOC record length
RecordLength tocLength = getValue(offsets::VTOC_REC_LEN);
// read TOC ecc offset
ECCOffset tocECCOffset = getValue(offsets::VTOC_ECC_OFF);
// read TOC ecc length
ECCLength tocECCLength = getValue(offsets::VTOC_ECC_LEN);
// read toc record data
Binary vtocRecord(tocLength);
vpdFileStream.seekg(tocOffset, std::ios::beg);
vpdFileStream.read(reinterpret_cast<char*>(vtocRecord.data()), tocLength);
// read toc ECC for ecc check
Binary vtocECC(tocECCLength);
vpdFileStream.seekg(tocECCOffset, std::ios::beg);
vpdFileStream.read(reinterpret_cast<char*>(vtocECC.data()), tocECCLength);
auto iterator = vtocRecord.cbegin();
// to get to the record name.
std::advance(iterator, sizeof(RecordId) + sizeof(RecordSize) +
// Skip past the RT keyword, which contains
// the record name.
lengths::KW_NAME + sizeof(KwSize));
std::string recordName(iterator, iterator + lengths::RECORD_NAME);
if ("VTOC" != recordName)
{
throw std::runtime_error("VTOC record not found");
}
// validate ecc for the record
checkECC(vtocRecord, vtocECC, tocLength, tocECCLength);
// jump to length of PT kwd
std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
// Note size of PT
Byte ptLen = *iterator;
std::advance(iterator, 1);
checkPTForRecord(iterator, ptLen);
}
void EditorImpl::updateKeyword(const Binary& kwdData)
{
vpdFileStream.open(vpdFilePath,
std::ios::in | std::ios::out | std::ios::binary);
if (!vpdFileStream)
{
throw std::runtime_error("unable to open vpd file to edit");
}
// process VTOC for PTT rkwd
readVTOC();
// check record for keywrod
checkRecordForKwd();
// update the data to the file
updateData(kwdData);
// update the ECC data for the record once data has been updated
updateRecordECC();
}
} // namespace editor
} // namespace manager
} // namespace vpd
} // namespace openpower