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