1 /*
2 * Copyright (c) 2022-2023 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
16 #include "patch_shared.h"
17
18 #include <string>
19 #include <unistd.h>
20 #include <cstdio>
21 #include <fcntl.h>
22 #include "log/log.h"
23 #include "applypatch/store.h"
24 #include "applypatch/transfer_manager.h"
25 #include "script_manager.h"
26 #include "applypatch/partition_record.h"
27 #include "utils.h"
28 #include "pkg_manager.h"
29 #include "updater_env.h"
30
31 using namespace Uscript;
32 using namespace Hpackage;
33 using namespace Updater;
34
35 namespace Updater {
36 constexpr int WRITE_FILE_SIZE = 4096;
37 struct UpdateBlockInfo {
38 std::string partitionName;
39 std::string transferName;
40 std::string newDataName;
41 std::string patchDataName;
42 std::string devPath;
43 };
44
UpdatePathCheck(const std::string & updatePath,size_t length)45 static bool UpdatePathCheck(const std::string &updatePath, size_t length)
46 {
47 if (updatePath.empty() || length == 0) {
48 LOG(ERROR) << "updatePath is nullptr.";
49 return false;
50 }
51
52 char realPath[PATH_MAX + 1] = {0};
53 if (realpath(updatePath.c_str(), realPath) == nullptr) {
54 LOG(ERROR) << "realPath is NULL" << " : " << strerror(errno);
55 return false;
56 }
57
58 if (access(realPath, F_OK) != 0) {
59 LOG(ERROR) << "package does not exist!";
60 return false;
61 }
62
63 return true;
64 }
65
GetUpdateBlockInfo(UpdateBlockInfo & infos,const std::string & packagePath,const std::string & srcImage,const std::string & targetPath)66 static int GetUpdateBlockInfo(UpdateBlockInfo &infos, const std::string &packagePath,
67 const std::string &srcImage, const std::string &targetPath)
68 {
69 if (!UpdatePathCheck(packagePath, packagePath.length())) {
70 LOG(ERROR) << packagePath << " is empty.";
71 return -1;
72 }
73
74 if (!UpdatePathCheck(srcImage, srcImage.length())) {
75 LOG(ERROR) << srcImage << " is empty.";
76 return -1;
77 }
78
79 if (!UpdatePathCheck(targetPath, targetPath.length())) {
80 LOG(INFO) << "need to make store";
81 if (Updater::Utils::MkdirRecursive(targetPath, S_IRWXU) != 0) {
82 LOG(ERROR) << "Failed to make store";
83 return -1;
84 }
85 }
86
87 infos.newDataName = "anco_hmos.new.dat";
88 infos.patchDataName = "anco_hmos.patch.dat";
89
90 infos.transferName = "anco_hmos.transfer.list";
91 infos.devPath = srcImage;
92 infos.partitionName = "/anco_hmos";
93
94 return 0;
95 }
96
ExtractFileByNameFunc(Uscript::UScriptEnv & env,const std::string & fileName,Hpackage::PkgManager::StreamPtr & outStream,uint8_t * & outBuf,size_t & buffSize)97 static int32_t ExtractFileByNameFunc(Uscript::UScriptEnv &env, const std::string &fileName,
98 Hpackage::PkgManager::StreamPtr &outStream, uint8_t *&outBuf, size_t &buffSize)
99 {
100 if (env.GetPkgManager() == nullptr) {
101 LOG(ERROR) << "Error to get pkg manager";
102 return USCRIPT_ERROR_EXECUTE;
103 }
104
105 const FileInfo *info = env.GetPkgManager()->GetFileInfo(fileName);
106 if (info == nullptr) {
107 LOG(ERROR) << "GetFileInfo fail";
108 return USCRIPT_ERROR_EXECUTE;
109 }
110 auto ret = env.GetPkgManager()->CreatePkgStream(outStream,
111 fileName, info->unpackedSize, PkgStream::PkgStreamType_MemoryMap);
112 if (ret != USCRIPT_SUCCESS || outStream == nullptr) {
113 LOG(ERROR) << "Error to create output stream";
114 return USCRIPT_ERROR_EXECUTE;
115 }
116 ret = env.GetPkgManager()->ExtractFile(fileName, outStream);
117 if (ret != USCRIPT_SUCCESS) {
118 LOG(ERROR) << "Error to extract file";
119 env.GetPkgManager()->ClosePkgStream(outStream);
120 return USCRIPT_ERROR_EXECUTE;
121 }
122 ret = outStream->GetBuffer(outBuf, buffSize);
123 LOG(INFO) << "outBuf data size is: " << buffSize;
124
125 return USCRIPT_SUCCESS;
126 }
127
ExecuteTransferCommand(int fd,const std::vector<std::string> & lines,TransferManagerPtr tm,const std::string & partitionName,const std::string & targetPath)128 static int32_t ExecuteTransferCommand(int fd, const std::vector<std::string> &lines,
129 TransferManagerPtr tm, const std::string &partitionName, const std::string &targetPath)
130 {
131 auto transferParams = tm->GetTransferParams();
132 auto writerThreadInfo = transferParams->writerThreadInfo.get();
133
134 transferParams->storeBase = targetPath + partitionName + "_tmp";
135 LOG(INFO) << "Store base path is " << transferParams->storeBase;
136 int32_t ret = Store::CreateNewSpace(transferParams->storeBase, true);
137 if (ret == -1) {
138 LOG(ERROR) << "Error to create new store space";
139 return -1;
140 }
141 transferParams->storeCreated = ret;
142
143 if (!tm->CommandsParser(fd, lines)) {
144 return Uscript::USCRIPT_ERROR_EXECUTE;
145 }
146 pthread_mutex_lock(&writerThreadInfo->mutex);
147 if (writerThreadInfo->readyToWrite) {
148 LOG(WARNING) << "New data writer thread is still available...";
149 }
150
151 writerThreadInfo->readyToWrite = false;
152 pthread_cond_broadcast(&writerThreadInfo->cond);
153 pthread_mutex_unlock(&writerThreadInfo->mutex);
154 ret = pthread_join(transferParams->thread, nullptr);
155 std::ostringstream logMessage;
156 logMessage << "pthread join returned with " << ret;
157 if (ret != 0) {
158 LOG(WARNING) << logMessage.str();
159 }
160 if (transferParams->storeCreated != -1) {
161 Store::DoFreeSpace(transferParams->storeBase);
162 (void)Utils::DeleteFile(transferParams->storeBase);
163 }
164 return Uscript::USCRIPT_SUCCESS;
165 }
166
DoExecuteUpdateBlock(const UpdateBlockInfo & infos,TransferManagerPtr tm,const std::vector<std::string> & lines,const std::string & targetPath,const std::string & dstImage)167 static int32_t DoExecuteUpdateBlock(const UpdateBlockInfo &infos, TransferManagerPtr tm,
168 const std::vector<std::string> &lines, const std::string &targetPath, const std::string &dstImage)
169 {
170 int fd = open(dstImage.c_str(), O_RDWR | O_LARGEFILE);
171 if (fd == -1) {
172 LOG(ERROR) << "Failed to open block";
173 return Uscript::USCRIPT_ERROR_EXECUTE;
174 }
175 int32_t ret = ExecuteTransferCommand(fd, lines, tm, infos.partitionName, targetPath);
176
177 fsync(fd);
178 close(fd);
179 fd = -1;
180 if (ret == Uscript::USCRIPT_SUCCESS) {
181 PartitionRecord::GetInstance().RecordPartitionUpdateStatus(infos.partitionName, true);
182 }
183 (void)Utils::DeleteFile(infos.devPath);
184
185 return ret;
186 }
187
ExtractNewDataFunc(const PkgBuffer & buffer,size_t size,size_t start,bool isFinish,const void * context)188 static int ExtractNewDataFunc(const PkgBuffer &buffer, size_t size, size_t start, bool isFinish, const void* context)
189 {
190 void *p = const_cast<void *>(context);
191 WriterThreadInfo *info = static_cast<WriterThreadInfo *>(p);
192 uint8_t *addr = buffer.buffer;
193 while (size > 0) {
194 pthread_mutex_lock(&info->mutex);
195 while (info->writer == nullptr) {
196 if (!info->readyToWrite) {
197 LOG(WARNING) << "writer is not ready to write.";
198 pthread_mutex_unlock(&info->mutex);
199 return Hpackage::PKG_INVALID_STREAM;
200 }
201 pthread_cond_wait(&info->cond, &info->mutex);
202 }
203 pthread_mutex_unlock(&info->mutex);
204 size_t toWrite = std::min(size, info->writer->GetBlocksSize() - info->writer->GetTotalWritten());
205 // No more data to write.
206 if (toWrite == 0) {
207 break;
208 }
209 bool ret = info->writer->Write(addr, toWrite, nullptr);
210 std::ostringstream logMessage;
211 logMessage << "Write " << toWrite << " byte(s) failed";
212 if (!ret) {
213 LOG(ERROR) << logMessage.str();
214 return Hpackage::PKG_INVALID_STREAM;
215 }
216 size -= toWrite;
217 addr += toWrite;
218
219 if (info->writer->IsWriteDone()) {
220 pthread_mutex_lock(&info->mutex);
221 info->writer.reset();
222 pthread_cond_broadcast(&info->cond);
223 pthread_mutex_unlock(&info->mutex);
224 }
225 }
226 return Hpackage::PKG_SUCCESS;
227 }
228
CondBroadcast(WriterThreadInfo * info)229 static inline void CondBroadcast(WriterThreadInfo *info)
230 {
231 pthread_mutex_lock(&info->mutex);
232 info->readyToWrite = false;
233 if (info->writer != nullptr) {
234 pthread_cond_broadcast(&info->cond);
235 }
236 pthread_mutex_unlock(&info->mutex);
237 }
238
UnpackNewDataFunc(void * arg)239 void* UnpackNewDataFunc(void *arg)
240 {
241 TransferManagerPtr tm = static_cast<TransferManagerPtr>(arg);
242 WriterThreadInfo *info = tm->GetTransferParams()->writerThreadInfo.get();
243 Hpackage::PkgManager::StreamPtr stream = nullptr;
244 if (info->newPatch.empty()) {
245 LOG(ERROR) << "new patch file name is empty. thread quit.";
246 CondBroadcast(info);
247 return nullptr;
248 }
249 LOG(DEBUG) << "new patch file name: " << info->newPatch;
250 auto env = tm->GetTransferParams()->env;
251 const FileInfo *file = env->GetPkgManager()->GetFileInfo(info->newPatch);
252 if (file == nullptr) {
253 LOG(ERROR) << "Cannot get file info of :" << info->newPatch;
254 CondBroadcast(info);
255 return nullptr;
256 }
257 LOG(DEBUG) << info->newPatch << " info: size " << file->packedSize << " unpacked size " <<
258 file->unpackedSize << " name " << file->identity;
259 int32_t ret = env->GetPkgManager()->CreatePkgStream(stream, info->newPatch, ExtractNewDataFunc, info);
260 if (ret != Hpackage::PKG_SUCCESS || stream == nullptr) {
261 LOG(ERROR) << "Cannot extract " << info->newPatch << " from package.";
262 CondBroadcast(info);
263 return nullptr;
264 }
265 ret = env->GetPkgManager()->ExtractFile(info->newPatch, stream);
266 env->GetPkgManager()->ClosePkgStream(stream);
267 LOG(DEBUG) << "new data writer ending...";
268 // extract new data done.
269 // tell command.
270 CondBroadcast(info);
271 return nullptr;
272 }
273
InitThread(const struct UpdateBlockInfo & infos,TransferManagerPtr tm)274 static int InitThread(const struct UpdateBlockInfo &infos, TransferManagerPtr tm)
275 {
276 auto transferParams = tm->GetTransferParams();
277 auto writerThreadInfo = transferParams->writerThreadInfo.get();
278 writerThreadInfo->readyToWrite = true;
279 pthread_mutex_init(&writerThreadInfo->mutex, nullptr);
280 pthread_cond_init(&writerThreadInfo->cond, nullptr);
281 pthread_attr_t attr;
282 pthread_attr_init(&attr);
283 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
284 writerThreadInfo->newPatch = infos.newDataName;
285 int error = pthread_create(&transferParams->thread, &attr, UnpackNewDataFunc, tm);
286 return error;
287 }
288
CreateFixedSizeEmptyFile(const UpdateBlockInfo & infos,const std::string & filename,int64_t size)289 static int CreateFixedSizeEmptyFile(const UpdateBlockInfo &infos, const std::string &filename, int64_t size)
290 {
291 if (size <= 0) {
292 LOG(ERROR) << "size is " << size;
293 return -1;
294 }
295 if (!Updater::Utils::CopyFile(infos.devPath, filename)) {
296 LOG(ERROR) << "copy " << infos.devPath << " to " << filename << " failed";
297 return -1;
298 }
299 size_t fileSize = Updater::Utils::GetFileSize(infos.devPath);
300 if (static_cast<int64_t>(fileSize) >= size) {
301 LOG(INFO) << "no need copy";
302 return 0;
303 }
304 std::ofstream file(filename, std::ios::binary | std::ios::ate);
305 if (!file.is_open()) {
306 LOG(ERROR) << "Failed to open file for writing.";
307 return -1;
308 }
309
310 /* fill the remaining space with zero values */
311 int writeFileTmp = (size - static_cast<int64_t>(fileSize)) / WRITE_FILE_SIZE;
312 char zerolist[WRITE_FILE_SIZE] = {0};
313 while (writeFileTmp > 0) {
314 file.write(zerolist, WRITE_FILE_SIZE);
315 writeFileTmp--;
316 }
317 writeFileTmp = (size - static_cast<int64_t>(fileSize)) % WRITE_FILE_SIZE;
318 char zero = 0;
319 while (writeFileTmp > 0) {
320 file.write(&zero, 1);
321 writeFileTmp--;
322 }
323
324 file.close();
325 return 0;
326 }
327
GetFileName(const std::string & srcImage)328 static std::string GetFileName(const std::string &srcImage)
329 {
330 std::vector<std::string> lines =
331 Updater::Utils::SplitString(std::string(srcImage), "/");
332 LOG(INFO) << "lines.size is " << lines.size();
333 if (lines.size() == 0) {
334 return "";
335 }
336 return lines[lines.size() - 1];
337 }
338
ExecuteUpdateBlock(Uscript::UScriptEnv & env,const UpdateBlockInfo & infos,const std::string targetPath,std::string destImage)339 static int32_t ExecuteUpdateBlock(Uscript::UScriptEnv &env, const UpdateBlockInfo &infos,
340 const std::string targetPath, std::string destImage)
341 {
342 Hpackage::PkgManager::StreamPtr outStream = nullptr;
343 uint8_t *transferListBuffer = nullptr;
344 size_t transferListSize = 0;
345 if (ExtractFileByNameFunc(env, infos.transferName,
346 outStream, transferListBuffer, transferListSize) != USCRIPT_SUCCESS) {
347 return USCRIPT_ERROR_EXECUTE;
348 }
349
350 std::unique_ptr<TransferManager> tm = std::make_unique<TransferManager>();
351 auto transferParams = tm->GetTransferParams();
352 /* Save Script Env to transfer manager */
353 transferParams->env = &env;
354 std::vector<std::string> lines =
355 Updater::Utils::SplitString(std::string(reinterpret_cast<const char*>(transferListBuffer)), "\n");
356 env.GetPkgManager()->ClosePkgStream(outStream);
357
358 if (ExtractFileByNameFunc(env, infos.patchDataName, outStream,
359 transferParams->patchDataBuffer, transferParams->patchDataSize) != USCRIPT_SUCCESS) {
360 return USCRIPT_ERROR_EXECUTE;
361 }
362 env.GetPkgManager()->ClosePkgStream(outStream);
363 uint8_t *ancoSizeBuffer = nullptr;
364 size_t ancoSizeListSize = 0;
365 const std::string fileName = "anco_size";
366 if (ExtractFileByNameFunc(env, fileName, outStream, ancoSizeBuffer,
367 ancoSizeListSize) != USCRIPT_SUCCESS) {
368 return USCRIPT_ERROR_EXECUTE;
369 }
370 env.GetPkgManager()->ClosePkgStream(outStream);
371 std::string str(reinterpret_cast<char*>(ancoSizeBuffer), ancoSizeListSize);
372 int64_t maxStashSize = std::stoll(str);
373 if (CreateFixedSizeEmptyFile(infos, destImage, maxStashSize) != 0) {
374 LOG(ERROR) << "Failed to create empty file";
375 return USCRIPT_ERROR_EXECUTE;
376 }
377
378 LOG(INFO) << "Ready to start a thread to handle new data processing";
379 if (InitThread(infos, tm.get()) != 0) {
380 LOG(ERROR) << "Failed to create pthread";
381 return USCRIPT_ERROR_EXECUTE;
382 }
383 int32_t ret = DoExecuteUpdateBlock(infos, tm.get(), lines, targetPath, destImage);
384 return ret;
385 }
386
RestoreOriginalFile(const std::string & packagePath,const std::string & srcImage,const std::string & targetPath)387 int RestoreOriginalFile(const std::string &packagePath, const std::string &srcImage, const std::string &targetPath)
388 {
389 UpdateBlockInfo infos {};
390 if (GetUpdateBlockInfo(infos, packagePath, srcImage, targetPath) != 0) {
391 return -1;
392 }
393 std::string destName = GetFileName(srcImage);
394 std::string destImage = targetPath + "/" + destName;
395
396 PkgManager::PkgManagerPtr pkgManager = PkgManager::CreatePackageInstance();
397 if (pkgManager == nullptr) {
398 LOG(ERROR) << "pkgManager is nullptr";
399 return -1;
400 }
401
402 std::vector<std::string> components;
403 std::string pckPath = packagePath;
404 int32_t ret = pkgManager->LoadPackage(pckPath, components, PkgFile::PKG_TYPE_ZIP);
405 if (ret != PKG_SUCCESS) {
406 LOG(ERROR) << "Fail to load package";
407 return -1;
408 }
409 PostMessageFunction postMessage = nullptr;
410 UScriptEnv *env = new (std::nothrow) UpdaterEnv(pkgManager, postMessage, false);
411 if (env == nullptr) {
412 LOG(ERROR) << "Fail to creat env";
413 return -1;
414 }
415
416 int result = ExecuteUpdateBlock(*env, infos, targetPath, destImage);
417 if (result != 0) {
418 LOG(ERROR) << "restore original file fail.";
419 }
420 delete env;
421 env = nullptr;
422 return result;
423 }
424 }