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