/* * 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 . */ use super::{CheckResult, CheckStatus, NextTarget}; use crate::config::trgt; // we could use lifetimes here instead of copying stuff, but // - we should have realatively low usage anyway // - lifetimes can get messy // - we don't have enough trgts to fully know how lifetimes will impact the // code. // so for now, clone pub struct GPT(trgt::gpt::GPT); impl GPT { pub fn new(cfg: &trgt::gpt::GPT) -> Self { GPT(cfg.clone()) } } impl GPT { /// generate a standard config that can be checked easily /// so that we can compare the config GPT with the device GPT fn generate_standard(&self, dev: &::udev::Device) -> trgt::gpt::GPT { let mut ret = trgt::gpt::GPT { id: self.0.id.clone(), guid: self.0.guid.clone(), max_partitions: self.0.max_partitions, partitions: Vec::with_capacity(self.0.partitions.len()), }; // we now have to recalculate the partition actual size, // and move them all to LBA // create (but never write) a new GPT table // and work with that to translate to LBA let gpt = ::gpt::GptConfig::new().writable(false); let node = match dev.devnode() { Some(node) => node, None => return ret, }; let mut disk = match gpt.open(node) { Ok(disk) => disk, Err(_) => { return ret; } }; let mut new_partitions = ::std::collections::BTreeMap::< u32, ::gpt::partition::Partition, >::new(); disk.update_partitions(new_partitions.clone()).unwrap(); let disk_alignment = match disk.logical_block_size() { ::gpt::disk::LogicalBlockSize::Lb512 => 512, ::gpt::disk::LogicalBlockSize::Lb4096 => 4096, }; let (min_lba, max_lba) = disk.find_free_sectors().get(0).unwrap().clone(); use trgt::gpt::{ Alignment, Index, PartFrom, PartId, PartTo, PartType, PartUUID, Partition, }; // convert all to LBA for ease of usage let mut lba_partitions = Vec::with_capacity(self.0.partitions.len()); for p in &self.0.partitions { let mut new_p = p.clone(); new_p.from.lba_align(disk_alignment, min_lba, max_lba); new_p.to.lba_align(disk_alignment, min_lba, max_lba); lba_partitions.push(new_p); } let mut last_lba: u64 = min_lba - 1; // now convert everything to // PartFrom::Index(Index::LBA(_), Alignment/LBA(0)) for p in &mut lba_partitions { // FIXME: // * we are ignoring alignment! // * we don't check over max // * we don't check to >= from p.from = match &p.from { idx @ PartFrom::Index(_, _) => idx.clone(), PartFrom::SkipFree(Index::LBA(l), al) => { let start_from = last_lba + 1 + l; last_lba = start_from; PartFrom::Index( Index::LBA(start_from), Alignment::Alignment(Index::LBA(0)), ) } PartFrom::SkipFreePercent(perc, al) => { let skip_lba: u64 = (((max_lba - last_lba) as f64) / 100.0 * perc) .ceil() as u64; let start_from = last_lba + 1 + skip_lba; last_lba = start_from; PartFrom::Index( Index::LBA(start_from), Alignment::Alignment(Index::LBA(0)), ) } _ => { assert!(false, "GPT: not everything is LBA"); return ret; } }; p.to = match &p.to { idx @ PartTo::Index(_, _) => idx.clone(), PartTo::Size(Index::LBA(l), al) => { let up_to = last_lba + l; last_lba = up_to; PartTo::Index( Index::LBA(up_to), Alignment::Alignment(Index::LBA(0)), ) } PartTo::Leave(Index::LBA(l), al) => { let up_to = max_lba - l; last_lba = up_to; PartTo::Index( Index::LBA(up_to), Alignment::Alignment(Index::LBA(0)), ) } PartTo::Percent(perc, al) => { let size: u64 = (((max_lba - last_lba) as f64) / 100.0 * perc) .ceil() as u64; let up_to = last_lba + size; last_lba = up_to; PartTo::Index( Index::LBA(up_to), Alignment::Alignment(Index::LBA(0)), ) } PartTo::LeavePercent(perc, al) => { let size: u64 = (((max_lba - last_lba) as f64) / 100.0 * perc) .ceil() as u64; let up_to = max_lba - size; last_lba = up_to; PartTo::Index( Index::LBA(up_to), Alignment::Alignment(Index::LBA(0)), ) } _ => { assert!(false, "GPT: not everything is LBA"); return ret; } }; } ret } /* /// get the current drevice GPT. make it standard enough that we can /// easily check with what comes from generate_standard(dev) fn get_current(&self, dev: &::udev::Device) -> Result { } */ } impl super::TargetApply for GPT { fn check( &mut self, logger: &::slog::Logger, cfg: &crate::state::cfg::Cfg, dev: &::udev::Device, ) -> Result { let node = match dev.devnode() { Some(node) => ::std::path::Path::new(node), None => { ::slog::error!( logger, "GPT: device \"{:?}\" does not have a corresponding node", dev.syspath() ); return Err(::std::io::Error::new( ::std::io::ErrorKind::NotFound, "GPT: device does not have a node", )); } }; let cfg = ::gpt::GptConfig::new().writable(false); if let Ok(true) = crate::state::is_empty(dev) { return Ok(CheckResult { status: CheckStatus::CanBeApplied, // FIXME next: Vec::new(), }); } let disk = match cfg.open(node) { Ok(disk) => disk, Err(e) => { ::slog::error!( logger, "GPT: device \"{:?}\" node \"{:?}\": can't open", dev.syspath(), node ); return Err(e); } }; let disk_guid = disk.guid(); if let trgt::gpt::GptUuid::UUID(uuid) = self.0.guid { if *disk_guid != uuid { ::slog::debug!( logger, "GPT: device \"{:?}\" wrong guid. Got: {:?} Expected: {:?}", dev.syspath(), disk_guid, uuid ); return Ok(CheckResult { status: CheckStatus::DoNotApply, next: Vec::new(), }); } } let disk_part = disk.partitions(); if disk_part.len() != self.0.partitions.len() { ::slog::debug!( logger, "GPT: device \"{:?}\" partitions don't match", dev.syspath() ); return Ok(CheckResult { status: CheckStatus::DoNotApply, next: Vec::new(), }); } for (idx, p) in disk_part { match self.0.partitions.iter().find(|x| x.number == *idx) { None => { ::slog::debug!( logger, "GPT: device \"{:?}\" partition {} does not match", dev.syspath(), idx, ); return Ok(CheckResult { status: CheckStatus::DoNotApply, next: Vec::new(), }); } Some(self_p) => { // TODO } } } Err(::std::io::Error::new( ::std::io::ErrorKind::Other, "Exec: nonZero exit status", )) } fn apply( &mut self, logger: &::slog::Logger, cfg: &crate::state::cfg::Cfg, cfg_dev: &crate::config::device::Device, dev: &::udev::Device, ) -> Result, ::std::io::Error> { Err(::std::io::Error::new( ::std::io::ErrorKind::Other, "Exec: nonZero exit status", )) } }