* 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 <filesystem>
#include <fstream>
#include "package_data.h"
#include "system_utils.h"
namespace OHOS {
namespace ObjectEditor {
namespace fs = std::filesystem;
namespace {
constexpr const char* PACKAGE_STREAM_NATIVE_NAME = "\x01Ole10Native";
constexpr const char* PACKAGE_STREAM_COMPOBJ_NAME = "\x01CompObj";
constexpr const char* PACKAGE_STREAM_OBJINFO_NAME = "\x03ObjInfo";
constexpr uint32_t U16_BUF_LEN = 2;
constexpr uint32_t U32_BUF_LEN = 4;
constexpr uint32_t MARKER_VAL = 2;
constexpr uint32_t MARKER2_VAL = 0x30000;
constexpr uint32_t CHUNK_SIZE = 4 * 1024 * 1024;
constexpr uint32_t BLOCK_SIZE = 4 * 1024;
}
std::unique_ptr<PackageData> PackageData::CreateByDocument(std::shared_ptr<ObjectEditorDocument> document)
{
if (document == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "document is null");
return nullptr;
}
std::unique_ptr<PackageData> data = std::make_unique<PackageData>();
if (data == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "PackageData is null");
return nullptr;
}
fs::path filepath(document->GetNativeFilePath());
fs::path filename = filepath.filename();
data->filename_ = filename.string();
data->filepath_ = filepath.string();
data->fileLink_ = document->GetOriFilePath();
data->document_ = document;
if (!data->SaveData()) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "SaveData failed");
return nullptr;
}
return data;
}
std::unique_ptr<PackageData> PackageData::LoadFromDocument(std::shared_ptr<ObjectEditorDocument> document)
{
if (document == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "document is null");
return nullptr;
}
std::unique_ptr<PackageData> data = std::make_unique<PackageData>();
if (data == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "PackageData is null");
return nullptr;
}
auto rootStorage = document->GetRootStorage();
if (rootStorage == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "rootStorage is null");
return nullptr;
}
auto stream = rootStorage->GetStream(PACKAGE_STREAM_NATIVE_NAME);
if (stream == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "stream is null");
return nullptr;
}
if (!data->ParseOle10NativeStream(stream, document->GetTmpFilePath())) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "LoadData failed");
return nullptr;
}
data->document_ = document;
return data;
}
bool ReadStreamUint32(Stream *stream, uint64_t streamSize, StreamPos &offset, uint32_t &value)
{
if (streamSize <= U32_BUF_LEN) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "stream size too small");
return false;
}
if (offset > streamSize - U32_BUF_LEN) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "Insufficient data for file size");
return false;
}
if (stream == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "stream is null");
return false;
}
stream->Seek(offset);
Byte fileSizeBuf[U32_BUF_LEN];
stream->Read(fileSizeBuf, U32_BUF_LEN);
value = ReadUint32(fileSizeBuf, U32_BUF_LEN);
offset += U32_BUF_LEN;
return true;
}
void WriteStreamUint32(std::vector<Byte> &buffer, const uint32_t &value)
{
Byte valueBuf[U32_BUF_LEN];
WriteUint32(valueBuf, value, U32_BUF_LEN);
buffer.insert(buffer.end(), std::begin(valueBuf), std::end(valueBuf));
}
void WriteStreamString(std::vector<Byte> &buffer, const std::string &value)
{
std::vector<Byte> valueBuf(value.begin(), value.end());
valueBuf.push_back('\0');
buffer.insert(buffer.end(), std::begin(valueBuf), std::end(valueBuf));
}
bool PackageData::ParseOle10NativeStream(Stream *stream, const std::string &tmpFilePath)
{
if (stream == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "stream is null");
return false;
}
uint64_t streamSize = stream->Size();
StreamPos offset = 0;
if (!ReadStreamUint32(stream, streamSize, offset, fileSize_)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "failed to read fileSize");
return false;
}
offset += U16_BUF_LEN;
stream->Seek(offset);
std::vector<Byte> filenameBuf;
auto filenameSize = stream->ReadBufferUntilNull(filenameBuf);
if (filenameSize <= 0) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "filename is empty");
return false;
}
filename_ = std::string(filenameBuf.begin(), filenameBuf.end());
offset += static_cast<size_t>(filenameSize) + 1;
stream->Seek(offset);
std::vector<Byte> filepathBuf;
auto filepathSize = stream->ReadBufferUntilNull(filepathBuf);
if (filepathSize <= 0) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "filepath is empty");
return false;
}
filepath_ = std::string(filepathBuf.begin(), filepathBuf.end());
offset += static_cast<size_t>(filepathSize) + 1;
offset += U32_BUF_LEN;
uint32_t fileLinkSize = 0;
if (!ReadStreamUint32(stream, streamSize, offset, fileLinkSize)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "failed to read fileLinkSize");
return false;
}
if (fileLinkSize > streamSize || offset > streamSize - fileLinkSize) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "Insufficient data for file link");
return false;
}
offset += fileLinkSize;
if (!ReadStreamUint32(stream, streamSize, offset, dataSize_)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "failed to read dataSize");
return false;
}
if (dataSize_ > streamSize || offset > streamSize - dataSize_) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "Insufficient data for data size");
return false;
}
if (!WriteFileToSandbox(stream, offset, tmpFilePath)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "failed to write sandbox");
return false;
}
return true;
}
bool PackageData::FormatOle10NativeStream(const std::string &tmpFilePath, std::vector<Byte> &buffer, bool &withData)
{
std::error_code ec;
if (!fs::exists(filepath_, ec)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "tmpFilePath: %{private}s not exists", tmpFilePath.c_str());
return false;
}
uint64_t dataSize = fs::file_size(filepath_, ec);
if (dataSize > UINT32_MAX) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "the file size exceeds 4GB");
return false;
}
WriteStreamUint32(buffer, 0);
Byte marker[U16_BUF_LEN];
WriteUint16(marker, MARKER_VAL, U16_BUF_LEN);
buffer.insert(buffer.end(), std::begin(marker), std::end(marker));
WriteStreamString(buffer, filename_);
WriteStreamString(buffer, filepath_);
WriteStreamUint32(buffer, MARKER2_VAL);
std::string fileLink = tmpFilePath;
std::vector<Byte> fileLinkBuf(std::begin(fileLink), std::end(fileLink));
fileLinkBuf.push_back('\0');
uint32_t fileLinkSize = static_cast<uint32_t>(fileLinkBuf.size());
WriteStreamUint32(buffer, fileLinkSize);
buffer.insert(buffer.end(), std::begin(fileLinkBuf), std::end(fileLinkBuf));
WriteStreamUint32(buffer, dataSize);
if (buffer.size() > UINT32_MAX - dataSize_) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "insufficient data for fileSize");
return false;
}
uint32_t fileSize = buffer.size() + dataSize;
Byte fileSizeBuf[U32_BUF_LEN];
WriteUint32(fileSizeBuf, fileSize, U32_BUF_LEN);
std::vector<Byte> newFileSizeBuf(fileSizeBuf, fileSizeBuf + U32_BUF_LEN);
if (buffer.size() < newFileSizeBuf.size()) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "buffer size small");
return false;
}
std::copy(newFileSizeBuf.begin(), newFileSizeBuf.end(), buffer.begin());
withData = dataSize_ < BLOCK_SIZE;
OBJECT_EDITOR_LOGI(ObjectEditorDomain::PACKAGE, "buffer size: %{public}u, dataSize: %{public}u",
static_cast<uint32_t>(buffer.size()), dataSize_);
return true;
}
bool PackageData::WriteFileToSandbox(Stream *stream, StreamPos &offset, const std::string &tmpFilePath)
{
if (stream == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "stream is null");
return false;
}
std::error_code ec;
fs::path filepath(tmpFilePath);
fs::path parentDir = filepath.parent_path();
if (!fs::exists(parentDir, ec)) {
fs::create_directories(parentDir, ec);
if (ec) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "create directories failed, ec: %{public}d", ec.value());
return false;
}
}
fs::path safeFilename = fs::path(filename_).filename();
std::string canonicalPath = parentDir.string() + "/" + safeFilename.string();
std::string outputPathStr;
if (!SystemUtils::ValidateAndNormalizePath(canonicalPath, outputPathStr)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "Failed to validate and normalize path");
return false;
}
std::ofstream outFile(outputPathStr, std::ios::binary);
if (!outFile) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "cannot create file: %{private}s", tmpFilePath.c_str());
return false;
}
uint32_t readOffset = 0;
do {
stream->Seek(offset + readOffset);
uint32_t readLen = CHUNK_SIZE;
if (dataSize_ - readOffset < readLen) {
readLen = dataSize_ - readOffset;
}
std::vector<Byte> data(readLen);
stream->Read(data.data(), readLen);
if (!outFile.write(reinterpret_cast<const char*>(data.data()), readLen)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "write failed");
if (fs::exists(outputPathStr)) {
fs::remove(outputPathStr);
}
return false;
}
readOffset += readLen;
} while (readOffset < dataSize_);
if (!outFile.good()) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "write file data failed");
if (fs::exists(outputPathStr)) {
fs::remove(outputPathStr);
}
return false;
}
filepath_ = outputPathStr;
OBJECT_EDITOR_LOGD(ObjectEditorDomain::PACKAGE, "filepath:%{private}s", filepath_.c_str());
return true;
}
bool HandleFileError(const std::ifstream &file, const uint32_t &chunkCount,
const size_t &totalWritten, const std::streamsize &bytesRead)
{
if (file.fail()) {
if (file.bad()) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "read file failed");
return false;
}
if (bytesRead == 0) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "no byte read from file");
return false;
}
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "partial read failed");
return false;
}
std::ios::iostate state = file.rdstate();
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "read file error: %{public}d",
static_cast<int32_t>(state));
return false;
}
bool PackageData::WriteDataToStream(Stream *stream)
{
if (stream == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "stream is null");
return false;
}
std::string canonicalPath;
if (!SystemUtils::ValidateAndNormalizePath(filepath_, canonicalPath)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "Failed to validate and normalize path");
return false;
}
std::ifstream file(canonicalPath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "failed to open file");
return false;
}
std::streamsize totalSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> buffer(CHUNK_SIZE);
size_t totalWritten = 0;
uint32_t chunkCount = 0;
OBJECT_EDITOR_LOGI(ObjectEditorDomain::PACKAGE, "totalSize: %{public}u", static_cast<uint32_t>(totalSize));
while (true) {
file.clear();
file.read(reinterpret_cast<char*>(buffer.data()), CHUNK_SIZE);
std::streamsize bytesRead = file.gcount();
totalWritten += static_cast<size_t>(bytesRead);
if (file.good()) {
stream->Write(buffer.data(), bytesRead);
chunkCount++;
continue;
}
if (file.eof()) {
if (bytesRead > 0) {
stream->Write(buffer.data(), bytesRead);
}
chunkCount++;
OBJECT_EDITOR_LOGI(ObjectEditorDomain::PACKAGE,
"file read completed successfully, %{public}u bytes read", static_cast<uint32_t>(totalWritten));
break;
}
return HandleFileError(file, chunkCount, totalWritten, bytesRead);
}
if (totalWritten != static_cast<size_t>(totalSize)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "write file data not match, %{public}u:%{public}u",
static_cast<uint32_t>(totalWritten), static_cast<uint32_t>(totalSize));
}
return true;
}
bool PackageData::WriteDataToBuffer(std::vector<Byte> &buffer)
{
std::string canonicalPath;
if (!SystemUtils::ValidateAndNormalizePath(filepath_, canonicalPath)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "Failed to validate and normalize path");
return false;
}
std::ifstream file(canonicalPath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "failed to open file");
return false;
}
std::streamsize totalSize = file.tellg();
file.seekg(0, std::ios::beg);
size_t originalSize = buffer.size();
OBJECT_EDITOR_LOGI(ObjectEditorDomain::PACKAGE, "bufferSize: %{public}u, totalSize: %{public}u",
static_cast<uint32_t>(originalSize), static_cast<uint32_t>(totalSize));
if (totalSize < 0 || static_cast<size_t>(totalSize) > buffer.max_size() - originalSize) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "buffer resize overflow");
buffer.resize(originalSize);
return false;
}
buffer.resize(originalSize + totalSize);
if (!file.read(reinterpret_cast<char*>(buffer.data() + originalSize), totalSize)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "read file failed");
buffer.resize(originalSize);
return false;
}
return true;
}
bool PackageData::SaveData()
{
auto documentPtr = document_.lock();
if (documentPtr == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "document is null");
return false;
}
auto rootStorage = documentPtr->GetRootStorage();
if (rootStorage == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "rootStorage is null");
return false;
}
auto compObjStream = rootStorage->GetStream(PACKAGE_STREAM_COMPOBJ_NAME, true, true);
if (compObjStream == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "compObjStream is null");
return false;
}
compObjStream->Seek(0);
auto oleStream = rootStorage->GetStream(PACKAGE_STREAM_OBJINFO_NAME, true, true);
if (oleStream == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "oleStream is null");
return false;
}
oleStream->Seek(0);
auto ole10NativeStream = rootStorage->GetStream(PACKAGE_STREAM_NATIVE_NAME, true, true);
if (ole10NativeStream == nullptr) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "ole10NativeStream is null");
return false;
}
ole10NativeStream->Seek(0);
std::vector<Byte> buffer;
bool withData = false;
if (!FormatOle10NativeStream(documentPtr->GetTmpFilePath(), buffer, withData)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "format ole10 native stream failed");
return false;
}
if (withData && !WriteDataToBuffer(buffer)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "write data to buffer failed");
return false;
}
ole10NativeStream->Write(buffer.data(), buffer.size());
if (!withData && !WriteDataToStream(ole10NativeStream)) {
OBJECT_EDITOR_LOGE(ObjectEditorDomain::PACKAGE, "WriteDataToStream failed");
return false;
}
return documentPtr->Flush();
}
}
}