1 /*
2 * Copyright (C) 2018 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 <fcntl.h>
18 #include <inttypes.h>
19 #include <linux/limits.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/mount.h>
24 #include <sys/stat.h>
25 #include <sys/statvfs.h>
26 #include <sys/types.h>
27 #include <sys/vfs.h>
28 #include <unistd.h>
29
30 #include <string>
31 #include <utility>
32
33 #include <android-base/file.h>
34 #include <android-base/logging.h>
35 #include <android-base/stringprintf.h>
36 #include <android-base/unique_fd.h>
37 #include <fstab/fstab.h>
38 #include <gtest/gtest.h>
39 #include <libdm/loop_control.h>
40 #include <libfiemap/fiemap_writer.h>
41 #include <libfiemap/split_fiemap_writer.h>
42 #include <libgsi/libgsi.h>
43 #include <storage_literals/storage_literals.h>
44
45 #include "utility.h"
46
47 namespace android {
48 namespace fiemap {
49
50 using namespace std;
51 using namespace std::string_literals;
52 using namespace android::fiemap;
53 using namespace android::storage_literals;
54 using unique_fd = android::base::unique_fd;
55 using LoopDevice = android::dm::LoopDevice;
56
57 std::string gTestDir;
58 uint64_t testfile_size = 536870912; // default of 512MiB
59 size_t gBlockSize = 0;
60
61 class FiemapWriterTest : public ::testing::Test {
62 protected:
SetUp()63 void SetUp() override {
64 const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
65 testfile = gTestDir + "/"s + tinfo->name();
66 }
67
TearDown()68 void TearDown() override { unlink(testfile.c_str()); }
69
70 // name of the file we use for testing
71 std::string testfile;
72 };
73
74 class SplitFiemapTest : public ::testing::Test {
75 protected:
SetUp()76 void SetUp() override {
77 const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
78 testfile = gTestDir + "/"s + tinfo->name();
79 }
80
TearDown()81 void TearDown() override {
82 std::string message;
83 if (!SplitFiemap::RemoveSplitFiles(testfile, &message)) {
84 cerr << "Could not remove all split files: " << message;
85 }
86 }
87
88 // name of the file we use for testing
89 std::string testfile;
90 };
91
TEST_F(FiemapWriterTest,CreateImpossiblyLargeFile)92 TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
93 // Try creating a file of size ~100TB but aligned to
94 // 512 byte to make sure block alignment tests don't
95 // fail.
96 FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1099511627997184);
97 EXPECT_EQ(fptr, nullptr);
98 EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
99 EXPECT_EQ(errno, ENOENT);
100 }
101
TEST_F(FiemapWriterTest,CreateUnalignedFile)102 TEST_F(FiemapWriterTest, CreateUnalignedFile) {
103 // Try creating a file of size 4097 bytes which is guaranteed
104 // to be unaligned to all known block sizes.
105 FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize + 1);
106 ASSERT_NE(fptr, nullptr);
107 ASSERT_EQ(fptr->size(), gBlockSize * 2);
108 }
109
TEST_F(FiemapWriterTest,CheckFilePath)110 TEST_F(FiemapWriterTest, CheckFilePath) {
111 FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
112 ASSERT_NE(fptr, nullptr);
113 EXPECT_EQ(fptr->size(), gBlockSize);
114 EXPECT_EQ(fptr->file_path(), testfile);
115 EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
116 }
117
TEST_F(FiemapWriterTest,CheckFileSize)118 TEST_F(FiemapWriterTest, CheckFileSize) {
119 // Create a large-ish file and test that the expected size matches.
120 FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1024 * 1024 * 16);
121 ASSERT_NE(fptr, nullptr);
122
123 struct stat s;
124 ASSERT_EQ(stat(testfile.c_str(), &s), 0);
125 EXPECT_EQ(static_cast<uint64_t>(s.st_size), fptr->size());
126 }
127
TEST_F(FiemapWriterTest,CheckProgress)128 TEST_F(FiemapWriterTest, CheckProgress) {
129 std::vector<uint64_t> expected;
130 size_t invocations = 0;
131 auto callback = [&](uint64_t done, uint64_t total) -> bool {
132 if (invocations >= expected.size()) {
133 return false;
134 }
135 EXPECT_EQ(done, expected[invocations]);
136 EXPECT_EQ(total, gBlockSize);
137 invocations++;
138 return true;
139 };
140
141 expected.push_back(gBlockSize);
142
143 auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
144 EXPECT_NE(ptr, nullptr);
145 EXPECT_EQ(invocations, expected.size());
146 }
147
TEST_F(FiemapWriterTest,CheckPinning)148 TEST_F(FiemapWriterTest, CheckPinning) {
149 auto ptr = FiemapWriter::Open(testfile, 4096);
150 ASSERT_NE(ptr, nullptr);
151 EXPECT_TRUE(FiemapWriter::HasPinnedExtents(testfile));
152 }
153
TEST_F(FiemapWriterTest,CheckBlockDevicePath)154 TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
155 FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
156 EXPECT_EQ(fptr->size(), gBlockSize);
157 EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
158
159 if (!android::gsi::IsGsiRunning()) {
160 EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
161 }
162 }
163
TEST_F(FiemapWriterTest,CheckFileCreated)164 TEST_F(FiemapWriterTest, CheckFileCreated) {
165 FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);
166 ASSERT_NE(fptr, nullptr);
167 unique_fd fd(open(testfile.c_str(), O_RDONLY));
168 EXPECT_GT(fd, -1);
169 }
170
TEST_F(FiemapWriterTest,CheckFileSizeActual)171 TEST_F(FiemapWriterTest, CheckFileSizeActual) {
172 FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
173 ASSERT_NE(fptr, nullptr);
174
175 struct stat sb;
176 ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
177 EXPECT_GE(sb.st_size, testfile_size);
178 }
179
TEST_F(FiemapWriterTest,CheckFileExtents)180 TEST_F(FiemapWriterTest, CheckFileExtents) {
181 FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
182 ASSERT_NE(fptr, nullptr);
183 EXPECT_GT(fptr->extents().size(), 0);
184 }
185
TEST_F(FiemapWriterTest,ExistingFile)186 TEST_F(FiemapWriterTest, ExistingFile) {
187 // Create the file.
188 { ASSERT_NE(FiemapWriter::Open(testfile, gBlockSize), nullptr); }
189 // Test that we can still open it.
190 {
191 auto ptr = FiemapWriter::Open(testfile, 0, false);
192 ASSERT_NE(ptr, nullptr);
193 EXPECT_GT(ptr->extents().size(), 0);
194 }
195 }
196
TEST_F(FiemapWriterTest,FileDeletedOnError)197 TEST_F(FiemapWriterTest, FileDeletedOnError) {
198 auto callback = [](uint64_t, uint64_t) -> bool { return false; };
199 auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
200 EXPECT_EQ(ptr, nullptr);
201 EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
202 EXPECT_EQ(errno, ENOENT);
203 }
204
TEST_F(FiemapWriterTest,MaxBlockSize)205 TEST_F(FiemapWriterTest, MaxBlockSize) {
206 uint64_t max_piece_size = 0;
207 ASSERT_TRUE(DetermineMaximumFileSize(testfile, &max_piece_size));
208 ASSERT_GT(max_piece_size, 0);
209 }
210
TEST_F(FiemapWriterTest,FibmapBlockAddressing)211 TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
212 FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
213 ASSERT_NE(fptr, nullptr);
214
215 switch (fptr->fs_type()) {
216 case F2FS_SUPER_MAGIC:
217 case EXT4_SUPER_MAGIC:
218 // Skip the test for FIEMAP supported filesystems. This is really
219 // because f2fs/ext4 have caches that seem to defeat reading back
220 // directly from the block device, and writing directly is too
221 // dangerous.
222 std::cout << "Skipping test, filesystem does not use FIBMAP\n";
223 return;
224 }
225
226 bool uses_dm;
227 std::string bdev_path;
228 ASSERT_TRUE(FiemapWriter::GetBlockDeviceForFile(testfile, &bdev_path, &uses_dm));
229
230 if (uses_dm) {
231 // We could use a device-mapper wrapper here to bypass encryption, but
232 // really this test is for FIBMAP correctness on VFAT (where encryption
233 // is never used), so we don't bother.
234 std::cout << "Skipping test, block device is metadata encrypted\n";
235 return;
236 }
237
238 std::string data(fptr->size(), '\0');
239 for (size_t i = 0; i < data.size(); i++) {
240 data[i] = 'A' + static_cast<char>(data.size() % 26);
241 }
242
243 {
244 unique_fd fd(open(testfile.c_str(), O_WRONLY | O_CLOEXEC));
245 ASSERT_GE(fd, 0);
246 ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
247 ASSERT_EQ(fsync(fd), 0);
248 }
249
250 ASSERT_FALSE(fptr->extents().empty());
251 const auto& first_extent = fptr->extents()[0];
252
253 unique_fd bdev(open(fptr->bdev_path().c_str(), O_RDONLY | O_CLOEXEC));
254 ASSERT_GE(bdev, 0);
255
256 off_t where = first_extent.fe_physical;
257 ASSERT_EQ(lseek(bdev, where, SEEK_SET), where);
258
259 // Note: this will fail on encrypted folders.
260 std::string actual(data.size(), '\0');
261 ASSERT_GE(first_extent.fe_length, data.size());
262 ASSERT_TRUE(android::base::ReadFully(bdev, actual.data(), actual.size()));
263 EXPECT_EQ(memcmp(actual.data(), data.data(), data.size()), 0);
264 }
265
TEST_F(FiemapWriterTest,CheckEmptyFile)266 TEST_F(FiemapWriterTest, CheckEmptyFile) {
267 // Can't get any fiemap_extent out of a zero-sized file.
268 FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 0);
269 EXPECT_EQ(fptr, nullptr);
270 EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
271 }
272
TEST_F(SplitFiemapTest,Create)273 TEST_F(SplitFiemapTest, Create) {
274 auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
275 ASSERT_NE(ptr, nullptr);
276
277 auto extents = ptr->extents();
278
279 // Destroy the fiemap, closing file handles. This should not delete them.
280 ptr = nullptr;
281
282 std::vector<std::string> files;
283 ASSERT_TRUE(SplitFiemap::GetSplitFileList(testfile, &files));
284 for (const auto& path : files) {
285 EXPECT_EQ(access(path.c_str(), F_OK), 0);
286 }
287
288 ASSERT_GE(extents.size(), files.size());
289 }
290
TEST_F(SplitFiemapTest,Open)291 TEST_F(SplitFiemapTest, Open) {
292 {
293 auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
294 ASSERT_NE(ptr, nullptr);
295 }
296
297 auto ptr = SplitFiemap::Open(testfile);
298 ASSERT_NE(ptr, nullptr);
299
300 auto extents = ptr->extents();
301 ASSERT_GE(extents.size(), 24);
302 }
303
TEST_F(SplitFiemapTest,DeleteOnFail)304 TEST_F(SplitFiemapTest, DeleteOnFail) {
305 auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 100, 1);
306 ASSERT_EQ(ptr, nullptr);
307
308 std::string first_file = testfile + ".0001";
309 ASSERT_NE(access(first_file.c_str(), F_OK), 0);
310 ASSERT_EQ(errno, ENOENT);
311 ASSERT_NE(access(testfile.c_str(), F_OK), 0);
312 ASSERT_EQ(errno, ENOENT);
313 }
314
TEST_F(SplitFiemapTest,CorruptSplit)315 TEST_F(SplitFiemapTest, CorruptSplit) {
316 unique_fd fd(open(testfile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0700));
317 ASSERT_GE(fd, 0);
318
319 // Make a giant random string.
320 std::vector<char> data;
321 for (size_t i = 0x1; i < 0x7f; i++) {
322 for (size_t j = 0; j < 100; j++) {
323 data.emplace_back(i);
324 }
325 }
326 ASSERT_GT(data.size(), PATH_MAX);
327
328 data.emplace_back('\n');
329
330 ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
331 fd = {};
332
333 ASSERT_TRUE(SplitFiemap::RemoveSplitFiles(testfile));
334 }
335
ReadSplitFiles(const std::string & base_path,size_t num_files)336 static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
337 std::string result;
338 for (int i = 0; i < num_files; i++) {
339 std::string path = base_path + android::base::StringPrintf(".%04d", i);
340 std::string data;
341 if (!android::base::ReadFileToString(path, &data)) {
342 return {};
343 }
344 result += data;
345 }
346 return result;
347 }
348
TEST_F(SplitFiemapTest,WriteWholeFile)349 TEST_F(SplitFiemapTest, WriteWholeFile) {
350 static constexpr size_t kChunkSize = 32768;
351 static constexpr size_t kSize = kChunkSize * 3;
352 auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
353 ASSERT_NE(ptr, nullptr);
354
355 auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
356 for (size_t i = 0; i < kSize / sizeof(int); i++) {
357 buffer[i] = i;
358 }
359 ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
360
361 std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
362 auto actual = ReadSplitFiles(testfile, 3);
363 ASSERT_EQ(expected.size(), actual.size());
364 EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
365 }
366
TEST_F(SplitFiemapTest,WriteFileInChunks1)367 TEST_F(SplitFiemapTest, WriteFileInChunks1) {
368 static constexpr size_t kChunkSize = 32768;
369 static constexpr size_t kSize = kChunkSize * 3;
370 auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
371 ASSERT_NE(ptr, nullptr);
372
373 auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
374 for (size_t i = 0; i < kSize / sizeof(int); i++) {
375 buffer[i] = i;
376 }
377
378 // Write in chunks of 1000 (so some writes straddle the boundary of two
379 // files).
380 size_t bytes_written = 0;
381 while (bytes_written < kSize) {
382 size_t to_write = std::min(kSize - bytes_written, (size_t)1000);
383 char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
384 ASSERT_TRUE(ptr->Write(data, to_write));
385 bytes_written += to_write;
386 }
387
388 std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
389 auto actual = ReadSplitFiles(testfile, 3);
390 ASSERT_EQ(expected.size(), actual.size());
391 EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
392 }
393
TEST_F(SplitFiemapTest,WriteFileInChunks2)394 TEST_F(SplitFiemapTest, WriteFileInChunks2) {
395 static constexpr size_t kChunkSize = 32768;
396 static constexpr size_t kSize = kChunkSize * 3;
397 auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
398 ASSERT_NE(ptr, nullptr);
399
400 auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
401 for (size_t i = 0; i < kSize / sizeof(int); i++) {
402 buffer[i] = i;
403 }
404
405 // Write in chunks of 32KiB so every write is exactly at the end of the
406 // current file.
407 size_t bytes_written = 0;
408 while (bytes_written < kSize) {
409 size_t to_write = std::min(kSize - bytes_written, kChunkSize);
410 char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
411 ASSERT_TRUE(ptr->Write(data, to_write));
412 bytes_written += to_write;
413 }
414
415 std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
416 auto actual = ReadSplitFiles(testfile, 3);
417 ASSERT_EQ(expected.size(), actual.size());
418 EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
419 }
420
TEST_F(SplitFiemapTest,WritePastEnd)421 TEST_F(SplitFiemapTest, WritePastEnd) {
422 static constexpr size_t kChunkSize = 32768;
423 static constexpr size_t kSize = kChunkSize * 3;
424 auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
425 ASSERT_NE(ptr, nullptr);
426
427 auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
428 for (size_t i = 0; i < kSize / sizeof(int); i++) {
429 buffer[i] = i;
430 }
431 ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
432 ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
433 }
434
435 // Get max file size and free space.
GetBigFileLimit(const std::string & mount_point)436 std::pair<uint64_t, uint64_t> GetBigFileLimit(const std::string& mount_point) {
437 struct statvfs fs;
438 if (statvfs(mount_point.c_str(), &fs) < 0) {
439 PLOG(ERROR) << "statfs failed";
440 return {0, 0};
441 }
442
443 auto fs_limit = static_cast<uint64_t>(fs.f_blocks) * (fs.f_bsize - 1);
444 auto fs_free = static_cast<uint64_t>(fs.f_bfree) * fs.f_bsize;
445
446 LOG(INFO) << "Big file limit: " << fs_limit << ", free space: " << fs_free;
447
448 return {fs_limit, fs_free};
449 }
450
451 class FsTest : public ::testing::Test {
452 protected:
453 // 2GB Filesystem and 4k block size by default
454 static constexpr uint64_t block_size = 4096;
455 static constexpr uint64_t fs_size = 64 * 1024 * 1024;
456
SetUp()457 void SetUp() {
458 android::fs_mgr::Fstab fstab;
459 ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
460
461 ASSERT_EQ(access(tmpdir_.path, F_OK), 0);
462 fs_path_ = tmpdir_.path + "/fs_image"s;
463 mntpoint_ = tmpdir_.path + "/mnt_point"s;
464
465 auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
466 ASSERT_NE(entry, nullptr);
467 if (entry->fs_type == "ext4") {
468 SetUpExt4();
469 } else if (entry->fs_type == "f2fs") {
470 SetUpF2fs();
471 } else {
472 FAIL() << "Unrecognized fs_type: " << entry->fs_type;
473 }
474 }
475
SetUpExt4()476 void SetUpExt4() {
477 uint64_t count = fs_size / block_size;
478 std::string dd_cmd =
479 ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
480 " count=%" PRIu64 " > /dev/null 2>&1",
481 fs_path_.c_str(), block_size, count);
482 std::string mkfs_cmd =
483 ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path_.c_str());
484 // create mount point
485 ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);
486 // create file for the file system
487 int ret = system(dd_cmd.c_str());
488 ASSERT_EQ(ret, 0);
489 // Get and attach a loop device to the filesystem we created
490 LoopDevice loop_dev(fs_path_, 10s);
491 ASSERT_TRUE(loop_dev.valid());
492 // create file system
493 ret = system(mkfs_cmd.c_str());
494 ASSERT_EQ(ret, 0);
495
496 // mount the file system
497 ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "ext4", 0, nullptr), 0);
498 }
499
SetUpF2fs()500 void SetUpF2fs() {
501 uint64_t count = fs_size / block_size;
502 std::string dd_cmd =
503 ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
504 " count=%" PRIu64 " > /dev/null 2>&1",
505 fs_path_.c_str(), block_size, count);
506 std::string mkfs_cmd =
507 ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path_.c_str());
508 // create mount point
509 ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);
510 // create file for the file system
511 int ret = system(dd_cmd.c_str());
512 ASSERT_EQ(ret, 0);
513 // Get and attach a loop device to the filesystem we created
514 LoopDevice loop_dev(fs_path_, 10s);
515 ASSERT_TRUE(loop_dev.valid());
516 // create file system
517 ret = system(mkfs_cmd.c_str());
518 ASSERT_EQ(ret, 0);
519
520 // mount the file system
521 ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "f2fs", 0, nullptr), 0);
522 }
523
TearDown()524 void TearDown() override {
525 umount(mntpoint_.c_str());
526 rmdir(mntpoint_.c_str());
527 unlink(fs_path_.c_str());
528 }
529
530 TemporaryDir tmpdir_;
531 std::string mntpoint_;
532 std::string fs_path_;
533 };
534
TEST_F(FsTest,LowSpaceError)535 TEST_F(FsTest, LowSpaceError) {
536 auto limits = GetBigFileLimit(mntpoint_);
537 ASSERT_GE(limits.first, 0);
538
539 FiemapUniquePtr ptr;
540
541 auto test_file = mntpoint_ + "/big_file";
542 auto status = FiemapWriter::Open(test_file, limits.first, &ptr);
543 ASSERT_FALSE(status.is_ok());
544 ASSERT_EQ(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);
545
546 // Also test for EFBIG.
547 status = FiemapWriter::Open(test_file, 16_TiB, &ptr);
548 ASSERT_FALSE(status.is_ok());
549 ASSERT_NE(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);
550 }
551
DetermineBlockSize()552 bool DetermineBlockSize() {
553 struct statfs s;
554 if (statfs(gTestDir.c_str(), &s)) {
555 std::cerr << "Could not call statfs: " << strerror(errno) << "\n";
556 return false;
557 }
558 if (!s.f_bsize) {
559 std::cerr << "Invalid block size: " << s.f_bsize << "\n";
560 return false;
561 }
562
563 gBlockSize = s.f_bsize;
564 return true;
565 }
566
567 } // namespace fiemap
568 } // namespace android
569
570 using namespace android::fiemap;
571
main(int argc,char ** argv)572 int main(int argc, char** argv) {
573 ::testing::InitGoogleTest(&argc, argv);
574 if (argc > 1 && argv[1] == "-h"s) {
575 cerr << "Usage: [test_dir] [file_size]\n";
576 cerr << "\n";
577 cerr << "Note: test_dir must be a writable, unencrypted directory.\n";
578 exit(EXIT_FAILURE);
579 }
580 ::android::base::InitLogging(argv, ::android::base::StderrLogger);
581
582 std::string root_dir = "/data/local/unencrypted";
583 if (access(root_dir.c_str(), F_OK)) {
584 root_dir = "/data";
585 }
586
587 std::string tempdir = root_dir + "/XXXXXX"s;
588 if (!mkdtemp(tempdir.data())) {
589 cerr << "unable to create tempdir on " << root_dir << "\n";
590 exit(EXIT_FAILURE);
591 }
592 if (!android::base::Realpath(tempdir, &gTestDir)) {
593 cerr << "unable to find realpath for " << tempdir;
594 exit(EXIT_FAILURE);
595 }
596
597 if (argc > 2) {
598 testfile_size = strtoull(argv[2], NULL, 0);
599 if (testfile_size == ULLONG_MAX) {
600 testfile_size = 512 * 1024 * 1024;
601 }
602 }
603
604 if (!DetermineBlockSize()) {
605 exit(EXIT_FAILURE);
606 }
607
608 auto result = RUN_ALL_TESTS();
609
610 std::string cmd = "rm -rf " + gTestDir;
611 system(cmd.c_str());
612
613 return result;
614 }
615