* Copyright (c) Huawei Device Co., Ltd. 2026-2026. All rights reserved.
* 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 <cstring>
#include <iomanip>
#include <sstream>
#include <securec.h>
#include <iostream>
#include "header.h"
#include "alloctable.h"
#include "utils.h"
namespace OHOS {
namespace ObjectEditor {
static const Byte g_cdMagic[] = {0xD0u, 0xCFu, 0x11u, 0xE0u, 0xA1u, 0xB1u, 0x1Au, 0xE1u};
Header::Header()
{
minorVersion_ = DEFAULT_MINOR_VERSION;
majorVersion_ = DEFAULT_MAJOR_VERSION;
byteOrder_ = DEFAULT_BYTE_ORDER;
bigBlockShift_ = DEFAULT_SECTOR_SHIFT;
miniBlockShift_ = DEFAULT_MINI_SECTOR_SHIFT;
dirSectorCount_ = 0;
numBat_ = 0;
direntStart_ = 0;
transactionSignature_ = 0;
threshold_ = MINI_STREAM_CUTOFF;
sbatStart_ = 0;
numSbat_ = 0;
difatStart_ = 0;
numDifat_ = 0;
for (size_t i = 0; i < FILE_SIGNATURE_SIZE; i++) {
id_[i] = g_cdMagic[i];
}
clsid_.fill(0);
for (size_t i = 0; i < HEADER_DIFAT_ARRAY_SIZE; i++) {
bbBlocks_[i] = AllocTable::Avail;
}
}
bool Header::Valid() const
{
constexpr int32_t MAX_SECTOR_SHIFT_SIZE = 31;
if (threshold_ != MINI_STREAM_CUTOFF || numBat_ == 0 || bigBlockShift_ <= DEFAULT_MINI_SECTOR_SHIFT ||
bigBlockShift_ >= MAX_SECTOR_SHIFT_SIZE || miniBlockShift_ >= bigBlockShift_) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid header");
return false;
}
if (numBat_ > HEADER_DIFAT_ARRAY_SIZE) {
const uint32_t sectorSize = 1u << bigBlockShift_;
const uint32_t entriesPerSector = sectorSize / static_cast<uint32_t>(sizeof(uint32_t));
const uint32_t difatEntries = entriesPerSector > 0 ? entriesPerSector - 1 : 0;
if (difatEntries == 0) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid DIFAT");
return false;
}
const uint32_t extraNeeded = numBat_ - HEADER_DIFAT_ARRAY_SIZE;
const uint32_t difatSectorsNeeded = (extraNeeded + difatEntries - 1) / difatEntries;
if (numDifat_ < difatSectorsNeeded) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid number of DIFAT sectors");
return false;
}
}
if ((numBat_ < HEADER_DIFAT_ARRAY_SIZE) && (numDifat_ != 0)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid numBat");
return false;
}
return true;
}
bool Header::IsCompoundDocument() const
{
for (size_t i = 0; i < FILE_SIGNATURE_SIZE; i++) {
if (id_[i] != g_cdMagic[i]) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid file signature");
return false;
}
}
return true;
}
bool Header::Load(const Byte *buffer, size_t len)
{
const size_t required = HEADER_FIXED_SIZE + HEADER_DIFAT_ARRAY_SIZE * FOUR_BYTE_SIZE;
if (len < required || buffer == nullptr) [[unlikely]] {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
"invalid buffer or len: %{public}zu, required: %{public}zu", len, required);
return false;
}
for (size_t i = 0; i < FILE_SIGNATURE_SIZE; i++) {
id_[i] = buffer[i];
}
if (memcpy_s(clsid_.data(), clsid_.size(), buffer + 0x08, clsid_.size()) != EOK) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "memcpy_s failed");
return false;
}
bigBlockShift_ = ReadUint16(buffer + HEADER_SECTOR_SHIFT_OFFSET);
miniBlockShift_ = ReadUint16(buffer + HEADER_MINI_SECTOR_SHIFT_OFFSET);
numBat_ = ReadUint32(buffer + HEADER_FAT_SECTOR_NUMBER_OFFSET);
direntStart_ = ReadUint32(buffer + HEADER_FIRST_DIR_SECTOR_OFFSET);
transactionSignature_ = ReadUint32(buffer + HEADER_TRANSACTION_SIGNATURE_OFFSET);
threshold_ = ReadUint32(buffer + HEADER_MINI_STREAM_CUTOFF_OFFSET);
if (threshold_ != MINI_STREAM_CUTOFF) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
"invalid threshold: %{public}u, expected: %{public}u",
threshold_, MINI_STREAM_CUTOFF);
return false;
}
sbatStart_ = ReadUint32(buffer + HEADER_FIRST_MINI_FAT_SECTOR_OFFSET);
numSbat_ = ReadUint32(buffer + HEADER_MINI_FAT_SECTOR_NUMBER_OFFSET);
difatStart_ = ReadUint32(buffer + HEADER_FIRST_DIFAT_SECTOR_OFFSET);
numDifat_ = ReadUint32(buffer + HEADER_DIFAT_SECTOR_NUMBER_OFFSET);
for (size_t i = 0; i < HEADER_DIFAT_ARRAY_SIZE; i++) {
bbBlocks_[i] = ReadUint32(buffer + HEADER_FIXED_SIZE + i * FOUR_BYTE_SIZE);
}
return true;
}
bool Header::Save(Byte *buffer, size_t len)
{
const size_t required = HEADER_FIXED_SIZE + HEADER_DIFAT_ARRAY_SIZE * FOUR_BYTE_SIZE;
if (len < required || buffer == nullptr) [[unlikely]] {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid buffer or len: %{public}zu, required: %{public}zu",
len, required);
return false;
}
auto ec = memset_s(buffer, len, 0, HEADER_FIXED_SIZE);
if (ec != EOK) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "memset_s fixed size failed, error: %{public}d", ec);
return false;
}
ec = memcpy_s(buffer, len, g_cdMagic, FILE_SIGNATURE_SIZE);
if (ec != EOK) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "memcpy_s file signature size failed: %{public}d", ec);
return false;
}
ec = memcpy_s(buffer + FILE_SIGNATURE_SIZE, len - FILE_SIGNATURE_SIZE, clsid_.data(), clsid_.size());
if (ec != EOK) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "memcpy_s clsid size failed, error: %{public}d", ec);
return false;
}
WriteUint16(buffer + HEADER_MINOR_VERSION_OFFSET, DEFAULT_MINOR_VERSION);
WriteUint16(buffer + HEADER_MAJOR_VERSION_OFFSET, DEFAULT_MAJOR_VERSION);
WriteUint16(buffer + HEADER_BYTE_ORDER_OFFSET, DEFAULT_BYTE_ORDER);
WriteUint16(buffer + HEADER_SECTOR_SHIFT_OFFSET, bigBlockShift_);
WriteUint16(buffer + HEADER_MINI_SECTOR_SHIFT_OFFSET, miniBlockShift_);
WriteUint32(buffer + HEADER_TRANSACTION_SIGNATURE_OFFSET, transactionSignature_);
WriteUint32(buffer + HEADER_FAT_SECTOR_NUMBER_OFFSET, numBat_);
WriteUint32(buffer + HEADER_FIRST_DIR_SECTOR_OFFSET, direntStart_);
threshold_ = MINI_STREAM_CUTOFF;
WriteUint32(buffer + HEADER_MINI_STREAM_CUTOFF_OFFSET, threshold_);
WriteUint32(buffer + HEADER_FIRST_MINI_FAT_SECTOR_OFFSET, sbatStart_);
WriteUint32(buffer + HEADER_MINI_FAT_SECTOR_NUMBER_OFFSET, numSbat_);
WriteUint32(buffer + HEADER_FIRST_DIFAT_SECTOR_OFFSET, difatStart_);
WriteUint32(buffer + HEADER_DIFAT_SECTOR_NUMBER_OFFSET, numDifat_);
for (size_t i = 0; i < HEADER_DIFAT_ARRAY_SIZE; i++) {
WriteUint32(buffer + HEADER_FIXED_SIZE + i * FOUR_BYTE_SIZE, bbBlocks_[i]);
}
return true;
}
void Header::Debug()
{
const uint32_t ENDOFCHAIN = AllocTable::Eof;
constexpr uint32_t HEX_WIDTH = 8;
std::ostringstream oss;
auto fmtHex8 = [&oss](uint32_t v) {
oss << "0x" << std::hex << std::setw(HEX_WIDTH) << std::setfill('0') << std::nouppercase << v;
return oss.str();
};
auto sectorOrNone = [&](uint32_t v) {
return v == ENDOFCHAIN ? std::string("[NONE]") : fmtHex8(v);
};
auto indexOrNoneDec = [&](uint32_t v) {
return v == ENDOFCHAIN ? std::string("[NONE]") : std::to_string(v);
};
constexpr uint32_t PRINT_WIDTH = 16;
oss << "==================== HEADER ====================" << std::endl;
oss << std::left << std::setw(PRINT_WIDTH) << "Block Shift:" << std::right << bigBlockShift_ << " (2^"
<< bigBlockShift_ << " = " << (1u << bigBlockShift_) << " bytes)" << std::endl;
oss << std::left << std::setw(PRINT_WIDTH) << "Mini Shift:" << std::right << miniBlockShift_ << " (2^"
<< miniBlockShift_ << " = " << (1u << miniBlockShift_) << " bytes)" << std::endl;
oss << std::left << std::setw(PRINT_WIDTH) << "FAT Blocks:" << std::right << numBat_ << std::endl;
oss << std::left << std::setw(PRINT_WIDTH) << "Dir Start:" << std::right << indexOrNoneDec(direntStart_)
<< std::endl;
oss << std::left << std::setw(PRINT_WIDTH) << "Threshold:" << std::right << threshold_ << " bytes"
<< std::endl;
oss << std::left << std::setw(PRINT_WIDTH) << "MiniFAT Start:" << std::right << sectorOrNone(sbatStart_)
<< std::endl;
if (numSbat_ != 0) {
oss << std::left << std::setw(PRINT_WIDTH) << "MiniFAT Count:" << std::right << numSbat_ << std::endl;
}
oss << std::left << std::setw(PRINT_WIDTH) << "DIFAT Start:" << std::right << sectorOrNone(difatStart_)
<< std::endl;
if (numDifat_ != 0) {
oss << std::left << std::setw(PRINT_WIDTH) << "DIFAT Count:" << std::right << numDifat_ << std::endl;
}
uint32_t s = (numBat_ <= HEADER_DIFAT_ARRAY_SIZE) ? numBat_ : HEADER_DIFAT_ARRAY_SIZE;
oss << std::left << std::setw(PRINT_WIDTH) << (std::string("BAT Blocks (first ") + std::to_string(s) + "): ");
if (s == 0) {
oss << "";
} else {
for (uint32_t i = 0; i < s; i++) {
if (i) {
oss << ' ';
}
oss << bbBlocks_[i];
}
}
oss << std::endl;
oss << "====================================================" << std::endl;
OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "dirtree dump: %{public}s", oss.str().c_str());
}
}
}