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