use crate::bindings;
use crate::common;
use crate::error::GlueError;
use crate::error::LibError;
use crate::error::LibErrorCode;
use crate::subvolume::Subvolume;
use crate::BtrfsUtilError;
use crate::Result;
use std::convert::Into;
use std::convert::TryFrom;
use bindings::btrfs_util_subvolume_info;
use chrono::NaiveDateTime;
use chrono::Timelike;
use uuid::Uuid;
#[derive(Clone, Debug)]
pub struct SubvolumeInfo {
pub id: u64,
pub parent_id: Option<u64>,
pub dir_id: Option<u64>,
pub flags: u64,
pub uuid: Uuid,
pub parent_uuid: Option<Uuid>,
pub received_uuid: Option<Uuid>,
pub generation: u64,
pub ctransid: u64,
pub otransid: u64,
pub stransid: Option<u64>,
pub rtransid: Option<u64>,
pub ctime: NaiveDateTime,
pub otime: NaiveDateTime,
pub stime: Option<NaiveDateTime>,
pub rtime: Option<NaiveDateTime>,
}
impl TryFrom<&Subvolume> for SubvolumeInfo {
type Error = BtrfsUtilError;
fn try_from(src: &Subvolume) -> Result<Self> {
let path_cstr = common::path_to_cstr(src.path()?)?;
let btrfs_subvolume_info_ptr: *mut btrfs_util_subvolume_info =
Box::into_raw(Box::from(btrfs_util_subvolume_info {
id: 0,
parent_id: 0,
dir_id: 0,
flags: 0,
uuid: [0; 16],
parent_uuid: [0; 16],
received_uuid: [0; 16],
generation: 0,
ctransid: 0,
otransid: 0,
stransid: 0,
rtransid: 0,
ctime: bindings::timespec {
tv_nsec: 0 as bindings::__time_t,
tv_sec: 0 as bindings::__syscall_slong_t,
},
otime: bindings::timespec {
tv_nsec: 0 as bindings::__time_t,
tv_sec: 0 as bindings::__syscall_slong_t,
},
stime: bindings::timespec {
tv_nsec: 0 as bindings::__time_t,
tv_sec: 0 as bindings::__syscall_slong_t,
},
rtime: bindings::timespec {
tv_nsec: 0 as bindings::__time_t,
tv_sec: 0 as bindings::__syscall_slong_t,
},
}));
unsafe_wrapper!(errcode, {
errcode =
btrfs_util_subvolume_info(path_cstr.as_ptr(), src.id(), btrfs_subvolume_info_ptr);
});
glue_error!(
btrfs_subvolume_info_ptr.is_null(),
GlueError::NullPointerReceived
);
SubvolumeInfo::try_from(unsafe { Box::from_raw(btrfs_subvolume_info_ptr) })
}
}
macro_rules! handle_uuid {
($src: expr) => {
match Uuid::from_slice($src) {
Ok(val) => val,
Err(e) => glue_error!(GlueError::UuidError(e)),
}
};
}
macro_rules! handle_timespec {
($src: expr) => {
match NaiveDateTime::from_timestamp_opt($src.tv_sec, $src.tv_nsec as u32) {
Some(val) => val,
None => glue_error!(GlueError::BadTimespec(format!("{:?}", $src))),
}
};
}
impl TryFrom<Box<btrfs_util_subvolume_info>> for SubvolumeInfo {
type Error = BtrfsUtilError;
fn try_from(src: Box<btrfs_util_subvolume_info>) -> Result<Self> {
let uuid: Uuid = handle_uuid!(&src.uuid);
let parent_uuid_val: Uuid = handle_uuid!(&src.parent_uuid);
let received_uuid_val: Uuid = handle_uuid!(&src.received_uuid);
let ctime: NaiveDateTime = handle_timespec!(src.ctime);
let otime: NaiveDateTime = handle_timespec!(src.otime);
let stime_val: NaiveDateTime = handle_timespec!(src.stime);
let rtime_val: NaiveDateTime = handle_timespec!(src.rtime);
let parent_id: Option<u64> = if src.parent_id == 0 {
None
} else {
Some(src.parent_id)
};
let dir_id: Option<u64> = if src.dir_id == 0 {
None
} else {
Some(src.dir_id)
};
let parent_uuid: Option<Uuid> = if parent_uuid_val.is_nil() {
None
} else {
Some(parent_uuid_val)
};
let received_uuid: Option<Uuid> = if received_uuid_val.is_nil() {
None
} else {
Some(received_uuid_val)
};
let stransid: Option<u64> = if src.stransid == 0 {
None
} else {
Some(src.stransid)
};
let rtransid: Option<u64> = if src.rtransid == 0 {
None
} else {
Some(src.rtransid)
};
let stime: Option<NaiveDateTime> = if stime_val.nanosecond() == 0 && stime_val.second() == 0
{
None
} else {
Some(stime_val)
};
let rtime: Option<NaiveDateTime> = if rtime_val.nanosecond() == 0 && rtime_val.second() == 0
{
None
} else {
Some(rtime_val)
};
Ok(Self {
id: src.id,
parent_id,
dir_id,
flags: src.flags,
uuid,
parent_uuid,
received_uuid,
generation: src.generation,
ctransid: src.ctransid,
otransid: src.otransid,
stransid,
rtransid,
ctime,
otime,
stime,
rtime,
})
}
}
impl Into<Subvolume> for SubvolumeInfo {
fn into(self) -> Subvolume {
Subvolume::new(self.id)
}
}