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 namespace OHOS {
39 namespace SysInstaller {
40 using namespace Updater;
41 using std::string;
42 
43 namespace {
44 constexpr mode_t MOUNT_POINT_MODE = 0755;
45 constexpr int32_t LOOP_DEVICE_SETUP_ATTEMPTS = 3;
46 
CreateLoopDevice(const string & path,const ImageStat & imageStat,Loop::LoopbackDeviceUniqueFd & loopbackDevice)47 bool CreateLoopDevice(const string &path, const ImageStat &imageStat, Loop::LoopbackDeviceUniqueFd &loopbackDevice)
48 {
49     for (int32_t attempts = 1; attempts <= LOOP_DEVICE_SETUP_ATTEMPTS; ++attempts) {
50         std::unique_ptr<Loop::LoopbackDeviceUniqueFd> device =
51             Loop::CreateLoopDevice(path, imageStat.imageOffset, imageStat.imageSize);
52         if (device != nullptr) {
53             loopbackDevice = std::move(*device);
54             break;
55         }
56     }
57     return loopbackDevice.deviceFd.Get() != -1;
58 }
59 
StageUpdateModulePackage(const string & updatePath,const string & stagePath)60 bool StageUpdateModulePackage(const string &updatePath, const string &stagePath)
61 {
62     int ret = 0;
63     if (CheckPathExists(stagePath)) {
64         LOG(INFO) << stagePath << " already exists. Deleting";
65         ret = unlink(stagePath.c_str());
66         if (ret != 0) {
67             LOG(ERROR) << "Failed to unlink " << stagePath;
68             return false;
69         }
70     }
71     string path = ExtractFilePath(stagePath);
72     // active dir should create by module_update_sa
73     if (!CheckPathExists(path)) {
74         LOG(ERROR) << path << " doesn't exist.";
75         return false;
76     }
77     ret = link(updatePath.c_str(), stagePath.c_str());
78     if (ret != 0) {
79         LOG(ERROR) << "Unable to link " << updatePath << " to " << stagePath;
80         return false;
81     }
82 
83     // stage other files
84     std::vector<std::string> files;
85     if (Updater::Utils::GetFilesFromDirectory(updatePath.substr(0, updatePath.rfind("/")), files, true) <= 0) {
86         LOG(ERROR) << "Failed to get files form " << updatePath;
87         return false;
88     }
89     for (const auto &file : files) {
90         std::string targetFile = path + ExtractFileName(file);
91         (void)unlink(targetFile.c_str());
92         ret = link(file.c_str(), targetFile.c_str());
93         if (ret != 0) {
94             LOG(ERROR) << "Unable to link " << file << " to " << targetFile;
95             return false;
96         }
97     }
98     LOG(INFO) << "success to link " << updatePath << " to " << stagePath;
99     return true;
100 }
101 
CheckModulePackage(const std::string & mountPoint,const ModuleFile & moduleFile)102 bool CheckModulePackage(const std::string &mountPoint, const ModuleFile &moduleFile)
103 {
104     if (!IsEmptyFolder(mountPoint)) {
105         LOG(ERROR) << mountPoint << " is not empty";
106         return false;
107     }
108     if (!moduleFile.GetImageStat().has_value()) {
109         LOG(ERROR) << "Could not mount empty module package " << moduleFile.GetPath();
110         return false;
111     }
112     return true;
113 }
114 }
115 
GetInstance()116 ModuleUpdate &ModuleUpdate::GetInstance()
117 {
118     static ModuleUpdate instance;
119     return instance;
120 }
121 
RemoveMountPoint(const string & hmpName)122 bool ModuleUpdate::RemoveMountPoint(const string &hmpName)
123 {
124     string mountPoint = string(MODULE_ROOT_DIR) + "/" + hmpName;
125     LOG(INFO) << "Remove old mountpoint " << mountPoint;
126     if (!CheckPathExists(mountPoint)) {
127         LOG(INFO) << mountPoint << " doesn't exist.";
128         return true;
129     }
130 
131     std::string prefixes[] = {MODULE_PREINSTALL_DIR, UPDATE_ACTIVE_DIR};
132     int ret = -1;
133     for (auto prefix : prefixes) {
134         std::string imagePath = prefix + "/" + hmpName + "/" + IMG_FILE_NAME;
135         if (Loop::RemoveDmLoopDevice(mountPoint, imagePath)) {
136             ret = 0;
137             LOG(INFO) << "Successful remove dm loop device, mountPoint=" << mountPoint
138                 << ", imagePath=" << imagePath;
139             break;
140         }
141     }
142     if (ret != 0) {
143         LOG(ERROR) << "Fail remove dm loop device, mountPoint=" << mountPoint;
144         return false;
145     }
146     ret = rmdir(mountPoint.c_str());
147     if (ret != 0) {
148         LOG(WARNING) << "Could not rmdir " << mountPoint << " errno: " << errno;
149         return false;
150     }
151     LOG(INFO) << "Successful remove mountPoint, hmpName: " << hmpName;
152     return true;
153 }
154 
GetLatestUpdateModulePackage(const string & hmpName)155 std::unique_ptr<ModuleFile> ModuleUpdate::GetLatestUpdateModulePackage(const string &hmpName)
156 {
157     std::unique_ptr<ModuleFile> activeModuleFile = repository_.GetModuleFile(UPDATE_ACTIVE_DIR, hmpName);
158     std::unique_ptr<ModuleFile> updateModuleFile = repository_.GetModuleFile(UPDATE_INSTALL_DIR, hmpName);
159     std::unique_ptr<ModuleFile> ret = nullptr;
160     if (updateModuleFile != nullptr) {
161         if (activeModuleFile == nullptr || ModuleFile::CompareVersion(*updateModuleFile, *activeModuleFile)) {
162             string updatePath = updateModuleFile->GetPath();
163             string activePath = UPDATE_ACTIVE_DIR +
164                 updatePath.substr(strlen(UPDATE_INSTALL_DIR), updatePath.length());
165             if (!StageUpdateModulePackage(updatePath, activePath)) {
166                 return ret;
167             }
168             updateModuleFile->SetPath(activePath);
169             ret = std::move(updateModuleFile);
170             LOG(INFO) << "add updateModuleFile " << updatePath;
171         }
172     }
173     if (ret == nullptr && activeModuleFile != nullptr) {
174         LOG(INFO) << "add activeModuleFile " << activeModuleFile->GetPath();
175         ret = std::move(activeModuleFile);
176     }
177     return ret;
178 }
179 
CheckMountComplete(const string & hmpName) const180 bool ModuleUpdate::CheckMountComplete(const string &hmpName) const
181 {
182     string path = std::string(MODULE_ROOT_DIR) + "/" + hmpName;
183     return CheckPathExists(path);
184 }
185 
ProcessHmpFile(const string & hmpFile,const ModuleUpdateStatus & status,const Timer & timer)186 void ModuleUpdate::ProcessHmpFile(const string &hmpFile, const ModuleUpdateStatus &status, const Timer &timer)
187 {
188     LOG(INFO) << "process hmp file=" << hmpFile;
189     std::unique_ptr<ModuleFile> moduleFile = ModuleFile::Open(hmpFile);
190     if (moduleFile == nullptr) {
191         LOG(ERROR) << "Process hmp file fail, module file is null";
192         return;
193     }
194     HmpInstallType type = moduleFile->GetHmpPackageType();
195     if (CheckBootComplete() && IsHotHmpPackage(static_cast<int32_t>(type))) {
196         if (type == HOT_SA_TYPE && IsRunning(moduleFile->GetVersionInfo().saInfoList.front().saId)) {
197             LOG(INFO) << "ondemand sa is running, saId=" << moduleFile->GetVersionInfo().saInfoList.front().saId;
198             return;
199         }
200         if (type == HOT_APP_TYPE) {
201             KillProcessOnArkWeb();
202         }
203         if (!RemoveMountPoint(status.hmpName)) {
204             return;
205         }
206     } else if (CheckMountComplete(status.hmpName)) {
207         LOG(INFO) << "Check mount complete, hmpName=" << status.hmpName;
208         return;
209     }
210     repository_.InitRepository(status.hmpName, timer);
211     PrepareModuleFileList(status);
212 }
213 
DoModuleUpdate(ModuleUpdateStatus & status)214 bool ModuleUpdate::DoModuleUpdate(ModuleUpdateStatus &status)
215 {
216     LOG(INFO) << "enter domoduleupdate";
217     Timer timer;
218     std::string hmpPackagePath = std::string(MODULE_PREINSTALL_DIR) + "/" + status.hmpName;
219     LOG(INFO) << "DoModuleUpdate hmp package path=" << hmpPackagePath;
220     std::vector<std::string> files;
221     GetDirFiles(hmpPackagePath, files);
222     ON_SCOPE_EXIT(clear) {
223         repository_.Clear();
224         moduleFileList_.clear();
225     };
226     for (auto &file : files) {
227         std::string hmpPackage = GetFileName(file);
228         if (!CheckFileSuffix(file, MODULE_PACKAGE_SUFFIX) || hmpPackage.empty()) {
229             continue;
230         }
231         ProcessHmpFile(file, status, timer);
232     }
233     if (moduleFileList_.size() != 1) {
234         LOG(INFO) << status.hmpName << " module size is invalid: " << moduleFileList_.size();
235         return false;
236     }
237     if (!Loop::PreAllocateLoopDevices(moduleFileList_.size())) {
238         LOG(ERROR) << "Failed to pre allocate loop devices, hmp package name=" << status.hmpName;
239         return false;
240     }
241     if (!ActivateModules(status, timer)) {
242         LOG(ERROR) << "Failed to activate modules, hmp package name=" << status.hmpName;
243         return false;
244     }
245     LOG(INFO) << "Success to activate modules, hmp package name=" << status.hmpName;
246     return true;
247 }
248 
CheckModuleUpdate()249 void ModuleUpdate::CheckModuleUpdate()
250 {
251     LOG(INFO) << "CheckModuleUpdate begin";
252     Timer timer;
253     std::vector<std::string> files;
254     std::unordered_set<std::string> hmpNameSet;
255     GetDirFiles(MODULE_PREINSTALL_DIR, files);
256     for (auto &file : files) {
257         if (!CheckFileSuffix(file, MODULE_PACKAGE_SUFFIX)) {
258             continue;
259         }
260         std::unique_ptr<ModuleFile> moduleFile = ModuleFile::Open(file);
261         if (moduleFile == nullptr) {
262             continue;
263         }
264         std::string hmpName = GetHmpName(file);
265         if (hmpName.empty()) {
266             continue;
267         }
268         hmpNameSet.emplace(hmpName);
269     }
270     auto &instance = ModuleUpdateTaskManager::GetInstance();
271     instance.Start();
272     ON_SCOPE_EXIT(clear) {
273         instance.ClearTask();
274         instance.Stop();
275         LOG(INFO) << "CheckModuleUpdate done, duration=" << timer;
276         if (!ForceRemoveDirectory(UPDATE_INSTALL_DIR)) {
277             LOG(ERROR) << "Failed to remove " << UPDATE_INSTALL_DIR << " err=" << errno;
278         }
279     };
280     for (auto &hmpName : hmpNameSet) {
281         instance.AddTask(hmpName);
282     }
283     while (instance.GetCurTaskNum() != 0) {
284         sleep(1);
285     }
286 }
287 
PrepareModuleFileList(const ModuleUpdateStatus & status)288 void ModuleUpdate::PrepareModuleFileList(const ModuleUpdateStatus &status)
289 {
290     std::unique_ptr<ModuleFile> systemModuleFile = repository_.GetModuleFile(MODULE_PREINSTALL_DIR, status.hmpName);
291     if (systemModuleFile == nullptr) {
292         LOG(ERROR) << "Failed to get preinstalled hmp " << status.hmpName;
293         return;
294     }
295     std::unique_ptr<ModuleFile> latestModuleFile = GetLatestUpdateModulePackage(status.hmpName);
296     if (latestModuleFile != nullptr && ModuleFile::CompareVersion(*latestModuleFile, *systemModuleFile)) {
297         moduleFileList_.emplace_back(std::move(*latestModuleFile));
298     } else {
299         moduleFileList_.emplace_back(std::move(*systemModuleFile));
300         // when choose preInstall hmp, remove activeHmp and backupHmp
301         RemoveSpecifiedDir(std::string(UPDATE_ACTIVE_DIR) + "/" + status.hmpName);
302         RemoveSpecifiedDir(std::string(UPDATE_BACKUP_DIR) + "/" + status.hmpName);
303     }
304 }
305 
ActivateModules(ModuleUpdateStatus & status,const Timer & timer)306 bool ModuleUpdate::ActivateModules(ModuleUpdateStatus &status, const Timer &timer)
307 {
308     // size = 1
309     for (const auto &moduleFile : moduleFileList_) {
310         if (!moduleFile.GetImageStat().has_value()) {
311             LOG(INFO) << moduleFile.GetPath() << " is empty module package";
312             continue;
313         }
314         status.isPreInstalled = repository_.IsPreInstalledModule(moduleFile);
315         status.isHotInstall = IsHotHmpPackage(static_cast<int32_t>(moduleFile.GetHmpPackageType()));
316         status.isAllMountSuccess = MountModulePackage(moduleFile, !status.isPreInstalled);
317         if (!status.isAllMountSuccess) {
318             LOG(ERROR) << "Failed to mount module package " << moduleFile.GetPath();
319             repository_.SaveInstallerResult(moduleFile.GetPath(), status.hmpName,
320                 ERR_INSTALL_FAIL, "mount fail", timer);
321         }
322         // bugfix: when sise = 1, for() find the second item
323         break;
324     }
325     ReportModuleUpdateStatus(status);
326     LOG(INFO) << "ActivateModule mount result:" << status.isAllMountSuccess << ", hmp package name:" << status.hmpName;
327     return status.isAllMountSuccess;
328 }
329 
WaitDevice(const std::string & blockDevice) const330 void ModuleUpdate::WaitDevice(const std::string &blockDevice) const
331 {
332     const int waitTime = 150; // wait max 3s
333     int time = 0;
334     while (!CheckPathExists(blockDevice) && time++ < waitTime) {
335         usleep(20000); // 20000: 20ms
336     }
337 }
338 
MountModulePackage(const ModuleFile & moduleFile,const bool mountOnVerity) const339 bool ModuleUpdate::MountModulePackage(const ModuleFile &moduleFile, const bool mountOnVerity) const
340 {
341     string mountPoint = string(MODULE_ROOT_DIR) + "/" + moduleFile.GetVersionInfo().hmpName;
342     LOG(INFO) << "Creating mount point: " << mountPoint;
343     Timer timer;
344     int ret = 0;
345     if (!CreateDirIfNeeded(mountPoint, MOUNT_POINT_MODE)) {
346         LOG(ERROR) << "Could not create mount point " << mountPoint << " errno: " << errno;
347         return false;
348     }
349     ON_SCOPE_EXIT(rmDir) {
350         ret = rmdir(mountPoint.c_str());
351         if (ret != 0) {
352             LOG(WARNING) << "Could not rmdir " << mountPoint << " errno: " << errno;
353         }
354     };
355     if (!CheckModulePackage(mountPoint, moduleFile)) {
356         return false;
357     }
358 
359     const string &fullPath = ExtractFilePath(moduleFile.GetPath()) + IMG_FILE_NAME;
360     const ImageStat &imageStat = moduleFile.GetImageStat().value();
361     Loop::LoopbackDeviceUniqueFd loopbackDevice;
362     if (!CreateLoopDevice(fullPath, imageStat, loopbackDevice)) {
363         LOG(ERROR) << "Could not create loop device for " << fullPath;
364         return false;
365     }
366     LOG(INFO) << "Loopback device created: " << loopbackDevice.name << " fsType=" << imageStat.fsType;
367     string blockDevice = loopbackDevice.name;
368     if (mountOnVerity) {
369         if (!CreateDmDevice(moduleFile, blockDevice)) {
370             LOG(ERROR) << "Could not create dm-verity device on " << blockDevice;
371             Loop::ClearDmLoopDevice(blockDevice, false);
372             return false;
373         }
374     }
375     WaitDevice(blockDevice);
376     uint32_t mountFlags = MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY;
377     ret = mount(blockDevice.c_str(), mountPoint.c_str(), imageStat.fsType, mountFlags, nullptr);
378     if (ret != 0) {
379         LOG(ERROR) << "Mounting failed for module package " << fullPath << " errno:" << errno;
380         Loop::ClearDmLoopDevice(blockDevice, true);
381         return false;
382     }
383     LOG(INFO) << "Successfully mounted module package " << fullPath << " on " << mountPoint << " duration=" << timer;
384     loopbackDevice.CloseGood();
385     CANCEL_SCOPE_EXIT_GUARD(rmDir);
386     return true;
387 }
388 
ReportModuleUpdateStatus(const ModuleUpdateStatus & status) const389 void ModuleUpdate::ReportModuleUpdateStatus(const ModuleUpdateStatus &status) const
390 {
391     auto &instance = ModuleUpdateTaskManager::GetInstance();
392     if (!instance.GetTaskResult()) {
393         LOG(ERROR) << "ReportModuleUpdateStatus, module update fail";
394         instance.ClearTask();
395     }
396     if (!status.isAllMountSuccess) {
397         LOG(ERROR) << "ReportModuleUpdateStatus mount fail, hmp name=" << status.hmpName;
398         RemoveSpecifiedDir(std::string(UPDATE_INSTALL_DIR) + "/" + status.hmpName);
399         Revert(status.hmpName, !status.isHotInstall);
400         return;
401     }
402     LOG(INFO) << "ReportModuleUpdateStatus mount success, hmp name=" << status.hmpName;
403 }
404 } // namespace SysInstaller
405 } // namespace OHOS
406