rsblkid 0.4.1

Safe Rust wrapper around the `util-linux/libblkid` C library
Documentation
// Copyright (c) 2023 Nick Piaddo
// SPDX-License-Identifier: Apache-2.0 OR MIT

// From dependency library

// From standard library
use std::mem::MaybeUninit;

// From this library
use crate::ffi_utils;
use crate::probe::PartitionTable;
use crate::probe::Probe;

/// A device partition.
#[derive(Debug)]
pub struct Partition<'a> {
    pub(super) ptr: libblkid::blkid_partition,
    marker: &'a Probe,
}

impl<'a> Partition<'a> {
    #[doc(hidden)]
    /// Creates a new `Partition` instance.
    pub(super) fn new(marker: &'a Probe, partition: libblkid::blkid_partition) -> Partition<'a> {
        log::debug!("Partition::new creating a new `Partition` instance");

        Self {
            ptr: partition,
            marker,
        }
    }

    /// Returns the partition's name, if supported by the partition type (e.g. `Mac`).
    pub fn name(&self) -> Option<String> {
        log::debug!("Partition::name getting partition name");

        let mut ptr = MaybeUninit::<*const libc::c_char>::zeroed();
        unsafe {
            ptr.write(libblkid::blkid_partition_get_name(self.ptr));
        }

        match unsafe { ptr.assume_init() } {
            name_ptr if name_ptr.is_null() => {
                log::debug!("Partition::name failed to get partition name. libblkid::blkid_partition_get_name returned a NULL pointer");

                None
            }
            name_ptr => {
                let name = ffi_utils::c_char_array_to_string(name_ptr);
                log::debug!("Partition::name got partition name {:?}", name);

                Some(name)
            }
        }
    }

    /// Returns the partition flags (or attributes for GPT partitions).
    pub fn flags(&self) -> u64 {
        let flags = unsafe { libblkid::blkid_partition_get_flags(self.ptr) };
        log::debug!("Partition::flags partition flags: 0x{:x}", flags);

        flags
    }

    /// Returns the partition's number (e.g. `N` in `/dev/sdN`).
    ///
    /// Note that this number is generated by the library independently of your OS.
    pub fn number(&self) -> usize {
        log::debug!("Partition::number getting partition number");

        let partition_number = unsafe { libblkid::blkid_partition_get_partno(self.ptr) };

        log::debug!(
            "Partition::number got partition number {:?}",
            partition_number
        );

        partition_number as usize
    }

    /// Returns the size of a partition in sectors.
    pub fn size_in_sectors(&self) -> u64 {
        let size = unsafe { libblkid::blkid_partition_get_size(self.ptr) as u64 };
        log::debug!(
            "Partition::size_in_sectors partition size (sectors): {:?}",
            size
        );

        size
    }

    /// Returns the size of a partition in bytes.
    pub fn size_in_bytes(&self) -> u64 {
        let size_in_sector = self.size_in_sectors();
        let size = size_in_sector * 512;
        log::debug!(
            "Partition::size_in_bytes partition size (bytes): {:?}",
            size
        );

        size
    }

    /// Returns the partition's location as an offset, in sectors, with respect
    /// to the beginning of the device.
    ///
    /// **Warning:**
    /// - when scanning is limited to a **device segment**, the returned location is still
    ///   expressed with respect to the beginning of the whole device,
    /// - for **nested partitions**, the offset can be relative to the location of the partition's
    ///   parent (e.g. `Solaris`), or the beginning of the entire device (e.g. `BSD`).
    pub fn location_in_sectors(&self) -> u64 {
        let location = unsafe { libblkid::blkid_partition_get_start(self.ptr) as u64 };
        log::debug!(
            "Partition::location_in_sectors partition location (sectors): {:?}",
            location
        );

        location
    }

    /// Returns the partition's location as an offset, in bytes, with respect
    /// to the beginning of the device.
    ///
    /// **Warning:**
    /// - when scanning is limited to a **device segment**, the returned location is still
    ///   expressed with respect to the beginning of the whole device,
    /// - for **nested partitions**, the offset can be relative to the location of the partition's
    ///   parent (e.g. `Solaris`), or the beginning of the entire device (e.g. `BSD`).
    pub fn location_in_bytes(&self) -> u64 {
        let location_in_sectors = self.location_in_sectors();
        let location = location_in_sectors * 512;
        log::debug!(
            "Partition::location_in_bytes partition location (bytes): {:?}",
            location
        );

        location
    }

    /// Returns a partition ID intended to specify the file system the partition contains, or to
    /// flag special access methods used to access these partitions (e.g. special `CHS` mappings, `LBA`
    /// access, logical mapped geometries, special driver access, hidden partitions, secured or
    /// encrypted file systems, etc.).
    pub fn partition_type(&self) -> i32 {
        let partition_type = unsafe { libblkid::blkid_partition_get_type(self.ptr) };
        log::debug!(
            "Partition::partition_type partition type: 0x{:x}",
            partition_type
        );

        partition_type
    }

    /// Returns a Globally Unique Identifier (GUID) number, in hexadecimal, identifying a partition
    /// type, when the partition is a member of a `Mac` or `EFI GPT` partition table.
    pub fn partition_type_string(&self) -> Option<String> {
        log::debug!("Partition::partition_type_string getting partition type");

        let mut ptr = MaybeUninit::<*const libc::c_char>::zeroed();
        unsafe {
            ptr.write(libblkid::blkid_partition_get_type_string(self.ptr));
        };

        match unsafe { ptr.assume_init() } {
            type_ptr if type_ptr.is_null() => {
                log::debug!("Partition::partition_type_string failed to get partition type. libblkid::blkid_partition_get_type_string returned a NULL pointer");

                None
            }
            type_ptr => {
                let partition_type = ffi_utils::c_char_array_to_string(type_ptr);
                log::debug!(
                    "Partition::partition_type_string partition type: {:?}",
                    partition_type
                );

                Some(partition_type)
            }
        }
    }

    /// Returns a partition's parent partition table.
    ///
    /// In general, all partitions on a device share the same parent partition table. Except for
    /// systems with nested partition tables, for example `BSD` or `Solaris` that use a partition
    /// table inside a standard DOS Primary Partition.
    pub fn partition_table(&self) -> Option<PartitionTable> {
        log::debug!("Partition::partition_table getting partition's parent partition table.");

        unsafe {
            let mut ptr = MaybeUninit::<libblkid::blkid_parttable>::zeroed();
            ptr.write(libblkid::blkid_partition_get_table(self.ptr));

            match ptr.assume_init() {
                table if table.is_null() => {
                    let err_msg = "failed to get partition's parent partition table".to_owned();
                    log::debug!("Partition::partition_table {}. libblkid::blkid_partition_get_table returned a NULL pointer", err_msg);

                    None
                }
                table => {
                    log::debug!(
                        "Partition::partition_table got partition's parent partition table."
                    );

                    Some(PartitionTable::new(self.marker, table))
                }
            }
        }
    }

    /// Returns a partition's UUID, when the partition is a member of a `GPT` partition table.
    pub fn uuid(&self) -> Option<String> {
        log::debug!("Partition::uuid getting partition uuid");

        let mut ptr = MaybeUninit::<*const libc::c_char>::zeroed();
        unsafe {
            ptr.write(libblkid::blkid_partition_get_uuid(self.ptr));
        }

        match unsafe { ptr.assume_init() } {
            uuid_ptr if uuid_ptr.is_null() => {
                log::debug!("Partition::uuid failed to get partition uuid. libblkid::blkid_partition_get_uuid returned a NULL pointer");

                None
            }
            uuid_ptr => {
                let uuid = ffi_utils::c_char_array_to_string(uuid_ptr);
                log::debug!("Partition::uuid partition uuid: {:?}", uuid);

                Some(uuid)
            }
        }
    }

    /// Returns `true` when the partition is an `Extended` partition.
    pub fn is_extended(&self) -> bool {
        let extended = unsafe { libblkid::blkid_partition_is_extended(self.ptr) == 1 };
        log::debug!("Partition::is_extended {:?}", extended);

        extended
    }

    /// Returns `true` when the partition is a `Logical` partition.
    ///
    /// Note: this method returns `true` for all partitions in nested partition tables (e.g. `BSD disklabel`).
    pub fn is_logical(&self) -> bool {
        let logical = unsafe { libblkid::blkid_partition_is_logical(self.ptr) == 1 };
        log::debug!("Partition::is_logical {:?}", logical);

        logical
    }

    /// Returns `true` when the partition is a `Primary` partition.
    ///
    /// **Note:** this method returns `false` for members of `DOS Extended Boot Record`, and all partitions
    /// in nested partition tables (e.g. `BSD disklabel`).
    pub fn is_primary(&self) -> bool {
        let primary = unsafe { libblkid::blkid_partition_is_primary(self.ptr) == 1 };
        log::debug!("Partition::is_primary {:?}", primary);

        primary
    }
}