123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- //
- // Copyright (C) 2015 The Android Open Source Project
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- #include "update_engine/payload_generator/ext2_filesystem.h"
- #include <et/com_err.h>
- #if defined(__clang__)
- // TODO(*): Remove these pragmas when b/35721782 is fixed.
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wmacro-redefined"
- #endif
- #include <ext2fs/ext2_io.h>
- #include <ext2fs/ext2fs.h>
- #if defined(__clang__)
- #pragma clang diagnostic pop
- #endif
- #include <map>
- #include <set>
- #include <base/logging.h>
- #include <base/strings/stringprintf.h>
- #include "update_engine/common/utils.h"
- #include "update_engine/payload_generator/extent_ranges.h"
- #include "update_engine/payload_generator/extent_utils.h"
- #include "update_engine/update_metadata.pb.h"
- using std::set;
- using std::string;
- using std::unique_ptr;
- using std::vector;
- namespace chromeos_update_engine {
- namespace {
- // Processes all blocks belonging to an inode and adds them to the extent list.
- // This function should match the prototype expected by ext2fs_block_iterate2().
- int ProcessInodeAllBlocks(ext2_filsys fs,
- blk_t* blocknr,
- e2_blkcnt_t blockcnt,
- blk_t ref_blk,
- int ref_offset,
- void* priv) {
- vector<Extent>* extents = static_cast<vector<Extent>*>(priv);
- AppendBlockToExtents(extents, *blocknr);
- return 0;
- }
- // Processes only indirect, double indirect or triple indirect metadata
- // blocks belonging to an inode. This function should match the prototype of
- // ext2fs_block_iterate2().
- int AddMetadataBlocks(ext2_filsys fs,
- blk_t* blocknr,
- e2_blkcnt_t blockcnt,
- blk_t ref_blk,
- int ref_offset,
- void* priv) {
- set<uint64_t>* blocks = static_cast<set<uint64_t>*>(priv);
- // If |blockcnt| is non-negative, |blocknr| points to the physical block
- // number.
- // If |blockcnt| is negative, it is one of the values: BLOCK_COUNT_IND,
- // BLOCK_COUNT_DIND, BLOCK_COUNT_TIND or BLOCK_COUNT_TRANSLATOR and
- // |blocknr| points to a block in the first three cases. The last case is
- // only used by GNU Hurd, so we shouldn't see those cases here.
- if (blockcnt == BLOCK_COUNT_IND || blockcnt == BLOCK_COUNT_DIND ||
- blockcnt == BLOCK_COUNT_TIND) {
- blocks->insert(*blocknr);
- }
- return 0;
- }
- struct UpdateFileAndAppendState {
- std::map<ext2_ino_t, FilesystemInterface::File>* inodes = nullptr;
- set<ext2_ino_t>* used_inodes = nullptr;
- vector<FilesystemInterface::File>* files = nullptr;
- ext2_filsys filsys;
- };
- int UpdateFileAndAppend(ext2_ino_t dir,
- int entry,
- struct ext2_dir_entry* dirent,
- int offset,
- int blocksize,
- char* buf,
- void* priv_data) {
- UpdateFileAndAppendState* state =
- static_cast<UpdateFileAndAppendState*>(priv_data);
- uint32_t file_type = dirent->name_len >> 8;
- // Directories can't have hard links, and they are added from the outer loop.
- if (file_type == EXT2_FT_DIR)
- return 0;
- auto ino_file = state->inodes->find(dirent->inode);
- if (ino_file == state->inodes->end())
- return 0;
- auto dir_file = state->inodes->find(dir);
- if (dir_file == state->inodes->end())
- return 0;
- string basename(dirent->name, dirent->name_len & 0xff);
- ino_file->second.name = dir_file->second.name;
- if (dir_file->second.name != "/")
- ino_file->second.name += "/";
- ino_file->second.name += basename;
- // Append this file to the output. If the file has a hard link, it will be
- // added twice to the output, but with different names, which is ok. That will
- // help identify all the versions of the same file.
- state->files->push_back(ino_file->second);
- state->used_inodes->insert(dirent->inode);
- return 0;
- }
- } // namespace
- unique_ptr<Ext2Filesystem> Ext2Filesystem::CreateFromFile(
- const string& filename) {
- if (filename.empty())
- return nullptr;
- unique_ptr<Ext2Filesystem> result(new Ext2Filesystem());
- result->filename_ = filename;
- errcode_t err = ext2fs_open(filename.c_str(),
- 0, // flags (read only)
- 0, // superblock block number
- 0, // block_size (autodetect)
- unix_io_manager,
- &result->filsys_);
- if (err) {
- LOG(ERROR) << "Opening ext2fs " << filename;
- return nullptr;
- }
- return result;
- }
- Ext2Filesystem::~Ext2Filesystem() {
- ext2fs_free(filsys_);
- }
- size_t Ext2Filesystem::GetBlockSize() const {
- return filsys_->blocksize;
- }
- size_t Ext2Filesystem::GetBlockCount() const {
- return ext2fs_blocks_count(filsys_->super);
- }
- bool Ext2Filesystem::GetFiles(vector<File>* files) const {
- TEST_AND_RETURN_FALSE_ERRCODE(ext2fs_read_inode_bitmap(filsys_));
- ext2_inode_scan iscan;
- TEST_AND_RETURN_FALSE_ERRCODE(
- ext2fs_open_inode_scan(filsys_, 0 /* buffer_blocks */, &iscan));
- std::map<ext2_ino_t, File> inodes;
- // List of directories. We need to first parse all the files in a directory
- // to later fix the absolute paths.
- vector<ext2_ino_t> directories;
- set<uint64_t> inode_blocks;
- // Iterator
- ext2_ino_t it_ino;
- ext2_inode it_inode;
- bool ok = true;
- while (true) {
- errcode_t error = ext2fs_get_next_inode(iscan, &it_ino, &it_inode);
- if (error) {
- LOG(ERROR) << "Failed to retrieve next inode (" << error << ")";
- ok = false;
- break;
- }
- if (it_ino == 0)
- break;
- // Skip inodes that are not in use.
- if (!ext2fs_test_inode_bitmap(filsys_->inode_map, it_ino))
- continue;
- File& file = inodes[it_ino];
- if (it_ino == EXT2_RESIZE_INO) {
- file.name = "<group-descriptors>";
- } else {
- file.name = base::StringPrintf("<inode-%u>", it_ino);
- }
- memset(&file.file_stat, 0, sizeof(file.file_stat));
- file.file_stat.st_ino = it_ino;
- file.file_stat.st_mode = it_inode.i_mode;
- file.file_stat.st_nlink = it_inode.i_links_count;
- file.file_stat.st_uid = it_inode.i_uid;
- file.file_stat.st_gid = it_inode.i_gid;
- file.file_stat.st_size = it_inode.i_size;
- file.file_stat.st_blksize = filsys_->blocksize;
- file.file_stat.st_blocks = it_inode.i_blocks;
- file.file_stat.st_atime = it_inode.i_atime;
- file.file_stat.st_mtime = it_inode.i_mtime;
- file.file_stat.st_ctime = it_inode.i_ctime;
- bool is_dir = (ext2fs_check_directory(filsys_, it_ino) == 0);
- if (is_dir)
- directories.push_back(it_ino);
- if (!ext2fs_inode_has_valid_blocks(&it_inode))
- continue;
- // Process the inode data and metadata blocks.
- // For normal files, inode blocks are indirect, double indirect
- // and triple indirect blocks (no data blocks). For directories and
- // the journal, all blocks are considered metadata blocks.
- int flags = it_ino < EXT2_GOOD_OLD_FIRST_INO ? 0 : BLOCK_FLAG_DATA_ONLY;
- error = ext2fs_block_iterate2(filsys_,
- it_ino,
- flags,
- nullptr, // block_buf
- ProcessInodeAllBlocks,
- &file.extents);
- if (error) {
- LOG(ERROR) << "Failed to enumerate inode " << it_ino << " blocks ("
- << error << ")";
- continue;
- }
- if (it_ino >= EXT2_GOOD_OLD_FIRST_INO) {
- ext2fs_block_iterate2(
- filsys_, it_ino, 0, nullptr, AddMetadataBlocks, &inode_blocks);
- }
- }
- ext2fs_close_inode_scan(iscan);
- if (!ok)
- return false;
- // The set of inodes already added to the output. There can be less elements
- // here than in files since the later can contain repeated inodes due to
- // hardlink files.
- set<ext2_ino_t> used_inodes;
- UpdateFileAndAppendState priv_data;
- priv_data.inodes = &inodes;
- priv_data.used_inodes = &used_inodes;
- priv_data.files = files;
- priv_data.filsys = filsys_;
- files->clear();
- // Iterate over all the files of each directory to update the name and add it.
- for (ext2_ino_t dir_ino : directories) {
- char* dir_name = nullptr;
- errcode_t error = ext2fs_get_pathname(filsys_, dir_ino, 0, &dir_name);
- if (error) {
- // Not being able to read a directory name is not a fatal error, it is
- // just skiped.
- LOG(WARNING) << "Reading directory name on inode " << dir_ino
- << " (error " << error << ")";
- inodes[dir_ino].name = base::StringPrintf("<dir-%u>", dir_ino);
- } else {
- inodes[dir_ino].name = dir_name;
- files->push_back(inodes[dir_ino]);
- used_inodes.insert(dir_ino);
- }
- ext2fs_free_mem(&dir_name);
- error = ext2fs_dir_iterate2(filsys_,
- dir_ino,
- 0,
- nullptr /* block_buf */,
- UpdateFileAndAppend,
- &priv_data);
- if (error) {
- LOG(WARNING) << "Failed to enumerate files in directory "
- << inodes[dir_ino].name << " (error " << error << ")";
- }
- }
- // Add <inode-blocks> file with the blocks that hold inodes.
- File inode_file;
- inode_file.name = "<inode-blocks>";
- for (uint64_t block : inode_blocks) {
- AppendBlockToExtents(&inode_file.extents, block);
- }
- files->push_back(inode_file);
- // Add <free-spacce> blocs.
- errcode_t error = ext2fs_read_block_bitmap(filsys_);
- if (error) {
- LOG(ERROR) << "Reading the blocks bitmap (error " << error << ")";
- } else {
- File free_space;
- free_space.name = "<free-space>";
- blk64_t blk_start = ext2fs_get_block_bitmap_start2(filsys_->block_map);
- blk64_t blk_end = ext2fs_get_block_bitmap_end2(filsys_->block_map);
- for (blk64_t block = blk_start; block < blk_end; block++) {
- if (!ext2fs_test_block_bitmap2(filsys_->block_map, block))
- AppendBlockToExtents(&free_space.extents, block);
- }
- files->push_back(free_space);
- }
- // Add all the unreachable files plus the pseudo-files with an inode. Since
- // these inodes aren't files in the filesystem, ignore the empty ones.
- for (const auto& ino_file : inodes) {
- if (used_inodes.find(ino_file.first) != used_inodes.end())
- continue;
- if (ino_file.second.extents.empty())
- continue;
- File file = ino_file.second;
- ExtentRanges ranges;
- ranges.AddExtents(file.extents);
- file.extents = ranges.GetExtentsForBlockCount(ranges.blocks());
- files->push_back(file);
- }
- return true;
- }
- bool Ext2Filesystem::LoadSettings(brillo::KeyValueStore* store) const {
- // First search for the settings inode following symlinks if we find some.
- ext2_ino_t ino_num = 0;
- errcode_t err = ext2fs_namei_follow(filsys_,
- EXT2_ROOT_INO /* root */,
- EXT2_ROOT_INO /* cwd */,
- "/etc/update_engine.conf",
- &ino_num);
- if (err != 0)
- return false;
- ext2_inode ino_data;
- if (ext2fs_read_inode(filsys_, ino_num, &ino_data) != 0)
- return false;
- // Load the list of blocks and then the contents of the inodes.
- vector<Extent> extents;
- err = ext2fs_block_iterate2(filsys_,
- ino_num,
- BLOCK_FLAG_DATA_ONLY,
- nullptr, // block_buf
- ProcessInodeAllBlocks,
- &extents);
- if (err != 0)
- return false;
- brillo::Blob blob;
- uint64_t physical_size = utils::BlocksInExtents(extents) * filsys_->blocksize;
- // Sparse holes in the settings file are not supported.
- if (EXT2_I_SIZE(&ino_data) > physical_size)
- return false;
- if (!utils::ReadExtents(
- filename_, extents, &blob, physical_size, filsys_->blocksize))
- return false;
- string text(blob.begin(), blob.begin() + EXT2_I_SIZE(&ino_data));
- return store->LoadFromString(text);
- }
- } // namespace chromeos_update_engine
|