cryptohome: add android cache directory deletion.
Design: go/arc-disk-full-design
BUG=b:29377354
TEST=P2_TEST_FILTER="FreeDiskSpaceTest.*" \
cros_workon_make --test cryptohome --board ${BOARD}
TEST=create a 500MB cache file for first user
log in as second user and fill up disk with
dd if=/dev/zero \
of=data/data/org.chromium.arc.applauncher/cache/f \
bs=1000000 count=5000
and observe only the cache is deleted and profile remains.
Change-Id: Icc85dc3d01e8d45099865dd667572109e16eccd6
Reviewed-on: https://chromium-review.googlesource.com/355330
Commit-Ready: Gwendal Grignou <gwendal@google.com>
Tested-by: Junichi Uekawa <uekawa@chromium.org>
Tested-by: Gwendal Grignou <gwendal@google.com>
Reviewed-by: Gwendal Grignou <gwendal@google.com>
(cherry picked from commit 147423c632a884081e9155b6024e1f1341b36624)
Reviewed-on: https://chromium-review.googlesource.com/360783
Trybot-Ready: Junichi Uekawa <uekawa@chromium.org>
Reviewed-by: Darren Krahn <dkrahn@chromium.org>
Reviewed-by: Dan Spaid <dspaid@chromium.org>
Commit-Queue: Junichi Uekawa <uekawa@chromium.org>
diff --git a/cryptohome/homedirs.cc b/cryptohome/homedirs.cc
index 5df7658..8173d01 100644
--- a/cryptohome/homedirs.cc
+++ b/cryptohome/homedirs.cc
@@ -36,6 +36,7 @@
const char *kShadowRoot = "/home/.shadow";
const char *kEmptyOwner = "";
const char kGCacheFilesAttribute[] = "user.GCacheFiles";
+const char kAndroidCacheFilesAttribute[] = "user.AndroidCache";
HomeDirs::HomeDirs()
: default_platform_(new Platform()),
@@ -98,6 +99,14 @@
if (freeDiskSpace >= kEnoughFreeSpace)
return true;
+ // Clean Cache directories for every user (except current one).
+ DoForEveryUnmountedCryptohome(base::Bind(
+ &HomeDirs::DeleteAndroidCacheCallback,
+ base::Unretained(this)));
+
+ if (platform_->AmountOfFreeDiskSpace(shadow_root_) >= kEnoughFreeSpace)
+ return true;
+
// Initialize user timestamp cache if it has not been yet. This reads the
// last-activity time from each homedir's SerializedVaultKeyset. This value
// is only updated in the value keyset on unmount and every 24 hrs, so a
@@ -861,6 +870,31 @@
}
}
+void HomeDirs::DeleteAndroidCacheCallback(const FilePath& vault) {
+ const FilePath root = vault.Append(kRootHomeSuffix);
+ // Find the cache directory by walking under vault/root directory
+ // and looking for AndroidCache xattr set. Data is stored under
+ // root/android-data/data/data/[package name]/cache. It is not
+ // desirable to make all package name directories unencrypted, they
+ // are not marked as tracked directory.
+ // TODO(crbug/625872): Mark root/android/data/data/ as pass through.
+ // TODO(uekawa): Not all boards have android running, we probably
+ // don't need to check for board that do not have an android
+ // configuration.
+ std::unique_ptr<cryptohome::FileEnumerator> file_enumerator(
+ platform_->GetFileEnumerator(root.value(), true,
+ base::FileEnumerator::DIRECTORIES));
+ std::string next_path;
+ while (!(next_path = file_enumerator->Next()).empty()) {
+ std::string value;
+ if (platform_->HasExtendedFileAttribute(
+ next_path, kAndroidCacheFilesAttribute)) {
+ LOG(WARNING) << "Deleting Android Cache " << next_path;
+ platform_->DeleteFile(next_path, true);
+ }
+ }
+}
+
void HomeDirs::AddUserTimestampToCacheCallback(const FilePath& vault) {
const FilePath user_dir = vault.DirName();
const std::string obfuscated_username = user_dir.BaseName().value();
diff --git a/cryptohome/homedirs.h b/cryptohome/homedirs.h
index a33ccbf..9ade585 100644
--- a/cryptohome/homedirs.h
+++ b/cryptohome/homedirs.h
@@ -34,6 +34,7 @@
const int64_t kEnoughFreeSpace = 1LL << 30;
extern const char kGCacheFilesAttribute[];
+extern const char kAndroidCacheFilesAttribute[];
class Credentials;
class Platform;
@@ -224,6 +225,8 @@
bool FindGCacheFilesDir(const base::FilePath& vault, std::string* dir);
// Callback used during FreeDiskSpace().
void DeleteGCacheTmpCallback(const base::FilePath& vault);
+ // Callback used during FreeDiskSpace().
+ void DeleteAndroidCacheCallback(const base::FilePath& vault);
// Recursively deletes all contents of a directory while leaving the directory
// itself intact.
void DeleteDirectoryContents(const base::FilePath& dir);
diff --git a/cryptohome/homedirs_unittest.cc b/cryptohome/homedirs_unittest.cc
index d2741e9..cfff68e 100644
--- a/cryptohome/homedirs_unittest.cc
+++ b/cryptohome/homedirs_unittest.cc
@@ -268,7 +268,7 @@
DoAll(SetArgPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
- .Times(3).WillRepeatedly(Return(0))
+ .Times(4).WillRepeatedly(Return(0))
.RetiresOnSaturation();
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
@@ -276,18 +276,19 @@
EXPECT_CALL(platform_, GetFileEnumerator(_, false, _))
.Times(user_count * 2)
.WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
- // N users * 1 GCache files dir
+ // N users * (1 GCache files dir + 1 Android cache dir)
EXPECT_CALL(platform_, GetFileEnumerator(_, true, _))
- .Times(user_count)
+ .Times(user_count * 2)
.WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
}
};
TEST_F(FreeDiskSpaceTest, InitializeTimeCacheWithNoTime) {
- // To get to the init logic, we need to fail three kEnoughFreeSpace checks.
+ // To get to the init logic, we need to fail four kEnoughFreeSpace checks.
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(0))
.WillOnce(Return(0))
+ .WillOnce(Return(0))
.WillOnce(Return(kEnoughFreeSpace - 1));
// Expect cache clean ups.
@@ -315,6 +316,11 @@
GetFileEnumerator(EndsWith("user/GCache/v1"), true, _))
.Times(4)
.WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
+ EXPECT_CALL(platform_,
+ GetFileEnumerator(EndsWith(
+ std::string(kVaultDir) + "/root"), true, _))
+ .Times(4)
+ .WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
// Now cover the actual initialization piece
EXPECT_CALL(timestamp_cache_, initialized())
@@ -346,10 +352,11 @@
}
TEST_F(FreeDiskSpaceTest, InitializeTimeCacheWithOneTime) {
- // To get to the init logic, we need to fail three kEnoughFreeSpace checks.
+ // To get to the init logic, we need to fail four kEnoughFreeSpace checks.
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(0))
.WillOnce(Return(0))
+ .WillOnce(Return(0))
.WillOnce(Return(kEnoughFreeSpace - 1));
// Expect cache clean ups.
@@ -375,6 +382,11 @@
true, _))
.Times(4)
.WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
+ EXPECT_CALL(platform_, GetFileEnumerator(EndsWith(std::string(kVaultDir) +
+ "/root"),
+ true, _))
+ .Times(4)
+ .WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
// Now cover the actual initialization piece
EXPECT_CALL(timestamp_cache_, initialized())
@@ -560,6 +572,77 @@
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
+TEST_F(FreeDiskSpaceTest, CacheAndGCacheAndAndroidCleanup) {
+ EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<2>(homedir_paths_),
+ Return(true)));
+ EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
+ .WillOnce(Return(0))
+ .WillOnce(Return(0))
+ .WillOnce(Return(0))
+ .WillOnce(Return(kEnoughFreeSpace + 1));
+ EXPECT_CALL(platform_, DirectoryExists(_))
+ .WillRepeatedly(Return(true));
+
+ // Skip per-cache and GCache enumerations done per user in order to
+ // test Android cache deletions.
+ EXPECT_CALL(platform_, GetFileEnumerator(EndsWith("/Cache"), false, _))
+ .Times(4)
+ .WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
+
+ // DeleteGCacheTmpCallback enumerate all directories to find GCache files
+ // directory.
+ EXPECT_CALL(
+ platform_,
+ GetFileEnumerator(EndsWith(std::string(kVaultDir) + "/user/GCache/v1"),
+ true, base::FileEnumerator::DIRECTORIES))
+ .Times(4)
+ .WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
+ EXPECT_CALL(platform_,
+ GetFileEnumerator(EndsWith("/GCache/v1/tmp"), false, _))
+ .Times(4)
+ .WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
+
+ // Now test for the Android user, just test for the first user.
+ NiceMock<MockFileEnumerator>* fe = new NiceMock<MockFileEnumerator>;
+ EXPECT_CALL(platform_, GetFileEnumerator(EndsWith(
+ std::string(kVaultDir) + "/root"), true, _))
+ .WillOnce(Return(fe))
+ .WillOnce(InvokeWithoutArgs(CreateMockFileEnumerator))
+ .WillOnce(InvokeWithoutArgs(CreateMockFileEnumerator))
+ .WillOnce(InvokeWithoutArgs(CreateMockFileEnumerator));
+
+ // Return a cache and non-cache directory.
+ EXPECT_CALL(*fe, Next())
+ .WillOnce(Return(StringPrintf(
+ "%s/%s", homedir_paths_[0].c_str(),
+ "android-data/data/data/com.google.hogehoge/cache")))
+ .WillOnce(Return(StringPrintf(
+ "%s/%s", homedir_paths_[0].c_str(),
+ "android-data/data/data/com.google.hogehoge/data")))
+ .WillRepeatedly(Return(""));
+
+ EXPECT_CALL(platform_, HasExtendedFileAttribute(
+ EndsWith("android-data/data/data/com.google.hogehoge/cache"),
+ kAndroidCacheFilesAttribute))
+ .WillOnce(Return(true));
+ EXPECT_CALL(platform_, HasExtendedFileAttribute(
+ EndsWith("android-data/data/data/com.google.hogehoge/data"),
+ kAndroidCacheFilesAttribute))
+ .WillOnce(Return(false));
+
+ // Confirm android cache dir is removed and data directory is not.
+ EXPECT_CALL(platform_, DeleteFile(
+ EndsWith("android-data/data/data/com.google.hogehoge/cache"), _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(platform_, DeleteFile(
+ EndsWith("android-data/data/data/com.google.hogehoge/data"), _))
+ .Times(0);
+
+ EXPECT_TRUE(homedirs_.FreeDiskSpace());
+}
+
TEST_F(FreeDiskSpaceTest, CleanUpOneUser) {
// Ensure that the oldest user directory deleted, but not any
// others, if the first deletion frees enough space.
@@ -865,18 +948,18 @@
// 3 users * (1 Cache dir + 1 GCache tmp dir)
EXPECT_CALL(platform_, GetFileEnumerator(_, false, _))
.Times(6).WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
- // 3 users * 1 GCache files dir
+ // 3 users * (1 GCache files dir + 1 Android cache)
EXPECT_CALL(platform_, GetFileEnumerator(_, true, _))
- .Times(3).WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
+ .Times(6).WillRepeatedly(InvokeWithoutArgs(CreateMockFileEnumerator));
EXPECT_CALL(platform_,
IsDirectoryMountedWith(StartsWith(homedir_paths_[0]), _))
- .Times(4) // Cache, GCache, mounted dir count, user removal
+ .Times(5) // Cache, GCache, android, mounted dir count, user removal
.WillRepeatedly(Return(true));
for (size_t i = 1; i < arraysize(kHomedirs); ++i) {
EXPECT_CALL(platform_,
IsDirectoryMountedWith(StartsWith(homedir_paths_[i]), _))
- .Times(3) // Cache, GCache, mounted dir count
+ .Times(4) // Cache, GCache, android, mounted dir count
.WillRepeatedly(Return(false));
}