* Copyright (C) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "entry.h"
#include <iostream>
#include <fstream>
#include <optional>
namespace Hdc {
constexpr size_t ENTRY_FILE_BUFSIZE = 4 * 1024;
constexpr uint64_t ENTRY_MAX_FILE_SIZE = static_cast<uint64_t>(4) * 1024 * 1024 * 1024;
std::optional<std::string> StripPrefix(const std::string& str, const std::string& prefix)
{
if (str.compare(0, prefix.length(), prefix) == 0) {
auto p_path = str.substr(prefix.length());
return p_path;
} else {
return std::nullopt;
}
}
Entry::Entry(std::string prefix, std::string path)
{
this->prefix = prefix + Base::GetPathSep();
uv_fs_t req;
int rc = uv_fs_lstat(nullptr, &req, path.c_str(), nullptr);
uv_fs_req_cleanup(&req);
if (rc == 0) {
if (req.statbuf.st_mode & S_IFDIR) {
header.UpdataFileType(TypeFlage::DIRECTORY);
header.UpdataSize(0);
} else if (req.statbuf.st_mode & S_IFREG) {
auto fileSize = req.statbuf.st_size;
if (fileSize < ENTRY_MAX_FILE_SIZE) {
header.UpdataSize(fileSize);
needSize = fileSize;
header.UpdataFileType(TypeFlage::ORDINARYFILE);
} else {
#ifdef HDC_HOST
Base::PrintMessage("[Warning]File: %s, size: %lldB, over the 4GB limit, ignored.",
path.c_str(), fileSize);
#else
WRITE_LOG(LOG_WARN, "File: %s, size: %lldB, over the 4GB limit, ignored.",
Hdc::MaskString(path).c_str(), fileSize);
#endif
}
}
}
UpdataName(path);
}
Entry::Entry(uint8_t data[512], int dataLen)
{
header = Header(data, dataLen);
needSize = header.Size();
}
void Entry::AddData(uint8_t *data, size_t len)
{
if (this->needSize == 0) {
return;
}
if (this->needSize > len) {
for (size_t i = 0; i < len; i++) {
this->data.push_back(data[i]);
}
this->needSize -= len;
} else {
for (size_t i = 0; i < this->needSize; i++) {
this->data.push_back(data[i]);
}
this->needSize = 0;
}
}
std::string Entry::GetName()
{
auto name = this->prefix + this->header.Name();
return name;
}
bool Entry::UpdataName(std::string name)
{
if (!this->prefix.empty()) {
auto p_path = Hdc::StripPrefix(name, this->prefix);
if (p_path.has_value()) {
return this->header.UpdataName(p_path.value());
}
}
return this->header.UpdataName(name);
}
bool Entry::CopyPayload(std::string prefixPath, std::ifstream &inFile)
{
switch (this->header.FileType()) {
case TypeFlage::ORDINARYFILE: {
if (!PayloadToFile(prefixPath, inFile)) {
return false;
}
break;
}
case TypeFlage::DIRECTORY: {
if (!PayloadToDir(prefixPath)) {
return false;
}
break;
}
default:
return false;
}
return true;
}
bool Entry::PayloadToFile(std::string prefixPath, std::ifstream &inFile)
{
std::string saveFile = "";
saveFile = prefixPath + GetName();
std::ofstream outFile(saveFile, std::ios::app | std::ios::binary);
if (!outFile.is_open()) {
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "PayloadToFile open %s fail", saveFile.c_str());
} else {
WRITE_LOG(LOG_FATAL, "PayloadToFile open %s fail", Hdc::MaskString(saveFile).c_str());
}
return false;
}
bool ret = true;
uint8_t *buffAppend = new uint8_t[ENTRY_FILE_BUFSIZE];
while (this->needSize >= ENTRY_FILE_BUFSIZE) {
ret = ReadAndWriteData(inFile, outFile, buffAppend, ENTRY_FILE_BUFSIZE, ENTRY_FILE_BUFSIZE);
if (!ret) {
break;
}
this->needSize -= ENTRY_FILE_BUFSIZE;
}
if (ret && this->needSize > 0) {
long int paddingSize = HEADER_LEN - (this->needSize % HEADER_LEN);
long int lastBufSize = (paddingSize == HEADER_LEN) ? this->needSize :
this->needSize + paddingSize;
ret = ReadAndWriteData(inFile, outFile, buffAppend, lastBufSize, this->needSize);
}
delete[] buffAppend;
buffAppend = nullptr;
outFile.close();
this->needSize = 0;
return ret;
}
bool Entry::ReadAndWriteData(std::ifstream &inFile, std::ofstream &outFile, uint8_t *buffAppend,
int readSize, int writeSize)
{
if (buffAppend == nullptr) {
WRITE_LOG(LOG_FATAL, "ReadAndWriteData buffAppend is null");
return false;
}
inFile.read(reinterpret_cast<char*>(buffAppend), readSize);
auto readcnt = inFile.gcount();
if (inFile.fail() || readcnt != readSize) {
WRITE_LOG(LOG_FATAL, "ReadAndWriteData read file error");
return false;
}
outFile.write(reinterpret_cast<const char*>(buffAppend), writeSize);
if (outFile.fail()) {
WRITE_LOG(LOG_FATAL, "ReadAndWriteData write file error");
return false;
}
return true;
}
bool Entry::PayloadToDir(std::string prefixPath)
{
std::string saveFile = "";
auto dirPath = prefixPath.append(GetName());
std::string estr;
bool b = Base::TryCreateDirectory(dirPath, estr);
if (!b) {
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "PayloadToDir mkdir failed dirPath:%s estr:%s", dirPath.c_str(), estr.c_str());
} else {
WRITE_LOG(LOG_FATAL, "PayloadToDir mkdir failed dirPath:%s estr:%s",
Hdc::MaskString(dirPath).c_str(), estr.c_str());
}
return false;
}
return true;
}
bool Entry::WriteToTar(std::ofstream &file)
{
switch (header.FileType()) {
case TypeFlage::ORDINARYFILE: {
char buff[HEADER_LEN] = {0};
header.GetBytes(reinterpret_cast<uint8_t *>(buff), HEADER_LEN);
file.write(buff, HEADER_LEN);
if (header.Size() == 0) {
break;
}
std::string name = Base::UnicodeToUtf8(GetName().c_str(), true);
std::ifstream inFile(name, std::ios::binary);
if (!inFile) {
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "open %s fail", name.c_str());
} else {
WRITE_LOG(LOG_FATAL, "open %s fail", Hdc::MaskString(name).c_str());
}
}
file << inFile.rdbuf();
auto pading = HEADER_LEN - (needSize % HEADER_LEN);
if (pading < HEADER_LEN) {
char pad[HEADER_LEN] = {0};
file.write(pad, pading);
}
break;
}
case TypeFlage::DIRECTORY: {
char buff[HEADER_LEN] = {0};
header.GetBytes(reinterpret_cast<uint8_t *>(buff), HEADER_LEN);
file.write(buff, HEADER_LEN);
break;
}
default:
return false;
}
return true;
}
}