Skip to content
mod.rs 6.1 KiB
Newer Older
Luker's avatar
Luker committed
/*
 * Copyright (c) 2021, Luca Fulchir <luker@fenrirproject.org>
 *
 * This file is part of dfim.
 *
 * dfim is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * dfim is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with dfim.  If not, see <https://www.gnu.org/licenses/>.
 */

mod flags;
mod part_type;
mod verify;
use crate::config::target;
Luker's avatar
Luker committed
pub use flags::Flag;
pub use verify::verify;

use crate::config::errors;
Luker's avatar
Luker committed
use strum_macros::Display;

#[::serde_with::serde_as]
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
Luker's avatar
Luker committed
#[serde(deny_unknown_fields)]
pub enum PartId {
    #[serde_as(as = "serde_with::hex::Hex")]
    GPTFdisk(u16),
    Def(part_type::PartType),
    UUID(::uuid::Uuid),
}

// This is an enum to force ron to use Alignemnt(u64) so the user will never
// confuse the fields
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
Luker's avatar
Luker committed
#[serde(deny_unknown_fields)]
pub enum Alignment {
    Alignment(u64),
}
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
Luker's avatar
Luker committed
#[serde(deny_unknown_fields)]
Luker's avatar
Luker committed
pub enum Index {
    LBA(u64),
    Bytes(u64),
}
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
Luker's avatar
Luker committed
#[serde(deny_unknown_fields)]
pub enum PartFrom {
    /// LBAs to leave free from the partition
Luker's avatar
Luker committed
    SkipFree(Index, Alignment),
Luker's avatar
Luker committed
    /// specify LBA number directly
Luker's avatar
Luker committed
    Index(Index, Alignment),
Luker's avatar
Luker committed
    /// this partition will have at most size X, calculated from the
    /// end of the drive, or from the partitions at the end of the drive
Luker's avatar
Luker committed
    FromEndOrLast(Index, Alignment),
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
Luker's avatar
Luker committed
#[serde(deny_unknown_fields)]
pub enum PartTo {
    /// give a size, it will be converted to the appropriate LBA
Luker's avatar
Luker committed
    Index(Index, Alignment),
Luker's avatar
Luker committed
    /// use all, leaving this size free
Luker's avatar
Luker committed
    Leave(Index, Alignment),
Luker's avatar
Luker committed
    /// use the specified percentage of the free disk
    Percent(f32, Alignment),
    /// use all, leaving this percentage of disk free
    LeavePercent(f32, Alignment),
}

#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
Luker's avatar
Luker committed
#[serde(deny_unknown_fields)]
pub enum PartUUID {
    Random,
    UUID(::uuid::Uuid),
}

#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
Luker's avatar
Luker committed
#[serde(deny_unknown_fields)]
pub struct Partition {
Luker's avatar
Luker committed
    pub label: String,
    #[serde(default)]
Luker's avatar
Luker committed
    pub number: Option<u32>,
Luker's avatar
Luker committed
    pub part_type: PartId,
    pub part_uuid: PartUUID,
Luker's avatar
Luker committed
    #[serde(default)]
Luker's avatar
Luker committed
    pub flags: Vec<flags::Flag>,
    pub from: PartFrom,
    pub to: PartTo,
Luker's avatar
Luker committed
    pub target: target::TargetId,
Luker's avatar
Luker committed
impl Partition {
    pub fn flag_bits(&self) -> u64 {
        let mut ret: u64 = 0;
        for f in &self.flags {
            ret |= f.bits();
        }
        ret
    }
}

#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
Luker's avatar
Luker committed
#[serde(deny_unknown_fields)]
pub enum GptUuid {
    Random,
    UUID(::uuid::Uuid),
}

#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
Luker's avatar
Luker committed
#[serde(deny_unknown_fields)]
pub struct GPT {
Luker's avatar
Luker committed
    pub id: String,
Luker's avatar
Luker committed
    pub guid: GptUuid,
Luker's avatar
Luker committed
    #[serde(default)]
Luker's avatar
Luker committed
    pub partitions: Vec<Partition>,
Luker's avatar
Luker committed
impl target::TargetCommon for GPT {
    fn id(&self) -> &str {
        self.id.as_str()
    }
Luker's avatar
Luker committed
    fn targets(&self) -> Vec<String> {
        let mut ret = Vec::with_capacity(self.partitions.len());
        for p in &self.partitions {
            if let target::TargetId::Id(t) = &p.target {
                ret.push(t.clone());
            }
        }
        ret
    }
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
Luker's avatar
Luker committed
#[serde(deny_unknown_fields)]
// somehow marking this "untagged" breaks the parsing of Gpt :/
//#[serde(untagged)]
pub enum GptId {
Luker's avatar
Luker committed
    Id(String),
Luker's avatar
Luker committed
    InlineGPT(GPT),
}

impl crate::config::CfgVerify for GPT {
    fn standardize(&mut self) {
        // if all partitions don't have numbers, force them
Luker's avatar
Luker committed
        let mut idx: u32 = 0;
        for p in self.partitions.iter_mut() {
            match p.number {
                None => {
                    p.number = Some(idx);
                    idx += 1;
                }
Luker's avatar
Luker committed
                Some(_) => return,
Luker's avatar
Luker committed
        // we can now assume all numbers are Some(idx)
        self.partitions
            .sort_unstable_by(|a, b| a.number.unwrap().cmp(&b.number.unwrap()));
    }
    fn check_consistency(
        &self,
        logger: &slog::Logger,
    ) -> Result<(), errors::ConfigError> {
        // FIXME: points of failure:
        // * partitions with same self.number (done)
        // * overlapping partitions
        // * some partitions numbered, others not (done)
        for p1 in self.partitions.iter() {
            for p2 in self.partitions.iter() {
                if p1.number == None || p2.number == None {
                    ::slog::error!(
                        logger,
                        "GPT: \"{}\": mixing automatic partition index and \
                         manual is not supported",
                        self.id
                    );
                    return Err(errors::ConfigError::Consistency(
                        "mixed automatic and manual partition number"
                            .to_owned(),
                    ));
                }
                if let Some(num) = p2.number {
                    if p1.number == p2.number {
                        ::slog::error!(
                            logger,
                            "GPT: multiple partitions with the same number \
                             ({}) in GPT: \"{}\"",
                            num,
                            self.id
                        );
                        return Err(errors::ConfigError::Consistency(
                            "multiple partitions with the same number"
                                .to_owned(),
                        ));
                    }
                }
            }
        }
        Ok(())
    }
}