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