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 ¶s)
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 ¶s,
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 ¶s, 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 ¶s)
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 ¶s,
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 ¶s,
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 ¶s, 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