1 /*
2 * Copyright (c) 2021 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 "update_image_block.h"
16 #include <cerrno>
17 #include <fcntl.h>
18 #include <pthread.h>
19 #include <sstream>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include "applypatch/block_set.h"
24 #include "applypatch/store.h"
25 #include "applypatch/transfer_manager.h"
26 #include "applypatch/partition_record.h"
27 #include "fs_manager/mount.h"
28 #include "log/dump.h"
29 #include "log/log.h"
30 #include "updater/updater_const.h"
31 #include "updater/hardware_fault_retry.h"
32 #include "utils.h"
33 #include "slot_info/slot_info.h"
34
35 using namespace Uscript;
36 using namespace Hpackage;
37 using namespace Updater;
38 using namespace Updater::Utils;
39
40 namespace Updater {
41 constexpr int32_t SHA_CHECK_SECOND = 2;
42 constexpr int32_t SHA_CHECK_PARAMS = 3;
43 constexpr int32_t SHA_CHECK_TARGETPAIRS_INDEX = 3;
44 constexpr int32_t SHA_CHECK_TARGETSHA_INDEX = 4;
45 constexpr int32_t SHA_CHECK_TARGET_PARAMS = 5;
46
GetWriteDevPath(const std::string & path,const std::string & partitionName,std::string & devPath)47 __attribute__((weak)) void GetWriteDevPath(const std::string &path, [[maybe_unused]] const std::string &partitionName,
48 std::string &devPath)
49 {
50 devPath = path;
51 LOG(INFO) << "GetWriteDevPath: " << devPath;
52 return;
53 }
54
SyncWriteDevPath(int fd,const std::string & partitionName)55 __attribute__((weak)) void SyncWriteDevPath(int fd, [[maybe_unused]] const std::string &partitionName)
56 {
57 if (fd < 0) {
58 LOG(ERROR) << "fd is invalid";
59 return;
60 }
61 if (fsync(fd) == -1) {
62 LOG(ERROR) << "fsync failed for " << partitionName;
63 }
64 return;
65 }
66
ExtractNewData(const PkgBuffer & buffer,size_t size,size_t start,bool isFinish,const void * context)67 static int ExtractNewData(const PkgBuffer &buffer, size_t size, size_t start, bool isFinish, const void* context)
68 {
69 void *p = const_cast<void *>(context);
70 WriterThreadInfo *info = static_cast<WriterThreadInfo *>(p);
71 uint8_t *addr = buffer.buffer;
72 while (size > 0) {
73 pthread_mutex_lock(&info->mutex);
74 while (info->writer == nullptr) {
75 if (!info->readyToWrite) {
76 LOG(WARNING) << "writer is not ready to write.";
77 pthread_mutex_unlock(&info->mutex);
78 return Hpackage::PKG_INVALID_STREAM;
79 }
80 pthread_cond_wait(&info->cond, &info->mutex);
81 }
82 pthread_mutex_unlock(&info->mutex);
83 size_t toWrite = std::min(size, info->writer->GetBlocksSize() - info->writer->GetTotalWritten());
84 // No more data to write.
85 if (toWrite == 0) {
86 break;
87 }
88 bool ret = info->writer->Write(addr, toWrite, nullptr);
89 std::ostringstream logMessage;
90 logMessage << "Write " << toWrite << " byte(s) failed";
91 if (!ret) {
92 LOG(ERROR) << logMessage.str();
93 return Hpackage::PKG_INVALID_STREAM;
94 }
95 size -= toWrite;
96 addr += toWrite;
97
98 if (info->writer->IsWriteDone()) {
99 pthread_mutex_lock(&info->mutex);
100 info->writer.reset();
101 pthread_cond_broadcast(&info->cond);
102 pthread_mutex_unlock(&info->mutex);
103 }
104 }
105 return Hpackage::PKG_SUCCESS;
106 }
107
CondBroadcast(WriterThreadInfo * info)108 static inline void CondBroadcast(WriterThreadInfo *info)
109 {
110 pthread_mutex_lock(&info->mutex);
111 info->readyToWrite = false;
112 if (info->writer != nullptr) {
113 pthread_cond_broadcast(&info->cond);
114 }
115 pthread_mutex_unlock(&info->mutex);
116 }
117
UnpackNewData(void * arg)118 void* UnpackNewData(void *arg)
119 {
120 TransferManagerPtr tm = static_cast<TransferManagerPtr>(arg);
121 WriterThreadInfo *info = tm->GetTransferParams()->writerThreadInfo.get();
122 Hpackage::PkgManager::StreamPtr stream = nullptr;
123 if (info->newPatch.empty()) {
124 LOG(ERROR) << "new patch file name is empty. thread quit.";
125 CondBroadcast(info);
126 return nullptr;
127 }
128 LOG(DEBUG) << "new patch file name: " << info->newPatch;
129 auto env = tm->GetTransferParams()->env;
130 const FileInfo *file = env->GetPkgManager()->GetFileInfo(info->newPatch);
131 if (file == nullptr) {
132 LOG(ERROR) << "Cannot get file info of :" << info->newPatch;
133 CondBroadcast(info);
134 return nullptr;
135 }
136 LOG(DEBUG) << info->newPatch << " info: size " << file->packedSize << " unpacked size " <<
137 file->unpackedSize << " name " << file->identity;
138 int32_t ret = env->GetPkgManager()->CreatePkgStream(stream, info->newPatch, ExtractNewData, info);
139 if (ret != Hpackage::PKG_SUCCESS || stream == nullptr) {
140 LOG(ERROR) << "Cannot extract " << info->newPatch << " from package.";
141 CondBroadcast(info);
142 return nullptr;
143 }
144 ret = env->GetPkgManager()->ExtractFile(info->newPatch, stream);
145 env->GetPkgManager()->ClosePkgStream(stream);
146 LOG(DEBUG) << "new data writer ending...";
147 // extract new data done.
148 // tell command.
149 CondBroadcast(info);
150 return nullptr;
151 }
152
ReturnAndPushParam(int32_t returnValue,Uscript::UScriptContext & context)153 static int32_t ReturnAndPushParam(int32_t returnValue, Uscript::UScriptContext &context)
154 {
155 context.PushParam(returnValue);
156 return returnValue;
157 }
158
GetUpdateBlockInfo(struct UpdateBlockInfo & infos,Uscript::UScriptEnv & env,Uscript::UScriptContext & context)159 static int32_t GetUpdateBlockInfo(struct UpdateBlockInfo &infos, Uscript::UScriptEnv &env,
160 Uscript::UScriptContext &context)
161 {
162 if (context.GetParamCount() != 4) { // 4:Determine the number of parameters
163 LOG(ERROR) << "Invalid param";
164 return ReturnAndPushParam(USCRIPT_INVALID_PARAM, context);
165 }
166
167 // Get partition Name first.
168 // Use partition name as zip file name. ${partition name}.zip
169 // load ${partition name}.zip from updater package.
170 // Try to unzip ${partition name}.zip, extract transfer.list, net.dat, patch.dat
171 size_t pos = 0;
172 int32_t ret = context.GetParam(pos++, infos.partitionName);
173 if (ret != USCRIPT_SUCCESS) {
174 LOG(ERROR) << "Error to get param 1";
175 return ret;
176 }
177 ret = context.GetParam(pos++, infos.transferName);
178 if (ret != USCRIPT_SUCCESS) {
179 LOG(ERROR) << "Error to get param 2";
180 return ret;
181 }
182 ret = context.GetParam(pos++, infos.newDataName);
183 if (ret != USCRIPT_SUCCESS) {
184 LOG(ERROR) << "Error to get param 3";
185 return ret;
186 }
187 ret = context.GetParam(pos++, infos.patchDataName);
188 if (ret != USCRIPT_SUCCESS) {
189 LOG(ERROR) << "Error to get param 4";
190 return ret;
191 }
192
193 LOG(INFO) << "ExecuteUpdateBlock::updating " << infos.partitionName << " ...";
194 infos.devPath = GetBlockDeviceByMountPoint(infos.partitionName);
195 #ifndef UPDATER_UT
196 if (infos.partitionName != "/userdata") {
197 std::string suffix = Utils::GetUpdateSuffix();
198 infos.devPath += suffix;
199 }
200 #else
201 infos.devPath = "/data/updater" + infos.partitionName;
202 #endif
203 LOG(INFO) << "ExecuteUpdateBlock::updating dev path : " << infos.devPath;
204 if (infos.devPath.empty()) {
205 LOG(ERROR) << "cannot get block device of partition";
206 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
207 }
208 return USCRIPT_SUCCESS;
209 }
210
ExecuteTransferCommand(int fd,const std::vector<std::string> & lines,TransferManagerPtr tm,Uscript::UScriptContext & context,const UpdateBlockInfo & infos)211 static int32_t ExecuteTransferCommand(int fd, const std::vector<std::string> &lines, TransferManagerPtr tm,
212 Uscript::UScriptContext &context, const UpdateBlockInfo &infos)
213 {
214 auto transferParams = tm->GetTransferParams();
215 auto writerThreadInfo = transferParams->writerThreadInfo.get();
216
217 transferParams->storeBase = std::string("/data/updater") + infos.partitionName + "_tmp";
218 transferParams->retryFile = std::string("/data/updater") + infos.partitionName + "_retry";
219 transferParams->devPath = infos.devPath;
220 LOG(INFO) << "Store base path is " << transferParams->storeBase;
221 int32_t ret = Store::CreateNewSpace(transferParams->storeBase, !transferParams->env->IsRetry());
222 if (ret == -1) {
223 LOG(ERROR) << "Error to create new store space";
224 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
225 }
226 transferParams->storeCreated = ret;
227
228 if (!tm->CommandsParser(fd, lines)) {
229 return USCRIPT_ERROR_EXECUTE;
230 }
231 pthread_mutex_lock(&writerThreadInfo->mutex);
232 if (writerThreadInfo->readyToWrite) {
233 LOG(WARNING) << "New data writer thread is still available...";
234 }
235
236 writerThreadInfo->readyToWrite = false;
237 pthread_cond_broadcast(&writerThreadInfo->cond);
238 pthread_mutex_unlock(&writerThreadInfo->mutex);
239 ret = pthread_join(transferParams->thread, nullptr);
240 std::ostringstream logMessage;
241 logMessage << "pthread join returned with " << ret;
242 if (ret != 0) {
243 LOG(WARNING) << logMessage.str();
244 }
245 if (transferParams->storeCreated != -1) {
246 Store::DoFreeSpace(transferParams->storeBase);
247 }
248 return USCRIPT_SUCCESS;
249 }
250
InitThread(const struct UpdateBlockInfo & infos,TransferManagerPtr tm)251 static int InitThread(const struct UpdateBlockInfo &infos, TransferManagerPtr tm)
252 {
253 auto transferParams = tm->GetTransferParams();
254 auto writerThreadInfo = transferParams->writerThreadInfo.get();
255 writerThreadInfo->readyToWrite = true;
256 pthread_mutex_init(&writerThreadInfo->mutex, nullptr);
257 pthread_cond_init(&writerThreadInfo->cond, nullptr);
258 pthread_attr_t attr;
259 pthread_attr_init(&attr);
260 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
261 writerThreadInfo->newPatch = infos.newDataName;
262 int error = pthread_create(&transferParams->thread, &attr, UnpackNewData, tm);
263 return error;
264 }
265
ExtractDiffPackageAndLoad(const UpdateBlockInfo & infos,Uscript::UScriptEnv & env,Uscript::UScriptContext & context)266 static int32_t ExtractDiffPackageAndLoad(const UpdateBlockInfo &infos, Uscript::UScriptEnv &env,
267 Uscript::UScriptContext &context)
268 {
269 Hpackage::PkgManager::StreamPtr outStream = nullptr;
270 LOG(DEBUG) << "partitionName is " << infos.partitionName;
271 const FileInfo *info = env.GetPkgManager()->GetFileInfo(infos.partitionName);
272 if (info == nullptr) {
273 LOG(WARNING) << "Error to get file info";
274 return USCRIPT_SUCCESS;
275 }
276 std::string diffPackage = std::string("/data/updater") + infos.partitionName;
277 int32_t ret = env.GetPkgManager()->CreatePkgStream(outStream,
278 diffPackage, info->unpackedSize, PkgStream::PkgStreamType_Write);
279 if (outStream == nullptr) {
280 LOG(ERROR) << "Error to create output stream";
281 return USCRIPT_ERROR_EXECUTE;
282 }
283
284 ret = env.GetPkgManager()->ExtractFile(infos.partitionName, outStream);
285 if (ret != USCRIPT_SUCCESS) {
286 LOG(ERROR) << "Error to extract file";
287 env.GetPkgManager()->ClosePkgStream(outStream);
288 return USCRIPT_ERROR_EXECUTE;
289 }
290 env.GetPkgManager()->ClosePkgStream(outStream);
291 std::string diffPackageZip = diffPackage + ".zip";
292 if (rename(diffPackage.c_str(), diffPackageZip.c_str()) != 0) {
293 LOG(ERROR) << "rename failed";
294 return USCRIPT_ERROR_EXECUTE;
295 }
296 LOG(DEBUG) << "Rename " << diffPackage << " to zip\nExtract " << diffPackage << " done\nReload " << diffPackageZip;
297 std::vector<std::string> diffPackageComponents;
298 ret = env.GetPkgManager()->LoadPackage(diffPackageZip, Updater::Utils::GetCertName(), diffPackageComponents);
299 if (diffPackageComponents.size() < 1) {
300 LOG(ERROR) << "Diff package is empty";
301 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
302 }
303 return USCRIPT_SUCCESS;
304 }
305
DoExecuteUpdateBlock(const UpdateBlockInfo & infos,TransferManagerPtr tm,Hpackage::PkgManager::StreamPtr & outStream,const std::vector<std::string> & lines,Uscript::UScriptContext & context)306 static int32_t DoExecuteUpdateBlock(const UpdateBlockInfo &infos, TransferManagerPtr tm,
307 Hpackage::PkgManager::StreamPtr &outStream, const std::vector<std::string> &lines, Uscript::UScriptContext &context)
308 {
309 std::string devPath = "";
310 GetWriteDevPath(infos.devPath, infos.partitionName, devPath);
311 int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE);
312 auto env = tm->GetTransferParams()->env;
313 if (fd == -1) {
314 LOG(ERROR) << "Failed to open block";
315 env->GetPkgManager()->ClosePkgStream(outStream);
316 return USCRIPT_ERROR_EXECUTE;
317 }
318 int32_t ret = ExecuteTransferCommand(fd, lines, tm, context, infos);
319 SyncWriteDevPath(fd, infos.partitionName);
320 close(fd);
321 fd = -1;
322 env->GetPkgManager()->ClosePkgStream(outStream);
323 if (ret == USCRIPT_SUCCESS) {
324 PartitionRecord::GetInstance().RecordPartitionUpdateStatus(infos.partitionName, true);
325 }
326 return ret;
327 }
328
ExtractFileByName(Uscript::UScriptEnv & env,const std::string & fileName,Hpackage::PkgManager::StreamPtr & outStream,uint8_t * & outBuf,size_t & buffSize)329 static int32_t ExtractFileByName(Uscript::UScriptEnv &env, const std::string &fileName,
330 Hpackage::PkgManager::StreamPtr &outStream, uint8_t *&outBuf, size_t &buffSize)
331 {
332 if (env.GetPkgManager() == nullptr) {
333 LOG(ERROR) << "Error to get pkg manager";
334 return USCRIPT_ERROR_EXECUTE;
335 }
336
337 const FileInfo *info = env.GetPkgManager()->GetFileInfo(fileName);
338 if (info == nullptr) {
339 LOG(ERROR) << "GetFileInfo fail";
340 return USCRIPT_ERROR_EXECUTE;
341 }
342 auto ret = env.GetPkgManager()->CreatePkgStream(outStream,
343 fileName, info->unpackedSize, PkgStream::PkgStreamType_MemoryMap);
344 if (ret != USCRIPT_SUCCESS || outStream == nullptr) {
345 LOG(ERROR) << "Error to create output stream";
346 return USCRIPT_ERROR_EXECUTE;
347 }
348 ret = env.GetPkgManager()->ExtractFile(fileName, outStream);
349 if (ret != USCRIPT_SUCCESS) {
350 LOG(ERROR) << "Error to extract file";
351 env.GetPkgManager()->ClosePkgStream(outStream);
352 return USCRIPT_ERROR_EXECUTE;
353 }
354 ret = outStream->GetBuffer(outBuf, buffSize);
355 LOG(DEBUG) << "outBuf data size is: " << buffSize;
356
357 return USCRIPT_SUCCESS;
358 }
359
ExtractPatchDatFile(Uscript::UScriptEnv & env,const UpdateBlockInfo & infos,Hpackage::PkgManager::StreamPtr & outStream,std::string & datFile)360 static int32_t ExtractPatchDatFile(Uscript::UScriptEnv &env, const UpdateBlockInfo &infos,
361 Hpackage::PkgManager::StreamPtr &outStream, std::string &datFile)
362 {
363 if (env.GetPkgManager() == nullptr) {
364 LOG(ERROR) << "Error to get pkg manager";
365 return USCRIPT_ERROR_EXECUTE;
366 }
367
368 if ((!Utils::IsDirExist(UPDATER_PATH)) && (Utils::MkdirRecursive(UPDATER_PATH, S_IRWXU) != 0)) {
369 LOG(ERROR) << "Failed to make path";
370 return USCRIPT_ERROR_EXECUTE;
371 }
372 std::string fileName = std::string(UPDATER_PATH) + "/" + infos.patchDataName;
373 auto ret = env.GetPkgManager()->CreatePkgStream(outStream, fileName, 0, PkgStream::PkgStreamType_Write);
374 if (ret != USCRIPT_SUCCESS || outStream == nullptr) {
375 LOG(ERROR) << "Error to create output stream";
376 return USCRIPT_ERROR_EXECUTE;
377 }
378 ret = env.GetPkgManager()->ExtractFile(infos.patchDataName, outStream);
379 if (ret != USCRIPT_SUCCESS) {
380 LOG(ERROR) << "Error to extract file";
381 env.GetPkgManager()->ClosePkgStream(outStream);
382 return USCRIPT_ERROR_EXECUTE;
383 }
384 datFile = fileName;
385 return USCRIPT_SUCCESS;
386 }
387
ExecuteUpdateBlock(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)388 static int32_t ExecuteUpdateBlock(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
389 {
390 UpdateBlockInfo infos {};
391 if (GetUpdateBlockInfo(infos, env, context) != USCRIPT_SUCCESS) {
392 return USCRIPT_ERROR_EXECUTE;
393 }
394
395 if (env.IsRetry()) {
396 LOG(DEBUG) << "Retry updater, check if current partition updatered already during last time";
397 if (PartitionRecord::GetInstance().IsPartitionUpdated(infos.partitionName)) {
398 LOG(INFO) << infos.partitionName << " already updated, skip";
399 return USCRIPT_SUCCESS;
400 }
401 }
402
403 if (ExtractDiffPackageAndLoad(infos, env, context) != USCRIPT_SUCCESS) {
404 return USCRIPT_ERROR_EXECUTE;
405 }
406
407 uint8_t *transferListBuffer = nullptr;
408 size_t transferListSize = 0;
409 Hpackage::PkgManager::StreamPtr outStream = nullptr;
410 if (ExtractFileByName(env, infos.transferName, outStream,
411 transferListBuffer, transferListSize) != USCRIPT_SUCCESS) {
412 return USCRIPT_ERROR_EXECUTE;
413 }
414
415 std::unique_ptr<TransferManager> tm = std::make_unique<TransferManager>();
416
417 auto transferParams = tm->GetTransferParams();
418 /* Save Script Env to transfer manager */
419 transferParams->env = &env;
420
421 transferParams->canWrite = true;
422 std::vector<std::string> lines =
423 Updater::Utils::SplitString(std::string(reinterpret_cast<const char*>(transferListBuffer)), "\n");
424 // Close stream opened before.
425 env.GetPkgManager()->ClosePkgStream(outStream);
426
427 LOG(INFO) << "Start unpack new data thread done. Get patch data: " << infos.patchDataName;
428 transferParams->isUpdaterMode = Utils::IsUpdaterMode();
429 int ret = transferParams->isUpdaterMode ? ExtractFileByName(env, infos.patchDataName, outStream,
430 transferParams->dataBuffer, transferParams->dataBufferSize) : ExtractPatchDatFile(env,
431 infos, outStream, transferParams->patchDatFile);
432 if (ret != USCRIPT_SUCCESS) {
433 return USCRIPT_ERROR_EXECUTE;
434 }
435
436 LOG(INFO) << "Ready to start a thread to handle new data processing";
437 if (InitThread(infos, tm.get()) != 0) {
438 LOG(ERROR) << "Failed to create pthread";
439 env.GetPkgManager()->ClosePkgStream(outStream);
440 return USCRIPT_ERROR_EXECUTE;
441 }
442 std::vector<pid_t> tids = GetAllTids(getpid());
443 env.PostMessage("set_binary_tids", VectorToString(tids));
444 return DoExecuteUpdateBlock(infos, tm.get(), outStream, lines, context);
445 }
446
Execute(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)447 int32_t UScriptInstructionBlockUpdate::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
448 {
449 int32_t result = ExecuteUpdateBlock(env, context);
450 context.PushParam(result);
451 return result;
452 }
453
ExecReadBlockInfo(const std::string & devPath,Uscript::UScriptContext & context,time_t & mountTime,uint16_t & mountCount)454 bool UScriptInstructionBlockCheck::ExecReadBlockInfo(const std::string &devPath, Uscript::UScriptContext &context,
455 time_t &mountTime, uint16_t &mountCount)
456 {
457 UPDATER_INIT_RECORD;
458 int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE);
459 if (fd == -1) {
460 LOG(ERROR) << "Failed to open file";
461 UPDATER_LAST_WORD("Failed to open file", devPath);
462 return false;
463 }
464 std::vector<uint8_t> block_buff(H_BLOCK_SIZE);
465 BlockSet blk0(std::vector<BlockPair> {BlockPair{0, 1}});
466
467 size_t pos = 0;
468 std::vector<BlockPair>::iterator it = blk0.Begin();
469 for (; it != blk0.End(); ++it) {
470 LOG(INFO) << "BlockSet::ReadDataFromBlock lseek64";
471 if (lseek64(fd, static_cast<off64_t>(it->first * H_BLOCK_SIZE), SEEK_SET) == -1) {
472 LOG(ERROR) << "Failed to seek";
473 close(fd);
474 UPDATER_LAST_WORD(false, "Failed to seek");
475 return false;
476 }
477 size_t size = (it->second - it->first) * H_BLOCK_SIZE;
478 LOG(INFO) << "BlockSet::ReadDataFromBlock Read " << size << " from block";
479 if (!Utils::ReadFully(fd, block_buff.data() + pos, size)) {
480 LOG(ERROR) << "Failed to read";
481 close(fd);
482 UPDATER_LAST_WORD(false, "Failed to read");
483 return false;
484 }
485 pos += size;
486 }
487 close(fd);
488 mountTime = *reinterpret_cast<uint32_t *>(&block_buff[0x400 + 0x2C]);
489 mountCount = *reinterpret_cast<uint16_t *>(&block_buff[0x400 + 0x34]);
490 return true;
491 }
492
Execute(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)493 int32_t UScriptInstructionBlockCheck::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
494 {
495 UPDATER_INIT_RECORD;
496 if (context.GetParamCount() != 1) {
497 LOG(ERROR) << "Invalid param";
498 UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
499 return ReturnAndPushParam(USCRIPT_INVALID_PARAM, context);
500 }
501 if (env.IsRetry()) {
502 return ReturnAndPushParam(USCRIPT_SUCCESS, context);
503 }
504 std::string partitionName;
505 int32_t ret = context.GetParam(0, partitionName);
506 if (ret != USCRIPT_SUCCESS) {
507 LOG(ERROR) << "Failed to get param";
508 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, "Failed to get param " + partitionName);
509 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
510 }
511 auto devPath = GetBlockDeviceByMountPoint(partitionName);
512 #ifndef UPDATER_UT
513 if (partitionName != "/userdata") {
514 std::string suffix = Utils::GetUpdateSuffix();
515 devPath += suffix;
516 }
517 #else
518 devPath = "/data/updater" + partitionName;
519 #endif
520 LOG(INFO) << "UScriptInstructionBlockCheck::dev path : " << devPath;
521 time_t mountTime = 0;
522 uint16_t mountCount = 0;
523 if (devPath.empty() || (!ExecReadBlockInfo(devPath, context, mountTime, mountCount))) {
524 LOG(ERROR) << "cannot get block device of partition";
525 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, devPath);
526 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
527 }
528
529 if (mountCount > 0) {
530 std::ostringstream ostr;
531 ostr << "Device was remounted R/W " << mountCount << "times\nLast remount happened on " <<
532 ctime(&mountTime) << std::endl;
533 std::string message = ostr.str();
534 env.PostMessage("ui_log", message);
535 LOG(ERROR) << message;
536 }
537 LOG(INFO) << "UScriptInstructionBlockCheck::Execute Success";
538 context.PushParam(USCRIPT_SUCCESS);
539 return USCRIPT_SUCCESS;
540 }
541
GetPartName(const std::string & partitionName)542 static std::string GetPartName(const std::string &partitionName)
543 {
544 if (partitionName.empty()) {
545 return "";
546 }
547 return partitionName[0] == '/' ? partitionName.substr(1) : partitionName;
548 }
549
DoBlocksVerify(Uscript::UScriptEnv & env,const std::string & partitionName,const std::string & devPath)550 int32_t UScriptInstructionShaCheck::DoBlocksVerify(Uscript::UScriptEnv &env, const std::string &partitionName,
551 const std::string &devPath)
552 {
553 UpdateBlockInfo infos {};
554 infos.partitionName = partitionName;
555 infos.transferName = GetPartName(partitionName) + ".transfer.list";
556 uint8_t *transferListBuffer = nullptr;
557 size_t transferListSize = 0;
558 Hpackage::PkgManager::StreamPtr outStream = nullptr;
559
560 if (ExtractFileByName(env, infos.transferName, outStream,
561 transferListBuffer, transferListSize) != USCRIPT_SUCCESS) {
562 LOG(ERROR) << "Error to extract " << infos.transferName;
563 return USCRIPT_ERROR_EXECUTE;
564 }
565
566 std::vector<std::string> lines =
567 Updater::Utils::SplitString(std::string(reinterpret_cast<const char*>(transferListBuffer)), "\n");
568 // Close stream opened before.
569 env.GetPkgManager()->ClosePkgStream(outStream);
570
571 std::unique_ptr<TransferManager> tm = std::make_unique<TransferManager>();
572 auto transferParams = tm->GetTransferParams();
573 transferParams->env = &env;
574 transferParams->canWrite = false;
575 transferParams->devPath = devPath;
576 transferParams->storeBase = std::string("/data/updater") + partitionName + "_tmp";
577 transferParams->retryFile = std::string("/data/updater") + partitionName + "_retry";
578
579 LOG(INFO) << "Store base path is " << transferParams->storeBase;
580 transferParams->storeCreated = Store::CreateNewSpace(transferParams->storeBase, !transferParams->env->IsRetry());
581 if (transferParams->storeCreated == -1) {
582 LOG(ERROR) << "Error to create new store space";
583 return USCRIPT_ERROR_EXECUTE;
584 }
585 int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE);
586 if (fd == -1) {
587 LOG(ERROR) << "Failed to open block";
588 return USCRIPT_ERROR_EXECUTE;
589 }
590 if (!tm->CommandsParser(fd, lines)) {
591 close(fd);
592 LOG(ERROR) << "Failed to block verify";
593 return USCRIPT_ERROR_EXECUTE;
594 }
595 if (transferParams->storeCreated != -1) {
596 Store::DoFreeSpace(transferParams->storeBase);
597 }
598 close(fd);
599 return USCRIPT_SUCCESS;
600 }
601
IsTargetShaDiff(const std::string & devPath,const ShaInfo & shaInfo)602 bool UScriptInstructionShaCheck::IsTargetShaDiff(const std::string &devPath, const ShaInfo &shaInfo)
603 {
604 std::string tgtResultSha = CalculateBlockSha(devPath, shaInfo.targetPairs);
605 if (tgtResultSha.empty()) {
606 LOG(WARNING) << "target sha is empty";
607 return true;
608 }
609 LOG(INFO) << "tgtResultSha: " << tgtResultSha << ", shaInfo.targetSha: " << shaInfo.targetSha;
610 return (tgtResultSha != shaInfo.targetSha);
611 }
612
ExecReadShaInfo(Uscript::UScriptEnv & env,const std::string & devPath,const ShaInfo & shaInfo,const std::string & partitionName)613 int UScriptInstructionShaCheck::ExecReadShaInfo(Uscript::UScriptEnv &env, const std::string &devPath,
614 const ShaInfo &shaInfo, const std::string &partitionName)
615 {
616 UPDATER_INIT_RECORD;
617 std::string resultSha = CalculateBlockSha(devPath, shaInfo.blockPairs);
618 if (resultSha != shaInfo.contrastSha && IsTargetShaDiff(devPath, shaInfo)) {
619 if (DoBlocksVerify(env, partitionName, devPath) != USCRIPT_SUCCESS) {
620 LOG(ERROR) << "Different sha256, cannot continue";
621 LOG(ERROR) << "blockPairs:" << shaInfo.blockPairs;
622 LOG(ERROR) << "resultSha: " << resultSha << ", shaInfo.contrastSha: " << shaInfo.contrastSha;
623 PrintAbnormalBlockHash(devPath, shaInfo.blockPairs);
624 UPDATER_LAST_WORD(devPath.substr(devPath.find_last_of("/") + 1), USCRIPT_ERROR_EXECUTE);
625 env.PostMessage(UPDATER_RETRY_TAG, VERIFY_FAILED_REBOOT);
626 return USCRIPT_ERROR_EXECUTE;
627 }
628 }
629 LOG(INFO) << "UScriptInstructionShaCheck::Execute Success";
630 return USCRIPT_SUCCESS;
631 }
632
PrintAbnormalBlockHash(const std::string & devPath,const std::string & blockPairs)633 void UScriptInstructionShaCheck::PrintAbnormalBlockHash(const std::string &devPath, const std::string &blockPairs)
634 {
635 int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE);
636 if (fd == -1) {
637 LOG(ERROR) << "Failed to open file " << devPath;
638 return;
639 }
640
641 BlockSet blk;
642 blk.ParserAndInsert(blockPairs);
643 std::vector<uint8_t> block_buff(H_BLOCK_SIZE);
644 std::vector<BlockPair>::iterator it = blk.Begin();
645 for (; it != blk.End(); ++it) {
646 if (lseek64(fd, static_cast<off64_t>(it->first * H_BLOCK_SIZE), SEEK_SET) == -1) {
647 LOG(ERROR) << "Failed to seek";
648 close(fd);
649 return;
650 }
651 SHA256_CTX ctx;
652 SHA256_Init(&ctx);
653 for (size_t i = it->first; i < it->second; ++i) {
654 if (!Utils::ReadFully(fd, block_buff.data(), H_BLOCK_SIZE)) {
655 LOG(ERROR) << "Failed to read";
656 close(fd);
657 return;
658 }
659 SHA256_Update(&ctx, block_buff.data(), H_BLOCK_SIZE);
660 }
661 uint8_t digest[SHA256_DIGEST_LENGTH] = {0};
662 SHA256_Final(digest, &ctx);
663 LOG(ERROR) << "block id:" << it->first << "-" << it->second <<
664 " hex:" << Utils::ConvertSha256Hex(digest, SHA256_DIGEST_LENGTH);
665 }
666 close(fd);
667 }
668
CalculateBlockSha(const std::string & devPath,const std::string & blockPairs)669 std::string UScriptInstructionShaCheck::CalculateBlockSha(const std::string &devPath, const std::string &blockPairs)
670 {
671 UPDATER_INIT_RECORD;
672 if (blockPairs.empty()) {
673 LOG(ERROR) << "Failed to get blockPairs";
674 return "";
675 }
676
677 int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE);
678 if (fd == -1) {
679 LOG(ERROR) << "Failed to open file";
680 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, devPath);
681 return "";
682 }
683
684 BlockSet blk;
685 blk.ParserAndInsert(blockPairs);
686 std::vector<uint8_t> block_buff(H_BLOCK_SIZE);
687 SHA256_CTX ctx;
688 SHA256_Init(&ctx);
689 std::vector<BlockPair>::iterator it = blk.Begin();
690 for (; it != blk.End(); ++it) {
691 if (lseek64(fd, static_cast<off64_t>(it->first * H_BLOCK_SIZE), SEEK_SET) == -1) {
692 LOG(ERROR) << "Failed to seek";
693 close(fd);
694 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, "Failed to seek");
695 return "";
696 }
697 for (size_t i = it->first; i < it->second; ++i) {
698 if (!Utils::ReadFully(fd, block_buff.data(), H_BLOCK_SIZE)) {
699 LOG(ERROR) << "Failed to read";
700 close(fd);
701 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, "Failed to read");
702 return "";
703 }
704 SHA256_Update(&ctx, block_buff.data(), H_BLOCK_SIZE);
705 }
706 }
707 close(fd);
708
709 uint8_t digest[SHA256_DIGEST_LENGTH] = {0};
710 SHA256_Final(digest, &ctx);
711 return Utils::ConvertSha256Hex(digest, SHA256_DIGEST_LENGTH);
712 }
713
SetShaInfo(Uscript::UScriptContext & context,ShaInfo & shaInfo)714 int32_t UScriptInstructionShaCheck::SetShaInfo(Uscript::UScriptContext &context, ShaInfo &shaInfo)
715 {
716 UPDATER_INIT_RECORD;
717 int32_t ret = context.GetParam(1, shaInfo.blockPairs);
718 if (ret != USCRIPT_SUCCESS) {
719 LOG(ERROR) << "Failed to get param blockPairs";
720 UPDATER_LAST_WORD(ret, "Failed to get param blockPairs");
721 return USCRIPT_ERROR_EXECUTE;
722 }
723
724 ret = context.GetParam(SHA_CHECK_SECOND, shaInfo.contrastSha);
725 if (ret != USCRIPT_SUCCESS) {
726 LOG(ERROR) << "Failed to get param contrastSha";
727 UPDATER_LAST_WORD(ret, "Failed to get param contrastSha");
728 return USCRIPT_ERROR_EXECUTE;
729 }
730
731 // Only three parameters can be obtained for the upgrade package of an earlier version.
732 ret = context.GetParam(SHA_CHECK_TARGETPAIRS_INDEX, shaInfo.targetPairs);
733 if (ret != USCRIPT_SUCCESS) {
734 LOG(WARNING) << "Failed to get param targetPairs";
735 }
736
737 ret = context.GetParam(SHA_CHECK_TARGETSHA_INDEX, shaInfo.targetSha);
738 if (ret != USCRIPT_SUCCESS) {
739 LOG(WARNING) << "Failed to get param targetSha";
740 }
741
742 return USCRIPT_SUCCESS;
743 }
744
Execute(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)745 int32_t UScriptInstructionShaCheck::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
746 {
747 UPDATER_INIT_RECORD;
748 int32_t paramCount = context.GetParamCount();
749 if (paramCount != SHA_CHECK_PARAMS && paramCount != SHA_CHECK_TARGET_PARAMS) {
750 LOG(ERROR) << "Invalid param";
751 UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM, "Invalid param");
752 return ReturnAndPushParam(USCRIPT_INVALID_PARAM, context);
753 }
754 if (env.IsRetry() && !Utils::CheckFaultInfo(VERIFY_FAILED_REBOOT)) {
755 return ReturnAndPushParam(USCRIPT_SUCCESS, context);
756 }
757
758 std::string partitionName;
759 int32_t ret = context.GetParam(0, partitionName);
760 if (ret != USCRIPT_SUCCESS) {
761 LOG(ERROR) << "Failed to get param";
762 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, "Failed to get param");
763 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
764 }
765
766 ShaInfo shaInfo {};
767 ret = SetShaInfo(context, shaInfo);
768 if (ret != USCRIPT_SUCCESS) {
769 LOG(ERROR) << "Failed to set sha info";
770 return ReturnAndPushParam(ret, context);
771 }
772
773 auto devPath = GetBlockDeviceByMountPoint(partitionName);
774 LOG(INFO) << "UScriptInstructionShaCheck::dev path : " << devPath;
775 if (devPath.empty()) {
776 LOG(ERROR) << "cannot get block device of partition";
777 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, "cannot get block device of partition");
778 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
779 }
780 #ifndef UPDATER_UT
781 if (partitionName != "/userdata") {
782 std::string suffix = Utils::GetUpdateSuffix();
783 devPath += suffix;
784 }
785 LOG(INFO) << "write partition path: " << devPath;
786 #else
787 devPath = "/data/updater" + partitionName;
788 #endif
789 ret = ExecReadShaInfo(env, devPath, shaInfo, partitionName);
790 return ReturnAndPushParam(ret, context);
791 }
792 }
793