vblk 0.1.2

Virtual block devices on Linux
Documentation
use nix::{errno, libc::ioctl, request_code_none};
use std::fs::File;
use std::io::{Error, ErrorKind, Result};
use std::os::unix::io::{AsRawFd, RawFd};
use zerocopy::{AsBytes, FromBytes};

macro_rules! nbd_ioctl {
    ($name:ident, $code:literal $(, $($arg:ident : $argty:ty),*)?) => {
        pub unsafe fn $name(file: &File$(, $($arg : $argty)*,)?) -> Result<()> {
            let _ = errno::Errno::result(
                ioctl(file.as_raw_fd(), request_code_none!(0xab, $code)$(, $($arg)*,)?)
            ).map_err(nix_error)?;

            Ok(())
        }
    };
}

nbd_ioctl!(set_sock, 0, sock: RawFd);
nbd_ioctl!(set_blksize, 1, blksize: u32);
nbd_ioctl!(do_it, 3);
nbd_ioctl!(clear_sock, 4);
nbd_ioctl!(clear_que, 5);
nbd_ioctl!(set_size_blocks, 7, size_blocks: u64);
nbd_ioctl!(disconnect, 8);
nbd_ioctl!(set_timeout, 9, timeout: u64);
nbd_ioctl!(set_flags, 10, flags: u64);

fn nix_error(error: nix::Error) -> Error {
    match error {
        nix::Error::Sys(errno) => Error::from_raw_os_error(errno as i32),
        nix::Error::UnsupportedOperation => panic!("unsupported by nix"),
        nix::Error::InvalidPath => Error::from(ErrorKind::InvalidInput),
        nix::Error::InvalidUtf8 => Error::from(ErrorKind::InvalidData),
    }
}

#[repr(C)]
#[derive(AsBytes, FromBytes, Default, Debug)]
pub struct Request {
    magic: u32,
    kind: u32,
    handle: u64,
    from: u64,
    len: u32,
    padding: u32,
}

pub const REQUEST_LEN: usize = std::mem::size_of::<Request>() - 4;

impl Request {
    pub fn is_magic_valid(&self) -> bool {
        self.magic == REQUEST_MAGIC.to_be()
    }

    pub fn new_reply_for_request(&self) -> Reply {
        Reply {
            magic: REPLY_MAGIC.to_be(),
            handle: self.handle,
            error: 0,
        }
    }

    pub fn command(&self) -> Command {
        match u32::from_be(self.kind) {
            CMD_READ => Command::Read,
            CMD_WRITE => Command::Write,
            CMD_DISC => Command::Disconnect,
            CMD_FLUSH => Command::Flush,
            CMD_TRIM => Command::Trim,
            _ => Command::Unknown,
        }
    }

    pub fn offset(&self) -> u64 {
        u64::from_be(self.from)
    }

    pub fn len(&self) -> u32 {
        u32::from_be(self.len)
    }
}

#[repr(C)]
#[derive(AsBytes, FromBytes, Debug)]
pub struct Reply {
    magic: u32,
    error: u32,
    handle: u64,
}

impl Reply {
    pub fn set_errno(&mut self, errno: i32) {
        self.error = (errno as u32).to_be();
    }
}

const REQUEST_MAGIC: u32 = 0x2560_9513;
const REPLY_MAGIC: u32 = 0x6744_6698;

const CMD_READ: u32 = 0;
const CMD_WRITE: u32 = 1;
const CMD_DISC: u32 = 2;
const CMD_FLUSH: u32 = 3;
const CMD_TRIM: u32 = 4;

pub const SEND_FLUSH: u64 = 1 << 2;
pub const SEND_TRIM: u64 = 1 << 5;

#[derive(Clone, Copy, Debug)]
pub enum Command {
    Read,
    Write,
    Disconnect,
    Flush,
    Trim,
    Unknown,
}