• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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