一、背景
Android新版本使用super分区替代原来的system、vendor后,就采用了overlayfs文件系统。这种文件系统在执行adb remount 后,修改system 、vendor分区内容并不是真正存储在原来的位置,而是单独利用super剩余空间或data分区存了一份新的,原来的文件并没有改变。系统使用时判断有overlay的就用新的,没有就是有原文件。具体概念可查看https://www.cnblogs.com/loongson-artc-lyc/p/15981855.html 了解。
这就带来一个问题,调试时使用adb push 更新了需要开机下载的固件,但是开机过程中不是一开始就挂载overlay fs的,这就可能导致加载的还是老的固件。我们需要在kernel中判断何时挂载了overlay fs再去加载固件,就可以解决这个问题。
二、Init阶段overlay fs挂载前后阶段分析
adb remount log
adb remount
[libfs_mgr]fs_mgr_do_format: Format /dev/block/dm-5 as 'f2fs'
Using overlayfs for /vendor
Now reboot your device for settings to take effect
remount succeeded
init 启动代码
system/core/init/main.cpp
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
return FirstStageMain(argc, argv);
}
上面函数会执行多次,执行FirstStageMain时会调用DoFirstStageMount跟据fstab挂载物理分区
和overlay fs
system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
....
if (access("/force_debuggable", F_OK) == 0) {
std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
!fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
LOG(ERROR) << "Failed to setup debug ramdisk";
} else {
// setenv for second-stage init to read above kDebugRamdisk* files.
setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
}
}
if (!DoFirstStageMount()) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
...
}
system/core/init/first_stage_mount.cpp
bool FirstStageMount::DoFirstStageMount() {
if (!IsDmLinearEnabled() && fstab_.empty()) {
// Nothing to mount.
LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
return true;
}
if (!InitDevices()) return false;
if (!MountPartitions()) return false;
return true;
}
bool FirstStageMount::MountPartitions() {
...
// If we don't see /system or / in the fstab, then we need to create an root entry for
// overlayfs.
if (!GetEntryForMountPoint(&fstab_, "/system") && !GetEntryForMountPoint(&fstab_, "/")) {
FstabEntry root_entry;
if (GetRootEntry(&root_entry)) {
fstab_.emplace_back(std::move(root_entry));
}
}
// heads up for instantiating required device(s) for overlayfs logic
auto init_devices = [this](std::set<std::string> devices) -> bool {
for (auto iter = devices.begin(); iter != devices.end();) {
if (android::base::StartsWith(*iter, "/dev/block/dm-")) {
if (!block_dev_init_.InitDmDevice(*iter)) {
return false;
}
iter = devices.erase(iter);
} else {
iter++;
}
}
return InitRequiredDevices(std::move(devices));
};
...
}
执行SecondStageMain会创建/dev/.booting和/dev/__properties__文件
system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {
...
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
...
PropertyInit();//此函数会创建/dev/__properties__
...
}
所以只需要我们在init的第二个阶段去加载固件就可以保证overlay fs已挂载,加载的是新的固件。
判断是否处于第二阶段可以判断/dev/.booting或/dev/__properties__是否存在即可。
三、内核增加判断示例
//#define SECOND_INIT_STAGE_FILE "/dev/.booting" //this file will be deleted when firmware load
#define SECOND_INIT_STAGE_FILE "/dev/__properties__" //this is a dir created at second init
file = filp_open(SECOND_INIT_STAGE_FILE, O_RDONLY, 0);
if (IS_ERR(file)){
dev_info(sdev->dev, "%s: open '%s' failed, ret %d\n",
__func__, SECOND_INIT_STAGE_FILE, ret);
} else {
filp_close(file,NULL);
}
或
bool folder_exists(struct path *path, const char *name) {
struct path lookup_path = *path; // 初始路径
struct qstr qname = QSTR_INIT(name, strlen(name));
int err = path_lookupat(lookup_path.dentry, lookup_path.mnt, qname, &lookup_path);
if (err == 0) {
// 文件夹存在
path_put(&lookup_path); // 释放路径
return true;
}
return false; // 文件夹不存在
}
或
error = vfs_stat(path,&stat)
if(error) {
return -ENOENT;//路径不存在
}
if(S_ISDIR(stat.mode)){
//是目录
}
else if(S_ISREG(stat.mode)) {
//是文件
}else {
//其他类型
}
参考:Android 动态分区详解(七) overlayfs 与 adb remount 操作_android overlayfs-CSDN博客
android 系统相关_android super分区-CSDN博客
标签:__,fs,return,overlay,dev,init,argv,path,Android From: https://blog.csdn.net/u013463707/article/details/141348912