/* * 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 #include #include namespace Hdc { constexpr size_t ENTRY_FILE_BUFSIZE = 4 * 1024; // 4KB constexpr uint64_t ENTRY_MAX_FILE_SIZE = static_cast(4) * 1024 * 1024 * 1024; // 4GB std::optional 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) { // max package size is 4GB 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.", 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, inFile)) { 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()) { WRITE_LOG(LOG_FATAL, "PayloadToFile open %s fail", 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(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(buffAppend), writeSize); if (outFile.fail()) { WRITE_LOG(LOG_FATAL, "ReadAndWriteData write file error"); return false; } return true; } bool Entry::PayloadToDir(std::string prefixPath, std::ifstream &inFile) { std::string saveFile = ""; auto dirPath = prefixPath.append(GetName()); std::string estr; bool b = Base::TryCreateDirectory(dirPath, estr); if (!b) { WRITE_LOG(LOG_FATAL, "PayloadToDir mkdir failed dirPath:%s estr:%s", 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(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) { WRITE_LOG(LOG_FATAL, "open %s fail", 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(buff), HEADER_LEN); file.write(buff, HEADER_LEN); break; } default: return false; } return true; } }