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