1 /*
2 * Copyright (c) 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 "module_update.h"
17
18 #include <chrono>
19 #include <sys/mount.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <thread>
23
24 #include "directory_ex.h"
25 #include "log/log.h"
26 #include "module_constants.h"
27 #include "module_dm.h"
28 #include "module_error_code.h"
29 #include "module_file_repository.h"
30 #include "module_loop.h"
31 #include "module_update_task.h"
32 #include "module_utils.h"
33 #include "parse_util.h"
34 #include "scope_guard.h"
35 #include "string_ex.h"
36 #include "utils.h"
37
38 #ifdef SUPPORT_HVB
39 #include "fs_hvb.h"
40 #endif
41
42 namespace OHOS {
43 namespace SysInstaller {
44 using namespace Updater;
45 using std::string;
46
47 namespace {
48 constexpr mode_t MOUNT_POINT_MODE = 0755;
49 constexpr int32_t LOOP_DEVICE_SETUP_ATTEMPTS = 3;
50
CreateLoopDevice(const string & path,const ImageStat & imageStat,Loop::LoopbackDeviceUniqueFd & loopbackDevice)51 bool CreateLoopDevice(const string &path, const ImageStat &imageStat, Loop::LoopbackDeviceUniqueFd &loopbackDevice)
52 {
53 for (int32_t attempts = 1; attempts <= LOOP_DEVICE_SETUP_ATTEMPTS; ++attempts) {
54 std::unique_ptr<Loop::LoopbackDeviceUniqueFd> device =
55 Loop::CreateLoopDevice(path, imageStat.imageOffset, imageStat.imageSize);
56 if (device != nullptr) {
57 loopbackDevice = std::move(*device);
58 break;
59 }
60 }
61 return loopbackDevice.deviceFd.Get() != -1;
62 }
63
StageUpdateModulePackage(const string & updatePath,const string & stagePath)64 bool StageUpdateModulePackage(const string &updatePath, const string &stagePath)
65 {
66 int ret = 0;
67 if (CheckPathExists(stagePath)) {
68 LOG(INFO) << stagePath << " already exists. Deleting";
69 ret = unlink(stagePath.c_str());
70 if (ret != 0) {
71 LOG(ERROR) << "Failed to unlink " << stagePath;
72 return false;
73 }
74 }
75 string path = ExtractFilePath(stagePath);
76 // active dir should create by module_update_sa
77 if (!CheckPathExists(path)) {
78 LOG(ERROR) << path << " doesn't exist.";
79 return false;
80 }
81 ret = link(updatePath.c_str(), stagePath.c_str());
82 if (ret != 0) {
83 LOG(ERROR) << "Unable to link " << updatePath << " to " << stagePath;
84 return false;
85 }
86
87 // stage other files
88 std::vector<std::string> files;
89 if (Updater::Utils::GetFilesFromDirectory(updatePath.substr(0, updatePath.rfind("/")), files, true) <= 0) {
90 LOG(ERROR) << "Failed to get files form " << updatePath;
91 return false;
92 }
93 for (const auto &file : files) {
94 std::string targetFile = path + ExtractFileName(file);
95 (void)unlink(targetFile.c_str());
96 ret = link(file.c_str(), targetFile.c_str());
97 if (ret != 0) {
98 LOG(ERROR) << "Unable to link " << file << " to " << targetFile;
99 return false;
100 }
101 }
102 LOG(INFO) << "success to link " << updatePath << " to " << stagePath;
103 return true;
104 }
105
CheckModulePackage(const std::string & mountPoint,const ModuleFile & moduleFile)106 bool CheckModulePackage(const std::string &mountPoint, const ModuleFile &moduleFile)
107 {
108 if (!IsEmptyFolder(mountPoint)) {
109 LOG(ERROR) << mountPoint << " is not empty";
110 return false;
111 }
112 if (!moduleFile.GetImageStat().has_value()) {
113 LOG(ERROR) << "Could not mount empty module package " << moduleFile.GetPath();
114 return false;
115 }
116 return true;
117 }
118
VerifyAndCreateDm(ModuleFile & moduleFile,string & blockDevice)119 bool VerifyAndCreateDm(ModuleFile &moduleFile, string &blockDevice)
120 {
121 LOG(ERROR) << "Verify and create dm.";
122 if (!moduleFile.VerifyModuleVerity()) {
123 LOG(ERROR) << "verify image failed of " << moduleFile.GetPath();
124 return false;
125 }
126 if (!CreateDmDevice(moduleFile, blockDevice)) {
127 LOG(ERROR) << "Could not create dm-verity device on " << blockDevice;
128 Loop::ClearDmLoopDevice(blockDevice, false);
129 return false;
130 }
131 return true;
132 }
133 }
134
GetInstance()135 ModuleUpdate &ModuleUpdate::GetInstance()
136 {
137 static ModuleUpdate instance;
138 return instance;
139 }
140
GetLatestUpdateModulePackage(const string & hmpName)141 std::unique_ptr<ModuleFile> ModuleUpdate::GetLatestUpdateModulePackage(const string &hmpName)
142 {
143 std::unique_ptr<ModuleFile> activeModuleFile = repository_.GetModuleFile(UPDATE_ACTIVE_DIR, hmpName);
144 std::unique_ptr<ModuleFile> updateModuleFile = repository_.GetModuleFile(UPDATE_INSTALL_DIR, hmpName);
145 std::unique_ptr<ModuleFile> ret = nullptr;
146 if (updateModuleFile != nullptr) {
147 if (activeModuleFile == nullptr || ModuleFile::CompareVersion(*updateModuleFile, *activeModuleFile)) {
148 string updatePath = updateModuleFile->GetPath();
149 string activePath = UPDATE_ACTIVE_DIR +
150 updatePath.substr(strlen(UPDATE_INSTALL_DIR), updatePath.length());
151 if (!StageUpdateModulePackage(updatePath, activePath)) {
152 return ret;
153 }
154 updateModuleFile->SetPath(activePath);
155 ret = std::move(updateModuleFile);
156 LOG(INFO) << "add updateModuleFile " << updatePath;
157 }
158 }
159 if (ret == nullptr && activeModuleFile != nullptr) {
160 LOG(INFO) << "add activeModuleFile " << activeModuleFile->GetPath();
161 ret = std::move(activeModuleFile);
162 }
163 return ret;
164 }
165
CheckMountComplete(const string & hmpName) const166 bool ModuleUpdate::CheckMountComplete(const string &hmpName) const
167 {
168 string path = std::string(MODULE_ROOT_DIR) + "/" + hmpName;
169 return CheckPathExists(path);
170 }
171
ProcessHmpFile(const string & hmpFile,const ModuleUpdateStatus & status,const Timer & timer)172 void ModuleUpdate::ProcessHmpFile(const string &hmpFile, const ModuleUpdateStatus &status, const Timer &timer)
173 {
174 LOG(INFO) << "process hmp file=" << hmpFile;
175 std::unique_ptr<ModuleFile> moduleFile = ModuleFile::Open(hmpFile);
176 if (moduleFile == nullptr) {
177 LOG(ERROR) << "Process hmp file fail, module file is null";
178 return;
179 }
180 if (CheckMountComplete(status.hmpName)) {
181 LOG(INFO) << "Check mount complete, hmpName=" << status.hmpName;
182 return;
183 }
184 repository_.InitRepository(status.hmpName, timer);
185 PrepareModuleFileList(status);
186 }
187
DoModuleUpdate(ModuleUpdateStatus & status)188 bool ModuleUpdate::DoModuleUpdate(ModuleUpdateStatus &status)
189 {
190 LOG(INFO) << "enter domoduleupdate";
191 Timer timer;
192 std::string hmpPackagePath = std::string(MODULE_PREINSTALL_DIR) + "/" + status.hmpName;
193 LOG(INFO) << "DoModuleUpdate hmp package path=" << hmpPackagePath;
194 std::vector<std::string> files;
195 GetDirFiles(hmpPackagePath, files);
196 ON_SCOPE_EXIT(clear) {
197 repository_.Clear();
198 moduleFileList_.clear();
199 };
200 for (auto &file : files) {
201 std::string hmpPackage = GetFileName(file);
202 if (!CheckFileSuffix(file, MODULE_PACKAGE_SUFFIX) || hmpPackage.empty()) {
203 continue;
204 }
205 ProcessHmpFile(file, status, timer);
206 }
207 if (moduleFileList_.size() != 1) {
208 LOG(INFO) << status.hmpName << " module size is invalid: " << moduleFileList_.size();
209 return false;
210 }
211 if (!Loop::PreAllocateLoopDevices(moduleFileList_.size())) {
212 LOG(ERROR) << "Failed to pre allocate loop devices, hmp package name=" << status.hmpName;
213 return false;
214 }
215 if (!ActivateModules(status, timer)) {
216 LOG(ERROR) << "Failed to activate modules, hmp package name=" << status.hmpName;
217 return false;
218 }
219 LOG(INFO) << "Success to activate modules, hmp package name=" << status.hmpName;
220 return true;
221 }
222
CheckModuleUpdate()223 void ModuleUpdate::CheckModuleUpdate()
224 {
225 InitUpdaterLogger("CheckModuleUpdate", MODULE_UPDATE_LOG_FILE, "", "");
226 LOG(INFO) << "CheckModuleUpdate begin";
227 Timer timer;
228 std::vector<std::string> files;
229 std::unordered_set<std::string> hmpNameSet;
230 GetDirFiles(MODULE_PREINSTALL_DIR, files);
231 for (auto &file : files) {
232 if (!CheckFileSuffix(file, MODULE_PACKAGE_SUFFIX)) {
233 continue;
234 }
235 std::unique_ptr<ModuleFile> moduleFile = ModuleFile::Open(file);
236 if (moduleFile == nullptr) {
237 continue;
238 }
239 std::string hmpName = GetHmpName(file);
240 if (hmpName.empty()) {
241 continue;
242 }
243 hmpNameSet.emplace(hmpName);
244 }
245 auto &instance = ModuleUpdateTaskManager::GetInstance();
246 instance.Start();
247 ON_SCOPE_EXIT(clear) {
248 instance.ClearTask();
249 instance.Stop();
250 LOG(INFO) << "CheckModuleUpdate done, duration=" << timer;
251 if (!ForceRemoveDirectory(UPDATE_INSTALL_DIR)) {
252 LOG(ERROR) << "Failed to remove " << UPDATE_INSTALL_DIR << " err=" << errno;
253 }
254 };
255 if (!hmpNameSet.empty()) {
256 MountModuleUpdateDir();
257 }
258 for (auto &hmpName : hmpNameSet) {
259 instance.AddTask(hmpName);
260 }
261 while (instance.GetCurTaskNum() != 0) {
262 usleep(3000); // 3000: 3ms
263 }
264 }
265
CheckRevert(const std::string & hmpName)266 bool ModuleUpdate::CheckRevert(const std::string &hmpName)
267 {
268 if (!CheckPathExists(std::string(UPDATE_BACKUP_DIR) + "/" + hmpName)) {
269 return false;
270 }
271 auto &moduleMap = repository_.GetModuleMap();
272 for (const auto &[key, value] : moduleMap) {
273 if (key != hmpName) {
274 continue;
275 }
276 // only system hmp
277 if (value.size() == 1) {
278 LOG(ERROR) << "active dir destroyed, but backup dir exists, try to revert.";
279 return true;
280 }
281 }
282 return false;
283 }
284
PrepareModuleFileList(const ModuleUpdateStatus & status)285 void ModuleUpdate::PrepareModuleFileList(const ModuleUpdateStatus &status)
286 {
287 std::unique_ptr<ModuleFile> systemModuleFile = repository_.GetModuleFile(MODULE_PREINSTALL_DIR, status.hmpName);
288 if (systemModuleFile == nullptr) {
289 LOG(ERROR) << "Failed to get preinstalled hmp " << status.hmpName;
290 return;
291 }
292 std::unique_ptr<ModuleFile> latestModuleFile = GetLatestUpdateModulePackage(status.hmpName);
293 if (latestModuleFile != nullptr && ModuleFile::CompareVersion(*latestModuleFile, *systemModuleFile)) {
294 moduleFileList_.emplace_back(std::move(*latestModuleFile));
295 } else {
296 moduleFileList_.emplace_back(std::move(*systemModuleFile));
297 if (CheckRevert(status.hmpName)) {
298 LOG(ERROR) << "some error happened, revert.";
299 NotifyBmsRevert(status.hmpName, true);
300 Revert(status.hmpName, true);
301 return;
302 }
303 // when choose preInstall hmp, remove activeHmp and backupHmp
304 RemoveSpecifiedDir(std::string(UPDATE_ACTIVE_DIR) + "/" + status.hmpName, false);
305 RemoveSpecifiedDir(std::string(UPDATE_BACKUP_DIR) + "/" + status.hmpName, false);
306 }
307 }
308
ActivateModules(ModuleUpdateStatus & status,const Timer & timer)309 bool ModuleUpdate::ActivateModules(ModuleUpdateStatus &status, const Timer &timer)
310 {
311 // size = 1
312 for (auto &moduleFile : moduleFileList_) {
313 if (!moduleFile.GetImageStat().has_value()) {
314 LOG(INFO) << moduleFile.GetPath() << " is empty module package";
315 continue;
316 }
317 status.isPreInstalled = repository_.IsPreInstalledModule(moduleFile);
318 status.isAllMountSuccess = MountModulePackage(moduleFile, !status.isPreInstalled);
319 if (!status.isAllMountSuccess) {
320 LOG(ERROR) << "Failed to mount module package " << moduleFile.GetPath();
321 repository_.SaveInstallerResult(moduleFile.GetPath(), status.hmpName,
322 ERR_INSTALL_FAIL, "mount fail", timer);
323 }
324 // bugfix: when sise = 1, for() find the second item
325 break;
326 }
327 ReportModuleUpdateStatus(status);
328 LOG(INFO) << "ActivateModule mount result:" << status.isAllMountSuccess << ", hmp package name:" << status.hmpName;
329 return status.isAllMountSuccess;
330 }
331
WaitDevice(const std::string & blockDevice) const332 void ModuleUpdate::WaitDevice(const std::string &blockDevice) const
333 {
334 const int waitTime = 150; // wait max 3s
335 int time = 0;
336 while (!CheckPathExists(blockDevice) && time++ < waitTime) {
337 usleep(20000); // 20000: 20ms
338 }
339 }
340
VerifyImageAndCreateDm(ModuleFile & moduleFile,bool mountOnVerity,string & blockDevice)341 bool ModuleUpdate::VerifyImageAndCreateDm(ModuleFile &moduleFile, bool mountOnVerity, string &blockDevice)
342 {
343 if (!mountOnVerity) {
344 LOG(INFO) << "Current hmp path is preInstalled, do not check.";
345 return true;
346 }
347 if (ImageVerifyFunc_ != nullptr) {
348 return ImageVerifyFunc_(moduleFile, blockDevice);
349 }
350 LOG(ERROR) << "ImageVerifyFunc_ is nullptr, error.";
351 return false;
352 }
353
CreateMountPoint(const ModuleFile & moduleFile) const354 string ModuleUpdate::CreateMountPoint(const ModuleFile &moduleFile) const
355 {
356 string mountPoint = string(MODULE_ROOT_DIR);
357 if (moduleFile.GetVersionInfo().type != HMP_TRAIN_TYPE) {
358 mountPoint = string(MODULE_ROOT_DIR) + "/" + moduleFile.GetVersionInfo().hmpName;
359 }
360 LOG(INFO) << "Creating mount point: " << mountPoint;
361 if (!CreateDirIfNeeded(mountPoint, MOUNT_POINT_MODE)) {
362 LOG(ERROR) << "Could not create mount point " << mountPoint << " errno: " << errno;
363 return "";
364 }
365 return mountPoint;
366 }
367
MountModulePackage(ModuleFile & moduleFile,const bool mountOnVerity)368 bool ModuleUpdate::MountModulePackage(ModuleFile &moduleFile, const bool mountOnVerity)
369 {
370 string mountPoint = CreateMountPoint(moduleFile);
371 if (mountPoint.empty()) {
372 return false;
373 }
374 Timer timer;
375 int ret = 0;
376 ON_SCOPE_EXIT(rmDir) {
377 ret = rmdir(mountPoint.c_str());
378 if (ret != 0) {
379 LOG(WARNING) << "Could not rmdir " << mountPoint << " errno: " << errno;
380 }
381 };
382 if (!CheckModulePackage(mountPoint, moduleFile)) {
383 return false;
384 }
385 const string &fullPath = ExtractFilePath(moduleFile.GetPath()) + IMG_FILE_NAME;
386 const ImageStat &imageStat = moduleFile.GetImageStat().value();
387 Loop::LoopbackDeviceUniqueFd loopbackDevice;
388 if (!CreateLoopDevice(fullPath, imageStat, loopbackDevice)) {
389 LOG(ERROR) << "Could not create loop device for " << fullPath;
390 return false;
391 }
392 LOG(INFO) << "Loopback device created: " << loopbackDevice.name << " fsType=" << imageStat.fsType;
393 string blockDevice = loopbackDevice.name;
394 if (!VerifyImageAndCreateDm(moduleFile, mountOnVerity, blockDevice)) {
395 return false;
396 }
397 WaitDevice(blockDevice);
398 uint32_t mountFlags = MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY;
399 ret = mount(blockDevice.c_str(), mountPoint.c_str(), imageStat.fsType, mountFlags, nullptr);
400 if (ret != 0) {
401 LOG(ERROR) << "Mounting failed for module package " << fullPath << " errno:" << errno;
402 Loop::ClearDmLoopDevice(blockDevice, true);
403 return false;
404 }
405 LOG(INFO) << "Successfully mounted module package " << fullPath << " on " << mountPoint << " duration=" << timer;
406 SetModuleVersion(moduleFile);
407 loopbackDevice.CloseGood();
408 CANCEL_SCOPE_EXIT_GUARD(rmDir);
409 return true;
410 }
411
ReportModuleUpdateStatus(const ModuleUpdateStatus & status) const412 void ModuleUpdate::ReportModuleUpdateStatus(const ModuleUpdateStatus &status) const
413 {
414 auto &instance = ModuleUpdateTaskManager::GetInstance();
415 if (!instance.GetTaskResult()) {
416 LOG(ERROR) << "ReportModuleUpdateStatus, module update fail";
417 instance.ClearTask();
418 }
419 if (!status.isAllMountSuccess) {
420 LOG(ERROR) << "ReportModuleUpdateStatus mount fail, hmp name=" << status.hmpName;
421 RemoveSpecifiedDir(std::string(UPDATE_INSTALL_DIR) + "/" + status.hmpName, false);
422 NotifyBmsRevert(status.hmpName, true);
423 Revert(status.hmpName, true);
424 return;
425 }
426 LOG(INFO) << "ReportModuleUpdateStatus mount success, hmp name=" << status.hmpName;
427 }
428
RegisterImageVerifyFunc(ImageVerifyFunc ptr,int32_t level)429 void ModuleUpdate::RegisterImageVerifyFunc(ImageVerifyFunc ptr, int32_t level)
430 {
431 if (level <= registeredLevel_) {
432 LOG(WARNING) << "register level is smaller, " << level;
433 return;
434 }
435 registeredLevel_ = level;
436 ImageVerifyFunc_ = std::move(ptr);
437 }
438
RegisterImgVerifyFunc(void)439 extern "C" __attribute__((constructor)) void RegisterImgVerifyFunc(void)
440 {
441 ModuleUpdate::GetInstance().RegisterImageVerifyFunc(VerifyAndCreateDm, REGISTER_LEVEL_ONE);
442 }
443
SetParameterFromFile(void) const444 void ModuleUpdate::SetParameterFromFile(void) const
445 {
446 const std::chrono::milliseconds waitTime(100); // 100ms: wait for file create
447 if (!WaitForFile(MODULE_UPDATE_PARAMS_FILE, waitTime)) {
448 LOG(ERROR) << "paramsFile is not ready.";
449 return;
450 }
451 std::ifstream fin {MODULE_UPDATE_PARAMS_FILE};
452 if (!fin.is_open()) {
453 LOG(ERROR) << "read params file fail " << MODULE_UPDATE_PARAMS_FILE;
454 return;
455 }
456 std::string line;
457 while (std::getline(fin, line)) {
458 size_t pos = line.find("=");
459 if (pos == std::string::npos) {
460 LOG(ERROR) << "read params error: " << line;
461 continue;
462 }
463 std::string paramName = line.substr(0, pos);
464 std::string paramValue = line.substr(pos + 1);
465 if (Utils::SetParameter(paramName.c_str(), paramValue.c_str()) != 0) {
466 LOG(WARNING) << "Failed to set module params: " << paramName << "; value is " << paramValue;
467 }
468 }
469 if (unlink(MODULE_UPDATE_PARAMS_FILE) != 0) {
470 LOG(WARNING) << "Failed to unlink params file, error is " << strerror(errno);
471 }
472 }
473
HandleExtraArgs(int argc,char ** argv) const474 void ModuleUpdate::HandleExtraArgs(int argc, char **argv) const
475 {
476 InitUpdaterLogger("CheckModuleUpdate", MODULE_UPDATE_LOG_FILE, "", "");
477 const std::unordered_map<std::string, std::function<void(void)>> handleFuncMap = {
478 {"setParam", [] () { return ModuleUpdate::GetInstance().SetParameterFromFile(); }},
479 };
480 for (int i = 1; i < argc; i++) {
481 LOG(INFO) << "i = " << i << "; argv[i] = " << argv[i];
482 auto iter = handleFuncMap.find(argv[i]);
483 if (iter != handleFuncMap.end()) {
484 iter->second();
485 }
486 }
487 }
488 } // namespace SysInstaller
489 } // namespace OHOS
490