xref: /developtools/hdc/src/common/entry.cpp (revision cc290419)
1/*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include "entry.h"
16
17#include <iostream>
18#include <fstream>
19#include <optional>
20
21namespace Hdc {
22
23constexpr size_t ENTRY_FILE_BUFSIZE = 4 * 1024; // 4KB
24constexpr uint64_t ENTRY_MAX_FILE_SIZE = static_cast<uint64_t>(4) * 1024 * 1024 * 1024; // 4GB
25std::optional<std::string> StripPrefix(const std::string& str, const std::string& prefix)
26{
27    if (str.compare(0, prefix.length(), prefix) == 0) {
28        auto p_path = str.substr(prefix.length());
29        return p_path;
30    } else {
31        return std::nullopt;
32    }
33}
34
35Entry::Entry(std::string prefix, std::string path)
36{
37    this->prefix = prefix + Base::GetPathSep();
38    uv_fs_t req;
39    int rc = uv_fs_lstat(nullptr, &req, path.c_str(), nullptr);
40    uv_fs_req_cleanup(&req);
41    if (rc == 0) {
42        if (req.statbuf.st_mode & S_IFDIR) {
43            header.UpdataFileType(TypeFlage::DIRECTORY);
44            header.UpdataSize(0);
45        } else if (req.statbuf.st_mode & S_IFREG) {
46            auto fileSize = req.statbuf.st_size;
47            if (fileSize < ENTRY_MAX_FILE_SIZE) { // max package size is 4GB
48                header.UpdataSize(fileSize);
49                needSize = fileSize;
50                header.UpdataFileType(TypeFlage::ORDINARYFILE);
51            } else {
52#ifdef HDC_HOST
53                Base::PrintMessage("[Warning]File: %s, size: %lldB, over the 4GB limit, ignored.",
54                    path.c_str(), fileSize);
55#else
56                WRITE_LOG(LOG_WARN, "File: %s, size: %lldB, over the 4GB limit, ignored.",
57                    path.c_str(), fileSize);
58#endif
59            }
60        }
61    }
62    UpdataName(path);
63}
64
65Entry::Entry(uint8_t data[512], int dataLen)
66{
67    header = Header(data, dataLen);
68    needSize = header.Size();
69}
70
71
72void Entry::AddData(uint8_t *data, size_t len)
73{
74    if (this->needSize == 0) {
75        return;
76    }
77    if (this->needSize > len) {
78        for (size_t i = 0; i < len; i++) {
79            this->data.push_back(data[i]);
80        }
81        this->needSize -= len;
82    } else {
83        for (size_t i = 0; i < this->needSize; i++) {
84            this->data.push_back(data[i]);
85        }
86        this->needSize = 0;
87    }
88}
89
90std::string Entry::GetName()
91{
92    auto name = this->prefix + this->header.Name();
93    return name;
94}
95
96bool Entry::UpdataName(std::string name)
97{
98    if (!this->prefix.empty()) {
99        auto p_path = Hdc::StripPrefix(name, this->prefix);
100        if (p_path.has_value()) {
101            return this->header.UpdataName(p_path.value());
102        }
103    }
104    return this->header.UpdataName(name);
105}
106
107bool Entry::CopyPayload(std::string prefixPath, std::ifstream &inFile)
108{
109    switch (this->header.FileType()) {
110        case TypeFlage::ORDINARYFILE: {
111            if (!PayloadToFile(prefixPath, inFile)) {
112                return false;
113            }
114            break;
115        }
116        case TypeFlage::DIRECTORY: {
117            if (!PayloadToDir(prefixPath, inFile)) {
118                return false;
119            }
120            break;
121        }
122        default:
123            return false;
124    }
125    return true;
126}
127
128bool Entry::PayloadToFile(std::string prefixPath, std::ifstream &inFile)
129{
130    std::string saveFile = "";
131    saveFile = prefixPath + GetName();
132    std::ofstream outFile(saveFile, std::ios::app | std::ios::binary);
133    if (!outFile.is_open()) {
134        WRITE_LOG(LOG_FATAL, "PayloadToFile open %s fail", saveFile.c_str());
135        return false;
136    }
137    bool ret = true;
138    uint8_t *buffAppend = new uint8_t[ENTRY_FILE_BUFSIZE];
139    while (this->needSize >= ENTRY_FILE_BUFSIZE) {
140        ret = ReadAndWriteData(inFile, outFile, buffAppend, ENTRY_FILE_BUFSIZE, ENTRY_FILE_BUFSIZE);
141        if (!ret) {
142            break;
143        }
144        this->needSize -= ENTRY_FILE_BUFSIZE;
145    }
146    if (ret && this->needSize > 0) {
147        long int paddingSize = HEADER_LEN - (this->needSize % HEADER_LEN);
148        long int lastBufSize = (paddingSize == HEADER_LEN) ? this->needSize :
149            this->needSize + paddingSize;
150        ret = ReadAndWriteData(inFile, outFile, buffAppend, lastBufSize, this->needSize);
151    }
152    delete[] buffAppend;
153    buffAppend = nullptr;
154    outFile.close();
155    this->needSize = 0;
156    return ret;
157}
158
159bool Entry::ReadAndWriteData(std::ifstream &inFile, std::ofstream &outFile, uint8_t *buffAppend,
160    int readSize, int writeSize)
161{
162    if (buffAppend == nullptr) {
163        WRITE_LOG(LOG_FATAL, "ReadAndWriteData buffAppend is null");
164        return false;
165    }
166    inFile.read(reinterpret_cast<char*>(buffAppend), readSize);
167    auto readcnt = inFile.gcount();
168    if (inFile.fail() || readcnt != readSize) {
169        WRITE_LOG(LOG_FATAL, "ReadAndWriteData read file error");
170        return false;
171    }
172    outFile.write(reinterpret_cast<const char*>(buffAppend), writeSize);
173    if (outFile.fail()) {
174        WRITE_LOG(LOG_FATAL, "ReadAndWriteData write file error");
175        return false;
176    }
177    return true;
178}
179
180bool Entry::PayloadToDir(std::string prefixPath, std::ifstream &inFile)
181{
182    std::string saveFile = "";
183    auto dirPath = prefixPath.append(GetName());
184    std::string estr;
185    bool b = Base::TryCreateDirectory(dirPath, estr);
186    if (!b) {
187        WRITE_LOG(LOG_FATAL, "PayloadToDir mkdir failed dirPath:%s estr:%s", dirPath.c_str(), estr.c_str());
188        return false;
189    }
190    return true;
191}
192
193bool Entry::WriteToTar(std::ofstream &file)
194{
195    switch (header.FileType()) {
196        case TypeFlage::ORDINARYFILE: {
197            char buff[HEADER_LEN] = {0};
198            header.GetBytes(reinterpret_cast<uint8_t *>(buff), HEADER_LEN);
199            file.write(buff, HEADER_LEN);
200            if (header.Size() == 0) {
201                break;
202            }
203            std::string name = Base::UnicodeToUtf8(GetName().c_str(), true);
204            std::ifstream inFile(name, std::ios::binary);
205            if (!inFile) {
206                WRITE_LOG(LOG_FATAL, "open %s fail", name.c_str());
207            }
208            file << inFile.rdbuf();
209            auto pading = HEADER_LEN - (needSize % HEADER_LEN);
210            if (pading < HEADER_LEN) {
211                char pad[HEADER_LEN] = {0};
212                file.write(pad, pading);
213            }
214            break;
215        }
216        case TypeFlage::DIRECTORY: {
217            char buff[HEADER_LEN] = {0};
218            header.GetBytes(reinterpret_cast<uint8_t *>(buff), HEADER_LEN);
219            file.write(buff, HEADER_LEN);
220            break;
221        }
222        default:
223            return false;
224    }
225    return true;
226}
227}
228