Android init阶段loop回环设备的使用

news/2025/2/21 20:52:25

环回设备是一种特殊的块设备,它允许你将一个普通的文件当作块设备来使用。这种功能在需要将文件系统挂载到一个文件上时特别有用,比如在处理磁盘镜像文件时。

内核驱动:drivers/block/loop.c。 在内核驱动初始化时会自动创建一定数量的loop设备,上层在通过/dev/loop-control节点ioctrl LOOP_CTL_GET_FREE时,如果没有设备会自动创建。

在ramdisk init阶段需要创建/dev/loop-control和/dev/block/loop0-3节点,并绑定内核环回设备:

system/core$
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index d050ed783..b34003635 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -233,7 +233,7 @@ int FirstStageMain(int argc, char** argv) {
     CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
     // Get the basic filesystem setup we need put together in the initramdisk
     // on / and then we'll let the rc file figure out the rest.
-    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
+    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755,size=1024M"));
     CHECKCALL(mkdir("/dev/pts", 0755));
     CHECKCALL(mkdir("/dev/socket", 0755));
     CHECKCALL(mkdir("/dev/dm-user", 0755));
@@ -266,6 +266,12 @@ int FirstStageMain(int argc, char** argv) {
     // This is needed for log wrapper, which gets called before ueventd runs.
     CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
     CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
+    CHECKCALL(mknod("/dev/loop-control", S_IFCHR | 0666, makedev(10, 237)));
+    CHECKCALL(mkdir("/dev/block", 0755));
+    CHECKCALL(mknod("/dev/block/loop0", S_IFBLK | 0755, makedev(7, 0)));
+    CHECKCALL(mknod("/dev/block/loop1", S_IFBLK | 0755, makedev(7, 8)));
+    CHECKCALL(mknod("/dev/block/loop2", S_IFBLK | 0755, makedev(7, 16)));
+    CHECKCALL(mknod("/dev/block/loop3", S_IFBLK | 0755, makedev(7, 24)));
  • mknod(const char *pathname, mode_t mode, dev_t dev) 是一个系统调用,用于创建一个文件系统节点(文件、设备特殊文件或命名管道)。

  • 参数解释:

    • "/dev/block/loop0":这是要创建的设备文件的路径。
    • S_IFBLK | 0755:这是文件的模式。S_IFBLK 表示创建的是一个块设备,0755 是文件的权限(所有者可读、写、执行,其他用户可读和执行)。
    • makedev(7, 0):这是设备号,makedev 函数用于生成设备号,7 是主设备号,0 是次设备号。对于环回设备,主设备号通常是 7。 主从设备号需要从机器里面通过ls -la /dev/block/loop*查看,不同的内核版本可能不一样
rk3588_t:/ $ ls -l /dev/block/loop*
brwxr-xr-x 1 root root 7,   0 2025-01-13 00:17 /dev/block/loop0
brwxr-xr-x 1 root root 7,   8 2025-01-13 00:17 /dev/block/loop1
brw------- 1 root root 7,  80 2025-01-13 00:18 /dev/block/loop10
brw------- 1 root root 7,  88 2025-01-13 00:18 /dev/block/loop11
brw------- 1 root root 7,  96 2025-01-13 00:18 /dev/block/loop12
brw------- 1 root root 7, 104 2025-01-13 00:18 /dev/block/loop13
brw------- 1 root root 7, 112 2025-01-13 00:18 /dev/block/loop14
brw------- 1 root root 7, 120 2025-01-13 00:18 /dev/block/loop15
brwxr-xr-x 1 root root 7,  16 2025-01-13 00:17 /dev/block/loop2
brwxr-xr-x 1 root root 7,  24 2025-01-13 00:18 /dev/block/loop3
brw------- 1 root root 7,  32 2025-01-13 00:18 /dev/block/loop4
brw------- 1 root root 7,  40 2025-01-13 00:18 /dev/block/loop5
brw------- 1 root root 7,  48 2025-01-13 00:18 /dev/block/loop6
brw------- 1 root root 7,  56 2025-01-13 00:18 /dev/block/loop7
brw------- 1 root root 7,  64 2025-01-13 00:18 /dev/block/loop8
brw------- 1 root root 7,  72 2025-01-13 00:18 /dev/block/loop9

init中编写代码将镜像文件绑定到loop设备上面

std::string setup_loop_device(const std::string& file_path) {
       std::string system_device;
    if (Loop::create(file_path, system_device) != 0) {
        LOG(ERROR) << "Failed to create loop device file_path";
        return nullptr;
    }
    LOG(INFO) << "created loop device: " << system_device;
    return system_device;
}

int Loop::create(const std::string& target, std::string& out_device) {
    unique_fd ctl_fd(open("/dev/loop-control", O_RDWR | O_CLOEXEC));
    if (ctl_fd.get() == -1) {
        PLOG(ERROR) << "Failed to open loop-control";
        return -errno;
    }

    int num = ioctl(ctl_fd.get(), LOOP_CTL_GET_FREE);
    if (num == -1) {
        PLOG(ERROR) << "Failed LOOP_CTL_GET_FREE";
        
		for (size_t id = 0; id < 3; ++id) {
			int ret = ioctl(ctl_fd.get(), LOOP_CTL_ADD, id);
			LOG(INFO) << "LOOP_CTL_ADD ADD ID=" << id;
			if (ret < 0 && errno != EEXIST) {
				LOG(ERROR) << "Failed LOOP_CTL_ADD";
				return -errno;
			}
		}
		num = ioctl(ctl_fd.get(), LOOP_CTL_GET_FREE);
		if (num == -1) {
			PLOG(ERROR) << "Failed LOOP_CTL_GET_FREE";       
			return -errno;
		}
	}

    out_device = StringPrintf("/dev/block/loop%d", num);

    unique_fd target_fd;
    for (size_t i = 0; i != kLoopDeviceRetryAttempts; ++i) {
        target_fd.reset(open(target.c_str(), O_RDWR | O_CLOEXEC));
        if (target_fd.get() != -1) {
            break;
        }
        usleep(50000);
    }
    if (target_fd.get() == -1) {
        PLOG(ERROR) << "Failed to open " << target;
        return -errno;
    }
    if (!android::fs_mgr::WaitForFile(out_device, 2s)) {
        LOG(ERROR) << "Failed to find " << out_device;
        return -ENOENT;
    }
    unique_fd device_fd(open(out_device.c_str(), O_RDWR | O_CLOEXEC));
    if (device_fd.get() == -1) {
        PLOG(ERROR) << "Failed to open " << out_device;
        return -errno;
    }

    if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get()) == -1) {
        PLOG(ERROR) << "Failed to LOOP_SET_FD";
        return -errno;
    }

    struct loop_info64 li;
    memset(&li, 0, sizeof(li));
    strlcpy((char*)li.lo_crypt_name, kVoldPrefix, LO_NAME_SIZE);
    if (ioctl(device_fd.get(), LOOP_SET_STATUS64, &li) == -1) {
        PLOG(ERROR) << "Failed to LOOP_SET_STATUS64";
        return -errno;
    }

    return 0;
}

