From de78b3978b086c3b9bef27146bac0904f070d6a8 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Tue, 23 Apr 2019 11:25:51 -0600 Subject: [PATCH] :ambulance: Fix for devices with a non-512-byte sector size --- crates/disk-ops/src/mklabel.rs | 14 ------------- crates/disk-ops/src/resize.rs | 10 +++++---- crates/disks/src/config/disk.rs | 16 +++++++++----- crates/disks/src/config/disk_trait.rs | 4 +++- crates/disks/src/config/disks.rs | 17 +++++++++------ crates/disks/src/lib.rs | 5 +++++ ffi/distinst.vapi | 5 +++++ ffi/src/auto.rs | 26 +++++++++++++++++++++-- src/auto/options/alongside_option.rs | 13 ++++++------ src/auto/options/apply.rs | 30 +++++++++++++++++++-------- src/auto/options/erase_option.rs | 9 ++++---- src/auto/options/mod.rs | 22 ++++++++++++++------ src/lib.rs | 6 +++++- 13 files changed, 119 insertions(+), 58 deletions(-) diff --git a/crates/disk-ops/src/mklabel.rs b/crates/disk-ops/src/mklabel.rs index d16baba3..882012ae 100644 --- a/crates/disk-ops/src/mklabel.rs +++ b/crates/disk-ops/src/mklabel.rs @@ -8,7 +8,6 @@ use std::path::Path; /// Writes a new partition table to the disk, clobbering it in the process. pub fn mklabel>(device_path: P, kind: PartitionTable) -> io::Result<()> { - let _ = zero(&device_path, 2047, 1); let _ = wipefs(&device_path); info!( @@ -41,16 +40,3 @@ pub fn mklabel>(device_path: P, kind: PartitionTable) -> io::Resu Ok(()) } - -/// Write sectors of zeroes to a block device -pub fn zero>(device: P, sectors: u64, offset: u64) -> io::Result<()> { - let zeroed_sector = [0; 512]; - File::open(device.as_ref()) - .and_then(|mut file| { - if offset != 0 { - file.seek(SeekFrom::Start(512 * offset)).map(|_| ())?; - } - - (0..sectors).map(|_| file.write(&zeroed_sector).map(|_| ())).collect() - }) -} diff --git a/crates/disk-ops/src/resize.rs b/crates/disk-ops/src/resize.rs index e37c976d..2dde269e 100644 --- a/crates/disk-ops/src/resize.rs +++ b/crates/disk-ops/src/resize.rs @@ -217,6 +217,8 @@ pub struct PartitionChange { pub start: u64, /// The end sector that the partition will have. pub end: u64, + /// Size of this partition's sectors. + pub sector_size: u64, /// The file system that is currently on the partition. pub filesystem: Option, /// A diff of flags which should be set on the partition. @@ -301,8 +303,8 @@ where // Each file system uses different units for specifying the size, and these // units are sometimes written in non-standard and conflicting ways. let size = match unit { - ResizeUnit::AbsoluteBytes => format!("{}", resize.absolute_sectors() * 512), - ResizeUnit::AbsoluteKibis => format!("{}ki", resize.absolute_sectors() / 2), + ResizeUnit::AbsoluteBytes => format!("{}", resize.absolute_sectors() * change.sector_size), + ResizeUnit::AbsoluteKibis => format!("{}ki", (resize.absolute_sectors() / 2) * (change.sector_size / 512)), ResizeUnit::AbsoluteSectorsWithUnit => format!("{}s", resize.absolute_sectors()), ResizeUnit::AbsoluteMebibyte => format!("{}M", resize.as_absolute_mebibyte()), ResizeUnit::AbsoluteMegabyte => format!("{}M", resize.as_absolute_megabyte()), @@ -345,7 +347,7 @@ where let abs_sectors = resize.absolute_sectors(); resize.old.resize_to(abs_sectors); // TODO: NLL - move_partition(&change.device_path, resize.offset(), 512) + move_partition(&change.device_path, resize.offset(), change.sector_size) .map_err(|why| io::Error::new( why.kind(), format!("failed to move partition at {}: {}", change.path.display(), why) @@ -383,7 +385,7 @@ where let abs_sectors = resize.absolute_sectors(); resize.old.resize_to(abs_sectors); // TODO: NLL - move_partition(&change.device_path, resize.offset(), 512) + move_partition(&change.device_path, resize.offset(), change.sector_size) .map_err(|why| io::Error::new( why.kind(), format!("failed to move partition at {}: {}", change.path.display(), why) diff --git a/crates/disks/src/config/disk.rs b/crates/disks/src/config/disk.rs index e32bfc6a..8df0d98a 100644 --- a/crates/disks/src/config/disk.rs +++ b/crates/disks/src/config/disk.rs @@ -95,6 +95,8 @@ pub struct Disk { pub mount_point: Option, /// The size of the disk in sectors. pub size: u64, + /// The size of each sector + pub sector_size: u64, /// The type of the device, such as SCSI. pub device_type: String, /// The partition table may be either **MSDOS** or **GPT**. @@ -119,7 +121,7 @@ impl BlockDeviceExt for Disk { } impl SectorExt for Disk { - fn get_sector_size(&self) -> u64 { 512 } + fn get_sector_size(&self) -> u64 { self.sector_size } fn get_sectors(&self) -> u64 { self.size } } @@ -173,6 +175,7 @@ impl Disk { }; let size = device.length(); + let sector_size = device.sector_size(); let device_type = format!("{:?}", device.type_()); let read_only = device.read_only(); @@ -197,6 +200,7 @@ impl Disk { file_system: None, serial, size, + sector_size, device_type, read_only, table_type, @@ -436,6 +440,7 @@ impl Disk { /// will be located at the provided `end` value, and checks whether or not that this will /// be possible to do. pub fn resize_partition(&mut self, partition: i32, mut end: u64) -> Result { + let sector_size = self.get_sector_size(); let (backup, num, start); { let partition = self.get_partition_mut(partition) @@ -450,7 +455,7 @@ impl Disk { { let length = end - partition.start_sector; - end -= length % (2 * 1024); + end -= length % (crate::sectors_normalize(2 * 1024, sector_size)); } info!( @@ -459,10 +464,10 @@ impl Disk { end - partition.start_sector ); - assert_eq!(0, (end - partition.start_sector) % (2 * 1024)); + assert_eq!(0, (end - partition.start_sector) % (crate::sectors_normalize(2 * 1024, sector_size))); if end < partition.start_sector - || end - partition.start_sector <= (10 * 1024 * 1024) / 512 + || end - partition.start_sector <= crate::sectors_normalize(10 * 1024 * 1024, sector_size) / sector_size { return Err(DiskError::new_partition_error( partition.device_path.clone(), @@ -540,7 +545,7 @@ impl Disk { self.path().display(), fs, ); - let sector_size = 512; + let sector_size = self.get_sector_size(); self.get_partition_mut(partition) .ok_or(DiskError::PartitionNotFound { partition }) .and_then(|partition| { @@ -750,6 +755,7 @@ impl Disk { kind: new.part_type, start: new.start_sector, end: new.end_sector, + sector_size: self.get_sector_size(), filesystem: source.filesystem, flags: flags_diff( &source.flags, diff --git a/crates/disks/src/config/disk_trait.rs b/crates/disks/src/config/disk_trait.rs index eba9c2a7..e966a957 100644 --- a/crates/disks/src/config/disk_trait.rs +++ b/crates/disks/src/config/disk_trait.rs @@ -90,6 +90,8 @@ pub trait DiskExt: BlockDeviceExt + SectorExt + PartitionTableExt { /// /// An error can occur if the partition will not fit onto the disk. fn add_partition(&mut self, mut builder: PartitionBuilder) -> Result<(), DiskError> { + let sector_size = self.get_sector_size(); + // Ensure that the values aren't already contained within an existing partition. if !Self::LOGICAL && builder.part_type != PartitionType::Extended { info!( @@ -136,7 +138,7 @@ pub trait DiskExt: BlockDeviceExt + SectorExt + PartitionTableExt { ).partition_type(PartitionType::Extended); self.push_partition(part.build()); - builder.start_sector += 1_024_000 / 512 + 1; + builder.start_sector += 1_024_000 / sector_size + 1; } let fs = builder.filesystem; diff --git a/crates/disks/src/config/disks.rs b/crates/disks/src/config/disks.rs index 5a7db150..5f53815f 100644 --- a/crates/disks/src/config/disks.rs +++ b/crates/disks/src/config/disks.rs @@ -460,6 +460,7 @@ impl Disks { partition: &mut PartitionInfo, path: &Path, enc: &LvmEncryption, + sector_size: u64, ) -> Result { // Attempt to decrypt the device. cryptsetup_open(path, &enc).map_err(|why| DecryptionError::Open { @@ -480,7 +481,7 @@ impl Disks { Some(Some(vg)) => { // Set values in the device's partition. partition.volume_group = Some((vg.clone(), Some(enc.clone()))); - let mut luks = LogicalDevice::new(vg, Some(enc.clone()), partition.get_sectors(), 512, true); + let mut luks = LogicalDevice::new(vg, Some(enc.clone()), partition.get_sectors(), sector_size, true); info!("settings luks_parent to {:?}", path); luks.set_luks_parent(path.to_path_buf()); @@ -494,7 +495,7 @@ impl Disks { pv, Some(enc.clone()), partition.get_sectors(), - 512, + sector_size, true ); @@ -516,16 +517,18 @@ impl Disks { // Attempt to find the device in the configuration. for device in &mut self.physical { + let sector_size = device.get_sector_size(); + // TODO: NLL if let Some(partition) = device.get_file_system_mut() { if partition.get_device_path() == path { - decrypt(partition, path, &enc)?; + decrypt(partition, path, &enc, sector_size)?; } } for partition in device.file_system.as_mut().into_iter().chain(device.partitions.iter_mut()) { if partition.get_device_path() == path { - new_device = Some(decrypt(partition, path, &enc)?); + new_device = Some(decrypt(partition, path, &enc, sector_size)?); break } } @@ -955,6 +958,8 @@ impl Disks { } }; + let sector_size = device.get_sector_size(); + if is_efi { // Check if the EFI partition is on a GPT disk. if device.get_partition_table() != Some(PartitionTable::Gpt) { @@ -988,9 +993,9 @@ impl Disks { } // 256 MiB should be the minimal size of the ESP partition. - const REQUIRED_SECTORS: u64 = 524_288; + let required_sectors = crate::sectors_normalize(524_288, sector_size); - if boot.get_sectors() < REQUIRED_SECTORS { + if boot.get_sectors() < required_sectors { return Err(io::Error::new( io::ErrorKind::InvalidInput, "the ESP partition must be at least 256 MiB in size" diff --git a/crates/disks/src/lib.rs b/crates/disks/src/lib.rs index cea2f4c5..9dfcb934 100644 --- a/crates/disks/src/lib.rs +++ b/crates/disks/src/lib.rs @@ -35,3 +35,8 @@ pub use bootloader::{Bootloader, FORCE_BOOTLOADER}; pub use self::config::*; pub use self::error::{DecryptionError, DiskError, PartitionError, PartitionSizeError}; pub use libparted::PartitionFlag; + +/// Normalize 512-byte sector counts to the given sector size +fn sectors_normalize(base_count: u64, sector_size: u64) -> u64 { + base_count / (sector_size / 512) +} diff --git a/ffi/distinst.vapi b/ffi/distinst.vapi index 5cb73bbc..10f2d829 100644 --- a/ffi/distinst.vapi +++ b/ffi/distinst.vapi @@ -146,6 +146,7 @@ namespace Distinst { public int get_partition (); public uint64 get_sectors_free (); public uint64 get_sectors_total (); + public uint64 get_sector_size (); } /** @@ -182,6 +183,10 @@ namespace Distinst { * Gets the number of sectors that this option's device contains. */ public uint64 get_sectors (); + /** + * Gets the size of sectors on this device + */ + public uint64 get_sector_size (); } /** diff --git a/ffi/src/auto.rs b/ffi/src/auto.rs index 682dd609..9f1f2207 100644 --- a/ffi/src/auto.rs +++ b/ffi/src/auto.rs @@ -120,7 +120,7 @@ pub unsafe extern "C" fn distinst_alongside_option_get_sectors_free( let option = &*(option as *const AlongsideOption); match option.method { AlongsideMethod::Shrink { sectors_free, .. } => sectors_free, - AlongsideMethod::Free(ref region) => region.size() + AlongsideMethod::Free(ref region, _) => region.size() } } @@ -131,10 +131,20 @@ pub unsafe extern "C" fn distinst_alongside_option_get_sectors_total( let option = &*(option as *const AlongsideOption); match option.method { AlongsideMethod::Shrink { sectors_total, .. } => sectors_total, - AlongsideMethod::Free(ref region) => region.size() + AlongsideMethod::Free(ref region, _) => region.size() } } +#[no_mangle] +pub unsafe extern "C" fn distinst_alongside_option_get_sector_size( + option: *const DistinstAlongsideOption, +) -> libc::uint64_t { + let option = &*(option as *const AlongsideOption); + match option.method { + AlongsideMethod::Shrink { sector_size, .. } => sector_size, + AlongsideMethod::Free(_, size) => size + } +} #[repr(C)] pub struct DistinstRefreshOption; @@ -284,6 +294,18 @@ pub unsafe extern "C" fn distinst_erase_option_get_sectors( option.sectors } +#[no_mangle] +pub unsafe extern "C" fn distinst_erase_option_get_sector_size( + option: *const DistinstEraseOption, +) -> libc::uint64_t { + if null_check(option).is_err() { + return 0; + } + + let option = &*(option as *const EraseOption); + option.sector_size +} + #[no_mangle] pub unsafe extern "C" fn distinst_erase_option_is_rotational( option: *const DistinstEraseOption, diff --git a/src/auto/options/alongside_option.rs b/src/auto/options/alongside_option.rs index c6d0bfa6..cf45b14b 100644 --- a/src/auto/options/alongside_option.rs +++ b/src/auto/options/alongside_option.rs @@ -8,9 +8,10 @@ pub enum AlongsideMethod { partition: i32, sectors_total: u64, sectors_free: u64, + sector_size: u64, path: PathBuf, }, - Free(Region) + Free(Region, u64) } #[derive(Debug)] @@ -37,24 +38,24 @@ impl fmt::Display for AlongsideOption { let device = self.device.display(); match self.method { - AlongsideMethod::Shrink { sectors_total, sectors_free, ref path, .. } => { + AlongsideMethod::Shrink { sectors_total, sectors_free, sector_size, ref path, .. } => { write!( f, "alongside {:?} ({}) by shrinking {}: {} of {} MiB free", os, device, path.display(), - sectors_free / 2048, - sectors_total / 2048 + sectors_free / 2048 * (sector_size / 512), + sectors_total / 2048 * (sector_size / 512) ) }, - AlongsideMethod::Free(ref region) => { + AlongsideMethod::Free(ref region, ref sector_size) => { write!( f, "alongside {:?} ({}) using free space: {} MiB free", os, device, - region.size() / 2048, + region.size() / 2048 * (*sector_size / 512), ) } } diff --git a/src/auto/options/apply.rs b/src/auto/options/apply.rs index 2b3929de..73fe6c39 100644 --- a/src/auto/options/apply.rs +++ b/src/auto/options/apply.rs @@ -123,6 +123,12 @@ fn alongside_config( path: option.device.clone(), })?; + let sector_size = device.get_sector_size(); + + let esp_sectors = crate::sectors_normalize(DEFAULT_ESP_SECTORS, sector_size); + let swap_sectors = crate::sectors_normalize(DEFAULT_SWAP_SECTORS, sector_size); + let recovery_sectors = crate::sectors_normalize(DEFAULT_RECOVER_SECTORS, sector_size); + let (mut start, end) = match option.method { AlongsideMethod::Shrink { partition, .. } => { let resize = device.get_partition_mut(partition) @@ -135,7 +141,7 @@ fn alongside_config( resize.shrink_to(sectors)?; (resize.end_sector + 1, end) } - AlongsideMethod::Free(ref region) => { + AlongsideMethod::Free(ref region, _) => { (region.start + 1, region.end - 1) } }; @@ -157,7 +163,7 @@ fn alongside_config( // } // } - let esp_end = start + DEFAULT_ESP_SECTORS; + let esp_end = start + esp_sectors; device.add_partition( PartitionBuilder::new(start, esp_end, Fat32) @@ -167,7 +173,7 @@ fn alongside_config( start = esp_end; - let recovery_end = start + DEFAULT_RECOVER_SECTORS; + let recovery_end = start + recovery_sectors; device.add_partition( PartitionBuilder::new(start, recovery_end, Fat32) .mount("/recovery".into()) @@ -177,7 +183,7 @@ fn alongside_config( start = recovery_end; } else if lvm.is_some() { // BIOS systems with an encrypted root must have a separate boot partition. - let boot_end = start + DEFAULT_ESP_SECTORS; + let boot_end = start + esp_sectors; device.add_partition( PartitionBuilder::new(start, boot_end, Ext4) @@ -197,7 +203,7 @@ fn alongside_config( .logical_volume(root_vg, Some(enc)) )?; } else { - let swap = end - DEFAULT_SWAP_SECTORS; + let swap = end - swap_sectors; // Only create a new unencrypted swap partition if a swap partition does not already exist. let end = if !device.get_partitions().iter().any(|p| p.filesystem == Some(Swap)) { @@ -222,7 +228,7 @@ fn alongside_config( .ok_or(InstallOptionError::LogicalDeviceNotFound { vg: root_vg })?; let start = lvm_device.get_sector(Sector::Start); - let swap = lvm_device.get_sector(Sector::UnitFromEnd(DEFAULT_SWAP_SECTORS)); + let swap = lvm_device.get_sector(Sector::UnitFromEnd(swap_sectors)); let end = lvm_device.get_sector(Sector::End); lvm_device.add_partition( @@ -434,9 +440,9 @@ fn erase_config( let bootloader = Bootloader::detect(); let start_sector = Sector::Start; - let boot_sector = Sector::Unit(DEFAULT_ESP_SECTORS); - let recovery_sector = Sector::Unit(DEFAULT_ESP_SECTORS + DEFAULT_RECOVER_SECTORS); - let swap_sector = Sector::UnitFromEnd(DEFAULT_SWAP_SECTORS); + let boot_sector: Sector; + let recovery_sector: Sector; + let swap_sector: Sector; let end_sector = Sector::End; let (lvm, root_vg) = match generate_encryption(password)? { @@ -451,6 +457,12 @@ fn erase_config( }, )?; + let sector_size = device.get_sector_size(); + + boot_sector = Sector::Unit(crate::sectors_normalize(DEFAULT_ESP_SECTORS, sector_size)); + recovery_sector = Sector::Unit(crate::sectors_normalize(DEFAULT_ESP_SECTORS + DEFAULT_RECOVER_SECTORS, sector_size)); + swap_sector = Sector::UnitFromEnd(crate::sectors_normalize(DEFAULT_SWAP_SECTORS, sector_size)); + let result = match bootloader { Bootloader::Efi => { device.mklabel(PartitionTable::Gpt) diff --git a/src/auto/options/erase_option.rs b/src/auto/options/erase_option.rs index 0234aec9..1af9095d 100644 --- a/src/auto/options/erase_option.rs +++ b/src/auto/options/erase_option.rs @@ -7,10 +7,11 @@ pub const MEETS_REQUIREMENTS: u8 = 4; #[derive(Debug)] pub struct EraseOption { - pub device: PathBuf, - pub model: String, - pub sectors: u64, - pub flags: u8, + pub device: PathBuf, + pub model: String, + pub sectors: u64, + pub sector_size: u64, + pub flags: u8, } impl fmt::Display for EraseOption { diff --git a/src/auto/options/mod.rs b/src/auto/options/mod.rs index 90ac1423..0414dc8d 100644 --- a/src/auto/options/mod.rs +++ b/src/auto/options/mod.rs @@ -44,7 +44,7 @@ impl InstallOptions { let erase_options = &mut erase_options; let refresh_options = &mut refresh_options; - let mut check_partition = |part: &PartitionInfo| -> Option { + let mut check_partition = |part: &PartitionInfo, required_space: u64| -> Option { // We're only going to find Linux on a Linux-compatible file system. if let Some(os) = part.probe_os() { info!("found OS on {:?}: {}", part.get_device_path(), match os { @@ -94,13 +94,17 @@ impl InstallOptions { continue } + let sector_size = device.get_sector_size(); + let required_space = crate::sectors_normalize(required_space, sector_size); + let shrink_overhead = crate::sectors_normalize(shrink_overhead, sector_size); + let mut last_end_sector = 1024; for part in device.get_partitions() { if let Ok(used) = part.sectors_used() { let sectors = part.get_sectors(); let free = sectors - used; - let os = check_partition(part); + let os = check_partition(part, required_space); if required_space + shrink_overhead < free { info!("found shrinkable partition on {:?}: {} free of {}", part.get_device_path(), free, sectors); alongside_options.push(AlongsideOption { @@ -110,7 +114,8 @@ impl InstallOptions { path: part.get_device_path().to_path_buf(), partition: part.number, sectors_free: free, - sectors_total: sectors + sectors_total: sectors, + sector_size } }); } @@ -121,7 +126,7 @@ impl InstallOptions { alongside_options.push(AlongsideOption { device: device.get_device_path().to_path_buf(), alongside: None, - method: AlongsideMethod::Free(Region::new(last_end_sector + 1, part.start_sector - 1)) + method: AlongsideMethod::Free(Region::new(last_end_sector + 1, part.start_sector - 1), sector_size) }) } @@ -134,7 +139,7 @@ impl InstallOptions { alongside_options.push(AlongsideOption { device: device.get_device_path().to_path_buf(), alongside: None, - method: AlongsideMethod::Free(Region::new(last_end_sector + 1, last_sector)) + method: AlongsideMethod::Free(Region::new(last_end_sector + 1, last_sector), sector_size) }) } @@ -149,6 +154,7 @@ impl InstallOptions { } let sectors = device.get_sectors(); + let sector_size = device.get_sector_size(); info!("found erase option on {:?}: {} sectors", device.get_device_path(), sectors); erase_options.push(EraseOption { device: device.get_device_path().to_path_buf(), @@ -161,6 +167,7 @@ impl InstallOptions { } }, sectors, + sector_size, flags: { let mut flags = if device.is_removable() { IS_REMOVABLE @@ -184,8 +191,11 @@ impl InstallOptions { } for device in disks.get_logical_devices() { + let sector_size = device.get_sector_size(); + let required_space = crate::sectors_normalize(required_space, sector_size); + for part in device.get_partitions() { - check_partition(part); + check_partition(part, required_space); } } } diff --git a/src/lib.rs b/src/lib.rs index 9093954e..cc0adf5d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,7 +92,7 @@ pub fn device_map_exists(name: &str) -> bool { dmlist().ok().map_or(false, |list| list.contains(&name.into())) } -/// Gets the minimum number of sectors required. The input should be in sectors, not bytes. +/// Gets the minimum number of 512-byte sectors required. The input should be in sectors, not bytes. /// /// The number of sectors required is calculated through: /// @@ -118,3 +118,7 @@ pub fn minimum_disk_size(default: u64) -> u64 { casper_size + DEFAULT_ESP_SECTORS + DEFAULT_RECOVER_SECTORS + DEFAULT_SWAP_SECTORS } + +fn sectors_normalize(base_count: u64, sector_size: u64) -> u64 { + base_count / (sector_size / 512) +}