Newer
Older
/*
* 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/>.
*/
use super::{CheckResult, CheckStatus, NextTarget};
// 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
pub fn new(cfg: &trgt::gpt::GPT) -> Self {
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,
) -> Result<trgt::gpt::GPT, ::std::io::Error> {
let (min_lba, max_lba, disk_alignment) = {
// 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 = dev.devnode().ok_or(::std::io::Error::new(
::std::io::ErrorKind::Other,
"device does not have a node",
))?;
// enclose this in a block just to drop the ::gpt::Disk early
let mut disk = gpt.open(node)?;
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();
(min_lba, max_lba, disk_alignment)
align_raw, Alignment, Index, PartFrom, PartId, PartTo, PartType,
PartUUID, Partition,
// convert all to Index(LBA), Alignment(LBA) for ease of usage
for p in &mut ret.partitions {
p.from.lba_align(disk_alignment, min_lba, max_lba);
p.to.lba_align(disk_alignment, min_lba, max_lba);
let mut last_lba: u64 = min_lba - 1;
// now convert everything to
// PartFrom::Index(Index::LBA(_), Alignment/LBA(0))
p.from = match &p.from {
idx @ PartFrom::Index(_, _) => idx.clone(),
PartFrom::SkipFree(
Index::LBA(l),
Alignment::Alignment(Index::LBA(al)),
) => {
let start_aligned = align_raw(start_from, *al);
last_lba = start_aligned;
PartFrom::SkipFreePercent(
perc,
Alignment::Alignment(Index::LBA(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;
let start_aligned = align_raw(start_from, *al);
last_lba = start_aligned;
Alignment::Alignment(Index::LBA(0)),
)
}
_ => {
assert!(false, "GPT: from: not everything is LBA");
return Err(::std::io::Error::new(
::std::io::ErrorKind::Other,
"internal error: \"from\" should be LBA",
));
}
};
p.to = match &p.to {
idx @ PartTo::Index(_, _) => idx.clone(),
PartTo::Size(
Index::LBA(l),
Alignment::Alignment(Index::LBA(al)),
) => {
let up_to_aligned = align_raw(up_to, *al);
last_lba = up_to_aligned;
Alignment::Alignment(Index::LBA(0)),
)
}
PartTo::Leave(
Index::LBA(l),
Alignment::Alignment(Index::LBA(al)),
) => {
let up_to_aligned = align_raw(up_to, *al);
last_lba = up_to_aligned;
Alignment::Alignment(Index::LBA(0)),
)
}
PartTo::Percent(perc, Alignment::Alignment(Index::LBA(al))) => {
let size: u64 = (((max_lba - last_lba) as f64) / 100.0
* perc)
.ceil() as u64;
let up_to = last_lba + size;
let up_to_aligned = align_raw(up_to, *al);
last_lba = up_to_aligned;
Alignment::Alignment(Index::LBA(0)),
)
}
PartTo::LeavePercent(
perc,
Alignment::Alignment(Index::LBA(al)),
) => {
let size: u64 = (((max_lba - last_lba) as f64) / 100.0
* perc)
.ceil() as u64;
let up_to = max_lba - size;
let up_to_aligned = align_raw(up_to, *al);
last_lba = up_to_aligned;
Alignment::Alignment(Index::LBA(0)),
)
}
_ => {
assert!(false, "GPT: to: not everything is LBA");
return Err(::std::io::Error::new(
::std::io::ErrorKind::Other,
"internal error: \"to\" should be LBA",
));
}
};
if let PartFrom::Index(Index::LBA(l_f), _) = p.from {
if let PartTo::Index(Index::LBA(l_t), _) = p.to {
if l_f >= l_t || l_t >= max_lba || l_f < min_lba {
return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData,
"Wrong partition boundaries",
));
}
/// get the current device GPT. make it standard enough that we can
fn get_current(
dev: &::udev::Device,
) -> Result<trgt::gpt::GPT, ::std::io::Error> {
let gpt = ::gpt::GptConfig::new().writable(false);
let node = dev.devnode().ok_or(::std::io::Error::new(
::std::io::ErrorKind::Other,
"device does not have a node",
))?;
let disk = gpt.open(node)?;
let disk_header =
disk.primary_header().ok_or(::std::io::Error::new(
::std::io::ErrorKind::InvalidData,
"Failed to retrieve GPT primary header",
))?;
let mut ret = trgt::gpt::GPT {
id: "DFIM generated for {}".to_owned()
+ dev.syspath().to_str().unwrap(),
guid: trgt::gpt::GptUuid::UUID(disk.guid().clone()),
max_partitions: disk_header.part_size,
partitions: Vec::with_capacity(disk_header.num_parts as usize),
};
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
for (num, disk_p) in disk.partitions() {
use trgt::gpt::{
Alignment, Index, PartFrom, PartId, PartTo, PartUUID, Partition,
};
let p = Partition {
label: disk_p.name.clone(),
number: *num,
part_type: PartId::UUID(
::uuid::Uuid::parse_str(&disk_p.part_type_guid.guid)
.unwrap(),
),
part_uuid: PartUUID::UUID(disk_p.part_guid),
flags: trgt::gpt::Flag::from_bits(disk_p.flags),
from: PartFrom::Index(
Index::LBA(disk_p.first_lba),
Alignment::Alignment(Index::LBA(0)),
),
to: PartTo::Index(
Index::LBA(disk_p.last_lba),
Alignment::Alignment(Index::LBA(0)),
),
target: trgt::TargetId::Id("None".to_owned()),
};
ret.partitions.push(p);
}
Ok(ret)
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
impl super::TargetApply for GPT {
fn check(
&mut self,
logger: &::slog::Logger,
cfg: &crate::state::cfg::Cfg,
dev: &::udev::Device,
) -> Result<CheckResult, ::std::io::Error> {
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 {
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
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<Vec<NextTarget>, ::std::io::Error> {
Err(::std::io::Error::new(
::std::io::ErrorKind::Other,
"Exec: nonZero exit status",
))
}
}