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