blob: 9758461bde063ae84baf94c8f80981de10d13c93 [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 "erofs.h"
#include <android-base/result.h>
#include <android-base/scopeguard.h>
// ignore unused-parameter in headers
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <erofs/config.h>
#include <erofs/dir.h>
#include <erofs/inode.h>
#pragma GCC diagnostic pop
#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 {
struct ReadDirContext {
struct erofs_dir_context ctx;
std::vector<std::string> names;
};
int ReadDirIter(struct erofs_dir_context* ctx) {
std::string name{ctx->dname, ctx->dname + ctx->de_namelen};
((ReadDirContext*)ctx)->names.push_back(name);
return 0;
}
Result<std::vector<std::string>> ReadDir(struct erofs_sb_info* sbi,
const fs::path& path) {
struct erofs_inode dir = {.sbi = sbi};
auto err = erofs_ilookup(path.string().c_str(), &dir);
if (err) {
return Error(err) << "failed to read inode for " << path;
}
if (!S_ISDIR(dir.i_mode)) {
return Error() << "failed to read dir: " << path << " is not a directory";
}
ReadDirContext ctx = {
{
.dir = &dir,
.cb = ReadDirIter,
.flags = EROFS_READDIR_VALID_PNID,
},
};
err = erofs_iterate_dir(&ctx.ctx, false);
if (err) {
return Error(err) << "failed to read dir";
}
return ctx.names;
}
Result<Entry> ReadEntry(struct erofs_sb_info* sbi, const fs::path& path) {
struct erofs_inode inode = {.sbi = sbi};
auto err = erofs_ilookup(path.string().c_str(), &inode);
if (err) {
return Error(err) << "failed to read inode for " << path;
}
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
char security_context[256];
err = erofs_getxattr(&inode, "security.selinux", security_context,
sizeof(security_context));
if (err < 0) {
return Error() << "failed to get security context of " << path << ": "
<< erofs_strerror(err);
}
return Entry{mode, entry_path, security_context};
}
} // namespace
Result<std::vector<Entry>> ErofsList(const std::string& image_path) {
erofs_init_configure();
auto configure = make_scope_guard(&erofs_exit_configure);
// open image
struct erofs_sb_info sbi;
auto err = erofs_dev_open(&sbi, image_path.c_str(), O_RDONLY | O_TRUNC);
if (err) {
return Error(err) << "failed to open image file";
}
auto dev = make_scope_guard([&] { erofs_dev_close(&sbi); });
// read superblock
err = erofs_read_superblock(&sbi);
if (err) {
return Error(err) << "failed to read superblock";
}
auto superblock = make_scope_guard([&] { erofs_put_super(&sbi); });
return List(std::bind(&ReadEntry, &sbi, _1), std::bind(&ReadDir, &sbi, _1));
}