/* * Copyright (c) 2021, Luca Fulchir * * 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 . */ mod flags; mod part_type; mod verify; use crate::config::target; pub use flags::Flag; pub use verify::verify; use crate::config::errors; use strum_macros::Display; #[::serde_with::serde_as] #[derive(::serde::Deserialize, ::serde::Serialize, Clone)] #[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)] #[serde(deny_unknown_fields)] pub enum Alignment { Alignment(u64), } #[derive(::serde::Deserialize, ::serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub enum Index { LBA(u64), Bytes(u64), } #[derive(::serde::Deserialize, ::serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub enum PartFrom { /// LBAs to leave free from the partition SkipFree(Index, Alignment), /// specify LBA number directly Index(Index, Alignment), /// 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 FromEndOrLast(Index, Alignment), } #[derive(::serde::Deserialize, ::serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub enum PartTo { /// give a size, it will be converted to the appropriate LBA Index(Index, Alignment), /// use all, leaving this size free Leave(Index, Alignment), /// 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)] #[serde(deny_unknown_fields)] pub enum PartUUID { Random, UUID(::uuid::Uuid), } #[derive(::serde::Deserialize, ::serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct Partition { pub label: String, #[serde(default)] pub number: Option, pub part_type: PartId, pub part_uuid: PartUUID, #[serde(default)] pub flags: Vec, pub from: PartFrom, pub to: PartTo, pub target: target::TargetId, } 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)] #[serde(deny_unknown_fields)] pub enum GptUuid { Random, UUID(::uuid::Uuid), } #[derive(::serde::Deserialize, ::serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct GPT { pub id: String, pub guid: GptUuid, #[serde(default)] pub partitions: Vec, } impl target::TargetCommon for GPT { fn id(&self) -> &str { self.id.as_str() } fn targets(&self) -> Vec { 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)] #[serde(deny_unknown_fields)] // somehow marking this "untagged" breaks the parsing of Gpt :/ //#[serde(untagged)] pub enum GptId { Id(String), InlineGPT(GPT), } impl crate::config::CfgVerify for GPT { fn standardize(&mut self) { // if all partitions don't have numbers, force them let mut idx: u32 = 0; for p in self.partitions.iter_mut() { match p.number { None => { p.number = Some(idx); idx += 1; } Some(_) => return, } } // 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(()) } }