#!/usr/bin/perl -w # # $Id: parted2fdisk.pl 3563 2016-04-11 17:43:30Z bruno $ # # parted2fdisk: fdisk like interface for parted # [developed for mindi/mondo http://www.mondorescue.org] # # Aims to be architecture independant (i386/ia64) # Tested on ia64 with RHAS 2.1 - Mandrake 9.0 - RHEL 3.0 - SLES 10 # # Copyright B. Cornec 2000-2015 # Provided under the GPL v2 use strict; use File::Basename; =pod =head1 NAME parted2fdisk is a fdisk like command using parted internally. =head1 DESCRIPTION parted2fdisk behaves like the fdisk command, but dialog internally with parted in order to manipulate partition tables, which allow it to support GPT partition format as well as MBR, contrary to fdisk. It aims at providing compatible external interface with fdisk. Developed initialy for ia64 Linux, it is also useful now on x86 systems using GPT partition format (for large HDDs). =head1 SYNOPSIS parted2fdisk -s partition parted2fdisk -l device parted2fdisk [-n] device =head1 OPTIONS =over 4 =item B<-s> Print the size (in blocks) of the given partition. =item B<-n> Fake mode. Doesn't pass the commands just simulate. =item B<-l> List the partition tables for the specified device and then exit. =item B Allow the creation and manipulation of partition tables. =back =head1 ARGUMENTS =over 4 =item B partition device file (only used with -s option). =item B device file to work on. =back =head1 WEB SITES The main Web site of the project is available at L. Bug reports should be filled using the trac instance of the project at L. =head1 USER MAILING LIST For community exchanges around MondoRescue please use the list L =head1 AUTHORS The MondoRescue team lead by Bruno Cornec L. =head1 COPYRIGHT MondoRescue is distributed under the GPL v2.0 license or later, described in the file C included with the distribution. =cut $ENV{LANG} = "C"; $ENV{LANGUAGE} = "C"; $ENV{LC_ALL} = "C"; # Log my $flog = "/var/log/parted2fdisk.log"; open(FLOG, "> $flog") || die "Unable to open $flog"; my $fdisk = "/sbin/fdisk"; $fdisk = "/usr/sbin/fdisk" if (not -x "/sbin/fdisk"); my $parted = "/sbin/parted"; $parted = "/usr/sbin/parted" if (not -x "/sbin/parted"); my $i; my $l; my $part; my $wpart; my $start = ""; my $end = ""; my $cylstart; my $cylend; my %start; my %end; my %type; my $arch; my $fake = 0; my $mega = 1048576; # Immediate flushing to avoids read error from mondorestore in log files $| = 1; # Determine on which arch we're running if (defined ($ENV{ARCH})) { $arch = $ENV{ARCH}; } else { $arch = `uname -m`; chomp($arch); } # # Looking for fdisk # $fdisk = is_lsb($fdisk); # # We always use fdisk except on ia64 with GPT types of # partition tables where we need parted # All should return fdisk like format so that callers # think they have called fdisk directly # my $un; my $type; my $args = ""; my $device = ""; my $endmax = ""; if ($#ARGV < 0) { printf FLOG "No arguments given exiting ...\n"; mysyn(); } my %pid = ( "FAT" => "6", "fat32" => "b", "fat16" => "e", "ext2" => "83", "ext3" => "83", "ext4" => "83", "xfs" => "83", "btrfs" => "83", "reiserfs" => "83", "linux-swap" => "82", "lvm" => "8e", "raid" => "fd", "" => "", ); my %pnum; # Reverse table of pid while (($i,$l) = each %pid) { next if ($i eq "ext2"); $pnum{$l} = $i; } foreach $i (@ARGV) { # We support at most one option and one device print FLOG "Parameter found : $i\n"; if ($i =~ /^\/dev\//) { $device = $i; next; } elsif ($i =~ /^-/) { $args = $i; next; } else { mysyn(); } } if (($args ne "") and ($device eq "")) { mysyn(); } # -s takes a partition as arg if ($args =~ /-s/) { $wpart = $device; $device =~ s/[0-9]+$//; } if ($args =~ /-n/) { print FLOG "Fake mode. Nothing will be really done\n"; $fake = 1; } print FLOG "Called with device $device and arg $args\n"; if ($arch =~ /^ia64/) { # Check partition table type print FLOG "We're on ia64 ...\n"; $parted = is_lsb($parted); $type = which_type($device); if ($type ne "msdos") { print FLOG "Not an msdos type of disk label\n"; if ($args =~ /-l/) { fdisk_list($device,undef,\%start,\%end, 1); } elsif ($args =~ /-s/) { fdisk_list($device,$wpart,\%start,\%end, 1); } elsif (($args =~ /-/) and ($fake == 0)) { printf FLOG "Option not supported ($args) ...\n"; printf FLOG "Please report to the author\n"; mysyn(); } else { # Read fdisk orders on stdin and pass them to parted # on the command line as parted doesn't read on stdin print FLOG "Translating fdisk command to parted\n"; while ($i = ) { if ($i =~ /^p$/) { fdisk_list($device,undef,\%start,\%end, 1); print "command (m for help) send back to fake fdisk for mondorestore\n"; } elsif ($i =~ /^n$/) { fdisk_list($device,undef,\%start,\%end, 0); if ($type ne "gpt") { print FLOG "Forcing GPT type of disk label\n"; print FLOG "mklabel gpt\n"; system "$parted -s $device mklabel gpt\n" if ($fake == 0); $type = "gpt"; } $l = ; if (not (defined $l)) { print FLOG "no primary/extended arg given for creation... assuming primary\n"; $l = "p"; } chomp($l); $part = ; if ((not (defined $part)) || ($part eq "")) { print FLOG "no partition given for creation... skipping\n"; next; } chomp($part); $cylstart = ; chomp($cylstart); if ((not (defined $cylstart)) || ($cylstart eq "")) { if (defined $start{$part-1}) { # in MB => cyl $cylstart = sprintf("%d",$end{$part-1}*$mega/$un + 1); print FLOG "no start cyl given for creation... assuming the following $cylstart\n"; } else { print FLOG "no start cyl given for creation... assuming the following 1\n"; $cylstart = 1; } } $cylstart = 1 if ($cylstart < 1); print FLOG "start cyl : $cylstart\n"; $un = get_un($device, "", 0); # parted needs MB if ($cylstart == 1) { $start = 0.01; } else { $start = $cylstart* $un / $mega + 0.001; } # this is a size in B/KB/MB/GB $endmax = get_max($device); $cylend = ; chomp($cylend); if ((not (defined $cylend)) || ($cylend eq "")) { print FLOG "no end cyl given for creation... assuming full disk)\n"; $cylend = $endmax; } # Handles end syntaxes (+, K, M, ...) # to give cylinders if ($cylend =~ /^\+/) { $cylend =~ s/^\+//; # Handles suffixes; return bytes $cylend = decode_Bsuf($cylend,1); # This gives the number of cyl $cylend /= $un; $cylend = sprintf("%d",$cylend); $cylend += $cylstart - 0.001; # We now have the end cyl } $cylend = $endmax if ($cylend > $endmax); print FLOG "end cyl : $cylend\n"; # parted needs MB $end = $cylend * $un / $mega; print FLOG "n $l $part $cylstart $cylend => mkpart primary $start $end\n"; system "$parted -s $device mkpart primary ext2 $start $end\n" if ($fake == 0); print "command (m for help) send back to fake fdisk for mondorestore\n"; } elsif ($i =~ /^d$/) { $part = ; if (not (defined $part)) { print FLOG "no partition given for deletion... skipping\n"; next; } chomp($part); print FLOG "d $part => rm $part\n"; system "$parted -s $device rm $part\n" if ($fake == 0); get_parted($device,undef,\%start,\%end,undef); print "command (m for help) send back to fake fdisk for mondorestore\n"; } elsif ($i =~ /^w$/) { print FLOG "w => quit\n"; } elsif ($i =~ /^t$/) { $part = ; if (not (defined $part)) { print FLOG "no partition given for tagging... skipping\n"; next; } chomp($part); # If no partition number given it's 1, and we received the type if ($part !~ /\d+/) { $l = $part; $part = 1 } else { $l = ; } if (not (defined $l)) { print FLOG "no type given for tagging partition $part... skipping\n"; next; } chomp($l); if (not (defined $pnum{$l})) { print FLOG "no partition number given for $l... please report to the author\n"; next; } if ($pnum{$l} eq "lvm") { # In that case this is a flag set, not a mkfs print FLOG "t $part $l => set $part $pnum{$l} on\n"; system "$parted -s $device set $part $pnum{$l} on\n" if ($fake == 0); } else { print FLOG "t $part $l => mkfs $part $pnum{$l}\n"; system "$parted -s $device mkfs $part $pnum{$l}\n" if ($fake == 0); } print "command (m for help) send back to fake fdisk for mondorestore\n"; } elsif ($i =~ /^a$/) { $part = ; if (not (defined $part)) { print FLOG "no partition given for tagging... skipping\n"; next; } chomp($part); # Partition shouldn't be negative or null. Then take the first one. $part = 1 if ($part le 0); print FLOG "a $part => set $part boot on\n"; system "$parted -s $device set $part boot on\n" if ($fake == 0); print "command (m for help) send back to fake fdisk for mondorestore\n"; } elsif ($i =~ /^q$/) { print FLOG "q => quit\n"; } else { print FLOG "Unknown command: $i\n"; print "command (m for help) send back to fake fdisk for mondorestore\n"; next; } } } myexit(0); } } # # Else everything is for fdisk # # Print only mode print FLOG "Passing everything to the real fdisk\n"; my $fargs = join(@ARGV); if ($args =~ /^-/) { # -l or -s open (FDISK, "$fdisk $fargs 2>/dev/null |") || die "Unable to read from $fdisk"; while () { print; } close(FDISK); } else { # Modification mode open (FDISK, "| $fdisk $fargs 2>/dev/null") || die "Unable to modify through $fdisk"; while () { print FDISK; } close(FDISK); close(STDIN); } myexit(0); # Is your system LSB ? sub is_lsb { my $cmd = shift; my $basename = basename($cmd); if (not (-x $cmd)) { print FLOG "Your system is not LSB/mondo compliant: $basename was not found as $cmd\n"; print FLOG "Searching elswhere..."; foreach $i (split(':',$ENV{PATH})) { if (-x "$i/$basename") { $cmd = "$i/$basename"; print FLOG "Found $cmd, using it !\n"; last; } } if (not (-x $cmd)) { print FLOG "Your system doesn't provide $basename in the PATH\n"; print FLOG "Please correct it before relaunching\n"; myexit(-1); } } return($cmd); } sub fdisk_list { my $device = shift; my $wpart = shift; my $start = shift; my $end = shift; my $verbose = shift; my $un; my $endmax; my $d; my $n; my %cmt = ( "FAT" => "FAT", "ext2" => "Linux", "ext3" => "Linux", "ext4" => "Linux", "xfs" => "Linux", "reiserfs" => "Linux", "linux-swap" => "Linux swap", "lvm" => "Linux LVM", "raid" => "RAID Linux auto", "fat16" => "fat16", "fat32" => "fat32", "" => "Linux", ); my $part; my $mstart; my $mend; my $length; my $pid; my $cmt; format FLOG1 = @<<<<<<<<<<<< @>>>>>>>>>> @>>>>>>>>>> @>>>>>>>>>> @>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<< $part, $mstart, $mend, $length, $pid, $cmt . format FLOG2 = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $part, @>>>>>>>>>> @>>>>>>>>>> @>>>>>>>>>> @>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<< $mstart, $mend, $length, $pid, $cmt . format STDOUT1 = @<<<<<<<<<<<< @>>>>>>>>>> @>>>>>>>>>> @>>>>>>>>>> @>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<< $part, $mstart, $mend, $length, $pid, $cmt . format STDOUT2 = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $part, @>>>>>>>>>> @>>>>>>>>>> @>>>>>>>>>> @>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<< $mstart, $mend, $length, $pid, $cmt . # Device Boot Start End Blocks Id System #/dev/hda1 1 77579 39099374+ ee EFI GPT # # Keep Fdisk headers # # this will return bytes $un = get_un ($device,$wpart,$verbose); $endmax = get_max($device); # This will return MB get_parted($device,$start,$end,\%type); while (($n,$d) = each %type) { # Print infos fdisk like $part = ${device}.$n; # start and end are in cylinder in fdisk format # so return in MB * 1MB / what represents 1 cyl in B $mstart = sprintf("%d",$$start{$n}*$mega/$un); $mstart = 1 if ($mstart < 1); $mstart = $endmax if ($mstart > $endmax); $mend = sprintf("%d",$$end{$n}*$mega/$un - 1); $mend = $endmax if ($mend > $endmax); $mend = 1 if ($mend < 1); # length is in 1K blocks $length = sprintf("%d",($mend-$mstart+1)*$un/1024); $pid = $pid{$type{$n}}; $cmt = $cmt{$type{$n}}; #print FLOG "$part - $mstart - $mend - $length\n"; if ($verbose == 1) { if (not (defined $wpart)) { if (length($part) > 13) { open(STDOUT2,">&STDOUT") || die "Unable to open STDOUT2"; select(STDOUT2); write; open(FLOG2,">&FLOG") || die "Unable to open FLOG2"; select(FLOG2); write; select(STDOUT); close(FLOG2); close(STDOUT2); } else { open(STDOUT1,">&STDOUT") || die "Unable to open STDOUT1"; select(STDOUT1); write; open(FLOG1,">&FLOG") || die "Unable to open FLOG1"; select(FLOG1); write; select(STDOUT); close(FLOG1); close(STDOUT1); } } else { # manage the -s option of fdisk here print "$length\n" if ($part eq $wpart); print FLOG "$part has $length KBytes\n" if ($part eq $wpart); } } } close(FDISK); close(PARTED); } # # Get max size from fdisk # sub get_max { my $device = shift; my $max = 0; my $foo; open (FDISK, "$fdisk -l $device 2>/dev/null |") || die "Unable to read from $fdisk"; while () { if ($_ =~ /heads/) { chomp; $max = $_; $max =~ s/.* ([0-9]+) cylinders/$1/; } } close(FDISK); print FLOG "get_max returns $max\n"; return($max); } # # Get units from fdisk (cylinder size) # sub get_un { my $device = shift; my $wpart = shift; my $verbose = shift; my $un = 0; my $foo; open (FDISK, "$fdisk -l $device 2>/dev/null |") || die "Unable to read from $fdisk"; while () { print if (($_ !~ /^\/dev\//) and (not (defined $wpart)) and ($verbose == 1)); if ($_ =~ /^Units/) { ($foo, $un , $foo) = split /=/; $un =~ s/[A-z\s=]//g; $un = eval($un); } } close(FDISK); print FLOG "get_un returns $un\n"; return($un); } # # Parted gives info in MB # (depending on versions - 1.6.25.1 provides suffixes) # sub get_parted { my $device = shift; my $start = shift; my $end = shift; my $type = shift; my $void; my $d; my $n; my $ret; my $mode; my $size; my $unit; open (PARTED, "$parted -v |") || die "Unable to read from $parted"; $d = ; print FLOG "$d"; close(PARTED); open (PARTED, "$parted -s $device print |") || die "Unable to read from $parted"; # Skip 3 first lines $d = ; $d = ; $d = ; # depending on parted version, information given change: if ($d =~ /\bSize\b/) { # SLES 10 parted >= 1.6.25 $mode=1; } else { # RHEL 3 parted 1.6.3 # RHEL 4 parted 1.6.19 $mode=0; } print FLOG "mode: $mode\n"; print FLOG "Got from parted: \n"; print FLOG "Minor Start End Filesystem\n"; # Get info from each partition line while (($n,$d) = split(/\s/, ,2)) { chomp($d); next if ($n !~ /^[1-9]/); $d =~ s/^\s*//; $d =~ s/\s+/ /g; if ($mode == 0) { ($$start{$n},$$end{$n},$$type{$n},$void) = split(/ /,$d); $unit = 1; } elsif ($mode == 1) { ($$start{$n},$$end{$n},$size,$$type{$n},$void) = split(/ /,$d); $unit = $mega; } else { die "Undefined mode $mode"; } $$start{$n} = "" if (not defined $$start{$n}); $$end{$n} = "" if (not defined $$end{$n}); $$type{$n} = "" if (not defined $$type{$n}); # Handles potential suffixes in latest parted version. Return MB $ret = decode_Bsuf($$start{$n},$unit); $$start{$n} = $ret; $ret = decode_Bsuf($$end{$n},$unit); $$end{$n} = $ret; print FLOG "$n $$start{$n} $$end{$n} $$type{$n}\n"; } close(PARTED); } sub decode_Bsuf { my $size = shift; my $unit = shift; my $ret = 0; #print FLOG "decode_Bsuf input: $size / $unit "; if ($size =~ /K[B]*$/i) { $size =~ s/K[B]*$//i; $size *= 1024; } elsif ($size =~ /M[B]*$/i) { $size =~ s/M[B]*$//i; $size *= 1048576; } elsif ($size =~ /G[B]*$/i) { $size =~ s/G[B]*$//i; $size *= 1073741824; } elsif ($size =~ /T[B]*$/i) { $size =~ s/T[B]*$//i; $size *= 1099511627776; } else { # Nothing to do } $ret = $size / $unit; #print FLOG " - output : $size => $ret\n"; return($ret); } sub myexit { my $val=shift; close(FLOG); exit($val); } sub which_type { my $device = shift; my $type = ""; open (FDISK, "$fdisk -l $device 2>/dev/null |") || die "Unable to read from $fdisk"; while () { if ($_ =~ /EFI GPT/) { $type= "gpt"; print FLOG "Found a GPT partition format\n"; last; } } close(FDISK); open (PARTED, "$parted -s $device print|") || die "Unable to read from $fdisk"; while () { if ($_ =~ /Disk label type: msdos/) { $type= "msdos"; print FLOG "Found a msdos partition format\n"; last; } } close(FDISK); return ($type); } sub mysyn { print "Syntax: $0 [-l] device | [-s] partition\n"; myexit(-1); }