FFernwehwarning fix
b39f856c创建于 10 天前历史提交
/*
 * 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 <algorithm>
#include <cstring>
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <limits>
#include <optional>
#include <securec.h>
#include <set>
#include <utility>
#include <filesystem>
#include <array>

#include "alloctable.h"
#include "dirtree.h"
#include "header.h"
#include "storage.h"
#include "stream.h"
#include "utils.h"
#include "system_utils.h"

namespace OHOS {
namespace ObjectEditor {
// LCOV_EXCL_START
namespace {
constexpr int32_t FLUSH_EXTRA_SIZE = 2;
}

StorageIO::StorageIO(const char *filename)
{
    dtModified_ = false;
    Init();
    auto f = std::make_unique<std::fstream>(
        filename, std::ios::binary | std::ios::in | std::ios::out);
    if (!f || f->fail()) {
        SetError(ErrorCode::OpenFailed, "Failed to open file for read/write");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to open file: %{private}s", filename);
        return;
    }
    file_ = std::move(f);
    stream_ = file_.get();
    const bool loaded = Load();
    if (!loaded) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Load failed");
    }
}

StorageIO::StorageIO(std::iostream *stream)
{
    dtModified_ = false;
    Init();
    if (!stream) {
        SetError(ErrorCode::OpenFailed, "Invalid input stream");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid input stream");
        return;
    }
    stream_ = stream;
    const bool loaded = Load();
    if (!loaded) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Load failed");
        return;
    }
}

StorageIO::StorageIO(const std::string &oeid)
{
    dtModified_ = false;
    Init();
    if (!ConfigMinimalCd(oeid)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "ConfigMinimalCd failed oeid: %{public}s",
            oeid.c_str());
        return;
    }

    if (!SerializeToMemory()) {
        SetError(ErrorCode::BadOLE, "Failed to serialize the minimal in-memory");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to serialize the minimal oeid: %{public}s",
            oeid.c_str());
        return;
    }
    ClearError();
}

bool StorageIO::ConfigMinimalCd(const std::string &oeid)
{
    if (!dirtree_ || !header_ || !bbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "dirtree or header or bbat is null");
        return false;
    }
    if (dirtree_) {
        dirtree_->Clear();
    }

    const auto clsidOpt = ParseOEidToClsid(oeid);
    if (!clsidOpt) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid OEID");
        return false;
    }
    const CLSID &clsid = *clsidOpt;
    header_->SetFatSectorCount(1);
    header_->SetFirstDirSector(1);
    header_->SetFirstMiniFat(AllocTable::Eof);
    header_->SetMiniFatCount(0);
    header_->SetFirstDifat(AllocTable::Eof);
    header_->SetDifatCount(0);
    header_->SetDifatElem(0, DIFAT_ELEM_VALUE);

    if (auto *root = GetRootEntry()) {
        root->SetType(DIR_ENTRY_ROOT);
        root->SetName("Root Entry");
        root->SetNameLen(ROOT_ENTRY_NAME_LEN);
        root->SetPrev(DIR_ENTRY_END);
        root->SetNext(DIR_ENTRY_END);
        root->SetChild(DIR_ENTRY_END);
        root->SetStart(0);
        root->SetSize(kDefaultSectorSize);
        root->SetClsid(clsid, std::size(clsid));
    }

    bbat_->SetBlockSize(1 << header_->BigBlockShift());
    const uint32_t fatEntriesPerSector = kDefaultSectorSize / static_cast<uint32_t>(sizeof(uint32_t));
    bbat_->Resize(fatEntriesPerSector);
    bbat_->Set(INDEX_ZERO, AllocTable::Eof);
    bbat_->Set(INDEX_ONE, AllocTable::Eof);
    bbat_->Set(INDEX_TWO, AllocTable::Bat);

    fatSectors_.clear();
    fatSectors_.push_back(INDEX_TWO);
    difatSectors_.clear();
    protectedBlocksCache_.clear();
    protectedBlocksDirty_ = true;

    for (size_t i = 3; i < fatEntriesPerSector; i++) {
        bbat_->Set(i, AllocTable::Avail);
    }
    return true;
}

bool StorageIO::SerializeToMemory()
{
    if (!dirtree_ || !header_ || !bbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "dirtree or header or bbat is null");
        return false;
    }
    const size_t sector = kHeaderSize;
    const size_t sectorCount = 4;
    std::vector<Byte> oleData(sector * sectorCount, 0);
    Byte *oleBytes = oleData.data();
    if (!header_->Save(oleBytes, sector)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to save header");
        return false;
    }

    std::fill_n(oleData.data() + sector, sector, 0xFF);
    Byte *dirSector = oleData.data() + sector * 2;
    std::fill_n(dirSector, sector, 0x00);
    if (!dirtree_->Save(dirSector, sector)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to save dirtree");
        return false;
    }
    Byte *fatSector = oleData.data() + sector * 3;
    std::fill_n(fatSector, sector, 0xFF);
    if (!bbat_->Save(fatSector, sectorCount * bbat_->Count())) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to save bbat");
        return false;
    }

    memoryBuffer_ = std::make_unique<std::vector<uint8_t>>(std::move(oleData));
    if (!memoryBuffer_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to create memory buffer");
        return false;
    }
    std::stringstream tempStream(std::string(reinterpret_cast<char *>(
        memoryBuffer_->data()), memoryBuffer_->size()),
        std::ios::in | std::ios::out | std::ios::binary);
    stream_ = &tempStream;
    bool loadResult = Load();
    stream_ = nullptr;
    return loadResult;
}

StorageIO::~StorageIO()
{
    Close();
}

void StorageIO::Init()
{
    auto newHeader = std::make_unique<Header>();
    if (newHeader == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to create new header");
        return;
    }
    auto newDirTree = std::make_unique<DirTree>();
    auto newBbat = std::make_unique<AllocTable>(1 << newHeader->BigBlockShift());
    auto newSbat = std::make_unique<AllocTable>(1 << newHeader->SmallBlockShift());
    std::vector<uint32_t> newFatSectors{};
    std::vector<uint32_t> newDifatSectors{};
    std::set<uint32_t> newProtectedBlocksCache{};

    error_ = ErrorCode::Ok;
    errorMsg_.clear();
    isValid_ = true;
    file_.reset();
    stream_ = nullptr;
    flushWriteBuffer_ = nullptr;
    header_.swap(newHeader);
    dirtree_.swap(newDirTree);
    bbat_.swap(newBbat);
    sbat_.swap(newSbat);
    size_ = 0;
    fatSectors_.swap(newFatSectors);
    difatSectors_.swap(newDifatSectors);
    protectedBlocksCache_.swap(newProtectedBlocksCache);
    protectedBlocksDirty_ = true;
}

bool StorageIO::IsValid() const noexcept
{
    return isValid_;
}

bool StorageIO::IsDirty() const noexcept
{
    return dtModified_;
}

StorageIO::ErrorCode StorageIO::LastError() const noexcept
{
    return error_;
}

const char *StorageIO::ErrorMessage() const noexcept
{
    if (!errorMsg_.empty()) {
        return errorMsg_.c_str();
    }
    switch (error_) {
        case ErrorCode::Ok:
            return "No error";
        case ErrorCode::OpenFailed:
            return "Failed to open file";
        case ErrorCode::NotOLE:
            return "Not a valid OLE file";
        case ErrorCode::BadOLE:
            return "Corrupted OLE structure";
        case ErrorCode::IOError:
            return "I/O operation failed";
        case ErrorCode::AllocationFailed:
            return "Block allocation failed";
        case ErrorCode::InvalidOperation:
            return "Invalid operation";
        case ErrorCode::Corruption:
            return "Data corruption detected";
        default:
            return "Unknown error";
    }
}

void StorageIO::ClearError() noexcept
{
    error_ = ErrorCode::Ok;
    errorMsg_.clear();
    isValid_ = true;
}

void StorageIO::SetError(ErrorCode code, const std::string &message, bool invalidate)
{
    error_ = code;
    if (!message.empty()) {
        errorMsg_ = message;
    } else {
        errorMsg_.clear();
    }
    if (invalidate) {
        isValid_ = false;
    }
}

bool StorageIO::IsValidReadParams(size_t offset, uint8_t *buf, size_t len, size_t *outRead)
{
    if (!outRead || (!buf && len > 0)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid parameters");
        return false;
    }
    *outRead = 0;

    if (len == 0) {
        ClearError();
        return true;
    }

    if (IsDirty()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Storage is dirty");
        return false;
    }

    if (!file_ || !stream_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "requires file-backed storage");
        return false;
    }

    const uint64_t maxStreamOff = static_cast<uint64_t>(std::numeric_limits<std::streamoff>::max());
    if (static_cast<uint64_t>(offset) > maxStreamOff) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Offset overflow");
        return false;
    }

    if (size_ < 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "File size is invalid");
        return false;
    }
    return true;
}

bool StorageIO::ReadRawCd(size_t offset, uint8_t *buf, size_t len, size_t *outRead)
{
    std::lock_guard<std::recursive_mutex> lock(ioMutex_);
    if (!IsValidReadParams(offset, buf, len, outRead)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid parameters");
        return false;
    }

    const uint64_t fileSize = static_cast<uint64_t>(size_);
    const uint64_t offset64 = static_cast<uint64_t>(offset);
    if (offset64 >= fileSize) {
        ClearError();
        return true;
    }

    const uint64_t remaining = fileSize - offset64;
    const uint64_t wanted = static_cast<uint64_t>(len);
    const uint64_t toRead64 = std::min(remaining, wanted);

    const uint64_t maxStreamSize = static_cast<uint64_t>(std::numeric_limits<std::streamsize>::max());
    if (toRead64 > maxStreamSize) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Read length overflow");
        return false;
    }
    file_->flush();
    if (auto *rdbuf = file_->rdbuf()) {
        rdbuf->pubsync();
    }

    stream_->clear();
    stream_->seekg(static_cast<std::streamoff>(offset64), std::ios::beg);
    if (!stream_->good()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to seek CD file");
        return false;
    }

    const std::streamsize toRead = static_cast<std::streamsize>(toRead64);
    stream_->read(reinterpret_cast<char *>(buf), toRead);
    const std::streamsize actualRead = stream_->gcount();
    if ((actualRead < toRead) && !stream_->eof()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to read CD file");
        *outRead = static_cast<size_t>(actualRead);
        return false;
    }
    stream_->clear();
    *outRead = static_cast<size_t>(actualRead);
    ClearError();
    return true;
}

bool StorageIO::Load()
{
    if (!stream_) {
        SetError(ErrorCode::OpenFailed, "No input stream available");
        return false;
    }

    stream_->seekg(0, std::ios::end);
    size_ = stream_->tellg();
    const uint64_t fileSize = size_ >= 0 ? static_cast<uint64_t>(size_) : 0;

    if (!ValidateHeader(fileSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate header");
        return false;
    }
    const uint32_t sectorSize = BigBlockSize();
    std::vector<uint32_t> fatBlocks;
    if (!LoadDifatChain(sectorSize, fatBlocks)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to load DIFAT chain");
        return false;
    }
    if (!LoadFatChain(sectorSize, fatBlocks)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to load FAT");
        return false;
    }
    SectorIndex sbStart = 0;
    if (!LoadDirectoryTree(sbStart)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to load directory tree");
        return false;
    }
    if (!LoadMiniFat(sbStart)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to load mini FAT");
        return false;
    }
    ClearError();
    return true;
}

bool StorageIO::ValidateHeader(uint64_t fileSize)
{
    if (!ReadAndLoadHeader()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to read header");
        return false;
    }
    uint32_t sectorSize = 0;
    if (!ValidateSectorSizes(sectorSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate sector sizes");
        return false;
    }
    if (!CheckClaimedTableSizes(fileSize, sectorSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to check claimed table sizes");
        return false;
    }
    if (!header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "header is null");
        return false;
    }
    if (!ValidateHeaderSectorIndex(header_->DirentStart(), sectorSize, "dirent_start", fileSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate dirent_start");
        return false;
    }
    if (!ValidateHeaderSectorIndex(header_->SbatStart(), sectorSize, "sbat_start", fileSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate sbat_start");
        return false;
    }
    if (!ValidateHeaderSectorIndex(header_->DifatStart(), sectorSize, "difatStart", fileSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate difatStart");
        return false;
    }
    return true;
}

bool StorageIO::LoadFatChain(uint32_t sectorSize, std::vector<uint32_t> &fatBlocks)
{
    const uint64_t bufLen64 = static_cast<uint64_t>(fatBlocks.size()) * static_cast<uint64_t>(sectorSize);
    if (bufLen64 > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "FAT buffer length overflow");
        return false;
    }
    const uint32_t bufLen = static_cast<uint32_t>(bufLen64);
    if (bufLen > 0) {
        std::vector<Byte> buffer(bufLen);
        const uint32_t bytesRead = LoadBigBlocks(fatBlocks, buffer.data(), bufLen);
        if (bytesRead != bufLen) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "Failed to read FAT sectors");
            return false;
        }
        if (bbat_) {
            bbat_->Load(buffer.data(), buffer.size());
        }
    }
    return true;
}

bool StorageIO::ReadDifatSector(uint32_t sectorSize, uint32_t difatSector,
    std::vector<uint8_t> &difatBuf)
{
    difatBuf.assign(sectorSize, 0xFF);
    const uint32_t bytesRead = LoadBigBlock(difatSector, difatBuf.data(), sectorSize);
    if (bytesRead != sectorSize) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Failed to load DIFAT sector");
        return false;
    }
    if (difatSectors_.empty() || difatSectors_.back() != difatSector) {
        difatSectors_.push_back(difatSector);
    }
    return true;
}

bool StorageIO::ProcessDifatEntries(uint32_t entriesPerSector, uint32_t expectedFromDifat,
    uint32_t expectedMaxSectors, uint32_t &loadedFromDifat,
    const std::vector<uint8_t> &difatBuf, std::vector<uint32_t> &fatBlocks)
{
    const uint32_t difatEntries = entriesPerSector > 0 ? entriesPerSector - 1 : 0;
    for (uint32_t i = 0; i < difatEntries && loadedFromDifat < expectedFromDifat; ++i) {
        if ((i + 1) * FOUR_BYTE_SIZE > difatBuf.size()) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "exceed difat buffer size");
            return false;
        }
        uint32_t entry = ReadUint32(difatBuf.data() + i * FOUR_BYTE_SIZE);
        if (entry == AllocTable::Avail) {
            continue;
        }
        if (expectedMaxSectors > 0 && entry >= expectedMaxSectors) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "DIFAT entry out of range");
            return false;
        }
        fatBlocks.push_back(entry);
        ++loadedFromDifat;
    }
    return true;
}

bool StorageIO::LoadDifatChainBody(uint32_t sectorSize, uint32_t expectedFromDifat,
    uint32_t expectedMaxSectors, std::vector<uint32_t> &fatBlocks)
{
    const uint32_t entriesPerSector = sectorSize / static_cast<uint32_t>(sizeof(uint32_t));
    if (!header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "header is null");
        return false;
    }
    uint32_t difatSector = header_->DifatStart();
    uint32_t loadedFromDifat = 0;
    std::set<uint32_t> visited;
    while (difatSector != AllocTable::Eof && loadedFromDifat < expectedFromDifat) {
        if (expectedMaxSectors > 0 && difatSector >= expectedMaxSectors) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "DIFAT sector out of range");
            return false;
        }
        if (!visited.insert(difatSector).second) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "DIFAT chain loop detected");
            return false;
        }

        std::vector<uint8_t> difatBuf;
        if (!ReadDifatSector(sectorSize, difatSector, difatBuf)) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to load DIFAT sector");
            return false;
        }
        if (!ProcessDifatEntries(entriesPerSector, expectedFromDifat,
            expectedMaxSectors, loadedFromDifat, difatBuf, fatBlocks)) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to process DIFAT entries");
            return false;
        }
        uint32_t nextDifat = (entriesPerSector > 0)
            ? ReadUint32(difatBuf.data() + (entriesPerSector - 1) * 4)
            : AllocTable::Eof;
        if (nextDifat == difatSector) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "DIFAT chain self-reference");
            return false;
        }
        if (expectedMaxSectors > 0 && nextDifat != AllocTable::Eof &&
            nextDifat >= expectedMaxSectors) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "DIFAT chain next sector out of range");
            return false;
        }
        difatSector = nextDifat;
    }

    if (loadedFromDifat < expectedFromDifat) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Incomplete DIFAT chain");
        return false;
    }

    return true;
}

bool StorageIO::LoadDifatChain(uint32_t sectorSize, std::vector<uint32_t> &fatBlocks)
{
    fatBlocks.clear();
    if (!header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "header is null");
        return false;
    }
    const BlockCount numBat = header_->NumBat();
    const BlockCount headerFatCount = std::min<BlockCount>(
        numBat, static_cast<BlockCount>(HEADER_DIFAT_ARRAY_SIZE));
    fatBlocks.reserve(static_cast<size_t>(numBat));
    for (uint32_t i = 0; i < headerFatCount; ++i) {
        fatBlocks.push_back(header_->BbBlocks()[i]);
    }

    const uint64_t fileSize = size_ >= 0 ? static_cast<uint64_t>(size_) : 0;
    uint32_t expectedMaxSectors = 0;
    if (sectorSize > 0 && fileSize >= sectorSize) {
        const uint64_t maxBlocks64 = fileSize / sectorSize;
        if (maxBlocks64 > 0) {
            expectedMaxSectors = static_cast<uint32_t>(std::min<uint64_t>(
                maxBlocks64 - 1, std::numeric_limits<uint32_t>::max()));
        }
    }
    if (numBat > headerFatCount) {
        const uint32_t expectedFromDifat = numBat - headerFatCount;
        if (!LoadDifatChainBody(sectorSize, expectedFromDifat, expectedMaxSectors, fatBlocks)) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to load DIFAT chain body");
            return false;
        }
    }
    fatSectors_ = fatBlocks;
    return true;
}

bool StorageIO::LoadDirectoryTree(SectorIndex &sbStart)
{
    std::vector<uint32_t> blocks;
    if (!bbat_ || !header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "header is null");
        return false;
    }
    if (!bbat_->Follow(header_->DirentStart(), blocks)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Failed to follow directory chain");
        return false;
    }

    const uint64_t bufLen64 = static_cast<uint64_t>(blocks.size()) *
        static_cast<uint64_t>(bbat_->BlockSize());
    if (bufLen64 > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Directory buffer length overflow");
        return false;
    }
    const uint32_t bufLen = static_cast<uint32_t>(bufLen64);
    std::vector<Byte> buffer(bufLen);
    const uint32_t bytesRead = LoadBigBlocks(blocks, buffer.data(), bufLen);
    if (bytesRead != bufLen) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Failed to read directory stream");
        return false;
    }
    if (!dirtree_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "dirtree is null");
        return false;
    }
    bool dirtreeLoaded = dirtree_->Load(buffer.data(), buffer.size());
    if (!dirtreeLoaded) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Failed to load directory tree");
        return false;
    }
    if (buffer.size() < DIR_ENTRY_SIZE_OFFSET) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Directory buffer too small to read sbStart");
        return false;
    }
    sbStart = ReadUint32(buffer.data() + 0x74);
    return true;
}

bool StorageIO::LoadMiniFat(SectorIndex sbStart)
{
    std::vector<uint32_t> blocks;
    std::vector<Byte> buffer;
    if (!LoadMiniFatBlocks(blocks, buffer)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to load mini FAT blocks");
        return false;
    }
    if (!FollowMiniStream(sbStart)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to follow mini FAT chain");
        return false;
    }
    size_t highestUsed = 0;
    if (!ValidateMiniFatEntries(highestUsed)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate mini FAT entries");
        return false;
    }
    return ValidateMiniRootCoverage(highestUsed);
}

bool StorageIO::LoadMiniFatBlocks(std::vector<uint32_t> &blocks, std::vector<Byte> &buffer)
{
    if (!bbat_ || !header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "header is null");
        return false;
    }
    if (!bbat_->Follow(header_->SbatStart(), blocks)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Failed to follow mini FAT chain");
        return false;
    }
    const uint64_t bufLen64 = static_cast<uint64_t>(blocks.size()) * static_cast<uint64_t>(bbat_->BlockSize());
    if (bufLen64 > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Mini FAT buffer length overflow");
        return false;
    }
    const uint32_t bufLen = static_cast<uint32_t>(bufLen64);
    if (bufLen == 0) {
        return true;
    }
    buffer.resize(bufLen);
    const uint32_t bytesRead = LoadBigBlocks(blocks, buffer.data(), bufLen);
    if (bytesRead != bufLen) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Failed to read mini FAT sectors");
        return false;
    }
    if (!sbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "sbat is null");
        return false;
    }
    sbat_->Load(buffer.data(), buffer.size());
    return true;
}

bool StorageIO::FollowMiniStream(SectorIndex sbStart)
{
    if (!bbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "bbat is null");
        return false;
    }
    if (!bbat_->Follow(sbStart, sbBlocks_)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Failed to follow mini FAT chain");
        return false;
    }
    return true;
}

bool StorageIO::ValidateMiniFatEntries(size_t &highestUsed)
{
    highestUsed = 0;
    if (!sbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "sbat is null");
        return false;
    }
    const size_t miniEntries = sbat_->Count();
    for (size_t i = 0; i < miniEntries; ++i) {
        const uint32_t entry = (*sbat_)[i];
        if (entry != AllocTable::Avail) {
            highestUsed = i + 1;
        }

        const bool isSpecial = (entry == AllocTable::Avail || entry == AllocTable::Eof ||
                                entry == AllocTable::Bat || entry == AllocTable::MetaBat);
        if (!isSpecial && entry >= miniEntries) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "Invalid mini FAT entry");
            return false;
        }
    }
    return true;
}

bool StorageIO::ValidateMiniRootCoverage(size_t highestUsed)
{
    if (highestUsed == 0) {
        return true;
    }
    const uint64_t requiredMiniBytes = static_cast<uint64_t>(highestUsed) * SmallBlockSize();
    const uint64_t bigBlockSz = BigBlockSize();
    if (bigBlockSz == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Invalid big block size");
        return false;
    }
    if (requiredMiniBytes > UINT64_MAX - bigBlockSz + 1) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "requiredMiniBytes overflow in rounding");
        return false;
    }
    const uint64_t requiredBigBlocks = (requiredMiniBytes + bigBlockSz - 1) / bigBlockSz;
    if (requiredBigBlocks > sbBlocks_.size()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Mini FAT reference exceeds available big blocks");
        return false;
    }
    return true;
}

bool StorageIO::Create(const char *filename)
{
    std::string canonicalFileName;
    if (!SystemUtils::ValidateAndNormalizePath(filename, canonicalFileName)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate and normalize path");
        return false;
    }
    auto f = std::make_unique<std::fstream>(canonicalFileName.c_str(), std::ios::binary | std::ios::out);
    if (!f || f->fail()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to open file");
        return false;
    }
    ClearError();
    file_ = std::move(f);
    stream_ = file_.get();
    return true;
}

void StorageIO::Close()
{
    const bool flushed = Flush();
    if (!flushed) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to flush file");
    }
    streams_.clear();
    if (file_) {
        file_->close();
        file_.reset();
        stream_ = nullptr;
    }
}

uint32_t StorageIO::ReadBigBlocksFromMemory(const std::vector<uint32_t> &blocks,
    Byte *data, uint32_t maxlen)
{
    if (!memoryBuffer_ || !bbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "memoryBuffer or bbat is null");
        return 0;
    }
    const uint32_t blockSize32 = bbat_->BlockSize();
    const uint64_t blockSize = blockSize32;
    uint32_t bytes = 0;
    for (uint32_t i = 0; (i < blocks.size()) && (bytes < maxlen); ++i) {
        const uint32_t block = blocks[i];
        const uint64_t pos = BlockToOffset(block, blockSize);
        const size_t bufferSize = memoryBuffer_->size();
        if (pos >= bufferSize) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "Block offset exceeds memory buffer size");
            return bytes;
        }
        uint32_t p = std::min<uint32_t>(blockSize32, maxlen - bytes);
        if (pos + p > bufferSize) {
            p = static_cast<uint32_t>(bufferSize - pos);
        }
        if (memcpy_s(data + bytes, static_cast<size_t>(maxlen - bytes),
            memoryBuffer_->data() + static_cast<size_t>(pos), p) != EOK) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "Failed to memcpy data from memory buffer");
            return 0;
        }
        bytes += p;
    }
    return bytes;
}

uint32_t StorageIO::ReadBigBlocksFromFile(const std::vector<uint32_t> &blocks,
    Byte *data, uint32_t maxlen)
{
    if (!stream_ || !stream_->good() || maxlen == 0 || blocks.empty() || !bbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "stream or bbat is null");
        return 0;
    }
    const uint32_t blockSize32 = bbat_->BlockSize();
    const uint64_t blockSize = blockSize32;
    if (blockSize32 == 0) {
        return 0;
    }
    if (file_) {
        file_->flush();
        if (auto *buf = file_->rdbuf()) {
            buf->pubsync();
        }
    }
    const uint64_t maxStreamOff = static_cast<uint64_t>(std::numeric_limits<std::streamoff>::max());
    const uint64_t fileSize = size_ >= 0 ? static_cast<uint64_t>(size_) : 0;

    uint32_t bytes = 0;
    for (uint32_t i = 0; (i < blocks.size()) && (bytes < maxlen); ++i) {
        const uint32_t block = blocks[i];
        if (block > maxStreamOff / blockSize) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "block overflow");
            return bytes;
        }
        const uint64_t pos = BlockToOffset(block, blockSize);
        if (pos > maxStreamOff) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Block offset overflow");
            return bytes;
        }
        if (pos >= fileSize) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Block offset exceeds file size");
            return bytes;
        }
        uint32_t p = std::min<uint32_t>(blockSize32, maxlen - bytes);
        const uint64_t endPos = pos + p;
        if (endPos > fileSize) {
            p = static_cast<uint32_t>(fileSize - pos);
        }
        stream_->seekg(static_cast<std::streamoff>(pos));
        stream_->read(reinterpret_cast<char *>(data) + bytes, p);
        const std::streamsize actualRead = stream_->gcount();
        if ((actualRead < static_cast<std::streamsize>(p)) && !stream_->eof()) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to read big block");
            return bytes + static_cast<uint32_t>(actualRead);
        }
        stream_->clear();
        bytes += static_cast<uint32_t>(actualRead);
    }
    return bytes;
}

uint32_t StorageIO::LoadBigBlocks(const std::vector<uint32_t> &blocks,
    Byte *data, uint32_t maxlen)
{
    if (!data || maxlen == 0 || blocks.empty()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid data or maxlen");
        return 0;
    }
    const uint32_t blockSize32 = bbat_ ? bbat_->BlockSize() : 0;
    if (blockSize32 == 0) {
        return 0;
    }
    if (memoryBuffer_) {
        return ReadBigBlocksFromMemory(blocks, data, maxlen);
    }
    return ReadBigBlocksFromFile(blocks, data, maxlen);
}

uint32_t StorageIO::LoadBigBlock(uint32_t block, Byte *data, uint32_t maxlen)
{
    std::lock_guard<std::recursive_mutex> lock(ioMutex_);
    if (!data) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid data");
        return 0;
    }
    std::vector<uint32_t> blocks;
    blocks.resize(1);
    blocks[0] = block;
    return LoadBigBlocks(blocks, data, maxlen);
}

bool StorageIO::ValidateMiniBlockAccess(uint32_t block, uint64_t pos64,
    uint64_t miniStreamSize, uint32_t bigBlockSz, uint32_t &bbindex)
{
    if (pos64 >= miniStreamSize) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "mini block offset exceeds miniStreamSize");
        return false;
    }
    if (pos64 > std::numeric_limits<uint64_t>::max() - bigBlockSz || bigBlockSz == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "mini block position overflow");
        return false;
    }
    
    bbindex = static_cast<uint32_t>(pos64 / bigBlockSz);
    if (bbindex >= sbBlocks_.size()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "mini block resolves outside Root stream");
        return false;
    }
    return true;
}

bool StorageIO::ReadMiniBlock(uint32_t block, uint64_t pos64, uint64_t miniStreamSize,
    uint32_t bigBlockSz, uint32_t smallBlockSz, uint32_t remaining, Byte *dest, uint32_t &copied)
{
    copied = 0;
    uint32_t bbindex = 0;
    if (!ValidateMiniBlockAccess(block, pos64, miniStreamSize, bigBlockSz, bbindex)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid mini block access");
        return false;
    }
    std::vector<Byte> buf(bigBlockSz);
    const uint32_t read = LoadBigBlock(sbBlocks_[bbindex], buf.data(), bigBlockSz);
    if (read != bigBlockSz || bigBlockSz == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Failed to read mini stream backing block");
        return false;
    }

    uint32_t offset = static_cast<uint32_t>(pos64 % bigBlockSz);
    uint32_t p = std::min(remaining, bigBlockSz - offset);
    if (p > smallBlockSz) {
        p = smallBlockSz;
    }
    if (pos64 > std::numeric_limits<uint64_t>::max() - p) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "mini block offset exceeds uint32_t max");
        return false;
    }
    const uint64_t endPos = pos64 + p;
    if (endPos > miniStreamSize) {
        if (miniStreamSize < pos64) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "mini stream truncated");
            return false;
        }
        p = static_cast<uint32_t>(miniStreamSize - pos64);
    }
    if (memcpy_s(dest, remaining, buf.data() + offset, p) != EOK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Failed to memcpy data from mini block");
        return false;
    }
    copied = p;
    return true;
}

uint32_t StorageIO::ReadMiniBlocks(const std::vector<uint32_t> &blocks, Byte *data, uint32_t maxlen)
{
    if (!data || maxlen == 0 || blocks.empty() || !bbat_ || !sbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid data or maxlen");
        return 0;
    }
    const uint32_t bigBlockSz = bbat_->BlockSize();
    const uint32_t smallBlockSz = sbat_->BlockSize();
    if (bigBlockSz == 0 || smallBlockSz == 0) {
        return 0;
    }
    const DirEntry *root = GetRootEntry();
    uint64_t miniStreamSize = root ? root->Size() : 0;
    if (miniStreamSize == 0 && !sbBlocks_.empty()) {
        if (static_cast<uint64_t>(sbBlocks_.size()) > UINT64_MAX / bigBlockSz) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "miniStreamSize overflow");
            return 0;
        }
        miniStreamSize = static_cast<uint64_t>(sbBlocks_.size()) * bigBlockSz;
    }
    uint32_t bytes = 0;
    for (uint32_t i = 0; (i < blocks.size()) && (bytes < maxlen); ++i) {
        const uint32_t block = blocks[i];
        if (block > UINT64_MAX / smallBlockSz) {
            return 0;
        }
        const uint64_t pos64 = static_cast<uint64_t>(block) * smallBlockSz;
        uint32_t copied = 0;
        const uint32_t remaining = maxlen - bytes;
        if (!ReadMiniBlock(block, pos64, miniStreamSize, bigBlockSz,
            smallBlockSz, remaining, data + bytes, copied)) {
            return bytes;
        }
        if (copied > maxlen - bytes) {
            return bytes;
        }
        bytes += copied;
    }
    return bytes;
}

uint32_t StorageIO::LoadSmallBlocks(const std::vector<uint32_t> &blocks, Byte *data, uint32_t maxlen)
{
    return ReadMiniBlocks(blocks, data, maxlen);
}

uint32_t StorageIO::LoadSmallBlock(uint32_t block, Byte *data, uint32_t maxlen)
{
    std::lock_guard<std::recursive_mutex> lock(ioMutex_);
    if (!data) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid data");
        return 0;
    }
    std::vector<uint32_t> blocks;
    blocks.resize(1);
    blocks.assign(1, block);
    return LoadSmallBlocks(blocks, data, maxlen);
}

uint32_t StorageIO::SaveBlockToFile(uint64_t physicalOffset, const Byte *data, uint32_t len)
{
    if (!file_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "file is null");
        return 0;
    }
    const uint64_t maxStreamOff = static_cast<uint64_t>(std::numeric_limits<std::streamoff>::max());
    const std::streamoff offset = static_cast<std::streamoff>(physicalOffset);
    file_->clear();
    file_->seekp(offset);
    if (!file_->good()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to seek to offset");
        return 0;
    }
    file_->write(reinterpret_cast<const char *>(data), static_cast<std::streamsize>(len));
    if (!file_->good()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to write block");
        return 0;
    }
    // Flush only when necessary
    writeBufferSize_ += len;
    if (writeBufferSize_ >= MAX_BUFFER_SIZE) {
        file_->flush();
        if (!file_->good()) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to flush file");
            return 0;
        }
        if (auto *buf = file_->rdbuf()) {
            if (buf != nullptr && buf->pubsync() == -1) {
                return 0;
            }
        } else {
            return 0;
        }
        writeBufferSize_ = 0;
    }
    const auto endPos = file_->tellp();
    if (endPos == static_cast<std::streampos>(-1)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to tellp");
        return 0;
    }
    file_->seekg(endPos);
    if (!file_->good()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to seek to end of file");
        return 0;
    }
    if (static_cast<uint64_t>(len) > maxStreamOff - physicalOffset) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to write block");
        return 0;
    }
    const uint64_t newEnd64 = physicalOffset + static_cast<uint64_t>(len);
    if (static_cast<std::streamoff>(newEnd64) > size_) {
        const uint64_t clamped = std::min<uint64_t>(newEnd64, maxStreamOff);
        size_ = static_cast<std::streamoff>(clamped);
    }
    return len;
}

uint32_t StorageIO::SaveBlockToBuffer(uint64_t physicalOffset, const Byte *data, uint32_t len,
    std::vector<uint8_t> &buffer)
{
    if (physicalOffset > UINT64_MAX - len) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "physicalOffset overflow");
        return 0;
    }
    const uint64_t requiredSize = physicalOffset + static_cast<uint64_t>(len);
    if (requiredSize > buffer.max_size()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "buffer size exceeds max_size");
        return 0;
    }
    if (requiredSize > buffer.size()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "buffer size overflow");
        buffer.resize(static_cast<size_t>(requiredSize), 0);
    }
    const size_t offset = static_cast<size_t>(physicalOffset);
    if (len > buffer.size() - offset) {
        return 0;
    }
    auto ec = memcpy_s(buffer.data() + offset, buffer.size() - offset, data, len);
    if (ec != EOK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to memcpy data to buffer");
        return 0;
    }
    return len;
}

void StorageIO::ListDirectory(std::list<std::string> &result) const
{
    if (!dirtree_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "dirtree is null");
        return;
    }
    std::vector<const DirEntry *> entries;
    dirtree_->ListDirectory(entries);
    for (size_t i = 0; i < entries.size(); i++) {
        if (entries[i]) {
            result.push_back(entries[i]->Name());
        }
    }
}

void StorageIO::ListEntries(std::vector<const DirEntry *> &result) const
{
    if (!dirtree_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "dirtree is null");
        return;
    }
    dirtree_->ListDirectory(result);
}

DirEntry *StorageIO::Entry(const std::string &path, bool create)
{
    std::lock_guard<std::recursive_mutex> lock(ioMutex_);
    DirEntry *result = dirtree_ ? dirtree_->Entry(path, create) : nullptr;
    if (create && result) {
        dtModified_ = true;
    }
    return result;
}

DirEntry *StorageIO::GetRootEntry()
{
    std::lock_guard<std::recursive_mutex> lock(ioMutex_);
    return dirtree_ ? dirtree_->GetEntryAt(0) : nullptr;
}

uint32_t StorageIO::SaveBlock(uint64_t physicalOffset, const Byte *data, uint32_t len)
{
    std::lock_guard<std::recursive_mutex> lock(ioMutex_);
    if (!data || len == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid data or len");
        return 0;
    }
    const uint64_t maxStreamOff = static_cast<uint64_t>(std::numeric_limits<std::streamoff>::max());
    if (physicalOffset > maxStreamOff) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "physicalOffset overflow");
        return 0;
    }
    if (file_) {
        return SaveBlockToFile(physicalOffset, data, len);
    }
    if (flushWriteBuffer_) {
        return SaveBlockToBuffer(physicalOffset, data, len, *flushWriteBuffer_);
    }
    if (memoryBuffer_) {
        const uint32_t written = SaveBlockToBuffer(physicalOffset, data, len, *memoryBuffer_);
        if (written > 0) {
            const uint64_t requiredSize = physicalOffset + static_cast<uint64_t>(written);
            const std::streamoff newEnd = static_cast<std::streamoff>(requiredSize);
            if (newEnd > size_) {
                size_ = newEnd;
            }
        }
        return written;
    }
    return 0;
}

bool StorageIO::WriteBlockChecked(uint64_t physicalOffset, const Byte *data,
    uint32_t len, const std::string &context)
{
    const uint32_t written = SaveBlock(physicalOffset, data, len);
    if (written != len) {
        if (error_ == ErrorCode::Ok) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "Failed to write block");
        }
        const std::string &errMessage = !errorMsg_.empty()
            ? errorMsg_
            : (context.empty() ? std::string("Write Failed") : context);
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Write Failed");
        return false;
    }
    return true;
}

bool StorageIO::ValidateConvertInputs(DirEntry *entry, uint64_t newSize, uint64_t &oldSize, uint32_t &blockSize,
    uint32_t &threshold)
{
    if (!entry || !bbat_ || !sbat_ || !header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Invalid inputs");
        return false;
    }
    oldSize = entry->Size();
    threshold = header_->Threshold();
    if (oldSize >= threshold) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Entry size exceeds threshold");
        return false;
    }
    if (newSize < threshold || newSize < oldSize) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "New size is smaller than threshold");
        return false;
    }
    blockSize = BigBlockSize();
    if (blockSize == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "Invalid block size");
        return false;
    }
    return true;
}

bool StorageIO::ReadMiniStream(DirEntry *entry, uint64_t oldSize, std::vector<uint32_t> &oldBlocks,
    std::vector<Byte> &smallBuffer)
{
    if (!entry) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid entry");
        return false;
    }
    if (!FollowSmallBlockTable(entry->Start(), oldBlocks)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to follow small block table");
        return false;
    }
    if (oldSize > 0 && oldBlocks.empty()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to read old blocks");
        return false;
    }
    if (oldSize == 0) {
        return true;
    }
    if (oldSize > std::numeric_limits<uint32_t>::max()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "oldSize exceeds uint32_t limit");
        return false;
    }
    smallBuffer.resize(static_cast<size_t>(oldSize));
    const uint32_t bytesRead = LoadSmallBlocks(oldBlocks, smallBuffer.data(), static_cast<uint32_t>(oldSize));
    if (bytesRead < static_cast<uint32_t>(oldSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to read old blocks");
        return false;
    }
    return true;
}

bool StorageIO::ComputeBlocksNeeded(uint64_t newSize, uint32_t blockSize, uint32_t &blocksNeeded)
{
    if (blockSize == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid block size");
        return false;
    }
    uint64_t blocksNeeded64 = (newSize + blockSize - 1) / blockSize;
    if (blocksNeeded64 == 0) {
        blocksNeeded64 = 1;
    }
    if (blocksNeeded64 > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "blocksNeeded overflow");
        return false;
    }
    blocksNeeded = static_cast<uint32_t>(blocksNeeded64);
    return true;
}

bool StorageIO::AllocateBigBlocksForConvert(uint32_t blocksNeeded, std::vector<uint32_t> &newBlocks)
{
    if (!bbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "bbat is null");
        return false;
    }
    newBlocks.reserve(static_cast<size_t>(blocksNeeded));
    for (uint32_t i = 0; i < blocksNeeded; i++) {
        const uint32_t newBlock = static_cast<uint32_t>(bbat_->Unused());
        newBlocks.push_back(newBlock);
        bbat_->Set(newBlock, AllocTable::Eof);
    }

    if (newBlocks.empty()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to allocate big blocks");
        return false;
    }
    bbat_->SetChain(newBlocks);
    return true;
}

std::vector<Byte> BuildBatchBuffer(uint64_t &batchStartIdx, uint64_t &batchEndIdx, uint64_t newSize,
    uint32_t blockSize, const std::vector<Byte> &smallBuffer, size_t &copied, uint32_t &batchWriteLen,
    uint64_t &lastWrittenIdx, bool &exhausted)
{
    std::vector<Byte> batchBuffer;
    for (size_t idx = batchStartIdx; idx <= batchEndIdx; ++idx) {
        const uint64_t offsetInStream = idx * static_cast<uint64_t>(blockSize);
        uint64_t remaining = newSize > offsetInStream ? newSize - offsetInStream : 0;
        const uint32_t chunk = static_cast<uint32_t>(std::min<uint64_t>(remaining, blockSize));
        if (chunk == 0) {
            exhausted = true;
            break;
        }
        const size_t blockOffset = (idx - batchStartIdx) * static_cast<size_t>(blockSize);
        const size_t required = blockOffset + static_cast<size_t>(chunk);
        if (required > batchBuffer.size()) {
            batchBuffer.resize(required, 0);
        }
        const size_t remainingOldData = copied < smallBuffer.size() ? smallBuffer.size() - copied : 0;
        const size_t dataToWrite = std::min(static_cast<size_t>(chunk), remainingOldData);
        if (dataToWrite > 0) {
            auto ec = memcpy_s(batchBuffer.data() + blockOffset, batchBuffer.size() - blockOffset,
                smallBuffer.data() + copied, dataToWrite);
            if (ec != EOK) {
                break;
            }
            copied += dataToWrite;
        }
        batchWriteLen += static_cast<uint32_t>(required);
        lastWrittenIdx = idx;
    }
    return batchBuffer;
}

bool StorageIO::CopyDataToBigBlocks(const std::vector<uint32_t> &blocks, const std::vector<Byte> &smallBuffer,
    uint64_t newSize, uint32_t blockSize)
{
    size_t copied = 0;
    size_t batchStartIdx = 0;
    bool exhausted = false;
    while (batchStartIdx < blocks.size() && !exhausted) {
        size_t batchEndIdx = batchStartIdx;
        while (batchEndIdx + 1 < blocks.size() && blocks[batchEndIdx + 1] == blocks[batchEndIdx] + 1) {
            ++batchEndIdx;
        }
        const size_t contiguousCount = batchEndIdx - batchStartIdx + 1;
        if (blockSize == 0) {
            return false;
        }
        const size_t maxBatchBlocks = std::max<size_t>(1, std::numeric_limits<uint32_t>::max() / blockSize);
        const size_t batchCount = std::min<size_t>(contiguousCount, maxBatchBlocks);
        if (batchStartIdx + batchCount - 1 < batchEndIdx) {
            batchEndIdx = batchStartIdx + batchCount - 1;
        }
        std::vector<Byte> batchBuffer;
        batchBuffer.reserve(batchCount * static_cast<size_t>(blockSize));
        uint32_t batchWriteLen = 0;
        size_t lastWrittenIdx = batchStartIdx;

        uint64_t batchStartIdx64 = batchStartIdx;
        uint64_t batchEndIdx64 = batchEndIdx;
        uint64_t lastWrittenIdx64 = lastWrittenIdx;
        batchBuffer = BuildBatchBuffer(batchStartIdx64, batchEndIdx64, newSize, blockSize, smallBuffer,
            copied, batchWriteLen, lastWrittenIdx64, exhausted);
        lastWrittenIdx = static_cast<size_t>(lastWrittenIdx64);
        if (batchBuffer.empty()) {
            return false;
        }
        
        if (batchWriteLen > 0) {
            const uint64_t physicalOffset = BlockToOffset(blocks[batchStartIdx], blockSize);
            const std::string context = "converted blocks " + std::to_string(blocks[batchStartIdx]) + "-" +
                std::to_string(blocks[lastWrittenIdx]);
            if (!WriteBlockChecked(physicalOffset, batchBuffer.data(), batchWriteLen, context)) {
                return false;
            }
        }

        batchStartIdx = exhausted ? blocks.size() : batchEndIdx + 1;
    }
    return true;
}

void StorageIO::ReleaseMiniBlocks(const std::vector<uint32_t> &oldBlocks)
{
    if (!sbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "sbat is null");
        return;
    }
    for (uint32_t block : oldBlocks) {
        sbat_->Set(block, AllocTable::Avail);
    }
}

bool StorageIO::ConvertSmallToBig(DirEntry *entry, uint64_t newSize)
{
    uint64_t oldSize = 0;
    uint32_t blockSize = 0;
    uint32_t threshold = 0;

    if (!ValidateConvertInputs(entry, newSize, oldSize, blockSize, threshold)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate convert inputs");
        return false;
    }
    std::vector<uint32_t> oldBlocks;
    std::vector<Byte> smallBuffer;
    if (!ReadMiniStream(entry, oldSize, oldBlocks, smallBuffer)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to read mini stream");
        return false;
    }
    uint32_t blocksNeeded = 0;
    if (!ComputeBlocksNeeded(newSize, blockSize, blocksNeeded)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to compute blocks needed");
        return false;
    }
    std::vector<uint32_t> newBlocks;
    if (!AllocateBigBlocksForConvert(blocksNeeded, newBlocks)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to allocate big blocks for convert");
        return false;
    }
    if (!bbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "bbat is null");
        return false;
    }
    auto releaseNewBlocks = [&]() {
        for (uint32_t block : newBlocks) {
            bbat_->Set(block, AllocTable::Avail);
        }
    };
    if (!CopyDataToBigBlocks(newBlocks, smallBuffer, newSize, blockSize)) {
        releaseNewBlocks();
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to copy data to big blocks");
        return false;
    }
    if (!entry) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid entry");
        return false;
    }
    ReleaseMiniBlocks(oldBlocks);
    entry->SetStart(newBlocks.front());
    entry->SetSize(newSize);
    dtModified_ = true;
    ClearError();
    return true;
}

bool StorageIO::ValidateFlushState(bool &memoryMode, size_t &blockSize)
{
    if (!IsValid()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Object is in invalid state");
        return false;
    }
    if (!header_ || !bbat_ || !dirtree_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Alloc tables are not initialized");
        return false;
    }
    memoryMode = (memoryBuffer_ != nullptr && file_ == nullptr);
    blockSize = static_cast<size_t>(BigBlockSize());
    if (blockSize == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Block size is 0");
        return false;
    }
    return true;
}

bool StorageIO::PrepareDirectoryBlocks(size_t blockSize, size_t dirEntries, std::vector<uint32_t> &blocks,
    size_t &neededBlocks)
{
    if (blockSize == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid block size");
        return false;
    }
    if (dirEntries > SIZE_MAX / kDirectoryEntrySize) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "dirEntries overflow");
        return false;
    }
    const size_t neededBytes = dirEntries * kDirectoryEntrySize;
    neededBlocks = (neededBytes + blockSize - 1) / blockSize;
    if (neededBlocks == 0) {
        neededBlocks = 1;
    }
    if (!bbat_ || !header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid inputs");
        return false;
    }
    if (!bbat_->Follow(header_->DirentStart(), blocks)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to follow dirent start");
        return false;
    }
    for (uint32_t block : blocks) {
        if (block >= bbat_->Count()) {
            continue;
        }
        if ((*bbat_)[block] == AllocTable::Avail) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Block is not available");
            return false;
        }
    }
    return true;
}

void StorageIO::BackupFlushState(FlushSnapshot &snap)
{
    if (header_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "header is null");
        return;
    }
    snap.headerBackup = *header_;
    snap.fatSectorsBackup = fatSectors_;
    snap.difatSectorsBackup = difatSectors_;
    snap.protectedBlocksCacheBackup = protectedBlocksCache_;
    snap.protectedBlocksDirtyBackup = protectedBlocksDirty_;
    snap.sbBlocksBackup = sbBlocks_;
    snap.sizeBackup = size_;
    snap.prevFlushWriteBuffer = flushWriteBuffer_;
    if (bbat_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "bbat is null");
        return;
    }
    snap.bbatSnapshot.reserve(bbat_->Count());
    for (size_t i = 0; i < bbat_->Count(); i++) {
        snap.bbatSnapshot.push_back((*bbat_)[i]);
    }
}

void StorageIO::RestoreFlushState(const FlushSnapshot &snap)
{
    flushWriteBuffer_ = snap.prevFlushWriteBuffer;
    if (bbat_) {
        bbat_->Resize(snap.bbatSnapshot.size());
        for (size_t i = 0; i < snap.bbatSnapshot.size(); i++) {
            bbat_->Set(i, snap.bbatSnapshot[i]);
        }
    }
    fatSectors_ = snap.fatSectorsBackup;
    difatSectors_ = snap.difatSectorsBackup;
    protectedBlocksCache_ = snap.protectedBlocksCacheBackup;
    protectedBlocksDirty_ = snap.protectedBlocksDirtyBackup;
    sbBlocks_ = snap.sbBlocksBackup;
    size_ = snap.sizeBackup;
    if (header_) {
        *header_ = snap.headerBackup;
    }
}

bool StorageIO::SetupStagingBuffer(bool memoryMode, FlushSnapshot &snap)
{
    if (memoryMode && memoryBuffer_) {
        snap.stagingMemory = *memoryBuffer_;
        flushWriteBuffer_ = &snap.stagingMemory;
    }
    return true;
}

bool StorageIO::ExecuteFlushSequence(std::vector<uint32_t> &blocks, size_t neededBlocks, size_t blockSize)
{
    if (!FlushDirectoryTree(blocks, neededBlocks, blockSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to flush directory tree");
        return false;
    }
    if (!FlushMiniFat()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to flush mini fat");
        return false;
    }
    if (!FlushFatChain()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to flush fat chain");
        return false;
    }
    if (!FlushDifatChain()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to flush difat chain");
        return false;
    }
    return FlushHeader();
}

bool StorageIO::FinalizeFlush(bool memoryMode, FlushSnapshot &snap, size_t blockSize)
{
    size_t maxUsedBlock = 0;
    if (bbat_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "bbat is null");
        return false;
    }
    for (size_t i = 0; i < bbat_->Count(); i++) {
        if ((*bbat_)[i] != AllocTable::Avail) {
            maxUsedBlock = i;
        }
    }
    if (maxUsedBlock > SIZE_MAX - FLUSH_EXTRA_SIZE) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "maxUsedBlock overflow in size computation");
        return false;
    }
    if (blockSize == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "blockSize less 0");
        return false;
    }
    if (maxUsedBlock + FLUSH_EXTRA_SIZE > SIZE_MAX / blockSize) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "computedSize overflow");
        return false;
    }
    const size_t computedSize = (maxUsedBlock + FLUSH_EXTRA_SIZE) * blockSize;
    const std::streamoff computedSizeOff = static_cast<std::streamoff>(
        std::min<size_t>(computedSize, static_cast<size_t>(std::numeric_limits<std::streamoff>::max())));

    if (file_) {
        file_->seekg(0, std::ios::end);
        size_ = file_->tellg();
    } else if (memoryMode && memoryBuffer_) {
        size_ = computedSizeOff;
        if (difatSectors_.empty() && snap.stagingMemory.size() != computedSize) {
            snap.stagingMemory.resize(computedSize, 0);
        }
        auto newBuffer = std::make_unique<std::vector<uint8_t>>(std::move(snap.stagingMemory));
        memoryBuffer_.swap(newBuffer);
    } else if (memoryBuffer_) {
        size_ = computedSizeOff;
        if (difatSectors_.empty() && memoryBuffer_->size() != computedSize) {
            memoryBuffer_->resize(computedSize, 0);
        }
    }

    flushWriteBuffer_ = nullptr;
    return true;
}

bool StorageIO::ValidateDeletePreconditions(uint32_t &threshold, uint32_t &miniBlockSize, uint32_t &bigBlockSize)
{
    if (!IsValid()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Storage is not valid");
        return false;
    }
    if (!dirtree_ || !header_ || !bbat_ || !sbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Storage is not valid");
        return false;
    }
    threshold = header_->Threshold();
    miniBlockSize = SmallBlockSize();
    bigBlockSize = BigBlockSize();
    if (bigBlockSize == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid block size");
        return false;
    }
    return true;
}

bool StorageIO::CollectDeleteTargets(const std::string &path, std::vector<DirEntry> &targets)
{
    if (!dirtree_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "dirtree is null");
        return false;
    }
    if (dirtree_->CollectSubtreeEntries(path, targets)) {
        return true;
    }
    OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid path: %{private}s", path.c_str());
    return false;
}

bool StorageIO::CollectChainForEntry(const DirEntry &entry, bool useBig, uint32_t miniBlockSize,
    uint32_t bigBlockSize, std::vector<uint32_t> &out)
{
    AllocTable *table = useBig ? bbat_.get() : sbat_.get();
    if (table == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "bbat or sbat is null");
        SetError(ErrorCode::InvalidOperation, "Cannot delete: storage not initialized", false);
        return false;
    }
    const uint32_t start = entry.Start();
    if (start == DIR_ENTRY_END || start == AllocTable::Eof || start == AllocTable::Avail ||
        start == AllocTable::Bat || start == AllocTable::MetaBat) {
        return true;
    }
    if (start >= table->Count()) {
        SetError(ErrorCode::Corruption, "Cannot delete: start block out of range");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "start block out of range");
        return false;
    }
    if ((*table)[start] == AllocTable::Avail) {
        return true;
    }
    if (!table->Follow(start, out)) {
        SetError(ErrorCode::Corruption, "Failed to follow allocation chain");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "failed to follow allocation chain");
        return false;
    }
    if (out.empty()) {
        return true;
    }
    const uint32_t blockSize = useBig ? bigBlockSize : miniBlockSize;
    if (blockSize == 0) {
        SetError(ErrorCode::InvalidOperation, "Cannot delete: block size is 0", false);
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "block size is 0");
        return false;
    }
    if (entry.Size() && (static_cast<uint64_t>(out.size()) * blockSize) < entry.Size()) {
        SetError(ErrorCode::Corruption, "Cannot delete: allocation chain is too short");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "allocation chain is too short");
        return false;
    }
    if (useBig) {
        for (uint32_t block : out) {
            if (protectedBlocksCache_.count(block)) {
                SetError(ErrorCode::InvalidOperation, "Cannot delete: protected block", false);
                OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "protected block");
                return false;
            }
        }
    }
    return true;
}

bool StorageIO::BuildReleasePlan(const std::vector<DirEntry> &targets,
    uint32_t threshold, uint32_t miniBlockSize, uint32_t bigBlockSize,
    std::vector<std::pair<bool, std::vector<uint32_t>>> &plan)
{
    plan.reserve(targets.size());
    for (const DirEntry &entry : targets) {
        if (!entry.IsFile() || entry.Size() == 0) {
            continue;
        }
        const bool useBig = !(entry.Size() < threshold);
        std::vector<uint32_t> blocks;
        if (!CollectChainForEntry(entry, useBig, miniBlockSize, bigBlockSize, blocks)) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to collect chain");
            return false;
        }
        if (!blocks.empty()) {
            plan.emplace_back(useBig, std::move(blocks));
        }
    }
    return true;
}

void StorageIO::ExecuteBlockRelease(const std::vector<std::pair<bool, std::vector<uint32_t>>> &plan)
{
    for (const auto &item : plan) {
        AllocTable *table = item.first ? bbat_.get() : sbat_.get();
        for (uint32_t block : item.second) {
            if (table) {
                table->Set(block, AllocTable::Avail);
            }
        }
    }
}

bool StorageIO::DeleteEntry(const std::string &path)
{
    std::lock_guard<std::recursive_mutex> lock(ioMutex_);
    uint32_t threshold = 0;
    uint32_t miniBlockSize = 0;
    uint32_t bigBlockSize = 0;
    if (!ValidateDeletePreconditions(threshold, miniBlockSize, bigBlockSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate delete preconditions");
        return false;
    }
    std::vector<DirEntry> targets;
    if (!CollectDeleteTargets(path, targets)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to collect delete targets");
        return false;
    }
    RebuildProtectedBlocksCache();

    std::vector<std::pair<bool, std::vector<uint32_t>>> plan;
    if (!BuildReleasePlan(targets, threshold, miniBlockSize, bigBlockSize, plan)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to build release plan");
        return false;
    }
    if (dirtree_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "dirtree is null");
        return false;
    }
    if (!dirtree_->DeleteEntry(path)) {
        SetError(ErrorCode::InvalidOperation, "Cannot delete: failed to delete entry", false);
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "failed to delete entry");
        return false;
    }
    ExecuteBlockRelease(plan);
    dtModified_ = true;
    return Flush();
}

bool StorageIO::ValidateExtendPreconditions(DirEntry *entry)
{
    if (!IsValid()) {
        SetError(ErrorCode::InvalidOperation, "Cannot extend: storage not initialized", false);
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "storage not initialized");
        return false;
    }

    if (!entry || !bbat_ || !header_ || !sbat_) {
        SetError(ErrorCode::InvalidOperation, "Cannot extend: entry or alloc table is null", false);
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "entry or alloc table is null");
        return false;
    }
    return true;
}

bool StorageIO::NeedsBigBlocks(uint64_t size) const
{
    return header_ && size >= header_->Threshold();
}

bool StorageIO::ValidateAndFetchExistingChain(DirEntry *entry, bool useBig,
    std::vector<uint32_t> &existingBlocks)
{
    if (entry == nullptr) {
        SetError(ErrorCode::InvalidOperation, "Cannot extend: entry is null", false);
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "entry is null");
        return false;
    }
    if (useBig) {
        if (!FollowBigBlockTable(entry->Start(), existingBlocks)) {
            SetError(ErrorCode::Corruption,
                "Cannot extend: failed to follow big block chain");
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "failed to follow big block chain");
            return false;
        }
        return true;
    }
    if (!FollowSmallBlockTable(entry->Start(), existingBlocks)) {
        SetError(ErrorCode::Corruption,
            "Cannot extend: failed to follow small block chain");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "failed to follow small block chain");
        return false;
    }
    return true;
}

bool StorageIO::EnsureRootForMiniGrowth(uint32_t requiredMiniBlocks, const std::string &logContext)
{
    if (ExtendRootStream(requiredMiniBlocks)) {
        return true;
    }
    SetError(ErrorCode::Corruption, "Cannot extend root stream for mini growth");
    OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
        "Failed to extend root stream for mini growth");
    return false;
}

bool StorageIO::ExtendSameTypeChain(DirEntry *entry, uint64_t oldSize, uint64_t newSize, bool useBig)
{
    const uint32_t blockSize = useBig ? BigBlockSize() : SmallBlockSize();
    AllocTable *allocTable = useBig ? bbat_.get() : sbat_.get();
    if (blockSize == 0 || allocTable == nullptr) {
        SetError(ErrorCode::InvalidOperation, "Cannot extend: alloc table is null", false);
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "alloc table is null");
        return false;
    }
    const uint64_t blockSize64 = blockSize;
    const uint64_t oldBlocksNeeded64 = (oldSize + blockSize64 - 1) / blockSize64;
    const uint64_t newBlocksNeeded64 = (newSize + blockSize64 - 1) / blockSize64;
    const uint64_t maxBlocks = static_cast<uint64_t>(std::numeric_limits<uint32_t>::max());
    if (oldBlocksNeeded64 > maxBlocks || newBlocksNeeded64 > maxBlocks) {
        SetError(ErrorCode::InvalidOperation, "Cannot extend: requested size exceeds FAT addressing range");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "requested size exceeds FAT addressing range");
        return false;
    }
    const uint32_t oldBlocksNeeded = static_cast<uint32_t>(oldBlocksNeeded64);
    const uint32_t newBlocksNeeded = static_cast<uint32_t>(newBlocksNeeded64);
    if (entry == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "entry is null");
        return false;
    }
    if (newBlocksNeeded <= oldBlocksNeeded) {
        entry->SetSize(newSize);
        dtModified_ = true;
        ClearError();
        return true;
    }

    std::vector<uint32_t> existingBlocks;
    if (!ValidateAndFetchExistingChain(entry, useBig, existingBlocks)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate existing chain");
        return false;
    }
    if (existingBlocks.empty()) {
        SetError(ErrorCode::Corruption, "Cannot extend: existing block chain is empty");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "existing block chain is empty");
        return false;
    }
    if (existingBlocks.size() < oldBlocksNeeded) {
        SetError(ErrorCode::Corruption, "Cannot extend: existing block chain is shorter than expected (expected " +
            std::to_string(oldBlocksNeeded) + ", got " + std::to_string(existingBlocks.size()) + ")");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "existing block chain is shorter than expected");
        return false;
    }
    const uint32_t additionalBlocks = newBlocksNeeded - oldBlocksNeeded;

    if (!useBig) {
        uint64_t highestMiniBlock = 0;
        if (sbat_ == nullptr) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "sbat is null");
            return false;
        }
        for (size_t i = 0; i < sbat_->Count(); ++i) {
            if ((*sbat_)[i] != AllocTable::Avail) {
                highestMiniBlock = i + 1;
            }
        }
        if (highestMiniBlock > std::numeric_limits<uint32_t>::max() - additionalBlocks) {
            SetError(ErrorCode::InvalidOperation, "Cannot extend: block count overflow");
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "block count overflow");
            return false;
        }
        highestMiniBlock += additionalBlocks;
        if (!EnsureRootForMiniGrowth(static_cast<uint32_t>(highestMiniBlock),
            "Failed to extend root stream when growing small stream")) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to extend root stream");
            return false;
        }
    }

    for (uint32_t i = 0; i < additionalBlocks; ++i) {
        const uint32_t newBlock = allocTable->Unused();
        allocTable->Set(existingBlocks.back(), newBlock);
        allocTable->Set(newBlock, AllocTable::Eof);
        existingBlocks.push_back(newBlock);
    }
    entry->SetSize(newSize);
    dtModified_ = true;
    ClearError();
    return true;
}

bool StorageIO::AllocateFreshChain(DirEntry *entry, uint64_t newSize, bool useBig)
{
    const uint32_t blockSize = useBig ? BigBlockSize() : SmallBlockSize();
    AllocTable *allocTable = useBig ? bbat_.get() : sbat_.get();
    if (blockSize == 0 || allocTable == nullptr) {
        SetError(ErrorCode::InvalidOperation, "Cannot extend: invalid block size", false);
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid block size");
        return false;
    }
    uint64_t blocksNeeded64 = (newSize + blockSize - 1) / blockSize;
    if (blocksNeeded64 == 0)
        blocksNeeded64 = 1;
    if (blocksNeeded64 > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
        SetError(ErrorCode::InvalidOperation, "Cannot allocate: block count overflow");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "block count overflow");
        return false;
    }
    const uint32_t blocksNeeded = static_cast<uint32_t>(blocksNeeded64);

    if (!useBig) {
        uint64_t totalMiniBlocks = 0;
        for (size_t i = 0; i < sbat_->Count(); ++i) {
            if ((*sbat_)[i] != AllocTable::Avail)
                totalMiniBlocks = i + 1;
        }
        totalMiniBlocks += blocksNeeded;
        if (totalMiniBlocks > std::numeric_limits<uint32_t>::max()) {
            SetError(ErrorCode::InvalidOperation, "Cannot extend: block count overflow");
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "block count overflow");
            return false;
        }
        if (!EnsureRootForMiniGrowth(static_cast<uint32_t>(totalMiniBlocks),
            "Failed to extend root stream for " + std::to_string(blocksNeeded) + " mini blocks")) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to extend root stream");
            return false;
        }
    }
    std::vector<uint32_t> blocks;
    blocks.reserve(blocksNeeded);
    for (uint32_t i = 0; i < blocksNeeded; ++i) {
        const uint32_t block = allocTable->Unused();
        blocks.push_back(block);
        allocTable->Set(block, AllocTable::Eof);
    }
    allocTable->SetChain(blocks);
    if (blocks.empty()) {
        SetError(ErrorCode::AllocationFailed, "Cannot allocate: no more free blocks");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "no more free blocks");
        return false;
    }
    if (entry == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "entry is null");
        return false;
    }
    entry->SetStart(blocks[0]);
    entry->SetSize(newSize);
    dtModified_ = true;
    ClearError();
    return true;
}

bool StorageIO::ExtendEntry(DirEntry *entry, uint64_t newSize)
{
    std::lock_guard<std::recursive_mutex> lock(ioMutex_);
    if (!ValidateExtendPreconditions(entry)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate extend preconditions");
        return false;
    }
    const uint64_t oldSize = entry->Size();
    const bool isFirstAllocation = (oldSize == 0);
    const bool oldUseBigBlocks = NeedsBigBlocks(oldSize);
    const bool newUseBigBlocks = NeedsBigBlocks(newSize);
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT,
        "ExtendEntry state, isFirstAllocation: %{public}d, oldUseBigBlocks: %{public}d, newUseBigBlocks: %{public}d",
        isFirstAllocation, oldUseBigBlocks, newUseBigBlocks);
    if (!isFirstAllocation && (oldUseBigBlocks != newUseBigBlocks)) {
        if (!oldUseBigBlocks && newUseBigBlocks) {
            OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "Convert from small to big blocks");
            const bool converted = ConvertSmallToBig(entry, newSize);
            if (converted) {
                ClearError();
            }
            return converted;
        }
        SetError(ErrorCode::InvalidOperation, "Cannot extend: cannot switch between big and small blocks", false);
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "BIG to SMALL is not supported");
        return false;
    }
    if (!isFirstAllocation)
        return ExtendSameTypeChain(entry, oldSize, newSize, newUseBigBlocks);
    return AllocateFreshChain(entry, newSize, newUseBigBlocks);
}

void StorageIO::AddBlocksFromChain(uint32_t start)
{
    if (bbat_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "BBAT is null");
        return;
    }
    std::vector<uint32_t> chain;
    if (bbat_->Follow(start, chain)) {
        for (uint32_t block : chain) {
            protectedBlocksCache_.insert(block);
        }
    }
}

void StorageIO::RebuildProtectedBlocksCache()
{
    if (!protectedBlocksDirty_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "protectedBlocks");
        return;
    }
    std::set<uint32_t> carry = protectedBlocksCache_;
    protectedBlocksCache_.clear();
    if (!bbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "BBAT is not initialized");
        protectedBlocksDirty_ = false;
        return;
    }

    if (header_) {
        AddBlocksFromChain(header_->DirentStart());
    }
    
    for (uint32_t fatSector : fatSectors_) {
        protectedBlocksCache_.insert(fatSector);
    }

    for (uint32_t difatSector : difatSectors_) {
        protectedBlocksCache_.insert(difatSector);
    }

    if (header_ && header_->SbatStart() != AllocTable::Eof) {
        AddBlocksFromChain(header_->SbatStart());
    }

    if (header_) {
        DirEntry *root = GetRootEntry();
        if (root && root->Start() != DIR_ENTRY_END) {
            AddBlocksFromChain(root->Start());
        }
    }

    protectedBlocksCache_.insert(carry.begin(), carry.end());
    protectedBlocksDirty_ = false;
}

uint32_t StorageIO::AllocateProtectedSector(uint32_t markerType)
{
    if (!bbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "BBAT is not initialized");
        return AllocTable::Eof;
    }

    RebuildProtectedBlocksCache();
    std::vector<std::pair<uint32_t, uint32_t>> tempMarks;
    uint32_t candidate = static_cast<uint32_t>(bbat_->Unused());
    while (protectedBlocksCache_.count(candidate) > 0) {
        tempMarks.emplace_back(candidate, (*bbat_)[candidate]);
        bbat_->Set(candidate, markerType);
        candidate = static_cast<uint32_t>(bbat_->Unused());
    }
    for (const auto &[block, originalVal] : tempMarks) {
        bbat_->Set(block, originalVal);
    }
    return candidate;
}

bool StorageIO::AdvertiseFATSectorInDIFAT(uint32_t fatSector, uint32_t current, uint32_t difatEntries)
{
    if (!header_ || !bbat_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "header or BBAT is null initialized");
        return false;
    }
    if (current < HEADER_DIFAT_ARRAY_SIZE) {
        header_->SetDifatElem(current, static_cast<uint32_t>(fatSector));
        return true;
    }
    if (difatEntries == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "DIFAT entries per sector is 0");
        return false;
    }
    const uint32_t difatIndex = current - HEADER_DIFAT_ARRAY_SIZE;
    const uint32_t difatSectorIndex = difatIndex / difatEntries;
    while (difatSectors_.size() <= difatSectorIndex) {
        uint32_t newDifat = AllocateProtectedSector(AllocTable::MetaBat);
        if (newDifat == AllocTable::Eof) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to allocate new DIFAT sector");
            return false;
        }
        difatSectors_.push_back(newDifat);
        bbat_->Set(newDifat, AllocTable::MetaBat);
        protectedBlocksCache_.insert(newDifat);

        if (difatSectors_.size() == 1) {
            header_->SetFirstDifat(newDifat);
        }
    }
    return true;
}

void StorageIO::EnsureFatCapacity()
{
    if (!bbat_ || !header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "BBAT or header is null initialized");
        return;
    }

    const uint32_t sectorSize = BigBlockSize();
    if (sectorSize == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid block size");
        return;
    }
    const uint32_t entriesPerSector = sectorSize / static_cast<uint32_t>(sizeof(uint32_t));
    if (entriesPerSector == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid block size");
        return;
    }
    const uint32_t difatEntries = entriesPerSector > 0 ? static_cast<uint32_t>(entriesPerSector - 1) : 0;

    RebuildProtectedBlocksCache();
    while (true) {
        const uint32_t entryCount = static_cast<uint32_t>(bbat_->Count());
        uint32_t required = (entryCount + entriesPerSector - 1) / entriesPerSector;
        if (required == 0) {
            required = 1;
        }
        if (required <= fatSectors_.size()) {
            break;
        }

        const uint32_t current = static_cast<uint32_t>(fatSectors_.size());
        uint32_t newFat = AllocateProtectedSector(AllocTable::Bat);
        if (newFat == AllocTable::Eof) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to allocate new FAT sector");
            return;
        }
        fatSectors_.push_back(newFat);
        bbat_->Set(newFat, AllocTable::Bat);
        protectedBlocksCache_.insert(newFat);

        if (!AdvertiseFATSectorInDIFAT(newFat, current, difatEntries)) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to advertise new FAT sector in DIFAT");
            return;
        }
    }
    header_->SetFatSectorCount(static_cast<uint32_t>(fatSectors_.size()));
    header_->SetDifatCount(static_cast<uint32_t>(difatSectors_.size()));
}

bool StorageIO::ExtendChainWithZeros(std::vector<uint32_t> &chain,
    uint32_t &neededBigBlocks, uint32_t &bigBlockSize)
{
    if (bbat_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "BBAT is null");
        return false;
    }
    while (chain.size() < neededBigBlocks) {
        uint32_t newBlock = bbat_->Unused();
        if (!chain.empty()) {
            bbat_->Set(chain.back(), newBlock);
        }
        bbat_->Set(newBlock, AllocTable::Eof);
        chain.push_back(newBlock);
        protectedBlocksDirty_ = true;

        std::vector<Byte> zeroBuf(static_cast<size_t>(bigBlockSize), 0);
        const uint64_t physicalOffset = BlockToOffset(newBlock, bigBlockSize);
        const std::string context = "Root stream block " + std::to_string(newBlock);
        if (!WriteBlockChecked(physicalOffset, zeroBuf.data(), bigBlockSize, context)) {
            if (!chain.empty()) {
                chain.pop_back();
            }
            bbat_->Set(newBlock, AllocTable::Avail);
            if (!chain.empty()) {
                bbat_->Set(chain.back(), AllocTable::Eof);
            }
            return false;
        }
    }
    return true;
}

bool StorageIO::ExtendRootStream(uint32_t requiredMiniBlocks)
{
    if (!bbat_ || !header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "BBAT or header is null initialized");
        return false;
    }
    DirEntry *root = GetRootEntry();
    if (!root) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "root entry is null");
        return false;
    }

    uint32_t bigBlockSize = BigBlockSize();
    const uint32_t smallBlockSize = SmallBlockSize();
    if (smallBlockSize == 0 || bigBlockSize == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid block size");
        return false;
    }
    const uint32_t miniBlocksPerBigBlock = bigBlockSize / smallBlockSize;
    if (miniBlocksPerBigBlock == 0) {
        SetError(ErrorCode::InvalidOperation, "Big block size is not divisible by small block size", false);
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Big block size is not divisible by small block size");
        return false;
    }
    uint64_t neededBigBlocks64 = (static_cast<uint64_t>(requiredMiniBlocks) +
        static_cast<uint64_t>(miniBlocksPerBigBlock) - 1) / static_cast<uint64_t>(miniBlocksPerBigBlock);
    if (neededBigBlocks64 > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
        SetError(ErrorCode::InvalidOperation, "Required big blocks count exceeds uint32_t max value");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Required big blocks count exceeds uint32_t max value");
        return false;
    }
    uint32_t neededBigBlocks = static_cast<uint32_t>(neededBigBlocks64);
    std::vector<uint32_t> chain;
    const uint32_t rootStart = root->Start();
    if (rootStart != DIR_ENTRY_END && rootStart != AllocTable::Eof) {
        if (!bbat_->Follow(root->Start(), chain)) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to follow BBAT chain");
            return false;
        }
    }

    if (chain.size() < neededBigBlocks) {
        if (!ExtendChainWithZeros(chain, neededBigBlocks, bigBlockSize)) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to extend BBAT chain");
            return false;
        }
    }

    if ((rootStart == DIR_ENTRY_END || rootStart == AllocTable::Eof) && !chain.empty()) {
        root->SetStart(chain.front());
    }

    const uint64_t miniStreamSize = static_cast<uint64_t>(requiredMiniBlocks) *
        static_cast<uint64_t>(smallBlockSize);
    root->SetSize(miniStreamSize);
    sbBlocks_ = chain;
    dtModified_ = true;
    return true;
}

bool StorageIO::ExtendAndFetchSbatChain(std::vector<uint32_t> &chain, uint32_t &neededBlocks)
{
    if (bbat_ == nullptr || header_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "BBAT or header is null initialized");
        return false;
    }
    uint32_t currentStart = header_->SbatStart();
    if (currentStart != AllocTable::Eof && currentStart != AllocTable::Avail) {
        if (!bbat_->Follow(currentStart, chain)) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to follow SBAT chain");
            return false;
        }
    }
    while (chain.size() < neededBlocks) {
        uint32_t newBlock = static_cast<uint32_t>(bbat_->Unused());
        if (!chain.empty()) {
            bbat_->Set(chain.back(), newBlock);
        }
        bbat_->Set(newBlock, AllocTable::Eof);
        chain.push_back(newBlock);
        protectedBlocksDirty_ = true;
    }
    if (chain.empty()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to extend SBAT chain");
        return false;
    }
    return true;
}

bool StorageIO::SaveMiniFat()
{
    if (!sbat_ || !bbat_ || !header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "SBAT, BBAT, or header is null initialized");
        return false;
    }
    size_t highestUsed = 0;
    for (size_t i = 0; i < sbat_->Count(); ++i) {
        if ((*sbat_)[i] != AllocTable::Avail) {
            highestUsed = i + 1;
        }
    }
    if (highestUsed == 0) {
        header_->SetFirstMiniFat(AllocTable::Eof);
        header_->SetMiniFatCount(0);
        protectedBlocksDirty_ = true;
        return true;
    }
    
    const size_t entryCount = sbat_->Count();
    const uint32_t blockSize = BigBlockSize();
    if (blockSize == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid block size");
        return false;
    }
    if (entryCount > SIZE_MAX / sizeof(uint32_t)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "entryCount overflow");
        return false;
    }
    const size_t bytesNeeded = entryCount * sizeof(uint32_t);
    uint32_t neededBlocks = static_cast<uint32_t>((bytesNeeded + blockSize - 1) / blockSize);
    if (neededBlocks == 0) {
        neededBlocks = 1;
    }
    std::vector<uint32_t> chain;

    if (!ExtendAndFetchSbatChain(chain, neededBlocks)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to extend SBAT chain");
        return false;
    }
    const size_t buflen = chain.size() * blockSize;
    std::vector<Byte> buffer(buflen, 0xFF);
    if (!sbat_->Save(buffer.data(), buflen)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to save MiniFAT");
        return false;
    }
    for (size_t i = 0; i < chain.size(); ++i) {
        const uint32_t block = chain[i];
        const uint64_t physicalOffset = BlockToOffset(block, blockSize);
        const std::string context = "MiniFAT sector " + std::to_string(block);
        if (!WriteBlockChecked(physicalOffset, buffer.data() + (i * blockSize), blockSize, context)) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to write MiniFAT sector");
            return false;
        }
    }
    header_->SetFirstMiniFat(chain.front());
    header_->SetMiniFatCount(static_cast<BlockCount>(chain.size()));
    return true;
}

bool StorageIO::SaveDifat()
{
    if (!header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "header is null initialized");
        return false;
    }
    const uint32_t sectorSize = BigBlockSize();
    if (sectorSize == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid block size");
        return false;
    }
    const uint32_t entriesPerSector = sectorSize / static_cast<uint32_t>(sizeof(uint32_t));
    if (entriesPerSector == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid sector size");
        return false;
    }
    const uint32_t difatEntries = entriesPerSector > 0 ? static_cast<uint32_t>(entriesPerSector - 1) : 0;
    const size_t headerCount = std::min<size_t>(fatSectors_.size(), HEADER_DIFAT_ARRAY_SIZE);
    for (size_t i = 0; i < headerCount; ++i) {
        header_->SetDifatElem(i, static_cast<uint32_t>(fatSectors_[i]));
    }
    for (size_t i = headerCount; i < HEADER_DIFAT_ARRAY_SIZE; ++i) {
        header_->SetDifatElem(i, AllocTable::Avail);
    }
    if (!difatSectors_.empty()) {
        for (size_t chainIdx = 0; chainIdx < difatSectors_.size(); ++chainIdx) {
            const uint32_t difatSector = difatSectors_[chainIdx];
            std::vector<Byte> buf(static_cast<size_t>(sectorSize), 0xFF);
            const size_t baseFatIndex = HEADER_DIFAT_ARRAY_SIZE + static_cast<size_t>(chainIdx * difatEntries);
            for (uint32_t i = 0; i < difatEntries && baseFatIndex + i < fatSectors_.size(); ++i) {
                WriteUint32(buf.data() + (i * FOUR_BYTE_SIZE), fatSectors_[baseFatIndex + i]);
            }
            uint32_t nextDifatValue = (chainIdx + 1 < difatSectors_.size())
                ? difatSectors_[chainIdx + 1]
                : AllocTable::Eof;
            WriteUint32(buf.data() + ((entriesPerSector - 1) * FOUR_BYTE_SIZE), nextDifatValue);
            const uint64_t physicalOffset = BlockToOffset(difatSector, sectorSize);
            const std::string context = "DIFAT sector " + std::to_string(difatSector);
            if (!WriteBlockChecked(physicalOffset, buf.data(), sectorSize, context)) {
                return false;
            }
        }
        header_->SetFirstDifat(difatSectors_[0]);
        header_->SetDifatCount(static_cast<BlockCount>(difatSectors_.size()));
    } else {
        header_->SetFirstDifat(AllocTable::Eof);
        header_->SetDifatCount(0);
    }
    return true;
}

bool StorageIO::SaveFat()
{
    if (!bbat_ || !header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "BBAT or header is null initialized");
        return false;
    }
    if (fatSectors_.empty()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "fatSectors_ is empty");
        return true;
    }
    const uint32_t blockSize = BigBlockSize();
    if (blockSize == 0) {
        SetError(ErrorCode::InvalidOperation, "Invalid block size");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid block size");
        return false;
    }
    const uint64_t buflen64 = static_cast<uint64_t>(fatSectors_.size()) * static_cast<uint64_t>(blockSize);
    if (buflen64 > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
        SetError(ErrorCode::InvalidOperation, "FAT buffer length overflow");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "FAT buffer length overflow");
        return false;
    }
    const uint32_t buflen = static_cast<uint32_t>(buflen64);
    std::vector<Byte> buffer(buflen);
    if (!bbat_->Save(buffer.data(), buflen)) {
        SetError(ErrorCode::IOError, "Failed to save FAT");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to save FAT");
        return false;
    }
    for (size_t i = 0; i < fatSectors_.size(); ++i) {
        const uint32_t sector = fatSectors_[i];
        const uint64_t physicalOffset = BlockToOffset(sector, blockSize);
        const std::string context = "FAT sector " + std::to_string(sector);
        if (!WriteBlockChecked(physicalOffset, buffer.data() + (i * blockSize), blockSize, context)) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to write FAT sector");
            return false;
        }
    }
    return true;
}

bool StorageIO::Flush()
{
    std::lock_guard<std::recursive_mutex> lock(ioMutex_);
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "modified_: %{public}d", dtModified_);
    bool memoryMode = false;
    size_t blockSize = 0;
    if (!ValidateFlushState(memoryMode, blockSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate flush state");
        return false;
    }
    if (!dtModified_) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "no modification, skip flush");
        ClearError();
        return true;
    }
    std::vector<uint32_t> blocks;
    size_t neededBlocks = 0;
    if (dirtree_ && !PrepareDirectoryBlocks(blockSize, dirtree_->EntryCount(), blocks, neededBlocks)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to prepare directory blocks");
        return false;
    }
    FlushSnapshot snap;
    BackupFlushState(snap);
    if (!SetupStagingBuffer(memoryMode, snap)) {
        RestoreFlushState(snap);
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to setup staging buffer");
        return false;
    }

    auto fail = [&]() {
        RestoreFlushState(snap);
        dtModified_ = true;
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Flush failed");
        return false;
    };
    if (!ExecuteFlushSequence(blocks, neededBlocks, blockSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to execute flush sequence");
        return fail();
    }
    if (!FinalizeFlush(memoryMode, snap, blockSize)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to finalize flush");
        return fail();
    }
    dtModified_ = false;
    ClearError();
    return true;
}

bool StorageIO::FlushDirectoryTree(std::vector<uint32_t> &blocks, size_t neededBlocks, size_t blockSize)
{
    while (blocks.size() < neededBlocks) {
        if (bbat_ == nullptr) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "BBAT is null");
            return false;
        }
        uint32_t newBlock = static_cast<uint32_t>(bbat_->Unused());
        if (!blocks.empty()) {
            bbat_->Set(blocks.back(), newBlock);
        }
        bbat_->Set(newBlock, AllocTable::Eof);
        blocks.push_back(newBlock);
    }
    if (header_ && !blocks.empty() && header_->DirentStart() != blocks.front()) {
        header_->SetFirstDirSector(blocks.front());
    }
    const uint64_t bufflen64 = static_cast<uint64_t>(blocks.size()) * static_cast<uint64_t>(blockSize);
    if (bufflen64 > static_cast<uint64_t>(std::numeric_limits<size_t>::max())) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Directory flush buffer length overflow");
        return false;
    }
    const size_t bufflen = static_cast<size_t>(bufflen64);
    if (bufflen == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "bufflen is 0");
        return false;
    }

    std::vector<Byte> buffer(bufflen);
    if (memset_s(buffer.data(), buffer.size(), 0, buffer.size()) != EOK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "memset_s failed");
        return false;
    }
    if (dirtree_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "dirtree_ is null");
        return false;
    }
    if (!dirtree_->Save(buffer.data(), bufflen)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Save failed");
        return false;
    }
    for (size_t ndx = 0; ndx < blocks.size(); ++ndx) {
        const uint64_t physicalOffset = BlockToOffset(blocks[ndx], blockSize);
        const std::string context = "directory sector " + std::to_string(blocks[ndx]);
        if (!WriteBlockChecked(physicalOffset, buffer.data() + (ndx * blockSize), static_cast<uint32_t>(blockSize),
            context)) {
            return false;
        }
    }
    return true;
}

bool StorageIO::FlushMiniFat()
{
    if (!SaveMiniFat()) {
        if (error_ == ErrorCode::Ok) {
            SetError(ErrorCode::IOError, "Failed to save mini FAT");
        }
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "SaveMiniFat failed");
        return false;
    }
    return true;
}

bool StorageIO::FlushFatChain()
{
    EnsureFatCapacity();
    if (!SaveFat()) {
        if (error_ == ErrorCode::Ok) {
            SetError(ErrorCode::IOError, "Failed to save FAT");
        }
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "SaveFat failed");
        return false;
    }
    return true;
}

bool StorageIO::FlushDifatChain()
{
    if (!SaveDifat()) {
        if (error_ == ErrorCode::Ok) {
            SetError(ErrorCode::IOError, "Failed to save Difat");
        }
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "SaveDifat failed");
        return false;
    }
    return true;
}

bool StorageIO::FlushHeader()
{
    Byte headerBuf[kHeaderSize];
    if (!header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "header is null");
        return false;
    }
    if (header_->Save(headerBuf, kHeaderSize)) {
        if (!WriteBlockChecked(0, headerBuf, kHeaderSize, "storage header")) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to write header");
            return false;
        }
    } else {
        SetError(ErrorCode::IOError, "Failed to save header");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Save failed");
        return false;
    }
    return true;
}

std::string NormalizeFilePath(const std::string &filename)
{
    std::string canonicalFileName;
    if (!SystemUtils::ValidateAndNormalizePath(filename, canonicalFileName)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to validate and normalize path");
        return "";
    }
    return canonicalFileName;
}

bool StorageIO::WriteBufferToFile(const std::string &filename)
{
    std::ofstream outFile(filename.c_str(), std::ios::binary | std::ios::trunc | std::ios::out);
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::DOCUMENT, "filename: %{private}s", filename.c_str());
    if (!outFile || outFile.fail()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "open file failed");
        return false;
    }
    if (memoryBuffer_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "memoryBuffer is null");
        return false;
    }
    const size_t usedSize = size_ > 0 ? static_cast<size_t>(size_) : 0;
    const size_t writeSize = std::min(usedSize, memoryBuffer_->size());
    outFile.write(reinterpret_cast<const char *>(memoryBuffer_->data()), writeSize);
    if (!outFile.good()) {
        SetError(ErrorCode::IOError, "Failed to write file");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "write file failed");
        return false;
    }
    outFile.flush();
    if (!outFile.good()) {
        SetError(ErrorCode::IOError, "Failed to flush file");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "flush file failed");
        return false;
    }
    return true;
}

bool StorageIO::SaveToFile(const char *filename, bool switchToFileMode)
{
    std::lock_guard<std::recursive_mutex> lock(ioMutex_);
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "filename: %{private}s, switchToFileMode: %{public}d",
        filename, switchToFileMode);
    if (!memoryBuffer_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "memoryBuffer is null");
        return false;
    }
    if (!Flush()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Flush failed");
        return false;
    }

    std::string canonicalFileName = NormalizeFilePath(filename);
    if (canonicalFileName.empty()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "NormalizeFilePath failed");
        return false;
    }
    if (!WriteBufferToFile(canonicalFileName)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "WriteBufferToFile failed");
        return false;
    }
    if (switchToFileMode) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "Switching to file mode");
        if (file_) {
            file_->close();
            file_.reset();
            stream_ = nullptr;
        }

        auto f =
            std::make_unique<std::fstream>(canonicalFileName.c_str(),
            std::ios::binary | std::ios::in | std::ios::out);
        if (!f || f->fail()) {
            SetError(ErrorCode::OpenFailed, std::string("Failed to open file: ")+ filename);
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "open file failed");
            return false;
        }
        file_ = std::move(f);
        stream_ = file_.get();
        file_->seekg(0, std::ios::end);
        size_ = file_->tellg();
        file_->seekg(0, std::ios::beg);
        memoryBuffer_.reset();
    }
    return true;
}

bool StorageIO::ReadAndLoadHeader()
{
    std::array<Byte, kHeaderSize> headerBuffer{};
    if (stream_ == nullptr || !header_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "stream or header is null");
        return false;
    }
    stream_->seekg(0);
    auto bytesRead = stream_->read(reinterpret_cast<char *>(headerBuffer.data()), headerBuffer.size()).gcount();
    if (bytesRead < 0 || static_cast<std::size_t>(bytesRead) != headerBuffer.size()) {
        SetError(ErrorCode::BadOLE, "read head too short");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "read head too short");
        return false;
    }
    if (!header_->Load(headerBuffer.data(), headerBuffer.size())) {
        SetError(ErrorCode::BadOLE, "Failed to load header");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "load header failed");
        return false;
    }
    if (!header_->IsCompoundDocument()) {
        SetError(ErrorCode::NotOLE, "Not a valid file signature");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid file signature");
        return false;
    }
    if (!header_->Valid()) {
        SetError(ErrorCode::BadOLE, "header failed validation");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "header failed validation");
        return false;
    }
    return true;
}

bool StorageIO::ValidateSectorSizes(uint32_t &sectorSize)
{
    if (bbat_ == nullptr || sbat_ == nullptr || header_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "BBAT, SBAT, or header is null");
        return false;
    }
    uint32_t bigBlockShift = header_->BigBlockShift();
    uint32_t smallBlockShift = header_->SmallBlockShift();
    bbat_->SetBlockSize(1 << bigBlockShift);
    sbat_->SetBlockSize(1 << smallBlockShift);
    difatSectors_.clear();
    fatSectors_.clear();
    protectedBlocksCache_.clear();
    protectedBlocksDirty_ = true;
    sectorSize = BigBlockSize();
    if (sectorSize == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Invalid sector size");
        SetError(ErrorCode::BadOLE, "Invalid sector size");
        return false;
    }
    return true;
}

bool StorageIO::CheckClaimedTableSizes(uint64_t fileSize, uint32_t sectorSize)
{
    if (header_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "header is null");
        return false;
    }
    if (header_->NumBat() > 0 && sectorSize > fileSize / header_->NumBat()) {
        SetError(ErrorCode::BadOLE, "FAT size exceeds file size");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "FAT size exceeds file size");
        return false;
    }
    if (header_->NumDifat() > 0 && sectorSize > fileSize / header_->NumDifat()) {
        SetError(ErrorCode::BadOLE, "FAT size exceeds file size");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "FAT size exceeds file size");
        return false;
    }
    return true;
}

bool StorageIO::ValidateHeaderSectorIndex(uint32_t sectorIdx, uint32_t sectorSize,
    const char *name, uint64_t fileSize)
{
    if (sectorIdx == AllocTable::Eof || sectorIdx == AllocTable::Avail) {
        return true;
    }
    if (sectorSize != 0 && sectorIdx > std::numeric_limits<uint64_t>::max() / sectorSize) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "exceed limit");
        return false;
    }
    const uint64_t sectorOffset = BlockToOffset(sectorIdx, sectorSize);
    if (sectorOffset >= fileSize) {
        SetError(ErrorCode::BadOLE, "Header references sector index exceeds file size");
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Header references sector index exceeds file size");
        return false;
    }
    return true;
}
// LCOV_EXCL_STOP
} // namespace ObjectEditor
} // namespace OHOS