1 /*
2 * Copyright (c) 2021-2022 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 "installd/installd_operator.h"
17
18 #include <cstdio>
19 #include <dirent.h>
20 #include <fstream>
21 #include <map>
22 #include <sstream>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #include "app_log_wrapper.h"
29 #include "bundle_constants.h"
30 #include "directory_ex.h"
31
32 namespace OHOS {
33 namespace AppExecFwk {
IsExistFile(const std::string & path)34 bool InstalldOperator::IsExistFile(const std::string &path)
35 {
36 if (path.empty()) {
37 return false;
38 }
39
40 struct stat buf = {};
41 if (stat(path.c_str(), &buf) != 0) {
42 return false;
43 }
44 return S_ISREG(buf.st_mode);
45 }
46
IsExistDir(const std::string & path)47 bool InstalldOperator::IsExistDir(const std::string &path)
48 {
49 if (path.empty()) {
50 return false;
51 }
52
53 struct stat buf = {};
54 if (stat(path.c_str(), &buf) != 0) {
55 return false;
56 }
57 return S_ISDIR(buf.st_mode);
58 }
59
MkRecursiveDir(const std::string & path,bool isReadByOthers)60 bool InstalldOperator::MkRecursiveDir(const std::string &path, bool isReadByOthers)
61 {
62 if (!OHOS::ForceCreateDirectory(path)) {
63 APP_LOGE("mkdir failed");
64 return false;
65 }
66 mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IXOTH;
67 mode |= (isReadByOthers ? S_IROTH : 0);
68 return OHOS::ChangeModeDirectory(path, mode);
69 }
70
DeleteDir(const std::string & path)71 bool InstalldOperator::DeleteDir(const std::string &path)
72 {
73 if (IsExistFile(path)) {
74 return OHOS::RemoveFile(path);
75 }
76 if (IsExistDir(path)) {
77 return OHOS::ForceRemoveDirectory(path);
78 }
79 return true;
80 }
81
ExtractFiles(const std::string & sourcePath,const std::string & targetPath,const std::string & targetSoPath,const std::string & cpuAbi)82 bool InstalldOperator::ExtractFiles(const std::string &sourcePath, const std::string &targetPath,
83 const std::string &targetSoPath, const std::string &cpuAbi)
84 {
85 BundleExtractor extractor(sourcePath);
86 if (!extractor.Init()) {
87 return false;
88 }
89 std::vector<std::string> entryNames;
90 if (!extractor.GetZipFileNames(entryNames)) {
91 return false;
92 }
93 if (entryNames.empty()) {
94 return false;
95 }
96
97 std::string targetDir = targetPath;
98 if (targetPath.back() != Constants::PATH_SEPARATOR[0]) {
99 targetDir = targetPath + Constants::PATH_SEPARATOR;
100 }
101 for (const auto &entryName : entryNames) {
102 if (entryName.find("..") != std::string::npos) {
103 return false;
104 }
105 if (entryName.back() == Constants::PATH_SEPARATOR[0]) {
106 continue;
107 }
108 // handle native so
109 if (isNativeSo(entryName, targetSoPath, cpuAbi)) {
110 ExtractSo(extractor, entryName, targetSoPath, cpuAbi);
111 continue;
112 }
113 const std::string dir = GetPathDir(entryName);
114 std::string filePath = targetDir + dir;
115 if (!dir.empty()) {
116 if (!MkRecursiveDir(filePath, true)) {
117 return false;
118 }
119 }
120 filePath = targetDir + entryName;
121 if (!extractor.ExtractFile(entryName, filePath)) {
122 return false;
123 }
124 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
125 if (!OHOS::ChangeModeFile(filePath, mode)) {
126 APP_LOGE("change mode failed");
127 }
128 filePath.clear();
129 }
130 return true;
131 }
132
isNativeSo(const std::string & entryName,const std::string & targetSoPath,const std::string & cpuAbi)133 bool InstalldOperator::isNativeSo(const std::string &entryName,
134 const std::string &targetSoPath, const std::string &cpuAbi)
135 {
136 APP_LOGD("isNativeSo, entryName : %{public}s", entryName.c_str());
137 if (targetSoPath.empty()) {
138 APP_LOGD("current hap not include so");
139 return false;
140 }
141 std::string prefix = Constants::LIBS + cpuAbi + Constants::PATH_SEPARATOR;
142 if (entryName.find(prefix) == std::string::npos) {
143 APP_LOGD("entryName not start with %{public}s", prefix.c_str());
144 return false;
145 }
146 size_t len = entryName.length();
147 if (len <= Constants::SO_SUFFIX_LEN) {
148 APP_LOGD("entryName length invalid");
149 return false;
150 }
151 std::string suffix = entryName.substr(len - Constants::SO_SUFFIX_LEN, len);
152 if (suffix != Constants::SO_SUFFIX) {
153 APP_LOGD("entryName suffix not .so");
154 return false;
155 }
156 APP_LOGD("find native so, entryName : %{public}s", entryName.c_str());
157 return true;
158 }
159
ExtractSo(const BundleExtractor & extractor,const std::string & entryName,const std::string & targetSoPath,const std::string & cpuAbi)160 void InstalldOperator::ExtractSo(const BundleExtractor &extractor, const std::string &entryName,
161 const std::string &targetSoPath, const std::string &cpuAbi)
162 {
163 // create dir if not exist
164 if (!IsExistDir(targetSoPath)) {
165 if (!MkRecursiveDir(targetSoPath, true)) {
166 APP_LOGE("create targetSoPath %{public}s failed", targetSoPath.c_str());
167 return;
168 }
169 }
170 std::string prefix = Constants::LIBS + cpuAbi + Constants::PATH_SEPARATOR;
171 std::string targetSoName = entryName.substr(prefix.length());
172 std::string targetSo = targetSoPath + targetSoName;
173 bool ret = extractor.ExtractFile(entryName, targetSo);
174 if (!ret) {
175 APP_LOGE("extract so failed, entryName : %{public}s", entryName.c_str());
176 return;
177 }
178 mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
179 if (!OHOS::ChangeModeFile(targetSo, mode)) {
180 APP_LOGE("change mode failed, targetSo : %{public}s", targetSo.c_str());
181 return;
182 }
183 APP_LOGD("extract so success, targetSo : %{public}s", targetSo.c_str());
184 }
185
RenameDir(const std::string & oldPath,const std::string & newPath)186 bool InstalldOperator::RenameDir(const std::string &oldPath, const std::string &newPath)
187 {
188 if (oldPath.empty() || oldPath.size() > PATH_MAX) {
189 APP_LOGE("oldpath error");
190 return false;
191 }
192 if (access(oldPath.c_str(), F_OK) != 0 && access(newPath.c_str(), F_OK) == 0) {
193 return true;
194 }
195 std::string realOldPath;
196 realOldPath.reserve(PATH_MAX);
197 realOldPath.resize(PATH_MAX - 1);
198 if (realpath(oldPath.c_str(), &(realOldPath[0])) == nullptr) {
199 APP_LOGE("realOldPath %{public}s", realOldPath.c_str());
200 return false;
201 }
202
203 if (!(IsValideCodePath(realOldPath) && IsValideCodePath(newPath))) {
204 APP_LOGE("IsValideCodePath failed");
205 return false;
206 }
207 return RenameFile(realOldPath, newPath);
208 }
209
GetPathDir(const std::string & path)210 std::string InstalldOperator::GetPathDir(const std::string &path)
211 {
212 std::size_t pos = path.rfind(Constants::PATH_SEPARATOR);
213 if (pos == std::string::npos) {
214 return std::string();
215 }
216 return path.substr(0, pos + 1);
217 }
218
ChangeFileAttr(const std::string & filePath,const int uid,const int gid)219 bool InstalldOperator::ChangeFileAttr(const std::string &filePath, const int uid, const int gid)
220 {
221 APP_LOGD("begin to change %{private}s file attribute", filePath.c_str());
222 if (chown(filePath.c_str(), uid, gid) != 0) {
223 APP_LOGE("fail to change %{private}s ownership", filePath.c_str());
224 return false;
225 }
226 APP_LOGD("change %{private}s file attribute successfully", filePath.c_str());
227 return true;
228 }
229
RenameFile(const std::string & oldPath,const std::string & newPath)230 bool InstalldOperator::RenameFile(const std::string &oldPath, const std::string &newPath)
231 {
232 if (oldPath.empty() || newPath.empty()) {
233 return false;
234 }
235 if (!DeleteDir(newPath)) {
236 return false;
237 }
238 return rename(oldPath.c_str(), newPath.c_str()) == 0;
239 }
240
IsValidPath(const std::string & rootDir,const std::string & path)241 bool InstalldOperator::IsValidPath(const std::string &rootDir, const std::string &path)
242 {
243 if (rootDir.find(Constants::PATH_SEPARATOR) != 0 ||
244 rootDir.rfind(Constants::PATH_SEPARATOR) != (rootDir.size() - 1) || rootDir.find("..") != std::string::npos) {
245 return false;
246 }
247 if (path.find("..") != std::string::npos) {
248 return false;
249 }
250 return path.compare(0, rootDir.size(), rootDir) == 0;
251 }
252
IsValideCodePath(const std::string & codePath)253 bool InstalldOperator::IsValideCodePath(const std::string &codePath)
254 {
255 if (codePath.empty()) {
256 return false;
257 }
258 return IsValidPath(Constants::BUNDLE_BASE_CODE_DIR + Constants::PATH_SEPARATOR, codePath);
259 }
260
DeleteFiles(const std::string & dataPath)261 bool InstalldOperator::DeleteFiles(const std::string &dataPath)
262 {
263 APP_LOGD("InstalldOperator::DeleteFiles start");
264 std::string subPath;
265 bool ret = true;
266 DIR *dir = opendir(dataPath.c_str());
267 if (dir == nullptr) {
268 return false;
269 }
270 while (true) {
271 struct dirent *ptr = readdir(dir);
272 if (ptr == nullptr) {
273 break;
274 }
275 if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
276 continue;
277 }
278 subPath = OHOS::IncludeTrailingPathDelimiter(dataPath) + std::string(ptr->d_name);
279 if (ptr->d_type == DT_DIR) {
280 ret = OHOS::ForceRemoveDirectory(subPath);
281 } else {
282 if (access(subPath.c_str(), F_OK) == 0) {
283 ret = OHOS::RemoveFile(subPath);
284 }
285 }
286 }
287 closedir(dir);
288 return ret;
289 }
290
MkOwnerDir(const std::string & path,bool isReadByOthers,const int uid,const int gid)291 bool InstalldOperator::MkOwnerDir(const std::string &path, bool isReadByOthers, const int uid, const int gid)
292 {
293 if (!MkRecursiveDir(path, isReadByOthers)) {
294 return false;
295 }
296 return ChangeFileAttr(path, uid, gid);
297 }
298
MkOwnerDir(const std::string & path,int mode,const int uid,const int gid)299 bool InstalldOperator::MkOwnerDir(const std::string &path, int mode, const int uid, const int gid)
300 {
301 if (!OHOS::ForceCreateDirectory(path)) {
302 APP_LOGE("mkdir failed");
303 return false;
304 }
305 if (!OHOS::ChangeModeDirectory(path, mode)) {
306 return false;
307 }
308 return ChangeFileAttr(path, uid, gid);
309 }
310
GetDiskUsage(const std::string & dir)311 int64_t InstalldOperator::GetDiskUsage(const std::string &dir)
312 {
313 if (dir.empty() || (dir.size() > Constants::PATH_MAX_SIZE)) {
314 APP_LOGE("GetDiskUsage dir path invaild");
315 return 0;
316 }
317 std::string filePath = "";
318 if (!PathToRealPath(dir, filePath)) {
319 APP_LOGE("file is not real path, file path: %{public}s", dir.c_str());
320 return 0;
321 }
322 DIR *dirPtr = opendir(filePath.c_str());
323 if (dirPtr == nullptr) {
324 APP_LOGE("GetDiskUsage open file dir:%{private}s is failure", filePath.c_str());
325 return 0;
326 }
327 if (filePath.back() != Constants::FILE_SEPARATOR_CHAR) {
328 filePath.push_back(Constants::FILE_SEPARATOR_CHAR);
329 }
330 struct dirent *entry = nullptr;
331 int64_t size = 0;
332 while ((entry = readdir(dirPtr)) != nullptr) {
333 if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
334 continue;
335 }
336 std::string path = filePath + entry->d_name;
337 std::string realPath = "";
338 if (!PathToRealPath(path, realPath)) {
339 APP_LOGE("file is not real path %{public}s", path.c_str());
340 continue;
341 }
342 struct stat fileInfo = {0};
343 if (stat(realPath.c_str(), &fileInfo) != 0) {
344 APP_LOGE("call stat error %{public}s", realPath.c_str());
345 fileInfo.st_size = 0;
346 }
347 size += fileInfo.st_size;
348 if (entry->d_type == DT_DIR) {
349 size += GetDiskUsage(realPath);
350 }
351 }
352 closedir(dirPtr);
353 return size;
354 }
355
TraverseCacheDirectory(const std::string & currentPath,std::vector<std::string> & cacheDirs)356 void InstalldOperator::TraverseCacheDirectory(const std::string ¤tPath, std::vector<std::string> &cacheDirs)
357 {
358 if (currentPath.empty() || (currentPath.size() > Constants::PATH_MAX_SIZE)) {
359 APP_LOGE("TraverseCacheDirectory current path invaild");
360 return;
361 }
362 std::string filePath = "";
363 if (!PathToRealPath(currentPath, filePath)) {
364 APP_LOGE("file is not real path, file path: %{public}s", currentPath.c_str());
365 return;
366 }
367 DIR* dir = opendir(filePath.c_str());
368 if (dir == nullptr) {
369 return;
370 }
371 if (filePath.back() != Constants::FILE_SEPARATOR_CHAR) {
372 filePath.push_back(Constants::FILE_SEPARATOR_CHAR);
373 }
374 struct dirent *ptr = nullptr;
375 while ((ptr = readdir(dir)) != nullptr) {
376 if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
377 continue;
378 }
379 if (ptr->d_type == DT_DIR && strcmp(ptr->d_name, Constants::CACHE_DIR.c_str()) == 0) {
380 std::string currentDir = filePath + std::string(ptr->d_name);
381 cacheDirs.emplace_back(currentDir);
382 continue;
383 }
384 if (ptr->d_type == DT_DIR) {
385 std::string currentDir = filePath + std::string(ptr->d_name);
386 TraverseCacheDirectory(currentDir, cacheDirs);
387 }
388 }
389 closedir(dir);
390 }
391
GetDiskUsageFromPath(const std::vector<std::string> & path)392 int64_t InstalldOperator::GetDiskUsageFromPath(const std::vector<std::string> &path)
393 {
394 int64_t fileSize = 0;
395 for (auto &st : path) {
396 fileSize += GetDiskUsage(st);
397 }
398 return fileSize;
399 }
400 } // namespace AppExecFwk
401 } // namespace OHOS