linux 基于debian/ubuntu AB系统适配(三)- overlayroot

Overlayroot

Overlayroot是一个实用工具,允许您创建一个只读的根文件系统和一个可写的覆盖文件系统。这对于创建一个更安全和稳定的系统很有用,因为对系统所做的任何更改都将存储在覆盖文件系统中,可以很容易地丢弃或重置。

在Debian下,分离的系统在/userdata/rootfs_overlay下,如:在根文件系统下创建一个111文件夹,实际创建在/userdata/rootfs_overlay下创建的。
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第1张图片

这样在切换A/B系统的时候,更新了文件系统就不会影响到用户数据,因为用户数据是在单独的一个分区。但目前种模式只是在但系统上实现,A/B系统没有实现,我们需要进行修改。

uboot

uboot阶段会使用bootargs给内核传overlayroot参数,使内核开启overlayroot
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第2张图片

但启动A/B系统之后会发现,overlayroot参数不见了,导致内核不能开启overlayroot。遂,查找原因。

通过不懈的查找发现,在bootargs中有root参数,该参数指向的就是所要使用的文件系统。如下:
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第3张图片

但在启动A/B后,会去修改root这个环境变量,修改为A/B中其中一个文件的UUID,如下:
在这里插入图片描述

问题就出现在这里了,在arch\arm\mach-rockchip\board.c中如果定义了CONFIG_ANDROID_AB则在bootarge中寻找root=,然后删除。问题就出现在这个删除上,因为overlayroot字段里也有root就导致了overlayroot字段也被删除了
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第4张图片

所以这里我们需要进行修改,把root=增加一些字段,使之具有唯一性。修改如下

diff --git a/arch/arm/mach-rockchip/board.c b/arch/arm/mach-rockchip/board.c
index 5c84f3dbfa..9d706b85fa 100644
--- a/arch/arm/mach-rockchip/board.c
+++ b/arch/arm/mach-rockchip/board.c
@@ -1181,7 +1181,7 @@ char *board_fdt_chosen_bootargs(void *fdt)
                 * to cmdline. The format is "roo=PARTUUID=xxxx...".
                 */
 #ifdef CONFIG_ANDROID_AB
-               env_update_filter("bootargs", bootargs, "root=");
+               env_update_filter("bootargs", bootargs, "root=PARTLABEL");
                ab_update_root_partition();
 #else
                env_update("bootargs", bootargs);

linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第5张图片

修改编译后发现overlayroot字段还是被删除了,思考后想到root字段删除了但后面还需要补上去的,所以在补上去的时候肯定把overlayroot字段被覆盖了,所以还得找。
在执行了env_update_filter之后又运行了ab_update_root_partition,在函数中又调用了ab_update_root_uuid,该函数就是用作设置root字段。
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第6张图片

在该函数可以看到又在查找root=字段,所以这个地方也需要修改。

diff --git a/common/android_ab.c b/common/android_ab.c
index d344a024e9..5df6143fd1 100644
--- a/common/android_ab.c
+++ b/common/android_ab.c
@@ -433,7 +433,7 @@ static void ab_update_root_uuid(void)
        if (ab_is_support_dynamic_partition(dev_desc))
                return;
 
