1 /*
2 * Copyright (C) 2019 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 <android-base/stringprintf.h>
19 #include <sys/select.h>
20
21 #include <unistd.h>
22
23 #include <optional>
24 #include <thread>
25
26 #include "IncFsTestBase.h"
27
28 using namespace android::incfs;
29 using namespace std::literals;
30 namespace ab = android::base;
31
32 struct ScopedUnmount {
33 std::string path_;
ScopedUnmountScopedUnmount34 explicit ScopedUnmount(std::string&& path) : path_(std::move(path)) {}
~ScopedUnmountScopedUnmount35 ~ScopedUnmount() { unmount(path_); }
36 };
37
38 class IncFsTest : public IncFsTestBase {
39 protected:
getReadTimeout()40 virtual int32_t getReadTimeout() {
41 return std::chrono::duration_cast<std::chrono::milliseconds>(kDefaultReadTimeout).count();
42 }
43
metadata(std::string_view sv)44 static IncFsSpan metadata(std::string_view sv) {
45 return {.data = sv.data(), .size = IncFsSize(sv.size())};
46 }
47
sizeToPages(int size)48 static int sizeToPages(int size) {
49 return (size + INCFS_DATA_FILE_BLOCK_SIZE - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
50 }
51
writeTestRanges(int id,int size)52 void writeTestRanges(int id, int size) {
53 auto wfd = openForSpecialOps(control_, fileId(id));
54 ASSERT_GE(wfd.get(), 0);
55
56 auto lastPage = sizeToPages(size) - 1;
57
58 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
59 DataBlock blocks[] = {{
60 .fileFd = wfd.get(),
61 .pageIndex = 1,
62 .compression = INCFS_COMPRESSION_KIND_NONE,
63 .dataSize = (uint32_t)data.size(),
64 .data = data.data(),
65 },
66 {
67 .fileFd = wfd.get(),
68 .pageIndex = 2,
69 .compression = INCFS_COMPRESSION_KIND_NONE,
70 .dataSize = (uint32_t)data.size(),
71 .data = data.data(),
72 },
73 {
74 .fileFd = wfd.get(),
75 .pageIndex = 10,
76 .compression = INCFS_COMPRESSION_KIND_NONE,
77 .dataSize = (uint32_t)data.size(),
78 .data = data.data(),
79 },
80 {
81 .fileFd = wfd.get(),
82 // last data page
83 .pageIndex = lastPage,
84 .compression = INCFS_COMPRESSION_KIND_NONE,
85 .dataSize = (uint32_t)data.size(),
86 .data = data.data(),
87 },
88 {
89 .fileFd = wfd.get(),
90 // first hash page
91 .pageIndex = 0,
92 .compression = INCFS_COMPRESSION_KIND_NONE,
93 .dataSize = (uint32_t)data.size(),
94 .kind = INCFS_BLOCK_KIND_HASH,
95 .data = data.data(),
96 },
97 {
98 .fileFd = wfd.get(),
99 .pageIndex = 2,
100 .compression = INCFS_COMPRESSION_KIND_NONE,
101 .dataSize = (uint32_t)data.size(),
102 .kind = INCFS_BLOCK_KIND_HASH,
103 .data = data.data(),
104 }};
105 ASSERT_EQ((int)std::size(blocks), writeBlocks({blocks, std::size(blocks)}));
106 }
107
writeBlock(int pageIndex)108 void writeBlock(int pageIndex) {
109 auto fd = openForSpecialOps(control_, fileId(1));
110 ASSERT_GE(fd.get(), 0);
111
112 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
113 auto block = DataBlock{
114 .fileFd = fd.get(),
115 .pageIndex = pageIndex,
116 .compression = INCFS_COMPRESSION_KIND_NONE,
117 .dataSize = (uint32_t)data.size(),
118 .data = data.data(),
119 };
120 ASSERT_EQ(1, writeBlocks({&block, 1}));
121 }
122
123 template <class ReadStruct>
testWriteBlockAndPageRead()124 void testWriteBlockAndPageRead() {
125 const auto id = fileId(1);
126 ASSERT_TRUE(control_.logs() >= 0);
127 ASSERT_EQ(0,
128 makeFile(control_, mountPath(test_file_name_), 0555, id,
129 {.size = test_file_size_}));
130 writeBlock(/*pageIndex=*/0);
131
132 std::thread wait_page_read_thread([&]() {
133 std::vector<ReadStruct> reads;
134 auto res = waitForPageReads(control_, std::chrono::seconds(5), &reads);
135 ASSERT_EQ(WaitResult::HaveData, res) << (int)res;
136 ASSERT_FALSE(reads.empty());
137 EXPECT_EQ(0, memcmp(&id, &reads[0].id, sizeof(id)));
138 EXPECT_EQ(0, int(reads[0].block));
139 if constexpr (std::is_same_v<ReadStruct, ReadInfoWithUid>) {
140 if (features() & Features::v2) {
141 EXPECT_NE(kIncFsNoUid, int(reads[0].uid));
142 } else {
143 EXPECT_EQ(kIncFsNoUid, int(reads[0].uid));
144 }
145 }
146 });
147
148 const auto file_path = mountPath(test_file_name_);
149 const android::base::unique_fd readFd(
150 open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
151 ASSERT_TRUE(readFd >= 0);
152 char buf[INCFS_DATA_FILE_BLOCK_SIZE];
153 ASSERT_TRUE(android::base::ReadFully(readFd, buf, sizeof(buf)));
154 wait_page_read_thread.join();
155 }
156
157 template <class PendingRead>
testWaitForPendingReads()158 void testWaitForPendingReads() {
159 const auto id = fileId(1);
160 ASSERT_EQ(0,
161 makeFile(control_, mountPath(test_file_name_), 0555, id,
162 {.size = test_file_size_}));
163
164 std::thread wait_pending_read_thread([&]() {
165 std::vector<PendingRead> pending_reads;
166 ASSERT_EQ(WaitResult::HaveData,
167 waitForPendingReads(control_, std::chrono::seconds(10), &pending_reads));
168 ASSERT_GT(pending_reads.size(), 0u);
169 EXPECT_EQ(0, memcmp(&id, &pending_reads[0].id, sizeof(id)));
170 EXPECT_EQ(0, (int)pending_reads[0].block);
171 if constexpr (std::is_same_v<PendingRead, ReadInfoWithUid>) {
172 if (features() & Features::v2) {
173 EXPECT_NE(kIncFsNoUid, int(pending_reads[0].uid));
174 } else {
175 EXPECT_EQ(kIncFsNoUid, int(pending_reads[0].uid));
176 }
177 }
178
179 auto fd = openForSpecialOps(control_, fileId(1));
180 ASSERT_GE(fd.get(), 0);
181
182 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
183 auto block = DataBlock{
184 .fileFd = fd.get(),
185 .pageIndex = 0,
186 .compression = INCFS_COMPRESSION_KIND_NONE,
187 .dataSize = (uint32_t)data.size(),
188 .data = data.data(),
189 };
190 ASSERT_EQ(1, writeBlocks({&block, 1}));
191 });
192
193 const auto file_path = mountPath(test_file_name_);
194 const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
195 ASSERT_GE(fd.get(), 0);
196 char buf[INCFS_DATA_FILE_BLOCK_SIZE];
197 ASSERT_TRUE(android::base::ReadFully(fd, buf, sizeof(buf)));
198 wait_pending_read_thread.join();
199 }
200
201 inline static const int test_file_size_ = INCFS_DATA_FILE_BLOCK_SIZE;
202 };
203
TEST_F(IncFsTest,GetIncfsFeatures)204 TEST_F(IncFsTest, GetIncfsFeatures) {
205 ASSERT_NE(features(), none);
206 }
207
TEST_F(IncFsTest,FalseIncfsPath)208 TEST_F(IncFsTest, FalseIncfsPath) {
209 TemporaryDir test_dir;
210 ASSERT_FALSE(isIncFsPath(test_dir.path));
211 }
212
TEST_F(IncFsTest,TrueIncfsPath)213 TEST_F(IncFsTest, TrueIncfsPath) {
214 ASSERT_TRUE(isIncFsPath(mount_dir_path_));
215 }
216
TEST_F(IncFsTest,TrueIncfsPathForBindMount)217 TEST_F(IncFsTest, TrueIncfsPathForBindMount) {
218 TemporaryDir tmp_dir_to_bind;
219 ASSERT_EQ(0, makeDir(control_, mountPath(test_dir_name_)));
220 ASSERT_EQ(0, bindMount(mountPath(test_dir_name_), tmp_dir_to_bind.path));
221 ScopedUnmount su(tmp_dir_to_bind.path);
222 ASSERT_TRUE(isIncFsPath(tmp_dir_to_bind.path));
223 }
224
TEST_F(IncFsTest,FalseIncfsPathFile)225 TEST_F(IncFsTest, FalseIncfsPathFile) {
226 TemporaryFile test_file;
227 ASSERT_FALSE(isIncFsFd(test_file.fd));
228 ASSERT_FALSE(isIncFsPath(test_file.path));
229 }
230
TEST_F(IncFsTest,TrueIncfsPathForBindMountFile)231 TEST_F(IncFsTest, TrueIncfsPathForBindMountFile) {
232 ASSERT_EQ(0,
233 makeFile(control_, mountPath(test_file_name_), 0555, fileId(1),
234 {.size = test_file_size_}));
235 const auto file_path = mountPath(test_file_name_);
236 const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
237 ASSERT_GE(fd.get(), 0);
238 ASSERT_TRUE(isIncFsFd(fd.get()));
239 ASSERT_TRUE(isIncFsPath(file_path));
240 }
241
TEST_F(IncFsTest,Control)242 TEST_F(IncFsTest, Control) {
243 ASSERT_TRUE(control_);
244 EXPECT_GE(IncFs_GetControlFd(control_, CMD), 0);
245 EXPECT_GE(IncFs_GetControlFd(control_, PENDING_READS), 0);
246 EXPECT_GE(IncFs_GetControlFd(control_, LOGS), 0);
247 EXPECT_EQ((features() & Features::v2) != 0, IncFs_GetControlFd(control_, BLOCKS_WRITTEN) >= 0);
248
249 auto fds = control_.releaseFds();
250 EXPECT_GE(fds.size(), size_t(4));
251 EXPECT_GE(fds[0].get(), 0);
252 EXPECT_GE(fds[1].get(), 0);
253 EXPECT_GE(fds[2].get(), 0);
254 ASSERT_TRUE(control_);
255 EXPECT_LT(IncFs_GetControlFd(control_, CMD), 0);
256 EXPECT_LT(IncFs_GetControlFd(control_, PENDING_READS), 0);
257 EXPECT_LT(IncFs_GetControlFd(control_, LOGS), 0);
258 EXPECT_LT(IncFs_GetControlFd(control_, BLOCKS_WRITTEN), 0);
259
260 control_.close();
261 EXPECT_FALSE(control_);
262
263 auto control = IncFs_CreateControl(fds[0].release(), fds[1].release(), fds[2].release(), -1);
264 ASSERT_TRUE(control);
265 EXPECT_GE(IncFs_GetControlFd(control, CMD), 0);
266 EXPECT_GE(IncFs_GetControlFd(control, PENDING_READS), 0);
267 EXPECT_GE(IncFs_GetControlFd(control, LOGS), 0);
268 IncFsFd rawFds[4];
269 EXPECT_EQ(-EINVAL, IncFs_ReleaseControlFds(nullptr, rawFds, 3));
270 EXPECT_EQ(-EINVAL, IncFs_ReleaseControlFds(control, nullptr, 3));
271 EXPECT_EQ(-ERANGE, IncFs_ReleaseControlFds(control, rawFds, 2));
272 EXPECT_EQ(4, IncFs_ReleaseControlFds(control, rawFds, 4));
273 EXPECT_GE(rawFds[0], 0);
274 EXPECT_GE(rawFds[1], 0);
275 EXPECT_GE(rawFds[2], 0);
276 ::close(rawFds[0]);
277 ::close(rawFds[1]);
278 ::close(rawFds[2]);
279 if (rawFds[3] >= 0) ::close(rawFds[3]);
280 IncFs_DeleteControl(control);
281 }
282
TEST_F(IncFsTest,MakeDir)283 TEST_F(IncFsTest, MakeDir) {
284 const auto dir_path = mountPath(test_dir_name_);
285 ASSERT_FALSE(exists(dir_path));
286 ASSERT_EQ(makeDir(control_, dir_path), 0);
287 ASSERT_TRUE(exists(dir_path));
288 }
289
TEST_F(IncFsTest,MakeDirs)290 TEST_F(IncFsTest, MakeDirs) {
291 const auto dir_path = mountPath(test_dir_name_);
292 ASSERT_FALSE(exists(dir_path));
293 ASSERT_EQ(makeDirs(control_, dir_path), 0);
294 ASSERT_TRUE(exists(dir_path));
295 ASSERT_EQ(makeDirs(control_, dir_path), 0);
296 auto nested = dir_path + "/couple/more/nested/levels";
297 ASSERT_EQ(makeDirs(control_, nested), 0);
298 ASSERT_TRUE(exists(nested));
299 ASSERT_NE(makeDirs(control_, "/"), 0);
300 }
301
TEST_F(IncFsTest,BindMount)302 TEST_F(IncFsTest, BindMount) {
303 {
304 TemporaryDir tmp_dir_to_bind;
305 ASSERT_EQ(0, makeDir(control_, mountPath(test_dir_name_)));
306 ASSERT_EQ(0, bindMount(mountPath(test_dir_name_), tmp_dir_to_bind.path));
307 ScopedUnmount su(tmp_dir_to_bind.path);
308 const auto test_file = mountPath(test_dir_name_, test_file_name_);
309 ASSERT_FALSE(exists(test_file.c_str())) << "Present: " << test_file;
310 ASSERT_EQ(0,
311 makeFile(control_, test_file, 0555, fileId(1),
312 {.size = test_file_size_, .metadata = metadata("md")}));
313 ASSERT_TRUE(exists(test_file.c_str())) << "Missing: " << test_file;
314 const auto file_binded_path = path::join(tmp_dir_to_bind.path, test_file_name_);
315 ASSERT_TRUE(exists(file_binded_path.c_str())) << "Missing: " << file_binded_path;
316 }
317
318 {
319 // Don't allow binding the root
320 TemporaryDir tmp_dir_to_bind;
321 ASSERT_EQ(-EINVAL, bindMount(mount_dir_path_, tmp_dir_to_bind.path));
322 }
323 }
324
TEST_F(IncFsTest,Root)325 TEST_F(IncFsTest, Root) {
326 ASSERT_EQ(mount_dir_path_, root(control_)) << "Error: " << errno;
327 }
328
TEST_F(IncFsTest,RootInvalidControl)329 TEST_F(IncFsTest, RootInvalidControl) {
330 const TemporaryFile tmp_file;
331 auto control{createControl(tmp_file.fd, -1, -1, -1)};
332 ASSERT_EQ("", root(control)) << "Error: " << errno;
333 }
334
TEST_F(IncFsTest,Open)335 TEST_F(IncFsTest, Open) {
336 Control control = open(mount_dir_path_);
337 ASSERT_TRUE(control.cmd() >= 0);
338 ASSERT_TRUE(control.pendingReads() >= 0);
339 ASSERT_TRUE(control.logs() >= 0);
340 }
341
TEST_F(IncFsTest,OpenFail)342 TEST_F(IncFsTest, OpenFail) {
343 TemporaryDir tmp_dir_to_bind;
344 Control control = open(tmp_dir_to_bind.path);
345 ASSERT_TRUE(control.cmd() < 0);
346 ASSERT_TRUE(control.pendingReads() < 0);
347 ASSERT_TRUE(control.logs() < 0);
348 }
349
TEST_F(IncFsTest,MakeFile)350 TEST_F(IncFsTest, MakeFile) {
351 ASSERT_EQ(0, makeDir(control_, mountPath(test_dir_name_)));
352 const auto file_path = mountPath(test_dir_name_, test_file_name_);
353 ASSERT_FALSE(exists(file_path));
354 ASSERT_EQ(0,
355 makeFile(control_, file_path, 0111, fileId(1),
356 {.size = test_file_size_, .metadata = metadata("md")}));
357 struct stat s;
358 ASSERT_EQ(0, stat(file_path.c_str(), &s));
359 ASSERT_EQ(test_file_size_, (int)s.st_size);
360 }
361
TEST_F(IncFsTest,MakeFile0)362 TEST_F(IncFsTest, MakeFile0) {
363 ASSERT_EQ(0, makeDir(control_, mountPath(test_dir_name_)));
364 const auto file_path = mountPath(test_dir_name_, ".info");
365 ASSERT_FALSE(exists(file_path));
366 ASSERT_EQ(0,
367 makeFile(control_, file_path, 0555, fileId(1),
368 {.size = 0, .metadata = metadata("mdsdfhjasdkfas l;jflaskdjf")}));
369 struct stat s;
370 ASSERT_EQ(0, stat(file_path.c_str(), &s));
371 ASSERT_EQ(0, (int)s.st_size);
372 }
373
TEST_F(IncFsTest,MakeMappedFile)374 TEST_F(IncFsTest, MakeMappedFile) {
375 ASSERT_EQ(0, makeDir(control_, mountPath(test_dir_name_)));
376
377 constexpr auto file_size = INCFS_DATA_FILE_BLOCK_SIZE * 2;
378 constexpr auto mapped_file_offset = file_size / 2;
379 constexpr auto mapped_file_size = file_size / 3;
380
381 const auto file_path = mountPath(test_dir_name_, test_file_name_);
382 ASSERT_FALSE(exists(file_path));
383 ASSERT_EQ(0,
384 makeFile(control_, file_path, 0111, fileId(1),
385 {.size = file_size, .metadata = metadata("md")}));
386 struct stat s = {};
387 ASSERT_EQ(0, stat(file_path.c_str(), &s));
388 ASSERT_EQ(file_size, (int)s.st_size);
389
390 const auto mapped_file_path = mountPath(test_dir_name_, test_mapped_file_name_);
391 ASSERT_FALSE(exists(mapped_file_path));
392 ASSERT_EQ(0,
393 makeMappedFile(control_, mapped_file_path, 0111,
394 {.sourceId = fileId(1),
395 .sourceOffset = mapped_file_offset,
396 .size = mapped_file_size}));
397 s = {};
398 ASSERT_EQ(0, stat(mapped_file_path.c_str(), &s));
399 ASSERT_EQ(mapped_file_size, (int)s.st_size);
400
401 // Check fileId for the source file.
402 ASSERT_EQ(fileId(1), getFileId(control_, file_path));
403 // Check that there is no fileId for the mapped file.
404 ASSERT_EQ(kIncFsInvalidFileId, getFileId(control_, mapped_file_path));
405 }
406
TEST_F(IncFsTest,GetFileId)407 TEST_F(IncFsTest, GetFileId) {
408 auto id = fileId(1);
409 ASSERT_EQ(0,
410 makeFile(control_, mountPath(test_file_name_), 0555, id,
411 {.size = test_file_size_, .metadata = metadata("md")}));
412 EXPECT_EQ(id, getFileId(control_, mountPath(test_file_name_))) << "errno = " << errno;
413 EXPECT_EQ(kIncFsInvalidFileId, getFileId(control_, test_file_name_));
414 EXPECT_EQ(kIncFsInvalidFileId, getFileId(control_, "asdf"));
415 EXPECT_EQ(kIncFsInvalidFileId, getFileId({}, mountPath(test_file_name_)));
416 }
417
TEST_F(IncFsTest,GetMetaData)418 TEST_F(IncFsTest, GetMetaData) {
419 const std::string_view md = "abc"sv;
420 ASSERT_EQ(0,
421 makeFile(control_, mountPath(test_file_name_), 0555, fileId(1),
422 {.size = test_file_size_, .metadata = metadata(md)}));
423 {
424 const auto raw_metadata = getMetadata(control_, mountPath(test_file_name_));
425 ASSERT_NE(0u, raw_metadata.size()) << errno;
426 const std::string result(raw_metadata.begin(), raw_metadata.end());
427 ASSERT_EQ(md, result);
428 }
429 {
430 const auto raw_metadata = getMetadata(control_, fileId(1));
431 ASSERT_NE(0u, raw_metadata.size()) << errno;
432 const std::string result(raw_metadata.begin(), raw_metadata.end());
433 ASSERT_EQ(md, result);
434 }
435 }
436
TEST_F(IncFsTest,LinkAndUnlink)437 TEST_F(IncFsTest, LinkAndUnlink) {
438 ASSERT_EQ(0, makeFile(control_, mountPath(test_file_name_), 0555, fileId(1), {.size = 0}));
439 ASSERT_EQ(0, makeDir(control_, mountPath(test_dir_name_)));
440 const std::string_view test_file = "test1.txt"sv;
441 const auto linked_file_path = mountPath(test_dir_name_, test_file);
442 ASSERT_FALSE(exists(linked_file_path));
443 ASSERT_EQ(0, link(control_, mountPath(test_file_name_), linked_file_path));
444 ASSERT_TRUE(exists(linked_file_path));
445 ASSERT_EQ(0, unlink(control_, linked_file_path));
446 ASSERT_FALSE(exists(linked_file_path));
447 }
448
TEST_F(IncFsTest,WriteBlocksAndPageRead)449 TEST_F(IncFsTest, WriteBlocksAndPageRead) {
450 ASSERT_NO_FATAL_FAILURE(testWriteBlockAndPageRead<ReadInfo>());
451 }
452
TEST_F(IncFsTest,WriteBlocksAndPageReadWithUid)453 TEST_F(IncFsTest, WriteBlocksAndPageReadWithUid) {
454 ASSERT_NO_FATAL_FAILURE(testWriteBlockAndPageRead<ReadInfoWithUid>());
455 }
456
TEST_F(IncFsTest,WaitForPendingReads)457 TEST_F(IncFsTest, WaitForPendingReads) {
458 ASSERT_NO_FATAL_FAILURE(testWaitForPendingReads<ReadInfo>());
459 }
460
TEST_F(IncFsTest,WaitForPendingReadsWithUid)461 TEST_F(IncFsTest, WaitForPendingReadsWithUid) {
462 ASSERT_NO_FATAL_FAILURE(testWaitForPendingReads<ReadInfoWithUid>());
463 }
464
TEST_F(IncFsTest,GetFilledRangesBad)465 TEST_F(IncFsTest, GetFilledRangesBad) {
466 EXPECT_EQ(-EBADF, IncFs_GetFilledRanges(-1, {}, nullptr));
467 EXPECT_EQ(-EINVAL, IncFs_GetFilledRanges(0, {}, nullptr));
468 EXPECT_EQ(-EINVAL, IncFs_GetFilledRangesStartingFrom(0, -1, {}, nullptr));
469
470 makeFileWithHash(1);
471 const android::base::unique_fd readFd(
472 open(mountPath(test_file_name_).c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
473 ASSERT_GE(readFd.get(), 0);
474
475 char buffer[1024];
476 IncFsFilledRanges res;
477 EXPECT_EQ(-EPERM, IncFs_GetFilledRanges(readFd.get(), {buffer, std::size(buffer)}, &res));
478 }
479
TEST_F(IncFsTest,GetFilledRanges)480 TEST_F(IncFsTest, GetFilledRanges) {
481 ASSERT_EQ(0,
482 makeFile(control_, mountPath(test_file_name_), 0555, fileId(1),
483 {.size = 4 * INCFS_DATA_FILE_BLOCK_SIZE}));
484 char buffer[1024];
485 const auto bufferSpan = IncFsSpan{.data = buffer, .size = std::size(buffer)};
486
487 auto fd = openForSpecialOps(control_, fileId(1));
488 ASSERT_GE(fd.get(), 0);
489
490 IncFsFilledRanges filledRanges;
491 EXPECT_EQ(0, IncFs_GetFilledRanges(fd.get(), IncFsSpan{}, &filledRanges));
492 EXPECT_EQ(0, filledRanges.dataRangesCount);
493 EXPECT_EQ(0, filledRanges.hashRangesCount);
494
495 EXPECT_EQ(0, IncFs_GetFilledRanges(fd.get(), bufferSpan, &filledRanges));
496 EXPECT_EQ(0, filledRanges.dataRangesCount);
497 EXPECT_EQ(0, filledRanges.hashRangesCount);
498
499 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 0, bufferSpan, &filledRanges));
500 EXPECT_EQ(0, filledRanges.dataRangesCount);
501 EXPECT_EQ(0, filledRanges.hashRangesCount);
502
503 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 1, bufferSpan, &filledRanges));
504 EXPECT_EQ(0, filledRanges.dataRangesCount);
505 EXPECT_EQ(0, filledRanges.hashRangesCount);
506
507 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 30, bufferSpan, &filledRanges));
508 EXPECT_EQ(0, filledRanges.dataRangesCount);
509 EXPECT_EQ(0, filledRanges.hashRangesCount);
510
511 EXPECT_EQ(-ENODATA, IncFs_IsFullyLoaded(fd.get()));
512 EXPECT_EQ(-ENODATA, IncFs_IsEverythingFullyLoaded(control_));
513
514 // write one block
515 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
516 auto block = DataBlock{
517 .fileFd = fd.get(),
518 .pageIndex = 0,
519 .compression = INCFS_COMPRESSION_KIND_NONE,
520 .dataSize = (uint32_t)data.size(),
521 .data = data.data(),
522 };
523 ASSERT_EQ(1, writeBlocks({&block, 1}));
524
525 EXPECT_EQ(0, IncFs_GetFilledRanges(fd.get(), bufferSpan, &filledRanges));
526 ASSERT_EQ(1, filledRanges.dataRangesCount);
527 EXPECT_EQ(0, filledRanges.dataRanges[0].begin);
528 EXPECT_EQ(1, filledRanges.dataRanges[0].end);
529 EXPECT_EQ(0, filledRanges.hashRangesCount);
530
531 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 0, bufferSpan, &filledRanges));
532 ASSERT_EQ(1, filledRanges.dataRangesCount);
533 EXPECT_EQ(0, filledRanges.dataRanges[0].begin);
534 EXPECT_EQ(1, filledRanges.dataRanges[0].end);
535 EXPECT_EQ(0, filledRanges.hashRangesCount);
536
537 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 1, bufferSpan, &filledRanges));
538 EXPECT_EQ(0, filledRanges.dataRangesCount);
539 EXPECT_EQ(0, filledRanges.hashRangesCount);
540
541 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 30, bufferSpan, &filledRanges));
542 EXPECT_EQ(0, filledRanges.dataRangesCount);
543 EXPECT_EQ(0, filledRanges.hashRangesCount);
544
545 EXPECT_EQ(-ENODATA, IncFs_IsFullyLoaded(fd.get()));
546 EXPECT_EQ(-ENODATA, IncFs_IsEverythingFullyLoaded(control_));
547
548 // append one more block next to the first one
549 block.pageIndex = 1;
550 ASSERT_EQ(1, writeBlocks({&block, 1}));
551
552 EXPECT_EQ(0, IncFs_GetFilledRanges(fd.get(), bufferSpan, &filledRanges));
553 ASSERT_EQ(1, filledRanges.dataRangesCount);
554 EXPECT_EQ(0, filledRanges.dataRanges[0].begin);
555 EXPECT_EQ(2, filledRanges.dataRanges[0].end);
556 EXPECT_EQ(0, filledRanges.hashRangesCount);
557
558 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 0, bufferSpan, &filledRanges));
559 ASSERT_EQ(1, filledRanges.dataRangesCount);
560 EXPECT_EQ(0, filledRanges.dataRanges[0].begin);
561 EXPECT_EQ(2, filledRanges.dataRanges[0].end);
562 EXPECT_EQ(0, filledRanges.hashRangesCount);
563
564 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 1, bufferSpan, &filledRanges));
565 ASSERT_EQ(1, filledRanges.dataRangesCount);
566 EXPECT_EQ(1, filledRanges.dataRanges[0].begin);
567 EXPECT_EQ(2, filledRanges.dataRanges[0].end);
568 EXPECT_EQ(0, filledRanges.hashRangesCount);
569
570 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 30, bufferSpan, &filledRanges));
571 EXPECT_EQ(0, filledRanges.dataRangesCount);
572 EXPECT_EQ(0, filledRanges.hashRangesCount);
573
574 EXPECT_EQ(-ENODATA, IncFs_IsFullyLoaded(fd.get()));
575 EXPECT_EQ(-ENODATA, IncFs_IsEverythingFullyLoaded(control_));
576
577 // now create a gap between filled blocks
578 block.pageIndex = 3;
579 ASSERT_EQ(1, writeBlocks({&block, 1}));
580
581 EXPECT_EQ(0, IncFs_GetFilledRanges(fd.get(), bufferSpan, &filledRanges));
582 ASSERT_EQ(2, filledRanges.dataRangesCount);
583 EXPECT_EQ(0, filledRanges.dataRanges[0].begin);
584 EXPECT_EQ(2, filledRanges.dataRanges[0].end);
585 EXPECT_EQ(3, filledRanges.dataRanges[1].begin);
586 EXPECT_EQ(4, filledRanges.dataRanges[1].end);
587 EXPECT_EQ(0, filledRanges.hashRangesCount);
588
589 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 0, bufferSpan, &filledRanges));
590 ASSERT_EQ(2, filledRanges.dataRangesCount);
591 EXPECT_EQ(0, filledRanges.dataRanges[0].begin);
592 EXPECT_EQ(2, filledRanges.dataRanges[0].end);
593 EXPECT_EQ(3, filledRanges.dataRanges[1].begin);
594 EXPECT_EQ(4, filledRanges.dataRanges[1].end);
595 EXPECT_EQ(0, filledRanges.hashRangesCount);
596
597 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 1, bufferSpan, &filledRanges));
598 ASSERT_EQ(2, filledRanges.dataRangesCount);
599 EXPECT_EQ(1, filledRanges.dataRanges[0].begin);
600 EXPECT_EQ(2, filledRanges.dataRanges[0].end);
601 EXPECT_EQ(3, filledRanges.dataRanges[1].begin);
602 EXPECT_EQ(4, filledRanges.dataRanges[1].end);
603 EXPECT_EQ(0, filledRanges.hashRangesCount);
604
605 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 2, bufferSpan, &filledRanges));
606 ASSERT_EQ(1, filledRanges.dataRangesCount);
607 EXPECT_EQ(3, filledRanges.dataRanges[0].begin);
608 EXPECT_EQ(4, filledRanges.dataRanges[0].end);
609 EXPECT_EQ(0, filledRanges.hashRangesCount);
610
611 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 30, bufferSpan, &filledRanges));
612 EXPECT_EQ(0, filledRanges.dataRangesCount);
613 EXPECT_EQ(0, filledRanges.hashRangesCount);
614
615 EXPECT_EQ(-ENODATA, IncFs_IsFullyLoaded(fd.get()));
616 EXPECT_EQ(-ENODATA, IncFs_IsEverythingFullyLoaded(control_));
617
618 // at last fill the whole file and make sure we report it as having a single range
619 block.pageIndex = 2;
620 ASSERT_EQ(1, writeBlocks({&block, 1}));
621
622 EXPECT_EQ(0, IncFs_GetFilledRanges(fd.get(), bufferSpan, &filledRanges));
623 ASSERT_EQ(1, filledRanges.dataRangesCount);
624 EXPECT_EQ(0, filledRanges.dataRanges[0].begin);
625 EXPECT_EQ(4, filledRanges.dataRanges[0].end);
626 EXPECT_EQ(0, filledRanges.hashRangesCount);
627
628 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 0, bufferSpan, &filledRanges));
629 ASSERT_EQ(1, filledRanges.dataRangesCount);
630 EXPECT_EQ(0, filledRanges.dataRanges[0].begin);
631 EXPECT_EQ(4, filledRanges.dataRanges[0].end);
632 EXPECT_EQ(0, filledRanges.hashRangesCount);
633
634 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 1, bufferSpan, &filledRanges));
635 ASSERT_EQ(1, filledRanges.dataRangesCount);
636 EXPECT_EQ(1, filledRanges.dataRanges[0].begin);
637 EXPECT_EQ(4, filledRanges.dataRanges[0].end);
638
639 EXPECT_EQ(0, IncFs_GetFilledRangesStartingFrom(fd.get(), 30, bufferSpan, &filledRanges));
640 EXPECT_EQ(0, filledRanges.dataRangesCount);
641 EXPECT_EQ(0, filledRanges.hashRangesCount);
642
643 EXPECT_EQ(0, IncFs_IsFullyLoaded(fd.get()));
644 EXPECT_EQ(0, IncFs_IsEverythingFullyLoaded(control_));
645 }
646
TEST_F(IncFsTest,GetFilledRangesSmallBuffer)647 TEST_F(IncFsTest, GetFilledRangesSmallBuffer) {
648 ASSERT_EQ(0,
649 makeFile(control_, mountPath(test_file_name_), 0555, fileId(1),
650 {.size = 5 * INCFS_DATA_FILE_BLOCK_SIZE}));
651 char buffer[1024];
652
653 auto fd = openForSpecialOps(control_, fileId(1));
654 ASSERT_GE(fd.get(), 0);
655
656 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
657 DataBlock blocks[] = {DataBlock{
658 .fileFd = fd.get(),
659 .pageIndex = 0,
660 .compression = INCFS_COMPRESSION_KIND_NONE,
661 .dataSize = (uint32_t)data.size(),
662 .data = data.data(),
663 },
664 DataBlock{
665 .fileFd = fd.get(),
666 .pageIndex = 2,
667 .compression = INCFS_COMPRESSION_KIND_NONE,
668 .dataSize = (uint32_t)data.size(),
669 .data = data.data(),
670 },
671 DataBlock{
672 .fileFd = fd.get(),
673 .pageIndex = 4,
674 .compression = INCFS_COMPRESSION_KIND_NONE,
675 .dataSize = (uint32_t)data.size(),
676 .data = data.data(),
677 }};
678 ASSERT_EQ(3, writeBlocks({blocks, 3}));
679
680 IncFsSpan bufferSpan = {.data = buffer, .size = sizeof(IncFsBlockRange)};
681 IncFsFilledRanges filledRanges;
682 EXPECT_EQ(-ERANGE, IncFs_GetFilledRanges(fd.get(), bufferSpan, &filledRanges));
683 ASSERT_EQ(1, filledRanges.dataRangesCount);
684 EXPECT_EQ(0, filledRanges.dataRanges[0].begin);
685 EXPECT_EQ(1, filledRanges.dataRanges[0].end);
686 EXPECT_EQ(0, filledRanges.hashRangesCount);
687 EXPECT_EQ(2, filledRanges.endIndex);
688
689 EXPECT_EQ(-ERANGE,
690 IncFs_GetFilledRangesStartingFrom(fd.get(), filledRanges.endIndex, bufferSpan,
691 &filledRanges));
692 ASSERT_EQ(1, filledRanges.dataRangesCount);
693 EXPECT_EQ(2, filledRanges.dataRanges[0].begin);
694 EXPECT_EQ(3, filledRanges.dataRanges[0].end);
695 EXPECT_EQ(0, filledRanges.hashRangesCount);
696 EXPECT_EQ(4, filledRanges.endIndex);
697
698 EXPECT_EQ(0,
699 IncFs_GetFilledRangesStartingFrom(fd.get(), filledRanges.endIndex, bufferSpan,
700 &filledRanges));
701 ASSERT_EQ(1, filledRanges.dataRangesCount);
702 EXPECT_EQ(4, filledRanges.dataRanges[0].begin);
703 EXPECT_EQ(5, filledRanges.dataRanges[0].end);
704 EXPECT_EQ(0, filledRanges.hashRangesCount);
705 EXPECT_EQ(5, filledRanges.endIndex);
706 }
707
TEST_F(IncFsTest,GetFilledRangesWithHashes)708 TEST_F(IncFsTest, GetFilledRangesWithHashes) {
709 auto size = makeFileWithHash(1);
710 ASSERT_GT(size, 0);
711 ASSERT_NO_FATAL_FAILURE(writeTestRanges(1, size));
712
713 auto fd = openForSpecialOps(control_, fileId(1));
714 ASSERT_GE(fd.get(), 0);
715
716 char buffer[1024];
717 IncFsSpan bufferSpan = {.data = buffer, .size = sizeof(buffer)};
718 IncFsFilledRanges filledRanges;
719 EXPECT_EQ(0, IncFs_GetFilledRanges(fd.get(), bufferSpan, &filledRanges));
720 ASSERT_EQ(3, filledRanges.dataRangesCount);
721 auto lastPage = sizeToPages(size) - 1;
722 EXPECT_EQ(lastPage, filledRanges.dataRanges[2].begin);
723 EXPECT_EQ(lastPage + 1, filledRanges.dataRanges[2].end);
724 EXPECT_EQ(2, filledRanges.hashRangesCount);
725 EXPECT_EQ(0, filledRanges.hashRanges[0].begin);
726 EXPECT_EQ(1, filledRanges.hashRanges[0].end);
727 EXPECT_EQ(2, filledRanges.hashRanges[1].begin);
728 EXPECT_EQ(3, filledRanges.hashRanges[1].end);
729 EXPECT_EQ(sizeToPages(size) + 3, filledRanges.endIndex);
730 }
731
TEST_F(IncFsTest,GetFilledRangesCpp)732 TEST_F(IncFsTest, GetFilledRangesCpp) {
733 auto size = makeFileWithHash(1);
734 ASSERT_GT(size, 0);
735 ASSERT_NO_FATAL_FAILURE(writeTestRanges(1, size));
736
737 auto fd = openForSpecialOps(control_, fileId(1));
738 ASSERT_GE(fd.get(), 0);
739
740 // simply get all ranges
741 auto [res, ranges] = getFilledRanges(fd.get());
742 EXPECT_EQ(res, 0);
743 EXPECT_EQ(size_t(5), ranges.totalSize());
744 ASSERT_EQ(size_t(3), ranges.dataRanges().size());
745 auto lastPage = sizeToPages(size) - 1;
746 EXPECT_EQ(lastPage, ranges.dataRanges()[2].begin);
747 EXPECT_EQ(size_t(1), ranges.dataRanges()[2].size());
748 ASSERT_EQ(size_t(2), ranges.hashRanges().size());
749 EXPECT_EQ(0, ranges.hashRanges()[0].begin);
750 EXPECT_EQ(size_t(1), ranges.hashRanges()[0].size());
751 EXPECT_EQ(2, ranges.hashRanges()[1].begin);
752 EXPECT_EQ(size_t(1), ranges.hashRanges()[1].size());
753
754 // now check how buffer size limiting works.
755 FilledRanges::RangeBuffer buf(ranges.totalSize() - 1);
756 auto [res2, ranges2] = getFilledRanges(fd.get(), std::move(buf));
757 ASSERT_EQ(-ERANGE, res2);
758 EXPECT_EQ(ranges.totalSize() - 1, ranges2.totalSize());
759 ASSERT_EQ(size_t(3), ranges2.dataRanges().size());
760 ASSERT_EQ(size_t(1), ranges2.hashRanges().size());
761 EXPECT_EQ(0, ranges2.hashRanges()[0].begin);
762 EXPECT_EQ(size_t(1), ranges2.hashRanges()[0].size());
763
764 // and now check the resumption from the previous result
765 auto [res3, ranges3] = getFilledRanges(fd.get(), std::move(ranges2));
766 ASSERT_EQ(0, res3);
767 EXPECT_EQ(ranges.totalSize(), ranges3.totalSize());
768 ASSERT_EQ(size_t(3), ranges3.dataRanges().size());
769 ASSERT_EQ(size_t(2), ranges3.hashRanges().size());
770 EXPECT_EQ(0, ranges3.hashRanges()[0].begin);
771 EXPECT_EQ(size_t(1), ranges3.hashRanges()[0].size());
772 EXPECT_EQ(2, ranges3.hashRanges()[1].begin);
773 EXPECT_EQ(size_t(1), ranges3.hashRanges()[1].size());
774
775 EXPECT_EQ(LoadingState::MissingBlocks, isFullyLoaded(fd.get()));
776 EXPECT_EQ(LoadingState::MissingBlocks, isEverythingFullyLoaded(control_));
777
778 {
779 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
780 DataBlock block = {.fileFd = fd.get(),
781 .pageIndex = 1,
782 .compression = INCFS_COMPRESSION_KIND_NONE,
783 .dataSize = (uint32_t)data.size(),
784 .data = data.data()};
785 for (auto i = 0; i != sizeToPages(size); ++i) {
786 block.pageIndex = i;
787 ASSERT_EQ(1, writeBlocks({&block, 1}));
788 }
789 block.kind = INCFS_BLOCK_KIND_HASH;
790 for (auto i = 0; i != 3; ++i) {
791 block.pageIndex = i;
792 ASSERT_EQ(1, writeBlocks({&block, 1}));
793 }
794 }
795 EXPECT_EQ(LoadingState::Full, isFullyLoaded(fd.get()));
796 EXPECT_EQ(LoadingState::Full, isEverythingFullyLoaded(control_));
797 }
798
TEST_F(IncFsTest,BlocksWritten)799 TEST_F(IncFsTest, BlocksWritten) {
800 if (!(features() & Features::v2)) {
801 GTEST_SKIP() << "test not supported: IncFS is too old";
802 return;
803 }
804 const auto id = fileId(1);
805 ASSERT_EQ(0,
806 makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = test_file_size_}));
807
808 IncFsSize blocksWritten = 0;
809 ASSERT_EQ(0, IncFs_WaitForFsWrittenBlocksChange(control_, 0, &blocksWritten));
810 EXPECT_EQ(0, blocksWritten);
811
812 auto fd = openForSpecialOps(control_, fileId(1));
813 ASSERT_GE(fd.get(), 0);
814
815 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
816 auto block = DataBlock{
817 .fileFd = fd.get(),
818 .pageIndex = 0,
819 .compression = INCFS_COMPRESSION_KIND_NONE,
820 .dataSize = (uint32_t)data.size(),
821 .data = data.data(),
822 };
823 ASSERT_EQ(1, writeBlocks({&block, 1}));
824
825 ASSERT_EQ(0, IncFs_WaitForFsWrittenBlocksChange(control_, 0, &blocksWritten));
826 EXPECT_EQ(1, blocksWritten);
827 }
828
TEST_F(IncFsTest,Timeouts)829 TEST_F(IncFsTest, Timeouts) {
830 if (!(features() & Features::v2)) {
831 GTEST_SKIP() << "test not supported: IncFS is too old";
832 return;
833 }
834
835 IncFsUidReadTimeouts timeouts[2] = {{1, 1000, 2000, 3000}, {2, 1000, 3000, 4000}};
836
837 EXPECT_EQ(0, IncFs_SetUidReadTimeouts(control_, timeouts, std::size(timeouts)));
838
839 IncFsUidReadTimeouts outTimeouts[3];
840
841 size_t outSize = 1;
842 EXPECT_EQ(-E2BIG, IncFs_GetUidReadTimeouts(control_, outTimeouts, &outSize));
843 EXPECT_EQ(size_t(2), outSize);
844
845 outSize = 3;
846 EXPECT_EQ(0, IncFs_GetUidReadTimeouts(control_, outTimeouts, &outSize));
847 EXPECT_EQ(size_t(2), outSize);
848
849 EXPECT_EQ(0, memcmp(timeouts, outTimeouts, 2 * sizeof(timeouts[0])));
850 }
851
TEST_F(IncFsTest,CompletionNoFiles)852 TEST_F(IncFsTest, CompletionNoFiles) {
853 if (!(features() & Features::v2)) {
854 GTEST_SKIP() << "test not supported: IncFS is too old";
855 return;
856 }
857
858 size_t count = 0;
859 EXPECT_EQ(0, IncFs_ListIncompleteFiles(control_, nullptr, &count));
860 EXPECT_EQ(size_t(0), count);
861 EXPECT_EQ(0, IncFs_WaitForLoadingComplete(control_, 0));
862 }
863
TEST_F(IncFsTest,CompletionOneFile)864 TEST_F(IncFsTest, CompletionOneFile) {
865 if (!(features() & Features::v2)) {
866 GTEST_SKIP() << "test not supported: IncFS is too old";
867 return;
868 }
869
870 const auto id = fileId(1);
871 ASSERT_EQ(0,
872 makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = test_file_size_}));
873
874 size_t count = 0;
875 EXPECT_EQ(-E2BIG, IncFs_ListIncompleteFiles(control_, nullptr, &count));
876 EXPECT_EQ(size_t(1), count);
877 EXPECT_EQ(-ETIMEDOUT, IncFs_WaitForLoadingComplete(control_, 0));
878
879 IncFsFileId ids[2];
880 count = 2;
881 EXPECT_EQ(0, IncFs_ListIncompleteFiles(control_, ids, &count));
882 EXPECT_EQ(size_t(1), count);
883 EXPECT_EQ(id, ids[0]);
884
885 auto fd = openForSpecialOps(control_, id);
886 ASSERT_GE(fd.get(), 0);
887 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
888 auto block = DataBlock{
889 .fileFd = fd.get(),
890 .pageIndex = 0,
891 .compression = INCFS_COMPRESSION_KIND_NONE,
892 .dataSize = (uint32_t)data.size(),
893 .data = data.data(),
894 };
895 ASSERT_EQ(1, writeBlocks({&block, 1}));
896
897 count = 2;
898 EXPECT_EQ(0, IncFs_ListIncompleteFiles(control_, ids, &count));
899 EXPECT_EQ(size_t(0), count);
900 EXPECT_EQ(0, IncFs_WaitForLoadingComplete(control_, 0));
901 }
902
TEST_F(IncFsTest,CompletionMultiple)903 TEST_F(IncFsTest, CompletionMultiple) {
904 if (!(features() & Features::v2)) {
905 GTEST_SKIP() << "test not supported: IncFS is too old";
906 return;
907 }
908
909 const auto id = fileId(1);
910 ASSERT_EQ(0,
911 makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = test_file_size_}));
912
913 size_t count = 0;
914 EXPECT_EQ(-E2BIG, IncFs_ListIncompleteFiles(control_, nullptr, &count));
915 EXPECT_EQ(size_t(1), count);
916 EXPECT_EQ(-ETIMEDOUT, IncFs_WaitForLoadingComplete(control_, 0));
917
918 // fill the existing file but add another one
919 const auto id2 = fileId(2);
920 ASSERT_EQ(0, makeFile(control_, mountPath("test2"), 0555, id2, {.size = test_file_size_}));
921
922 IncFsFileId ids[2];
923 count = 2;
924 EXPECT_EQ(0, IncFs_ListIncompleteFiles(control_, ids, &count));
925 EXPECT_EQ(size_t(2), count);
926 EXPECT_EQ(id, ids[0]);
927 EXPECT_EQ(id2, ids[1]);
928
929 auto fd = openForSpecialOps(control_, id);
930 ASSERT_GE(fd.get(), 0);
931 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
932 auto block = DataBlock{
933 .fileFd = fd.get(),
934 .pageIndex = 0,
935 .compression = INCFS_COMPRESSION_KIND_NONE,
936 .dataSize = (uint32_t)data.size(),
937 .data = data.data(),
938 };
939 ASSERT_EQ(1, writeBlocks({&block, 1}));
940
941 count = 2;
942 EXPECT_EQ(0, IncFs_ListIncompleteFiles(control_, ids, &count));
943 EXPECT_EQ(size_t(1), count);
944 EXPECT_EQ(id2, ids[0]);
945 EXPECT_EQ(-ETIMEDOUT, IncFs_WaitForLoadingComplete(control_, 0));
946 }
947
TEST_F(IncFsTest,CompletionWait)948 TEST_F(IncFsTest, CompletionWait) {
949 if (!(features() & Features::v2)) {
950 GTEST_SKIP() << "test not supported: IncFS is too old";
951 return;
952 }
953
954 ASSERT_EQ(0,
955 makeFile(control_, mountPath("test1"), 0555, fileId(1),
956 {.size = INCFS_DATA_FILE_BLOCK_SIZE}));
957 ASSERT_EQ(0,
958 makeFile(control_, mountPath("test2"), 0555, fileId(2),
959 {.size = INCFS_DATA_FILE_BLOCK_SIZE}));
960 ASSERT_EQ(0,
961 makeFile(control_, mountPath("test3"), 0555, fileId(3),
962 {.size = INCFS_DATA_FILE_BLOCK_SIZE}));
963
964 std::atomic<int> res = -1;
965 auto waiter = std::thread([&] { res = IncFs_WaitForLoadingComplete(control_, 5 * 1000); });
966
967 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
968
969 {
970 auto fd = openForSpecialOps(control_, fileId(1));
971 ASSERT_GE(fd.get(), 0);
972 auto block = DataBlock{
973 .fileFd = fd.get(),
974 .pageIndex = 0,
975 .compression = INCFS_COMPRESSION_KIND_NONE,
976 .dataSize = (uint32_t)data.size(),
977 .data = data.data(),
978 };
979 ASSERT_EQ(1, writeBlocks({&block, 1}));
980 }
981 ASSERT_TRUE(res == -1);
982
983 {
984 auto fd = openForSpecialOps(control_, fileId(3));
985 ASSERT_GE(fd.get(), 0);
986 auto block = DataBlock{
987 .fileFd = fd.get(),
988 .pageIndex = 0,
989 .compression = INCFS_COMPRESSION_KIND_NONE,
990 .dataSize = (uint32_t)data.size(),
991 .data = data.data(),
992 };
993 ASSERT_EQ(1, writeBlocks({&block, 1}));
994 }
995 ASSERT_TRUE(res == -1);
996
997 {
998 auto fd = openForSpecialOps(control_, fileId(2));
999 ASSERT_GE(fd.get(), 0);
1000 auto block = DataBlock{
1001 .fileFd = fd.get(),
1002 .pageIndex = 0,
1003 .compression = INCFS_COMPRESSION_KIND_NONE,
1004 .dataSize = (uint32_t)data.size(),
1005 .data = data.data(),
1006 };
1007 ASSERT_EQ(1, writeBlocks({&block, 1}));
1008 }
1009
1010 waiter.join();
1011
1012 auto listIncomplete = [&] {
1013 IncFsFileId ids[3];
1014 size_t count = 3;
1015 if (IncFs_ListIncompleteFiles(control_, ids, &count) != 0) {
1016 return "error listing incomplete files"s;
1017 }
1018 auto res = ab::StringPrintf("[%d]", int(count));
1019 for (size_t i = 0; i < count; ++i) {
1020 ab::StringAppendF(&res, " %s", toString(ids[i]).c_str());
1021 }
1022 return res;
1023 };
1024 EXPECT_EQ(0, res) << "Incomplete files: " << listIncomplete();
1025 }
1026
TEST_F(IncFsTest,GetBlockCounts)1027 TEST_F(IncFsTest, GetBlockCounts) {
1028 if (!(features() & Features::v2)) {
1029 GTEST_SKIP() << "test not supported: IncFS is too old";
1030 return;
1031 }
1032
1033 const auto id = fileId(1);
1034 ASSERT_EQ(0,
1035 makeFile(control_, mountPath(test_file_name_), 0555, id,
1036 {.size = 20 * INCFS_DATA_FILE_BLOCK_SIZE + 3}));
1037
1038 IncFsBlockCounts counts = {};
1039 EXPECT_EQ(0,
1040 IncFs_GetFileBlockCountByPath(control_, mountPath(test_file_name_).c_str(), &counts));
1041 EXPECT_EQ(21, counts.totalDataBlocks);
1042 EXPECT_EQ(0, counts.filledDataBlocks);
1043 EXPECT_EQ(0, counts.totalHashBlocks);
1044 EXPECT_EQ(0, counts.filledHashBlocks);
1045
1046 EXPECT_EQ(0, IncFs_GetFileBlockCountById(control_, id, &counts));
1047 EXPECT_EQ(21, counts.totalDataBlocks);
1048 EXPECT_EQ(0, counts.filledDataBlocks);
1049 EXPECT_EQ(0, counts.totalHashBlocks);
1050 EXPECT_EQ(0, counts.filledHashBlocks);
1051
1052 auto fd = openForSpecialOps(control_, id);
1053 ASSERT_GE(fd.get(), 0);
1054 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
1055 auto block = DataBlock{
1056 .fileFd = fd.get(),
1057 .pageIndex = 3,
1058 .compression = INCFS_COMPRESSION_KIND_NONE,
1059 .dataSize = (uint32_t)data.size(),
1060 .data = data.data(),
1061 };
1062 ASSERT_EQ(1, writeBlocks({&block, 1}));
1063
1064 EXPECT_EQ(0,
1065 IncFs_GetFileBlockCountByPath(control_, mountPath(test_file_name_).c_str(), &counts));
1066 EXPECT_EQ(21, counts.totalDataBlocks);
1067 EXPECT_EQ(1, counts.filledDataBlocks);
1068 EXPECT_EQ(0, counts.totalHashBlocks);
1069 EXPECT_EQ(0, counts.filledHashBlocks);
1070
1071 EXPECT_EQ(0, IncFs_GetFileBlockCountById(control_, id, &counts));
1072 EXPECT_EQ(21, counts.totalDataBlocks);
1073 EXPECT_EQ(1, counts.filledDataBlocks);
1074 EXPECT_EQ(0, counts.totalHashBlocks);
1075 EXPECT_EQ(0, counts.filledHashBlocks);
1076 }
1077
TEST_F(IncFsTest,GetBlockCountsHash)1078 TEST_F(IncFsTest, GetBlockCountsHash) {
1079 if (!(features() & Features::v2)) {
1080 GTEST_SKIP() << "test not supported: IncFS is too old";
1081 return;
1082 }
1083
1084 auto size = makeFileWithHash(1);
1085 ASSERT_GT(size, 0);
1086
1087 IncFsBlockCounts counts = {};
1088 EXPECT_EQ(0,
1089 IncFs_GetFileBlockCountByPath(control_, mountPath(test_file_name_).c_str(), &counts));
1090 EXPECT_EQ(sizeToPages(size), counts.totalDataBlocks);
1091 EXPECT_EQ(0, counts.filledDataBlocks);
1092 EXPECT_EQ(3, counts.totalHashBlocks);
1093 EXPECT_EQ(0, counts.filledHashBlocks);
1094
1095 ASSERT_NO_FATAL_FAILURE(writeTestRanges(1, size));
1096
1097 EXPECT_EQ(0,
1098 IncFs_GetFileBlockCountByPath(control_, mountPath(test_file_name_).c_str(), &counts));
1099 EXPECT_EQ(sizeToPages(size), counts.totalDataBlocks);
1100 EXPECT_EQ(4, counts.filledDataBlocks);
1101 EXPECT_EQ(3, counts.totalHashBlocks);
1102 EXPECT_EQ(2, counts.filledHashBlocks);
1103 }
1104
TEST_F(IncFsTest,ReserveSpace)1105 TEST_F(IncFsTest, ReserveSpace) {
1106 auto size = makeFileWithHash(1);
1107 ASSERT_GT(size, 0);
1108
1109 EXPECT_EQ(-ENOENT,
1110 IncFs_ReserveSpaceByPath(control_, mountPath("1"s += test_file_name_).c_str(), size));
1111 EXPECT_EQ(0, IncFs_ReserveSpaceByPath(control_, mountPath(test_file_name_).c_str(), size));
1112 EXPECT_EQ(0, IncFs_ReserveSpaceByPath(control_, mountPath(test_file_name_).c_str(), 2 * size));
1113 EXPECT_EQ(0, IncFs_ReserveSpaceByPath(control_, mountPath(test_file_name_).c_str(), 2 * size));
1114 EXPECT_EQ(0,
1115 IncFs_ReserveSpaceByPath(control_, mountPath(test_file_name_).c_str(),
1116 kTrimReservedSpace));
1117 EXPECT_EQ(0,
1118 IncFs_ReserveSpaceByPath(control_, mountPath(test_file_name_).c_str(),
1119 kTrimReservedSpace));
1120
1121 EXPECT_EQ(-ENOENT, IncFs_ReserveSpaceById(control_, fileId(2), size));
1122 EXPECT_EQ(0, IncFs_ReserveSpaceById(control_, fileId(1), size));
1123 EXPECT_EQ(0, IncFs_ReserveSpaceById(control_, fileId(1), 2 * size));
1124 EXPECT_EQ(0, IncFs_ReserveSpaceById(control_, fileId(1), 2 * size));
1125 EXPECT_EQ(0, IncFs_ReserveSpaceById(control_, fileId(1), kTrimReservedSpace));
1126 EXPECT_EQ(0, IncFs_ReserveSpaceById(control_, fileId(1), kTrimReservedSpace));
1127 }
1128
TEST_F(IncFsTest,ForEachFile)1129 TEST_F(IncFsTest, ForEachFile) {
1130 const auto incompleteSupported = (features() & Features::v2) != 0;
1131 EXPECT_EQ(-EINVAL, IncFs_ForEachFile(nullptr, nullptr, nullptr));
1132 EXPECT_EQ(-EINVAL, IncFs_ForEachIncompleteFile(nullptr, nullptr, nullptr));
1133 EXPECT_EQ(-EINVAL, IncFs_ForEachFile(control_, nullptr, nullptr));
1134 EXPECT_EQ(-EINVAL, IncFs_ForEachIncompleteFile(control_, nullptr, nullptr));
1135 EXPECT_EQ(0, IncFs_ForEachFile(control_, nullptr, [](auto, auto, auto) { return true; }));
1136 EXPECT_EQ(incompleteSupported ? 0 : -ENOTSUP,
1137 IncFs_ForEachIncompleteFile(control_, nullptr,
1138 [](auto, auto, auto) { return true; }));
1139 EXPECT_EQ(0, IncFs_ForEachFile(control_, this, [](auto, auto, auto) { return true; }));
1140 EXPECT_EQ(incompleteSupported ? 0 : -ENOTSUP,
1141 IncFs_ForEachIncompleteFile(control_, this, [](auto, auto, auto) { return true; }));
1142
1143 int res = makeFile(control_, mountPath("incomplete.txt"), 0555, fileId(1),
1144 {.metadata = metadata("md")});
1145 ASSERT_EQ(res, 0);
1146
1147 EXPECT_EQ(1, IncFs_ForEachFile(control_, this, [](auto, auto context, auto id) {
1148 auto self = (IncFsTest*)context;
1149 EXPECT_EQ(self->fileId(1), id);
1150 return true;
1151 }));
1152 EXPECT_EQ(incompleteSupported ? 0 : -ENOTSUP,
1153 IncFs_ForEachIncompleteFile(control_, this, [](auto, auto, auto) { return true; }));
1154
1155 auto size = makeFileWithHash(2);
1156 ASSERT_GT(size, 0);
1157
1158 EXPECT_EQ(1, IncFs_ForEachFile(control_, this, [](auto, auto context, auto id) {
1159 auto self = (IncFsTest*)context;
1160 EXPECT_TRUE(id == self->fileId(1) || id == self->fileId(2));
1161 return false;
1162 }));
1163 EXPECT_EQ(2, IncFs_ForEachFile(control_, this, [](auto, auto context, auto id) {
1164 auto self = (IncFsTest*)context;
1165 EXPECT_TRUE(id == self->fileId(1) || id == self->fileId(2));
1166 return true;
1167 }));
1168 EXPECT_EQ(incompleteSupported ? 1 : -ENOTSUP,
1169 IncFs_ForEachIncompleteFile(control_, this, [](auto, auto context, auto id) {
1170 auto self = (IncFsTest*)context;
1171 EXPECT_EQ(self->fileId(2), id);
1172 return true;
1173 }));
1174 }
1175
TEST(CStrWrapperTest,EmptyStringView)1176 TEST(CStrWrapperTest, EmptyStringView) {
1177 ASSERT_STREQ("", details::c_str({}).get());
1178 ASSERT_STREQ("", details::c_str({nullptr, 0}).get());
1179 }
1180
1181 class IncFsGetMetricsTest : public IncFsTestBase {
1182 protected:
getReadTimeout()1183 int32_t getReadTimeout() override { return 100 /* 0.1 second */; }
1184 };
1185
TEST_F(IncFsGetMetricsTest,MetricsWithNoEvents)1186 TEST_F(IncFsGetMetricsTest, MetricsWithNoEvents) {
1187 if (!(features() & Features::v2)) {
1188 GTEST_SKIP() << "test not supported: IncFS is too old";
1189 return;
1190 }
1191 IncFsLastReadError lastReadError = {.id = fileId(-1),
1192 .timestampUs = static_cast<uint64_t>(-1),
1193 .block = static_cast<IncFsBlockIndex>(-1),
1194 .errorNo = static_cast<uint32_t>(-1),
1195 .uid = static_cast<IncFsUid>(-1)};
1196 EXPECT_EQ(0, IncFs_GetLastReadError(control_, &lastReadError));
1197 // All fields should be zero
1198 EXPECT_EQ(FileId{}, lastReadError.id);
1199 EXPECT_EQ(0, (int)lastReadError.timestampUs);
1200 EXPECT_EQ(0, (int)lastReadError.block);
1201 EXPECT_EQ(0, (int)lastReadError.errorNo);
1202 EXPECT_EQ(0, (int)lastReadError.uid);
1203
1204 IncFsMetrics incfsMetrics = {10, 10, 10, 10, 10, 10, 10, 10, 10};
1205 EXPECT_EQ(0, IncFs_GetMetrics(metrics_key_.c_str(), &incfsMetrics));
1206 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMin);
1207 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMinUs);
1208 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPending);
1209 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPendingUs);
1210 EXPECT_EQ(0, (int)incfsMetrics.readsFailedHashVerification);
1211 EXPECT_EQ(0, (int)incfsMetrics.readsFailedOther);
1212 EXPECT_EQ(0, (int)incfsMetrics.readsFailedTimedOut);
1213 }
1214
TEST_F(IncFsGetMetricsTest,MetricsWithReadsTimeOut)1215 TEST_F(IncFsGetMetricsTest, MetricsWithReadsTimeOut) {
1216 if (!(features() & Features::v2)) {
1217 GTEST_SKIP() << "test not supported: IncFS is too old";
1218 return;
1219 }
1220 const auto id = fileId(1);
1221 ASSERT_EQ(0,
1222 makeFile(control_, mountPath(test_file_name_), 0555, id,
1223 {.size = INCFS_DATA_FILE_BLOCK_SIZE}));
1224
1225 const auto file_path = mountPath(test_file_name_);
1226 const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
1227 ASSERT_GE(fd.get(), 0);
1228 // Read should timeout immediately
1229 char buf[INCFS_DATA_FILE_BLOCK_SIZE];
1230 EXPECT_FALSE(android::base::ReadFully(fd, buf, sizeof(buf)));
1231 IncFsLastReadError lastReadError = {.id = fileId(-1),
1232 .timestampUs = static_cast<uint64_t>(-1),
1233 .block = static_cast<IncFsBlockIndex>(-1),
1234 .errorNo = static_cast<uint32_t>(-1),
1235 .uid = static_cast<IncFsUid>(-1)};
1236 EXPECT_EQ(0, IncFs_GetLastReadError(control_, &lastReadError));
1237 EXPECT_EQ(id, lastReadError.id);
1238 EXPECT_TRUE(lastReadError.timestampUs > 0);
1239 EXPECT_EQ(0, (int)lastReadError.block);
1240 EXPECT_EQ(-ETIME, (int)lastReadError.errorNo);
1241 EXPECT_EQ((int)getuid(), (int)lastReadError.uid);
1242
1243 IncFsMetrics incfsMetrics = {10, 10, 10, 10, 10, 10, 10, 10, 10};
1244 EXPECT_EQ(0, IncFs_GetMetrics(metrics_key_.c_str(), &incfsMetrics));
1245 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMin);
1246 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMinUs);
1247 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPending);
1248 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPendingUs);
1249 EXPECT_EQ(0, (int)incfsMetrics.readsFailedHashVerification);
1250 EXPECT_EQ(0, (int)incfsMetrics.readsFailedOther);
1251 EXPECT_EQ(1, (int)incfsMetrics.readsFailedTimedOut);
1252 }
1253
TEST_F(IncFsGetMetricsTest,MetricsWithHashFailure)1254 TEST_F(IncFsGetMetricsTest, MetricsWithHashFailure) {
1255 if (!(features() & Features::v2)) {
1256 GTEST_SKIP() << "test not supported: IncFS is too old";
1257 return;
1258 }
1259 auto size = makeFileWithHash(1);
1260 ASSERT_GT(size, 0);
1261 // Make data and hash mismatch
1262 const auto id = fileId(1);
1263 char data[INCFS_DATA_FILE_BLOCK_SIZE]{static_cast<char>(-1)};
1264 char hashData[INCFS_DATA_FILE_BLOCK_SIZE]{};
1265 auto wfd = openForSpecialOps(control_, id);
1266 ASSERT_GE(wfd.get(), 0);
1267 DataBlock blocks[] = {{
1268 .fileFd = wfd.get(),
1269 .pageIndex = 0,
1270 .compression = INCFS_COMPRESSION_KIND_NONE,
1271 .dataSize = INCFS_DATA_FILE_BLOCK_SIZE,
1272 .data = data,
1273 },
1274 {
1275 .fileFd = wfd.get(),
1276 // first hash page
1277 .pageIndex = 0,
1278 .compression = INCFS_COMPRESSION_KIND_NONE,
1279 .dataSize = INCFS_DATA_FILE_BLOCK_SIZE,
1280 .kind = INCFS_BLOCK_KIND_HASH,
1281 .data = hashData,
1282 },
1283 {
1284 .fileFd = wfd.get(),
1285 .pageIndex = 2,
1286 .compression = INCFS_COMPRESSION_KIND_NONE,
1287 .dataSize = INCFS_DATA_FILE_BLOCK_SIZE,
1288 .kind = INCFS_BLOCK_KIND_HASH,
1289 .data = hashData,
1290 }};
1291 ASSERT_EQ((int)std::size(blocks), writeBlocks({blocks, std::size(blocks)}));
1292 const auto file_path = mountPath(test_file_name_);
1293 const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
1294 ASSERT_GE(fd.get(), 0);
1295 // Read should fail at reading the first block due to hash failure
1296 char buf[INCFS_DATA_FILE_BLOCK_SIZE];
1297 EXPECT_FALSE(android::base::ReadFully(fd, buf, sizeof(buf)));
1298 IncFsLastReadError lastReadError = {.id = fileId(-1),
1299 .timestampUs = static_cast<uint64_t>(-1),
1300 .block = static_cast<IncFsBlockIndex>(-1),
1301 .errorNo = static_cast<uint32_t>(-1),
1302 .uid = static_cast<IncFsUid>(-1)};
1303 EXPECT_EQ(0, IncFs_GetLastReadError(control_, &lastReadError));
1304 EXPECT_EQ(0, std::strcmp(lastReadError.id.data, id.data));
1305 EXPECT_TRUE(lastReadError.timestampUs > 0);
1306 EXPECT_EQ(0, (int)lastReadError.block);
1307 EXPECT_EQ(-EBADMSG, (int)lastReadError.errorNo);
1308 EXPECT_EQ((int)getuid(), (int)lastReadError.uid);
1309
1310 IncFsMetrics incfsMetrics = {10, 10, 10, 10, 10, 10, 10, 10, 10};
1311 EXPECT_EQ(0, IncFs_GetMetrics(metrics_key_.c_str(), &incfsMetrics));
1312 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMin);
1313 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMinUs);
1314 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPending);
1315 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPendingUs);
1316 EXPECT_EQ(1, (int)incfsMetrics.readsFailedHashVerification);
1317 EXPECT_EQ(0, (int)incfsMetrics.readsFailedOther);
1318 EXPECT_EQ(0, (int)incfsMetrics.readsFailedTimedOut);
1319 }
1320
TEST_F(IncFsGetMetricsTest,MetricsWithReadsDelayed)1321 TEST_F(IncFsGetMetricsTest, MetricsWithReadsDelayed) {
1322 if (!(features() & Features::v2)) {
1323 GTEST_SKIP() << "test not supported: IncFS is too old";
1324 return;
1325 }
1326 const auto id = fileId(1);
1327 int testFileSize = INCFS_DATA_FILE_BLOCK_SIZE;
1328 int waitBeforeWriteUs = 10000;
1329 ASSERT_EQ(0, makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = testFileSize}));
1330 std::thread wait_before_write_thread([&]() {
1331 std::vector<ReadInfoWithUid> pending_reads;
1332 ASSERT_EQ(WaitResult::HaveData,
1333 waitForPendingReads(control_, std::chrono::seconds(1), &pending_reads));
1334 // Additional wait is needed for the kernel jiffies counter to increment
1335 usleep(waitBeforeWriteUs);
1336 auto fd = openForSpecialOps(control_, fileId(1));
1337 ASSERT_GE(fd.get(), 0);
1338 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
1339 auto block = DataBlock{
1340 .fileFd = fd.get(),
1341 .pageIndex = 0,
1342 .compression = INCFS_COMPRESSION_KIND_NONE,
1343 .dataSize = (uint32_t)data.size(),
1344 .data = data.data(),
1345 };
1346 ASSERT_EQ(1, writeBlocks({&block, 1}));
1347 });
1348
1349 const auto file_path = mountPath(test_file_name_);
1350 const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
1351 ASSERT_GE(fd.get(), 0);
1352 char buf[testFileSize];
1353 EXPECT_TRUE(android::base::ReadFully(fd, buf, sizeof(buf)));
1354 wait_before_write_thread.join();
1355
1356 IncFsLastReadError lastReadError = {.id = fileId(-1), 1, 1, 1, 1};
1357 EXPECT_EQ(0, IncFs_GetLastReadError(control_, &lastReadError));
1358 EXPECT_EQ(FileId{}, lastReadError.id);
1359 EXPECT_EQ(0, (int)lastReadError.timestampUs);
1360 EXPECT_EQ(0, (int)lastReadError.block);
1361 EXPECT_EQ(0, (int)lastReadError.errorNo);
1362 EXPECT_EQ(0, (int)lastReadError.uid);
1363
1364 IncFsMetrics incfsMetrics = {10, 10, 10, 10, 10, 10, 10, 10, 10};
1365 EXPECT_EQ(0, IncFs_GetMetrics(metrics_key_.c_str(), &incfsMetrics));
1366 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMin);
1367 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedMinUs);
1368 EXPECT_EQ(1, (int)incfsMetrics.readsDelayedPending);
1369 EXPECT_TRUE((int)incfsMetrics.readsDelayedPendingUs > 0);
1370 EXPECT_EQ(0, (int)incfsMetrics.readsFailedHashVerification);
1371 EXPECT_EQ(0, (int)incfsMetrics.readsFailedOther);
1372 EXPECT_EQ(0, (int)incfsMetrics.readsFailedTimedOut);
1373 }
1374
TEST_F(IncFsGetMetricsTest,MetricsWithReadsDelayedPerUidTimeout)1375 TEST_F(IncFsGetMetricsTest, MetricsWithReadsDelayedPerUidTimeout) {
1376 if (!(features() & Features::v2)) {
1377 GTEST_SKIP() << "test not supported: IncFS is too old";
1378 return;
1379 }
1380 const auto id = fileId(1);
1381 int testFileSize = INCFS_DATA_FILE_BLOCK_SIZE;
1382 ASSERT_EQ(0, makeFile(control_, mountPath(test_file_name_), 0555, id, {.size = testFileSize}));
1383
1384 auto fdToFill = openForSpecialOps(control_, fileId(1));
1385 ASSERT_GE(fdToFill.get(), 0);
1386 std::vector<char> data(INCFS_DATA_FILE_BLOCK_SIZE);
1387 auto block = DataBlock{
1388 .fileFd = fdToFill.get(),
1389 .pageIndex = 0,
1390 .compression = INCFS_COMPRESSION_KIND_NONE,
1391 .dataSize = (uint32_t)data.size(),
1392 .data = data.data(),
1393 };
1394 ASSERT_EQ(1, writeBlocks({&block, 1}));
1395
1396 // Set per-uid read timeout then read
1397 uint32_t readTimeoutUs = 1000000;
1398 IncFsUidReadTimeouts timeouts[1] = {
1399 {static_cast<IncFsUid>(getuid()), readTimeoutUs, readTimeoutUs, readTimeoutUs}};
1400 ASSERT_EQ(0, IncFs_SetUidReadTimeouts(control_, timeouts, std::size(timeouts)));
1401 const auto file_path = mountPath(test_file_name_);
1402 const android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
1403 char buf[testFileSize];
1404 ASSERT_GE(fd.get(), 0);
1405 ASSERT_TRUE(android::base::ReadFully(fd, buf, sizeof(buf)));
1406
1407 IncFsLastReadError lastReadError = {.id = fileId(-1), 1, 1, 1, 1};
1408 EXPECT_EQ(0, IncFs_GetLastReadError(control_, &lastReadError));
1409 EXPECT_EQ(FileId{}, lastReadError.id);
1410 EXPECT_EQ(0, (int)lastReadError.timestampUs);
1411 EXPECT_EQ(0, (int)lastReadError.block);
1412 EXPECT_EQ(0, (int)lastReadError.errorNo);
1413 EXPECT_EQ(0, (int)lastReadError.uid);
1414
1415 IncFsMetrics incfsMetrics = {10, 10, 10, 10, 10, 10, 10, 10, 10};
1416 EXPECT_EQ(0, IncFs_GetMetrics(metrics_key_.c_str(), &incfsMetrics));
1417 EXPECT_EQ(1, (int)incfsMetrics.readsDelayedMin);
1418 EXPECT_EQ(readTimeoutUs, (uint32_t)incfsMetrics.readsDelayedMinUs);
1419 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPending);
1420 EXPECT_EQ(0, (int)incfsMetrics.readsDelayedPendingUs);
1421 EXPECT_EQ(0, (int)incfsMetrics.readsFailedHashVerification);
1422 EXPECT_EQ(0, (int)incfsMetrics.readsFailedOther);
1423 EXPECT_EQ(0, (int)incfsMetrics.readsFailedTimedOut);
1424 }
1425
operator ==(const BlockCounts & lhs,const BlockCounts & rhs)1426 inline bool operator==(const BlockCounts& lhs, const BlockCounts& rhs) {
1427 return lhs.totalDataBlocks == rhs.totalDataBlocks &&
1428 lhs.filledDataBlocks == rhs.filledDataBlocks &&
1429 lhs.totalHashBlocks == rhs.totalHashBlocks &&
1430 lhs.filledHashBlocks == rhs.filledHashBlocks;
1431 }
1432
TEST_F(IncFsTest,LoadingProgress)1433 TEST_F(IncFsTest, LoadingProgress) {
1434 ASSERT_EQ(0, makeDir(control_, mountPath(test_dir_name_)));
1435
1436 constexpr auto file_size = INCFS_DATA_FILE_BLOCK_SIZE * 2;
1437 constexpr auto mapped_file_offset = file_size / 2;
1438 constexpr auto mapped_file_size = file_size / 3;
1439
1440 const auto file_id = fileId(1);
1441
1442 const auto file_path = mountPath(test_dir_name_, test_file_name_);
1443 ASSERT_FALSE(exists(file_path));
1444 ASSERT_EQ(0,
1445 makeFile(control_, file_path, 0111, file_id,
1446 {.size = file_size, .metadata = metadata("md")}));
1447 struct stat s = {};
1448 ASSERT_EQ(0, stat(file_path.c_str(), &s));
1449 ASSERT_EQ(file_size, (int)s.st_size);
1450
1451 const auto mapped_file_path = mountPath(test_dir_name_, test_mapped_file_name_);
1452 ASSERT_FALSE(exists(mapped_file_path));
1453 ASSERT_EQ(0,
1454 makeMappedFile(control_, mapped_file_path, 0111,
1455 {.sourceId = file_id,
1456 .sourceOffset = mapped_file_offset,
1457 .size = mapped_file_size}));
1458 s = {};
1459 ASSERT_EQ(0, stat(mapped_file_path.c_str(), &s));
1460 ASSERT_EQ(mapped_file_size, (int)s.st_size);
1461
1462 // Check fully loaded first.
1463 ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_path));
1464 ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_id));
1465 ASSERT_EQ((LoadingState)-ENOTSUP, isFullyLoaded(control_, mapped_file_path));
1466
1467 // Next is loading progress.
1468 ASSERT_EQ(BlockCounts{.totalDataBlocks = 2}, *getBlockCount(control_, file_path));
1469 ASSERT_EQ(BlockCounts{.totalDataBlocks = 2}, *getBlockCount(control_, file_id));
1470 ASSERT_FALSE(getBlockCount(control_, mapped_file_path));
1471
1472 // Now write a page #0.
1473 ASSERT_NO_FATAL_FAILURE(writeBlock(0));
1474
1475 // Recheck everything.
1476 ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_path));
1477 ASSERT_EQ(LoadingState::MissingBlocks, isFullyLoaded(control_, file_id));
1478 ASSERT_EQ((LoadingState)-ENOTSUP, isFullyLoaded(control_, mapped_file_path));
1479
1480 BlockCounts onePage{.totalDataBlocks = 2, .filledDataBlocks = 1};
1481 ASSERT_EQ(onePage, *getBlockCount(control_, file_path));
1482 ASSERT_EQ(onePage, *getBlockCount(control_, file_id));
1483 ASSERT_FALSE(getBlockCount(control_, mapped_file_path));
1484
1485 // Now write a page #1.
1486 ASSERT_NO_FATAL_FAILURE(writeBlock(1));
1487
1488 // Check for fully loaded.
1489 ASSERT_EQ(LoadingState::Full, isFullyLoaded(control_, file_path));
1490 ASSERT_EQ(LoadingState::Full, isFullyLoaded(control_, file_id));
1491 ASSERT_EQ((LoadingState)-ENOTSUP, isFullyLoaded(control_, mapped_file_path));
1492
1493 BlockCounts twoPages{.totalDataBlocks = 2, .filledDataBlocks = 2};
1494 ASSERT_EQ(twoPages, *getBlockCount(control_, file_path));
1495 ASSERT_EQ(twoPages, *getBlockCount(control_, file_id));
1496 ASSERT_FALSE(getBlockCount(control_, mapped_file_path));
1497 }
1498