1414
1515use anyhow:: { anyhow, bail, Context , Result } ;
1616use gptman:: { GPTPartitionEntry , GPT } ;
17+ use mbrman:: { MBRPartitionEntry , MBR } ;
1718use nix:: sys:: stat:: { major, minor} ;
1819use nix:: { errno:: Errno , mount, sched} ;
1920use regex:: Regex ;
@@ -32,7 +33,7 @@ use std::path::{Path, PathBuf};
3233use std:: process:: Command ;
3334use std:: thread:: sleep;
3435use std:: time:: Duration ;
35- use uuid:: Uuid ;
36+ use uuid:: { uuid , Uuid } ;
3637
3738use crate :: cmdline:: PartitionFilter ;
3839use crate :: util:: * ;
@@ -519,6 +520,36 @@ pub struct SavedPartitions {
519520 partitions : Vec < ( u32 , GPTPartitionEntry ) > ,
520521}
521522
523+ fn translate_mbr_types_to_gpt ( sys : u8 ) -> Uuid {
524+ // non inclusive best effort mapping of MBR types to GPT types
525+ // https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs && https://tldp.org/HOWTO/Partition-Mass-Storage-Definitions-Naming-HOWTO/x190.html
526+
527+ match sys {
528+ // Linux filesystem
529+ 0x01 => uuid ! ( "0FC63DAF-8483-4772-8E79-3D69D8477DE4" ) ,
530+ 0x04 => uuid ! ( "0FC63DAF-8483-4772-8E79-3D69D8477DE4" ) ,
531+ 0x06 => uuid ! ( "0FC63DAF-8483-4772-8E79-3D69D8477DE4" ) ,
532+ // Linux Filesystem Data
533+ 0x83 => uuid ! ( "0FC63DAF-8483-4772-8E79-3D69D8477DE4" ) ,
534+ // Linux LVM
535+ 0x8e => uuid ! ( "E6D6D379-F507-44C2-A23C-238F2A3DF928" ) ,
536+ // Linux Swap
537+ 0x82 => uuid ! ( "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F" ) ,
538+ _ => uuid ! ( "00000000-0000-0000-0000-000000000000" ) ,
539+ }
540+ }
541+
542+ fn translate_mbr_to_gpt ( mbr_partition : & MBRPartitionEntry ) -> GPTPartitionEntry {
543+ let mut gpt_partition = GPTPartitionEntry :: empty ( ) ;
544+
545+ gpt_partition. partition_type_guid = * translate_mbr_types_to_gpt ( mbr_partition. sys ) . as_bytes ( ) ;
546+ gpt_partition. unique_partition_guid = * Uuid :: new_v4 ( ) . as_bytes ( ) ;
547+ gpt_partition. starting_lba = u64:: from ( mbr_partition. starting_lba ) ;
548+ // -1 because the end is inclusive
549+ gpt_partition. ending_lba = u64:: from ( mbr_partition. starting_lba + mbr_partition. sectors ) - 1 ;
550+ gpt_partition
551+ }
552+
522553impl SavedPartitions {
523554 /// Create a SavedPartitions for a block device with a sector size.
524555 pub fn new_from_disk ( disk : & mut File , filters : & [ PartitionFilter ] ) -> Result < Self > {
@@ -566,39 +597,58 @@ impl SavedPartitions {
566597 } ) ;
567598 }
568599
600+ // Create a vector of the partitions
601+ let mut partitions = Vec :: new ( ) ;
602+
569603 // read GPT
570- let gpt = match GPT :: find_from ( disk) {
571- Ok ( gpt) => gpt,
604+ match GPT :: find_from ( disk) {
605+ Ok ( gpt) => {
606+ // cross-check GPT sector size
607+ Self :: verify_gpt_sector_size ( & gpt, sector_size) ?;
608+
609+ // save partitions accepted by filters
610+ for ( i, p) in gpt. iter ( ) {
611+ if Self :: matches_filters ( i, p, filters) {
612+ partitions. push ( ( i, p. clone ( ) ) ) ;
613+ }
614+ }
615+ }
572616 Err ( gptman:: Error :: InvalidSignature ) => {
573- // ensure no indexes are listed to be saved from a MBR disk
574- // we don't need to check for labels since MBR does not support them
617+ // no GPT, check for MBR
575618 if filters
576619 . iter ( )
577620 . any ( |f| matches ! ( f, PartitionFilter :: Index ( _, _) ) )
578621 && disk_has_mbr ( disk) . context ( "checking if disk has an MBR" ) ?
579622 {
580- bail ! ( "saving partitions from an MBR disk is not yet supported" ) ;
623+ let mbr = MBR :: read_from ( disk, u32:: try_from ( sector_size) ?)
624+ . context ( "reading disk as MBR" ) ?;
625+
626+ // cross-check MBR sector size
627+ Self :: verify_mbr_sector_size ( & mbr, sector_size) ?;
628+
629+ for ( i, p) in mbr. iter ( ) {
630+ if Self :: matches_filters_mbr ( i. try_into ( ) . unwrap ( ) , p, filters) {
631+ // if the partition is using any of the last 33 sectors, we need to bail.
632+ if p. starting_lba + p. sectors < mbr. disk_size - 33 {
633+ partitions. push ( (
634+ i. try_into ( ) . context ( "convert usize into u32" ) ?,
635+ translate_mbr_to_gpt ( p) ,
636+ ) ) ;
637+ } else {
638+ bail ! ( "MBR partition {} is using the last 33 sectors of the disk, which is not supported by GPT" , i) ;
639+ }
640+ }
641+ }
581642 }
582-
583643 // no GPT on this disk, so no partitions to save
584644 return Ok ( Self {
585645 sector_size,
586- partitions : Vec :: new ( ) ,
646+ partitions : partitions ,
587647 } ) ;
588648 }
589649 Err ( e) => return Err ( e) . context ( "reading partition table" ) ,
590650 } ;
591651
592- // cross-check GPT sector size
593- Self :: verify_gpt_sector_size ( & gpt, sector_size) ?;
594-
595- // save partitions accepted by filters
596- let mut partitions = Vec :: new ( ) ;
597- for ( i, p) in gpt. iter ( ) {
598- if Self :: matches_filters ( i, p, filters) {
599- partitions. push ( ( i, p. clone ( ) ) ) ;
600- }
601- }
602652 let result = Self {
603653 sector_size,
604654 partitions,
@@ -654,6 +704,17 @@ impl SavedPartitions {
654704 Ok ( ( ) )
655705 }
656706
707+ fn verify_mbr_sector_size ( mbr : & MBR , sector_size : u64 ) -> Result < ( ) > {
708+ if u64:: from ( mbr. sector_size ) != sector_size {
709+ bail ! (
710+ "MBR sector size {} doesn't match expected {}" ,
711+ mbr. sector_size,
712+ sector_size
713+ ) ;
714+ }
715+ Ok ( ( ) )
716+ }
717+
657718 fn matches_filters ( i : u32 , p : & GPTPartitionEntry , filters : & [ PartitionFilter ] ) -> bool {
658719 use PartitionFilter :: * ;
659720 if !p. is_used ( ) {
@@ -668,6 +729,19 @@ impl SavedPartitions {
668729 } )
669730 }
670731
732+ fn matches_filters_mbr ( i : u32 , p : & MBRPartitionEntry , filters : & [ PartitionFilter ] ) -> bool {
733+ use PartitionFilter :: * ;
734+ if !p. is_used ( ) {
735+ return false ;
736+ }
737+ filters. iter ( ) . any ( |f| match f {
738+ Index ( Some ( first) , _) if first. get ( ) > i => false ,
739+ Index ( _, Some ( last) ) if last. get ( ) < i => false ,
740+ Index ( _, _) => true ,
741+ _ => false ,
742+ } )
743+ }
744+
671745 /// Unconditionally write the saved partitions, and only the saved
672746 /// partitions, to the disk. Write a protective MBR and overwrite any
673747 /// MBR boot code. Updating the kernel partition table is the caller's
@@ -1569,17 +1643,35 @@ mod tests {
15691643
15701644 // test trying to save partitions from a MBR disk
15711645 let mut disk = make_unformatted_disk ( ) ;
1572- gptman:: GPT :: write_protective_mbr_into ( & mut disk, 512 ) . unwrap ( ) ;
1573- // label only
1574- SavedPartitions :: new ( & mut disk, 512 , & vec ! [ label( "*i*" ) ] ) . unwrap ( ) ;
1646+ let mut mbr = mbrman:: MBR :: new_from ( & mut disk, 512 as u32 , [ 0xff ; 4 ] )
1647+ . expect ( "could not create partition table" ) ;
1648+
1649+ // create a mbr partition to copy over to gpt
1650+ mbr[ 1 ] = mbrman:: MBRPartitionEntry {
1651+ boot : mbrman:: BOOT_INACTIVE ,
1652+ first_chs : mbrman:: CHS :: empty ( ) ,
1653+ sys : 0x0f ,
1654+ last_chs : mbrman:: CHS :: empty ( ) ,
1655+ starting_lba : 1 ,
1656+ sectors : mbr. disk_size - 5000 ,
1657+ } ;
1658+
1659+ mbr. write_into ( & mut disk) . unwrap ( ) ;
15751660 // index only
1576- assert_eq ! (
1577- SavedPartitions :: new( & mut disk, 512 , & vec![ Index ( index( 1 ) , index( 1 ) ) ] )
1578- . unwrap_err( )
1579- . to_string( ) ,
1580- "saving partitions from an MBR disk is not yet supported"
1581- ) ;
1582- // label and index
1661+ let saved = SavedPartitions :: new ( & mut disk, 512 , & vec ! [ Index ( index( 1 ) , index( 1 ) ) ] ) . unwrap ( ) ;
1662+ assert ! ( saved. is_saved( ) ) ;
1663+
1664+ // create a mbr partition that uses some of the last 33 sectors of the disk
1665+ mbr[ 1 ] = mbrman:: MBRPartitionEntry {
1666+ boot : mbrman:: BOOT_INACTIVE ,
1667+ first_chs : mbrman:: CHS :: empty ( ) ,
1668+ sys : 0x0f ,
1669+ last_chs : mbrman:: CHS :: empty ( ) ,
1670+ starting_lba : 1 ,
1671+ sectors : mbr. disk_size - 22 ,
1672+ } ;
1673+ mbr. write_into ( & mut disk) . unwrap ( ) ;
1674+
15831675 assert_eq ! (
15841676 SavedPartitions :: new(
15851677 & mut disk,
@@ -1588,7 +1680,7 @@ mod tests {
15881680 )
15891681 . unwrap_err( )
15901682 . to_string( ) ,
1591- "saving partitions from an MBR disk is not yet supported"
1683+ "MBR partition 1 is using the last 33 sectors of the disk, which is not supported by GPT "
15921684 ) ;
15931685
15941686 // test sector size mismatch
0 commit comments