Skip to content
mod.rs 5.71 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/>.
 */

Luker's avatar
Luker committed
pub mod cfg;
pub mod s_trgt;
Luker's avatar
Luker committed
use super::config;
use ::std::io::Read;
use ::std::io::Seek;
Luker's avatar
Luker committed
use s_trgt::CheckStatus;
Luker's avatar
Luker committed

/// Check if a device is empty. Meaning: no gpt/mbr, first and last MB all zeros
Luker's avatar
Luker committed
pub fn is_empty(dev: &::udev::Device) -> Result<bool, ::std::io::Error> {
Luker's avatar
Luker committed
    let node = match dev.devnode() {
        Some(node) => node,
        None => {
            return Err(::std::io::Error::new(
                ::std::io::ErrorKind::NotFound,
                "device does not have a node",
            ));
        }
    };
Luker's avatar
Luker committed
    // MBR/GPT have headers in the first/last MB,
    // so checking those will guarantee us that there is no MBR/GPT
Luker's avatar
Luker committed
    let mut device = ::std::fs::File::open(node)?;
Luker's avatar
Luker committed
    // 1MB buffer
    let mut buffer = Vec::<u8>::with_capacity(1024 * 1024);
    unsafe { buffer.set_len(buffer.capacity()) }

    let bytes = device.read(&mut buffer[..])?;
    if buffer[..bytes].iter().any(|&x| x != 0) {
        return Ok(false);
    }
    // check the end of file
    device.seek(::std::io::SeekFrom::End(0))?;
    let max_size = device.stream_position()?;
    if max_size > 1024 * 1024 {
        // else we have already checked it
Luker's avatar
Luker committed
        let to_end = ::std::cmp::max(max_size - 1024 * 1024, 1024 * 1024);
        device.seek(::std::io::SeekFrom::Start(to_end))?;
        let bytes = device.read(&mut buffer[..])?;
        if buffer[..bytes].iter().any(|&x| x != 0) {
            return Ok(false);
        }
    }
    Ok(true)
}

pub fn get(
    logger: &::slog::Logger,
Luker's avatar
Luker committed
    cfg: &cfg::Cfg,
Luker's avatar
Luker committed
    cfg_dev: &config::device::Device,
Luker's avatar
Luker committed
    cfg_target_id: &str,
Luker's avatar
Luker committed
    dev: &::udev::Device,
Luker's avatar
Luker committed
) -> Result<CheckStatus, ::std::io::Error> {
Luker's avatar
Luker committed
    // analyze a device, get the partition table and filesystem
    // to check if the current state and the destination coincide
Luker's avatar
Luker committed
    use crate::config::trgt::TargetCommon;
    use crate::state::s_trgt::TargetApply;
Luker's avatar
Luker committed

    let mut queue = Vec::<s_trgt::NextTarget>::new();
    queue.push(s_trgt::NextTarget {
        id: cfg_target_id.to_owned(),
        dev: dev.clone(),
    });
    let mut return_status = CheckStatus::DoNotApply;

    while queue.len() > 0 {
        let work = queue.pop().unwrap();
        let cfg_target = match cfg.targets.iter().find(|x| x.id() == work.id) {
            Some(cfg_target) => cfg_target,
            None => {
                assert!(false, "A target returned a non-existant id");
                continue;
            }
        };

        let mut check_target: s_trgt::Target = cfg_target.into();
        let mut res = match check_target.check(logger, cfg, cfg_dev, &work.dev)
        {
            Ok(res) => res,
            Err(e) => {
                ::slog::warn!(
                    logger,
                    "Failed to check target {}: {:?}",
                    cfg_target.id(),
                    e
                );
                return Err(e);
            }
        };
        return_status = match res.status {
            status @ CheckStatus::DoNotApply => return Ok(status),
            CheckStatus::CanBeApplied => match return_status {
                CheckStatus::DoNotApply | CheckStatus::CanBeApplied => {
                    CheckStatus::CanBeApplied
                }
                CheckStatus::AlreadyApplied => CheckStatus::CanBeApplied,
            },
            CheckStatus::AlreadyApplied => match return_status {
                CheckStatus::DoNotApply | CheckStatus::AlreadyApplied => {
                    CheckStatus::AlreadyApplied
                }
                CheckStatus::CanBeApplied => CheckStatus::CanBeApplied,
            },
        };
        queue.append(&mut res.next);
    }
    Ok(return_status)
Luker's avatar
Luker committed
}

pub fn set(
    logger: &::slog::Logger,
Luker's avatar
Luker committed
    cfg: &cfg::Cfg,
    cfg_dev: &config::device::Device,
Luker's avatar
Luker committed
    cfg_target_id: &str,
    dev: &::udev::Device,
Luker's avatar
Luker committed
) -> Result<(), ::std::io::Error> {
Luker's avatar
Luker committed
    use crate::config::trgt::TargetCommon;
    use crate::state::s_trgt::NextTarget;
    use crate::state::s_trgt::TargetApply;
Luker's avatar
Luker committed

Luker's avatar
Luker committed
    let mut queue = Vec::<NextTarget>::new();
    queue.push(NextTarget {
        id: cfg_target_id.to_owned(),
        dev: dev.clone(),
    });

    while queue.len() > 0 {
        let work = queue.pop().unwrap();
        let cfg_target = match cfg.targets.iter().find(|x| x.id() == work.id) {
            Some(cfg_target) => cfg_target,
            None => {
                assert!(false, "A target returned a non-existant id");
                continue;
            }
        };
        let mut apply_target: s_trgt::Target = cfg_target.into();

        match apply_target.apply(logger, cfg, cfg_dev, &work.dev) {
            Ok(mut next) => {
                ::slog::debug!(
                    logger,
                    "Target {} successfully applied",
                    cfg_target.id(),
                );
                queue.append(&mut next);
            }
            Err(e) => {
                ::slog::warn!(
                    logger,
                    "Failed to apply target {}: {:?}",
                    cfg_target.id(),
                    e
                );
                return Err(e);
            }
        };
    }
Luker's avatar
Luker committed
    Ok(())
}