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(), &times);
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