-       if (!strstr(boot_args, "root=")) {
+       if (!strstr(boot_args, "root=PARTUUID")) {
                get_partition_unique_uuid(ANDROID_PARTITION_SYSTEM,
                                          guid_buf, UUID_SIZE);
                strcat(root_partuuid, guid_buf);

linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第7张图片

编译后,参数终于正确了
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第8张图片

kernel

修改uboot启动系统后,overlayroot能够顺利建立了,但新的问题又来了,建立的overlayroot文件名称不对,是以分区名称为前缀建立的,如下
在这里插入图片描述

这样会导致切换系统后,用户数据丢失,需要修改,使用mount命令可以看到,,overlayroot挂载在/userdata下,并带有参数:
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第9张图片

lowerdir=/root-ro,  //原始文件所在的位置,lower层的文件只读
upperdir=/userdata/system_b_overlay, //任何修改都将反映在Upper层 ,故upper层可读可写
workdir=/userdata/system_b_overlay-workdir/_ //指定文件系统的工作基础目录,挂载后内容会被清空,且在使用过程中其内容用户不可见

可以看出该参数很重要,所以先修改挂载目录,通过搜索查找到了他们赋值的位置,在Y:\android\rk3588_debian\kernel\fs\overlayfs\super.c,中
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第10张图片

有尝试过直接修改他们的值,但是会导致整个系统崩溃。所以需要找到原始赋值的地方。
数据是通过opt传入进来的,该值是通过file_system_type的系统调用来传输的,通过mount接口使用ovl_mount函数指针传入进来,在往上就是应用层面了,所以只能在ovl_mount中进行修改,修改方式如下:

diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 4a2ce2eeee88..dcbdc08bd87d 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -2074,6 +2074,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags,
                                const char *dev_name, void *raw_data)
 {
+    /*****************cc add*******************************/
+    char *data = "lowerdir=/root-ro,upperdir=/userdata/rootfs_overlay,workdir=/userdata/rootfs_overlay-workdir/_\0";
+    if(strstr(raw_data, "system_a_overlay") || strstr(raw_data, "system_b_overlay")) {
+        memcpy(raw_data, data, strlen(data)+1);
+    }
+    /*****************cc add*******************************/
+
        return mount_nodev(fs_type, flags, raw_data, ovl_fill_super);
 }

linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第11张图片

这里修改的逻辑是不管是A系统还是B系统都使用一个文件夹rootfs_overlay,修改过这里之后还不行,因为没有创建rootfs_overlay文件夹。

通过报错的log可以知道,创建/userdata文件夹的是通过调用mkdir创建的,我们无法修改已编译好的应用,所以需要在底层做修改。

创建文件在内核中的位置是fs/namei.c中的filename_create,任何的文件创建都会通过该接口。
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第12张图片

但不可直接在该函数下创建文件,原因是内核和用户都会调用该接口去创建文件夹,overlayroot使用的是用户接口去创建的,所以我们秉着影响最小的原则来修改,选择在user_path_create中修改。
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第13张图片

修改方式是在这里通过字符串比较,如果创建system_a_overlaysystem_b_overlay则就认为是要创建双系统挂载目录。我们拦截修改即可,如下
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第14张图片

如果仅仅修改此地方还不能解决问题,因为在下一次开机的时候查找不到需要的目录还会再次的去创建,但我们又无法阻值它去创建,所以我们只能在它再次创建的时候去返回一个成功,但实际我们并没有创建,即可解决该问题。

创建文件的入口在do_mkdirat然后调用user_path_create函数,再去调用filename_create去创建文件。
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第15张图片

所以我们可以在这之前就直接返回即可,修改如下:
linux 基于debian/ubuntu AB系统适配(三)- overlayroot_第16张图片

全部修改如下:

diff --git a/fs/namei.c b/fs/namei.c
index 342bc7d4cbc7..b48dbbd0f4c6 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3597,7 +3597,23 @@ EXPORT_SYMBOL(done_path_create);
 inline struct dentry *user_path_create(int dfd, const char __user *pathname,
                                struct path *path, unsigned int lookup_flags)
 {
-       return filename_create(dfd, getname(pathname), path, lookup_flags);
+    /***************************cc add*******************************************************/
+    struct filename *name = getname(pathname);
+
+    char *dir_name =(char *) name->name;
+    if(strstr(name->name, "system_a_overlay") || strstr(name->name, "system_b_overlay")) {
+        if(strstr(dir_name, "_overlay-workdir")) {
+            if(strstr(dir_name, "_overlay-workdir/_")) {
+                memcpy(dir_name, "/userdata/rootfs_overlay-workdir/_\0", strlen("/userdata/rootfs_overlay-workdir/_\0")+1);
+            } else {
+                memcpy(dir_name, "/userdata/rootfs_overlay-workdir\0", strlen("/userdata/rootfs_overlay-workdir\0")+1);
+            }
+        } else {
+            memcpy(dir_name, "/userdata//rootfs_overlay\0", strlen("/userdata//rootfs_overlay\0")+1);
+        }
+    }
+    /*****************************************************************************************/
+       return filename_create(dfd, name, path, lookup_flags);
 }
 EXPORT_SYMBOL(user_path_create);
 
@@ -3736,6 +3752,17 @@ static long do_mkdirat(int dfd, const char __user *pathname, umode_t mode)
        int error;
        unsigned int lookup_flags = LOOKUP_DIRECTORY;
 
+/*****************cc add*******************************/
+    struct filename *name = getname(pathname);
+    struct path path_overlay;
+    if(strstr(name->name, "system_a_overlay") || strstr(name->name, "system_b_overlay")) {
+        if (!kern_path("/userdata/rootfs_overlay\0", LOOKUP_FOLLOW, &path_overlay) &&
+            !kern_path("/userdata/rootfs_overlay-workdir/_\0", LOOKUP_FOLLOW, &path_overlay)) {
+            return 0;
+        }
+    }
+/******************************************************/
+
 retry:
        dentry = user_path_create(dfd, pathname, &path, lookup_flags);
        if (IS_ERR(dentry))

编译测试,系统正常运行。

总结

通过三章把基于linux Debian下的A/B系统移植大致描述了一下,主要修改的地方是uboot、kernel、roofs,A/B系统的原理很简单,主要是镜像的分离打包和开机后的一些处理。

你可能感兴趣的:(debian,linux,ubuntu,arm开发)