1 // 2 // Copyright (C) 2015 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/ext2_filesystem.h" 18 19 #include <et/com_err.h> 20 #if defined(__clang__) 21 // TODO(*): Remove these pragmas when b/35721782 is fixed. 22 #pragma clang diagnostic push 23 #pragma clang diagnostic ignored "-Wmacro-redefined" 24 #endif 25 #include <ext2fs/ext2_io.h> 26 #include <ext2fs/ext2fs.h> 27 #if defined(__clang__) 28 #pragma clang diagnostic pop 29 #endif 30 31 #include <map> 32 #include <set> 33 34 #include <base/logging.h> 35 #include <base/strings/stringprintf.h> 36 37 #include "update_engine/common/utils.h" 38 #include "update_engine/payload_generator/extent_ranges.h" 39 #include "update_engine/payload_generator/extent_utils.h" 40 #include "update_engine/update_metadata.pb.h" 41 42 using std::set; 43 using std::string; 44 using std::unique_ptr; 45 using std::vector; 46 47 namespace chromeos_update_engine { 48 49 namespace { 50 // Processes all blocks belonging to an inode and adds them to the extent list. 51 // This function should match the prototype expected by ext2fs_block_iterate2(). 52 int ProcessInodeAllBlocks(ext2_filsys fs, 53 blk_t* blocknr, 54 e2_blkcnt_t blockcnt, 55 blk_t ref_blk, 56 int ref_offset, 57 void* priv) { 58 vector<Extent>* extents = static_cast<vector<Extent>*>(priv); 59 AppendBlockToExtents(extents, *blocknr); 60 return 0; 61 } 62 63 // Processes only indirect, double indirect or triple indirect metadata 64 // blocks belonging to an inode. This function should match the prototype of 65 // ext2fs_block_iterate2(). 66 int AddMetadataBlocks(ext2_filsys fs, 67 blk_t* blocknr, 68 e2_blkcnt_t blockcnt, 69 blk_t ref_blk, 70 int ref_offset, 71 void* priv) { 72 set<uint64_t>* blocks = static_cast<set<uint64_t>*>(priv); 73 // If |blockcnt| is non-negative, |blocknr| points to the physical block 74 // number. 75 // If |blockcnt| is negative, it is one of the values: BLOCK_COUNT_IND, 76 // BLOCK_COUNT_DIND, BLOCK_COUNT_TIND or BLOCK_COUNT_TRANSLATOR and 77 // |blocknr| points to a block in the first three cases. The last case is 78 // only used by GNU Hurd, so we shouldn't see those cases here. 79 if (blockcnt == BLOCK_COUNT_IND || blockcnt == BLOCK_COUNT_DIND || 80 blockcnt == BLOCK_COUNT_TIND) { 81 blocks->insert(*blocknr); 82 } 83 return 0; 84 } 85 86 struct UpdateFileAndAppendState { 87 std::map<ext2_ino_t, FilesystemInterface::File>* inodes = nullptr; 88 set<ext2_ino_t>* used_inodes = nullptr; 89 vector<FilesystemInterface::File>* files = nullptr; 90 ext2_filsys filsys; 91 }; 92 93 int UpdateFileAndAppend(ext2_ino_t dir, 94 int entry, 95 struct ext2_dir_entry *dirent, 96 int offset, 97 int blocksize, 98 char *buf, 99 void *priv_data) { 100 UpdateFileAndAppendState* state = 101 static_cast<UpdateFileAndAppendState*>(priv_data); 102 uint32_t file_type = dirent->name_len >> 8; 103 // Directories can't have hard links, and they are added from the outer loop. 104 if (file_type == EXT2_FT_DIR) 105 return 0; 106 107 auto ino_file = state->inodes->find(dirent->inode); 108 if (ino_file == state->inodes->end()) 109 return 0; 110 auto dir_file = state->inodes->find(dir); 111 if (dir_file == state->inodes->end()) 112 return 0; 113 string basename(dirent->name, dirent->name_len & 0xff); 114 ino_file->second.name = dir_file->second.name; 115 if (dir_file->second.name != "/") 116 ino_file->second.name += "/"; 117 ino_file->second.name += basename; 118 119 // Append this file to the output. If the file has a hard link, it will be 120 // added twice to the output, but with different names, which is ok. That will 121 // help identify all the versions of the same file. 122 state->files->push_back(ino_file->second); 123 state->used_inodes->insert(dirent->inode); 124 return 0; 125 } 126 127 } // namespace 128 129 unique_ptr<Ext2Filesystem> Ext2Filesystem::CreateFromFile( 130 const string& filename) { 131 if (filename.empty()) 132 return nullptr; 133 unique_ptr<Ext2Filesystem> result(new Ext2Filesystem()); 134 result->filename_ = filename; 135 136 errcode_t err = ext2fs_open(filename.c_str(), 137 0, // flags (read only) 138 0, // superblock block number 139 0, // block_size (autodetect) 140 unix_io_manager, 141 &result->filsys_); 142 if (err) { 143 LOG(ERROR) << "Opening ext2fs " << filename; 144 return nullptr; 145 } 146 return result; 147 } 148 149 Ext2Filesystem::~Ext2Filesystem() { 150 ext2fs_free(filsys_); 151 } 152 153 size_t Ext2Filesystem::GetBlockSize() const { 154 return filsys_->blocksize; 155 } 156 157 size_t Ext2Filesystem::GetBlockCount() const { 158 return ext2fs_blocks_count(filsys_->super); 159 } 160 161 bool Ext2Filesystem::GetFiles(vector<File>* files) const { 162 TEST_AND_RETURN_FALSE_ERRCODE(ext2fs_read_inode_bitmap(filsys_)); 163 164 ext2_inode_scan iscan; 165 TEST_AND_RETURN_FALSE_ERRCODE( 166 ext2fs_open_inode_scan(filsys_, 0 /* buffer_blocks */, &iscan)); 167 168 std::map<ext2_ino_t, File> inodes; 169 170 // List of directories. We need to first parse all the files in a directory 171 // to later fix the absolute paths. 172 vector<ext2_ino_t> directories; 173 174 set<uint64_t> inode_blocks; 175 176 // Iterator 177 ext2_ino_t it_ino; 178 ext2_inode it_inode; 179 180 bool ok = true; 181 while (true) { 182 errcode_t error = ext2fs_get_next_inode(iscan, &it_ino, &it_inode); 183 if (error) { 184 LOG(ERROR) << "Failed to retrieve next inode (" << error << ")"; 185 ok = false; 186 break; 187 } 188 if (it_ino == 0) 189 break; 190 191 // Skip inodes that are not in use. 192 if (!ext2fs_test_inode_bitmap(filsys_->inode_map, it_ino)) 193 continue; 194 195 File& file = inodes[it_ino]; 196 if (it_ino == EXT2_RESIZE_INO) { 197 file.name = "<group-descriptors>"; 198 } else { 199 file.name = base::StringPrintf("<inode-%u>", it_ino); 200 } 201 202 memset(&file.file_stat, 0, sizeof(file.file_stat)); 203 file.file_stat.st_ino = it_ino; 204 file.file_stat.st_mode = it_inode.i_mode; 205 file.file_stat.st_nlink = it_inode.i_links_count; 206 file.file_stat.st_uid = it_inode.i_uid; 207 file.file_stat.st_gid = it_inode.i_gid; 208 file.file_stat.st_size = it_inode.i_size; 209 file.file_stat.st_blksize = filsys_->blocksize; 210 file.file_stat.st_blocks = it_inode.i_blocks; 211 file.file_stat.st_atime = it_inode.i_atime; 212 file.file_stat.st_mtime = it_inode.i_mtime; 213 file.file_stat.st_ctime = it_inode.i_ctime; 214 215 bool is_dir = (ext2fs_check_directory(filsys_, it_ino) == 0); 216 if (is_dir) 217 directories.push_back(it_ino); 218 219 if (!ext2fs_inode_has_valid_blocks(&it_inode)) 220 continue; 221 222 // Process the inode data and metadata blocks. 223 // For normal files, inode blocks are indirect, double indirect 224 // and triple indirect blocks (no data blocks). For directories and 225 // the journal, all blocks are considered metadata blocks. 226 int flags = it_ino < EXT2_GOOD_OLD_FIRST_INO ? 0 : BLOCK_FLAG_DATA_ONLY; 227 error = ext2fs_block_iterate2(filsys_, it_ino, flags, 228 nullptr, // block_buf 229 ProcessInodeAllBlocks, 230 &file.extents); 231 232 if (error) { 233 LOG(ERROR) << "Failed to enumerate inode " << it_ino 234 << " blocks (" << error << ")"; 235 continue; 236 } 237 if (it_ino >= EXT2_GOOD_OLD_FIRST_INO) { 238 ext2fs_block_iterate2(filsys_, it_ino, 0, nullptr, 239 AddMetadataBlocks, 240 &inode_blocks); 241 } 242 } 243 ext2fs_close_inode_scan(iscan); 244 if (!ok) 245 return false; 246 247 // The set of inodes already added to the output. There can be less elements 248 // here than in files since the later can contain repeated inodes due to 249 // hardlink files. 250 set<ext2_ino_t> used_inodes; 251 252 UpdateFileAndAppendState priv_data; 253 priv_data.inodes = &inodes; 254 priv_data.used_inodes = &used_inodes; 255 priv_data.files = files; 256 priv_data.filsys = filsys_; 257 258 files->clear(); 259 // Iterate over all the files of each directory to update the name and add it. 260 for (ext2_ino_t dir_ino : directories) { 261 char* dir_name = nullptr; 262 errcode_t error = ext2fs_get_pathname(filsys_, dir_ino, 0, &dir_name); 263 if (error) { 264 // Not being able to read a directory name is not a fatal error, it is 265 // just skiped. 266 LOG(WARNING) << "Reading directory name on inode " << dir_ino 267 << " (error " << error << ")"; 268 inodes[dir_ino].name = base::StringPrintf("<dir-%u>", dir_ino); 269 } else { 270 inodes[dir_ino].name = dir_name; 271 files->push_back(inodes[dir_ino]); 272 used_inodes.insert(dir_ino); 273 } 274 ext2fs_free_mem(&dir_name); 275 276 error = ext2fs_dir_iterate2( 277 filsys_, dir_ino, 0, nullptr /* block_buf */, 278 UpdateFileAndAppend, &priv_data); 279 if (error) { 280 LOG(WARNING) << "Failed to enumerate files in directory " 281 << inodes[dir_ino].name << " (error " << error << ")"; 282 } 283 } 284 285 // Add <inode-blocks> file with the blocks that hold inodes. 286 File inode_file; 287 inode_file.name = "<inode-blocks>"; 288 for (uint64_t block : inode_blocks) { 289 AppendBlockToExtents(&inode_file.extents, block); 290 } 291 files->push_back(inode_file); 292 293 // Add <free-spacce> blocs. 294 errcode_t error = ext2fs_read_block_bitmap(filsys_); 295 if (error) { 296 LOG(ERROR) << "Reading the blocks bitmap (error " << error << ")"; 297 } else { 298 File free_space; 299 free_space.name = "<free-space>"; 300 blk64_t blk_start = ext2fs_get_block_bitmap_start2(filsys_->block_map); 301 blk64_t blk_end = ext2fs_get_block_bitmap_end2(filsys_->block_map); 302 for (blk64_t block = blk_start; block < blk_end; block++) { 303 if (!ext2fs_test_block_bitmap2(filsys_->block_map, block)) 304 AppendBlockToExtents(&free_space.extents, block); 305 } 306 files->push_back(free_space); 307 } 308 309 // Add all the unreachable files plus the pseudo-files with an inode. Since 310 // these inodes aren't files in the filesystem, ignore the empty ones. 311 for (const auto& ino_file : inodes) { 312 if (used_inodes.find(ino_file.first) != used_inodes.end()) 313 continue; 314 if (ino_file.second.extents.empty()) 315 continue; 316 317 File file = ino_file.second; 318 ExtentRanges ranges; 319 ranges.AddExtents(file.extents); 320 file.extents = ranges.GetExtentsForBlockCount(ranges.blocks()); 321 322 files->push_back(file); 323 } 324 325 return true; 326 } 327 328 bool Ext2Filesystem::LoadSettings(brillo::KeyValueStore* store) const { 329 // First search for the settings inode following symlinks if we find some. 330 ext2_ino_t ino_num = 0; 331 errcode_t err = ext2fs_namei_follow( 332 filsys_, EXT2_ROOT_INO /* root */, EXT2_ROOT_INO /* cwd */, 333 "/etc/update_engine.conf", &ino_num); 334 if (err != 0) 335 return false; 336 337 ext2_inode ino_data; 338 if (ext2fs_read_inode(filsys_, ino_num, &ino_data) != 0) 339 return false; 340 341 // Load the list of blocks and then the contents of the inodes. 342 vector<Extent> extents; 343 err = ext2fs_block_iterate2(filsys_, ino_num, BLOCK_FLAG_DATA_ONLY, 344 nullptr, // block_buf 345 ProcessInodeAllBlocks, 346 &extents); 347 if (err != 0) 348 return false; 349 350 brillo::Blob blob; 351 uint64_t physical_size = utils::BlocksInExtents(extents) * filsys_->blocksize; 352 // Sparse holes in the settings file are not supported. 353 if (EXT2_I_SIZE(&ino_data) > physical_size) 354 return false; 355 if (!utils::ReadExtents(filename_, extents, &blob, physical_size, 356 filsys_->blocksize)) 357 return false; 358 359 string text(blob.begin(), blob.begin() + EXT2_I_SIZE(&ino_data)); 360 return store->LoadFromString(text); 361 } 362 363 } // namespace chromeos_update_engine 364