Loading cmds/installd/CacheItem.cpp +55 −9 Original line number Diff line number Diff line Loading @@ -16,8 +16,9 @@ #include "CacheItem.h" #include <stdint.h> #include <inttypes.h> #include <stdint.h> #include <sys/xattr.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> Loading @@ -29,13 +30,24 @@ using android::base::StringPrintf; namespace android { namespace installd { CacheItem::CacheItem(const std::shared_ptr<CacheItem>& parent, FTSENT* p) : mParent(parent) { CacheItem::CacheItem(FTSENT* p) { level = p->fts_level; directory = S_ISDIR(p->fts_statp->st_mode); size = p->fts_statp->st_blocks * 512; modified = p->fts_statp->st_mtime; mParent = static_cast<CacheItem*>(p->fts_parent->fts_pointer); if (mParent) { atomic = mParent->atomic; tombstone = mParent->tombstone; mName = p->fts_name; mName.insert(0, "/"); } else { atomic = false; tombstone = false; mName = p->fts_path; } } CacheItem::~CacheItem() { } Loading @@ -46,7 +58,7 @@ std::string CacheItem::toString() { std::string CacheItem::buildPath() { std::string res = mName; std::shared_ptr<CacheItem> parent = mParent; CacheItem* parent = mParent; while (parent) { res.insert(0, parent->mName); parent = parent->mParent; Loading @@ -57,13 +69,47 @@ std::string CacheItem::buildPath() { int CacheItem::purge() { auto path = buildPath(); if (directory) { return delete_dir_contents_and_dir(path, true); FTS *fts; FTSENT *p; char *argv[] = { (char*) path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) { PLOG(WARNING) << "Failed to fts_open " << path; return -1; } while ((p = fts_read(fts)) != nullptr) { switch (p->fts_info) { case FTS_D: if (p->fts_level == 0) { p->fts_number = tombstone; } else { int res = unlink(path.c_str()); if (res != 0) { PLOG(WARNING) << "Failed to unlink " << path; p->fts_number = p->fts_parent->fts_number | (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0); } break; case FTS_F: if (p->fts_parent->fts_number) { truncate(p->fts_path, 0); } else { unlink(p->fts_path); } break; case FTS_DEFAULT: case FTS_SL: case FTS_SLNONE: unlink(p->fts_path); break; case FTS_DP: rmdir(p->fts_path); break; } } return 0; } else { if (tombstone) { return truncate(path.c_str(), 0); } else { return unlink(path.c_str()); } return res; } } Loading cmds/installd/CacheItem.h +4 −2 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ namespace installd { */ class CacheItem { public: CacheItem(const std::shared_ptr<CacheItem>& parent, FTSENT* p); CacheItem(FTSENT* p); ~CacheItem(); std::string toString(); Loading @@ -46,11 +46,13 @@ public: short level; bool directory; bool atomic; bool tombstone; int64_t size; time_t modified; private: std::shared_ptr<CacheItem> mParent; CacheItem* mParent; std::string mName; DISALLOW_COPY_AND_ASSIGN(CacheItem); Loading cmds/installd/CacheTracker.cpp +49 −19 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <fts.h> #include <sys/quota.h> #include <sys/xattr.h> #include <utils/Trace.h> #include <android-base/logging.h> Loading Loading @@ -86,30 +87,59 @@ void CacheTracker::loadItemsFrom(const std::string& path) { PLOG(WARNING) << "Failed to fts_open " << path; return; } // TODO: add support for "user.atomic" and "user.tombstone" xattrs while ((p = fts_read(fts)) != NULL) { while ((p = fts_read(fts)) != nullptr) { if (p->fts_level == 0) continue; // Create tracking nodes for everything we encounter switch (p->fts_info) { case FTS_D: // Track the newest mtime of anything inside so we consider // deleting the directory last p->fts_number = p->fts_statp->st_mtime; break; case FTS_DP: p->fts_statp->st_mtime = p->fts_number; case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: { auto item = std::shared_ptr<CacheItem>(new CacheItem(p)); p->fts_pointer = static_cast<void*>(item.get()); items.push_back(item); } } // Ignore the actual top-level cache directories if (p->fts_level == 0) break; switch (p->fts_info) { case FTS_D: { auto item = static_cast<CacheItem*>(p->fts_pointer); item->atomic |= (getxattr(p->fts_path, kXattrCacheAtomic, nullptr, 0) >= 0); item->tombstone |= (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0); // When atomic, immediately collect all files under tree if (item->atomic) { while ((p = fts_read(fts)) != nullptr) { if (p->fts_info == FTS_DP && p->fts_level == item->level) break; switch (p->fts_info) { case FTS_D: case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: // TODO: optimize path memory footprint items.push_back(std::shared_ptr<CacheItem>(new CacheItem(nullptr, p))); item->size += p->fts_statp->st_blocks * 512; item->modified = std::max(item->modified, p->fts_statp->st_mtime); } } } } } // Track the newest modified item under this tree p->fts_parent->fts_number = std::max(p->fts_parent->fts_number, p->fts_statp->st_mtime); break; // Bubble up modified time to parent switch (p->fts_info) { case FTS_DP: case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: { auto item = static_cast<CacheItem*>(p->fts_pointer); auto parent = static_cast<CacheItem*>(p->fts_parent->fts_pointer); if (parent) { parent->modified = std::max(parent->modified, item->modified); } } } } fts_close(fts); Loading Loading @@ -137,7 +167,7 @@ void CacheTracker::loadItems() { } return left->directory; }; std::sort(items.begin(), items.end(), cmp); std::stable_sort(items.begin(), items.end(), cmp); ATRACE_END(); } Loading cmds/installd/InstalldNativeService.cpp +10 −1 Original line number Diff line number Diff line Loading @@ -86,7 +86,8 @@ static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; static constexpr int FLAG_USE_QUOTA = 1 << 12; static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13; static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 14; static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14; static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 15; namespace { Loading Loading @@ -833,6 +834,14 @@ binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::strin ATRACE_BEGIN("bounce"); std::shared_ptr<CacheTracker> active; while (active || !queue.empty()) { // Only look at apps under quota when explicitly requested if (active && (active->getCacheRatio() < 10000) && !(flags & FLAG_FREE_CACHE_V2_DEFY_QUOTA)) { LOG(DEBUG) << "Active ratio " << active->getCacheRatio() << " isn't over quota, and defy not requested"; break; } // Find the best tracker to work with; this might involve swapping // if the active tracker is no longer the most over quota bool nextBetter = active && !queue.empty() Loading cmds/installd/tests/Android.bp +19 −0 Original line number Diff line number Diff line Loading @@ -14,3 +14,22 @@ cc_test { "libdiskusage", ], } cc_test { name: "installd_cache_test", clang: true, srcs: ["installd_cache_test.cpp"], shared_libs: [ "libbase", "libbinder", "libcutils", "liblog", "liblogwrap", "libselinux", "libutils", ], static_libs: [ "libinstalld", "libdiskusage", ], } Loading
cmds/installd/CacheItem.cpp +55 −9 Original line number Diff line number Diff line Loading @@ -16,8 +16,9 @@ #include "CacheItem.h" #include <stdint.h> #include <inttypes.h> #include <stdint.h> #include <sys/xattr.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> Loading @@ -29,13 +30,24 @@ using android::base::StringPrintf; namespace android { namespace installd { CacheItem::CacheItem(const std::shared_ptr<CacheItem>& parent, FTSENT* p) : mParent(parent) { CacheItem::CacheItem(FTSENT* p) { level = p->fts_level; directory = S_ISDIR(p->fts_statp->st_mode); size = p->fts_statp->st_blocks * 512; modified = p->fts_statp->st_mtime; mParent = static_cast<CacheItem*>(p->fts_parent->fts_pointer); if (mParent) { atomic = mParent->atomic; tombstone = mParent->tombstone; mName = p->fts_name; mName.insert(0, "/"); } else { atomic = false; tombstone = false; mName = p->fts_path; } } CacheItem::~CacheItem() { } Loading @@ -46,7 +58,7 @@ std::string CacheItem::toString() { std::string CacheItem::buildPath() { std::string res = mName; std::shared_ptr<CacheItem> parent = mParent; CacheItem* parent = mParent; while (parent) { res.insert(0, parent->mName); parent = parent->mParent; Loading @@ -57,13 +69,47 @@ std::string CacheItem::buildPath() { int CacheItem::purge() { auto path = buildPath(); if (directory) { return delete_dir_contents_and_dir(path, true); FTS *fts; FTSENT *p; char *argv[] = { (char*) path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) { PLOG(WARNING) << "Failed to fts_open " << path; return -1; } while ((p = fts_read(fts)) != nullptr) { switch (p->fts_info) { case FTS_D: if (p->fts_level == 0) { p->fts_number = tombstone; } else { int res = unlink(path.c_str()); if (res != 0) { PLOG(WARNING) << "Failed to unlink " << path; p->fts_number = p->fts_parent->fts_number | (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0); } break; case FTS_F: if (p->fts_parent->fts_number) { truncate(p->fts_path, 0); } else { unlink(p->fts_path); } break; case FTS_DEFAULT: case FTS_SL: case FTS_SLNONE: unlink(p->fts_path); break; case FTS_DP: rmdir(p->fts_path); break; } } return 0; } else { if (tombstone) { return truncate(path.c_str(), 0); } else { return unlink(path.c_str()); } return res; } } Loading
cmds/installd/CacheItem.h +4 −2 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ namespace installd { */ class CacheItem { public: CacheItem(const std::shared_ptr<CacheItem>& parent, FTSENT* p); CacheItem(FTSENT* p); ~CacheItem(); std::string toString(); Loading @@ -46,11 +46,13 @@ public: short level; bool directory; bool atomic; bool tombstone; int64_t size; time_t modified; private: std::shared_ptr<CacheItem> mParent; CacheItem* mParent; std::string mName; DISALLOW_COPY_AND_ASSIGN(CacheItem); Loading
cmds/installd/CacheTracker.cpp +49 −19 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <fts.h> #include <sys/quota.h> #include <sys/xattr.h> #include <utils/Trace.h> #include <android-base/logging.h> Loading Loading @@ -86,30 +87,59 @@ void CacheTracker::loadItemsFrom(const std::string& path) { PLOG(WARNING) << "Failed to fts_open " << path; return; } // TODO: add support for "user.atomic" and "user.tombstone" xattrs while ((p = fts_read(fts)) != NULL) { while ((p = fts_read(fts)) != nullptr) { if (p->fts_level == 0) continue; // Create tracking nodes for everything we encounter switch (p->fts_info) { case FTS_D: // Track the newest mtime of anything inside so we consider // deleting the directory last p->fts_number = p->fts_statp->st_mtime; break; case FTS_DP: p->fts_statp->st_mtime = p->fts_number; case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: { auto item = std::shared_ptr<CacheItem>(new CacheItem(p)); p->fts_pointer = static_cast<void*>(item.get()); items.push_back(item); } } // Ignore the actual top-level cache directories if (p->fts_level == 0) break; switch (p->fts_info) { case FTS_D: { auto item = static_cast<CacheItem*>(p->fts_pointer); item->atomic |= (getxattr(p->fts_path, kXattrCacheAtomic, nullptr, 0) >= 0); item->tombstone |= (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0); // When atomic, immediately collect all files under tree if (item->atomic) { while ((p = fts_read(fts)) != nullptr) { if (p->fts_info == FTS_DP && p->fts_level == item->level) break; switch (p->fts_info) { case FTS_D: case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: // TODO: optimize path memory footprint items.push_back(std::shared_ptr<CacheItem>(new CacheItem(nullptr, p))); item->size += p->fts_statp->st_blocks * 512; item->modified = std::max(item->modified, p->fts_statp->st_mtime); } } } } } // Track the newest modified item under this tree p->fts_parent->fts_number = std::max(p->fts_parent->fts_number, p->fts_statp->st_mtime); break; // Bubble up modified time to parent switch (p->fts_info) { case FTS_DP: case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: { auto item = static_cast<CacheItem*>(p->fts_pointer); auto parent = static_cast<CacheItem*>(p->fts_parent->fts_pointer); if (parent) { parent->modified = std::max(parent->modified, item->modified); } } } } fts_close(fts); Loading Loading @@ -137,7 +167,7 @@ void CacheTracker::loadItems() { } return left->directory; }; std::sort(items.begin(), items.end(), cmp); std::stable_sort(items.begin(), items.end(), cmp); ATRACE_END(); } Loading
cmds/installd/InstalldNativeService.cpp +10 −1 Original line number Diff line number Diff line Loading @@ -86,7 +86,8 @@ static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; static constexpr int FLAG_USE_QUOTA = 1 << 12; static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13; static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 14; static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14; static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 15; namespace { Loading Loading @@ -833,6 +834,14 @@ binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::strin ATRACE_BEGIN("bounce"); std::shared_ptr<CacheTracker> active; while (active || !queue.empty()) { // Only look at apps under quota when explicitly requested if (active && (active->getCacheRatio() < 10000) && !(flags & FLAG_FREE_CACHE_V2_DEFY_QUOTA)) { LOG(DEBUG) << "Active ratio " << active->getCacheRatio() << " isn't over quota, and defy not requested"; break; } // Find the best tracker to work with; this might involve swapping // if the active tracker is no longer the most over quota bool nextBetter = active && !queue.empty() Loading
cmds/installd/tests/Android.bp +19 −0 Original line number Diff line number Diff line Loading @@ -14,3 +14,22 @@ cc_test { "libdiskusage", ], } cc_test { name: "installd_cache_test", clang: true, srcs: ["installd_cache_test.cpp"], shared_libs: [ "libbase", "libbinder", "libcutils", "liblog", "liblogwrap", "libselinux", "libutils", ], static_libs: [ "libinstalld", "libdiskusage", ], }