为 macOS 开发用户空间只读磁盘驱动程序

Develop a userspace read-only disk driver for macOS

我有一种特殊的磁盘映像格式(压缩并带有坏块等额外信息),并希望在 macOS 中将其用作块设备(例如 /dev/rdisk)。

我正在寻找有关如何编写只读块级用户空间驱动程序的指示。

我认为我需要使用 IOKit 来完成此操作,但该领域的文档非常稀少,据我所知没有为此提供示例代码。

我看过 Amit Singh 的 Mac OS Internals,但那只是关于过滤通过添加的驱动程序路由的现有块。但是我需要从一个单独的文件中读取数据,即我需要使用文件系统,如果可能的话,我也想在用户空间应用程序中执行此操作,因为制作 kext 既难以调试,现在也被弃用了.

理想情况下,这应该适用于当前的 macOS 版本,包括 10.15,但我也很高兴有一个解决方案只适用于从 10.6 开始的旧 macOS 版本。

也许我还是对这里有些误解。我的印象是甚至在 10.15 之前就可以编写用户空间 IOKit 驱动程序。但也许这对 Block Storage 设备来说是不可能的?欢迎任何澄清。我觉得很失落。

更新 2020 年 6 月 22 日

我也在 OSXFUSE 支持论坛上问过这个问题,在那里得到了 negative reply

Maybe I am still misunderstanding some things here. I was under the impression that it was possible even before 10.15 to write userspace IOKit drivers.

这是一个正确的说法,但这并不意味着可以在用户space中编写所有类型的驱动程序,即使是 10.15。混淆可能源于这样一个事实,即 "IOKit" 在不同的上下文中用于表示微妙不同的事物:

  • 从根本上说,它是XNU内核(macOS,iOS等)内置的基于OOP的驱动栈技术,用C++编写。它的核心数据结构是 I/O Kit 注册表,它是表示设备、设备(子)功能、虚拟设备、服务、驱动程序、用户 space 客户端连接等对象的有向图(它的大部分都有它的属性,但节点在技术上可以有多个 "parent" 节点,有些实际上在典型的 Mac 系统上这样做)
  • IOKit 用户 space 库。 (a.k.a。IOKit.framework a.k.a。IOKitLib + 各种 IOCFPlugins)这些向用户 查看 内核的 I/O 注册表82=] 程序,并允许它们与 IOKit 对象执行某些交互。这基本上限于:
    • 迭代注册表图并根据匹配规则匹配服务。 return 都处理用户 space 程序的特定 I/O 注册表项。
    • 获取 I/O 注册表项的属性,如果对象明确支持它,则设置(某些)属性。
    • 正在创建用户客户端连接。这是从普通用户 space 在 I/O 注册表中 创建 条目的唯一方法,并且这些条目始终是 IOUserClient subclasses,并且仅当父对象明确允许时。
  • IOKit 视图可用于基于 DriverKit 的系统扩展进程(10.15 中的新增功能),它允许实例化完全由用户 space 支持的注册表项对象,用于子class(有界)选择超级 class 类型。

回答您具体案例的问题:

  • 要实现块存储设备的驱动程序,在系统中显示为与任何其他设备一样,您需要子class IOStorage 或其子classes (通常 IOBlockStorageDevice).
  • IOKit.framework 允许您创建用户 space 驱动程序,它将 由该用户 space 进程或其他用户 space 进程。 因此无法使用它来创建将由内核的某些部分使用的驱动程序。例如,不可能创建将由内核的 VFS 系统使用的块设备驱动程序。
  • DriverKit 目前(从 macOS 10.15.4 SDK 开始)不允许任何存储堆栈的 classes 的 dext-backed subclasses。所以dext解决不了问题
  • 据我所知,做你想做的事情的唯一方法是拥有一个由两部分组成的驱动程序:一个实现 IOBlockStorageDevice subclass 的 kext ,以及为某些用户 space 进程提供用户客户端接口以创建块设备实例的服务,该服务将处理对设备的 I/O 请求。这是一种类似于 FUSE 机制的机制,它在 VFS 层而不是块层工作。
    这正是 macOS 的内置磁盘映像挂载系统的工作方式。不幸的是,它的 kext 和用户 space 部分都不是开源的、文档化的或 public API。因此,除非您对其进行逆向工程并找到一个不强制执行 Apple 代码签名要求的边界,否则您基本上必须重新实现它。

我目前并不知道现有的开源系统可以执行此操作,但我并没有仔细研究过。特别是对于只读访问,如果你知道自己在做什么,应该不会很难做到,但它伴随着开发和分发 kexts 的常见缺点。

请注意,如果 FUSE 代表的块设备托管了您希望挂载的文件系统,并且存在该文件系统的 FUSE 实现,您也可以使用 FUSE 打开磁盘映像。