squashfs_filesystem_unittest.cc revision 3cd4df127c29eb90c0e203373edfbc9534c79169
1// 2// Copyright (C) 2017 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 "update_engine/payload_generator/squashfs_filesystem.h" 18 19#include <unistd.h> 20 21#include <algorithm> 22#include <map> 23#include <set> 24#include <string> 25#include <vector> 26 27#include <base/format_macros.h> 28#include <base/logging.h> 29#include <base/strings/string_number_conversions.h> 30#include <base/strings/string_util.h> 31#include <base/strings/stringprintf.h> 32#include <gtest/gtest.h> 33 34#include "update_engine/common/test_utils.h" 35#include "update_engine/common/utils.h" 36#include "update_engine/payload_generator/extent_utils.h" 37 38namespace chromeos_update_engine { 39 40using std::map; 41using std::set; 42using std::string; 43using std::unique_ptr; 44using std::vector; 45 46using test_utils::GetBuildArtifactsPath; 47 48namespace { 49 50constexpr uint64_t kTestBlockSize = 4096; 51constexpr uint64_t kTestSqfsBlockSize = 1 << 15; 52 53// Checks that all the blocks in |extents| are in the range [0, total_blocks). 54void ExpectBlocksInRange(const vector<Extent>& extents, uint64_t total_blocks) { 55 for (const Extent& extent : extents) { 56 EXPECT_LE(0U, extent.start_block()); 57 EXPECT_LE(extent.start_block() + extent.num_blocks(), total_blocks); 58 } 59} 60 61SquashfsFilesystem::SquashfsHeader GetSimpleHeader() { 62 // These properties are enough for now. Add more as needed. 63 return { 64 .magic = 0x73717368, 65 .block_size = kTestSqfsBlockSize, 66 .compression_type = 1, // For gzip. 67 .major_version = 4, 68 }; 69} 70 71} // namespace 72 73class SquashfsFilesystemTest : public ::testing::Test { 74 public: 75 void CheckSquashfs(const unique_ptr<SquashfsFilesystem>& fs) { 76 ASSERT_TRUE(fs); 77 EXPECT_EQ(kTestBlockSize, fs->GetBlockSize()); 78 79 vector<FilesystemInterface::File> files; 80 ASSERT_TRUE(fs->GetFiles(&files)); 81 82 map<string, FilesystemInterface::File> map_files; 83 for (const auto& file : files) { 84 EXPECT_EQ(map_files.end(), map_files.find(file.name)) 85 << "File " << file.name << " repeated in the list."; 86 map_files[file.name] = file; 87 ExpectBlocksInRange(file.extents, fs->GetBlockCount()); 88 } 89 90 // Checking the sortness. 91 EXPECT_TRUE(std::is_sorted(files.begin(), 92 files.end(), 93 [](const FilesystemInterface::File& a, 94 const FilesystemInterface::File& b) { 95 return a.extents[0].start_block() < 96 b.extents[0].start_block(); 97 })); 98 99 auto overlap_check = [](const FilesystemInterface::File& a, 100 const FilesystemInterface::File& b) { 101 // Return true if overlapping. 102 return a.extents[0].start_block() + a.extents[0].num_blocks() > 103 b.extents[0].start_block(); 104 }; 105 // Check files are not overlapping. 106 EXPECT_EQ(std::adjacent_find(files.begin(), files.end(), overlap_check), 107 files.end()); 108 } 109}; 110 111TEST_F(SquashfsFilesystemTest, EmptyFilesystemTest) { 112 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFile( 113 GetBuildArtifactsPath("gen/disk_sqfs_empty.img"), true); 114 CheckSquashfs(fs); 115 116 // Even an empty squashfs filesystem is rounded up to 4K. 117 EXPECT_EQ(4096 / kTestBlockSize, fs->GetBlockCount()); 118 119 vector<FilesystemInterface::File> files; 120 ASSERT_TRUE(fs->GetFiles(&files)); 121 ASSERT_EQ(files.size(), 1u); 122 123 FilesystemInterface::File file; 124 file.name = "<metadata-0>"; 125 file.extents.emplace_back(); 126 file.extents[0].set_start_block(0); 127 file.extents[0].set_num_blocks(1); 128 EXPECT_EQ(files[0].name, file.name); 129 EXPECT_EQ(files[0].extents, file.extents); 130} 131 132TEST_F(SquashfsFilesystemTest, DefaultFilesystemTest) { 133 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFile( 134 GetBuildArtifactsPath("gen/disk_sqfs_default.img"), true); 135 CheckSquashfs(fs); 136 137 vector<FilesystemInterface::File> files; 138 ASSERT_TRUE(fs->GetFiles(&files)); 139 ASSERT_EQ(files.size(), 1u); 140 141 FilesystemInterface::File file; 142 file.name = "<fragment-0>"; 143 file.extents.emplace_back(); 144 file.extents[0].set_start_block(0); 145 file.extents[0].set_num_blocks(1); 146 EXPECT_EQ(files[0].name, file.name); 147 EXPECT_EQ(files[0].extents, file.extents); 148} 149 150TEST_F(SquashfsFilesystemTest, SimpleFileMapTest) { 151 string filemap = R"(dir1/file1 96 4000 152 dir1/file2 4096 100)"; 153 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFileMap( 154 filemap, kTestBlockSize * 2, GetSimpleHeader()); 155 CheckSquashfs(fs); 156 157 vector<FilesystemInterface::File> files; 158 ASSERT_TRUE(fs->GetFiles(&files)); 159 EXPECT_EQ(files.size(), 2u); 160} 161 162TEST_F(SquashfsFilesystemTest, FileMapZeroSizeFileTest) { 163 // The second file's size is zero. 164 string filemap = R"(dir1/file1 96 4000 165 dir1/file2 4096 166 dir1/file3 4096 100)"; 167 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFileMap( 168 filemap, kTestBlockSize * 2, GetSimpleHeader()); 169 CheckSquashfs(fs); 170 171 vector<FilesystemInterface::File> files; 172 ASSERT_TRUE(fs->GetFiles(&files)); 173 // The second and third files are removed. The file with size zero is removed. 174 EXPECT_EQ(files.size(), 2u); 175} 176 177// Testing the compressed bit. 178TEST_F(SquashfsFilesystemTest, CompressedBitTest) { 179 string filemap = "dir1/file1 0 " + std::to_string(4000 | (1 << 24)) + "\n"; 180 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFileMap( 181 filemap, kTestBlockSize, GetSimpleHeader()); 182 CheckSquashfs(fs); 183 184 vector<FilesystemInterface::File> files; 185 ASSERT_TRUE(fs->GetFiles(&files)); 186 ASSERT_EQ(files.size(), 1u); 187 EXPECT_EQ(files[0].extents[0].num_blocks(), 1u); 188} 189 190// Test overlap. 191TEST_F(SquashfsFilesystemTest, OverlapingFiles1Test) { 192 string filemap = R"(file1 0 6000 193 file2 5000 5000)"; 194 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFileMap( 195 filemap, kTestBlockSize * 3, GetSimpleHeader()); 196 CheckSquashfs(fs); 197 198 vector<FilesystemInterface::File> files; 199 ASSERT_TRUE(fs->GetFiles(&files)); 200 ASSERT_EQ(files.size(), 2u); 201 EXPECT_EQ(files[0].extents[0].num_blocks(), 1u); 202 EXPECT_EQ(files[1].extents[0].num_blocks(), 2u); 203} 204 205// Test overlap, first inside second. 206TEST_F(SquashfsFilesystemTest, OverlapingFiles2Test) { 207 string filemap = R"(file1 0 4000 208 file2 0 6000)"; 209 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFileMap( 210 filemap, kTestBlockSize * 2, GetSimpleHeader()); 211 CheckSquashfs(fs); 212 213 vector<FilesystemInterface::File> files; 214 ASSERT_TRUE(fs->GetFiles(&files)); 215 ASSERT_EQ(files.size(), 1u); 216 EXPECT_EQ(files[0].name, "file2"); 217 EXPECT_EQ(files[0].extents[0].num_blocks(), 2u); 218} 219 220// Test overlap, second inside first. 221TEST_F(SquashfsFilesystemTest, OverlapingFiles3Test) { 222 string filemap = R"(file1 0 8000 223 file2 100 100)"; 224 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFileMap( 225 filemap, kTestBlockSize * 2, GetSimpleHeader()); 226 CheckSquashfs(fs); 227 228 vector<FilesystemInterface::File> files; 229 ASSERT_TRUE(fs->GetFiles(&files)); 230 ASSERT_EQ(files.size(), 1u); 231 EXPECT_EQ(files[0].name, "file1"); 232 EXPECT_EQ(files[0].extents[0].num_blocks(), 2u); 233} 234 235// Fail a line with only one argument. 236TEST_F(SquashfsFilesystemTest, FailOnlyFileNameTest) { 237 string filemap = "dir1/file1\n"; 238 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFileMap( 239 filemap, kTestBlockSize, GetSimpleHeader()); 240 EXPECT_FALSE(fs); 241} 242 243// Fail a line with space separated filen name 244TEST_F(SquashfsFilesystemTest, FailSpaceInFileNameTest) { 245 string filemap = "dir1 file1 0 10\n"; 246 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFileMap( 247 filemap, kTestBlockSize, GetSimpleHeader()); 248 EXPECT_FALSE(fs); 249} 250 251// Fail empty line 252TEST_F(SquashfsFilesystemTest, FailEmptyLineTest) { 253 // The second file's size is zero. 254 string filemap = R"( 255 /t 256 dir1/file3 4096 100)"; 257 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFileMap( 258 filemap, kTestBlockSize * 2, GetSimpleHeader()); 259 EXPECT_FALSE(fs); 260} 261 262// Fail on bad magic or major 263TEST_F(SquashfsFilesystemTest, FailBadMagicOrMajorTest) { 264 string filemap = "dir1/file1 0 10\n"; 265 auto header = GetSimpleHeader(); 266 header.magic = 1; 267 EXPECT_FALSE( 268 SquashfsFilesystem::CreateFromFileMap(filemap, kTestBlockSize, header)); 269 270 header = GetSimpleHeader(); 271 header.major_version = 3; 272 EXPECT_FALSE( 273 SquashfsFilesystem::CreateFromFileMap(filemap, kTestBlockSize, header)); 274} 275 276// Fail size with larger than block_size 277TEST_F(SquashfsFilesystemTest, FailLargerThanBlockSizeTest) { 278 string filemap = "file1 0 " + std::to_string(kTestSqfsBlockSize + 1) + "\n"; 279 unique_ptr<SquashfsFilesystem> fs = SquashfsFilesystem::CreateFromFileMap( 280 filemap, kTestBlockSize, GetSimpleHeader()); 281 EXPECT_FALSE(fs); 282} 283 284// Test is squashfs image. 285TEST_F(SquashfsFilesystemTest, IsSquashfsImageTest) { 286 // Some sample from a recent squashfs file. 287 brillo::Blob super_block = { 288 0x68, 0x73, 0x71, 0x73, 0x59, 0x05, 0x00, 0x00, 0x09, 0x3a, 0x89, 0x58, 289 0x00, 0x00, 0x02, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x11, 0x00, 290 0xc0, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x89, 0x18, 0xf7, 0x7c, 291 0x00, 0x00, 0x00, 0x00, 0x2e, 0x33, 0xcd, 0x16, 0x00, 0x00, 0x00, 0x00, 292 0x3a, 0x30, 0xcd, 0x16, 0x00, 0x00, 0x00, 0x00, 0x16, 0x33, 0xcd, 0x16, 293 0x00, 0x00, 0x00, 0x00, 0x07, 0x62, 0xcc, 0x16, 0x00, 0x00, 0x00, 0x00, 294 0x77, 0xe6, 0xcc, 0x16, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x25, 0xcd, 0x16, 295 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0xcd, 0x16, 0x00, 0x00, 0x00, 0x00}; 296 297 EXPECT_TRUE(SquashfsFilesystem::IsSquashfsImage(super_block)); 298 299 // Bad magic 300 auto bad_super_block = super_block; 301 bad_super_block[1] = 0x02; 302 EXPECT_FALSE(SquashfsFilesystem::IsSquashfsImage(bad_super_block)); 303 304 // Bad major 305 bad_super_block = super_block; 306 bad_super_block[28] = 0x03; 307 EXPECT_FALSE(SquashfsFilesystem::IsSquashfsImage(bad_super_block)); 308 309 // Small size; 310 bad_super_block = super_block; 311 bad_super_block.resize(10); 312 EXPECT_FALSE(SquashfsFilesystem::IsSquashfsImage(bad_super_block)); 313} 314 315} // namespace chromeos_update_engine 316