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