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