* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ptable.h"
#include <algorithm>
#include <map>
#include <sys/stat.h>
#include <thread>
#include "applypatch/data_writer.h"
#include "log/log.h"
#include "securec.h"
#include "utils.h"
#include "fs_manager.h"
namespace Updater {
constexpr const char *PTABLE_CONFIG_PATH = "/etc/ptable_data.json";
constexpr const char *VENDOR_PTABLE_CONFIG_PATH = "/vendor/etc/ptable_data.json";
constexpr const char *PTABLE_DATA_LABEL = "ptableData";
constexpr const char *EMMC_GPT_DATA_LEN_LABEL = "emmcGptDataLen";
constexpr const char *LBA_LEN_LABEL = "lbaLen";
constexpr const char *GPT_HEADER_LEN_LABEL = "gptHeaderLen";
constexpr const char *BLOCK_SIZE_LABEL = "blockSize";
constexpr const char *IMG_LUN_SIZE_LABEL = "imgLuSize";
constexpr const char *START_LUN_NUM_LABEL = "startLunNumber";
constexpr const char *WRITE_DEVICE_LUN_SIZE_LABEL = "writeDeviceLunSize";
constexpr const char *DEFAULT_LUN_NUM_LABEL = "defaultLunNum";
std::vector<Ptable::PtnInfo> Ptable::GetPtablePartitionInfo() const
{
return partitionInfo_;
}
bool Ptable::SyncABLunPtableDevice(const int sourceSlot)
{
(void)sourceSlot;
return false;
}
bool Ptable::GetABLunPartitionInfo(const int sourceSlot, std::string &srcNode, std::string &tgtNode, uint32_t &offset)
{
(void)sourceSlot;
(void)srcNode;
(void)tgtNode;
(void)offset;
return false;
}
uint32_t Ptable::GetPtablePartitionNum() const
{
return partitionInfo_.size();
}
bool Ptable::LoadPtnInfo(const std::vector<PtnInfo> &ptnInfo)
{
if (ptnInfo.empty()) {
LOG(ERROR) << "ptnInfo is empty";
return false;
}
partitionInfo_ = ptnInfo;
return true;
}
bool Ptable::CorrectBufByPtnList(uint8_t *imageBuf, uint64_t imgBufSize, const std::vector<PtnInfo> &srcInfo,
const std::vector<PtnInfo> &dstInfo)
{
return false;
}
void Ptable::SetReservedSize(uint64_t reservedSize)
{
reservedSize_ = reservedSize;
}
std::vector<Ptable::PtnInfo>& Ptable::GetPtablePartitionInfoInstance()
{
return partitionInfo_;
}
void Ptable::ClearPartitionInfo()
{
partitionInfo_.clear();
}
bool Ptable::InitPtable()
{
if (!partitionInfo_.empty()) {
std::vector<PtnInfo>().swap(partitionInfo_);
}
if (!ParsePtableData()) {
LOG(ERROR) << "parse PtableData from json file error";
return false;
}
return true;
}
bool Ptable::ParsePtableDataNode(const JsonNode &ptableDataNode)
{
std::map<std::string, uint32_t*> ptableDataVars = {
{EMMC_GPT_DATA_LEN_LABEL, &ptableData_.emmcGptDataLen},
{LBA_LEN_LABEL, &ptableData_.lbaLen},
{GPT_HEADER_LEN_LABEL, &ptableData_.gptHeaderLen},
{BLOCK_SIZE_LABEL, &ptableData_.blockSize},
{IMG_LUN_SIZE_LABEL, &ptableData_.imgLuSize},
{START_LUN_NUM_LABEL, &ptableData_.startLunNumber},
{WRITE_DEVICE_LUN_SIZE_LABEL, &ptableData_.writeDeviceLunSize},
{DEFAULT_LUN_NUM_LABEL, &ptableData_.defaultLunNum},
};
for (auto dataVar : ptableDataVars) {
auto dataValue = ptableDataNode[dataVar.first.c_str()].As<uint32_t>();
if (!dataValue) {
LOG(ERROR) << "parse json failed! " << dataVar.first << " is nullptr!";
return false;
}
*(dataVar.second) = *dataValue;
LOG(INFO) << "set " << dataVar.first << " : " << *dataValue;
}
return true;
}
std::string Ptable::GetPtableConfigPath()
{
std::string ptableConfigPath = std::string(PTABLE_CONFIG_PATH);
if (access(VENDOR_PTABLE_CONFIG_PATH, F_OK) == 0) {
ptableConfigPath = std::string(VENDOR_PTABLE_CONFIG_PATH);
}
return ptableConfigPath;
}
bool Ptable::ParsePtableData()
{
(void)memset_s(&ptableData_, sizeof(ptableData_), 0, sizeof(ptableData_));
std::string ptableConfigPath = GetPtableConfigPath();
std::ifstream ifs(ptableConfigPath);
if (!ifs.is_open()) {
LOG(ERROR) << PTABLE_CONFIG_PATH << " not exist";
return false;
}
std::string content {std::istreambuf_iterator<char> {ifs}, {}};
cJSONPtr root(cJSON_Parse(content.c_str()), cJSON_Delete);
if (root == nullptr) {
LOG(ERROR) << PTABLE_CONFIG_PATH << " contained json invalid";
return false;
}
JsonNode node(root.get(), false);
const JsonNode &ptableDataNode = node[PTABLE_DATA_LABEL];
bool ret = ParsePtableDataNode(ptableDataNode);
ptableData_.dataValid = ret;
return ret;
}
uint32_t Ptable::GetDefaultImageSize()
{
return ptableData_.emmcGptDataLen + ptableData_.defaultLunNum * ptableData_.imgLuSize;
}
bool Ptable::CheckFileExist(const std::string &fileName)
{
struct stat buffers;
if (memset_s(&buffers, sizeof(buffers), 0, sizeof(buffers)) != EOK) {
LOG(WARNING) << "memset_s fail";
}
if (stat(fileName.c_str(), &buffers) == 0) {
LOG(DEBUG) << fileName << " is exist";
return true;
}
LOG(INFO) << fileName << " is not exist";
return false;
}
bool Ptable::MemReadWithOffset(const std::string &filePath, const uint64_t offset,
uint8_t *outData, const uint32_t dataSize)
{
if (filePath.length() == 0 || outData == nullptr || dataSize == 0) {
LOG(ERROR) << "invaild input";
return false;
}
std::ifstream fin(filePath, std::ios::in);
if (fin.fail()) {
LOG(ERROR) << "open " << filePath << " fail";
return false;
}
fin.seekg(offset, std::ios::beg);
if (fin.tellg() != static_cast<long long>(offset)) {
LOG(ERROR) << "seekp 0x" << std::hex << offset << " bytes in " << filePath <<
" failed. Now is in 0x" << std::hex << fin.tellg() << std::dec;
fin.close();
return false;
}
if (!fin.read(reinterpret_cast<char *>(outData), dataSize)) {
LOG(ERROR) << "read 0x" << std::hex << dataSize << " bytes in " << filePath <<
" failed. only read 0x" << std::hex << fin.gcount() << std::dec;
fin.close();
return false;
}
fin.close();
return true;
}
uint32_t Ptable::Reflect(uint32_t data, const uint32_t len)
{
uint32_t ref = 0;
for (uint32_t i = 0; i < len; i++) {
if (data & 0x1) {
ref |= (1 << ((len - 1) - i));
}
data = (data >> 1);
}
return ref;
}
uint32_t Ptable::CalculateCrc32(const uint8_t *buffer, const uint32_t len)
{
if (buffer == nullptr || len == 0) {
LOG(INFO) << "invaild input";
return 0;
}
const uint32_t byteLen = 8;
uint32_t msb;
const uint64_t polynomial = 0x104C11DB7LL;
uint32_t regs = 0xFFFFFFFF;
const uint32_t regsMask = 0xFFFFFFFF;
uint32_t regsMsb;
for (uint32_t i = 0; i < len; i++) {
uint32_t dataByte = buffer[i];
dataByte = Reflect(dataByte, 8);
for (uint32_t j = 0; j < byteLen; j++) {
msb = dataByte >> (byteLen - 1);
msb &= 1;
regsMsb = (regs >> 31) & 1;
regs = regs << 1;
if (regsMsb ^ msb) {
regs = regs ^ polynomial;
}
regs = regs & regsMask;
dataByte <<= 1;
}
}
regs = regs & regsMask;
uint32_t ret = Reflect(regs, 32) ^ 0xFFFFFFFF;
return ret;
}
bool Ptable::VerifyMbrMagicNum(const uint8_t *buffer, const uint32_t size)
{
if (size < (MBR_MAGIC_NUM_POS + 1)) {
LOG(ERROR) << "size < (TABLE_SIGNATURE + 1)";
return false;
}
if ((buffer[MBR_MAGIC_NUM_POS] != MBR_MAGIC_NUM_0) ||
(buffer[MBR_MAGIC_NUM_POS + 1] != MBR_MAGIC_NUM_1)) {
LOG(ERROR) << "MBR magic number does not match, magic buffer is " << unsigned(*(buffer + MBR_MAGIC_NUM_POS));
return false;
}
return true;
}
bool Ptable::CheckProtectiveMbr(const uint8_t *gptImage, const uint32_t imgLen)
{
if (!VerifyMbrMagicNum(gptImage, imgLen)) {
LOG(ERROR) << "MBR magic number verify failed!";
return false;
}
uint32_t type;
for (uint32_t i = 0; i < MBR_GPT_MAX_NUM; i++) {
if (MBR_GPT_ENTRY + i * MBR_GPT_ENTRY_SIZE + GPT_TYPE_SIGN_OFFSET >= imgLen) {
LOG(INFO) << "not find Protective MBR(type: 0xEE) in this partition";
return false;
}
type = gptImage[MBR_GPT_ENTRY + i * MBR_GPT_ENTRY_SIZE + GPT_TYPE_SIGN_OFFSET];
if (type == MBR_PROTECTIVE_GPT_TYPE) {
LOG(INFO) << "type is MBR_PROTECTIVE_GPT_TYPE(0xEE), GPT partitions exist";
return true;
}
LOG(INFO) << "the " << i << " main GPT's type=0x" << std::hex << type << std::dec;
}
LOG(INFO) << "not find Protective MBR(type: 0xEE) in this partition";
return false;
}
bool Ptable::CheckIfValidGpt(const uint8_t *gptImage, const uint32_t gptImageLen)
{
if (gptImageLen < 8) {
LOG(ERROR) << "gptImageLen is less than 8.";
return false;
}
uint64_t gptMagic = GET_LLWORD_FROM_BYTE(gptImage);
if (gptMagic != EFI_MAGIC_NUMBER) {
LOG(ERROR) << "invaild partiton with gptMagic:0x" << std::hex << gptMagic << std::dec;
return false;
}
return true;
}
bool Ptable::GetCapacity(const std::string &filePath, uint64_t &lunCapacity)
{
if (filePath.empty()) {
LOG(ERROR) << "filePath is empty or lunCapacity is nullptr";
return false;
}
std::ifstream fin(filePath, std::ios::in);
if (!fin.is_open()) {
LOG(ERROR) << "open " << filePath << " fail";
return false;
}
uint64_t sector = 0;
fin >> sector;
if (sector == 0) {
LOG(ERROR) << "read data from " << filePath << " fail";
fin.close();
return false;
}
uint64_t capacity = sector * SECTOR_SIZE;
LOG(INFO) << "lun capacity = 0x" << std::hex << capacity << std::dec;
lunCapacity = capacity;
fin.close();
return true;
}
bool Ptable::GetPartitionGptHeaderInfo(const uint8_t *buffer, const uint32_t bufferLen, GPTHeaderInfo& gptHeaderInfo)
{
if (buffer == nullptr || bufferLen < LBA_LENGTH) {
LOG(ERROR) << "input invalid";
return false;
}
if (!CheckIfValidGpt(buffer, bufferLen)) {
LOG(ERROR) << "invaild partiton with gptMagic";
return false;
}
gptHeaderInfo.headerSize = GET_LWORD_FROM_BYTE(buffer + HEADER_SIZE_OFFSET);
gptHeaderInfo.firstUsableLba = GET_LLWORD_FROM_BYTE(buffer + FIRST_USABLE_LBA_OFFSET);
gptHeaderInfo.maxPartitionCount = GET_LWORD_FROM_BYTE(buffer + PARTITION_COUNT_OFFSET);
gptHeaderInfo.partitionEntrySize = GET_LWORD_FROM_BYTE(buffer + PENTRY_SIZE_OFFSET);
if (gptHeaderInfo.maxPartitionCount == 0 || gptHeaderInfo.partitionEntrySize == 0) {
LOG(ERROR) << "invalid gpt header info";
return false;
}
return true;
}
void Ptable::PatchBackUpGptHeader(uint8_t *gptHeader, const uint32_t len, uint64_t backGptEntryStart)
{
if (std::max({GPT_HEADER_OFFSET, BACKUP_HEADER_OFFSET, PARTITION_ENTRY_OFFSET}) + sizeof(uint64_t) > len ||
HEADER_CRC_OFFSET + sizeof(uint32_t) > len) {
LOG(ERROR) << "input param invalid";
return;
}
uint64_t gptHeaderOffset = GET_LLWORD_FROM_BYTE(gptHeader + GPT_HEADER_OFFSET);
uint64_t backHeaderOffset = GET_LLWORD_FROM_BYTE(gptHeader + BACKUP_HEADER_OFFSET);
PUT_LONG_LONG(gptHeader + GPT_HEADER_OFFSET, backHeaderOffset);
PUT_LONG_LONG(gptHeader + BACKUP_HEADER_OFFSET, gptHeaderOffset);
PUT_LONG_LONG(gptHeader + PARTITION_ENTRY_OFFSET, backGptEntryStart);
PUT_LONG(gptHeader + HEADER_CRC_OFFSET, 0);
uint32_t crcValue = CalculateCrc32(gptHeader, GPT_CRC_LEN);
PUT_LONG(gptHeader + HEADER_CRC_OFFSET, crcValue);
LOG(INFO) << "gpt header offset " << gptHeaderOffset << ", back header offset " << backHeaderOffset <<
", crc value " << crcValue;
}
bool Ptable::CheckGptHeader(uint8_t *buffer, const uint32_t bufferLen, const uint64_t lbaNum,
const GPTHeaderInfo& gptHeaderInfo)
{
if (bufferLen < LBA_LENGTH || lbaNum == 0) {
LOG(ERROR) << "bufferLen < LBA_LENGTH || lbaNum == 0";
return false;
}
if (gptHeaderInfo.headerSize < GPT_HEADER_SIZE || gptHeaderInfo.headerSize > bufferLen) {
LOG(ERROR) << "GPT Header size is invaild";
return false;
}
uint32_t orgCrcVal = GET_LWORD_FROM_BYTE(buffer + HEADER_CRC_OFFSET);
PUT_LONG(buffer + HEADER_CRC_OFFSET, 0);
uint32_t crcVal = CalculateCrc32(buffer, gptHeaderInfo.headerSize);
if (crcVal != orgCrcVal) {
LOG(ERROR) << "Header crc mismatch crcVal = " << std::hex << crcVal << " with orgCrcVal = " <<
orgCrcVal << std::dec;
return false;
}
PUT_LONG(buffer + HEADER_CRC_OFFSET, crcVal);
uint32_t currentLba = GET_LLWORD_FROM_BYTE(buffer + PRIMARY_HEADER_OFFSET);
uint32_t lastUsableLba = GET_LLWORD_FROM_BYTE(buffer + LAST_USABLE_LBA_OFFSET);
uint32_t partition0 = GET_LLWORD_FROM_BYTE(buffer + PARTITION_ENTRIES_OFFSET);
if (gptHeaderInfo.firstUsableLba > lbaNum || lastUsableLba > lbaNum) {
LOG(ERROR) << "invalid usable lba " << gptHeaderInfo.firstUsableLba << ", last is " << lastUsableLba <<
" lbaNum is " << lbaNum;
return false;
}
if (gptHeaderInfo.partitionEntrySize != PARTITION_ENTRY_SIZE ||
gptHeaderInfo.maxPartitionCount > (MIN_PARTITION_ARRAY_SIZE / PARTITION_ENTRY_SIZE)) {
LOG(ERROR) << "invalid parition entry size or max count";
return false;
}
if (currentLba != 0x1) {
LOG(ERROR) << "starting LBA mismatch currentLba " << currentLba << " partition0 " << partition0;
return false;
}
return true;
}
bool Ptable::PartitionCheckGptHeader(const HeaderCheckInputs &inputs,
const uint32_t blockSize, GPTHeaderInfo& gptHeaderInfo)
{
if (inputs.len < ptableData_.writeDeviceLunSize || inputs.lbaNum == 0 ||
inputs.lunNum > PTABLE_MAX_LUN_NUMBERS ||
inputs.len < 2 * blockSize) {
LOG(ERROR) << "len " << inputs.len << " ptableData_.writeDeviceLunSize " << ptableData_.writeDeviceLunSize
<< " lbaNum " << inputs.lbaNum << " blockSize " << blockSize;
return false;
}
uint8_t *buffer = new(std::nothrow) uint8_t[blockSize]();
if (buffer == nullptr) {
LOG(ERROR) << "new buffer failed!";
return false;
}
if (memcpy_s(buffer, blockSize, inputs.gptImage + blockSize, blockSize) != EOK) {
LOG(ERROR) << "copy gpt header fail";
delete [] buffer;
return false;
}
if (!CheckGptHeader(buffer, blockSize, inputs.lbaNum, gptHeaderInfo)) {
LOG(ERROR) << "CheckGptHeader fail";
delete [] buffer;
return false;
}
uint32_t partition0 = GET_LLWORD_FROM_BYTE(&buffer[PARTITION_ENTRIES_OFFSET]);
uint32_t orgCrcVal = GET_LWORD_FROM_BYTE(&buffer[PARTITION_CRC_OFFSET]);
delete [] buffer;
uint32_t crcVal = GetGptEntryCrc(inputs, partition0 * blockSize,
gptHeaderInfo.maxPartitionCount * gptHeaderInfo.partitionEntrySize);
if (crcVal != orgCrcVal) {
LOG(ERROR) << "partition entires crc mismatch crcVal =" << std::hex << crcVal << " with orgCrcVal =" <<
orgCrcVal << std::dec;
return false;
}
LOG(INFO) << "PartitionCheckGptHeader ok";
return true;
}
void Ptable::PrintPtableInfo() const
{
if (partitionInfo_.empty()) {
LOG(ERROR) << "ptable vector is empty!";
return;
}
LOG(INFO) << "print ptable";
PrintPtableChanges(partitionInfo_);
}
void Ptable::PrintPtableInfo(const std::vector<PtnInfo> &ptnInfo) const
{
if (ptnInfo.empty()) {
LOG(ERROR) << "ptable vector is empty!";
return;
}
LOG(INFO) << "print ptable";
PrintPtableChanges(ptnInfo);
}
void Ptable::SetPartitionType(const std::string &partName, PtnInfo &ptnInfo)
{
if (partName.find("RESERVED") != std::string::npos) {
ptnInfo.partType = PartType::RESERVED_TYPE;
return;
}
if (PARTITION_AB_SUFFIX_SIZE > partName.size()) {
ptnInfo.partType = PartType::COMMON_TYPE;
return;
}
std::string partSuffix = partName.substr(partName.size() - PARTITION_AB_SUFFIX_SIZE,
PARTITION_AB_SUFFIX_SIZE);
if (strcasecmp(partSuffix.c_str(), PARTITION_A_SUFFIX) == 0) {
ptnInfo.partType = PartType::A_TYPE;
return;
}
if (strcasecmp(partSuffix.c_str(), PARTITION_B_SUFFIX) == 0) {
ptnInfo.partType = PartType::B_TYPE;
return;
}
ptnInfo.partType = PartType::COMMON_TYPE;
}
void Ptable::ParsePartitionName(const uint8_t *data, const uint32_t dataLen,
std::string &name, const uint32_t nameLen)
{
if (data == nullptr || dataLen == 0 || nameLen == 0) {
LOG(ERROR) << "dataLen == 0 || nameLen == 0";
return;
}
char utf16Name[MAX_GPT_NAME_SIZE] = {0};
if (memcpy_s(utf16Name, sizeof(utf16Name), data, dataLen) != EOK) {
LOG(ERROR) << "memcpy name fail";
return;
}
std::string outName;
for (uint32_t n = 0; n < nameLen && n < (MAX_GPT_NAME_SIZE / 2) && utf16Name[n * 2] != '\0'; n++) {
outName = outName + utf16Name[n * 2];
}
for (uint32_t i = 0; i < outName.size(); i++) {
outName[i] = static_cast<char>(toupper(outName[i]));
}
name = outName;
return;
}
bool Ptable::WritePartitionName(const std::string &name, const uint32_t nameLen,
uint8_t *data, const uint32_t dataLen)
{
if (data == nullptr || dataLen == 0 || nameLen == 0 || dataLen < nameLen * 2) {
LOG(ERROR) << "Invalid parameters";
return false;
}
for (uint32_t n = 0; n < nameLen; n++) {
data[n * 2] = static_cast<uint8_t>(tolower(name[n]));
data[n * 2 + 1] = 0;
}
return true;
}
bool Ptable::WriteBufferToPath(const std::string &path, const uint64_t offset,
const uint8_t *buffer, const uint32_t size)
{
std::unique_ptr<DataWriter> writer = DataWriter::CreateDataWriter(WRITE_RAW, path, offset);
if (writer == nullptr) {
LOG(ERROR) << "create writer class failed!";
return false;
}
bool ret = writer->Write(buffer, size, nullptr);
if (!ret) {
LOG(ERROR) << "writer to " << path << " with offset " << offset << " failed ";
DataWriter::ReleaseDataWriter(writer);
return false;
}
DataWriter::ReleaseDataWriter(writer);
return true;
}
bool Ptable::GetPartionInfoByName(const std::string &partitionName, PtnInfo &ptnInfo, int32_t &index)
{
if (partitionInfo_.empty()) {
LOG(ERROR) << "get partition failed! partitionInfo_ is empty";
return false;
}
auto findPart = [&ptnInfo, &index, this] (const std::string &partitionName) {
for (int32_t i = 0; i < static_cast<int32_t>(partitionInfo_.size()); i++) {
if (partitionInfo_[i].dispName.size() == partitionName.size() &&
strcasecmp(partitionInfo_[i].dispName.c_str(), partitionName.c_str()) == 0) {
index = i;
ptnInfo = partitionInfo_[i];
return true;
}
}
return false;
};
if (findPart(partitionName)) {
LOG_SEN(INFO) << "find partition name " << partitionName;
return true;
}
std::string partitionNameAB = partitionName;
int updateSlot = Utils::GetUpdateSlot();
if (updateSlot < 1 || updateSlot > 2) {
LOG(ERROR) << "get update slot fail";
return false;
}
partitionNameAB += (updateSlot == SLOT_A ? PARTITION_A_SUFFIX : PARTITION_B_SUFFIX);
if (findPart(partitionNameAB)) {
LOG(INFO) << "find partitionAB name " << partitionNameAB;
return true;
}
LOG(ERROR) << "get partition info failed! Not found partition:" << partitionName;
return false;
}
bool Ptable::AdjustGpt(uint8_t *ptnInfoBuf, uint64_t bufSize, const std::string &ptnName, uint64_t preLastLBA,
uint64_t lastPtnLastLBA)
{
if (ptnInfoBuf == nullptr || bufSize == 0 || ptnName.empty()) {
LOG(ERROR) << "invalid input";
return false;
}
if (ptnName != LAST_PATITION_NAME) {
uint64_t firstLBA = GET_LLWORD_FROM_BYTE(&ptnInfoBuf[FIRST_LBA_OFFSET]);
uint64_t lastLBA = GET_LLWORD_FROM_BYTE(&ptnInfoBuf[LAST_LBA_OFFSET]);
lastLBA = lastLBA - firstLBA + preLastLBA + 1;
firstLBA = preLastLBA + 1;
PUT_LONG_LONG(ptnInfoBuf + FIRST_LBA_OFFSET, firstLBA);
PUT_LONG_LONG(ptnInfoBuf + LAST_LBA_OFFSET, lastLBA);
} else {
uint64_t firstLBA = preLastLBA + 1;
if (lastPtnLastLBA < firstLBA) {
LOG(ERROR) << "patch last partition fail";
return false;
}
PUT_LONG_LONG(ptnInfoBuf + FIRST_LBA_OFFSET, firstLBA);
PUT_LONG_LONG(ptnInfoBuf + LAST_LBA_OFFSET, lastPtnLastLBA);
}
return true;
}
bool Ptable::ChangeGpt(uint8_t *gptBuf, uint64_t gptSize, GptParseInfo gptInfo, PtnInfo &modifyInfo)
{
if (gptBuf == nullptr || gptSize == 0 || gptSize <= gptInfo.imgBlockSize || gptInfo.devBlockSize == 0) {
LOG(ERROR) << "input param invalid";
return false;
}
bool modifyDectect = false;
uint8_t *gptHead = gptBuf + gptInfo.imgBlockSize;
uint32_t ptnEntrySize = GET_LLWORD_FROM_BYTE(&gptHead[PENTRY_SIZE_OFFSET]);
uint64_t ptnStart = GET_LLWORD_FROM_BYTE(&gptHead[PARTITION_ENTRIES_OFFSET]);
uint64_t readSize = ptnStart * gptInfo.imgBlockSize;
uint8_t *ptnInfoBuf = gptBuf + readSize;
uint64_t preLastLBA = 0;
uint64_t lastPtnLastLBA = gptInfo.devDensity / gptInfo.devBlockSize - 1;
while (readSize < gptSize) {
std::string dispName;
ParsePartitionName(&ptnInfoBuf[GPT_PARTITION_NAME_OFFSET], MAX_GPT_NAME_SIZE, dispName, MAX_GPT_NAME_SIZE / 2);
if (dispName.empty()) {
break;
}
if (modifyDectect) {
if (!AdjustGpt(ptnInfoBuf, gptSize - readSize, dispName, preLastLBA, lastPtnLastLBA)) {
return false;
}
preLastLBA = GET_LLWORD_FROM_BYTE(&ptnInfoBuf[LAST_LBA_OFFSET]);
ptnInfoBuf += ptnEntrySize;
readSize += static_cast<uint64_t>(ptnEntrySize);
continue;
}
if (dispName == modifyInfo.dispName) {
LOG(INFO) << "modify part dectected!! dispName = " << dispName;
uint64_t firstLBA = modifyInfo.startAddr / gptInfo.devBlockSize;
uint64_t lastLBA = firstLBA + modifyInfo.partitionSize / gptInfo.devBlockSize - 1;
if ((dispName == LAST_PATITION_NAME) && (lastLBA != lastPtnLastLBA)) {
return false;
}
PUT_LONG_LONG(ptnInfoBuf + FIRST_LBA_OFFSET, firstLBA);
PUT_LONG_LONG(ptnInfoBuf + LAST_LBA_OFFSET, lastLBA);
modifyDectect = true;
preLastLBA = lastLBA;
}
ptnInfoBuf += ptnEntrySize;
readSize += static_cast<uint64_t>(ptnEntrySize);
}
return true;
}
bool Ptable::WritePartitionBufToFile(uint8_t *ptbImgBuffer, const uint32_t imgBufSize, const std::string &ptbPath)
{
if (ptbImgBuffer == nullptr || imgBufSize == 0) {
LOG(ERROR) << "Invalid param ";
return false;
}
std::ofstream ptbFile(ptbPath, std::ios::ate | std::ios::binary);
if (ptbFile.fail()) {
LOG(ERROR) << "Failed to open " << ptbPath << strerror(errno);
return false;
}
ptbFile.write(reinterpret_cast<const char*>(ptbImgBuffer), imgBufSize);
if (ptbFile.bad()) {
LOG(ERROR) << "Failed to write ptable tmp file " << imgBufSize << strerror(errno);
return false;
}
ptbFile.flush();
sync();
return true;
}
bool Ptable::ReadPartitionFileToBuffer(uint8_t *ptbImgBuffer, uint32_t &imgBufSize, const std::string &ptbPath)
{
if (ptbImgBuffer == nullptr || imgBufSize == 0) {
LOG(ERROR) << "Invalid param ";
return false;
}
std::string realPath {};
if (!Utils::PathToRealPath(ptbPath, realPath)) {
LOG(ERROR) << "realpath failed:" << ptbPath;
return false;
}
std::ifstream ptbFile(realPath, std::ios::in | std::ios::binary);
if (!ptbFile.is_open()) {
LOG(ERROR) << "open " << realPath << " failed " << strerror(errno);
return false;
}
ptbFile.seekg(0, std::ios::end);
uint32_t fileSize = static_cast<uint32_t>(ptbFile.tellg());
if (ptbFile.tellg() <= 0 || fileSize > imgBufSize) {
LOG(ERROR) << "ptbFile is error";
return false;
}
ptbFile.seekg(0, std::ios::beg);
if (!ptbFile.read(reinterpret_cast<char*>(ptbImgBuffer), fileSize)) {
LOG(ERROR) << "read ptable file to buffer failed " << fileSize << strerror(errno);
return false;
}
imgBufSize = fileSize;
return true;
}
void Ptable::DeletePartitionTmpFile()
{
const char* ptablePath = Utils::IsUpdaterMode() ? PTABLE_TEMP_PATH : PTABLE_NORMAL_PATH;
if (Utils::DeleteFile(ptablePath) != 0) {
LOG(ERROR) << "delete ptable tmp file fail " << ptablePath << strerror(errno);
return;
}
LOG(INFO) << "delete ptable tmp file success " << ptablePath;
}
void Ptable::PrintPtableChanges(const std::vector<Ptable::PtnInfo> &ptnInfo)
{
if (ptnInfo.empty()) {
LOG(ERROR) << "ptable vector is empty!";
return;
}
static std::vector<PtnInfo> lastPrint;
bool isSame = std::equal(lastPrint.begin(), lastPrint.end(), ptnInfo.begin(), ptnInfo.end(),
[](const PtnInfo &lhs, const PtnInfo &rhs) {
return std::tie(lhs.lun, lhs.startAddr, lhs.partitionSize, lhs.dispName, lhs.partType)
== std::tie(rhs.lun, rhs.startAddr, rhs.partitionSize, rhs.dispName, rhs.partType);
});
lastPrint = ptnInfo;
LOG(INFO) << "ptnInfo : ===========================================";
if (isSame) {
LOG(INFO) << "partition count = " << ptnInfo.size() << " ptable not change";
} else {
LOG(INFO) << "partition count = " << ptnInfo.size();
for (size_t i = 0; i < ptnInfo.size(); ++i) {
PrintPartition(i, ptnInfo[i]);
}
constexpr int waitMilliseconds = 500;
std::this_thread::sleep_for(std::chrono::milliseconds(waitMilliseconds));
}
LOG(INFO) << "ptnInfo : ===========================================";
}
void Ptable::PrintPartition(size_t index, const PtnInfo &info)
{
LOG_LITE_SEN(INFO) << "[" << index << "].name=" << info.dispName << ", addr=0x" << std::hex << info.startAddr
<< ", size=0x" << info.partitionSize << ", lun=" << std::dec << info.lun
<< ", type=" << static_cast<std::underlying_type<PartType>::type>(info.partType);
}
bool Ptable::WritePtableLunOffset(uint32_t lunIndex, uint64_t offset)
{
return true;
}
uint32_t Ptable::GetGptEntryCrc(const HeaderCheckInputs &inputs, uint64_t offset, uint32_t crcLen)
{
if (inputs.len < static_cast<uint32_t>(offset) + crcLen) {
LOG(ERROR) << "input invalid " << inputs.len << " " << offset << " " << crcLen;
return 0;
}
return CalculateCrc32(inputs.gptImage + offset, crcLen);
}
}