ext2_filesystem_unittest.cc revision 2b19cfbcdb1aa8c5d1f338d19312fe14b6734bd5
1// Copyright 2015 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/payload_generator/ext2_filesystem.h"
6
7#include <unistd.h>
8
9#include <map>
10#include <set>
11#include <string>
12#include <vector>
13
14#include <base/format_macros.h>
15#include <base/logging.h>
16#include <base/strings/stringprintf.h>
17#include <base/strings/string_number_conversions.h>
18#include <base/strings/string_util.h>
19#include <gtest/gtest.h>
20
21#include "update_engine/payload_generator/extent_utils.h"
22#include "update_engine/test_utils.h"
23#include "update_engine/utils.h"
24
25using chromeos_update_engine::test_utils::System;
26using std::map;
27using std::set;
28using std::string;
29using std::unique_ptr;
30using std::vector;
31
32namespace chromeos_update_engine {
33
34namespace {
35
36uint64_t kDefaultFilesystemSize = 4 * 1024 * 1024;
37size_t kDefaultFilesystemBlockCount = 1024;
38size_t kDefaultFilesystemBlockSize = 4096;
39
40// Checks that all the blocks in |extents| are in the range [0, total_blocks).
41void ExpectBlocksInRange(const vector<Extent>& extents, uint64_t total_blocks) {
42  for (const Extent& extent : extents) {
43    EXPECT_LE(0, extent.start_block());
44    EXPECT_LE(extent.start_block() + extent.num_blocks(), total_blocks);
45  }
46}
47
48}  // namespace
49
50
51class Ext2FilesystemTest : public ::testing::Test {
52 protected:
53  void SetUp() override {
54    ASSERT_TRUE(utils::MakeTempFile("Ext2FilesystemTest-XXXXXX",
55                                    &fs_filename_, nullptr));
56    ASSERT_EQ(0, truncate(fs_filename_.c_str(), kDefaultFilesystemSize));
57  }
58
59  void TearDown() override {
60    unlink(fs_filename_.c_str());
61  }
62
63  string fs_filename_;
64};
65
66TEST_F(Ext2FilesystemTest, InvalidFilesystem) {
67  unique_ptr<Ext2Filesystem> fs = Ext2Filesystem::CreateFromFile(fs_filename_);
68  ASSERT_EQ(nullptr, fs.get());
69
70  fs = Ext2Filesystem::CreateFromFile("/path/to/invalid/file");
71  ASSERT_EQ(nullptr, fs.get());
72}
73
74TEST_F(Ext2FilesystemTest, EmptyFilesystem) {
75  EXPECT_EQ(0, System(base::StringPrintf(
76      "/sbin/mkfs.ext2 -q -b %" PRIuS " -F %s",
77      kDefaultFilesystemBlockSize, fs_filename_.c_str())));
78  unique_ptr<Ext2Filesystem> fs = Ext2Filesystem::CreateFromFile(fs_filename_);
79
80  ASSERT_NE(nullptr, fs.get());
81  EXPECT_EQ(kDefaultFilesystemBlockCount, fs->GetBlockCount());
82  EXPECT_EQ(kDefaultFilesystemBlockSize, fs->GetBlockSize());
83
84  vector<FilesystemInterface::File> files;
85  EXPECT_TRUE(fs->GetFiles(&files));
86
87  map<string, FilesystemInterface::File> map_files;
88  for (const auto& file : files) {
89    EXPECT_EQ(map_files.end(), map_files.find(file.name))
90        << "File " << file.name << " repeated in the list.";
91    map_files[file.name] = file;
92    ExpectBlocksInRange(file.extents, fs->GetBlockCount());
93  }
94  EXPECT_EQ(2, map_files["/"].file_stat.st_ino);
95  EXPECT_FALSE(map_files["<free-space>"].extents.empty());
96}
97
98// This test parses the sample images generated during build time with the
99// "generate_image.sh" script. The expected conditions of each file in these
100// images is encoded in the file name, as defined in the mentioned script.
101TEST_F(Ext2FilesystemTest, ParseGeneratedImages) {
102  const vector<string> kGeneratedImages = {
103      "disk_ext2_1k.img",
104      "disk_ext2_4k.img" };
105  base::FilePath build_path = test_utils::GetBuildArtifactsPath().Append("gen");
106  for (const string& fs_name : kGeneratedImages) {
107    LOG(INFO) << "Testing " << fs_name;
108    unique_ptr<Ext2Filesystem> fs = Ext2Filesystem::CreateFromFile(
109        build_path.Append(fs_name).value());
110    ASSERT_NE(nullptr, fs.get());
111
112    vector<FilesystemInterface::File> files;
113    map<string, FilesystemInterface::File> map_files;
114    set<string> filenames;
115    EXPECT_TRUE(fs->GetFiles(&files));
116    for (const auto& file : files) {
117      // Check no repeated files. We should parse hard-links with two different
118      // names.
119      EXPECT_EQ(map_files.end(), map_files.find(file.name))
120          << "File " << file.name << " repeated in the list.";
121      map_files[file.name] = file;
122      filenames.insert(file.name);
123      ExpectBlocksInRange(file.extents, fs->GetBlockCount());
124    }
125
126    // Check that all the files are parsed, and the /removed file should not
127    // be included in the list.
128    set<string> kExpectedFiles = {
129        "/",
130        "/dir1",
131        "/dir1/file",
132        "/dir1/dir2",
133        "/dir1/dir2/file",
134        "/dir1/dir2/dir1",
135        "/empty-file",
136        "/link-hard-regular-16k",
137        "/link-long_symlink",
138        "/link-short_symlink",
139        "/lost+found",
140        "/regular-small",
141        "/regular-16k",
142        "/regular-32k-zeros",
143        "/regular-with_net_cap",
144        "/sparse_empty-10k",
145        "/sparse_empty-2blocks",
146        "/sparse-10000blocks",
147        "/sparse-16k-last_block",
148        "/sparse-16k-first_block",
149        "/sparse-16k-holes",
150        "<inode-blocks>",
151        "<free-space>",
152        "<group-descriptors>",
153    };
154    EXPECT_EQ(kExpectedFiles, filenames);
155
156    FilesystemInterface::File file;
157
158    // Small symlinks don't actually have data blocks.
159    EXPECT_TRUE(map_files["/link-short_symlink"].extents.empty());
160    EXPECT_EQ(1, BlocksInExtents(map_files["/link-long_symlink"].extents));
161
162    // Hard-links report the same list of blocks.
163    EXPECT_EQ(map_files["/link-hard-regular-16k"].extents,
164              map_files["/regular-16k"].extents);
165    EXPECT_FALSE(map_files["/regular-16k"].extents.empty());
166
167    // The number of blocks in these files doesn't depend on the
168    // block size.
169    EXPECT_TRUE(map_files["/empty-file"].extents.empty());
170    EXPECT_EQ(1, BlocksInExtents(map_files["/regular-small"].extents));
171    EXPECT_EQ(1, BlocksInExtents(map_files["/regular-with_net_cap"].extents));
172    EXPECT_TRUE(map_files["/sparse_empty-10k"].extents.empty());
173    EXPECT_TRUE(map_files["/sparse_empty-2blocks"].extents.empty());
174    EXPECT_EQ(1, BlocksInExtents(map_files["/sparse-16k-last_block"].extents));
175    EXPECT_EQ(1, BlocksInExtents(map_files["/sparse-16k-first_block"].extents));
176    EXPECT_EQ(2, BlocksInExtents(map_files["/sparse-16k-holes"].extents));
177  }
178}
179
180}  // namespace chromeos_update_engine
181