use crate::common;
use crate::subvolume::Subvolume;
use crate::BtrfsUtilError;
use crate::Result;
use std::convert::TryFrom;
use std::path::PathBuf;
use btrfsutil_sys::btrfs_util_subvolume_info;
use chrono::DateTime;
use chrono::Local;
use chrono::TimeZone;
use chrono::Timelike;
use uuid::Uuid;
#[derive(Clone, Debug, PartialEq)]
pub struct SubvolumeInfo {
pub id: u64,
pub path: PathBuf,
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: DateTime<Local>,
pub otime: DateTime<Local>,
pub stime: Option<DateTime<Local>>,
pub rtime: Option<DateTime<Local>>,
}
impl From<&SubvolumeInfo> for Subvolume {
fn from(info: &SubvolumeInfo) -> Self {
Self::new(info.id, info.path.clone())
}
}
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: btrfsutil_sys::timespec {
tv_nsec: 0 as btrfsutil_sys::__time_t,
tv_sec: 0 as btrfsutil_sys::__syscall_slong_t,
},
otime: btrfsutil_sys::timespec {
tv_nsec: 0 as btrfsutil_sys::__time_t,
tv_sec: 0 as btrfsutil_sys::__syscall_slong_t,
},
stime: btrfsutil_sys::timespec {
tv_nsec: 0 as btrfsutil_sys::__time_t,
tv_sec: 0 as btrfsutil_sys::__syscall_slong_t,
},
rtime: btrfsutil_sys::timespec {
tv_nsec: 0 as btrfsutil_sys::__time_t,
tv_sec: 0 as btrfsutil_sys::__syscall_slong_t,
},
}));
unsafe_wrapper!({
btrfs_util_subvolume_info(path_cstr.as_ptr(), src.id(), btrfs_subvolume_info_ptr)
})?;
let info: Box<btrfs_util_subvolume_info> =
unsafe { Box::from_raw(btrfs_subvolume_info_ptr) };
let uuid: Uuid = Uuid::from_slice(&info.uuid).expect("Failed to get uuid from C");
let parent_uuid_val: Uuid =
Uuid::from_slice(&info.parent_uuid).expect("Failed to get parent uuid from C");
let received_uuid_val: Uuid =
Uuid::from_slice(&info.received_uuid).expect("Failed to get received uuid from C");
let ctime: DateTime<Local> = Local
.timestamp_opt(info.ctime.tv_sec, info.ctime.tv_nsec as u32)
.single()
.expect("Failed to generate timestamp from C");
let otime: DateTime<Local> = Local
.timestamp_opt(info.otime.tv_sec, info.otime.tv_nsec as u32)
.single()
.expect("Failed to generate timestamp from C");
let stime_val: DateTime<Local> = Local
.timestamp_opt(info.stime.tv_sec, info.stime.tv_nsec as u32)
.single()
.expect("Failed to generate timestamp from C");
let rtime_val: DateTime<Local> = Local
.timestamp_opt(info.rtime.tv_sec, info.rtime.tv_nsec as u32)
.single()
.expect("Failed to generate timestamp from C");
let parent_id: Option<u64> = if info.parent_id == 0 {
None
} else {
Some(info.parent_id)
};
let dir_id: Option<u64> = if info.dir_id == 0 {
None
} else {
Some(info.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 info.stransid == 0 {
None
} else {
Some(info.stransid)
};
let rtransid: Option<u64> = if info.rtransid == 0 {
None
} else {
Some(info.rtransid)
};
let stime: Option<DateTime<Local>> =
if stime_val.nanosecond() == 0 && stime_val.second() == 0 {
None
} else {
Some(stime_val)
};
let rtime: Option<DateTime<Local>> =
if rtime_val.nanosecond() == 0 && rtime_val.second() == 0 {
None
} else {
Some(rtime_val)
};
Ok(Self {
id: info.id,
path: src.path().to_path_buf(),
parent_id,
dir_id,
flags: info.flags,
uuid,
parent_uuid,
received_uuid,
generation: info.generation,
ctransid: info.ctransid,
otransid: info.otransid,
stransid,
rtransid,
ctime,
otime,
stime,
rtime,
})
}
}