GPT fdisk 0.5.0

Added several features, including a restructuring of the menu system,
GPT-to-MBR conversion, and the ability to re-read the MBR to generate
a fresh GPT from the current on-disk MBR.
This commit is contained in:
srs5694
2009-09-21 20:51:47 -04:00
parent e35eb1beb6
commit 978041ca61
13 changed files with 1167 additions and 966 deletions

View File

@@ -1,3 +1,45 @@
0.5.0:
------
- Added GPT-to-MBR conversion function. It's very limited, but potentially
useful in some cases.
- Fixed bug that caused incorrect file sizes to be reported on 32-bit
Linux, thus causing problems when editing partition tables in disk images
or when loading GPT backup files.
- Fixed bug that caused bogus CRC error reports when loading backup GPT
data.
- Reorganized menus. There are now three: the main menu, the experts' menu,
and the recovery & transformation menu. The last of these has most of the
items that had been on the earlier versions' experts' menu.
- Added ability to re-load the MBR and generate a fresh GPT from it. This
is normally identical to quitting and re-running the program, but it
could be handy if, say, the GPT partitions on a hybrid configuration are
badly messed up; this will enable using the hybridized partitions as the
starting point for a new GPT setup.
- The program now generates CHS values for hybrid and GPT-to-MBR conversion
MBRs. For the moment, the assumption is the maximum number of heads and
sectors per track (255 and 63, respectively), although the bulk of the
code supports other values -- it'd just be awkward to enter the data in
the user interface.
- Fixed minor display bug that caused number of sectors on the disk to be
shown as 0 on large disks when running 32-bit binaries.
- Reverted 0.4.2's zap (destroy GPT) changes, since I don't want to wipe
out a valid MBR if the user created that MBR over an older GPT without
first properly wiping out the GPT, and the user now wants to wipe out
the GPT.
- Reformatted and edited the man page. Aside from edits related to the
preceding program changes, I've altered the markup slightly and trimmed
much of the more tutorial information from the man page to better
conform to typical terse man page style.
0.4.2:
------

666
gdisk.8
View File

