gptfdisk-1.0.5/0000775000175000017500000000000013622612343013600 5ustar rodsmithrodsmithgptfdisk-1.0.5/gdisk_test.sh0000775000175000017500000001776213622612343016314 0ustar rodsmithrodsmith#!/bin/bash # test gdisk and sgdisk by creating a dd file # Copyright (C) 2011 Guillaume Delacour # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # # Requires: coreutils (mktemp, dd) and 64M of disk space in /tmp (temp dd disk) # # This script test gdisk commands through the following scenario: # - Initialize a new GPT table # - Create a single Linux partition # - Change name of partition # - Change type of partition # - Backup to file the GPT table # - Delete the single partition # - Restore from backup file the GPT table # - Wipe the GPT table # TODO # Try to generate a wrong GPT table to detect problems (test --verify) # Create MBR partition table with fdisk and migrate it with gdisk GDISK_BIN=./gdisk SGDISK_BIN=./sgdisk OPT_CLEAR="o" OPT_NEW="n" OPT_CHANGE_NAME="c" OPT_CHANGE_TYPE="t" OPT_BACKUP="b" OPT_DELETE="d" OPT_ZAP="z" # temp disk for testing gdisk TEMP_DISK=$(mktemp) # 64 MiB TEMP_DISK_SIZE=65536 # the test partition to create TEST_PART_TYPE="8300" TEST_PART_DEFAULT_NAME="Linux filesystem" # newname for the partition TEST_PART_NEWNAME=$(tr -dc "[:alpha:]" < /dev/urandom | head -c 8) # and new type (swap for example) TEST_PART_NEWTYPE="8200" # GPT data backup to filename GPT_BACKUP_FILENAME=$(mktemp) # Pretty print string (Red if FAILED or green if SUCCESS) # $1: string to pretty print pretty_print() { if [ "$1" = "SUCCESS" ] then # green color="32" else # red color="31" fi printf "\033[0;${color}m**$1**\033[m $2\n" } # Verify that the partition exist and has the given type/name # $1: Partition type to verify (ex.: 8300) # $2: Partition name to verify (ex.: Linux filesystem) # $3: Text to print verify_part() { partition=$($GDISK_BIN -l $TEMP_DISK | tail -n 1) echo $partition | grep -q "$1[[:space:]]$2$" if [ $? -eq 0 ] then pretty_print "SUCCESS" "$3" else pretty_print "FAILED" "$3" exit 1 fi } ##################################### # Get GUID of disk ##################################### get_diskguid() { DISK_GUID=$($GDISK_BIN -l $TEMP_DISK | grep "^Disk identifier (GUID):" | awk '{print $4}') return $DISK_GUID } ##################################### # Create a new empty table ##################################### create_table() { case $1 in gdisk) $GDISK_BIN $TEMP_DISK << EOF $OPT_CLEAR Y w Y EOF ret=$? if [ $ret -ne 0 ] then pretty_print "FAILED" "gdisk return $ret when creating partition table" exit 1 fi ;; sgdisk) $SGDISK_BIN $TEMP_DISK -${OPT_CLEAR} ret=$? if [ $ret -ne 0 ] then pretty_print "FAILED" "sgdisk return $ret when creating partition table" exit 1 fi ;; esac # verify that the table is empty # only the columns should appear in the table verify_part "Code" "Name" "Create new empty GPT table" echo "" } ##################################### # First create a new partition ##################################### create_partition() { case $1 in gdisk) $GDISK_BIN $TEMP_DISK << EOF $OPT_NEW 1 $TEST_PART_TYPE w Y EOF ;; sgdisk) $SGDISK_BIN $TEMP_DISK -${OPT_NEW} 1 -${OPT_CHANGE_NAME} 1:"${TEST_PART_DEFAULT_NAME}" -${OPT_CHANGE_TYPE} 1:$TEST_PART_TYPE ;; esac verify_part "$TEST_PART_TYPE" "$TEST_PART_DEFAULT_NAME" "Create new partition" echo "" } ##################################### # Change name of partition ##################################### change_partition_name() { case $1 in gdisk) $GDISK_BIN $TEMP_DISK << EOF $OPT_CHANGE_NAME $TEST_PART_NEWNAME w Y EOF ;; sgdisk) $SGDISK_BIN $TEMP_DISK -${OPT_CHANGE_NAME} 1:${TEST_PART_NEWNAME} ;; esac verify_part "$TEST_PART_TYPE" "$TEST_PART_NEWNAME" "Change partition 1 name ($TEST_PART_DEFAULT_NAME -> $TEST_PART_NEWNAME)" echo "" } change_partition_type() { ##################################### # Change type of partition ##################################### case $1 in gdisk) $GDISK_BIN $TEMP_DISK << EOF $OPT_CHANGE_TYPE $TEST_PART_NEWTYPE w Y EOF ;; sgdisk) $SGDISK_BIN $TEMP_DISK -${OPT_CHANGE_TYPE} 1:${TEST_PART_NEWTYPE} ;; esac verify_part "$TEST_PART_NEWTYPE" "$TEST_PART_NEWNAME" "Change partition 1 type ($TEST_PART_TYPE -> $TEST_PART_NEWTYPE)" echo "" } ##################################### # Backup GPT data to file ##################################### backup_table() { case $1 in gdisk) $GDISK_BIN $TEMP_DISK << EOF $OPT_BACKUP $GPT_BACKUP_FILENAME q EOF echo "" ;; sgdisk) $SGDISK_BIN $TEMP_DISK -${OPT_BACKUP} ${GPT_BACKUP_FILENAME} ;; esac # if exist and not empty; we will test it after if [ -s $GPT_BACKUP_FILENAME ] then pretty_print "SUCCESS" "GPT data backuped sucessfully" else pretty_print "FAILED" "Unable to create GPT backup file !" exit 1 fi } ##################################### # Now, we can delete the partition ##################################### delete_partition() { case $1 in gdisk) $GDISK_BIN $TEMP_DISK << EOF $OPT_DELETE w Y EOF ;; sgdisk) $SGDISK_BIN $TEMP_DISK -${OPT_DELETE} 1 ;; esac # verify that the table is empty (just one partition): # only the columns should appear in the table verify_part "Code" "Name" "Delete partition 1" echo "" } ##################################### # Restore GPT table ##################################### restore_table() { $GDISK_BIN $TEMP_DISK << EOF r r l $GPT_BACKUP_FILENAME w Y EOF verify_part "$TEST_PART_NEWTYPE" "$TEST_PART_NEWNAME" "Restore the GPT backup" echo "" } ##################################### # Change UID of disk ##################################### change_disk_uid() { # get UID of disk before changing it GUID=get_diskguid case $1 in gdisk) $GDISK_BIN $TEMP_DISK << EOF x g R w Y EOF ;; sgdisk) $SGDISK_BIN $TEMP_DISK -U=R ;; esac # get GUID after change NEW_DISK_GUID=get_diskguid # compare them if [ "$DISK_GUID" != "$NEW_DISK_GUID" ] then pretty_print "SUCCESS" "GUID of disk has been sucessfully changed" else pretty_print "FAILED" "GUID of disk is the same as the previous one" exit 1 fi } ##################################### # Wipe GPT table ##################################### wipe_table() { case $1 in gdisk) $GDISK_BIN $TEMP_DISK << EOF x $OPT_ZAP Y Y EOF ;; sgdisk) $SGDISK_BIN $TEMP_DISK -${OPT_ZAP} esac # verify that the table is empty (just one partition): # only the columns should appear in the table verify_part "Code" "Name" "Wipe GPT table" echo "" } ##################################### # Test stdin EOF ##################################### eof_stdin() { $SGDISK_BIN $TEMP_DISK << EOF ^D EOF pretty_print "SUCCESS" "EOF successfully exit gdisk" } ################################### # Main ################################### # create a file to simulate a real device dd if=/dev/zero of=$TEMP_DISK bs=1024 count=$TEMP_DISK_SIZE > /dev/null 2>&1 if [ -s $TEMP_DISK ] then pretty_print "SUCCESS" "Temp disk sucessfully created" else pretty_print "FAILED" "Unable to create temp disk !" exit 1 fi # test gdisk and sgdisk for binary in gdisk sgdisk do echo "" printf "\033[0;34m**Testing $binary binary**\033[m\n" echo "" create_table "$binary" create_partition "$binary" change_partition_name "$binary" change_partition_type "$binary" backup_table "$binary" delete_partition "$binary" restore_table # only with gdisk change_disk_uid "$binary" wipe_table "$binary" eof_stdin # only with gdisk done # remove temp files rm -f $TEMP_DISK $GPT_BACKUP_FILENAME exit 0 gptfdisk-1.0.5/gpttext.h0000664000175000017500000000476313622612343015462 0ustar rodsmithrodsmith/* Implementation of GPTData class derivative with basic text-mode interaction Copyright (C) 2010-2018 Roderick W. Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __GPTDATATEXT_H #define __GPTDATATEXT_H #include "gpt.h" using namespace std; class GPTDataTextUI : public GPTData { protected: public: GPTDataTextUI(void); GPTDataTextUI(string filename); ~GPTDataTextUI(void); // This one needs to be explicitly defined, even though it does nothing new.... // const GPTPart & operator[](uint32_t partNum) {return GPTData::operator[](partNum);} // Extended (interactive) versions of some base-class functions WhichToUse UseWhichPartitions(void); int XFormDisklabel(void); // Request information from the user (& possibly do something with it) uint32_t GetPartNum(void); void ResizePartitionTable(void); void MoveMainTable(void); void CreatePartition(void); void DeletePartition(void); void ChangePartType(void); void ChangeUniqueGuid(void); void SetAttributes(uint32_t partNum); int SetName(uint32_t partNum); int SwapPartitions(void); int DestroyGPTwPrompt(void); // Returns 1 if user proceeds void ShowDetails(void); void MakeHybrid(void); int XFormToMBR(void); // convert GPT to MBR, wiping GPT afterwards. Returns 1 if successful // An informational function.... void WarnAboutIffyMBRPart(int partNum); // Main menu functions void MainMenu(string filename); void ShowCommands(void); void ExpertsMenu(string filename); void ShowExpertCommands(void); void RecoveryMenu(string filename); void ShowRecoveryCommands(void); }; // class GPTDataTextUI int GetMBRTypeCode(int defType); UnicodeString ReadUString(void); #endif // __GPTDATATEXT_H gptfdisk-1.0.5/sgdisk.html0000644000175000017500000007264413622612343015765 0ustar rodsmithrodsmithContent-type: text/html; charset=UTF-8 Man page of SGDISK

SGDISK

Section: GPT fdisk Manual (8)
Updated: 1.0.5
Index Return to Main Contents
 

NAME

sgdisk - Command-line GUID partition table (GPT) manipulator for Linux and Unix  

SYNOPSIS

sgdisk [ options ] device

 

DESCRIPTION

GPT fdisk is a text-mode menu-driven package for creation and manipulation of partition tables. It consists of two programs: the text-mode interactive gdisk and the command-line sgdisk. Either program 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 GUID partition table. This man page documents the command-line sgdisk program.

Some advanced data manipulation and 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. For information on MBR vs. GPT, as well as GPT terminology and structure, see the extended gdisk documentation at http://www.rodsbooks.com/gdisk/ or consult Wikipedia.

The sgdisk program employs a user interface that's based entirely on the command line, making it suitable for use in scripts or by experts who want to make one or two quick changes to a disk. (The program may query the user when certain errors are encountered, though.) The program's name is based on sfdisk, but the user options of the two programs are entirely different from one another.

Ordinarily, sgdisk operates on disk device files, such as /dev/sda or /dev/hda under Linux, /dev/disk0 under Mac OS X, or /dev/ad0 or /dev/da0 under FreeBSD. The program can also operate on disk image files, which can be either copies of whole disks (made with dd, for instance) or raw disk images used by emulators such as QEMU or VMWare. Note that only raw disk images are supported; sgdisk 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 exclusively. Thus, GPT data structures, and therefore sgdisk, do not need to deal with CHS geometries and all the problems they create.

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 gdisk, sgdisk, or GNU Parted programs.

Upon start, sgdisk attempts to identify the partition type in use on the disk. If it finds valid GPT data, sgdisk will use it. If sgdisk 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. If you specify any option that results in changes to an MBR or BSD disklabel, sgdisk ignores those changes unless the -g (--mbrtogpt), -z (--zap), or -Z (--zap-all) option is used. If you use the -g option, sgdisk replaces the MBR or disklabel with a GPT. This action is potentially dangerous! 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.

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 (--sort) option, if you like. (Doing this may require you to update your /etc/fstab file.)

When creating a fresh partition table, certain considerations may be in order:

*
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.

*
Boot disks for EFI-based systems require an EFI System Partition (gdisk internal code 0xEF00) formatted as FAT-32. I recommended making this partition 550 MiB. (Smaller ESPs are common, but some EFIs have flaky FAT drivers that necessitate a larger partition for reliable operation.) Boot-related files are stored here. (Note that GNU Parted identifies such partitions as having the "boot flag" set.)

*
Some boot loaders for BIOS-based systems make use of a BIOS Boot Partition (gdisk internal code 0xEF02), in which the secondary boot loader is stored, possibly without the benefit of a filesystem. (GRUB2 may optionally use such a partition.) This partition can typically be quite small (roughly 32 to 200 KiB, although 1 MiB is more common in practice), but you should consult your boot loader documentation for details.

*
If Windows is to boot from a GPT disk, a partition of type Microsoft Reserved (sgdisk 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 FAT partitions as this type, which actually makes the partition unusable for normal file storage in both Windows and Mac OS X.)

*
Some OSes' GPT utilities create some blank space (typically 128 MiB) after each partition. The intent is to enable future disk utilities to use this space. Such free space is not required of GPT disks, but creating it may help in future disk maintenance.

 

OPTIONS

Some options take no arguments, others take one argument (typically a partition number), and others take compound arguments with colon delimitation. For instance, -n (--new) takes a partition number, a starting sector number, and an ending sector number, as in sgdisk -n 2:2000:50000 /dev/sdc, which creates a new partition, numbered 2, starting at sector 2000 an ending at sector 50,000, on /dev/sdc.

Unrelated options may be combined; however, some such combinations will be nonsense (such as deleting a partition and then changing its GUID type code). sgdisk interprets options in the order in which they're entered, so effects can vary depending on order. For instance, sgdisk -s -d 2 sorts the partition table entries and then deletes partition 2 from the newly-sorted list; but sgdisk -d 2 -s deletes the original partition 2 and then sorts the modified partition table.

Error checking and opportunities to correct mistakes in sgdisk are minimal. Although the program endeavors to keep the GPT data structures legal, it does not prompt for verification before performing its actions. Unless you require a command-line-driven program, you should use the interactive gdisk instead of sgdisk, since gdisk allows you to quit without saving your changes, should you make a mistake.

Although sgdisk is based on the same partition-manipulation code as gdisk, sgdisk implements fewer features than its interactive sibling. Options available in sgdisk are:

-a, --set-alignment=value
Set the sector alignment multiple. GPT fdisk aligns the start of partitions to sectors that are multiples of this value, which defaults to 1 MiB (2048 on disks with 512-byte sectors) on freshly formatted disks. This alignment value is necessary to obtain optimum performance with Western Digital Advanced Format and similar drives with larger physical than logical sector sizes, with some types of RAID arrays, and with SSD devices.

-A, --attributes=list|[partnum:show|or|nand|xor|=|set|clear|toggle|get[:bitnum|hexbitmask]]
View or set partition attributes. Use list to see defined (known) attribute values. Omit the partition number (and even the device filename) when using this option. The others require a partition number. The show and get options show the current attribute settings (all attributes or for a particular bit, respectively). The or, nand, xor, =, set, clear, and toggle options enable you to change the attribute bit value. The set, clear, toggle, and get options work on a bit number; the others work on a hexadecimal bit mask. For example, type sgdisk -A 4:set:2 /dev/sdc to set the bit 2 attribute (legacy BIOS bootable) on partition 4 on /dev/sdc.

-b, --backup=file
Save partition data to a backup file. You can back up your current in-memory 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 backup is of the current in-memory data structures, so if you launch the program, make changes, and then use this option, the backup will reflect your changes. If the GPT data structures are damaged, the backup may not accurately reflect the damaged state; instead, they will reflect GPT fdisk's first-pass interpretation of the GPT.

-c, --change-name=partnum:name
Change the GPT name of a partition. This name is encoded as a UTF-16 string, but proper entry and display of anything beyond basic ASCII values requires suitable locale and font support. For the most part, Linux ignores the partition name, but it may be important in some OSes. If you want to set a name that includes a space, enclose it in quotation marks, as in sgdisk -c 1:"Sample Name" /dev/sdb. Note that the GPT name of a partition is distinct from the filesystem name, which is encoded in the filesystem's data structures.

-C, --recompute-chs
Recompute CHS values in protective or hybrid MBR. This option can sometimes help if a disk utility, OS, or BIOS doesn't like the CHS values used by the partitions in the protective or hybrid MBR. In particular, the GPT specification requires a CHS value of 0xFFFFFF for over-8GiB partitions, but this value is technically illegal by the usual standards. Some BIOSes hang if they encounter this value. This option will recompute a more normal CHS value -- 0xFEFFFF for over-8GiB partitions, enabling these BIOSes to boot.

-d, --delete=partnum
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, gdisk deletes it, as well, and expands any adjacent 0xEE (EFI GPT) MBR protective partition to fill the new free space.

-D, --display-alignment
Display current sector alignment value. Partitions will be created on multiples of the sector value reported by this option. You can change the alignment value with the -a option.

-e, --move-second-header
Move backup GPT data structures to the end of the disk. Use this option if you've added disks to a RAID array, thus creating a virtual disk with space that follows the backup GPT data structures. This command moves the backup GPT data structures to the end of the disk, where they belong.

-E, --end-of-largest
Displays the sector number of the end of the largest available block of sectors on the disk. A script may store this value and pass it back as part of -n's option to create a partition. If no unallocated sectors are available, this function returns the value 0.

-f, --first-in-largest
Displays the sector number of the start of the largest available block of sectors on the disk. A script may store this value and pass it back as part of -n's option to create a partition. If no unallocated sectors are available, this function returns the value 0. Note that this parameter is blind to partition alignment; when you actually create a partition, its start point might be changed from this value.

-F, --first-aligned-in-largest
Similar to -f (--first-in-largest), except returns the sector number with the current alignment correction applied. Use this function if you need to compute the actual partition start point rather than a theoretical start point or the actual start point if you set the alignment value to 1.

-g, --mbrtogpt
Convert an MBR or BSD disklabel disk to a GPT disk. As a safety measure, use of this option is required on MBR or BSD disklabel disks if you intend to save your changes, in order to prevent accidentally damaging such disks.

-G, --randomize-guids
Randomize the disk's GUID and all partitions' unique GUIDs (but not their partition type code GUIDs). This function may be used after cloning a disk in order to render all GUIDs once again unique.

-h, --hybrid
Create a hybrid MBR. This option takes from one to three partition numbers, separated by colons, as arguments. You may optionally specify a final partition "EE" to indicate that the EFI GPT (type 0xEE) should be placed last in the table, otherwise it will be placed first, followed by the partition(s) you specify. Their type codes are based on the GPT fdisk type codes divided by 0x0100, which is usually correct for Windows partitions. If the active/bootable flag should be set, you must do so in another program, such as fdisk. The gdisk program offers additional hybrid MBR creation options.

-i, --info=partnum
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 sgdisk's internal partition type code to a plain type name. The -i option displays this information for a single partition.

-j, --adjust-main-table=sector
Adjust the location of the main partition table. This value is normally 2, but it may need to be increased in some cases, such as when a system-on-chip (SoC) is hard-coded to read boot code from sector 2. I recommend against adjusting this value unless doing so is absolutely necessary.

-l, --load-backup=file
Load partition data from a backup file. This option is the reverse of the -b option. Note that restoring partition data from anything but the original disk is not recommended. This option will work even if the disk's original partition table is bad; however, most other options on the same command line will be ignored.

-L, --list-types
Display a summary of partition types. GPT uses a GUID to identify partition types for particular OSes and purposes. For ease of data entry, sgdisk 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 gdisk. A one-to-one correspondence is impossible, though. Most notably, the codes for all varieties of FAT and NTFS partition correspond to a single GPT code (entered as 0x0700 in sgdisk). Some OSes use a single MBR code but employ many more codes in GPT. For these, sgdisk 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 gdisk and sgdisk. This option does not require you to specify a valid disk device filename.

-m, --gpttombr
Convert disk from GPT to MBR form. This option takes from one to four partition numbers, separated by colons, as arguments. Their type codes are based on the GPT fdisk type codes divided by 0x0100. If the active/bootable flag should be set, you must do so in another program, such as fdisk. The gdisk program offers additional MBR conversion options. It is not possible to convert more than four partitions from GPT to MBR form or to convert partitions that start above the 2TiB mark or that are larger than 2TiB.

-n, --new=partnum:start:end
Create a new partition. 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 kibibytes (K), mebibytes (M), gibibytes (G), tebibytes (T), or pebibytes (P); for instance, 40M specifies a position 40MiB from the start of the disk. You can specify locations relative to the start or end of the specified default range by preceding the number by a '+' or '-' symbol, as in +2G to specify a point 2GiB after the default start sector, or -200M to specify a point 200MiB before the last available sector. A start or end value of 0 specifies the default value, which is the start of the largest available block for the start sector and the end of the same block for the end sector. A partnum value of 0 causes the program to use the first available partition number. Subsequent uses of the -A, -c, -t, and -u options may also use 0 to refer to the same partition.

-N, --largest-new=num
Create a new partition that fills the largest available block of space on the disk. You can use the -a (--set-alignment) option to adjust the alignment, if desired. A num value of 0 causes the program to use the first available partition number.

-o, --clear
Clear out all partition data. This includes GPT header data, all partition definitions, and the protective MBR. Note that this operation will, like most other operations, fail on a damaged disk. If you want to prepare a disk you know to be damaged for GPT use, you should first wipe it with -Z and then partition it normally. This option will work even if the disk's original partition table is bad; however, most other options on the same command line will be ignored.

-O, --print-mbr
Display basic MBR partition summary data. This includes partition numbers, starting and ending sector numbers, partition sizes, MBR partition types codes, and partition names. This option is useful mainly for diagnosing partition table problems, particularly on disks with hybrid MBRs.

-p, --print
Display basic GPT partition summary data. This includes partition numbers, starting and ending sector numbers, partition sizes, sgdisk's partition types codes, and partition names. For additional information, use the -i (--info) option.

-P, --pretend
Pretend to make specified changes. In-memory GPT data structures are altered according to other parameters, but changes are not written to disk.

-r, --transpose
Swap two partitions' entries in the partition table. One or both partitions may be empty, although swapping two empty partitions is pointless. For instance, if partitions 1-4 are defined, transposing 1 and 5 results in a table with partitions numbered from 2-5. Transposing partitions in this way has no effect on their disk space allocation; it only alters their order in the partition table.

-R, --replicate=second_device_filename
Replicate the main device's partition table on the specified second device. Note that the replicated partition table is an exact copy, including all GUIDs; if the device should have its own unique GUIDs, you should use the -G option on the new disk.

-s, --sort
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 sort partitions whenever they make changes. Such changes will be reflected in your device filenames, so you may need to edit /etc/fstab if you use this option.

-t, --typecode=partnum:{hexcode|GUID}
Change a single partition's type code. You enter the type code using either a two-byte hexadecimal number, as described earlier, or a fully-specified GUID value, such as EBD0A0A2-B9E5-4433-87C0-68B6B72699C7.

-T, --transform-bsd=partnum
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. sgdisk 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 sgdisk being unable to convert a BSD disklabel is high compared to the likelihood of problems with an MBR conversion.

-u, --partition-guid=partnum:guid
Set the partition unique GUID for an individual partition. The GUID may be a complete GUID or 'R' to set a random GUID.

-U, --disk-guid=guid
Set the GUID for the disk. The GUID may be a complete GUID or 'R' to set a random GUID.

--usage
Print a brief summary of available options.

-v, --verify
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 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. This option will work even if the disk's original partition table is bad; however, most other options on the same command line will be ignored.

-V, --version
Display program version information. This option may be used without specifying a device filename.

-z, --zap
Zap (destroy) the GPT data structures and then exit. Use this option if you want to repartition a GPT disk using fdisk or some other GPT-unaware program. This option destroys only the GPT data structures; it leaves the MBR intact. This makes it useful for wiping out GPT data structures after a disk has been repartitioned for MBR using a GPT-unaware utility; however, there's a risk that it will damage boot loaders or even the start of the first or end of the last MBR partition. If you use it on a valid GPT disk, the MBR will be left with an inappropriate EFI GPT (0xEE) partition definition, which you can delete using another utility.

-Z, --zap-all
Zap (destroy) the GPT and MBR data structures and then exit. This option works much like -z, but as it wipes the MBR as well as the GPT, it's more suitable if you want to repartition a disk after using this option, and completely unsuitable if you've already repartitioned the disk.

-?, --help
Print a summary of options.

 

RETURN VALUES

sgdisk returns various values depending on its success or failure:

0
Normal program execution

1
Too few arguments

2
An error occurred while reading the partition table

3
Non-GPT disk detected and no -g option, but operation requires a write action

4
An error prevented saving changes

5
An error occurred while reading standard input (should never occur with sgdisk, but may with gdisk)

8
Disk replication operation (-R) failed

 

BUGS

Known bugs and limitations include:

*
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 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 gpt, fdisk, and dd.) This limitation can be overcome by typing sysctl kern.geom.debugflags=16 at a shell prompt.

*
The fields used to display the start and end sector numbers for partitions in the -p option are 14 characters wide. This translates to a limitation of about 45 PiB. On larger disks, the displayed columns will go out of alignment.

*
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 #define MAX_MBR_PARTS line in the basicmbr.h source code file and recompiling; however, such a change will require using a larger-than-normal partition table. (The limit of 128 partitions was chosen because that number equals the 128 partitions supported by the most common partition table size.)

*
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 prior to conversion with gdisk.

*
MBR conversions work only if the disk has correct LBA partition descriptors. These descriptors should be present on any disk over 8 GiB in size or on smaller disks partitioned with any but very ancient software.

*
BSD disklabel support can create first and/or last partitions that overlap with the GPT data structures. This can sometimes be compensated by adjusting the partition table size, but in extreme cases the affected partition(s) may need to be deleted.

*
Because of the highly variable nature of BSD disklabel structures, 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!

*
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 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 recovery & transformation menu) or abandoning GPT in favor of MBR may be your only options in this case.

 

AUTHORS

Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)

Contributors:

* Yves Blusseau (1otnwmz02@sneakemail.com)

* David Hubbard (david.c.hubbard@gmail.com)

* Justin Maggard (justin.maggard@netgear.com)

* Dwight Schauer (dschauer@gmail.com)

* Florian Zumbiehl (florz@florz.de)

 

SEE ALSO

cfdisk(8), cgdisk(8), fdisk(8), gdisk(8), mkfs(8), parted(8), sfdisk(8), fixparts(8).

http://en.wikipedia.org/wiki/GUID_Partition_Table

http://developer.apple.com/technotes/tn2006/tn2166.html

http://www.rodsbooks.com/gdisk/

 

AVAILABILITY

The sgdisk command is part of the GPT fdisk package and is available from Rod Smith.


 

Index

NAME
SYNOPSIS
DESCRIPTION
OPTIONS
RETURN VALUES
BUGS
AUTHORS
SEE ALSO
AVAILABILITY

This document was created by man2html, using the manual pages.
Time: 22:34:11 GMT, February 17, 2020 gptfdisk-1.0.5/gptcl.cc0000664000175000017500000005722513622612343015233 0ustar rodsmithrodsmith/* Implementation of GPTData class derivative with popt-based command line processing Copyright (C) 2010-2014 Roderick W. Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include "gptcl.h" GPTDataCL::GPTDataCL(void) { attributeOperation = backupFile = partName = hybrids = newPartInfo = NULL; mbrParts = twoParts = outDevice = typeCode = partGUID = diskGUID = NULL; alignment = DEFAULT_ALIGNMENT; deletePartNum = infoPartNum = largestPartNum = bsdPartNum = 0; tableSize = GPT_SIZE; } // GPTDataCL constructor GPTDataCL::GPTDataCL(string filename) { } // GPTDataCL constructor with filename GPTDataCL::~GPTDataCL(void) { } // GPTDataCL destructor void GPTDataCL::LoadBackupFile(string backupFile, int &saveData, int &neverSaveData) { if (LoadGPTBackup(backupFile) == 1) { JustLooking(0); saveData = 1; } else { saveData = 0; neverSaveData = 1; cerr << "Error loading backup file!\n"; } // else } // GPTDataCL::LoadBackupFile() // Perform the actions specified on the command line. This is necessarily one // monster of a function! // Returns values: // 0 = success // 1 = too few arguments // 2 = error when reading partition table // 3 = non-GPT disk and no -g option // 4 = unable to save changes // 8 = disk replication operation (-R) failed int GPTDataCL::DoOptions(int argc, char* argv[]) { GPTData secondDevice; int opt, numOptions = 0, saveData = 0, neverSaveData = 0; int partNum = 0, newPartNum = -1, saveNonGPT = 1, retval = 0, pretend = 0; uint64_t low, high, startSector, endSector, sSize, mainTableLBA; uint64_t temp; // temporary variable; free to use in any case char *device; string cmd, typeGUID, name; PartType typeHelper; struct poptOption theOptions[] = { {"attributes", 'A', POPT_ARG_STRING, &attributeOperation, 'A', "operate on partition attributes", "list|[partnum:show|or|nand|xor|=|set|clear|toggle|get[:bitnum|hexbitmask]]"}, {"set-alignment", 'a', POPT_ARG_INT, &alignment, 'a', "set sector alignment", "value"}, {"backup", 'b', POPT_ARG_STRING, &backupFile, 'b', "backup GPT to file", "file"}, {"change-name", 'c', POPT_ARG_STRING, &partName, 'c', "change partition's name", "partnum:name"}, {"recompute-chs", 'C', POPT_ARG_NONE, NULL, 'C', "recompute CHS values in protective/hybrid MBR", ""}, {"delete", 'd', POPT_ARG_INT, &deletePartNum, 'd', "delete a partition", "partnum"}, {"display-alignment", 'D', POPT_ARG_NONE, NULL, 'D', "show number of sectors per allocation block", ""}, {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second header to end of disk", ""}, {"end-of-largest", 'E', POPT_ARG_NONE, NULL, 'E', "show end of largest free block", ""}, {"first-in-largest", 'f', POPT_ARG_NONE, NULL, 'f', "show start of the largest free block", ""}, {"first-aligned-in-largest", 'F', POPT_ARG_NONE, NULL, 'F', "show start of the largest free block, aligned", ""}, {"mbrtogpt", 'g', POPT_ARG_NONE, NULL, 'g', "convert MBR to GPT", ""}, {"randomize-guids", 'G', POPT_ARG_NONE, NULL, 'G', "randomize disk and partition GUIDs", ""}, {"hybrid", 'h', POPT_ARG_STRING, &hybrids, 'h', "create hybrid MBR", "partnum[:partnum...][:EE]"}, {"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"}, {"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "adjust the location of the main partition table", "sector"}, {"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"}, {"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""}, {"gpttombr", 'm', POPT_ARG_STRING, &mbrParts, 'm', "convert GPT to MBR", "partnum[:partnum...]"}, {"new", 'n', POPT_ARG_STRING, &newPartInfo, 'n', "create new partition", "partnum:start:end"}, {"largest-new", 'N', POPT_ARG_INT, &largestPartNum, 'N', "create largest possible new partition", "partnum"}, {"clear", 'o', POPT_ARG_NONE, NULL, 'o', "clear partition table", ""}, {"print-mbr", 'O', POPT_ARG_NONE, NULL, 'O', "print MBR partition table", ""}, {"print", 'p', POPT_ARG_NONE, NULL, 'p', "print partition table", ""}, {"pretend", 'P', POPT_ARG_NONE, NULL, 'P', "make changes in memory, but don't write them", ""}, {"transpose", 'r', POPT_ARG_STRING, &twoParts, 'r', "transpose two partitions", "partnum:partnum"}, {"replicate", 'R', POPT_ARG_STRING, &outDevice, 'R', "replicate partition table", "device_filename"}, {"sort", 's', POPT_ARG_NONE, NULL, 's', "sort partition table entries", ""}, {"resize-table", 'S', POPT_ARG_INT, &tableSize, 'S', "resize partition table", "numparts"}, {"typecode", 't', POPT_ARG_STRING, &typeCode, 't', "change partition type code", "partnum:{hexcode|GUID}"}, {"transform-bsd", 'T', POPT_ARG_INT, &bsdPartNum, 'T', "transform BSD disklabel partition to GPT", "partnum"}, {"partition-guid", 'u', POPT_ARG_STRING, &partGUID, 'u', "set partition GUID", "partnum:guid"}, {"disk-guid", 'U', POPT_ARG_STRING, &diskGUID, 'U', "set disk GUID", "guid"}, {"verify", 'v', POPT_ARG_NONE, NULL, 'v', "check partition table integrity", ""}, {"version", 'V', POPT_ARG_NONE, NULL, 'V', "display version information", ""}, {"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT (but not MBR) data structures", ""}, {"zap-all", 'Z', POPT_ARG_NONE, NULL, 'Z', "zap (destroy) GPT and MBR data structures", ""}, POPT_AUTOHELP { NULL, 0, 0, NULL, 0 } }; // Create popt context... poptCon = poptGetContext(NULL, argc, (const char**) argv, theOptions, 0); poptSetOtherOptionHelp(poptCon, " [OPTION...] "); if (argc < 2) { poptPrintUsage(poptCon, stderr, 0); return 1; } // Do one loop through the options to find the device filename and deal // with options that don't require a device filename, to flag destructive // (o, z, or Z) options, and to flag presence of a --pretend/-P option while ((opt = poptGetNextOpt(poptCon)) > 0) { switch (opt) { case 'A': cmd = GetString(attributeOperation, 1); if (cmd == "list") Attributes::ListAttributes(); break; case 'L': typeHelper.ShowAllTypes(0); break; case 'P': pretend = 1; break; case 'V': cout << "GPT fdisk (sgdisk) version " << GPTFDISK_VERSION << "\n\n"; break; default: break; } // switch numOptions++; } // while // Assume first non-option argument is the device filename.... device = (char*) poptGetArg(poptCon); poptResetContext(poptCon); if (device != NULL) { JustLooking(); // reset as necessary BeQuiet(); // Tell called functions to be less verbose & interactive if (LoadPartitions((string) device)) { if ((WhichWasUsed() == use_mbr) || (WhichWasUsed() == use_bsd)) saveNonGPT = 0; // flag so we don't overwrite unless directed to do so sSize = GetBlockSize(); while ((opt = poptGetNextOpt(poptCon)) > 0) { switch (opt) { case 'A': { if (cmd != "list") { partNum = (int) GetInt(attributeOperation, 1) - 1; if (partNum < 0) partNum = newPartNum; if ((partNum >= 0) && (partNum < (int) GetNumParts())) { switch (ManageAttributes(partNum, GetString(attributeOperation, 2), GetString(attributeOperation, 3))) { case -1: saveData = 0; neverSaveData = 1; break; case 1: JustLooking(0); saveData = 1; break; default: break; } // switch } else { cerr << "Error: Invalid partition number " << partNum + 1 << "\n"; saveData = 0; neverSaveData = 1; } // if/else reasonable partition # } // if (cmd != "list") break; } // case 'A': case 'a': SetAlignment(alignment); break; case 'b': SaveGPTBackup(backupFile); free(backupFile); break; case 'c': cout << "Setting name!\n"; JustLooking(0); partNum = (int) GetInt(partName, 1) - 1; if (partNum < 0) partNum = newPartNum; cout << "partNum is " << partNum << "\n"; if ((partNum >= 0) && (partNum < (int) GetNumParts())) { name = GetString(partName, 2); if (SetName(partNum, (UnicodeString) name.c_str())) { saveData = 1; } else { cerr << "Unable to set partition " << partNum + 1 << "'s name to '" << GetString(partName, 2) << "'!\n"; neverSaveData = 1; } // if/else free(partName); } break; case 'C': JustLooking(0); RecomputeCHS(); saveData = 1; break; case 'd': JustLooking(0); if (DeletePartition(deletePartNum - 1) == 0) { cerr << "Error " << errno << " deleting partition!\n"; neverSaveData = 1; } else saveData = 1; break; case 'D': cout << GetAlignment() << "\n"; break; case 'e': JustLooking(0); MoveSecondHeaderToEnd(); saveData = 1; break; case 'E': cout << FindLastInFree(FindFirstInLargest()) << "\n"; break; case 'f': cout << FindFirstInLargest() << "\n"; break; case 'F': temp = FindFirstInLargest(); Align(&temp); cout << temp << "\n"; break; case 'g': JustLooking(0); saveData = 1; saveNonGPT = 1; break; case 'G': JustLooking(0); saveData = 1; RandomizeGUIDs(); break; case 'h': JustLooking(0); if (BuildMBR(hybrids, 1) == 1) saveData = 1; break; case 'i': ShowPartDetails(infoPartNum - 1); break; case 'j': if (MoveMainTable(mainTableLBA)) { JustLooking(0); saveData = 1; } else { neverSaveData = 1; } // if/else break; case 'l': LoadBackupFile(backupFile, saveData, neverSaveData); free(backupFile); break; case 'L': break; case 'm': JustLooking(0); if (BuildMBR(mbrParts, 0) == 1) { if (!pretend) { if (SaveMBR()) { DestroyGPT(); } else cerr << "Problem saving MBR!\n"; } // if saveNonGPT = 0; pretend = 1; // Not really, but works around problem if -g is used with this... saveData = 0; } // if break; case 'n': JustLooking(0); newPartNum = (int) GetInt(newPartInfo, 1) - 1; if (newPartNum < 0) newPartNum = FindFirstFreePart(); low = FindFirstInLargest(); Align(&low); high = FindLastInFree(low); startSector = IeeeToInt(GetString(newPartInfo, 2), sSize, low, high, low); endSector = IeeeToInt(GetString(newPartInfo, 3), sSize, startSector, high, high); if (CreatePartition(newPartNum, startSector, endSector)) { saveData = 1; } else { cerr << "Could not create partition " << newPartNum + 1 << " from " << startSector << " to " << endSector << "\n"; neverSaveData = 1; } // if/else free(newPartInfo); break; case 'N': JustLooking(0); startSector = FindFirstInLargest(); Align(&startSector); endSector = FindLastInFree(startSector); if (largestPartNum <= 0) largestPartNum = FindFirstFreePart() + 1; if (CreatePartition(largestPartNum - 1, startSector, endSector)) { saveData = 1; } else { cerr << "Could not create partition " << largestPartNum << " from " << startSector << " to " << endSector << "\n"; neverSaveData = 1; } // if/else break; case 'o': JustLooking(0); ClearGPTData(); saveData = 1; break; case 'O': DisplayMBRData(); break; case 'p': DisplayGPTData(); break; case 'P': pretend = 1; break; case 'r': JustLooking(0); uint64_t p1, p2; p1 = GetInt(twoParts, 1) - 1; p2 = GetInt(twoParts, 2) - 1; if (SwapPartitions((uint32_t) p1, (uint32_t) p2) == 0) { neverSaveData = 1; cerr << "Cannot swap partitions " << p1 + 1 << " and " << p2 + 1 << "\n"; } else saveData = 1; break; case 'R': secondDevice = *this; secondDevice.SetDisk(outDevice); secondDevice.JustLooking(0); if (!secondDevice.SaveGPTData(1)) retval = 8; break; case 's': JustLooking(0); SortGPT(); saveData = 1; break; case 'S': JustLooking(0); if (SetGPTSize(tableSize) == 0) neverSaveData = 1; else saveData = 1; break; case 't': JustLooking(0); partNum = (int) GetInt(typeCode, 1) - 1; if (partNum < 0) partNum = newPartNum; if ((partNum >= 0) && (partNum < (int) GetNumParts())) { typeHelper = GetString(typeCode, 2); if ((typeHelper != PartType::unusedPartType) && (ChangePartType(partNum, typeHelper))) { saveData = 1; } else { cerr << "Could not change partition " << partNum + 1 << "'s type code to " << GetString(typeCode, 2) << "!\n"; neverSaveData = 1; } // if/else free(typeCode); } break; case 'T': JustLooking(0); XFormDisklabel(bsdPartNum - 1); saveData = 1; break; case 'u': JustLooking(0); saveData = 1; partNum = (int) GetInt(partGUID, 1) - 1; if (partNum < 0) partNum = newPartNum; if ((partNum >= 0) && (partNum < (int) GetNumParts())) { SetPartitionGUID(partNum, GetString(partGUID, 2).c_str()); } break; case 'U': JustLooking(0); saveData = 1; SetDiskGUID(diskGUID); break; case 'v': Verify(); break; case 'z': if (!pretend) { DestroyGPT(); } // if saveNonGPT = 1; saveData = 0; break; case 'Z': if (!pretend) { DestroyGPT(); DestroyMBR(); } // if saveNonGPT = 1; saveData = 0; break; default: cerr << "Unknown option (-" << opt << ")!\n"; break; } // switch } // while } else { // if loaded OK poptResetContext(poptCon); // Do a few types of operations even if there are problems.... while ((opt = poptGetNextOpt(poptCon)) > 0) { switch (opt) { case 'l': LoadBackupFile(backupFile, saveData, neverSaveData); cout << "Information: Loading backup partition table; will override earlier problems!\n"; free(backupFile); retval = 0; break; case 'o': JustLooking(0); ClearGPTData(); saveData = 1; cout << "Information: Creating fresh partition table; will override earlier problems!\n"; retval = 0; break; case 'v': cout << "Verification may miss some problems or report too many!\n"; Verify(); break; case 'z': if (!pretend) { DestroyGPT(); } // if saveNonGPT = 1; saveData = 0; break; case 'Z': if (!pretend) { DestroyGPT(); DestroyMBR(); } // if saveNonGPT = 1; saveData = 0; break; } // switch } // while retval = 2; } // if/else loaded OK if ((saveData) && (!neverSaveData) && (saveNonGPT) && (!pretend)) { if (!SaveGPTData(1)) retval = 4; } if (saveData && (!saveNonGPT)) { cout << "Non-GPT disk; not saving changes. Use -g to override.\n"; retval = 3; } // if if (neverSaveData) { cerr << "Error encountered; not saving changes.\n"; retval = 4; } // if } // if (device != NULL) poptFreeContext(poptCon); return retval; } // GPTDataCL::DoOptions() // Create a hybrid or regular MBR from GPT data structures int GPTDataCL::BuildMBR(char* argument, int isHybrid) { int numParts, allOK = 1, i, origPartNum; int eeLast, mbrNum = 0; MBRPart newPart; BasicMBRData newMBR; if (argument != NULL) { numParts = CountColons(argument) + 1; if (isHybrid) { eeLast = GetString(argument, numParts) == "EE"; if (eeLast) { numParts--; } } if (numParts <= (4 - isHybrid)) { newMBR.SetDisk(GetDisk()); for (i = 0; i < numParts; i++) { origPartNum = GetInt(argument, i + 1) - 1; if (IsUsedPartNum(origPartNum) && (partitions[origPartNum].IsSizedForMBR() == MBR_SIZED_GOOD)) { mbrNum = i + (isHybrid && ! eeLast); newPart.SetInclusion(PRIMARY); newPart.SetLocation(operator[](origPartNum).GetFirstLBA(), operator[](origPartNum).GetLengthLBA()); newPart.SetStatus(0); newPart.SetType((uint8_t)(operator[](origPartNum).GetHexType() / 0x0100)); newMBR.AddPart(mbrNum, newPart); } else { cerr << "Original partition " << origPartNum + 1 << " does not exist or is too big! Aborting operation!\n"; allOK = 0; } // if/else } // for if (isHybrid) { if (eeLast) { mbrNum = i; } else { mbrNum = 0; } newPart.SetInclusion(PRIMARY); newPart.SetLocation(1, newMBR.FindLastInFree(1)); newPart.SetStatus(0); newPart.SetType(0xEE); newMBR.AddPart(mbrNum, newPart); } // if if (allOK) SetProtectiveMBR(newMBR); } else allOK = 0; } else allOK = 0; if (!allOK) cerr << "Problem creating MBR!\n"; return allOK; } // GPTDataCL::BuildMBR() // Returns the number of colons in argument string, ignoring the // first character (thus, a leading colon is ignored, as GetString() // does). int CountColons(char* argument) { int num = 0; while ((argument[0] != '\0') && (argument = strchr(&argument[1], ':'))) num++; return num; } // GPTDataCL::CountColons() // Extract integer data from argument string, which should be colon-delimited uint64_t GetInt(const string & argument, int itemNum) { uint64_t retval; istringstream inString(GetString(argument, itemNum)); inString >> retval; return retval; } // GPTDataCL::GetInt() // Extract string data from argument string, which should be colon-delimited // If string begins with a colon, that colon is skipped in the counting. If an // invalid itemNum is specified, returns an empty string. string GetString(string argument, int itemNum) { size_t startPos = 0, endPos = 0; string retVal = ""; int foundLast = 0; int numFound = 0; if (argument[0] == ':') argument.erase(0, 1); while ((numFound < itemNum) && (!foundLast)) { endPos = argument.find(':', startPos); numFound++; if (endPos == string::npos) { foundLast = 1; endPos = argument.length(); } else if (numFound < itemNum) { startPos = endPos + 1; } // if/elseif } // while if ((numFound == itemNum) && (numFound > 0)) retVal = argument.substr(startPos, endPos - startPos); return retVal; } // GetString() gptfdisk-1.0.5/gptcurses.h0000664000175000017500000001047513622612343015777 0ustar rodsmithrodsmith/* * Implementation of GPTData class derivative with curses-based text-mode * interaction * Copyright (C) 2011-2018 Roderick W. Smith * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include "gptpart.h" #include "gpt.h" #ifndef __GPT_CURSES #define __GPT_CURSES using namespace std; struct MenuItem { int key; // Keyboard shortcut string name; // Item name; 8 characters string desc; // Description }; static struct MenuItem menuMain[] = { { 'a', "Align ", "Set partition alignment policy" }, { 'b', "Backup", "Back up the partition table" }, { 'd', "Delete", "Delete the current partition" }, { 'h', " Help ", "Print help screen" }, { 'i', " Info ", "Display information about the partition" }, { 'l', " Load ", "Load partition table backup from file" }, { 'm', " naMe ", "Change the partition's name" }, { 'n', " New ", "Create new partition from free space" }, { 'q', " Quit ", "Quit program without writing partition table" }, { 't', " Type ", "Change the filesystem type code GUID" }, { 'v', "Verify", "Verify the integrity of the disk's data structures" }, { 'w', "Write ", "Write partition table to disk (this might destroy data)" }, { 0, "", "" } }; #define EMPTY_SPACE_OPTIONS "abhlnqvw" #define PARTITION_OPTIONS "abdhilmqtvw" // Constants for how to highlight a selected menu item #define USE_CURSES 1 #define USE_ARROW 2 // A "Space" is a partition or an unallocated chunk of disk space, maintained // in a doubly-linked-list data structure to facilitate creating displays of // partitions and unallocated chunks of space on the disk in the main // cgdisk partition list. This list MUST be correctly maintained and in order, // and the numSpaces variable in the main GPTDataCurses class must specify // how many Spaces are in the main linked list of Spaces. struct Space { uint64_t firstLBA; uint64_t lastLBA; GPTPart *origPart; int partNum; Space *nextSpace; Space *prevSpace; }; class GPTDataCurses : public GPTData { protected: static int numInstances; GPTPart emptySpace; Space *firstSpace; Space *lastSpace; Space *currentSpace; int currentSpaceNum; string whichOptions; char currentKey; int numSpaces; int displayType; // Functions relating to Spaces data structures void EmptySpaces(void); int MakeSpacesFromParts(void); void AddEmptySpace(uint64_t firstLBA, uint64_t lastLBA); int AddEmptySpaces(void); void UnlinkSpace(Space *theSpace); void LinkToEnd(Space *theSpace); void SortSpaces(void); void IdentifySpaces(void); // Data display functions Space* ShowSpace(int spaceNum, int lineNum); int DisplayParts(int selected); public: GPTDataCurses(void); ~GPTDataCurses(void); // Functions corresponding to main menu items void DeletePartition(int partNum); void ShowInfo(int partNum); void ChangeName(int partNum); void ChangeType(int partNum); void SetAlignment(void); void Verify(void); void MakeNewPart(void); void SaveData(void); void Backup(void); void LoadBackup(void); void ShowHelp(void); // User input and menuing functions void SetDisplayType(int dt) {displayType = dt;} void ChangeSpaceSelection(int delta); void MoveSelection(int delta); void DisplayOptions(char selectedKey); void AcceptInput(); int Dispatch(char operation); void DrawMenu(void); int MainMenu(void); }; // class GPTDataCurses // Non-class support functions (mostly to do simple curses stuff).... void ClearLine(int lineNum); void ClearBottom(void); void PromptToContinue(void); void Report(string theText); void ShowTypes(void); #endif gptfdisk-1.0.5/sgdisk.cc0000664000175000017500000000125113622612343015372 0ustar rodsmithrodsmith// sgdisk.cc // Command-line-based version of gdisk. This program is named after sfdisk, // and it can serve a similar role (easily scripted, etc.), but it's used // strictly via command-line arguments, and it doesn't bear much resemblance // to sfdisk in actual use. // // by Rod Smith, project began February 2009; sgdisk begun January 2010. /* This program is copyright (c) 2009-2011 by Roderick W. Smith. It is distributed under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ #include "gptcl.h" using namespace std; #define MAX_OPTIONS 50 int main(int argc, char *argv[]) { GPTDataCL theGPT; return theGPT.DoOptions(argc, argv); } // main gptfdisk-1.0.5/Makefile.freebsd0000664000175000017500000000273613622612343016661 0ustar rodsmithrodsmithCC=gcc CXX=g++ CFLAGS+=-D_FILE_OFFSET_BITS=64 #CXXFLAGS+=-Wall -D_FILE_OFFSET_BITS=64 -D USE_UTF16 -I/usr/local/include CXXFLAGS+=-Wall -D_FILE_OFFSET_BITS=64 -I /usr/local/include LDFLAGS+= LIB_NAMES=crc32 support guid gptpart mbrpart basicmbr mbr gpt bsd parttypes attributes diskio diskio-unix MBR_LIBS=support diskio diskio-unix basicmbr mbrpart LIB_OBJS=$(LIB_NAMES:=.o) MBR_LIB_OBJS=$(MBR_LIBS:=.o) LIB_HEADERS=$(LIB_NAMES:=.h) DEPEND= makedepend $(CXXFLAGS) all: gdisk cgdisk sgdisk fixparts gdisk: $(LIB_OBJS) gdisk.o gpttext.o # $(CXX) $(LIB_OBJS) gdisk.o gpttext.o -L/usr/local/lib $(LDFLAGS) -licuio -luuid -o gdisk $(CXX) $(LIB_OBJS) gdisk.o gpttext.o -L/usr/local/lib $(LDFLAGS) -luuid -o gdisk cgdisk: $(LIB_OBJS) cgdisk.o gptcurses.o # $(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o -L/usr/local/lib $(LDFLAGS) -licuio -luuid -lncurses -o cgdisk $(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o -L/usr/local/lib $(LDFLAGS) -luuid -lncurses -o cgdisk sgdisk: $(LIB_OBJS) sgdisk.o gptcl.o # $(CXX) $(LIB_OBJS) sgdisk.o gptcl.o -L/usr/local/lib $(LDFLAGS) -luuid -licuio -lpopt -o sgdisk $(CXX) $(LIB_OBJS) sgdisk.o gptcl.o -L/usr/local/lib $(LDFLAGS) -luuid -lpopt -o sgdisk fixparts: $(MBR_LIB_OBJS) fixparts.o $(CXX) $(MBR_LIB_OBJS) fixparts.o -L/usr/local/lib $(LDFLAGS) -o fixparts lint: #no pre-reqs lint $(SRCS) clean: #no pre-reqs rm -f core *.o *~ gdisk sgdisk # what are the source dependencies depend: $(SRCS) $(DEPEND) $(SRCS) $(OBJS): $(CRITICAL_CXX_FLAGS) # DO NOT DELETE gptfdisk-1.0.5/Makefile.mingw0000664000175000017500000000242713622612343016365 0ustar rodsmithrodsmithCC=/usr/bin/i686-w64-mingw32-gcc CXX=/usr/bin/i686-w64-mingw32-g++ STRIP=/usr/bin/i686-w64-mingw32-strip CFLAGS=-O2 -Wall -static -static-libgcc -static-libstdc++ -D_FILE_OFFSET_BITS=64 -g CXXFLAGS=-O2 -Wall -static -static-libgcc -static-libstdc++ -D_FILE_OFFSET_BITS=64 -g #CXXFLAGS=-O2 -Wall -D_FILE_OFFSET_BITS=64 -I /usr/local/include -I/opt/local/include -g LIB_NAMES=guid gptpart bsd parttypes attributes crc32 mbrpart basicmbr mbr gpt support diskio diskio-windows MBR_LIBS=support diskio diskio-windows basicmbr mbrpart LIB_SRCS=$(NAMES:=.cc) LIB_OBJS=$(LIB_NAMES:=.o) MBR_LIB_OBJS=$(MBR_LIBS:=.o) LIB_HEADERS=$(LIB_NAMES:=.h) DEPEND= makedepend $(CFLAGS) all: gdisk fixparts gdisk: $(LIB_OBJS) gdisk.o gpttext.o $(CXX) $(CXXFLAGS) $(LIB_OBJS) gdisk.o gpttext.o -lrpcrt4 -static-libgcc -o gdisk32.exe sgdisk: $(LIB_OBJS) sgdisk.o $(CXX) $(CXXFLAGS) $(LIB_OBJS) sgdisk.o -lpopt -static-libgcc -o sgdisk32.exe fixparts: $(MBR_LIB_OBJS) fixparts.o $(CXX) $(CXXFLAGS) $(MBR_LIB_OBJS) fixparts.o $(LDFLAGS) -static-libgcc -o fixparts32.exe lint: #no pre-reqs lint $(SRCS) clean: #no pre-reqs rm -f core *.o *~ gdisk.exe sgdisk.exe strip: #no pre-reqs $(STRIP) gdisk32.exe fixparts32.exe # what are the source dependencies depend: $(SRCS) $(DEPEND) $(SRCS) $(OBJS): # DO NOT DELETE gptfdisk-1.0.5/mbr.h0000664000175000017500000000237313622612343014536 0ustar rodsmithrodsmith/* mbr.h -- MBR data structure definitions, types, and functions */ /* This program is copyright (c) 2009-2013 by Roderick W. Smith. It is distributed under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ #include #include #include "gptpart.h" //#include "partnotes.h" #include "diskio.h" #include "basicmbr.h" #ifndef __MBRSTRUCTS #define __MBRSTRUCTS using namespace std; /**************************************** * * * MBRData class and related structures * * * ****************************************/ // Full data in tweaked MBR format class MBRData : public BasicMBRData { protected: public: MBRData(void) {} MBRData(string deviceFilename) : BasicMBRData(deviceFilename) {} MBRData & operator=(const BasicMBRData & orig); ~MBRData(void); // Functions to create, delete, or change partitions // Pass EmptyMBR 1 to clear the boot loader code, 0 to leave it intact void MakeProtectiveMBR(int clearBoot = 0); void OptimizeEESize(void); int DeleteByLocation(uint64_t start64, uint64_t length64); // Functions to extract data on specific partitions.... GPTPart AsGPT(int i); }; // struct MBRData #endif gptfdisk-1.0.5/mbr.cc0000664000175000017500000001366613622612343014703 0ustar rodsmithrodsmith/* mbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition data. */ /* Initial coding by Rod Smith, January to February, 2009 */ /* This program is copyright (c) 2009-2013 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 #include #include #include #include #include #include #include #include #include #include "mbr.h" using namespace std; /**************************************** * * * MBRData class and related structures * * * ****************************************/ MBRData::~MBRData(void) { } // MBRData destructor // Assignment operator -- copy entire set of MBR data. MBRData & MBRData::operator=(const BasicMBRData & orig) { BasicMBRData::operator=(orig); return *this; } // MBRData::operator=() /***************************************************** * * * Functions to create, delete, or change partitions * * * *****************************************************/ // Create a protective MBR. Clears the boot loader area if clearBoot > 0. void MBRData::MakeProtectiveMBR(int clearBoot) { EmptyMBR(clearBoot); // Initialize variables nulls = 0; MBRSignature = MBR_SIGNATURE; diskSignature = UINT32_C(0); partitions[0].SetStatus(0); // Flag the protective part. as unbootable partitions[0].SetType(UINT8_C(0xEE)); if (diskSize < UINT32_MAX) { // If the disk is under 2TiB partitions[0].SetLocation(UINT32_C(1), (uint32_t) diskSize - UINT32_C(1)); } else { // disk is too big to represent, so fake it... partitions[0].SetLocation(UINT32_C(1), UINT32_MAX); } // if/else partitions[0].SetInclusion(PRIMARY); state = gpt; } // MBRData::MakeProtectiveMBR() // Optimizes the size of the 0xEE (EFI GPT) partition void MBRData::OptimizeEESize(void) { int i, typeFlag = 0; uint64_t after; for (i = 0; i < 4; i++) { // Check for non-empty and non-0xEE partitions if ((partitions[i].GetType() != 0xEE) && (partitions[i].GetType() != 0x00)) typeFlag++; if (partitions[i].GetType() == 0xEE) { // Blank space before this partition; fill it.... if (SectorUsedAs(partitions[i].GetStartLBA() - 1, 4) == NONE) { partitions[i].SetStartLBA(FindFirstInFree(partitions[i].GetStartLBA() - 1)); } // if // Blank space after this partition; fill it.... after = partitions[i].GetStartLBA() + partitions[i].GetLengthLBA(); if (SectorUsedAs(after, 4) == NONE) { partitions[i].SetLengthLBA(FindLastInFree(after) - partitions[i].GetStartLBA() + 1); } // if free space after if (after > diskSize) { if (diskSize < UINT32_MAX) { // If the disk is under 2TiB partitions[i].SetLengthLBA((uint32_t) diskSize - partitions[i].GetStartLBA()); } else { // disk is too big to represent, so fake it... partitions[i].SetLengthLBA(UINT32_MAX - partitions[i].GetStartLBA()); } // if/else } // if protective partition is too big RecomputeCHS(i); } // if partition is 0xEE } // for partition loop if (typeFlag == 0) { // No non-hybrid partitions found MakeProtectiveMBR(); // ensure it's a fully compliant protective MBR. } // if } // MBRData::OptimizeEESize() // Delete a partition if one exists at the specified location. // Returns 1 if a partition was deleted, 0 otherwise.... // 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, deleted = 0; if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) { start32 = (uint32_t) start64; length32 = (uint32_t) length64; for (i = 0; i < MAX_MBR_PARTS; i++) { if ((partitions[i].GetType() != 0xEE) && (partitions[i].GetStartLBA() == start32) && (partitions[i].GetLengthLBA() == length32)) { DeletePartition(i); if (state == hybrid) OptimizeEESize(); deleted = 1; } // if (match found) } // for i (partition scan) } // if (hybrid & GPT partition < 2TiB) return deleted; } // MBRData::DeleteByLocation() /****************************************************** * * * Functions that extract data on specific partitions * * * ******************************************************/ // Return the MBR data as a GPT partition.... GPTPart MBRData::AsGPT(int i) { MBRPart* origPart; GPTPart newPart; uint8_t origType; uint64_t firstSector, lastSector; newPart.BlankPartition(); origPart = GetPartition(i); if (origPart != NULL) { origType = origPart->GetType(); // don't convert extended, hybrid protective, or null (non-existent) // partitions (Note similar protection is in GPTData::XFormPartitions(), // but I want it here too in case I call this function in another // context in the future....) if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) && (origType != 0x00) && (origType != 0xEE)) { firstSector = (uint64_t) origPart->GetStartLBA(); newPart.SetFirstLBA(firstSector); lastSector = (uint64_t) origPart->GetLastLBA(); newPart.SetLastLBA(lastSector); newPart.SetType(((uint16_t) origType) * 0x0100); newPart.RandomizeUniqueGUID(); newPart.SetAttributes(0); newPart.SetName(newPart.GetTypeName()); } // if not extended, protective, or non-existent } // if (origPart != NULL) return newPart; } // MBRData::AsGPT() gptfdisk-1.0.5/diskio-unix.cc0000664000175000017500000003741313622612343016362 0ustar rodsmithrodsmith// // C++ Interface: diskio (Unix components [Linux, FreeBSD, Mac OS X]) // // Description: Class to handle low-level disk I/O for GPT fdisk // // // Author: Rod Smith , (C) 2009 // // 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. #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include "linux/hdreg.h" #endif #include #include #include #include "diskio.h" using namespace std; // Returns the official "real" name for a shortened version of same. // Trivial here; more important in Windows void DiskIO::MakeRealName(void) { realFilename = userFilename; } // DiskIO::MakeRealName() // Open the currently on-record file for reading. Returns 1 if the file is // already open or is opened by this call, 0 if opening the file doesn't // work. int DiskIO::OpenForRead(void) { int shouldOpen = 1; struct stat64 st; if (isOpen) { // file is already open if (openForWrite) { Close(); } else { shouldOpen = 0; } // if/else } // if if (shouldOpen) { fd = open(realFilename.c_str(), O_RDONLY); if (fd == -1) { cerr << "Problem opening " << realFilename << " for reading! Error is " << errno << ".\n"; if (errno == EACCES) // User is probably not running as root cerr << "You must run this program as root or use sudo!\n"; if (errno == ENOENT) cerr << "The specified file does not exist!\n"; realFilename = ""; userFilename = ""; modelName = ""; isOpen = 0; openForWrite = 0; } else { isOpen = 0; openForWrite = 0; if (fstat64(fd, &st) == 0) { if (S_ISDIR(st.st_mode)) cerr << "The specified path is a directory!\n"; #if !(defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) \ && !defined(__APPLE__) else if (S_ISCHR(st.st_mode)) cerr << "The specified path is a character device!\n"; #endif else if (S_ISFIFO(st.st_mode)) cerr << "The specified path is a FIFO!\n"; else if (S_ISSOCK(st.st_mode)) cerr << "The specified path is a socket!\n"; else isOpen = 1; } // if (fstat64()...) #if defined(__linux__) && !defined(EFI) if (isOpen && realFilename.substr(0,4) == "/dev") { ostringstream modelNameFilename; modelNameFilename << "/sys/block" << realFilename.substr(4,512) << "/device/model"; ifstream modelNameFile(modelNameFilename.str().c_str()); if (modelNameFile.is_open()) { getline(modelNameFile, modelName); } // if } // if #endif } // if/else } // if return isOpen; } // DiskIO::OpenForRead(void) // An extended file-open function. This includes some system-specific checks. // Returns 1 if the file is open, 0 otherwise.... int DiskIO::OpenForWrite(void) { if ((isOpen) && (openForWrite)) return 1; // Close the disk, in case it's already open for reading only.... Close(); // try to open the device; may fail.... fd = open(realFilename.c_str(), O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); #ifdef __APPLE__ // MacOS X requires a shared lock under some circumstances.... if (fd < 0) { cerr << "Warning: Devices opened with shared lock will not have their\npartition table automatically reloaded!\n"; fd = open(realFilename.c_str(), O_WRONLY | O_SHLOCK); } // if #endif if (fd >= 0) { isOpen = 1; openForWrite = 1; } else { isOpen = 0; openForWrite = 0; } // if/else return isOpen; } // DiskIO::OpenForWrite(void) // Close the disk device. Note that this does NOT erase the stored filenames, // so the file can be re-opened without specifying the filename. void DiskIO::Close(void) { if (isOpen) if (close(fd) < 0) cerr << "Warning! Problem closing file!\n"; isOpen = 0; openForWrite = 0; } // DiskIO::Close() // Returns block size of device pointed to by fd file descriptor. If the ioctl // returns an error condition, print a warning but return a value of SECTOR_SIZE // (512). If the disk can't be opened at all, return a value of 0. int DiskIO::GetBlockSize(void) { int err = -1, blockSize = 0; #ifdef __sun__ struct dk_minfo minfo; #endif // If disk isn't open, try to open it.... if (!isOpen) { OpenForRead(); } // if if (isOpen) { #ifdef __APPLE__ err = ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize); #endif #ifdef __sun__ err = ioctl(fd, DKIOCGMEDIAINFO, &minfo); if (err == 0) blockSize = minfo.dki_lbsize; #endif #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) err = ioctl(fd, DIOCGSECTORSIZE, &blockSize); #endif #ifdef __linux__ err = ioctl(fd, BLKSSZGET, &blockSize); #endif if (err == -1) { blockSize = SECTOR_SIZE; // ENOTTY = inappropriate ioctl; probably being called on a disk image // file, so don't display the warning message.... // 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)) { cerr << "\aError " << errno << " when determining sector size! Setting sector size to " << SECTOR_SIZE << "\n"; cout << "Disk device is " << realFilename << "\n"; } // if } // if (err == -1) } // if (isOpen) return (blockSize); } // DiskIO::GetBlockSize() // Returns the physical block size of the device, if possible. If this is // not supported, or if an error occurs, this function returns 0. // TODO: Get this working in more OSes than Linux. int DiskIO::GetPhysBlockSize(void) { int err = -1, physBlockSize = 0; // If disk isn't open, try to open it.... if (!isOpen) { OpenForRead(); } // if if (isOpen) { #if defined __linux__ && !defined(EFI) err = ioctl(fd, BLKPBSZGET, &physBlockSize); #endif } // if (isOpen) if (err == -1) physBlockSize = 0; return (physBlockSize); } // DiskIO::GetPhysBlockSize(void) // Returns the number of heads, according to the kernel, or 255 if the // correct value can't be determined. uint32_t DiskIO::GetNumHeads(void) { uint32_t numHeads = 255; #ifdef HDIO_GETGEO struct hd_geometry geometry; // If disk isn't open, try to open it.... if (!isOpen) OpenForRead(); if (!ioctl(fd, HDIO_GETGEO, &geometry)) numHeads = (uint32_t) geometry.heads; #endif return numHeads; } // DiskIO::GetNumHeads(); // Returns the number of sectors per track, according to the kernel, or 63 // if the correct value can't be determined. uint32_t DiskIO::GetNumSecsPerTrack(void) { uint32_t numSecs = 63; #ifdef HDIO_GETGEO struct hd_geometry geometry; // If disk isn't open, try to open it.... if (!isOpen) OpenForRead(); if (!ioctl(fd, HDIO_GETGEO, &geometry)) numSecs = (uint32_t) geometry.sectors; #endif return numSecs; } // DiskIO::GetNumSecsPerTrack() // Resync disk caches so the OS uses the new partition table. This code varies // a lot from one OS to another. // Returns 1 on success, 0 if the kernel continues to use the old partition table. // (Note that for most OSes, the default of 0 is returned because I've not yet // looked into how to test for success in the underlying system calls...) int DiskIO::DiskSync(void) { int i, retval = 0, platformFound = 0; // If disk isn't open, try to open it.... if (!isOpen) { OpenForRead(); } // if if (isOpen) { sync(); #if defined(__APPLE__) || defined(__sun__) cout << "Warning: The kernel may continue to use old or deleted partitions.\n" << "You should reboot or remove the drive.\n"; /* don't know if this helps * it definitely will get things on disk though: * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */ #ifdef __sun__ i = ioctl(fd, DKIOCFLUSHWRITECACHE); #else i = ioctl(fd, DKIOCSYNCHRONIZECACHE); #endif platformFound++; #endif #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) sleep(2); i = ioctl(fd, DIOCGFLUSH); cout << "Warning: The kernel may continue to use old or deleted partitions.\n" << "You should reboot or remove the drive.\n"; platformFound++; #endif #ifdef __linux__ sleep(1); // Theoretically unnecessary, but ioctl() fails sometimes if omitted.... fsync(fd); i = ioctl(fd, BLKRRPART); if (i) { cout << "Warning: The kernel is still using the old partition table.\n" << "The new table will be used at the next reboot or after you\n" << "run partprobe(8) or kpartx(8)\n"; } else { retval = 1; } // if/else platformFound++; #endif if (platformFound == 0) cerr << "Warning: Platform not recognized!\n"; if (platformFound > 1) cerr << "\nWarning: We seem to be running on multiple platforms!\n"; } // if (isOpen) return retval; } // DiskIO::DiskSync() // Seek to the specified sector. Returns 1 on success, 0 on failure. // Note that seeking beyond the end of the file is NOT detected as a failure! int DiskIO::Seek(uint64_t sector) { int retval = 1; off_t seekTo, sought; // If disk isn't open, try to open it.... if (!isOpen) { retval = OpenForRead(); } // if if (isOpen) { seekTo = sector * (uint64_t) GetBlockSize(); sought = lseek64(fd, seekTo, SEEK_SET); if (sought != seekTo) { retval = 0; } // if } // if return retval; } // DiskIO::Seek() // A variant on the standard read() function. Done to work around // limitations in FreeBSD concerning the matching of the sector // size with the number of bytes read. // Returns the number of bytes read into buffer. int DiskIO::Read(void* buffer, int numBytes) { int blockSize, numBlocks, retval = 0; char* tempSpace; // If disk isn't open, try to open it.... if (!isOpen) { OpenForRead(); } // if if (isOpen) { // Compute required space and allocate memory blockSize = GetBlockSize(); if (numBytes <= blockSize) { numBlocks = 1; tempSpace = new char [blockSize]; } else { numBlocks = numBytes / blockSize; if ((numBytes % blockSize) != 0) numBlocks++; tempSpace = new char [numBlocks * blockSize]; } // if/else if (tempSpace == NULL) { cerr << "Unable to allocate memory in DiskIO::Read()! Terminating!\n"; exit(1); } // if // Read the data into temporary space, then copy it to buffer retval = read(fd, tempSpace, numBlocks * blockSize); memcpy(buffer, tempSpace, numBytes); // Adjust the return value, if necessary.... if (((numBlocks * blockSize) != numBytes) && (retval > 0)) retval = numBytes; delete[] tempSpace; } // if (isOpen) return retval; } // DiskIO::Read() // A variant on the standard write() function. Done to work around // limitations in FreeBSD concerning the matching of the sector // size with the number of bytes read. // Returns the number of bytes written. int DiskIO::Write(void* buffer, int numBytes) { int blockSize = 512, i, numBlocks, retval = 0; char* tempSpace; // If disk isn't open, try to open it.... if ((!isOpen) || (!openForWrite)) { OpenForWrite(); } // if if (isOpen) { // Compute required space and allocate memory blockSize = GetBlockSize(); if (numBytes <= blockSize) { numBlocks = 1; tempSpace = new char [blockSize]; } else { numBlocks = numBytes / blockSize; if ((numBytes % blockSize) != 0) numBlocks++; tempSpace = new char [numBlocks * blockSize]; } // if/else if (tempSpace == NULL) { cerr << "Unable to allocate memory in DiskIO::Write()! Terminating!\n"; exit(1); } // if // Copy the data to my own buffer, then write it memcpy(tempSpace, buffer, numBytes); for (i = numBytes; i < numBlocks * blockSize; i++) { tempSpace[i] = 0; } // for retval = write(fd, tempSpace, numBlocks * blockSize); // Adjust the return value, if necessary.... if (((numBlocks * blockSize) != numBytes) && (retval > 0)) retval = numBytes; delete[] tempSpace; } // if (isOpen) return retval; } // DiskIO:Write() /************************************************************************************** * * * Below functions are lifted from various sources, as documented in comments before * * each one. * * * **************************************************************************************/ // The disksize function is taken from the Linux fdisk code and modified // greatly since then to enable FreeBSD and MacOS support, as well as to // return correct values for disk image files. uint64_t DiskIO::DiskSize(int *err) { uint64_t sectors = 0; // size in sectors off_t bytes = 0; // size in bytes struct stat64 st; int platformFound = 0; #ifdef __sun__ struct dk_minfo minfo; #endif // If disk isn't open, try to open it.... if (!isOpen) { OpenForRead(); } // if if (isOpen) { // Note to self: I recall testing a simplified version of // this code, similar to what's in the __APPLE__ block, // on Linux, but I had some problems. IIRC, it ran OK on 32-bit // systems but not on 64-bit. Keep this in mind in case of // 32/64-bit issues on MacOS.... #ifdef __APPLE__ *err = ioctl(fd, DKIOCGETBLOCKCOUNT, §ors); platformFound++; #endif #ifdef __sun__ *err = ioctl(fd, DKIOCGMEDIAINFO, &minfo); if (*err == 0) sectors = minfo.dki_capacity; platformFound++; #endif #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) *err = ioctl(fd, DIOCGMEDIASIZE, &bytes); long long b = GetBlockSize(); sectors = bytes / b; platformFound++; #endif #ifdef __linux__ long sz; long long b; *err = ioctl(fd, BLKGETSIZE, &sz); if (*err) { sectors = sz = 0; } // if if ((!*err) || (errno == EFBIG)) { *err = ioctl(fd, BLKGETSIZE64, &b); if (*err || b == 0 || b == sz) sectors = sz; else sectors = (b >> 9); } // if // Unintuitively, the above returns values in 512-byte blocks, no // matter what the underlying device's block size. Correct for this.... sectors /= (GetBlockSize() / 512); platformFound++; #endif if (platformFound != 1) cerr << "Warning! We seem to be running on no known platform!\n"; // The above methods have failed, so let's assume it's a regular // file (a QEMU image, dd backup, or what have you) and see what // fstat() gives us.... if ((sectors == 0) || (*err == -1)) { if (fstat64(fd, &st) == 0) { bytes = st.st_size; if ((bytes % UINT64_C(512)) != 0) cerr << "Warning: File size is not a multiple of 512 bytes!" << " Misbehavior is likely!\n\a"; sectors = bytes / UINT64_C(512); } // if } // if } // if (isOpen) return sectors; } // DiskIO::DiskSize() gptfdisk-1.0.5/gdisk.cc0000664000175000017500000000377413622612343015223 0ustar rodsmithrodsmith// gdisk.cc // Program modelled after Linux fdisk, but it manipulates GPT partitions // rather than MBR partitions. // // by Rod Smith, project began February 2009 /* This program is copyright (c) 2009-2013 by Roderick W. Smith. It is distributed under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ #include #include #include "gpttext.h" int main(int argc, char* argv[]) { GPTDataTextUI theGPT; string device = ""; UnicodeString uString; int isError = 0; #ifndef EFI cout << "GPT fdisk (gdisk) version " << GPTFDISK_VERSION << "\n\n"; #endif /*EFI*/ if (!SizesOK()) exit(1); switch (argc) { case 1: cout << "Type device filename, or press to exit: "; device = ReadString(); if (device.length() == 0) exit(0); else if (theGPT.LoadPartitions(device)) { if (theGPT.GetState() != use_gpt) WinWarning(); theGPT.MainMenu(device); } // if/elseif break; case 2: // basic usage if (theGPT.LoadPartitions(argv[1])) { if (theGPT.GetState() != use_gpt) WinWarning(); theGPT.MainMenu(argv[1]); } // if break; case 3: // usage with "-l" option if (strcmp(argv[1], "-l") == 0) { device = (string) argv[2]; } else if (strcmp(argv[2], "-l") == 0) { device = (string) argv[1]; } else { // 3 arguments, but none is "-l" cerr << "Usage: " << argv[0] << " [-l] device_file\n"; isError = 1; } // if/elseif/else if (device != "") { theGPT.JustLooking(); if (theGPT.LoadPartitions(device)) theGPT.DisplayGPTData(); else isError = 1; } // if break; default: cerr << "Usage: " << argv[0] << " [-l] device_file\n"; isError = 1; break; } // switch return (isError); } // main gptfdisk-1.0.5/gptpart.h0000664000175000017500000000737313622612343015444 0ustar rodsmithrodsmith// // C++ Interface: gptpart // // Description: Class to implement a single GPT partition // // // Author: Rod Smith , (C) 2009 // // 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. #ifndef __GPTPART_H #define __GPTPART_H #include #include #include #include "support.h" #include "parttypes.h" #include "guid.h" #include "attributes.h" using namespace std; // Values returned by GPTPart::IsSizedForMBR() #define MBR_SIZED_GOOD 0 /* Whole partition under 2^32 sectors */ #define MBR_SIZED_IFFY 1 /* Partition starts under 2^32 & is less than 2^32, but ends over 2^32 */ #define MBR_SIZED_BAD 2 /* Partition starts over 2^32, is bigger than 2^32, or otherwise bad */ /**************************************** * * * GPTPart class and related structures * * * ****************************************/ class GPTPart { protected: // Caution: The non-static data in GPTPart is precisely the right size // to enable easy loading of the data directly from disk. If any // non-static variables are added to the below, the data size will // change and the program will stop working. This can be corrected by // adjusting the data-load operation in GPTData::LoadMainTable() and // GPTData::LoadSecondTableAsMain() and then removing the GPTPart // size check in SizesOK() (in gpt.cc file). PartType partitionType; GUIDData uniqueGUID; uint64_t firstLBA; uint64_t lastLBA; Attributes attributes; uint16_t name[NAME_SIZE]; public: GPTPart(void); GPTPart(const GPTPart &); ~GPTPart(void); // Simple data retrieval: PartType & GetType(void) {return partitionType;} uint16_t GetHexType(void) const; string GetTypeName(void); UnicodeString GetUTypeName(void); const GUIDData GetUniqueGUID(void) const {return uniqueGUID;} uint64_t GetFirstLBA(void) const {return firstLBA;} uint64_t GetLastLBA(void) const {return lastLBA;} uint64_t GetLengthLBA(void) const; Attributes GetAttributes(void) {return attributes;} void ShowAttributes(uint32_t partNum) {attributes.ShowAttributes(partNum);} UnicodeString GetDescription(void); int IsUsed(void); int IsSizedForMBR(void); // Simple data assignment: void SetType(PartType t); void SetType(uint16_t hex) {partitionType = hex;} void SetUniqueGUID(GUIDData u) {uniqueGUID = u;} void RandomizeUniqueGUID(void) {uniqueGUID.Randomize();} void SetFirstLBA(uint64_t f) {firstLBA = f;} void SetLastLBA(uint64_t l) {lastLBA = l;} void SetAttributes(uint64_t a) {attributes = a;} void SetAttributes(void) {attributes.ChangeAttributes();} void SetName(const string & theName); #ifdef USE_UTF16 void SetName(const UnicodeString & theName); #endif void SetDefaultDescription(void); // Additional functions GPTPart & operator=(const GPTPart & orig); bool operator<(const GPTPart &other) const; 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(const GPTPart & other); // returns 1 if there's overlap void ReversePartBytes(void); // reverse byte order of all integer fields // Functions requiring user interaction void ChangeType(void); // Change the type code }; // struct GPTPart #endif gptfdisk-1.0.5/gptpart.cc0000664000175000017500000003410213622612343015570 0ustar rodsmithrodsmith// // C++ Implementation: gptpart // // Description: Class to implement a SINGLE GPT partition // // // Author: Rod Smith , (C) 2009-2018 // // 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. #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #ifdef USE_UTF16 #include #else #define UnicodeString string #endif #include #include #include #include "gptpart.h" #include "attributes.h" using namespace std; GPTPart::GPTPart(void) { partitionType.Zero(); uniqueGUID.Zero(); firstLBA = 0; lastLBA = 0; attributes = 0; memset(name, 0, NAME_SIZE * sizeof(name[0]) ); } // Default constructor GPTPart::GPTPart(const GPTPart & orig) { partitionType = orig.partitionType; uniqueGUID = orig.uniqueGUID; firstLBA = orig.firstLBA; lastLBA = orig.lastLBA; attributes = orig.attributes; memcpy(name, orig.name, NAME_SIZE * sizeof( name[ 0 ] ) ); } // Copy constructor GPTPart::~GPTPart(void) { } // destructor // Return the gdisk-specific two-byte hex code for the partition uint16_t GPTPart::GetHexType(void) const { return partitionType.GetHexType(); } // GPTPart::GetHexType() // Return a plain-text description of the partition type (e.g., "Linux/Windows // data" or "Linux swap"). string GPTPart::GetTypeName(void) { return partitionType.TypeName(); } // GPTPart::GetNameType() #ifdef USE_UTF16 // Return a Unicode description of the partition type (e.g., "Linux/Windows // data" or "Linux swap"). UnicodeString GPTPart::GetUTypeName(void) { return partitionType.UTypeName(); } // GPTPart::GetNameType() #endif // Compute and return the partition's length (or 0 if the end is incorrectly // set before the beginning). uint64_t GPTPart::GetLengthLBA(void) const { uint64_t length = 0; if (firstLBA <= lastLBA) length = lastLBA - firstLBA + UINT64_C(1); return length; } // GPTPart::GetLengthLBA() #ifdef USE_UTF16 // Return partition's name field, converted to a Unicode string UnicodeString GPTPart::GetDescription(void) { return (UChar*) name; } // GPTPart::GetDescription() #else // Return partition's name field, converted to a C++ UTF-8 string string GPTPart::GetDescription(void) { // convert name to utf32 then to utf8 string utf8 ; size_t pos = 0 ; while ( ( pos < NAME_SIZE ) && ( name[ pos ] != 0 ) ) { uint16_t cp = name[ pos ++ ] ; if ( ! IsLittleEndian() ) ReverseBytes( & cp , 2 ) ; // first to utf32 uint32_t uni ; if ( cp < 0xd800 || cp > 0xdfff ) { uni = cp ; } // if else if ( cp < 0xdc00 ) { // lead surrogate uni = ( (uint32_t)( cp & 0x3ff ) ) << 10 ; if ( pos >= NAME_SIZE ) { // missing trail surrogate, name[] is invalid break ; } // if cp = name[ pos ++ ] ; if ( cp < 0xdc00 || cp > 0xdfff ) { // invalid trail surrogate, name[] is invalid break ; } // if // trail surrogate uni |= cp & 0x3ff ; uni += 0x10000 ; } // if else { // unexpected trail surrogate, name[] is invalid break ; } // if // then to utf8 if ( uni < 0x80 ) { utf8 += (char) uni ; } // if else if ( uni < 0x800 ) { utf8 += (char) ( 0xc0 | ( uni >> 6 ) ) ; utf8 += (char) ( 0x80 | ( uni & 0x3f ) ) ; } // if else if ( uni < 0x10000 ) { utf8 += (char) ( 0xe0 | ( uni >> 12 ) ) ; utf8 += (char) ( 0x80 | ( ( uni >> 6 ) & 0x3f ) ) ; utf8 += (char) ( 0x80 | ( uni & 0x3f ) ) ; } // if else { utf8 += (char) ( 0xf0 | ( uni >> 18 ) ) ; utf8 += (char) ( 0xe0 | ( ( uni >> 12 ) & 0x3f ) ) ; utf8 += (char) ( 0x80 | ( ( uni >> 6 ) & 0x3f ) ) ; utf8 += (char) ( 0x80 | ( uni & 0x3f ) ) ; } // if } return utf8 ; } // GPTPart::GetDescription(), UTF-8 version #endif // Return 1 if the partition is in use int GPTPart::IsUsed(void) { return (partitionType != GUIDData("0x00")); } // GPTPart::IsUsed() // Returns MBR_SIZED_GOOD, MBR_SIZED_IFFY, or MBR_SIZED_BAD; see comments // in header file for details. int GPTPart::IsSizedForMBR(void) { int retval = MBR_SIZED_GOOD; if ((firstLBA > UINT32_MAX) || ((lastLBA - firstLBA) > UINT32_MAX) || (firstLBA > lastLBA)) retval = MBR_SIZED_BAD; else if (lastLBA > UINT32_MAX) retval = MBR_SIZED_IFFY; return (retval); } // GPTPart::IsSizedForMBR() // Set the type code to the specified one. Also changes the partition // name *IF* the current name is the generic one for the current partition // type. void GPTPart::SetType(PartType t) { #ifdef USE_UTF16 if (GetDescription() == partitionType.UTypeName()) { #else if (GetDescription() == partitionType.TypeName()) { #endif SetName(t.TypeName()); } // if partitionType = t; } // GPTPart::SetType() #ifdef USE_UTF16 // Set the name for a partition to theName, using a C++-style string as // input. void GPTPart::SetName(const string & theName) { SetName((UnicodeString) theName.c_str()); } // GPTPart::SetName() // Set the name for a partition to theName, using a Unicode string as // input. void GPTPart::SetName(const UnicodeString & theName) { if (theName.isBogus()) { cerr << "Bogus UTF-16 name found in GPTPart::SetName()! Name not changed!\n"; } else { memset(name, 0, NAME_SIZE * sizeof(name[0]) ); theName.extractBetween(0, NAME_SIZE, (UChar*) name); } // if/else } // GPTPart::SetName() #else // Set the name for a partition to theName. Note that theName is a // standard C++-style ASCII string, although the GUID partition definition // requires a UTF-16LE string. This function creates a simple-minded copy // for this. void GPTPart::SetName(const string & theName) { // convert utf8 to utf32 then to utf16le size_t len = theName.length() ; size_t pos = 0 ; for ( size_t i = 0 ; pos < NAME_SIZE && i < len ; ) { uint32_t uni ; uint8_t cp = theName[ i ++ ] ; int todo ; if ( cp < 0x80 ) { uni = cp ; todo = 0 ; } // if else if ( cp < 0xc0 || cp > 0xf7 ) { // invalid byte, theName is broken break ; } // if else if ( cp < 0xe0 ) { uni = cp & 0x1f ; todo = 1 ; } // if else if ( cp < 0xf0 ) { uni = cp & 0x0f ; todo = 2 ; } // if else { uni = cp & 0x7 ; todo = 3 ; } // if while ( todo > 0 ) { if ( i >= len ) { // missing continuation byte, theName is broken goto break_converter ; } // if cp = theName[ i ++ ] ; if ( cp > 0xbf || cp < 0x80 ) { // invalid continuation byte, theName is broken goto break_converter ; } // if uni <<= 6 ; uni |= cp & 0x3f ; todo -- ; } // while // then to utf16le if ( uni < 0x10000 ) { name[ pos ] = (uint16_t) uni ; if ( ! IsLittleEndian() ) ReverseBytes( name + pos , 2 ) ; pos ++ ; } // if else { if ( pos > NAME_SIZE - 2 ) { // not enough room for two surrogates, truncate break ; } // if uni -= 0x10000 ; name[ pos ] = (uint16_t)( uni >> 10 ) | 0xd800 ; if ( ! IsLittleEndian() ) ReverseBytes( name + pos , 2 ) ; pos ++ ; name[ pos ] = (uint16_t)( uni & 0x3ff ) | 0xdc00 ; if ( ! IsLittleEndian() ) ReverseBytes( name + pos , 2 ) ; pos ++ ; } } // for break_converter : ; // finally fill with zeroes while ( pos < NAME_SIZE ) { name[ pos ++ ] = 0 ; } // while } // GPTPart::SetName(), UTF-8 version #endif // Set the name for the partition based on the current GUID partition type // code's associated name void GPTPart::SetDefaultDescription(void) { SetName(partitionType.TypeName()); } // GPTPart::SetDefaultDescription() GPTPart & GPTPart::operator=(const GPTPart & orig) { partitionType = orig.partitionType; uniqueGUID = orig.uniqueGUID; firstLBA = orig.firstLBA; lastLBA = orig.lastLBA; attributes = orig.attributes; memcpy(name, orig.name, NAME_SIZE * sizeof( name[ 0 ] ) ); return *this; } // assignment operator // Compare the values, and return a bool result. // Because this is intended for sorting and a firstLBA value of 0 denotes // a partition that's not in use and so that should be sorted upwards, // we return the opposite of the usual arithmetic result when either // firstLBA value is 0. bool GPTPart::operator<(const GPTPart &other) const { if (firstLBA && other.firstLBA) return (firstLBA < other.firstLBA); else return (other.firstLBA < firstLBA); } // GPTPart::operator<() // Display summary information; does nothing if the partition is empty. void GPTPart::ShowSummary(int partNum, uint32_t blockSize) { string sizeInIeee; UnicodeString description; size_t i; if (firstLBA != 0) { sizeInIeee = BytesToIeee(lastLBA - firstLBA + 1, blockSize); cout.fill(' '); cout.width(4); cout << partNum + 1 << " "; cout.width(14); cout << firstLBA << " "; cout.width(14); cout << lastLBA << " "; cout << sizeInIeee << " "; if (sizeInIeee.length() < 10) for (i = 0; i < 10 - sizeInIeee.length(); i++) cout << " "; cout.fill('0'); cout.width(4); cout.setf(ios::uppercase); cout << hex << partitionType.GetHexType() << " " << dec; cout.fill(' '); #ifdef USE_UTF16 GetDescription().extractBetween(0, 23, description); cout << description << "\n"; #else string desc = GetDescription() ; size_t n = 0 ; size_t i = 0 ; size_t len = desc.length() ; while ( n < 22 && i < len ) { i ++ ; if ( i >= len ) { // short description break ; } // if // skip continuation bytes while ( i < len && ( ( desc[ i ] & 0xC0 ) == 0x80 ) ) { // utf8 continuation byte i ++ ; } // while n ++ ; } // while if ( i < len ) { n = 0 ; i = 0 ; // description is long we will truncate it while ( n < 19 && i < len ) { i ++ ; if ( i >= len ) { // should not happen break ; } // if // skip continuation bytes while ( i < len && ( ( desc[ i ] & 0xC0 ) == 0x80 ) ) { // utf8 continuation byte i ++ ; } // while n ++ ; } // while } // for cout << GetDescription().substr( 0 , i ) ; if ( i < len ) cout << "..." ; cout << "\n"; #endif cout.fill(' '); } // if } // GPTPart::ShowSummary() // Show detailed partition information. Does nothing if the partition is // empty (as determined by firstLBA being 0). void GPTPart::ShowDetails(uint32_t blockSize) { uint64_t size; if (firstLBA != 0) { cout << "Partition GUID code: " << partitionType; cout << " (" << partitionType.TypeName() << ")\n"; cout << "Partition unique GUID: " << uniqueGUID << "\n"; cout << "First sector: " << firstLBA << " (at " << BytesToIeee(firstLBA, blockSize) << ")\n"; cout << "Last sector: " << lastLBA << " (at " << BytesToIeee(lastLBA, blockSize) << ")\n"; size = (lastLBA - firstLBA + 1); cout << "Partition size: " << size << " sectors (" << BytesToIeee(size, blockSize) << ")\n"; cout << "Attribute flags: "; cout.fill('0'); cout.width(16); cout << hex; cout << attributes << "\n"; cout << dec; cout << "Partition name: '" << GetDescription() << "'\n"; cout.fill(' '); } // if } // GPTPart::ShowDetails() // Blank (delete) a single partition void GPTPart::BlankPartition(void) { uniqueGUID.Zero(); partitionType.Zero(); firstLBA = 0; lastLBA = 0; attributes = 0; memset(name, 0, NAME_SIZE * sizeof( name[0]) ); } // GPTPart::BlankPartition // Returns 1 if the two partitions overlap, 0 if they don't int GPTPart::DoTheyOverlap(const GPTPart & other) { // Don't bother checking unless these are defined (both start and end points // are 0 for undefined partitions, so just check the start points) return firstLBA && other.firstLBA && (firstLBA <= other.lastLBA) != (lastLBA < other.firstLBA); } // GPTPart::DoTheyOverlap() // Reverse the bytes of integral data types and of the UTF-16LE name; // used on big-endian systems. void GPTPart::ReversePartBytes(void) { int i; ReverseBytes(&firstLBA, 8); ReverseBytes(&lastLBA, 8); ReverseBytes(&attributes, 8); for (i = 0; i < NAME_SIZE; i ++ ) ReverseBytes(name + i, 2); } // GPTPart::ReverseBytes() /**************************************** * Functions requiring user interaction * ****************************************/ // Change the type code on the partition. Also changes the name if the original // name is the generic one for the partition type. void GPTPart::ChangeType(void) { string line; int changeName; PartType tempType = PartType::unusedPartType; #ifdef USE_UTF16 changeName = (GetDescription() == GetUTypeName()); #else changeName = (GetDescription() == GetTypeName()); #endif cout << "Current type is " << hex << GetHexType() << dec << " (" << GetTypeName() << ")\n"; do { cout << "Hex code or GUID (L to show codes, Enter = " << hex << GetHexType() << dec << "): "; line = ReadString(); if ((line[0] == 'L') || (line[0] == 'l')) { partitionType.ShowAllTypes(); } else { if (line.length() == 0) tempType = GetHexType(); else tempType = line; } // if/else } while (tempType == PartType::unusedPartType); partitionType = tempType; cout << "Changed type of partition to '" << partitionType.TypeName() << "'\n"; if (changeName) { SetDefaultDescription(); } // if } // GPTPart::ChangeType() gptfdisk-1.0.5/COPYING0000664000175000017500000004312213622612343014635 0ustar rodsmithrodsmith GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. gptfdisk-1.0.5/sgdisk.80000664000175000017500000006551613622612343015172 0ustar rodsmithrodsmith.\" Copyright 2011-2020 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU General Public License .TH "SGDISK" "8" "1.0.5" "Roderick W. Smith" "GPT fdisk Manual" .SH "NAME" sgdisk \- Command\-line GUID partition table (GPT) manipulator for Linux and Unix .SH "SYNOPSIS" .BI "sgdisk " [ options ] .I device .SH "DESCRIPTION" GPT fdisk is a text\-mode menu\-driven package for creation and manipulation of partition tables. It consists of two programs: the text\-mode interactive \fBgdisk\fR and the command\-line \fBsgdisk\fR. Either program 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 GUID partition table. This man page documents the command\-line \fBsgdisk\fR program. Some advanced data manipulation and 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. 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 \fBsgdisk\fR program employs a user interface that's based entirely on the command line, making it suitable for use in scripts or by experts who want to make one or two quick changes to a disk. (The program may query the user when certain errors are encountered, though.) The program's name is based on \fBsfdisk\fR, but the user options of the two programs are entirely different from one another. Ordinarily, \fBsgdisk\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; \fBsgdisk\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 exclusively. Thus, GPT data structures, and therefore \fBsgdisk\fR, do not need to deal with CHS geometries and all the problems they create. 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, \fBsgdisk\fR, or GNU Parted programs. Upon start, \fBsgdisk\fR attempts to identify the partition type in use on the disk. If it finds valid GPT data, \fBsgdisk\fR will use it. If \fBsgdisk\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. If you specify any option that results in changes to an MBR or BSD disklabel, \fBsgdisk\fR ignores those changes unless the \fI\-g\fR (\fI\-\-mbrtogpt\fR), \fI\-z\fR (\fI\-\-zap\fR), or \fI\-Z\fR (\fI\-\-zap\-all\fR) option is used. If you use the \fI\-g\fR option, \fBsgdisk\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. 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 \fI\-s\fR (\fI\-\-sort\fR) option, if you like. (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 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 \fIEFI System Partition\fR (\fBgdisk\fR internal code 0xEF00) formatted as FAT\-32. I recommended making this partition 550 MiB. (Smaller ESPs are common, but some EFIs have flaky FAT drivers that necessitate a larger partition for reliable operation.) 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 \fIBIOS Boot Partition\fR (\fBgdisk\fR internal code 0xEF02), in which the secondary boot loader is stored, possibly without the benefit of a filesystem. (GRUB2 may optionally use such a partition.) This partition can typically be quite small (roughly 32 to 200 KiB, although 1 MiB is more common in practice), but you should consult your boot loader documentation for details. .TP .B * If Windows is to boot from a GPT disk, a partition of type \fIMicrosoft Reserved\fR (\fBsgdisk\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 FAT partitions as this type, which actually makes the partition unusable for normal file storage in both Windows and Mac OS X.) .TP .B * Some OSes' GPT utilities create some blank space (typically 128 MiB) after each partition. The intent is to enable future disk utilities to use this space. Such free space is not required of GPT disks, but creating it may help in future disk maintenance. .SH "OPTIONS" Some options take no arguments, others take one argument (typically a partition number), and others take compound arguments with colon delimitation. For instance, \fI\-n\fR (\fI\-\-new\fR) takes a partition number, a starting sector number, and an ending sector number, as in \fBsgdisk \-n 2:2000:50000 /dev/sdc\fR, which creates a new partition, numbered 2, starting at sector 2000 an ending at sector 50,000, on \fI/dev/sdc\fR. Unrelated options may be combined; however, some such combinations will be nonsense (such as deleting a partition and then changing its GUID type code). \fBsgdisk\fR interprets options in the order in which they're entered, so effects can vary depending on order. For instance, \fBsgdisk \-s \-d 2\fR sorts the partition table entries and then deletes partition 2 from the newly\-sorted list; but \fBsgdisk \-d 2 \-s\fR deletes the original partition 2 and then sorts the modified partition table. Error checking and opportunities to correct mistakes in \fBsgdisk\fR are minimal. Although the program endeavors to keep the GPT data structures legal, it does not prompt for verification before performing its actions. Unless you require a command\-line\-driven program, you should use the interactive \fBgdisk\fR instead of \fBsgdisk\fR, since \fBgdisk\fR allows you to quit without saving your changes, should you make a mistake. Although \fBsgdisk\fR is based on the same partition\-manipulation code as \fBgdisk\fR, \fBsgdisk\fR implements fewer features than its interactive sibling. Options available in \fBsgdisk\fR are: .TP .B \-a, \-\-set\-alignment=value Set the sector alignment multiple. GPT fdisk aligns the start of partitions to sectors that are multiples of this value, which defaults to 1 MiB (2048 on disks with 512-byte sectors) on freshly formatted disks. This alignment value is necessary to obtain optimum performance with Western Digital Advanced Format and similar drives with larger physical than logical sector sizes, with some types of RAID arrays, and with SSD devices. .TP .B \-A, \-\-attributes=list|[partnum:show|or|nand|xor|=|set|clear|toggle|get[:bitnum|hexbitmask]] View or set partition attributes. Use \fIlist\fR to see defined (known) attribute values. Omit the partition number (and even the device filename) when using this option. The others require a partition number. The \fIshow\fR and \fIget\fR options show the current attribute settings (all attributes or for a particular bit, respectively). The \fIor\fR, \fInand\fR, \fIxor\fR, \fI=\fR, \fIset\fR, \fIclear\fR, and \fItoggle\fR options enable you to change the attribute bit value. The \fIset\fR, \fIclear\fR, \fItoggle\fR, and \fIget\fR options work on a bit number; the others work on a hexadecimal bit mask. For example, type \fBsgdisk -A 4:set:2 /dev/sdc\fR to set the bit 2 attribute (legacy BIOS bootable) on partition 4 on \fI/dev/sdc\fR. .TP .B \-b, \-\-backup=file Save partition data to a backup file. You can back up your current in\-memory 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 backup is of the current in\-memory data structures, so if you launch the program, make changes, and then use this option, the backup will reflect your changes. If the GPT data structures are damaged, the backup may not accurately reflect the damaged state; instead, they will reflect GPT fdisk's first\-pass interpretation of the GPT. .TP .B \-c, \-\-change\-name=partnum:name Change the GPT name of a partition. This name is encoded as a UTF\-16 string, but proper entry and display of anything beyond basic ASCII values requires suitable locale and font support. For the most part, Linux ignores the partition name, but it may be important in some OSes. If you want to set a name that includes a space, enclose it in quotation marks, as in \fIsgdisk \-c 1:"Sample Name" /dev/sdb\fR. Note that the GPT name of a partition is distinct from the filesystem name, which is encoded in the filesystem's data structures. .TP .B \-C, \-\-recompute-chs Recompute CHS values in protective or hybrid MBR. This option can sometimes help if a disk utility, OS, or BIOS doesn't like the CHS values used by the partitions in the protective or hybrid MBR. In particular, the GPT specification requires a CHS value of 0xFFFFFF for over-8GiB partitions, but this value is technically illegal by the usual standards. Some BIOSes hang if they encounter this value. This option will recompute a more normal CHS value -- 0xFEFFFF for over-8GiB partitions, enabling these BIOSes to boot. .TP .B \-d, \-\-delete=partnum 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, \fBgdisk\fR deletes it, as well, and expands any adjacent 0xEE (EFI GPT) MBR protective partition to fill the new free space. .TP .B \-D, \-\-display\-alignment Display current sector alignment value. Partitions will be created on multiples of the sector value reported by this option. You can change the alignment value with the \-a option. .TP .B \-e, \-\-move\-second\-header Move backup GPT data structures to the end of the disk. Use this option if you've added disks to a RAID array, thus creating a virtual disk with space that follows the backup GPT data structures. This command moves the backup GPT data structures to the end of the disk, where they belong. .TP .B \-E, \-\-end\-of\-largest Displays the sector number of the end of the largest available block of sectors on the disk. A script may store this value and pass it back as part of \fI\-n\fR's option to create a partition. If no unallocated sectors are available, this function returns the value 0. .TP .B \-f, \-\-first\-in\-largest Displays the sector number of the start of the largest available block of sectors on the disk. A script may store this value and pass it back as part of \fI\-n\fR's option to create a partition. If no unallocated sectors are available, this function returns the value 0. Note that this parameter is blind to partition alignment; when you actually create a partition, its start point might be changed from this value. .TP .B \-F, \-\-first\-aligned\-in\-largest Similar to \fI\-f\fR (\fI\-\-first\-in\-largest\fR), except returns the sector number with the current alignment correction applied. Use this function if you need to compute the actual partition start point rather than a theoretical start point or the actual start point if you set the alignment value to 1. .TP .B \-g, \-\-mbrtogpt Convert an MBR or BSD disklabel disk to a GPT disk. As a safety measure, use of this option is required on MBR or BSD disklabel disks if you intend to save your changes, in order to prevent accidentally damaging such disks. .TP .B \-G, \-\-randomize\-guids Randomize the disk's GUID and all partitions' unique GUIDs (but not their partition type code GUIDs). This function may be used after cloning a disk in order to render all GUIDs once again unique. .TP .B \-h, \-\-hybrid Create a hybrid MBR. This option takes from one to three partition numbers, separated by colons, as arguments. You may optionally specify a final partition "EE" to indicate that the EFI GPT (type 0xEE) should be placed last in the table, otherwise it will be placed first, followed by the partition(s) you specify. Their type codes are based on the GPT fdisk type codes divided by 0x0100, which is usually correct for Windows partitions. If the active/bootable flag should be set, you must do so in another program, such as \fBfdisk\fR. The \fBgdisk\fR program offers additional hybrid MBR creation options. .TP .B \-i, \-\-info=partnum Show detailed partition information. The summary information produced by the \fI\-p\fR command necessarily omits many details, such as the partition's unique GUID and the translation of \fBsgdisk\fR's internal partition type code to a plain type name. The \fI\-i\fR option displays this information for a single partition. .TP .B \-j, \-\-adjust\-main\-table=sector Adjust the location of the main partition table. This value is normally 2, but it may need to be increased in some cases, such as when a system\-on\-chip (SoC) is hard\-coded to read boot code from sector 2. I recommend against adjusting this value unless doing so is absolutely necessary. .TP .B \-l, \-\-load\-backup=file Load partition data from a backup file. This option is the reverse of the \fI\-b\fR option. Note that restoring partition data from anything but the original disk is not recommended. This option will work even if the disk's original partition table is bad; however, most other options on the same command line will be ignored. .TP .B \-L, \-\-list\-types Display a summary of partition types. GPT uses a GUID to identify partition types for particular OSes and purposes. For ease of data entry, \fBsgdisk\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, the codes for all varieties of FAT and NTFS partition correspond to a single GPT code (entered as 0x0700 in \fBsgdisk\fR). Some OSes use a single MBR code but employ many more codes in GPT. For these, \fBsgdisk\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 \fBgdisk\fR and \fBsgdisk\fR. This option does not require you to specify a valid disk device filename. .TP .B \-m, \-\-gpttombr Convert disk from GPT to MBR form. This option takes from one to four partition numbers, separated by colons, as arguments. Their type codes are based on the GPT fdisk type codes divided by 0x0100. If the active/bootable flag should be set, you must do so in another program, such as \fBfdisk\fR. The \fBgdisk\fR program offers additional MBR conversion options. It is not possible to convert more than four partitions from GPT to MBR form or to convert partitions that start above the 2TiB mark or that are larger than 2TiB. .TP .B \-n, \-\-new=partnum:start:end Create a new partition. 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 kibibytes (K), mebibytes (M), gibibytes (G), tebibytes (T), or pebibytes (P); 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 default range by preceding the number by a '+' or '\-' symbol, as in \fI\fB+2G\fR\fR to specify a point 2GiB after the default start sector, or \fI\fB\-200M\fR\fR to specify a point 200MiB before the last available sector. A start or end value of 0 specifies the default value, which is the start of the largest available block for the start sector and the end of the same block for the end sector. A partnum value of 0 causes the program to use the first available partition number. Subsequent uses of the \fI\-A\fR, \fI\-c\fR, \fI\-t\fR, and \fI\-u\fR options may also use \fI0\fR to refer to the same partition. .TP .B \-N, \-\-largest\-new=num Create a new partition that fills the largest available block of space on the disk. You can use the \fI\-a\fR (\fI\-\-set\-alignment\fR) option to adjust the alignment, if desired. A num value of 0 causes the program to use the first available partition number. .TP .B \-o, \-\-clear Clear out all partition data. This includes GPT header data, all partition definitions, and the protective MBR. Note that this operation will, like most other operations, fail on a damaged disk. If you want to prepare a disk you know to be damaged for GPT use, you should first wipe it with -Z and then partition it normally. This option will work even if the disk's original partition table is bad; however, most other options on the same command line will be ignored. .TP .B \-O, \-\-print\-mbr Display basic \fIMBR\fR partition summary data. This includes partition numbers, starting and ending sector numbers, partition sizes, MBR partition types codes, and partition names. This option is useful mainly for diagnosing partition table problems, particularly on disks with hybrid MBRs. .TP .B \-p, \-\-print Display basic GPT partition summary data. This includes partition numbers, starting and ending sector numbers, partition sizes, \fBsgdisk\fR's partition types codes, and partition names. For additional information, use the \fI\-i\fR (\fI\-\-info\fR) option. .TP .B \-P, \-\-pretend Pretend to make specified changes. In\-memory GPT data structures are altered according to other parameters, but changes are not written to disk. .TP .B \-r, \-\-transpose Swap two partitions' entries in the partition table. One or both partitions may be empty, although swapping two empty partitions is pointless. For instance, if partitions 1\-4 are defined, transposing 1 and 5 results in a table with partitions numbered from 2\-5. Transposing partitions in this way has no effect on their disk space allocation; it only alters their order in the partition table. .TP .B \-R, \-\-replicate=second_device_filename Replicate the main device's partition table on the specified second device. Note that the replicated partition table is an exact copy, including all GUIDs; if the device should have its own unique GUIDs, you should use the \-G option on the new disk. .TP .B \-s, \-\-sort 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 sort partitions whenever they make changes. Such changes will be reflected in your device filenames, so you may need to edit \fI/etc/fstab\fR if you use this option. .TP .B \-t, \-\-typecode=partnum:{hexcode|GUID} Change a single partition's type code. You enter the type code using either a two\-byte hexadecimal number, as described earlier, or a fully-specified GUID value, such as EBD0A0A2-B9E5-4433-87C0-68B6B72699C7. .TP .B \-T, \-\-transform\-bsd=partnum 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. \fBsgdisk\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 \fBsgdisk\fR being unable to convert a BSD disklabel is high compared to the likelihood of problems with an MBR conversion. .TP .B \-u, \-\-partition-guid=partnum:guid Set the partition unique GUID for an individual partition. The GUID may be a complete GUID or 'R' to set a random GUID. .TP .B \-U, \-\-disk-guid=guid Set the GUID for the disk. The GUID may be a complete GUID or 'R' to set a random GUID. .TP .B \-\-usage Print a brief summary of available options. .TP .B \-v, \-\-verify 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 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. This option will work even if the disk's original partition table is bad; however, most other options on the same command line will be ignored. .TP .B \-V, \-\-version Display program version information. This option may be used without specifying a device filename. .TP .B \-z, \-\-zap Zap (destroy) the GPT data structures and then exit. Use this option if you want to repartition a GPT disk using \fBfdisk\fR or some other GPT\-unaware program. This option destroys only the GPT data structures; it leaves the MBR intact. This makes it useful for wiping out GPT data structures after a disk has been repartitioned for MBR using a GPT\-unaware utility; however, there's a risk that it will damage boot loaders or even the start of the first or end of the last MBR partition. If you use it on a valid GPT disk, the MBR will be left with an inappropriate EFI GPT (0xEE) partition definition, which you can delete using another utility. .TP .B \-Z, \-\-zap\-all Zap (destroy) the GPT and MBR data structures and then exit. This option works much like \fI\-z\fR, but as it wipes the MBR as well as the GPT, it's more suitable if you want to repartition a disk after using this option, and completely unsuitable if you've already repartitioned the disk. .TP .B \-?, \-\-help Print a summary of options. .SH "RETURN VALUES" \fBsgdisk\fR returns various values depending on its success or failure: .TP .B 0 Normal program execution .TP .B 1 Too few arguments .TP .B 2 An error occurred while reading the partition table .TP .B 3 Non\-GPT disk detected and no \fI\-g\fR option, but operation requires a write action .TP .B 4 An error prevented saving changes .TP .B 5 An error occurred while reading standard input (should never occur with sgdisk, but may with gdisk) .TP .B 8 Disk replication operation (-R) failed .SH "BUGS" Known bugs and limitations include: .TP .B * 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. .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 \fBgpt\fR, \fBfdisk\fR, and \fBdd\fR.) This limitation can be overcome by typing \fBsysctl kern.geom.debugflags=16\fR at a shell prompt. .TP .B * The fields used to display the start and end sector numbers for partitions in the \fI\-p\fR option are 14 characters wide. This translates to a limitation of about 45 PiB. On larger disks, the displayed columns will go out of alignment. .TP .B * 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 \fIbasicmbr.h\fR source code file and recompiling; however, such a change will require using a larger\-than\-normal partition table. (The limit of 128 partitions was chosen because that number equals the 128 partitions supported by the most common partition table size.) .TP .B * 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 prior to conversion with \fBgdisk\fR. .TP .B * MBR conversions work only if the disk has correct LBA partition descriptors. These descriptors should be present on any disk over 8 GiB in size or on smaller disks partitioned with any but very ancient software. .TP .B * BSD disklabel support can create first and/or last partitions that overlap with the GPT data structures. This can sometimes be compensated by adjusting the partition table size, but in extreme cases the affected 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, converted in a way that creates overlaps with other partitions, or converted with incorrect start or end values. Use this feature with 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 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 recovery & transformation menu) or abandoning GPT in favor of MBR may be your only options in this case. .PP .SH "AUTHORS" Primary author: Roderick W. Smith (rodsmith@rodsbooks.com) Contributors: * Yves Blusseau (1otnwmz02@sneakemail.com) * David Hubbard (david.c.hubbard@gmail.com) * Justin Maggard (justin.maggard@netgear.com) * Dwight Schauer (dschauer@gmail.com) * Florian Zumbiehl (florz@florz.de) .SH "SEE ALSO" .BR cfdisk (8), .BR cgdisk (8), .BR fdisk (8), .BR gdisk (8), .BR mkfs (8), .BR parted (8), .BR sfdisk (8), .BR fixparts (8). \fIhttp://en.wikipedia.org/wiki/GUID_Partition_Table\fR \fIhttp://developer.apple.com/technotes/tn2006/tn2166.html\fR \fIhttp://www.rodsbooks.com/gdisk/\fR .SH "AVAILABILITY" The \fBsgdisk\fR command is part of the \fIGPT fdisk\fR package and is available from Rod Smith. gptfdisk-1.0.5/mbrpart.h0000664000175000017500000000756413622612343015434 0ustar rodsmithrodsmith/* MBRPart class, part of GPT fdisk program family. Copyright (C) 2011 Roderick W. Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef MBRPART_H #define MBRPART_H #include #define MAX_HEADS 255 /* numbered 0 - 254 */ #define MAX_SECSPERTRACK 63 /* numbered 1 - 63 */ #define MAX_CYLINDERS 1024 /* numbered 0 - 1023 */ #define NONE 0 /* don't include partition when writing */ #define PRIMARY 1 /* write partition as primary */ #define LOGICAL 2 /* write partition as logical */ #define EBR 4 /* sector is used as an EBR or MBR */ #define INVALID 8 /* sector number is too large for disk */ using namespace std; // Data for a single MBR partition record // Note that firstSector and lastSector are in CHS addressing, which // splits the bits up in a weird way. // On read or write of MBR entries, firstLBA is an absolute disk sector. // On read of logical entries, it's relative to the EBR record for that // partition. When writing EBR records, it's relative to the extended // partition's start. #pragma pack(1) struct MBRRecord { uint8_t status; uint8_t firstSector[3]; uint8_t partitionType; uint8_t lastSector[3]; uint32_t firstLBA; // see above uint32_t lengthLBA; }; // struct MBRRecord #pragma pack () class MBRPart { protected: uint8_t status; uint8_t firstSector[3]; uint8_t partitionType; uint8_t lastSector[3]; uint32_t firstLBA; // see above uint32_t lengthLBA; int includeAs; // PRIMARY, LOGICAL, or NONE int canBeLogical; int canBePrimary; static uint32_t numHeads; static uint32_t numSecspTrack; static uint64_t diskSize; static uint32_t blockSize; static int numInstances; public: MBRPart(); MBRPart(const MBRPart& other); virtual ~MBRPart(); virtual MBRPart& operator=(const MBRPart& orig); virtual MBRPart& operator=(const struct MBRRecord& orig); bool operator<(const MBRPart &other) const; // Set information on partitions or disks... void SetGeometry(uint32_t heads, uint32_t sectors, uint64_t ds, uint32_t bs); void Empty(void); void SetStartLBA(uint64_t s); void SetLengthLBA(uint64_t l); void SetLocation(uint64_t start, uint64_t length); int SetType(uint8_t typeCode, int isExtended = 0); void SetStatus(uint8_t s) {status = s;} void SetInclusion(int status = PRIMARY) {includeAs = status;} void SetCanBeLogical(int c) {canBeLogical = c;} void SetCanBePrimary(int c) {canBePrimary = c;} void StoreInStruct(struct MBRRecord *theStruct); // Get information on partitions or disk.... uint8_t GetType(void) {return partitionType;} uint8_t GetStatus(void) {return status;} uint64_t GetStartLBA(void) {return firstLBA;} uint64_t GetLengthLBA(void) {return lengthLBA;} uint64_t GetLastLBA(void) const; uint8_t GetInclusion(void) {return includeAs;} int CanBeLogical(void) {return canBeLogical;} int CanBePrimary(void) {return canBePrimary;} int DoTheyOverlap (const MBRPart& other); // Adjust information on partitions or disks... int RecomputeCHS(void); int LBAtoCHS(uint32_t lba, uint8_t * chs); void ReverseByteOrder(void); // User I/O... void ShowData(int isGpt); }; // MBRPart #endif // MBRPART_H gptfdisk-1.0.5/crc32.h0000664000175000017500000000114213622612343014663 0ustar rodsmithrodsmith/* * efone - Distributed internet phone system. * * (c) 1999,2000 Krzysztof Dabrowski * (c) 1999,2000 ElysiuM deeZine * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */ /* based on implementation by Finn Yannick Jacobs. */ #include void chksum_crc32gentab (); uint32_t chksum_crc32 (unsigned char *block, unsigned int length); extern unsigned int crc_tab[256]; gptfdisk-1.0.5/support.cc0000664000175000017500000003070613622612343015631 0ustar rodsmithrodsmith// support.cc // Non-class support functions for gdisk program. // Primarily by Rod Smith, February 2009, but with a few functions // copied from other sources (see attributions below). /* This program is copyright (c) 2009-2018 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 #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include #include #include #include "support.h" #include // As of 1/2010, BLKPBSZGET is very new, so I'm explicitly defining it if // it's not already defined. This should become unnecessary in the future. // Note that this is a Linux-only ioctl.... #ifndef BLKPBSZGET #define BLKPBSZGET _IO(0x12,123) #endif using namespace std; // Reads a string from stdin, returning it as a C++-style string. // Note that the returned string will NOT include the carriage return // entered by the user. #ifdef EFI extern int __sscanf( const char * str , const char * format , ... ) ; string ReadString(void) { string inString; char efiString[256]; int stringLength; if (fgets(efiString, 255, stdin) != NULL) { stringLength = strlen(efiString); if ((stringLength > 0) && (efiString[stringLength - 1] == '\n')) efiString[stringLength - 1] = '\0'; inString = efiString; } else { inString = ""; } return inString; } // ReadString() #else string ReadString(void) { string inString; cout << flush; getline(cin, inString); if (!cin.good()) exit(5); return inString; } // ReadString() #endif // Get a numeric value from the user, between low and high (inclusive). // Keeps looping until the user enters a value within that range. // If user provides no input, def (default value) is returned. // (If def is outside of the low-high range, an explicit response // is required.) uint64_t GetNumber(uint64_t low, uint64_t high, uint64_t def, const string & prompt) { uint64_t response, num; char line[255]; if (low != high) { // bother only if low and high differ... do { cout << prompt << flush; cin.getline(line, 255); if (!cin.good()) exit(5); num = sscanf(line, "%" PRIu64, &response); if (num == 1) { // user provided a response if ((response < low) || (response > high)) cout << "Value out of range\n"; } else { // user hit enter; return default response = def; } // if/else } while ((response < low) || (response > high)); } else { // low == high, so return this value cout << "Using " << low << "\n"; response = low; } // else return (response); } // GetNumber() // Gets a Y/N response (and converts lowercase to uppercase) char GetYN(void) { char response; string line; bool again = 0 ; do { if ( again ) { cout << "Your option? " ; } again = 1 ; cout << "(Y/N): " << flush; line = ReadString(); response = toupper(line[0]); } while ((response != 'Y') && (response != 'N')); return response; } // GetYN(void) // Obtains a sector number, between low and high, from the // user, accepting values prefixed by "+" to add sectors to low, // or the same with "K", "M", "G", "T", or "P" as suffixes to add // kilobytes, megabytes, gigabytes, terabytes, or petabytes, // respectively. If a "-" prefix is used, use the high value minus // the user-specified number of sectors (or KiB, MiB, etc.). Use the // def value as the default if the user just hits Enter. The sSize is // the sector size of the device. uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, uint64_t sSize, const string & prompt) { uint64_t response; char line[255]; do { cout << prompt; cin.getline(line, 255); if (!cin.good()) exit(5); response = IeeeToInt(line, sSize, low, high, def); } while ((response < low) || (response > high)); return response; } // GetSectorNum() // Convert an IEEE-1541-2002 value (K, M, G, T, P, or E) to its equivalent in // number of sectors. If no units are appended, interprets as the number // of sectors; otherwise, interprets as number of specified units and // converts to sectors. For instance, with 512-byte sectors, "1K" converts // to 2. If value includes a "+", adds low and subtracts 1; if SIValue // inclues a "-", subtracts from high. If IeeeValue is empty, returns def. // Returns final sector value. In case inValue is invalid, returns 0 (a // sector value that's always in use on GPT and therefore invalid); and if // inValue works out to something outside the range low-high, returns the // computed value; the calling function is responsible for checking the // validity of this value. // NOTE: There's a difference in how GCC and VC++ treat oversized values // (say, "999999999999999999999") read via the ">>" operator; GCC turns // them into the maximum value for the type, whereas VC++ turns them into // 0 values. The result is that IeeeToInt() returns UINT64_MAX when // compiled with GCC (and so the value is rejected), whereas when VC++ // is used, the default value is returned. uint64_t IeeeToInt(string inValue, uint64_t sSize, uint64_t low, uint64_t high, uint64_t def) { uint64_t response = def, bytesPerUnit = 1, mult = 1, divide = 1; size_t foundAt = 0; char suffix = ' ', plusFlag = ' '; string suffixes = "KMGTPE"; int badInput = 0; // flag bad input; once this goes to 1, other values are irrelevant if (sSize == 0) { sSize = SECTOR_SIZE; cerr << "Bug: Sector size invalid in IeeeToInt()!\n"; } // if // Remove leading spaces, if present while (inValue[0] == ' ') inValue.erase(0, 1); // If present, flag and remove leading plus or minus sign if ((inValue[0] == '+') || (inValue[0] == '-')) { plusFlag = inValue[0]; inValue.erase(0, 1); } // if // Extract numeric response and, if present, suffix istringstream inString(inValue); if (((inString.peek() < '0') || (inString.peek() > '9')) && (inString.peek() != -1)) badInput = 1; inString >> response >> suffix; suffix = toupper(suffix); // If no response, or if response == 0, use default (def) if ((inValue.length() == 0) || (response == 0)) { response = def; suffix = ' '; plusFlag = ' '; } // if // Find multiplication and division factors for the suffix foundAt = suffixes.find(suffix); if (foundAt != string::npos) { bytesPerUnit = UINT64_C(1) << (10 * (foundAt + 1)); mult = bytesPerUnit / sSize; divide = sSize / bytesPerUnit; } // if // Adjust response based on multiplier and plus flag, if present if (mult > 1) { if (response > (UINT64_MAX / mult)) badInput = 1; else response *= mult; } else if (divide > 1) { response /= divide; } // if/elseif if (plusFlag == '+') { // Recompute response based on low part of range (if default == high // value, which should be the case when prompting for the end of a // range) or the defaut value (if default != high, which should be // the case for the first sector of a partition). if (def == high) { if (response > 0) response--; if (response > (UINT64_MAX - low)) badInput = 1; else response = response + low; } else { if (response > (UINT64_MAX - def)) badInput = 1; else response = response + def; } // if/else } else if (plusFlag == '-') { if (response > high) badInput = 1; else response = high - response; } // if if (badInput) response = UINT64_C(0); return response; } // IeeeToInt() // Takes a size and converts this to a size in IEEE-1541-2002 units (KiB, MiB, // GiB, TiB, PiB, or EiB), returned in C++ string form. The size is either in // units of the sector size or, if that parameter is omitted, in bytes. // (sectorSize defaults to 1). Note that this function uses peculiar // manual computation of decimal value rather than simply setting // theValue.precision() because this isn't possible using the available // EFI library. string BytesToIeee(uint64_t size, uint32_t sectorSize) { uint64_t sizeInIeee; uint64_t previousIeee; float decimalIeee; uint64_t index = 0; string units, prefixes = " KMGTPEZ"; ostringstream theValue; sizeInIeee = previousIeee = size * (uint64_t) sectorSize; while ((sizeInIeee > 1024) && (index < (prefixes.length() - 1))) { index++; previousIeee = sizeInIeee; sizeInIeee /= 1024; } // while if (prefixes[index] == ' ') { theValue << sizeInIeee << " bytes"; } else { units = " iB"; units[1] = prefixes[index]; decimalIeee = ((float) previousIeee - ((float) sizeInIeee * 1024.0) + 51.2) / 102.4; if (decimalIeee >= 10.0) { decimalIeee = 0.0; sizeInIeee++; } theValue << sizeInIeee << "." << (uint32_t) decimalIeee << units; } // if/else return theValue.str(); } // BytesToIeee() // Converts two consecutive characters in the input string into a // number, interpreting the string as a hexadecimal number, starting // at the specified position. unsigned char StrToHex(const string & input, unsigned int position) { unsigned char retval = 0x00; unsigned int temp; if (input.length() > position) { sscanf(input.substr(position, 2).c_str(), "%x", &temp); retval = (unsigned char) temp; } // if return retval; } // StrToHex() // Returns 1 if input can be interpreted as a hexadecimal number -- // all characters must be spaces, digits, or letters A-F (upper- or // lower-case), with at least one valid hexadecimal digit; with the // exception of the first two characters, which may be "0x"; otherwise // returns 0. int IsHex(string input) { int isHex = 1, foundHex = 0, i; if (input.substr(0, 2) == "0x") input.erase(0, 2); for (i = 0; i < (int) input.length(); i++) { if ((input[i] < '0') || (input[i] > '9')) { if ((input[i] < 'A') || (input[i] > 'F')) { if ((input[i] < 'a') || (input[i] > 'f')) { if ((input[i] != ' ') && (input[i] != '\n')) { isHex = 0; } } else foundHex = 1; } else foundHex = 1; } else foundHex = 1; } // for if (!foundHex) isHex = 0; return isHex; } // IsHex() // Return 1 if the CPU architecture is little endian, 0 if it's big endian.... int IsLittleEndian(void) { int littleE = 1; // assume little-endian (Intel-style) union { uint32_t num; unsigned char uc[sizeof(uint32_t)]; } endian; endian.num = 1; if (endian.uc[0] != (unsigned char) 1) { littleE = 0; } // if return (littleE); } // IsLittleEndian() // Reverse the byte order of theValue; numBytes is number of bytes void ReverseBytes(void* theValue, int numBytes) { char* tempValue = NULL; int i; tempValue = new char [numBytes]; if (tempValue != NULL) { memcpy(tempValue, theValue, numBytes); for (i = 0; i < numBytes; i++) ((char*) theValue)[i] = tempValue[numBytes - i - 1]; delete[] tempValue; } else { cerr << "Could not allocate memory in ReverseBytes()! Terminating\n"; exit(1); } // if/else } // ReverseBytes() // On Windows, display a warning and ask whether to continue. If the user elects // not to continue, exit immediately. void WinWarning(void) { #ifdef _WIN32 cout << "\a************************************************************************\n" << "Most versions of Windows cannot boot from a GPT disk except on a UEFI-based\n" << "computer, and most varieties prior to Vista cannot read GPT disks. Therefore,\n" << "you should exit now unless you understand the implications of converting MBR\n" << "to GPT or creating a new GPT disk layout!\n" << "************************************************************************\n\n"; cout << "Are you SURE you want to continue? "; if (GetYN() != 'Y') exit(0); #endif } // WinWarning() // Returns the input string in lower case string ToLower(const string& input) { string lower = input; // allocate correct size through copy transform(input.begin(), input.end(), lower.begin(), ::tolower); return lower; } // ToLower() gptfdisk-1.0.5/basicmbr.cc0000664000175000017500000016532313622612343015703 0ustar rodsmithrodsmith/* basicmbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition data. */ /* Initial coding by Rod Smith, January to February, 2009 */ /* This program is copyright (c) 2009-2013 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 #include #include #include #include #include #include #include #include #include #include #include "mbr.h" #include "support.h" using namespace std; /**************************************** * * * MBRData class and related structures * * * ****************************************/ BasicMBRData::BasicMBRData(void) { blockSize = SECTOR_SIZE; diskSize = 0; device = ""; state = invalid; numHeads = MAX_HEADS; numSecspTrack = MAX_SECSPERTRACK; myDisk = NULL; canDeleteMyDisk = 0; // memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint32_t)); EmptyMBR(); } // BasicMBRData default constructor BasicMBRData::BasicMBRData(const BasicMBRData & orig) { int i; if (&orig != this) { memcpy(code, orig.code, 440); diskSignature = orig.diskSignature; nulls = orig.nulls; MBRSignature = orig.MBRSignature; blockSize = orig.blockSize; diskSize = orig.diskSize; numHeads = orig.numHeads; numSecspTrack = orig.numSecspTrack; canDeleteMyDisk = orig.canDeleteMyDisk; device = orig.device; state = orig.state; myDisk = new DiskIO; if (myDisk == NULL) { cerr << "Unable to allocate memory in BasicMBRData copy constructor! Terminating!\n"; exit(1); } // if if (orig.myDisk != NULL) myDisk->OpenForRead(orig.myDisk->GetName()); for (i = 0; i < MAX_MBR_PARTS; i++) { partitions[i] = orig.partitions[i]; } // for } // if } // BasicMBRData copy constructor BasicMBRData::BasicMBRData(string filename) { blockSize = SECTOR_SIZE; diskSize = 0; device = filename; state = invalid; numHeads = MAX_HEADS; numSecspTrack = MAX_SECSPERTRACK; myDisk = NULL; canDeleteMyDisk = 0; // memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint32_t)); // Try to read the specified partition table, but if it fails.... if (!ReadMBRData(filename)) { EmptyMBR(); device = ""; } // if } // BasicMBRData(string filename) constructor // Free space used by myDisk only if that's OK -- sometimes it will be // copied from an outside source, in which case that source should handle // it! BasicMBRData::~BasicMBRData(void) { if (canDeleteMyDisk) delete myDisk; } // BasicMBRData destructor // Assignment operator -- copy entire set of MBR data. BasicMBRData & BasicMBRData::operator=(const BasicMBRData & orig) { int i; if (&orig != this) { memcpy(code, orig.code, 440); diskSignature = orig.diskSignature; nulls = orig.nulls; MBRSignature = orig.MBRSignature; blockSize = orig.blockSize; diskSize = orig.diskSize; numHeads = orig.numHeads; numSecspTrack = orig.numSecspTrack; canDeleteMyDisk = orig.canDeleteMyDisk; device = orig.device; state = orig.state; myDisk = new DiskIO; if (myDisk == NULL) { cerr << "Unable to allocate memory in BasicMBRData::operator=()! Terminating!\n"; exit(1); } // if if (orig.myDisk != NULL) myDisk->OpenForRead(orig.myDisk->GetName()); for (i = 0; i < MAX_MBR_PARTS; i++) { partitions[i] = orig.partitions[i]; } // for } // if return *this; } // BasicMBRData::operator=() /********************** * * * 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. int BasicMBRData::ReadMBRData(const string & deviceFilename) { int allOK = 1; if (myDisk == NULL) { myDisk = new DiskIO; if (myDisk == NULL) { cerr << "Unable to allocate memory in BasicMBRData::ReadMBRData()! Terminating!\n"; exit(1); } // if canDeleteMyDisk = 1; } // if if (myDisk->OpenForRead(deviceFilename)) { allOK = ReadMBRData(myDisk); } else { allOK = 0; } // if if (allOK) device = deviceFilename; return allOK; } // BasicMBRData::ReadMBRData(const string & deviceFilename) // 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 omitted from // in the partitions[] array; these partitions must be re-created when // the partition table is saved in MBR format. int BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) { int allOK = 1, i, logicalNum = 3; int err = 1; TempMBR tempMBR; if ((myDisk != NULL) && (myDisk != theDisk) && (canDeleteMyDisk)) { delete myDisk; canDeleteMyDisk = 0; } // if myDisk = theDisk; // Empty existing MBR data, including the logical partitions... EmptyMBR(0); if (myDisk->Seek(0)) if (myDisk->Read(&tempMBR, 512)) err = 0; if (err) { cerr << "Problem reading disk in BasicMBRData::ReadMBRData()!\n"; } else { for (i = 0; i < 440; i++) code[i] = tempMBR.code[i]; diskSignature = tempMBR.diskSignature; nulls = tempMBR.nulls; for (i = 0; i < 4; i++) { partitions[i] = tempMBR.partitions[i]; if (partitions[i].GetLengthLBA() > 0) partitions[i].SetInclusion(PRIMARY); } // for i... (reading all four partitions) MBRSignature = tempMBR.MBRSignature; ReadCHSGeom(); // Reverse the byte order, if necessary if (IsLittleEndian() == 0) { ReverseBytes(&diskSignature, 4); ReverseBytes(&nulls, 2); ReverseBytes(&MBRSignature, 2); for (i = 0; i < 4; i++) { partitions[i].ReverseByteOrder(); } // for } // if if (MBRSignature != MBR_SIGNATURE) { allOK = 0; state = invalid; } // if // Find disk size diskSize = myDisk->DiskSize(&err); // Find block size if (checkBlockSize) { blockSize = myDisk->GetBlockSize(); } // if (checkBlockSize) // Load logical partition data, if any is found.... if (allOK) { for (i = 0; i < 4; i++) { if ((partitions[i].GetType() == 0x05) || (partitions[i].GetType() == 0x0f) || (partitions[i].GetType() == 0x85)) { // Found it, so call a function to load everything from them.... logicalNum = ReadLogicalParts(partitions[i].GetStartLBA(), abs(logicalNum) + 1); if (logicalNum < 0) { cerr << "Error reading logical partitions! List may be truncated!\n"; } // if maxLogicals valid DeletePartition(i); } // if primary partition is extended } // for primary partition loop if (allOK) { // Loaded logicals OK state = mbr; } else { state = invalid; } // if } // if // Check to see if it's in GPT format.... if (allOK) { for (i = 0; i < 4; i++) { if (partitions[i].GetType() == UINT8_C(0xEE)) { state = gpt; } // if } // for } // if // If there's an EFI GPT partition, look for other partition types, // to flag as hybrid if (state == gpt) { for (i = 0 ; i < 4; i++) { if ((partitions[i].GetType() != UINT8_C(0xEE)) && (partitions[i].GetType() != UINT8_C(0x00))) state = hybrid; if (logicalNum != 3) cerr << "Warning! MBR Logical partitions found on a hybrid MBR disk! This is an\n" << "EXTREMELY dangerous configuration!\n\a"; } // for } // if (hybrid detection code) } // no initial error return allOK; } // BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) // This is a 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 times the // that index if there was a problem. (Some problems can leave valid logical // partition data.) // Parameters: // extendedStart = LBA of the start of the extended partition // partNum = number of first partition in extended partition (normally 4). int BasicMBRData::ReadLogicalParts(uint64_t extendedStart, int partNum) { struct TempMBR ebr; int i, another = 1, allOK = 1; uint8_t ebrType; uint64_t offset; uint64_t EbrLocations[MAX_MBR_PARTS]; offset = extendedStart; memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint64_t)); while (another && (partNum < MAX_MBR_PARTS) && (partNum >= 0) && (allOK > 0)) { for (i = 0; i < MAX_MBR_PARTS; i++) { if (EbrLocations[i] == offset) { // already read this one; infinite logical partition loop! cerr << "Logical partition infinite loop detected! This is being corrected.\n"; allOK = -1; partNum -= 1; } // if } // for EbrLocations[partNum] = offset; if (myDisk->Seek(offset) == 0) { // seek to EBR record cerr << "Unable to seek to " << offset << "! Aborting!\n"; allOK = -1; } if (myDisk->Read(&ebr, 512) != 512) { // Load the data.... cerr << "Error seeking to or reading logical partition data from " << offset << "!\nSome logical partitions may be missing!\n"; allOK = -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) { allOK = -1; cerr << "EBR signature for logical partition invalid; read 0x"; cerr.fill('0'); cerr.width(4); cerr.setf(ios::uppercase); cerr << hex << ebr.MBRSignature << ", but should be 0x"; cerr.width(4); cerr << MBR_SIGNATURE << dec << "\n"; cerr.fill(' '); } // if if ((partNum >= 0) && (partNum < MAX_MBR_PARTS) && (allOK > 0)) { // Sometimes an EBR points directly to another EBR, rather than defining // a logical partition and then pointing to another EBR. Thus, we skip // the logical partition when this is the case.... ebrType = ebr.partitions[0].partitionType; if ((ebrType == 0x05) || (ebrType == 0x0f) || (ebrType == 0x85)) { cout << "EBR points to an EBR!\n"; offset = extendedStart + ebr.partitions[0].firstLBA; } else { // Copy over the basic data.... partitions[partNum] = ebr.partitions[0]; // Adjust the start LBA, since it's encoded strangely.... partitions[partNum].SetStartLBA(ebr.partitions[0].firstLBA + offset); partitions[partNum].SetInclusion(LOGICAL); // Find the next partition (if there is one) if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum < (MAX_MBR_PARTS - 1))) { offset = extendedStart + ebr.partitions[1].firstLBA; partNum++; } else { another = 0; } // if another partition } // if/else } // if } // while() return (partNum * allOK); } // BasicMBRData::ReadLogicalPart() // Write the MBR data to the default defined device. This writes both the // MBR itself and any defined logical partitions, provided there's an // MBR extended partition. int BasicMBRData::WriteMBRData(void) { int allOK = 1; if (myDisk != NULL) { if (myDisk->OpenForWrite() != 0) { allOK = WriteMBRData(myDisk); cout << "Done writing data!\n"; } else { allOK = 0; } // if/else myDisk->Close(); } else allOK = 0; return allOK; } // BasicMBRData::WriteMBRData(void) // Save the MBR data to a file. This writes both the // MBR itself and any defined logical partitions. int BasicMBRData::WriteMBRData(DiskIO *theDisk) { int i, j, partNum, next, allOK = 1, moreLogicals = 0; uint64_t extFirstLBA = 0; uint64_t writeEbrTo; // 64-bit because we support extended in 2-4TiB range TempMBR tempMBR; allOK = CreateExtended(); if (allOK) { // First write the main MBR data structure.... memcpy(tempMBR.code, code, 440); tempMBR.diskSignature = diskSignature; tempMBR.nulls = nulls; tempMBR.MBRSignature = MBRSignature; for (i = 0; i < 4; i++) { partitions[i].StoreInStruct(&tempMBR.partitions[i]); if (partitions[i].GetType() == 0x0f) { extFirstLBA = partitions[i].GetStartLBA(); moreLogicals = 1; } // if } // for i... } // if allOK = allOK && WriteMBRData(tempMBR, theDisk, 0); // Set up tempMBR with some constant data for logical partitions... tempMBR.diskSignature = 0; for (i = 2; i < 4; i++) { tempMBR.partitions[i].firstLBA = tempMBR.partitions[i].lengthLBA = 0; tempMBR.partitions[i].partitionType = 0x00; for (j = 0; j < 3; j++) { tempMBR.partitions[i].firstSector[j] = 0; tempMBR.partitions[i].lastSector[j] = 0; } // for j } // for i partNum = FindNextInUse(4); writeEbrTo = (uint64_t) extFirstLBA; // Write logicals... while (allOK && moreLogicals && (partNum < MAX_MBR_PARTS) && (partNum >= 0)) { partitions[partNum].StoreInStruct(&tempMBR.partitions[0]); tempMBR.partitions[0].firstLBA = 1; // tempMBR.partitions[1] points to next EBR or terminates EBR linked list... next = FindNextInUse(partNum + 1); if ((next < MAX_MBR_PARTS) && (next > 0) && (partitions[next].GetStartLBA() > 0)) { tempMBR.partitions[1].partitionType = 0x0f; tempMBR.partitions[1].firstLBA = (uint32_t) (partitions[next].GetStartLBA() - extFirstLBA - 1); tempMBR.partitions[1].lengthLBA = (uint32_t) (partitions[next].GetLengthLBA() + 1); LBAtoCHS((uint64_t) tempMBR.partitions[1].firstLBA, (uint8_t *) &tempMBR.partitions[1].firstSector); LBAtoCHS(tempMBR.partitions[1].lengthLBA - extFirstLBA, (uint8_t *) &tempMBR.partitions[1].lastSector); } else { tempMBR.partitions[1].partitionType = 0x00; tempMBR.partitions[1].firstLBA = 0; tempMBR.partitions[1].lengthLBA = 0; moreLogicals = 0; } // if/else allOK = WriteMBRData(tempMBR, theDisk, writeEbrTo); writeEbrTo = (uint64_t) tempMBR.partitions[1].firstLBA + (uint64_t) extFirstLBA; partNum = next; } // while DeleteExtendedParts(); return allOK; } // BasicMBRData::WriteMBRData(DiskIO *theDisk) int BasicMBRData::WriteMBRData(const string & deviceFilename) { device = deviceFilename; return WriteMBRData(); } // BasicMBRData::WriteMBRData(const string & deviceFilename) // Write a single MBR record to the specified sector. Used by the like-named // function to write both the MBR and multiple EBR (for logical partition) // records. // Returns 1 on success, 0 on failure int BasicMBRData::WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector) { int i, allOK; // Reverse the byte order, if necessary if (IsLittleEndian() == 0) { ReverseBytes(&mbr.diskSignature, 4); ReverseBytes(&mbr.nulls, 2); ReverseBytes(&mbr.MBRSignature, 2); for (i = 0; i < 4; i++) { ReverseBytes(&mbr.partitions[i].firstLBA, 4); ReverseBytes(&mbr.partitions[i].lengthLBA, 4); } // for } // if // Now write the data structure... allOK = theDisk->OpenForWrite(); if (allOK && theDisk->Seek(sector)) { if (theDisk->Write(&mbr, 512) != 512) { allOK = 0; cerr << "Error " << errno << " when saving MBR!\n"; } // if } else { allOK = 0; cerr << "Error " << errno << " when seeking to MBR to write it!\n"; } // if/else theDisk->Close(); // Reverse the byte order back, if necessary if (IsLittleEndian() == 0) { ReverseBytes(&mbr.diskSignature, 4); ReverseBytes(&mbr.nulls, 2); ReverseBytes(&mbr.MBRSignature, 2); for (i = 0; i < 4; i++) { ReverseBytes(&mbr.partitions[i].firstLBA, 4); ReverseBytes(&mbr.partitions[i].lengthLBA, 4); } // for }// if return allOK; } // BasicMBRData::WriteMBRData(uint64_t sector) // Set a new disk device; used in copying one disk's partition // table to another disk. void BasicMBRData::SetDisk(DiskIO *theDisk) { int err; myDisk = theDisk; diskSize = theDisk->DiskSize(&err); canDeleteMyDisk = 0; ReadCHSGeom(); } // BasicMBRData::SetDisk() /******************************************** * * * Functions that display data for the user * * * ********************************************/ // Show the MBR data to the user, up to the specified maximum number // of partitions.... void BasicMBRData::DisplayMBRData(void) { int i; cout << "\nDisk size is " << diskSize << " sectors (" << BytesToIeee(diskSize, blockSize) << ")\n"; cout << "MBR disk identifier: 0x"; cout.width(8); cout.fill('0'); cout.setf(ios::uppercase); cout << hex << diskSignature << dec << "\n"; cout << "MBR partitions:\n\n"; if ((state == gpt) || (state == hybrid)) { cout << "Number Boot Start Sector End Sector Status Code\n"; } else { cout << " Can Be Can Be\n"; cout << "Number Boot Start Sector End Sector Status Logical Primary Code\n"; UpdateCanBeLogical(); } // for (i = 0; i < MAX_MBR_PARTS; i++) { if (partitions[i].GetLengthLBA() != 0) { cout.fill(' '); cout.width(4); cout << i + 1 << " "; partitions[i].ShowData((state == gpt) || (state == hybrid)); } // if cout.fill(' '); } // for } // BasicMBRData::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 BasicMBRData::ShowState(void) { switch (state) { case invalid: cout << " MBR: not present\n"; break; case gpt: cout << " MBR: protective\n"; break; case hybrid: cout << " MBR: hybrid\n"; break; case mbr: cout << " MBR: MBR only\n"; break; default: cout << "\a MBR: unknown -- bug!\n"; break; } // switch } // BasicMBRData::ShowState() /************************ * * * GPT Checks and fixes * * * ************************/ // Perform a very rudimentary check for GPT data on the disk; searches for // the GPT signature in the main and backup metadata areas. // Returns 0 if GPT data not found, 1 if main data only is found, 2 if // backup only is found, 3 if both main and backup data are found, and // -1 if a disk error occurred. int BasicMBRData::CheckForGPT(void) { int retval = 0, err; char signature1[9], signature2[9]; if (myDisk != NULL) { if (myDisk->OpenForRead() != 0) { if (myDisk->Seek(1)) { myDisk->Read(signature1, 8); signature1[8] = '\0'; } else retval = -1; if (myDisk->Seek(myDisk->DiskSize(&err) - 1)) { myDisk->Read(signature2, 8); signature2[8] = '\0'; } else retval = -1; if ((retval >= 0) && (strcmp(signature1, "EFI PART") == 0)) retval += 1; if ((retval >= 0) && (strcmp(signature2, "EFI PART") == 0)) retval += 2; } else { retval = -1; } // if/else myDisk->Close(); } else retval = -1; return retval; } // BasicMBRData::CheckForGPT() // Blanks the 2nd (sector #1, numbered from 0) and last sectors of the disk, // but only if GPT data are verified on the disk, and only for the sector(s) // with GPT signatures. // Returns 1 if operation completes successfully, 0 if not (returns 1 if // no GPT data are found on the disk). int BasicMBRData::BlankGPTData(void) { int allOK = 1, err; uint8_t blank[512]; memset(blank, 0, 512); switch (CheckForGPT()) { case -1: allOK = 0; break; case 0: break; case 1: if ((myDisk != NULL) && (myDisk->OpenForWrite())) { if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512))) allOK = 0; myDisk->Close(); } else allOK = 0; break; case 2: if ((myDisk != NULL) && (myDisk->OpenForWrite())) { if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) && (myDisk->Write(blank, 512) == 512))) allOK = 0; myDisk->Close(); } else allOK = 0; break; case 3: if ((myDisk != NULL) && (myDisk->OpenForWrite())) { if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512))) allOK = 0; if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) && (myDisk->Write(blank, 512) == 512))) allOK = 0; myDisk->Close(); } else allOK = 0; break; default: break; } // switch() return allOK; } // BasicMBRData::BlankGPTData /********************************************************************* * * * Functions that set or get disk metadata (CHS geometry, disk size, * * etc.) * * * *********************************************************************/ // Read the CHS geometry using OS calls, or if that fails, set to // the most common value for big disks (255 heads, 63 sectors per // track, & however many cylinders that computes to). void BasicMBRData::ReadCHSGeom(void) { int err; numHeads = myDisk->GetNumHeads(); numSecspTrack = myDisk->GetNumSecsPerTrack(); diskSize = myDisk->DiskSize(&err); blockSize = myDisk->GetBlockSize(); partitions[0].SetGeometry(numHeads, numSecspTrack, diskSize, blockSize); } // BasicMBRData::ReadCHSGeom() // Find the low and high used partition numbers (numbered from 0). // Return value is the number of partitions found. Note that the // *low and *high values are both set to 0 when no partitions // are found, as well as when a single partition in the first // position exists. Thus, the return value is the only way to // tell when no partitions exist. int BasicMBRData::GetPartRange(uint32_t *low, uint32_t *high) { uint32_t i; int numFound = 0; *low = MAX_MBR_PARTS + 1; // code for "not found" *high = 0; for (i = 0; i < MAX_MBR_PARTS; i++) { if (partitions[i].GetStartLBA() != UINT32_C(0)) { // it exists *high = i; // since we're counting up, set the high value // Set the low value only if it's not yet found... if (*low == (MAX_MBR_PARTS + 1)) *low = i; numFound++; } // if } // for // Above will leave *low pointing to its "not found" value if no partitions // are defined, so reset to 0 if this is the case.... if (*low == (MAX_MBR_PARTS + 1)) *low = 0; return numFound; } // GPTData::GetPartRange() // 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 BasicMBRData::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 >= ((uint64_t) 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] = (uint8_t) 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); } // BasicMBRData::LBAtoCHS() // Look for overlapping partitions. Also looks for a couple of non-error // conditions that the user should be told about. // Returns the number of problems found int BasicMBRData::FindOverlaps(void) { int i, j, numProbs = 0, numEE = 0, ProtectiveOnOne = 0; for (i = 0; i < MAX_MBR_PARTS; i++) { for (j = i + 1; j < MAX_MBR_PARTS; j++) { if ((partitions[i].GetInclusion() != NONE) && (partitions[j].GetInclusion() != NONE) && (partitions[i].DoTheyOverlap(partitions[j]))) { numProbs++; cout << "\nProblem: MBR partitions " << i + 1 << " and " << j + 1 << " overlap!\n"; } // if } // for (j...) if (partitions[i].GetType() == 0xEE) { numEE++; if (partitions[i].GetStartLBA() == 1) ProtectiveOnOne = 1; } // if } // for (i...) if (numEE > 1) cout << "\nCaution: More than one 0xEE MBR partition found. This can cause problems\n" << "in some OSes.\n"; if (!ProtectiveOnOne && (numEE > 0)) cout << "\nWarning: 0xEE partition doesn't start on sector 1. This can cause " << "problems\nin some OSes.\n"; return numProbs; } // BasicMBRData::FindOverlaps() // Returns the number of primary partitions, including the extended partition // required to hold any logical partitions found. int BasicMBRData::NumPrimaries(void) { int i, numPrimaries = 0, logicalsFound = 0; for (i = 0; i < MAX_MBR_PARTS; i++) { if (partitions[i].GetLengthLBA() > 0) { if (partitions[i].GetInclusion() == PRIMARY) numPrimaries++; if (partitions[i].GetInclusion() == LOGICAL) logicalsFound = 1; } // if } // for return (numPrimaries + logicalsFound); } // BasicMBRData::NumPrimaries() // Returns the number of logical partitions. int BasicMBRData::NumLogicals(void) { int i, numLogicals = 0; for (i = 0; i < MAX_MBR_PARTS; i++) { if (partitions[i].GetInclusion() == LOGICAL) numLogicals++; } // for return numLogicals; } // BasicMBRData::NumLogicals() // Returns the number of partitions (primaries plus logicals), NOT including // the extended partition required to house the logicals. int BasicMBRData::CountParts(void) { int i, num = 0; for (i = 0; i < MAX_MBR_PARTS; i++) { if ((partitions[i].GetInclusion() == LOGICAL) || (partitions[i].GetInclusion() == PRIMARY)) num++; } // for return num; } // BasicMBRData::CountParts() // Updates the canBeLogical and canBePrimary flags for all the partitions. void BasicMBRData::UpdateCanBeLogical(void) { int i, j, sectorBefore, numPrimaries, numLogicals, usedAsEBR; uint64_t firstLogical, lastLogical, lStart, pStart; numPrimaries = NumPrimaries(); numLogicals = NumLogicals(); firstLogical = FirstLogicalLBA() - 1; lastLogical = LastLogicalLBA(); for (i = 0; i < MAX_MBR_PARTS; i++) { usedAsEBR = (SectorUsedAs(partitions[i].GetLastLBA()) == EBR); if (usedAsEBR) { partitions[i].SetCanBeLogical(0); partitions[i].SetCanBePrimary(0); } else if (partitions[i].GetLengthLBA() > 0) { // First determine if it can be logical.... sectorBefore = SectorUsedAs(partitions[i].GetStartLBA() - 1); lStart = partitions[i].GetStartLBA(); // start of potential logical part. if ((lastLogical > 0) && ((sectorBefore == EBR) || (sectorBefore == NONE))) { // Assume it can be logical, then search for primaries that make it // not work and, if found, flag appropriately. partitions[i].SetCanBeLogical(1); for (j = 0; j < MAX_MBR_PARTS; j++) { if ((i != j) && (partitions[j].GetInclusion() == PRIMARY)) { pStart = partitions[j].GetStartLBA(); if (((pStart < lStart) && (firstLogical < pStart)) || ((pStart > lStart) && (firstLogical > pStart))) { partitions[i].SetCanBeLogical(0); } // if/else } // if } // for } else { if ((sectorBefore != EBR) && (sectorBefore != NONE)) partitions[i].SetCanBeLogical(0); else partitions[i].SetCanBeLogical(lastLogical == 0); // can be logical only if no logicals already } // if/else // Now determine if it can be primary. Start by assuming it can be... partitions[i].SetCanBePrimary(1); if ((numPrimaries >= 4) && (partitions[i].GetInclusion() != PRIMARY)) { partitions[i].SetCanBePrimary(0); if ((partitions[i].GetInclusion() == LOGICAL) && (numLogicals == 1) && (numPrimaries == 4)) partitions[i].SetCanBePrimary(1); } // if if ((partitions[i].GetStartLBA() > (firstLogical + 1)) && (partitions[i].GetLastLBA() < lastLogical)) partitions[i].SetCanBePrimary(0); } // else if } // for } // BasicMBRData::UpdateCanBeLogical() // Returns the first sector occupied by any logical partition. Note that // this does NOT include the logical partition's EBR! Returns UINT32_MAX // if there are no logical partitions defined. uint64_t BasicMBRData::FirstLogicalLBA(void) { int i; uint64_t firstFound = UINT32_MAX; for (i = 0; i < MAX_MBR_PARTS; i++) { if ((partitions[i].GetInclusion() == LOGICAL) && (partitions[i].GetStartLBA() < firstFound)) { firstFound = partitions[i].GetStartLBA(); } // if } // for return firstFound; } // BasicMBRData::FirstLogicalLBA() // Returns the last sector occupied by any logical partition, or 0 if // there are no logical partitions defined. uint64_t BasicMBRData::LastLogicalLBA(void) { int i; uint64_t lastFound = 0; for (i = 0; i < MAX_MBR_PARTS; i++) { if ((partitions[i].GetInclusion() == LOGICAL) && (partitions[i].GetLastLBA() > lastFound)) lastFound = partitions[i].GetLastLBA(); } // for return lastFound; } // BasicMBRData::LastLogicalLBA() // Returns 1 if logical partitions are contiguous (have no primaries // in their midst), or 0 if one or more primaries exist between // logicals. int BasicMBRData::AreLogicalsContiguous(void) { int allOK = 1, i = 0; uint64_t firstLogical, lastLogical; firstLogical = FirstLogicalLBA() - 1; // subtract 1 for EBR lastLogical = LastLogicalLBA(); if (lastLogical > 0) { do { if ((partitions[i].GetInclusion() == PRIMARY) && (partitions[i].GetStartLBA() >= firstLogical) && (partitions[i].GetStartLBA() <= lastLogical)) { allOK = 0; } // if i++; } while ((i < MAX_MBR_PARTS) && allOK); } // if return allOK; } // BasicMBRData::AreLogicalsContiguous() // Returns 1 if all partitions fit on the disk, given its size; 0 if any // partition is too big. int BasicMBRData::DoTheyFit(void) { int i, allOK = 1; for (i = 0; i < MAX_MBR_PARTS; i++) { if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize)) { allOK = 0; } // if } // for return allOK; } // BasicMBRData::DoTheyFit(void) // Returns 1 if there's at least one free sector immediately preceding // all partitions flagged as logical; 0 if any logical partition lacks // this space. int BasicMBRData::SpaceBeforeAllLogicals(void) { int i = 0, allOK = 1; do { if ((partitions[i].GetStartLBA() > 0) && (partitions[i].GetInclusion() == LOGICAL)) { allOK = allOK && (SectorUsedAs(partitions[i].GetStartLBA() - 1) == EBR); } // if i++; } while (allOK && (i < MAX_MBR_PARTS)); return allOK; } // BasicMBRData::SpaceBeforeAllLogicals() // Returns 1 if the partitions describe a legal layout -- all logicals // are contiguous and have at least one preceding empty sector, // the number of primaries is under 4 (or under 3 if there are any // logicals), there are no overlapping partitions, etc. // Does NOT assume that primaries are numbered 1-4; uses the // IsItPrimary() function of the MBRPart class to determine // primary status. Also does NOT consider partition order; there // can be gaps and it will still be considered legal. int BasicMBRData::IsLegal(void) { int allOK = 1; allOK = (FindOverlaps() == 0); allOK = (allOK && (NumPrimaries() <= 4)); allOK = (allOK && AreLogicalsContiguous()); allOK = (allOK && DoTheyFit()); allOK = (allOK && SpaceBeforeAllLogicals()); return allOK; } // BasicMBRData::IsLegal() // Returns 1 if the 0xEE partition in the protective/hybrid MBR is marked as // active/bootable. int BasicMBRData::IsEEActive(void) { int i, IsActive = 0; for (i = 0; i < MAX_MBR_PARTS; i++) { if ((partitions[i].GetStatus() & 0x80) && (partitions[i].GetType() == 0xEE)) IsActive = 1; } return IsActive; } // BasicMBRData::IsEEActive() // Finds the next in-use partition, starting with start (will return start // if it's in use). Returns -1 if no subsequent partition is in use. int BasicMBRData::FindNextInUse(int start) { if (start >= MAX_MBR_PARTS) start = -1; while ((start < MAX_MBR_PARTS) && (start >= 0) && (partitions[start].GetInclusion() == NONE)) start++; if ((start < 0) || (start >= MAX_MBR_PARTS)) start = -1; return start; } // BasicMBRData::FindFirstLogical(); /***************************************************** * * * 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 BasicMBRData::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) { EmptyBootloader(); } // if // Blank out the partitions for (i = 0; i < MAX_MBR_PARTS; i++) { partitions[i].Empty(); } // for MBRSignature = MBR_SIGNATURE; state = mbr; } // BasicMBRData::EmptyMBR() // Blank out the boot loader area. Done with the initial MBR-to-GPT // conversion, since MBR boot loaders don't understand GPT, and so // need to be replaced.... void BasicMBRData::EmptyBootloader(void) { int i; for (i = 0; i < 440; i++) code[i] = 0; nulls = 0; } // BasicMBRData::EmptyBootloader // Create a partition of the specified number based on the passed // partition. 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 BasicMBRData::AddPart(int num, const MBRPart& newPart) { partitions[num] = newPart; } // BasicMBRData::AddPart() // Create a partition of the specified number, starting LBA, and // length. This function does almost 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 BasicMBRData::MakePart(int num, uint64_t start, uint64_t length, int type, int bootable) { if ((num >= 0) && (num < MAX_MBR_PARTS) && (start <= UINT32_MAX) && (length <= UINT32_MAX)) { partitions[num].Empty(); partitions[num].SetType(type); partitions[num].SetLocation(start, length); if (num < 4) partitions[num].SetInclusion(PRIMARY); else partitions[num].SetInclusion(LOGICAL); SetPartBootable(num, bootable); } // if valid partition number & size } // BasicMBRData::MakePart() // Set the partition's type code. // Returns 1 if successful, 0 if not (invalid partition number) int BasicMBRData::SetPartType(int num, int type) { int allOK = 1; if ((num >= 0) && (num < MAX_MBR_PARTS)) { if (partitions[num].GetLengthLBA() != UINT32_C(0)) { allOK = partitions[num].SetType(type); } else allOK = 0; } else allOK = 0; return allOK; } // BasicMBRData::SetPartType() // Set (or remove) the partition's bootable flag. Setting it is the // default; pass 0 as bootable to remove the flag. // Returns 1 if successful, 0 if not (invalid partition number) int BasicMBRData::SetPartBootable(int num, int bootable) { int allOK = 1; if ((num >= 0) && (num < MAX_MBR_PARTS)) { if (partitions[num].GetLengthLBA() != UINT32_C(0)) { if (bootable == 0) partitions[num].SetStatus(UINT8_C(0x00)); else partitions[num].SetStatus(UINT8_C(0x80)); } else allOK = 0; } else allOK = 0; return allOK; } // BasicMBRData::SetPartBootable() // Create a partition that fills the most available space. Returns // 1 if partition was created, 0 otherwise. Intended for use in // creating hybrid MBRs. int BasicMBRData::MakeBiggestPart(int i, int type) { uint64_t start = UINT64_C(1); // starting point for each search uint64_t firstBlock; // first block in a segment uint64_t lastBlock; // last block in a segment uint64_t segmentSize; // size of segment in blocks uint64_t selectedSegment = UINT64_C(0); // location of largest segment uint64_t selectedSize = UINT64_C(0); // size of largest segment in blocks int found = 0; string anything; do { firstBlock = FindFirstAvailable(start); if (firstBlock > UINT64_C(0)) { // something's free... lastBlock = FindLastInFree(firstBlock); segmentSize = lastBlock - firstBlock + UINT64_C(1); if (segmentSize > selectedSize) { selectedSize = segmentSize; selectedSegment = firstBlock; } // if start = lastBlock + 1; } // if } while (firstBlock != 0); if ((selectedSize > UINT64_C(0)) && (selectedSize < diskSize)) { found = 1; MakePart(i, selectedSegment, selectedSize, type, 0); } else { found = 0; } // if/else return found; } // BasicMBRData::MakeBiggestPart(int i) // Delete partition #i void BasicMBRData::DeletePartition(int i) { partitions[i].Empty(); } // BasicMBRData::DeletePartition() // Set the inclusion status (PRIMARY, LOGICAL, or NONE) with some sanity // checks to ensure the table remains legal. // Returns 1 on success, 0 on failure. int BasicMBRData::SetInclusionwChecks(int num, int inclStatus) { int allOK = 1, origValue; if (IsLegal()) { if ((inclStatus == PRIMARY) || (inclStatus == LOGICAL) || (inclStatus == NONE)) { origValue = partitions[num].GetInclusion(); partitions[num].SetInclusion(inclStatus); if (!IsLegal()) { partitions[num].SetInclusion(origValue); cerr << "Specified change is not legal! Aborting change!\n"; } // if } else { cerr << "Invalid partition inclusion code in BasicMBRData::SetInclusionwChecks()!\n"; } // if/else } else { cerr << "Partition table is not currently in a valid state. Aborting change!\n"; allOK = 0; } // if/else return allOK; } // BasicMBRData::SetInclusionwChecks() // Recomputes the CHS values for the specified partition and adjusts the value. // Note that this will create a technically incorrect CHS value for EFI GPT (0xEE) // protective partitions, but this is required by some buggy BIOSes, so I'm // providing a function to do this deliberately at the user's command. // This function does nothing if the partition's length is 0. void BasicMBRData::RecomputeCHS(int partNum) { partitions[partNum].RecomputeCHS(); } // BasicMBRData::RecomputeCHS() // Sorts the partitions starting with partition #start. This function // does NOT pay attention to primary/logical assignment, which is // critical when writing the partitions. void BasicMBRData::SortMBR(int start) { if ((start < MAX_MBR_PARTS) && (start >= 0)) sort(partitions + start, partitions + MAX_MBR_PARTS); } // BasicMBRData::SortMBR() // Delete any partitions that are too big to fit on the disk // or that are too big for MBR (32-bit limits). // This deletes the partitions by setting values to 0, not just // by setting them as being omitted. // Returns the number of partitions deleted in this way. int BasicMBRData::DeleteOversizedParts() { int num = 0, i; for (i = 0; i < MAX_MBR_PARTS; i++) { if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize) || (partitions[i].GetStartLBA() > UINT32_MAX) || (partitions[i].GetLengthLBA() > UINT32_MAX)) { cerr << "\aWarning: Deleting oversized partition #" << i + 1 << "! Start = " << partitions[i].GetStartLBA() << ", length = " << partitions[i].GetLengthLBA() << "\n"; partitions[i].Empty(); num++; } // if } // for return num; } // BasicMBRData::DeleteOversizedParts() // Search for and delete extended partitions. // Returns the number of partitions deleted. int BasicMBRData::DeleteExtendedParts() { int i, numDeleted = 0; uint8_t type; for (i = 0; i < MAX_MBR_PARTS; i++) { type = partitions[i].GetType(); if (((type == 0x05) || (type == 0x0f) || (type == (0x85))) && (partitions[i].GetLengthLBA() > 0)) { partitions[i].Empty(); numDeleted++; } // if } // for return numDeleted; } // BasicMBRData::DeleteExtendedParts() // Finds any overlapping partitions and omits the smaller of the two. void BasicMBRData::OmitOverlaps() { int i, j; for (i = 0; i < MAX_MBR_PARTS; i++) { for (j = i + 1; j < MAX_MBR_PARTS; j++) { if ((partitions[i].GetInclusion() != NONE) && partitions[i].DoTheyOverlap(partitions[j])) { if (partitions[i].GetLengthLBA() < partitions[j].GetLengthLBA()) partitions[i].SetInclusion(NONE); else partitions[j].SetInclusion(NONE); } // if } // for (j...) } // for (i...) } // BasicMBRData::OmitOverlaps() // Convert as many partitions into logicals as possible, except for // the first partition, if possible. void BasicMBRData::MaximizeLogicals() { int earliestPart = 0, earliestPartWas = NONE, i; for (i = MAX_MBR_PARTS - 1; i >= 0; i--) { UpdateCanBeLogical(); earliestPart = i; if (partitions[i].CanBeLogical()) { partitions[i].SetInclusion(LOGICAL); } else if (partitions[i].CanBePrimary()) { partitions[i].SetInclusion(PRIMARY); } else { partitions[i].SetInclusion(NONE); } // if/elseif/else } // for // If we have spare primaries, convert back the earliest partition to // its original state.... if ((NumPrimaries() < 4) && (partitions[earliestPart].GetInclusion() == LOGICAL)) partitions[earliestPart].SetInclusion(earliestPartWas); } // BasicMBRData::MaximizeLogicals() // Add primaries up to the maximum allowed, from the omitted category. void BasicMBRData::MaximizePrimaries() { int num, i = 0; num = NumPrimaries(); while ((num < 4) && (i < MAX_MBR_PARTS)) { if ((partitions[i].GetInclusion() == NONE) && (partitions[i].CanBePrimary())) { partitions[i].SetInclusion(PRIMARY); num++; UpdateCanBeLogical(); } // if i++; } // while } // BasicMBRData::MaximizePrimaries() // Remove primary partitions in excess of 4, starting with the later ones, // in terms of the array location.... void BasicMBRData::TrimPrimaries(void) { int numToDelete, i = MAX_MBR_PARTS - 1; numToDelete = NumPrimaries() - 4; while ((numToDelete > 0) && (i >= 0)) { if (partitions[i].GetInclusion() == PRIMARY) { partitions[i].SetInclusion(NONE); numToDelete--; } // if i--; } // while (numToDelete > 0) } // BasicMBRData::TrimPrimaries() // Locates primary partitions located between logical partitions and // either converts the primaries into logicals (if possible) or omits // them. void BasicMBRData::MakeLogicalsContiguous(void) { uint64_t firstLogicalLBA, lastLogicalLBA; int i; firstLogicalLBA = FirstLogicalLBA(); lastLogicalLBA = LastLogicalLBA(); for (i = 0; i < MAX_MBR_PARTS; i++) { if ((partitions[i].GetInclusion() == PRIMARY) && (partitions[i].GetStartLBA() >= firstLogicalLBA) && (partitions[i].GetLastLBA() <= lastLogicalLBA)) { if (SectorUsedAs(partitions[i].GetStartLBA() - 1) == NONE) partitions[i].SetInclusion(LOGICAL); else partitions[i].SetInclusion(NONE); } // if } // for } // BasicMBRData::MakeLogicalsContiguous() // If MBR data aren't legal, adjust primary/logical assignments and, // if necessary, drop partitions, to make the data legal. void BasicMBRData::MakeItLegal(void) { if (!IsLegal()) { DeleteOversizedParts(); MaximizeLogicals(); MaximizePrimaries(); if (!AreLogicalsContiguous()) MakeLogicalsContiguous(); if (NumPrimaries() > 4) TrimPrimaries(); OmitOverlaps(); } // if } // BasicMBRData::MakeItLegal() // Removes logical partitions and deactivated partitions from first four // entries (primary space). // Returns the number of partitions moved. int BasicMBRData::RemoveLogicalsFromFirstFour(void) { int i, j = 4, numMoved = 0, swapped = 0; MBRPart temp; for (i = 0; i < 4; i++) { if ((partitions[i].GetInclusion() != PRIMARY) && (partitions[i].GetLengthLBA() > 0)) { j = 4; swapped = 0; do { if ((partitions[j].GetInclusion() == NONE) && (partitions[j].GetLengthLBA() == 0)) { temp = partitions[j]; partitions[j] = partitions[i]; partitions[i] = temp; swapped = 1; numMoved++; } // if j++; } while ((j < MAX_MBR_PARTS) && !swapped); if (j >= MAX_MBR_PARTS) cerr << "Warning! Too many partitions in BasicMBRData::RemoveLogicalsFromFirstFour()!\n"; } // if } // for i... return numMoved; } // BasicMBRData::RemoveLogicalsFromFirstFour() // Move all primaries into the first four partition spaces // Returns the number of partitions moved. int BasicMBRData::MovePrimariesToFirstFour(void) { int i, j = 0, numMoved = 0, swapped = 0; MBRPart temp; for (i = 4; i < MAX_MBR_PARTS; i++) { if (partitions[i].GetInclusion() == PRIMARY) { j = 0; swapped = 0; do { if (partitions[j].GetInclusion() != PRIMARY) { temp = partitions[j]; partitions[j] = partitions[i]; partitions[i] = temp; swapped = 1; numMoved++; } // if j++; } while ((j < 4) && !swapped); } // if } // for return numMoved; } // BasicMBRData::MovePrimariesToFirstFour() // Create an extended partition, if necessary, to hold the logical partitions. // This function also sorts the primaries into the first four positions of // the table. // Returns 1 on success, 0 on failure. int BasicMBRData::CreateExtended(void) { int allOK = 1, i = 0, swapped = 0; MBRPart temp; if (IsLegal()) { // Move logicals out of primary space... RemoveLogicalsFromFirstFour(); // Move primaries out of logical space... MovePrimariesToFirstFour(); // Create the extended partition if (NumLogicals() > 0) { SortMBR(4); // sort starting from 4 -- that is, logicals only temp.Empty(); temp.SetStartLBA(FirstLogicalLBA() - 1); temp.SetLengthLBA(LastLogicalLBA() - FirstLogicalLBA() + 2); temp.SetType(0x0f, 1); temp.SetInclusion(PRIMARY); do { if ((partitions[i].GetInclusion() == NONE) || (partitions[i].GetLengthLBA() == 0)) { partitions[i] = temp; swapped = 1; } // if i++; } while ((i < 4) && !swapped); if (!swapped) { cerr << "Could not create extended partition; no room in primary table!\n"; allOK = 0; } // if } // if (NumLogicals() > 0) } else allOK = 0; // Do a final check for EFI GPT (0xEE) partitions & flag as a problem if found // along with an extended partition for (i = 0; i < MAX_MBR_PARTS; i++) if (swapped && partitions[i].GetType() == 0xEE) allOK = 0; return allOK; } // BasicMBRData::CreateExtended() /**************************************** * * * Functions to find data on free space * * * ****************************************/ // Finds the first free space on the disk from start onward; returns 0 // if none available.... uint64_t BasicMBRData::FindFirstAvailable(uint64_t start) { uint64_t first; uint64_t i; int firstMoved; if ((start >= (UINT32_MAX - 1)) || (start >= (diskSize - 1))) return 0; first = start; // ...now search through all partitions; if first is within an // existing partition, move it to the next sector after that // partition and repeat. If first was moved, set firstMoved // flag; repeat until firstMoved is not set, so as to catch // cases where partitions are out of sequential order.... do { firstMoved = 0; for (i = 0; i < 4; i++) { // Check if it's in the existing partition if ((first >= partitions[i].GetStartLBA()) && (first < (partitions[i].GetStartLBA() + partitions[i].GetLengthLBA()))) { first = partitions[i].GetStartLBA() + partitions[i].GetLengthLBA(); firstMoved = 1; } // if } // for } while (firstMoved == 1); if ((first >= diskSize) || (first > UINT32_MAX)) first = 0; return (first); } // BasicMBRData::FindFirstAvailable() // Finds the last free sector on the disk from start forward. uint64_t BasicMBRData::FindLastInFree(uint64_t start) { uint64_t nearestStart; uint64_t i; if ((diskSize <= UINT32_MAX) && (diskSize > 0)) nearestStart = diskSize - 1; else nearestStart = UINT32_MAX - 1; for (i = 0; i < 4; i++) { if ((nearestStart > partitions[i].GetStartLBA()) && (partitions[i].GetStartLBA() > start)) { nearestStart = partitions[i].GetStartLBA() - 1; } // if } // for return (nearestStart); } // BasicMBRData::FindLastInFree() // Finds the first free sector on the disk from start backward. uint64_t BasicMBRData::FindFirstInFree(uint64_t start) { uint64_t bestLastLBA, thisLastLBA; int i; bestLastLBA = 1; for (i = 0; i < 4; i++) { thisLastLBA = partitions[i].GetLastLBA() + 1; if (thisLastLBA > 0) thisLastLBA--; if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) bestLastLBA = thisLastLBA + 1; } // for return (bestLastLBA); } // BasicMBRData::FindFirstInFree() // Returns NONE (unused), PRIMARY, LOGICAL, EBR (for EBR or MBR), or INVALID. // Note: If the sector immediately before a logical partition is in use by // another partition, this function returns PRIMARY or LOGICAL for that // sector, rather than EBR. int BasicMBRData::SectorUsedAs(uint64_t sector, int topPartNum) { int i = 0, usedAs = NONE; do { if ((partitions[i].GetStartLBA() <= sector) && (partitions[i].GetLastLBA() >= sector)) usedAs = partitions[i].GetInclusion(); if ((partitions[i].GetStartLBA() == (sector + 1)) && (partitions[i].GetInclusion() == LOGICAL)) usedAs = EBR; if (sector == 0) usedAs = EBR; if (sector >= diskSize) usedAs = INVALID; i++; } while ((i < topPartNum) && ((usedAs == NONE) || (usedAs == EBR))); return usedAs; } // BasicMBRData::SectorUsedAs() /****************************************************** * * * Functions that extract data on specific partitions * * * ******************************************************/ uint8_t BasicMBRData::GetStatus(int i) { MBRPart* thePart; uint8_t retval; thePart = GetPartition(i); if (thePart != NULL) retval = thePart->GetStatus(); else retval = UINT8_C(0); return retval; } // BasicMBRData::GetStatus() uint8_t BasicMBRData::GetType(int i) { MBRPart* thePart; uint8_t retval; thePart = GetPartition(i); if (thePart != NULL) retval = thePart->GetType(); else retval = UINT8_C(0); return retval; } // BasicMBRData::GetType() uint64_t BasicMBRData::GetFirstSector(int i) { MBRPart* thePart; uint64_t retval; thePart = GetPartition(i); if (thePart != NULL) retval = thePart->GetStartLBA(); else retval = UINT32_C(0); return retval; } // BasicMBRData::GetFirstSector() uint64_t BasicMBRData::GetLength(int i) { MBRPart* thePart; uint64_t retval; thePart = GetPartition(i); if (thePart != NULL) retval = thePart->GetLengthLBA(); else retval = UINT64_C(0); return retval; } // BasicMBRData::GetLength() /*********************** * * * Protected functions * * * ***********************/ // Return a pointer to a primary or logical partition, or NULL if // the partition is out of range.... MBRPart* BasicMBRData::GetPartition(int i) { MBRPart* thePart = NULL; if ((i >= 0) && (i < MAX_MBR_PARTS)) thePart = &partitions[i]; return thePart; } // GetPartition() /******************************************* * * * Functions that involve user interaction * * * *******************************************/ // Present the MBR operations menu. Note that the 'w' option does not // immediately write data; that's handled by the calling function. // Returns the number of partitions defined on exit, or -1 if the // user selected the 'q' option. (Thus, the caller should save data // if the return value is >0, or possibly >=0 depending on intentions.) int BasicMBRData::DoMenu(const string& prompt) { int goOn = 1, quitting = 0, retval, num, haveShownInfo = 0; unsigned int hexCode; string tempStr; do { cout << prompt; switch (ReadString()[0]) { case '\0': goOn = cin.good(); break; case 'a': case 'A': num = GetNumber(1, MAX_MBR_PARTS, 1, "Toggle active flag for partition: ") - 1; if (partitions[num].GetInclusion() != NONE) partitions[num].SetStatus(partitions[num].GetStatus() ^ 0x80); break; case 'c': case 'C': for (num = 0; num < MAX_MBR_PARTS; num++) RecomputeCHS(num); break; case 'l': case 'L': num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as logical: ") - 1; SetInclusionwChecks(num, LOGICAL); break; case 'o': case 'O': num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to omit: ") - 1; SetInclusionwChecks(num, NONE); break; case 'p': case 'P': if (!haveShownInfo) { cout << "\n** NOTE: Partition numbers do NOT indicate final primary/logical " << "status,\n** unlike in most MBR partitioning tools!\n\a"; cout << "\n** Extended partitions are not displayed, but will be generated " << "as required.\n"; haveShownInfo = 1; } // if DisplayMBRData(); break; case 'q': case 'Q': cout << "This will abandon your changes. Are you sure? "; if (GetYN() == 'Y') { goOn = 0; quitting = 1; } // if break; case 'r': case 'R': num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as primary: ") - 1; SetInclusionwChecks(num, PRIMARY); break; case 's': case 'S': SortMBR(); break; case 't': case 'T': num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to change type code: ") - 1; hexCode = 0x00; if (partitions[num].GetLengthLBA() > 0) { while ((hexCode <= 0) || (hexCode > 255)) { cout << "Enter an MBR hex code: "; tempStr = ReadString(); if (IsHex(tempStr)) sscanf(tempStr.c_str(), "%x", &hexCode); } // while partitions[num].SetType(hexCode); } // if break; case 'w': case 'W': goOn = 0; break; default: ShowCommands(); break; } // switch } while (goOn); if (quitting) retval = -1; else retval = CountParts(); return (retval); } // BasicMBRData::DoMenu() void BasicMBRData::ShowCommands(void) { cout << "a\ttoggle the active/boot flag\n"; cout << "c\trecompute all CHS values\n"; cout << "l\tset partition as logical\n"; cout << "o\tomit partition\n"; cout << "p\tprint the MBR partition table\n"; cout << "q\tquit without saving changes\n"; cout << "r\tset partition as primary\n"; cout << "s\tsort MBR partitions\n"; cout << "t\tchange partition type code\n"; cout << "w\twrite the MBR partition table to disk and exit\n"; } // BasicMBRData::ShowCommands() gptfdisk-1.0.5/guid.h0000664000175000017500000000323113622612343014700 0ustar rodsmithrodsmith// // C++ Interface: GUIDData // // Description: GUIDData class header // Implements the GUIDData data structure and support methods // // // Author: Rod Smith , (C) 2010-2011 // // Copyright: See COPYING file that comes with this distribution // // #ifndef __GUIDDATA_CLASS #define __GUIDDATA_CLASS #include #include // Have to play games with uuid_t since it's defined in incompatible ways // for Unix (libuuid) vs. Windows (in rpc.h) #ifdef _WIN32 #include #ifdef _MSC_VER #pragma comment(lib, "Rpcrt4.lib") #endif typedef unsigned char my_uuid_t[16]; #else // Not Windows #include typedef uuid_t my_uuid_t; #endif using namespace std; // Note: This class's data size is critical. If data elements must be added, // it will be necessary to modify various GPT classes to compensate. class GUIDData { private: static bool firstInstance; protected: my_uuid_t uuidData; string DeleteSpaces(string s); public: GUIDData(void); GUIDData(const GUIDData & orig); GUIDData(const string & orig); GUIDData(const char * orig); ~GUIDData(void); // Data assignment operators.... GUIDData & operator=(const GUIDData & orig); GUIDData & operator=(const string & orig); GUIDData & operator=(const char * orig); void Zero(void); void Randomize(void); // Data tests.... int operator==(const GUIDData & orig) const; int operator!=(const GUIDData & orig) const; // Data retrieval.... string AsString(void) const; }; // class GUIDData ostream & operator<<(ostream & os, const GUIDData & data); #endif gptfdisk-1.0.5/diskio-windows.cc0000664000175000017500000002523113622612343017064 0ustar rodsmithrodsmith// // C++ Interface: diskio (Windows-specific components) // // Description: Class to handle low-level disk I/O for GPT fdisk // // // Author: Rod Smith , (C) 2009 // // Copyright: See COPYING file that comes with this distribution // // // This program is copyright (c) 2009, 2010 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 #include #include #define fstat64 fstat #define stat64 stat #define S_IRGRP 0 #define S_IROTH 0 #include #include #include #include #include #include #include #include "support.h" #include "diskio.h" using namespace std; // Returns the official Windows name for a shortened version of same. void DiskIO::MakeRealName(void) { size_t colonPos; colonPos = userFilename.find(':', 0); if ((colonPos != string::npos) && (colonPos <= 3)) { realFilename = "\\\\.\\physicaldrive"; realFilename += userFilename.substr(0, colonPos); } else { realFilename = userFilename; } // if/else } // DiskIO::MakeRealName() // Open the currently on-record file for reading int DiskIO::OpenForRead(void) { int shouldOpen = 1; if (isOpen) { // file is already open if (openForWrite) { Close(); } else { shouldOpen = 0; } // if/else } // if if (shouldOpen) { fd = CreateFile(realFilename.c_str(),GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fd == INVALID_HANDLE_VALUE) { CloseHandle(fd); cerr << "Problem opening " << realFilename << " for reading!\n"; realFilename = ""; userFilename = ""; isOpen = 0; openForWrite = 0; } else { isOpen = 1; openForWrite = 0; } // if/else } // if return isOpen; } // DiskIO::OpenForRead(void) // An extended file-open function. This includes some system-specific checks. // Returns 1 if the file is open, 0 otherwise.... int DiskIO::OpenForWrite(void) { if ((isOpen) && (openForWrite)) return 1; // Close the disk, in case it's already open for reading only.... Close(); // try to open the device; may fail.... fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // Preceding call can fail when creating backup files; if so, try // again with different option... if (fd == INVALID_HANDLE_VALUE) { CloseHandle(fd); fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } // if if (fd == INVALID_HANDLE_VALUE) { CloseHandle(fd); isOpen = 0; openForWrite = 0; errno = GetLastError(); } else { isOpen = 1; openForWrite = 1; } // if/else return isOpen; } // DiskIO::OpenForWrite(void) // Close the disk device. Note that this does NOT erase the stored filenames, // so the file can be re-opened without specifying the filename. void DiskIO::Close(void) { if (isOpen) CloseHandle(fd); isOpen = 0; openForWrite = 0; } // DiskIO::Close() // Returns block size of device pointed to by fd file descriptor. If the ioctl // returns an error condition, assume it's a disk file and return a value of // SECTOR_SIZE (512). If the disk can't be opened at all, return a value of 0. int DiskIO::GetBlockSize(void) { DWORD blockSize = 0, retBytes; DISK_GEOMETRY_EX geom; // If disk isn't open, try to open it.... if (!isOpen) { OpenForRead(); } // if if (isOpen) { if (DeviceIoControl(fd, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &geom, sizeof(geom), &retBytes, NULL)) { blockSize = geom.Geometry.BytesPerSector; } else { // was probably an ordinary file; set default value.... blockSize = SECTOR_SIZE; } // if/else } // if (isOpen) return (blockSize); } // DiskIO::GetBlockSize() // In theory, returns the physical block size. In practice, this is only // supported in Linux, as of yet. // TODO: Get this working in Windows. int DiskIO::GetPhysBlockSize(void) { return 0; } // DiskIO::GetPhysBlockSize() // Returns the number of heads, according to the kernel, or 255 if the // correct value can't be determined. uint32_t DiskIO::GetNumHeads(void) { return UINT32_C(255); } // DiskIO::GetNumHeads(); // Returns the number of sectors per track, according to the kernel, or 63 // if the correct value can't be determined. uint32_t DiskIO::GetNumSecsPerTrack(void) { return UINT32_C(63); } // DiskIO::GetNumSecsPerTrack() // Resync disk caches so the OS uses the new partition table. This code varies // a lot from one OS to another. // Returns 1 on success, 0 if the kernel continues to use the old partition table. int DiskIO::DiskSync(void) { DWORD i; GET_LENGTH_INFORMATION buf; int retval = 0; // If disk isn't open, try to open it.... if (!openForWrite) { OpenForWrite(); } // if if (isOpen) { if (DeviceIoControl(fd, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, &buf, sizeof(buf), &i, NULL) == 0) { cout << "Disk synchronization failed! The computer may use the old partition table\n" << "until you reboot or remove and re-insert the disk!\n"; } else { cout << "Disk synchronization succeeded! The computer should now use the new\n" << "partition table.\n"; retval = 1; } // if/else } else { cout << "Unable to open the disk for synchronization operation! The computer will\n" << "continue to use the old partition table until you reboot or remove and\n" << "re-insert the disk!\n"; } // if (isOpen) return retval; } // DiskIO::DiskSync() // Seek to the specified sector. Returns 1 on success, 0 on failure. int DiskIO::Seek(uint64_t sector) { int retval = 1; LARGE_INTEGER seekTo; // If disk isn't open, try to open it.... if (!isOpen) { retval = OpenForRead(); } // if if (isOpen) { seekTo.QuadPart = sector * (uint64_t) GetBlockSize(); retval = SetFilePointerEx(fd, seekTo, NULL, FILE_BEGIN); if (retval == 0) { errno = GetLastError(); cerr << "Error when seeking to " << seekTo.QuadPart << "! Error is " << errno << "\n"; retval = 0; } // if } // if return retval; } // DiskIO::Seek() // A variant on the standard read() function. Done to work around // limitations in FreeBSD concerning the matching of the sector // size with the number of bytes read. // Returns the number of bytes read into buffer. int DiskIO::Read(void* buffer, int numBytes) { int blockSize = 512, i, numBlocks; char* tempSpace; DWORD retval = 0; // If disk isn't open, try to open it.... if (!isOpen) { OpenForRead(); } // if if (isOpen) { // Compute required space and allocate memory blockSize = GetBlockSize(); if (numBytes <= blockSize) { numBlocks = 1; tempSpace = new char [blockSize]; } else { numBlocks = numBytes / blockSize; if ((numBytes % blockSize) != 0) numBlocks++; tempSpace = new char [numBlocks * blockSize]; } // if/else if (tempSpace == NULL) { cerr << "Unable to allocate memory in DiskIO::Read()! Terminating!\n"; exit(1); } // if // Read the data into temporary space, then copy it to buffer ReadFile(fd, tempSpace, numBlocks * blockSize, &retval, NULL); for (i = 0; i < numBytes; i++) { ((char*) buffer)[i] = tempSpace[i]; } // for // Adjust the return value, if necessary.... if (((numBlocks * blockSize) != numBytes) && (retval > 0)) retval = numBytes; delete[] tempSpace; } // if (isOpen) return retval; } // DiskIO::Read() // A variant on the standard write() function. // Returns the number of bytes written. int DiskIO::Write(void* buffer, int numBytes) { int blockSize = 512, i, numBlocks, retval = 0; char* tempSpace; DWORD numWritten; // If disk isn't open, try to open it.... if ((!isOpen) || (!openForWrite)) { OpenForWrite(); } // if if (isOpen) { // Compute required space and allocate memory blockSize = GetBlockSize(); if (numBytes <= blockSize) { numBlocks = 1; tempSpace = new char [blockSize]; } else { numBlocks = numBytes / blockSize; if ((numBytes % blockSize) != 0) numBlocks++; tempSpace = new char [numBlocks * blockSize]; } // if/else if (tempSpace == NULL) { cerr << "Unable to allocate memory in DiskIO::Write()! Terminating!\n"; exit(1); } // if // Copy the data to my own buffer, then write it for (i = 0; i < numBytes; i++) { tempSpace[i] = ((char*) buffer)[i]; } // for for (i = numBytes; i < numBlocks * blockSize; i++) { tempSpace[i] = 0; } // for WriteFile(fd, tempSpace, numBlocks * blockSize, &numWritten, NULL); retval = (int) numWritten; // Adjust the return value, if necessary.... if (((numBlocks * blockSize) != numBytes) && (retval > 0)) retval = numBytes; delete[] tempSpace; } // if (isOpen) return retval; } // DiskIO:Write() // Returns the size of the disk in blocks. uint64_t DiskIO::DiskSize(int *err) { uint64_t sectors = 0; // size in sectors DWORD bytes, moreBytes; // low- and high-order bytes of file size GET_LENGTH_INFORMATION buf; DWORD i; // If disk isn't open, try to open it.... if (!isOpen) { OpenForRead(); } // if if (isOpen) { // Note to self: I recall testing a simplified version of // this code, similar to what's in the __APPLE__ block, // on Linux, but I had some problems. IIRC, it ran OK on 32-bit // systems but not on 64-bit. Keep this in mind in case of // 32/64-bit issues on MacOS.... if (DeviceIoControl(fd, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf, sizeof(buf), &i, NULL)) { sectors = (uint64_t) buf.Length.QuadPart / GetBlockSize(); *err = 0; } else { // doesn't seem to be a disk device; assume it's an image file.... bytes = GetFileSize(fd, &moreBytes); sectors = ((uint64_t) bytes + ((uint64_t) moreBytes) * UINT32_MAX) / GetBlockSize(); *err = 0; } // if } else { *err = -1; sectors = 0; } // if/else (isOpen) return sectors; } // DiskIO::DiskSize() gptfdisk-1.0.5/parttypes.h0000664000175000017500000000377213622612343016015 0ustar rodsmithrodsmith/* This program is copyright (c) 2009-2018 by Roderick W. Smith. It is distributed under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ #include #include #ifdef USE_UTF16 #include #else #define UnicodeString string #endif #include #include "support.h" #include "guid.h" #ifndef __PARTITION_TYPES #define __PARTITION_TYPES using namespace std; // A partition type struct AType { // I'm using a custom 16-bit extension of the original MBR 8-bit // type codes, so as to permit disambiguation and use of new // codes required by GPT uint16_t MBRType; GUIDData GUIDType; string name; int display; // 1 to show to users as available type, 0 not to AType* next; }; // struct AType class PartType : public GUIDData { protected: static int numInstances; static AType* allTypes; // Linked list holding all the data static AType* lastType; // Pointer to last entry in the list void AddAllTypes(void); public: // PartType with GUID "00000000-0000-0000-0000-000000000000" static const PartType unusedPartType; PartType(void); PartType(const PartType & orig); PartType(const GUIDData & orig); ~PartType(void); // Set up type information int AddType(uint16_t mbrType, const char * guidData, const char * name, int toDisplay = 1); // New assignment operators.... PartType & operator=(const string & orig); PartType & operator=(const char * orig); // Assignment operators based on base class.... GUIDData & operator=(const GUIDData & orig) {return GUIDData::operator=(orig);} // New data assignment PartType & operator=(uint16_t ID); // Use MBR type code times 0x0100 to assign GUID // Retrieve transformed GUID data based on type code matches string TypeName(void) const; UnicodeString UTypeName(void) const; uint16_t GetHexType() const; // Information relating to all type data void ShowAllTypes(int maxLines = 21) const; int Valid(uint16_t code) const; }; #endif gptfdisk-1.0.5/cgdisk.cc0000664000175000017500000000472313622612343015361 0ustar rodsmithrodsmith/* Copyright (C) 2011 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* This class implements an interactive curses-based interface atop the GPTData class */ #include #include "gptcurses.h" using namespace std; #define MAX_OPTIONS 50 int main(int argc, char *argv[]) { string device = ""; int displayType = USE_CURSES; if (!SizesOK()) exit(1); switch (argc) { case 1: cout << "Type device filename, or press to exit: "; device = ReadString(); if (device.length() == 0) exit(0); break; case 2: // basic usage device = (string) argv[1]; break; case 3: // "-a" usage or illegal if (strcmp(argv[1], "-a") == 0) { device = (string) argv[2]; } else if (strcmp(argv[2], "-a") == 0) { device = (string) argv[1]; } else { cerr << "Usage: " << argv[0] << " [-a] device_file\n"; exit(1); } // if/elseif/else displayType = USE_ARROW; break; default: cerr << "Usage: " << argv[0] << " [-a] device_file\n"; exit(1); break; } // switch GPTDataCurses theGPT; theGPT.SetDisplayType(displayType); if (theGPT.LoadPartitions(device)) { if (theGPT.GetState() != use_gpt) { Report("Warning! Non-GPT or damaged disk detected! This program will attempt to\n" "convert to GPT form or repair damage to GPT data structures, but may not\n" "succeed. Use gdisk or another disk repair tool if you have a damaged GPT\n" "disk."); } // if theGPT.MainMenu(); } else { Report("Could not load partitions from '" + device + "'! Aborting!"); } // if/else return 0; } // main gptfdisk-1.0.5/Makefile0000664000175000017500000000274113622612343015244 0ustar rodsmithrodsmithCFLAGS+=-D_FILE_OFFSET_BITS=64 #CXXFLAGS+=-Wall -D_FILE_OFFSET_BITS=64 -D USE_UTF16 CXXFLAGS+=-Wall -D_FILE_OFFSET_BITS=64 LDFLAGS+= LIB_NAMES=crc32 support guid gptpart mbrpart basicmbr mbr gpt bsd parttypes attributes diskio diskio-unix MBR_LIBS=support diskio diskio-unix basicmbr mbrpart LIB_OBJS=$(LIB_NAMES:=.o) MBR_LIB_OBJS=$(MBR_LIBS:=.o) LIB_HEADERS=$(LIB_NAMES:=.h) DEPEND= makedepend $(CXXFLAGS) all: cgdisk gdisk sgdisk fixparts gdisk: $(LIB_OBJS) gdisk.o gpttext.o $(CXX) $(LIB_OBJS) gdisk.o gpttext.o $(LDFLAGS) -luuid $(LDLIBS) -o gdisk # $(CXX) $(LIB_OBJS) gdisk.o gpttext.o $(LDFLAGS) -licuio -licuuc -luuid -o gdisk cgdisk: $(LIB_OBJS) cgdisk.o gptcurses.o $(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o $(LDFLAGS) -luuid -lncursesw $(LDLIBS) -o cgdisk # $(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o $(LDFLAGS) -licuio -licuuc -luuid -lncurses -o cgdisk sgdisk: $(LIB_OBJS) sgdisk.o gptcl.o $(CXX) $(LIB_OBJS) sgdisk.o gptcl.o $(LDFLAGS) -luuid -lpopt $(LDLIBS) -o sgdisk # $(CXX) $(LIB_OBJS) sgdisk.o gptcl.o $(LDFLAGS) -licuio -licuuc -luuid -lpopt -o sgdisk fixparts: $(MBR_LIB_OBJS) fixparts.o $(CXX) $(MBR_LIB_OBJS) fixparts.o $(LDFLAGS) $(LDLIBS) -o fixparts test: ./gdisk_test.sh lint: #no pre-reqs lint $(SRCS) clean: #no pre-reqs rm -f core *.o *~ gdisk sgdisk cgdisk fixparts # what are the source dependencies depend: $(SRCS) $(DEPEND) $(SRCS) $(OBJS): $(CRITICAL_CXX_FLAGS) # makedepend dependencies below -- type "makedepend *.cc" to regenerate.... # DO NOT DELETE gptfdisk-1.0.5/crc32.cc0000664000175000017500000000323013622612343015021 0ustar rodsmithrodsmith/* * efone - Distributed internet phone system. * * (c) 1999,2000 Krzysztof Dabrowski * (c) 1999,2000 ElysiuM deeZine * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */ /* based on implementation by Finn Yannick Jacobs */ #include #include #include #include "crc32.h" /* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab(). * so make sure, you call it before using the other * functions! */ uint32_t crc_tab[256]; /* chksum_crc() -- to a given block, this one calculates the * crc32-checksum until the length is * reached. the crc32-checksum will be * the result. */ uint32_t chksum_crc32 (unsigned char *block, unsigned int length) { register unsigned long crc; unsigned long i; crc = 0xFFFFFFFF; for (i = 0; i < length; i++) { crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF]; } return (crc ^ 0xFFFFFFFF); } /* chksum_crc32gentab() -- to a global crc_tab[256], this one will * calculate the crcTable for crc32-checksums. * it is generated to the polynom [..] */ void chksum_crc32gentab () { unsigned long crc, poly; int i, j; poly = 0xEDB88320L; for (i = 0; i < 256; i++) { crc = i; for (j = 8; j > 0; j--) { if (crc & 1) { crc = (crc >> 1) ^ poly; } else { crc >>= 1; } } crc_tab[i] = crc; } } gptfdisk-1.0.5/parttypes.cc0000664000175000017500000007305513622612343016154 0ustar rodsmithrodsmith// parttypes.cc // Class to manage partition type codes -- a slight variant on MBR type // codes, GUID type codes, and associated names. /* This program is copyright (c) 2009-2018 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 #include #include #include #include #include "parttypes.h" using namespace std; int PartType::numInstances = 0; AType* PartType::allTypes = NULL; AType* PartType::lastType = NULL; const PartType PartType::unusedPartType = (GUIDData) "00000000-0000-0000-0000-000000000000"; #define SCREEN_WIDTH 80 #define NUM_COLUMNS 2 #define DESC_LENGTH (SCREEN_WIDTH - (6 * NUM_COLUMNS)) / NUM_COLUMNS // Constructor. Its main task is to initialize the data list, but only // if this is the first instance, since it's a static linked list. // Partition type codes are MBR type codes multiplied by 0x0100, with // additional related codes taking on following numbers. For instance, // the FreeBSD disklabel code in MBR is 0xa5; here, it's 0xa500, with // additional FreeBSD codes being 0xa501, 0xa502, and so on. This gives // related codes similar numbers and (given appropriate entry positions // in the linked list) keeps them together in the listings generated // by typing "L" at the main gdisk menu. PartType::PartType(void) : GUIDData() { numInstances++; if (numInstances == 1) { AddAllTypes(); } // if } // default constructor PartType::PartType(const PartType & orig) : GUIDData(orig) { numInstances++; if (numInstances == 1) { // should never happen; just being paranoid AddAllTypes(); } // if } // PartType copy constructor PartType::PartType(const GUIDData & orig) : GUIDData(orig) { numInstances++; if (numInstances == 1) { AddAllTypes(); } // if } // PartType copy constructor PartType::~PartType(void) { AType* tempType; numInstances--; if (numInstances == 0) { while (allTypes != NULL) { tempType = allTypes; allTypes = allTypes->next; delete tempType; } // while } // if } // destructor // Add all partition type codes to the internal linked-list structure. // Used by constructors. // See http://www.win.tue.nl/~aeb/partitions/partition_types-1.html // for a list of MBR partition type codes. void PartType::AddAllTypes(void) { // Start with the "unused entry," which should normally appear only // on empty partition table entries.... AddType(0x0000, "00000000-0000-0000-0000-000000000000", "Unused entry", 0); // DOS/Windows partition types, most of which are hidden from the "L" listing // (they're available mainly for MBR-to-GPT conversions). AddType(0x0100, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // FAT-12 AddType(0x0400, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // FAT-16 < 32M AddType(0x0600, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // FAT-16 AddType(0x0700, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 1); // NTFS (or HPFS) AddType(0x0b00, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // FAT-32 AddType(0x0c00, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // FAT-32 LBA AddType(0x0c01, "E3C9E316-0B5C-4DB8-817D-F92DF00215AE", "Microsoft reserved"); AddType(0x0e00, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // FAT-16 LBA AddType(0x1100, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // Hidden FAT-12 AddType(0x1400, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // Hidden FAT-16 < 32M AddType(0x1600, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // Hidden FAT-16 AddType(0x1700, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // Hidden NTFS (or HPFS) AddType(0x1b00, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // Hidden FAT-32 AddType(0x1c00, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // Hidden FAT-32 LBA AddType(0x1e00, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Microsoft basic data", 0); // Hidden FAT-16 LBA AddType(0x2700, "DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", "Windows RE"); // Open Network Install Environment (ONIE) specific types. // See http://www.onie.org/ and // https://github.com/opencomputeproject/onie/blob/master/patches/gptfdisk/add-onie-partition-types.patch AddType(0x3000, "7412F7D5-A156-4B13-81DC-867174929325", "ONIE boot"); AddType(0x3001, "D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149", "ONIE config"); // Plan 9; see http://man.cat-v.org/9front/8/prep AddType(0x3900, "C91818F9-8025-47AF-89D2-F030D7000C2C", "Plan 9"); // PowerPC reference platform boot partition AddType(0x4100, "9E1A2D38-C612-4316-AA26-8B49521E5A8B", "PowerPC PReP boot"); // Windows LDM ("dynamic disk") types AddType(0x4200, "AF9B60A0-1431-4F62-BC68-3311714A69AD", "Windows LDM data"); // Logical disk manager AddType(0x4201, "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", "Windows LDM metadata"); // Logical disk manager AddType(0x4202, "E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D", "Windows Storage Spaces"); // A newer LDM-type setup // An oddball IBM filesystem.... AddType(0x7501, "37AFFC90-EF7D-4E96-91C3-2D7AE055B174", "IBM GPFS"); // General Parallel File System (GPFS) // ChromeOS-specific partition types... // Values taken from vboot_reference/firmware/lib/cgptlib/include/gpt.h in // ChromeOS source code, retrieved 12/23/2010. They're also at // http://www.chromium.org/chromium-os/chromiumos-design-docs/disk-format. // These have no MBR equivalents, AFAIK, so I'm using 0x7Fxx values, since they're close // to the Linux values. AddType(0x7f00, "FE3A2A5D-4F32-41A7-B725-ACCC3285A309", "ChromeOS kernel"); AddType(0x7f01, "3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC", "ChromeOS root"); AddType(0x7f02, "2E0A753D-9E48-43B0-8337-B15192CB1B5E", "ChromeOS reserved"); // Linux-specific partition types.... AddType(0x8200, "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", "Linux swap"); // Linux swap (or Solaris on MBR) AddType(0x8300, "0FC63DAF-8483-4772-8E79-3D69D8477DE4", "Linux filesystem"); // Linux native AddType(0x8301, "8DA63339-0007-60C0-C436-083AC8230908", "Linux reserved"); // See https://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html // and https://systemd.io/DISCOVERABLE_PARTITIONS AddType(0x8302, "933AC7E1-2EB4-4F13-B844-0E14E2AEF915", "Linux /home"); // Linux /home (auto-mounted by systemd) AddType(0x8303, "44479540-F297-41B2-9AF7-D131D5F0458A", "Linux x86 root (/)"); // Linux / on x86 (auto-mounted by systemd) AddType(0x8304, "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709", "Linux x86-64 root (/)"); // Linux / on x86-64 (auto-mounted by systemd) AddType(0x8305, "B921B045-1DF0-41C3-AF44-4C6F280D3FAE", "Linux ARM64 root (/)"); // Linux / on 64-bit ARM (auto-mounted by systemd) AddType(0x8306, "3B8F8425-20E0-4F3B-907F-1A25A76F98E8", "Linux /srv"); // Linux /srv (auto-mounted by systemd) AddType(0x8307, "69DAD710-2CE4-4E3C-B16C-21A1D49ABED3", "Linux ARM32 root (/)"); // Linux / on 32-bit ARM (auto-mounted by systemd) AddType(0x8308, "7FFEC5C9-2D00-49B7-8941-3EA10A5586B7", "Linux dm-crypt"); AddType(0x8309, "CA7D7CCB-63ED-4C53-861C-1742536059CC", "Linux LUKS"); AddType(0x830A, "993D8D3D-F80E-4225-855A-9DAF8ED7EA97", "Linux IA-64 root (/)"); // Linux / on Itanium (auto-mounted by systemd) AddType(0x830B, "D13C5D3B-B5D1-422A-B29F-9454FDC89D76", "Linux x86 root verity"); AddType(0x830C, "2C7357ED-EBD2-46D9-AEC1-23D437EC2BF5", "Linux x86-64 root verity"); AddType(0x830D, "7386CDF2-203C-47A9-A498-F2ECCE45A2D6", "Linux ARM32 root verity"); AddType(0x830E, "DF3300CE-D69F-4C92-978C-9BFB0F38D820", "Linux ARM64 root verity"); AddType(0x830F, "86ED10D5-B607-45BB-8957-D350F23D0571", "Linux IA-64 root verity"); AddType(0x8310, "4D21B016-B534-45C2-A9FB-5C16E091FD2D", "Linux /var"); // Linux /var (auto-mounted by systemd) AddType(0x8311, "7EC6F557-3BC5-4ACA-B293-16EF5DF639D1", "Linux /var/tmp"); // Linux /var/tmp (auto-mounted by systemd) // Used by Intel Rapid Start technology AddType(0x8400, "D3BFE2DE-3DAF-11DF-BA40-E3A556D89593", "Intel Rapid Start"); // Type codes for Container Linux (formerly CoreOS; https://coreos.com) AddType(0x8500, "5DFBF5F4-2848-4BAC-AA5E-0D9A20B745A6", "Container Linux /usr"); AddType(0x8501, "3884DD41-8582-4404-B9A8-E9B84F2DF50E", "Container Linux resizable rootfs"); AddType(0x8502, "C95DC21A-DF0E-4340-8D7B-26CBFA9A03E0", "Container Linux /OEM customizations"); AddType(0x8503, "BE9067B9-EA49-4F15-B4F6-F36F8C9E1818", "Container Linux root on RAID"); // Another Linux type code.... AddType(0x8e00, "E6D6D379-F507-44C2-A23C-238F2A3DF928", "Linux LVM"); // Android type codes.... // from Wikipedia, https://gist.github.com/culots/704afd126dec2f45c22d0c9d42cb7fab, // and my own Android devices' partition tables AddType(0xa000, "2568845D-2332-4675-BC39-8FA5A4748D15", "Android bootloader"); AddType(0xa001, "114EAFFE-1552-4022-B26E-9B053604CF84", "Android bootloader 2"); AddType(0xa002, "49A4D17F-93A3-45C1-A0DE-F50B2EBE2599", "Android boot 1"); AddType(0xa003, "4177C722-9E92-4AAB-8644-43502BFD5506", "Android recovery 1"); AddType(0xa004, "EF32A33B-A409-486C-9141-9FFB711F6266", "Android misc"); AddType(0xa005, "20AC26BE-20B7-11E3-84C5-6CFDB94711E9", "Android metadata"); AddType(0xa006, "38F428E6-D326-425D-9140-6E0EA133647C", "Android system 1"); AddType(0xa007, "A893EF21-E428-470A-9E55-0668FD91A2D9", "Android cache"); AddType(0xa008, "DC76DDA9-5AC1-491C-AF42-A82591580C0D", "Android data"); AddType(0xa009, "EBC597D0-2053-4B15-8B64-E0AAC75F4DB1", "Android persistent"); AddType(0xa00a, "8F68CC74-C5E5-48DA-BE91-A0C8C15E9C80", "Android factory"); AddType(0xa00b, "767941D0-2085-11E3-AD3B-6CFDB94711E9", "Android fastboot/tertiary"); AddType(0xa00c, "AC6D7924-EB71-4DF8-B48D-E267B27148FF", "Android OEM"); AddType(0xa00d, "C5A0AEEC-13EA-11E5-A1B1-001E67CA0C3C", "Android vendor"); AddType(0xa00e, "BD59408B-4514-490D-BF12-9878D963F378", "Android config"); AddType(0xa00f, "9FDAA6EF-4B3F-40D2-BA8D-BFF16BFB887B", "Android factory (alt)"); AddType(0xa010, "19A710A2-B3CA-11E4-B026-10604B889DCF", "Android meta"); AddType(0xa011, "193D1EA4-B3CA-11E4-B075-10604B889DCF", "Android EXT"); AddType(0xa012, "DEA0BA2C-CBDD-4805-B4F9-F428251C3E98", "Android SBL1"); AddType(0xa013, "8C6B52AD-8A9E-4398-AD09-AE916E53AE2D", "Android SBL2"); AddType(0xa014, "05E044DF-92F1-4325-B69E-374A82E97D6E", "Android SBL3"); AddType(0xa015, "400FFDCD-22E0-47E7-9A23-F16ED9382388", "Android APPSBL"); AddType(0xa016, "A053AA7F-40B8-4B1C-BA08-2F68AC71A4F4", "Android QSEE/tz"); AddType(0xa017, "E1A6A689-0C8D-4CC6-B4E8-55A4320FBD8A", "Android QHEE/hyp"); AddType(0xa018, "098DF793-D712-413D-9D4E-89D711772228", "Android RPM"); AddType(0xa019, "D4E0D938-B7FA-48C1-9D21-BC5ED5C4B203", "Android WDOG debug/sdi"); AddType(0xa01a, "20A0C19C-286A-42FA-9CE7-F64C3226A794", "Android DDR"); AddType(0xa01b, "A19F205F-CCD8-4B6D-8F1E-2D9BC24CFFB1", "Android CDT"); AddType(0xa01c, "66C9B323-F7FC-48B6-BF96-6F32E335A428", "Android RAM dump"); AddType(0xa01d, "303E6AC3-AF15-4C54-9E9B-D9A8FBECF401", "Android SEC"); AddType(0xa01e, "C00EEF24-7709-43D6-9799-DD2B411E7A3C", "Android PMIC"); AddType(0xa01f, "82ACC91F-357C-4A68-9C8F-689E1B1A23A1", "Android misc 1"); AddType(0xa020, "E2802D54-0545-E8A1-A1E8-C7A3E245ACD4", "Android misc 2"); AddType(0xa021, "65ADDCF4-0C5C-4D9A-AC2D-D90B5CBFCD03", "Android device info"); AddType(0xa022, "E6E98DA2-E22A-4D12-AB33-169E7DEAA507", "Android APDP"); AddType(0xa023, "ED9E8101-05FA-46B7-82AA-8D58770D200B", "Android MSADP"); AddType(0xa024, "11406F35-1173-4869-807B-27DF71802812", "Android DPO"); AddType(0xa025, "9D72D4E4-9958-42DA-AC26-BEA7A90B0434", "Android recovery 2"); AddType(0xa026, "6C95E238-E343-4BA8-B489-8681ED22AD0B", "Android persist"); AddType(0xa027, "EBBEADAF-22C9-E33B-8F5D-0E81686A68CB", "Android modem ST1"); AddType(0xa028, "0A288B1F-22C9-E33B-8F5D-0E81686A68CB", "Android modem ST2"); AddType(0xa029, "57B90A16-22C9-E33B-8F5D-0E81686A68CB", "Android FSC"); AddType(0xa02a, "638FF8E2-22C9-E33B-8F5D-0E81686A68CB", "Android FSG 1"); AddType(0xa02b, "2013373E-1AC4-4131-BFD8-B6A7AC638772", "Android FSG 2"); AddType(0xa02c, "2C86E742-745E-4FDD-BFD8-B6A7AC638772", "Android SSD"); AddType(0xa02d, "DE7D4029-0F5B-41C8-AE7E-F6C023A02B33", "Android keystore"); AddType(0xa02e, "323EF595-AF7A-4AFA-8060-97BE72841BB9", "Android encrypt"); AddType(0xa02f, "45864011-CF89-46E6-A445-85262E065604", "Android EKSST"); AddType(0xa030, "8ED8AE95-597F-4C8A-A5BD-A7FF8E4DFAA9", "Android RCT"); AddType(0xa031, "DF24E5ED-8C96-4B86-B00B-79667DC6DE11", "Android spare1"); AddType(0xa032, "7C29D3AD-78B9-452E-9DEB-D098D542F092", "Android spare2"); AddType(0xa033, "379D107E-229E-499D-AD4F-61F5BCF87BD4", "Android spare3"); AddType(0xa034, "0DEA65E5-A676-4CDF-823C-77568B577ED5", "Android spare4"); AddType(0xa035, "4627AE27-CFEF-48A1-88FE-99C3509ADE26", "Android raw resources"); AddType(0xa036, "20117F86-E985-4357-B9EE-374BC1D8487D", "Android boot 2"); AddType(0xa037, "86A7CB80-84E1-408C-99AB-694F1A410FC7", "Android FOTA"); AddType(0xa038, "97D7B011-54DA-4835-B3C4-917AD6E73D74", "Android system 2"); AddType(0xa039, "5594C694-C871-4B5F-90B1-690A6F68E0F7", "Android cache"); AddType(0xa03a, "1B81E7E6-F50D-419B-A739-2AEEF8DA3335", "Android user data"); AddType(0xa03b, "98523EC6-90FE-4C67-B50A-0FC59ED6F56D", "LG (Android) advanced flasher"); AddType(0xa03c, "2644BCC0-F36A-4792-9533-1738BED53EE3", "Android PG1FS"); AddType(0xa03d, "DD7C91E9-38C9-45C5-8A12-4A80F7E14057", "Android PG2FS"); AddType(0xa03e, "7696D5B6-43FD-4664-A228-C563C4A1E8CC", "Android board info"); AddType(0xa03f, "0D802D54-058D-4A20-AD2D-C7A362CEACD4", "Android MFG"); AddType(0xa040, "10A0C19C-516A-5444-5CE3-664C3226A794", "Android limits"); // Atari TOS partition type AddType(0xa200, "734E5AFE-F61A-11E6-BC64-92361F002671", "Atari TOS basic data"); // FreeBSD partition types.... // Note: Rather than extract FreeBSD disklabel data, convert FreeBSD // partitions in-place, and let FreeBSD sort out the details.... AddType(0xa500, "516E7CB4-6ECF-11D6-8FF8-00022D09712B", "FreeBSD disklabel"); AddType(0xa501, "83BD6B9D-7F41-11DC-BE0B-001560B84F0F", "FreeBSD boot"); AddType(0xa502, "516E7CB5-6ECF-11D6-8FF8-00022D09712B", "FreeBSD swap"); AddType(0xa503, "516E7CB6-6ECF-11D6-8FF8-00022D09712B", "FreeBSD UFS"); AddType(0xa504, "516E7CBA-6ECF-11D6-8FF8-00022D09712B", "FreeBSD ZFS"); AddType(0xa505, "516E7CB8-6ECF-11D6-8FF8-00022D09712B", "FreeBSD Vinum/RAID"); // Midnight BSD partition types.... AddType(0xa580, "85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7", "Midnight BSD data"); AddType(0xa581, "85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7", "Midnight BSD boot"); AddType(0xa582, "85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7", "Midnight BSD swap"); AddType(0xa583, "0394Ef8B-237E-11E1-B4B3-E89A8F7FC3A7", "Midnight BSD UFS"); AddType(0xa584, "85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7", "Midnight BSD ZFS"); AddType(0xa585, "85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7", "Midnight BSD Vinum"); // OpenBSD partition type.... AddType(0xa600, "824CC7A0-36A8-11E3-890A-952519AD3F61", "OpenBSD disklabel"); // A MacOS partition type, separated from others by NetBSD partition types... AddType(0xa800, "55465300-0000-11AA-AA11-00306543ECAC", "Apple UFS"); // Mac OS X // NetBSD partition types. Note that the main entry sets it up as a // FreeBSD disklabel. I'm not 100% certain this is the correct behavior. AddType(0xa900, "516E7CB4-6ECF-11D6-8FF8-00022D09712B", "FreeBSD disklabel", 0); // NetBSD disklabel AddType(0xa901, "49F48D32-B10E-11DC-B99B-0019D1879648", "NetBSD swap"); AddType(0xa902, "49F48D5A-B10E-11DC-B99B-0019D1879648", "NetBSD FFS"); AddType(0xa903, "49F48D82-B10E-11DC-B99B-0019D1879648", "NetBSD LFS"); AddType(0xa904, "2DB519C4-B10F-11DC-B99B-0019D1879648", "NetBSD concatenated"); AddType(0xa905, "2DB519EC-B10F-11DC-B99B-0019D1879648", "NetBSD encrypted"); AddType(0xa906, "49F48DAA-B10E-11DC-B99B-0019D1879648", "NetBSD RAID"); // Mac OS partition types (See also 0xa800, above).... AddType(0xab00, "426F6F74-0000-11AA-AA11-00306543ECAC", "Recovery HD"); AddType(0xaf00, "48465300-0000-11AA-AA11-00306543ECAC", "Apple HFS/HFS+"); AddType(0xaf01, "52414944-0000-11AA-AA11-00306543ECAC", "Apple RAID"); AddType(0xaf02, "52414944-5F4F-11AA-AA11-00306543ECAC", "Apple RAID offline"); AddType(0xaf03, "4C616265-6C00-11AA-AA11-00306543ECAC", "Apple label"); AddType(0xaf04, "5265636F-7665-11AA-AA11-00306543ECAC", "AppleTV recovery"); AddType(0xaf05, "53746F72-6167-11AA-AA11-00306543ECAC", "Apple Core Storage"); AddType(0xaf06, "B6FA30DA-92D2-4A9A-96F1-871EC6486200", "Apple SoftRAID Status"); AddType(0xaf07, "2E313465-19B9-463F-8126-8A7993773801", "Apple SoftRAID Scratch"); AddType(0xaf08, "FA709C7E-65B1-4593-BFD5-E71D61DE9B02", "Apple SoftRAID Volume"); AddType(0xaf09, "BBBA6DF5-F46F-4A89-8F59-8765B2727503", "Apple SoftRAID Cache"); AddType(0xaf0a, "7C3457EF-0000-11AA-AA11-00306543ECAC", "Apple APFS"); // QNX Power-Safe (QNX6) AddType(0xb300, "CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1", "QNX6 Power-Safe"); // Acronis Secure Zone AddType(0xbc00, "0311FC50-01CA-4725-AD77-9ADBB20ACE98", "Acronis Secure Zone"); // Solaris partition types (one of which is shared with MacOS) AddType(0xbe00, "6A82CB45-1DD2-11B2-99A6-080020736631", "Solaris boot"); AddType(0xbf00, "6A85CF4D-1DD2-11B2-99A6-080020736631", "Solaris root"); AddType(0xbf01, "6A898CC3-1DD2-11B2-99A6-080020736631", "Solaris /usr & Mac ZFS"); // Solaris/MacOS AddType(0xbf02, "6A87C46F-1DD2-11B2-99A6-080020736631", "Solaris swap"); AddType(0xbf03, "6A8B642B-1DD2-11B2-99A6-080020736631", "Solaris backup"); AddType(0xbf04, "6A8EF2E9-1DD2-11B2-99A6-080020736631", "Solaris /var"); AddType(0xbf05, "6A90BA39-1DD2-11B2-99A6-080020736631", "Solaris /home"); AddType(0xbf06, "6A9283A5-1DD2-11B2-99A6-080020736631", "Solaris alternate sector"); AddType(0xbf07, "6A945A3B-1DD2-11B2-99A6-080020736631", "Solaris Reserved 1"); AddType(0xbf08, "6A9630D1-1DD2-11B2-99A6-080020736631", "Solaris Reserved 2"); AddType(0xbf09, "6A980767-1DD2-11B2-99A6-080020736631", "Solaris Reserved 3"); AddType(0xbf0a, "6A96237F-1DD2-11B2-99A6-080020736631", "Solaris Reserved 4"); AddType(0xbf0b, "6A8D2AC7-1DD2-11B2-99A6-080020736631", "Solaris Reserved 5"); // I can find no MBR equivalents for these, but they're on the // Wikipedia page for GPT, so here we go.... AddType(0xc001, "75894C1E-3AEB-11D3-B7C1-7B03A0000000", "HP-UX data"); AddType(0xc002, "E2A1E728-32E3-11D6-A682-7B03A0000000", "HP-UX service"); // Open Network Install Environment (ONIE) partitions.... AddType(0xe100, "7412F7D5-A156-4B13-81DC-867174929325", "ONIE boot"); AddType(0xe101, "D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149", "ONIE config"); // Veracrypt (https://www.veracrypt.fr/en/Home.html) encrypted partition AddType(0xe900, "8C8F8EFF-AC95-4770-814A-21994F2DBC8F", "Veracrypt data"); // See http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec AddType(0xea00, "BC13C2FF-59E6-4262-A352-B275FD6F7172", "Freedesktop $BOOT"); // Type code for Haiku; uses BeOS MBR code as hex code base AddType(0xeb00, "42465331-3BA3-10F1-802A-4861696B7521", "Haiku BFS"); // Manufacturer-specific ESP-like partitions (in order in which they were added) AddType(0xed00, "F4019732-066E-4E12-8273-346C5641494F", "Sony system partition"); AddType(0xed01, "BFBFAFE7-A34F-448A-9A5B-6213EB736C22", "Lenovo system partition"); // EFI system and related partitions AddType(0xef00, "C12A7328-F81F-11D2-BA4B-00A0C93EC93B", "EFI system partition"); // Parted identifies these as having the "boot flag" set AddType(0xef01, "024DEE41-33E7-11D3-9D69-0008C781F39F", "MBR partition scheme"); // Used to nest MBR in GPT AddType(0xef02, "21686148-6449-6E6F-744E-656564454649", "BIOS boot partition"); // Used by GRUB // Ceph type codes; see https://github.com/ceph/ceph/blob/9bcc42a3e6b08521694b5c0228b2c6ed7b3d312e/src/ceph-disk#L76-L81 // and Wikipedia AddType(0xf800, "4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D", "Ceph OSD"); // Ceph Object Storage Daemon AddType(0xf801, "4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D", "Ceph dm-crypt OSD"); // Ceph Object Storage Daemon (encrypted) AddType(0xf802, "45B0969E-9B03-4F30-B4C6-B4B80CEFF106", "Ceph journal"); AddType(0xf803, "45B0969E-9B03-4F30-B4C6-5EC00CEFF106", "Ceph dm-crypt journal"); AddType(0xf804, "89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE", "Ceph disk in creation"); AddType(0xf805, "89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE", "Ceph dm-crypt disk in creation"); AddType(0xf806, "CAFECAFE-9B03-4F30-B4C6-B4B80CEFF106", "Ceph block"); AddType(0xf807, "30CD0809-C2B2-499C-8879-2D6B78529876", "Ceph block DB"); AddType(0xf808, "5CE17FCE-4087-4169-B7FF-056CC58473F9", "Ceph block write-ahead log"); AddType(0xf809, "FB3AABF9-D25F-47CC-BF5E-721D1816496B", "Ceph lockbox for dm-crypt keys"); AddType(0xf80a, "4FBD7E29-8AE0-4982-BF9D-5A8D867AF560", "Ceph multipath OSD"); AddType(0xf80b, "45B0969E-8AE0-4982-BF9D-5A8D867AF560", "Ceph multipath journal"); AddType(0xf80c, "CAFECAFE-8AE0-4982-BF9D-5A8D867AF560", "Ceph multipath block 1"); AddType(0xf80d, "7F4A666A-16F3-47A2-8445-152EF4D03F6C", "Ceph multipath block 2"); AddType(0xf80e, "EC6D6385-E346-45DC-BE91-DA2A7C8B3261", "Ceph multipath block DB"); AddType(0xf80f, "01B41E1B-002A-453C-9F17-88793989FF8F", "Ceph multipath block write-ahead log"); AddType(0xf810, "CAFECAFE-9B03-4F30-B4C6-5EC00CEFF106", "Ceph dm-crypt block"); AddType(0xf811, "93B0052D-02D9-4D8A-A43B-33A3EE4DFBC3", "Ceph dm-crypt block DB"); AddType(0xf812, "306E8683-4FE2-4330-B7C0-00A917C16966", "Ceph dm-crypt block write-ahead log"); AddType(0xf813, "45B0969E-9B03-4F30-B4C6-35865CEFF106", "Ceph dm-crypt LUKS journal"); AddType(0xf814, "CAFECAFE-9B03-4F30-B4C6-35865CEFF106", "Ceph dm-crypt LUKS block"); AddType(0xf815, "166418DA-C469-4022-ADF4-B30AFD37F176", "Ceph dm-crypt LUKS block DB"); AddType(0xf816, "86A32090-3647-40B9-BBBD-38D8C573AA86", "Ceph dm-crypt LUKS block write-ahead log"); AddType(0xf817, "4FBD7E29-9D25-41B8-AFD0-35865CEFF05D", "Ceph dm-crypt LUKS OSD"); // VMWare ESX partition types codes AddType(0xfb00, "AA31E02A-400F-11DB-9590-000C2911D1B8", "VMWare VMFS"); AddType(0xfb01, "9198EFFC-31C0-11DB-8F78-000C2911D1B8", "VMWare reserved"); AddType(0xfc00, "9D275380-40AD-11DB-BF97-000C2911D1B8", "VMWare kcore crash protection"); // A straggler Linux partition type.... AddType(0xfd00, "A19D880F-05FC-4D3B-A006-743F0F84911E", "Linux RAID"); // Note: DO NOT use the 0xffff code; that's reserved to indicate an // unknown GUID type code. } // PartType::AddAllTypes() // Add a single type to the linked list of types. Returns 1 if operation // succeeds, 0 otherwise. int PartType::AddType(uint16_t mbrType, const char * guidData, const char * name, int toDisplay) { AType* tempType; int allOK = 1; tempType = new AType; if (tempType != NULL) { tempType->MBRType = mbrType; tempType->GUIDType = guidData; tempType->name = name; tempType->display = toDisplay; tempType->next = NULL; if (allTypes == NULL) { // first entry allTypes = tempType; } else { lastType->next = tempType; } // if/else lastType = tempType; } else { cerr << "Unable to allocate memory in PartType::AddType()! Partition type list will\n"; cerr << "be incomplete!\n"; allOK = 0; } // if/else return allOK; } // GUID::AddType(const char* variant) // Assignment operator by string. If the original string is short, // interpret it as a gdisk hex code; if it's longer, interpret it as // a direct entry of a GUID value. If a short string isn't a hex // number, do nothing. PartType & PartType::operator=(const string & orig) { uint32_t hexCode; if (orig.length() < 32) { if (IsHex(orig)) { sscanf(orig.c_str(), "%x", &hexCode); *this = hexCode; } } else { GUIDData::operator=(orig); } // if/else hexCode or GUID return *this; } // PartType::operator=(const char * orig) // Assignment from C-style string; rely on C++ casting.... PartType & PartType::operator=(const char * orig) { return operator=((string) orig); } // PartType::operator=(const char * orig) // Assign a GUID based on my custom 2-byte (16-bit) MBR hex ID variant PartType & PartType::operator=(uint16_t ID) { AType* theItem = allTypes; int found = 0; // Now search the type list for a match to the ID.... while ((theItem != NULL) && (!found)) { if (theItem->MBRType == ID) { GUIDData::operator=(theItem->GUIDType); found = 1; } else { theItem = theItem->next; } // if/else } // while if (!found) { // Assign a default value.... operator=(DEFAULT_GPT_TYPE); cout.setf(ios::uppercase); cout.fill('0'); cout << "Exact type match not found for type code "; cout.width(4); cout << hex << ID << "; assigning type code for\n'" << TypeName() << "'\n" << dec; cout.fill(' '); } // if (!found) return *this; } // PartType::operator=(uint16_t ID) // Return the English description of the partition type (e.g., "Linux filesystem") string PartType::TypeName(void) const { AType* theItem = allTypes; int found = 0; string typeName; while ((theItem != NULL) && (!found)) { if (theItem->GUIDType == *this) { // found it! typeName = theItem->name; found = 1; } else { theItem = theItem->next; } // if/else } // while if (!found) { typeName = "Unknown"; } // if (!found) return typeName; } // PartType::TypeName() #ifdef USE_UTF16 // Return the Unicode description of the partition type (e.g., "Linux filesystem") UnicodeString PartType::UTypeName(void) const { AType* theItem = allTypes; int found = 0; UnicodeString typeName; while ((theItem != NULL) && (!found)) { if (theItem->GUIDType == *this) { // found it! typeName = theItem->name.c_str(); found = 1; } else { theItem = theItem->next; } // if/else } // while if (!found) { typeName = "Unknown"; } // if (!found) return typeName; } // PartType::TypeName() #endif // Return the custom GPT fdisk 2-byte (16-bit) hex code for this GUID partition type // Note that this function ignores entries for which the display variable // is set to 0. This enables control of which values get returned when // there are multiple possibilities, but opens the algorithm up to the // potential for problems should the data in the list be bad. uint16_t PartType::GetHexType() const { AType* theItem = allTypes; int found = 0; uint16_t theID = 0xFFFF; while ((theItem != NULL) && (!found)) { if ((theItem->GUIDType == *this) && (theItem->display == 1)) { // found it! theID = theItem->MBRType; found = 1; } else { theItem = theItem->next; } // if/else } // while if (!found) { theID = 0xFFFF; } // if (!found) return theID; } // PartType::GetHex() // Displays the available types and my extended MBR codes for same.... // Note: This function assumes an 80-column display. On wider displays, // it stops at under 80 columns; on narrower displays, lines will wrap // in an ugly way. The maxLines value is the maximum number of lines // to display before prompting to continue, or 0 (or a negative value) // for no limit. If (maxLines > 0), this function will prompt for a // substring to search for in the partition type description, so it's // imperative that maxLines be set to 0 in non-interactive contexts // (namely, sgdisk). void PartType::ShowAllTypes(int maxLines) const { int colCount = 1, lineCount = 1; size_t i; AType* thisType = allTypes; string line, matchString = ""; size_t found; cout.unsetf(ios::uppercase); if (maxLines > 0) { cout << "Type search string, or to show all codes: "; matchString = ToLower(ReadString()); } // if while (thisType != NULL) { found = ToLower(thisType->name).find(matchString); if ((thisType->display == 1) && (found != string::npos)) { // show it cout.fill('0'); cout.width(4); cout << hex << thisType->MBRType << " "; cout << thisType->name.substr(0, DESC_LENGTH); for (i = 0; i < (DESC_LENGTH - (thisType->name.substr(0, DESC_LENGTH).length())); i++) cout << " "; if ((colCount % NUM_COLUMNS) == 0) { if (thisType->next) { cout << "\n"; if ((maxLines > 0) && (lineCount++ % maxLines) == 0) { cout << "Press the key to see more codes, q to quit: "; getline(cin, line); if ((line[0] =='q') || (line[0] =='Q')) break; } // if reached screen line limit } // if there's another entry following this one } else { cout << " "; } colCount++; } // if thisType = thisType->next; } // while cout.fill(' '); cout << "\n" << dec; } // PartType::ShowAllTypes(int maxLines) // Returns 1 if code is a valid extended MBR code, 0 if it's not int PartType::Valid(uint16_t code) const { AType* thisType = allTypes; int found = 0; while ((thisType != NULL) && (!found)) { if (thisType->MBRType == code) { found = 1; } // if thisType = thisType->next; } // while return found; } // PartType::Valid() gptfdisk-1.0.5/fixparts.html0000644000175000017500000003117613622612343016334 0ustar rodsmithrodsmithContent-type: text/html; charset=UTF-8 Man page of FIXPARTS

FIXPARTS

Section: FixParts Manual (8)
Updated: 1.0.5
Index Return to Main Contents
 

NAME

fixparts - MBR partition table repair utility  

SYNOPSIS

fixparts device

 

DESCRIPTION

FixParts (aka fixparts) is a text-mode menu-driven program for repairing certain types of problems with Master Boot Record (MBR) partition tables. The program has three design goals, although a few additional features are supported, as well:

*
It can remove stray GUID Partition Table (GPT) data, which can be left behind on a disk that was once used as a GPT disk but then incompletely converted to the more common (as of 2011) MBR form.

*
It can repair mis-sized extended partitions -- either partitions that extend beyond the physical end of the disk or that overlap with nearby primary partitions. FixParts is designed in such a way that this type of repair occurs automatically, so if it's the only problem with your disk, you can launch the program and then immediately save the partition table, making no manual changes, and the program will fix the problem.

*
You can change primary partitions into logical partitions or vice-versa, within constraints imposed by the MBR data structures.

Additional features include the ability to change partition type codes or boot/active flags, to delete partitions, and to recompute CHS values. With the possible exception of recomputing CHS values, these secondary features are better performed with fdisk, because fixparts' design means that it's likely to alter partition numbering even when such changes are not requested.

The fixparts program employs a user interface similar to that of Linux's fdisk, but fixparts is much more specialized. Most importantly, you can't create new partitions with fixparts, although you can change primary/logical assignment.

In the MBR scheme, partitions come in three varieties:

primary
These partitions are defined in the first sector of the hard disk and are limited in number to four. Some OSes, such as Windows and FreeBSD, must boot from a primary partition.

extended
Extended partitions are specialized primary partitions. They serve as holding areas for logical partitions.

logical
A disk can contain an arbitrary number of logical partitions (fixparts, however, imposes a limit of 124 logical partitions). All the logical partitions reside inside a single extended partition, and are defined using a linked-list data structure. This fact means that every logical partition must be preceded by at least one sector of unallocated space to hold its defining data structure (an Extended Boot Record, or EBR).

These distinctions mean that primary and logical partitions cannot be arbitrarily interspersed. A disk can contain one to three primary partitions, a block of one or more logical partitions, and one to three more primary partitions (for a total of three primary partitions, not counting the extended partition). Primary partitions may not be sandwiched between logical partitions, since this would mean placing a primary partition within an extended partition (which is just a specific type of primary partition).

Unlike most disk utilities, fixparts' user interface ignores extended partitions. Internally, the program discards the information on the original extended partition and, when you tell it to save its changes, it generates a new extended partition to contain the then-defined logical partitions. This is done because most of the repairs and manipulations the tool performs require generating a fresh extended partition, so keeping the original in the user interface would only be a complication.

Another unusual feature of fixparts' user interface is that partition numbers do not necessarily correlate with primary/logical status. In most utilities, partitions 1-4 correspond to primary partitions, whereas partitions 5 and up are logical partitions. In fixparts, any partition number may be assigned primary or logical status, so long as the rules for layout described earlier are obeyed. When the partition table is saved, partitions will be assigned appropriately and then tools such as the Linux kernel and fdisk will give them conventional numbers.

When it first starts, fixparts performs a scan for GPT data. If the disk looks like a conventional GPT disk, fixparts refuses to run. If the disk appears to be a conventional MBR disk but GPT signatures are present in the GPT primary or secondary header areas, fixparts offers to delete this extraneous data. If you tell it to do so, the program immediately wipes the GPT header or headers. (If only one header was found, only that one header will be erased, to minimize the risk of damaging a boot loader or other data that might have overwritten just one of the GPT headers.)

With the exception of optionally erasing leftover GPT data when it first starts, fixparts keeps all changes in memory until the user writes changes with the w command. Thus, you can adjust your partitions in the user interface and abort those changes by typing q to quit without saving changes.

 

OPTIONS

The fixparts utility supports no command-line options, except for specification of the target device.

Most interactions with fixparts occur with its interactive text-mode menu. Specific functions are:

a
Toggle the active/boot flag. This flag is required by some boot loaders and OSes.

c
Recompute the cylinder/head/sector (CHS) values for all partitions. CHS addressing mode is largely obsolete, but some OSes and utilities complain if they don't like the CHS values. Note that fixparts' CHS values are likely to be incorrect on disks smaller than about 8 GiB except on Linux.

l
Change a partition's status to logical. This option will only work if the current partition layout supports such a change. Note that if changing a partition's status in this way is not currently possible, making some other change may make it possible. For instance, omitting a partition that precedes the target partition may enable converting a partition to logical form if there had been no free sectors between the two partitions.

o
Omit a partition. Once omitted, the partition will still appear in the fixparts partition list, but it will be flagged as omitted. You can subsequently convert it to primary or logical form with the r or l commands, respectively. When you save your changes with w, though, the partition will be lost.

p
Display basic partition summary data. This includes partition's number, the boot/active flag's status, starting and ending sector numbers, primary/logical/omitted status, whether or not the partition may be converted to logical form, and the partition's MBR types code.

q
Quit from the program without saving your changes. 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.

r
Change a partition's status to primary. This option will only work if the current partition layout supports such a change. Note that every partition can theoretically become a primary partition, although in some configurations, making this change will require omitting some partitions. If fixparts refuses to allow changing a partition to primary, you may need to convert other partitions to logical form or omit them entirely.

s
Sort partition entries. This option orders partitions in the display to match their on-disk positions, which can make understanding the disk layout easier in some cases. This option has no effect on the ultimate ordering of logical partitions, which are sorted before being saved. The order of primary partitions in the final saved partition table may be affected by this option. In both cases, as already noted, the partition numbers displayed by fixparts may not be the same as those used by the kernel or displayed by other partitioning tools.

t
Change a partition's type code. You enter the type code using a one-byte hexadecimal number.

w
Write data. Use this command to save your changes and exit from the program.

?
Print the menu. Type this command (or any other unrecognized command) to see a summary of available options.

 

BUGS

Known bugs and limitations include:

*
The program compiles correctly only on Linux, FreeBSD, Mac OS X, and Windows. 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. Under FreeBSD, 32-bit (x86) and 64-bit (x86-64) versions have been tested. Only 32-bit versions for Mac OS X and Windows have been tested.

*
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 gpt, fdisk, and dd.) This limitation can be overcome by typing sysctl kern.geom.debugflags=16 at a shell prompt.

*
The program can load only up to 128 partitions (4 primary partitions and 124 logical partitions). This limit can be raised by changing the #define MAX_MBR_PARTS line in the basicmbr.h source code file and recompiling.

*
The program can read partitions only if the disk has correct LBA partition descriptors. These descriptors should be present on any disk over 8 GiB in size or on smaller disks partitioned with any but very ancient software.

*
The program makes no effort to preserve partition numbers. This can have consequences for boot loaders and for mounting filesystems via /etc/fstab. It may be necessary to edit configuration files or even to re-install your boot loader.

*

The program may change the order of partitions in the partition table.

 

AUTHORS

Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)

Contributors:

* Yves Blusseau (1otnwmz02@sneakemail.com)

* David Hubbard (david.c.hubbard@gmail.com)

* Justin Maggard (justin.maggard@netgear.com)

* Dwight Schauer (dschauer@gmail.com)

* Florian Zumbiehl (florz@florz.de)

 

SEE ALSO

cfdisk(8), cgdisk(8), fdisk(8), mkfs(8), parted(8), sfdisk(8), gdisk(8), sgdisk(8).

http://en.wikipedia.org/wiki/Master_boot_record

http://www.rodsbooks.com/fixparts/

 

AVAILABILITY

The fixparts command is part of the GPT fdisk package and is available from Rod Smith.


 

Index

NAME
SYNOPSIS
DESCRIPTION
OPTIONS
BUGS
AUTHORS
SEE ALSO
AVAILABILITY

This document was created by man2html, using the manual pages.
Time: 22:34:11 GMT, February 17, 2020 gptfdisk-1.0.5/fixparts.80000664000175000017500000002506013622612343015534 0ustar rodsmithrodsmith.\" Copyright 2011-2020 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU General Public License .TH "FIXPARTS" "8" "1.0.5" "Roderick W. Smith" "FixParts Manual" .SH "NAME" fixparts \- MBR partition table repair utility .SH "SYNOPSIS" .BI "fixparts " .I device .SH "DESCRIPTION" FixParts (aka \fBfixparts\fR) is a text\-mode menu\-driven program for repairing certain types of problems with Master Boot Record (MBR) partition tables. The program has three design goals, although a few additional features are supported, as well: .TP .B * It can remove stray GUID Partition Table (GPT) data, which can be left behind on a disk that was once used as a GPT disk but then incompletely converted to the more common (as of 2011) MBR form. .TP .B * It can repair mis-sized extended partitions \-\- either partitions that extend beyond the physical end of the disk or that overlap with nearby primary partitions. FixParts is designed in such a way that this type of repair occurs automatically, so if it's the only problem with your disk, you can launch the program and then immediately save the partition table, making no manual changes, and the program will fix the problem. .TP .B * You can change primary partitions into logical partitions or vice\-versa, within constraints imposed by the MBR data structures. .PP Additional features include the ability to change partition type codes or boot/active flags, to delete partitions, and to recompute CHS values. With the possible exception of recomputing CHS values, these secondary features are better performed with \fBfdisk\fR, because \fBfixparts\fR' design means that it's likely to alter partition numbering even when such changes are not requested. The \fBfixparts\fR program employs a user interface similar to that of Linux's \fBfdisk\fR, but \fBfixparts\fR is much more specialized. Most importantly, you can't create new partitions with \fBfixparts\fR, although you can change primary/logical assignment. In the MBR scheme, partitions come in three varieties: .TP .B primary These partitions are defined in the first sector of the hard disk and are limited in number to four. Some OSes, such as Windows and FreeBSD, must boot from a primary partition. .TP .B extended Extended partitions are specialized primary partitions. They serve as holding areas for logical partitions. .TP .B logical A disk can contain an arbitrary number of logical partitions (\fBfixparts\fR, however, imposes a limit of 124 logical partitions). All the logical partitions reside inside a single extended partition, and are defined using a linked-list data structure. This fact means that every logical partition must be preceded by at least one sector of unallocated space to hold its defining data structure (an Extended Boot Record, or EBR). .PP These distinctions mean that primary and logical partitions cannot be arbitrarily interspersed. A disk can contain one to three primary partitions, a block of one or more logical partitions, and one to three more primary partitions (for a total of three primary partitions, not counting the extended partition). Primary partitions may not be sandwiched between logical partitions, since this would mean placing a primary partition within an extended partition (which is just a specific type of primary partition). Unlike most disk utilities, \fBfixparts\fR' user interface ignores extended partitions. Internally, the program discards the information on the original extended partition and, when you tell it to save its changes, it generates a new extended partition to contain the then-defined logical partitions. This is done because most of the repairs and manipulations the tool performs require generating a fresh extended partition, so keeping the original in the user interface would only be a complication. Another unusual feature of \fBfixparts\fR' user interface is that partition numbers do not necessarily correlate with primary/logical status. In most utilities, partitions 1\-4 correspond to primary partitions, whereas partitions 5 and up are logical partitions. In \fBfixparts\fR, any partition number may be assigned primary or logical status, so long as the rules for layout described earlier are obeyed. When the partition table is saved, partitions will be assigned appropriately and then tools such as the Linux kernel and \fBfdisk\fR will give them conventional numbers. When it first starts, \fBfixparts\fR performs a scan for GPT data. If the disk looks like a conventional GPT disk, \fBfixparts\fR refuses to run. If the disk appears to be a conventional MBR disk but GPT signatures are present in the GPT primary or secondary header areas, \fBfixparts\fR offers to delete this extraneous data. If you tell it to do so, the program immediately wipes the GPT header or headers. (If only one header was found, only that one header will be erased, to minimize the risk of damaging a boot loader or other data that might have overwritten just one of the GPT headers.) With the exception of optionally erasing leftover GPT data when it first starts, \fBfixparts\fR keeps all changes in memory until the user writes changes with the \fBw\fR command. Thus, you can adjust your partitions in the user interface and abort those changes by typing \fBq\fR to quit without saving changes. .SH "OPTIONS" The \fBfixparts\fR utility supports no command\-line options, except for specification of the target device. Most interactions with \fBfixparts\fR occur with its interactive text\-mode menu. Specific functions are: .TP .B a Toggle the active/boot flag. This flag is required by some boot loaders and OSes. .TP .B c Recompute the cylinder/head/sector (CHS) values for all partitions. CHS addressing mode is largely obsolete, but some OSes and utilities complain if they don't like the CHS values. Note that \fBfixparts\fR' CHS values are likely to be incorrect on disks smaller than about 8 GiB except on Linux. .TP .B l Change a partition's status to logical. This option will only work if the current partition layout supports such a change. Note that if changing a partition's status in this way is not currently possible, making some other change may make it possible. For instance, omitting a partition that precedes the target partition may enable converting a partition to logical form if there had been no free sectors between the two partitions. .TP .B o Omit a partition. Once omitted, the partition will still appear in the \fBfixparts\fR partition list, but it will be flagged as omitted. You can subsequently convert it to primary or logical form with the \fBr\fR or \fBl\fR commands, respectively. When you save your changes with \fBw\fR, though, the partition will be lost. .TP .B p Display basic partition summary data. This includes partition's number, the boot/active flag's status, starting and ending sector numbers, primary/logical/omitted status, whether or not the partition may be converted to logical form, and the partition's MBR types code. .TP .B q 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 Change a partition's status to primary. This option will only work if the current partition layout supports such a change. Note that every partition can theoretically become a primary partition, although in some configurations, making this change will require omitting some partitions. If \fBfixparts\fR refuses to allow changing a partition to primary, you may need to convert other partitions to logical form or omit them entirely. .TP .B s Sort partition entries. This option orders partitions in the display to match their on-disk positions, which can make understanding the disk layout easier in some cases. This option has no effect on the ultimate ordering of logical partitions, which are sorted before being saved. The order of primary partitions in the final saved partition table may be affected by this option. In both cases, as already noted, the partition numbers displayed by \fBfixparts\fR may not be the same as those used by the kernel or displayed by other partitioning tools. .TP .B t Change a partition's type code. You enter the type code using a one\-byte hexadecimal number. .TP .B w Write data. Use this command to save your changes and exit from the program. .TP .B ? Print the menu. Type this command (or any other unrecognized command) to see a summary of available options. .PP .SH "BUGS" Known bugs and limitations include: .TP .B * The program compiles correctly only on Linux, FreeBSD, Mac OS X, and Windows. 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. Under FreeBSD, 32\-bit (x86) and 64\-bit (x86\-64) versions have been tested. Only 32\-bit versions for Mac OS X and Windows have been tested. .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 \fBgpt\fR, \fBfdisk\fR, and \fBdd\fR.) This limitation can be overcome by typing \fBsysctl kern.geom.debugflags=16\fR at a shell prompt. .TP .B * The program can load only up to 128 partitions (4 primary partitions and 124 logical partitions). This limit can be raised by changing the \fI#define MAX_MBR_PARTS\fR line in the \fIbasicmbr.h\fR source code file and recompiling. .TP .B * The program can read partitions only if the disk has correct LBA partition descriptors. These descriptors should be present on any disk over 8 GiB in size or on smaller disks partitioned with any but very ancient software. .TP .B * The program makes no effort to preserve partition numbers. This can have consequences for boot loaders and for mounting filesystems via \fB/etc/fstab\fR. It may be necessary to edit configuration files or even to re-install your boot loader. .TP .B * The program may change the order of partitions in the partition table. .PP .SH "AUTHORS" Primary author: Roderick W. Smith (rodsmith@rodsbooks.com) Contributors: * Yves Blusseau (1otnwmz02@sneakemail.com) * David Hubbard (david.c.hubbard@gmail.com) * Justin Maggard (justin.maggard@netgear.com) * Dwight Schauer (dschauer@gmail.com) * Florian Zumbiehl (florz@florz.de) .SH "SEE ALSO" .BR cfdisk (8), .BR cgdisk (8), .BR fdisk (8), .BR mkfs (8), .BR parted (8), .BR sfdisk (8), .BR gdisk (8), .BR sgdisk (8). \fIhttp://en.wikipedia.org/wiki/Master_boot_record\fR \fIhttp://www.rodsbooks.com/fixparts/\fR .SH "AVAILABILITY" The \fBfixparts\fR command is part of the \fIGPT fdisk\fR package and is available from Rod Smith. gptfdisk-1.0.5/gdisk.80000664000175000017500000007044713622612343015006 0ustar rodsmithrodsmith.\" Copyright 2011-2020 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU General Public License .TH "GDISK" "8" "1.0.5" "Roderick W. Smith" "GPT fdisk Manual" .SH "NAME" gdisk \- Interactive GUID partition table (GPT) manipulator .SH "SYNOPSIS" .BI "gdisk " [ \-l ] .I device .SH "DESCRIPTION" 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 GUID partition table. When used with the \fI\-l\fR command\-line option, the program displays the current partition table and then exits. 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 \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. 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 \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 \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 leave your partitions unmodified. 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 exclusively. Thus, GPT data structures, and therefore \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 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, \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, \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 \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 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 \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 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 \fIEFI System Partition\fR (\fBgdisk\fR internal code 0xEF00) formatted as FAT\-32. I recommended making this partition 550 MiB. (Smaller ESPs are common, but some EFIs have flaky FAT drivers that necessitate a larger partition for reliable operation.) 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 \fIBIOS Boot Partition\fR (\fBgdisk\fR internal code 0xEF02), in which the secondary boot loader is stored, possibly without the benefit of a filesystem. (GRUB2 may optionally use such a partition.) This partition can typically be quite small (roughly 32 to 200 KiB, although 1 MiB is more common in practice), but you should consult your boot loader documentation for details. .TP .B * 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 old versions of GNU Parted create all FAT partitions as this type, which actually makes the partition unusable for normal file storage in both Windows and Mac OS X.) .TP .B * Some OSes' GPT utilities create some blank space (typically 128 MiB) after each partition. The intent is to enable future disk utilities to use this space. Such free space is not required of GPT disks, but creating it may 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" .TP .B \-l List the partition table for the specified device and then exits. .PP 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 Save partition data to a backup file. You can back up your current in\-memory 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 backup is of the current in\-memory data structures, so if you launch the program, make changes, and then use this option, the backup will reflect your changes. Note also 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 proper entry and display of anything beyond basic ASCII values requires suitable locale and font support. 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. Note that the GPT partition name is different from the filesystem name, which is encoded in the filesystem's data structures. .TP .B d 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, \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 \fBgdisk\fR's internal partition type code to a plain type name. The 'i' option displays this information for a single partition. .TP .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, \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, the codes for all varieties of FAT and NTFS partition correspond to a single GPT code (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 \fBgdisk\fR. The type code list may optionally be filtered by a search string; for instance, entering \fI\fBlinux\fR\fR shows only partition type codes with descriptions that include the string \fILinux\fR. This search is performed case\-insensitively. .TP .B n Create a new partition. This command is modeled after the equivalent \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 kibibytes (K), mebibytes (M), gibibytes (G), tebibytes (T), or pebibytes (P); 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 default range by preceding the number by a '+' or '\-' symbol, as in \fI\fB+2G\fR\fR to specify a point 2GiB after the default start 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 end of the same block for the end sector. .TP .B o Clear out all partition data. This includes GPT header data, all partition definitions, and the protective MBR. The sector alignment is reset to the default (1 MiB, or 2048 sectors on a disk with 512-byte sectors). .TP .B p Display basic partition summary data. This includes partition numbers, starting and ending sector numbers, partition sizes, \fBgdisk\fR's partition types codes, and partition names. For additional information, use the 'i' command. .TP .B q 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 sort partitions whenever they make changes. Such changes will be reflected in your device filenames, so you may need to edit \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 \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 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 Write data. Use this command to save your changes. .TP .B x 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 .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 b 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 \fBdd\fR). .TP .B c 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 instead. Note that this will almost certainly produce no or strange partition entries if you've just converted an MBR disk to GPT format, since there will be no backup partition table on disk. .TP .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 .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 .B f 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 Convert GPT into MBR and exit. This option converts as many partitions as possible 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 primary MBR partitions or three primary partitions and as many logical partitions as can be generated. Each logical partition requires at least one unallocated block immediately before its first block. Therefore, it may be possible to convert a maximum of four partitions on disks with tightly\-packed partitions; however, if free space was inserted between partitions when they were created, and if the disk is under 2 TiB in size, it should be possible to convert all the partitions to MBR form. See also the 'h' option. .TP .B h 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 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 re\-create the hybrid MBR if you use such tools. Unlike the 'g' option, this option does not support converting any partitions into MBR logical partitions. .TP .B i Show detailed partition information. This option is identical to the 'i' option on the main menu. .TP .B l 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 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 d Display the sector alignment value. See the description of the 'l' option for more details. .TP .B e Move backup GPT data structures to the end of the disk. Use this command if you've added disks to a RAID array, thus creating a virtual disk with space that follows the backup GPT data structures. This command moves the backup GPT data structures to the end of the disk, where they belong. .TP .B f Randomize the disk's GUID and all partitions' unique GUIDs (but not their partition type code GUIDs). This function may be used after cloning a disk with another utility in order to render all GUIDs once again unique. .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 h Recompute CHS values in protective or hybrid MBR. This option can sometimes help if a disk utility, OS, or BIOS doesn't like the CHS values used by the partitions in the protective or hybrid MBR. In particular, the GPT specification requires a CHS value of 0xFFFFFF for over-8GiB partitions, but this value is technically illegal by the usual standards. Some BIOSes hang if they encounter this value. This option will recompute a more normal CHS value -- 0xFEFFFF for over-8GiB partitions, enabling these BIOSes to boot. .TP .B i Show detailed partition information. This option is identical to the 'i' option on the main menu. .TP .B j Adjust the location of the main partition table. This value is normally 2, but it may need to be increased in some cases, such as when a system\-on\-chip (SoC) is hard\-coded to read boot code from sector 2. I recommend against adjusting this value unless doing so is absolutely necessary. .TP .B l Change the sector alignment value. Disks with more logical sectors per physical sectors (such as modern Advanced Format drives), some RAID configurations, and many SSD devices, can suffer performance problems if partitions are not aligned properly for their internal data structures. On new disks, GPT fdisk attempts to align partitions on 1 MiB boundaries (2048\-sectors on disks with 512-byte sectors) by default, which optimizes performance for all of these disk types. On pre\-partitioned disks, GPT fdisk attempts to identify the alignment value used on that disk, but will set 8-sector alignment on disks larger than 300 GB even if lesser alignment values are detected. In either case, it can be changed by using this option. .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 Enter the recovery & transformations menu. This option is identical to the 'r' option on the main menu. .TP .B s Resize partition table. The default partition table size is 128 entries. Officially, sizes of less than 16KB (128 entries, given the normal entry size) are unsupported by the GPT specification; however, in practice they seem to work, and can sometimes be useful in converting MBR disks. Larger sizes also work fine. OSes may impose their own limits on the number of partitions, though. .TP .B t Swap two partitions' entries in the partition table. One partition may be empty. For instance, if partitions 1\-4 are defined, transposing 1 and 5 results in a table with partitions numbered from 2\-5. Transposing partitions in this way has no effect on their disk space allocation; it only alters their order in the partition table. .TP .B u Replicate the current device's partition table on another device. You will be prompted to type the new device's filename. After the write operation completes, you can continue editing the original device's partition table. Note that the replicated partition table is an exact copy, including all GUIDs; if the device should have its own unique GUIDs, you should use the \fBf\fR option on the new disk. .TP .B v Verify disk. This option is identical to the 'v' option in the main menu. .TP .B z 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 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, \fBgdisk\fR usually bypasses the prompt entirely. .SH "BUGS" Known bugs and limitations include: .TP .B * The program compiles correctly only on Linux, FreeBSD, Mac OS X, and Windows. 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. Under FreeBSD, 32\-bit (x86) and 64\-bit (x86\-64) versions have been tested. Only 32\-bit versions for Mac OS X and Windows have been tested by the author, although I've heard of 64-bit versions being successfully compiled. .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 \fBgpt\fR, \fBfdisk\fR, and \fBdd\fR.) This limitation can be overcome by typing \fBsysctl kern.geom.debugflags=16\fR at a shell prompt. .TP .B * The fields used to display the start and end sector numbers for partitions in the 'p' command are 14 characters wide. This translates to a limitation of about 45 PiB. On larger disks, the displayed columns will go out of alignment. .TP .B * In the Windows version, 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' and 'p' menu options' displays; however, they should be preserved when loading and saving partitions. Binaries for Linux, FreeBSD, and OS X support full UTF-16 partition names. .TP .B * 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 \fIbasicmbr.h\fR source code file and recompiling; however, such a change will require using a larger\-than\-normal partition table. (The limit of 128 partitions was chosen because that number equals the 128 partitions supported by the most common partition table size.) .TP .B * 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 prior to conversion with \fBgdisk\fR. .TP .B * MBR conversions work only if the disk has correct LBA partition descriptors. These descriptors should be present on any disk over 8 GiB in size or on smaller disks partitioned with any but very ancient software. .TP .B * BSD disklabel support can create first and/or last partitions that overlap with the GPT data structures. This can sometimes be compensated by adjusting the partition table size, but in extreme cases the affected 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, converted in a way that creates overlaps with other partitions, or converted with incorrect start or end values. Use this feature with 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 platforms, Windows through at least Windows 7 doesn't support booting 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 .SH "AUTHORS" Primary author: Roderick W. Smith (rodsmith@rodsbooks.com) Contributors: * Yves Blusseau (1otnwmz02@sneakemail.com) * David Hubbard (david.c.hubbard@gmail.com) * Justin Maggard (justin.maggard@netgear.com) * Dwight Schauer (dschauer@gmail.com) * Florian Zumbiehl (florz@florz.de) .SH "SEE ALSO" .BR cfdisk (8), .BR cgdisk (8), .BR fdisk (8), .BR mkfs (8), .BR parted (8), .BR sfdisk (8), .BR sgdisk (8), .BR fixparts (8). \fIhttp://en.wikipedia.org/wiki/GUID_Partition_Table\fR \fIhttp://developer.apple.com/technotes/tn2006/tn2166.html\fR \fIhttp://www.rodsbooks.com/gdisk/\fR .SH "AVAILABILITY" The \fBgdisk\fR command is part of the \fIGPT fdisk\fR package and is available from Rod Smith. gptfdisk-1.0.5/attributes.h0000664000175000017500000000225013622612343016136 0ustar rodsmithrodsmith/* This program is copyright (c) 2009-2011 by Roderick W. Smith. It is distributed under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ #include #include #ifndef __GPT_ATTRIBUTES #define __GPT_ATTRIBUTES #define NUM_ATR 64 /* # of attributes -- 64, since it's a 64-bit field */ #define ATR_NAME_SIZE 25 /* maximum size of attribute names */ using namespace std; class Attributes { protected: static string atNames[NUM_ATR]; static int numAttrs; void Setup(void); uint64_t attributes; public: Attributes(void); Attributes(const uint64_t a); ~Attributes(void); void operator=(uint64_t a) {attributes = a;} uint64_t GetAttributes(void) const {return attributes;} void DisplayAttributes(void); void ShowAttributes(const uint32_t partNum); void ChangeAttributes(void); bool OperateOnAttributes(const uint32_t partNum, const string& attributeOperator, const string& attributeBits); static const string& GetAttributeName(const uint32_t bitNum) {return atNames [bitNum];} static void ListAttributes(void); }; // class Attributes ostream & operator<<(ostream & os, const Attributes & data); #endif gptfdisk-1.0.5/cgdisk.html0000644000175000017500000004411613622612343015736 0ustar rodsmithrodsmithContent-type: text/html; charset=UTF-8 Man page of CGDISK

CGDISK

Section: GPT fdisk Manual (8)
Updated: 1.0.5
Index Return to Main Contents
 

NAME

cgdisk - Curses-based GUID partition table (GPT) manipulator  

SYNOPSIS

cgdisk [ -a ] device

 

DESCRIPTION

GPT fdisk is a text-mode family of programs for creation and manipulation of partition tables. The cgdisk member of this family employs a curses-based user interface for interaction using a text-mode menuing system. 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 GUID partition table. Other members of this program family are gdisk (the most feature-rich program of the group, with a non-curses-based interactive user interface) and sgdisk (which is driven via command-line options for use by experts or in scripts). FixParts is a related program for fixing a limited set of problems with MBR disks.

For information on MBR vs. GPT, as well as GPT terminology and structure, see the extended GPT fdisk documentation at http://www.rodsbooks.com/gdisk/ or consult Wikipedia.

The cgdisk program employs a user interface similar to that of Linux's cfdisk, but cgdisk modifies GPT partitions. It also has the capability of transforming MBR partitions or BSD disklabels into GPT partitions. Like the original cfdisk program, cgdisk 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 Quit option to leave your partitions unmodified.

Ordinarily, cgdisk operates on disk device files, such as /dev/sda or /dev/hda under Linux, /dev/disk0 under Mac OS X, or /dev/ad0 or /dev/da0 under FreeBSD. The program can also operate on disk image files, which can be either copies of whole disks (made with dd, for instance) or raw disk images used by emulators such as QEMU or VMWare. Note that only raw disk images are supported; cgdisk cannot work on compressed or other advanced disk image formats.

Upon start, cgdisk attempts to identify the partition type in use on the disk. If it finds valid GPT data, cgdisk will use it. If cgdisk 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.) Upon exiting with the 'w' option, cgdisk replaces the MBR or disklabel with a GPT. This action is potentially dangerous! 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 cgdisk on an MBR disk, you can safely exit the program without making any changes by using the Quit option.

When creating a fresh partition table, certain considerations may be in order:

*
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.

*
Boot disks for EFI-based systems require an EFI System Partition (GPT fdisk internal code 0xEF00) formatted as FAT-32. The recommended size of this partition is between 100 and 300 MiB. Boot-related files are stored here. (Note that GNU Parted identifies such partitions as having the "boot flag" set.)

*
The GRUB 2 boot loader for BIOS-based systems makes use of a BIOS Boot Partition (GPT fdisk internal code 0xEF02), in which the secondary boot loader is stored, without the benefit of a filesystem. This partition can typically be quite small (roughly 32 KiB to 1 MiB), but you should consult your boot loader documentation for details.

*
If Windows is to boot from a GPT disk, a partition of type Microsoft Reserved (GPT fdisk 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 old versions of GNU Parted create all FAT partitions as this type, which actually makes the partition unusable for normal file storage in both Windows and Mac OS X.)

*
Some OSes' GPT utilities create some blank space (typically 128 MiB) after each partition. The intent is to enable future disk utilities to use this space. Such free space is not required of GPT disks, but creating it may 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.

 

OPTIONS

Only one command-line option is accepted, aside from the device filename: -a. This option alters the highlighting of partitions and blocks of free space: Instead of using ncurses, when -a is used cgdisk uses a ">" symbol to the left of the selected partition or free space. This option is intended for use on limited display devices such as teletypes and screen readers.

Interactions with cgdisk occur with its interactive text-mode menus. The display is broken into two interactive parts:

*
The partition display area, in which partitions and gaps between them (marked as "free space") are summarized.

*
The option selection area, in which buttons for the main options appear.

In addition, the top of the display shows the program's name and version number, the device filename associated with the disk, and the disk's size in both sectors and IEEE-1541 units (GiB, TiB, and so on).

You can use the following keys to move among the various options and to select among them:

up arrow
This key moves the partition selection up by one partition.

down arrow
This key moves the partition selection down by one partition.

Page Up
This key moves the partition selection up by one screen.

Page Down
This key moves the partition selection down by one screen.

right arrow
This key moves the option selection to the right by one item.

left arrow
This key moves the option selection to the left by one item.

Enter
This key activates the currently selected option. You can also activate an option by typing the capitalized letter in the option's name on the keyboard, such as a to activate the Align option.

If more partitions exist than can be displayed in one screen, you can scroll between screens using the partition selection keys, much as in a text editor.

Available options are as described below. (Note that cgdisk provides a much more limited set of options than its sibling gdisk. If you need to perform partition table recovery, hybrid MBR modifcation, or other advanced operations, you should consult the gdisk documentation.)

Align
Change the sector alignment value. Disks with more logical sectors than physical sectors (such as modern Advanced Format drives), some RAID configurations, and many SSD devices, can suffer performance problems if partitions are not aligned properly for their internal data structures. On new disks, GPT fdisk attempts to align partitions on 1 MiB boundaries (2048-sectors on disks with 512-byte sectors) by default, which optimizes performance for all of these disk types. On pre-partitioned disks, GPT fdisk attempts to identify the alignment value used on that disk, but will set 8-sector alignment on disks larger than 300 GB even if lesser alignment values are detected. In either case, it can be changed by using this option.

Backup
Save partition data to a backup file. You can back up your current in-memory 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 backup is of the current in-memory data structures, so if you launch the program, make changes, and then use this option, the backup will reflect your changes.

Delete
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, gdisk deletes it, as well, and expands any adjacent 0xEE (EFI GPT) MBR protective partition to fill the new free space.

Help
Print brief descriptions of all the options.

Info
Show detailed partition information. The summary information shown in the partition display area necessarily omits many details, such as the partitions' unique GUIDs and the partitions' sector-exact start and end points. The Info option displays this information for a single partition.

Load
Load partition data from a backup file. This option is the reverse of the Backup option. Note that restoring partition data from anything but the original disk is not recommended.

naMe
Change the GPT name of a partition. This name is encoded as a UTF-16 string, but proper entry and display of anything beyond basic ASCII values requires suitable locale and font support. 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. Note that the GPT partition name is different from the filesystem name, which is encoded in the filesystem's data structures. Note also that to activate this item by typing its alphabetic equivalent, you must use M, not the more obvious N, because the latter is used by the next option....

New
Create a new partition. You enter a starting sector, a size, a type code, and a name. The start sector can be specified in absolute terms as a sector number or as a position measured in kibibytes (K), mebibytes (M), gibibytes (G), tebibytes (T), or pebibytes (P); for instance, 40M specifies a position 40MiB from the start of the disk. You can specify locations relative to the start or end of the specified default range by preceding the number by a '+' symbol, as in +2G to specify a point 2GiB after the default start sector. The size value can use the K, M, G, T, and P suffixes, too. 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 full available size for the size.

Quit
Quit from the program without saving your changes. 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.

Type
Change a single partition's type code. You enter the type code using a two-byte hexadecimal number. You may also enter a GUID directly, if you have one and cgdisk doesn't know it. If you don't know the type code for your partition, you can type L to see a list of known type codes. The type code list may optionally be filtered by a search string; for instance, entering linux shows only partition type codes with descriptions that include the string Linux. This search is performed case-insensitively.

Verify
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 most problems, though; for that, you must use gdisk. If no problems are found, this command displays a summary of unallocated disk space.

Write
Write data. Use this command to save your changes.

 

BUGS

Known bugs and limitations include:

*
The program compiles correctly only on Linux, FreeBSD, and Mac OS X. In theory, it should compile under Windows if the Ncurses library for Windows is installed, but I have not tested this capability. 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. Under FreeBSD, 32-bit (x86) and 64-bit (x86-64) versions have been tested. Only 32-bit versions for Mac OS X has been tested by the author.

*
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 gpt, fdisk, and dd.) This limitation can be overcome by typing sysctl kern.geom.debugflags=16 at a shell prompt.

*
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 #define MAX_MBR_PARTS line in the basicmbr.h source code file and recompiling; however, such a change will require using a larger-than-normal partition table. (The limit of 128 partitions was chosen because that number equals the 128 partitions supported by the most common partition table size.)

*
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 in gdisk) can sometimes overcome this problem; however, in extreme cases it may be necessary to resize a partition using GNU Parted or a similar tool prior to conversion with GPT fdisk.

*
MBR conversions work only if the disk has correct LBA partition descriptors. These descriptors should be present on any disk over 8 GiB in size or on smaller disks partitioned with any but very ancient software.

*
BSD disklabel support can create first and/or last partitions that overlap with the GPT data structures. This can sometimes be compensated by adjusting the partition table size, but in extreme cases the affected partition(s) may need to be deleted.

*
Because of the highly variable nature of BSD disklabel structures, 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!

*
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 platforms, Windows through at least Windows 7 doesn't support booting from GPT disks. Creating a hybrid MBR (using the 'h' option on the recovery & transformation menu in gdisk) or abandoning GPT in favor of MBR may be your only options in this case.

*
The cgdisk Verify function and the partition type listing obtainable by typing L in the Type function (or when specifying a partition type while creating a new partition) both currently exit ncurses mode. This limitation is a minor cosmetic blemish that does not affect functionality.

 

AUTHORS

Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)

Contributors:

* Yves Blusseau (1otnwmz02@sneakemail.com)

* David Hubbard (david.c.hubbard@gmail.com)

* Justin Maggard (justin.maggard@netgear.com)

* Dwight Schauer (dschauer@gmail.com)

* Florian Zumbiehl (florz@florz.de)

 

SEE ALSO

cfdisk(8), fdisk(8), gdisk(8), mkfs(8), parted(8), sfdisk(8), sgdisk(8), fixparts(8).

http://en.wikipedia.org/wiki/GUID_Partition_Table

http://developer.apple.com/technotes/tn2006/tn2166.html

http://www.rodsbooks.com/gdisk/

 

AVAILABILITY

The cgdisk command is part of the GPT fdisk package and is available from Rod Smith.


 

Index

NAME
SYNOPSIS
DESCRIPTION
OPTIONS
BUGS
AUTHORS
SEE ALSO
AVAILABILITY

This document was created by man2html, using the manual pages.
Time: 22:34:11 GMT, February 17, 2020 gptfdisk-1.0.5/guid.cc0000664000175000017500000001640013622612343015040 0ustar rodsmithrodsmith// // C++ Implementation: GUIDData // // Description: GUIDData class header // Implements the GUIDData data structure and support methods // // // Author: Rod Smith , (C) 2010-2011 // // Copyright: See COPYING file that comes with this distribution // // #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #include #include #include #include #include #include "guid.h" #include "support.h" using namespace std; bool GUIDData::firstInstance = 1; GUIDData::GUIDData(void) { if (firstInstance) { srand((unsigned int) time(0)); firstInstance = 0; } // if Zero(); } // constructor GUIDData::GUIDData(const GUIDData & orig) { memcpy(uuidData, orig.uuidData, sizeof(uuidData)); } // copy constructor GUIDData::GUIDData(const string & orig) { operator=(orig); } // copy (from string) constructor GUIDData::GUIDData(const char * orig) { operator=(orig); } // copy (from char*) constructor GUIDData::~GUIDData(void) { } // destructor GUIDData & GUIDData::operator=(const GUIDData & orig) { memcpy(uuidData, orig.uuidData, sizeof(uuidData)); return *this; } // GUIDData::operator=(const GUIDData & orig) // Assign the GUID from a string input value. A GUID is normally formatted // with four dashes as element separators, for a total length of 36 // characters. If the input string is this long or longer, this function // assumes standard separator positioning; if the input string is less // than 36 characters long, this function assumes the input GUID has // been compressed by removal of separators. In either event, there's // little in the way of sanity checking, so garbage in = garbage out! // One special case: If the first character is 'r' or 'R', a random // GUID is assigned. GUIDData & GUIDData::operator=(const string & orig) { string copy, fragment; size_t len; // Break points for segments, either with or without characters separating the segments.... size_t longSegs[6] = {0, 9, 14, 19, 24, 36}; size_t shortSegs[6] = {0, 8, 12, 16, 20, 32}; size_t *segStart = longSegs; // Assume there are separators between segments // If first character is an 'R' or 'r', set a random GUID; otherwise, // try to parse it as a real GUID if ((orig[0] == 'R') || (orig[0] == 'r')) { Randomize(); } else { Zero(); // Delete stray spaces.... copy = DeleteSpaces(orig); // If length is too short, assume there are no separators between segments len = copy.length(); if (len < 36) { segStart = shortSegs; }; // Extract data fragments at fixed locations and convert to // integral types.... if (len >= segStart[1]) { uuidData[3] = StrToHex(copy, 0); uuidData[2] = StrToHex(copy, 2); uuidData[1] = StrToHex(copy, 4); uuidData[0] = StrToHex(copy, 6); } // if if (len >= segStart[2]) { uuidData[5] = StrToHex(copy, (unsigned int) segStart[1]); uuidData[4] = StrToHex(copy, (unsigned int) segStart[1] + 2); } // if if (len >= segStart[3]) { uuidData[7] = StrToHex(copy, (unsigned int) segStart[2]); uuidData[6] = StrToHex(copy, (unsigned int) segStart[2] + 2); } // if if (len >= segStart[4]) { uuidData[8] = StrToHex(copy, (unsigned int) segStart[3]); uuidData[9] = StrToHex(copy, (unsigned int) segStart[3] + 2); } // if if (len >= segStart[5]) { uuidData[10] = StrToHex(copy, (unsigned int) segStart[4]); uuidData[11] = StrToHex(copy, (unsigned int) segStart[4] + 2); uuidData[12] = StrToHex(copy, (unsigned int) segStart[4] + 4); uuidData[13] = StrToHex(copy, (unsigned int) segStart[4] + 6); uuidData[14] = StrToHex(copy, (unsigned int) segStart[4] + 8); uuidData[15] = StrToHex(copy, (unsigned int) segStart[4] + 10); } // if } // if/else randomize/set value return *this; } // GUIDData::operator=(const string & orig) // Assignment from C-style string; rely on C++ casting.... GUIDData & GUIDData::operator=(const char * orig) { return operator=((string) orig); } // GUIDData::operator=(const char * orig) // Erase the contents of the GUID void GUIDData::Zero(void) { memset(uuidData, 0, sizeof(uuidData)); } // GUIDData::Zero() // Set a completely random GUID value.... // The uuid_generate() function returns a value that needs to have its // first three fields byte-reversed to conform to Intel's GUID layout. // The Windows UuidCreate() function doesn't need this adjustment. If // neither function is defined, or if UuidCreate() fails, set a completely // random GUID -- not completely kosher, but it works on most platforms // (immediately after creating the UUID on Windows 7 being an important // exception). void GUIDData::Randomize(void) { int i, uuidGenerated = 0; #ifdef _UUID_UUID_H uuid_generate(uuidData); ReverseBytes(&uuidData[0], 4); ReverseBytes(&uuidData[4], 2); ReverseBytes(&uuidData[6], 2); uuidGenerated = 1; #endif #if defined (_RPC_H) || defined (__RPC_H__) UUID MsUuid; if (UuidCreate(&MsUuid) == RPC_S_OK) { memcpy(uuidData, &MsUuid, 16); uuidGenerated = 1; } // if #endif if (!uuidGenerated) { cerr << "Warning! Unable to generate a proper UUID! Creating an improper one as a last\n" << "resort! Windows 7 may crash if you save this partition table!\a\n"; for (i = 0; i < 16; i++) uuidData[i] = (unsigned char) (256.0 * (rand() / (RAND_MAX + 1.0))); } // if } // GUIDData::Randomize // Equality operator; returns 1 if the GUIDs are equal, 0 if they're unequal int GUIDData::operator==(const GUIDData & orig) const { return !memcmp(uuidData, orig.uuidData, sizeof(uuidData)); } // GUIDData::operator== // Inequality operator; returns 1 if the GUIDs are unequal, 0 if they're equal int GUIDData::operator!=(const GUIDData & orig) const { return !operator==(orig); } // GUIDData::operator!= // Return the GUID as a string, suitable for display to the user. string GUIDData::AsString(void) const { char theString[40]; sprintf(theString, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", uuidData[3], uuidData[2], uuidData[1], uuidData[0], uuidData[5], uuidData[4], uuidData[7], uuidData[6], uuidData[8], uuidData[9], uuidData[10], uuidData[11], uuidData[12], uuidData[13], uuidData[14], uuidData[15]); return theString; } // GUIDData::AsString(void) // Delete spaces or braces (which often enclose GUIDs) from the orig string, // returning modified string. string GUIDData::DeleteSpaces(string s) { size_t position; if (s.length() > 0) { for (position = s.length(); position > 0; position--) { if ((s[position - 1] == ' ') || (s[position - 1] == '{') || (s[position - 1] == '}')) { s.erase(position - 1, 1); } // if } // for } // if return s; } // GUIDData::DeleteSpaces() /******************************* * * * Non-class support functions * * * *******************************/ // Display a GUID as a string.... ostream & operator<<(ostream & os, const GUIDData & data) { // string asString; os << data.AsString(); return os; } // GUIDData::operator<<() gptfdisk-1.0.5/diskio.cc0000664000175000017500000000410613622612343015372 0ustar rodsmithrodsmith// // C++ Interface: diskio (platform-independent components) // // Description: Class to handle low-level disk I/O for GPT fdisk // // // Author: Rod Smith , (C) 2009 // // 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. #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #ifdef _WIN32 #include #include #define fstat64 fstat #define stat64 stat #define S_IRGRP 0 #define S_IROTH 0 #else #include #endif #include #include #include #include #include #include #include "support.h" #include "diskio.h" //#include "gpt.h" using namespace std; DiskIO::DiskIO(void) { userFilename = ""; realFilename = ""; modelName = ""; isOpen = 0; openForWrite = 0; } // constructor DiskIO::~DiskIO(void) { Close(); } // destructor // Open a disk device for reading. Returns 1 on success, 0 on failure. int DiskIO::OpenForRead(const string & filename) { int shouldOpen = 1; if (isOpen) { // file is already open if (((realFilename != filename) && (userFilename != filename)) || (openForWrite)) { Close(); } else { shouldOpen = 0; } // if/else } // if if (shouldOpen) { userFilename = filename; MakeRealName(); OpenForRead(); } // if return isOpen; } // DiskIO::OpenForRead(string filename) // Open a disk for reading and writing by filename. // Returns 1 on success, 0 on failure. int DiskIO::OpenForWrite(const string & filename) { int retval = 0; if ((isOpen) && (openForWrite) && ((filename == realFilename) || (filename == userFilename))) { retval = 1; } else { userFilename = filename; MakeRealName(); retval = OpenForWrite(); if (retval == 0) { realFilename = userFilename = ""; } // if } // if/else return retval; } // DiskIO::OpenForWrite(string filename) gptfdisk-1.0.5/gpt.h0000664000175000017500000001762113622612343014552 0ustar rodsmithrodsmith/* gpt.h -- GPT and data structure definitions, types, and functions */ /* This program is copyright (c) 2009-2011 by Roderick W. Smith. It is distributed under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ #include #include #include "gptpart.h" #include "support.h" #include "mbr.h" #include "bsd.h" #include "gptpart.h" #ifndef __GPTSTRUCTS #define __GPTSTRUCTS // Default values for sector alignment #define DEFAULT_ALIGNMENT 2048 #define MAX_ALIGNMENT 65536 #define MIN_AF_ALIGNMENT 8 // Below constant corresponds to a ~279GiB (300GB) disk, since the // smallest Advanced Format drive I know of is 320GB in size #define SMALLEST_ADVANCED_FORMAT UINT64_C(585937500) using namespace std; /**************************************** * * * GPTData class and related structures * * * ****************************************/ // Validity state of GPT data enum GPTValidity {gpt_valid, gpt_corrupt, gpt_invalid}; // Which set of partition data to use enum WhichToUse {use_gpt, use_mbr, use_bsd, use_new, use_abort}; // Header (first 512 bytes) of GPT table #pragma pack(1) struct GPTHeader { uint64_t signature; uint32_t revision; uint32_t headerSize; uint32_t headerCRC; uint32_t reserved; uint64_t currentLBA; uint64_t backupLBA; uint64_t firstUsableLBA; uint64_t lastUsableLBA; GUIDData diskGUID; uint64_t partitionEntriesLBA; uint32_t numParts; uint32_t sizeOfPartitionEntries; uint32_t partitionEntriesCRC; unsigned char reserved2[GPT_RESERVED]; }; // struct GPTHeader #pragma pack () // Data in GPT format class GPTData { protected: struct GPTHeader mainHeader; GPTPart *partitions; uint32_t numParts; // # of partitions the table can hold struct GPTHeader secondHeader; MBRData protectiveMBR; string device; // device filename DiskIO myDisk; uint32_t blockSize; // device logical block size uint32_t physBlockSize; // device physical block size (or 0 if it can't be determined) uint64_t diskSize; // size of device, in logical blocks GPTValidity state; // is GPT valid? int justLooking; // Set to 1 if program launched with "-l" or if read-only int mainCrcOk; int secondCrcOk; int mainPartsCrcOk; int secondPartsCrcOk; int apmFound; // set to 1 if APM detected int bsdFound; // set to 1 if BSD disklabel detected in MBR uint32_t sectorAlignment; // Start partitions at multiples of sectorAlignment int beQuiet; WhichToUse whichWasUsed; int LoadHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector, int *crcOk); int LoadPartitionTable(const struct GPTHeader & header, DiskIO & disk, uint64_t sector = 0); int CheckTable(struct GPTHeader *header); int SaveHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector); int SavePartitionTable(DiskIO & disk, uint64_t sector); public: // Basic necessary functions.... GPTData(void); GPTData(const GPTData &); GPTData(string deviceFilename); virtual ~GPTData(void); GPTData & operator=(const GPTData & orig); // Verify (or update) data integrity int Verify(void); int CheckGPTSize(void); int CheckHeaderValidity(void); int CheckHeaderCRC(struct GPTHeader* header, int warn = 0); void RecomputeCRCs(void); void RebuildMainHeader(void); void RebuildSecondHeader(void); int VerifyMBR(void) {return protectiveMBR.FindOverlaps();} int FindHybridMismatches(void); int FindOverlaps(void); int FindInsanePartitions(void); // Load or save data from/to disk int SetDisk(const string & deviceFilename); DiskIO* GetDisk(void) {return &myDisk;} int LoadMBR(const string & f) {return protectiveMBR.ReadMBRData(f);} int WriteProtectiveMBR(void) {return protectiveMBR.WriteMBRData(&myDisk);} void PartitionScan(void); int LoadPartitions(const string & deviceFilename); int ForceLoadGPTData(void); int LoadMainTable(void); int LoadSecondTableAsMain(void); int SaveGPTData(int quiet = 0); int SaveGPTBackup(const string & filename); int LoadGPTBackup(const string & filename); int SaveMBR(void); int DestroyGPT(void); int DestroyMBR(void); // Display data.... void ShowAPMState(void); void ShowGPTState(void); void DisplayGPTData(void); void DisplayMBRData(void) {protectiveMBR.DisplayMBRData();} void ShowPartDetails(uint32_t partNum); // Convert between GPT and other formats virtual WhichToUse UseWhichPartitions(void); void XFormPartitions(void); int XFormDisklabel(uint32_t partNum); int XFormDisklabel(BSDData* disklabel); int OnePartToMBR(uint32_t gptPart, int mbrPart); // add one partition to MBR. Returns 1 if successful // Adjust GPT structures WITHOUT user interaction... int SetGPTSize(uint32_t numEntries, int fillGPTSectors = 1); int MoveMainTable(uint64_t pteSector); void BlankPartitions(void); int DeletePartition(uint32_t partNum); uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector); void SortGPT(void); int SwapPartitions(uint32_t partNum1, uint32_t partNum2); int ClearGPTData(void); void MoveSecondHeaderToEnd(); int SetName(uint32_t partNum, const UnicodeString & theName); void SetDiskGUID(GUIDData newGUID); int SetPartitionGUID(uint32_t pn, GUIDData theGUID); void RandomizeGUIDs(void); int ChangePartType(uint32_t pn, PartType theGUID); void MakeProtectiveMBR(void) {protectiveMBR.MakeProtectiveMBR();} void RecomputeCHS(void); int Align(uint64_t* sector); void SetProtectiveMBR(BasicMBRData & newMBR) {protectiveMBR = newMBR;} // Return data about the GPT structures.... WhichToUse GetState(void) {return whichWasUsed;} int GetPartRange(uint32_t* low, uint32_t* high); int FindFirstFreePart(void); uint32_t GetNumParts(void) {return mainHeader.numParts;} uint64_t GetTableSizeInSectors(void) {return (((numParts * GPT_SIZE) / blockSize) + (((numParts * GPT_SIZE) % blockSize) != 0)); } uint64_t GetMainHeaderLBA(void) {return mainHeader.currentLBA;} uint64_t GetSecondHeaderLBA(void) {return secondHeader.currentLBA;} uint64_t GetMainPartsLBA(void) {return mainHeader.partitionEntriesLBA;} uint64_t GetSecondPartsLBA(void) {return secondHeader.partitionEntriesLBA;} uint64_t GetFirstUsableLBA(void) {return mainHeader.firstUsableLBA;} uint64_t GetLastUsableLBA(void) {return mainHeader.lastUsableLBA;} uint32_t CountParts(void); bool ValidPartNum (const uint32_t partNum); const GPTPart & operator[](uint32_t partNum) const; const GUIDData & GetDiskGUID(void) const; uint32_t GetBlockSize(void) {return blockSize;} // Find information about free space uint64_t FindFirstAvailable(uint64_t start = 0); uint64_t FindFirstUsedLBA(void); uint64_t FindFirstInLargest(void); uint64_t FindLastAvailable(); uint64_t FindLastInFree(uint64_t start); uint64_t FindFreeBlocks(uint32_t *numSegments, uint64_t *largestSegment); int IsFree(uint64_t sector, uint32_t *partNum = NULL); int IsFreePartNum(uint32_t partNum); int IsUsedPartNum(uint32_t partNum); // Change how functions work, or return information on same void SetAlignment(uint32_t n); uint32_t ComputeAlignment(void); // Set alignment based on current partitions uint32_t GetAlignment(void) {return sectorAlignment;} void JustLooking(int i = 1) {justLooking = i;} void BeQuiet(int i = 1) {beQuiet = i;} WhichToUse WhichWasUsed(void) {return whichWasUsed;} // Endianness functions void ReverseHeaderBytes(struct GPTHeader* header); void ReversePartitionBytes(); // for endianness // Attributes functions int ManageAttributes(int partNum, const string & command, const string & bits); void ShowAttributes(const uint32_t partNum); void GetAttribute(const uint32_t partNum, const string& attributeBits); }; // class GPTData // Function prototypes.... int SizesOK(void); #endif gptfdisk-1.0.5/Makefile.mac0000644000175000017500000000363713622612343016006 0ustar rodsmithrodsmithCC=gcc CXX=c++ # FATBINFLAGS=-arch x86_64 -arch i386 -mmacosx-version-min=10.9 FATBINFLAGS=-arch x86_64 -mmacosx-version-min=10.9 THINBINFLAGS=-arch x86_64 -mmacosx-version-min=10.9 CFLAGS=$(FATBINFLAGS) -O2 -D_FILE_OFFSET_BITS=64 -g #CXXFLAGS=-O2 -Wall -D_FILE_OFFSET_BITS=64 -D USE_UTF16 -I/opt/local/include -I/usr/local/include -I/opt/local/include -g CXXFLAGS=$(FATBINFLAGS) -O2 -Wall -D_FILE_OFFSET_BITS=64 -stdlib=libc++ -I/opt/local/include -I /usr/local/include -I/opt/local/include -g LIB_NAMES=crc32 support guid gptpart mbrpart basicmbr mbr gpt bsd parttypes attributes diskio diskio-unix MBR_LIBS=support diskio diskio-unix basicmbr mbrpart #LIB_SRCS=$(NAMES:=.cc) LIB_OBJS=$(LIB_NAMES:=.o) MBR_LIB_OBJS=$(MBR_LIBS:=.o) LIB_HEADERS=$(LIB_NAMES:=.h) DEPEND= makedepend $(CFLAGS) all: gdisk sgdisk cgdisk fixparts gdisk: $(LIB_OBJS) gpttext.o gdisk.o $(CXX) $(LIB_OBJS) gpttext.o gdisk.o $(FATBINFLAGS) -o gdisk # $(CXX) $(LIB_OBJS) -L/usr/lib -licucore gpttext.o gdisk.o -o gdisk cgdisk: $(LIB_OBJS) cgdisk.o gptcurses.o $(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o /usr/lib/libncurses.dylib $(LDFLAGS) $(FATBINFLAGS) -o cgdisk # $(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o $(LDFLAGS) -licucore -lncurses -o cgdisk sgdisk: $(LIB_OBJS) gptcl.o sgdisk.o # $(CXX) $(LIB_OBJS) gptcl.o sgdisk.o /opt/local/lib/libiconv.a /opt/local/lib/libintl.a /opt/local/lib/libpopt.a $(FATBINFLAGS) -o sgdisk $(CXX) $(LIB_OBJS) gptcl.o sgdisk.o -L/usr/local/lib -lpopt $(THINBINFLAGS) -o sgdisk # $(CXX) $(LIB_OBJS) gptcl.o sgdisk.o -L/sw/lib -licucore -lpopt -o sgdisk fixparts: $(MBR_LIB_OBJS) fixparts.o $(CXX) $(MBR_LIB_OBJS) fixparts.o $(LDFLAGS) $(FATBINFLAGS) -o fixparts testguid: $(LIB_OBJS) testguid.o $(CXX) $(LIB_OBJS) testguid.o -o testguid lint: #no pre-reqs lint $(SRCS) clean: #no pre-reqs rm -f core *.o *~ gdisk sgdisk cgdisk fixparts # what are the source dependencies depend: $(SRCS) $(DEPEND) $(SRCS) $(OBJS): # DO NOT DELETE gptfdisk-1.0.5/basicmbr.h0000664000175000017500000001212313622612343015532 0ustar rodsmithrodsmith/* basicmbr.h -- MBR data structure definitions, types, and functions */ /* This program is copyright (c) 2009-2013 by Roderick W. Smith. It is distributed under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ #include #include #include "diskio.h" #include "mbrpart.h" #ifndef __BASICMBRSTRUCTS #define __BASICMBRSTRUCTS #define MBR_SIGNATURE UINT16_C(0xAA55) // Maximum number of MBR partitions #define MAX_MBR_PARTS 128 using namespace std; /**************************************** * * * MBRData class and related structures * * * ****************************************/ // A 512-byte data structure into which the MBR can be loaded in one // go. Also used when loading logical partitions. #pragma pack(1) struct TempMBR { uint8_t code[440]; uint32_t diskSignature; uint16_t nulls; struct MBRRecord partitions[4]; uint16_t MBRSignature; }; // struct TempMBR #pragma pack () // Possible states of the MBR enum MBRValidity {invalid, gpt, hybrid, mbr}; // Full data in tweaked MBR format class BasicMBRData { protected: uint8_t code[440]; uint32_t diskSignature; uint16_t nulls; // MAX_MBR_PARTS defaults to 128. This array holds both the primary and // the logical partitions, to simplify data retrieval for GPT conversions. MBRPart 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 uint32_t numHeads; // number of heads, in CHS scheme uint32_t numSecspTrack; // number of sectors per track, in CHS scheme DiskIO* myDisk; int canDeleteMyDisk; string device; MBRValidity state; MBRPart* GetPartition(int i); // Return primary or logical partition public: BasicMBRData(void); BasicMBRData(string deviceFilename); BasicMBRData(const BasicMBRData &); ~BasicMBRData(void); BasicMBRData & operator=(const BasicMBRData & orig); // File I/O functions... int ReadMBRData(const string & deviceFilename); int ReadMBRData(DiskIO * theDisk, int checkBlockSize = 1); int ReadLogicalParts(uint64_t extendedStart, int partNum); int WriteMBRData(void); int WriteMBRData(DiskIO *theDisk); int WriteMBRData(const string & deviceFilename); int WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector); void DiskSync(void) {myDisk->DiskSync();} void SetDisk(DiskIO *theDisk); // Display data for user... void DisplayMBRData(void); void ShowState(void); // GPT checks and fixes... int CheckForGPT(void); int BlankGPTData(void); // Functions that set or get disk metadata (size, CHS geometry, etc.) void SetDiskSize(uint64_t ds) {diskSize = ds;} void SetBlockSize(uint32_t bs) {blockSize = bs;} MBRValidity GetValidity(void) {return state;} void SetHybrid(void) {state = hybrid;} // Set hybrid flag void ReadCHSGeom(void); int GetPartRange(uint32_t* low, uint32_t* high); int LBAtoCHS(uint64_t lba, uint8_t * chs); // Convert LBA to CHS int FindOverlaps(void); int NumPrimaries(void); int NumLogicals(void); int CountParts(void); void UpdateCanBeLogical(void); uint64_t FirstLogicalLBA(void); uint64_t LastLogicalLBA(void); int AreLogicalsContiguous(void); int DoTheyFit(void); int SpaceBeforeAllLogicals(void); int IsLegal(void); int IsEEActive(void); int FindNextInUse(int start); // 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 EmptyBootloader(void); void AddPart(int num, const MBRPart& newPart); void MakePart(int num, uint64_t startLBA, uint64_t lengthLBA, int type = 0x07, int bootable = 0); int SetPartType(int num, int type); int SetPartBootable(int num, int bootable = 1); int MakeBiggestPart(int i, int type); // Make partition filling most space void DeletePartition(int i); int SetInclusionwChecks(int num, int inclStatus); void RecomputeCHS(int partNum); void SortMBR(int start = 0); int DeleteOversizedParts(); int DeleteExtendedParts(); void OmitOverlaps(void); void MaximizeLogicals(); void MaximizePrimaries(); void TrimPrimaries(); void MakeLogicalsContiguous(void); void MakeItLegal(void); int RemoveLogicalsFromFirstFour(void); int MovePrimariesToFirstFour(void); int CreateExtended(void); // Functions to find information on free space.... uint64_t FindFirstAvailable(uint64_t start = 1); uint64_t FindLastInFree(uint64_t start); uint64_t FindFirstInFree(uint64_t start); int SectorUsedAs(uint64_t sector, int topPartNum = MAX_MBR_PARTS); // Functions to extract data on specific partitions.... uint8_t GetStatus(int i); uint8_t GetType(int i); uint64_t GetFirstSector(int i); uint64_t GetLength(int i); // User interaction functions.... int DoMenu(const string& prompt = "\nMBR command (? for help): "); void ShowCommands(void); }; // class BasicMBRData #endif gptfdisk-1.0.5/gdisk.html0000644000175000017500000007622113622612343015575 0ustar rodsmithrodsmithContent-type: text/html; charset=UTF-8 Man page of GDISK

GDISK

Section: GPT fdisk Manual (8)
Updated: 1.0.5
Index Return to Main Contents
 

NAME

gdisk - Interactive GUID partition table (GPT) manipulator  

SYNOPSIS

gdisk [ -l ] device

 

DESCRIPTION

GPT fdisk (aka gdisk) 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 GUID partition table. When used with the -l command-line option, the program displays the current partition table and then exits.

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 gptsync or gdisk'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. For information on MBR vs. GPT, as well as GPT terminology and structure, see the extended gdisk documentation at http://www.rodsbooks.com/gdisk/ or consult Wikipedia.

The gdisk program employs a user interface similar to that of Linux's fdisk, but gdisk modifies GPT partitions. It also has the capability of transforming MBR partitions or BSD disklabels into GPT partitions. Like the original fdisk program, gdisk 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 leave your partitions unmodified.

Ordinarily, gdisk operates on disk device files, such as /dev/sda or /dev/hda under Linux, /dev/disk0 under Mac OS X, or /dev/ad0 or /dev/da0 under FreeBSD. The program can also operate on disk image files, which can be either copies of whole disks (made with dd, for instance) or raw disk images used by emulators such as QEMU or VMWare. Note that only raw disk images are supported; gdisk 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 exclusively. Thus, GPT data structures, and therefore gdisk, do not need to deal with CHS geometries and all the problems they create. Users of fdisk will note that gdisk lacks the options and limitations associated with CHS geometries.

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 gdisk or GNU Parted program.

Upon start, gdisk attempts to identify the partition type in use on the disk. If it finds valid GPT data, gdisk will use it. If gdisk 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, gdisk replaces the MBR or disklabel with a GPT. This action is potentially dangerous! 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 gdisk 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 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 /etc/fstab file.)

When creating a fresh partition table, certain considerations may be in order:

*
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.

*
Boot disks for EFI-based systems require an EFI System Partition (gdisk internal code 0xEF00) formatted as FAT-32. I recommended making this partition 550 MiB. (Smaller ESPs are common, but some EFIs have flaky FAT drivers that necessitate a larger partition for reliable operation.) Boot-related files are stored here. (Note that GNU Parted identifies such partitions as having the "boot flag" set.)

*
Some boot loaders for BIOS-based systems make use of a BIOS Boot Partition (gdisk internal code 0xEF02), in which the secondary boot loader is stored, possibly without the benefit of a filesystem. (GRUB2 may optionally use such a partition.) This partition can typically be quite small (roughly 32 to 200 KiB, although 1 MiB is more common in practice), but you should consult your boot loader documentation for details.

*
If Windows is to boot from a GPT disk, a partition of type Microsoft Reserved (gdisk 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 old versions of GNU Parted create all FAT partitions as this type, which actually makes the partition unusable for normal file storage in both Windows and Mac OS X.)

*
Some OSes' GPT utilities create some blank space (typically 128 MiB) after each partition. The intent is to enable future disk utilities to use this space. Such free space is not required of GPT disks, but creating it may 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.

 

OPTIONS

-l
List the partition table for the specified device and then exits.

Most interactions with gdisk 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:

b
Save partition data to a backup file. You can back up your current in-memory 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 backup is of the current in-memory data structures, so if you launch the program, make changes, and then use this option, the backup will reflect your changes. Note also that the restore option is on the recovery & transformation menu; the backup option is on the main menu to encourage its use.

c
Change the GPT name of a partition. This name is encoded as a UTF-16 string, but proper entry and display of anything beyond basic ASCII values requires suitable locale and font support. 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. Note that the GPT partition name is different from the filesystem name, which is encoded in the filesystem's data structures.

d
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, gdisk deletes it, as well, and expands any adjacent 0xEE (EFI GPT) MBR protective partition to fill the new free space.

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 gdisk's internal partition type code to a plain type name. The 'i' option displays this information for a single partition.

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, gdisk 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 gdisk. A one-to-one correspondence is impossible, though. Most notably, the codes for all varieties of FAT and NTFS partition correspond to a single GPT code (entered as 0x0700 in gdisk). Some OSes use a single MBR code but employ many more codes in GPT. For these, gdisk 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 gdisk. The type code list may optionally be filtered by a search string; for instance, entering linux shows only partition type codes with descriptions that include the string Linux. This search is performed case-insensitively.

n
Create a new partition. This command is modeled after the equivalent 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 kibibytes (K), mebibytes (M), gibibytes (G), tebibytes (T), or pebibytes (P); for instance, 40M specifies a position 40MiB from the start of the disk. You can specify locations relative to the start or end of the specified default range by preceding the number by a '+' or '-' symbol, as in +2G to specify a point 2GiB after the default start sector, or -200M 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 end of the same block for the end sector.

o
Clear out all partition data. This includes GPT header data, all partition definitions, and the protective MBR. The sector alignment is reset to the default (1 MiB, or 2048 sectors on a disk with 512-byte sectors).

p
Display basic partition summary data. This includes partition numbers, starting and ending sector numbers, partition sizes, gdisk's partition types codes, and partition names. For additional information, use the 'i' command.

q
Quit from the program without saving your changes. 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.

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.

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 sort partitions whenever they make changes. Such changes will be reflected in your device filenames, so you may need to edit /etc/fstab if you use this option.

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 gdisk doesn't know it.

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 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.

w
Write data. Use this command to save your changes.

x
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.

?
Print the menu. Type this command (or any other unrecognized command) to see a summary of available options.

The second gdisk 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:

b
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 dd).

c
Load backup partition table. Ordinarily, gdisk 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 instead. Note that this will almost certainly produce no or strange partition entries if you've just converted an MBR disk to GPT format, since there will be no backup partition table on disk.

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.

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.

f
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.

g
Convert GPT into MBR and exit. This option converts as many partitions as possible 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 primary MBR partitions or three primary partitions and as many logical partitions as can be generated. Each logical partition requires at least one unallocated block immediately before its first block. Therefore, it may be possible to convert a maximum of four partitions on disks with tightly-packed partitions; however, if free space was inserted between partitions when they were created, and if the disk is under 2 TiB in size, it should be possible to convert all the partitions to MBR form. See also the 'h' option.

h
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 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 re-create the hybrid MBR if you use such tools. Unlike the 'g' option, this option does not support converting any partitions into MBR logical partitions.

i
Show detailed partition information. This option is identical to the 'i' option on the main menu.

l
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.

m
Return to the main menu. This option enables you to enter main-menu commands.

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.

p
Print the partition table. This option is identical to the 'p' option in the main menu.

q
Quit without saving changes. This option is identical to the 'q' option in the main menu.

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. 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 gdisk being unable to convert a BSD disklabel is high compared to the likelihood of problems with an MBR conversion.

v
Verify disk. This option is identical to the 'v' option in the main menu.

w
Write table to disk and exit. This option is identical to the 'w' option in the main menu.

x
Enter the experts' menu. This option is identical to the 'x' option in the main menu.

?
Print the menu. This option (or any unrecognized entry) displays a summary of the menu options.

The third gdisk 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:

a
Set attributes. GPT provides a 64-bit attributes field that can be used to set features for each partition. gdisk supports four attributes: system partition, read-only, hidden, and 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.

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, 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 because of buggy GUID assignments (hopefully not in gdisk) or sheer incredible coincidence.

d
Display the sector alignment value. See the description of the 'l' option for more details.

e
Move backup GPT data structures to the end of the disk. Use this command if you've added disks to a RAID array, thus creating a virtual disk with space that follows the backup GPT data structures. This command moves the backup GPT data structures to the end of the disk, where they belong.

f
Randomize the disk's GUID and all partitions' unique GUIDs (but not their partition type code GUIDs). This function may be used after cloning a disk with another utility in order to render all GUIDs once again unique.

g
Change disk GUID. Each disk has a unique GUID code, which gdisk assigns randomly upon creation of the GPT data structures. You can generate a fresh random GUID or enter one manually with this option.

h
Recompute CHS values in protective or hybrid MBR. This option can sometimes help if a disk utility, OS, or BIOS doesn't like the CHS values used by the partitions in the protective or hybrid MBR. In particular, the GPT specification requires a CHS value of 0xFFFFFF for over-8GiB partitions, but this value is technically illegal by the usual standards. Some BIOSes hang if they encounter this value. This option will recompute a more normal CHS value -- 0xFEFFFF for over-8GiB partitions, enabling these BIOSes to boot.

i
Show detailed partition information. This option is identical to the 'i' option on the main menu.

j
Adjust the location of the main partition table. This value is normally 2, but it may need to be increased in some cases, such as when a system-on-chip (SoC) is hard-coded to read boot code from sector 2. I recommend against adjusting this value unless doing so is absolutely necessary.

l
Change the sector alignment value. Disks with more logical sectors per physical sectors (such as modern Advanced Format drives), some RAID configurations, and many SSD devices, can suffer performance problems if partitions are not aligned properly for their internal data structures. On new disks, GPT fdisk attempts to align partitions on 1 MiB boundaries (2048-sectors on disks with 512-byte sectors) by default, which optimizes performance for all of these disk types. On pre-partitioned disks, GPT fdisk attempts to identify the alignment value used on that disk, but will set 8-sector alignment on disks larger than 300 GB even if lesser alignment values are detected. In either case, it can be changed by using this option.

m
Return to the main menu. This option enables you to enter main-menu commands.

n
Create a new protective MBR. Use this option if the current protective MBR is damaged in a way that gdisk doesn't automatically detect and correct, or if you want to convert a hybrid MBR into a "pure" GPT with a conventional protective MBR.

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.

p
Print the partition table. This option is identical to the 'p' option in the main menu.

q
Quit without saving changes. This option is identical to the 'q' option in the main menu.

r
Enter the recovery & transformations menu. This option is identical to the 'r' option on the main menu.

s
Resize partition table. The default partition table size is 128 entries. Officially, sizes of less than 16KB (128 entries, given the normal entry size) are unsupported by the GPT specification; however, in practice they seem to work, and can sometimes be useful in converting MBR disks. Larger sizes also work fine. OSes may impose their own limits on the number of partitions, though.

t
Swap two partitions' entries in the partition table. One partition may be empty. For instance, if partitions 1-4 are defined, transposing 1 and 5 results in a table with partitions numbered from 2-5. Transposing partitions in this way has no effect on their disk space allocation; it only alters their order in the partition table.

u
Replicate the current device's partition table on another device. You will be prompted to type the new device's filename. After the write operation completes, you can continue editing the original device's partition table. Note that the replicated partition table is an exact copy, including all GUIDs; if the device should have its own unique GUIDs, you should use the f option on the new disk.

v
Verify disk. This option is identical to the 'v' option in the main menu.

z
Zap (destroy) the GPT data structures and exit. Use this option if you want to repartition a GPT disk using fdisk 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 or if you've already created new MBR partitions and want to erase the remnants of your GPT partitions. If you've already created new MBR partitions, it's conceivable that this option will damage the first and/or last MBR partitions! Such an event is unlikely, but could occur if your new MBR partitions overlap the old GPT data structures.

?
Print the menu. This option (or any unrecognized entry) displays a summary of the menu options.

In many cases, you can press the Enter key to select a default option when entering data. When only one option is possible, gdisk usually bypasses the prompt entirely.

 

BUGS

Known bugs and limitations include:

*
The program compiles correctly only on Linux, FreeBSD, Mac OS X, and Windows. 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. Under FreeBSD, 32-bit (x86) and 64-bit (x86-64) versions have been tested. Only 32-bit versions for Mac OS X and Windows have been tested by the author, although I've heard of 64-bit versions being successfully compiled.

*
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 gpt, fdisk, and dd.) This limitation can be overcome by typing sysctl kern.geom.debugflags=16 at a shell prompt.

*
The fields used to display the start and end sector numbers for partitions in the 'p' command are 14 characters wide. This translates to a limitation of about 45 PiB. On larger disks, the displayed columns will go out of alignment.

*
In the Windows version, 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' and 'p' menu options' displays; however, they should be preserved when loading and saving partitions. Binaries for Linux, FreeBSD, and OS X support full UTF-16 partition names.

*
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 #define MAX_MBR_PARTS line in the basicmbr.h source code file and recompiling; however, such a change will require using a larger-than-normal partition table. (The limit of 128 partitions was chosen because that number equals the 128 partitions supported by the most common partition table size.)

*
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 prior to conversion with gdisk.

*
MBR conversions work only if the disk has correct LBA partition descriptors. These descriptors should be present on any disk over 8 GiB in size or on smaller disks partitioned with any but very ancient software.

*
BSD disklabel support can create first and/or last partitions that overlap with the GPT data structures. This can sometimes be compensated by adjusting the partition table size, but in extreme cases the affected partition(s) may need to be deleted.

*
Because of the highly variable nature of BSD disklabel structures, 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!

*
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 platforms, Windows through at least Windows 7 doesn't support booting 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.

 

AUTHORS

Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)

Contributors:

* Yves Blusseau (1otnwmz02@sneakemail.com)

* David Hubbard (david.c.hubbard@gmail.com)

* Justin Maggard (justin.maggard@netgear.com)

* Dwight Schauer (dschauer@gmail.com)

* Florian Zumbiehl (florz@florz.de)

 

SEE ALSO

cfdisk(8), cgdisk(8), fdisk(8), mkfs(8), parted(8), sfdisk(8), sgdisk(8), fixparts(8).

http://en.wikipedia.org/wiki/GUID_Partition_Table

http://developer.apple.com/technotes/tn2006/tn2166.html

http://www.rodsbooks.com/gdisk/

 

AVAILABILITY

The gdisk command is part of the GPT fdisk package and is available from Rod Smith.


 

Index

NAME
SYNOPSIS
DESCRIPTION
OPTIONS
BUGS
AUTHORS
SEE ALSO
AVAILABILITY

This document was created by man2html, using the manual pages.
Time: 22:34:11 GMT, February 17, 2020 gptfdisk-1.0.5/README0000664000175000017500000003277213622612343014473 0ustar rodsmithrodsmithGPT fdisk (aka gdisk, cgdisk, and sgdisk) and FixParts by Roderick W. Smith, rodsmith@rodsbooks.com Introduction ------------ This package includes the source code for four related disk partitioning programs: - gdisk -- This program is modeled after Linux fdisk, but it operates on GUID Partition Table (GPT) disks rather than the Master Boot Record (MBR) disks that fdisk modifies. As such, gdisk is an interactive text-mode tool for manipulating partitions, but it does nothing to the contents of those partitions (usually filesystems, but sometimes swap space or other data). - cgdisk -- This program is modeled after Linux cfdisk, but it operates on GPT disks rather than the MBR disks that cfdisk modifies. As such, cgdisk is a curses-based text-mode tool for manipulating partitions, which is to say that it uses an interface that relies on arrow keys and a dynamic display rather than the command letters and a scrolling display like gdisk uses. - sgdisk -- This program is conceptually similar to the Linux sfdisk and FreeBSD gpt programs, but its operational details differ. It enables manipulation of GPT disks using command-line options, so it's suitable for use in scripts or by experts to perform specific tasks that might take several commands in gdisk to accomplish. - fixparts -- This program, unlike the preceding three, operates on MBR disks. It's intended to fix certain problems that can be created by various utilities. Specifically, it can fix mis-sized extended partitions and primary partitions located in the middle of extended partitions. It also enables changing primary vs. logical partition status (within limits of what's legal in the MBR scheme) and making a few other minor changes. It does NOT support creating new partitions; for that, you should use fdisk, parted, or some other tool. More details about the abilities of these tools follows. All four programs rely on the same set of underlying code base; they differ only in their control interfaces (defined in gdisk.cc, cgdisk.cc, sgdisk.cc, and fixparts.cc, respectively) and in which support code they use. GPT fdisk (gdisk, cgdisk, and sgdisk) Details --------------------------------------------- The gdisk program is intended as a (somewhat) fdisk-workalike program for GPT-partitioned disks, cgdisk is similarly a workalike for fdisk, and sgdisk provides most of gdisk's functionality in a more script-friendly program. Although libparted and programs that use it (GNU Parted, gparted, etc.) provide the ability to handle GPT disks, they have certain limitations that gdisk overcomes. Specific advantages of gdisk, cgdisk, and sgdisk include: * The ability to convert MBR-partitioned disks in-place to GPT format, without losing data * The ability to convert BSD disklabels in-place to create GPT partitions, without losing data * The ability to convert from GPT format to MBR format without data loss (gdisk and sgdisk only) * More flexible specification of filesystem type code GUIDs, which GNU Parted tends to corrupt * Clear identification of the number of unallocated sectors on a disk * A user interface that's familiar to long-time users of Linux fdisk and cfdisk (gdisk and cgdisk only) * The MBR boot loader code is left alone * The ability to create a hybrid MBR, which permits GPT-unaware OSes to access up to three GPT partitions on the disk (gdisk and sgdisk only) Of course, GPT fdisk isn't without its limitations. Most notably, it lacks the filesystem awareness and filesystem-related features of GParted. You can't resize a partition's filesystem or create a partition with a filesystem already in place with gdisk, for instance. There's no GUI version of gdisk. The GPT fdisk package provides three program files: the interactive text-mode gdisk, the curses-based interactive cgdisk, and the command-line-driven sgdisk. The first two are intended for use in manually partitioning disks or changing partitioning details; sgdisk is intended for use in scripts to help automate tasks such as disk cloning or preparing multiple disks for Linux installation. FixParts Details ---------------- This program's creation was motivated by cries for help I've seen in online forums from users who have found their partition tables to be corrupted by various buggy partitioning tools. Although most OSes can handle the afflicted disks fine, libparted-based tools (GParted, parted, most Linux installers, etc.) tend to flake out when presented with these disks. Typically, the symptom is a disk that appears to hold no partitions; however, sometimes the libparted tool presents partitions other than those that the OS sees. I've observed four causes of these symptoms, three of which FixParts can correct: * Old GPT data -- If a disk is used as a GPT disk and then re-used as an MBR disk, the GPT data may be incompletely erased. This happens if the disk is repartitioned with fdisk or the Microsoft Windows installer, for instance. (Tools based on libparted correctly remove the old GPT data when converting from GPT to MBR format.) FixParts checks for this problem when it starts and offers to correct it. If you opt to erase the GPT data, this erasure occurs immediately, unlike other changes the program makes. * Mis-sized extended partitions -- Some tools create an extended partition that's too large, typically ending after the last sector of the disk. FixParts automatically corrects this problem (if you use the 'w' option to save the partition table). * Primary partitions inside an extended partition -- Some utilities create or move primary partitions to within the range covered by the extended partition. FixParts can usually correct this problem by turning the primary partition into a logical partition or by changing one or more other logical partitions into primaries. Such corrections aren't always possible, though, at least not without deleting or resizing other partitions. * Leftover RAID data -- If a disk is used in a RAID array and then re-used as a non-RAID disk, some utilities can become confused and fail to see the disk. FixParts can NOT correct this problem. You must destroy the old RAID data, or possibly remove the dmraid package from the system, to fix this problem. When run, FixParts presents an fdisk-like interface, enabling you to adjust partition types (primary, logical, or omitted), change type codes, change the bootable flag, and so on. Although you can delete a partition (by omitting it), you can't create new partitions with the program. If you're used to partitioning disks, particularly with Linux fdisk, two unusual features of FixParts require elaboration: * No extended partitions -- Internally, FixParts reads the partition table and discards data on any extended partition(s) it finds. When you save the partition table, the program generates a new extended partition. This design means that the program automatically corrects many problems related to the extended partition. It also means that you'll see no evidence of extended partitions in the FixParts user interface, although it keeps track of the requirements and prevents you from creating illegal layouts, such as a primary between two logicals. * Partition numbering -- In most Linux tools, partitions 1-4 are primaries and partitions 5 and up are logicals. Although a legal partition table loaded into FixParts will initially conform to this convention, some types of damaged table might not, and various changes you make can also cause deviations. When FixParts writes the partition table, its numbering will be altered to conform to the standard MBR conventions, but you should use the explicit labeling of partitions as primary or logical rather than the partition numbers to determine a partition's status. Installing ---------- To compile GPT fdisk, you must have appropriate development tools installed, most notably the GNU Compiler Collection (GCC) and its g++ compiler for C++. I've also tested compilation with Clang, which seems to work; however, I've not done extensive testing of the resulting binaries, beyond checking a few basics. Under Windows, Microsoft Visual C++ 2008 can be used instead. In addition, note these requirements: * On Linux, FreeBSD, OS X, and Solaris, libuuid must be installed. This is the standard for Linux and OS X, although you may need to install a package called uuid-dev or something similar to get the headers. On FreeBSD, the e2fsprogs-libuuid port must be installed. * The ICU library (http://site.icu-project.org), which provides support for Unicode partition names, is optional on all platforms except Windows, on which it's not supported. Using this library was required to get proper UTF-16 partition name support in GPT fdisk versions prior to 0.8.9, but as of that version it should not longer be required. Nonetheless, you can use it if you're having problems with the new UTF-16 support. This library is normally installed in Linux and OS X, but you may need to install the development headers (libicu-dev or something similar in Linux; or the libicu36-dev Fink package in OS X). To compile with ICU support, you must modify the Makefile: Look for commented-out lines that refer to USE_UTF16, -licuuc, -licudata, or -licucore. Uncomment them and comment out the equivalents that lack these lines. * The cgdisk program requires the ncurses library and its development files (headers). Most Linux distributions install ncurses by default, but you may need to install a package called libncurses5-dev, ncurses-devel, or something similar to obtain the header files. These files were installed already on my Mac OS X development system; however, they may have been installed as dependencies of other programs I've installed. If you're having problems installing ncurses, you can compile gdisk and/or sgdisk without cgdisk by specifying only the targets you want to compile to make. * The sgdisk program requires the popt library and its development files (headers). Most Linux distributions install popt by default, but you may need to install a package called popt-dev, popt-devel, or something similar to obtain the header files. Mac OS users can find a version of popt for Mac OS from Darwin Ports (http://popt.darwinports.com), MacPorts (https://trac.macports.org/browser/trunk/dports/devel/popt/Portfile), Fink (http://www.finkproject.org), or brew (http://macappstore.org/popt/); however, you'll first need to install the relevant environment (instructions exist on the relevant projects' pages). When I re-built my Mac build environment in February of 2020, I found that brew was, by far, the easiest of these to install. Some of the others seem to have been abandoned, but I didn't investigate thoroughly. I'm leaving the references in case they might be useful in the future. Instead of installing one of These ports, you can compile gdisk and/or cgdisk alone, without sgdisk; gdisk and cgdisk don't require popt. When all the necessary development tools and libraries are installed, you can uncompress the package and type "make" at the command prompt in the resulting directory. (You may need to type "make -f Makefile.mac" on Mac OS X, "make -f Makefile.freebsd" on FreeBSD, "make -f Makefile.solaris" on Solaris, or "make -f Makefile.mingw" to compile using MinGW for Windows.) You may also need to add header (include) directories or library directories by setting the CXXFLAGS environment variable or by editing the Makefile. The result should be program files called gdisk, cgdisk, sgdisk, and fixparts. Typing "make gdisk", "make cgdisk", "make sgdisk", or "make fixparts" will compile only the requested programs. You can use these programs in place or copy the files to a suitable directory, such as /usr/local/sbin. You can copy the man pages (gdisk.8, cgdisk.8, sgdisk.8, and fixparts.8) to /usr/local/man/man8 to make them available. Caveats ------- THIS SOFTWARE IS BETA SOFTWARE! IF IT WIPES OUT YOUR HARD DISK OR EATS YOUR CAT, DON'T BLAME ME! To date, I've tested the software on several USB flash drives, physical hard disks, and virtual disks in the QEMU and VirtualBox environments. Many others have now used the software on their computers, as well. I believe all data-corruption bugs to be squashed, but I know full well that the odds of my missing something are high. This is particularly true for large (over-2TiB) drives; my only direct testing with such disks is with virtual QEMU and VirtualBox disks. I've received user reports of success with RAID arrays over 2TiB in size, though. My main development platform is a system running the 64-bit version of Ubuntu Linux. I've also tested on several other 32- and 64-bit Linux distributions, Intel-based Mac OS X 10.6 and several later versions, 64-bit FreeBSD 7.1, and Windows 7 and 10. Redistribution -------------- This program is licensed under terms of the GNU GPL (see the file COPYING). Acknowledgements ---------------- This code is mostly my own; however, I've used three functions from two other GPLed programs: - The code used to generate CRCs is taken from the efone program by Krzysztof Dabrowski and ElysiuM deeZine. (See the crc32.h and crc32.cc source code files.) - A function to find the disk size is taken from Linux fdisk by A. V. Le Blanc. This code has subsequently been heavily modified. Additional code contributors include: - Yves Blusseau (1otnwmz02@sneakemail.com) - David Hubbard (david.c.hubbard@gmail.com) - Justin Maggard (justin.maggard@netgear.com) - Dwight Schauer (dschauer@ti.com) - Florian Zumbiehl (florz@florz.de) - Guillaume Delacour (contributed the gdisk_test.sh script) gptfdisk-1.0.5/fixparts.cc0000664000175000017500000000715013622612343015752 0ustar rodsmithrodsmith// fixparts // Program to fix certain types of damaged Master Boot Record (MBR) partition // tables // // Copyright 2011 by Roderick W. Smith // // This program is distributed under the terms of the GNU GPL, as described // in the COPYING file. // // Based on C++ classes originally created for GPT fdisk (gdisk and sgdisk) // programs #include #include #include #include #include #include "basicmbr.h" #include "support.h" using namespace std; void DoMBR(BasicMBRData & mbrTable); int main(int argc, char* argv[]) { BasicMBRData mbrTable; string device; cout << "FixParts " << GPTFDISK_VERSION << "\n"; switch (argc) { case 1: cout << "Type device filename, or press to exit: "; device = ReadString(); if (device.length() == 0) exit(0); break; case 2: device = argv[1]; break; default: cerr << "Usage: " << argv[0] << " device_filename\n"; exit(1); } // switch cout << "\nLoading MBR data from " << device << "\n"; if (!mbrTable.ReadMBRData(device)) { cerr << "\nUnable to read MBR data from '" << device << "'! Exiting!\n\n"; exit(1); } // if // This switch() statement weeds out disks with GPT signatures and non-MBR // disks so we don't accidentally damage them.... switch(mbrTable.GetValidity()) { case hybrid: case gpt: cerr << "\nThis disk appears to be a GPT disk. Use GNU Parted or GPT fdisk on it!\n"; cerr << "Exiting!\n\n"; exit(1); break; case invalid: cerr << "\nCannot find valid MBR data on '" << device << "'! Exiting!\n\n"; exit(1); break; case mbr: DoMBR(mbrTable); break; default: cerr << "\nCannot determine the validity of the disk on '" << device << "'! Exiting!\n\n"; exit(1); break; } // switch() return 0; } // main() // Do the bulk of the processing on actual MBR disks. First checks for old // GPT data (note this is different from the earlier check; this one only // looks for the GPT signatures in the main and backup GPT area, not for // a protective partition in the MBR, which we know is NOT present, since // if it were, this function would NOT be called!) and offers to destroy // it, if found; then makes sure the partitions are in a consistent and // legal state; then presents the MBR menu and, if it returns a "1" value // (meaning the user opted to write changes), writes the table to disk. void DoMBR(BasicMBRData & mbrTable) { int doItAgain; if (mbrTable.CheckForGPT() > 0) { cout << "\nNOTICE: GPT signatures detected on the disk, but no 0xEE protective " << "partition!\nThe GPT signatures are probably left over from a previous " << "partition table.\nDo you want to delete them (if you answer 'Y', this " << "will happen\nimmediately)? "; if (GetYN() == 'Y') { cout << "Erasing GPT data!\n"; if (mbrTable.BlankGPTData() != 1) cerr << "GPT signature erasure failed!\n"; } // if } // if mbrTable.MakeItLegal(); do { doItAgain = 0; if (mbrTable.DoMenu() > 0) { cout << "\nFinal checks complete. About to write MBR data. THIS WILL OVERWRITE " << "EXISTING\nPARTITIONS!!\n\nDo you want to proceed? "; if (GetYN() == 'Y') { mbrTable.WriteMBRData(); mbrTable.DiskSync(); doItAgain = 0; } else { doItAgain = 1; } // else } // if } while (doItAgain); } // DoMBR() gptfdisk-1.0.5/mbrpart.cc0000664000175000017500000002637113622612343015567 0ustar rodsmithrodsmith/* MBRPart class, part of GPT fdisk program family. Copyright (C) 2011 Roderick W. Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #include #include #include #include "support.h" #include "mbrpart.h" using namespace std; uint32_t MBRPart::numHeads = MAX_HEADS; uint32_t MBRPart::numSecspTrack = MAX_SECSPERTRACK; uint64_t MBRPart::diskSize = 0; uint32_t MBRPart::blockSize = 512; int MBRPart::numInstances = 0; MBRPart::MBRPart() { int i; status = 0; for (i = 0; i < 3; i++) { firstSector[i] = 0; lastSector[i] = 0; } // for partitionType = 0x00; firstLBA = 0; lengthLBA = 0; includeAs = NONE; canBePrimary = 0; canBeLogical = 0; if (numInstances == 0) { numHeads = MAX_HEADS; numSecspTrack = MAX_SECSPERTRACK; diskSize = 0; blockSize = 512; } // if numInstances++; } MBRPart::MBRPart(const MBRPart& orig) { numInstances++; operator=(orig); } MBRPart::~MBRPart() { numInstances--; } MBRPart& MBRPart::operator=(const MBRPart& orig) { int i; status = orig.status; for (i = 0; i < 3; i++) { firstSector[i] = orig.firstSector[i]; lastSector[i] = orig.lastSector[i]; } // for partitionType = orig.partitionType; firstLBA = orig.firstLBA; lengthLBA = orig.lengthLBA; includeAs = orig.includeAs; canBePrimary = orig.canBePrimary; canBeLogical = orig.canBeLogical; return *this; } // MBRPart::operator=(const MBRPart& orig) // Set partition data from packed MBRRecord structure. MBRPart& MBRPart::operator=(const struct MBRRecord& orig) { int i; status = orig.status; for (i = 0; i < 3; i++) { firstSector[i] = orig.firstSector[i]; lastSector[i] = orig.lastSector[i]; } // for partitionType = orig.partitionType; firstLBA = orig.firstLBA; lengthLBA = orig.lengthLBA; if (lengthLBA > 0) includeAs = PRIMARY; else includeAs = NONE; return *this; } // MBRPart::operator=(const struct MBRRecord& orig) // Compare the values, and return a bool result. // Because this is intended for sorting and a lengthLBA value of 0 denotes // a partition that's not in use and so that should be sorted upwards, // we return the opposite of the usual arithmetic result when either // lengthLBA value is 0. bool MBRPart::operator<(const MBRPart &other) const { if (lengthLBA && other.lengthLBA) return (firstLBA < other.firstLBA); else return (other.firstLBA < firstLBA); } // operator<() /************************************************** * * * Set information on partitions or disks without * * interacting with the user.... * * * **************************************************/ void MBRPart::SetGeometry(uint32_t heads, uint32_t sectors, uint64_t ds, uint32_t bs) { numHeads = heads; numSecspTrack = sectors; diskSize = ds; blockSize = bs; } // MBRPart::SetGeometry // Empty the partition (zero out all values). void MBRPart::Empty(void) { status = UINT8_C(0); firstSector[0] = UINT8_C(0); firstSector[1] = UINT8_C(0); firstSector[2] = UINT8_C(0); partitionType = UINT8_C(0); lastSector[0] = UINT8_C(0); lastSector[1] = UINT8_C(0); lastSector[2] = UINT8_C(0); firstLBA = UINT32_C(0); lengthLBA = UINT32_C(0); includeAs = NONE; } // MBRPart::Empty() // Sets the type code, but silently refuses to change it to an extended type // code. // Returns 1 on success, 0 on failure (extended type code) int MBRPart::SetType(uint8_t typeCode, int isExtended) { int allOK = 0; if ((isExtended == 1) || ((typeCode != 0x05) && (typeCode != 0x0f) && (typeCode != 0x85))) { partitionType = typeCode; allOK = 1; } // if return allOK; } // MBRPart::SetType() void MBRPart::SetStartLBA(uint64_t start) { if (start > UINT32_MAX) cerr << "Partition start out of range! Continuing, but problems now likely!\n"; firstLBA = (uint32_t) start; RecomputeCHS(); } // MBRPart::SetStartLBA() void MBRPart::SetLengthLBA(uint64_t length) { if (length > UINT32_MAX) cerr << "Partition length out of range! Continuing, but problems now likely!\n"; lengthLBA = (uint32_t) length; RecomputeCHS(); } // MBRPart::SetLengthLBA() // Set the start point and length of the partition. This function takes LBA // values, sets them directly, and sets the CHS values based on the LBA // values and the current geometry settings. void MBRPart::SetLocation(uint64_t start, uint64_t length) { int validCHS; if ((start > UINT32_MAX) || (length > UINT32_MAX)) { cerr << "Partition values out of range in MBRPart::SetLocation()!\n" << "Continuing, but strange problems are now likely!\n"; } // if firstLBA = (uint32_t) start; lengthLBA = (uint32_t) length; validCHS = RecomputeCHS(); // If this is a complete 0xEE protective MBR partition, max out its // CHS last sector value, as per the GPT spec. (Set to 0xffffff, // although the maximum legal MBR value is 0xfeffff, which is // actually what GNU Parted and Apple's Disk Utility use, in // violation of the GPT spec.) if ((partitionType == 0xEE) && (!validCHS) && (firstLBA == 1) && ((lengthLBA == diskSize - 1) || (lengthLBA == UINT32_MAX))) { lastSector[0] = lastSector[1] = lastSector[2] = 0xFF; } // if } // MBRPart::SetLocation() // Store the MBR data in the packed structure used for disk I/O... void MBRPart::StoreInStruct(MBRRecord* theStruct) { int i; theStruct->firstLBA = firstLBA; theStruct->lengthLBA = lengthLBA; theStruct->partitionType = partitionType; theStruct->status = status; for (i = 0; i < 3; i++) { theStruct->firstSector[i] = firstSector[i]; theStruct->lastSector[i] = lastSector[i]; } // for } // MBRPart::StoreInStruct() /********************************************** * * * Get information on partitions or disks.... * * * **********************************************/ // Returns the last LBA value. Note that this can theoretically be a 33-bit // value, so we return a 64-bit value. If lengthLBA == 0, returns 0, even if // firstLBA is non-0. uint64_t MBRPart::GetLastLBA(void) const { if (lengthLBA > 0) return (uint64_t) firstLBA + (uint64_t) lengthLBA - UINT64_C(1); else return 0; } // MBRPart::GetLastLBA() // Returns 1 if other overlaps with the current partition, 0 if they don't // overlap int MBRPart::DoTheyOverlap (const MBRPart& other) { return lengthLBA && other.lengthLBA && (firstLBA <= other.GetLastLBA()) != (GetLastLBA() < other.firstLBA); } // MBRPart::DoTheyOverlap() /************************************************* * * * Adjust information on partitions or disks.... * * * *************************************************/ // Recompute the CHS values for the start and end points. // Returns 1 if both computed values are within the range // that can be expressed by that CHS, 0 otherwise. int MBRPart::RecomputeCHS(void) { int retval = 1; if (lengthLBA > 0) { retval = LBAtoCHS(firstLBA, firstSector); retval *= LBAtoCHS(firstLBA + lengthLBA - 1, lastSector); } // if return retval; } // MBRPart::RecomputeCHS() // Converts 32-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 MBRPart::LBAtoCHS(uint32_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 / (numHeads * numSecspTrack); remainder = lba - (cylinder * numHeads * numSecspTrack); head = remainder / numSecspTrack; remainder -= head * numSecspTrack; sector = remainder; if (head < numHeads) chs[0] = (uint8_t) head; else retval = 0; if (sector < numSecspTrack) { chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64); chs[2] = (uint8_t) (cylinder & UINT32_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); } // MBRPart::LBAtoCHS() // Reverses the byte order, but only if we're on a big-endian platform. // Note that most data come in 8-bit structures, so don't need reversing; // only the LBA data needs to be reversed.... void MBRPart::ReverseByteOrder(void) { if (IsLittleEndian() == 0) { ReverseBytes(&firstLBA, 4); ReverseBytes(&lengthLBA, 4); } // if } // MBRPart::ReverseByteOrder() /************************** * * * User I/O functions.... * * * **************************/ // Show MBR data. Should update canBeLogical flags before calling. // If isGpt == 1, omits the "can be logical" and "can be primary" columns. void MBRPart::ShowData(int isGpt) { char bootCode = ' '; if (status & 0x80) // it's bootable bootCode = '*'; cout.fill(' '); cout << bootCode << " "; cout.width(13); cout << firstLBA; cout.width(13); cout << GetLastLBA() << " "; switch (includeAs) { case PRIMARY: cout << "primary"; break; case LOGICAL: cout << "logical"; break; case NONE: cout << "omitted"; break; default: cout << "error "; break; } // switch cout.width(7); if (!isGpt) { if (canBeLogical) cout << " Y "; else cout << " "; if (canBePrimary) cout << " Y "; else cout << " "; } // if cout << "0x"; cout.width(2); cout.fill('0'); cout << hex << (int) partitionType << dec << "\n"; } // MBRPart::ShowData() gptfdisk-1.0.5/gptcurses.cc0000664000175000017500000006464313622612343016143 0ustar rodsmithrodsmith/* * Implementation of GPTData class derivative with curses-based text-mode * interaction * Copyright (C) 2011-2018 Roderick W. Smith * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #ifdef __APPLE__ #include #else #include #endif #include "gptcurses.h" #include "support.h" using namespace std; // # of lines to reserve for general information and headers (RESERVED_TOP) // and for options and messages (RESERVED_BOTTOM) #define RESERVED_TOP 7 #define RESERVED_BOTTOM 5 int GPTDataCurses::numInstances = 0; GPTDataCurses::GPTDataCurses(void) { if (numInstances > 0) { refresh(); } else { setlocale( LC_ALL , "" ); initscr(); cbreak(); noecho(); intrflush(stdscr, false); keypad(stdscr, true); nonl(); numInstances++; } // if/else firstSpace = NULL; lastSpace = NULL; currentSpace = NULL; currentSpaceNum = -1; whichOptions = ""; // current set of options currentKey = 'b'; // currently selected option displayType = USE_CURSES; } // GPTDataCurses constructor GPTDataCurses::~GPTDataCurses(void) { numInstances--; if ((numInstances == 0) && !isendwin()) endwin(); } // GPTDataCurses destructor /************************************************ * * * Functions relating to Spaces data structures * * * ************************************************/ void GPTDataCurses::EmptySpaces(void) { Space *trash; while (firstSpace != NULL) { trash = firstSpace; firstSpace = firstSpace->nextSpace; delete trash; } // if numSpaces = 0; lastSpace = NULL; } // GPTDataCurses::EmptySpaces() // Create Spaces from partitions. Does NOT creates Spaces to represent // unpartitioned space on the disk. // Returns the number of Spaces created. int GPTDataCurses::MakeSpacesFromParts(void) { uint i; Space *tempSpace; EmptySpaces(); for (i = 0; i < numParts; i++) { if (partitions[i].IsUsed()) { tempSpace = new Space; tempSpace->firstLBA = partitions[i].GetFirstLBA(); tempSpace->lastLBA = partitions[i].GetLastLBA(); tempSpace->origPart = &partitions[i]; tempSpace->partNum = (int) i; LinkToEnd(tempSpace); } // if } // for return numSpaces; } // GPTDataCurses::MakeSpacesFromParts() // Add a single empty Space to the current Spaces linked list and sort the result.... void GPTDataCurses::AddEmptySpace(uint64_t firstLBA, uint64_t lastLBA) { Space *tempSpace; tempSpace = new Space; tempSpace->firstLBA = firstLBA; tempSpace->lastLBA = lastLBA; tempSpace->origPart = &emptySpace; tempSpace->partNum = -1; LinkToEnd(tempSpace); SortSpaces(); } // GPTDataCurses::AddEmptySpace(); // Add Spaces to represent the unallocated parts of the partition table. // Returns the number of Spaces added. int GPTDataCurses::AddEmptySpaces(void) { int numAdded = 0; Space *current; SortSpaces(); if (firstSpace == NULL) { AddEmptySpace(GetFirstUsableLBA(), GetLastUsableLBA()); numAdded++; } else { current = firstSpace; while ((current != NULL) /* && (current->partNum != -1) */ ) { if ((current == firstSpace) && (current->firstLBA > GetFirstUsableLBA())) { AddEmptySpace(GetFirstUsableLBA(), current->firstLBA - 1); numAdded++; } // if if ((current == lastSpace) && (current->lastLBA < GetLastUsableLBA())) { AddEmptySpace(current->lastLBA + 1, GetLastUsableLBA()); numAdded++; } // if if ((current->prevSpace != NULL) && (current->prevSpace->lastLBA < (current->firstLBA - 1))) { AddEmptySpace(current->prevSpace->lastLBA + 1, current->firstLBA - 1); numAdded++; } // if current = current->nextSpace; } // while } // if/else return numAdded; } // GPTDataCurses::AddEmptySpaces() // Remove the specified Space from the linked list and set its previous and // next pointers to NULL. void GPTDataCurses::UnlinkSpace(Space *theSpace) { if (theSpace != NULL) { if (theSpace->prevSpace != NULL) theSpace->prevSpace->nextSpace = theSpace->nextSpace; if (theSpace->nextSpace != NULL) theSpace->nextSpace->prevSpace = theSpace->prevSpace; if (theSpace == firstSpace) firstSpace = theSpace->nextSpace; if (theSpace == lastSpace) lastSpace = theSpace->prevSpace; theSpace->nextSpace = NULL; theSpace->prevSpace = NULL; numSpaces--; } // if } // GPTDataCurses::UnlinkSpace // Link theSpace to the end of the current linked list. void GPTDataCurses::LinkToEnd(Space *theSpace) { if (lastSpace == NULL) { firstSpace = lastSpace = theSpace; theSpace->nextSpace = NULL; theSpace->prevSpace = NULL; } else { theSpace->prevSpace = lastSpace; theSpace->nextSpace = NULL; lastSpace->nextSpace = theSpace; lastSpace = theSpace; } // if/else numSpaces++; } // GPTDataCurses::LinkToEnd() // Sort spaces into ascending order by on-disk position. void GPTDataCurses::SortSpaces(void) { Space *oldFirst, *oldLast, *earliest = NULL, *current = NULL; oldFirst = firstSpace; oldLast = lastSpace; firstSpace = lastSpace = NULL; while (oldFirst != NULL) { current = earliest = oldFirst; while (current != NULL) { if (current->firstLBA < earliest->firstLBA) earliest = current; current = current->nextSpace; } // while if (oldFirst == earliest) oldFirst = earliest->nextSpace; if (oldLast == earliest) oldLast = earliest->prevSpace; UnlinkSpace(earliest); LinkToEnd(earliest); } // while } // GPTDataCurses::SortSpaces() // Identify the spaces on the disk, a "space" being defined as a partition // or an empty gap between, before, or after partitions. The spaces are // presented to users in the main menu display. void GPTDataCurses::IdentifySpaces(void) { MakeSpacesFromParts(); AddEmptySpaces(); } // GPTDataCurses::IdentifySpaces() /************************** * * * Data display functions * * * **************************/ // Display a single Space on line # lineNum. // Returns a pointer to the space being displayed Space* GPTDataCurses::ShowSpace(int spaceNum, int lineNum) { Space *space; int i = 0; #ifdef USE_UTF16 char temp[40]; #endif space = firstSpace; while ((space != NULL) && (i < spaceNum)) { space = space->nextSpace; i++; } // while if ((space != NULL) && (lineNum < (LINES - 5))) { ClearLine(lineNum); if (space->partNum == -1) { // space is empty move(lineNum, 12); printw(BytesToIeee((space->lastLBA - space->firstLBA + 1), blockSize).c_str()); move(lineNum, 24); printw("free space"); } else { // space holds a partition move(lineNum, 3); printw("%d", space->partNum + 1); move(lineNum, 12); printw(BytesToIeee((space->lastLBA - space->firstLBA + 1), blockSize).c_str()); move(lineNum, 24); printw(space->origPart->GetTypeName().c_str()); move(lineNum, 50); #ifdef USE_UTF16 space->origPart->GetDescription().extract(0, 39, temp, 39); printw(temp); #else printw(space->origPart->GetDescription().c_str()); #endif } // if/else } // if return space; } // GPTDataCurses::ShowSpace // Display the partitions, being sure that the space #selected is displayed // and highlighting that space. // Returns the number of the space being shown (should be selected, but will // be -1 if something weird happens) int GPTDataCurses::DisplayParts(int selected) { int lineNum = 5, i = 0, retval = -1, numToShow, pageNum; string theLine; move(lineNum++, 0); theLine = "Part. # Size Partition Type Partition Name"; printw(theLine.c_str()); move(lineNum++, 0); theLine = "----------------------------------------------------------------"; printw(theLine.c_str()); numToShow = LINES - RESERVED_TOP - RESERVED_BOTTOM; pageNum = selected / numToShow; for (i = pageNum * numToShow; i <= (pageNum + 1) * numToShow - 1; i++) { if (i < numSpaces) { // real space; show it if (i == selected) { currentSpaceNum = i; if (displayType == USE_CURSES) { attron(A_REVERSE); currentSpace = ShowSpace(i, lineNum++); attroff(A_REVERSE); } else { currentSpace = ShowSpace(i, lineNum); move(lineNum++, 0); printw(">"); } DisplayOptions(i); retval = selected; } else { ShowSpace(i, lineNum++); } } else { // blank in display ClearLine(lineNum++); } // if/else } // for refresh(); return retval; } // GPTDataCurses::DisplayParts() /********************************************** * * * Functions corresponding to main menu items * * * **********************************************/ // Delete the specified partition and re-detect partitions and spaces.... void GPTDataCurses::DeletePartition(int partNum) { if (!GPTData::DeletePartition(partNum)) Report("Could not delete partition!"); IdentifySpaces(); if (currentSpaceNum >= numSpaces) { currentSpaceNum = numSpaces - 1; currentSpace = lastSpace; } // if } // GPTDataCurses::DeletePartition() // Displays information on the specified partition void GPTDataCurses::ShowInfo(int partNum) { uint64_t size; #ifdef USE_UTF16 char temp[NAME_SIZE + 1]; #endif clear(); move(2, (COLS - 29) / 2); printw("Information for partition #%d\n\n", partNum + 1); printw("Partition GUID code: %s (%s)\n", partitions[partNum].GetType().AsString().c_str(), partitions[partNum].GetTypeName().c_str()); printw("Partition unique GUID: %s\n", partitions[partNum].GetUniqueGUID().AsString().c_str()); printw("First sector: %lld (at %s)\n", partitions[partNum].GetFirstLBA(), BytesToIeee(partitions[partNum].GetFirstLBA(), blockSize).c_str()); printw("Last sector: %lld (at %s)\n", partitions[partNum].GetLastLBA(), BytesToIeee(partitions[partNum].GetLastLBA(), blockSize).c_str()); size = partitions[partNum].GetLastLBA() - partitions[partNum].GetFirstLBA() + 1; printw("Partition size: %lld sectors (%s)\n", size, BytesToIeee(size, blockSize).c_str()); printw("Attribute flags: %016x\n", partitions[partNum].GetAttributes().GetAttributes()); #ifdef USE_UTF16 partitions[partNum].GetDescription().extract(0, NAME_SIZE , temp, NAME_SIZE ); printw("Partition name: '%s'\n", temp); #else printw("Partition name: '%s'\n", partitions[partNum].GetDescription().c_str()); #endif PromptToContinue(); } // GPTDataCurses::ShowInfo() // Prompt for and change a partition's name.... void GPTDataCurses::ChangeName(int partNum) { char temp[NAME_SIZE + 1]; if (ValidPartNum(partNum)) { move(LINES - 4, 0); clrtobot(); move(LINES - 4, 0); #ifdef USE_UTF16 partitions[partNum].GetDescription().extract(0, NAME_SIZE , temp, NAME_SIZE ); printw("Current partition name is '%s'\n", temp); #else printw("Current partition name is '%s'\n", partitions[partNum].GetDescription().c_str()); #endif printw("Enter new partition name, or to use the current name:\n"); echo(); getnstr(temp, NAME_SIZE ); partitions[partNum].SetName((string) temp); noecho(); } // if } // GPTDataCurses::ChangeName() // Change the partition's type code.... void GPTDataCurses::ChangeType(int partNum) { char temp[80] = "L\0"; PartType tempType; echo(); do { move(LINES - 4, 0); clrtobot(); move(LINES - 4, 0); printw("Current type is %04x (%s)\n", partitions[partNum].GetType().GetHexType(), partitions[partNum].GetTypeName().c_str()); printw("Hex code or GUID (L to show codes, Enter = %04x): ", partitions[partNum].GetType().GetHexType()); getnstr(temp, 79); if ((temp[0] == 'L') || (temp[0] == 'l')) { ShowTypes(); } else { if (temp[0] == '\0') tempType = partitions[partNum].GetType().GetHexType(); tempType = temp; partitions[partNum].SetType(tempType); } // if } while ((temp[0] == 'L') || (temp[0] == 'l') || (partitions[partNum].GetType() == (GUIDData) "0x0000")); noecho(); } // GPTDataCurses::ChangeType // Sets the partition alignment value void GPTDataCurses::SetAlignment(void) { int alignment; char conversion_specifier[] = "%d"; move(LINES - 4, 0); clrtobot(); printw("Current partition alignment, in sectors, is %d.", GetAlignment()); do { move(LINES - 3, 0); printw("Type new alignment value, in sectors: "); echo(); scanw(conversion_specifier, &alignment); noecho(); } while ((alignment == 0) || (alignment > MAX_ALIGNMENT)); GPTData::SetAlignment(alignment); } // GPTDataCurses::SetAlignment() // Verify the data structures. Note that this function leaves curses mode and // relies on the underlying GPTData::Verify() function to report on problems void GPTDataCurses::Verify(void) { char junk; def_prog_mode(); endwin(); GPTData::Verify(); cout << "\nPress the key to continue: "; cin.get(junk); reset_prog_mode(); refresh(); } // GPTDataCurses::Verify() // Create a new partition in the space pointed to by currentSpace. void GPTDataCurses::MakeNewPart(void) { uint64_t size, newFirstLBA = 0, newLastLBA = 0; int partNum; char inLine[80]; move(LINES - 4, 0); clrtobot(); while ((newFirstLBA < currentSpace->firstLBA) || (newFirstLBA > currentSpace->lastLBA)) { newFirstLBA = currentSpace->firstLBA; move(LINES - 4, 0); clrtoeol(); newFirstLBA = currentSpace->firstLBA; Align(&newFirstLBA); printw("First sector (%lld-%lld, default = %lld): ", newFirstLBA, currentSpace->lastLBA, newFirstLBA); echo(); getnstr(inLine, 79); noecho(); newFirstLBA = IeeeToInt(inLine, blockSize, currentSpace->firstLBA, currentSpace->lastLBA, newFirstLBA); Align(&newFirstLBA); } // while size = currentSpace->lastLBA - newFirstLBA + 1; while ((newLastLBA > currentSpace->lastLBA) || (newLastLBA < newFirstLBA)) { move(LINES - 3, 0); clrtoeol(); printw("Size in sectors or {KMGTP} (default = %lld): ", size); echo(); getnstr(inLine, 79); noecho(); newLastLBA = newFirstLBA + IeeeToInt(inLine, blockSize, 1, size, size) - 1; } // while partNum = FindFirstFreePart(); if (CreatePartition(partNum, newFirstLBA, newLastLBA)) { // created OK; set type code & name.... ChangeType(partNum); ChangeName(partNum); } else { Report("Error creating partition!"); } // if/else } // GPTDataCurses::MakeNewPart() // Prompt user for permission to save data and, if it's given, do so! void GPTDataCurses::SaveData(void) { string answer = ""; char inLine[80]; move(LINES - 4, 0); clrtobot(); move (LINES - 2, 14); printw("Warning!! This may destroy data on your disk!"); echo(); while ((answer != "yes") && (answer != "no")) { move (LINES - 4, 2); printw("Are you sure you want to write the partition table to disk? (yes or no): "); getnstr(inLine, 79); answer = inLine; if ((answer != "yes") && (answer != "no")) { move(LINES - 2, 0); clrtoeol(); move(LINES - 2, 14); printw("Please enter 'yes' or 'no'"); } // if } // while() noecho(); if (answer == "yes") { if (SaveGPTData(1)) { if (!myDisk.DiskSync()) Report("The kernel may be using the old partition table. Reboot to use the new\npartition table!"); } else { Report("Problem saving data! Your partition table may be damaged!"); } } } // GPTDataCurses::SaveData() // Back up the partition table, prompting user for a filename.... void GPTDataCurses::Backup(void) { char inLine[80]; ClearBottom(); move(LINES - 3, 0); printw("Enter backup filename to save: "); echo(); getnstr(inLine, 79); noecho(); SaveGPTBackup(inLine); } // GPTDataCurses::Backup() // Load a GPT backup from a file void GPTDataCurses::LoadBackup(void) { char inLine[80]; ClearBottom(); move(LINES - 3, 0); printw("Enter backup filename to load: "); echo(); getnstr(inLine, 79); noecho(); if (!LoadGPTBackup(inLine)) Report("Restoration failed!"); IdentifySpaces(); } // GPTDataCurses::LoadBackup() // Display some basic help information void GPTDataCurses::ShowHelp(void) { int i = 0; clear(); move(0, (COLS - 22) / 2); printw("Help screen for cgdisk"); move(2, 0); printw("This is cgdisk, a curses-based disk partitioning program. You can use it\n"); printw("to create, delete, and modify partitions on your hard disk.\n\n"); attron(A_BOLD); printw("Use cgdisk only on GUID Partition Table (GPT) disks!\n"); attroff(A_BOLD); printw("Use cfdisk on Master Boot Record (MBR) disks.\n\n"); printw("Command Meaning\n"); printw("------- -------\n"); while (menuMain[i].key != 0) { printw(" %c %s\n", menuMain[i].key, menuMain[i].desc.c_str()); i++; } // while() PromptToContinue(); } // GPTDataCurses::ShowHelp() /************************************ * * * User input and menuing functions * * * ************************************/ // Change the currently-selected space.... void GPTDataCurses::ChangeSpaceSelection(int delta) { if (currentSpace != NULL) { while ((delta > 0) && (currentSpace->nextSpace != NULL)) { currentSpace = currentSpace->nextSpace; delta--; currentSpaceNum++; } // while while ((delta < 0) && (currentSpace->prevSpace != NULL)) { currentSpace = currentSpace->prevSpace; delta++; currentSpaceNum--; } // while } // if // Below will hopefully never be true; bad counting error (bug), so reset to // the first Space as a failsafe.... if (DisplayParts(currentSpaceNum) != currentSpaceNum) { currentSpaceNum = 0; currentSpace = firstSpace; DisplayParts(currentSpaceNum); } // if } // GPTDataCurses // Move option selection left or right.... void GPTDataCurses::MoveSelection(int delta) { int newKeyNum; // Begin with a sanity check to ensure a valid key is selected.... if (whichOptions.find(currentKey) == string::npos) currentKey = 'n'; newKeyNum = whichOptions.find(currentKey); newKeyNum += delta; if (newKeyNum < 0) newKeyNum = whichOptions.length() - 1; newKeyNum %= whichOptions.length(); currentKey = whichOptions[newKeyNum]; DisplayOptions(currentKey); } // GPTDataCurses::MoveSelection() // Show user's options. Refers to currentSpace to determine which options to show. // Highlights the option with the key selectedKey; or a default if that's invalid. void GPTDataCurses::DisplayOptions(char selectedKey) { uint i, j = 0, firstLine, numPerLine; string optionName, optionDesc = ""; if (currentSpace != NULL) { if (currentSpace->partNum == -1) { // empty space is selected whichOptions = EMPTY_SPACE_OPTIONS; if (whichOptions.find(selectedKey) == string::npos) selectedKey = 'n'; } else { // a partition is selected whichOptions = PARTITION_OPTIONS; if (whichOptions.find(selectedKey) == string::npos) selectedKey = 't'; } // if/else firstLine = LINES - 4; numPerLine = (COLS - 8) / 12; ClearBottom(); move(firstLine, 0); for (i = 0; i < whichOptions.length(); i++) { optionName = ""; for (j = 0; menuMain[j].key; j++) { if (menuMain[j].key == whichOptions[i]) { optionName = menuMain[j].name; if (whichOptions[i] == selectedKey) optionDesc = menuMain[j].desc; } // if } // for move(firstLine + i / numPerLine, (i % numPerLine) * 12 + 4); if (whichOptions[i] == selectedKey) { attron(A_REVERSE); printw("[ %s ]", optionName.c_str()); attroff(A_REVERSE); } else { printw("[ %s ]", optionName.c_str()); } // if/else } // for move(LINES - 1, (COLS - optionDesc.length()) / 2); printw(optionDesc.c_str()); currentKey = selectedKey; } // if } // GPTDataCurses::DisplayOptions() // Accept user input and process it. Returns when the program should terminate. void GPTDataCurses::AcceptInput() { int inputKey, exitNow = 0; do { refresh(); inputKey = getch(); switch (inputKey) { case KEY_UP: ChangeSpaceSelection(-1); break; case KEY_DOWN: ChangeSpaceSelection(+1); break; case 339: // page up key ChangeSpaceSelection(RESERVED_TOP + RESERVED_BOTTOM - LINES); break; case 338: // page down key ChangeSpaceSelection(LINES - RESERVED_TOP - RESERVED_BOTTOM); break; case KEY_LEFT: MoveSelection(-1); break; case KEY_RIGHT: MoveSelection(+1); break; case KEY_ENTER: case 13: exitNow = Dispatch(currentKey); break; case 27: // escape key exitNow = 1; break; default: exitNow = Dispatch(inputKey); break; } // switch() } while (!exitNow); } // GPTDataCurses::AcceptInput() // Operation has been selected, so do it. Returns 1 if the program should // terminate on return from this program, 0 otherwise. int GPTDataCurses::Dispatch(char operation) { int exitNow = 0; switch (operation) { case 'a': case 'A': SetAlignment(); break; case 'b': case 'B': Backup(); break; case 'd': case 'D': if (ValidPartNum(currentSpace->partNum)) DeletePartition(currentSpace->partNum); break; case 'h': case 'H': ShowHelp(); break; case 'i': case 'I': if (ValidPartNum(currentSpace->partNum)) ShowInfo(currentSpace->partNum); break; case 'l': case 'L': LoadBackup(); break; case 'm': case 'M': if (ValidPartNum(currentSpace->partNum)) ChangeName(currentSpace->partNum); break; case 'n': case 'N': if (currentSpace->partNum < 0) { MakeNewPart(); IdentifySpaces(); } // if break; case 'q': case 'Q': exitNow = 1; break; case 't': case 'T': if (ValidPartNum(currentSpace->partNum)) ChangeType(currentSpace->partNum); break; case 'v': case 'V': Verify(); break; case 'w': case 'W': SaveData(); break; default: break; } // switch() DrawMenu(); return exitNow; } // GPTDataCurses::Dispatch() // Draws the main menu void GPTDataCurses::DrawMenu(void) { string title="cgdisk "; title += GPTFDISK_VERSION; string drive="Disk Drive: "; drive += device; ostringstream size; size << "Size: " << diskSize << ", " << BytesToIeee(diskSize, blockSize); clear(); move(0, (COLS - title.length()) / 2); printw(title.c_str()); move(2, (COLS - drive.length()) / 2); printw(drive.c_str()); move(3, (COLS - size.str().length()) / 2); printw(size.str().c_str()); DisplayParts(currentSpaceNum); } // DrawMenu int GPTDataCurses::MainMenu(void) { if (((LINES - RESERVED_TOP - RESERVED_BOTTOM) < 2) || (COLS < 80)) { Report("Display is too small; it must be at least 80 x 14 characters!"); } else { if (GPTData::Verify() > 0) Report("Warning! Problems found on disk! Use the Verify function to learn more.\n" "Using gdisk or some other program may be necessary to repair the problems."); IdentifySpaces(); currentSpaceNum = 0; DrawMenu(); AcceptInput(); } // if/else endwin(); return 0; } // GPTDataCurses::MainMenu /*********************************************************** * * * Non-class support functions (mostly related to ncurses) * * * ***********************************************************/ // Clears the specified line of all data.... void ClearLine(int lineNum) { move(lineNum, 0); clrtoeol(); } // ClearLine() // Clear the last few lines of the display void ClearBottom(void) { move(LINES - RESERVED_BOTTOM, 0); clrtobot(); } // ClearBottom() void PromptToContinue(void) { ClearBottom(); move(LINES - 2, (COLS - 29) / 2); printw("Press any key to continue...."); cbreak(); getch(); } // PromptToContinue() // Display one line of text on the screen and prompt to press any key to continue. void Report(string theText) { clear(); move(0, 0); printw(theText.c_str()); move(LINES - 2, (COLS - 29) / 2); printw("Press any key to continue...."); cbreak(); getch(); } // Report() // Displays all the partition type codes and then prompts to continue.... // NOTE: This function temporarily exits curses mode as a matter of // convenience. void ShowTypes(void) { PartType tempType; char junk; def_prog_mode(); endwin(); tempType.ShowAllTypes(LINES - 3); cout << "\nPress the key to continue: "; cin.get(junk); reset_prog_mode(); refresh(); } // ShowTypes() gptfdisk-1.0.5/Makefile.mingw640000664000175000017500000000244113622612343016533 0ustar rodsmithrodsmithCC=/usr/bin/x86_64-w64-mingw32-gcc CXX=/usr/bin/x86_64-w64-mingw32-g++ STRIP=/usr/bin/x86_64-w64-mingw32-strip CFLAGS=-O2 -Wall -static -static-libgcc -static-libstdc++ -D_FILE_OFFSET_BITS=64 -g CXXFLAGS=-O2 -Wall -static -static-libgcc -static-libstdc++ -D_FILE_OFFSET_BITS=64 -g #CXXFLAGS=-O2 -Wall -D_FILE_OFFSET_BITS=64 -I /usr/local/include -I/opt/local/include -g LIB_NAMES=guid gptpart bsd parttypes attributes crc32 mbrpart basicmbr mbr gpt support diskio diskio-windows MBR_LIBS=support diskio diskio-windows basicmbr mbrpart LIB_SRCS=$(NAMES:=.cc) LIB_OBJS=$(LIB_NAMES:=.o) MBR_LIB_OBJS=$(MBR_LIBS:=.o) LIB_HEADERS=$(LIB_NAMES:=.h) DEPEND= makedepend $(CFLAGS) all: gdisk fixparts gdisk: $(LIB_OBJS) gdisk.o gpttext.o $(CXX) $(CXXFLAGS) $(LIB_OBJS) gdisk.o gpttext.o -lrpcrt4 -static-libgcc -o gdisk64.exe sgdisk: $(LIB_OBJS) sgdisk.o $(CXX) $(CXXFLAGS) $(LIB_OBJS) sgdisk.o -lpopt -static-libgcc -o sgdisk64.exe fixparts: $(MBR_LIB_OBJS) fixparts.o $(CXX) $(CXXFLAGS) $(MBR_LIB_OBJS) fixparts.o $(LDFLAGS) -static-libgcc -o fixparts64.exe lint: #no pre-reqs lint $(SRCS) clean: #no pre-reqs rm -f core *.o *~ gdisk64.exe sgdisk64.exe strip: #no pre-reqs $(STRIP) gdisk64.exe fixparts64.exe # what are the source dependencies depend: $(SRCS) $(DEPEND) $(SRCS) $(OBJS): # DO NOT DELETE gptfdisk-1.0.5/diskio.h0000664000175000017500000000405613622612343015240 0ustar rodsmithrodsmith// // C++ Interface: diskio // // Description: Class to handle low-level disk I/O for GPT fdisk // // // Author: Rod Smith , (C) 2009 // // 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. #ifndef __DISKIO_H #define __DISKIO_H #include #include #include #ifdef _WIN32 #include #include #else #include #endif #ifdef __sun__ #include #endif #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__) #define fstat64 fstat #define stat64 stat #endif #include "support.h" //#include "parttypes.h" using namespace std; /*************************************** * * * DiskIO class and related structures * * * ***************************************/ class DiskIO { protected: string userFilename; string realFilename; string modelName; int isOpen; int openForWrite; #ifdef _WIN32 HANDLE fd; #else int fd; #endif public: DiskIO(void); ~DiskIO(void); void MakeRealName(void); int OpenForRead(const string & filename); int OpenForRead(void); int OpenForWrite(const string & filename); int OpenForWrite(void); void Close(); int Seek(uint64_t sector); int Read(void* buffer, int numBytes); int Write(void* buffer, int numBytes); int DiskSync(void); // resync disk caches to use new partitions int GetBlockSize(void); int GetPhysBlockSize(void); string GetModel(void) {return modelName;} uint32_t GetNumHeads(void); uint32_t GetNumSecsPerTrack(void); int IsOpen(void) {return isOpen;} int IsOpenForWrite(void) {return openForWrite;} string GetName(void) const {return realFilename;} uint64_t DiskSize(int* err); }; // class DiskIO #endif gptfdisk-1.0.5/bsd.h0000664000175000017500000000731213622612343014524 0ustar rodsmithrodsmith/* bsd.h -- BSD disklabel data structure definitions, types, and functions */ /* 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. */ #include #include #include "gptpart.h" #include "diskio.h" #ifndef __BSD_STRUCTS #define __BSD_STRUCTS #define BSD_SIGNATURE UINT32_C(0x82564557) /* BSD disklabel signature ("magic") */ // BSD disklabels can start at offsets of 64 or the sector size -- at least, // I *THINK* that's what's going on. I've seen them at 64 or 512 on disks // with 512-byte blocks and at 2048 on disks with 2048-byte blocks. The // LABEL_OFFSET2 value will be replaced by the block size in the // ReadBSDData() function.... #define LABEL_OFFSET1 64 #define LABEL_OFFSET2 512 #define NUM_OFFSETS 2 // FreeBSD documents a maximum # of partitions of 8, but I saw 16 on a NetBSD // disk. I'm quadrupling that for further safety. Note that BSDReadData() // uses a 4096-byte I/O buffer. In combination with LABEL_OFFSET3 and the // additional 148-byte offset to the actual partition data, that gives a // theoretical maximum of 118.75 partitions that the program can handle before // memory errors will occur. #define MAX_BSD_PARTS 64 using namespace std; /**************************************** * * * BSDData class and related structures * * * ****************************************/ // Possible states of the MBR enum BSDValidity {unknown, bsd_invalid, bsd}; // Data for a single BSD partition record // Create entries for all fields, although we only use lengthLBA, firstLBA, // and fsType, to simplify loading the data from disk.... struct BSDRecord { // the partition table uint32_t lengthLBA; // number of sectors in partition uint32_t firstLBA; // starting sector uint32_t fragSize; // filesystem basic fragment size uint8_t fsType; // filesystem type, see below uint8_t frag; // filesystem fragments per block uint16_t pcpg; // filesystem cylinders per group }; // Full data in tweaked BSD format // For some reason this has to be packed or MS Visual C++'s debugger complains // about memory errors whenever a BSDData variable is destroyed. #pragma pack (8) class BSDData { protected: // We only need a few items from the main BSD disklabel data structure.... uint32_t signature; // the magic number uint32_t sectorSize; // # of bytes per sector uint32_t signature2; // the magic number (again) uint16_t numParts; // number of partitions in table struct BSDRecord* partitions; // partition array // Above are basic BSD disklabel data; now add more stuff.... uint64_t labelFirstLBA; // first sector of BSD disklabel (partition or disk) uint64_t labelLastLBA; // final sector of BSD disklabel uint64_t labelStart; // BSD disklabel start point in bytes from labelFirstLBA BSDValidity state; public: BSDData(void); ~BSDData(void); int ReadBSDData(const string & deviceFilename, uint64_t startSector, uint64_t endSector); int ReadBSDData(DiskIO *myDisk, uint64_t startSector, uint64_t endSector); void ReverseMetaBytes(void); void DisplayBSDData(void); int ShowState(void); // returns 1 if BSD disklabel detected int IsDisklabel(void); // Functions to extract data on specific partitions.... uint8_t GetType(int i); uint64_t GetFirstSector(int i); uint64_t GetLength(int i); int GetNumParts(void); GPTPart AsGPT(int i); // Return BSD part. as GPT part. }; // struct MBRData #pragma pack () #endif gptfdisk-1.0.5/attributes.cc0000664000175000017500000001657113622612343016307 0ustar rodsmithrodsmith// attributes.cc // Class to manage partition attribute codes. These are binary bit fields, // of which only four are currently (2/2011) documented on Wikipedia, and // two others found from other sources. /* This program is copyright (c) 2009-2013 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 #include #include #include #include #include "attributes.h" #include "support.h" using namespace std; string Attributes::atNames[NUM_ATR]; int Attributes::numAttrs = 0; //Attributes::staticInit Attributes::staticInitializer; // Default constructor Attributes::Attributes(void) { numAttrs++; if (numAttrs == 1) Setup(); attributes = 0; } // constructor // Alternate constructor Attributes::Attributes(const uint64_t a) { numAttrs++; if (numAttrs == 1) Setup(); attributes = a; } // alternate constructor // Destructor. Attributes::~Attributes(void) { numAttrs--; } // Attributes destructor void Attributes::Setup(void) { ostringstream temp; // Most bits are undefined, so start by giving them an // appropriate name for (int i = 0; i < NUM_ATR; i++) { temp.str(""); temp << "Undefined bit #" << i; Attributes::atNames[i] = temp.str(); } // for // Now reset those names that are defined.... atNames[0] = "system partition"; // required for computer to operate atNames[1] = "hide from EFI"; atNames[2] = "legacy BIOS bootable"; atNames[60] = "read-only"; atNames[62] = "hidden"; atNames[63] = "do not automount"; } // Attributes::Setup() // Display current attributes to user void Attributes::DisplayAttributes(void) { uint32_t i; int numSet = 0; cout << "Attribute value is "; cout.setf(ios::uppercase); cout.fill('0'); cout.width(16); cout << hex << attributes << dec << ". Set fields are:\n"; for (i = 0; i < NUM_ATR; i++) { if ((UINT64_C(1) << i) & attributes) { cout << i << " (" << GetAttributeName(i) << ")" << "\n"; numSet++; } // if } // for cout.fill(' '); if (numSet == 0) cout << " No fields set\n"; cout << "\n"; } // Attributes::DisplayAttributes() // Display attributes for a partition. Note that partNum is just passed for // immediate display; it's not used to access a particular partition. void Attributes::ShowAttributes(const uint32_t partNum) { uint32_t bitNum; bool bitset; for (bitNum = 0; bitNum < 64; bitNum++) { bitset = (UINT64_C(1) << bitNum) & attributes; if (bitset) { cout << partNum+1 << ":" << bitNum << ":" << bitset << " (" << GetAttributeName(bitNum) << ")" << endl; } // if } // for } // Attributes::ShowAttributes // Prompt user for attribute changes void Attributes::ChangeAttributes(void) { int response; uint64_t bitValue; cout << "Known attributes are:\n"; ListAttributes(); cout << "\n"; do { DisplayAttributes(); response = GetNumber(0, NUM_ATR, 64, "Toggle which attribute field (0-63, 64 or to exit): "); if (response != 64) { bitValue = UINT64_C(1) << response; // Find the integer value of the bit if (bitValue & attributes) { // bit is set attributes &= ~bitValue; // so unset it cout << "Have disabled the '" << atNames[response] << "' attribute.\n"; } else { // bit is not set attributes |= bitValue; // so set it cout << "Have enabled the '" << atNames[response] << "' attribute.\n"; } // if/else } // if } while (response != 64); } // Attributes::ChangeAttributes() // Display all defined attributes on the screen (omits undefined bits). void Attributes::ListAttributes(void) { uint32_t bitNum; string tempAttr; for (bitNum = 0; bitNum < NUM_ATR; bitNum++) { tempAttr = GetAttributeName(bitNum); if (tempAttr.substr(0, 15) != "Undefined bit #" ) cout << bitNum << ": " << Attributes::GetAttributeName(bitNum) << "\n"; } // for } // Attributes::ListAttributes // multifaceted attributes access // returns true upon success, false upon failure bool Attributes::OperateOnAttributes(const uint32_t partNum, const string& attributeOperator, const string& attributeBits) { // attribute access opcode typedef enum { ao_or, ao_nand, ao_xor, ao_assignall, // operate on all attributes (bitmask) ao_unknown, // must be after bitmask operators and before bitnum operators ao_set, ao_clear, ao_toggle, ao_get // operate on a single attribute (bitnum) } attribute_opcode_t; // typedef enum // translate attribute operator into an attribute opcode attribute_opcode_t attributeOpcode = ao_unknown; { // opcode is not known yet if (attributeOperator == "or") attributeOpcode = ao_or; else if (attributeOperator == "nand") attributeOpcode = ao_nand; else if (attributeOperator == "xor") attributeOpcode = ao_xor; else if (attributeOperator == "=") attributeOpcode = ao_assignall; else if (attributeOperator == "set") attributeOpcode = ao_set; else if (attributeOperator == "clear") attributeOpcode = ao_clear; else if (attributeOperator == "toggle") attributeOpcode = ao_toggle; else if (attributeOperator == "get") attributeOpcode = ao_get; else { cerr << "Unknown attributes operator: " << attributeOperator << endl; return false; } // else } // attributeOpcode // get bit mask if operating on entire attribute set uint64_t attributeBitMask; { if (attributeOpcode < ao_unknown) { if (1 != sscanf (attributeBits.c_str(), "%qx", (long long unsigned int*) &attributeBitMask)) { cerr << "Could not convert hex attribute mask" << endl; return false; } // if }} // attributeBitMask, if // get bit number and calculate bit mask if operating on a single attribute int bitNum; { if (attributeOpcode > ao_unknown) { if (1 != sscanf (attributeBits.c_str(), "%d", &bitNum)) { cerr << "Could not convert bit number" << endl; return false; } // if const uint64_t one = 1; attributeBitMask = one << bitNum; }} // bitNum, if switch (attributeOpcode) { // assign all attributes at once case ao_assignall: attributes = attributeBitMask; break; // set individual attribute(s) case ao_set: case ao_or: attributes |= attributeBitMask; break; // clear individual attribute(s) case ao_clear: case ao_nand: attributes &= ~attributeBitMask; break; // toggle individual attribute(s) case ao_toggle: case ao_xor: attributes ^= attributeBitMask; break; // display a single attribute case ao_get: { cout << partNum+1 << ":" << bitNum << ":" << bool (attributeBitMask & attributes) << endl; break; } // case ao_get default: break; // will never get here } // switch return true; } // Attributes::OperateOnAttributes() /******************************* * * * Non-class support functions * * * *******************************/ // Display attributes ostream & operator<<(ostream & os, const Attributes & data) { os << data.GetAttributes(); return os; } // operator<<() gptfdisk-1.0.5/NEWS0000664000175000017500000016434713622612343014316 0ustar rodsmithrodsmith1.0.5 (2/17/2020): ------------------ - Fixed typos and minor formatting issues in man pages - Changed number of columns in type code output ("sgdisk -L" and equivalents in gdisk and cgdisk) from 3 to 2, since some descriptions are long enough that they're ambiguous with three columns. - Makefile change: Add $(LDLIBS) support to enable clean static builds (for libintl). - You can now put the 0xEE partition last in a hybrid MBR using sgdisk. (Previously, this was possible with gdisk but not with sgdisk.) See the sgdisk man page for details. - Added numerous type codes for Container Linux, Veracrypt, and Freedesktop.org's Discoverable Partitions Specification - Apple no longer supports building i386 or "fat" binaries in XCode (or if they do, they're making it hard), so I've removed that support. GPT fdisk macOS binaries are now x86-64 only. Similarly, building now seems to require macOS 10.9 or later, so that's now the minimum macOS version. I've also re-built my Mac build environment and tweaked Makefile.mac appropriately. - Partition type name searches are now case-insensitive. - It's now possible to quit out of partition type name searches by typing "q". - When changing a partition type code, the default is now the current type code, not a platform-specific type code. 1.0.4 (7/5/2018): ----------------- - Added some explicit copy constructors and made some other tweaks to avoid compiler warnings. - The macOS binary for sgdisk is now a pure 64-bit build; I'm no longer supporting 32-bit builds of sgdisk. The gdisk and cgdisk binaries remain "fat" 32-/64-bit builds. The reason for dropping the 32-bit support from sgdisk is that I've re-built my macOS development system, and I had trouble building a "fat" binary with the fresh install of the popt libraries upon which sgdisk relies. 32-bit support for the other binaries is now officially deprecated, too. - Added search feature to partition type list functions ("L" on main menu of gdisk and "L" when entered in response to the "Hex code or GUID" prompt in gdisk and sgdisk). This feature filters the partition type list to those which include the search term in their GPT fdisk descriptions. For instance, typing "Linux" shows only partitions with "Linux" in their descriptions. Note that the search/filter is case-sensitive. If is pressed, no filter is applied. - Change to Makefile.mac to use standard libncurses rather than a 3rd-party version; should help with cgdisk compatibility. - Minor bug fix in alignment of internal data structures. - Minor bug fix in handling of damaged disks. Also, GPT fdisk now reports more information on what data structures are damaged when a damaged disk is detected. - Added type code for Apple APFS (7C3457EF-0000-11AA-AA11-00306543ECAC, 0xaf0a). - Added type code for Atari TOS basic data (0xa200, 734E5AFE-F61A-11E6-BC64-92361F002671). - Added type codes for Linux dm-crypt (0x8308, 7FFEC5C9-2D00-49B7-8941-3EA10A5586B7) and LUKS (0x8309, CA7D7CCB-63ED-4C53-861C-1742536059CC) partitions. - Added 18 Ceph partition type codes. - Added 52 (yes, 52!) Android partition type codes. - Changed "Creating new GPT entries" message to read "Creating new GPT entries in memory" because the latter is clearer, particularly when using sgdisk with a non-destructive option, like "-p". 1.0.3 (7/27/2017): ------------------ - Fixed a major bug that caused invalid partition tables to be generated when creating a new partition table (that is, partitioning a blank disk or converting from MBR). 1.0.2 (7/26/2017): ------------------ - On Linux, the p/-p/--print command now shows the disk's model name, as reported in /sys/block/sda/device/model (or equivalent filenames for other disks). This feature does not yet work on other platforms, on which the model name line is omitted from the output. This line is also not shown when accessing disk image files, even on Linux. - GPT fdisk can now report both the physical and logical sector sizes of disks, but only on 2.6.32 and later Linux kernels. The verify feature now uses the larger of the set alignment and physical/logical block sizes for testing alignment, and setting alignment to something other than an exact multiple of the ratio of the physical to logical block size results in a warning. - Addition of new verification checks, mostly (but not exclusively) related to the new j/-j/--move-main-table option. - Added new option: 'j' on the experts' menu in gdisk; '-j/--move-main-table={sector}' in sgdisk. This option enables relocating the main partition table from sector 2 (the default location) to somewhere else on the disk. The main reason I know of to do this is if the disk is to be used with a system-on-chip (SoC) computer, some of which require the boot loader to be located at sector 2. If you pass this option the default value of 2, it has the effect of reducing the padding placed between the main partition table and the first usable sector value created by the Linux fdisk tool. - Updated man pages with new recommendations for ESP and BIOS Boot Partition sizes. - Added four type codes (AF06 through AF09) for Apple SoftRAID (Status, Scratch, Volume, and Cache). - Added two type codes for the Open Network Install Environment (ONIE): 0xe100 (7412F7D5-A156-4B13-81DC-867174929325) and 0xe101 (D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149). - Added thirteen type codes for Android partitions (0xa000 through 0xa00c). - Added type code for QNX6 (aka QNX Power-Safe) filesystem: 0xb300, for CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1. - Removed stray debug message ("REALLY setting name!") from sgdisk, when setting new name via -c option. 1.0.1 (10/18/2015): ------------------- - Created uninstall-fixparts and uninstall-gdisk scripts for OS X. As the names imply, these scripts remove the files installed by the fixparts and gdisk packages, respectively. - Fixed bug that caused -N/--largest-new option to sgdisk to fail when fed a "0" option. - Fixed bug that caused input glitches in EFI version of gdisk. - Fixed bug that caused sgdisk to not return an appropriate error code when it encountered a write error when saving changes. - Fixed bug that caused cgdisk's "Info" display to under-report the partition's size by one sector. - OS X 10.11 includes new security features that prevent GPT fdisk from working unless these features are disabled. To do so, you must boot to a Recovery HD system, open a Terminal, type "csrutil disable", and reboot into the normal system. You can re-enable the security features by repeating the process, but specify "enable" rather than "disable". I've added a message pointing users to a Web page explaining how to disable this feature when gdisk detects that it can't write to the disk under OS X. If you know of a way around this (including code changes to gdisk), please contact me. - I've updated the OS X installation location from the Unix-standard /usr/sbin (and related locations for documentation) to /usr/local/bin (and related locations for documentation). This is Just Plain Crazy from a Unix point of view, but Apple has to be Apple and do things just a little bit differently. - I've updated my OS X environment to OS X 10.11 and LLVM 7.0.0. This has also meant installing fresh versions of popt and ncurses from MacPorts, which may require upgrading popt to get sgdisk working on some systems. (gdisk, cgdisk, and fixparts should continue to work normally on all systems.) The OS X binaries are now "fat" (32- and 64-bit) versions, which should have no noticeable effect unless you have a Mac with broken 32-bit support, in which case the binaries will now work. - Changed the default name of 0xab00 partitions from "Apple boot" to "Recovery HD", since the latter is the name that Apple gives these partitions. Also, I discovered through painful experience that OS X flakes out and won't boot if the name is something other than "Recovery HD", so it really has to have the right name! - Changed the OpenBSD type codes (0xa600 and 0xa601): 0xa600 is now 824CC7A0-36A8-11E3-890A-952519AD3F61 (OpenBSD disklabel) and 0xa601 is now gone. Previously, 0xa600 was 516E7CB4-6ECF-11D6-8FF8-00022D09712B, a duplicate of the FreeBSD disklabel, and 0xa601 was 824CC7A0-36A8-11E3-890A-952519AD3F61. OpenBSD is now officially supporting 824CC7A0-36A8-11E3-890A-952519AD3F61 as a disklabel type, though. It's unclear what, if anything, OpenBSD will use for non-disklabel type codes at the moment. - Added GUID 0311FC50-01CA-4725-AD77-9ADBB20ACE98 (0xbc00) for Acronis Secure Zone backup partitions. - Fixed bug that caused random crashes on ppc64el systems (and perhaps others). - Added GUID C91818F9-8025-47AF-89D2-F030D7000C2C (0x3900) for Plan 9. - Added GUID 69DAD710-2CE4-4E3C-B16C-21A1D49ABED3 (0x8307) for 32-bit ARM Linux root (/) partition, as per the Freedesktop.org Discoverable Partition Spec (http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/). - Edited man pages to clarify that default alignment is to 1MiB boundaries; this translates to 2048 sectors on disks with 512-byte sectors, but it will be something else on disks with other sector sizes. - Changed behavior of -z/--zap and -Z/--zap-all options to sgdisk so that if a subsequent command causes changes, they'll be written to disk. Previously, doing something like "sgdisk --zap-all --clear /dev/sdd" would wipe the disk but not create a partition table; to create a blank table you'd need to do "sgdisk --zap-all --clear --mbrtogpt /dev/sdd", which is a bit odd and counter-intuitive, to the point of arguably being a bug. 1.0.0 (3/16/2015): ------------------ - I'm now building a binary package of gdisk_x64.efi, using the UEFI GPT fdisk package. - Added partition type for OpenBSD data (824CC7A0-36A8-11E3-890A-952519AD3F61/0xa601). Also mapped 0xa600 to the FreeBSD disklabel type code (516E7CB4-6ECF-11D6-8FF8-00022D09712B). I'm not sure that's 100% correct, but since I can't find references to an OpenBSD disklabel GPT type code, it seems the best choice at the moment. - Added partition type for Windows Storage Spaces (E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D/0x4202) - Added -O/--print-mbr option to sgdisk, enabling easier display of MBR data structures without invoking gdisk. - Updated warning message: "EBR describes a logical partition" now reads "EBR points to an EBR," which is more technically correct. - Altered warning displayed when run from Windows on non-GPT disk, because Windows on UEFI-based systems is becoming more common. - Fixed spurious "1" return value in gdisk. - Small code changes to support compilation as EFI application with the UEFI GPT fdisk library (http://sourceforge.net/projects/uefigptfdisk/?source=directory) - Added new partition type codes for Ceph (https://github.com/ceph/ceph/blob/9bcc42a3e6b08521694b5c0228b2c6ed7b3d312e/src/ceph-disk#L76-L81): 4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D/0xf800 (Ceph OSD), 4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D/0xf801 (Ceph dm-crypt OSD), 45B0969E-9B03-4F30-B4C6-B4B80CEFF106/0xf802 (Ceph journal), 45B0969E-9B03-4F30-B4C6-5EC00CEFF106/0xf803 (Ceph dm-crypt journal), 89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE/0xf804 (Ceph disk in creation), and 89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE/0xf805 (Ceph dm-crypt disk in creation) - Added new partition type codes from http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/: 44479540-F297-41B2-9AF7-D131D5F0458A/0x8303 (Linux / on x86), 4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709/0x8304 (Linux / on x86-64), B921B045-1DF0-41C3-AF44-4C6F280D3FAE/0x8305 (Linux / on 64-bit ARM), 3B8F8425-20E0-4F3B-907F-1A25A76F98E8/0x8306 (Linux /srv). 0.8.10 (3/2/2014): ------------------ - Added feature to sgdisk's -A/--attributes, -c/--change-name, -t/--typecode, and -u/--partition-guid commands: If a -n/--new option with "0" as the partition number precedes these options on the command line, passin "0" as the partition number to the following options causes them to use the newly-created partition. For instance, "sgdisk -n 0:0:+550M -t 0:EF00 /dev/sda" creates a new partition with a type code of EF00. (Previous versions would ignore the "-t 0:EF00" option.) - Fixed bug that caused incorrect partition number to be displayed by sgdisk in error messages when the user specified a non-existent partition for inclusion in a hybrid MBR or conversion to a conventional MBR. - Fixed new (in 0.8.9) bug that caused a failure to create more than one hybridized partition when creating a hybrid MBR. - Fixed bug that caused gdisk and sgdisk to create hybridized partitions that ended at or above the 2^32 sector point with incorrect end values. The behavior now varies between gdisk and sgdisk: gdisk now creates hybrid partitions that begin below 2^32 sectors and that are smaller than 2^32 sectors, since this is technically legal; but gdisk displays a warning, because some OSes (such as DOS, Windows XP, OS/2, and BeOS) misbehave with such partitions. AFAIK, only Linux, FreeBSD, and Windows 7 work properly with such partitions. Because of this fact and because sgdisk is a more automated tool, it's stricter in how it handles things: It refuses to create a hybrid partition if the original ends at or above the 2^32 sector mark. 0.8.9 (2/17/2014): ------------------ - Removed dependency on libicu for UTF-16 support. - Fixed spurious "0xEE partition doesn't start on sector 1" warning in FixParts (and perhaps in other programs under some circumstances). - Added GPT regeneration command to GPT-destruction options ('z' in gdisk, -z and -Z options to sgdisk). This is done to avoid wiping out data mid-disk that might not be backup GPT data structures, which could otherwise occur if a RAID array was resized in certain ways. - Added check for an oversized 0xEE protective partition. The program now auto-repairs this condition on loading if the GPT data seem otherwise valid. This is done because I've been receiving reports of some disks (possibly from some OEM Windows 8 loads) that violate the GPT spec in this way, and gdisk was reporting write errors when saving data. - If the GPT data seem to be damaged in some way or if the disk seems to be a hybrid MBR and if the MBR partition(s) don't fit on the disk, the verify (v) function now warns of this condition, and writing the disk if it exists also displays a more specific error message about the problem. - Added new type codes (3000, 7412F7D5-A156-4B13-81DC-867174929325 and 3001, D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149) for Open Network Install Environment (ONIE) boot and config partitions, respectively. - Added new type ccde (ED01, BFBFAFE7-A34F-448A-9A5B-6213EB736C22), for Lenovo's ESP-like partition. 0.8.8 (10/14/2013): ------------------- - Fixed bug that could cause segfault when passing an invalid partition number to sgdisk's -i/--info command. - Added new type code: 933AC7E1-2EB4-4F13-B844-0E14E2AEF915, or gdisk code 8302, for Linux /home partitions. This type code is used by recent versions of systemd to permit /home to be auto-mounted; see http://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html for details. - Added new type code: 9E1A2D38-C612-4316-AA26-8B49521E5A8B, or gdisk code 4100, for PowerPC PReP (PowerPC reference platform) boot. - The number of partition type codes has grown large enough that it fills an 80x24 display. I've therefore added a pause (with a prompt to hit ) to display more items after showing 21 lines in gdisk or after the screen has nearly filled with entries in cgdisk. There's no such pause/prompt in sgdisk, though. - Fine-tuned verification ('v') check for 0xEE partition that doesn't begin on sector 1: Previously, a disk with multiple 0xEE partitions would always trigger this warning. Now, the warning occurs only if NONE of the 0xEE partitions begins on sector 1. - Fixed hybrid MBR creation on disks larger than 2TiB: Previously, if one opted to create an extra partition to cover unused space following hybridized partitions, gdisk would hang. - Added check for an active/bootable 0xEE protective partition to the verify ('v') function. If found, this is not counted as an error, but it is called out to the user, since it can cause some EFIs (such as VirtualBox's EFI) to ignore the disk. 0.8.7 (7/8/2013): ----------------- - Modified Mac version so that it can work on /dev/rdisk* devices as well as /dev/disk* devices. The result is that, when using the /dev/rdisk* devices, the partition table can sometimes be re-read without removing the disk or rebooting. - Added "-a" option to cgdisk to use a ">" symbol to the left of the selected partition rather than ncurses highlighting. - Modified "converting MBR to GPT" message to clarify that the conversion is being held in memory, since some people have mistakenly assumed that a "gdisk -l" operation will change an MBR disk to a GPT disk without prompting. - Added partition type code for freedesktop.org's proposed $BOOT partition (bc13c2ff-59e6-4262-a352-b275fd6f7172; GPT fdisk type code EA00) - Adjusted alignment code when using -n or -N in sgdisk to keep the requested partition size (if specified using +###{MGT} terminology) as the requested value rather than relative to the requested start point. This gives you the requested partition size rather than be slightly smaller if sgdisk needs to adjust the start point up a bit and it prevents gaps from appearing between partitions if several are created in succession using automatic placement of the start point. - Fixed small bugs in gdisk_test.sh script. - Removed stray debug message that would appear when reading MBR disks. - Added partition type code for Intel Rapid Start partition (GUID D3BFE2DE-3DAF-11DF-BA40-E3A556D89593, code 8400), used by systems that implement Intel's Rapid Start technology. See http://blog.adios.tw/2012/10/funtoo-linux-and-intel-rapid-start.html or http://mjg59.dreamwidth.org/26022.html. - Added partition type code for Haiku BFS (GUID 42465331-3BA3-10F1-802A-4861696B7521; code EB00). 0.8.6 (1/9/2013): ----------------- - Fixed a bug that could cause sgdisk to crash when passing a partition number of 0 to the -t option. - Added support for building under Solaris. - Added a new check to the verification code. - Added partition type code for Sony system partition (F4019732-066E-4E12-8273-346C5641494F). I'm not entirely clear what this is used for, but it's appearing on some new Sony computers. - Tweaked hybrid MBR creation options to fix a problem that caused the main 0xEE MBR partition to NOT be created if the user told gdisk to NOT place it at the start of the disk AND IF fewer than three partitions are hybridize AND IF the user opted to create a second protective partition. - Changed default build options for Mac OS X to *NOT* use libicu, since it seems to have broken somewhere along the line. It still works on Linux, though. - Added partition type codes for VMWare ESX (FB00, FB01, and FC00). 0.8.5 (5/30/2012): ------------------ - Changed code that writes the partition table so that a disk sync operation occurs even if one or more write operations failed (but not if they all failed). This is intended to work around a bug that a user reported on a Windows system on which the write of the protective MBR failed, although everything else worked. (I suspect anti-virus software may have been blocking write access to the MBR.) - Added type codes for Midnight BSD (0xA580 - 0xA585). I used these codes because Midnight BSD uses the same 0xA5 type code as FreeBSD on MBR disks, so I'm starting Midnight BSD's numbering halfway through the 0xA5## range. 0.8.4 (3/25/2012): ------------------ - REALLY fixed Ctrl+D problems! Now gdisk terminates upon receiving a Ctrl+D. In all previous versions, it could lock itself into a CPU-hogging loop if launched via "sudo" from a terminal window that was then closed or if Ctrl+D was pressed at certain input prompts (for a partition name or sector number, for instance). 0.8.3 (3/23/2012): ------------------ - Fixed compilation problem on GCC 4.7. - Improved handling of Ctrl+D on some systems. - Added disk's name to message stating that a disk write was successful. - Fixed bug that caused creation of >2TiB partitions on 32-bit systems to be truncated in sgdisk. 0.8.2 (1/22/2012): ------------------ - Adjusted the code to support a number of partitions that's not a multiple of the number of partition table entries that fits in a sector (normally 4 per sector). The program still rounds up, when necessary, when resizing the partition table manually, but not when loading a partition table that contains a peculiar number of partitions. This helps prevent spurious error messages about CRC problems when loading some Solaris partition tables. - Fixed bugs relating to the handling of empty partitions; Solaris's ZFS tools create weird empty partitions that are legal but that gdisk wasn't handling properly. (Specifically, they sometimes have non-zero end points; gdisk assumed empty partitions had end points of 0.) - Fixed a bug that caused an infinite loop of input prompts if the user pressed Ctrl+D. - Changed gdisk's first-sector input operation to specify a sector number that's properly aligned as the default value. This eliminates the need to alter that value and notify the user of the change when the user hits "Enter" for the default value as the first partition on an empty disk (as well as in some other situations). 0.8.1 (10/1/2011): ------------------ - Fixed bug that could cause FixParts to keep a partition's assignment as logical when FixPart could not actually do so. This could happen when there are no gaps between two logical partitions. Some partitioning tools can create such configurations, but FixParts can't. Such configurations are extremely rare. I've only encountered them when logical partitions are out of order. - Added code to detect infinite loops of logical partitions when reading MBR data. When detected, the program now stops reading after the first loop, so no duplicates appear in the partition list. - Fixed bug in partition overlap detection in MBR code. - Changed GPT reading code to use the size encoded in GPT headers to determine how much of the header to use in computing a CRC, with the restriction that the size be equal to or less than the disk's sector size. This should work around problems with libefi in ZFS, which sets the header size to 512 rather than the more common 92. A caveat: If the disk's sector size is larger than the GPTHeader data structure size (512 bytes), then the rest of the sector's contents are ignored and replaced with 0 values. This could produce false positives on CRC checks on disks with over-512-byte sector sizes if the header sector is padded with something other than 0 values. - Fixed bug in new (as of 0.8.0) check that main and backup partition tables are identical on big-endian (PowerPC, etc.) hardware. 0.8.0 (9/10/2011): ------------------ - Added new return option for sgdisk: 8, which means that a replication operation (-R or --replicate) failed. Note that other operations on the same command line might still have succeeded. - Added gdisk_test.sh shell script, contributed by Guillaume Delacour. This script tests some common gdisk and sgdisk operations to be sure they're working correctly. - Enable sgdisk's -l (--load-backup) and -o (--clear) options to work even on disks that are damaged. Most other options will still be ignored, though, so if you suspect a disk may be bad and want to use one of these options, you should do so on a line by itself, followed by a separate command to perform other actions (such as adding new partitions). - Added check for mis-matched primary and backup partition tables. A mismatch is reported as a CRC error. - Added Apple Core Storage partition type code (hex code AF05, GUID 53746F72-6167-11AA-AA11-00306543ECAC). - Added cgdisk program to the family. This program is a rough workalike to cfdisk, much as gdisk is a rough workalike to fdisk. See the cgdisk man page or http://www.rodsbooks.com/gdisk/cgdisk-walkthrough.html for details about its operation. - Fixed bug that caused CHS end point for protective MBR to be set to 0xfeffff rather than the spec-mandated 0xffffff on disks over ~8GB. This is a very minor bug, since not much cares about this, and most other GPT tools get it wrong in the same way, too. 0.7.2 (6/26/2011): ------------------ - The Windows version now (finally!) generates proper GUIDs rather than a purely random number. This fixes a bug that caused Windows 7 to crash when converting a disk from MBR format (but, oddly, not when creating a fresh partition table or doing various other things). - Added a warning when an MBR partition is discarded because it's too big for the disk. - Changed warning to Windows users about the dangers of converting to GPT so that it appears only on disks that aren't already in GPT form. - Fixed bug that caused bogus "3" values to pad the ends of partition names on some disks (particularly those created by Microsoft's disk partitioning tools). - Made compilation without Unicode support possible (see README file) - Made default filesystem type code OS-dependent (based on the compilation platform). - Added new Linux-only filesystem partition type GUID code, 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (8300 entry code). Also changed name of the EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 (0700 entry code) to "Microsoft basic data"). - Fixed a bug that caused an incorrect code to be set for active/bootable partitions when generating a hybrid MBR. - Enable entry of hex codes that begin with "0x" for both GPT and MBR partitions. - Fixed bug that caused the boot loader code to be lost when creating a hybrid MBR. - Fixed bug in sector input code that could produce improper values if the user inputs ridiculously large "+" values. 0.7.1 (3/21/2011): ------------------ - Added support for proper UTF-16LE partition names rather than the "shortcut" that properly encoded only ASCII names. This support works only in Linux, FreeBSD, and OS X, though, at least for the moment. Although it's possible to compile this support into Windows when using Visual C++, it doesn't seem to work properly. Since using this feature would require distributing the ICU libraries with the Windows binary, thus bloating the binary package's size to no effect, I've disabled it in my standard Windows build, at least for now. - Added check to fixparts to keep it from operating on devices that lack an existing MBR signature. (In 0.7.0, it could write an empty MBR data structure to a device on which it was mistakenly launched.) - Fixed bug that caused the protective MBR to not be written when restoring a backup of the GPT data. - Fixed bug that caused second protective MBR partition, when created as part of a hybrid MBR, to always be of type 0xEE, even when the user specified something else. - Integrated a number of code cleanups contributed by Florian Zumbiehl. 0.7.0 (3/11/2011): ------------------ - Fixed bug that caused some types of logical partitions to be misread. - Created FixParts program, to fix problems on MBR-partitioned disks. Although this program is part of the GPT fdisk family, it is NOT used on GPT disks. - Completely redid the GPT-to-MBR code, used both for converting to MBR form and for creating hybrid MBRs. - Fixed a bug that caused gdisk to "forget" some partitions if there were numbering gaps when a conversion to MBR was aborted. - Improved CHS value creation on small (<~8GB) disks for protective MBR and when creating hybrid MBRs or converting to MBR format. Linux-only, for the moment; other platforms still produce bad CHS values on sub-~8GB disks (but few OSes care these days). - Enhanced disk replication features ('u' on the experts' menu in gdisk; -R or --replicate in sgdisk). It's now possible to replicate the partition table from a larger to a smaller disk, so long as all the partitions fit on the smaller disk. In sgdisk, the secondary GPT data are moved automatically if disk sizes don't match. In gdisk, the secondary GPT data are moved automatically if the target disk is smaller than the source disk; if the target disk is larger than the source disk, the user is given the option of making this adjustment. - Fixed --load-backup (-l) option to sgdisk, which was broken. - Changed largest drive that's not given a minimum 4 KiB alignment even when smaller alignment is detected on the disk to 300 GB. - Fixed bug that prevented aborting a partition table backup ('u' on the experts' menu) by hitting the Enter key for the device filename. - Implemented a number of code cleanups provided by Florian Zumbiehl. 0.6.14 (1/8/2011): ------------------ - Made small change to the way the start sector is interpreted if you use a "+" specification, as in "+2G" to locate a partition 2 GiB into the default range. This change makes adjustments for sector alignment less likely. - Modified sgdisk's -n (--new) option to work with relative start and end values (which the man page incorrectly stated it already did). Values of 0 for the start and end sectors refer to the first and last available sectors in the largest free block, and a partition number of 0 refers to the first available partition. - Added ChromeOS GUID values to list of recognized partition type GUIDs. 7F00 = ChromeOS kernel, 7501 = ChromeOS root, 7502 = ChromeOS reserved. Untested on actual ChromeOS system. - Tweaked APM detection to look for APM signature even if an MBR signature has already been found. Helps in diagnosis of cases in which an MBR has overwritten an APM disk. 0.6.13 (10/12/2010): -------------------- - Added notification about nonexistent partitions to hybrid MBR creation in gdisk. - Fixed bug in GPT-to-MBR conversion that could sometimes enable creation of an extended partition that overlaps a preceding partition. - Fixed bug in GPT-to-MBR conversion that prevented creation of an MBR table with logical partitions if there were four or fewer partitions. 0.6.12 (10/7/2010): ------------------- - Adjusted alignment code to use 1 MiB alignment by default for drives with other than 512-byte sector sizes. (Previous versions increased this -- for instance, to 4 MiB for drives with 2048-byte logical sector size.) - Entry of non-hexadecimal value for partition type code now causes re-prompting for a new value, fixing a recently-introduced minor bug. - Fixed bug in sector entry using K/M/G/T/P suffixes on disks with other-than-512-byte sector numbers. - Added "P" (PiB, pebibyte) suffix to suffixes accepted in entering partition sizes. - Fixed bug that caused sgdisk to segfault if fed the (invalid) "-A show" parameter. Now it terminates with a complaint about an invalid partition number 0. - Removed warning when running on big-endian hardware, since this support has been present for quite a while with no bug reports. 0.6.11 (9/25/2010): ------------------- - Added -F (--first-aligned-in-largest) option to sgdisk. This option is a variant on -f (--first-in-largest); it returns the number of the first sector that will be used in the largest free area, given the current alignment value (set via -a/--set-alignment). - Streamlined GUID code entry in gdisk; it no longer offers the option to enter GUIDs in separate segments. - The -t option to sgdisk now accepts GUID values as well as the sgdisk/gdisk-specific two-byte hex codes. - Added check that the protective 0xEE MBR partition begins on sector 1 to the verify function. If it doesn't, a warning message is displayed, but it doesn't count as an error. - Added check for overlapping MBR partitions to verify function (gdisk "v" function on all menus; sgdisk -v/--verify function). Also warns about multiple MBR 0xEE partitions (causes problems in some OSes). - Added check to GPT-to-MBR and hybrid MBR creation options to prevent creation of disks with duplicate partitions. When told to create a disk with duplicates, sgdisk now aborts with the error message "Problem creating MBR!" When attempting to create a hybrid MBR with duplicates, gdisk silently drops duplicate partitions, leaving fewer than requested. Creating duplicates should not be possible in sgdisk when converting to MBR form. 0.6.10 (8/22/2010): ------------------- - Enable disk-wipe (-z and -Z) and verification (-v) operations in sgdisk even if the disk is badly damaged. - Added support for setting attributes in sgdisk (-A/--attributes option) in sgdisk. - Fixed bug that created backwards attribute field values (bit #2 was entered as bit #61, etc.). - Fixed bug that caused creation of hybrid MBR to wipe out the MBR's boot code. - Added ability to save partition table from one device to another (gdisk: 'u' on experts' menu; sgdisk: -R or --replicate option). - Fixed inaccessible -C/--recompute-chs option in sgdisk. 0.6.9 (7/4/2010): ------------------ - Fixed minor error in sgdisk man page (--largest-new option requires a partition number). - Fixed major bug in hybrid MBR creation, which caused incorrect protective partition end point settings and occasionally other problems. 0.6.8 (5/23/2010): ------------------ - Added tests to see if the file to be opened is a directory, character device, FIFO, or socket; program now terminates if any of these conditions is met. (Linux/FreeBSD/OS X only.) Thanks to Justin Maggard for this patch. - Added 'f' option on gdisk's experts' menu (-G/--randomize-guids in sgdisk). This option randomizes the disk's GUID and all partitions' GUIDs. Intended for use after cloning a disk with a utility that copies the GUIDs intact (such as a raw dd copy) if you want each disk copy to have its own set of GUIDs. - Added -u/--partition-guid and -U/--disk-guid options to sgdisk. These are the equivalents of the 'g' and 'c' options, respectively, on the gdisk experts' menu: They enable adjusting an individual partition's GUID or a disk's GUID. The GUID may be either a fully specified GUID value or 'R' or 'r' to set a random GUID value. - Fixed compile problem for FreeBSD (its math library lacks a log2() function). Also created separate Makefile.freebsd with a couple of FreeBSD-specific options. - Added -N (--largest-new) command to sgdisk. This command creates a single partition that fills the largest single unpartitioned block of space on the disk. - Fixed sgdisk man page error: the --change-name option was incorrectly listed as --change. - Added 'h' option to gdisk experts' menu (-C or --recompute-chs in sgdisk) to recompute all protective/hybrid MBR CHS values. This option is intended to work around a bug in at least one BIOS that prevents the computer from booting when the GPT-mandated (but technically illegal) 0xFFFFFF CHS value is used as the end point for a protective MBR. The recomputed values will be legal (e.g., 0xFEFFFF instead of 0xFFFFFF), but incorrect in GPT terms, and will therefore enable at least one BIOS to boot with a GPT disk. See http://www.rodsbooks.com/gdisk/bios.html for all I know about BIOS/GPT incompatibilities. 0.6.7 (5/1/2010): ----------------- - Undid earlier change, with version 0.6.4, that wiped the MBR boot loader when doing MBR-to-GPT conversions. I've now become skeptical that MBR boot loaders were causing any real problems on GPT disks, so I'm going back to the philosophy of leaving as much alone as possible. - Fixed bug that caused incorrect reporting of free space on 0-size disks (e.g., files of 0 length passed as disk images). - Fixed bug that caused segfault on some invalid disks - Fixed bug that caused incorrect partition numbers to be displayed for some verify problems. 0.6.6 (3/21/2010): ----------------- - Added support for the "no block IO protocol" (referred to as "hide from EFI" in GPT fdisk) and "legacy BIOS bootable" attribute bits. See Table 19 of the UEFI 2.3 specification (p. 153) for details. - Changed the sequence in which GPT data structures are written to disk; backups are now written first, followed by the main structures. This is as recommended in the UEFI 2.3 specification, since it's safer in the extremely unlikely event that a RAID array's size is increased and there's a power outage mid-write. (If the main structures are written first in this case, they'll point to data that's not yet been written; but by writing the backups first, the old main structures will still point to the valid old backup structures.) - Protective MBRs now have disk signatures of 0x00000000, to better conform with GPT as described in the UEFI 2.3 specification. - Added alignment information to the summary data produced by the 'p' main-menu option in gdisk or the -p option to sgdisk. - More alignment changes: GPT fdisk now attempts to determine the alignment value based on alignment of current partitions, if any are defined. If no partitions are defined, a default value of 2048 is set. If the computed value is less than 8 on drives over about 596GiB, it's reset to 8, since the drive might be a WD Advanced Format unit that requires an 8-sector (or larger power-of-2) alignment value for best performance. The 2048-sector default provides better alignment in some RAID configurations. - Changed behavior when a backup restore fails. Previously, GPT fdisk would create a fresh blank set of partitions. Now it does so only if the failure occurs when interpreting the backup's contents; if the user typed the wrong filename, the in-memory data structures aren't touched. 0.6.5 (3/7/2010): ----------------- - Added tests to verify ('v') function and to pre-save checks to look for partitions that end before they begin or that are too big for their disks. - Fixed a bug that could cause spurious data to appear in a grown partition table. - Added ability to convert some or all partitions to logical partitions in GPT-to-MBR conversion. This feature is limited by the fact that at least one free sector must exist immediately prior to each logical partition, so it won't do much good if partitions are crammed together. It should be possible to convert back to MBR any disk that started that way, provided no partitions were added or resized when the disk was in GPT form; and disks that were partitioned with Apple's Disk Utility or other tools that insert unpartitioned space should also be convertible. CAUTION: THE LOGICAL PARTITION CREATION FEATURE DOESN'T TRY TO ALIGN PARTITIONS OR PARTITION HEADER DATA TO CYLINDER BOUNDARIES! It's conceivable that some older OSes or utilities will object to these disks, although Linux, OS X, Windows Vista, and Windows 7 all seem happy with them. - Fixed bug that caused creation of 0-length file if an incorrect device filename was typed. - The gdisk program now prompts for a device filename if it's called with no options. This enables gdisk to do something useful if it's launched by double-clicking its icon in a GUI environment. - Added workaround for bug in some versions of MinGW that caused the program to garble input sector numbers. - The Windows version now works on disks with over-512-byte sectors. Tested on a magneto-optical (MO) drive with 2048-byte sectors. - Added -D (--display-alignment) option to sgdisk, to display sector alignment value (by default, 1 for sub-800GiB disks and 8 for disks over that size). - Fixed bug in computation of CHS geometries for protective MBR. This is non-critical, since most modern utilities ignore the CHS geometries. Concerned users can use the 'n' option on the experts' menu to build new protective MBRs with the new algorithm, if desired. (Note that GNU Parted, at least, gets this wrong, too.) - Fixed memory-allocation bug when reading GPT disks with partition tables with over 128 entries; could cause program to crash on startup. 0.6.4-2 (2/20/2010): -------------------- Note: Neither of the following changes affects actual program code, so I've left the version number in the program at 0.6.4. - Altered Makefile to pass user's compiler and linker environment variables through. - Added #include to gpttext.cc to enable it to compile on the latest GCC versions (it was failing on at least some 4.4.x compilers). 0.6.4 (2/19/2010): ------------------- - Added -m (--gpttombr) option to sgdisk, enabling conversion of GPT disks to MBR format, with a limit of four partitions total, and of course without overcoming the 2TiB limit. - Added -h (--hybrid) option to sgdisk, enabling creation of hybrid MBRs. Fewer options are available in sgdisk than in gdisk, though, in order to keep the user interface manageable. - Fixed off-by-one bug in specification of partition when using the -T (--transform-bsd) option in sgdisk. - Changed the code to create a new MBR unique disk signature whenever a new protective MBR is generated (when doing an MBR-to-GPT conversion, when using the 'n' option on the experts' menu, or when using the 'o' option on the main menu, for example). Previous versions attempted to preserve the existing MBR disk signature in most cases, but this resulted in values of 0x00000000 whenever an empty disk was partitioned, and often in other cases, too. Better to risk changing this value too often than to leave multiple disks with 0x00000000 values, I think. - Added transpose ('t' on experts' menu in gdisk; or -r or --transpose in sgdisk) command to enable fine-tuning partition order without doing a full sort. - Added code to clear the MBR boot loader when doing an MBR-to-GPT conversion. (This was already done in full-disk BSD-to-GPT conversions.) This is done because I've seen a few problem reports that make me think some MBR boot loaders freak out and hang the system when they encounter GPT disks, and/or they attempt to load a second-stage boot loader stored in what is now GPT territory, causing a system hang. Since MBR boot loaders don't work on GPT disks anyhow (even GRUB needs to be reinstalled), this new wiping behavior shouldn't cause any problems, and may prevent a few. - Fixed bug in Windows version that prevented saving backup files. - Fixed bug that caused second and subsequent partition numbers in prompts in hybrid MBR conversion procedure to be displayed in hexadecimal. - Fixed very obscure potential bug in hybrid MBR/GPT synchronization when deleting partitions; code wasn't matching partition lengths correctly, which would only affect partitions that start at the same point but have different lengths in MBR vs. GPT. - Fixed bug in the -E option to sgdisk; it was actually returning the last free sector, not the last free sector in the largest free block. - Fixed bug in -t option to sgdisk; it was corrupting partition type codes. - Fixed minor alignment bug in partition summary list ('p' from any menu) when partition sizes are between 1000 and 1024 units. - Backup restore function ('l' on recovery & transformation menu) now accepts both backups generated by GPT fdisk and backups created by a direct copy (via dd, etc.) of the MBR, main GPT header, and main GPT partition table, in that order. ("dd if=/dev/sda of=backup.gpt bs=512 count=34" will do this on Linux for a disk with a typical-sized GPT table of 128 entries.) 0.6.3 (2/3/2010): ------------------ - Fixed serious data corruption bug on big-endian (PowerPC and similar) systems. - Changed several GPT fdisk Solaris type codes to correct a duplicate - Corrected error in GPT fdisk type codes for NetBSD LFS and NetBSD RAID; they were identical, but I've now changed NetBSD RAID to A906, which is unique. - Added GUID for IBM General Parallel File System (GPFS) partition type code. Somewhat arbitrarily set it to use the 7501 number (MBR code 0x75 is used by IBM PC/IX, so it's at least the right company, by my loose numbering rules....). - Improved GUID generation. Prior versions generated completely random numbers for GUIDs. This works, but is technically a violation of the spec. Unix versions now employ libuuid to generate GUIDs in a more correct way. The Windows version still generates random numbers, though. - Turned PartTypes class into a derived class of GUIDData, and renamed it to PartType. - Created new GUIDData class, to replace the original GUIDData struct. 0.6.2 (1/29/2010): ------------------ - The change-type ('t' on main menu) option now changes the partition's name *IF* the current name is the generic one for the partition type. If the current name is not the generic name, it is NOT changed. - Fixed bug that caused new protective MBR to not be created when the MBR was invalid and the GPT was damaged and the user opts to try to use the GPT data. - Enabled default partition type code of 0700 when creating partitions or changing their type codes. (Type 0700, Linux/Windows data, is set if the user hits the Enter key alone.) - Fixed bug in sort ('s' on main menu) option that caused partition numbers to begin at more than 1 if the original partition list had too many empty partitions before the last one defined. - Improved code to determine which partition table to load in case of CRC mismatches between the partition tables and the stored CRC values in the headers. - Compiles using MinGW (http://www.mingw.org) to create a Windows binary. - Moved all disk I/O functions to the new DiskIO class. This helps with the Windows port; it uses diskio-windows.cc for Windows-specific code, diskio-unix.cc for the Linux, FreeBSD, and OS X code, and diskio.cc for cross-platform disk I/O code. - Changed BSD disklabel detection code to be more correct (I think). This change has no effect on my test disks, but I hope it'll work better on disks with sector sizes other than 512 or 2048. 0.6.1 (1/20/2010): ------------------ - Fixed bug that returned incorrect disk size on 32-bit versions of FreeBSD. - Fixed bug that prevented FreeBSD version from working on disk image files. - Fixed bug that caused BSD disklabel conversion to fail. 0.6.0 (1/15/2010): ------------------ - Fixed bug that caused the convert to MBR function to fail. - Added support for disks with other than 512-byte sectors. - Created embryonic sgdisk program. - Fixed bug that caused relative sector numbers entered by users (e.g, "+128M") to be misinterpreted as from the start of the range rather than from the default value. 0.5.3 (1/4/2010): ----------------- - Fixed bug in display of GUIDs when compiled with some versions of GCC. - Eliminated warnings caused by additional checks in latest versions of GCC. These warnings were harmless, but to eliminate them I've added more error checking on disk I/O. - Eliminated unnecessary warnings about potential data loss if the program was launched with the -l option or if writes aren't possible. - Added code to set the partition boundary value based on the physical sector size. (FindAlignment() function.) This function, however, works only on Linux, and then only if the BLKPBSZGET ioctl is defined. This ioctl is new in kernel 2.6.32 or thereabouts. 0.5.2 (12/31/2009): ------------------- - Modified partition creation function to begin partitions on 8-sector boundaries by default. This improves performance on the new Western Digital Advanced Format drives. The new 'd' and 'l' options on the experts' menu display and change, respectively, the boundary size. - Tweaked code to produce fewer warnings on the latest versions of GCC. 0.5.1: ------ - Made some minor edits to the man page. - Incorporated RPM .spec file changes contributed by Scott Collier (boodle11@gmail.com). - Changed method of locating and loading backup GPT data, to use the main header's pointer, if it's valid, rather than seeking to the end of the disk. - Added 'e' option (relocate backup GPT data structures) to the experts' menu. - Fixed bug that prevented recovery of partitions in case of partially damaged GPT data (bad main and good backup or bad backup and good main header, for instance). 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: ------ - Code cleanup. - Fixed very small formatting bug in display of hex code when a match isn't found when converting from an MBR/gdisk hex code to a GUID type code. - Added the ability to work on disk image files (raw files for virtual machines, backup images, etc.). The program assumes that all such disk image files have 512-byte sectors. - Added verification prompt to 'o' main-menu option to avoid accidental erasures of all partitions. - The "destroy GPT data structures" option ('z' on the experts' menu) now also destroys all EFI GPT (0xEE) partitions in the MBR. - Added an extra warning to the "destroy GPT data structures" option if an APM or BSD disklabel was detected on the disk. - Added a buffer flush after destroying GPT data structures, to get the OS to read the new (empty or MBR-only) partition table. - Fixed bug that allowed entry of nonexistent partition numbers when creating a hybrid MBR. 0.4.1: ------ - Code cleanup/re-organization - Partition creation function ('n' on main menu) now uses the start of the largest available chunk of free space rather than the first available sector as the default starting sector number. This should enable easier partition creation if there are small bits of free space on the disk. - You can now specify the end point of a partition by using a minus sign, in which case the end point is the default value minus the specified size. For instance, "-200M" creates a partition that ends 200MiB before the default end point. - You can now specify the start point of a partition by using a plus or minus sign, in which case the start point is the specified distance from the start (+) or end (-) of free space. This is exactly the same as the new rules for entry of the end point, except that the default value is set differently. - Deleting a partition now checks for a matching hybrid MBR partition, and if one is found, it's deleted. Any empty space that then surrounds the 0xEE (EFI GPT) MBR partitions is then added to the nearby 0xEE partition. If no non-0xEE partitions are left, a fresh protective MBR is generated. - Added hybrid MBR consistency check to the verify ('v') option and to pre-write checks. If non-0xEE/non-0x00 MBR partitions without corresponding GPT partitions are found, the user is warned. This finding does NOT prevent writing the partition table, though. - Added non-destructive write test when opening the device file, in order to detect the problem with FreeBSD being unable to write to disks with mounted partitions (or other potential problems). 0.4.0: ------ - Added support for BSD disklabels. The program can now convert disks that use "raw" disklabels, with the caveat that the first partition will almost certainly need to be deleted because it'll overlap the main GPT header; and convert disklabels contained within a GPT (or a former MBR, converted to GPT) partition. In the latter case, the 'b' main menu option is used. - Added support for compiling on FreeBSD. - Fixed bug that could cause crashes or incomplete sorts when sorting the partition table. - New partitions, including converted ones, now take on the name of the partition type as a default name. - Reorganized some code; created a separate C++ class for GPT partitions (GPTPart), which replaced a struct and enabled moving code from the bloated GPTData class into GPTPart. - Fixed a bug that produced spurious warnings about unknown sector sizes when loading a backup file. 0.3.5: ------ Note: This version was not officially publicly released; I wanted to test the big-endian support while developing 0.4.0. - Tweaked the disk type identification code to warn users to re-sync their hybrid MBRs when one is detected. - Tweaked MBR-reading code to ignore 0xEE (EFI GPT) partitions. This will only have an effect on a poorly partitioned MBR disk that contains an inappropriate EFI GPT partition, or when attempting to recover a corrupted disk by using the hybrid MBR for data recovery. - Added big-endian (PowerPC, etc.) support! - Added code to identify and warn of the presence of an Apple Partition Map (APM) on the disk. - Enabled MBR conversion code to handle multiple logical partitions. 0.3.4: ------ - Fixed bug that enabled (possibly accidental) entry of MBR type codes of 0x00 in GPTData::MakeHybrid(). The fix also enables entry of default type code by pressing the Enter key when prompted. Applied a similar fix to the entry of the type code for the second protective partition, if one is used. - Fixed a typo: "sectors" was spelled "sectprs" in one spot! - Fixed bug that caused default entry for end sector to be refused if an initial value using a plus sign (e.g., "+20G") was also refused. 0.3.3: ------ - Gave users control over the way MBR partitions are assigned to slots in a hybrid MBR setup; the original method (putting the 0xEE partition after the real partitions) works well for non-boot disks, but both GRUB and GRUB2 become confused by this type of setup, so it needs changing. - Changed "blocks" to "sectors" in GPT and MBR table displays. - Added "Boot" column to MBR table display; shows an asterisk (*) when the partition's status is bootable. 0.3.2: ------ - Changed __DARWIN_UNIX03 to __APPLE__ as code to enable MacOS X support. - Added the ability to create a hybrid MBR ('h' on experts' menu). This was motivated by my discovery that Windows 7 remains brain-dead when it comes to the ability to boot from a GPT disk, at least on BIOS-based machines. - Added 'z' option to experts' menu, to destroy GPT data structures and exit. The intent is to use this feature to enable subsequent partitioning of the disk using fdisk or other GPT-unaware tools. (GNU Parted will wipe the GPT data structures itself when you create a new MBR ["msdos disklabel," in Parted parlance], so using Parted is another option.) - Slightly altered the effect of the 'o' command on the main menu. It now blanks out the protective MBR, as well as the GPT data. 0.3.1: ------ - Added Mac OS X support, provided as a patch by David Hubbard (david.c.hubbard@gmail.com). - Fixed bug in disksize() function on Mac OS. (Possibly dependent on the kernel and/or GCC version.) The disk size, of type uint64_t, was not being passed correctly, so I reorganized the function to return it as the function's return value rather than as a parameter. This seems to work OK on my Mac OS test system and on both 32- and 64-bit Linux systems. - Fixed off-by-one bug in GPTData::FindLastAvailable(). - Fixed bug that caused display of options after a disk-write error. - Fixed several incorrect MacOS X partition type GUIDs, thanks to Yves Blusseau (1otnwmz02@sneakemail.com). 0.3.0: ------ - Changed version number to 0.3.0, reflecting the fact that I've received no significant bug reports and so am elevating the program to "beta" status. This change also entailed altering the warning the program displays when saving partition table changes. - Fixed minor bug in CHS geometry of the protective MBR's type EE partition (was producing 0x000200 as the start value, but should be 0x000100). Should be a non-critical bug since the protective MBR partition definition is only there to keep MBR-only disk utilities from messing with the disk. - Added ability to enter GUIDs as single massive strings rather than in chunks. 0.2.2: ------ - Added #include directives required to compile the program using GCC 4.4.0. 0.2.1: ------ - Fixed partition numbering problem in reports of partition overlaps in verification function. - Fixed 1-sector partition size problem when creating new partitions (partitions were 1 sector too big when using the +size option). - Changed BytesToSI() to display values in bytes without decimal points (e.g., "512 bytes" rather than "512.0 bytes"). - Added GPTData class member functions to retrieve GPT data structure locations on disk; used in my internal-use-only GPT-wiping program. - Eliminated the "a reboot is recommended" notice after writing the partition table. - Added notice after sorting the partition table to the effect that editing /etc/fstab and/or the boot loader files may be required. - Fixed bug in MBR-reading function that caused 0x0f (Win95 LBA) and 0x85 (Linux extended) extended partitions to not be read. - Fixed bug in GetLastSector() (in support.cc) that would have prevented correct user entry of over-32-bit sector numbers on 32-bit systems. - Made some changes/corrections to the partition type list in parttypes.cc. Most of these were based on newly-discovered MBR type codes for Apple (Mac OS X) filesystems. - General code cleanup (setting explicit casts, etc.) 0.2.0: ------ - Initial semi-public release gptfdisk-1.0.5/gpt.cc0000664000175000017500000031251113622612343014704 0ustar rodsmithrodsmith/* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition data. */ /* By Rod Smith, initial coding January to February, 2009 */ /* This program is copyright (c) 2009-2018 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 #include #include #include #include #include #include #include #include #include #include #include #include "crc32.h" #include "gpt.h" #include "bsd.h" #include "support.h" #include "parttypes.h" #include "attributes.h" #include "diskio.h" using namespace std; #ifdef __FreeBSD__ #define log2(x) (log(x) / M_LN2) #endif // __FreeBSD__ #ifdef _MSC_VER #define log2(x) (log((double) x) / log(2.0)) #endif // Microsoft Visual C++ #ifdef EFI // in UEFI mode MMX registers are not yet available so using the // x86_64 ABI to move "double" values around is not an option. #ifdef log2 #undef log2 #endif #define log2(x) log2_32( x ) static inline uint32_t log2_32(uint32_t v) { int r = -1; while (v >= 1) { r++; v >>= 1; } return r; } #endif /**************************************** * * * GPTData class and related structures * * * ****************************************/ // Default constructor GPTData::GPTData(void) { blockSize = SECTOR_SIZE; // set a default physBlockSize = 0; // 0 = can't be determined diskSize = 0; partitions = NULL; state = gpt_valid; device = ""; justLooking = 0; mainCrcOk = 0; secondCrcOk = 0; mainPartsCrcOk = 0; secondPartsCrcOk = 0; apmFound = 0; bsdFound = 0; sectorAlignment = MIN_AF_ALIGNMENT; // Align partitions on 4096-byte boundaries by default beQuiet = 0; whichWasUsed = use_new; mainHeader.numParts = 0; numParts = 0; SetGPTSize(NUM_GPT_ENTRIES); // Initialize CRC functions... chksum_crc32gentab(); } // GPTData default constructor GPTData::GPTData(const GPTData & orig) { uint32_t i; if (&orig != this) { mainHeader = orig.mainHeader; numParts = orig.numParts; secondHeader = orig.secondHeader; protectiveMBR = orig.protectiveMBR; device = orig.device; blockSize = orig.blockSize; physBlockSize = orig.physBlockSize; diskSize = orig.diskSize; state = orig.state; justLooking = orig.justLooking; mainCrcOk = orig.mainCrcOk; secondCrcOk = orig.secondCrcOk; mainPartsCrcOk = orig.mainPartsCrcOk; secondPartsCrcOk = orig.secondPartsCrcOk; apmFound = orig.apmFound; bsdFound = orig.bsdFound; sectorAlignment = orig.sectorAlignment; beQuiet = orig.beQuiet; whichWasUsed = orig.whichWasUsed; myDisk.OpenForRead(orig.myDisk.GetName()); delete[] partitions; partitions = new GPTPart [numParts]; if (partitions == NULL) { cerr << "Error! Could not allocate memory for partitions in GPTData::operator=()!\n" << "Terminating!\n"; exit(1); } // if for (i = 0; i < numParts; i++) { partitions[i] = orig.partitions[i]; } // for } // if } // GPTData copy constructor // The following constructor loads GPT data from a device file GPTData::GPTData(string filename) { blockSize = SECTOR_SIZE; // set a default diskSize = 0; partitions = NULL; state = gpt_invalid; device = ""; justLooking = 0; mainCrcOk = 0; secondCrcOk = 0; mainPartsCrcOk = 0; secondPartsCrcOk = 0; apmFound = 0; bsdFound = 0; sectorAlignment = MIN_AF_ALIGNMENT; // Align partitions on 4096-byte boundaries by default beQuiet = 0; whichWasUsed = use_new; mainHeader.numParts = 0; numParts = 0; // Initialize CRC functions... chksum_crc32gentab(); if (!LoadPartitions(filename)) exit(2); } // GPTData(string filename) constructor // Destructor GPTData::~GPTData(void) { delete[] partitions; } // GPTData destructor // Assignment operator GPTData & GPTData::operator=(const GPTData & orig) { uint32_t i; if (&orig != this) { mainHeader = orig.mainHeader; numParts = orig.numParts; secondHeader = orig.secondHeader; protectiveMBR = orig.protectiveMBR; device = orig.device; blockSize = orig.blockSize; physBlockSize = orig.physBlockSize; diskSize = orig.diskSize; state = orig.state; justLooking = orig.justLooking; mainCrcOk = orig.mainCrcOk; secondCrcOk = orig.secondCrcOk; mainPartsCrcOk = orig.mainPartsCrcOk; secondPartsCrcOk = orig.secondPartsCrcOk; apmFound = orig.apmFound; bsdFound = orig.bsdFound; sectorAlignment = orig.sectorAlignment; beQuiet = orig.beQuiet; whichWasUsed = orig.whichWasUsed; myDisk.OpenForRead(orig.myDisk.GetName()); delete[] partitions; partitions = new GPTPart [numParts]; if (partitions == NULL) { cerr << "Error! Could not allocate memory for partitions in GPTData::operator=()!\n" << "Terminating!\n"; exit(1); } // if for (i = 0; i < numParts; i++) { partitions[i] = orig.partitions[i]; } // for } // if return *this; } // GPTData::operator=() /********************************************************************* * * * Begin functions that verify data, or that adjust the verification * * information (compute CRCs, rebuild headers) * * * *********************************************************************/ // Perform detailed verification, reporting on any problems found, but // do *NOT* recover from these problems. Returns the total number of // problems identified. int GPTData::Verify(void) { int problems = 0, alignProbs = 0; uint32_t i, numSegments, testAlignment = sectorAlignment; uint64_t totalFree, largestSegment; // First, check for CRC errors in the GPT data.... if (!mainCrcOk) { problems++; cout << "\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n" << "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n" << "header ('b' on the recovery & transformation menu). This report may be a false\n" << "alarm if you've already corrected other problems.\n"; } // if if (!mainPartsCrcOk) { problems++; cout << "\nProblem: The CRC for the main partition table is invalid. This table may be\n" << "corrupt. Consider loading the backup partition table ('c' on the recovery &\n" << "transformation menu). This report may be a false alarm if you've already\n" << "corrected other problems.\n"; } // if if (!secondCrcOk) { problems++; cout << "\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n" << "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n" << "header ('d' on the recovery & transformation menu). This report may be a false\n" << "alarm if you've already corrected other problems.\n"; } // if if (!secondPartsCrcOk) { problems++; cout << "\nCaution: The CRC for the backup partition table is invalid. This table may\n" << "be corrupt. This program will automatically create a new backup partition\n" << "table when you save your partitions.\n"; } // if // Now check that the main and backup headers both point to themselves.... if (mainHeader.currentLBA != 1) { problems++; cout << "\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++; cout << "\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n" << "at the end of the disk. If you've added a disk to a RAID array, use the 'e'\n" << "option on the experts' menu to adjust the secondary header's and partition\n" << "table's locations.\n"; } // if // Now check that critical main and backup GPT entries match each other if (mainHeader.currentLBA != secondHeader.backupLBA) { problems++; cout << "\nProblem: main GPT header's current LBA pointer (" << mainHeader.currentLBA << ") doesn't\nmatch the backup GPT header's alternate LBA pointer(" << secondHeader.backupLBA << ").\n"; } // if if (mainHeader.backupLBA != secondHeader.currentLBA) { problems++; cout << "\nProblem: main GPT header's backup LBA pointer (" << mainHeader.backupLBA << ") doesn't\nmatch the backup GPT header's current LBA pointer (" << secondHeader.currentLBA << ").\n" << "The 'e' option on the experts' menu may fix this problem.\n"; } // if if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) { problems++; cout << "\nProblem: main GPT header's first usable LBA pointer (" << mainHeader.firstUsableLBA << ") doesn't\nmatch the backup GPT header's first usable LBA pointer (" << secondHeader.firstUsableLBA << ")\n"; } // if if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) { problems++; cout << "\nProblem: main GPT header's last usable LBA pointer (" << mainHeader.lastUsableLBA << ") doesn't\nmatch the backup GPT header's last usable LBA pointer (" << secondHeader.lastUsableLBA << ")\n" << "The 'e' option on the experts' menu can probably fix this problem.\n"; } // if if ((mainHeader.diskGUID != secondHeader.diskGUID)) { problems++; cout << "\nProblem: main header's disk GUID (" << mainHeader.diskGUID << ") doesn't\nmatch the backup GPT header's disk GUID (" << secondHeader.diskGUID << ")\n" << "You should use the 'b' or 'd' option on the recovery & transformation menu to\n" << "select one or the other header.\n"; } // if if (mainHeader.numParts != secondHeader.numParts) { problems++; cout << "\nProblem: main GPT header's number of partitions (" << mainHeader.numParts << ") doesn't\nmatch the backup GPT header's number of partitions (" << secondHeader.numParts << ")\n" << "Resizing the partition table ('s' on the experts' menu) may help.\n"; } // if if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) { problems++; cout << "\nProblem: main GPT header's size of partition entries (" << mainHeader.sizeOfPartitionEntries << ") doesn't\n" << "match the backup GPT header's size of partition entries (" << secondHeader.sizeOfPartitionEntries << ")\n" << "You should use the 'b' or 'd' option on the recovery & transformation menu to\n" << "select one or the other header.\n"; } // if // Now check for a few other miscellaneous problems... // Check that the disk size will hold the data... if (mainHeader.backupLBA >= diskSize) { problems++; cout << "\nProblem: Disk is too small to hold all the data!\n" << "(Disk size is " << diskSize << " sectors, needs to be " << mainHeader.backupLBA + UINT64_C(1) << " sectors.)\n" << "The 'e' option on the experts' menu may fix this problem.\n"; } // if // Check the main and backup partition tables for overlap with things and unusual gaps if (mainHeader.partitionEntriesLBA + GetTableSizeInSectors() > mainHeader.firstUsableLBA) { problems++; cout << "\nProblem: Main partition table extends past the first usable LBA.\n" << "Using 'j' on the experts' menu may enable fixing this problem.\n"; } // if if (mainHeader.partitionEntriesLBA < 2) { problems++; cout << "\nProblem: Main partition table appears impossibly early on the disk.\n" << "Using 'j' on the experts' menu may enable fixing this problem.\n"; } // if if (secondHeader.partitionEntriesLBA + GetTableSizeInSectors() > secondHeader.currentLBA) { problems++; cout << "\nProblem: The backup partition table overlaps the backup header.\n" << "Using 'e' on the experts' menu may fix this problem.\n"; } // if if (mainHeader.partitionEntriesLBA != 2) { cout << "\nWarning: There is a gap between the main metadata (sector 1) and the main\n" << "partition table (sector " << mainHeader.partitionEntriesLBA << "). This is helpful in some exotic configurations,\n" << "but is generally ill-advised. Using 'j' on the experts' menu can adjust this\n" << "gap.\n"; } // if if (mainHeader.partitionEntriesLBA + GetTableSizeInSectors() != mainHeader.firstUsableLBA) { cout << "\nWarning: There is a gap between the main partition table (ending sector " << mainHeader.partitionEntriesLBA + GetTableSizeInSectors() - 1 << ")\n" << "and the first usable sector (" << mainHeader.firstUsableLBA << "). This is helpful in some exotic configurations,\n" << "but is unusual. The util-linux fdisk program often creates disks like this.\n" << "Using 'j' on the experts' menu can adjust this gap.\n"; } // if if (mainHeader.sizeOfPartitionEntries * mainHeader.numParts < 16384) { cout << "\nWarning: The size of the partition table (" << mainHeader.sizeOfPartitionEntries * mainHeader.numParts << " bytes) is less than the minimum\n" << "required by the GPT specification. Most OSes and tools seem to work fine on\n" << "such disks, but this is a violation of the GPT specification and so may cause\n" << "problems.\n"; } // if if ((mainHeader.lastUsableLBA >= diskSize) || (mainHeader.lastUsableLBA > mainHeader.backupLBA)) { problems++; cout << "\nProblem: GPT claims the disk is larger than it is! (Claimed last usable\n" << "sector is " << mainHeader.lastUsableLBA << ", but backup header is at\n" << mainHeader.backupLBA << " and disk size is " << diskSize << " sectors.\n" << "The 'e' option on the experts' menu will probably fix this problem\n"; } // Check for overlapping partitions.... problems += FindOverlaps(); // Check for insane partitions (start after end, hugely big, etc.) problems += FindInsanePartitions(); // Check for mismatched MBR and GPT partitions... problems += FindHybridMismatches(); // Check for MBR-specific problems.... problems += VerifyMBR(); // Check for a 0xEE protective partition that's marked as active.... if (protectiveMBR.IsEEActive()) { cout << "\nWarning: The 0xEE protective partition in the MBR is marked as active. This is\n" << "technically a violation of the GPT specification, and can cause some EFIs to\n" << "ignore the disk, but it is required to boot from a GPT disk on some BIOS-based\n" << "computers. You can clear this flag by creating a fresh protective MBR using\n" << "the 'n' option on the experts' menu.\n"; } // Verify that partitions don't run into GPT data areas.... problems += CheckGPTSize(); if (!protectiveMBR.DoTheyFit()) { cout << "\nPartition(s) in the protective MBR are too big for the disk! Creating a\n" << "fresh protective or hybrid MBR is recommended.\n"; problems++; } // Check that partitions are aligned on proper boundaries (for WD Advanced // Format and similar disks).... if ((physBlockSize != 0) && (blockSize != 0)) testAlignment = physBlockSize / blockSize; testAlignment = max(testAlignment, sectorAlignment); if (testAlignment == 0) // Should not happen; just being paranoid. testAlignment = sectorAlignment; for (i = 0; i < numParts; i++) { if ((partitions[i].IsUsed()) && (partitions[i].GetFirstLBA() % testAlignment) != 0) { cout << "\nCaution: Partition " << i + 1 << " doesn't begin on a " << testAlignment << "-sector boundary. This may\nresult " << "in degraded performance on some modern (2009 and later) hard disks.\n"; alignProbs++; } // if } // for if (alignProbs > 0) cout << "\nConsult http://www.ibm.com/developerworks/linux/library/l-4kb-sector-disks/\n" << "for information on disk alignment.\n"; // Now compute available space, but only if no problems found, since // problems could affect the results if (problems == 0) { totalFree = FindFreeBlocks(&numSegments, &largestSegment); cout << "\nNo problems found. " << totalFree << " free sectors (" << BytesToIeee(totalFree, blockSize) << ") available in " << numSegments << "\nsegments, the largest of which is " << largestSegment << " (" << BytesToIeee(largestSegment, blockSize) << ") in size.\n"; } else { cout << "\nIdentified " << problems << " problems!\n"; } // if/else return (problems); } // GPTData::Verify() // Checks to see if the GPT tables overrun existing partitions; if they // do, issues a warning but takes no action. Returns number of problems // detected (0 if OK, 1 to 2 if problems). int GPTData::CheckGPTSize(void) { uint64_t overlap, firstUsedBlock, lastUsedBlock; uint32_t i; int numProbs = 0; // first, locate the first & last used blocks firstUsedBlock = UINT64_MAX; lastUsedBlock = 0; for (i = 0; i < numParts; i++) { if (partitions[i].IsUsed()) { if (partitions[i].GetFirstLBA() < firstUsedBlock) firstUsedBlock = partitions[i].GetFirstLBA(); if (partitions[i].GetLastLBA() > lastUsedBlock) { lastUsedBlock = partitions[i].GetLastLBA(); } // if } // if } // for // If the disk size is 0 (the default), then it means that various // variables aren't yet set, so the below tests will be useless; // therefore we should skip everything if (diskSize != 0) { if (mainHeader.firstUsableLBA > firstUsedBlock) { overlap = mainHeader.firstUsableLBA - firstUsedBlock; cout << "Warning! Main partition table overlaps the first partition by " << overlap << " blocks!\n"; if (firstUsedBlock > 2) { cout << "Try reducing the partition table size by " << overlap * 4 << " entries.\n(Use the 's' item on the experts' menu.)\n"; } else { cout << "You will need to delete this partition or resize it in another utility.\n"; } // if/else numProbs++; } // Problem at start of disk if (mainHeader.lastUsableLBA < lastUsedBlock) { overlap = lastUsedBlock - mainHeader.lastUsableLBA; cout << "\nWarning! Secondary partition table overlaps the last partition by\n" << overlap << " blocks!\n"; if (lastUsedBlock > (diskSize - 2)) { cout << "You will need to delete this partition or resize it in another utility.\n"; } else { cout << "Try reducing the partition table size by " << overlap * 4 << " entries.\n(Use the 's' item on the experts' menu.)\n"; } // if/else numProbs++; } // Problem at end of disk } // if (diskSize != 0) return numProbs; } // GPTData::CheckGPTSize() // Check the validity of the GPT header. Returns 1 if the main header // is valid, 2 if the backup header is valid, 3 if both are valid, and // 0 if neither is valid. Note that this function checks the GPT signature, // revision value, and CRCs in both headers. int GPTData::CheckHeaderValidity(void) { int valid = 3; cout.setf(ios::uppercase); cout.fill('0'); // Note: failed GPT signature checks produce no error message because // a message is displayed in the ReversePartitionBytes() function if ((mainHeader.signature != GPT_SIGNATURE) || (!CheckHeaderCRC(&mainHeader, 1))) { valid -= 1; } else if ((mainHeader.revision != 0x00010000) && valid) { valid -= 1; cout << "Unsupported GPT version in main header; read 0x"; cout.width(8); cout << hex << mainHeader.revision << ", should be\n0x"; cout.width(8); cout << UINT32_C(0x00010000) << dec << "\n"; } // if/else/if if ((secondHeader.signature != GPT_SIGNATURE) || (!CheckHeaderCRC(&secondHeader))) { valid -= 2; } else if ((secondHeader.revision != 0x00010000) && valid) { valid -= 2; cout << "Unsupported GPT version in backup header; read 0x"; cout.width(8); cout << hex << secondHeader.revision << ", should be\n0x"; cout.width(8); cout << UINT32_C(0x00010000) << dec << "\n"; } // if/else/if // Check for an Apple disk signature if (((mainHeader.signature << 32) == APM_SIGNATURE1) || (mainHeader.signature << 32) == APM_SIGNATURE2) { apmFound = 1; // Will display warning message later } // if cout.fill(' '); return valid; } // GPTData::CheckHeaderValidity() // Check the header CRC to see if it's OK... // Note: Must be called with header in platform-ordered byte order. // Returns 1 if header's computed CRC matches the stored value, 0 if the // computed and stored values don't match int GPTData::CheckHeaderCRC(struct GPTHeader* header, int warn) { uint32_t oldCRC, newCRC, hSize; uint8_t *temp; // Back up old header CRC and then blank it, since it must be 0 for // computation to be valid oldCRC = header->headerCRC; header->headerCRC = UINT32_C(0); hSize = header->headerSize; if (IsLittleEndian() == 0) ReverseHeaderBytes(header); if ((hSize > blockSize) || (hSize < HEADER_SIZE)) { if (warn) { cerr << "\aWarning! Header size is specified as " << hSize << ", which is invalid.\n"; cerr << "Setting the header size for CRC computation to " << HEADER_SIZE << "\n"; } // if hSize = HEADER_SIZE; } else if ((hSize > sizeof(GPTHeader)) && warn) { cout << "\aCaution! Header size for CRC check is " << hSize << ", which is greater than " << sizeof(GPTHeader) << ".\n"; cout << "If stray data exists after the header on the header sector, it will be ignored,\n" << "which may result in a CRC false alarm.\n"; } // if/elseif temp = new uint8_t[hSize]; if (temp != NULL) { memset(temp, 0, hSize); if (hSize < sizeof(GPTHeader)) memcpy(temp, header, hSize); else memcpy(temp, header, sizeof(GPTHeader)); newCRC = chksum_crc32((unsigned char*) temp, hSize); delete[] temp; } else { cerr << "Could not allocate memory in GPTData::CheckHeaderCRC()! Aborting!\n"; exit(1); } if (IsLittleEndian() == 0) ReverseHeaderBytes(header); header->headerCRC = oldCRC; return (oldCRC == newCRC); } // GPTData::CheckHeaderCRC() // Recompute all the CRCs. Must be called before saving if any changes have // been made. Must be called on platform-ordered data (this function reverses // byte order and then undoes that reversal.) void GPTData::RecomputeCRCs(void) { uint32_t crc, hSize; int littleEndian = 1; // If the header size is bigger than the GPT header data structure, reset it; // otherwise, set both header sizes to whatever the main one is.... if (mainHeader.headerSize > sizeof(GPTHeader)) hSize = secondHeader.headerSize = mainHeader.headerSize = HEADER_SIZE; else hSize = secondHeader.headerSize = mainHeader.headerSize; if ((littleEndian = IsLittleEndian()) == 0) { ReversePartitionBytes(); ReverseHeaderBytes(&mainHeader); ReverseHeaderBytes(&secondHeader); } // if // Compute CRC of partition tables & store in main and secondary headers crc = chksum_crc32((unsigned char*) partitions, numParts * GPT_SIZE); mainHeader.partitionEntriesCRC = crc; secondHeader.partitionEntriesCRC = crc; if (littleEndian == 0) { ReverseBytes(&mainHeader.partitionEntriesCRC, 4); ReverseBytes(&secondHeader.partitionEntriesCRC, 4); } // if // Zero out GPT headers' own CRCs (required for correct computation) mainHeader.headerCRC = 0; secondHeader.headerCRC = 0; crc = chksum_crc32((unsigned char*) &mainHeader, hSize); if (littleEndian == 0) ReverseBytes(&crc, 4); mainHeader.headerCRC = crc; crc = chksum_crc32((unsigned char*) &secondHeader, hSize); if (littleEndian == 0) ReverseBytes(&crc, 4); secondHeader.headerCRC = crc; if (littleEndian == 0) { ReverseHeaderBytes(&mainHeader); ReverseHeaderBytes(&secondHeader); ReversePartitionBytes(); } // if } // GPTData::RecomputeCRCs() // Rebuild the main GPT header, using the secondary header as a model. // Typically called when the main header has been found to be corrupt. void GPTData::RebuildMainHeader(void) { mainHeader.signature = GPT_SIGNATURE; mainHeader.revision = secondHeader.revision; mainHeader.headerSize = secondHeader.headerSize; mainHeader.headerCRC = UINT32_C(0); mainHeader.reserved = secondHeader.reserved; mainHeader.currentLBA = secondHeader.backupLBA; mainHeader.backupLBA = secondHeader.currentLBA; mainHeader.firstUsableLBA = secondHeader.firstUsableLBA; mainHeader.lastUsableLBA = secondHeader.lastUsableLBA; mainHeader.diskGUID = secondHeader.diskGUID; mainHeader.numParts = secondHeader.numParts; mainHeader.partitionEntriesLBA = secondHeader.firstUsableLBA - GetTableSizeInSectors(); mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries; mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC; memcpy(mainHeader.reserved2, secondHeader.reserved2, sizeof(mainHeader.reserved2)); mainCrcOk = secondCrcOk; SetGPTSize(mainHeader.numParts, 0); } // GPTData::RebuildMainHeader() // Rebuild the secondary GPT header, using the main header as a model. void GPTData::RebuildSecondHeader(void) { secondHeader.signature = GPT_SIGNATURE; secondHeader.revision = mainHeader.revision; secondHeader.headerSize = mainHeader.headerSize; secondHeader.headerCRC = UINT32_C(0); secondHeader.reserved = mainHeader.reserved; secondHeader.currentLBA = mainHeader.backupLBA; secondHeader.backupLBA = mainHeader.currentLBA; secondHeader.firstUsableLBA = mainHeader.firstUsableLBA; secondHeader.lastUsableLBA = mainHeader.lastUsableLBA; secondHeader.diskGUID = mainHeader.diskGUID; secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1); secondHeader.numParts = mainHeader.numParts; secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries; secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC; memcpy(secondHeader.reserved2, mainHeader.reserved2, sizeof(secondHeader.reserved2)); secondCrcOk = mainCrcOk; SetGPTSize(secondHeader.numParts, 0); } // GPTData::RebuildSecondHeader() // Search for hybrid MBR entries that have no corresponding GPT partition. // Returns number of such mismatches found int GPTData::FindHybridMismatches(void) { int i, found, numFound = 0; uint32_t j; uint64_t mbrFirst, mbrLast; for (i = 0; i < 4; i++) { if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) { j = 0; found = 0; mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i); mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1); do { if ((j < numParts) && (partitions[j].GetFirstLBA() == mbrFirst) && (partitions[j].GetLastLBA() == mbrLast) && (partitions[j].IsUsed())) found = 1; j++; } while ((!found) && (j < numParts)); if (!found) { numFound++; cout << "\nWarning! Mismatched GPT and MBR partition! MBR partition " << i + 1 << ", of type 0x"; cout.fill('0'); cout.setf(ios::uppercase); cout.width(2); cout << hex << (int) protectiveMBR.GetType(i) << ",\n" << "has no corresponding GPT partition! You may continue, but this condition\n" << "might cause data loss in the future!\a\n" << dec; cout.fill(' '); } // if } // if } // for return numFound; } // GPTData::FindHybridMismatches // Find overlapping partitions and warn user about them. Returns number of // overlapping partitions. // Returns number of overlapping segments found. int GPTData::FindOverlaps(void) { int problems = 0; uint32_t i, j; for (i = 1; i < numParts; i++) { for (j = 0; j < i; j++) { if ((partitions[i].IsUsed()) && (partitions[j].IsUsed()) && (partitions[i].DoTheyOverlap(partitions[j]))) { problems++; cout << "\nProblem: partitions " << i + 1 << " and " << j + 1 << " overlap:\n"; cout << " Partition " << i + 1 << ": " << partitions[i].GetFirstLBA() << " to " << partitions[i].GetLastLBA() << "\n"; cout << " Partition " << j + 1 << ": " << partitions[j].GetFirstLBA() << " to " << partitions[j].GetLastLBA() << "\n"; } // if } // for j... } // for i... return problems; } // GPTData::FindOverlaps() // Find partitions that are insane -- they start after they end or are too // big for the disk. (The latter should duplicate detection of overlaps // with GPT backup data structures, but better to err on the side of // redundant tests than to miss something....) // Returns number of problems found. int GPTData::FindInsanePartitions(void) { uint32_t i; int problems = 0; for (i = 0; i < numParts; i++) { if (partitions[i].IsUsed()) { if (partitions[i].GetFirstLBA() > partitions[i].GetLastLBA()) { problems++; cout << "\nProblem: partition " << i + 1 << " ends before it begins.\n"; } // if if (partitions[i].GetLastLBA() >= diskSize) { problems++; cout << "\nProblem: partition " << i + 1 << " is too big for the disk.\n"; } // if } // if } // for return problems; } // GPTData::FindInsanePartitions(void) /****************************************************************** * * * Begin functions that load data from disk or save data to disk. * * * ******************************************************************/ // Change the filename associated with the GPT. Used for duplicating // the partition table to a new disk and saving backups. // Returns 1 on success, 0 on failure. int GPTData::SetDisk(const string & deviceFilename) { int err, allOK = 1; device = deviceFilename; if (allOK && myDisk.OpenForRead(deviceFilename)) { // store disk information.... diskSize = myDisk.DiskSize(&err); blockSize = (uint32_t) myDisk.GetBlockSize(); physBlockSize = (uint32_t) myDisk.GetPhysBlockSize(); } // if protectiveMBR.SetDisk(&myDisk); protectiveMBR.SetDiskSize(diskSize); protectiveMBR.SetBlockSize(blockSize); return allOK; } // GPTData::SetDisk() // Scan for partition data. This function loads the MBR data (regular MBR or // protective MBR) and loads BSD disklabel data (which is probably invalid). // It also looks for APM data, forces a load of GPT data, and summarizes // the results. void GPTData::PartitionScan(void) { BSDData bsdDisklabel; // Read the MBR & check for BSD disklabel protectiveMBR.ReadMBRData(&myDisk); bsdDisklabel.ReadBSDData(&myDisk, 0, diskSize - 1); // Load the GPT data, whether or not it's valid ForceLoadGPTData(); // Some tools create a 0xEE partition that's too big. If this is detected, // normalize it.... if ((state == gpt_valid) && !protectiveMBR.DoTheyFit() && (protectiveMBR.GetValidity() == gpt)) { if (!beQuiet) { cerr << "\aThe protective MBR's 0xEE partition is oversized! Auto-repairing.\n\n"; } // if protectiveMBR.MakeProtectiveMBR(); } // if if (!beQuiet) { cout << "Partition table scan:\n"; protectiveMBR.ShowState(); bsdDisklabel.ShowState(); ShowAPMState(); // Show whether there's an Apple Partition Map present ShowGPTState(); // Show GPT status cout << "\n"; } // if if (apmFound) { cout << "\n*******************************************************************\n" << "This disk appears to contain an Apple-format (APM) partition table!\n"; if (!justLooking) { cout << "It will be destroyed if you continue!\n"; } // if cout << "*******************************************************************\n\n\a"; } // if } // GPTData::PartitionScan() // Read GPT data from a disk. int GPTData::LoadPartitions(const string & deviceFilename) { BSDData bsdDisklabel; int err, allOK = 1; MBRValidity mbrState; if (myDisk.OpenForRead(deviceFilename)) { err = myDisk.OpenForWrite(deviceFilename); if ((err == 0) && (!justLooking)) { cout << "\aNOTE: Write test failed with error number " << errno << ". It will be impossible to save\nchanges to this disk's partition table!\n"; #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) cout << "You may be able to enable writes by exiting this program, typing\n" << "'sysctl kern.geom.debugflags=16' at a shell prompt, and re-running this\n" << "program.\n"; #endif #if defined (__APPLE__) cout << "You may need to deactivate System Integrity Protection to use this program. See\n" << "https://www.quora.com/How-do-I-turn-off-the-rootless-in-OS-X-El-Capitan-10-11\n" << "for more information.\n"; #endif cout << "\n"; } // if myDisk.Close(); // Close and re-open read-only in case of bugs } else allOK = 0; // if if (allOK && myDisk.OpenForRead(deviceFilename)) { // store disk information.... diskSize = myDisk.DiskSize(&err); blockSize = (uint32_t) myDisk.GetBlockSize(); physBlockSize = (uint32_t) myDisk.GetPhysBlockSize(); device = deviceFilename; PartitionScan(); // Check for partition types, load GPT, & print summary whichWasUsed = UseWhichPartitions(); switch (whichWasUsed) { case use_mbr: XFormPartitions(); break; case use_bsd: bsdDisklabel.ReadBSDData(&myDisk, 0, diskSize - 1); // bsdDisklabel.DisplayBSDData(); ClearGPTData(); protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1) XFormDisklabel(&bsdDisklabel); break; case use_gpt: mbrState = protectiveMBR.GetValidity(); if ((mbrState == invalid) || (mbrState == mbr)) protectiveMBR.MakeProtectiveMBR(); break; case use_new: ClearGPTData(); protectiveMBR.MakeProtectiveMBR(); break; case use_abort: allOK = 0; cerr << "Invalid partition data!\n"; break; } // switch if (allOK) CheckGPTSize(); myDisk.Close(); ComputeAlignment(); } else { allOK = 0; } // if/else return (allOK); } // GPTData::LoadPartitions() // Loads the GPT, as much as possible. Returns 1 if this seems to have // succeeded, 0 if there are obvious problems.... int GPTData::ForceLoadGPTData(void) { int allOK, validHeaders, loadedTable = 1; allOK = LoadHeader(&mainHeader, myDisk, 1, &mainCrcOk); if (mainCrcOk && (mainHeader.backupLBA < diskSize)) { allOK = LoadHeader(&secondHeader, myDisk, mainHeader.backupLBA, &secondCrcOk) && allOK; } else { allOK = LoadHeader(&secondHeader, myDisk, diskSize - UINT64_C(1), &secondCrcOk) && allOK; if (mainCrcOk && (mainHeader.backupLBA >= diskSize)) cout << "Warning! Disk size is smaller than the main header indicates! Loading\n" << "secondary header from the last sector of the disk! You should use 'v' to\n" << "verify disk integrity, and perhaps options on the experts' menu to repair\n" << "the disk.\n"; } // if/else if (!allOK) state = gpt_invalid; // Return valid headers code: 0 = both headers bad; 1 = main header // good, backup bad; 2 = backup header good, main header bad; // 3 = both headers good. Note these codes refer to valid GPT // signatures, version numbers, and CRCs. validHeaders = CheckHeaderValidity(); // Read partitions (from primary array) if (validHeaders > 0) { // if at least one header is OK.... // GPT appears to be valid.... state = gpt_valid; // We're calling the GPT valid, but there's a possibility that one // of the two headers is corrupt. If so, use the one that seems to // be in better shape to regenerate the bad one if (validHeaders == 1) { // valid main header, invalid backup header cerr << "\aCaution: invalid backup GPT header, but valid main header; regenerating\n" << "backup header from main header.\n\n"; RebuildSecondHeader(); state = gpt_corrupt; secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main } else if (validHeaders == 2) { // valid backup header, invalid main header cerr << "\aCaution: invalid main GPT header, but valid backup; regenerating main header\n" << "from backup!\n\n"; RebuildMainHeader(); state = gpt_corrupt; mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup } // if/else/if // Figure out which partition table to load.... // Load the main partition table, if its header's CRC is OK if (validHeaders != 2) { if (LoadMainTable() == 0) allOK = 0; } else { // bad main header CRC and backup header CRC is OK state = gpt_corrupt; if (LoadSecondTableAsMain()) { loadedTable = 2; cerr << "\aWarning: Invalid CRC on main header data; loaded backup partition table.\n"; } else { // backup table bad, bad main header CRC, but try main table in desperation.... if (LoadMainTable() == 0) { allOK = 0; loadedTable = 0; cerr << "\a\aWarning! Unable to load either main or backup partition table!\n"; } // if } // if/else (LoadSecondTableAsMain()) } // if/else (load partition table) if (loadedTable == 1) secondPartsCrcOk = CheckTable(&secondHeader); else if (loadedTable == 2) mainPartsCrcOk = CheckTable(&mainHeader); else mainPartsCrcOk = secondPartsCrcOk = 0; // Problem with main partition table; if backup is OK, use it instead.... if (secondPartsCrcOk && secondCrcOk && !mainPartsCrcOk) { state = gpt_corrupt; allOK = allOK && LoadSecondTableAsMain(); mainPartsCrcOk = 0; // LoadSecondTableAsMain() resets this, so re-flag as bad cerr << "\aWarning! Main partition table CRC mismatch! Loaded backup " << "partition table\ninstead of main partition table!\n\n"; } // if */ // Check for valid CRCs and warn if there are problems if ((validHeaders != 3) || (mainPartsCrcOk == 0) || (secondPartsCrcOk == 0)) { cerr << "Warning! One or more CRCs don't match. You should repair the disk!\n"; // Show detail status of header and table if (validHeaders & 0x1) cerr << "Main header: OK\n"; else cerr << "Main header: ERROR\n"; if (validHeaders & 0x2) cerr << "Backup header: OK\n"; else cerr << "Backup header: ERROR\n"; if (mainPartsCrcOk) cerr << "Main partition table: OK\n"; else cerr << "Main partition table: ERROR\n"; if (secondPartsCrcOk) cerr << "Backup partition table: OK\n"; else cerr << "Backup partition table: ERROR\n"; cerr << "\n"; state = gpt_corrupt; } // if } else { state = gpt_invalid; } // if/else return allOK; } // GPTData::ForceLoadGPTData() // Loads the partition table pointed to by the main GPT header. The // main GPT header in memory MUST be valid for this call to do anything // sensible! // Returns 1 on success, 0 on failure. CRC errors do NOT count as failure. int GPTData::LoadMainTable(void) { return LoadPartitionTable(mainHeader, myDisk); } // GPTData::LoadMainTable() // Load the second (backup) partition table as the primary partition // table. Used in repair functions, and when starting up if the main // partition table is damaged. // Returns 1 on success, 0 on failure. CRC errors do NOT count as failure. int GPTData::LoadSecondTableAsMain(void) { return LoadPartitionTable(secondHeader, myDisk); } // GPTData::LoadSecondTableAsMain() // Load a single GPT header (main or backup) from the specified disk device and // sector. Applies byte-order corrections on big-endian platforms. Sets crcOk // value appropriately. // Returns 1 on success, 0 on failure. Note that CRC errors do NOT qualify as // failure. int GPTData::LoadHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector, int *crcOk) { int allOK = 1; GPTHeader tempHeader; disk.Seek(sector); if (disk.Read(&tempHeader, 512) != 512) { cerr << "Warning! Read error " << errno << "; strange behavior now likely!\n"; allOK = 0; } // if // Reverse byte order, if necessary if (IsLittleEndian() == 0) { ReverseHeaderBytes(&tempHeader); } // if *crcOk = CheckHeaderCRC(&tempHeader); if (allOK && (numParts != tempHeader.numParts) && *crcOk) { allOK = SetGPTSize(tempHeader.numParts, 0); } *header = tempHeader; return allOK; } // GPTData::LoadHeader // Load a partition table (either main or secondary) from the specified disk, // using header as a reference for what to load. If sector != 0 (the default // is 0), loads from the specified sector; otherwise loads from the sector // indicated in header. // Returns 1 on success, 0 on failure. CRC errors do NOT count as failure. int GPTData::LoadPartitionTable(const struct GPTHeader & header, DiskIO & disk, uint64_t sector) { uint32_t sizeOfParts, newCRC; int retval; if (disk.OpenForRead()) { if (sector == 0) { retval = disk.Seek(header.partitionEntriesLBA); } else { retval = disk.Seek(sector); } // if/else if (retval == 1) retval = SetGPTSize(header.numParts, 0); if (retval == 1) { sizeOfParts = header.numParts * header.sizeOfPartitionEntries; if (disk.Read(partitions, sizeOfParts) != (int) sizeOfParts) { cerr << "Warning! Read error " << errno << "! Misbehavior now likely!\n"; retval = 0; } // if newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts); mainPartsCrcOk = secondPartsCrcOk = (newCRC == header.partitionEntriesCRC); if (IsLittleEndian() == 0) ReversePartitionBytes(); if (!mainPartsCrcOk) { cout << "Caution! After loading partitions, the CRC doesn't check out!\n"; } // if } else { cerr << "Error! Couldn't seek to partition table!\n"; } // if/else } else { cerr << "Error! Couldn't open device " << device << " when reading partition table!\n"; retval = 0; } // if/else return retval; } // GPTData::LoadPartitionsTable() // Check the partition table pointed to by header, but don't keep it // around. // Returns 1 if the CRC is OK & this table matches the one already in memory, // 0 if not or if there was a read error. int GPTData::CheckTable(struct GPTHeader *header) { uint32_t sizeOfParts, newCRC; GPTPart *partsToCheck; GPTHeader *otherHeader; int allOK = 0; // Load partition table into temporary storage to check // its CRC and store the results, then discard this temporary // storage, since we don't use it in any but recovery operations if (myDisk.Seek(header->partitionEntriesLBA)) { partsToCheck = new GPTPart[header->numParts]; sizeOfParts = header->numParts * header->sizeOfPartitionEntries; if (partsToCheck == NULL) { cerr << "Could not allocate memory in GPTData::CheckTable()! Terminating!\n"; exit(1); } // if if (myDisk.Read(partsToCheck, sizeOfParts) != (int) sizeOfParts) { cerr << "Warning! Error " << errno << " reading partition table for CRC check!\n"; } else { newCRC = chksum_crc32((unsigned char*) partsToCheck, sizeOfParts); allOK = (newCRC == header->partitionEntriesCRC); if (header == &mainHeader) otherHeader = &secondHeader; else otherHeader = &mainHeader; if (newCRC != otherHeader->partitionEntriesCRC) { cerr << "Warning! Main and backup partition tables differ! Use the 'c' and 'e' options\n" << "on the recovery & transformation menu to examine the two tables.\n\n"; allOK = 0; } // if } // if/else delete[] partsToCheck; } // if return allOK; } // GPTData::CheckTable() // Writes GPT (and protective MBR) to disk. If quiet==1, moves the second // header later on the disk without asking for permission, if necessary, and // doesn't confirm the operation before writing. If quiet==0, asks permission // before moving the second header and asks for final confirmation of any // write. // Returns 1 on successful write, 0 if there was a problem. int GPTData::SaveGPTData(int quiet) { int allOK = 1, syncIt = 1; char answer; // First do some final sanity checks.... // This test should only fail on read-only disks.... if (justLooking) { cout << "The justLooking flag is set. This probably means you can't write to the disk.\n"; allOK = 0; } // if // Check that disk is really big enough to handle the second header... if (mainHeader.backupLBA >= diskSize) { cerr << "Caution! Secondary header was placed beyond the disk's limits! Moving the\n" << "header, but other problems may occur!\n"; MoveSecondHeaderToEnd(); } // if // Is there enough space to hold the GPT headers and partition tables, // given the partition sizes? if (CheckGPTSize() > 0) { allOK = 0; } // if // Check that second header is properly placed. Warn and ask if this should // be corrected if the test fails.... if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) { if (quiet == 0) { cout << "Warning! Secondary header is placed too early on the disk! Do you want to\n" << "correct this problem? "; if (GetYN() == 'Y') { MoveSecondHeaderToEnd(); cout << "Have moved second header and partition table to correct location.\n"; } else { cout << "Have not corrected the problem. Strange problems may occur in the future!\n"; } // if correction requested } else { // Go ahead and do correction automatically MoveSecondHeaderToEnd(); } // if/else quiet } // if if ((mainHeader.lastUsableLBA >= diskSize) || (mainHeader.lastUsableLBA > mainHeader.backupLBA)) { if (quiet == 0) { cout << "Warning! The claimed last usable sector is incorrect! Do you want to correct\n" << "this problem? "; if (GetYN() == 'Y') { MoveSecondHeaderToEnd(); cout << "Have adjusted the second header and last usable sector value.\n"; } else { cout << "Have not corrected the problem. Strange problems may occur in the future!\n"; } // if correction requested } else { // go ahead and do correction automatically MoveSecondHeaderToEnd(); } // if/else quiet } // if // Check for overlapping or insane partitions.... if ((FindOverlaps() > 0) || (FindInsanePartitions() > 0)) { allOK = 0; cerr << "Aborting write operation!\n"; } // if // Check that protective MBR fits, and warn if it doesn't.... if (!protectiveMBR.DoTheyFit()) { cerr << "\nPartition(s) in the protective MBR are too big for the disk! Creating a\n" << "fresh protective or hybrid MBR is recommended.\n"; } // Check for mismatched MBR and GPT data, but let it pass if found // (function displays warning message) FindHybridMismatches(); RecomputeCRCs(); if ((allOK) && (!quiet)) { cout << "\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n" << "PARTITIONS!!\n\nDo you want to proceed? "; answer = GetYN(); if (answer == 'Y') { cout << "OK; writing new GUID partition table (GPT) to " << myDisk.GetName() << ".\n"; } else { allOK = 0; } // if/else } // if // Do it! if (allOK) { if (myDisk.OpenForWrite()) { // As per UEFI specs, write the secondary table and GPT first.... allOK = SavePartitionTable(myDisk, secondHeader.partitionEntriesLBA); if (!allOK) { cerr << "Unable to save backup partition table! Perhaps the 'e' option on the experts'\n" << "menu will resolve this problem.\n"; syncIt = 0; } // if // Now write the secondary GPT header... allOK = allOK && SaveHeader(&secondHeader, myDisk, mainHeader.backupLBA); // Now write the main partition tables... allOK = allOK && SavePartitionTable(myDisk, mainHeader.partitionEntriesLBA); // Now write the main GPT header... allOK = allOK && SaveHeader(&mainHeader, myDisk, 1); // To top it off, write the protective MBR... allOK = allOK && protectiveMBR.WriteMBRData(&myDisk); // re-read the partition table // Note: Done even if some write operations failed, but not if all of them failed. // Done this way because I've received one problem report from a user one whose // system the MBR write failed but everything else was OK (on a GPT disk under // Windows), and the failure to sync therefore caused Windows to restore the // original partition table from its cache. OTOH, such restoration might be // desirable if the error occurs later; but that seems unlikely unless the initial // write fails.... if (syncIt) myDisk.DiskSync(); if (allOK) { // writes completed OK cout << "The operation has completed successfully.\n"; } else { cerr << "Warning! An error was reported when writing the partition table! This error\n" << "MIGHT be harmless, or the disk might be damaged! Checking it is advisable.\n"; } // if/else myDisk.Close(); } else { cerr << "Unable to open device '" << myDisk.GetName() << "' for writing! Errno is " << errno << "! Aborting write!\n"; allOK = 0; } // if/else } else { cout << "Aborting write of new partition table.\n"; } // if return (allOK); } // GPTData::SaveGPTData() // Save GPT data to a backup file. This function does much less error // checking than SaveGPTData(). It can therefore preserve many types of // corruption for later analysis; however, it preserves only the MBR, // the main GPT header, the backup GPT header, and the main partition // table; it discards the backup partition table, since it should be // identical to the main partition table on healthy disks. int GPTData::SaveGPTBackup(const string & filename) { int allOK = 1; DiskIO backupFile; if (backupFile.OpenForWrite(filename)) { // 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(); protectiveMBR.WriteMBRData(&backupFile); protectiveMBR.SetDisk(&myDisk); if (allOK) { // MBR write closed disk, so re-open and seek to end.... backupFile.OpenForWrite(); allOK = SaveHeader(&mainHeader, backupFile, 1); } // if (allOK) if (allOK) allOK = SaveHeader(&secondHeader, backupFile, 2); if (allOK) allOK = SavePartitionTable(backupFile, 3); if (allOK) { // writes completed OK cout << "The operation has completed successfully.\n"; } else { cerr << "Warning! An error was reported when writing the backup file.\n" << "It may not be usable!\n"; } // if/else backupFile.Close(); } else { cerr << "Unable to open file '" << filename << "' for writing! Aborting!\n"; allOK = 0; } // if/else return allOK; } // GPTData::SaveGPTBackup() // Write a GPT header (main or backup) to the specified sector. Used by both // the SaveGPTData() and SaveGPTBackup() functions. // Should be passed an architecture-appropriate header (DO NOT call // ReverseHeaderBytes() on the header before calling this function) // Returns 1 on success, 0 on failure int GPTData::SaveHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector) { int littleEndian, allOK = 1; littleEndian = IsLittleEndian(); if (!littleEndian) ReverseHeaderBytes(header); if (disk.Seek(sector)) { if (disk.Write(header, 512) == -1) allOK = 0; } else allOK = 0; // if (disk.Seek()...) if (!littleEndian) ReverseHeaderBytes(header); return allOK; } // GPTData::SaveHeader() // Save the partitions to the specified sector. Used by both the SaveGPTData() // and SaveGPTBackup() functions. // Should be passed an architecture-appropriate header (DO NOT call // ReverseHeaderBytes() on the header before calling this function) // Returns 1 on success, 0 on failure int GPTData::SavePartitionTable(DiskIO & disk, uint64_t sector) { int littleEndian, allOK = 1; littleEndian = IsLittleEndian(); if (disk.Seek(sector)) { if (!littleEndian) ReversePartitionBytes(); if (disk.Write(partitions, mainHeader.sizeOfPartitionEntries * numParts) == -1) allOK = 0; if (!littleEndian) ReversePartitionBytes(); } else allOK = 0; // if (myDisk.Seek()...) return allOK; } // GPTData::SavePartitionTable() // Load GPT data from a backup file created by SaveGPTBackup(). This function // does minimal error checking. It returns 1 if it completed successfully, // 0 if there was a problem. In the latter case, it creates a new empty // set of partitions. int GPTData::LoadGPTBackup(const string & filename) { int allOK = 1, val, err; int shortBackup = 0; DiskIO backupFile; if (backupFile.OpenForRead(filename)) { // Let the MBRData class load the saved MBR... protectiveMBR.ReadMBRData(&backupFile, 0); // 0 = don't check block size protectiveMBR.SetDisk(&myDisk); LoadHeader(&mainHeader, backupFile, 1, &mainCrcOk); // Check backup file size and rebuild second header if file is right // size to be direct dd copy of MBR, main header, and main partition // table; if other size, treat it like a GPT fdisk-generated backup // file shortBackup = ((backupFile.DiskSize(&err) * backupFile.GetBlockSize()) == (mainHeader.numParts * mainHeader.sizeOfPartitionEntries) + 1024); if (shortBackup) { RebuildSecondHeader(); secondCrcOk = mainCrcOk; } else { LoadHeader(&secondHeader, backupFile, 2, &secondCrcOk); } // if/else // Return valid headers code: 0 = both headers bad; 1 = main header // good, backup bad; 2 = backup header good, main header bad; // 3 = both headers good. Note these codes refer to valid GPT // signatures and version numbers; more subtle problems will elude // this check! if ((val = CheckHeaderValidity()) > 0) { if (val == 2) { // only backup header seems to be good SetGPTSize(secondHeader.numParts, 0); } else { // main header is OK SetGPTSize(mainHeader.numParts, 0); } // if/else if (secondHeader.currentLBA != diskSize - UINT64_C(1)) { cout << "Warning! Current disk size doesn't match that of the backup!\n" << "Adjusting sizes to match, but subsequent problems are possible!\n"; MoveSecondHeaderToEnd(); } // if if (!LoadPartitionTable(mainHeader, backupFile, (uint64_t) (3 - shortBackup))) cerr << "Warning! Read error " << errno << " loading partition table; strange behavior now likely!\n"; } else { allOK = 0; } // if/else // Something went badly wrong, so blank out partitions if (allOK == 0) { cerr << "Improper backup file! Clearing all partition data!\n"; ClearGPTData(); protectiveMBR.MakeProtectiveMBR(); } // if } else { allOK = 0; cerr << "Unable to open file '" << filename << "' for reading! Aborting!\n"; } // if/else return allOK; } // GPTData::LoadGPTBackup() int GPTData::SaveMBR(void) { return protectiveMBR.WriteMBRData(&myDisk); } // GPTData::SaveMBR() // This function destroys the on-disk GPT structures, but NOT the on-disk // MBR. // Returns 1 if the operation succeeds, 0 if not. int GPTData::DestroyGPT(void) { int sum, tableSize, allOK = 1; uint8_t blankSector[512]; uint8_t* emptyTable; memset(blankSector, 0, sizeof(blankSector)); ClearGPTData(); if (myDisk.OpenForWrite()) { if (!myDisk.Seek(mainHeader.currentLBA)) allOK = 0; if (myDisk.Write(blankSector, 512) != 512) { // blank it out cerr << "Warning! GPT main header not overwritten! Error is " << errno << "\n"; allOK = 0; } // if if (!myDisk.Seek(mainHeader.partitionEntriesLBA)) allOK = 0; tableSize = numParts * mainHeader.sizeOfPartitionEntries; emptyTable = new uint8_t[tableSize]; if (emptyTable == NULL) { cerr << "Could not allocate memory in GPTData::DestroyGPT()! Terminating!\n"; exit(1); } // if memset(emptyTable, 0, tableSize); if (allOK) { sum = myDisk.Write(emptyTable, tableSize); if (sum != tableSize) { cerr << "Warning! GPT main partition table not overwritten! Error is " << errno << "\n"; allOK = 0; } // if write failed } // if if (!myDisk.Seek(secondHeader.partitionEntriesLBA)) allOK = 0; if (allOK) { sum = myDisk.Write(emptyTable, tableSize); if (sum != tableSize) { cerr << "Warning! GPT backup partition table not overwritten! Error is " << errno << "\n"; allOK = 0; } // if wrong size written } // if if (!myDisk.Seek(secondHeader.currentLBA)) allOK = 0; if (allOK) { if (myDisk.Write(blankSector, 512) != 512) { // blank it out cerr << "Warning! GPT backup header not overwritten! Error is " << errno << "\n"; allOK = 0; } // if } // if myDisk.DiskSync(); myDisk.Close(); cout << "GPT data structures destroyed! You may now partition the disk using fdisk or\n" << "other utilities.\n"; delete[] emptyTable; } else { cerr << "Problem opening '" << device << "' for writing! Program will now terminate.\n"; } // if/else (fd != -1) return (allOK); } // GPTDataTextUI::DestroyGPT() // Wipe MBR data from the disk (zero it out completely) // Returns 1 on success, 0 on failure. int GPTData::DestroyMBR(void) { int allOK; uint8_t blankSector[512]; memset(blankSector, 0, sizeof(blankSector)); allOK = myDisk.OpenForWrite() && myDisk.Seek(0) && (myDisk.Write(blankSector, 512) == 512); if (!allOK) cerr << "Warning! MBR not overwritten! Error is " << errno << "!\n"; return allOK; } // GPTData::DestroyMBR(void) // Tell user whether Apple Partition Map (APM) was discovered.... void GPTData::ShowAPMState(void) { if (apmFound) cout << " APM: present\n"; else cout << " APM: not present\n"; } // GPTData::ShowAPMState() // Tell user about the state of the GPT data.... void GPTData::ShowGPTState(void) { switch (state) { case gpt_invalid: cout << " GPT: not present\n"; break; case gpt_valid: cout << " GPT: present\n"; break; case gpt_corrupt: cout << " GPT: damaged\n"; break; default: cout << "\a GPT: unknown -- bug!\n"; break; } // switch } // GPTData::ShowGPTState() // Display the basic GPT data void GPTData::DisplayGPTData(void) { uint32_t i; uint64_t temp, totalFree; cout << "Disk " << device << ": " << diskSize << " sectors, " << BytesToIeee(diskSize, blockSize) << "\n"; if (myDisk.GetModel() != "") cout << "Model: " << myDisk.GetModel() << "\n"; if (physBlockSize > 0) cout << "Sector size (logical/physical): " << blockSize << "/" << physBlockSize << " bytes\n"; else cout << "Sector size (logical): " << blockSize << " bytes\n"; cout << "Disk identifier (GUID): " << mainHeader.diskGUID << "\n"; cout << "Partition table holds up to " << numParts << " entries\n"; cout << "Main partition table begins at sector " << mainHeader.partitionEntriesLBA << " and ends at sector " << mainHeader.partitionEntriesLBA + GetTableSizeInSectors() - 1 << "\n"; cout << "First usable sector is " << mainHeader.firstUsableLBA << ", last usable sector is " << mainHeader.lastUsableLBA << "\n"; totalFree = FindFreeBlocks(&i, &temp); cout << "Partitions will be aligned on " << sectorAlignment << "-sector boundaries\n"; cout << "Total free space is " << totalFree << " sectors (" << BytesToIeee(totalFree, blockSize) << ")\n"; cout << "\nNumber Start (sector) End (sector) Size Code Name\n"; for (i = 0; i < numParts; i++) { partitions[i].ShowSummary(i, blockSize); } // for } // GPTData::DisplayGPTData() // Show detailed information on the specified partition void GPTData::ShowPartDetails(uint32_t partNum) { if ((partNum < numParts) && !IsFreePartNum(partNum)) { partitions[partNum].ShowDetails(blockSize); } else { cout << "Partition #" << partNum + 1 << " does not exist.\n"; } // if } // GPTData::ShowPartDetails() /************************************************************************** * * * Partition table transformation functions (MBR or BSD disklabel to GPT) * * (some of these functions may require user interaction) * * * **************************************************************************/ // Examines the MBR & GPT data to determine which set of data to use: the // MBR (use_mbr), the GPT (use_gpt), the BSD disklabel (use_bsd), or create // a new set of partitions (use_new). A return value of use_abort indicates // that this function couldn't determine what to do. Overriding functions // in derived classes may ask users questions in such cases. WhichToUse GPTData::UseWhichPartitions(void) { WhichToUse which = use_new; MBRValidity mbrState; mbrState = protectiveMBR.GetValidity(); if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) { cout << "\n***************************************************************\n" << "Found invalid GPT and valid MBR; converting MBR to GPT format\n" << "in memory. "; if (!justLooking) { cout << "\aTHIS OPERATION IS POTENTIALLY DESTRUCTIVE! Exit by\n" << "typing 'q' if you don't want to convert your MBR partitions\n" << "to GPT format!"; } // if cout << "\n***************************************************************\n\n"; which = use_mbr; } // if if ((state == gpt_invalid) && bsdFound) { cout << "\n**********************************************************************\n" << "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n" << "to GPT format."; if ((!justLooking) && (!beQuiet)) { cout << "\a THIS OPERATION IS POTENTIALLY DESTRUCTIVE! Your first\n" << "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n" << "want to convert your BSD partitions to GPT format!"; } // if cout << "\n**********************************************************************\n\n"; which = use_bsd; } // if if ((state == gpt_valid) && (mbrState == gpt)) { which = use_gpt; if (!beQuiet) cout << "Found valid GPT with protective MBR; using GPT.\n"; } // if if ((state == gpt_valid) && (mbrState == hybrid)) { which = use_gpt; if (!beQuiet) cout << "Found valid GPT with hybrid MBR; using GPT.\n"; } // if if ((state == gpt_valid) && (mbrState == invalid)) { cout << "\aFound valid GPT with corrupt MBR; using GPT and will write new\n" << "protective MBR on save.\n"; which = use_gpt; } // if if ((state == gpt_valid) && (mbrState == mbr)) { which = use_abort; } // if if (state == gpt_corrupt) { if (mbrState == gpt) { cout << "\a\a****************************************************************************\n" << "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n" << "verification and recovery are STRONGLY recommended.\n" << "****************************************************************************\n"; which = use_gpt; } else { which = use_abort; } // if/else MBR says disk is GPT } // if GPT corrupt if (which == use_new) cout << "Creating new GPT entries in memory.\n"; return which; } // UseWhichPartitions() // Convert MBR partition table into GPT form. void GPTData::XFormPartitions(void) { int i, numToConvert; uint8_t origType; // Clear out old data & prepare basics.... ClearGPTData(); // Convert the smaller of the # of GPT or MBR partitions if (numParts > MAX_MBR_PARTS) numToConvert = MAX_MBR_PARTS; else numToConvert = numParts; for (i = 0; i < numToConvert; i++) { origType = protectiveMBR.GetType(i); // don't waste CPU time trying to convert extended, hybrid protective, or // null (non-existent) partitions if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) && (origType != 0x00) && (origType != 0xEE)) partitions[i] = protectiveMBR.AsGPT(i); } // for // Convert MBR into protective MBR protectiveMBR.MakeProtectiveMBR(); // Record that all original CRCs were OK so as not to raise flags // when doing a disk verification mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1; } // GPTData::XFormPartitions() // Transforms BSD disklabel on the specified partition (numbered from 0). // If an invalid partition number is given, the program does nothing. // Returns the number of new partitions created. int GPTData::XFormDisklabel(uint32_t partNum) { uint32_t low, high; int goOn = 1, numDone = 0; BSDData disklabel; if (GetPartRange(&low, &high) == 0) { goOn = 0; cout << "No partitions!\n"; } // if if (partNum > high) { goOn = 0; cout << "Specified partition is invalid!\n"; } // if // If all is OK, read the disklabel and convert it. if (goOn) { goOn = disklabel.ReadBSDData(&myDisk, partitions[partNum].GetFirstLBA(), partitions[partNum].GetLastLBA()); if ((goOn) && (disklabel.IsDisklabel())) { numDone = XFormDisklabel(&disklabel); if (numDone == 1) cout << "Converted 1 BSD partition.\n"; else cout << "Converted " << numDone << " BSD partitions.\n"; } else { cout << "Unable to convert partitions! Unrecognized BSD disklabel.\n"; } // if/else } // if if (numDone > 0) { // converted partitions; delete carrier partitions[partNum].BlankPartition(); } // if return numDone; } // GPTData::XFormDisklabel(uint32_t i) // Transform the partitions on an already-loaded BSD disklabel... int GPTData::XFormDisklabel(BSDData* disklabel) { int i, partNum = 0, numDone = 0; if (disklabel->IsDisklabel()) { for (i = 0; i < disklabel->GetNumParts(); i++) { partNum = FindFirstFreePart(); if (partNum >= 0) { partitions[partNum] = disklabel->AsGPT(i); if (partitions[partNum].IsUsed()) numDone++; } // if } // for if (partNum == -1) cerr << "Warning! Too many partitions to convert!\n"; } // if // Record that all original CRCs were OK so as not to raise flags // when doing a disk verification mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1; return numDone; } // GPTData::XFormDisklabel(BSDData* disklabel) // Add one GPT partition to MBR. Used by PartsToMBR() functions. Created // partition has the active/bootable flag UNset and uses the GPT fdisk // type code divided by 0x0100 as the MBR type code. // Returns 1 if operation was 100% successful, 0 if there were ANY // problems. int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) { int allOK = 1; if ((mbrPart < 0) || (mbrPart > 3)) { cout << "MBR partition " << mbrPart + 1 << " is out of range; omitting it.\n"; allOK = 0; } // if if (gptPart >= numParts) { cout << "GPT partition " << gptPart + 1 << " is out of range; omitting it.\n"; allOK = 0; } // if if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) { cout << "GPT partition " << gptPart + 1 << " is undefined; omitting it.\n"; allOK = 0; } // if if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) && (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) { if (partitions[gptPart].GetLastLBA() > UINT32_MAX) { cout << "Caution: Partition end point past 32-bit pointer boundary;" << " some OSes may\nreact strangely.\n"; } // if protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(), (uint32_t) partitions[gptPart].GetLengthLBA(), partitions[gptPart].GetHexType() / 256, 0); } else { // partition out of range if (allOK) // Display only if "else" triggered by out-of-bounds condition cout << "Partition " << gptPart + 1 << " begins beyond the 32-bit pointer limit of MBR " << "partitions, or is\n too big; omitting it.\n"; allOK = 0; } // if/else return allOK; } // GPTData::OnePartToMBR() /********************************************************************** * * * Functions that adjust GPT data structures WITHOUT user interaction * * (they may display information for the user's benefit, though) * * * **********************************************************************/ // Resizes GPT to specified number of entries. Creates a new table if // necessary, copies data if it already exists. If fillGPTSectors is 1 // (the default), rounds numEntries to fill all the sectors necessary to // hold the GPT. // Returns 1 if all goes well, 0 if an error is encountered. int GPTData::SetGPTSize(uint32_t numEntries, int fillGPTSectors) { GPTPart* newParts; uint32_t i, high, copyNum, entriesPerSector; int allOK = 1; // First, adjust numEntries upward, if necessary, to get a number // that fills the allocated sectors entriesPerSector = blockSize / GPT_SIZE; if (fillGPTSectors && ((numEntries % entriesPerSector) != 0)) { cout << "Adjusting GPT size from " << numEntries << " to "; numEntries = ((numEntries / entriesPerSector) + 1) * entriesPerSector; cout << numEntries << " to fill the sector\n"; } // if // Do the work only if the # of partitions is changing. Along with being // efficient, this prevents mucking with the location of the secondary // partition table, which causes problems when loading data from a RAID // array that's been expanded because this function is called when loading // data. if (((numEntries != numParts) || (partitions == NULL)) && (numEntries > 0)) { newParts = new GPTPart [numEntries]; if (newParts != NULL) { if (partitions != NULL) { // existing partitions; copy them over GetPartRange(&i, &high); if (numEntries < (high + 1)) { // Highest entry too high for new # cout << "The highest-numbered partition is " << high + 1 << ", which is greater than the requested\n" << "partition table size of " << numEntries << "; cannot resize. Perhaps sorting will help.\n"; allOK = 0; delete[] newParts; } else { // go ahead with copy if (numEntries < numParts) copyNum = numEntries; else copyNum = numParts; for (i = 0; i < copyNum; i++) { newParts[i] = partitions[i]; } // for delete[] partitions; partitions = newParts; } // if } else { // No existing partition table; just create it partitions = newParts; } // if/else existing partitions numParts = numEntries; mainHeader.firstUsableLBA = GetTableSizeInSectors() + mainHeader.partitionEntriesLBA; secondHeader.firstUsableLBA = mainHeader.firstUsableLBA; MoveSecondHeaderToEnd(); if (diskSize > 0) CheckGPTSize(); } else { // Bad memory allocation cerr << "Error allocating memory for partition table! Size is unchanged!\n"; allOK = 0; } // if/else } // if/else mainHeader.numParts = numParts; secondHeader.numParts = numParts; return (allOK); } // GPTData::SetGPTSize() // Change the start sector for the main partition table. // Returns 1 on success, 0 on failure int GPTData::MoveMainTable(uint64_t pteSector) { uint64_t pteSize = GetTableSizeInSectors(); int retval = 1; if ((pteSector >= 2) && ((pteSector + pteSize) <= FindFirstUsedLBA())) { mainHeader.partitionEntriesLBA = pteSector; mainHeader.firstUsableLBA = pteSector + pteSize; RebuildSecondHeader(); } else { cerr << "Unable to set the main partition table's location to " << pteSector << "!\n"; retval = 0; } // if/else return retval; } // GPTData::MoveMainTable() // Blank the partition array void GPTData::BlankPartitions(void) { uint32_t i; for (i = 0; i < numParts; i++) { partitions[i].BlankPartition(); } // for } // GPTData::BlankPartitions() // Delete a partition by number. Returns 1 if successful, // 0 if there was a problem. Returns 1 if partition was in // range, 0 if it was out of range. int GPTData::DeletePartition(uint32_t partNum) { uint64_t startSector, length; uint32_t low, high, numUsedParts, retval = 1;; numUsedParts = GetPartRange(&low, &high); if ((numUsedParts > 0) && (partNum >= low) && (partNum <= high)) { // In case there's a protective MBR, look for & delete matching // MBR partition.... startSector = partitions[partNum].GetFirstLBA(); length = partitions[partNum].GetLengthLBA(); protectiveMBR.DeleteByLocation(startSector, length); // Now delete the GPT partition partitions[partNum].BlankPartition(); } else { cerr << "Partition number " << partNum + 1 << " out of range!\n"; retval = 0; } // if/else return retval; } // GPTData::DeletePartition(uint32_t partNum) // Non-interactively create a partition. // Returns 1 if the operation was successful, 0 if a problem was discovered. uint32_t GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) { int retval = 1; // assume there'll be no problems uint64_t origSector = startSector; if (IsFreePartNum(partNum)) { if (Align(&startSector)) { cout << "Information: Moved requested sector from " << origSector << " to " << startSector << " in\norder to align on " << sectorAlignment << "-sector boundaries.\n"; } // if if (IsFree(startSector) && (startSector <= endSector)) { if (FindLastInFree(startSector) >= endSector) { partitions[partNum].SetFirstLBA(startSector); partitions[partNum].SetLastLBA(endSector); partitions[partNum].SetType(DEFAULT_GPT_TYPE); partitions[partNum].RandomizeUniqueGUID(); } else retval = 0; // if free space until endSector } else retval = 0; // if startSector is free } else retval = 0; // if legal partition number return retval; } // GPTData::CreatePartition(partNum, startSector, endSector) // Sort the GPT entries, eliminating gaps and making for a logical // ordering. void GPTData::SortGPT(void) { if (numParts > 0) sort(partitions, partitions + numParts); } // GPTData::SortGPT() // Swap the contents of two partitions. // Returns 1 if successful, 0 if either partition is out of range // (that is, not a legal number; either or both can be empty). // Note that if partNum1 = partNum2 and this number is in range, // it will be considered successful. int GPTData::SwapPartitions(uint32_t partNum1, uint32_t partNum2) { GPTPart temp; int allOK = 1; if ((partNum1 < numParts) && (partNum2 < numParts)) { if (partNum1 != partNum2) { temp = partitions[partNum1]; partitions[partNum1] = partitions[partNum2]; partitions[partNum2] = temp; } // if } else allOK = 0; // partition numbers are valid return allOK; } // GPTData::SwapPartitions() // Set up data structures for entirely new set of partitions on the // specified device. Returns 1 if OK, 0 if there were problems. // Note that this function does NOT clear the protectiveMBR data // structure, since it may hold the original MBR partitions if the // program was launched on an MBR disk, and those may need to be // converted to GPT format. int GPTData::ClearGPTData(void) { int goOn = 1, i; // Set up the partition table.... delete[] partitions; partitions = NULL; SetGPTSize(NUM_GPT_ENTRIES); // Now initialize a bunch of stuff that's static.... mainHeader.signature = GPT_SIGNATURE; mainHeader.revision = 0x00010000; mainHeader.headerSize = HEADER_SIZE; mainHeader.reserved = 0; mainHeader.currentLBA = UINT64_C(1); mainHeader.partitionEntriesLBA = (uint64_t) 2; mainHeader.sizeOfPartitionEntries = GPT_SIZE; mainHeader.firstUsableLBA = GetTableSizeInSectors() + mainHeader.partitionEntriesLBA; for (i = 0; i < GPT_RESERVED; i++) { mainHeader.reserved2[i] = '\0'; } // for if (blockSize > 0) sectorAlignment = DEFAULT_ALIGNMENT * SECTOR_SIZE / blockSize; else sectorAlignment = DEFAULT_ALIGNMENT; // Now some semi-static items (computed based on end of disk) mainHeader.backupLBA = diskSize - UINT64_C(1); mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA; // Set a unique GUID for the disk, based on random numbers mainHeader.diskGUID.Randomize(); // Copy main header to backup header RebuildSecondHeader(); // Blank out the partitions array.... BlankPartitions(); // Flag all CRCs as being OK.... mainCrcOk = 1; secondCrcOk = 1; mainPartsCrcOk = 1; secondPartsCrcOk = 1; return (goOn); } // GPTData::ClearGPTData() // Set the location of the second GPT header data to the end of the disk. // If the disk size has actually changed, this also adjusts the protective // entry in the MBR, since it's probably no longer correct. // Used internally and called by the 'e' option on the recovery & // transformation menu, to help users of RAID arrays who add disk space // to their arrays or to adjust data structures in restore operations // involving unequal-sized disks. void GPTData::MoveSecondHeaderToEnd() { mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1); if (mainHeader.lastUsableLBA != diskSize - mainHeader.firstUsableLBA) { if (protectiveMBR.GetValidity() == hybrid) { protectiveMBR.OptimizeEESize(); RecomputeCHS(); } // if if (protectiveMBR.GetValidity() == gpt) MakeProtectiveMBR(); } // if mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA; secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1); } // GPTData::FixSecondHeaderLocation() // Sets the partition's name to the specified UnicodeString without // user interaction. // Returns 1 on success, 0 on failure (invalid partition number). int GPTData::SetName(uint32_t partNum, const UnicodeString & theName) { int retval = 1; if (IsUsedPartNum(partNum)) partitions[partNum].SetName(theName); else retval = 0; return retval; } // GPTData::SetName // Set the disk GUID to the specified value. Note that the header CRCs must // be recomputed after calling this function. void GPTData::SetDiskGUID(GUIDData newGUID) { mainHeader.diskGUID = newGUID; secondHeader.diskGUID = newGUID; } // SetDiskGUID() // Set the unique GUID of the specified partition. Returns 1 on // successful completion, 0 if there were problems (invalid // partition number). int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) { int retval = 0; if (pn < numParts) { if (partitions[pn].IsUsed()) { partitions[pn].SetUniqueGUID(theGUID); retval = 1; } // if } // if return retval; } // GPTData::SetPartitionGUID() // Set new random GUIDs for the disk and all partitions. Intended to be used // after disk cloning or similar operations that don't randomize the GUIDs. void GPTData::RandomizeGUIDs(void) { uint32_t i; mainHeader.diskGUID.Randomize(); secondHeader.diskGUID = mainHeader.diskGUID; for (i = 0; i < numParts; i++) if (partitions[i].IsUsed()) partitions[i].RandomizeUniqueGUID(); } // GPTData::RandomizeGUIDs() // Change partition type code non-interactively. Returns 1 if // successful, 0 if not.... int GPTData::ChangePartType(uint32_t partNum, PartType theGUID) { int retval = 1; if (!IsFreePartNum(partNum)) { partitions[partNum].SetType(theGUID); } else retval = 0; return retval; } // GPTData::ChangePartType() // Recompute the CHS values of all the MBR partitions. Used to reset // CHS values that some BIOSes require, despite the fact that the // resulting CHS values violate the GPT standard. void GPTData::RecomputeCHS(void) { int i; for (i = 0; i < 4; i++) protectiveMBR.RecomputeCHS(i); } // GPTData::RecomputeCHS() // Adjust sector number so that it falls on a sector boundary that's a // multiple of sectorAlignment. This is done to improve the performance // of Western Digital Advanced Format disks and disks with similar // technology from other companies, which use 4096-byte sectors // internally although they translate to 512-byte sectors for the // benefit of the OS. If partitions aren't properly aligned on these // disks, some filesystem data structures can span multiple physical // sectors, degrading performance. This function should be called // only on the FIRST sector of the partition, not the last! // This function returns 1 if the alignment was altered, 0 if it // was unchanged. int GPTData::Align(uint64_t* sector) { int retval = 0, sectorOK = 0; uint64_t earlier, later, testSector; if ((*sector % sectorAlignment) != 0) { earlier = (*sector / sectorAlignment) * sectorAlignment; later = earlier + (uint64_t) sectorAlignment; // Check to see that every sector between the earlier one and the // requested one is clear, and that it's not too early.... if (earlier >= mainHeader.firstUsableLBA) { sectorOK = 1; testSector = earlier; do { sectorOK = IsFree(testSector++); } while ((sectorOK == 1) && (testSector < *sector)); if (sectorOK == 1) { *sector = earlier; retval = 1; } // if } // if firstUsableLBA check // If couldn't move the sector earlier, try to move it later instead.... if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) { sectorOK = 1; testSector = later; do { sectorOK = IsFree(testSector--); } while ((sectorOK == 1) && (testSector > *sector)); if (sectorOK == 1) { *sector = later; retval = 1; } // if } // if } // if return retval; } // GPTData::Align() /******************************************************** * * * Functions that return data about GPT data structures * * (most of these are inline in gpt.h) * * * ********************************************************/ // Find the low and high used partition numbers (numbered from 0). // Return value is the number of partitions found. Note that the // *low and *high values are both set to 0 when no partitions // are found, as well as when a single partition in the first // position exists. Thus, the return value is the only way to // tell when no partitions exist. int GPTData::GetPartRange(uint32_t *low, uint32_t *high) { uint32_t i; int numFound = 0; *low = numParts + 1; // code for "not found" *high = 0; for (i = 0; i < numParts; i++) { if (partitions[i].IsUsed()) { // it exists *high = i; // since we're counting up, set the high value // Set the low value only if it's not yet found... if (*low == (numParts + 1)) *low = i; numFound++; } // if } // for // Above will leave *low pointing to its "not found" value if no partitions // are defined, so reset to 0 if this is the case.... if (*low == (numParts + 1)) *low = 0; return numFound; } // GPTData::GetPartRange() // Returns the value of the first free partition, or -1 if none is // unused. int GPTData::FindFirstFreePart(void) { int i = 0; if (partitions != NULL) { while ((i < (int) numParts) && (partitions[i].IsUsed())) i++; if (i >= (int) numParts) i = -1; } else i = -1; return i; } // GPTData::FindFirstFreePart() // Returns the number of defined partitions. uint32_t GPTData::CountParts(void) { uint32_t i, counted = 0; for (i = 0; i < numParts; i++) { if (partitions[i].IsUsed()) counted++; } // for return counted; } // GPTData::CountParts() /**************************************************** * * * Functions that return data about disk free space * * * ****************************************************/ // Find the first available block after the starting point; returns 0 if // there are no available blocks left uint64_t GPTData::FindFirstAvailable(uint64_t start) { uint64_t first; uint32_t i; int firstMoved = 0; // Begin from the specified starting point or from the first usable // LBA, whichever is greater... if (start < mainHeader.firstUsableLBA) first = mainHeader.firstUsableLBA; else first = start; // ...now search through all partitions; if first is within an // existing partition, move it to the next sector after that // partition and repeat. If first was moved, set firstMoved // flag; repeat until firstMoved is not set, so as to catch // cases where partitions are out of sequential order.... do { firstMoved = 0; for (i = 0; i < numParts; i++) { if ((partitions[i].IsUsed()) && (first >= partitions[i].GetFirstLBA()) && (first <= partitions[i].GetLastLBA())) { // in existing part. first = partitions[i].GetLastLBA() + 1; firstMoved = 1; } // if } // for } while (firstMoved == 1); if (first > mainHeader.lastUsableLBA) first = 0; return (first); } // GPTData::FindFirstAvailable() // Returns the LBA of the start of the first partition on the disk (by // sector number), or 0 if there are no partitions defined. uint64_t GPTData::FindFirstUsedLBA(void) { uint32_t i; uint64_t firstFound = UINT64_MAX; for (i = 0; i < numParts; i++) { if ((partitions[i].IsUsed()) && (partitions[i].GetFirstLBA() < firstFound)) { firstFound = partitions[i].GetFirstLBA(); } // if } // for return firstFound; } // GPTData::FindFirstUsedLBA() // Finds the first available sector in the largest block of unallocated // space on the disk. Returns 0 if there are no available blocks left uint64_t GPTData::FindFirstInLargest(void) { uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0; start = 0; do { firstBlock = FindFirstAvailable(start); if (firstBlock != UINT32_C(0)) { // something's free... lastBlock = FindLastInFree(firstBlock); segmentSize = lastBlock - firstBlock + UINT32_C(1); if (segmentSize > selectedSize) { selectedSize = segmentSize; selectedSegment = firstBlock; } // if start = lastBlock + 1; } // if } while (firstBlock != 0); return selectedSegment; } // GPTData::FindFirstInLargest() // Find the last available block on the disk. // Returns 0 if there are no available sectors uint64_t GPTData::FindLastAvailable(void) { uint64_t last; uint32_t i; int lastMoved = 0; // Start by assuming the last usable LBA is available.... last = mainHeader.lastUsableLBA; // ...now, similar to algorithm in FindFirstAvailable(), search // through all partitions, moving last when it's in an existing // partition. Set the lastMoved flag so we repeat to catch cases // where partitions are out of logical order. do { lastMoved = 0; for (i = 0; i < numParts; i++) { if ((last >= partitions[i].GetFirstLBA()) && (last <= partitions[i].GetLastLBA())) { // in existing part. last = partitions[i].GetFirstLBA() - 1; lastMoved = 1; } // if } // for } while (lastMoved == 1); if (last < mainHeader.firstUsableLBA) last = 0; return (last); } // GPTData::FindLastAvailable() // Find the last available block in the free space pointed to by start. uint64_t GPTData::FindLastInFree(uint64_t start) { uint64_t nearestStart; uint32_t i; nearestStart = mainHeader.lastUsableLBA; for (i = 0; i < numParts; i++) { if ((nearestStart > partitions[i].GetFirstLBA()) && (partitions[i].GetFirstLBA() > start)) { nearestStart = partitions[i].GetFirstLBA() - 1; } // if } // for return (nearestStart); } // GPTData::FindLastInFree() // Finds the total number of free blocks, the number of segments in which // they reside, and the size of the largest of those segments uint64_t GPTData::FindFreeBlocks(uint32_t *numSegments, uint64_t *largestSegment) { uint64_t start = UINT64_C(0); // starting point for each search uint64_t totalFound = UINT64_C(0); // running total uint64_t firstBlock; // first block in a segment uint64_t lastBlock; // last block in a segment uint64_t segmentSize; // size of segment in blocks uint32_t num = 0; *largestSegment = UINT64_C(0); if (diskSize > 0) { do { firstBlock = FindFirstAvailable(start); if (firstBlock != UINT64_C(0)) { // something's free... lastBlock = FindLastInFree(firstBlock); segmentSize = lastBlock - firstBlock + UINT64_C(1); if (segmentSize > *largestSegment) { *largestSegment = segmentSize; } // if totalFound += segmentSize; num++; start = lastBlock + 1; } // if } while (firstBlock != 0); } // if *numSegments = num; return totalFound; } // GPTData::FindFreeBlocks() // Returns 1 if sector is unallocated, 0 if it's allocated to a partition. // If it's allocated, return the partition number to which it's allocated // in partNum, if that variable is non-NULL. (A value of UINT32_MAX is // returned in partNum if the sector is in use by basic GPT data structures.) int GPTData::IsFree(uint64_t sector, uint32_t *partNum) { int isFree = 1; uint32_t i; for (i = 0; i < numParts; i++) { if ((sector >= partitions[i].GetFirstLBA()) && (sector <= partitions[i].GetLastLBA())) { isFree = 0; if (partNum != NULL) *partNum = i; } // if } // for if ((sector < mainHeader.firstUsableLBA) || (sector > mainHeader.lastUsableLBA)) { isFree = 0; if (partNum != NULL) *partNum = UINT32_MAX; } // if return (isFree); } // GPTData::IsFree() // Returns 1 if partNum is unused AND if it's a legal value. int GPTData::IsFreePartNum(uint32_t partNum) { return ((partNum < numParts) && (partitions != NULL) && (!partitions[partNum].IsUsed())); } // GPTData::IsFreePartNum() // Returns 1 if partNum is in use. int GPTData::IsUsedPartNum(uint32_t partNum) { return ((partNum < numParts) && (partitions != NULL) && (partitions[partNum].IsUsed())); } // GPTData::IsUsedPartNum() /*********************************************************** * * * Change how functions work or return information on them * * * ***********************************************************/ // Set partition alignment value; partitions will begin on multiples of // the specified value void GPTData::SetAlignment(uint32_t n) { if (n > 0) { sectorAlignment = n; if ((physBlockSize > 0) && (n % (physBlockSize / blockSize) != 0)) { cout << "Warning: Setting alignment to a value that does not match the disk's\n" << "physical block size! Performance degradation may result!\n" << "Physical block size = " << physBlockSize << "\n" << "Logical block size = " << blockSize << "\n" << "Optimal alignment = " << physBlockSize / blockSize << " or multiples thereof.\n"; } // if } else { cerr << "Attempt to set partition alignment to 0!\n"; } // if/else } // GPTData::SetAlignment() // Compute sector alignment based on the current partitions (if any). Each // partition's starting LBA is examined, and if it's divisible by a power-of-2 // value less than or equal to the DEFAULT_ALIGNMENT value (adjusted for the // sector size), but not by the previously-located alignment value, then the // alignment value is adjusted down. If the computed alignment is less than 8 // and the disk is bigger than SMALLEST_ADVANCED_FORMAT, resets it to 8. This // is a safety measure for Advanced Format drives. If no partitions are // defined, the alignment value is set to DEFAULT_ALIGNMENT (2048) (or an // adjustment of that based on the current sector size). The result is that new // drives are aligned to 2048-sector multiples but the program won't complain // about other alignments on existing disks unless a smaller-than-8 alignment // is used on big disks (as safety for Advanced Format drives). // Returns the computed alignment value. uint32_t GPTData::ComputeAlignment(void) { uint32_t i = 0, found, exponent = 31; uint32_t align = DEFAULT_ALIGNMENT; if (blockSize > 0) align = DEFAULT_ALIGNMENT * SECTOR_SIZE / blockSize; exponent = (uint32_t) log2(align); for (i = 0; i < numParts; i++) { if (partitions[i].IsUsed()) { found = 0; while (!found) { align = UINT64_C(1) << exponent; if ((partitions[i].GetFirstLBA() % align) == 0) { found = 1; } else { exponent--; } // if/else } // while } // if } // for if ((align < MIN_AF_ALIGNMENT) && (diskSize >= SMALLEST_ADVANCED_FORMAT)) align = MIN_AF_ALIGNMENT; sectorAlignment = align; return align; } // GPTData::ComputeAlignment() /******************************** * * * Endianness support functions * * * ********************************/ void GPTData::ReverseHeaderBytes(struct GPTHeader* header) { ReverseBytes(&header->signature, 8); ReverseBytes(&header->revision, 4); ReverseBytes(&header->headerSize, 4); ReverseBytes(&header->headerCRC, 4); ReverseBytes(&header->reserved, 4); ReverseBytes(&header->currentLBA, 8); ReverseBytes(&header->backupLBA, 8); ReverseBytes(&header->firstUsableLBA, 8); ReverseBytes(&header->lastUsableLBA, 8); ReverseBytes(&header->partitionEntriesLBA, 8); ReverseBytes(&header->numParts, 4); ReverseBytes(&header->sizeOfPartitionEntries, 4); ReverseBytes(&header->partitionEntriesCRC, 4); ReverseBytes(header->reserved2, GPT_RESERVED); } // GPTData::ReverseHeaderBytes() // Reverse byte order for all partitions. void GPTData::ReversePartitionBytes() { uint32_t i; for (i = 0; i < numParts; i++) { partitions[i].ReversePartBytes(); } // for } // GPTData::ReversePartitionBytes() // Validate partition number bool GPTData::ValidPartNum (const uint32_t partNum) { if (partNum >= numParts) { cerr << "Partition number out of range: " << partNum << "\n"; return false; } // if return true; } // GPTData::ValidPartNum // Return a single partition for inspection (not modification!) by other // functions. const GPTPart & GPTData::operator[](uint32_t partNum) const { if (partNum >= numParts) { cerr << "Partition number out of range (" << partNum << " requested, but only " << numParts << " available)\n"; exit(1); } // if if (partitions == NULL) { cerr << "No partitions defined in GPTData::operator[]; fatal error!\n"; exit(1); } // if return partitions[partNum]; } // operator[] // Return (not for modification!) the disk's GUID value const GUIDData & GPTData::GetDiskGUID(void) const { return mainHeader.diskGUID; } // GPTData::GetDiskGUID() // Manage attributes for a partition, based on commands passed to this function. // (Function is non-interactive.) // Returns 1 if a modification command succeeded, 0 if the command should not have // modified data, and -1 if a modification command failed. int GPTData::ManageAttributes(int partNum, const string & command, const string & bits) { int retval = 0; Attributes theAttr; if (partNum >= (int) numParts) { cerr << "Invalid partition number (" << partNum + 1 << ")\n"; retval = -1; } else { if (command == "show") { ShowAttributes(partNum); } else if (command == "get") { GetAttribute(partNum, bits); } else { theAttr = partitions[partNum].GetAttributes(); if (theAttr.OperateOnAttributes(partNum, command, bits)) { partitions[partNum].SetAttributes(theAttr.GetAttributes()); retval = 1; } else { retval = -1; } // if/else } // if/elseif/else } // if/else invalid partition # return retval; } // GPTData::ManageAttributes() // Show all attributes for a specified partition.... void GPTData::ShowAttributes(const uint32_t partNum) { if ((partNum < numParts) && partitions[partNum].IsUsed()) partitions[partNum].ShowAttributes(partNum); } // GPTData::ShowAttributes // Show whether a single attribute bit is set (terse output)... void GPTData::GetAttribute(const uint32_t partNum, const string& attributeBits) { if (partNum < numParts) partitions[partNum].GetAttributes().OperateOnAttributes(partNum, "get", attributeBits); } // GPTData::GetAttribute /****************************************** * * * Additional non-class support functions * * * ******************************************/ // Check to be sure that data type sizes are correct. The basic types (uint*_t) should // never fail these tests, but the struct types may fail depending on compile options. // Specifically, the -fpack-struct option to gcc may be required to ensure proper structure // sizes. int SizesOK(void) { int allOK = 1; if (sizeof(uint8_t) != 1) { cerr << "uint8_t is " << sizeof(uint8_t) << " bytes, should be 1 byte; aborting!\n"; allOK = 0; } // if if (sizeof(uint16_t) != 2) { cerr << "uint16_t is " << sizeof(uint16_t) << " bytes, should be 2 bytes; aborting!\n"; allOK = 0; } // if if (sizeof(uint32_t) != 4) { cerr << "uint32_t is " << sizeof(uint32_t) << " bytes, should be 4 bytes; aborting!\n"; allOK = 0; } // if if (sizeof(uint64_t) != 8) { cerr << "uint64_t is " << sizeof(uint64_t) << " bytes, should be 8 bytes; aborting!\n"; allOK = 0; } // if if (sizeof(struct MBRRecord) != 16) { cerr << "MBRRecord is " << sizeof(MBRRecord) << " bytes, should be 16 bytes; aborting!\n"; allOK = 0; } // if if (sizeof(struct TempMBR) != 512) { cerr << "TempMBR is " << sizeof(TempMBR) << " bytes, should be 512 bytes; aborting!\n"; allOK = 0; } // if if (sizeof(struct GPTHeader) != 512) { cerr << "GPTHeader is " << sizeof(GPTHeader) << " bytes, should be 512 bytes; aborting!\n"; allOK = 0; } // if if (sizeof(GPTPart) != 128) { cerr << "GPTPart is " << sizeof(GPTPart) << " bytes, should be 128 bytes; aborting!\n"; allOK = 0; } // if if (sizeof(GUIDData) != 16) { cerr << "GUIDData is " << sizeof(GUIDData) << " bytes, should be 16 bytes; aborting!\n"; allOK = 0; } // if if (sizeof(PartType) != 16) { cerr << "PartType is " << sizeof(PartType) << " bytes, should be 16 bytes; aborting!\n"; allOK = 0; } // if return (allOK); } // SizesOK() gptfdisk-1.0.5/gpttext.cc0000664000175000017500000010670313622612343015615 0ustar rodsmithrodsmith/* Copyright (C) 2010-2018 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* This class implements an interactive text-mode interface atop the GPTData class */ #include #include #include #include #include #include #include #include #include "attributes.h" #include "gpttext.h" #include "support.h" using namespace std; /******************************************** * * * GPTDataText class and related structures * * * ********************************************/ GPTDataTextUI::GPTDataTextUI(void) : GPTData() { } // default constructor GPTDataTextUI::GPTDataTextUI(string filename) : GPTData(filename) { } // constructor passing filename GPTDataTextUI::~GPTDataTextUI(void) { } // default destructor /********************************************************************* * * * The following functions are extended (interactive) versions of * * simpler functions in the base class.... * * * *********************************************************************/ // Overridden function; calls base-class function and then makes // additional queries of the user, if the base-class function can't // decide what to do. WhichToUse GPTDataTextUI::UseWhichPartitions(void) { WhichToUse which; MBRValidity mbrState; int answer; which = GPTData::UseWhichPartitions(); if ((which != use_abort) || beQuiet) return which; // If we get past here, it means that the non-interactive tests were // inconclusive, so we must ask the user which table to use.... mbrState = protectiveMBR.GetValidity(); if ((state == gpt_valid) && (mbrState == mbr)) { cout << "Found valid MBR and GPT. Which do you want to use?\n"; answer = GetNumber(1, 3, 2, " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: "); if (answer == 1) { which = use_mbr; } else if (answer == 2) { which = use_gpt; cout << "Using GPT and creating fresh protective MBR.\n"; } else which = use_new; } // if // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other // problems) if (state == gpt_corrupt) { if ((mbrState == mbr) || (mbrState == hybrid)) { cout << "Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n" << "GPT MAY permit recovery of GPT data.)\n"; answer = GetNumber(1, 3, 2, " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: "); if (answer == 1) { which = use_mbr; } else if (answer == 2) { which = use_gpt; } else which = use_new; } else if (mbrState == invalid) { cout << "Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n" << "GPT MAY permit recovery of GPT data.)\n"; answer = GetNumber(1, 2, 1, " 1 - Use current GPT\n 2 - Create blank GPT\n\nYour answer: "); if (answer == 1) { which = use_gpt; } else which = use_new; } // if/else/else } // if (corrupt GPT) return which; } // UseWhichPartitions() // Ask the user for a partition number; and prompt for verification // if the requested partition isn't of a known BSD type. // Lets the base-class function do the work, and returns its value (the // number of converted partitions). int GPTDataTextUI::XFormDisklabel(void) { uint32_t partNum; uint16_t hexCode; int goOn = 1, numDone = 0; BSDData disklabel; partNum = GetPartNum(); // Now see if the specified partition has a BSD type code.... hexCode = partitions[partNum].GetHexType(); if ((hexCode != 0xa500) && (hexCode != 0xa900)) { cout << "Specified partition doesn't have a disklabel partition type " << "code.\nContinue anyway? "; goOn = (GetYN() == 'Y'); } // if if (goOn) numDone = GPTData::XFormDisklabel(partNum); return numDone; } // GPTDataTextUI::XFormDisklabel(void) /********************************************************************* * * * Begin functions that obtain information from the users, and often * * do something with that information (call other functions) * * * *********************************************************************/ // Prompts user for partition number and returns the result. Returns "0" // (the first partition) if none are currently defined. uint32_t GPTDataTextUI::GetPartNum(void) { uint32_t partNum; uint32_t low, high; ostringstream prompt; if (GetPartRange(&low, &high) > 0) { prompt << "Partition number (" << low + 1 << "-" << high + 1 << "): "; partNum = GetNumber(low + 1, high + 1, low, prompt.str()); } else partNum = 1; return (partNum - 1); } // GPTDataTextUI::GetPartNum() // What it says: Resize the partition table. (Default is 128 entries.) void GPTDataTextUI::ResizePartitionTable(void) { int newSize; ostringstream prompt; uint32_t curLow, curHigh; cout << "Current partition table size is " << numParts << ".\n"; GetPartRange(&curLow, &curHigh); curHigh++; // since GetPartRange() returns numbers starting from 0... // There's no point in having fewer than four partitions.... if (curHigh < (blockSize / GPT_SIZE)) curHigh = blockSize / GPT_SIZE; prompt << "Enter new size (" << curHigh << " up, default " << NUM_GPT_ENTRIES << "): "; newSize = GetNumber(4, 65535, 128, prompt.str()); if (newSize < 128) { cout << "Caution: The partition table size should officially be 16KB or larger,\n" << "which works out to 128 entries. In practice, smaller tables seem to\n" << "work with most OSes, but this practice is risky. I'm proceeding with\n" << "the resize, but you may want to reconsider this action and undo it.\n\n"; } // if SetGPTSize(newSize); } // GPTDataTextUI::ResizePartitionTable() // Move the main partition table (to enable some SoC boot loaders to place // code at sector 2, for instance). void GPTDataTextUI::MoveMainTable(void) { uint64_t newStart, pteSize = GetTableSizeInSectors(); uint64_t maxValue = FindFirstUsedLBA() - pteSize; ostringstream prompt; cout << "Currently, main partition table begins at sector " << mainHeader.partitionEntriesLBA << " and ends at sector " << mainHeader.partitionEntriesLBA + pteSize - 1 << "\n"; prompt << "Enter new starting location (2 to " << maxValue << "; default is 2; 1 to abort): "; newStart = GetNumber(1, maxValue, 2, prompt.str()); if (newStart != 1) { GPTData::MoveMainTable(newStart); } else { cout << "Aborting change!\n"; } // if } // GPTDataTextUI::MoveMainTable() // Interactively create a partition void GPTDataTextUI::CreatePartition(void) { uint64_t firstBlock, firstInLargest, lastBlock, sector, origSector; uint32_t firstFreePart = 0; ostringstream prompt1, prompt2, prompt3; int partNum; // Find first free partition... while (partitions[firstFreePart].GetFirstLBA() != 0) { firstFreePart++; } // while if (((firstBlock = FindFirstAvailable()) != 0) && (firstFreePart < numParts)) { lastBlock = FindLastAvailable(); firstInLargest = FindFirstInLargest(); Align(&firstInLargest); // Get partition number.... prompt1 << "Partition number (" << firstFreePart + 1 << "-" << numParts << ", default " << firstFreePart + 1 << "): "; do { partNum = GetNumber(firstFreePart + 1, numParts, firstFreePart + 1, prompt1.str()) - 1; if (partitions[partNum].GetFirstLBA() != 0) cout << "partition " << partNum + 1 << " is in use.\n"; } while (partitions[partNum].GetFirstLBA() != 0); // Get first block for new partition... prompt2 << "First sector (" << firstBlock << "-" << lastBlock << ", default = " << firstInLargest << ") or {+-}size{KMGTP}: "; do { sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, blockSize, prompt2.str()); } while (IsFree(sector) == 0); origSector = sector; if (Align(§or)) { cout << "Information: Moved requested sector from " << origSector << " to " << sector << " in\norder to align on " << sectorAlignment << "-sector boundaries.\n"; if (!beQuiet) cout << "Use 'l' on the experts' menu to adjust alignment\n"; } // if // Align(§or); // Align sector to correct multiple firstBlock = sector; // Get last block for new partitions... lastBlock = FindLastInFree(firstBlock); prompt3 << "Last sector (" << firstBlock << "-" << lastBlock << ", default = " << lastBlock << ") or {+-}size{KMGTP}: "; do { sector = GetSectorNum(firstBlock, lastBlock, lastBlock, blockSize, prompt3.str()); } while (IsFree(sector) == 0); lastBlock = sector; firstFreePart = GPTData::CreatePartition(partNum, firstBlock, lastBlock); partitions[partNum].ChangeType(); partitions[partNum].SetDefaultDescription(); } else { if (firstFreePart >= numParts) cout << "No table partition entries left\n"; else cout << "No free sectors available\n"; } // if/else } // GPTDataTextUI::CreatePartition() // Interactively delete a partition (duh!) void GPTDataTextUI::DeletePartition(void) { int partNum; uint32_t low, high; ostringstream prompt; if (GetPartRange(&low, &high) > 0) { prompt << "Partition number (" << low + 1 << "-" << high + 1 << "): "; partNum = GetNumber(low + 1, high + 1, low, prompt.str()); GPTData::DeletePartition(partNum - 1); } else { cout << "No partitions\n"; } // if/else } // GPTDataTextUI::DeletePartition() // Prompt user for a partition number, then change its type code void GPTDataTextUI::ChangePartType(void) { int partNum; uint32_t low, high; if (GetPartRange(&low, &high) > 0) { partNum = GetPartNum(); partitions[partNum].ChangeType(); } else { cout << "No partitions\n"; } // if/else } // GPTDataTextUI::ChangePartType() // Prompt user for a partition number, then change its unique // GUID. void GPTDataTextUI::ChangeUniqueGuid(void) { int partNum; uint32_t low, high; string guidStr; if (GetPartRange(&low, &high) > 0) { partNum = GetPartNum(); cout << "Enter the partition's new unique GUID ('R' to randomize): "; guidStr = ReadString(); if ((guidStr.length() >= 32) || (guidStr[0] == 'R') || (guidStr[0] == 'r')) { SetPartitionGUID(partNum, (GUIDData) guidStr); cout << "New GUID is " << partitions[partNum].GetUniqueGUID() << "\n"; } else { cout << "GUID is too short!\n"; } // if/else } else cout << "No partitions\n"; } // GPTDataTextUI::ChangeUniqueGuid() // Partition attributes seem to be rarely used, but I want a way to // adjust them for completeness.... void GPTDataTextUI::SetAttributes(uint32_t partNum) { partitions[partNum].SetAttributes(); } // GPTDataTextUI::SetAttributes() // Prompts the user for a partition name and sets the partition's // name. Returns 1 on success, 0 on failure (invalid partition // number). (Note that the function skips prompting when an // invalid partition number is detected.) int GPTDataTextUI::SetName(uint32_t partNum) { UnicodeString theName = ""; int retval = 1; if (IsUsedPartNum(partNum)) { cout << "Enter name: "; #ifdef USE_UTF16 theName = ReadUString(); #else theName = ReadString(); #endif partitions[partNum].SetName(theName); } else { cerr << "Invalid partition number (" << partNum << ")\n"; retval = 0; } // if/else return retval; } // GPTDataTextUI::SetName() // Ask user for two partition numbers and swap them in the table. Note that // this just reorders table entries; it doesn't adjust partition layout on // the disk. // Returns 1 if successful, 0 if not. (If user enters identical numbers, it // counts as successful.) int GPTDataTextUI::SwapPartitions(void) { int partNum1, partNum2, didIt = 0; uint32_t low, high; ostringstream prompt; GPTPart temp; if (GetPartRange(&low, &high) > 0) { partNum1 = GetPartNum(); if (high >= numParts - 1) high = 0; prompt << "New partition number (1-" << numParts << ", default " << high + 2 << "): "; partNum2 = GetNumber(1, numParts, high + 2, prompt.str()) - 1; didIt = GPTData::SwapPartitions(partNum1, partNum2); } else { cout << "No partitions\n"; } // if/else return didIt; } // GPTDataTextUI::SwapPartitionNumbers() // This function destroys the on-disk GPT structures. Returns 1 if the user // confirms destruction, 0 if the user aborts or if there's a disk error. int GPTDataTextUI::DestroyGPTwPrompt(void) { int allOK = 1; if ((apmFound) || (bsdFound)) { cout << "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 cout << "\a\aAbout to wipe out GPT on " << device << ". Proceed? "; if (GetYN() == 'Y') { if (DestroyGPT()) { // 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). cout << "Blank out MBR? "; if (GetYN() == 'Y') { DestroyMBR(); } else { cout << "MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n" << "with fdisk or another tool.\n"; } // if/else } else allOK = 0; // if GPT structures destroyed } else allOK = 0; // if user confirms destruction return (allOK); } // GPTDataTextUI::DestroyGPTwPrompt() // Get partition number from user and then call ShowPartDetails(partNum) // to show its detailed information void GPTDataTextUI::ShowDetails(void) { int partNum; uint32_t low, high; if (GetPartRange(&low, &high) > 0) { partNum = GetPartNum(); ShowPartDetails(partNum); } else { cout << "No partitions\n"; } // if/else } // GPTDataTextUI::ShowDetails() // Create a hybrid MBR -- an ugly, funky thing that helps GPT work with // OSes that don't understand GPT. void GPTDataTextUI::MakeHybrid(void) { uint32_t partNums[3] = {0, 0, 0}; string line; int numPartsToCvt = 0, numConverted = 0, i, j, mbrNum = 0; unsigned int hexCode = 0; MBRPart hybridPart; MBRData hybridMBR; char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table cout << "\nWARNING! Hybrid MBRs are flaky and dangerous! If you decide not to use one,\n" << "just hit the Enter key at the below prompt and your MBR partition table will\n" << "be untouched.\n\n\a"; // Use a local MBR structure, copying from protectiveMBR to keep its // boot loader code intact.... hybridMBR = protectiveMBR; hybridMBR.EmptyMBR(0); // Now get the numbers of up to three partitions to add to the // hybrid MBR.... cout << "Type from one to three GPT partition numbers, separated by spaces, to be\n" << "added to the hybrid MBR, in sequence: "; line = ReadString(); istringstream inLine(line); do { inLine >> partNums[numPartsToCvt]; if (partNums[numPartsToCvt] > 0) numPartsToCvt++; } while (!inLine.eof() && (numPartsToCvt < 3)); if (numPartsToCvt > 0) { cout << "Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? "; eeFirst = GetYN(); } // if for (i = 0; i < numPartsToCvt; i++) { j = partNums[i] - 1; if (partitions[j].IsUsed() && (partitions[j].IsSizedForMBR() != MBR_SIZED_BAD)) { mbrNum = i + (eeFirst == 'Y'); cout << "\nCreating entry for GPT partition #" << j + 1 << " (MBR partition #" << mbrNum + 1 << ")\n"; hybridPart.SetType(GetMBRTypeCode(partitions[j].GetHexType() / 256)); hybridPart.SetLocation(partitions[j].GetFirstLBA(), partitions[j].GetLengthLBA()); hybridPart.SetInclusion(PRIMARY); cout << "Set the bootable flag? "; if (GetYN() == 'Y') hybridPart.SetStatus(0x80); else hybridPart.SetStatus(0x00); hybridPart.SetInclusion(PRIMARY); if (partitions[j].IsSizedForMBR() == MBR_SIZED_IFFY) WarnAboutIffyMBRPart(j + 1); numConverted++; } else { cerr << "\nGPT partition #" << j + 1 << " does not exist or is too big; skipping.\n"; } // if/else hybridMBR.AddPart(mbrNum, hybridPart); } // for if (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. hybridPart.SetLocation(1, hybridMBR.FindLastInFree(1)); hybridPart.SetStatus(0); hybridPart.SetType(0xEE); hybridPart.SetInclusion(PRIMARY); // newNote firstLBA and lastLBA are computed later... if (eeFirst == 'Y') { hybridMBR.AddPart(0, hybridPart); } else { hybridMBR.AddPart(numConverted, hybridPart); } // else hybridMBR.SetHybrid(); // ... and for good measure, if there are any partition spaces left, // optionally create another protective EFI partition to cover as much // space as possible.... if (hybridMBR.CountParts() < 4) { // unused entry.... cout << "\nUnused partition space(s) found. Use one to protect more partitions? "; if (GetYN() == 'Y') { cout << "Note: Default is 0xEE, but this may confuse Mac OS X.\n"; // Comment on above: Mac OS treats disks with more than one // 0xEE MBR partition as MBR disks, not as GPT disks. hexCode = GetMBRTypeCode(0xEE); hybridMBR.MakeBiggestPart(3, hexCode); } // if (GetYN() == 'Y') } // if unused entry protectiveMBR = hybridMBR; } else { cout << "\nNo partitions converted; original protective/hybrid MBR is unmodified!\n"; } // if/else (numConverted > 0) } // GPTDataTextUI::MakeHybrid() // Convert the GPT to MBR form, storing partitions in the protectiveMBR // variable. This function is necessarily limited; it may not be able to // convert all partitions, depending on the disk size and available space // before each partition (one free sector is required to create a logical // partition, which are necessary to convert more than four partitions). // Returns the number of converted partitions; if this value // is over 0, the calling function should call DestroyGPT() to destroy // the GPT data, call SaveMBR() to save the MBR, and then exit. int GPTDataTextUI::XFormToMBR(void) { uint32_t i; protectiveMBR.EmptyMBR(0); for (i = 0; i < numParts; i++) { if (partitions[i].IsUsed()) { if (partitions[i].IsSizedForMBR() == MBR_SIZED_IFFY) WarnAboutIffyMBRPart(i + 1); // Note: MakePart() checks for oversized partitions, so don't // bother checking other IsSizedForMBR() return values.... protectiveMBR.MakePart(i, partitions[i].GetFirstLBA(), partitions[i].GetLengthLBA(), partitions[i].GetHexType() / 0x0100, 0); } // if } // for protectiveMBR.MakeItLegal(); return protectiveMBR.DoMenu(); } // GPTDataTextUI::XFormToMBR() /****************************************************** * * * Display informational messages for the user.... * * * ******************************************************/ // Although an MBR partition that begins below sector 2^32 and is less than 2^32 sectors in // length is technically legal even if it ends above the 2^32-sector mark, such a partition // tends to confuse a lot of OSes, so warn the user about such partitions. This function is // called by XFormToMBR() and MakeHybrid(); it's a separate function just to consolidate the // lengthy message in one place. void GPTDataTextUI::WarnAboutIffyMBRPart(int partNum) { cout << "\a\nWarning! GPT partition " << partNum << " ends after the 2^32 sector mark! The partition\n" << "begins before this point, and is smaller than 2^32 sectors. This is technically\n" << "legal, but will confuse some OSes. The partition IS being added to the MBR, but\n" << "if your OS misbehaves or can't see the partition, the partition may simply be\n" << "unusable in that OS and may need to be resized or omitted from the MBR.\n\n"; } // GPTDataTextUI::WarnAboutIffyMBRPart() /********************************************************************* * * * The following functions provide the main menus for the gdisk * * program.... * * * *********************************************************************/ // Accept a command and execute it. Returns only when the user // wants to exit (such as after a 'w' or 'q' command). void GPTDataTextUI::MainMenu(string filename) { int goOn = 1; PartType typeHelper; uint32_t temp1, temp2; do { cout << "\nCommand (? for help): "; switch (ReadString()[0]) { case '\0': goOn = cin.good(); break; case 'b': case 'B': cout << "Enter backup filename to save: "; SaveGPTBackup(ReadString()); break; case 'c': case 'C': if (GetPartRange(&temp1, &temp2) > 0) SetName(GetPartNum()); else cout << "No partitions\n"; break; case 'd': case 'D': DeletePartition(); break; case 'i': case 'I': ShowDetails(); break; case 'l': case 'L': typeHelper.ShowAllTypes(); break; case 'n': case 'N': CreatePartition(); break; case 'o': case 'O': cout << "This option deletes all partitions and creates a new protective MBR.\n" << "Proceed? "; if (GetYN() == 'Y') { ClearGPTData(); MakeProtectiveMBR(); } // if break; case 'p': case 'P': DisplayGPTData(); break; case 'q': case 'Q': goOn = 0; break; case 'r': case 'R': RecoveryMenu(filename); goOn = 0; break; case 's': case 'S': SortGPT(); cout << "You may need to edit /etc/fstab and/or your boot loader configuration!\n"; break; case 't': case 'T': ChangePartType(); break; case 'v': case 'V': Verify(); break; case 'w': case 'W': if (SaveGPTData() == 1) goOn = 0; break; case 'x': case 'X': ExpertsMenu(filename); goOn = 0; break; default: ShowCommands(); break; } // switch } while (goOn); } // GPTDataTextUI::MainMenu() void GPTDataTextUI::ShowCommands(void) { cout << "b\tback up GPT data to a file\n"; cout << "c\tchange a partition's name\n"; cout << "d\tdelete a partition\n"; cout << "i\tshow detailed information on a partition\n"; cout << "l\tlist known partition types\n"; cout << "n\tadd a new partition\n"; cout << "o\tcreate a new empty GUID partition table (GPT)\n"; cout << "p\tprint the partition table\n"; cout << "q\tquit without saving changes\n"; cout << "r\trecovery and transformation options (experts only)\n"; cout << "s\tsort partitions\n"; cout << "t\tchange a partition's type code\n"; cout << "v\tverify disk\n"; cout << "w\twrite table to disk and exit\n"; cout << "x\textra functionality (experts only)\n"; cout << "?\tprint this menu\n"; } // GPTDataTextUI::ShowCommands() // Accept a recovery & transformation menu command. Returns only when the user // issues an exit command, such as 'w' or 'q'. void GPTDataTextUI::RecoveryMenu(string filename) { uint32_t numParts; int goOn = 1, temp1; do { cout << "\nRecovery/transformation command (? for help): "; switch (ReadString()[0]) { case '\0': goOn = cin.good(); break; case 'b': case 'B': RebuildMainHeader(); break; case 'c': case 'C': cout << "Warning! This will probably do weird things if you've converted an MBR to\n" << "GPT form and haven't yet saved the GPT! Proceed? "; if (GetYN() == 'Y') LoadSecondTableAsMain(); break; case 'd': case 'D': RebuildSecondHeader(); break; case 'e': case 'E': cout << "Warning! This will probably do weird things if you've converted an MBR to\n" << "GPT form and haven't yet saved the GPT! Proceed? "; if (GetYN() == 'Y') LoadMainTable(); break; case 'f': case 'F': cout << "Warning! This will destroy the currently defined partitions! Proceed? "; if (GetYN() == 'Y') { if (LoadMBR(filename) == 1) { // successful load XFormPartitions(); } else { cout << "Problem loading MBR! GPT is untouched; regenerating protective MBR!\n"; MakeProtectiveMBR(); } // if/else } // if break; case 'g': case 'G': numParts = GetNumParts(); temp1 = XFormToMBR(); if (temp1 > 0) cout << "\nConverted " << temp1 << " partitions. Finalize and exit? "; if ((temp1 > 0) && (GetYN() == 'Y')) { if ((DestroyGPT() > 0) && (SaveMBR())) { goOn = 0; } // if } else { MakeProtectiveMBR(); SetGPTSize(numParts, 0); cout << "Note: New protective MBR created\n\n"; } // if/else break; case 'h': case 'H': MakeHybrid(); break; case 'i': case 'I': ShowDetails(); break; case 'l': case 'L': cout << "Enter backup filename to load: "; LoadGPTBackup(ReadString()); break; case 'm': case 'M': MainMenu(filename); goOn = 0; break; case 'o': case 'O': DisplayMBRData(); break; case 'p': case 'P': DisplayGPTData(); break; case 'q': case 'Q': goOn = 0; break; case 't': case 'T': XFormDisklabel(); break; case 'v': case 'V': Verify(); break; case 'w': case 'W': if (SaveGPTData() == 1) { goOn = 0; } // if break; case 'x': case 'X': ExpertsMenu(filename); goOn = 0; break; default: ShowRecoveryCommands(); break; } // switch } while (goOn); } // GPTDataTextUI::RecoveryMenu() void GPTDataTextUI::ShowRecoveryCommands(void) { cout << "b\tuse backup GPT header (rebuilding main)\n"; cout << "c\tload backup partition table from disk (rebuilding main)\n"; cout << "d\tuse main GPT header (rebuilding backup)\n"; cout << "e\tload main partition table from disk (rebuilding backup)\n"; cout << "f\tload MBR and build fresh GPT from it\n"; cout << "g\tconvert GPT into MBR and exit\n"; cout << "h\tmake hybrid MBR\n"; cout << "i\tshow detailed information on a partition\n"; cout << "l\tload partition data from a backup file\n"; cout << "m\treturn to main menu\n"; cout << "o\tprint protective MBR data\n"; cout << "p\tprint the partition table\n"; cout << "q\tquit without saving changes\n"; cout << "t\ttransform BSD disklabel partition\n"; cout << "v\tverify disk\n"; cout << "w\twrite table to disk and exit\n"; cout << "x\textra functionality (experts only)\n"; cout << "?\tprint this menu\n"; } // GPTDataTextUI::ShowRecoveryCommands() // Accept an experts' menu command. Returns only after the user // selects an exit command, such as 'w' or 'q'. void GPTDataTextUI::ExpertsMenu(string filename) { GPTData secondDevice; uint32_t temp1, temp2; int goOn = 1; string guidStr, device; GUIDData aGUID; ostringstream prompt; do { cout << "\nExpert command (? for help): "; switch (ReadString()[0]) { case '\0': goOn = cin.good(); break; case 'a': case 'A': if (GetPartRange(&temp1, &temp2) > 0) SetAttributes(GetPartNum()); else cout << "No partitions\n"; break; case 'c': case 'C': ChangeUniqueGuid(); break; case 'd': case 'D': cout << "Partitions will begin on " << GetAlignment() << "-sector boundaries.\n"; break; case 'e': case 'E': cout << "Relocating backup data structures to the end of the disk\n"; MoveSecondHeaderToEnd(); break; case 'f': case 'F': RandomizeGUIDs(); break; case 'g': case 'G': cout << "Enter the disk's unique GUID ('R' to randomize): "; guidStr = ReadString(); if ((guidStr.length() >= 32) || (guidStr[0] == 'R') || (guidStr[0] == 'r')) { SetDiskGUID((GUIDData) guidStr); cout << "The new disk GUID is " << GetDiskGUID() << "\n"; } else { cout << "GUID is too short!\n"; } // if/else break; case 'h': case 'H': RecomputeCHS(); break; case 'i': case 'I': ShowDetails(); break; case 'j': case 'J': MoveMainTable(); break; case 'l': case 'L': prompt.seekp(0); prompt << "Enter the sector alignment value (1-" << MAX_ALIGNMENT << ", default = " << DEFAULT_ALIGNMENT << "): "; temp1 = GetNumber(1, MAX_ALIGNMENT, DEFAULT_ALIGNMENT, prompt.str()); SetAlignment(temp1); break; case 'm': case 'M': MainMenu(filename); goOn = 0; break; case 'n': case 'N': MakeProtectiveMBR(); break; case 'o': case 'O': DisplayMBRData(); break; case 'p': case 'P': DisplayGPTData(); break; case 'q': case 'Q': goOn = 0; break; case 'r': case 'R': RecoveryMenu(filename); goOn = 0; break; case 's': case 'S': ResizePartitionTable(); break; case 't': case 'T': SwapPartitions(); break; case 'u': case 'U': cout << "Type device filename, or press to exit: "; device = ReadString(); if (device.length() > 0) { secondDevice = *this; secondDevice.SetDisk(device); secondDevice.SaveGPTData(0); } // if break; case 'v': case 'V': Verify(); break; case 'w': case 'W': if (SaveGPTData() == 1) { goOn = 0; } // if break; case 'z': case 'Z': if (DestroyGPTwPrompt() == 1) { goOn = 0; } break; default: ShowExpertCommands(); break; } // switch } while (goOn); } // GPTDataTextUI::ExpertsMenu() void GPTDataTextUI::ShowExpertCommands(void) { cout << "a\tset attributes\n"; cout << "c\tchange partition GUID\n"; cout << "d\tdisplay the sector alignment value\n"; cout << "e\trelocate backup data structures to the end of the disk\n"; cout << "f\trandomize disk and partition unique GUIDs\n"; cout << "g\tchange disk GUID\n"; cout << "h\trecompute CHS values in protective/hybrid MBR\n"; cout << "i\tshow detailed information on a partition\n"; cout << "j\tmove the main partition table\n"; cout << "l\tset the sector alignment value\n"; cout << "m\treturn to main menu\n"; cout << "n\tcreate a new protective MBR\n"; cout << "o\tprint protective MBR data\n"; cout << "p\tprint the partition table\n"; cout << "q\tquit without saving changes\n"; cout << "r\trecovery and transformation options (experts only)\n"; cout << "s\tresize partition table\n"; cout << "t\ttranspose two partition table entries\n"; cout << "u\treplicate partition table on new device\n"; cout << "v\tverify disk\n"; cout << "w\twrite table to disk and exit\n"; cout << "z\tzap (destroy) GPT data structures and exit\n"; cout << "?\tprint this menu\n"; } // GPTDataTextUI::ShowExpertCommands() /******************************** * * * Non-class support functions. * * * ********************************/ // GetMBRTypeCode() doesn't really belong in the class, since it's MBR- // specific, but it's also user I/O-related, so I want to keep it in // this file.... // Get an MBR type code from the user and return it int GetMBRTypeCode(int defType) { string line; int typeCode; cout.setf(ios::uppercase); cout.fill('0'); do { cout << "Enter an MBR hex code (default " << hex; cout.width(2); cout << defType << "): " << dec; line = ReadString(); if (line[0] == '\0') typeCode = defType; else typeCode = StrToHex(line, 0); } while ((typeCode <= 0) || (typeCode > 255)); cout.fill(' '); return typeCode; } // GetMBRTypeCode #ifdef USE_UTF16 // Note: ReadUString() is here rather than in support.cc so that the ICU // libraries need not be linked to fixparts. // Reads a Unicode string from stdin, returning it as an ICU-style string. // Note that the returned string will NOT include the carriage return // entered by the user. Relies on the ICU constructor from a string // encoded in the current codepage to work. UnicodeString ReadUString(void) { return ReadString().c_str(); } // ReadUString() #endif gptfdisk-1.0.5/gptcl.h0000664000175000017500000000343213622612343015064 0ustar rodsmithrodsmith/* Implementation of GPTData class derivative with popt-based command line processing Copyright (C) 2010-2013 Roderick W. Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __GPTCL_H #define __GPTCL_H #include "gpt.h" #include using namespace std; class GPTDataCL : public GPTData { protected: // Following are variables associated with popt parameters.... char *attributeOperation, *backupFile, *partName, *hybrids; char *newPartInfo, *mbrParts, *twoParts, *outDevice, *typeCode; char *partGUID, *diskGUID; int alignment, deletePartNum, infoPartNum, largestPartNum, bsdPartNum; uint32_t tableSize; poptContext poptCon; int BuildMBR(char* argument, int isHybrid); public: GPTDataCL(void); GPTDataCL(string filename); ~GPTDataCL(void); void LoadBackupFile(string backupFile, int &saveData, int &neverSaveData); int DoOptions(int argc, char* argv[]); }; // class GPTDataCL int CountColons(char* argument); uint64_t GetInt(const string & argument, int itemNum); string GetString(string argument, int itemNum); #endif gptfdisk-1.0.5/bsd.cc0000664000175000017500000002655013622612343014667 0ustar rodsmithrodsmith/* bsd.cc -- Functions for loading and manipulating legacy BSD disklabel data. */ /* By Rod Smith, initial coding August, 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. */ #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #include //#include #include #include #include #include #include #include #include #include "support.h" #include "bsd.h" using namespace std; BSDData::BSDData(void) { state = unknown; signature = UINT32_C(0); signature2 = UINT32_C(0); sectorSize = 512; numParts = 0; labelFirstLBA = 0; labelLastLBA = 0; labelStart = LABEL_OFFSET1; // assume raw disk format partitions = NULL; } // default constructor BSDData::~BSDData(void) { delete[] partitions; } // destructor // Read BSD disklabel data from the specified device filename. This function // just opens the device file and then calls an overloaded function to do // the bulk of the work. Returns 1 on success, 0 on failure. int BSDData::ReadBSDData(const string & device, uint64_t startSector, uint64_t endSector) { int allOK = 1; DiskIO myDisk; if (device != "") { if (myDisk.OpenForRead(device)) { allOK = ReadBSDData(&myDisk, startSector, endSector); } else { allOK = 0; } // if/else myDisk.Close(); } else { allOK = 0; } // if/else return allOK; } // BSDData::ReadBSDData() (device filename version) // Load the BSD disklabel data from an already-opened disk // file, starting with the specified sector number. int BSDData::ReadBSDData(DiskIO *theDisk, uint64_t startSector, uint64_t endSector) { int allOK = 1; int i, foundSig = 0, bigEnd = 0; int relative = 0; // assume absolute partition sector numbering uint8_t buffer[4096]; // I/O buffer uint32_t realSig; uint32_t* temp32; uint16_t* temp16; BSDRecord* tempRecords; int offset[NUM_OFFSETS] = { LABEL_OFFSET1, LABEL_OFFSET2 }; labelFirstLBA = startSector; labelLastLBA = endSector; offset[1] = theDisk->GetBlockSize(); // Read 4096 bytes (eight 512-byte sectors or equivalent) // into memory; we'll extract data from this buffer. // (Done to work around FreeBSD limitation on size of reads // from block devices.) allOK = theDisk->Seek(startSector); if (allOK) allOK = theDisk->Read(buffer, 4096); // Do some strangeness to support big-endian architectures... bigEnd = (IsLittleEndian() == 0); realSig = BSD_SIGNATURE; if (bigEnd && allOK) ReverseBytes(&realSig, 4); // Look for the signature at any of two locations. // Note that the signature is repeated at both the original // offset and 132 bytes later, so we need two checks.... if (allOK) { i = 0; do { temp32 = (uint32_t*) &buffer[offset[i]]; signature = *temp32; if (signature == realSig) { // found first, look for second temp32 = (uint32_t*) &buffer[offset[i] + 132]; signature2 = *temp32; if (signature2 == realSig) { foundSig = 1; labelStart = offset[i]; } // if found signature } // if/else i++; } while ((!foundSig) && (i < NUM_OFFSETS)); allOK = foundSig; } // if // Load partition metadata from the buffer.... if (allOK) { temp32 = (uint32_t*) &buffer[labelStart + 40]; sectorSize = *temp32; temp16 = (uint16_t*) &buffer[labelStart + 138]; numParts = *temp16; } // if // Make it big-endian-aware.... if ((IsLittleEndian() == 0) && allOK) ReverseMetaBytes(); // Check validity of the data and flag it appropriately.... if (foundSig && (numParts <= MAX_BSD_PARTS) && allOK) { state = bsd; } else { state = bsd_invalid; } // if/else // If the state is good, go ahead and load the main partition data.... if (state == bsd) { partitions = new struct BSDRecord[numParts * sizeof(struct BSDRecord)]; if (partitions == NULL) { cerr << "Unable to allocate memory in BSDData::ReadBSDData()! Terminating!\n"; exit(1); } // if for (i = 0; i < numParts; i++) { // Once again, we use the buffer, but index it using a BSDRecord // pointer (dangerous, but effective).... tempRecords = (BSDRecord*) &buffer[labelStart + 148]; partitions[i].lengthLBA = tempRecords[i].lengthLBA; partitions[i].firstLBA = tempRecords[i].firstLBA; partitions[i].fsType = tempRecords[i].fsType; if (bigEnd) { // reverse data (fsType is a single byte) ReverseBytes(&partitions[i].lengthLBA, 4); ReverseBytes(&partitions[i].firstLBA, 4); } // if big-endian // Check for signs of relative sector numbering: A "0" first sector // number on a partition with a non-zero length -- but ONLY if the // length is less than the disk size, since NetBSD has a habit of // creating a disk-sized partition within a carrier MBR partition // that's too small to house it, and this throws off everything.... if ((partitions[i].firstLBA == 0) && (partitions[i].lengthLBA > 0) && (partitions[i].lengthLBA < labelLastLBA)) relative = 1; } // for // Some disklabels use sector numbers relative to the enclosing partition's // start, others use absolute sector numbers. If relative numbering was // detected above, apply a correction to all partition start sectors.... if (relative) { for (i = 0; i < numParts; i++) { partitions[i].firstLBA += (uint32_t) startSector; } // for } // if } // if signatures OK // DisplayBSDData(); return allOK; } // BSDData::ReadBSDData(DiskIO* theDisk, uint64_t startSector) // Reverse metadata's byte order; called only on big-endian systems void BSDData::ReverseMetaBytes(void) { ReverseBytes(&signature, 4); ReverseBytes(§orSize, 4); ReverseBytes(&signature2, 4); ReverseBytes(&numParts, 2); } // BSDData::ReverseMetaByteOrder() // Display basic BSD partition data. Used for debugging. void BSDData::DisplayBSDData(void) { int i; if (state == bsd) { cout << "BSD partitions:\n"; for (i = 0; i < numParts; i++) { cout.width(4); cout << i + 1 << "\t"; cout.width(13); cout << partitions[i].firstLBA << "\t"; cout.width(15); cout << partitions[i].lengthLBA << " \t0x"; cout.width(2); cout.fill('0'); cout.setf(ios::uppercase); cout << hex << (int) partitions[i].fsType << "\n" << dec; cout.fill(' '); } // for } // if } // BSDData::DisplayBSDData() // Displays the BSD disklabel state. Called during program launch to inform // the user about the partition table(s) status int BSDData::ShowState(void) { int retval = 0; switch (state) { case bsd_invalid: cout << " BSD: not present\n"; break; case bsd: cout << " BSD: present\n"; retval = 1; break; default: cout << "\a BSD: unknown -- bug!\n"; break; } // switch return retval; } // BSDData::ShowState() // Weirdly, this function has stopped working when defined inline, // but it's OK here.... int BSDData::IsDisklabel(void) { return (state == bsd); } // BSDData::IsDiskLabel() // Returns the BSD table's partition type code uint8_t BSDData::GetType(int i) { uint8_t retval = 0; // 0 = "unused" if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0)) retval = partitions[i].fsType; return(retval); } // BSDData::GetType() // Returns the number of the first sector of the specified partition uint64_t BSDData::GetFirstSector(int i) { uint64_t retval = UINT64_C(0); if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0)) retval = (uint64_t) partitions[i].firstLBA; return retval; } // BSDData::GetFirstSector // Returns the length (in sectors) of the specified partition uint64_t BSDData::GetLength(int i) { uint64_t retval = UINT64_C(0); if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0)) retval = (uint64_t) partitions[i].lengthLBA; return retval; } // BSDData::GetLength() // Returns the number of partitions defined in the current table int BSDData::GetNumParts(void) { return numParts; } // BSDData::GetNumParts() // Returns the specified partition as a GPT partition. Used in BSD-to-GPT // conversion process GPTPart BSDData::AsGPT(int i) { GPTPart guid; // dump data in here, then return it uint64_t sectorOne, sectorEnd; // first & last sectors of partition int passItOn = 1; // Set to 0 if partition is empty or invalid guid.BlankPartition(); sectorOne = (uint64_t) partitions[i].firstLBA; sectorEnd = sectorOne + (uint64_t) partitions[i].lengthLBA; if (sectorEnd > 0) sectorEnd--; // Note on above: BSD partitions sometimes have a length of 0 and a start // sector of 0. With unsigned ints, the usual way (start + length - 1) to // find the end will result in a huge number, which will be confusing. // Thus, apply the "-1" part only if it's reasonable to do so. // Do a few sanity checks on the partition before we pass it on.... // First, check that it falls within the bounds of its container // and that it starts before it ends.... if ((sectorOne < labelFirstLBA) || (sectorEnd > labelLastLBA) || (sectorOne > sectorEnd)) passItOn = 0; // Some disklabels include a pseudo-partition that's the size of the entire // disk or containing partition. Don't return it. if ((sectorOne <= labelFirstLBA) && (sectorEnd >= labelLastLBA) && (GetType(i) == 0)) passItOn = 0; // If the end point is 0, it's not a valid partition. if ((sectorEnd == 0) || (sectorEnd == labelFirstLBA)) passItOn = 0; if (passItOn) { guid.SetFirstLBA(sectorOne); guid.SetLastLBA(sectorEnd); // Now set a random unique GUID for the partition.... guid.RandomizeUniqueGUID(); // ... zero out the attributes and name fields.... guid.SetAttributes(UINT64_C(0)); // Most BSD disklabel type codes seem to be archaic or rare. // They're also ambiguous; a FreeBSD filesystem is impossible // to distinguish from a NetBSD one. Thus, these code assignment // are going to be rough to begin with. For a list of meanings, // see http://fxr.watson.org/fxr/source/sys/dtype.h?v=DFBSD, // or Google it. switch (GetType(i)) { case 1: // BSD swap guid.SetType(0xa502); break; case 7: // BSD FFS guid.SetType(0xa503); break; case 8: case 11: // MS-DOS or HPFS guid.SetType(0x0700); break; case 9: // log-structured fs guid.SetType(0xa903); break; case 13: // bootstrap guid.SetType(0xa501); break; case 14: // vinum guid.SetType(0xa505); break; case 15: // RAID guid.SetType(0xa903); break; case 27: // FreeBSD ZFS guid.SetType(0xa504); break; default: guid.SetType(0xa503); break; } // switch // Set the partition name to the name of the type code.... guid.SetName(guid.GetTypeName()); } // if return guid; } // BSDData::AsGPT() gptfdisk-1.0.5/cgdisk.80000664000175000017500000003727213622612343015150 0ustar rodsmithrodsmith.\" Copyright 2011-2020 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU General Public License .TH "CGDISK" "8" "1.0.5" "Roderick W. Smith" "GPT fdisk Manual" .SH "NAME" cgdisk \- Curses-based GUID partition table (GPT) manipulator .SH "SYNOPSIS" .BI "cgdisk " [ \-a ] .I device .SH "DESCRIPTION" GPT fdisk is a text\-mode family of programs for creation and manipulation of partition tables. The \fBcgdisk\fR member of this family employs a curses-based user interface for interaction using a text\-mode menuing system. 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 GUID partition table. Other members of this program family are \fBgdisk\fR (the most feature-rich program of the group, with a non-curses-based interactive user interface) and \fBsgdisk\fR (which is driven via command-line options for use by experts or in scripts). FixParts is a related program for fixing a limited set of problems with MBR disks. For information on MBR vs. GPT, as well as GPT terminology and structure, see the extended GPT fdisk documentation at \fIhttp://www.rodsbooks.com/gdisk/\fR or consult Wikipedia. The \fBcgdisk\fR program employs a user interface similar to that of Linux's \fBcfdisk\fR, but \fBcgdisk\fR modifies GPT partitions. It also has the capability of transforming MBR partitions or BSD disklabels into GPT partitions. Like the original \fBcfdisk\fR program, \fBcgdisk\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 Quit option to leave your partitions unmodified. Ordinarily, \fBcgdisk\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; \fBcgdisk\fR cannot work on compressed or other advanced disk image formats. Upon start, \fBcgdisk\fR attempts to identify the partition type in use on the disk. If it finds valid GPT data, \fBcgdisk\fR will use it. If \fBcgdisk\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.) Upon exiting with the 'w' option, \fBcgdisk\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 \fBcgdisk\fR on an MBR disk, you can safely exit the program without making any changes by using the Quit option. 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 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 \fIEFI System Partition\fR (GPT fdisk internal code 0xEF00) formatted as FAT\-32. The recommended size of this partition is between 100 and 300 MiB. Boot\-related files are stored here. (Note that GNU Parted identifies such partitions as having the "boot flag" set.) .TP .B * The GRUB 2 boot loader for BIOS\-based systems makes use of a \fIBIOS Boot Partition\fR (GPT fdisk internal code 0xEF02), in which the secondary boot loader is stored, without the benefit of a filesystem. This partition can typically be quite small (roughly 32 KiB to 1 MiB), but you should consult your boot loader documentation for details. .TP .B * If Windows is to boot from a GPT disk, a partition of type \fIMicrosoft Reserved\fR (GPT fdisk 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 old versions of GNU Parted create all FAT partitions as this type, which actually makes the partition unusable for normal file storage in both Windows and Mac OS X.) .TP .B * Some OSes' GPT utilities create some blank space (typically 128 MiB) after each partition. The intent is to enable future disk utilities to use this space. Such free space is not required of GPT disks, but creating it may 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" .PP Only one command-line option is accepted, aside from the device filename: \fI\-a\fR. This option alters the highlighting of partitions and blocks of free space: Instead of using ncurses, when \fI\-a\fR is used \fBcgdisk\fR uses a ">" symbol to the left of the selected partition or free space. This option is intended for use on limited display devices such as teletypes and screen readers. Interactions with \fBcgdisk\fR occur with its interactive text\-mode menus. The display is broken into two interactive parts: .TP .B * The partition display area, in which partitions and gaps between them (marked as "free space") are summarized. .TP .B * The option selection area, in which buttons for the main options appear. .PP In addition, the top of the display shows the program's name and version number, the device filename associated with the disk, and the disk's size in both sectors and IEEE-1541 units (GiB, TiB, and so on). You can use the following keys to move among the various options and to select among them: .TP .B up arrow This key moves the partition selection up by one partition. .TP .B down arrow This key moves the partition selection down by one partition. .TP .B Page Up This key moves the partition selection up by one screen. .TP .B Page Down This key moves the partition selection down by one screen. .TP .B right arrow This key moves the option selection to the right by one item. .TP .B left arrow This key moves the option selection to the left by one item. .TP .B Enter This key activates the currently selected option. You can also activate an option by typing the capitalized letter in the option's name on the keyboard, such as \fBa\fR to activate the Align option. .PP If more partitions exist than can be displayed in one screen, you can scroll between screens using the partition selection keys, much as in a text editor. Available options are as described below. (Note that \fBcgdisk\fR provides a much more limited set of options than its sibling \fBgdisk\fR. If you need to perform partition table recovery, hybrid MBR modifcation, or other advanced operations, you should consult the \fBgdisk\fR documentation.) .TP .B Align Change the sector alignment value. Disks with more logical sectors than physical sectors (such as modern Advanced Format drives), some RAID configurations, and many SSD devices, can suffer performance problems if partitions are not aligned properly for their internal data structures. On new disks, GPT fdisk attempts to align partitions on 1 MiB boundaries (2048\-sectors on disks with 512-byte sectors) by default, which optimizes performance for all of these disk types. On pre\-partitioned disks, GPT fdisk attempts to identify the alignment value used on that disk, but will set 8-sector alignment on disks larger than 300 GB even if lesser alignment values are detected. In either case, it can be changed by using this option. .TP .B Backup Save partition data to a backup file. You can back up your current in\-memory 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 backup is of the current in\-memory data structures, so if you launch the program, make changes, and then use this option, the backup will reflect your changes. .TP .B Delete 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, \fBgdisk\fR deletes it, as well, and expands any adjacent 0xEE (EFI GPT) MBR protective partition to fill the new free space. .TP .B Help Print brief descriptions of all the options. .TP .B Info Show detailed partition information. The summary information shown in the partition display area necessarily omits many details, such as the partitions' unique GUIDs and the partitions' sector-exact start and end points. The Info option displays this information for a single partition. .TP .B Load Load partition data from a backup file. This option is the reverse of the Backup option. Note that restoring partition data from anything but the original disk is not recommended. .TP .B naMe Change the GPT name of a partition. This name is encoded as a UTF\-16 string, but proper entry and display of anything beyond basic ASCII values requires suitable locale and font support. 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. Note that the GPT partition name is different from the filesystem name, which is encoded in the filesystem's data structures. Note also that to activate this item by typing its alphabetic equivalent, you must use \fBM\fR, not the more obvious \fBN\fR, because the latter is used by the next option.... .TP .B New Create a new partition. You enter a starting sector, a size, a type code, and a name. The start sector can be specified in absolute terms as a sector number or as a position measured in kibibytes (K), mebibytes (M), gibibytes (G), tebibytes (T), or pebibytes (P); 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 default range by preceding the number by a '+' symbol, as in \fI\fB+2G\fR\fR to specify a point 2GiB after the default start sector. The size value can use the K, M, G, T, and P suffixes, too. 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 full available size for the size. .TP .B Quit 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 Type Change a single partition's type code. You enter the type code using a two\-byte hexadecimal number. You may also enter a GUID directly, if you have one and \fBcgdisk\fR doesn't know it. If you don't know the type code for your partition, you can type \fBL\fR to see a list of known type codes. The type code list may optionally be filtered by a search string; for instance, entering \fI\fBlinux\fR\fR shows only partition type codes with descriptions that include the string \fILinux\fR. This search is performed case\-insensitively. .TP .B Verify 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 most problems, though; for that, you must use \fBgdisk\fR. If no problems are found, this command displays a summary of unallocated disk space. .TP .B Write Write data. Use this command to save your changes. .SH "BUGS" Known bugs and limitations include: .TP .B * The program compiles correctly only on Linux, FreeBSD, and Mac OS X. In theory, it should compile under Windows if the Ncurses library for Windows is installed, but I have not tested this capability. 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. Under FreeBSD, 32\-bit (x86) and 64\-bit (x86\-64) versions have been tested. Only 32\-bit versions for Mac OS X has been tested by the author. .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 \fBgpt\fR, \fBfdisk\fR, and \fBdd\fR.) This limitation can be overcome by typing \fBsysctl kern.geom.debugflags=16\fR at a shell prompt. .TP .B * 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 \fIbasicmbr.h\fR source code file and recompiling; however, such a change will require using a larger\-than\-normal partition table. (The limit of 128 partitions was chosen because that number equals the 128 partitions supported by the most common partition table size.) .TP .B * 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 in \fBgdisk\fR) can sometimes overcome this problem; however, in extreme cases it may be necessary to resize a partition using GNU Parted or a similar tool prior to conversion with GPT fdisk. .TP .B * MBR conversions work only if the disk has correct LBA partition descriptors. These descriptors should be present on any disk over 8 GiB in size or on smaller disks partitioned with any but very ancient software. .TP .B * BSD disklabel support can create first and/or last partitions that overlap with the GPT data structures. This can sometimes be compensated by adjusting the partition table size, but in extreme cases the affected 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, converted in a way that creates overlaps with other partitions, or converted with incorrect start or end values. Use this feature with 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 platforms, Windows through at least Windows 7 doesn't support booting from GPT disks. Creating a hybrid MBR (using the 'h' option on the recovery & transformation menu in \fBgdisk\fR) or abandoning GPT in favor of MBR may be your only options in this case. .TP .B * The \fBcgdisk\fR Verify function and the partition type listing obtainable by typing \fIL\fR in the Type function (or when specifying a partition type while creating a new partition) both currently exit ncurses mode. This limitation is a minor cosmetic blemish that does not affect functionality. .SH "AUTHORS" Primary author: Roderick W. Smith (rodsmith@rodsbooks.com) Contributors: * Yves Blusseau (1otnwmz02@sneakemail.com) * David Hubbard (david.c.hubbard@gmail.com) * Justin Maggard (justin.maggard@netgear.com) * Dwight Schauer (dschauer@gmail.com) * Florian Zumbiehl (florz@florz.de) .SH "SEE ALSO" .BR cfdisk (8), .BR fdisk (8), .BR gdisk (8), .BR mkfs (8), .BR parted (8), .BR sfdisk (8), .BR sgdisk (8), .BR fixparts (8). \fIhttp://en.wikipedia.org/wiki/GUID_Partition_Table\fR \fIhttp://developer.apple.com/technotes/tn2006/tn2166.html\fR \fIhttp://www.rodsbooks.com/gdisk/\fR .SH "AVAILABILITY" The \fBcgdisk\fR command is part of the \fIGPT fdisk\fR package and is available from Rod Smith. gptfdisk-1.0.5/current.spec0000664000175000017500000000472713622612343016150 0ustar rodsmithrodsmithSummary: GPT partitioning and MBR repair software Name: gptfdisk Version: 1.0.5 Release: 1%{?dist} License: GPLv2 URL: http://www.rodsbooks.com/gdisk Group: Applications/System Source: http://www.rodsbooks.com/gdisk/gptfdisk-1.0.5.tar.gz BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) %description Partitioning software for GPT disks and to repair MBR disks. The gdisk, cgdisk, and sgdisk utilities (in the gdisk package) are GPT-enabled partitioning tools; the fixparts utility (in the fixparts package) fixes some problems with MBR disks that can be created by buggy partitioning software. %package -n gdisk Group: Applications/System Summary: An fdisk-like partitioning tool for GPT disks %description -n gdisk An fdisk-like partitioning tool for GPT disks. GPT fdisk features a command-line interface, fairly direct manipulation of partition table structures, recovery tools to help you deal with corrupt partition tables, and the ability to convert MBR disks to GPT format. %prep %setup -q %build CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_CXX_FLAGS" make %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/usr/sbin install -Dp -m0755 gdisk $RPM_BUILD_ROOT/usr/sbin install -Dp -m0755 sgdisk $RPM_BUILD_ROOT/usr/sbin install -Dp -m0755 cgdisk $RPM_BUILD_ROOT/usr/sbin install -Dp -m0755 fixparts $RPM_BUILD_ROOT/usr/sbin install -Dp -m0644 gdisk.8 $RPM_BUILD_ROOT/%{_mandir}/man8/gdisk.8 install -Dp -m0644 sgdisk.8 $RPM_BUILD_ROOT/%{_mandir}/man8/sgdisk.8 install -Dp -m0644 cgdisk.8 $RPM_BUILD_ROOT/%{_mandir}/man8/cgdisk.8 install -Dp -m0644 fixparts.8 $RPM_BUILD_ROOT/%{_mandir}/man8/fixparts.8 %clean rm -rf $RPM_BUILD_ROOT %files -n gdisk %defattr(-,root,root -) %doc NEWS COPYING README /usr/sbin/gdisk /usr/sbin/sgdisk /usr/sbin/cgdisk %doc %{_mandir}/man8/gdisk.8* %doc %{_mandir}/man8/sgdisk.8* %doc %{_mandir}/man8/cgdisk.8* %package -n fixparts Group: Applications/System Summary: A tool for repairing certain types of damage to MBR disks %description -n fixparts A program that corrects errors that can creep into MBR-partitioned disks. Removes stray GPT data, fixes mis-sized extended partitions, and enables changing primary vs. logical partition status. Also provides a few additional partition manipulation features. %files -n fixparts %defattr(-,root,root -) %doc NEWS COPYING README /usr/sbin/fixparts %doc %{_mandir}/man8/fixparts.8* %changelog * Mon Feb 17 2020 R Smith - 1.0.5 - Created spec file for 1.0.5 release gptfdisk-1.0.5/support.h0000664000175000017500000000466413622612343015477 0ustar rodsmithrodsmith/* This program is copyright (c) 2009-2018 by Roderick W. Smith. It is distributed under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ #include #include #include #ifndef __GPTSUPPORT #define __GPTSUPPORT #define GPTFDISK_VERSION "1.0.5" #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__) // Darwin (Mac OS) & FreeBSD: disk IOCTLs are different, and there is no lseek64 #include #define lseek64 lseek #endif #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) #define DEFAULT_GPT_TYPE 0xA503 #endif #ifdef __APPLE__ #define DEFAULT_GPT_TYPE 0xAF00 #endif #ifdef _WIN32 #define DEFAULT_GPT_TYPE 0x0700 #endif #ifdef __sun__ #define DEFAULT_GPT_TYPE 0xbf01 #endif // Microsoft Visual C++ only #if defined (_MSC_VER) #define sscanf sscanf_s #define strcpy strcpy_s #define sprintf sprintf_s #endif // Linux only.... #ifdef __linux__ #include #define DEFAULT_GPT_TYPE 0x8300 #endif #ifndef DEFAULT_GPT_TYPE #define DEFAULT_GPT_TYPE 0x8300 #endif // Set this as a default #define SECTOR_SIZE UINT32_C(512) // Signatures for Apple (APM) disks, multiplied by 0x100000000 #define APM_SIGNATURE1 UINT64_C(0x00004D5000000000) #define APM_SIGNATURE2 UINT64_C(0x0000535400000000) /************************** * Some GPT constants.... * **************************/ #define GPT_SIGNATURE UINT64_C(0x5452415020494645) // Number and size of GPT entries... #define NUM_GPT_ENTRIES 128 #define GPT_SIZE 128 #define HEADER_SIZE UINT32_C(92) #define GPT_RESERVED 420 #define NAME_SIZE 36 // GPT allows 36 UTF-16LE code units for a name in a 128 byte partition entry using namespace std; string ReadString(void); uint64_t GetNumber(uint64_t low, uint64_t high, uint64_t def, const string & prompt); char GetYN(void); uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, uint64_t sSize, const std::string& prompt); uint64_t IeeeToInt(string IeeeValue, uint64_t sSize, uint64_t low, uint64_t high, uint64_t def = 0); string BytesToIeee(uint64_t size, uint32_t sectorSize); unsigned char StrToHex(const string & input, unsigned int position); int IsHex(string input); // Returns 1 if input can be hexadecimal number.... int IsLittleEndian(void); // Returns 1 if CPU is little-endian, 0 if it's big-endian void ReverseBytes(void* theValue, int numBytes); // Reverses byte-order of theValue void WinWarning(void); string ToLower(const string& input); #endif