// // Copyright (C) 2025 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 "ext4.h" #include #include extern "C" { #include #include #include } #include #include #include #include #include using android::base::Error; using android::base::make_scope_guard; using android::base::Result; namespace fs = std::filesystem; using namespace std::placeholders; namespace { Result PathToIno(ext2_filsys fs, const fs::path& path) { ext2_ino_t ino; auto err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path.string().c_str(), &ino); if (err) { return Error() << "failed to resolve path" << path << ": " << error_message(err); } return ino; } Result GetXattr(ext2_filsys fs, ext2_ino_t ino, const std::string& key) { struct ext2_xattr_handle* h; auto err = ext2fs_xattrs_open(fs, ino, &h); if (err) { return Error() << "failed to open xattr: " << error_message(err); } auto close = make_scope_guard([&] { ext2fs_xattrs_close(&h); }); err = ext2fs_xattrs_read(h); if (err) { return Error() << "failed to read xattr: " << error_message(err); } char* buf = nullptr; size_t buflen; err = ext2fs_xattr_get(h, key.c_str(), (void**)&buf, &buflen); if (err) { return Error() << "failed to get xattr " << key << ": " << error_message(err); } std::string value = buf; ext2fs_free_mem(&buf); return value; } struct ReadDirContext { fs::path dir; std::vector names; }; int ReadDirIter(ext2_ino_t dir, int entry, struct ext2_dir_entry* dirent, int offset, int blocksize, char* buf, void* priv_data) { (void)dir; (void)entry; (void)offset; (void)blocksize; (void)buf; ReadDirContext* ctx = (ReadDirContext*)priv_data; auto len = ext2fs_dirent_name_len(dirent); std::string name(dirent->name, dirent->name + len); // ignore ./lost+found if (ctx->dir == "." && name == "lost+found") { return 0; } ctx->names.push_back(name); return 0; } Result> ReadDir(ext2_filsys fs, const fs::path& path) { ext2_ino_t ino = OR_RETURN(PathToIno(fs, path)); ReadDirContext ctx = {.dir = path}; auto err = ext2fs_dir_iterate2(fs, ino, /*flag*/ 0, /*block_buf*/ nullptr, &ReadDirIter, /*priv_data*/ &ctx); if (err) { return Error() << "failed to read dir " << path << ": " << error_message(err); } return ctx.names; } Result ReadEntry(ext2_filsys fs, const fs::path& path) { ext2_ino_t ino = OR_RETURN(PathToIno(fs, path)); struct ext2_inode inode; auto err = ext2fs_read_inode(fs, ino, &inode); if (err) { return Error() << "failed to read inode for " << path << ": " << error_message(err); } mode_t mode = inode.i_mode; std::string entry_path = path.string(); // make sure dir path ends with '/' if (S_ISDIR(mode)) { entry_path += '/'; } // read security context auto security_context = OR_RETURN(GetXattr(fs, ino, "security.selinux")); return Entry{mode, entry_path, security_context}; } } // namespace Result> Ext4List(const std::string& image_path) { // open image ext2_filsys fs; io_manager io_ptr = unix_io_manager; auto err = ext2fs_open( image_path.c_str(), EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_64BITS | EXT2_FLAG_THREADS, /* superblock */ 0, /* blocksize */ 0, io_ptr, &fs); if (err) { return Error() << "failed to open " << image_path << ": " << error_message(err); } auto close = make_scope_guard([&] { ext2fs_close_free(&fs); }); return List(std::bind(&ReadEntry, fs, _1), std::bind(&ReadDir, fs, _1)); }