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