Android 启动时判断overlay fs是否挂载

一、背景

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博客

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Free飝Fly

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值