@@ -16,6 +16,7 @@ use anyhow::{anyhow, bail, Context, Result};
1616use gptman:: { GPTPartitionEntry , GPT } ;
1717use nix:: sys:: stat:: { major, minor} ;
1818use nix:: { errno:: Errno , mount, sched} ;
19+ use mbrman:: { MBRPartitionEntry , MBR } ;
1920use regex:: Regex ;
2021use std:: collections:: { HashMap , HashSet } ;
2122use std:: fs:: {
@@ -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,33 @@ 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+ match sys {
526+ // Linux filesystem
527+ 0x01 => uuid ! ( "0FC63DAF-8483-4772-8E79-3D69D8477DE4" ) ,
528+ // Linux Filesystem Data
529+ 0x83 => uuid ! ( "0FC63DAF-8483-4772-8E79-3D69D8477DE4" ) ,
530+ // Linux LVM
531+ 0x8e => uuid ! ( "E6D6D379-F507-44C2-A23C-238F2A3DF928" ) ,
532+ // Linux Swap
533+ 0x82 => uuid ! ( "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F" ) ,
534+ _ => uuid ! ( "00000000-0000-0000-0000-ffff00000000" ) ,
535+ }
536+ }
537+
538+ fn translate_mbr_to_gpt ( mbr_partition : & MBRPartitionEntry ) -> GPTPartitionEntry {
539+ let mut gpt_partition = GPTPartitionEntry :: empty ( ) ;
540+
541+ gpt_partition. partition_type_guid = * translate_mbr_types_to_gpt ( mbr_partition. sys ) . as_bytes ( ) ;
542+ gpt_partition. unique_partition_guid = * Uuid :: new_v4 ( ) . as_bytes ( ) ;
543+ gpt_partition. starting_lba = u64:: from ( mbr_partition. starting_lba ) ;
544+ // -1 because the end is inclusive
545+ gpt_partition. ending_lba = u64:: from ( mbr_partition. starting_lba + mbr_partition. sectors ) - 1 ;
546+ gpt_partition
547+ }
548+
549+
522550impl SavedPartitions {
523551 /// Create a SavedPartitions for a block device with a sector size.
524552 pub fn new_from_disk ( disk : & mut File , filters : & [ PartitionFilter ] ) -> Result < Self > {
@@ -556,6 +584,7 @@ impl SavedPartitions {
556584 Self :: new ( disk, sector_size, filters)
557585 }
558586
587+
559588 fn new ( disk : & mut File , sector_size : u64 , filters : & [ PartitionFilter ] ) -> Result < Self > {
560589 // if there are no filters, ignore existing GPT, since we're going to
561590 // overwrite it
@@ -565,40 +594,57 @@ impl SavedPartitions {
565594 partitions : Vec :: new ( ) ,
566595 } ) ;
567596 }
597+
598+ // Create a vector of the partitions
599+ let mut partitions = Vec :: new ( ) ;
568600
569601 // read GPT
570- let gpt = match GPT :: find_from ( disk) {
571- Ok ( gpt) => gpt,
602+ match GPT :: find_from ( disk) {
603+ Ok ( gpt) => {
604+ // cross-check GPT sector size
605+ Self :: verify_gpt_sector_size ( & gpt, sector_size) ?;
606+
607+ // save partitions accepted by filters
608+ for ( i, p) in gpt. iter ( ) {
609+ if Self :: matches_filters ( i, p, filters) {
610+ partitions. push ( ( i, p. clone ( ) ) ) ;
611+ }
612+ }
613+ } ,
572614 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
615+ // no GPT, check for MBR
575616 if filters
576617 . iter ( )
577618 . any ( |f| matches ! ( f, PartitionFilter :: Index ( _, _) ) )
578619 && disk_has_mbr ( disk) . context ( "checking if disk has an MBR" ) ?
579- {
580- bail ! ( "saving partitions from an MBR disk is not yet supported" ) ;
581- }
620+ {
621+ let mbr = MBR :: read_from ( disk, u32:: try_from ( sector_size) ?) . context ( "reading disk as MBR" ) ?;
622+
623+ // cross-check MBR sector size
624+ Self :: verify_mbr_sector_size ( & mbr, sector_size) ?;
625+
626+ for ( i, p) in mbr. iter ( ) {
627+ if Self :: matches_filters_mbr ( i. try_into ( ) . unwrap ( ) , p, filters) {
628+ // if the partition is using any of the last 33 sectors, we need to bail.
629+ if p. starting_lba + p. sectors < mbr. disk_size - 33 {
630+ partitions. push ( ( i. try_into ( ) . context ( "convert usize into u32" ) ?, translate_mbr_to_gpt ( p) ) ) ;
631+ } else {
632+ bail ! ( "MBR partition {} is using the last 33 sectors of the disk, which is not supported by GPT" , i) ;
633+ }
634+ }
635+ }
582636
637+ }
583638 // no GPT on this disk, so no partitions to save
584639 return Ok ( Self {
585640 sector_size,
586- partitions : Vec :: new ( ) ,
641+ partitions : partitions ,
587642 } ) ;
588643 }
589644 Err ( e) => return Err ( e) . context ( "reading partition table" ) ,
590645 } ;
591646
592- // cross-check GPT sector size
593- Self :: verify_gpt_sector_size ( & gpt, sector_size) ?;
594647
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- }
602648 let result = Self {
603649 sector_size,
604650 partitions,
@@ -653,6 +699,17 @@ impl SavedPartitions {
653699 }
654700 Ok ( ( ) )
655701 }
702+
703+ fn verify_mbr_sector_size ( mbr : & MBR , sector_size : u64 ) -> Result < ( ) > {
704+ if u64:: from ( mbr. sector_size ) != sector_size {
705+ bail ! (
706+ "MBR sector size {} doesn't match expected {}" ,
707+ mbr. sector_size,
708+ sector_size
709+ ) ;
710+ }
711+ Ok ( ( ) )
712+ }
656713
657714 fn matches_filters ( i : u32 , p : & GPTPartitionEntry , filters : & [ PartitionFilter ] ) -> bool {
658715 use PartitionFilter :: * ;
@@ -668,6 +725,19 @@ impl SavedPartitions {
668725 } )
669726 }
670727
728+ fn matches_filters_mbr ( i : u32 , p : & MBRPartitionEntry , filters : & [ PartitionFilter ] ) -> bool {
729+ use PartitionFilter :: * ;
730+ if !p. is_used ( ) {
731+ return false ;
732+ }
733+ filters. iter ( ) . any ( |f| match f {
734+ Index ( Some ( first) , _) if first. get ( ) > i => false ,
735+ Index ( _, Some ( last) ) if last. get ( ) < i => false ,
736+ Index ( _, _) => true ,
737+ _ => false ,
738+ } )
739+ }
740+
671741 /// Unconditionally write the saved partitions, and only the saved
672742 /// partitions, to the disk. Write a protective MBR and overwrite any
673743 /// MBR boot code. Updating the kernel partition table is the caller's
@@ -1569,17 +1639,35 @@ mod tests {
15691639
15701640 // test trying to save partitions from a MBR disk
15711641 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 ( ) ;
1642+ let mut mbr = mbrman:: MBR :: new_from ( & mut disk, 512 as u32 , [ 0xff ; 4 ] )
1643+ . expect ( "could not create partition table" ) ;
1644+
1645+ // create a mbr partition to copy over to gpt
1646+ mbr[ 1 ] = mbrman:: MBRPartitionEntry {
1647+ boot : mbrman:: BOOT_INACTIVE ,
1648+ first_chs : mbrman:: CHS :: empty ( ) ,
1649+ sys : 0x0f ,
1650+ last_chs : mbrman:: CHS :: empty ( ) ,
1651+ starting_lba : 1 ,
1652+ sectors : mbr. disk_size - 5000 ,
1653+ } ;
1654+
1655+ mbr. write_into ( & mut disk) . unwrap ( ) ;
15751656 // 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
1657+ let saved = SavedPartitions :: new ( & mut disk, 512 , & vec ! [ Index ( index( 1 ) , index( 1 ) ) ] ) . unwrap ( ) ;
1658+ assert ! ( saved. is_saved( ) ) ;
1659+
1660+ // create a mbr partition that uses some of the last 33 sectors of the disk
1661+ mbr[ 1 ] = mbrman:: MBRPartitionEntry {
1662+ boot : mbrman:: BOOT_INACTIVE ,
1663+ first_chs : mbrman:: CHS :: empty ( ) ,
1664+ sys : 0x0f ,
1665+ last_chs : mbrman:: CHS :: empty ( ) ,
1666+ starting_lba : 1 ,
1667+ sectors : mbr. disk_size - 22 ,
1668+ } ;
1669+ mbr. write_into ( & mut disk) . unwrap ( ) ;
1670+
15831671 assert_eq ! (
15841672 SavedPartitions :: new(
15851673 & mut disk,
@@ -1588,7 +1676,7 @@ mod tests {
15881676 )
15891677 . unwrap_err( )
15901678 . to_string( ) ,
1591- "saving partitions from an MBR disk is not yet supported"
1679+ "MBR partition 1 is using the last 33 sectors of the disk, which is not supported by GPT "
15921680 ) ;
15931681
15941682 // test sector size mismatch
0 commit comments