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 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/statvfs.h>
20 #include <sys/xattr.h>
21
22 #include <android-base/logging.h>
23 #include <android-base/stringprintf.h>
24 #include <cutils/properties.h>
25 #include <gtest/gtest.h>
26
27 #include "InstalldNativeService.h"
28 #include "globals.h"
29 #include "utils.h"
30
31 using android::base::StringPrintf;
32
33 namespace android {
34 namespace installd {
35
36 constexpr const char* kTestUuid = "TEST";
37
38 constexpr int64_t kKbInBytes = 1024;
39 constexpr int64_t kMbInBytes = 1024 * kKbInBytes;
40 constexpr int64_t kGbInBytes = 1024 * kMbInBytes;
41 constexpr int64_t kTbInBytes = 1024 * kGbInBytes;
42
43 #define FLAG_FREE_CACHE_V2 InstalldNativeService::FLAG_FREE_CACHE_V2
44 #define FLAG_FREE_CACHE_V2_DEFY_QUOTA InstalldNativeService::FLAG_FREE_CACHE_V2_DEFY_QUOTA
45
get_property(const char * key,char * value,const char * default_value)46 int get_property(const char *key, char *value, const char *default_value) {
47 return property_get(key, value, default_value);
48 }
49
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)50 bool calculate_oat_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
51 const char *oat_dir ATTRIBUTE_UNUSED,
52 const char *apk_path ATTRIBUTE_UNUSED,
53 const char *instruction_set ATTRIBUTE_UNUSED) {
54 return false;
55 }
56
calculate_odex_file_path(char path[PKG_PATH_MAX]ATTRIBUTE_UNUSED,const char * apk_path ATTRIBUTE_UNUSED,const char * instruction_set ATTRIBUTE_UNUSED)57 bool calculate_odex_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
58 const char *apk_path ATTRIBUTE_UNUSED,
59 const char *instruction_set ATTRIBUTE_UNUSED) {
60 return false;
61 }
62
create_cache_path(char path[PKG_PATH_MAX]ATTRIBUTE_UNUSED,const char * src ATTRIBUTE_UNUSED,const char * instruction_set ATTRIBUTE_UNUSED)63 bool create_cache_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
64 const char *src ATTRIBUTE_UNUSED,
65 const char *instruction_set ATTRIBUTE_UNUSED) {
66 return false;
67 }
68
mkdir(const char * path)69 static void mkdir(const char* path) {
70 const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
71 ::mkdir(fullPath.c_str(), 0755);
72 }
73
touch(const char * path,int len,int time)74 static void touch(const char* path, int len, int time) {
75 const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
76 int fd = ::open(fullPath.c_str(), O_RDWR | O_CREAT, 0644);
77 ::fallocate(fd, 0, 0, len);
78 ::close(fd);
79 struct utimbuf times;
80 times.actime = times.modtime = std::time(0) + time;
81 ::utime(fullPath.c_str(), ×);
82 }
83
exists(const char * path)84 static int exists(const char* path) {
85 const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
86 return ::access(fullPath.c_str(), F_OK);
87 }
88
size(const char * path)89 static int64_t size(const char* path) {
90 const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
91 struct stat buf;
92 if (!stat(fullPath.c_str(), &buf)) {
93 return buf.st_size;
94 } else {
95 return -1;
96 }
97 }
98
free()99 static int64_t free() {
100 struct statvfs buf;
101 if (!statvfs("/data/local/tmp", &buf)) {
102 return static_cast<int64_t>(buf.f_bavail) * buf.f_frsize;
103 } else {
104 PLOG(ERROR) << "Failed to statvfs";
105 return -1;
106 }
107 }
108
setxattr(const char * path,const char * key)109 static void setxattr(const char* path, const char* key) {
110 const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
111 ::setxattr(fullPath.c_str(), key, "", 0, 0);
112 }
113
114 class CacheTest : public testing::Test {
115 protected:
116 InstalldNativeService* service;
117 std::unique_ptr<std::string> testUuid;
118
SetUp()119 virtual void SetUp() {
120 setenv("ANDROID_LOG_TAGS", "*:v", 1);
121 android::base::InitLogging(nullptr);
122
123 service = new InstalldNativeService();
124 testUuid = std::make_unique<std::string>();
125 *testUuid = std::string(kTestUuid);
126 system("mkdir -p /data/local/tmp/user/0");
127 }
128
TearDown()129 virtual void TearDown() {
130 delete service;
131 system("rm -rf /data/local/tmp/user");
132 }
133 };
134
TEST_F(CacheTest,FreeCache_All)135 TEST_F(CacheTest, FreeCache_All) {
136 LOG(INFO) << "FreeCache_All";
137
138 mkdir("com.example");
139 touch("com.example/normal", 1 * kMbInBytes, 60);
140 mkdir("com.example/cache");
141 mkdir("com.example/cache/foo");
142 touch("com.example/cache/foo/one", 1 * kMbInBytes, 60);
143 touch("com.example/cache/foo/two", 2 * kMbInBytes, 120);
144
145 EXPECT_EQ(0, exists("com.example/normal"));
146 EXPECT_EQ(0, exists("com.example/cache/foo/one"));
147 EXPECT_EQ(0, exists("com.example/cache/foo/two"));
148
149 service->freeCache(testUuid, kTbInBytes, 0,
150 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
151
152 EXPECT_EQ(0, exists("com.example/normal"));
153 EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
154 EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
155 }
156
TEST_F(CacheTest,FreeCache_Age)157 TEST_F(CacheTest, FreeCache_Age) {
158 LOG(INFO) << "FreeCache_Age";
159
160 mkdir("com.example");
161 mkdir("com.example/cache");
162 mkdir("com.example/cache/foo");
163 touch("com.example/cache/foo/one", kMbInBytes, 60);
164 touch("com.example/cache/foo/two", kMbInBytes, 120);
165
166 service->freeCache(testUuid, free() + kKbInBytes, 0,
167 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
168
169 EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
170 EXPECT_EQ(0, exists("com.example/cache/foo/two"));
171
172 service->freeCache(testUuid, free() + kKbInBytes, 0,
173 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
174
175 EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
176 EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
177 }
178
TEST_F(CacheTest,FreeCache_Tombstone)179 TEST_F(CacheTest, FreeCache_Tombstone) {
180 LOG(INFO) << "FreeCache_Tombstone";
181
182 mkdir("com.example");
183 mkdir("com.example/cache");
184 mkdir("com.example/cache/foo");
185 touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60);
186 touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 60);
187 mkdir("com.example/cache/bar");
188 touch("com.example/cache/bar/bar1", 2 * kMbInBytes, 120);
189 touch("com.example/cache/bar/bar2", 2 * kMbInBytes, 120);
190
191 setxattr("com.example/cache/bar", "user.cache_tombstone");
192
193 EXPECT_EQ(0, exists("com.example/cache/foo/foo1"));
194 EXPECT_EQ(0, exists("com.example/cache/foo/foo2"));
195 EXPECT_EQ(0, exists("com.example/cache/bar/bar1"));
196 EXPECT_EQ(0, exists("com.example/cache/bar/bar2"));
197 EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1"));
198 EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2"));
199
200 service->freeCache(testUuid, kTbInBytes, 0,
201 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
202
203 EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
204 EXPECT_EQ(-1, exists("com.example/cache/foo/foo2"));
205 EXPECT_EQ(0, exists("com.example/cache/bar/bar1"));
206 EXPECT_EQ(0, exists("com.example/cache/bar/bar2"));
207 EXPECT_EQ(0, size("com.example/cache/bar/bar1"));
208 EXPECT_EQ(0, size("com.example/cache/bar/bar2"));
209 }
210
TEST_F(CacheTest,FreeCache_Group)211 TEST_F(CacheTest, FreeCache_Group) {
212 LOG(INFO) << "FreeCache_Group";
213
214 mkdir("com.example");
215 mkdir("com.example/cache");
216 mkdir("com.example/cache/foo");
217 touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60);
218 touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 120);
219
220 setxattr("com.example/cache/foo", "user.cache_group");
221
222 service->freeCache(testUuid, free() + kKbInBytes, 0,
223 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
224
225 EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
226 EXPECT_EQ(-1, exists("com.example/cache/foo/foo2"));
227 }
228
TEST_F(CacheTest,FreeCache_GroupTombstone)229 TEST_F(CacheTest, FreeCache_GroupTombstone) {
230 LOG(INFO) << "FreeCache_GroupTombstone";
231
232 mkdir("com.example");
233 mkdir("com.example/cache");
234
235 // this dir must look really old for some reason?
236 mkdir("com.example/cache/group");
237 touch("com.example/cache/group/file1", kMbInBytes, 120);
238 touch("com.example/cache/group/file2", kMbInBytes, 120);
239 mkdir("com.example/cache/group/dir");
240 touch("com.example/cache/group/dir/file1", kMbInBytes, 120);
241 touch("com.example/cache/group/dir/file2", kMbInBytes, 120);
242 mkdir("com.example/cache/group/tomb");
243 touch("com.example/cache/group/tomb/file1", kMbInBytes, 120);
244 touch("com.example/cache/group/tomb/file2", kMbInBytes, 120);
245 mkdir("com.example/cache/group/tomb/dir");
246 touch("com.example/cache/group/tomb/dir/file1", kMbInBytes, 120);
247 touch("com.example/cache/group/tomb/dir/file2", kMbInBytes, 120);
248
249 mkdir("com.example/cache/tomb");
250 touch("com.example/cache/tomb/file1", kMbInBytes, 240);
251 touch("com.example/cache/tomb/file2", kMbInBytes, 240);
252 mkdir("com.example/cache/tomb/dir");
253 touch("com.example/cache/tomb/dir/file1", kMbInBytes, 240);
254 touch("com.example/cache/tomb/dir/file2", kMbInBytes, 240);
255 mkdir("com.example/cache/tomb/group");
256 touch("com.example/cache/tomb/group/file1", kMbInBytes, 60);
257 touch("com.example/cache/tomb/group/file2", kMbInBytes, 60);
258 mkdir("com.example/cache/tomb/group/dir");
259 touch("com.example/cache/tomb/group/dir/file1", kMbInBytes, 60);
260 touch("com.example/cache/tomb/group/dir/file2", kMbInBytes, 60);
261
262 setxattr("com.example/cache/group", "user.cache_group");
263 setxattr("com.example/cache/group/tomb", "user.cache_tombstone");
264 setxattr("com.example/cache/tomb", "user.cache_tombstone");
265 setxattr("com.example/cache/tomb/group", "user.cache_group");
266
267 service->freeCache(testUuid, free() + kKbInBytes, 0,
268 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
269
270 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file1"));
271 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file2"));
272 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/dir/file1"));
273 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/dir/file2"));
274 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/file1"));
275 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/file2"));
276 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/dir/file1"));
277 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/dir/file2"));
278
279 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1"));
280 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2"));
281 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1"));
282 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2"));
283 EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
284 EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
285 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
286 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
287
288 service->freeCache(testUuid, free() + kKbInBytes, 0,
289 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
290
291 EXPECT_EQ(-1, size("com.example/cache/group/file1"));
292 EXPECT_EQ(-1, size("com.example/cache/group/file2"));
293 EXPECT_EQ(-1, size("com.example/cache/group/dir/file1"));
294 EXPECT_EQ(-1, size("com.example/cache/group/dir/file2"));
295 EXPECT_EQ(0, size("com.example/cache/group/tomb/file1"));
296 EXPECT_EQ(0, size("com.example/cache/group/tomb/file2"));
297 EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file1"));
298 EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file2"));
299
300 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1"));
301 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2"));
302 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1"));
303 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2"));
304 EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
305 EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
306 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
307 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
308
309 service->freeCache(testUuid, kTbInBytes, 0,
310 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
311
312 EXPECT_EQ(-1, size("com.example/cache/group/file1"));
313 EXPECT_EQ(-1, size("com.example/cache/group/file2"));
314 EXPECT_EQ(-1, size("com.example/cache/group/dir/file1"));
315 EXPECT_EQ(-1, size("com.example/cache/group/dir/file2"));
316 EXPECT_EQ(0, size("com.example/cache/group/tomb/file1"));
317 EXPECT_EQ(0, size("com.example/cache/group/tomb/file2"));
318 EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file1"));
319 EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file2"));
320
321 EXPECT_EQ(0, size("com.example/cache/tomb/file1"));
322 EXPECT_EQ(0, size("com.example/cache/tomb/file2"));
323 EXPECT_EQ(0, size("com.example/cache/tomb/dir/file1"));
324 EXPECT_EQ(0, size("com.example/cache/tomb/dir/file2"));
325 EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
326 EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
327 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
328 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
329 }
330
331 } // namespace installd
332 } // namespace android
333