1 /*
2  * Copyright (C) 2020 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 <android-base/file.h>
18 #include <sys/select.h>
19 
20 #include <setjmp.h>
21 #include <unistd.h>
22 
23 #include "IncFsTestBase.h"
24 
25 #include "util/map_ptr.h"
26 
27 using namespace android::incfs;
28 using namespace std::literals;
29 
30 constexpr int FILE_PAGES = 5U;
31 constexpr int FILE_SIZE = INCFS_DATA_FILE_BLOCK_SIZE * FILE_PAGES;
32 constexpr int FILE_MISSING_PAGE = 3U;
33 
34 class MapPtrTest : public IncFsTestBase {
35 protected:
SetUp()36     virtual void SetUp() override {
37         IncFsTestBase::SetUp();
38 
39         const auto id = fileId(1);
40         ASSERT_TRUE(control_.logs() >= 0);
41         ASSERT_EQ(0, makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = FILE_SIZE}));
42         auto fd = openForSpecialOps(control_, fileId(1));
43         ASSERT_GE(fd.get(), 0);
44 
45         // Generate the file data.
46         std::vector<uint32_t> data(FILE_SIZE);
47         for (int i = 0; i < FILE_SIZE; i++) {
48             data[i] = i;
49         }
50 
51         // Write the file, but leave one page missing.
52         for (int p = 0; p < FILE_PAGES; p++) {
53             if (p == FILE_MISSING_PAGE) {
54                 continue;
55             }
56             auto block = DataBlock{
57                     .fileFd = fd.get(),
58                     .pageIndex = p,
59                     .compression = INCFS_COMPRESSION_KIND_NONE,
60                     .dataSize = (uint32_t)INCFS_DATA_FILE_BLOCK_SIZE,
61                     .data = reinterpret_cast<const char *>(data.data()) +
62                             INCFS_DATA_FILE_BLOCK_SIZE * p,
63             };
64             ASSERT_EQ(1, writeBlocks({&block, 1}));
65         }
66 
67         mount_path_ = mountPath(test_file_name_);
68     }
69 
GetFd()70     android::base::unique_fd GetFd() {
71         return android::base::unique_fd(open(mount_path_.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
72     }
73 
getReadTimeout()74     int32_t getReadTimeout() override { return 1; }
75 
GetFileMap(int fd,off64_t offset,size_t length)76     std::unique_ptr<IncFsFileMap> GetFileMap(int fd, off64_t offset, size_t length) {
77         auto map = std::make_unique<IncFsFileMap>();
78         return map->Create(fd, offset, length, nullptr) ? std::move(map) : nullptr;
79     }
80 
81 private:
82     std::string mount_path_;
83 };
84 
85 struct TwoValues {
86     uint32_t first;
87     uint32_t second;
88 };
89 
TEST_F(MapPtrTest,ReadAtStart)90 TEST_F(MapPtrTest, ReadAtStart) {
91     auto fd = GetFd();
92     auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
93     ASSERT_NE(nullptr, map);
94 
95     auto p1 = map->data<uint32_t>();
96     ASSERT_TRUE(p1);
97     ASSERT_EQ(0U, p1.value());
98 
99     auto p2 = map->data<TwoValues>();
100     ASSERT_TRUE(p2);
101     ASSERT_EQ(0U, p2->first);
102     ASSERT_EQ(1U, p2->second);
103 }
104 
TEST_F(MapPtrTest,ReadNull)105 TEST_F(MapPtrTest, ReadNull) {
106     auto fd = GetFd();
107     auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
108     ASSERT_NE(nullptr, map);
109 
110     auto p1 = map->data<uint32_t>();
111     ASSERT_TRUE(p1);
112     ASSERT_EQ(0U, p1.value());
113 
114     p1 = nullptr;
115     ASSERT_FALSE(p1);
116 
117     p1 = map->data<uint32_t>();
118     ASSERT_TRUE(p1);
119     ASSERT_EQ(0U, p1.value());
120 }
121 
TEST_F(MapPtrTest,ReadAtStartWithOffset)122 TEST_F(MapPtrTest, ReadAtStartWithOffset) {
123     auto fd = GetFd();
124     auto map = GetFileMap(fd.get(), sizeof(uint32_t) * 4U /* offset */, FILE_SIZE);
125     ASSERT_NE(nullptr, map);
126 
127     auto p1 = map->data<uint32_t>();
128     ASSERT_TRUE(p1);
129     ASSERT_EQ(4U, p1.value());
130 
131     auto p2 = map->data<TwoValues>();
132     ASSERT_TRUE(p2);
133     ASSERT_EQ(4U, p2->first);
134     ASSERT_EQ(5U, p2->second);
135 }
136 
TEST_F(MapPtrTest,PointerArithmetic)137 TEST_F(MapPtrTest, PointerArithmetic) {
138     auto fd = GetFd();
139     auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
140     ASSERT_NE(nullptr, map);
141 
142     auto p1 = map->data<uint32_t>() + 11U;
143     ASSERT_TRUE(p1);
144     ASSERT_EQ(11U, p1.value());
145 
146     auto p2 = p1 - 5U;
147     ASSERT_TRUE(p2);
148     ASSERT_EQ(6U, p2.value());
149 
150     auto dis = p1 - p2;
151     ASSERT_EQ((ptrdiff_t)5U, dis);
152 }
153 
TEST_F(MapPtrTest,PointerIncrement)154 TEST_F(MapPtrTest, PointerIncrement) {
155     auto fd = GetFd();
156     auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
157     ASSERT_NE(nullptr, map);
158 
159     auto p1 = map->data<uint32_t>();
160     ASSERT_TRUE(p1);
161     ASSERT_EQ(0U, p1.value());
162 
163     auto p2 = p1++;
164     ASSERT_TRUE(p1);
165     ASSERT_TRUE(p2);
166     ASSERT_EQ(1U, p1.value());
167     ASSERT_EQ(0U, p2.value());
168 
169     auto p3 = ++p2;
170     ASSERT_TRUE(p2);
171     ASSERT_TRUE(p3);
172     ASSERT_EQ(1U, p2.value());
173     ASSERT_EQ(1U, p3.value());
174 }
175 
TEST_F(MapPtrTest,PointerComparison)176 TEST_F(MapPtrTest, PointerComparison) {
177     auto fd = GetFd();
178     auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
179     ASSERT_NE(nullptr, map);
180 
181     auto p1 = map->data<uint32_t>();
182     ASSERT_TRUE(p1);
183     ASSERT_EQ(0U, p1.value());
184 
185     auto p2 = p1;
186     ASSERT_TRUE(p1 == p2);
187     ASSERT_TRUE(p1 < p2 + 1U);
188     ASSERT_TRUE(p2 != p2 + 1U);
189 }
190 
TEST_F(MapPtrTest,PointerConvert)191 TEST_F(MapPtrTest, PointerConvert) {
192     auto fd = GetFd();
193     auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
194     ASSERT_NE(nullptr, map);
195 
196     auto p1 = (map->data<uint32_t>() + 11U).convert<TwoValues>();
197     ASSERT_TRUE(p1);
198     ASSERT_EQ(11U, p1->first);
199     ASSERT_EQ(12U, p1->second);
200 }
201 
TEST_F(MapPtrTest,PointerOffset)202 TEST_F(MapPtrTest, PointerOffset) {
203     auto fd = GetFd();
204     auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
205     ASSERT_NE(nullptr, map);
206 
207     auto p1 = map->data().offset(11U * sizeof(uint32_t)).convert<TwoValues>();
208     ASSERT_TRUE(p1);
209     ASSERT_EQ(11U, p1->first);
210     ASSERT_EQ(12U, p1->second);
211 }
212 
TEST_F(MapPtrTest,Iterator)213 TEST_F(MapPtrTest, Iterator) {
214     auto fd = GetFd();
215     auto map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
216     ASSERT_NE(nullptr, map);
217 
218     auto it = map->data<uint32_t>().iterator();
219     ASSERT_TRUE(*it);
220     ASSERT_EQ(0U, (*it).value());
221 
222     auto it2 = it;
223     ASSERT_EQ(it, it2);
224 
225     auto it3 = it++;
226     ASSERT_TRUE(*it3);
227     ASSERT_EQ(0U, (*it3).value());
228 
229     ASSERT_NE(it, it2);
230     ASSERT_EQ(1, it - it2);
231     ASSERT_EQ(-1, it2 - it);
232 
233     auto it4 = ++it;
234     ASSERT_TRUE(*it4);
235     ASSERT_EQ(2U, (*it4).value());
236 
237     it += 10;
238     ASSERT_EQ(12U, (*it).value());
239 }
240 
241 static jmp_buf buf;
242 
sigbus_handler(int sig)243 void sigbus_handler(int sig) {
244     if (sig == SIGBUS) {
245         siglongjmp(buf, 1);
246     } else {
247         FAIL();
248     }
249 }
250 
251 #define ASSERT_SIGBUS(test)               \
252     do {                                  \
253         signal(SIGBUS, &sigbus_handler);  \
254         if (sigsetjmp(buf, 1) == 0) {     \
255             ASSERT_EQ(0U, (test));        \
256             FAIL() << "No signal raised"; \
257         }                                 \
258     } while (0)
259 
TEST_F(MapPtrTest,VerifyMissingPageFails)260 TEST_F(MapPtrTest, VerifyMissingPageFails) {
261     for (uint32_t off :
262          std::vector<uint32_t>{0U, INCFS_DATA_FILE_BLOCK_SIZE / 2 - 1,
263                                INCFS_DATA_FILE_BLOCK_SIZE / 2 + 1, INCFS_DATA_FILE_BLOCK_SIZE,
264                                INCFS_DATA_FILE_BLOCK_SIZE * 3 / 2 - 1,
265                                INCFS_DATA_FILE_BLOCK_SIZE * 3 / 2 + 1}) {
266         auto fd = GetFd();
267         auto map = GetFileMap(fd.get(), off /* offset */, FILE_SIZE);
268         ASSERT_NE(nullptr, map);
269 
270         auto missing_page_start = INCFS_DATA_FILE_BLOCK_SIZE * FILE_MISSING_PAGE;
271         auto p1 = map->data().offset(missing_page_start - off).convert<uint32_t>();
272         ASSERT_FALSE(p1);
273         ASSERT_SIGBUS(p1.value());
274 
275         const auto p2 = p1;
276         ASSERT_FALSE(p2);
277         ASSERT_SIGBUS(p2.value());
278 
279         const auto p3 = p2 - 1U;
280         ASSERT_TRUE(p3);
281         ASSERT_EQ(3071U, p3.value());
282 
283         auto p4 = p3;
284         ASSERT_TRUE(p4);
285         ASSERT_EQ(3071U, p4.value());
286 
287         ASSERT_FALSE(p4 + 1U);
288         ASSERT_SIGBUS((p4 + 1U).value());
289 
290         auto p5 = p4++;
291         ASSERT_TRUE(p5);
292         ASSERT_EQ(3071U, p5.value());
293         ASSERT_FALSE(p4);
294         ASSERT_SIGBUS(p4.value());
295 
296         auto p6 = p3;
297         ASSERT_TRUE(p6);
298         ASSERT_EQ(3071U, p6.value());
299 
300         auto p7 = ++p6;
301         ASSERT_FALSE(p7);
302         ASSERT_SIGBUS(p7.value());
303         ASSERT_FALSE(p6);
304         ASSERT_SIGBUS(p6.value());
305 
306         auto missing_page_end = INCFS_DATA_FILE_BLOCK_SIZE * (FILE_MISSING_PAGE + 1);
307         auto p8 = map->data().offset(missing_page_end - off).convert<uint32_t>();
308         ASSERT_TRUE(p8);
309         ASSERT_EQ(4096U, p8.value());
310 
311         ASSERT_FALSE(p8 - 1U);
312         ASSERT_SIGBUS((p8 - 1U).value());
313     }
314 }
315 
TEST_F(MapPtrTest,GetDataAfterClose)316 TEST_F(MapPtrTest, GetDataAfterClose) {
317     std::unique_ptr<IncFsFileMap> map;
318     {
319         auto fd = GetFd();
320         map = GetFileMap(fd.get(), 0U /* offset */, FILE_SIZE);
321         ASSERT_NE(nullptr, map);
322     }
323 
324     auto missing_page_start = INCFS_DATA_FILE_BLOCK_SIZE * FILE_MISSING_PAGE;
325     auto p1 = map->data().offset(missing_page_start).convert<uint32_t>();
326     ASSERT_FALSE(p1);
327     ASSERT_SIGBUS(p1.value());
328 }
329