Overlayroot是一个实用工具,允许您创建一个只读的根文件系统和一个可写的覆盖文件系统。这对于创建一个更安全和稳定的系统很有用,因为对系统所做的任何更改都将存储在覆盖文件系统中,可以很容易地丢弃或重置。
在Debian下,分离的系统在/userdata/rootfs_overlay
下,如:在根文件系统下创建一个111
文件夹,实际创建在/userdata/rootfs_overlay
下创建的。
这样在切换A/B系统的时候,更新了文件系统就不会影响到用户数据,因为用户数据是在单独的一个分区。但目前种模式只是在但系统上实现,A/B系统没有实现,我们需要进行修改。
uboot阶段会使用bootargs
给内核传overlayroot
参数,使内核开启overlayroot
。
但启动A/B系统之后会发现,overlayroot
参数不见了,导致内核不能开启overlayroot
。遂,查找原因。
通过不懈的查找发现,在bootargs
中有root
参数,该参数指向的就是所要使用的文件系统。如下:
但在启动A/B后,会去修改root
这个环境变量,修改为A/B中其中一个文件的UUID
,如下:
问题就出现在这里了,在arch\arm\mach-rockchip\board.c
中如果定义了CONFIG_ANDROID_AB
则在bootarge
中寻找root=
,然后删除。问题就出现在这个删除上,因为overlayroot
字段里也有root
就导致了overlayroot
字段也被删除了
所以这里我们需要进行修改,把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);
修改编译后发现overlayroot
字段还是被删除了,思考后想到root
字段删除了但后面还需要补上去的,所以在补上去的时候肯定把overlayroot
字段被覆盖了,所以还得找。
在执行了env_update_filter
之后又运行了ab_update_root_partition
,在函数中又调用了ab_update_root_uuid
,该函数就是用作设置root
字段。
在该函数可以看到又在查找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);
修改uboot启动系统后,overlayroot能够顺利建立了,但新的问题又来了,建立的overlayroot文件名称不对,是以分区名称为前缀建立的,如下
这样会导致切换系统后,用户数据丢失,需要修改,使用mount
命令可以看到,,overlayroot
挂载在/userdata
下,并带有参数:
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
,中
有尝试过直接修改他们的值,但是会导致整个系统崩溃。所以需要找到原始赋值的地方。
数据是通过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);
}
这里修改的逻辑是不管是A系统还是B系统都使用一个文件夹rootfs_overlay
,修改过这里之后还不行,因为没有创建rootfs_overlay
文件夹。
通过报错的log可以知道,创建/userdata
文件夹的是通过调用mkdir
创建的,我们无法修改已编译好的应用,所以需要在底层做修改。
创建文件在内核中的位置是fs/namei.c
中的filename_create
,任何的文件创建都会通过该接口。
但不可直接在该函数下创建文件,原因是内核和用户都会调用该接口去创建文件夹,overlayroot
使用的是用户接口去创建的,所以我们秉着影响最小的原则来修改,选择在user_path_create
中修改。
修改方式是在这里通过字符串比较,如果创建system_a_overlay
或system_b_overlay
则就认为是要创建双系统挂载目录。我们拦截修改即可,如下
如果仅仅修改此地方还不能解决问题,因为在下一次开机的时候查找不到需要的目录还会再次的去创建,但我们又无法阻值它去创建,所以我们只能在它再次创建的时候去返回一个成功,但实际我们并没有创建,即可解决该问题。
创建文件的入口在do_mkdirat
然后调用user_path_create
函数,再去调用filename_create
去创建文件。
全部修改如下:
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系统的原理很简单,主要是镜像的分离打包和开机后的一些处理。