diff options
author | 2017-02-21 18:30:28 -0700 | |
---|---|---|
committer | 2017-02-22 11:44:00 -0700 | |
commit | 871a8f236ef2a055b9955b47a342b2c44c020ef7 (patch) | |
tree | 5a244a3f593ac20f0fd771a8ae8ca37b7fb90aeb | |
parent | 42714ad63c08899719bc1a6fe7fb3966fd66f0a8 (diff) |
Logic for atmoic/tombstone behavior; split mode.
Flesh out logic for cache directories that request new atomic and/or
tombstone clearing behaviors. Atomic directories are considered for
deletion as a single all-or-nothing unit, and tombstone directories
truncate any removed files instead of unlinking them.
Since these behaviors can be mixed together, add local tests that
quickly verify several different permutations.
Reduce memory footprint of CacheItem objects by only storing name
and pointer to parent (instead of full path). Fix ordering bug by
switching to std::stable_sort.
Add "V2_DEFY_QUOTA" flag so we can split clearing into two distinct
phases: clearing data for apps above their quotas, and then pushing
deeper by clearing data for apps below their quotas.
Test: adb shell /data/nativetest64/installd_cache_test/installd_cache_test
Bug: 34692014, 33811826
Change-Id: I156897de1d1d1c371b2b837128b2e286bf33d40d
-rw-r--r-- | cmds/installd/CacheItem.cpp | 64 | ||||
-rw-r--r-- | cmds/installd/CacheItem.h | 6 | ||||
-rw-r--r-- | cmds/installd/CacheTracker.cpp | 68 | ||||
-rw-r--r-- | cmds/installd/InstalldNativeService.cpp | 11 | ||||
-rw-r--r-- | cmds/installd/tests/Android.bp | 19 | ||||
-rw-r--r-- | cmds/installd/tests/installd_cache_test.cpp | 323 | ||||
-rw-r--r-- | cmds/installd/tests/installd_utils_test.cpp | 12 | ||||
-rw-r--r-- | cmds/installd/utils.cpp | 4 | ||||
-rw-r--r-- | cmds/installd/utils.h | 2 |
9 files changed, 474 insertions, 35 deletions
diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp index d1bdded8b1..6caf149b1c 100644 --- a/cmds/installd/CacheItem.cpp +++ b/cmds/installd/CacheItem.cpp @@ -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> @@ -29,12 +30,23 @@ 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; - mName = p->fts_path; + + 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() { @@ -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; @@ -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 { + 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 { - int res = unlink(path.c_str()); - if (res != 0) { - PLOG(WARNING) << "Failed to unlink " << path; + if (tombstone) { + return truncate(path.c_str(), 0); + } else { + return unlink(path.c_str()); } - return res; } } diff --git a/cmds/installd/CacheItem.h b/cmds/installd/CacheItem.h index bec8bc85b8..afbb15dafb 100644 --- a/cmds/installd/CacheItem.h +++ b/cmds/installd/CacheItem.h @@ -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(); @@ -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); diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp index 23c4330ca2..9377836ad6 100644 --- a/cmds/installd/CacheTracker.cpp +++ b/cmds/installd/CacheTracker.cpp @@ -20,6 +20,7 @@ #include <fts.h> #include <sys/quota.h> +#include <sys/xattr.h> #include <utils/Trace.h> #include <android-base/logging.h> @@ -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); + } + } + + 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: + item->size += p->fts_statp->st_blocks * 512; + item->modified = std::max(item->modified, p->fts_statp->st_mtime); + } + } + } + } + } - // Ignore the actual top-level cache directories - if (p->fts_level == 0) 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: - // TODO: optimize path memory footprint - items.push_back(std::shared_ptr<CacheItem>(new CacheItem(nullptr, p))); - - // 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; + 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); @@ -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(); } diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index aa106662e0..a84b051b3e 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -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 { @@ -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() diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index a32df22265..b5b080d263 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -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", + ], +} diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp new file mode 100644 index 0000000000..50a47d2619 --- /dev/null +++ b/cmds/installd/tests/installd_cache_test.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <sys/statvfs.h> +#include <sys/xattr.h> + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <cutils/properties.h> +#include <gtest/gtest.h> + +#include "InstalldNativeService.h" +#include "globals.h" +#include "utils.h" + +using android::base::StringPrintf; + +namespace android { +namespace installd { + +constexpr const char* kTestUuid = "TEST"; + +constexpr int64_t kKbInBytes = 1024; +constexpr int64_t kMbInBytes = 1024 * kKbInBytes; +constexpr int64_t kGbInBytes = 1024 * kMbInBytes; +constexpr int64_t kTbInBytes = 1024 * kGbInBytes; + +static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13; +static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14; + +int get_property(const char *key, char *value, const char *default_value) { + return property_get(key, value, default_value); +} + +bool calculate_oat_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED, + const char *oat_dir ATTRIBUTE_UNUSED, + const char *apk_path ATTRIBUTE_UNUSED, + const char *instruction_set ATTRIBUTE_UNUSED) { + return false; +} + +bool calculate_odex_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED, + const char *apk_path ATTRIBUTE_UNUSED, + const char *instruction_set ATTRIBUTE_UNUSED) { + return false; +} + +bool create_cache_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED, + const char *src ATTRIBUTE_UNUSED, + const char *instruction_set ATTRIBUTE_UNUSED) { + return false; +} + +static void mkdir(const char* path) { + const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); + ::mkdir(fullPath, 0755); +} + +static void touch(const char* path, int len, int time) { + const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); + int fd = ::open(fullPath, O_RDWR | O_CREAT, 0644); + ::fallocate(fd, 0, 0, len); + ::close(fd); + struct utimbuf times; + times.actime = times.modtime = std::time(0) + time; + ::utime(fullPath, ×); +} + +static int exists(const char* path) { + const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); + return ::access(fullPath, F_OK); +} + +static int64_t size(const char* path) { + const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); + struct stat buf; + if (!stat(fullPath, &buf)) { + return buf.st_size; + } else { + return -1; + } +} + +static int64_t free() { + struct statvfs buf; + if (!statvfs("/data/local/tmp", &buf)) { + return buf.f_bavail * buf.f_bsize; + } else { + return -1; + } +} + +static void setxattr(const char* path, const char* key) { + const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); + ::setxattr(fullPath, key, "", 0, 0); +} + +class CacheTest : public testing::Test { +protected: + InstalldNativeService* service; + std::unique_ptr<std::string> testUuid; + + virtual void SetUp() { + setenv("ANDROID_LOG_TAGS", "*:v", 1); + android::base::InitLogging(nullptr); + + service = new InstalldNativeService(); + testUuid = std::make_unique<std::string>(); + *testUuid = std::string(kTestUuid); + system("mkdir -p /data/local/tmp/user/0"); + } + + virtual void TearDown() { + delete service; + system("rm -rf /data/local/tmp/user"); + } +}; + +TEST_F(CacheTest, FreeCache_All) { + mkdir("com.example"); + touch("com.example/normal", 1 * kMbInBytes, 60); + mkdir("com.example/cache"); + mkdir("com.example/cache/foo"); + touch("com.example/cache/foo/one", 1 * kMbInBytes, 60); + touch("com.example/cache/foo/two", 2 * kMbInBytes, 120); + + EXPECT_EQ(0, exists("com.example/normal")); + EXPECT_EQ(0, exists("com.example/cache/foo/one")); + EXPECT_EQ(0, exists("com.example/cache/foo/two")); + + service->freeCache(testUuid, kTbInBytes, + FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); + + EXPECT_EQ(0, exists("com.example/normal")); + EXPECT_EQ(-1, exists("com.example/cache/foo/one")); + EXPECT_EQ(-1, exists("com.example/cache/foo/two")); +} + +TEST_F(CacheTest, FreeCache_Age) { + mkdir("com.example"); + mkdir("com.example/cache"); + mkdir("com.example/cache/foo"); + touch("com.example/cache/foo/one", kMbInBytes, 60); + touch("com.example/cache/foo/two", kMbInBytes, 120); + + service->freeCache(testUuid, free() + kKbInBytes, + FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); + + EXPECT_EQ(-1, exists("com.example/cache/foo/one")); + EXPECT_EQ(0, exists("com.example/cache/foo/two")); + + service->freeCache(testUuid, free() + kKbInBytes, + FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); + + EXPECT_EQ(-1, exists("com.example/cache/foo/one")); + EXPECT_EQ(-1, exists("com.example/cache/foo/two")); +} + +TEST_F(CacheTest, FreeCache_Tombstone) { + mkdir("com.example"); + mkdir("com.example/cache"); + mkdir("com.example/cache/foo"); + touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60); + touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 60); + mkdir("com.example/cache/bar"); + touch("com.example/cache/bar/bar1", 2 * kMbInBytes, 120); + touch("com.example/cache/bar/bar2", 2 * kMbInBytes, 120); + + setxattr("com.example/cache/bar", "user.cache_tombstone"); + + EXPECT_EQ(0, exists("com.example/cache/foo/foo1")); + EXPECT_EQ(0, exists("com.example/cache/foo/foo2")); + EXPECT_EQ(0, exists("com.example/cache/bar/bar1")); + EXPECT_EQ(0, exists("com.example/cache/bar/bar2")); + EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1")); + EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2")); + + service->freeCache(testUuid, kTbInBytes, + FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); + + EXPECT_EQ(-1, exists("com.example/cache/foo/foo1")); + EXPECT_EQ(-1, exists("com.example/cache/foo/foo2")); + EXPECT_EQ(0, exists("com.example/cache/bar/bar1")); + EXPECT_EQ(0, exists("com.example/cache/bar/bar2")); + EXPECT_EQ(0, size("com.example/cache/bar/bar1")); + EXPECT_EQ(0, size("com.example/cache/bar/bar2")); +} + +TEST_F(CacheTest, FreeCache_Atomic) { + mkdir("com.example"); + mkdir("com.example/cache"); + mkdir("com.example/cache/foo"); + touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60); + touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 120); + + setxattr("com.example/cache/foo", "user.cache_atomic"); + + service->freeCache(testUuid, free() + kKbInBytes, + FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); + + EXPECT_EQ(-1, exists("com.example/cache/foo/foo1")); + EXPECT_EQ(-1, exists("com.example/cache/foo/foo2")); +} + +TEST_F(CacheTest, FreeCache_AtomicTombstone) { + LOG(INFO) << "FreeCache_AtomicTombstone"; + + mkdir("com.example"); + mkdir("com.example/cache"); + + // this dir must look really old for some reason? + mkdir("com.example/cache/atomic"); + touch("com.example/cache/atomic/file1", kMbInBytes, 120); + touch("com.example/cache/atomic/file2", kMbInBytes, 120); + mkdir("com.example/cache/atomic/dir"); + touch("com.example/cache/atomic/dir/file1", kMbInBytes, 120); + touch("com.example/cache/atomic/dir/file2", kMbInBytes, 120); + mkdir("com.example/cache/atomic/tomb"); + touch("com.example/cache/atomic/tomb/file1", kMbInBytes, 120); + touch("com.example/cache/atomic/tomb/file2", kMbInBytes, 120); + mkdir("com.example/cache/atomic/tomb/dir"); + touch("com.example/cache/atomic/tomb/dir/file1", kMbInBytes, 120); + touch("com.example/cache/atomic/tomb/dir/file2", kMbInBytes, 120); + + mkdir("com.example/cache/tomb"); + touch("com.example/cache/tomb/file1", kMbInBytes, 240); + touch("com.example/cache/tomb/file2", kMbInBytes, 240); + mkdir("com.example/cache/tomb/dir"); + touch("com.example/cache/tomb/dir/file1", kMbInBytes, 240); + touch("com.example/cache/tomb/dir/file2", kMbInBytes, 240); + mkdir("com.example/cache/tomb/atomic"); + touch("com.example/cache/tomb/atomic/file1", kMbInBytes, 60); + touch("com.example/cache/tomb/atomic/file2", kMbInBytes, 60); + mkdir("com.example/cache/tomb/atomic/dir"); + touch("com.example/cache/tomb/atomic/dir/file1", kMbInBytes, 60); + touch("com.example/cache/tomb/atomic/dir/file2", kMbInBytes, 60); + + setxattr("com.example/cache/atomic", "user.cache_atomic"); + setxattr("com.example/cache/atomic/tomb", "user.cache_tombstone"); + setxattr("com.example/cache/tomb", "user.cache_tombstone"); + setxattr("com.example/cache/tomb/atomic", "user.cache_atomic"); + + service->freeCache(testUuid, free() + kKbInBytes, + FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); + + EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/file1")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/file2")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/dir/file1")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/dir/file2")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/tomb/file1")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/tomb/file2")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/tomb/dir/file1")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/tomb/dir/file2")); + + EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file1")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file2")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file1")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file2")); + + service->freeCache(testUuid, free() + kKbInBytes, + FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); + + EXPECT_EQ(-1, size("com.example/cache/atomic/file1")); + EXPECT_EQ(-1, size("com.example/cache/atomic/file2")); + EXPECT_EQ(-1, size("com.example/cache/atomic/dir/file1")); + EXPECT_EQ(-1, size("com.example/cache/atomic/dir/file2")); + EXPECT_EQ(0, size("com.example/cache/atomic/tomb/file1")); + EXPECT_EQ(0, size("com.example/cache/atomic/tomb/file2")); + EXPECT_EQ(0, size("com.example/cache/atomic/tomb/dir/file1")); + EXPECT_EQ(0, size("com.example/cache/atomic/tomb/dir/file2")); + + EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1")); + EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file1")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file2")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file1")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file2")); + + service->freeCache(testUuid, kTbInBytes, + FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); + + EXPECT_EQ(-1, size("com.example/cache/atomic/file1")); + EXPECT_EQ(-1, size("com.example/cache/atomic/file2")); + EXPECT_EQ(-1, size("com.example/cache/atomic/dir/file1")); + EXPECT_EQ(-1, size("com.example/cache/atomic/dir/file2")); + EXPECT_EQ(0, size("com.example/cache/atomic/tomb/file1")); + EXPECT_EQ(0, size("com.example/cache/atomic/tomb/file2")); + EXPECT_EQ(0, size("com.example/cache/atomic/tomb/dir/file1")); + EXPECT_EQ(0, size("com.example/cache/atomic/tomb/dir/file2")); + + EXPECT_EQ(0, size("com.example/cache/tomb/file1")); + EXPECT_EQ(0, size("com.example/cache/tomb/file2")); + EXPECT_EQ(0, size("com.example/cache/tomb/dir/file1")); + EXPECT_EQ(0, size("com.example/cache/tomb/dir/file2")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file1")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file2")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file1")); + EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file2")); +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index 947cc0db3f..4b685746b1 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -29,6 +29,7 @@ #define TEST_DATA_DIR "/data/" #define TEST_APP_DIR "/data/app/" #define TEST_APP_PRIVATE_DIR "/data/app-private/" +#define TEST_APP_EPHEMERAL_DIR "/data/app-ephemeral/" #define TEST_ASEC_DIR "/mnt/asec/" #define TEST_EXPAND_DIR "/mnt/expand/" @@ -57,6 +58,9 @@ protected: android_app_private_dir.path = (char*) TEST_APP_PRIVATE_DIR; android_app_private_dir.len = strlen(TEST_APP_PRIVATE_DIR); + android_app_ephemeral_dir.path = (char*) TEST_APP_EPHEMERAL_DIR; + android_app_ephemeral_dir.len = strlen(TEST_APP_EPHEMERAL_DIR); + android_data_dir.path = (char*) TEST_DATA_DIR; android_data_dir.len = strlen(TEST_DATA_DIR); @@ -85,19 +89,19 @@ TEST_F(UtilsTest, IsValidApkPath_BadPrefix) { // Bad prefixes directories const char *badprefix1 = "/etc/passwd"; EXPECT_EQ(-1, validate_apk_path(badprefix1)) - << badprefix1 << " should be allowed as a valid path"; + << badprefix1 << " should not be allowed as a valid path"; const char *badprefix2 = "../.." TEST_APP_DIR "../../../blah"; EXPECT_EQ(-1, validate_apk_path(badprefix2)) - << badprefix2 << " should be allowed as a valid path"; + << badprefix2 << " should not be allowed as a valid path"; const char *badprefix3 = "init.rc"; EXPECT_EQ(-1, validate_apk_path(badprefix3)) - << badprefix3 << " should be allowed as a valid path"; + << badprefix3 << " should not be allowed as a valid path"; const char *badprefix4 = "/init.rc"; EXPECT_EQ(-1, validate_apk_path(badprefix4)) - << badprefix4 << " should be allowed as a valid path"; + << badprefix4 << " should not be allowed as a valid path"; } TEST_F(UtilsTest, IsValidApkPath_Internal) { diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 37215eae9f..6d50f558a2 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -33,6 +33,7 @@ #include <android-base/logging.h> #include <android-base/stringprintf.h> #include <cutils/fs.h> +#include <cutils/properties.h> #include <log/log.h> #include <private/android_filesystem_config.h> @@ -155,6 +156,9 @@ int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname, std::string create_data_path(const char* volume_uuid) { if (volume_uuid == nullptr) { return "/data"; + } else if (!strcmp(volume_uuid, "TEST")) { + CHECK(property_get_bool("ro.debuggable", false)); + return "/data/local/tmp"; } else { CHECK(is_valid_filename(volume_uuid)); return StringPrintf("/mnt/expand/%s", volume_uuid); diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 5226d1c8cb..aa83dc2fa6 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -66,6 +66,8 @@ typedef struct { constexpr const char* kXattrInodeCache = "user.inode_cache"; constexpr const char* kXattrInodeCodeCache = "user.inode_code_cache"; +constexpr const char* kXattrCacheAtomic = "user.cache_atomic"; +constexpr const char* kXattrCacheTombstone = "user.cache_tombstone"; int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname, |