Skip to content

Commit

Permalink
feat(zfs): Ability to send snapshot (#119)
Browse files Browse the repository at this point in the history
* feat(zfs): Ability to send snapshot
  • Loading branch information
andoriyu authored Feb 29, 2020
1 parent 7721776 commit 1ac0560
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 6 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ cstr-argument = "0.1.1"
strum = "0.17.1"
strum_macros = "0.17.1"
chrono = "0.4.10"
bitflags = "1.2.1"

[dependencies.libnv]
version = "0.2.2"
Expand All @@ -47,6 +48,7 @@ cavity = "1.1"
rand = "0.7"
slog-term = "2"
tempdir = "0.3"
tempfile = "3"

[package.metadata.release]
sign-commit = true
Expand Down
23 changes: 21 additions & 2 deletions src/zfs/delegating.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{slog::Logger,
zfs::{lzc::ZfsLzc, open3::ZfsOpen3, BookmarkRequest, CreateDatasetRequest,
DatasetKind, DestroyTiming, Properties, Result, ZfsEngine}};
use std::{collections::HashMap, path::PathBuf};
DatasetKind, DestroyTiming, Properties, Result, SendFlags, ZfsEngine}};
use std::{collections::HashMap, os::unix::io::AsRawFd, path::PathBuf};

/// Handy wrapper that delegates your call to correct implementation.
pub struct DelegatingZfsEngine {
Expand Down Expand Up @@ -65,4 +65,23 @@ impl ZfsEngine for DelegatingZfsEngine {
fn read_properties<N: Into<PathBuf>>(&self, path: N) -> Result<Properties> {
self.open3.read_properties(path)
}

fn send_full<N: Into<PathBuf>, FD: AsRawFd>(
&self,
path: N,
fd: FD,
flags: SendFlags,
) -> Result<()> {
self.lzc.send_full(path, fd, flags)
}

fn send_incremental<N: Into<PathBuf>, F: Into<PathBuf>, FD: AsRawFd>(
&self,
path: N,
from: F,
fd: FD,
flags: SendFlags,
) -> Result<()> {
self.lzc.send_incremental(path, from, fd, flags)
}
}
52 changes: 50 additions & 2 deletions src/zfs/lzc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::zfs::{BookmarkRequest, Checksum, Compression, Copies, CreateDatasetRequest,
DatasetKind, DestroyTiming, Error, Result, SnapDir, ValidationError, ZfsEngine};
DatasetKind, DestroyTiming, Error, Result, SendFlags, SnapDir, ValidationError,
ZfsEngine};
use cstr_argument::CStrArgument;
use libnv::nvpair::NvList;
use slog::{Drain, Logger};
Expand All @@ -8,7 +9,7 @@ use slog_stdlog::StdLog;
use crate::zfs::{errors::Error::ValidationErrors,
properties::{AclInheritMode, AclMode, ZfsProp},
PathExt};
use std::{collections::HashMap, ffi::CString, path::PathBuf, ptr::null_mut};
use std::{collections::HashMap, ffi::CString, os::unix::io::AsRawFd, os::unix::io::RawFd, path::PathBuf, ptr::null_mut};
use zfs_core_sys as sys;

fn setup_logger<L: Into<Logger>>(logger: L) -> Logger {
Expand Down Expand Up @@ -44,6 +45,34 @@ impl ZfsLzc {
}

pub fn logger(&self) -> &Logger { &self.logger }


fn send(
&self,
path: PathBuf,
from: Option<PathBuf>,
fd: RawFd,
flags: SendFlags,
) -> Result<()> {
let snapshot = CString::new(path.to_str().unwrap())
.expect("Failed to create CString from path");
let snapshot_ptr = snapshot.as_ptr();
let from = from.map(|f| {
CString::new(f.to_str().unwrap()).expect("Failed to create CString from path")
});
let from_ptr = from.map(|cst| cst.as_ptr()).unwrap_or_else(|| std::ptr::null());
let fd_raw = fd;

let errno = unsafe { zfs_core_sys::lzc_send(snapshot_ptr, from_ptr, fd_raw, flags.bits) };

match errno {
0 => Ok(()),
_ => {
let io_error = std::io::Error::from_raw_os_error(errno);
Err(Error::Io(io_error))
},
}
}
}

impl ZfsEngine for ZfsLzc {
Expand Down Expand Up @@ -290,6 +319,25 @@ impl ZfsEngine for ZfsLzc {
},
}
}

