Home | History | Annotate | Download | only in installd
      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