• 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 <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     uint32_t uintprjId = static_cast<uint32_t>(prjId);
268     if (fsx.fsx_projid == uintprjId) {
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     return E_OK;
293 }
294 
ReadIncludesExcludesPath(const std::string & bundleName,const int64_t lastBackupTime,const uint32_t userId)295 static std::tuple<std::vector<std::string>, std::vector<std::string>> ReadIncludesExcludesPath(
296     const std::string &bundleName, const int64_t lastBackupTime, const uint32_t userId)
297 {
298     if (bundleName.empty()) {
299         LOGE("bundleName is empty");
300         return { {}, {} };
301     }
302     // 保存includeExclude的path
303     std::string filePath = BACKUP_PATH_PREFIX + std::to_string(userId) + BACKUP_PATH_SURFFIX +
304         bundleName + FILE_SEPARATOR_CHAR + BACKUP_INCEXC_SYMBOL + std::to_string(lastBackupTime);
305     std::ifstream incExcFile;
306     incExcFile.open(filePath.data());
307     if (!incExcFile.is_open()) {
308         LOGE("Cannot open include/exclude file, fail errno:%{public}d", errno);
309         return { {}, {} };
310     }
311 
312     std::vector<std::string> includes;
313     std::vector<std::string> excludes;
314     bool incOrExt = true;
315     while (incExcFile) {
316         std::string line;
317         std::getline(incExcFile, line);
318         if (line.empty()) {
319             LOGD("Read Complete");
320             break;
321         }
322         if (line == BACKUP_INCLUDE) {
323             incOrExt = true;
324         } else if (line == BACKUP_EXCLUDE) {
325             incOrExt = false;
326         }
327         if (incOrExt && line != BACKUP_INCLUDE) {
328             includes.emplace_back(line);
329         } else if (!incOrExt && line != BACKUP_EXCLUDE) {
330             excludes.emplace_back(line);
331         }
332     }
333     incExcFile.close();
334     return {includes, excludes};
335 }
336 
AddPathMapForPathWildCard(uint32_t userId,const std::string & bundleName,const std::string & phyPath,std::map<std::string,std::string> & pathMap)337 static bool AddPathMapForPathWildCard(uint32_t userId, const std::string &bundleName, const std::string &phyPath,
338     std::map<std::string, std::string> &pathMap)
339 {
340     std::string physicalPrefixEl1 = PHY_APP + EL1 + FILE_SEPARATOR_CHAR + std::to_string(userId) + BASE +
341         bundleName + FILE_SEPARATOR_CHAR;
342     std::string physicalPrefixEl2 = PHY_APP + EL2 + FILE_SEPARATOR_CHAR + std::to_string(userId) + BASE +
343         bundleName + FILE_SEPARATOR_CHAR;
344     if (phyPath.find(physicalPrefixEl1) == 0) {
345         std::string relatePath = phyPath.substr(physicalPrefixEl1.size());
346         pathMap.insert({phyPath, BASE_EL1 + relatePath});
347     } else if (phyPath.find(physicalPrefixEl2) == 0) {
348         std::string relatePath = phyPath.substr(physicalPrefixEl2.size());
349         pathMap.insert({phyPath, BASE_EL2 + relatePath});
350     } else {
351         LOGE("Invalid phyiscal path");
352         return false;
353     }
354     return true;
355 }
356 
GetPathWildCard(uint32_t userId,const std::string & bundleName,const std::string & includeWildCard,std::vector<std::string> & includePathList,std::map<std::string,std::string> & pathMap)357 static bool GetPathWildCard(uint32_t userId, const std::string &bundleName, const std::string &includeWildCard,
358     std::vector<std::string> &includePathList, std::map<std::string, std::string> &pathMap)
359 {
360     size_t pos = includeWildCard.rfind(WILDCARD_DEFAULT_INCLUDE);
361     if (pos == std::string::npos) {
362         LOGE("GetPathWildCard: path should include *");
363         return false;
364     }
365     std::string pathBeforeWildCard = includeWildCard.substr(0, pos);
366     DIR *dirPtr = opendir(pathBeforeWildCard.c_str());
367     if (dirPtr == nullptr) {
368         LOGE("GetPathWildCard open file dir:%{private}s fail, errno:%{public}d", pathBeforeWildCard.c_str(), errno);
369         return false;
370     }
371     struct dirent *entry = nullptr;
372     std::vector<std::string> subDirs;
373     while ((entry = readdir(dirPtr)) != nullptr) {
374         if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
375             continue;
376         }
377         std::string path = pathBeforeWildCard + entry->d_name;
378         if (entry->d_type == DT_DIR) {
379             subDirs.emplace_back(path);
380         }
381     }
382     closedir(dirPtr);
383     for (auto &subDir : subDirs) {
384         DIR *subDirPtr = opendir(subDir.c_str());
385         if (subDirPtr == nullptr) {
386             LOGE("GetPathWildCard open file dir:%{private}s fail, errno:%{public}d", subDir.c_str(), errno);
387             return false;
388         }
389         while ((entry = readdir(subDirPtr)) != nullptr) {
390             if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
391                 continue;
392             }
393             std::string dirName = std::string(entry->d_name);
394 
395             std::string path = subDir + FILE_SEPARATOR_CHAR + entry->d_name;
396             if (entry->d_type == DT_DIR && (dirName == DEFAULT_INCLUDE_PATH_IN_HAP_FILES ||
397                 dirName == DEFAULT_INCLUDE_PATH_IN_HAP_DATABASE ||
398                 dirName == DEFAULT_INCLUDE_PATH_IN_HAP_PREFERENCE)) {
399                 includePathList.emplace_back(path);
400                 AddPathMapForPathWildCard(userId, bundleName, path, pathMap);
401             }
402         }
403         closedir(subDirPtr);
404     }
405     return true;
406 }
407 
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)408 static void RecognizeSandboxWildCard(const uint32_t userId, const std::string &bundleName,
409     const std::string &sandboxPathStr, std::vector<std::string> &phyIncludes,
410     std::map<std::string, std::string>& pathMap)
411 {
412     if (sandboxPathStr.find(BASE_EL1 + DEFAULT_PATH_WITH_WILDCARD) == 0) {
413         std::string physicalPrefix = PHY_APP + EL1 + FILE_SEPARATOR_CHAR + std::to_string(userId) + BASE +
414             bundleName + FILE_SEPARATOR_CHAR;
415         std::string relatePath = sandboxPathStr.substr(BASE_EL1.size());
416         if (!GetPathWildCard(userId, bundleName, physicalPrefix + relatePath, phyIncludes, pathMap)) {
417             LOGE("el1 GetPathWildCard dir path invaild");
418         }
419     } else if (sandboxPathStr.find(BASE_EL2 + DEFAULT_PATH_WITH_WILDCARD) == 0) {
420         std::string physicalPrefix = PHY_APP + EL2 + FILE_SEPARATOR_CHAR + std::to_string(userId) + BASE +
421             bundleName + FILE_SEPARATOR_CHAR;
422         std::string relatePath = sandboxPathStr.substr(BASE_EL2.size());
423         if (!GetPathWildCard(userId, bundleName, physicalPrefix + relatePath, phyIncludes, pathMap)) {
424             LOGE("el2 GetPathWildCard dir path invaild");
425         }
426     }
427 }
428 
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)429 static void ConvertSandboxRealPath(const uint32_t userId, const std::string &bundleName,
430     const std::string &sandboxPathStr, std::vector<std::string> &realPaths,
431     std::map<std::string, std::string>& pathMap)
432 {
433     std::string uriString;
434     if (sandboxPathStr.find(NORMAL_SAND_PREFIX) == 0) {
435         // for normal hap, start with file://bundleName
436         uriString = URI_PREFIX + bundleName;
437     } else if (sandboxPathStr.find(FILE_SAND_PREFIX) == 0) {
438         // for public files, start with file://docs
439         uriString = URI_PREFIX + FILE_AUTHORITY;
440     } else if (sandboxPathStr.find(MEDIA_SAND_PREFIX) == 0) {
441         std::string physicalPath = sandboxPathStr;
442         physicalPath.insert(MEDIA_SAND_PREFIX.length(), FILE_SEPARATOR_CHAR + std::to_string(userId));
443         realPaths.emplace_back(physicalPath);
444         pathMap.insert({physicalPath, sandboxPathStr});
445         return;
446     } else if (sandboxPathStr.find(MEDIA_CLOUD_SAND_PREFIX) == 0) {
447         std::string physicalPath = sandboxPathStr;
448         physicalPath.insert(MEDIA_CLOUD_SAND_PREFIX.length(), FILE_SEPARATOR_CHAR + std::to_string(userId));
449         realPaths.emplace_back(physicalPath);
450         pathMap.insert({physicalPath, sandboxPathStr});
451         return;
452     }
453 
454     if (!uriString.empty()) {
455         std::string sandboxPathUriStr = AppFileService::SandboxHelper::Encode(sandboxPathStr);
456         uriString += sandboxPathUriStr;
457         AppFileService::ModuleFileUri::FileUri uri(uriString);
458         // files
459         std::string physicalPath;
460         int ret = AppFileService::SandboxHelper::GetBackupPhysicalPath(uri.ToString(), std::to_string(userId),
461             physicalPath);
462         if (ret != 0) {
463             LOGE("Get physical path failed with %{public}d", ret);
464             return;
465         }
466         realPaths.emplace_back(physicalPath);
467         pathMap.insert({physicalPath, sandboxPathStr});
468     }
469 }
470 
WriteFileList(std::ofstream & statFile,struct FileStat fileStat,BundleStatsParas & paras)471 static void WriteFileList(std::ofstream &statFile, struct FileStat fileStat, BundleStatsParas &paras)
472 {
473     if (!statFile.is_open() || fileStat.filePath.empty()) {
474         LOGE("WriteFileList Param failed");
475         return;
476     }
477     std::string fileLine = "";
478     bool encodeFlag = false;
479     if (fileStat.filePath.find(LINE_SEP) != std::string::npos) {
480         fileLine += AppFileService::SandboxHelper::Encode(fileStat.filePath) + FILE_CONTENT_SEPARATOR;
481         encodeFlag = true;
482     } else {
483         fileLine += fileStat.filePath + FILE_CONTENT_SEPARATOR;
484     }
485     fileLine += std::to_string(fileStat.mode) + FILE_CONTENT_SEPARATOR;
486     if (fileStat.isDir) {
487         fileLine += std::to_string(1) + FILE_CONTENT_SEPARATOR;
488     } else {
489         fileLine += std::to_string(0) + FILE_CONTENT_SEPARATOR;
490     }
491     fileLine += std::to_string(fileStat.fileSize) + FILE_CONTENT_SEPARATOR;
492     fileLine += std::to_string(fileStat.lastUpdateTime) + FILE_CONTENT_SEPARATOR;
493     fileLine += FILE_CONTENT_SEPARATOR;
494     if (fileStat.isIncre) {
495         fileLine += std::to_string(1);
496     } else {
497         fileLine += std::to_string(0);
498     }
499     fileLine += FILE_CONTENT_SEPARATOR;
500     if (encodeFlag) {
501         fileLine += std::to_string(1);
502     } else {
503         fileLine += std::to_string(0);
504     }
505     // te file line
506     statFile << fileLine << std::endl;
507     if (fileStat.isIncre) {
508         paras.incFileSizeSum += fileStat.fileSize;
509     }
510     paras.fileSizeSum += fileStat.fileSize;
511 }
512 
ExcludeFilter(std::map<std::string,bool> & excludesMap,const std::string & path)513 static bool ExcludeFilter(std::map<std::string, bool> &excludesMap, const std::string &path)
514 {
515     if (path.empty()) {
516         LOGE("ExcludeFilter Param failed");
517         return true;
518     }
519     std::string formatPath = path;
520     for (auto exclude = excludesMap.begin(); exclude != excludesMap.end(); exclude++) {
521         if (exclude->second != true) {
522             if (formatPath.compare(exclude->first) == 0) {
523                 return true;
524             }
525         } else {
526             if (formatPath.compare(0, exclude->first.size(), exclude->first) == 0) {
527                 return true;
528             }
529         }
530     }
531     return false;
532 }
533 
534 /**
535  * @brief Check if path in includes is directory or not
536  *
537  * @param path            path in includes
538  * @param paras           start time for last backup and file size sum
539  * @param pathMap         map for file sandbox path and physical path
540  * @param statFile        target file stream pointer
541  * @param excludeMap      map for exclude physical path and isDir
542  *
543  * @return std::tuple<bool, bool> : is success or not for system call / is directory or not
544  */
CheckIfDirForIncludes(const std::string & path,BundleStatsParas & paras,std::map<std::string,std::string> & pathMap,std::ofstream & statFile,std::map<std::string,bool> & excludesMap)545 static std::tuple<bool, bool> CheckIfDirForIncludes(const std::string &path, BundleStatsParas &paras,
546     std::map<std::string, std::string> &pathMap, std::ofstream &statFile, std::map<std::string, bool> &excludesMap)
547 {
548     if (!statFile.is_open() || path.empty()) {
549         LOGE("CheckIfDirForIncludes Param failed");
550         return {false, false};
551     }
552     // check whether the path exists
553     struct stat fileStatInfo = {0};
554     if (stat(path.c_str(), &fileStatInfo) != 0) {
555         LOGE("CheckIfDirForIncludes call stat error %{private}s, fail errno:%{public}d", path.c_str(), errno);
556         return {false, false};
557     }
558     if (S_ISDIR(fileStatInfo.st_mode)) {
559         LOGD("%{private}s exists and is a directory", path.c_str());
560         return {true, true};
561     } else {
562         std::string sandboxPath = path;
563         auto it = pathMap.find(path);
564         if (it != pathMap.end()) {
565             sandboxPath = it->second;
566         }
567 
568         struct FileStat fileStat;
569         fileStat.filePath = sandboxPath;
570         fileStat.fileSize = fileStatInfo.st_size;
571         // mode
572         fileStat.mode = static_cast<int32_t>(fileStatInfo.st_mode);
573         fileStat.isDir = false;
574         int64_t lastUpdateTime = static_cast<int64_t>(fileStatInfo.st_mtime);
575         fileStat.lastUpdateTime = lastUpdateTime;
576         if (paras.lastBackupTime == 0 || lastUpdateTime > paras.lastBackupTime) {
577             fileStat.isIncre = true;
578         }
579         if (ExcludeFilter(excludesMap, path) == false) {
580             WriteFileList(statFile, fileStat, paras);
581         }
582         return {true, false};
583     }
584 }
585 
PhysicalToSandboxPath(const std::string & dir,const std::string & sandboxDir,const std::string & path)586 static std::string PhysicalToSandboxPath(const std::string &dir, const std::string &sandboxDir,
587     const std::string &path)
588 {
589     std::size_t dirPos = dir.size();
590     std::string pathSurffix = path.substr(dirPos);
591     return sandboxDir + pathSurffix;
592 }
593 
AddOuterDirIntoFileStat(const std::string & dir,BundleStatsParas & paras,const std::string & sandboxDir,std::ofstream & statFile,std::map<std::string,bool> & excludesMap)594 static bool AddOuterDirIntoFileStat(const std::string &dir, BundleStatsParas &paras, const std::string &sandboxDir,
595     std::ofstream &statFile, std::map<std::string, bool> &excludesMap)
596 {
597     if (!statFile.is_open() || dir.empty()) {
598         LOGE("AddOuterDirIntoFileStat Param failed");
599         return false;
600     }
601     struct stat fileInfo = {0};
602     if (stat(dir.c_str(), &fileInfo) != 0) {
603         LOGE("AddOuterDirIntoFileStat call stat error %{private}s, fail errno:%{public}d", dir.c_str(), errno);
604         return false;
605     }
606     struct FileStat fileStat = {};
607     fileStat.filePath = PhysicalToSandboxPath(dir, sandboxDir, dir);
608     fileStat.fileSize = fileInfo.st_size;
609     // mode
610     fileStat.mode = static_cast<int32_t>(fileInfo.st_mode);
611     int64_t lastUpdateTime = static_cast<int64_t>(fileInfo.st_mtime);
612     fileStat.lastUpdateTime = lastUpdateTime;
613     fileStat.isIncre = (paras.lastBackupTime == 0 || lastUpdateTime > paras.lastBackupTime) ? true : false;
614     fileStat.isDir = true;
615     std::string formatPath = dir;
616     if (formatPath.back() != FILE_SEPARATOR_CHAR) {
617         formatPath.push_back(FILE_SEPARATOR_CHAR);
618     }
619     if (ExcludeFilter(excludesMap, formatPath) == false) {
620         WriteFileList(statFile, fileStat, paras);
621     }
622     return true;
623 }
624 
CheckOverLongPath(const std::string & path)625 uint32_t CheckOverLongPath(const std::string &path)
626 {
627     uint32_t len = path.length();
628     if (len >= PATH_MAX_LEN) {
629         size_t found = path.find_last_of('/');
630         std::string sub = path.substr(found + 1);
631         LOGE("Path over long, length:%{public}d, fileName:%{public}s.", len, sub.c_str());
632     }
633     return len;
634 }
635 
InsertStatFile(const std::string & path,struct FileStat fileStat,std::ofstream & statFile,std::map<std::string,bool> & excludesMap,BundleStatsParas & paras)636 static void InsertStatFile(const std::string &path, struct FileStat fileStat,
637     std::ofstream &statFile, std::map<std::string, bool> &excludesMap, BundleStatsParas &paras)
638 {
639     if (!statFile.is_open() || path.empty()) {
640         LOGE("InsertStatFile Param failed");
641         return;
642     }
643     std::string formatPath = path;
644     if (fileStat.isDir == true && formatPath.back() != FILE_SEPARATOR_CHAR) {
645         formatPath.push_back(FILE_SEPARATOR_CHAR);
646     }
647     if (!ExcludeFilter(excludesMap, formatPath)) {
648         WriteFileList(statFile, fileStat, paras);
649     }
650 }
651 
GetIncludesFileStats(const std::string & dir,BundleStatsParas & paras,std::map<std::string,std::string> & pathMap,std::ofstream & statFile,std::map<std::string,bool> & excludesMap)652 static bool GetIncludesFileStats(const std::string &dir, BundleStatsParas &paras,
653     std::map<std::string, std::string> &pathMap,
654     std::ofstream &statFile, std::map<std::string, bool> &excludesMap)
655 {
656     std::string sandboxDir = dir;
657     auto it = pathMap.find(dir);
658     if (it != pathMap.end()) {
659         sandboxDir = it->second;
660     }
661     // stat current directory info
662     AddOuterDirIntoFileStat(dir, paras, sandboxDir, statFile, excludesMap);
663 
664     std::stack<std::string> folderStack;
665     std::string filePath;
666     folderStack.push(dir);
667     // stat files and sub-directory in current directory info
668     while (!folderStack.empty()) {
669         filePath = folderStack.top();
670         folderStack.pop();
671         DIR *dirPtr = opendir(filePath.c_str());
672         if (dirPtr == nullptr) {
673             LOGE("GetIncludesFileStats open file dir:%{private}s fail, errno:%{public}d", filePath.c_str(), errno);
674             continue;
675         }
676         if (filePath.back() != FILE_SEPARATOR_CHAR) {
677             filePath.push_back(FILE_SEPARATOR_CHAR);
678         }
679 
680         struct dirent *entry = nullptr;
681         while ((entry = readdir(dirPtr)) != nullptr) {
682             if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
683                 continue;
684             }
685             std::string path = filePath + entry->d_name;
686             struct stat fileInfo = {0};
687             if (stat(path.c_str(), &fileInfo) != 0) {
688                 LOGE("GetIncludesFileStats call stat error %{private}s, errno:%{public}d", path.c_str(), errno);
689                 fileInfo.st_size = 0;
690             }
691             struct FileStat fileStat = {};
692             fileStat.filePath = PhysicalToSandboxPath(dir, sandboxDir, path);
693             fileStat.fileSize = fileInfo.st_size;
694             CheckOverLongPath(fileStat.filePath);
695             // mode
696             fileStat.mode = static_cast<int32_t>(fileInfo.st_mode);
697             int64_t lastUpdateTime = static_cast<int64_t>(fileInfo.st_mtime);
698             fileStat.lastUpdateTime = lastUpdateTime;
699             fileStat.isIncre = (paras.lastBackupTime == 0 || lastUpdateTime > paras.lastBackupTime) ? true : false;
700             if (entry->d_type == DT_DIR) {
701                 fileStat.isDir = true;
702                 folderStack.push(path);
703             }
704             InsertStatFile(path, fileStat, statFile, excludesMap, paras);
705         }
706         closedir(dirPtr);
707     }
708     return true;
709 }
710 
SetExcludePathMap(std::string & excludePath,std::map<std::string,bool> & excludesMap)711 static void SetExcludePathMap(std::string &excludePath, std::map<std::string, bool> &excludesMap)
712 {
713     if (excludePath.empty()) {
714         LOGE("SetExcludePathMap Param failed");
715         return;
716     }
717     struct stat fileStatInfo = {0};
718     if (stat(excludePath.c_str(), &fileStatInfo) != 0) {
719         LOGE("SetExcludePathMap call stat error %{private}s, errno:%{public}d", excludePath.c_str(), errno);
720         return;
721     }
722     if (S_ISDIR(fileStatInfo.st_mode)) {
723         if (excludePath.back() != FILE_SEPARATOR_CHAR) {
724             excludePath.push_back(FILE_SEPARATOR_CHAR);
725         }
726         excludesMap.insert({excludePath, true});
727     } else {
728         excludesMap.insert({excludePath, false});
729     }
730 }
731 
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)732 static void ScanExtensionPath(BundleStatsParas &paras,
733     const std::vector<std::string> &includes, const std::vector<std::string> &excludes,
734     std::map<std::string, std::string> &pathMap, std::ofstream &statFile)
735 {
736     std::map<std::string, bool> excludesMap;
737     for (auto exclude : excludes) {
738         SetExcludePathMap(exclude, excludesMap);
739     }
740     // all file with stats in include directory
741     for (const auto &includeDir : includes) {
742         // Check if includeDir is a file path
743         auto [isSucc, isDir] = CheckIfDirForIncludes(includeDir, paras, pathMap, statFile, excludesMap);
744         if (!isSucc) {
745             continue;
746         }
747         // recognize all file in include directory
748         if (isDir && !GetIncludesFileStats(includeDir, paras, pathMap, statFile, excludesMap)) {
749             LOGE("Faied to get include files for includeDir");
750         }
751     }
752 }
753 
DealWithIncludeFiles(const BundleStatsParas & paras,const std::vector<std::string> & includes,std::vector<std::string> & phyIncludes,std::map<std::string,std::string> & pathMap)754 static void DealWithIncludeFiles(const BundleStatsParas &paras, const std::vector<std::string> &includes,
755     std::vector<std::string> &phyIncludes, std::map<std::string, std::string>& pathMap)
756 {
757     uint32_t userId = paras.userId;
758     std::string bundleName = paras.bundleName;
759     for (const auto &include : includes) {
760         std::string includeStr = include;
761         if (includeStr.front() != FILE_SEPARATOR_CHAR) {
762             includeStr = FILE_SEPARATOR_CHAR + includeStr;
763         }
764         if (includeStr.find(BASE_EL1 + DEFAULT_PATH_WITH_WILDCARD) == 0 ||
765             includeStr.find(BASE_EL2 + DEFAULT_PATH_WITH_WILDCARD) == 0) {
766             // recognize sandbox path to physical path with wild card
767             RecognizeSandboxWildCard(userId, bundleName, includeStr, phyIncludes, pathMap);
768             if (phyIncludes.empty()) {
769                 LOGE("DealWithIncludeFiles failed to recognize path with wildcard %{private}s", bundleName.c_str());
770                 continue;
771             }
772         } else {
773             // convert sandbox to physical path
774             ConvertSandboxRealPath(userId, bundleName, includeStr, phyIncludes, pathMap);
775         }
776     }
777 }
778 
PathSortFunc(const std::string & path1,const std::string & path2)779 static inline bool PathSortFunc(const std::string &path1, const std::string &path2)
780 {
781     return path1 < path2;
782 }
783 
DeduplicationPath(std::vector<std::string> & configPaths)784 static void DeduplicationPath(std::vector<std::string> &configPaths)
785 {
786     sort(configPaths.begin(), configPaths.end(), PathSortFunc);
787     auto it = unique(configPaths.begin(), configPaths.end(), [](const std::string &path1, const std::string &path2) {
788         return path1 == path2;
789     });
790     configPaths.erase(it, configPaths.end());
791 }
792 
GetBundleStatsForIncreaseEach(uint32_t userId,std::string & bundleName,int64_t lastBackupTime,std::vector<int64_t> & pkgFileSizes,std::vector<int64_t> & incPkgFileSizes)793 static void GetBundleStatsForIncreaseEach(uint32_t userId, std::string &bundleName, int64_t lastBackupTime,
794     std::vector<int64_t> &pkgFileSizes, std::vector<int64_t> &incPkgFileSizes)
795 {
796     // input parameters
797     BundleStatsParas paras = {.userId = userId, .bundleName = bundleName,
798                               .lastBackupTime = lastBackupTime, .fileSizeSum = 0, .incFileSizeSum = 0};
799 
800     // obtain includes, excludes in backup extension config
801     auto [includes, excludes] = ReadIncludesExcludesPath(bundleName, lastBackupTime, userId);
802     if (includes.empty()) {
803         pkgFileSizes.emplace_back(0);
804         incPkgFileSizes.emplace_back(0);
805         return;
806     }
807     // physical paths
808     std::vector<std::string> phyIncludes;
809     // map about sandbox path to physical path
810     std::map<std::string, std::string> pathMap;
811 
812     // recognize physical path for include directory
813     DealWithIncludeFiles(paras, includes, phyIncludes, pathMap);
814     if (phyIncludes.empty()) {
815         LOGE("Incorrect convert for include sandbox path for %{private}s", bundleName.c_str());
816         pkgFileSizes.emplace_back(0);
817         incPkgFileSizes.emplace_back(0);
818         return;
819     }
820 
821     // recognize physical path for exclude directory
822     std::vector<std::string> phyExcludes;
823     for (const auto &exclude : excludes) {
824         std::string excludeStr = exclude;
825         if (excludeStr.front() != FILE_SEPARATOR_CHAR) {
826             excludeStr = FILE_SEPARATOR_CHAR + excludeStr;
827         }
828         // convert sandbox to physical path
829         ConvertSandboxRealPath(userId, bundleName, excludeStr, phyExcludes, pathMap);
830     }
831 
832     std::string filePath = BACKUP_PATH_PREFIX + std::to_string(userId) + BACKUP_PATH_SURFFIX +
833         bundleName + FILE_SEPARATOR_CHAR + BACKUP_STAT_SYMBOL + std::to_string(lastBackupTime);
834     std::ofstream statFile;
835     statFile.open(filePath.data(), std::ios::out | std::ios::trunc);
836     if (!statFile.is_open()) {
837         LOGE("creat file fail, errno:%{public}d.", errno);
838         pkgFileSizes.emplace_back(0);
839         incPkgFileSizes.emplace_back(0);
840         return;
841     }
842     statFile << VER_10_LINE1 << std::endl;
843     statFile << VER_10_LINE2 << std::endl;
844 
845     DeduplicationPath(phyIncludes);
846     ScanExtensionPath(paras, phyIncludes, phyExcludes, pathMap, statFile);
847     // calculate summary file sizes
848     pkgFileSizes.emplace_back(paras.fileSizeSum);
849     incPkgFileSizes.emplace_back(paras.incFileSizeSum);
850     LOGI("bundleName: %{public}s, size: %{public}lld", bundleName.c_str(), static_cast<long long>(paras.fileSizeSum));
851     statFile.close();
852 }
853 
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)854 int32_t QuotaManager::GetBundleStatsForIncrease(uint32_t userId, const std::vector<std::string> &bundleNames,
855     const std::vector<int64_t> &incrementalBackTimes, std::vector<int64_t> &pkgFileSizes,
856     std::vector<int64_t> &incPkgFileSizes)
857 {
858     LOGI("GetBundleStatsForIncrease start");
859     if (bundleNames.size() != incrementalBackTimes.size()) {
860         LOGE("Invalid paramters, size of bundleNames should match incrementalBackTimes.");
861         return E_SYS_ERR;
862     }
863 
864     for (size_t i = 0; i < bundleNames.size(); i++) {
865         std::string bundleName = bundleNames[i];
866         int64_t lastBackupTime = incrementalBackTimes[i];
867         GetBundleStatsForIncreaseEach(userId, bundleName, lastBackupTime, pkgFileSizes, incPkgFileSizes);
868     }
869     return E_OK;
870 }
871 } // namespace STORAGE_DAEMON
872 } // namespace OHOS
873