3 个版本

0.1.2 2020 年 1 月 2 日
0.1.1 2020 年 1 月 2 日
0.1.0 2020 年 1 月 2 日

#1219 in 文件系统

MIT 许可证

17KB
225

vblk

Documentation Crates.io

本库提供了一个接口,用于创建虚拟块设备,这些设备在读写操作时调用您的 Rust 代码。这类似于 FUSE,但您只暴露一个固定大小的文件作为块设备,而不是整个文件系统。如果您需要与远程块设备接口,但没有方便的方式挂载它,或者用于原型设计块设备驱动程序,这将很有用。

这是通过(滥用)Linux 内核的 NBD 驱动程序来实现的,而不是实现一个新的通用块设备驱动程序;在 Rust 应用程序中启动了一个基本的 NBD 客户端,用于处理 NBD 请求,这些请求通过 Unix 域套接字在内核驱动程序之间传递。大多数情况下这是通过分叉来完成的;在这个库中,启动了一个单独的线程来托管内核端的 NBD 服务器。

此库仅适用于 Linux 2.6 及以上版本,并且默认情况下需要安装和加载 NBD 内核模块。它还默认需要 root 权限。较新的 NBD 功能,如 FLUSHTRIM 命令,可能仅在较新的 Linux 内核版本中可用;对于较旧版本,这些命令的 Rust 处理器将根本不会被调用。

用法

假设您有适当的权限并且已加载 NBD 模块,以下示例将在 /dev/nbd0 上挂载一个虚拟块设备,该设备将读取整个块设备上的 0xDEADBEEF。由于未指定写入处理程序,因此将拒绝写入块设备的请求。您可以在 examples/deadbeef.rs 中找到并运行此示例,还有一个 ramdisk 示例展示了写入的工作原理,在 examples/ramdisk.rs 中。

use std::io::Error;
use vblk::{mount, BlockDevice};

struct DeadbeefDevice;

impl BlockDevice for DeadbeefDevice {
    fn read(&mut self, offset: u64, bytes: &mut [u8]) -> Result<(), Error> {
        for (index, byte) in bytes.iter_mut().enumerate() {
            *byte = match (index as u64 + offset) % 4 {
                0 => 0xDE,
                1 => 0xAD,
                2 => 0xBE,
                _ => 0xEF,
            };
        }

        Ok(())
    }

    fn block_size(&self) -> u32 {
        1024
    }

    fn blocks(&self) -> u64 {
        4096 // 4MB device
    }
}

fn main() {
    unsafe { mount(&mut DeadbeefDevice, "/dev/nbd0", |_device| Ok(())).unwrap() };
}

启动后,您应该能够访问和读取块设备

# xxd /dev/nbd0 | head -n 2
00000000: dead beef dead beef dead beef dead beef  ................
00000010: dead beef dead beef dead beef dead beef  ................
# blockdev --getsize64 /dev/nbd0
4194304

但尝试写入块设备将失败,因为我们没有提供 write 处理程序,如下所示。请注意,oflag=dsync 参数在这里很重要,否则写入将缓存在内核中,并异步写入,您将在 dmesg 中找到写入错误。

# dd oflag=dsync if=/dev/zero of=/dev/nbd0 bs=1024 count=4096
dd: error writing '/dev/nbd0': Input/output error
1+0 records in
0+0 records out
0 bytes copied, 0.000263995 s, 0.0 kB/s

卸载

虚拟块设备只有在NBD连接优雅地中断时,其unmount处理程序才会被调用,因此默认情况下,仅发送Ctrl+C到您的应用程序不会执行此操作。您传递给mount函数的回调返回一个拥有的Device结构体,该结构体上有一个unmount方法;该结构体旨在存储在某个地方,并在您的应用程序需要关闭时使用。您不得在callback中阻塞;块设备将在返回之前不会挂载。

以下示例显示了如何使用ctrlc包,通过截获Ctrl+C并卸载块设备,这将导致mount方法优雅地返回。

权限

默认情况下,在大多数发行版中,只有root(或disk组的用户)可以与NBD内核模块和NBD块设备交互。我还没有尝试过,但根据您的环境,应该可以通过例如udev规则配置NBD设备驱动程序。

许可证

本软件依据MIT许可证提供。

依赖项

约4.5MB
约96K SLoC