• 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 "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