fn send_full<N: Into<PathBuf>, FD: AsRawFd>(
&self,
path: N,
fd: FD,
flags: SendFlags,
) -> Result<()> {
self.send(path.into(), None, fd.as_raw_fd(), flags)
}

fn send_incremental<N: Into<PathBuf>, F: Into<PathBuf>, FD: AsRawFd>(
&self,
path: N,
from: F,
fd: FD,
flags: SendFlags,
) -> Result<()> {
self.send(path.into(), Some(from.into()), fd.as_raw_fd(), flags)
}
}

// This should be mapped to values from nvpair.
Expand Down
37 changes: 36 additions & 1 deletion src/zfs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::path::PathBuf;
use std::{os::unix::io::AsRawFd, path::PathBuf};

use bitflags::bitflags;

pub mod description;
pub use description::DatasetKind;
Expand Down Expand Up @@ -58,6 +60,16 @@ impl BookmarkRequest {
}
}

bitflags! {
#[derive(Default)]
pub struct SendFlags: u32 {
const LZC_SEND_FLAG_EMBED_DATA = 1 << 0;
const LZC_SEND_FLAG_LARGE_BLOCK = 1 << 1;
const LZC_SEND_FLAG_COMPRESS = 1 << 2;
const LZC_SEND_FLAG_RAW = 1 << 3;
const LZC_SEND_FLAG_SAVED = 1 << 4;
}
}
pub trait ZfsEngine {
/// Check if a dataset (a filesystem, or a volume, or a snapshot with the given name exists.
///
Expand Down Expand Up @@ -124,6 +136,29 @@ pub trait ZfsEngine {
fn read_properties<N: Into<PathBuf>>(&self, _path: N) -> Result<Properties> {
Err(Error::Unimplemented)
}

/// Send a full snapshot to a specified file descriptor.
#[cfg_attr(tarpaulin, skip)]
fn send_full<N: Into<PathBuf>, FD: AsRawFd>(
&self,
_path: N,
_fd: FD,
_flags: SendFlags,
) -> Result<()> {
Err(Error::Unimplemented)
}

/// Send an incremental snapshot to a specified file descriptor.
#[cfg_attr(tarpaulin, skip)]
fn send_incremental<N: Into<PathBuf>, F: Into<PathBuf>, FD: AsRawFd>(
&self,
_path: N,
_from: F,
_fd: FD,
_flags: SendFlags,
) -> Result<()> {
Err(Error::Unimplemented)
}
}

#[derive(Default, Builder, Debug, Clone, Getters)]
Expand Down
26 changes: 25 additions & 1 deletion tests/test_zfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rand::Rng;

use libzetta::{slog::*,
zfs::{BookmarkRequest, Copies, CreateDatasetRequest, DatasetKind, Error,
Properties, SnapDir, ZfsEngine, ZfsLzc},
Properties, SendFlags, SnapDir, ZfsEngine, ZfsLzc},
zpool::{CreateVdevRequest, CreateZpoolRequest, ZpoolEngine, ZpoolOpen3}};

use libzetta::{zfs::{properties::VolumeMode, DelegatingZfsEngine, DestroyTiming},
Expand Down Expand Up @@ -411,3 +411,27 @@ fn read_properties_of_volume() {
panic!("Read not fs properties");
}
}
#[test]
fn send_snapshot() {
let zpool = SHARED_ZPOOL.clone();
let zfs = DelegatingZfsEngine::new(None).expect("Failed to initialize ZfsLzc");
let root_name = get_dataset_name();
let root = PathBuf::from(format!("{}/{}", zpool, &root_name));
let request = CreateDatasetRequest::builder()
.name(root.clone())
.kind(DatasetKind::Volume)
.volume_size(ONE_MB_IN_BYTES)
.build()
.unwrap();
zfs.create(request).expect("Failed to create a root dataset");

let snapshot_name = format!("{}/{}@tosend", zpool, &root_name);
let snapshot = PathBuf::from(&snapshot_name);

zfs.snapshot(&[PathBuf::from(&snapshot_name)], None).expect("Failed to create snapshots");

let mut tmpfile = tempfile::tempfile().unwrap();


zfs.send_full(snapshot, tmpfile, SendFlags::empty()).unwrap();
}

0 comments on commit 1ac0560

Please sign in to comment.