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