use super::{
cvt, get_optional, prefer_snap, snap, Alignment, Constraint, ConstraintSource, Device,
Geometry, Partition, MOVE_DOWN, MOVE_STILL, MOVE_UP, SECT_END, SECT_START,
};
use libparted_sys::{
ped_constraint_any, ped_disk_add_partition, ped_disk_check as check, ped_disk_clobber,
ped_disk_commit as commit, ped_disk_commit_to_dev as commit_to_dev,
ped_disk_commit_to_os as commit_to_os, ped_disk_delete_all as delete_all,
ped_disk_delete_partition, ped_disk_destroy, ped_disk_duplicate, ped_disk_extended_partition,
ped_disk_get_flag, ped_disk_get_last_partition_num, ped_disk_get_max_partition_geometry,
ped_disk_get_max_primary_partition_count, ped_disk_get_max_supported_partition_count,
ped_disk_get_partition, ped_disk_get_partition_alignment, ped_disk_get_partition_by_sector,
ped_disk_get_primary_partition_count, ped_disk_is_flag_available,
ped_disk_max_partition_length, ped_disk_max_partition_start_sector,
ped_disk_maximize_partition, ped_disk_minimize_extended_partition, ped_disk_new,
ped_disk_new_fresh, ped_disk_next_partition, ped_disk_print, ped_disk_set_flag,
ped_disk_set_partition_geom, ped_disk_type_check_feature, ped_disk_type_get,
ped_disk_type_get_next, ped_disk_type_register, ped_disk_type_unregister, PedDisk, PedDiskType,
PedPartition,
};
use std::ffi::{CStr, CString};
use std::io::Result;
use std::marker::PhantomData;
use std::ptr;
use std::str;
pub use libparted_sys::_PedDiskFlag as DiskFlag;
pub use libparted_sys::_PedDiskTypeFeature as DiskTypeFeature;
macro_rules! disk_fn_mut {
($(#[$attr:meta])* fn $method:tt) => {
$(#[$attr])*
pub fn $method(&mut self) -> Result<()> {
cvt(unsafe { $method(self.disk) })?;
Ok(())
}
}
}
pub struct Disk<'a> {
pub(crate) disk: *mut PedDisk,
pub(crate) phantom: PhantomData<&'a PedDisk>,
is_droppable: bool,
}
pub struct DiskType<'a> {
pub(crate) type_: *mut PedDiskType,
pub(crate) phantom: PhantomData<&'a PedDiskType>,
}
impl<'a> DiskType<'a> {
pub fn check_feature(&self, feature: DiskTypeFeature) -> bool {
unsafe { ped_disk_type_check_feature(self.type_, feature) != 0 }
}
pub fn get_next(&'a self) -> Option<DiskType<'a>> {
let type_ = unsafe { ped_disk_type_get_next(self.type_) };
if type_.is_null() {
None
} else {
Some(DiskType {
type_,
phantom: PhantomData,
})
}
}
pub fn get(name: &str) -> Option<DiskType<'a>> {
CString::new(name.as_bytes())
.ok()
.map(|name| unsafe { ped_disk_type_get(name.as_ptr()) })
.and_then(|type_| {
if type_.is_null() {
None
} else {
Some(DiskType {
type_,
phantom: PhantomData,
})
}
})
}
pub fn register(&self) {
unsafe { ped_disk_type_register(self.type_) }
}
pub fn unregister(&self) {
unsafe { ped_disk_type_unregister(self.type_) }
}
}
pub struct DiskPartIter<'a>(&'a Disk<'a>, *mut PedPartition);
impl<'a> Disk<'a> {
pub fn new(device: &'a mut Device) -> Result<Disk<'a>> {
let is_droppable = device.is_droppable;
let disk = cvt(unsafe { ped_disk_new(device.ped_device()) })?;
Ok(Disk {
disk,
phantom: PhantomData,
is_droppable,
})
}
pub fn new_fresh(device: &'a mut Device, type_: DiskType) -> Result<Disk<'a>> {
cvt(unsafe { ped_disk_new_fresh(device.ped_device(), type_.type_) }).map(|disk| Disk {
disk,
phantom: PhantomData,
is_droppable: true,
})
}
#[allow(clippy::missing_safety_doc)]
pub unsafe fn get_device<'b>(&self) -> Device<'b> {
let mut device = Device::from_ped_device((*self.disk).dev);
device.is_droppable = false;
device
}
#[allow(clippy::missing_safety_doc)]
pub unsafe fn get_device_mut(&mut self) -> Device {
let mut device = Device::from_ped_device((*self.disk).dev);
device.is_droppable = false;
device
}
pub fn constraint_any<'b>(&self) -> Option<Constraint<'b>> {
unsafe {
let constraint = ped_constraint_any((*self.disk).dev);
if constraint.is_null() {
None
} else {
Some(Constraint {
constraint,
source: ConstraintSource::New,
phantom: PhantomData,
})
}
}
}
pub fn get_disk_type_name(&self) -> Option<&str> {
unsafe {
let type_ = (*self.disk).type_;
let name = (*type_).name;
if name.is_null() {
None
} else {
let cstr = CStr::from_ptr(name).to_bytes();
Some(str::from_utf8_unchecked(cstr))
}
}
}
pub fn needs_clobber(&self) -> bool {
unsafe { (*self.disk).needs_clobber != 0 }
}
pub fn update_mode(&self) -> bool {
unsafe { (*self.disk).update_mode != 0 }
}
pub fn get_flag_state(&self, flag: DiskFlag) -> bool {
unsafe { ped_disk_get_flag(self.disk, flag) != 0 }
}
pub fn is_flag_available(&self, flag: DiskFlag) -> bool {
unsafe { ped_disk_is_flag_available(self.disk, flag) != 0 }
}
pub fn print(&self) {
unsafe {
ped_disk_print(self.disk);
}
}
pub fn parts(&self) -> DiskPartIter {
DiskPartIter(self, ptr::null_mut())
}
pub fn add_partition(&mut self, part: &mut Partition, constraint: &Constraint) -> Result<()> {
part.is_droppable = false;
cvt(unsafe { ped_disk_add_partition(self.disk, part.part, constraint.constraint) })?;
Ok(())
}
pub fn get_last_partition_num(&self) -> Option<u32> {
match unsafe { ped_disk_get_last_partition_num(self.disk) } {
-1 => None,
num => Some(num.abs() as u32),
}
}
pub fn get_max_supported_partition_count(&self) -> Option<u32> {
let mut supported = 0i32;
if unsafe { ped_disk_get_max_supported_partition_count(self.disk, &mut supported) } {
if supported < 0 {
None
} else {
Some(supported.abs() as u32)
}
} else {
None
}
}
pub fn get_max_primary_partition_count(&self) -> u32 {
unsafe { ped_disk_get_max_primary_partition_count(self.disk) as u32 }
}
pub fn get_max_partition_geometry(
&'a self,
part: &Partition,
constraint: &Constraint,
) -> Result<Geometry<'a>> {
cvt(unsafe {
ped_disk_get_max_partition_geometry(self.disk, part.part, constraint.constraint)
})
.map(Geometry::from_raw)
}
disk_fn_mut!(
fn check
);
pub fn clobber(&mut self) -> Result<()> {
cvt(unsafe { ped_disk_clobber((*self.disk).dev) })?;
Ok(())
}
disk_fn_mut!(
fn commit
);
disk_fn_mut!(
fn commit_to_dev
);
disk_fn_mut!(
fn commit_to_os
);
disk_fn_mut!(
fn delete_all
);
pub fn duplicate<'b>(&mut self) -> Result<Disk<'b>> {
cvt(unsafe { ped_disk_duplicate(self.disk) }).map(|disk| Disk {
disk,
phantom: PhantomData,
is_droppable: true,
})
}
pub fn extended_partition(&self) -> Option<Partition> {
get_optional(unsafe { ped_disk_extended_partition(self.disk) }).map(|part| {
let mut partition = Partition::from(part);
partition.is_droppable = false;
partition
})
}
pub fn get_partition_alignment(&'a self) -> Result<Alignment<'a>> {
cvt(unsafe { ped_disk_get_partition_alignment(self.disk) }).map(|alignment| Alignment {
alignment,
phantom: PhantomData,
})
}
pub fn get_partition_by_sector(&'a self, sector: i64) -> Option<Partition<'a>> {
let part = unsafe { ped_disk_get_partition_by_sector(self.disk, sector) };
if part.is_null() {
None
} else {
let mut partition = Partition::from(part);
partition.is_droppable = false;
Some(partition)
}
}
#[allow(clippy::missing_safety_doc)]
pub unsafe fn get_partition_by_sector_raw(&self, sector: i64) -> *mut PedPartition {
ped_disk_get_partition_by_sector(self.disk, sector)
}
pub fn get_partition(&'a self, num: u32) -> Option<Partition<'a>> {
get_optional(unsafe { ped_disk_get_partition(self.disk, num as i32) }).map(|part| {
let mut partition = Partition::from(part);
partition.is_droppable = false;
partition
})
}
#[allow(clippy::missing_safety_doc)]
pub unsafe fn get_partition_raw(&self, num: u32) -> *mut PedPartition {
ped_disk_get_partition(self.disk, num as i32)
}
pub fn get_primary_partition_count(&self) -> u32 {
unsafe { ped_disk_get_primary_partition_count(self.disk) as u32 }
}
pub fn max_partition_length(&self) -> i64 {
unsafe { ped_disk_max_partition_length(self.disk) }
}
pub fn max_partition_start_sector(&self) -> i64 {
unsafe { ped_disk_max_partition_start_sector(self.disk) }
}
pub fn maximize_partition(
&mut self,
part: &mut Partition,
constraint: &Constraint,
) -> Result<()> {
cvt(unsafe { ped_disk_maximize_partition(self.disk, part.part, constraint.constraint) })
.map(|_| ())
}
pub fn minimize_extended_partition(&mut self) -> Result<()> {
cvt(unsafe { ped_disk_minimize_extended_partition(self.disk) }).map(|_| ())
}
#[allow(clippy::missing_safety_doc)]
pub unsafe fn remove_partition(&mut self, part: *mut PedPartition) -> Result<()> {
cvt(ped_disk_delete_partition(self.disk, part)).map(|_| ())
}
pub fn remove_partition_by_number(&mut self, num: u32) -> Result<()> {
unsafe {
cvt(ped_disk_get_partition(self.disk, num as i32))
.and_then(|part| cvt(ped_disk_delete_partition(self.disk, part)))
.map(|_| ())
}
}
pub fn remove_partition_by_sector(&mut self, sector: i64) -> Result<()> {
unsafe {
cvt(ped_disk_get_partition_by_sector(self.disk, sector))
.and_then(|part| cvt(ped_disk_delete_partition(self.disk, part)))
.map(|_| ())
}
}
pub fn set_flag(&mut self, flag: DiskFlag, state: bool) -> bool {
let state = if state { 1 } else { 0 };
unsafe { ped_disk_set_flag(self.disk, flag, state) != 0 }
}
pub fn set_partition_geometry(
&mut self,
part: &mut Partition,
constraint: &Constraint,
start: i64,
end: i64,
) -> Result<()> {
cvt(unsafe {
ped_disk_set_partition_geom(self.disk, part.part, constraint.constraint, start, end)
})
.map(|_| ())
}
pub fn snap_to_boundaries(
&self,
new_geom: &mut Geometry,
old_geom: Option<&Geometry>,
start_range: &Geometry,
end_range: &Geometry,
) {
let (mut start_dist, mut end_dist) = (-1, -1);
let mut start = new_geom.start();
let mut end = new_geom.end();
let start_part = match self.get_partition_by_sector(start) {
Some(part) => part,
None => Partition::from(ptr::null_mut()),
};
let end_part = match self.get_partition_by_sector(end) {
Some(part) => part,
None => Partition::from(ptr::null_mut()),
};
let adjacent = start_part.geom_end() + 1 == end_part.geom_start();
let mut start_allow = MOVE_STILL | MOVE_UP | MOVE_DOWN;
let mut end_allow = start_allow;
if let Some(old_geom) = old_geom {
if snap(&mut start, old_geom.start(), start_range) {
start_allow = MOVE_STILL;
}
if snap(&mut end, old_geom.end(), end_range) {
end_allow = MOVE_STILL;
}
}
if start_part == end_part {
start_allow &= !MOVE_UP;
end_allow &= !MOVE_DOWN;
}
let mut start_want = prefer_snap(
start,
SECT_START,
start_range,
&mut start_allow,
&start_part,
&mut start_dist,
);
let mut end_want = prefer_snap(
end,
SECT_END,
end_range,
&mut end_allow,
&end_part,
&mut end_dist,
);
debug_assert!(start_dist >= 0 && end_dist >= 0);
if adjacent && start_want == MOVE_UP && end_want == MOVE_DOWN {
if end_dist < start_dist {
start_allow &= !MOVE_UP;
start_want = prefer_snap(
start,
SECT_START,
start_range,
&mut start_allow,
&start_part,
&mut start_dist,
);
debug_assert!(start_dist >= 0);
} else {
end_allow &= !MOVE_DOWN;
end_want = prefer_snap(
start,
SECT_END,
end_range,
&mut end_allow,
&end_part,
&mut end_dist,
);
debug_assert!(end_dist >= 0);
}
}
start = match start_want {
MOVE_DOWN => start_part.geom_start(),
MOVE_UP => start_part.geom_end() + 1,
_ => start,
};
end = match end_want {
MOVE_DOWN => end_part.geom_start() - 1,
MOVE_UP => end_part.geom_end(),
_ => end,
};
debug_assert!(start_range.test_sector_inside(start));
debug_assert!(end_range.test_sector_inside(end));
debug_assert!(start <= end);
let _ = new_geom.set(start, end - start + 1);
}
}
impl<'a> Iterator for DiskPartIter<'a> {
type Item = Partition<'a>;
fn next(&mut self) -> Option<Partition<'a>> {
let partition = unsafe { ped_disk_next_partition((self.0).disk, self.1) };
if partition.is_null() {
None
} else {
self.1 = partition;
let mut partition = Partition::from(partition);
partition.is_droppable = false;
Some(partition)
}
}
}
impl<'a> Drop for Disk<'a> {
fn drop(&mut self) {
if self.is_droppable {
unsafe {
ped_disk_destroy(self.disk);
}
}
}
}