• 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 
23 #include "directory_ex.h"
24 #include "log/log.h"
25 #include "module_constants.h"
26 #include "module_dm.h"
27 #include "module_error_code.h"
28 #include "module_file_repository.h"
29 #include "module_loop.h"
30 #include "module_update_kits.h"
31 #include "module_utils.h"
32 #include "parse_util.h"
33 #include "scope_guard.h"
34 #include "string_ex.h"
35 #include "utils.h"
36 
37 namespace OHOS {
38 namespace SysInstaller {
39 using namespace Updater;
40 using std::string;
41 
42 namespace {
43 constexpr mode_t MOUNT_POINT_MODE = 0755;
44 constexpr int32_t LOOP_DEVICE_SETUP_ATTEMPTS = 3;
45 constexpr int32_t RETRY_TIMES_FOR_MODULE_UPDATE_SERVICE = 10;
46 constexpr std::chrono::milliseconds MILLISECONDS_WAITING_MODULE_UPDATE_SERVICE(100);
47 
CreateLoopDevice(const string & path,const ImageStat & imageStat,Loop::LoopbackDeviceUniqueFd & loopbackDevice)48 bool CreateLoopDevice(const string &path, const ImageStat &imageStat, Loop::LoopbackDeviceUniqueFd &loopbackDevice)
49 {
50     for (int32_t attempts = 1; attempts <= LOOP_DEVICE_SETUP_ATTEMPTS; ++attempts) {
51         std::unique_ptr<Loop::LoopbackDeviceUniqueFd> device =
52             Loop::CreateLoopDevice(path, imageStat.imageOffset, imageStat.imageSize);
53         if (device != nullptr) {
54             loopbackDevice = std::move(*device);
55             break;
56         }
57     }
58     return loopbackDevice.deviceFd.Get() != -1;
59 }
60 
StageUpdateModulePackage(const string & updatePath,const string & stagePath)61 bool StageUpdateModulePackage(const string &updatePath, const string &stagePath)
62 {
63     int ret = 0;
64     if (CheckPathExists(stagePath)) {
65         LOG(INFO) << stagePath << " already exists. Deleting";
66         ret = unlink(stagePath.c_str());
67         if (ret != 0) {
68             LOG(ERROR) << "Failed to unlink " << stagePath;
69             return false;
70         }
71     }
72     string path = ExtractFilePath(stagePath);
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 
GetLatestUpdateModulePackage(const int32_t saId)102 std::unique_ptr<ModuleFile> GetLatestUpdateModulePackage(const int32_t saId)
103 {
104     auto &instance = ModuleFileRepository::GetInstance();
105     std::unique_ptr<ModuleFile> activeModuleFile = instance.GetModuleFile(UPDATE_ACTIVE_DIR, saId);
106     std::unique_ptr<ModuleFile> updateModuleFile = instance.GetModuleFile(UPDATE_INSTALL_DIR, saId);
107     std::unique_ptr<ModuleFile> ret = nullptr;
108     if (updateModuleFile != nullptr) {
109         if (activeModuleFile == nullptr || ModuleFile::CompareVersion(*updateModuleFile, *activeModuleFile)) {
110             string updatePath = updateModuleFile->GetPath();
111             string activePath = UPDATE_ACTIVE_DIR +
112                 updatePath.substr(strlen(UPDATE_INSTALL_DIR), updatePath.length());
113             if (!StageUpdateModulePackage(updatePath, activePath)) {
114                 return ret;
115             }
116             updateModuleFile->SetPath(activePath);
117             ret = std::move(updateModuleFile);
118             LOG(INFO) << "add updateModuleFile " << updatePath;
119         }
120     }
121     if (ret == nullptr && activeModuleFile != nullptr) {
122         LOG(INFO) << "add activeModuleFile " << activeModuleFile->GetPath();
123         ret = std::move(activeModuleFile);
124     }
125     return ret;
126 }
127 
GetSuccessSaIdString(const ModuleUpdateStatus & status)128 string GetSuccessSaIdString(const ModuleUpdateStatus &status)
129 {
130     string ret = "";
131     for (const SaStatus &saStatus : status.saStatusList) {
132         if (saStatus.isMountSuccess) {
133             ret += std::to_string(saStatus.saId) + " ";
134         }
135     }
136     return ret;
137 }
138 }
139 
CheckMountComplete(string & status) const140 bool ModuleUpdate::CheckMountComplete(string &status) const
141 {
142     bool mountStatus = false;
143     for (int32_t saId : saIdSet_) {
144         string path = std::string(MODULE_ROOT_DIR) + "/" + std::to_string(saId);
145         if (CheckPathExists(path)) {
146             mountStatus = true;
147             status += std::to_string(saId) + " ";
148         }
149     }
150     return mountStatus;
151 }
152 
CheckModuleUpdate(const string & path)153 string ModuleUpdate::CheckModuleUpdate(const string &path)
154 {
155     LOG(INFO) << "CheckModuleUpdate path=" << path;
156     Timer timer;
157     string ret = "";
158     if (!ParseSaProfiles(path)) {
159         LOG(ERROR) << "Failed to parse sa profile";
160         return ret;
161     }
162     if (CheckMountComplete(ret)) {
163         LOG(INFO) << "CheckMountComplete ret=" << ret;
164         return ret;
165     }
166 
167     ModuleFileRepository::GetInstance().InitRepository(saIdSet_);
168     ON_SCOPE_EXIT(clear) {
169         ModuleFileRepository::GetInstance().Clear();
170     };
171     PrepareModuleFileList();
172     if (moduleFileList_.empty()) {
173         LOG(INFO) << "No module needs to activate";
174         return ret;
175     }
176     if (!Loop::PreAllocateLoopDevices(moduleFileList_.size())) {
177         LOG(ERROR) << "Failed to pre allocate loop devices";
178         return ret;
179     }
180     if (!ActivateModules()) {
181         LOG(ERROR) << "Failed to activate modules";
182         return ret;
183     }
184     ret = GetSuccessSaIdString(status_);
185     LOG(INFO) << "CheckModuleUpdate done, duration=" << timer << ", ret=" << ret;
186     return ret;
187 }
188 
ParseSaProfiles(const string & path)189 bool ModuleUpdate::ParseSaProfiles(const string &path)
190 {
191     string realProfilePath = "";
192     if (!PathToRealPath(path, realProfilePath)) {
193         LOG(ERROR) << "Failed to get real path " << path;
194         return false;
195     }
196     ParseUtil parser;
197     if (!parser.ParseSaProfiles(realProfilePath)) {
198         LOG(ERROR) << "ParseSaProfiles failed! path=" << realProfilePath;
199         return false;
200     }
201     status_.process = Str16ToStr8(parser.GetProcessName());
202     auto saInfos = parser.GetAllSaProfiles();
203     for (const auto &saInfo : saInfos) {
204         saIdSet_.insert(saInfo.saId);
205     }
206     return true;
207 }
208 
PrepareModuleFileList()209 void ModuleUpdate::PrepareModuleFileList()
210 {
211     for (int32_t saId : saIdSet_) {
212         auto &instance = ModuleFileRepository::GetInstance();
213         std::unique_ptr<ModuleFile> systemModuleFile = instance.GetModuleFile(MODULE_PREINSTALL_DIR, saId);
214         if (systemModuleFile == nullptr) {
215             LOG(ERROR) << "Failed to get preinstalled sa " << saId;
216             continue;
217         }
218         std::unique_ptr<ModuleFile> latestModuleFile = GetLatestUpdateModulePackage(saId);
219         if (latestModuleFile != nullptr && ModuleFile::CompareVersion(*latestModuleFile, *systemModuleFile)) {
220             moduleFileList_.emplace_back(std::move(*latestModuleFile));
221         } else {
222             moduleFileList_.emplace_back(std::move(*systemModuleFile));
223         }
224     }
225 }
226 
ActivateModules()227 bool ModuleUpdate::ActivateModules()
228 {
229     bool activateSuccess = true;
230     for (const auto &moduleFile : moduleFileList_) {
231         if (!moduleFile.GetImageStat().has_value()) {
232             LOG(INFO) << moduleFile.GetPath() << " is empty module package";
233             continue;
234         }
235         SaStatus saStatus;
236         saStatus.saId = moduleFile.GetSaId();
237         saStatus.isPreInstalled = ModuleFileRepository::GetInstance().IsPreInstalledModule(moduleFile);
238         saStatus.isMountSuccess = MountModulePackage(moduleFile, !saStatus.isPreInstalled);
239         if (!saStatus.isMountSuccess) {
240             LOG(ERROR) << "Failed to mount module package " << moduleFile.GetPath();
241             activateSuccess = false;
242             ModuleFileRepository::GetInstance().SaveInstallerResult(moduleFile.GetPath(),
243                 GetHmpName(moduleFile.GetPath()), ERR_INSTALL_FAIL, "mount fail");
244         }
245         status_.saStatusList.emplace_back(std::move(saStatus));
246     }
247     LOG(INFO) << "ActivateModules activateSuccess:" << activateSuccess;
248     ReportMountStatus(status_);
249     return activateSuccess;
250 }
251 
WaitDevice(const std::string & blockDevice) const252 void ModuleUpdate::WaitDevice(const std::string &blockDevice) const
253 {
254     const int waitTime = 150; // wait max 3s
255     int time = 0;
256     while (!CheckPathExists(blockDevice) && time++ < waitTime) {
257         usleep(20000); // 20000: 20ms
258     }
259 }
260 
MountModulePackage(const ModuleFile & moduleFile,const bool mountOnVerity) const261 bool ModuleUpdate::MountModulePackage(const ModuleFile &moduleFile, const bool mountOnVerity) const
262 {
263     string mountPoint = string(MODULE_ROOT_DIR) + "/" + std::to_string(moduleFile.GetSaId());
264     LOG(INFO) << "Creating mount point: " << mountPoint;
265     Timer timer;
266     int ret = 0;
267     if (!CreateDirIfNeeded(mountPoint, MOUNT_POINT_MODE)) {
268         LOG(ERROR) << "Could not create mount point " << mountPoint << " errno: " << errno;
269         return false;
270     }
271     ON_SCOPE_EXIT(rmDir) {
272         ret = rmdir(mountPoint.c_str());
273         if (ret != 0) {
274             LOG(WARNING) << "Could not rmdir " << mountPoint << " errno: " << errno;
275         }
276     };
277     if (!IsEmptyFolder(mountPoint)) {
278         LOG(ERROR) << mountPoint << " is not empty";
279         return false;
280     }
281     const string &fullPath = moduleFile.GetPath();
282     if (!moduleFile.GetImageStat().has_value()) {
283         LOG(ERROR) << "Could not mount empty module package " << moduleFile.GetPath();
284         return false;
285     }
286     const ImageStat &imageStat = moduleFile.GetImageStat().value();
287     Loop::LoopbackDeviceUniqueFd loopbackDevice;
288     if (!CreateLoopDevice(fullPath, imageStat, loopbackDevice)) {
289         LOG(ERROR) << "Could not create loop device for " << fullPath;
290         return false;
291     }
292     LOG(INFO) << "Loopback device created: " << loopbackDevice.name << " fsType=" << imageStat.fsType;
293 
294     string blockDevice = loopbackDevice.name;
295     if (mountOnVerity) {
296         if (!CreateDmDevice(moduleFile, blockDevice)) {
297             LOG(ERROR) << "Could not create dm-verity device on " << blockDevice;
298             return false;
299         }
300     }
301     WaitDevice(blockDevice);
302     uint32_t mountFlags = MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY;
303     ret = mount(blockDevice.c_str(), mountPoint.c_str(), imageStat.fsType, mountFlags, nullptr);
304     if (ret != 0) {
305         LOG(ERROR) << "Mounting failed for module package " << fullPath << " errno:" << errno;
306         return false;
307     }
308     LOG(INFO) << "Successfully mounted module package " << fullPath << " on " << mountPoint << " duration=" << timer;
309     loopbackDevice.CloseGood();
310     CANCEL_SCOPE_EXIT_GUARD(rmDir);
311     return true;
312 }
313 
ReportMountStatus(const ModuleUpdateStatus & status) const314 void ModuleUpdate::ReportMountStatus(const ModuleUpdateStatus &status) const
315 {
316     int32_t times = RETRY_TIMES_FOR_MODULE_UPDATE_SERVICE;
317     constexpr int32_t duration = std::chrono::microseconds(MILLISECONDS_WAITING_MODULE_UPDATE_SERVICE).count();
318     while (times > 0) {
319         times--;
320         int32_t ret = ModuleUpdateKits::GetInstance().ReportModuleUpdateStatus(status);
321         if (ret == ModuleErrorCode::ERR_SERVICE_NOT_FOUND) {
322             LOG(INFO) << "retry to report mount failed";
323             usleep(duration);
324         } else {
325             if (ret != ModuleErrorCode::MODULE_UPDATE_SUCCESS) {
326                 LOG(ERROR) << "Failed to report mount failed";
327             }
328             return;
329         }
330     }
331     LOG(ERROR) << "Report mount failed timeout";
332 }
333 } // namespace SysInstaller
334 } // namespace OHOS
335