#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(missing_docs)]
use bitflags::bitflags;
#[cfg(feature = "nix")]
pub use nix::sys::socket::SockaddrLike;
use std::{
ffi::{CStr, CString},
error,
fmt,
io,
mem,
os::raw::{c_char, c_int, c_double, c_void},
ptr,
slice,
sync::Once,
};
#[derive(Debug)]
pub struct Error {
source: Box<dyn error::Error + 'static>,
errno: i32
}
impl Error {
fn errno(&self) -> i32 {
self.errno
}
pub fn new<E>(errno: i32, error: E) -> Error
where E: Into<Box<dyn error::Error + 'static>>
{
Error {
source: error.into(),
errno
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&*self.source)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.source.fmt(f)
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
Error {
errno: e.raw_os_error().unwrap_or(0),
source: Box::new(e),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
static mut INITIALIZED: bool = false;
mod unreachable {
use super::*;
pub(super) fn config(_k: &str, _v: &str) -> Result<()> { unreachable!() }
pub(super) fn config_complete() -> Result<()> { unreachable!() }
pub(super) fn dump_plugin() { unreachable!() }
pub(super) fn open(_: bool) -> Result<Box<dyn Server>> { unreachable!() }
pub(super) fn preconnect(_: bool) -> Result<()> { unreachable!() }
pub(super) fn thread_model() -> Result<ThreadModel> { unreachable!() }
}
static mut AFTER_FORK: fn() -> Result<()> = unreachable::config_complete;
static mut CONFIG: fn(k: &str, v: &str) -> Result<()> = unreachable::config;
static mut CONFIG_COMPLETE: fn() -> Result<()> = unreachable::config_complete;
static mut CONFIG_HELP: Vec<u8> = Vec::new();
static mut DESCRIPTION: Vec<u8> = Vec::new();
static mut DUMP_PLUGIN: fn() = unreachable::dump_plugin;
static mut GET_READY: fn() -> Result<()> = unreachable::config_complete;
static mut LOAD: fn() = unreachable::dump_plugin;
static mut LONGNAME: Vec<u8> = Vec::new();
static mut MAGIC_CONFIG_KEY: Vec<u8> = Vec::new();
static mut NAME: Vec<u8> = Vec::new();
static mut OPEN: fn(readonly: bool) -> Result<Box<dyn Server>> = unreachable::open;
static mut PRECONNECT: fn(readonly: bool) -> Result<()> = unreachable::preconnect;
static mut THREAD_MODEL: fn() -> Result<ThreadModel> = unreachable::thread_model;
static mut UNLOAD: fn() = unreachable::dump_plugin;
static mut VERSION: Vec<u8> = Vec::new();
static INIT: Once = Once::new();
bitflags! {
pub struct Flags: u32 {
const MAY_TRIM = 0b00000001;
const FUA = 0b00000010;
const REQ_ONE = 0b00000100;
const FAST_ZERO = 0b00001000;
}
}
#[repr(i32)]
pub enum CacheFlags {
None = 0,
Emulate = 1,
Native = 2,
}
impl From<CacheFlags> for i32 {
fn from(cf: CacheFlags) -> Self {
cf as Self
}
}
#[repr(i32)]
pub enum FuaFlags {
None = 0,
Emulate = 1,
Native = 2,
}
impl From<FuaFlags> for i32 {
fn from(cf: FuaFlags) -> Self {
cf as Self
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum ThreadModel {
SerializeConnections = 0,
SerializeAllRequests = 1,
SerializeRequests = 2,
Parallel = 3,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u32)]
pub enum ExtentType {
Allocated = 0,
Hole = 1,
Zero = 2,
HoleZero = 3
}
#[derive(Debug)]
pub struct ExtentHandle(*mut c_void);
impl ExtentHandle {
pub fn add(&mut self, offset: u64, len: u64, et: ExtentType) -> Result<()> {
let r = unsafe { nbdkit_add_extent(self.0, offset, len, et as u32) };
match r {
0 => Ok(()),
-1 => Err(io::Error::last_os_error().into()),
x => panic!("Undocumented return value {} from nbdkit_add_extent", x)
}
}
}
mod ffi {
use super::*;
pub(super) extern fn after_fork() -> c_int {
match unsafe { AFTER_FORK() } {
Ok(()) => 0,
Err(e) => {
set_error(e);
-1
}
}
}
macro_rules! can_method {
( $meth:ident ) => {
pub(super) extern fn $meth(h: *mut c_void) -> c_int {
let server = unsafe { downcast(h) };
match server.$meth() {
Err(e) => {
set_error(e);
-1
},
Ok(x) => x.into(),
}
}
}
}
macro_rules! trim_like {
( $meth:ident) => {
pub(super) extern fn $meth(h: *mut c_void,
count: u32,
offset: u64,
rawflags: u32) -> c_int
{
let server = unsafe { downcast(h) };
let flags = Flags::from_bits(rawflags)
.expect(&format!("Unknown flags value {:#x?}", rawflags));
match server.$meth(count, offset, flags) {
Ok(()) => 0,
Err(e) => {
set_error(e);
-1
}
}
}
}
}
pub(super) extern fn cache(h: *mut c_void,
count: u32,
offset: u64,
_rawflags: u32) -> c_int
{
let server = unsafe { downcast(h) };
match server.cache(count, offset) {
Ok(()) => 0,
Err(e) => {
set_error(e);
-1
}
}
}
pub(super) extern fn close(selfp: *mut c_void) {
unsafe {
drop(Box::from_raw(selfp as *mut Box<dyn Server>));
}
}
can_method!(can_cache);
can_method!(can_extents);
can_method!(can_flush);
can_method!(can_fua);
can_method!(can_fast_zero);
can_method!(can_multi_conn);
can_method!(can_write);
can_method!(can_trim);
can_method!(can_zero);
pub(super) extern fn config(k: *const c_char, v: *const c_char) -> c_int {
let key = match unsafe { CStr::from_ptr(k) }.to_str() {
Ok(s) => s,
Err(e) => {
let error = Error::new(libc::EINVAL, e.to_string());
set_error(error);
return -1;
}
};
let value = match unsafe { CStr::from_ptr(v) }.to_str() {
Ok(s) => s,
Err(e) => {
let error = Error::new(libc::EINVAL, e.to_string());
set_error(error);
return -1;
}
};
match unsafe { CONFIG(key, value) } {
Ok(()) => 0,
Err(e) => {
set_error(e);
-1
}
}
}
pub(super) extern fn config_complete() -> c_int {
match unsafe { CONFIG_COMPLETE() } {
Ok(()) => 0,
Err(e) => {
set_error(e);
-1
}
}
}
unsafe fn downcast<'a>(p: *const c_void) -> &'a dyn Server {
&**(p as *const Box<dyn Server>)
}
pub(super) extern fn dump_plugin() {
unsafe { DUMP_PLUGIN() }
}
pub(super) extern fn extents(h: *mut c_void,
count: u32,
offset: u64,
rawflags: u32,
extents: *mut c_void ) -> c_int
{
let server = unsafe { downcast(h) };
let mut exh = ExtentHandle(extents);
let flags = Flags::from_bits(rawflags).expect("Unknown extents flags");
match server.extents(count, offset, flags, &mut exh) {
Ok(()) => 0,
Err(e) => {
set_error(e);
-1
}
}
}
pub(super) extern fn flush(h: *mut c_void, _rawflags: u32) -> c_int {
let server = unsafe { downcast(h) };
match server.flush() {
Ok(()) => 0,
Err(e) => {
set_error(e);
-1
}
}
}
pub(super) extern fn get_ready() -> c_int {
match unsafe { GET_READY() } {
Ok(()) => 0,
Err(e) => {
set_error(e);
-1
}
}
}
pub(super) extern fn get_size(h: *mut c_void) -> i64 {
let server = unsafe { downcast(h) };
match server.get_size() {
Ok(size) => size,
Err(e) => {
set_error(e);
-1
}
}
}
can_method!(is_rotational);
pub(super) extern fn load() {
unsafe { LOAD() }
}
pub(super) extern fn open(readonly: c_int) -> *mut c_void {
match unsafe { OPEN(readonly != 0) } {
Ok(server) =>
Box::into_raw(Box::new(server)) as *mut c_void,
Err(e) => {
set_error(e);
ptr::null_mut()
}
}
}
pub(super) extern fn pread(h: *mut c_void,
bufp: *mut c_char,
count: u32,
offset: u64,
_flags: u32) -> c_int
{
let server = unsafe { downcast(h) };
let buf = unsafe {
slice::from_raw_parts_mut(bufp as *mut u8, count as usize)
};
match server.read_at(buf, offset) {
Ok(()) => 0,
Err(e) => {
set_error(e);
-1
}
}
}
pub(super) extern fn preconnect(readonly: c_int) -> c_int {
match unsafe { PRECONNECT(readonly != 0) } {
Ok(()) => 0,
Err(e) => {
set_error(e);
-1
}
}
}
pub(super) extern fn pwrite(h: *mut c_void,
bufp: *const c_char,
count: u32,
offset: u64,
rawflags: u32) -> c_int
{
let server = unsafe { downcast(h) };
let buf = unsafe {
slice::from_raw_parts(bufp as *mut u8, count as usize)
};
let flags = Flags::from_bits(rawflags).expect("Unknown pwrite flags");
match server.write_at(buf, offset, flags) {
Ok(()) => 0,
Err(e) => {
set_error(e);
-1
}
}
}
fn set_error(e: Error) {
let fmt = CString::new("%s").unwrap();
let msg = CString::new(e.to_string()).expect("CString::new");
unsafe {
nbdkit_error(fmt.as_ptr(), msg.as_ptr());
nbdkit_set_error(e.errno());
}
}
pub(super) extern fn thread_model() -> c_int {
match unsafe { THREAD_MODEL() } {
Ok(x) => x as c_int,
Err(e) => {
set_error(e);
-1
}
}
}
trim_like!(trim);
pub(super) extern fn unload() {
unsafe { UNLOAD() }
}
trim_like!(zero);
}
#[allow(unused_variables)]
pub trait Server {
fn after_fork() -> Result<()> where Self: Sized { unimplemented!() }
fn cache(&self, count: u32, offset: u64) -> Result<()> {
unimplemented!()
}
fn can_cache(&self) -> Result<CacheFlags> { unimplemented!() }
fn can_extents(&self) -> Result<bool> { unimplemented!() }
fn can_flush(&self) -> Result<bool> { unimplemented!() }
fn can_fast_zero(&self) -> Result<bool> { unimplemented!() }
fn can_fua(&self) -> Result<FuaFlags> { unimplemented!() }
fn can_multi_conn(&self) -> Result<bool> { unimplemented!() }
fn can_trim(&self) -> Result<bool> { unimplemented!() }
fn can_write(&self) -> Result<bool> { unimplemented!() }
fn can_zero(&self) -> Result<bool> { unimplemented!() }
fn config(key: &str, value: &str) -> Result<()> where Self: Sized {
unimplemented!()
}
fn config_complete() -> Result<()> where Self: Sized { unimplemented!() }
fn dump_plugin() where Self: Sized { unimplemented!() }
fn config_help() -> Option<&'static str> where Self: Sized { None }
fn description() -> Option<&'static str> where Self: Sized { None }
fn extents(&self,
count: u32,
offset: u64,
flags: Flags,
extent_handle: &mut ExtentHandle)
-> Result<()>
{
unimplemented!()
}
fn flush(&self) -> Result<()> { unimplemented!() }
fn get_ready() -> Result<()> where Self: Sized { unimplemented!() }
fn get_size(&self) -> Result<i64>;
fn is_rotational(&self) -> Result<bool> { unimplemented!() }
fn load() where Self: Sized { unimplemented!() }
fn longname() -> Option<&'static str> where Self: Sized { None }
fn magic_config_key() -> Option<&'static str> where Self: Sized { None }
fn name() -> &'static str where Self: Sized;
fn open(readonly: bool) -> Result<Box<dyn Server>> where Self: Sized;
#[cfg_attr(feature = "nix", doc = "[`peername`]")]
#[cfg_attr(not(feature = "nix"), doc = "the peer name")]
fn preconnect(readonly: bool) -> Result<()> where Self: Sized {
unimplemented!()
}
fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<()>;
fn thread_model() -> Result<ThreadModel> where Self: Sized { unimplemented!() }
fn trim(&self, count: u32, offset: u64, flags: Flags) -> Result<()> {
unimplemented!()
}
fn unload() where Self: Sized { unimplemented!() }
fn version() -> Option<&'static str> where Self: Sized { None }
fn write_at(&self, buf: &[u8], offset: u64, flags: Flags) -> Result<()> {
unimplemented!()
}
fn zero(&self, count: u32, offset: u64, flags: Flags) -> Result<()> {
unimplemented!()
}
}
macro_rules! opt_method {
( $self:ident, $method:ident ) => {
if $self.$method {Some(ffi::$method)} else {None}
};
( $self:ident, $method:ident, $ffi_method:ident ) => {
if $self.$method {Some(ffi::$ffi_method)} else {None}
}
}
#[doc(hidden)]
#[derive(Default)]
pub struct Builder {
pub after_fork: bool,
pub cache: bool,
pub can_cache: bool,
pub can_extents: bool,
pub can_flush: bool,
pub can_fast_zero: bool,
pub can_fua: bool,
pub can_multi_conn: bool,
pub can_trim: bool,
pub can_write: bool,
pub can_zero: bool,
pub config: bool,
pub config_complete: bool,
pub config_help: bool,
pub dump_plugin: bool,
pub extents: bool,
pub flush: bool,
pub get_ready: bool,
pub is_rotational: bool,
pub load: bool,
pub preconnect: bool,
pub thread_model: bool,
pub trim: bool,
pub unload: bool,
pub write_at: bool,
pub zero: bool,
}
impl Builder {
#[doc(hidden)]
pub fn into_ptr<S: Server>(self) -> *const Plugin {
INIT.call_once(|| {
unsafe {
assert!(!INITIALIZED);
INITIALIZED = true;
CONFIG = S::config;
CONFIG_COMPLETE = S::config_complete;
DUMP_PLUGIN = S::dump_plugin;
GET_READY = S::get_ready;
AFTER_FORK = S::after_fork;
LOAD = S::load;
OPEN = S::open;
PRECONNECT = S::preconnect;
THREAD_MODEL = S::thread_model;
UNLOAD = S::unload;
NAME = CString::new(S::name()).unwrap().into_bytes_with_nul();
if let Some(s) = S::config_help() {
CONFIG_HELP = CString::new(s).unwrap().into_bytes_with_nul();
}
if let Some(s) = S::description() {
DESCRIPTION = CString::new(s).unwrap().into_bytes_with_nul();
}
if let Some(s) = S::longname() {
LONGNAME = CString::new(s).unwrap().into_bytes_with_nul();
}
if let Some(s) = S::magic_config_key() {
MAGIC_CONFIG_KEY = CString::new(s).unwrap()
.into_bytes_with_nul();
}
if let Some(s) = S::version() {
VERSION = CString::new(s).unwrap().into_bytes_with_nul();
}
};
});
let config_help = S::config_help()
.map(|_| unsafe {CONFIG_HELP.as_ptr()} as *const c_char)
.unwrap_or(ptr::null());
let description = S::description()
.map(|_| unsafe {DESCRIPTION.as_ptr()} as *const c_char)
.unwrap_or(ptr::null());
let longname = S::longname()
.map(|_| unsafe {LONGNAME.as_ptr()} as *const c_char)
.unwrap_or(ptr::null());
let magic_config_key = S::magic_config_key()
.map(|_| unsafe {MAGIC_CONFIG_KEY.as_ptr()} as *const c_char)
.unwrap_or(ptr::null());
let version = S::version()
.map(|_| unsafe {VERSION.as_ptr()} as *const c_char)
.unwrap_or(ptr::null());
let plugin = Plugin {
_struct_size: mem::size_of::<Plugin>() as u64,
_api_version: 2,
_thread_model: ThreadModel::Parallel as c_int,
name: unsafe{ NAME.as_ptr() } as *const c_char,
longname,
version,
description,
load: opt_method!(self, load),
unload: opt_method!(self, unload),
config: opt_method!(self, config),
config_complete: opt_method!(self, config_complete),
config_help,
open: ffi::open,
close: ffi::close,
get_size: ffi::get_size,
can_write: opt_method!(self, can_write),
can_flush: opt_method!(self, can_flush),
is_rotational: opt_method!(self, is_rotational),
can_trim: opt_method!(self, can_trim),
_pread_v1: None,
_pwrite_v1: None,
_flush_v1: None,
_trim_v1: None,
_zero_v1: None,
errno_is_preserved: 0,
dump_plugin: opt_method!(self, dump_plugin),
can_zero: opt_method!(self, can_zero),
can_fua: opt_method!(self, can_fua),
pread: ffi::pread,
pwrite: opt_method!(self, write_at, pwrite),
flush: opt_method!(self, flush),
trim: opt_method!(self, trim),
zero: opt_method!(self, zero),
magic_config_key,
can_multi_conn: opt_method!(self, can_multi_conn),
can_extents: opt_method!(self, can_extents),
extents: opt_method!(self, extents),
can_cache: opt_method!(self, can_cache),
cache: opt_method!(self, cache),
thread_model: opt_method!(self, thread_model),
can_fast_zero: opt_method!(self, can_fast_zero),
preconnect: opt_method!(self, preconnect),
get_ready: opt_method!(self, get_ready),
after_fork: opt_method!(self, after_fork)
};
Box::into_raw(Box::new(plugin))
}
#[doc(hidden)]
pub fn new() -> Builder
{
Builder::default()
}
}
extern "C" {
fn nbdkit_add_extent(extents: *mut c_void, offset: u64, length: u64,
ty: u32) -> c_int;
#[doc(hidden)]
pub fn nbdkit_debug(fmt: *const c_char, ...);
fn nbdkit_error(fmt: *const c_char, ...);
fn nbdkit_export_name() -> *const c_char;
fn nbdkit_set_error(errno: c_int);
#[cfg(feature = "nix")]
fn nbdkit_peer_name( addr: *mut libc::sockaddr,
addrlen: *mut libc::socklen_t) -> c_int;
fn nbdkit_shutdown();
fn nbdkit_disconnect(force: bool);
fn nbdkit_stdio_safe() -> c_int;
fn nbdkit_is_tls() -> c_int;
fn nbdkit_parse_bool(s: *const c_char) -> c_int;
fn nbdkit_parse_size(s: *const c_char) -> i64;
fn nbdkit_parse_probability(what: *const c_char,
s: *const c_char,
retp: *mut c_double) -> c_int;
}
#[macro_export]
macro_rules! debug {
($($args:tt)*) => {
match ::std::ffi::CString::new(format!($($args)*)) {
Ok(msg) =>
unsafe {
$crate::nbdkit_debug(b"%s\0".as_ptr()
as *const ::libc::c_char,
msg.as_ptr());
},
Err(_) =>
()
}
}
}
pub fn export_name() -> std::result::Result<String, Box<dyn error::Error>> {
unsafe {
let p = nbdkit_export_name();
if p.is_null() {
return Err("No export name available".into());
}
CStr::from_ptr(p)
}.to_str()
.map(|s| s.to_owned())
.map_err(|e| Box::new(e) as Box<dyn error::Error>)
}
pub fn is_stdio_safe() -> bool {
unsafe { nbdkit_stdio_safe() != 0 } }
pub fn is_tls() -> Result<bool> {
let r = unsafe { nbdkit_is_tls() };
match r {
0 => Ok(false),
x if x > 0 => Ok(true),
_ =>
Err(Error::new(libc::EINVAL, "nbdkit_is_tls failed"))
}
}
#[cfg(feature = "nix")]
pub fn peername<T: SockaddrLike>() -> std::result::Result<T, Box<dyn error::Error>> {
let mut s = mem::MaybeUninit::<T>::uninit();
let mut len = T::size();
unsafe {
let sa = s.as_mut_ptr() as *mut libc::sockaddr;
let r = nbdkit_peer_name(sa, &mut len as *mut _);
if r == -1 {
return Err("No peer name available".into());
}
T::from_raw(s.assume_init().as_ptr(), Some(len)).ok_or_else(|| {
Box::new(Error::new(
libc::EINVAL,
"Invalid sockaddr returned by nbdkit_peer_name"
)) as Box<dyn error::Error>
})
}
}
pub fn shutdown() {
unsafe { nbdkit_shutdown() };
}
pub fn disconnect(force: bool) {
unsafe { nbdkit_disconnect(force) };
}
pub fn parse_bool(s: &str) -> Result<bool> {
let cs = CString::new(s).unwrap();
let r = unsafe { nbdkit_parse_bool(cs.as_ptr()) };
match r {
-1 =>
Err(Error::new(libc::EINVAL,
"failed to parse boolean using nbdkit_parse_bool")),
0 => Ok(false),
_ => Ok(true)
}
}
pub fn parse_size(s: &str) -> Result<i64> {
let cs = CString::new(s).unwrap();
let r = unsafe { nbdkit_parse_size(cs.as_ptr()) };
if r == -1 {
return Err(
Error::new(libc::EINVAL,
"failed to parse size using nbdkit_parse_size"));
}
Ok(r)
}
pub fn parse_probability(what: &str, s: &str) -> Result<f64> {
let ws = CString::new(what).unwrap();
let cs = CString::new(s).unwrap();
let mut d = 0.0;
let r = unsafe { nbdkit_parse_probability(ws.as_ptr(), cs.as_ptr(),
&mut d) };
if r == -1 {
return Err(
Error::new(libc::EINVAL,
"failed to parse probability using nbdkit_parse_probability"));
}
Ok(d)
}
#[doc(hidden)]
#[repr(C)]
pub struct Plugin {
#[doc(hidden)] pub _struct_size: u64,
#[doc(hidden)] pub _api_version: c_int,
#[doc(hidden)] pub _thread_model: c_int,
pub name: *const c_char,
pub longname: *const c_char,
pub version: *const c_char,
pub description: *const c_char,
pub load: Option<extern fn ()>,
pub unload: Option<extern fn ()>,
pub config: Option<extern fn (*const c_char, *const c_char) -> c_int>,
pub config_complete: Option<extern fn () -> c_int>,
pub config_help: *const c_char,
pub open: extern fn (c_int) -> *mut c_void,
pub close: extern fn (*mut c_void),
pub get_size: extern fn (*mut c_void) -> i64,
pub can_write: Option<extern fn (*mut c_void) -> c_int>,
pub can_flush: Option<extern fn (*mut c_void) -> c_int>,
pub is_rotational: Option<extern fn (*mut c_void) -> c_int>,
pub can_trim: Option<extern fn (*mut c_void) -> c_int>,
#[doc(hidden)] pub _pread_v1: Option<extern fn ()>,
#[doc(hidden)] pub _pwrite_v1: Option<extern fn ()>,
#[doc(hidden)] pub _flush_v1: Option<extern fn ()>,
#[doc(hidden)] pub _trim_v1: Option<extern fn ()>,
#[doc(hidden)] pub _zero_v1: Option<extern fn ()>,
#[doc(hidden)] pub errno_is_preserved: c_int,
pub dump_plugin: Option<extern fn ()>,
pub can_zero: Option<extern fn (*mut c_void) -> c_int>,
pub can_fua: Option<extern fn (*mut c_void) -> c_int>,
pub pread: extern fn (h: *mut c_void, buf: *mut c_char, count: u32,
offset: u64,
flags: u32) -> c_int,
pub pwrite: Option<extern fn (h: *mut c_void, buf: *const c_char,
count: u32, offset: u64,
flags: u32) -> c_int>,
pub flush: Option<extern fn (h: *mut c_void, flags: u32) -> c_int>,
pub trim: Option<extern fn (h: *mut c_void,
count: u32, offset: u64,
flags: u32) -> c_int>,
pub zero: Option<extern fn (h: *mut c_void,
count: u32, offset: u64,
flags: u32) -> c_int>,
pub magic_config_key: *const c_char,
pub can_multi_conn: Option<extern fn (h: *mut c_void) -> c_int>,
pub can_extents: Option<extern fn (h: *mut c_void) -> c_int>,
pub extents: Option<extern fn (h: *mut c_void,
count: u32,
offset: u64,
rawflags: u32,
extent_handle: *mut c_void) -> c_int>,
pub can_cache: Option<extern fn (h: *mut c_void) -> c_int>,
pub cache: Option<extern fn (h: *mut c_void,
count: u32, offset: u64,
flags: u32) -> c_int>,
pub thread_model: Option<extern fn () -> c_int>,
pub can_fast_zero: Option<extern fn (h: *mut c_void) -> c_int>,
pub preconnect: Option<extern fn(readonly: c_int) -> c_int>,
pub get_ready: Option<extern fn() -> c_int>,
pub after_fork: Option<extern fn() -> c_int>,
}
#[macro_export]
macro_rules! plugin {
( $cls:path { $($feat:ident),* } ) => {
#[no_mangle]
pub extern fn plugin_init () -> *const ::nbdkit::Plugin {
let mut plugin = ::nbdkit::Builder::new();
$(plugin.$feat = true;)*
plugin.into_ptr::<$cls>()
}
}
}
#[cfg(test)]
mod t {
#[cfg(feature = "nix")]
mod peername {
use super::super::*;
use lazy_static::lazy_static;
use memoffset::offset_of;
use mockall::{mock, predicate::*};
use nix::sys::socket::{SockaddrIn, SockaddrIn6, UnixAddr};
use std::sync::Mutex;
lazy_static! {
static ref MOCK_NBDKIT_MTX: Mutex<()> = Mutex::new(());
}
mock! {
pub Nbdkit {
fn peer_name(addr: *mut libc::sockaddr,
addrlen: *mut libc::socklen_t) -> c_int;
}
}
#[no_mangle]
extern fn nbdkit_peer_name( addr: *mut libc::sockaddr,
addrlen: *mut libc::socklen_t) -> c_int
{
MockNbdkit::peer_name(addr, addrlen)
}
#[test]
fn error() {
let _m = MOCK_NBDKIT_MTX.lock().unwrap();
let ctx = MockNbdkit::peer_name_context();
ctx.expect()
.return_const(-1);
let e = peername::<SockaddrIn>().unwrap_err();
ctx.checkpoint();
assert_eq!("No peer name available", e.to_string());
}
#[test]
fn in4() {
let _m = MOCK_NBDKIT_MTX.lock().unwrap();
let ctx = MockNbdkit::peer_name_context();
ctx.expect()
.withf(|_, len| {
let l = unsafe {**len as usize};
l == mem::size_of::<libc::sockaddr_in>()
}).returning(|sa, sl| {
let sin = sa as *mut libc::sockaddr_in;
unsafe {
*sin = libc::sockaddr_in {
sin_family: libc::AF_INET as libc::sa_family_t,
sin_port: u16::from_le_bytes([4, 0xd2]),
sin_addr: libc::in_addr {
s_addr: u32::from_le_bytes([127, 0, 0, 1])
},
.. mem::zeroed()
};
*sl = mem::size_of::<libc::sockaddr_in>()
as libc::socklen_t;
}
0
});
assert_eq!("127.0.0.1:1234", peername::<SockaddrIn>().unwrap().to_string());
ctx.checkpoint();
}
#[test]
fn in6() {
let _m = MOCK_NBDKIT_MTX.lock().unwrap();
let ctx = MockNbdkit::peer_name_context();
ctx.expect()
.withf(|_, len| {
let l = unsafe {**len as usize};
l == mem::size_of::<libc::sockaddr_in6>()
}).returning(|sa, sl| {
let sin6 = sa as *mut libc::sockaddr_in6;
unsafe {
*sin6 = libc::sockaddr_in6 {
sin6_family: libc::AF_INET6 as libc::sa_family_t,
sin6_port: u16::from_le_bytes([4, 0xd2]),
sin6_addr: libc::in6_addr {
s6_addr: [0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1],
},
.. mem::zeroed()
};
*sl = mem::size_of::<libc::sockaddr_in6>()
as libc::socklen_t;
}
0
});
assert_eq!("[::1]:1234", peername::<SockaddrIn6>().unwrap().to_string());
ctx.checkpoint();
}
#[test]
fn un() {
let _m = MOCK_NBDKIT_MTX.lock().unwrap();
let ctx = MockNbdkit::peer_name_context();
ctx.expect()
.withf(|_, len| {
let l = unsafe {**len as usize};
l == mem::size_of::<libc::sockaddr_un>()
}).returning(|sa, sl| {
let sun = sa as *mut libc::sockaddr_un;
unsafe {
*sun = mem::zeroed();
(*sun).sun_family = libc::AF_UNIX as libc::sa_family_t;
ptr::copy_nonoverlapping(
b"rust-test.sock\0".as_ptr() as *const c_char,
(*sun).sun_path.as_mut_ptr(), 14);
let len = offset_of!(libc::sockaddr_un, sun_path) as u8
+ 14;
#[cfg(any(target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "macos",
target_os = "ios",
target_os = "dragonfly"
))]
{
(*sun).sun_len = len;
}
*sl = len as libc::socklen_t;
}
0
});
assert_eq!("rust-test.sock",
peername::<UnixAddr>().unwrap().to_string());
ctx.checkpoint();
}
}
}