@@ -1,242 +1,108 @@
.\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com)
.\" May be distributed under the GNU General Public License
.TH GDISK 8 "August 2009" "0.4.2" "GPT fdisk Manual"
.SH NAME
.TH "GDISK" "8" "0.5.0" "Roderick W. Smith" "GPT fdisk Manual"
.SH "NAME"
gdisk \- GPT partition table manipulator for Linux and Unix
.SH SYNOPSIS
.SH "SYNOPSIS"
.BI "gdisk "
[ \-l ]
.I device
.SH DESCRIPTION
.SH "DESCRIPTION"
Hard disks can be divided into one or more segments, known as
.IR partitions .
This division is described in the
.I "partition table"
of the disk. Several different partition table formats exist, each with its
advantages and disadvantages.
GPT fdisk (aka \fBgdisk\fR) is a text\-mode menu\-driven program for
creation and manipulation of partition tables. It will automatically
convert an old\-style Master Boot Record (MBR) partition table or BSD
disklabel stored without an MBR carrier partition to the newer Globally
Unique Identifier (GUID) Partition Table (GPT) format, or will load a GPT
partition table. When used with the \fI\-l\fR command\-line option, the
program displays the current partition table and then exits.
The original partitioning system used on PCs, now known as the
.IR "MBR partitioning scheme",
is subject to several limitations. These include an awkward distinction
between
.IR "primary",
.IR "extended",
and
.IR "logical"
partitions; no redundancy or error correction capabilities; and 32-bit data
structures that, in conjunction with the common 512-byte sector size,
impose a hard 2 TiB limit on the size of partitions and disks. This final
drawback makes MBR partitions unsuitable for use on large hardware RAID
arrays. Individual disk sizes are expected to surpass the 2 TiB limit in
2009, so MBR will become an unsuitable partitioning system even for
individual hard disks in the near future.
The successor to MBR partitions is the
.IR "Globally Unique Identifier (GUID) Partition Table (GPT)"
system. GPT addresses each of the major limitations of MBR partitions, and
includes a dummy MBR partition table with a single
.IR "protective MBR"
entry to keep GPT-unaware programs from modifying the disk's GPT partitions. GPT
is a new partitioning scheme, though, and as such, older utilities and OSes
must be replaced or modified to handle GPT. Linux's venerable
.B "fdisk"
program, in particular, cannot process GPT disks. (The same is true of
related programs, such as
.B "sfdisk"
and
.BR "cfdisk".)
The alternative GNU
Parted and related programs, however, are capable of working on both MBR
and GPT disks.
GPT is often associated with the
.IR "Extensible Firmware Interface (EFI)",
which is Intel's intended successor to the traditional (legacy) PC BIOS. It
is possible to use and even boot from GPT disks on non-EFI systems,
including those that use a legacy BIOS. Using GPT disks on such a system
isn't a great challenge, although the OS must support GPT. Booting from a
GPT-based disk requires that the OS support this action, and if the system
is BIOS-based, a GPT-aware boot loader is required. Patched versions of the
.IR "Grand Unified Bootloader (GRUB)"
0.97, as well as GRUB2, support GPT.
GPT creates five distinct data structures of three types:
.TP
.B "Protective MBR"
The first sector (512 bytes) of the disk is devoted to an MBR that
consists of a single partition spanning the entire disk (or 2 TiB for disks
larger than this). The protective MBR may optionally include first-stage
boot loader code.
.TP
.B "GPT headers"
Two GPT headers exist, a main header and a backup header. The primary
header resides immediately after the protective MBR, and the backup header
is stored on the last sector of the disk. These headers contain disk
metadata, such as the location of the partition table, the size of the
partition table, and a "serial number" (GUID) that should be unique for
each disk. Each GPT header also stores two CRC checksums, one for the
partition table and one for the GPT header itself.
.TP
.B "Partition tables"
Each GPT header points to one partition table. The main partition table
appears immediately after the main GPT header, and the backup partition
table comes immediately before its GPT header. Typically, the partition
tables may hold data on up to 128 partitions, although
.B gdisk
enables you to change this value. Each entry contains 64-bit start and stop
sector numbers, a name, a partition GUID type code, a unique partition GUID
identifier, and additional data.
.PP
The GPT fdisk (aka
.BR "gdisk")
program operates mainly on the GPT headers and partition tables; however,
GPT fdisk operates mainly on the GPT headers and partition tables; however,
it can and will generate a fresh protective MBR, when required. (Any boot
loader code in the protective MBR will not be disturbed.) If you've created
an unusual protective MBR, such as a hybrid MBR created by
.IR "gptsync"
or
.BR "gdisk"'s
own hybrid MBR creation feature,
\fBgptsync\fR or \fBgdisk\fR's own hybrid MBR creation feature,
this should not be disturbed by most ordinary actions. Some advanced data
recovery options require you to understand the distinctions between the
main and backup data, as well as between the GPT headers and the partition
tables.
tables. For information on MBR vs. GPT, as well as GPT terminology and
structure, see the extended \fBgdisk\fR documentation at
\fIhttp://www.rodsbooks.com/gdisk/\fR or consult Wikipedia.
The
.B "gdisk"
program employs a user interface similar to that of Linux's
.BR "fdisk",
but
.B "gdisk"
The \fBgdisk\fR program employs a user interface similar to that of Linux's
\fBfdisk\fR, but \fBgdisk\fR
modifies GPT partitions. It also has the capability of transforming MBR
partitions or BSD disklabels into GPT partitions. Like the original
.B fdisk
program,
.B gdisk
\fBfdisk\fR program, \fBgdisk\fR
does not modify disk structures until you explicitly write them to disk, so
if you make a mistake, you can exit from the program with the 'q' option to
save your partitions.
.B gdisk
is a text-mode menu-driven program for creation and manipulation of
partition tables. It will automatically convert an MBR partition table or
BSD disklabel stored without an MBR carrier partition to GPT format, or
will load a GPT partition table. When used with the
.IR "\-l"
command-line option, the program displays the current partition table and
then exits.
Linux hard disk device filenames take the form
.IR "/dev/sdx"
or
.IR "/dev/hdx",
where
.IR "x"
is a letter from
.IR "a"
onward. The
.IR "hdx"
devices originally referred to IDE (aka PATA) drives, whereas
.IR "sdx"
devices originally referred to SCSI drives. These distinctions are now
blurring. Modern SATA drives and USB flash drives usually acquire
.IR "sdx"
names, and the same can even be true of PATA drives, depending on kernel
driver options. For instance,
.IR "/dev/hda"
refers to the first PATA drive, whereas
.IR "/dev/sdb"
is the second SCSI, SATA, USB, or other SCSI-equivalent drive. To use
.BR "gdisk",
you must pass a device filename to the program on the command line.
The
.I partition
is a
device name followed by a partition number. For example,
.B /dev/hda1
is the first partition on the first PATA hard disk.
.B gdisk
creates partitions, but you don't pass partition numbers or partition
device filenames to the program. Linux generates numbers for GPT partitions
based on the partition's position in the partition table.
Ordinarily, \fBgdisk\fR operates on disk device files, such as
\fI/dev/sda\fR or \fI/dev/hda\fR under Linux, \fI/dev/disk0\fR under
Mac OS X, or \fI/dev/ad0\fR or \fI/dev/da0\fR under FreeBSD. The program
can also operate on disk image files, which can be either copies of whole
disks (made with \fBdd\fR, for instance) or raw disk images used by
emulators such as QEMU or VMWare. Note that only \fIraw\fR disk images
are supported; \fBgdisk\fR cannot work on compressed or other advanced
disk image formats.
The MBR partitioning system uses a combination of cylinder/head/sector
(CHS) addressing and logical block addressing (LBA). The former is klunky
and limiting. GPT drops CHS addressing and uses 64-bit LBA mode
and limiting. GPT drops CHS addressing and uses 64\-bit LBA mode
exclusively. Thus, GPT data structures, and therefore
.BR "gdisk",
do not need to deal with CHS geometries and all the problems they create.
Users of
.BR "fdisk"
will note that
.B "gdisk"
\fBgdisk\fR, do not need to deal with CHS geometries and all the problems
they create. Users of \fBfdisk\fR will note that \fBgdisk\fR
lacks the options and limitations associated with CHS geometries.
For best results, you should always use an OS-specific partition table
program. For example, you should make Mac OS X partitions with the Mac OS
X Disk Utility
program and Linux partitions with the Linux
.B "gdisk"
or GNU Parted program.
For best results, you should use an OS\-specific partition table
program whenever possible. For example, you should make Mac OS X
partitions with the Mac OS X Disk Utility program and Linux partitions
with the Linux \fBgdisk\fR or GNU Parted program.
Upon start,
.B gdisk
attempts to identify the partition type in use on the specified disk. If it
finds valid GPT data,
.B gdisk
will use it. If
.B gdisk
Upon start, \fBgdisk\fR attempts to identify the partition type in use
on the disk. If it finds valid GPT data, \fBgdisk\fR
will use it. If \fBgdisk\fR
finds a valid MBR or BSD disklabel but no GPT data, it will attempt to
convert the MBR or disklabel into GPT form. (BSD disklabels are likely to
have unusable first and/or final partitions because they overlap with the
GPT data structures, though.) GPT fdisk can identify, but not use data in,
Apple Partition Map (APM) disks, which are used on 680x0- and PowerPC-based
Macintoshes. Upon exiting with the 'w' option,
.B gdisk
will then replace the MBR or disklabel with a GPT.
.IR "This action is potentially dangerous!"
Apple Partition Map (APM) disks, which are used on 680x0\- and PowerPC\-based
Macintoshes. Upon exiting with the 'w' option, \fBgdisk\fR replaces
the MBR or disklabel with a GPT. \fIThis action is potentially dangerous!\fR
Your system may become unbootable, and partition type codes may become
corrupted if the disk uses unrecognized type codes. Boot problems are
particularly likely if you're multi-booting with any GPT-unaware OS. If you
mistakenly launch
.B gdisk
on an MBR disk, you can safely exit the program
without making any changes by using the 'q' option.
particularly likely if you're multi\-booting with any GPT\-unaware OS. If you
mistakenly launch \fBgdisk\fR on an MBR disk, you can safely exit
the program without making any changes by using the 'q' option.
The MBR-to-GPT conversion will leave at least one gap in the partition
The MBR\-to\-GPT conversion will leave at least one gap in the partition
numbering if the original MBR used logical partitions. These gaps are
harmless, but you can eliminate them by using the 's' option, if you like.
(Doing this may require you to update your
.IR "/etc/fstab"
file.)
(Doing this may require you to update your \fI/etc/fstab\fR file.)
When creating a fresh partition table, certain considerations may be in
order:
.TP
.B *
For data (non-boot) disks, and for boot disks used on BIOS-based computers
For data (non\-boot) disks, and for boot disks used on BIOS\-based computers
with GRUB as the boot loader, partitions may be created in whatever order
and in whatever sizes are desired.
.TP
.B *
Boot disks for EFI-based systems require an "EFI System
Partition" (
.B "gdisk"
internal code 0xEF00) formatted as FAT-32. The recommended size of this
partition is 100 MiB. Boot-related files are stored here. (Note that GNU
Parted identifies such partitions as having the "boot flag" set.)
Boot disks for EFI\-based systems require an \fIEFI System
Partition\fR (\fBgdisk\fR internal code 0xEF00) formatted as FAT\-32.
The recommended size of this partition between 100 and 200 MiB.
Boot\-related files are stored here. (Note that GNU Parted identifies
such partitions as having the "boot flag" set.)
.TP
.B *
Some boot loaders for BIOS-based systems make use of a "BIOS Boot
Partition" (
.B "gdisk"
Some boot loaders for BIOS\-based systems make use of a \fIBIOS Boot
Partition\fR (\fBgdisk\fR
internal code 0xEF02), in which the secondary boot loader is stored,
possibly without the benefit of a filesystem. This partition can
typically be quite small (a few tens of kilobytes), but you should
@@ -244,9 +110,8 @@ consult your boot loader documentation for details.
.TP
.B *
If Windows is to boot from a GPT disk, a partition of type "Microsoft
Reserved" (
.B "gdisk"
If Windows is to boot from a GPT disk, a partition of type \fIMicrosoft
Reserved\fR (\fBgdisk\fR
internal code 0x0C01) is recommended. This partition should be about 128 MiB
in size. It ordinarily follows the EFI System Partition and immediately
precedes the Windows data partitions. (Note that GNU Parted creates all
@@ -262,34 +127,31 @@ help in future disk maintenance. You can use GPT fdisk's relative partition
positioning option (specifying the starting sector as '+128M', for
instance) to simplify creating such gaps.
.SH OPTIONS
.SH "OPTIONS"
.TP
.B \-l
List the partition tables for the specified devices and then exit.
.PP
Most interactions with
.B gdisk
occur with its interactive text-mode menus. The main menu provides the
following options:
Most interactions with \fBgdisk\fR
occur with its interactive text\-mode menus. Three menus exist: the main
menu, the recovery & transformation menu, and the experts' menu. The main
menu provides the functions that are most likely to be useful for typical partitioning tasks, such as creating and deleting partitions, changing partition type codes, and so on. Specific functions are:
.TP
.B b
Convert BSD partitions into GPT partitions. This option works on BSD
disklabels held within GPT (or converted MBR) partitions. Converted
partitions' type codes are likely to need manual adjustment.
.B gdisk
will attempt to convert BSD disklabels stored on the main disk when
launched, but this conversion is likely to produce first and/or last
partitions that are unusable. The many BSD variants means that the
probability of GPT fdisk being unable to convert a BSD disklabel are high
compared to the likelihood of problems with an MBR conversion.
Save partition data to a backup file. You can back up your partition table
to a disk file using this option. The resulting file is a binary file
consisting of the protective MBR, the main GPT header, the backup GPT
header, and one copy of the partition table, in that order. Note that the
restore option is on the recovery & transformation menu; the backup
option is on the main menu to encourage its use.
.TP
.B c
Change the GPT name of a partition. This name is encoded as a UTF-16
string, but
.B gdisk
Change the GPT name of a partition. This name is encoded as a UTF\-16
string, but \fBgdisk\fR
supports only ASCII characters as names. For the most part, Linux ignores
the partition name, but it may be important in some OSes. GPT fdisk sets
a default name based on the partition type code.
@@ -299,16 +161,14 @@ a default name based on the partition type code.
Delete a partition. This action deletes the entry from the partition table
but does not disturb the data within the sectors originally allocated to
the partition on the disk. If a corresponding hybrid MBR partition exists,
.B gdisk
deletes it, as well, and expands any adjacent 0xEE (EFI GPT) MBR protective
partition to fill the new free space.
\fBgdisk\fR deletes it, as well, and expands any adjacent 0xEE (EFI GPT)
MBR protective partition to fill the new free space.
.TP
.B i
Show detailed partition information. The summary information produced by
the 'p' command necessarily omits many details, such as the partition's
unique GUID and the translation of
.BR "gdisk"'s
unique GUID and the translation of \fBgdisk\fR's
internal partition type code to a plain type name. The 'i' option
displays this information for a single partition.
@@ -316,45 +176,30 @@ displays this information for a single partition.
.B l
Display a summary of partition types. GPT uses a GUID to identify
partition types for particular OSes and purposes. For ease of data entry,
.B gdisk
compresses these into two-byte (four-digit hexadecimal) values that are
related to their MBR codes. Specifically, the MBR code is multiplied by
hexadecimal 0x0100. For instance, the code for Linux swap space in MBR is
0x82, and it's 0x8200 in
.BR "gdisk".
A one-to-one correspondence is impossible, though. Most notably, many DOS,
\fBgdisk\fR compresses these into two\-byte (four\-digit hexadecimal)
values that are related to their equivalent MBR codes. Specifically, the
MBR code is multiplied by hexadecimal 0x0100. For instance, the code for
Linux swap space in MBR is 0x82, and it's 0x8200 in \fBgdisk\fR.
A one\-to\-one correspondence is impossible, though. Most notably, many DOS,
Windows, and Linux data partition codes correspond to a single GPT code
(entered as 0x0700 in
.BR "gdisk" ).
Some OSes use a single MBR code but employ many more codes in GPT. For
these,
.B gdisk
(entered as 0x0700 in \fBgdisk\fR). Some OSes use a single MBR code but
employ many more codes in GPT. For these, \fBgdisk\fR
adds code numbers sequentially, such as 0xa500 for a FreeBSD disklabel,
0xa501 for FreeBSD boot, 0xa502 for FreeBSD swap, and so on. Note that
these two-byte codes are unique to
.BR "gdisk".
.TP
.B m
Print the menu. Type this command (or any other unrecognized command) to
see a summary of available options.
these two\-byte codes are unique to \fBgdisk\fR.
.TP
.B n
Create a new partition. This command is modelled after the equivalent
.B fdisk
option, although some differences exist. You enter a partition number,
starting sector, and an ending sector. Both start and end sectors can be
specified in absolute terms as sector numbers or as positions measured in
kilobytes (K), megabytes (M), gigabytes (G), or terabytes (T); for
instance,
.BI 40M
\fBfdisk\fR option, although some differences exist. You enter a partition
number, starting sector, and an ending sector. Both start and end sectors
can be specified in absolute terms as sector numbers or as positions measured
in kilobytes (K), megabytes (M), gigabytes (G), or terabytes (T); for
instance, \fI\fB40M\fR\fR
specifies a position 40MiB from the start of the disk. You can specify
locations relative to the start or end of the specified range by preceding
the number by a '+' or '-' symbol, as in
.BI +2G
to specify a point 2GiB after the first available sector, or
.BI -200M
the number by a '+' or '\-' symbol, as in \fI\fB+2G\fR\fR
to specify a point 2GiB after the first available sector, or \fI\fB\-200M\fR\fR
to specify a point 200MiB before the last available sector. Pressing the
Enter key with no input specifies the default value, which is the start of
the largest available block for the start sector and the last available
@@ -369,42 +214,44 @@ all partition definitions, and the protective MBR.
.B p
Display basic partition summary data. This includes partition
numbers, starting and ending sector numbers, partition sizes,
.BR "gdisk"'s
partition types codes, and partition names. For additional information,
use the 'i' command.
\fBgdisk\fR's partition types codes, and partition names. For
additional information, use the 'i' command.
.TP
.B q
Quit from the program
.IR "without saving data".
Quit from the program \fIwithout saving your changes\fR.
Use this option if you just wanted to view information or if you make a
mistake and want to back out of all your changes.
.TP
.B r
Enter the recovery & transformation menu. This menu includes emergency
recovery options (to fix damaged GPT data structures) and options to
transform to or from other partitioning systems, including creating
hybrid MBRs.
.TP
.B s
Sort partition entries. GPT partition numbers need not match the order of
partitions on the disk. If you want them to match, you can use this option.
Note that some partitioning utilities, such as GNU Parted, will sort
Note that some partitioning utilities sort
partitions whenever they make changes. Such changes will be reflected in
your device filenames, so you may need to edit
.IR "/etc/fstab"
if you use this option.
\fI/etc/fstab\fR if you use this option.
.TP
.B t
Change a single partition's type code. You enter the type code using a
two-byte hexadecimal number, as described earlier. You may also enter a
GUID directly, if you have one and
.B gdisk
doesn't know it.
two\-byte hexadecimal number, as described earlier. You may also enter a
GUID directly, if you have one and \fBgdisk\fR doesn't know it.
.TP
.B v
Verify disk. This option checks for a variety of problems, such as
incorrect CRCs and mismatched main and backup data. This option does not
automatically correct these problems, though; for that, you must use
options on the experts' menu. If no problems are found, this command
displays a summary of unallocated disk space.
automatically correct most problems, though; for that, you must use
options on the recovery & transformation menu. If no problems are found,
this command displays a summary of unallocated disk space.
.TP
.B w
@@ -416,33 +263,29 @@ Enter the experts' menu. Using this option provides access to features you
can use to get into even more trouble than the main menu allows.
.PP
A few options on the experts' menu duplicate functionality on the main
menu, for the sake of convenience; however, for the most part the experts'
menu provides unusually dangerous or obscure options. These are:
.TP
.B ?
Print the menu. Type this command (or any other unrecognized command) to
see a summary of available options.
.PP
The second \fBgdisk\fR menu is the recovery & transformation menu, which
provides access to data recovery options and features related to the
transformation of partitions between partitioning schemes (converting
BSD disklabels into GPT partitions or creating hybrid MBRs, for instance).
A few options on this menu duplicate functionality on the main
menu, for the sake of convenience. The options on this menu are:
.TP
.B a
Set attributes. GPT provides a 64-bit attributes field that can be used to
set partition features.
.B gdisk
supports four attributes:
.IR "system partition",
.IR "read-only",
.IR "hidden",
and
.IR "do not automount".
You can set other attributes, but their numbers aren't translated into
anything useful. In practice, most OSes seem to ignore these attributes.
.TP
.B b
Rebuild main GPT header from backup. You can use the backup GPT header to
Rebuild GPT header from backup. You can use the backup GPT header to
rebuild the main GPT header with this option. It's likely to be useful if
your main GPT header was damaged or destroyed (say, by sloppy use of
.IR "dd").
\fBdd\fR).
.TP
.B c
Load backup partition table. Ordinarily,
.B gdisk
Load backup partition table. Ordinarily, \fBgdisk\fR
uses only the main partition table (although the backup's integrity is
checked when you launch the program). If the main partition table has been
damaged, you can use this option to load the backup from disk and use it
@@ -454,79 +297,168 @@ there will be no backup partition table on disk.
.B d
Use main GPT header and rebuild the backup. This option is likely to be
useful if the backup GPT header has been damaged or destroyed.
.TP
.TP
.B e
Load main partition table. This option reloads the main partition table
from disk. It's only likely to be useful if you've tried to use the backup
partition table (via 'c') but it's in worse shape then the main partition
table.
.TP
.TP
.B f
Change partition GUID. You can enter a custom unique GUID for a partition
using this option. (Note this refers to the GUID that uniquely identifies a
partition, not to its type code.) Ordinarily,
.B gdisk
assigns this number randomly; however, you might want to adjust the number
manually if you've wound up with the same GUID on two partitions.
Load MBR and build fresh GPT from it. Use this option if your GPT is corrupt
or conflicts with the MBR and you want to use the MBR as the basis for a new
set of GPT partitions.
.TP
.B g
Change disk GUID. Each disk has a unique GUID code, which
.B gdisk
assigns randomly upon creation of the GPT data structures. You can generate
a fresh random GUID or enter one manually with this option.
Convert GPT into MBR and exit. This option converts up to four GPT partitions
into MBR form, destroys the GPT data structures, saves the new MBR, and exits.
Use this option if you've tried GPT and find that MBR works better for you.
Note that this function generates up to four \fIprimary\fR MBR partitions;
it cannot generate logical partitions, and so it cannot transform more than
four partitions. If four or fewer partitions exist, and if they can be represented
in the 32\-bit MBR LBA scheme, this function converts
them all. If more than four partitions exist, you'll be asked to select which
ones to convert. See also the 'h' option.
.TP
.B h
Create a hybrid MBR. This is an ugly workaround that enables GPT-unaware
OSes, or that that can't boot from a GPT disk, to access up to three of
Create a hybrid MBR. This is an ugly workaround that enables GPT\-unaware
OSes, or those that can't boot from a GPT disk, to access up to three of
the partitions on the disk by creating MBR entries for them. Note that
these hybrid MBR entries are not updated when you make subsequent changes
to the GPT entries, so you must re-run this option whenever you make
changes that would affect the hybridized partitions.
these hybrid MBR entries can easily go out of sync with the GPT entries,
particularly when hybrid\-unaware GPT utilities are used to edit the disk.
Thus, you may need to recreate the hybrid MBR if you use such tools.
.TP
.B i
Show detailed partition information. This option is identical to the 'i'
option on the main menu.
.TP
.B k
Save partition data to a backup file. You can back up your partition table
to a disk file using this option. The resulting file is a binary file
consisting of the protective MBR, the main GPT header, the backup GPT
header, and one copy of the partition table, in that order.
.TP
.B l
Load partition data from a backup file. This option is the reverse of the 'k'
option. Note that restoring partition data from anything but the
original disk is not recommended.
Load partition data from a backup file. This option is the reverse of the 'b'
option on the main menu. Note that restoring partition data from anything
but the original disk is not recommended.
.TP
.B m
Print the menu. This option (or any unrecognized entry) displays a summary
of the menu options.
.TP
.B n
Create a new protective MBR. Use this option if the current protective MBR
is damaged in a way that
.B gdisk
doesn't automatically detect and correct.
Return to the main menu. This option enables you to enter main\-menu commands.
.TP
.B o
Print protective MBR data. You can see a summary of the protective MBR's
partitions with this option. This may enable you to spot glaring problems
or help identify the partitions in a hybrid MBR.
.TP
.B p
Print the partition table. This option is identical to the 'p' option in
the main menu.
.TP
.B q
Quit without saving changes. This option is identical to the 'q' option in
the main menu.
.TP
.B t
Transform BSD partitions into GPT partitions. This option works on BSD
disklabels held within GPT (or converted MBR) partitions. Converted
partitions' type codes are likely to need manual adjustment. \fBgdisk\fR
will attempt to convert BSD disklabels stored on the main disk when
launched, but this conversion is likely to produce first and/or last
partitions that are unusable. The many BSD variants means that the
probability of \fBgdisk\fR being unable to convert a BSD disklabel is
high compared to the likelihood of problems with an MBR conversion.
.TP
.B v
Verify disk. This option is identical to the 'v' option in the main menu.
.TP
.B w
Write table to disk and exit. This option is identical to the 'w' option in
the main menu.
.TP
.B x
Enter the experts' menu. This option is identical to the 'x' option in the
main menu.
.TP
.B ?
Print the menu. This option (or any unrecognized entry) displays a summary
of the menu options.
.PP
The third \fBgdisk\fR menu is the experts' menu. This menu provides advanced
options that aren't closely related to recovery or transformation between
partitioning systems. Its options are:
.TP
.B a
Set attributes. GPT provides a 64\-bit attributes field that can be used to
set features for each partition. \fBgdisk\fR supports four attributes:
\fIsystem partition\fR, \fIread\-only\fR, \fIhidden\fR, and
\fIdo not automount\fR. You can set other attributes, but their numbers
aren't translated into anything useful. In practice, most OSes seem to
ignore these attributes.
.TP
.B c
Change partition GUID. You can enter a custom unique GUID for a partition
using this option. (Note this refers to the GUID that uniquely identifies a
partition, not to its type code, which you can change with the 't' main\-menu
option.) Ordinarily, \fBgdisk\fR assigns this number randomly; however,
you might want to adjust the number manually if you've wound up with the
same GUID on two partitions because of buggy GUID assignments (hopefully
not in \fBgdisk\fR) or sheer incredible coincidence.
.TP
.B g
Change disk GUID. Each disk has a unique GUID code, which \fBgdisk\fR
assigns randomly upon creation of the GPT data structures. You can generate
a fresh random GUID or enter one manually with this option.
.TP
.B i
Show detailed partition information. This option is identical to the 'i'
option on the main menu.
.TP
.B m
Return to the main menu. This option enables you to enter main\-menu commands.
.TP
.B n
Create a new protective MBR. Use this option if the current protective MBR
is damaged in a way that \fBgdisk\fR doesn't automatically detect and
correct, or if you want to convert a hybrid MBR into a "pure" GPT with a
conventional protective MBR.
.TP
.B o
Print protective MBR data. You can see a summary of the protective MBR's
partitions with this option. This may enable you to spot glaring problems
or help identify the partitions in a hybrid MBR.
.TP
.B p
Print the partition table. This option is identical to the 'p' option in
the main menu.
.TP
.B q
Quit without saving changes. This option is identical to the 'q' option in
the main menu.
.TP
.B r
Return to the main menu. You can go back to the main menu with this option.
Enter the recovery & transformations menu. This option is identical to
the 'r' option on the main menu.
.TP
.B s
@@ -540,51 +472,47 @@ partitions, though.
.TP
.B v
Verify disk. This option is identical to the 'v' option in the main menu.
.TP
.B w
Write table to disk and exit. This option is identical to the 'w' option in
the main menu.
.TP
.B z
Destroy the GPT data structures and exit. Use this option if you want to
repartition a GPT disk using
.B "fdisk"
or some other GPT-unaware program.
Zap (destroy) the GPT data structures and exit. Use this option if you want to
repartition a GPT disk using \fBfdisk\fR or some other GPT\-unaware program.
You'll be given the choice of preserving the existing MBR, in case it's a
hybrid MBR with salvageable partitions.
hybrid MBR with salvageable partitions or if you've already created new MBR
partitions and want to erase the remnants of your GPT partitions. \fIIf you've
already created new MBR partitions, it's conceivable that this option will
damage the first and/or last MBR partitions!\fR Such an event is unlikely, but
could occur if your new MBR partitions overlap the old GPT data structures.
.TP
.B ?
Print the menu. This option (or any unrecognized entry) displays a summary
of the menu options.
.PP
In many cases, you can press the Enter key to select a default option when
entering data. When only one option is possible,
.B gdisk
entering data. When only one option is possible, \fBgdisk\fR
usually bypasses the prompt entirely.
.SH BUGS
As of September 2009 (version 0.4.2),
.B gdisk
.SH "BUGS"
As of September 2009 (version 0.5.0), \fBgdisk\fR
should be considered beta software. Known bugs and limitations include:
.TP
.B *
The program compiles correctly only on Linux, FreeBSD, and Mac OS X. Both
64-bit (x86-64) and 32-bit (x86) versions for Linux have been tested, the
former more thoroughly than the latter. The Mac OS X support was added with
version 0.3.1 and has not been as thoroughly tested. FreeBSD support was
added with version 0.4.0 and has not been very thoroughly tested.
The program compiles correctly only on Linux, FreeBSD, and Mac OS X. Linux
versions for x86\-64 (64\-bit), x86 (32\-bit), and PowerPC (32\-bit) have been
tested, with the x86\-64 version having seen the most testing. The Mac OS X
support was added with version 0.3.1 and has not been as thoroughly tested.
FreeBSD support was added with version 0.4.0 and has seen even less
testing.
.TP
.B *
The FreeBSD version of the program can't write changes to the partition
table to a disk when existing partitions on that disk are mounted. (The
same problem exists with many other FreeBSD utilities, such as
.B "gpt"
,
.B "fdisk"
, and
.B "dd".
\fBgpt\fR, \fBfdisk\fR, and \fBdd\fR.)
.TP
.B *
@@ -596,21 +524,19 @@ alignment.
.TP
.B *
Only ASCII characters are supported in the partition name field. If an
existing partition uses non-ASCII UTF-16 characters, they're likely to be
corrupted in the 'i' menu option's display; however, they should be
existing partition uses non\-ASCII UTF\-16 characters, they're likely to be
corrupted in the 'i' and 'p' menu options' displays; however, they should be
preserved when loading and saving partitions.
.TP
.B *
The program can load only up to 124 logical partitions when converting from
MBR format. This limit can be raised by changing the #define NUM_LOGICALS
line in the
.IR "mbr.cc"
source code file and recompiling; however, such a change will require using
a larger-than-normal GPT partition table. (The limit of 124 logical
partitions was chosen because that number plus the four primary partitions
equals the 128 partitions supported by the most common GPT partition table
size.)
The program can load only up to 128 partitions (4 primary partitions and
124 logical partitions) when converting from MBR format. This limit can
be raised by changing the \fI#define MAX_MBR_PARTS\fR line in the
\fImbr.h\fR source code file and recompiling; however, such a change
will require using a larger\-than\-normal GPT partition table. (The limit
of 128 partitions was chosen because that number equals the 128 partitions
supported by the most common GPT partition table size.)
.TP
.B *
@@ -618,7 +544,8 @@ Converting from MBR format sometimes fails because of insufficient space at
the start or (more commonly) the end of the disk. Resizing the partition
table (using the 's' option in the experts' menu) can sometimes overcome
this problem; however, in extreme cases it may be necessary to resize a
partition using GNU Parted or a similar tool.
partition using GNU Parted or a similar tool prior to conversion with
\fBgdisk\fR.
.TP
.B *
@@ -636,7 +563,7 @@ partition(s) may need to be deleted.
.TP
.B *
Because of the highly variable nature of BSD disklabel structures,
conversions from this form may be unreliable -- partitions may be dropped,
conversions from this form may be unreliable \-\- partitions may be dropped,
converted in a way that creates overlaps with other partitions, or
converted with incorrect start or end values. Use this feature with
caution!
@@ -644,21 +571,20 @@ caution!
.TP
.B *
Booting after converting an MBR or BSD disklabel disk is likely to be
disrupted. Sometimes re-installing a boot loader will fix the problem, but
other times you may need to switch boot loaders. Except on EFI-based
disrupted. Sometimes re\-installing a boot loader will fix the problem, but
other times you may need to switch boot loaders. Except on EFI\-based
platforms, Windows through at least Windows 7 RC doesn't support booting
from GPT disks. Creating a hybrid MBR (using the 'h' option on the experts'
menu) or abandoning GPT in favor of MBR may be your only options in this
case.
from GPT disks. Creating a hybrid MBR (using the 'h' option on the recovery &
transformation menu) or abandoning GPT in favor of MBR may be your only
options in this case.
.PP
The support for big-endian CPUs (PowerPC, for example) is new, as of version
The support for big\-endian CPUs (PowerPC, for example) is new, as of version
0.3.5. I advise using caution on that platform, particularly with the more
obscure features of the program.
.SH AUTHORS
.SH "AUTHORS"
Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)
Contributors:
@@ -668,18 +594,18 @@ Contributors:
* David Hubbard (david.c.hubbard@gmail.com)
.SH "SEE ALSO"
.BR cfdisk (8),
.BR fdisk (8),
.BR mkfs (8),
.BR parted (8),
.BR sfdisk (8)
\fBcfdisk (8)\fR,
\fBfdisk (8)\fR,
\fBmkfs (8)\fR,
\fBparted (8)\fR,
\fBsfdisk (8)\fR
.IR "http://en.wikipedia.org/wiki/GUID_Partition_Table"
\fIhttp://en.wikipedia.org/wiki/GUID_Partition_Table\fR
.IR "http://developer.apple.com/technotes/tn2006/tn2166.html"
\fIhttp://developer.apple.com/technotes/tn2006/tn2166.html\fR
.IR "http://www.rodsbooks.com/gdisk/"
\fIhttp://www.rodsbooks.com/gdisk/\fR
.SH AVAILABILITY
The gdisk command is part of the GPT fdisk package and is available from
Rod Smith.
.SH "AVAILABILITY"
The \fBgdisk\fR command is part of the \fIGPT fdisk\fR package and is
available from Rod Smith.

