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,32 @@ 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+
522549impl SavedPartitions {
523550 /// Create a SavedPartitions for a block device with a sector size.
524551 pub fn new_from_disk ( disk : & mut File , filters : & [ PartitionFilter ] ) -> Result < Self > {
@@ -566,39 +593,58 @@ impl SavedPartitions {
566593 } ) ;
567594 }
568595
596+ // Create a vector of the partitions
597+ let mut partitions = Vec :: new ( ) ;
598+
569599 // read GPT
570- let gpt = match GPT :: find_from ( disk) {
571- Ok ( gpt) => gpt,
600+ match GPT :: find_from ( disk) {
601+ Ok ( gpt) => {
602+ // cross-check GPT sector size
603+ Self :: verify_gpt_sector_size ( & gpt, sector_size) ?;
604+
605+ // save partitions accepted by filters
606+ for ( i, p) in gpt. iter ( ) {
607+ if Self :: matches_filters ( i, p, filters) {
608+ partitions. push ( ( i, p. clone ( ) ) ) ;
609+ }
610+ }
611+ }
572612 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
613+ // no GPT, check for MBR
575614 if filters
576615 . iter ( )
577616 . any ( |f| matches ! ( f, PartitionFilter :: Index ( _, _) ) )
578617 && disk_has_mbr ( disk) . context ( "checking if disk has an MBR" ) ?
579618 {
580- bail ! ( "saving partitions from an MBR disk is not yet supported" ) ;
619+ let mbr = MBR :: read_from ( disk, u32:: try_from ( sector_size) ?)
620+ . context ( "reading disk as MBR" ) ?;
621+
622+ // cross-check MBR sector size
623+ Self :: verify_mbr_sector_size ( & mbr, sector_size) ?;
624+
625+ for ( i, p) in mbr. iter ( ) {
626+ if Self :: matches_filters_mbr ( i. try_into ( ) . unwrap ( ) , p, filters) {
627+ // if the partition is using any of the last 33 sectors, we need to bail.
628+ if p. starting_lba + p. sectors < mbr. disk_size - 33 {
629+ partitions. push ( (
630+ i. try_into ( ) . context ( "convert usize into u32" ) ?,
631+ translate_mbr_to_gpt ( p) ,
632+ ) ) ;
633+ } else {
634+ bail ! ( "MBR partition {} is using the last 33 sectors of the disk, which is not supported by GPT" , i) ;
635+ }
636+ }
637+ }
581638 }
582-
583639 // no GPT on this disk, so no partitions to save
584640 return Ok ( Self {
585641 sector_size,
586- partitions : Vec :: new ( ) ,
642+ partitions : partitions ,
587643 } ) ;
588644 }
589645 Err ( e) => return Err ( e) . context ( "reading partition table" ) ,
590646 } ;
591647
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- }
602648 let result = Self {
603649 sector_size,
604650 partitions,
@@ -654,6 +700,17 @@ impl SavedPartitions {
654700 Ok ( ( ) )
655701 }
656702
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+ }
713+
657714 fn matches_filters ( i : u32 , p : & GPTPartitionEntry , filters : & [ PartitionFilter ] ) -> bool {
658715 use PartitionFilter :: * ;
659716 if !p. is_used ( ) {
@@ -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