1 /*
2 * Copyright (c) 2023-2024 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 "quota/quota_manager.h"
17
18 #include <cstdint>
19 #include <cstdlib>
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <fstream>
23 #include <iostream>
24 #include <linux/dqblk_xfs.h>
25 #include <linux/fs.h>
26 #include <linux/quota.h>
27 #include <map>
28 #include <sstream>
29 #include <stack>
30 #include <sys/ioctl.h>
31 #include <sys/quota.h>
32 #include <sys/stat.h>
33 #include <sys/statvfs.h>
34 #include <sys/types.h>
35 #include <tuple>
36 #include <unique_fd.h>
37 #include <unistd.h>
38 #include <cstdio>
39
40 #include "file_uri.h"
41 #include "sandbox_helper.h"
42 #include "storage_service_errno.h"
43 #include "storage_service_log.h"
44 #include "storage_service_constant.h"
45 #include "utils/file_utils.h"
46
47 namespace OHOS {
48 namespace StorageDaemon {
49 const std::string QUOTA_DEVICE_DATA_PATH = "/data";
50 const std::string PROC_MOUNTS_PATH = "/proc/mounts";
51 const std::string DEV_BLOCK_PATH = "/dev/block/";
52 const char LINE_SEP = '\n';
53 const int32_t DEV_BLOCK_PATH_LEN = DEV_BLOCK_PATH.length();
54 const uint64_t ONE_KB = 1;
55 const uint64_t ONE_MB = 1024 * ONE_KB;
56 const uint64_t PATH_MAX_LEN = 4096;
57 static std::map<std::string, std::string> mQuotaReverseMounts;
58 std::recursive_mutex mMountsLock;
59
60 QuotaManager* QuotaManager::instance_ = nullptr;
GetInstance()61 QuotaManager* QuotaManager::GetInstance()
62 {
63 if (instance_ == nullptr) {
64 instance_ = new QuotaManager();
65 }
66
67 return instance_;
68 }
69
InitialiseQuotaMounts()70 static bool InitialiseQuotaMounts()
71 {
72 std::lock_guard<std::recursive_mutex> lock(mMountsLock);
73 mQuotaReverseMounts.clear();
74 std::ifstream in(PROC_MOUNTS_PATH);
75
76 if (!in.is_open()) {
77 LOGE("Failed to open mounts file");
78 return false;
79 }
80 std::string source;
81 std::string target;
82 std::string ignored;
83
84 while (in.peek() != EOF) {
85 std::getline(in, source, ' ');
86 std::getline(in, target, ' ');
87 std::getline(in, ignored);
88 if (source.compare(0, DEV_BLOCK_PATH_LEN, DEV_BLOCK_PATH) == 0) {
89 struct dqblk dq;
90 if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0, reinterpret_cast<char*>(&dq)) == 0) {
91 mQuotaReverseMounts[target] = source;
92 }
93 }
94 }
95
96 return true;
97 }
98
GetQuotaSrcMountPath(const std::string &target)99 static std::string GetQuotaSrcMountPath(const std::string &target)
100 {
101 std::lock_guard<std::recursive_mutex> lock(mMountsLock);
102 if (mQuotaReverseMounts.find(target) != mQuotaReverseMounts.end()) {
103 return mQuotaReverseMounts[target];
104 } else {
105 return "";
106 }
107 }
108
GetOccupiedSpaceForUid(int32_t uid, int64_t &size)109 static int64_t GetOccupiedSpaceForUid(int32_t uid, int64_t &size)
110 {
111 if (InitialiseQuotaMounts() != true) {
112 LOGE("Failed to initialise quota mounts");
113 return E_SYS_ERR;
114 }
115
116 std::string device = "";
117 device = GetQuotaSrcMountPath(QUOTA_DEVICE_DATA_PATH);
118 if (device.empty()) {
119 LOGE("skip when device no quotas present");
120 return E_OK;
121 }
122
123 struct dqblk dq;
124 if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, reinterpret_cast<char*>(&dq)) != 0) {
125 LOGE("Failed to get quotactl, errno : %{public}d", errno);
126 return E_SYS_ERR;
127 }
128
129 size = static_cast<int64_t>(dq.dqb_curspace);
130 return E_OK;
131 }
132
GetOccupiedSpaceForGid(int32_t gid, int64_t &size)133 static int64_t GetOccupiedSpaceForGid(int32_t gid, int64_t &size)
134 {
135 if (InitialiseQuotaMounts() != true) {
136 LOGE("Failed to initialise quota mounts");
137 return E_SYS_ERR;
138 }
139
140 std::string device = "";
141 device = GetQuotaSrcMountPath(QUOTA_DEVICE_DATA_PATH);
142 if (device.empty()) {
143 LOGE("skip when device no quotas present");
144 return E_OK;
145 }
146
147 struct dqblk dq;
148 if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), gid, reinterpret_cast<char*>(&dq)) != 0) {
149 LOGE("Failed to get quotactl, errno : %{public}d", errno);
150 return E_SYS_ERR;
151 }
152
153 size = static_cast<int64_t>(dq.dqb_curspace);
154 return E_OK;
155 }
156
157
GetOccupiedSpaceForPrjId(int32_t prjId, int64_t &size)158 static int64_t GetOccupiedSpaceForPrjId(int32_t prjId, int64_t &size)
159 {
160 if (InitialiseQuotaMounts() != true) {
161 LOGE("Failed to initialise quota mounts");
162 return E_SYS_ERR;
163 }
164
165 std::string device = "";
166 device = GetQuotaSrcMountPath(QUOTA_DEVICE_DATA_PATH);
167 if (device.empty()) {
168 LOGE("skip when device no quotas present");
169 return E_OK;
170 }
171
172 struct dqblk dq;
173 if (quotactl(QCMD(Q_GETQUOTA, PRJQUOTA), device.c_str(), prjId, reinterpret_cast<char*>(&dq)) != 0) {
174 LOGE("Failed to get quotactl, errno : %{public}d", errno);
175 return E_SYS_ERR;
176 }
177
178 size = static_cast<int64_t>(dq.dqb_curspace);
179 return E_OK;
180 }
181
GetOccupiedSpace(int32_t idType, int32_t id, int64_t &size)182 int32_t QuotaManager::GetOccupiedSpace(int32_t idType, int32_t id, int64_t &size)
183 {
184 switch (idType) {
185 case USRID:
186 return GetOccupiedSpaceForUid(id, size);
187 break;
188 case GRPID:
189 return GetOccupiedSpaceForGid(id, size);
190 break;
191 case PRJID:
192 return GetOccupiedSpaceForPrjId(id, size);
193 break;
194 default:
195 return E_NON_EXIST;
196 }
197 return E_OK;
198 }
199
SetBundleQuota(const std::string &bundleName, int32_t uid, const std::string &bundleDataDirPath, int32_t limitSizeMb)200 int32_t QuotaManager::SetBundleQuota(const std::string &bundleName, int32_t uid,
201 const std::string &bundleDataDirPath, int32_t limitSizeMb)
202 {
203 if (bundleName.empty() || bundleDataDirPath.empty() || uid < 0 || limitSizeMb < 0) {
204 LOGE("Calling the function SetBundleQuota with invalid param");
205 return E_NON_EXIST;
206 }
207
208 LOGE("SetBundleQuota Start, bundleName is %{public}s, uid is %{public}d, bundleDataDirPath is %{public}s, "
209 "limit is %{public}d.", bundleName.c_str(), uid, bundleDataDirPath.c_str(), limitSizeMb);
210 if (InitialiseQuotaMounts() != true) {
211 LOGE("Failed to initialise quota mounts");
212 return E_NON_EXIST;
213 }
214
215 std::string device = "";
216 if (bundleDataDirPath.find(QUOTA_DEVICE_DATA_PATH) == 0) {
217 device = GetQuotaSrcMountPath(QUOTA_DEVICE_DATA_PATH);
218 }
219 if (device.empty()) {
220 LOGE("skip when device no quotas present");
221 return E_OK;
222 }
223
224 struct dqblk dq;
225 if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, reinterpret_cast<char*>(&dq)) != 0) {
226 LOGE("Failed to get hard quota, errno : %{public}d", errno);
227 return E_SYS_CALL;
228 }
229
230 // dqb_bhardlimit is count of 1kB blocks, dqb_curspace is bytes
231 struct statvfs stat;
232 if (statvfs(bundleDataDirPath.c_str(), &stat) != 0) {
233 LOGE("Failed to statvfs, errno : %{public}d", errno);
234 return E_SYS_CALL;
235 }
236
237 dq.dqb_valid = QIF_LIMITS;
238 dq.dqb_bhardlimit = (uint32_t)limitSizeMb * ONE_MB;
239 if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), device.c_str(), uid, reinterpret_cast<char*>(&dq)) != 0) {
240 LOGE("Failed to set hard quota, errno : %{public}d", errno);
241 return E_SYS_CALL;
242 } else {
243 LOGE("Applied hard quotas ok");
244 return E_OK;
245 }
246 }
247
SetQuotaPrjId(const std::string &path, int32_t prjId, bool inherit)248 int32_t QuotaManager::SetQuotaPrjId(const std::string &path, int32_t prjId, bool inherit)
249 {
250 struct fsxattr fsx;
251 char *realPath = realpath(path.c_str(), nullptr);
252 if (realPath == nullptr) {
253 LOGE("realpath failed");
254 return E_SYS_CALL;
255 }
256 FILE *f = fopen(realPath, "r");
257 free(realPath);
258 if (f == nullptr) {
259 LOGE("Failed to open %{public}s, errno: %{public}d", path.c_str(), errno);
260 return E_SYS_CALL;
261 }
262 int fd = fileno(f);
263 if (fd < 0) {
264 LOGE("Failed to open %{public}s, errno: %{public}d", path.c_str(), errno);
265 return E_SYS_CALL;
266 }
267 if (ioctl(fd, FS_IOC_FSGETXATTR, &fsx) == -1) {
268 LOGE("Failed to get extended attributes of %{public}s, errno: %{public}d", path.c_str(), errno);
269 (void)fclose(f);
270 return E_SYS_CALL;
271 }
272 if (fsx.fsx_projid == static_cast<uint32_t>(prjId)) {
273 (void)fclose(f);
274 return E_OK;
275 }
276 fsx.fsx_projid = static_cast<uint32_t>(prjId);
277 if (ioctl(fd, FS_IOC_FSSETXATTR, &fsx) == -1) {
278 LOGE("Failed to set project id for %{public}s, errno: %{public}d", path.c_str(), errno);
279 (void)fclose(f);
280 return E_SYS_CALL;
281 }
282 if (inherit) {
283 uint32_t flags;
284 if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) {
285 LOGE("Failed to get flags for %{public}s, errno:%{public}d", path.c_str(), errno);
286 (void)fclose(f);
287 return E_SYS_CALL;
288 }
289 flags |= FS_PROJINHERIT_FL;
290 if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1) {
291 LOGE("Failed to set flags for %{public}s, errno:%{public}d", path.c_str(), errno);
292 (void)fclose(f);
293 return E_SYS_CALL;
294 }
295 }
296 (void)fclose(f);
297 return E_OK;
298 }
299
ReadIncludesExcludesPath( const std::string &bundleName, const int64_t lastBackupTime, const uint32_t userId)300 static std::tuple<std::vector<std::string>, std::vector<std::string>> ReadIncludesExcludesPath(
301 const std::string &bundleName, const int64_t lastBackupTime, const uint32_t userId)
302 {
303 if (bundleName.empty()) {
304 LOGE("bundleName is empty");
305 return { {}, {} };
306 }
307 // 保存includeExclude的path
308 std::string filePath = BACKUP_PATH_PREFIX + std::to_string(userId) + BACKUP_PATH_SURFFIX +
309 bundleName + FILE_SEPARATOR_CHAR + BACKUP_INCEXC_SYMBOL + std::to_string(lastBackupTime);
310 std::ifstream incExcFile;
311 incExcFile.open(filePath.data());
312 if (!incExcFile.is_open()) {
313 LOGE("Cannot open include/exclude file, fail errno:%{public}d", errno);
314 return { {}, {} };
315 }
316
317 std::vector<std::string> includes;
318 std::vector<std::string> excludes;
319 bool incOrExt = true;
320 while (incExcFile) {
321 std::string line;
322 std::getline(incExcFile, line);
323 if (line.empty()) {
324 LOGI("Read Complete");
325 break;
326 }
327 if (line == BACKUP_INCLUDE) {
328 incOrExt = true;
329 } else if (line == BACKUP_EXCLUDE) {
330 incOrExt = false;
331 }
332 if (incOrExt && line != BACKUP_INCLUDE) {
333 includes.emplace_back(line);
334 } else if (!incOrExt && line != BACKUP_EXCLUDE) {
335 excludes.emplace_back(line);
336 }
337 }
338 incExcFile.close();
339 return {includes, excludes};
340 }
341
AddPathMapForPathWildCard(uint32_t userId, const std::string &bundleName, const std::string &phyPath, std::map<std::string, std::string> &pathMap)342 static bool AddPathMapForPathWildCard(uint32_t userId, const std::string &bundleName, const std::string &phyPath,
343 std::map<std::string, std::string> &pathMap)
344 {
345 std::string physicalPrefixEl1 = PHY_APP + EL1 + FILE_SEPARATOR_CHAR + std::to_string(userId) + BASE +
346 bundleName + FILE_SEPARATOR_CHAR;
347 std::string physicalPrefixEl2 = PHY_APP + EL2 + FILE_SEPARATOR_CHAR + std::to_string(userId) + BASE +
348 bundleName + FILE_SEPARATOR_CHAR;
349 if (phyPath.find(physicalPrefixEl1) == 0) {
350 std::string relatePath = phyPath.substr(physicalPrefixEl1.size());
351 pathMap.insert({phyPath, BASE_EL1 + relatePath});
352 } else if (phyPath.find(physicalPrefixEl2) == 0) {
353 std::string relatePath = phyPath.substr(physicalPrefixEl2.size());
354 pathMap.insert({phyPath, BASE_EL2 + relatePath});
355 } else {
356 LOGE("Invalid phyiscal path");
357 return false;
358 }
359 return true;
360 }
361
GetPathWildCard(uint32_t userId, const std::string &bundleName, const std::string &includeWildCard, std::vector<std::string> &includePathList, std::map<std::string, std::string> &pathMap)362 static bool GetPathWildCard(uint32_t userId, const std::string &bundleName, const std::string &includeWildCard,
363 std::vector<std::string> &includePathList, std::map<std::string, std::string> &pathMap)
364 {
365 size_t pos = includeWildCard.rfind(WILDCARD_DEFAULT_INCLUDE);
366 if (pos == std::string::npos) {
367 LOGE("GetPathWildCard: path should include *");
368 return false;
369 }
370 std::string pathBeforeWildCard = includeWildCard.substr(0, pos);
371 DIR *dirPtr = opendir(pathBeforeWildCard.c_str());
372 if (dirPtr == nullptr) {
373 LOGE("GetPathWildCard open file dir:%{private}s fail, errno:%{public}d", pathBeforeWildCard.c_str(), errno);
374 return false;
375 }
376 struct dirent *entry = nullptr;
377 std::vector<std::string> subDirs;
378 while ((entry = readdir(dirPtr)) != nullptr) {
379 if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
380 continue;
381 }
382 std::string path = pathBeforeWildCard + entry->d_name;
383 if (entry->d_type == DT_DIR) {
384 subDirs.emplace_back(path);
385 }
386 }
387 closedir(dirPtr);
388 for (auto &subDir : subDirs) {
389 DIR *subDirPtr = opendir(subDir.c_str());
390 if (subDirPtr == nullptr) {
391 LOGE("GetPathWildCard open file dir:%{private}s fail, errno:%{public}d", subDir.c_str(), errno);
392 return false;
393 }
394 while ((entry = readdir(subDirPtr)) != nullptr) {
395 if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
396 continue;
397 }
398 std::string dirName = std::string(entry->d_name);
399
400 std::string path = subDir + FILE_SEPARATOR_CHAR + entry->d_name;
401 if (entry->d_type == DT_DIR && (dirName == DEFAULT_INCLUDE_PATH_IN_HAP_FILES ||
402 dirName == DEFAULT_INCLUDE_PATH_IN_HAP_DATABASE ||
403 dirName == DEFAULT_INCLUDE_PATH_IN_HAP_PREFERENCE)) {
404 includePathList.emplace_back(path);
405 AddPathMapForPathWildCard(userId, bundleName, path, pathMap);
406 }
407 }
408 closedir(subDirPtr);
409 }
410 return true;
411 }
412
RecognizeSandboxWildCard(const uint32_t userId, const std::string &bundleName, const std::string &sandboxPathStr, std::vector<std::string> &phyIncludes, std::map<std::string, std::string>& pathMap)413 static void RecognizeSandboxWildCard(const uint32_t userId, const std::string &bundleName,
414 const std::string &sandboxPathStr, std::vector<std::string> &phyIncludes,
415 std::map<std::string, std::string>& pathMap)
416 {
417 if (sandboxPathStr.find(BASE_EL1 + DEFAULT_PATH_WITH_WILDCARD) == 0) {
418 std::string physicalPrefix = PHY_APP + EL1 + FILE_SEPARATOR_CHAR + std::to_string(userId) + BASE +
419 bundleName + FILE_SEPARATOR_CHAR;
420 std::string relatePath = sandboxPathStr.substr(BASE_EL1.size());
421 if (!GetPathWildCard(userId, bundleName, physicalPrefix + relatePath, phyIncludes, pathMap)) {
422 LOGE("el1 GetPathWildCard dir path invaild");
423 }
424 } else if (sandboxPathStr.find(BASE_EL2 + DEFAULT_PATH_WITH_WILDCARD) == 0) {
425 std::string physicalPrefix = PHY_APP + EL2 + FILE_SEPARATOR_CHAR + std::to_string(userId) + BASE +
426 bundleName + FILE_SEPARATOR_CHAR;
427 std::string relatePath = sandboxPathStr.substr(BASE_EL2.size());
428 if (!GetPathWildCard(userId, bundleName, physicalPrefix + relatePath, phyIncludes, pathMap)) {
429 LOGE("el2 GetPathWildCard dir path invaild");
430 }
431 }
432 }
433
ConvertSandboxRealPath(const uint32_t userId, const std::string &bundleName, const std::string &sandboxPathStr, std::vector<std::string> &realPaths, std::map<std::string, std::string>& pathMap)434 static void ConvertSandboxRealPath(const uint32_t userId, const std::string &bundleName,
435 const std::string &sandboxPathStr, std::vector<std::string> &realPaths,
436 std::map<std::string, std::string>& pathMap)
437 {
438 std::string uriString;
439 if (sandboxPathStr.find(NORMAL_SAND_PREFIX) == 0) {
440 // for normal hap, start with file://bundleName
441 uriString = URI_PREFIX + bundleName;
442 } else if (sandboxPathStr.find(FILE_SAND_PREFIX) == 0) {
443 // for public files, start with file://docs
444 uriString = URI_PREFIX + FILE_AUTHORITY;
445 } else if (sandboxPathStr.find(MEDIA_SAND_PREFIX) == 0) {
446 std::string physicalPath = sandboxPathStr;
447 physicalPath.insert(MEDIA_SAND_PREFIX.length(), FILE_SEPARATOR_CHAR + std::to_string(userId));
448 realPaths.emplace_back(physicalPath);
449 pathMap.insert({physicalPath, sandboxPathStr});
450 return;
451 } else if (sandboxPathStr.find(MEDIA_CLOUD_SAND_PREFIX) == 0) {
452 std::string physicalPath = sandboxPathStr;
453 physicalPath.insert(MEDIA_CLOUD_SAND_PREFIX.length(), FILE_SEPARATOR_CHAR + std::to_string(userId));
454 realPaths.emplace_back(physicalPath);
455 pathMap.insert({physicalPath, sandboxPathStr});
456 return;
457 }
458
459 if (!uriString.empty()) {
460 uriString += sandboxPathStr;
461 AppFileService::ModuleFileUri::FileUri uri(uriString);
462 // files
463 std::string physicalPath;
464 int ret = AppFileService::SandboxHelper::GetBackupPhysicalPath(uri.ToString(), std::to_string(userId),
465 physicalPath);
466 if (ret != 0) {
467 LOGE("Get physical path failed with %{public}d", ret);
468 return;
469 }
470 realPaths.emplace_back(physicalPath);
471 pathMap.insert({physicalPath, sandboxPathStr});
472 }
473 }
474
WriteFileList(std::ofstream &statFile, struct FileStat fileStat, BundleStatsParas ¶s)475 static void WriteFileList(std::ofstream &statFile, struct FileStat fileStat, BundleStatsParas ¶s)
476 {
477 if (!statFile.is_open() || fileStat.filePath.empty()) {
478 LOGE("WriteFileList Param failed");
479 return;
480 }
481 std::string fileLine = "";
482 bool encodeFlag = false;
483 if (fileStat.filePath.find(LINE_SEP) != std::string::npos) {
484 fileLine += AppFileService::SandboxHelper::Encode(fileStat.filePath) + FILE_CONTENT_SEPARATOR;
485 encodeFlag = true;
486 } else {
487 fileLine += fileStat.filePath + FILE_CONTENT_SEPARATOR;
488 }
489 fileLine += std::to_string(fileStat.mode) + FILE_CONTENT_SEPARATOR;
490 if (fileStat.isDir) {
491 fileLine += std::to_string(1) + FILE_CONTENT_SEPARATOR;
492 } else {
493 fileLine += std::to_string(0) + FILE_CONTENT_SEPARATOR;
494 }
495 fileLine += std::to_string(fileStat.fileSize) + FILE_CONTENT_SEPARATOR;
496 fileLine += std::to_string(fileStat.lastUpdateTime) + FILE_CONTENT_SEPARATOR;
497 fileLine += FILE_CONTENT_SEPARATOR;
498 if (fileStat.isIncre) {
499 fileLine += std::to_string(1);
500 } else {
501 fileLine += std::to_string(0);
502 }
503 fileLine += FILE_CONTENT_SEPARATOR;
504 if (encodeFlag) {
505 fileLine += std::to_string(1);
506 } else {
507 fileLine += std::to_string(0);
508 }
509 // te file line
510 statFile << fileLine << std::endl;
511 if (fileStat.isIncre) {
512 paras.incFileSizeSum += fileStat.fileSize;
513 }
514 paras.fileSizeSum += fileStat.fileSize;
515 }
516
ExcludeFilter(std::map<std::string, bool> &excludesMap, const std::string &path)517 static bool ExcludeFilter(std::map<std::string, bool> &excludesMap, const std::string &path)
518 {
519 if (path.empty()) {
520 LOGE("ExcludeFilter Param failed");
521 return true;
522 }
523 std::string formatPath = path;
524 for (auto exclude = excludesMap.begin(); exclude != excludesMap.end(); exclude++) {
525 if (exclude->second != true) {
526 if (formatPath.compare(exclude->first) == 0) {
527 return true;
528 }
529 } else {
530 if (formatPath.compare(0, exclude->first.size(), exclude->first) == 0) {
531 return true;
532 }
533 }
534 }
535 return false;
536 }
537
538 /**
539 * @brief Check if path in includes is directory or not
540 *
541 * @param path path in includes
542 * @param paras start time for last backup and file size sum
543 * @param pathMap map for file sandbox path and physical path
544 * @param statFile target file stream pointer
545 * @param excludeMap map for exclude physical path and isDir
546 *
547 * @return std::tuple<bool, bool> : is success or not for system call / is directory or not
548 */
CheckIfDirForIncludes(const std::string &path, BundleStatsParas ¶s, std::map<std::string, std::string> &pathMap, std::ofstream &statFile, std::map<std::string, bool> &excludesMap)549 static std::tuple<bool, bool> CheckIfDirForIncludes(const std::string &path, BundleStatsParas ¶s,
550 std::map<std::string, std::string> &pathMap, std::ofstream &statFile, std::map<std::string, bool> &excludesMap)
551 {
552 if (!statFile.is_open() || path.empty()) {
553 LOGE("CheckIfDirForIncludes Param failed");
554 return {false, false};
555 }
556 // check whether the path exists
557 struct stat fileStatInfo = {0};
558 if (stat(path.c_str(), &fileStatInfo) != 0) {
559 LOGE("CheckIfDirForIncludes call stat error %{private}s, fail errno:%{public}d", path.c_str(), errno);
560 return {false, false};
561 }
562 if (S_ISDIR(fileStatInfo.st_mode)) {
563 LOGI("%{private}s exists and is a directory", path.c_str());
564 return {true, true};
565 } else {
566 std::string sandboxPath = path;
567 auto it = pathMap.find(path);
568 if (it != pathMap.end()) {
569 sandboxPath = it->second;
570 }
571
572 struct FileStat fileStat;
573 fileStat.filePath = sandboxPath;
574 fileStat.fileSize = fileStatInfo.st_size;
575 // mode
576 fileStat.mode = static_cast<int32_t>(fileStatInfo.st_mode);
577 fileStat.isDir = false;
578 int64_t lastUpdateTime = static_cast<int64_t>(fileStatInfo.st_mtime);
579 fileStat.lastUpdateTime = lastUpdateTime;
580 if (paras.lastBackupTime == 0 || lastUpdateTime > paras.lastBackupTime) {
581 fileStat.isIncre = true;
582 }
583 if (ExcludeFilter(excludesMap, path) == false) {
584 WriteFileList(statFile, fileStat, paras);
585 }
586 return {true, false};
587 }
588 }
589
PhysicalToSandboxPath(const std::string &dir, const std::string &sandboxDir, const std::string &path)590 static std::string PhysicalToSandboxPath(const std::string &dir, const std::string &sandboxDir,
591 const std::string &path)
592 {
593 std::size_t dirPos = dir.size();
594 std::string pathSurffix = path.substr(dirPos);
595 return sandboxDir + pathSurffix;
596 }
597
AddOuterDirIntoFileStat(const std::string &dir, BundleStatsParas ¶s, const std::string &sandboxDir, std::ofstream &statFile, std::map<std::string, bool> &excludesMap)598 static bool AddOuterDirIntoFileStat(const std::string &dir, BundleStatsParas ¶s, const std::string &sandboxDir,
599 std::ofstream &statFile, std::map<std::string, bool> &excludesMap)
600 {
601 if (!statFile.is_open() || dir.empty()) {
602 LOGE("AddOuterDirIntoFileStat Param failed");
603 return false;
604 }
605 struct stat fileInfo = {0};
606 if (stat(dir.c_str(), &fileInfo) != 0) {
607 LOGE("AddOuterDirIntoFileStat call stat error %{private}s, fail errno:%{public}d", dir.c_str(), errno);
608 return false;
609 }
610 struct FileStat fileStat = {};
611 fileStat.filePath = PhysicalToSandboxPath(dir, sandboxDir, dir);
612 fileStat.fileSize = fileInfo.st_size;
613 // mode
614 fileStat.mode = static_cast<int32_t>(fileInfo.st_mode);
615 int64_t lastUpdateTime = static_cast<int64_t>(fileInfo.st_mtime);
616 fileStat.lastUpdateTime = lastUpdateTime;
617 fileStat.isIncre = (paras.lastBackupTime == 0 || lastUpdateTime > paras.lastBackupTime) ? true : false;
618 fileStat.isDir = true;
619 std::string formatPath = dir;
620 if (formatPath.back() != FILE_SEPARATOR_CHAR) {
621 formatPath.push_back(FILE_SEPARATOR_CHAR);
622 }
623 if (ExcludeFilter(excludesMap, formatPath) == false) {
624 WriteFileList(statFile, fileStat, paras);
625 }
626 return true;
627 }
628
CheckOverLongPath(const std::string &path)629 uint32_t CheckOverLongPath(const std::string &path)
630 {
631 uint32_t len = path.length();
632 if (len >= PATH_MAX_LEN) {
633 size_t found = path.find_last_of('/');
634 std::string sub = path.substr(found + 1);
635 LOGE("Path over long, length:%{public}d, fileName:%{public}s.", len, sub.c_str());
636 }
637 return len;
638 }
639
InsertStatFile(const std::string &path, struct FileStat fileStat, std::ofstream &statFile, std::map<std::string, bool> &excludesMap, BundleStatsParas ¶s)640 static void InsertStatFile(const std::string &path, struct FileStat fileStat,
641 std::ofstream &statFile, std::map<std::string, bool> &excludesMap, BundleStatsParas ¶s)
642 {
643 if (!statFile.is_open() || path.empty()) {
644 LOGE("InsertStatFile Param failed");
645 return;
646 }
647 std::string formatPath = path;
648 if (fileStat.isDir == true && formatPath.back() != FILE_SEPARATOR_CHAR) {
649 formatPath.push_back(FILE_SEPARATOR_CHAR);
650 }
651 if (!ExcludeFilter(excludesMap, formatPath)) {
652 WriteFileList(statFile, fileStat, paras);
653 }
654 }
655
GetIncludesFileStats(const std::string &dir, BundleStatsParas ¶s, std::map<std::string, std::string> &pathMap, std::ofstream &statFile, std::map<std::string, bool> &excludesMap)656 static bool GetIncludesFileStats(const std::string &dir, BundleStatsParas ¶s,
657 std::map<std::string, std::string> &pathMap,
658 std::ofstream &statFile, std::map<std::string, bool> &excludesMap)
659 {
660 std::string sandboxDir = dir;
661 auto it = pathMap.find(dir);
662 if (it != pathMap.end()) {
663 sandboxDir = it->second;
664 }
665 // stat current directory info
666 AddOuterDirIntoFileStat(dir, paras, sandboxDir, statFile, excludesMap);
667
668 std::stack<std::string> folderStack;
669 std::string filePath;
670 folderStack.push(dir);
671 // stat files and sub-directory in current directory info
672 while (!folderStack.empty()) {
673 filePath = folderStack.top();
674 folderStack.pop();
675 DIR *dirPtr = opendir(filePath.c_str());
676 if (dirPtr == nullptr) {
677 LOGE("GetIncludesFileStats open file dir:%{private}s fail, errno:%{public}d", filePath.c_str(), errno);
678 continue;
679 }
680 if (filePath.back() != FILE_SEPARATOR_CHAR) {
681 filePath.push_back(FILE_SEPARATOR_CHAR);
682 }
683
684 struct dirent *entry = nullptr;
685 while ((entry = readdir(dirPtr)) != nullptr) {
686 if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
687 continue;
688 }
689 std::string path = filePath + entry->d_name;
690 struct stat fileInfo = {0};
691 if (stat(path.c_str(), &fileInfo) != 0) {
692 LOGE("GetIncludesFileStats call stat error %{private}s, errno:%{public}d", path.c_str(), errno);
693 fileInfo.st_size = 0;
694 }
695 struct FileStat fileStat = {};
696 fileStat.filePath = PhysicalToSandboxPath(dir, sandboxDir, path);
697 fileStat.fileSize = fileInfo.st_size;
698 CheckOverLongPath(fileStat.filePath);
699 // mode
700 fileStat.mode = static_cast<int32_t>(fileInfo.st_mode);
701 int64_t lastUpdateTime = static_cast<int64_t>(fileInfo.st_mtime);
702 fileStat.lastUpdateTime = lastUpdateTime;
703 fileStat.isIncre = (paras.lastBackupTime == 0 || lastUpdateTime > paras.lastBackupTime) ? true : false;
704 if (entry->d_type == DT_DIR) {
705 fileStat.isDir = true;
706 folderStack.push(path);
707 }
708 InsertStatFile(path, fileStat, statFile, excludesMap, paras);
709 }
710 closedir(dirPtr);
711 }
712 return true;
713 }
714
SetExcludePathMap(std::string &excludePath, std::map<std::string, bool> &excludesMap)715 static void SetExcludePathMap(std::string &excludePath, std::map<std::string, bool> &excludesMap)
716 {
717 if (excludePath.empty()) {
718 LOGE("SetExcludePathMap Param failed");
719 return;
720 }
721 struct stat fileStatInfo = {0};
722 if (stat(excludePath.c_str(), &fileStatInfo) != 0) {
723 LOGE("SetExcludePathMap call stat error %{private}s, errno:%{public}d", excludePath.c_str(), errno);
724 return;
725 }
726 if (S_ISDIR(fileStatInfo.st_mode)) {
727 if (excludePath.back() != FILE_SEPARATOR_CHAR) {
728 excludePath.push_back(FILE_SEPARATOR_CHAR);
729 }
730 excludesMap.insert({excludePath, true});
731 } else {
732 excludesMap.insert({excludePath, false});
733 }
734 }
735
ScanExtensionPath(BundleStatsParas ¶s, const std::vector<std::string> &includes, const std::vector<std::string> &excludes, std::map<std::string, std::string> &pathMap, std::ofstream &statFile)736 static void ScanExtensionPath(BundleStatsParas ¶s,
737 const std::vector<std::string> &includes, const std::vector<std::string> &excludes,
738 std::map<std::string, std::string> &pathMap, std::ofstream &statFile)
739 {
740 std::map<std::string, bool> excludesMap;
741 for (auto exclude : excludes) {
742 SetExcludePathMap(exclude, excludesMap);
743 }
744 // all file with stats in include directory
745 for (const auto &includeDir : includes) {
746 // Check if includeDir is a file path
747 auto [isSucc, isDir] = CheckIfDirForIncludes(includeDir, paras, pathMap, statFile, excludesMap);
748 if (!isSucc) {
749 continue;
750 }
751 // recognize all file in include directory
752 if (isDir && !GetIncludesFileStats(includeDir, paras, pathMap, statFile, excludesMap)) {
753 LOGE("Faied to get include files for includeDir");
754 }
755 }
756 }
757
DealWithIncludeFiles(const BundleStatsParas ¶s, const std::vector<std::string> &includes, std::vector<std::string> &phyIncludes, std::map<std::string, std::string>& pathMap)758 static void DealWithIncludeFiles(const BundleStatsParas ¶s, const std::vector<std::string> &includes,
759 std::vector<std::string> &phyIncludes, std::map<std::string, std::string>& pathMap)
760 {
761 uint32_t userId = paras.userId;
762 std::string bundleName = paras.bundleName;
763 for (const auto &include : includes) {
764 std::string includeStr = include;
765 if (includeStr.front() != FILE_SEPARATOR_CHAR) {
766 includeStr = FILE_SEPARATOR_CHAR + includeStr;
767 }
768 if (includeStr.find(BASE_EL1 + DEFAULT_PATH_WITH_WILDCARD) == 0 ||
769 includeStr.find(BASE_EL2 + DEFAULT_PATH_WITH_WILDCARD) == 0) {
770 // recognize sandbox path to physical path with wild card
771 RecognizeSandboxWildCard(userId, bundleName, includeStr, phyIncludes, pathMap);
772 if (phyIncludes.empty()) {
773 LOGE("DealWithIncludeFiles failed to recognize path with wildcard %{private}s", bundleName.c_str());
774 continue;
775 }
776 } else {
777 // convert sandbox to physical path
778 ConvertSandboxRealPath(userId, bundleName, includeStr, phyIncludes, pathMap);
779 }
780 }
781 }
782
PathSortFunc(const std::string &path1, const std::string &path2)783 static inline bool PathSortFunc(const std::string &path1, const std::string &path2)
784 {
785 return path1 < path2;
786 }
787
DeduplicationPath(std::vector<std::string> &configPaths)788 static void DeduplicationPath(std::vector<std::string> &configPaths)
789 {
790 sort(configPaths.begin(), configPaths.end(), PathSortFunc);
791 auto it = unique(configPaths.begin(), configPaths.end(), [](const std::string &path1, const std::string &path2) {
792 return path1 == path2;
793 });
794 configPaths.erase(it, configPaths.end());
795 }
796
GetBundleStatsForIncreaseEach(uint32_t userId, std::string &bundleName, int64_t lastBackupTime, std::vector<int64_t> &pkgFileSizes, std::vector<int64_t> &incPkgFileSizes)797 static void GetBundleStatsForIncreaseEach(uint32_t userId, std::string &bundleName, int64_t lastBackupTime,
798 std::vector<int64_t> &pkgFileSizes, std::vector<int64_t> &incPkgFileSizes)
799 {
800 // input parameters
801 BundleStatsParas paras = {.userId = userId, .bundleName = bundleName,
802 .lastBackupTime = lastBackupTime, .fileSizeSum = 0, .incFileSizeSum = 0};
803
804 // obtain includes, excludes in backup extension config
805 auto [includes, excludes] = ReadIncludesExcludesPath(bundleName, lastBackupTime, userId);
806 if (includes.empty()) {
807 pkgFileSizes.emplace_back(0);
808 incPkgFileSizes.emplace_back(0);
809 return;
810 }
811 // physical paths
812 std::vector<std::string> phyIncludes;
813 // map about sandbox path to physical path
814 std::map<std::string, std::string> pathMap;
815
816 // recognize physical path for include directory
817 DealWithIncludeFiles(paras, includes, phyIncludes, pathMap);
818 if (phyIncludes.empty()) {
819 LOGE("Incorrect convert for include sandbox path for %{private}s", bundleName.c_str());
820 pkgFileSizes.emplace_back(0);
821 incPkgFileSizes.emplace_back(0);
822 return;
823 }
824
825 // recognize physical path for exclude directory
826 std::vector<std::string> phyExcludes;
827 for (const auto &exclude : excludes) {
828 std::string excludeStr = exclude;
829 if (excludeStr.front() != FILE_SEPARATOR_CHAR) {
830 excludeStr = FILE_SEPARATOR_CHAR + excludeStr;
831 }
832 // convert sandbox to physical path
833 ConvertSandboxRealPath(userId, bundleName, excludeStr, phyExcludes, pathMap);
834 }
835
836 std::string filePath = BACKUP_PATH_PREFIX + std::to_string(userId) + BACKUP_PATH_SURFFIX +
837 bundleName + FILE_SEPARATOR_CHAR + BACKUP_STAT_SYMBOL + std::to_string(lastBackupTime);
838 std::ofstream statFile;
839 statFile.open(filePath.data(), std::ios::out | std::ios::trunc);
840 if (!statFile.is_open()) {
841 LOGE("creat file fail, errno:%{public}d.", errno);
842 pkgFileSizes.emplace_back(0);
843 incPkgFileSizes.emplace_back(0);
844 return;
845 }
846 statFile << VER_10_LINE1 << std::endl;
847 statFile << VER_10_LINE2 << std::endl;
848
849 DeduplicationPath(phyIncludes);
850 ScanExtensionPath(paras, phyIncludes, phyExcludes, pathMap, statFile);
851 // calculate summary file sizes
852 pkgFileSizes.emplace_back(paras.fileSizeSum);
853 incPkgFileSizes.emplace_back(paras.incFileSizeSum);
854 LOGI("bundleName: %{public}s, size: %{public}lld", bundleName.c_str(), static_cast<long long>(paras.fileSizeSum));
855 statFile.close();
856 }
857
GetBundleStatsForIncrease(uint32_t userId, const std::vector<std::string> &bundleNames, const std::vector<int64_t> &incrementalBackTimes, std::vector<int64_t> &pkgFileSizes, std::vector<int64_t> &incPkgFileSizes)858 int32_t QuotaManager::GetBundleStatsForIncrease(uint32_t userId, const std::vector<std::string> &bundleNames,
859 const std::vector<int64_t> &incrementalBackTimes, std::vector<int64_t> &pkgFileSizes,
860 std::vector<int64_t> &incPkgFileSizes)
861 {
862 LOGI("GetBundleStatsForIncrease start");
863 if (bundleNames.size() != incrementalBackTimes.size()) {
864 LOGE("Invalid paramters, size of bundleNames should match incrementalBackTimes.");
865 return E_SYS_ERR;
866 }
867
868 for (size_t i = 0; i < bundleNames.size(); i++) {
869 std::string bundleName = bundleNames[i];
870 int64_t lastBackupTime = incrementalBackTimes[i];
871 GetBundleStatsForIncreaseEach(userId, bundleName, lastBackupTime, pkgFileSizes, incPkgFileSizes);
872 }
873 return E_OK;
874 }
875 } // namespace STORAGE_DAEMON
876 } // namespace OHOS
877