http://www.niftyadmin.cn/n/5861309.html

相关文章

【数据结构-并查集】力扣1202. 交换字符串中的元素

给你一个字符串 s&#xff0c;以及该字符串中的一些「索引对」数组 pairs&#xff0c;其中 pairs[i] [a, b] 表示字符串中的两个索引&#xff08;编号从 0 开始&#xff09;。 你可以 任意多次交换 在 pairs 中任意一对索引处的字符。 返回在经过若干次交换后&#xff0c;s …

html - - - - - modal弹窗出现时,页面怎么能限制滚动

html - - - - - 弹出出现时&#xff0c;页面怎么能限制滚动 1. 全局添加css样式2. 更改弹窗状态时的操作 1. 全局添加css样式 .no-scroll {overflow: hidden;height: 100vh; /* 防止移动端地址栏隐藏导致的页面跳动 */ }2. 更改弹窗状态时的操作 if(show){// 打开弹窗&#…

OpenCV中的边缘检测

边缘检测是图像处理和计算机视觉中的关键技术之一&#xff0c;旨在识别图像中像素强度发生显著变化的区域&#xff0c;这些区域通常对应于物体的边界或轮廓。边缘检测在机器视觉中具有重要的需求背景&#xff0c;主要体现在以下几个方面&#xff1a; 图像分割&#xff1a;边缘…

Linux 下 VIM 编辑器学习记录:从基础到进阶(中)

在 Linux 系统的学习与实践过程中&#xff0c;对文件内容的查看是一项极为基础且高频的操作。熟练掌握各类内容查看命令&#xff0c;不仅能提升我们在 Linux 环境下的工作效率&#xff0c;对于学习 Java 全栈开发的同学来说&#xff0c;在处理项目相关的配置文件、日志文件时也…

C#的序列化[Serializable()]

[Serializable] 是 .NET 框架中的一个特性&#xff08;Attribute&#xff09;&#xff0c;用于标记一个类、结构体、枚举或委托可以被序列化。序列化是将对象的状态转换为可以存储或传输的格式&#xff08;如二进制、XML 或 JSON&#xff09;的过程&#xff0c;以便在需要时可以…

C语言学习,插入排序

C语言&#xff0c;插入排序是一种简单直观的排序算法&#xff0c;插入排序是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。 示例&#xff1a; // 插入排序函数 void insertionSort(int arr[], int n)…

ref() 和 reactive()响应性 浅解

文章目录 1. ref() 和 reactive() 的区别2. 解构 详解2.1. 什么是解构2.2. 解构避免丢失响应性的办法2.2.1. 解决方案&#xff1a;toRefs() 保持响应性2.2.2. 解决方案&#xff1a; toRef()保持响应性 3. 最佳实践 在 Vue 3 中&#xff0c;ref() 和 reactive() 都是用于响应式数…

2024华为OD机试真题-恢复数字序列(C++/Java/Python)-E卷-100分

2024华为OD机试最新E卷题库-(C卷+D卷+E卷)-(JAVA、Python、C++) 目录 题目描述 输入描述 输出描述 用例1 题目解析 代码 c++ Java python 题目描述 对于一个连续正整数组成的序列,可以将其拼接成一个字符串,再将字符串里的部分字符打乱顺序。 如序列8 9 10 11 12…