1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER 18 19 #include "CacheTracker.h" 20 21 #include <fts.h> 22 #include <sys/xattr.h> 23 #include <utils/Trace.h> 24 25 #include <android-base/logging.h> 26 #include <android-base/stringprintf.h> 27 28 #include "QuotaUtils.h" 29 #include "utils.h" 30 31 using android::base::StringPrintf; 32 33 namespace android { 34 namespace installd { 35 36 CacheTracker::CacheTracker(userid_t userId, appid_t appId, const std::string& uuid) 37 : cacheUsed(0), 38 cacheQuota(0), 39 mUserId(userId), 40 mAppId(appId), 41 mItemsLoaded(false), 42 mUuid(uuid) { 43 } 44 45 CacheTracker::~CacheTracker() { 46 } 47 48 std::string CacheTracker::toString() { 49 return StringPrintf("UID=%d used=%" PRId64 " quota=%" PRId64 " ratio=%d", 50 multiuser_get_uid(mUserId, mAppId), cacheUsed, cacheQuota, getCacheRatio()); 51 } 52 53 void CacheTracker::addDataPath(const std::string& dataPath) { 54 mDataPaths.push_back(dataPath); 55 } 56 57 void CacheTracker::loadStats() { 58 ATRACE_BEGIN("loadStats quota"); 59 cacheUsed = 0; 60 if (loadQuotaStats()) { 61 return; 62 } 63 ATRACE_END(); 64 65 ATRACE_BEGIN("loadStats tree"); 66 cacheUsed = 0; 67 for (const auto& path : mDataPaths) { 68 auto cachePath = read_path_inode(path, "cache", kXattrInodeCache); 69 auto codeCachePath = read_path_inode(path, "code_cache", kXattrInodeCodeCache); 70 calculate_tree_size(cachePath, &cacheUsed); 71 calculate_tree_size(codeCachePath, &cacheUsed); 72 } 73 ATRACE_END(); 74 } 75 76 bool CacheTracker::loadQuotaStats() { 77 int cacheGid = multiuser_get_cache_gid(mUserId, mAppId); 78 int extCacheGid = multiuser_get_ext_cache_gid(mUserId, mAppId); 79 if (IsQuotaSupported(mUuid) && cacheGid != -1 && extCacheGid != -1) { 80 int64_t space; 81 if ((space = GetOccupiedSpaceForGid(mUuid, cacheGid)) != -1) { 82 cacheUsed += space; 83 } else { 84 return false; 85 } 86 87 if ((space = GetOccupiedSpaceForGid(mUuid, extCacheGid)) != -1) { 88 cacheUsed += space; 89 } else { 90 return false; 91 } 92 return true; 93 } else { 94 return false; 95 } 96 } 97 98 void CacheTracker::loadItemsFrom(const std::string& path) { 99 FTS *fts; 100 FTSENT *p; 101 char *argv[] = { (char*) path.c_str(), nullptr }; 102 if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { 103 PLOG(WARNING) << "Failed to fts_open " << path; 104 return; 105 } 106 while ((p = fts_read(fts)) != nullptr) { 107 if (p->fts_level == 0) continue; 108 109 // Create tracking nodes for everything we encounter 110 switch (p->fts_info) { 111 case FTS_D: 112 case FTS_DEFAULT: 113 case FTS_F: 114 case FTS_SL: 115 case FTS_SLNONE: { 116 auto item = std::shared_ptr<CacheItem>(new CacheItem(p)); 117 p->fts_pointer = static_cast<void*>(item.get()); 118 items.push_back(item); 119 } 120 } 121 122 switch (p->fts_info) { 123 case FTS_D: { 124 auto item = static_cast<CacheItem*>(p->fts_pointer); 125 item->group |= (getxattr(p->fts_path, kXattrCacheGroup, nullptr, 0) >= 0); 126 item->tombstone |= (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0); 127 128 // When group, immediately collect all files under tree 129 if (item->group) { 130 while ((p = fts_read(fts)) != nullptr) { 131 if (p->fts_info == FTS_DP && p->fts_level == item->level) break; 132 switch (p->fts_info) { 133 case FTS_D: 134 case FTS_DEFAULT: 135 case FTS_F: 136 case FTS_SL: 137 case FTS_SLNONE: 138 item->size += p->fts_statp->st_blocks * 512; 139 item->modified = std::max(item->modified, p->fts_statp->st_mtime); 140 } 141 } 142 } 143 } 144 } 145 146 // Bubble up modified time to parent 147 CHECK(p != nullptr); 148 switch (p->fts_info) { 149 case FTS_DP: 150 case FTS_DEFAULT: 151 case FTS_F: 152 case FTS_SL: 153 case FTS_SLNONE: { 154 auto item = static_cast<CacheItem*>(p->fts_pointer); 155 auto parent = static_cast<CacheItem*>(p->fts_parent->fts_pointer); 156 if (parent) { 157 parent->modified = std::max(parent->modified, item->modified); 158 } 159 } 160 } 161 } 162 fts_close(fts); 163 } 164 165 void CacheTracker::loadItems() { 166 items.clear(); 167 168 ATRACE_BEGIN("loadItems"); 169 for (const auto& path : mDataPaths) { 170 loadItemsFrom(read_path_inode(path, "cache", kXattrInodeCache)); 171 loadItemsFrom(read_path_inode(path, "code_cache", kXattrInodeCodeCache)); 172 } 173 ATRACE_END(); 174 175 ATRACE_BEGIN("sortItems"); 176 auto cmp = [](std::shared_ptr<CacheItem> left, std::shared_ptr<CacheItem> right) { 177 // TODO: sort dotfiles last 178 // TODO: sort code_cache last 179 if (left->modified != right->modified) { 180 return (left->modified > right->modified); 181 } 182 if (left->level != right->level) { 183 return (left->level < right->level); 184 } 185 return left->directory; 186 }; 187 std::stable_sort(items.begin(), items.end(), cmp); 188 ATRACE_END(); 189 } 190 191 void CacheTracker::ensureItems() { 192 if (mItemsLoaded) { 193 return; 194 } else { 195 loadItems(); 196 mItemsLoaded = true; 197 } 198 } 199 200 int CacheTracker::getCacheRatio() { 201 if (cacheQuota == 0) { 202 return 0; 203 } else { 204 return (cacheUsed * 10000) / cacheQuota; 205 } 206 } 207 208 } // namespace installd 209 } // namespace android 210