blob: 08aea1a574947a2b6a7c98f2bfc521a6ba251850 [file] [edit]
//
// 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 <android-base/result.h>
#include <android-base/scopeguard.h>
extern "C" {
#include <et/com_err.h>
#include <ext2fs/ext2_io.h>
#include <ext2fs/ext2fs.h>
}
#include <sys/stat.h>
#include <filesystem>
#include <functional>
#include <string>
#include <vector>
using android::base::Error;
using android::base::make_scope_guard;
using android::base::Result;
namespace fs = std::filesystem;
using namespace std::placeholders;
namespace {
Result<ext2_ino_t> 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<std::string> 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<std::string> 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<std::vector<std::string>> 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<Entry> 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<std::vector<Entry>> 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));
}