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
21 namespace Hdc {
22
23 constexpr size_t ENTRY_FILE_BUFSIZE = 4 * 1024; // 4KB
24 constexpr uint64_t ENTRY_MAX_FILE_SIZE = static_cast<uint64_t>(4) * 1024 * 1024 * 1024; // 4GB
StripPrefix(const std::string& str, const std::string& prefix)25 std::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
Entry(std::string prefix, std::string path)35 Entry::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
Entry(uint8_t data[512], int dataLen)65 Entry::Entry(uint8_t data[512], int dataLen)
66 {
67 header = Header(data, dataLen);
68 needSize = header.Size();
69 }
70
71
AddData(uint8_t *data, size_t len)72 void 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
GetName()90 std::string Entry::GetName()
91 {
92 auto name = this->prefix + this->header.Name();
93 return name;
94 }
95
UpdataName(std::string name)96 bool 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
CopyPayload(std::string prefixPath, std::ifstream &inFile)107 bool 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
PayloadToFile(std::string prefixPath, std::ifstream &inFile)128 bool 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
ReadAndWriteData(std::ifstream &inFile, std::ofstream &outFile, uint8_t *buffAppend, int readSize, int writeSize)159 bool 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
PayloadToDir(std::string prefixPath, std::ifstream &inFile)180 bool 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
WriteToTar(std::ofstream &file)193 bool 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