226
gdisk.cc
View File

@@ -17,25 +17,27 @@
// Function prototypes....
// int ReadPartitions(char* filename, struct GPTData* theGPT);
int DoCommand(char* filename, struct GPTData* theGPT);
void MainMenu(char* filename, struct GPTData* theGPT);
void ShowCommands(void);
void ExpertsMenu(char* filename, struct GPTData* theGPT);
void ShowExpertCommands(void);
int ExpertsMenu(char* filename, struct GPTData* theGPT);
void RecoveryMenu(char* filename, struct GPTData* theGPT);
void ShowRecoveryCommands(void);
int main(int argc, char* argv[]) {
GPTData theGPT;
int doMore = 1;
char* device = NULL;
printf("GPT fdisk (gdisk) version 0.4.2\n\n");
printf("GPT fdisk (gdisk) version 0.5.0\n\n");
if (argc == 2) { // basic usage
if (SizesOK()) {
doMore = theGPT.LoadPartitions(argv[1]);
while (doMore) {
doMore = DoCommand(argv[1], &theGPT);
} // while
} // if
if (doMore) {
MainMenu(argv[1], &theGPT);
} // if (doMore)
} // if (SizesOK())
} else if (argc == 3) { // usage with "-l" option
if (SizesOK()) {
if (strcmp(argv[1], "-l") == 0) {
@@ -55,21 +57,24 @@ int main(int argc, char* argv[]) {
} // if/else
} // main
// Accept a command and execute it. Returns 0 if the command includes
// an exit condition (such as a q or w command), 1 if more commands
// should be processed.
int DoCommand(char* filename, struct GPTData* theGPT) {
char command, line[255];
int retval = 1;
// Accept a command and execute it. Returns only when the user
// wants to exit (such as after a 'w' or 'q' command).
void MainMenu(char* filename, struct GPTData* theGPT) {
char command, line[255], buFile[255];
int goOn = 1;
PartTypes typeHelper;
uint32_t temp1, temp2;
printf("\nCommand (m for help): ");
do {
printf("\nCommand (? for help): ");
fgets(line, 255, stdin);
sscanf(line, "%c", &command);
switch (command) {
case 'b': case 'B':
theGPT->XFormDisklabel();
printf("Enter backup filename to save: ");
fgets(line, 255, stdin);
sscanf(line, "%s", &buFile);
theGPT->SaveGPTBackup(buFile);
break;
case 'c': case 'C':
if (theGPT->GetPartRange(&temp1, &temp2) > 0)
@@ -101,7 +106,11 @@ int DoCommand(char* filename, struct GPTData* theGPT) {
theGPT->DisplayGPTData();
break;
case 'q': case 'Q':
retval = 0;
goOn = 0;
break;
case 'r': case 'R':
RecoveryMenu(filename, theGPT);
goOn = 0;
break;
case 's': case 'S':
theGPT->SortGPT();
@@ -118,58 +127,51 @@ int DoCommand(char* filename, struct GPTData* theGPT) {
break;
case 'w': case 'W':
if (theGPT->SaveGPTData() == 1)
retval = 0;
goOn = 0;
break;
case 'x': case 'X':
retval = ExpertsMenu(filename, theGPT);
ExpertsMenu(filename, theGPT);
goOn = 0;
break;
default:
ShowCommands();
break;
} // switch
return (retval);
} // DoCommand()
} while (goOn);
} // MainMenu()
void ShowCommands(void) {
printf("b\tconvert BSD disklabel partitions\n");
printf("b\tback up GPT data to a file\n");
printf("c\tchange a partition's name\n");
printf("d\tdelete a partition\n");
printf("i\tshow detailed information on a partition\n");
printf("l\tlist available partition types\n");
printf("m\tprint this menu\n");
printf("l\tlist known partition types\n");
printf("n\tadd a new partition\n");
printf("o\tcreate a new empty GUID partition table (GPT)\n");
printf("p\tprint the partition table\n");
printf("q\tquit without saving changes\n");
printf("r\trecovery and transformation options (experts only)\n");
printf("s\tsort partitions\n");
printf("t\tchange a partition's type code\n");
printf("v\tverify disk\n");
printf("w\twrite table to disk and exit\n");
printf("x\textra functionality (experts only)\n");
printf("?\tprint this menu\n");
} // ShowCommands()
// Accept a command and execute it. Returns 0 if the command includes
// an exit condition (such as a q or w command), 1 if more commands
// should be processed.
int ExpertsMenu(char* filename, struct GPTData* theGPT) {
// Accept a recovery & transformation menu command. Returns only when the user
// issues an exit command, such as 'w' or 'q'.
void RecoveryMenu(char* filename, struct GPTData* theGPT) {
char command, line[255], buFile[255];
int retval = 1;
PartTypes typeHelper;
uint32_t pn;
uint32_t temp1, temp2;
uint32_t temp1;
int goOn = 1;
do {
printf("\nExpert command (m for help): ");
printf("\nrecovery/transformation command (? for help): ");
fgets(line, 255, stdin);
sscanf(line, "%c", &command);
switch (command) {
case 'a': case 'A':
if (theGPT->GetPartRange(&temp1, &temp2) > 0)
theGPT->SetAttributes(theGPT->GetPartNum());
else
printf("No partitions\n");
break;
case 'b': case 'B':
theGPT->RebuildMainHeader();
break;
@@ -189,6 +191,118 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
theGPT->LoadMainTable();
break;
case 'f': case 'F':
printf("Warning! This will destroy the currently defined partitions! Proceed? ");
if (GetYN() == 'Y') {
if (theGPT->LoadMBR(filename) == 1) { // successful load
theGPT->XFormPartitions();
} else {
printf("Problem loading MBR! GPT is untouched; regenerating protective MBR!\n");
theGPT->MakeProtectiveMBR();
} // if/else
} // if
break;
case 'g': case 'G':
temp1 = theGPT->XFormToMBR();
if (temp1 > 0) {
printf("Converted %d partitions. Finalize and exit? ", temp1);
if (GetYN() == 'Y') {
if (theGPT->DestroyGPT(0) > 0)
goOn = 0;
} else {
theGPT->MakeProtectiveMBR();
printf("Note: New protective MBR created.\n");
} // if/else
} // if
break;
case 'h': case 'H':
theGPT->MakeHybrid();
break;
case 'i': case 'I':
theGPT->ShowDetails();
break;
case 'l': case 'L':
printf("Enter backup filename to load: ");
fgets(line, 255, stdin);
sscanf(line, "%s", &buFile);
theGPT->LoadGPTBackup(buFile);
break;
case 'm': case 'M':
MainMenu(filename, theGPT);
goOn = 0;
break;
case 'o': case 'O':
theGPT->DisplayMBRData();
break;
case 'p': case 'P':
theGPT->DisplayGPTData();
break;
case 'q': case 'Q':
goOn = 0;
break;
case 't': case 'T':
theGPT->XFormDisklabel();
break;
case 'v': case 'V':
theGPT->Verify();
break;
case 'w': case 'W':
if (theGPT->SaveGPTData() == 1) {
goOn = 0;
} // if
break;
case 'x': case 'X':
ExpertsMenu(filename, theGPT);
goOn = 0;
break;
default:
ShowRecoveryCommands();
break;
} // switch
} while (goOn);
} // RecoveryMenu()
void ShowRecoveryCommands(void) {
printf("b\tuse backup GPT header (rebuilding main)\n");
printf("c\tload backup partition table from disk (rebuilding main)\n");
printf("d\tuse main GPT header (rebuilding backup)\n");
printf("e\tload main partition table from disk (rebuilding backup)\n");
printf("f\tload MBR and build fresh GPT from it\n");
printf("g\tconvert GPT into MBR and exit\n");
printf("h\tmake hybrid MBR\n");
printf("i\tshow detailed information on a partition\n");
printf("l\tload partition data from a backup file\n");
printf("m\treturn to main menu\n");
printf("o\tprint protective MBR data\n");
printf("p\tprint the partition table\n");
printf("q\tquit without saving changes\n");
printf("t\ttransform BSD disklabel partition\n");
printf("v\tverify disk\n");
printf("w\twrite table to disk and exit\n");
printf("x\textra functionality (experts only)\n");
printf("?\tprint this menu\n");
} // ShowRecoveryCommands()
// Accept an experts' menu command. Returns only after the user
// selects an exit command, such as 'w' or 'q'.
void ExpertsMenu(char* filename, struct GPTData* theGPT) {
char command, line[255];
PartTypes typeHelper;
uint32_t pn;
uint32_t temp1, temp2;
int goOn = 1;
do {
printf("\nExpert command (? for help): ");
fgets(line, 255, stdin);
sscanf(line, "%c", &command);
switch (command) {
case 'a': case 'A':
if (theGPT->GetPartRange(&temp1, &temp2) > 0)
theGPT->SetAttributes(theGPT->GetPartNum());
else
printf("No partitions\n");
break;
case 'c': case 'C':
if (theGPT->GetPartRange(&temp1, &temp2) > 0) {
pn = theGPT->GetPartNum();
printf("Enter the partition's new unique GUID:\n");
@@ -199,23 +313,12 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
printf("Enter the disk's unique GUID:\n");
theGPT->SetDiskGUID(GetGUID());
break;
case 'h': case 'H':
theGPT->MakeHybrid();
break;
case 'i': case 'I':
theGPT->ShowDetails();
break;
case 'k': case 'K':
printf("Enter backup filename to save: ");
fgets(line, 255, stdin);
sscanf(line, "%s", &buFile);
theGPT->SaveGPTBackup(buFile);
break;
case 'l': case 'L':
printf("Enter backup filename to load: ");
fgets(line, 255, stdin);
sscanf(line, "%s", &buFile);
theGPT->LoadGPTBackup(buFile);
case 'm': case 'M':
MainMenu(filename, theGPT);
goOn = 0;
break;
case 'n': case 'N':
theGPT->MakeProtectiveMBR();
@@ -227,10 +330,10 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
theGPT->DisplayGPTData();
break;
case 'q': case 'Q':
retval = 0;
goOn = 0;
break;
case 'r': case 'R':
RecoveryMenu(filename, theGPT);
goOn = 0;
break;
case 's': case 'S':
@@ -241,13 +344,11 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
break;
case 'w': case 'W':
if (theGPT->SaveGPTData() == 1) {
retval = 0;
goOn = 0;
} // if
break;
case 'z': case 'Z':
if (theGPT->DestroyGPT() == 1) {
retval = 0;
goOn = 0;
}
break;
@@ -256,29 +357,22 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
break;
} // switch
} while (goOn);
return (retval);
} // ExpertsMenu()
void ShowExpertCommands(void) {
printf("a\tset attributes\n");
printf("b\tuse backup GPT header (rebuilding main)\n");
printf("c\tload backup partition table from disk (rebuilding main)\n");
printf("d\tuse main GPT header (rebuilding backup)\n");
printf("e\tload main partition table from disk (rebuilding backup)\n");
printf("f\tchange partition GUID\n");
printf("c\tchange partition GUID\n");
printf("g\tchange disk GUID\n");
printf("h\tmake hybrid MBR\n");
printf("i\tshow detailed information on a partition\n");
printf("k\tsave partition data to a backup file\n");
printf("l\tload partition data from a backup file\n");
printf("m\tprint this menu\n");
printf("m\treturn to main menu\n");
printf("n\tcreate a new protective MBR\n");
printf("o\tprint protective MBR data\n");
printf("p\tprint the partition table\n");
printf("q\tquit without saving changes\n");
printf("r\treturn to main menu\n");
printf("r\trecovery and transformation options (experts only)\n");
printf("s\tresize partition table\n");
printf("v\tverify disk\n");
printf("w\twrite table to disk and exit\n");
printf("z\tDestroy GPT data structures and exit\n");
printf("z\tzap (destroy) GPT data structures and exit\n");
printf("?\tprint this menu\n");
} // ShowExpertCommands()

237
gpt.cc
View File

@@ -112,7 +112,24 @@ int GPTData::Verify(void) {
"table when you save your partitions.\n");
} // if
// Now check that critical main and backup GPT entries match
// Now check that the main and backup headers both point to themselves....
if (mainHeader.currentLBA != 1) {
problems++;
printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
"is being automatically corrected, but it may be a symptom of more serious\n"
"problems. Think carefully before saving changes with 'w' or using this disk.\n");
mainHeader.currentLBA = 1;
} // if
if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
problems++;
printf("\nProblem: The secondary header's self-pointer doesn't point to itself. This\n"
"problem is being automatically corrected, but it may be a symptom of more\n"
"serious problems. Think carefully before saving changes with 'w' or using this\n"
"disk.\n");
secondHeader.currentLBA = diskSize - UINT64_C(1);
} // if
// Now check that critical main and backup GPT entries match each other
if (mainHeader.currentLBA != secondHeader.backupLBA) {
problems++;
printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
@@ -295,34 +312,38 @@ int GPTData::CheckHeaderValidity(void) {
// Note: Must be called BEFORE byte-order reversal on big-endian
// systems!
int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
uint32_t oldCRC, newCRC;
uint32_t oldCRC, newCRC, hSize;
// Back up old header CRC and then blank it, since it must be 0 for
// computation to be valid
oldCRC = header->headerCRC;
if (IsLittleEndian() == 0)
ReverseBytes(&oldCRC, 4);
header->headerCRC = UINT32_C(0);
hSize = header->headerSize;
// If big-endian system, reverse byte order
if (IsLittleEndian() == 0) {
ReverseBytes(&oldCRC, 4);
} // if
// Initialize CRC functions...
chksum_crc32gentab();
// Compute CRC, restore original, and return result of comparison
newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
mainHeader.headerCRC = oldCRC;
header->headerCRC = oldCRC;
return (oldCRC == newCRC);
} // GPTData::CheckHeaderCRC()
// Recompute all the CRCs. Must be called before saving (but after reversing
// byte order on big-endian systems) if any changes have been made.
void GPTData::RecomputeCRCs(void) {
uint32_t crc;
uint32_t trueNumParts;
uint32_t crc, hSize, trueNumParts;
int littleEndian = 1;
// Initialize CRC functions...
chksum_crc32gentab();
hSize = mainHeader.headerSize;
littleEndian = IsLittleEndian();
// Compute CRC of partition tables & store in main and secondary headers
@@ -342,11 +363,11 @@ void GPTData::RecomputeCRCs(void) {
secondHeader.headerCRC = 0;
// Compute & store CRCs of main & secondary headers...
crc = chksum_crc32((unsigned char*) &mainHeader, HEADER_SIZE);
crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
if (littleEndian == 0)
ReverseBytes(&crc, 4);
mainHeader.headerCRC = crc;
crc = chksum_crc32((unsigned char*) &secondHeader, HEADER_SIZE);
crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
if (littleEndian == 0)
ReverseBytes(&crc, 4);
secondHeader.headerCRC = crc;
@@ -359,7 +380,7 @@ void GPTData::RebuildMainHeader(void) {
mainHeader.signature = GPT_SIGNATURE;
mainHeader.revision = secondHeader.revision;
mainHeader.headerSize = HEADER_SIZE;
mainHeader.headerSize = secondHeader.headerSize;
mainHeader.headerCRC = UINT32_C(0);
mainHeader.reserved = secondHeader.reserved;
mainHeader.currentLBA = secondHeader.backupLBA;
@@ -382,7 +403,7 @@ void GPTData::RebuildSecondHeader(void) {
secondHeader.signature = GPT_SIGNATURE;
secondHeader.revision = mainHeader.revision;
secondHeader.headerSize = HEADER_SIZE;
secondHeader.headerSize = mainHeader.headerSize;
secondHeader.headerCRC = UINT32_C(0);
secondHeader.reserved = mainHeader.reserved;
secondHeader.currentLBA = mainHeader.backupLBA;
@@ -486,12 +507,6 @@ void GPTData::PartitionScan(int fd) {
printf("It will be destroyed if you continue!\n");
printf("*******************************************************************\n\n\a");
} // if
/* if (bsdFound) {
printf("\n*************************************************************************\n");
printf("This disk appears to contain a BSD disklabel! It will be destroyed if you\n"
"continue!\n");
printf("*************************************************************************\n\n\a");
} // if */
} // GPTData::PartitionScan()
// Read GPT data from a disk.
@@ -703,7 +718,7 @@ void GPTData::LoadSecondTableAsMain(void) {
// Writes GPT (and protective MBR) to disk. Returns 1 on successful
// write, 0 if there was a problem.
int GPTData::SaveGPTData(void) {
int allOK = 1, i;
int allOK = 1;
char answer, line[256];
int fd;
uint64_t secondTable;
@@ -856,6 +871,12 @@ int GPTData::SaveGPTBackup(char* filename) {
ReverseHeaderBytes(&secondHeader);
} // if
// Recomputing the CRCs is likely to alter them, which could be bad
// if the intent is to save a potentially bad GPT for later analysis;
// but if we don't do this, we get bogus errors when we load the
// backup. I'm favoring misses over false alarms....
RecomputeCRCs();
// Now write the protective MBR...
protectiveMBR.WriteMBRData(fd);
@@ -1021,8 +1042,8 @@ void GPTData::DisplayGPTData(void) {
uint64_t temp, totalFree;
BytesToSI(diskSize * blockSize, sizeInSI);
printf("Disk %s: %lu sectors, %s\n", device,
(unsigned long) diskSize, sizeInSI);
printf("Disk %s: %llu sectors, %s\n", device,
(unsigned long long) diskSize, sizeInSI);
printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
printf("First usable sector is %lu, last usable sector is %lu\n",
@@ -1033,7 +1054,7 @@ void GPTData::DisplayGPTData(void) {
BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
printf("\nNumber Start (sector) End (sector) Size Code Name\n");
for (i = 0; i < mainHeader.numParts; i++) {
partitions[i].ShowSummary(i, blockSize, sizeInSI);
partitions[i].ShowSummary(i, blockSize);
} // for
} // GPTData::DisplayGPTData()
@@ -1212,20 +1233,24 @@ void GPTData::SetAttributes(uint32_t partNum) {
// This function destroys the on-disk GPT structures. Returns 1 if the
// user confirms destruction, 0 if the user aborts.
int GPTData::DestroyGPT(void) {
// If prompt == 0, don't ask user about proceeding and do NOT wipe out
// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
int GPTData::DestroyGPT(int prompt) {
int fd, i;
char blankSector[512], goOn;
char blankSector[512], goOn = 'Y', blank = 'N';
for (i = 0; i < 512; i++) {
blankSector[i] = '\0';
} // for
if ((apmFound) || (bsdFound)) {
if (((apmFound) || (bsdFound)) && prompt) {
printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
"damage any APM or BSD partitions on this disk!\n");
} // if APM or BSD
if (prompt) {
printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
goOn = GetYN();
} // if
if (goOn == 'Y') {
fd = open(device, O_WRONLY);
#ifdef __APPLE__
@@ -1245,18 +1270,21 @@ int GPTData::DestroyGPT(void) {
write(fd, blankSector, 512);
lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
write(fd, blankSector, 512); // blank it out
if (prompt) {
printf("Blank out MBR? ");
if (GetYN() == 'Y') {
blank = GetYN();
}// if
// Note on below: Touch the MBR only if the user wants it completely
// blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
// the MBR, but this could wipe out a valid MBR that the program
// had subsequently discarded (say, if it conflicted with older GPT
// structures).
if (blank == 'Y') {
lseek64(fd, 0, SEEK_SET);
write(fd, blankSector, 512); // blank it out
} else { // write current protective MBR, in case it's hybrid....
// find and delete 0xEE partitions in MBR
for (i = 0; i < 4; i++) {
if (protectiveMBR.GetType(i) == (uint8_t) 0xEE) {
protectiveMBR.DeletePartition(i);
} // if
} // for
protectiveMBR.WriteMBRData(fd);
} else {
printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
"with fdisk or another tool.\n");
} // if/else
DiskSync(fd);
close(fd);
@@ -1374,8 +1402,8 @@ int GPTData::XFormPartitions(void) {
ClearGPTData();
// Convert the smaller of the # of GPT or MBR partitions
if (mainHeader.numParts > (NUM_LOGICALS + 4))
numToConvert = NUM_LOGICALS + 4;
if (mainHeader.numParts > (MAX_MBR_PARTS))
numToConvert = MAX_MBR_PARTS;
else
numToConvert = mainHeader.numParts;
@@ -1467,15 +1495,102 @@ int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
return numDone;
} // GPTData::XFormDisklabel(BSDData* disklabel)
// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
// functions. Returns 1 if operation was successful.
int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
int allOK = 1, typeCode, bootable;
uint64_t length;
char line[255];
if ((mbrPart < 0) || (mbrPart > 3)) {
printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
allOK = 0;
} // if
if (gptPart >= mainHeader.numParts) {
printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
allOK = 0;
} // if
if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
allOK = 0;
} // if
if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
(partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
printf("Caution: Partition end point past 32-bit pointer boundary;"
" some OSes may\nreact strangely.\n");
} // if partition ends past 32-bit (usually 2TiB) boundary
do {
printf("Enter an MBR hex code (default %02X): ",
typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
fgets(line, 255, stdin);
sscanf(line, "%x", &typeCode);
if (line[0] == '\n')
typeCode = partitions[gptPart].GetHexType() / 256;
} while ((typeCode <= 0) || (typeCode > 255));
printf("Set the bootable flag? ");
bootable = (GetYN() == 'Y');
length = partitions[gptPart].GetLengthLBA();
protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
(uint32_t) length, typeCode, bootable);
} else { // partition out of range
printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
"partitions, or is\n too big; omitting it.\n", gptPart + 1);
allOK = 0;
} // if/else
return allOK;
} // GPTData::OnePartToMBR()
// Convert the GPT to MBR form. This function is necessarily limited; it
// handles at most four partitions and creates layouts that ignore CHS
// geometries. Returns the number of converted partitions; if this value
// is over 0, the calling function should call DestroyGPT() to destroy
// the GPT data, and then exit.
int GPTData::XFormToMBR(void) {
char line[255];
int i, j, numParts, numConverted = 0;
uint32_t partNums[4];
// Get the numbers of up to four partitions to add to the
// hybrid MBR....
numParts = CountParts();
printf("Counted %d partitions.\n", numParts);
// Prepare the MBR for conversion (empty it of existing partitions).
protectiveMBR.EmptyMBR(0);
protectiveMBR.SetDiskSize(diskSize);
if (numParts > 4) { // Over four partitions; engage in triage
printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
"used in the MBR, in sequence: ");
fgets(line, 255, stdin);
numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
&partNums[2], &partNums[3]);
} else { // Four or fewer partitions; convert them all
i = j = 0;
while ((j < numParts) && (i < mainHeader.numParts)) {
if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
partNums[j++] = ++i; // flag it for conversion
} else i++;
} // while
} // if/else
for (i = 0; i < numParts; i++) {
j = partNums[i] - 1;
printf("\nCreating entry for partition #%d\n", j + 1);
numConverted += OnePartToMBR(j, i);
} // for
return numConverted;
} // GPTData::XFormToMBR()
// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
// OSes that don't understand GPT.
void GPTData::MakeHybrid(void) {
uint32_t partNums[3];
char line[255];
int numParts, i, j, typeCode, bootable, mbrNum;
uint64_t length;
int numParts, numConverted = 0, i, j, typeCode, mbrNum;
char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
char eeFirst; // Whether EFI GPT (0xEE) partition comes first in table
char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
"to use one, just hit the Enter key at the below prompt and your MBR\n"
@@ -1500,38 +1615,14 @@ void GPTData::MakeHybrid(void) {
for (i = 0; i < numParts; i++) {
j = partNums[i] - 1;
printf("\nCreating entry for partition #%d\n", j + 1);
if ((j >= 0) && (j < mainHeader.numParts)) {
if ((partitions[j].GetLastLBA() < UINT32_MAX) &&
(partitions[j].GetLastLBA() > UINT64_C(0))) {
do {
printf("Enter an MBR hex code (default %02X): ",
typeHelper.GUIDToID(partitions[j].GetType()) / 256);
fgets(line, 255, stdin);
sscanf(line, "%x", &typeCode);
if (line[0] == '\n')
typeCode = partitions[j].GetHexType() / 256;
} while ((typeCode <= 0) || (typeCode > 255));
printf("Set the bootable flag? ");
bootable = (GetYN() == 'Y');
length = partitions[j].GetLengthLBA();
if (eeFirst == 'Y')
mbrNum = i + 1;
else
mbrNum = i;
protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].GetFirstLBA(),
(uint32_t) length, typeCode, bootable);
protectiveMBR.SetHybrid();
} else { // partition out of range
printf("Partition %d ends beyond the 2TiB limit of MBR partitions or does not exist;\n"
"omitting it.\n",
j + 1);
} // if/else
} else {
printf("Partition %d is out of range; omitting it.\n", j + 1);
} // if/else
numConverted += OnePartToMBR(j, mbrNum);
} // for
if (numParts > 0) { // User opted to create a hybrid MBR....
if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
// Create EFI protective partition that covers the start of the disk.
// If this location (covering the main GPT data structures) is omitted,
// Linux won't find any partitions on the disk. Note that this is
@@ -1544,6 +1635,7 @@ void GPTData::MakeHybrid(void) {
else
mbrNum = numParts;
protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
protectiveMBR.SetHybrid();
// ... and for good measure, if there are any partition spaces left,
// optionally create another protective EFI partition to cover as much
@@ -1692,7 +1784,7 @@ int GPTData::ClearGPTData(void) {
// Now initialize a bunch of stuff that's static....
mainHeader.signature = GPT_SIGNATURE;
mainHeader.revision = 0x00010000;
mainHeader.headerSize = (uint32_t) HEADER_SIZE;
mainHeader.headerSize = HEADER_SIZE;
mainHeader.reserved = 0;
mainHeader.currentLBA = UINT64_C(1);
mainHeader.partitionEntriesLBA = (uint64_t) 2;
@@ -1790,6 +1882,17 @@ int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
return numFound;
} // GPTData::GetPartRange()
// Returns the number of defined partitions.
uint32_t GPTData::CountParts(void) {
int i, counted = 0;
for (i = 0; i < mainHeader.numParts; i++) {
if (partitions[i].GetFirstLBA() > 0)
counted++;
} // for
return counted;
} // GPTData::CountParts()
/****************************************************
* *
* Functions that return data about disk free space *
@@ -2017,8 +2120,8 @@ int SizesOK(void) {
fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
allOK = 0;
} // if
if (sizeof(struct EBRRecord) != 512) {
fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(EBRRecord));
if (sizeof(struct TempMBR) != 512) {
fprintf(stderr, "TempMBR is %d bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
allOK = 0;
} // if
if (sizeof(struct GPTHeader) != 512) {

10
gpt.h
View File

@@ -67,7 +67,6 @@ protected:
int secondPartsCrcOk;
int apmFound; // set to 1 if APM detected
int bsdFound; // set to 1 if BSD disklabel detected in MBR
// uint32_t units; // display units, in multiples of sectors
PartTypes typeHelper;
public:
// Basic necessary functions....
@@ -87,6 +86,7 @@ public:
int FindOverlaps(void);
// Load or save data from/to disk
int LoadMBR(char* f) {return protectiveMBR.ReadMBRData(f);}
void PartitionScan(int fd);
int LoadPartitions(char* deviceFilename);
int ForceLoadGPTData(int fd);
@@ -111,13 +111,15 @@ public:
void DeletePartition(void);
void ChangePartType(void);
void SetAttributes(uint32_t partNum);
int DestroyGPT(void); // Returns 1 if user proceeds
int DestroyGPT(int prompt = 1); // Returns 1 if user proceeds
// Convert to GPT from other formats (may require user interaction)
// Convert between GPT and other formats (may require user interaction)
WhichToUse UseWhichPartitions(void);
int XFormPartitions(void);
int XFormDisklabel(int OnGptPart = -1);
int XFormDisklabel(BSDData* disklabel, int startPart);
int OnePartToMBR(uint32_t gptPart, int mbrPart); // add one partition to MBR. Returns 1 if successful
int XFormToMBR(void); // convert GPT to MBR, wiping GPT afterwards. Returns 1 if successful
void MakeHybrid(void);
// Adjust GPT structures WITHOUT user interaction...
@@ -139,6 +141,8 @@ public:
uint64_t GetSecondPartsLBA(void) {return secondHeader.partitionEntriesLBA;}
uint64_t GetBlocksInPartTable(void) {return (mainHeader.numParts *
mainHeader.sizeOfPartitionEntries) / blockSize;}
uint32_t CountParts(void);
// Find information about free space
uint64_t FindFirstAvailable(uint64_t start = 0);

View File

@@ -9,8 +9,8 @@
// Copyright: See COPYING file that comes with this distribution
//
//
/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
// under the terms of the GNU GPL version 2, as detailed in the COPYING file.
#define __STDC_LIMIT_MACROS
#define __STDC_CONSTANT_MACROS
@@ -25,6 +25,10 @@ using namespace std;
PartTypes GPTPart::typeHelper;
GPTPart::GPTPart(void) {
int i;
for (i = 0; i < NAME_SIZE; i++)
name[i] = '\0';
} // Default constructor
GPTPart::~GPTPart(void) {
@@ -128,16 +132,16 @@ void GPTPart::ReversePartBytes(void) {
} // GPTPart::ReverseBytes()
// Display summary information; does nothing if the partition is empty.
void GPTPart::ShowSummary(int i, uint32_t blockSize, char* sizeInSI) {
int j;
void GPTPart::ShowSummary(int partNum, uint32_t blockSize) {
char sizeInSI[255];
int j = 0;
if (firstLBA != 0) {
BytesToSI(blockSize * (lastLBA - firstLBA + 1), sizeInSI);
printf("%4d %14lu %14lu", i + 1, (unsigned long) firstLBA,
printf("%4d %14lu %14lu", partNum + 1, (unsigned long) firstLBA,
(unsigned long) lastLBA);
printf(" %-10s %04X ", sizeInSI,
typeHelper.GUIDToID(partitionType));
j = 0;
while ((name[j] != '\0') && (j < 44)) {
printf("%c", name[j]);
j += 2;

View File

@@ -9,8 +9,8 @@
// Copyright: See COPYING file that comes with this distribution
//
//
/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
// under the terms of the GNU GPL version 2, as detailed in the COPYING file.
#ifndef __GPTPART_H
#define __GPTPART_H
@@ -23,11 +23,11 @@
using namespace std;
/*****************************************
/****************************************
* *
* GUIDPart class and related structures *
* GPTPart class and related structures *
* *
*****************************************/
****************************************/
class GPTPart {
protected:
@@ -73,7 +73,7 @@ class GPTPart {
// Additional functions
GPTPart & operator=(const GPTPart & orig);
void ShowSummary(int i, uint32_t blockSize, char* sizeInSI); // display summary information (1-line)
void ShowSummary(int partNum, uint32_t blockSize); // display summary information (1-line)
void ShowDetails(uint32_t blockSize); // display detailed information (multi-line)
void BlankPartition(void); // empty partition of data
int DoTheyOverlap(GPTPart* other); // returns 1 if there's overlap

540
mbr.cc
View File

@@ -1,7 +1,7 @@
/* mbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition
data. */
/* By Rod Smith, January to February, 2009 */
/* Initial coding by Rod Smith, January to February, 2009 */
/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
@@ -35,6 +35,8 @@ MBRData::MBRData(void) {
strcpy(device, "");
state = invalid;
srand((unsigned int) time(NULL));
numHeads = MAX_HEADS;
numSecspTrack = MAX_SECSPERTRACK;
EmptyMBR();
} // MBRData default constructor
@@ -43,6 +45,8 @@ MBRData::MBRData(char *filename) {
diskSize = 0;
strcpy(device, filename);
state = invalid;
numHeads = MAX_HEADS;
numSecspTrack = MAX_SECSPERTRACK;
srand((unsigned int) time(NULL));
// Try to read the specified partition table, but if it fails....
@@ -55,51 +59,11 @@ MBRData::MBRData(char *filename) {
MBRData::~MBRData(void) {
} // MBRData destructor
// Empty all data. Meant mainly for calling by constructors, but it's also
// used by the hybrid MBR functions in the GPTData class.
void MBRData::EmptyMBR(int clearBootloader) {
int i;
// Zero out the boot loader section, the disk signature, and the
// 2-byte nulls area only if requested to do so. (This is the
// default.)
if (clearBootloader == 1) {
for (i = 0; i < 440; i++)
code[i] = 0;
diskSignature = (uint32_t) rand();
nulls = 0;
} // if
// Blank out the partitions
for (i = 0; i < 4; i++) {
partitions[i].status = UINT8_C(0);
partitions[i].firstSector[0] = UINT8_C(0);
partitions[i].firstSector[1] = UINT8_C(0);
partitions[i].firstSector[2] = UINT8_C(0);
partitions[i].partitionType = UINT8_C(0);
partitions[i].lastSector[0] = UINT8_C(0);
partitions[i].lastSector[1] = UINT8_C(0);
partitions[i].lastSector[2] = UINT8_C(0);
partitions[i].firstLBA = UINT32_C(0);
partitions[i].lengthLBA = UINT32_C(0);
} // for
MBRSignature = MBR_SIGNATURE;
blockSize = SECTOR_SIZE;
diskSize = 0;
for (i = 0; i < NUM_LOGICALS; i++) {
logicals[i].status = UINT8_C(0);
logicals[i].firstSector[0] = UINT8_C(0);
logicals[i].firstSector[1] = UINT8_C(0);
logicals[i].firstSector[2] = UINT8_C(0);
logicals[i].partitionType = UINT8_C(0);
logicals[i].lastSector[0] = UINT8_C(0);
logicals[i].lastSector[1] = UINT8_C(0);
logicals[i].lastSector[2] = UINT8_C(0);
logicals[i].firstLBA = UINT32_C(0);
logicals[i].lengthLBA = UINT32_C(0);
} // for
} // MBRData::EmptyMBR()
/**********************
* *
* Disk I/O functions *
* *
**********************/
// Read data from MBR. Returns 1 if read was successful (even if the
// data isn't a valid MBR), 0 if the read failed.
@@ -120,25 +84,18 @@ int MBRData::ReadMBRData(char* deviceFilename) {
return allOK;
} // MBRData::ReadMBRData(char* deviceFilename)
// Read data from MBR.
// Read data from MBR. If checkBlockSize == 1 (the default), the block
// size is checked; otherwise it's set to the default (512 bytes).
// Note that any extended partition(s) present will be explicitly stored
// in the partitions[] array, along with their contained partitions; the
// extended container partition(s) should be ignored by other functions.
void MBRData::ReadMBRData(int fd, int checkBlockSize) {
int allOK = 1, i, j, maxLogicals = 0;
int allOK = 1, i, j, logicalNum;
int err;
TempMBR tempMBR;
// Clear logical partition array
for (i = 0; i < NUM_LOGICALS; i++) {
logicals[i].status = UINT8_C(0);
logicals[i].firstSector[0] = UINT8_C(0);
logicals[i].firstSector[1] = UINT8_C(0);
logicals[i].firstSector[2] = UINT8_C(0);
logicals[i].partitionType = UINT8_C(0);
logicals[i].lastSector[0] = UINT8_C(0);
logicals[i].lastSector[1] = UINT8_C(0);
logicals[i].lastSector[2] = UINT8_C(0);
logicals[i].firstLBA = UINT32_C(0);
logicals[i].lengthLBA = UINT32_C(0);
} // for
// Empty existing MBR data, including the logical partitions...
EmptyMBR(0);
err = lseek64(fd, 0, SEEK_SET);
err = read(fd, &tempMBR, 512);
@@ -154,8 +111,8 @@ void MBRData::ReadMBRData(int fd, int checkBlockSize) {
for (j = 0; j < 3; j++) {
partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j];
partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j];
} // for j...
} // for i...
} // for j... (reading parts of CHS geometry)
} // for i... (reading all four partitions)
MBRSignature = tempMBR.MBRSignature;
// Reverse the byte order, if necessary
@@ -180,11 +137,6 @@ void MBRData::ReadMBRData(int fd, int checkBlockSize) {
// Find block size
if (checkBlockSize) {
blockSize = GetBlockSize(fd);
// if ((blockSize = GetBlockSize(fd)) == -1) {
// blockSize = SECTOR_SIZE;
// printf("Unable to determine sector size; assuming %lu bytes!\n",
// (unsigned long) SECTOR_SIZE);
// } // if
} // if (checkBlockSize)
// Load logical partition data, if any is found....
@@ -193,8 +145,8 @@ void MBRData::ReadMBRData(int fd, int checkBlockSize) {
if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
|| (partitions[i].partitionType == 0x85)) {
// Found it, so call a recursive algorithm to load everything from them....
maxLogicals = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), maxLogicals);
if ((maxLogicals < 0) || (maxLogicals > NUM_LOGICALS)) {
logicalNum = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), 4);
if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
allOK = 0;
fprintf(stderr, "Error reading logical partitions! List may be truncated!\n");
} // if maxLogicals valid
@@ -224,26 +176,70 @@ void MBRData::ReadMBRData(int fd, int checkBlockSize) {
(partitions[i].partitionType != UINT8_C(0x00)))
state = hybrid;
} // for
} // if hybrid
/* // Tell the user what the MBR state is...
switch (state) {
case invalid:
printf("Information: MBR appears to be empty or invalid.\n");
break;
case gpt:
printf("Information: MBR holds GPT placeholder partitions.\n");
break;
case hybrid:
printf("Information: MBR holds hybrid GPT/MBR data.\n");
break;
case mbr:
printf("Information: MBR data appears to be valid.\n");
break;
} // switch */
} // if (hybrid detection code)
} // MBRData::ReadMBRData(int fd)
// Write the MBR data to the default defined device.
// This is a recursive function to read all the logical partitions, following the
// logical partition linked list from the disk and storing the basic data in the
// partitions[] array. Returns last index to partitions[] used, or -1 if there was
// a problem.
// Parameters:
// fd = file descriptor
// extendedStart = LBA of the start of the extended partition
// diskOffset = LBA offset WITHIN the extended partition of the one to be read
// partNum = location in partitions[] array to store retrieved data
int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
uint32_t diskOffset, int partNum) {
struct TempMBR ebr;
off_t offset;
// Check for a valid partition number. Note that partitions MAY be read into
// the area normally used by primary partitions, although the only calling
// function as of GPT fdisk version 0.5.0 doesn't do so.
if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
offset = (off_t) (extendedStart + diskOffset) * blockSize;
if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
partNum = -1;
}
if (read(fd, &ebr, 512) != 512) { // Load the data....
fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
(unsigned long) offset);
partNum = -1;
} else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
ReverseBytes(&ebr.MBRSignature, 2);
ReverseBytes(&ebr.partitions[0].firstLBA, 4);
ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
ReverseBytes(&ebr.partitions[1].firstLBA, 4);
ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
} // if/else/if
if (ebr.MBRSignature != MBR_SIGNATURE) {
partNum = -1;
fprintf(stderr, "MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
(unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
} // if
// Copy over the basic data....
partitions[partNum].status = ebr.partitions[0].status;
partitions[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
partitions[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
partitions[partNum].partitionType = ebr.partitions[0].partitionType;
// Find the next partition (if there is one) and recurse....
if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) &&
(partNum < (MAX_MBR_PARTS - 1))) {
partNum = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA,
partNum + 1);
} else {
partNum++;
} // if another partition
} // Not enough space for all the logicals (or previous error encountered)
return (partNum);
} // MBRData::ReadLogicalPart()
// Write the MBR data to the default defined device. Note that this writes
// ONLY the MBR itself, not the logical partition data.
int MBRData::WriteMBRData(void) {
int allOK = 1, fd;
@@ -295,12 +291,6 @@ void MBRData::WriteMBRData(int fd) {
lseek64(fd, 0, SEEK_SET);
write(fd, &tempMBR, 512);
/* write(fd, code, 440);
write(fd, &diskSignature, 4);
write(fd, &nulls, 2);
write(fd, partitions, 64);
write(fd, &MBRSignature, 2); */
// Reverse the byte order back, if necessary
if (IsLittleEndian() == 0) {
ReverseBytes(&diskSignature, 4);
@@ -313,56 +303,11 @@ void MBRData::WriteMBRData(int fd) {
}// if
} // MBRData::WriteMBRData(int fd)
// This is a recursive function to read all the logical partitions, following the
// logical partition linked list from the disk and storing the basic data in the
// logicals[] array. Returns last index to logicals[] uses, or -1 if there was a
// problem
int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
uint32_t diskOffset, int partNum) {
struct EBRRecord ebr;
off_t offset;
if ((partNum < NUM_LOGICALS) && (partNum >= 0)) {
offset = (off_t) (extendedStart + diskOffset) * blockSize;
if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
partNum = -1;
}
if (read(fd, &ebr, 512) != 512) { // Load the data....
fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
(unsigned long) offset);
partNum = -1;
} else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
ReverseBytes(&ebr.MBRSignature, 2);
ReverseBytes(&ebr.partitions[0].firstLBA, 4);
ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
ReverseBytes(&ebr.partitions[1].firstLBA, 4);
ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
} // if/else/if
if (ebr.MBRSignature != MBR_SIGNATURE) {
partNum = -1;
fprintf(stderr, "MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
(unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
} // if
// Copy over the basic data....
logicals[partNum].status = ebr.partitions[0].status;
logicals[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
logicals[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
logicals[partNum].partitionType = ebr.partitions[0].partitionType;
// Find the next partition (if there is one) and recurse....
if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 0) &&
(partNum < (NUM_LOGICALS - 1))) {
partNum = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA,
partNum + 1);
} else {
partNum++;
} // if another partition
} // Not enough space for all the logicals (or previous error encountered)
return (partNum);
} // MBRData::ReadLogicalPart()
/********************************************
* *
* Functions that display data for the user *
* *
********************************************/
// Show the MBR data to the user....
void MBRData::DisplayMBRData(void) {
@@ -373,7 +318,7 @@ void MBRData::DisplayMBRData(void) {
printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
printf("MBR partitions:\n");
printf("Number\t Boot\t Start (sector)\t Length (sectors)\tType\n");
for (i = 0; i < 4; i++) {
for (i = 0; i < MAX_MBR_PARTS; i++) {
if (partitions[i].lengthLBA != 0) {
if (partitions[i].status && 0x80) // it's bootable
bootCode = '*';
@@ -384,31 +329,144 @@ void MBRData::DisplayMBRData(void) {
(unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
} // if
} // for
// Now display logical partition data....
for (i = 0; i < NUM_LOGICALS; i++) {
if (logicals[i].lengthLBA != 0) {
printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 5, (unsigned long) logicals[i].firstLBA,
(unsigned long) logicals[i].lengthLBA, logicals[i].partitionType);
} // if
} // for
printf("\nDisk size is %lu sectors (%s)\n", (unsigned long) diskSize,
printf("\nDisk size is %llu sectors (%s)\n", (unsigned long long) diskSize,
BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
} // MBRData::DisplayMBRData()
// Displays the state, as a word, on stdout. Used for debugging & to
// tell the user about the MBR state when the program launches....
void MBRData::ShowState(void) {
switch (state) {
case invalid:
printf(" MBR: not present\n");
break;
case gpt:
printf(" MBR: protective\n");
break;
case hybrid:
printf(" MBR: hybrid\n");
break;
case mbr:
printf(" MBR: MBR only\n");
break;
default:
printf("\a MBR: unknown -- bug!\n");
break;
} // switch
} // MBRData::ShowState()
/*********************************************************************
* *
* Functions that set or get disk metadata (CHS geometry, disk size, *
* etc.) *
* *
*********************************************************************/
// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function.
// Note that this only sets the heads and sectors; the number of
// cylinders is determined by these values and the disk size.
void MBRData::SetCHSGeom(uint32_t h, uint32_t s) {
if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
numHeads = h;
numSecspTrack = s;
} else {
printf("Warning! Attempt to set invalid CHS geometry!\n");
} // if/else
} // MBRData::SetCHSGeom()
// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
// was within the range that can be expressed by CHS (including 0, for an
// empty partition), 0 if the value is outside that range, and -1 if chs is
// invalid.
int MBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
uint64_t cylinder, head, sector; // all numbered from 0
uint64_t remainder;
int retval = 1;
int done = 0;
if (chs != NULL) {
// Special case: In case of 0 LBA value, zero out CHS values....
if (lba == 0) {
chs[0] = chs[1] = chs[2] = UINT8_C(0);
done = 1;
} // if
// If LBA value is too large for CHS, max out CHS values....
if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
chs[0] = 254;
chs[1] = chs[2] = 255;
done = 1;
retval = 0;
} // if
// If neither of the above applies, compute CHS values....
if (!done) {
cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
remainder = lba - (cylinder * numHeads * numSecspTrack);
head = remainder / numSecspTrack;
remainder -= head * numSecspTrack;
sector = remainder;
if (head < numHeads)
chs[0] = head;
else
retval = 0;
if (sector < numSecspTrack) {
chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
} else {
retval = 0;
} // if/else
} // if value is expressible and non-0
} else { // Invalid (NULL) chs pointer
retval = -1;
} // if CHS pointer valid
return (retval);
} // MBRData::LBAtoCHS()
/*****************************************************
* *
* Functions to create, delete, or change partitions *
* *
*****************************************************/
// Empty all data. Meant mainly for calling by constructors, but it's also
// used by the hybrid MBR functions in the GPTData class.
void MBRData::EmptyMBR(int clearBootloader) {
int i;
// Zero out the boot loader section, the disk signature, and the
// 2-byte nulls area only if requested to do so. (This is the
// default.)
if (clearBootloader == 1) {
for (i = 0; i < 440; i++)
code[i] = 0;
diskSignature = (uint32_t) rand();
nulls = 0;
} // if
// Blank out the partitions
for (i = 0; i < MAX_MBR_PARTS; i++) {
partitions[i].status = UINT8_C(0);
partitions[i].firstSector[0] = UINT8_C(0);
partitions[i].firstSector[1] = UINT8_C(0);
partitions[i].firstSector[2] = UINT8_C(0);
partitions[i].partitionType = UINT8_C(0);
partitions[i].lastSector[0] = UINT8_C(0);
partitions[i].lastSector[1] = UINT8_C(0);
partitions[i].lastSector[2] = UINT8_C(0);
partitions[i].firstLBA = UINT32_C(0);
partitions[i].lengthLBA = UINT32_C(0);
} // for
MBRSignature = MBR_SIGNATURE;
} // MBRData::EmptyMBR()
// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
void MBRData::MakeProtectiveMBR(int clearBoot) {
int i;
EmptyMBR(clearBoot);
// Initialize variables
nulls = 0;
MBRSignature = MBR_SIGNATURE;
if (clearBoot > 0) {
for (i = 0; i < 440; i++)
code[i] = (uint8_t) 0;
} // if
partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
// Write CHS data. This maxes out the use of the disk, as much as
@@ -427,44 +485,43 @@ void MBRData::MakeProtectiveMBR(int clearBoot) {
partitions[0].partitionType = UINT8_C(0xEE);
partitions[0].firstLBA = UINT32_C(1);
if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
partitions[0].lengthLBA = diskSize - 1;
partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
} else { // disk is too big to represent, so fake it...
partitions[0].lengthLBA = UINT32_MAX;
} // if/else
// Zero out three unused primary partitions...
for (i = 1; i < 4; i++) {
partitions[i].status = UINT8_C(0);
partitions[i].firstSector[0] = UINT8_C(0);
partitions[i].firstSector[1] = UINT8_C(0);
partitions[i].firstSector[2] = UINT8_C(0);
partitions[i].partitionType = UINT8_C(0);
partitions[i].lastSector[0] = UINT8_C(0);
partitions[i].lastSector[1] = UINT8_C(0);
partitions[i].lastSector[2] = UINT8_C(0);
partitions[i].firstLBA = UINT32_C(0);
partitions[i].lengthLBA = UINT32_C(0);
} // for
// Zero out all the logical partitions. Not necessary for data
// integrity on write, but eliminates stray entries if user wants
// to view the MBR after converting the disk
for (i = 0; i < NUM_LOGICALS; i++) {
logicals[i].status = UINT8_C(0);
logicals[i].firstSector[0] = UINT8_C(0);
logicals[i].firstSector[1] = UINT8_C(0);
logicals[i].firstSector[2] = UINT8_C(0);
logicals[i].partitionType = UINT8_C(0);
logicals[i].lastSector[0] = UINT8_C(0);
logicals[i].lastSector[1] = UINT8_C(0);
logicals[i].lastSector[2] = UINT8_C(0);
logicals[i].firstLBA = UINT32_C(0);
logicals[i].lengthLBA = UINT32_C(0);
} // for
state = gpt;
} // MBRData::MakeProtectiveMBR()
// Create a partition of the specified number, starting LBA, and
// length. This function does *NO* error checking, so it's possible
// to seriously screw up a partition table using this function!
// Note: This function should NOT be used to create the 0xEE partition
// in a conventional GPT configuration, since that partition has
// specific size requirements that this function won't handle. It may
// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
// since those toss the rulebook away anyhow....
void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
int bootable) {
if ((num >= 0) && (num < MAX_MBR_PARTS)) {
partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
partitions[num].firstSector[0] = UINT8_C(0);
partitions[num].firstSector[1] = UINT8_C(0);
partitions[num].firstSector[2] = UINT8_C(0);
partitions[num].partitionType = (uint8_t) type;
partitions[num].lastSector[0] = UINT8_C(0);
partitions[num].lastSector[1] = UINT8_C(0);
partitions[num].lastSector[2] = UINT8_C(0);
partitions[num].firstLBA = start;
partitions[num].lengthLBA = length;
// If this is a "real" partition, set its CHS geometry
if (length > 0) {
LBAtoCHS((uint64_t) start, partitions[num].firstSector);
LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
} // if (length > 0)
} // if valid partition number
} // MBRData::MakePart()
// Create a partition that fills the most available space. Returns
// 1 if partition was created, 0 otherwise. Intended for use in
// creating hybrid MBRs.
@@ -517,15 +574,16 @@ void MBRData::DeletePartition(int i) {
// Used to help keep GPT & hybrid MBR partitions in sync....
int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
uint32_t start32, length32;
int i, j, deleted = 0;
int i, deleted = 0;
if ((state == hybrid) && (start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
start32 = (uint32_t) start64;
length32 = (uint32_t) length64;
for (i = 0; i < 4; i++) {
for (i = 0; i < MAX_MBR_PARTS; i++) {
if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
(partitions[i].partitionType != 0xEE)) {
DeletePartition(i);
if (state == hybrid)
OptimizeEESize();
deleted = 1;
} // if (match found)
@@ -554,74 +612,24 @@ void MBRData::OptimizeEESize(void) {
partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
} // if free space after
} // if partition is 0xEE
} // for partition loop
if (typeFlag == 0) { // No non-hybrid partitions found
MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
} // if
} // for partition loop
} // MBRData::OptimizeEESize()
// Return a pointer to a primary or logical partition, or NULL if
// the partition is out of range....
struct MBRRecord* MBRData::GetPartition(int i) {
MBRRecord* thePart = NULL;
if ((i >= 0) && (i < 4)) { // primary partition
thePart = &partitions[i];
} // if
if ((i >= 4) && (i < (NUM_LOGICALS + 4))) {
thePart = &logicals[i - 4];
} // if
return thePart;
} // GetPartition()
// Displays the state, as a word, on stdout. Used for debugging & to
// tell the user about the MBR state when the program launches....
void MBRData::ShowState(void) {
switch (state) {
case invalid:
printf(" MBR: not present\n");
break;
case gpt:
printf(" MBR: protective\n");
break;
case hybrid:
printf(" MBR: hybrid\n");
break;
case mbr:
printf(" MBR: MBR only\n");
break;
default:
printf("\a MBR: unknown -- bug!\n");
break;
} // switch
} // MBRData::ShowState()
// Create a primary partition of the specified number, starting LBA,
// and length. This function does *NO* error checking, so it's possible
// to seriously screw up a partition table using this function! It's
// intended as a way to create a hybrid MBR, which is a pretty funky
// setup to begin with....
void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
int bootable) {
partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
partitions[num].firstSector[0] = UINT8_C(0);
partitions[num].firstSector[1] = UINT8_C(0);
partitions[num].firstSector[2] = UINT8_C(0);
partitions[num].partitionType = (uint8_t) type;
partitions[num].lastSector[0] = UINT8_C(0);
partitions[num].lastSector[1] = UINT8_C(0);
partitions[num].lastSector[2] = UINT8_C(0);
partitions[num].firstLBA = start;
partitions[num].lengthLBA = length;
} // MakePart()
/****************************************
* *
* Functions to find data on free space *
* *
****************************************/
// Finds the first free space on the disk from start onward; returns 0
// if none available....
uint32_t MBRData::FindFirstAvailable(uint32_t start) {
uint32_t first;
uint32_t i;
int firstMoved = 0;
int firstMoved;
first = start;
@@ -651,7 +659,7 @@ uint32_t MBRData::FindLastInFree(uint32_t start) {
uint32_t nearestStart;
uint32_t i;
if (diskSize <= UINT32_MAX)
if ((diskSize <= UINT32_MAX) && (diskSize > 0))
nearestStart = diskSize - 1;
else
nearestStart = UINT32_MAX - 1;
@@ -688,6 +696,8 @@ int MBRData::IsFree(uint32_t sector) {
for (i = 0; i < 4; i++) {
first = partitions[i].firstLBA;
// Note: Weird two-line thing to avoid subtracting 1 from a 0 value
// for an unsigned int....
last = first + partitions[i].lengthLBA;
if (last > 0) last--;
if ((first <= sector) && (last >= sector))
@@ -696,6 +706,12 @@ int MBRData::IsFree(uint32_t sector) {
return isFree;
} // MBRData::IsFree()
/******************************************************
* *
* Functions that extract data on specific partitions *
* *
******************************************************/
uint8_t MBRData::GetStatus(int i) {
MBRRecord* thePart;
uint8_t retval;
@@ -772,7 +788,23 @@ GPTPart MBRData::AsGPT(int i) {
newPart.SetUniqueGUID(1);
newPart.SetAttributes(0);
newPart.SetName((unsigned char*) newPart.GetNameType(tempStr));
} // if
} // if
} // if not extended, protective, or non-existent
} // if (origPart != NULL)
return newPart;
} // MBRData::AsGPT()
/***********************
* *
* Protected functions *
* *
***********************/
// Return a pointer to a primary or logical partition, or NULL if
// the partition is out of range....
struct MBRRecord* MBRData::GetPartition(int i) {
MBRRecord* thePart = NULL;
if ((i >= 0) && (i < MAX_MBR_PARTS))
thePart = &partitions[i];
return thePart;
} // GetPartition()

62
mbr.h
View File

@@ -12,9 +12,12 @@
#define __MBRSTRUCTS
#define MBR_SIGNATURE UINT16_C(0xAA55)
#define MAX_HEADS 255 /* numbered 0 - 254 */
#define MAX_SECSPERTRACK 63 /* numbered 1 - 63 */
#define MAX_CYLINDERS 1024 /* numbered 0 - 1023 */
// Maximum number of logical partitions supported
#define NUM_LOGICALS 124
// Maximum number of MBR partitions
#define MAX_MBR_PARTS 128
using namespace std;
@@ -36,9 +39,10 @@ struct MBRRecord {
uint32_t lengthLBA;
}; // struct MBRRecord
// Create a 512-byte data structure into which the MBR can be loaded in one
// A 512-byte data structure into which the MBR can be loaded in one
// go, for the benefit of FreeBSD which seems to flake out when loading
// from block devices in multiples other than the block size....
// from block devices in multiples other than the block size.
// Also used when loading logical partitions.
struct TempMBR {
uint8_t code[440];
uint32_t diskSignature;
@@ -47,20 +51,6 @@ struct TempMBR {
uint16_t MBRSignature;
}; // struct TempMBR
// Extended Boot Record (EBR) data, used to hold one logical partition's
// data within an extended partition. Includes pointer to next record for
// in-memory linked-list access. This is similar to MBRData, but with a
// few tweaks....
struct EBRRecord {
uint8_t code[446]; // generally 0s (and we don't care if they aren't)
// First partition entry defines partition; second points to next
// entry in on-disk linked list; remaining two are unused. Note that
// addresses are relative to the extended partition, not to the disk
// as a whole.
struct MBRRecord partitions[4];
uint16_t MBRSignature;
}; // struct EBRRecord
// Possible states of the MBR
enum MBRValidity {invalid, gpt, hybrid, mbr};
@@ -70,45 +60,55 @@ protected:
uint8_t code[440];
uint32_t diskSignature;
uint16_t nulls;
struct MBRRecord partitions[4];
// MAX_MBR_PARTS defaults to 128. This array holds both the primary and
// the logical partitions, to simplify data retrieval for GPT conversions.
struct MBRRecord partitions[MAX_MBR_PARTS];
uint16_t MBRSignature;
// Above are basic MBR data; now add more stuff....
uint32_t blockSize; // block size (usually 512)
uint64_t diskSize; // size in blocks
uint64_t numHeads; // number of heads, in CHS scheme
uint64_t numSecspTrack; // number of sectors per track, in CHS scheme
char device[256];
// Now an array of partitions for the logicals, in array form (easier
// than a linked list, and good enough for the GPT itself, so....)
struct MBRRecord logicals[NUM_LOGICALS];
MBRValidity state;
struct MBRRecord* GetPartition(int i); // Return primary or logical partition
public:
MBRData(void);
MBRData(char* deviceFilename);
~MBRData(void);
// Pass EmptyMBR 1 to clear the boot loader code, 0 to leave it intact
void EmptyMBR(int clearBootloader = 1);
void SetDiskSize(uint64_t ds) {diskSize = ds;}
// File I/O functions...
int ReadMBRData(char* deviceFilename);
void ReadMBRData(int fd, int checkBlockSize = 1);
int ReadLogicalPart(int fd, uint32_t extendedStart, uint32_t diskOffset,
int partNum);
int WriteMBRData(void);
void WriteMBRData(int fd);
// ReadLogicalPart() returns last partition # read to logicals[] array,
// or -1 if there was a problem....
int ReadLogicalPart(int fd, uint32_t extendedStart, uint32_t diskOffset,
int partNum);
// Display data for user...
void DisplayMBRData(void);
void MakeProtectiveMBR(int clearBoot = 0);
MBRValidity GetValidity(void) {return state;}
void ShowValidity(void);
void ShowState(void);
// Functions that set or get disk metadata (size, CHS geometry, etc.)
void SetDiskSize(uint64_t ds) {diskSize = ds;}
MBRValidity GetValidity(void) {return state;}
void SetHybrid(void) {state = hybrid;} // Set hybrid flag
void SetCHSGeom(uint32_t h, uint32_t s);
int LBAtoCHS(uint64_t lba, uint8_t * chs); // Convert LBA to CHS
// Functions to create, delete, or change partitions
// Pass EmptyMBR 1 to clear the boot loader code, 0 to leave it intact
void EmptyMBR(int clearBootloader = 1);
void MakeProtectiveMBR(int clearBoot = 0);
void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07,
int bootable = 0);
int MakeBiggestPart(int i, int type); // Make partition filling most space
void DeletePartition(int i);
int DeleteByLocation(uint64_t start64, uint64_t length64);
void OptimizeEESize(void);
void SetHybrid(void) {state = hybrid;} // Set hybrid flag
// Functions to find information on free space....
uint32_t FindFirstAvailable(uint32_t start = 1);

View File

@@ -179,7 +179,7 @@ PartTypes::PartTypes(void) {
"EFI System"); // EFI System (parted marks Linux boot
// partitions like this)
AddType(0xEF01, UINT64_C(0x11d333e7024dee41), UINT64_C(0x9FF381C70800699d),
"MBR partition scheme"); // Whatever that is (from Wikipedia)
"MBR partition scheme"); // Used to nest an MBR table on a GPT disk
AddType(0xEF02, UINT64_C(0x6E6F644921686148), UINT64_C(0x4946456465654E74),
"BIOS boot partition"); //

View File

@@ -205,7 +205,9 @@ int GetBlockSize(int fd) {
result = SECTOR_SIZE;
// ENOTTY = inappropriate ioctl; probably being called on a disk image
// file, so don't display the warning message....
if (errno != ENOTTY) {
// 32-bit code returns EINVAL, I don't know why. I know I'm treading on
// thin ice here, but it should be OK in all but very weird cases....
if ((errno != ENOTTY) && (errno != EINVAL)) {
printf("\aError %d when determining sector size! Setting sector size to %d\n",
errno, SECTOR_SIZE);
} // if
@@ -434,8 +436,9 @@ void DiskSync(int fd) {
uint64_t disksize(int fd, int *err) {
long sz; // Do not delete; needed for Linux
long long b; // Do not delete; needed for Linux
uint64_t sectors = 0, bytes = 0; // size in sectors & bytes
struct stat st;
uint64_t sectors = 0; // size in sectors
off_t bytes = 0; // size in bytes
struct stat64 st;
// Note to self: I recall testing a simplified version of
// this code, similar to what's in the __APPLE__ block,
@@ -462,17 +465,6 @@ uint64_t disksize(int fd, int *err) {
sectors = (b >> 9);
} // if
// if (*err) {
// sz = 0;
// if (errno != EFBIG)
// return sz;
// }
// *err = ioctl(fd, BLKGETSIZE64, &b);
// if (*err || b == 0 || b == sz)
// sectors = sz;
// else
// sectors = (b >> 9);
#endif
#endif
@@ -480,7 +472,7 @@ uint64_t disksize(int fd, int *err) {
// so let's assume it's a regular file (a QEMU image, dd backup, or
// what have you) and see what stat() gives us....
if (sectors == 0) {
if (fstat(fd, &st) == 0) {
if (fstat64(fd, &st) == 0) {
bytes = (uint64_t) st.st_size;
if ((bytes % UINT64_C(512)) != 0)
fprintf(stderr, "Warning: File size is not a multiple of 512 bytes!"

View File

@@ -4,6 +4,10 @@
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#ifndef __GPTSUPPORT
#define __GPTSUPPORT
#if defined (__FreeBSD__) || defined (__APPLE__)
// Darwin (Mac OS) only: disk IOCTLs are different, and there is no lseek64
@@ -18,10 +22,10 @@
#include <linux/fs.h>
#endif
#include <string.h>
#ifndef __GPTSUPPORT
#define __GPTSUPPORT
#ifdef __FreeBSD__
#define fstat64 fstat
#define stat64 stat
#endif
// Set this as a default
#define SECTOR_SIZE UINT32_C(512)
@@ -39,7 +43,7 @@
// Number and size of GPT entries...
#define NUM_GPT_ENTRIES 128
#define GPT_SIZE 128
#define HEADER_SIZE 92
#define HEADER_SIZE UINT32_C(92)
#define GPT_RESERVED 420
#define NAME_SIZE 72