summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsrs5694 <srs5694@users.sourceforge.net>2009-08-18 13:16:10 -0400
committersrs5694 <srs5694@users.sourceforge.net>2009-08-18 13:16:10 -0400
commite7b4ff9317fc4e551cf974684eaa88697de5a28d (patch)
tree5b5cc2b2fdce62960d03ad537f151df8c581902e
downloadsgdisk-e7b4ff9317fc4e551cf974684eaa88697de5a28d.tar.gz
Initial git repository creation, version 0.3.1 plus changes
-rw-r--r--CHANGELOG83
-rw-r--r--COPYING340
-rw-r--r--Makefile34
-rw-r--r--README95
-rw-r--r--attributes.cc81
-rw-r--r--attributes.h28
-rw-r--r--crc32.cc70
-rw-r--r--crc32.h20
-rw-r--r--gdisk.8602
-rw-r--r--gdisk.cc270
-rw-r--r--gpt.cc1660
-rw-r--r--gpt.h153
-rw-r--r--mbr.cc447
-rw-r--r--mbr.h95
-rw-r--r--parttypes.cc332
-rw-r--r--parttypes.h44
-rw-r--r--support.cc354
-rw-r--r--support.h42
18 files changed, 4750 insertions, 0 deletions
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..4ea90ef
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,83 @@
+0.3.1:
+------
+
+- Added Mac OS X support, provided as a patch by an anonymous contributor.
+
+- 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.
+
+- Added test for writability when opening a disk for reading. If the
+ test fails, a warning message is displayed. (The test simply opens
+ the disk for writing and then closes it before writing any data,
+ so the test shouldn't cause problems.)
+
+- 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.
+
+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
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..623b625
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2c55332
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+CC=gcc
+CXX=g++
+#CFLAGS=-O2 -fpack-struct
+CFLAGS=-O2 -fpack-struct -D_FILE_OFFSET_BITS=64 -g
+CXXFLAGS=-O2 -fpack-struct -D_FILE_OFFSET_BITS=64 -g
+LIB_NAMES=support crc32 mbr gpt parttypes attributes
+LIB_SRCS=$(NAMES:=.cc)
+LIB_OBJS=$(LIB_NAMES:=.o)
+LIB_HEADERS=$(LIB_NAMES:=.h)
+DEPEND= makedepend $(CFLAGS)
+
+#$(APPNAME): $(MBR2GPT_OBJS)
+# $(CC) $(MBR2GPT_OBJS) -o $@
+
+gdisk: $(LIB_OBJS) gdisk.o
+ $(CXX) $(LIB_OBJS) gdisk.o -o gdisk
+
+wipegpt: $(LIB_OBJS) wipegpt.o
+ $(CXX) $(LIB_OBJS) wipegpt.o -o wipegpt
+
+lint: #no pre-reqs
+ lint $(SRCS)
+
+clean: #no pre-reqs
+ rm -f core *.o *~ gdisk
+
+# what are the source dependencies
+depend: $(SRCS)
+ $(DEPEND) $(SRCS)
+
+$(OBJS):
+
+# DO NOT DELETE
+
diff --git a/README b/README
new file mode 100644
index 0000000..1f12582
--- /dev/null
+++ b/README
@@ -0,0 +1,95 @@
+GPT fdisk (aka gdisk)
+by Roderick W. Smith, rodsmith@rodsbooks.com
+
+Introduction
+------------
+
+This software is intended as a (somewhat) fdisk-workalike program for
+GPT-partitioned disks. 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
+include:
+
+* The ability to convert MBR-partitioned disks in-place to GPT format,
+ without losing data
+
+* The ability to specify sector-exact partition sizes
+
+* More flexible specification of filesystem type code GUIDs, which
+ GNU Parted tends to corrupt (particularly for FAT partitions)
+
+* Clear identification of the number of unallocated sectors on a
+ disk
+
+* A user interface that's familiar to long-time users of Linux
+ fdisk
+
+* The MBR boot loader code is left alone (GNU Parted tends to
+ wipe it out with every change)
+
+Of course, gdisk isn't without its limitations. Most notably, it lacks the
+filesystem awareness and filesystem-related features of GNU Parted. 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.
+
+Installing
+----------
+
+To compile gdisk, you must have appropriate development tools installed,
+most notably the GNU Compiler Collection (GCC) and its g++ compiler for
+C++. uncompress the package and type "make" at the command prompt in the
+resulting directory. The result should be a program file called gdisk. You
+can use this in place or copy the file to a suitable directory, such as
+/usr/local/sbin. You can copy the man page (gdisk.8) to /usr/local/man/man8
+to make it available.
+
+Caveats
+-------
+
+THIS SOFTWARE IS EARLY BETA SOFTWARE! IF IT WIPES OUT YOUR HARD DISK OR
+EATS YOUR CAT, DON'T BLAME ME! To date, I've tested the software mainly on
+two USB flash drives, 2 GiB and 8 GiB in size. I've also made a few minor
+tweaks to a production system with a 500 GiB hard disk and made more
+extensive changes to a handful of 80-160 GiB hard disks. 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 drives;
+I have no way of testing the software with > 2TiB drives, which will test
+the 64-bit sector pointer support.
+
+The MBR-to-GPT feature seems to work well for data drives, but it's largely
+untested on boot drives. One attempt with Windows failed miserably, but I
+believe that was because of Windows' inherent limitations with respect to
+GPT. (The partitions themselves were intact.)
+
+My main development platform is a system running the 64-bit version of
+Ubuntu. I've also tested on 64-bit OpenSuSE and 32-bit Fedora 10. Problems
+relating to 64-bit integers on the 32-bit Linux have been common during
+development and may crop up in the future. The Mac OS X support is new,
+and has at least one bug/limitation: It seems to be impossible to write
+a new partition table if any partitions from the disk are currently
+mounted.
+
+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.
+
+Additional code contributors include:
+
+- Yves Blusseau (1otnwmz02@sneakemail.com)
+
+- One anonymous contributor
diff --git a/attributes.cc b/attributes.cc
new file mode 100644
index 0000000..1a299ea
--- /dev/null
+++ b/attributes.cc
@@ -0,0 +1,81 @@
+// attributes.cc
+// Class to manage partition attribute codes. These are binary bit fields,
+// of which only three are currently (2/2009) documented on Wikipedia.
+
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include "attributes.h"
+
+using namespace std;
+
+// Constructor. Its main task is to initialize the attribute name
+// data.
+Attributes::Attributes(void) {
+ int i;
+ char temp[ATR_NAME_SIZE];
+
+ // Most bits are undefined, so start by giving them an
+ // appropriate name
+ for (i = 1; i < NUM_ATR; i++) {
+ sprintf(temp, "Undefined bit #%d", i);
+ strcpy(atNames[i], temp);
+ } // for
+
+ // Now reset those names that are defined....
+ strcpy(atNames[0], "system partition");
+ strcpy(atNames[60], "read-only");
+ strcpy(atNames[62], "hidden");
+ strcpy(atNames[63], "do not automount");
+} // Attributes constructor
+
+// Destructor.
+Attributes::~Attributes(void) {
+} // Attributes destructor
+
+// Display current attributes to user
+void Attributes::DisplayAttributes(void) {
+ int i;
+
+ printf("Attribute value is %llX. Set fields are:\n",
+ (unsigned long long) attributes);
+ for (i = 0; i < NUM_ATR; i++) {
+ if (((attributes >> i) % 2) == 1) { // bit is set
+/* if (strncmp("Undefined", atNames[i], 9) != 0)
+ printf("%s\n", atNames[i]); */
+ if (strncmp("Undefined", atNames[NUM_ATR - i - 1], 9) != 0)
+ printf("%s\n", atNames[NUM_ATR - i - 1]);
+ } // if
+ } // for
+} // Attributes::DisplayAttributes()
+
+// Prompt user for attribute changes
+void Attributes::ChangeAttributes(void) {
+ int response, i;
+ uint64_t bitValue;
+
+ printf("Known attributes are:\n");
+ for (i = 0; i < NUM_ATR; i++) {
+ if (strncmp("Undefined", atNames[i], 9) != 0)
+ printf("%d - %s\n", i, atNames[i]);
+ } // for
+
+ do {
+ response = GetNumber(0, 64, -1, "Toggle which attribute field (0-63, 64 to exit): ");
+ if (response != 64) {
+ bitValue = PowerOf2(NUM_ATR - response - 1); // Find the integer value of the bit
+// bitValue = PowerOf2(response); // Find the integer value of the bit
+ if ((bitValue & attributes) == bitValue) { // bit is set
+ attributes -= bitValue; // so unset it
+ printf("Have disabled the '%s' attribute.\n", atNames[response]);
+ } else { // bit is not set
+ attributes += bitValue; // so set it
+ printf("Have enabled the '%s' attribute.\n", atNames[response]);
+ } // if/else
+ } // if
+ } while (response != 64);
+} // Attributes::ChangeAttributes()
+
diff --git a/attributes.h b/attributes.h
new file mode 100644
index 0000000..a7538a1
--- /dev/null
+++ b/attributes.h
@@ -0,0 +1,28 @@
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string>
+#include "support.h"
+
+#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:
+ uint64_t attributes;
+ char atNames[NUM_ATR][ATR_NAME_SIZE];
+public:
+ Attributes(void);
+ ~Attributes(void);
+ void SetAttributes(uint64_t a) {attributes = a;}
+ uint64_t GetAttributes(void) {return attributes;}
+ void DisplayAttributes(void);
+ void ChangeAttributes(void);
+}; // class Attributes
+
+#endif
diff --git a/crc32.cc b/crc32.cc
new file mode 100644
index 0000000..a446ffc
--- /dev/null
+++ b/crc32.cc
@@ -0,0 +1,70 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+/* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab().
+ * so make sure, you call it before using the other
+ * functions!
+ */
+u_int32_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.
+ */
+u_int32_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;
+ }
+}
diff --git a/crc32.h b/crc32.h
new file mode 100644
index 0000000..b1ca28b
--- /dev/null
+++ b/crc32.h
@@ -0,0 +1,20 @@
+/*
+ * 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 <stdint.h>
+
+void chksum_crc32gentab ();
+uint32_t chksum_crc32 (unsigned char *block, unsigned int length);
+extern unsigned int crc_tab[256];
diff --git a/gdisk.8 b/gdisk.8
new file mode 100644
index 0000000..628a230
--- /dev/null
+++ b/gdisk.8
@@ -0,0 +1,602 @@
+.\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com)
+.\" May be distributed under the GNU General Public License
+.TH GDISK 8 "August 2009" "Linux 2.6" "GPT fdisk Manual"
+.SH NAME
+gdisk \- GPT partition table manipulator for Linux
+.SH SYNOPSIS
+.BI "gdisk "
+[ \-l ]
+.I device
+.SH DESCRIPTION
+Hard disks can be divided into one or more segments, known as
+.IR partitions .
+This division is described in the
+.I "partition table"
+of the disk. Several different partition table formats exist, each with its
+advantages and disadvantages.
+
+The original partitioning system used on PCs, now known as the
+.IR "MBR partitioning scheme",
+is subject to several limitations. These include an awkward distinction
+between
+.IR "primary",
+.IR "extended",
+and
+.IR "logical"
+partitions; no redundancy or error correction capabilities; and 32-bit data
+structures that, in conjunction with the common 512-byte sector size,
+impose a hard 2 TiB limit on the size of partitions and disks. This final
+drawback makes MBR partitions unsuitable for use on large hardware RAID
+arrays. Individual disk sizes are expected to reach the 2 TiB limit in
+2009, so MBR will become an unsuitable partitioning system even for
+individual hard disks in the near future.
+
+The successor to MBR partitions is the
+.IR "Globally Unique Identifier (GUID) Partition Table (GPT)"
+system. GPT addresses each of the major limitations of MBR partitions, and
+includes a dummy MBR partition table with a single
+.IR "protective MBR"
+entry to keep GPT-unaware programs from modifying the disk's GPT partitions. GPT
+is a new partitioning scheme, though, and as such, older utilities and OSes
+must be replaced or modified to handle GPT. Linux's venerable
+.B "fdisk"
+program, in particular, cannot process GPT disks. (The same is true of
+related programs, such as
+.B "sfdisk"
+and
+.BR "cfdisk".)
+The alternative GNU
+Parted and related programs, however, are capable of working on both MBR
+and GPT disks.
+
+GPT is often associated with the
+.IR "Extensible Firmware Interface (EFI)",
+which is Intel's intended successor to the traditional (legacy) PC BIOS. It
+is possible to use and even boot from GPT disks on non-EFI systems,
+including those that use a legacy BIOS. Using GPT disks on such a system
+isn't a great challenge, although the OS must support GPT. Booting from a
+GPT-based disk requires that the OS support this action, and if the system
+is BIOS-based, a GPT-aware boot loader is required. Patched versions of the
+.IR "Grand Unified Bootloader (GRUB)"
+0.97, as well as GRUB2, support GPT.
+
+GPT creates five distinct data structures of three types:
+.TP
+.B "Protective MBR"
+The first sector (512 bytes) of the disk is devoted to an MBR that
+consists of a single partition spanning the entire disk (or 2 TiB for disks
+larger than this). The protective MBR may optionally include first-stage
+boot loader code.
+.TP
+.B "GPT headers"
+Two GPT headers exist, a main header and a backup header. The primary
+header resides immediately after the protective MBR, and the backup header
+is stored on the last sector of the disk. These headers contain disk
+metadata, such as the location of the partition table, the size of the
+partition table, and a "serial number" (GUID) that should be unique for
+each disk. Each GPT header also stores two CRC checksums, one for the
+partition table and one for the GPT header itself.
+.TP
+.B "Partition tables"
+Each GPT header points to one partition table. The main partition table
+appears immediately after the main GPT header, and the backup partition
+table comes immediately before its GPT header. Typically, the partition
+tables may hold data on up to 128 partitions, although
+.B gdisk
+enables you to change this value. Each entry contains 64-bit start and stop
+sector numbers, a name, a partition GUID type code, a unique partition GUID
+identifier, and additional data.
+.PP
+
+The GPT fdisk (aka
+.BR "gdisk")
+program operates mainly on the GPT headers and partition tables; however,
+it can and will generate a fresh protective MBR, when required. (Any boot
+loader code in the protective MBR will not be disturbed.) If you've created
+an unusual protective MBR, such as a hybrid MBR created by
+.IR "gptsync",
+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.
+
+The
+.B "gdisk"
+program employs a user interface similar to that of
+.BR "fdisk",
+but
+.B "gdisk"
+modifies GPT partitions. It also has the capability of transforming MBR
+partitions into GPT partitions. Like the original
+.B fdisk
+program,
+.B 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
+save your partitions.
+
+.B gdisk
+is a text-mode menu-driven program for creation and manipulation of
+partition tables. It will automatically convert an MBR partition table to
+GPT format, or will load a GPT partition table. When used with the
+.IR "\-l"
+command-line option, the program displays the current partition table and
+then exits.
+
+Linux hard disk device filenames take the form
+.IR "/dev/sdx"
+or
+.IR "/dev/hdx",
+where
+.IR "x"
+is a letter from
+.IR "a"
+onward. The
+.IR "hdx"
+devices originally referred to IDE (aka PATA) drives, whereas
+.IR "sdx"
+devices originally referred to SCSI drives. These distinctions are now
+blurring. Modern SATA drives and USB flash drives usually acquire
+.IR "sdx"
+names, and the same can even be true of PATA drives, depending on kernel
+driver options. For instance,
+.IR "/dev/hda"
+refers to the first PATA drive, whereas
+.IR "/dev/sdb"
+is the second SCSI, SATA, USB, or other SCSI-equivalent drive. To use
+.BR "gdisk",
+you must pass a device filename to the program on the command line.
+
+The
+.I partition
+is a
+device name followed by a partition number. For example,
+.B /dev/hda1
+is the first partition on the first PATA hard disk.
+.B gdisk
+creates partitions, but you don't pass partition numbers or partition
+device filenames to the program. Linux generates numbers for GPT partitions
+based on the partition's position in the partition table.
+
+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
+.BR "gdisk",
+do not need to deal with CHS geometries and all the problems they create.
+Users of
+.BR "fdisk"
+will note that
+.B "gdisk"
+lacks the options and limitations associated with CHS geometries.
+
+For best results, you should always use an OS-specific partition table
+program. For example, you should make Mac OS X partitions with the Mac OS
+X Disk Utility
+program and Linux partitions with the Linux
+.B "gdisk"
+or GNU Parted program.
+
+Upon start,
+.B gdisk
+attempts to identify the partition type in use on the specified disk. If it
+finds valid GPT data,
+.B gdisk
+will use it. If
+.B gdisk
+finds a valid MBR but no GPT data, it will attempt to convert the MBR into
+GPT form. Upon exiting with the 'w' option,
+.B gdisk
+will then replace the MBR with a GPT.
+.IR "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
+.B 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
+.IR "/etc/fstab"
+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
+.IR "EFI System Partition" (
+.B "gdisk"
+internal code 0xEF00) formatted as FAT-32. The recommended size of this
+partition is 100 MiB. Boot-related files are stored here. (Note that GNU
+Parted identifies such partitions as having the "boot flag" set.)
+
+.TP
+.B *
+If Windows is to boot from a GPT disk, a partition of type "Microsoft
+Reserved" (
+.B "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 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
+.TP
+.B \-l
+List the partition tables for the specified devices and then exit.
+.PP
+
+Most interactions with
+.B gdisk
+occur with its interactive text-mode menus. The main menu provides the
+following options:
+
+.TP
+.B c
+Change the GPT name of a partition. This name is encoded as a UTF-16
+string, but
+.B gdisk
+supports only ASCII characters as names. For the most part, Linux ignores
+the partition name, but it may be important in some OSes.
+
+.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.
+
+.TP
+.B i
+Show detailed partition information. The summary information produced by
+the 'p' command necessarily omits many details, such as the partition's
+unique GUID and the translation of
+.BR "gdisk"'s
+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,
+.B gdisk
+compresses these into two-byte (four-digit hexadecimal) values that are
+related to their MBR codes. Specifically, the MBR code is multiplied by
+hexadecimal 0x0100. For instance, the code for Linux swap space in MBR is
+0x82, and it's 0x8200 in
+.BR "gdisk".
+A one-to-one correspondence is impossible, though. Most notably, many DOS,
+Windows, and Linux data partition codes correspond to a single GPT code
+(entered as 0x0700 in
+.BR "gdisk" ).
+Some OSes use a single MBR code but employ many more codes in GPT. For
+these,
+.B gdisk
+adds code numbers sequentially, such as 0xa500 for a FreeBSD disklabel,
+0xa501 for FreeBSD boot, 0xa502 for FreeBSD swap, and so on. Note that
+these two-byte codes are unique to
+.BR "gdisk".
+
+.TP
+.B m
+Print the menu. Type this command (or any other unrecognized command) to
+see a summary of available options.
+
+.TP
+.B n
+Create a new partition. This command is modelled after the equivalent
+.B fdisk
+option, although some differences exist. You enter a partition number,
+starting sector, and either an ending sector or increment (in integral
+multiples of sectors, kilobytes, megabytes, gigabytes, or terabytes). You
+must also set a partition type code.
+
+.TP
+.B o
+Clear out all partition data. This includes GPT header data,
+all partition definitions, and the protective MBR.
+
+.TP
+.B p
+Display basic partition summary data. This includes partition
+numbers, starting and ending sector numbers, partition sizes,
+.BR "gdisk"'s
+partition types codes, and partition names. For additional information,
+use the 'i' command.
+
+.TP
+.B q
+Quit from the program
+.IR "without saving data".
+Use it if you just wanted to view information or if you make a mistake and
+want to back out of all your changes.
+
+.TP
+.B s
+Sort partition entries. GPT partition numbers need not match the order of
+partitions on the disk. If you want them to match, you can use this option.
+Note that some partitioning utilities, such as GNU Parted, will sort
+partitions whenever they make changes. Such changes will be reflected in
+your Linux device filenames, so you may need to edit
+.IR "/etc/fstab"
+if you use this option.
+
+.TP
+.B t
+Change a single partition's type code. You enter the type code using a
+two-byte hexadecimal number, as described earlier. You may also enter a
+GUID directly, if you have one and
+.B gdisk
+doesn't know it.
+
+.TP
+.B v
+Verify disk. This option checks for a variety of problems, such as
+incorrect CRCs and mismatched main and backup data. This option does not
+automatically correct these problems, though; for that, you must use
+options on the experts' menu. If no problems are found, this command
+displays a summary of unallocated disk space.
+
+.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
+
+A few options on the experts' menu duplicate functionality on the main
+menu, for the sake of convenience; however, for the most part the experts'
+menu provides unusually dangerous or obscure options. These are:
+
+.TP
+.B a
+Set attributes. GPT provides a 64-bit attributes field that can be used to
+set partition features.
+.B gdisk
+supports four attributes:
+.IR "system partition",
+.IR "read-only",
+.IR "hidden",
+and
+.IR "do not automount".
+You can set other attributes, but their numbers aren't translated into
+anything useful. In practice, most OSes seem to ignore these attributes.
+.TP
+.B b
+Rebuild main GPT header from backup. You can use the backup GPT header to
+rebuild the main GPT header with this option. It's likely to be useful if
+your main GPT header was damaged or destroyed (say, by sloppy use of
+.IR "dd").
+.TP
+.B c
+Load backup partition table. Ordinarily,
+.B 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.
+
+.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
+Change partition GUID. You can enter a custom unique GUID for a partition
+using this option. (Note this refers to the GUID that uniquely identifies a
+partition, not to its type code.) Ordinarily,
+.B gdisk
+assigns this number randomly; however, you might want to adjust the number
+manually if you've wound up with the same GUID on two partitions.
+.TP
+.B g
+Change disk GUID. Each disk has a unique GUID code, which
+.B gdisk
+assigns randomly upon creation of the GPT data structures. You can generate
+a fresh random GUID or enter one manually with this option.
+.TP
+.B i
+Show detailed partition information. This option is identical to the 'i'
+option on the main menu.
+.TP
+.B k
+Save partition data to a backup file. You can back up your partition table
+to a disk file using this option. The resulting file is a binary file
+consisting of the protective MBR, the main GPT header, the backup GPT
+header, and one copy of the partition table, in that order.
+.TP
+.B l
+Load partition data from a backup file. This option is the reverse of the 'k'
+option. Note that restoring partition data from anything but the
+original disk is not recommended.
+.TP
+.B m
+Print the menu. This option (or any unrecognized entry) displays a summary
+of the menu options.
+.TP
+.B n
+Create a new protective MBR. Use this option if the current protective MBR
+is damaged in a way that
+.B gdisk
+doesn't automatically detect and correct.
+.TP
+.B o
+Print protective MBR data. You can see a summary of the protective MBR's
+partitions with this option. This may enable you to spot glaring problems
+or help identify the partitions in a hybrid MBR.
+.TP
+.B p
+Print the partition table. This option is identical to the 'p' option in
+the main menu.
+.TP
+.B q
+Quit without saving changes. This option is identical to the 'q' option in
+the main menu.
+.TP
+.B r
+Return to the main menu. You can go back to the main menu with this option.
+.TP
+.B s
+Resize partition table. The partition table may be resized with this
+option. The default 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. Linux imposes
+its own limits on the number of partitions, though.
+.TP
+.B v
+Verify disk. This option is identical to the 'v' option in the main menu.
+.TP
+.B w
+Write table to disk and exit. This option is identical to the 'w' option in
+the main menu.
+.PP
+
+In many cases, you can press the Enter key to select a default option when
+entering data. When only one option is possible,
+.B gdisk
+usually bypasses the prompt entirely.
+
+.SH BUGS
+As of August of 2009 (version 0.3.1),
+.B gdisk
+should be considered early beta software. Known bugs and
+limitations include:
+
+.TP
+.B *
+The program runs correctly only on little-endian (Intel and similar) CPUs.
+It should fail gracefully on PowerPC and other big-endian CPUs, but this
+hasn't been tested.
+
+.TP
+.B *
+The program compiles correctly only on Linux and Mac OS X. Both 64-bit
+(x86-64) and 32-bit (x86) versions for Linux have been tested, the former
+more thoroughly than the latter. The Mac OS X support was added with
+version 0.3.1 and has not been thoroughly tested.
+
+.TP
+.B *
+Under Mac OS X, the program will only save a partition table if no
+partitions from the disk are currently mounted. (This limitation does not
+exist in the Linux version of the program.)
+
+.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 *
+Only ASCII characters are supported in the partition name field. If an
+existing partition uses non-ASCII UTF-16 characters, they're likely to be
+corrupted in the 'i' menu option's display; however, they should be
+preserved when loading and saving partitions.
+
+.TP
+.B *
+The program can load only up to 124 logical partitions when converting from
+MBR format. This limit can be raised by changing the #define NUM_LOGICALS
+line in the
+.IR "mbr.cc"
+source code file and recompiling; however, such a change will require using
+a larger-than-normal GPT partition table. (The limit of 124 logical
+partitions was chosen because that number plus the four primary partitions
+equals the 128 partitions supported by the most common GPT partition table
+size.)
+
+.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.
+
+.TP
+.B *
+Converting from MBR supports only one extended partition. If multiple
+extended partitions are found, only the final extended partition's logical
+partitions are guaranteed to be converted intact; some or all of the
+earlier extended partition(s) logical partitions will be lost.
+
+.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 *
+If an MBR disk contains a FreeBSD disklabel partition, it's converted
+in-place as such rather than splitting out its constituent disklabel
+partitions into GPT partitions. Other OSes' disklabel partitions may not
+get appropriate GUID type codes at all.
+
+.TP
+.B *
+Booting after converting an MBR disk may 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
+Vista doesn't support booting from GPT disks.
+.PP
+
+.SH AUTHORS
+
+Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)
+
+Contributors:
+
+* Yves Blusseau (1otnwmz02@sneakemail.com)
+
+* One anonymous contributor
+
+.SH "SEE ALSO"
+.BR cfdisk (8),
+.BR fdisk (8),
+.BR mkfs (8),
+.BR parted (8),
+.BR sfdisk (8)
+
+.IR "http://en.wikipedia.org/wiki/GUID_Partition_Table"
+
+.IR "http://developer.apple.com/technotes/tn2006/tn2166.html"
+
+.IR "http://www.rodsbooks.com/gdisk/"
+
+.SH AVAILABILITY
+The gdisk command is part of the GPT fdisk package and is available from
+Rod Smith.
diff --git a/gdisk.cc b/gdisk.cc
new file mode 100644
index 0000000..7cfb2bf
--- /dev/null
+++ b/gdisk.cc
@@ -0,0 +1,270 @@
+// gdisk.cc
+// Program modelled after Linux fdisk, but it manipulates GPT partitions
+// rather than MBR partitions.
+//
+// by Rod Smith, February 2009
+
+//#include <iostream>
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include "mbr.h"
+#include "gpt.h"
+#include "support.h"
+
+// Function prototypes....
+// int ReadPartitions(char* filename, struct GPTData* theGPT);
+int DoCommand(char* filename, struct GPTData* theGPT);
+void ShowCommands(void);
+void ShowExpertCommands(void);
+int ExpertsMenu(char* filename, struct GPTData* theGPT);
+
+int main(int argc, char* argv[]) {
+ GPTData theGPT;
+ int doMore = 1;
+ char* device = NULL;
+
+ printf("GPT fdisk (gdisk) version 0.3.1\n\n");
+
+ if (argc == 2) { // basic usage
+ if (SizesOK()) {
+ doMore = theGPT.LoadPartitions(argv[1]);
+ while (doMore) {
+ doMore = DoCommand(argv[1], &theGPT);
+ } // while
+ } // if
+ } else if (argc == 3) { // usage with "-l" option
+ if (SizesOK()) {
+ if (strcmp(argv[1], "-l") == 0) {
+ device = argv[2];
+ } else if (strcmp(argv[2], "-l") == 0) {
+ device = argv[1];
+ } else { // 3 arguments, but none is "-l"
+ fprintf(stderr, "Usage: %s [-l] device_file\n", argv[0]);
+ } // if/elseif/else
+ if (device != NULL) {
+ doMore = theGPT.LoadPartitions(device);
+ if (doMore) theGPT.DisplayGPTData();
+ } // if
+ } // if
+ } else {
+ fprintf(stderr, "Usage: %s [-l] device_file\n", argv[0]);
+ } // if/else
+} // main
+
+// Accept a command and execute it. Returns 0 if the command includes
+// an exit condition (such as a q or w command), 1 if more commands
+// should be processed.
+int DoCommand(char* filename, struct GPTData* theGPT) {
+ char command, line[255];
+ int retval = 1;
+ PartTypes typeHelper;
+ uint32_t temp1, temp2;
+
+ printf("\nCommand (m for help): ");
+ fgets(line, 255, stdin);
+ sscanf(line, "%c", &command);
+ switch (command) {
+/* case 'b': case 'B':
+ GetGUID();
+ break; */
+ case 'c': case 'C':
+ if (theGPT->GetPartRange(&temp1, &temp2) > 0)
+ theGPT->SetName(theGPT->GetPartNum());
+ else
+ printf("No partitions\n");
+ break;
+ case 'd': case 'D':
+ theGPT->DeletePartition();
+ break;
+ case 'i': case 'I':
+ theGPT->ShowDetails();
+ break;
+ case 'l': case 'L':
+ typeHelper.ShowTypes();
+ break;
+ case 'n': case 'N':
+ theGPT->CreatePartition();
+ break;
+ case 'o': case 'O':
+ theGPT->ClearGPTData();
+// theGPT->protectiveMBR.MakeProtectiveMBR();
+// theGPT->BlankPartitions();
+ break;
+ case 'p': case 'P':
+ theGPT->DisplayGPTData();
+ break;
+ case 'q': case 'Q':
+ retval = 0;
+ break;
+ case 's': case 'S':
+ theGPT->SortGPT();
+ printf("You may need to edit /etc/fstab and/or your boot loader configuration!\n");
+ break;
+ case 't': case 'T':
+ theGPT->ChangePartType();
+ break;
+ case 'v': case 'V':
+ if (theGPT->Verify() > 0) { // problems found
+ printf("You may be able to correct the problems by using options on the experts\n"
+ "menu (press 'x' at the command prompt). Good luck!\n");
+ } // if
+ break;
+ case 'w': case 'W':
+ if (theGPT->SaveGPTData() == 1)
+ retval = 0;
+ break;
+ case 'x': case 'X':
+ retval = ExpertsMenu(filename, theGPT);
+ break;
+ default:
+ ShowCommands();
+ break;
+ } // switch
+ return (retval);
+} // DoCommand()
+
+void ShowCommands(void) {
+ printf("c\tchange a partition's name\n");
+ printf("d\tdelete a partition\n");
+ printf("i\tshow detailed information on a partition\n");
+ printf("l\tlist available partition types\n");
+ printf("m\tprint this menu\n");
+ printf("n\tadd a new partition\n");
+ printf("o\tcreate a new empty GUID partition table (GPT)\n");
+ printf("p\tprint the partition table\n");
+ printf("q\tquit without saving changes\n");
+ printf("s\tsort partitions\n");
+ printf("t\tchange a partition's type code\n");
+ printf("v\tverify disk\n");
+ printf("w\twrite table to disk and exit\n");
+ printf("x\textra functionality (experts only)\n");
+} // ShowCommands()
+
+// Accept a command and execute it. Returns 0 if the command includes
+// an exit condition (such as a q or w command), 1 if more commands
+// should be processed.
+int ExpertsMenu(char* filename, struct GPTData* theGPT) {
+ char command, line[255], buFile[255];
+ int retval = 1;
+ PartTypes typeHelper;
+ uint32_t pn;
+ uint32_t temp1, temp2;
+ int goOn = 1;
+
+ do {
+ printf("\nExpert command (m for help): ");
+ fgets(line, 255, stdin);
+ sscanf(line, "%c", &command);
+ switch (command) {
+ case 'a': case 'A':
+ if (theGPT->GetPartRange(&temp1, &temp2) > 0)
+ theGPT->SetAttributes(theGPT->GetPartNum());
+ else
+ printf("No partitions\n");
+ break;
+ case 'b': case 'B':
+ theGPT->RebuildMainHeader();
+ break;
+ case 'c': case 'C':
+ printf("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')
+ theGPT->LoadSecondTableAsMain();
+ break;
+ case 'd': case 'D':
+ theGPT->RebuildSecondHeader();
+ break;
+ case 'e': case 'E':
+ printf("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')
+ theGPT->LoadMainTable();
+ break;
+ case 'f': case 'F':
+ if (theGPT->GetPartRange(&temp1, &temp2) > 0) {
+ pn = theGPT->GetPartNum();
+ printf("Enter the partition's new unique GUID:\n");
+ theGPT->SetPartitionGUID(pn, GetGUID());
+ } else printf("No partitions\n");
+ break;
+ case 'g': case 'G':
+ printf("Enter the disk's unique GUID:\n");
+ theGPT->SetDiskGUID(GetGUID());
+ break;
+/* case 'h': case 'H':
+ theGPT->MakeHybrid();
+ break; */
+ case 'i': case 'I':
+ theGPT->ShowDetails();
+ break;
+ case 'k': case 'K':
+ printf("Enter backup filename to save: ");
+ fgets(line, 255, stdin);
+ sscanf(line, "%s", &buFile);
+ theGPT->SaveGPTBackup(buFile);
+ break;
+ case 'l': case 'L':
+ printf("Enter backup filename to load: ");
+ fgets(line, 255, stdin);
+ sscanf(line, "%s", &buFile);
+ theGPT->LoadGPTBackup(buFile);
+ break;
+ case 'n': case 'N':
+ theGPT->MakeProtectiveMBR();
+ break;
+ case 'o': case 'O':
+ theGPT->DisplayMBRData();
+ break;
+ case 'p': case 'P':
+ theGPT->DisplayGPTData();
+ break;
+ case 'q': case 'Q':
+ retval = 0;
+ goOn = 0;
+ break;
+ case 'r': case 'R':
+ goOn = 0;
+ break;
+ case 's': case 'S':
+ theGPT->ResizePartitionTable();
+ break;
+ case 'v': case 'V':
+ theGPT->Verify();
+ break;
+ case 'w': case 'W':
+ if (theGPT->SaveGPTData() == 1) {
+ retval = 0;
+ goOn = 0;
+ } // if
+ break;
+ default:
+ ShowExpertCommands();
+ break;
+ } // switch
+ } while (goOn);
+ return (retval);
+} // ExpertsMenu()
+
+void ShowExpertCommands(void) {
+ printf("a\tset attributes\n");
+ printf("b\tuse backup GPT header (rebuilding main)\n");
+ printf("c\tload backup partition table from disk (rebuilding main)\n");
+ printf("d\tuse main GPT header (rebuilding backup)\n");
+ printf("e\tload main partition table from disk (rebuilding backup)\n");
+ printf("f\tchange partition GUID\n");
+ printf("g\tchange disk GUID\n");
+// printf("h\tmake hybrid MBR\n");
+ printf("i\tshow detailed information on a partition\n");
+ printf("k\tsave partition data to a backup file\n");
+ printf("l\tload partition data from a backup file\n");
+ printf("m\tprint this menu\n");
+ printf("n\tcreate a new protective MBR\n");
+ printf("o\tprint protective MBR data\n");
+ printf("p\tprint the partition table\n");
+ printf("q\tquit without saving changes\n");
+ printf("r\treturn to main menu\n");
+ printf("s\tresize partition table\n");
+ printf("v\tverify disk\n");
+ printf("w\twrite table to disk and exit\n");
+} // ShowExpertCommands()
diff --git a/gpt.cc b/gpt.cc
new file mode 100644
index 0000000..83bf046
--- /dev/null
+++ b/gpt.cc
@@ -0,0 +1,1660 @@
+/* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition
+ data. */
+
+/* By Rod Smith, January to February, 2009 */
+
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include "crc32.h"
+#include "gpt.h"
+#include "support.h"
+#include "parttypes.h"
+#include "attributes.h"
+
+using namespace std;
+
+/****************************************
+ * *
+ * GPTData class and related structures *
+ * *
+ ****************************************/
+
+GPTData::GPTData(void) {
+ blockSize = SECTOR_SIZE; // set a default
+ diskSize = 0;
+ partitions = NULL;
+ state = gpt_valid;
+ strcpy(device, "");
+ mainCrcOk = 0;
+ secondCrcOk = 0;
+ mainPartsCrcOk = 0;
+ secondPartsCrcOk = 0;
+ srand((unsigned int) time(NULL));
+ SetGPTSize(NUM_GPT_ENTRIES);
+} // GPTData default constructor
+
+// The following constructor loads GPT data from a device file
+GPTData::GPTData(char* filename) {
+ blockSize = SECTOR_SIZE; // set a default
+ diskSize = 0;
+ partitions = NULL;
+ state = gpt_invalid;
+ strcpy(device, "");
+ mainCrcOk = 0;
+ secondCrcOk = 0;
+ mainPartsCrcOk = 0;
+ secondPartsCrcOk = 0;
+ srand((unsigned int) time(NULL));
+ LoadPartitions(filename);
+} // GPTData(char* filename) constructor
+
+GPTData::~GPTData(void) {
+ free(partitions);
+} // GPTData destructor
+
+// Resizes GPT to specified number of entries. Creates a new table if
+// necessary, copies data if it already exists.
+int GPTData::SetGPTSize(uint32_t numEntries) {
+ struct GPTPartition* newParts;
+ struct GPTPartition* trash;
+ uint32_t i, high, copyNum;
+ int allOK = 1;
+
+ // First, adjust numEntries upward, if necessary, to get a number
+ // that fills the allocated sectors
+ i = blockSize / GPT_SIZE;
+ if ((numEntries % i) != 0) {
+ printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
+ numEntries = ((numEntries / i) + 1) * i;
+ printf("to %lu to fill the sector\n", (unsigned long) numEntries);
+ } // if
+
+ newParts = (struct GPTPartition*) calloc(numEntries, sizeof (struct GPTPartition));
+ if (newParts != NULL) {
+ if (partitions != NULL) { // existing partitions; copy them over
+ GetPartRange(&i, &high);
+ if (numEntries < (high + 1)) { // Highest entry too high for new #
+ printf("The highest-numbered partition is %lu, which is greater than the requested\n"
+ "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
+ (unsigned long) (high + 1), numEntries);
+ allOK = 0;
+ } else { // go ahead with copy
+ if (numEntries < mainHeader.numParts)
+ copyNum = numEntries;
+ else
+ copyNum = mainHeader.numParts;
+ for (i = 0; i < copyNum; i++) {
+ newParts[i] = partitions[i];
+ } // for
+ trash = partitions;
+ partitions = newParts;
+ free(trash);
+ } // if
+ } else { // No existing partition table; just create it
+ partitions = newParts;
+ } // if/else existing partitions
+ mainHeader.numParts = numEntries;
+ secondHeader.numParts = numEntries;
+ mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
+ secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
+ mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
+ secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
+ secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
+ if (diskSize > 0)
+ CheckGPTSize();
+ } else { // Bad memory allocation
+ fprintf(stderr, "Error allocating memory for partition table!\n");
+ allOK = 0;
+ } // if/else
+ return (allOK);
+} // GPTData::SetGPTSize()
+
+// Checks to see if the GPT tables overrun existing partitions; if they
+// do, issues a warning but takes no action. Returns 1 if all is OK, 0
+// if problems were detected.
+int GPTData::CheckGPTSize(void) {
+ uint64_t overlap, firstUsedBlock, lastUsedBlock;
+ uint32_t i;
+ int allOK = 1;
+
+ // first, locate the first & last used blocks
+ firstUsedBlock = UINT64_MAX;
+ lastUsedBlock = 0;
+ for (i = 0; i < mainHeader.numParts; i++) {
+ if ((partitions[i].firstLBA < firstUsedBlock) &&
+ (partitions[i].firstLBA != 0))
+ firstUsedBlock = partitions[i].firstLBA;
+ if (partitions[i].lastLBA > lastUsedBlock)
+ lastUsedBlock = partitions[i].lastLBA;
+ } // 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;
+ printf("Warning! Main partition table overlaps the first partition by %lu\n"
+ "blocks! Try reducing the partition table size by %lu entries.\n",
+ (unsigned long) overlap, (unsigned long) (overlap * 4));
+ printf("(Use the 's' item on the experts' menu.)\n");
+ allOK = 0;
+ } // Problem at start of disk
+ if (mainHeader.lastUsableLBA < lastUsedBlock) {
+ overlap = lastUsedBlock - mainHeader.lastUsableLBA;
+ printf("Warning! Secondary partition table overlaps the last partition by %lu\n"
+ "blocks! Try reducing the partition table size by %lu entries.\n",
+ (unsigned long) overlap, (unsigned long) (overlap * 4));
+ printf("(Use the 's' item on the experts' menu.)\n");
+ allOK = 0;
+ } // Problem at end of disk
+ } // if (diskSize != 0)
+ return allOK;
+} // GPTData::CheckGPTSize()
+
+// Read GPT data from a disk.
+int GPTData::LoadPartitions(char* deviceFilename) {
+ int fd, err;
+ int allOK = 1, i;
+ uint64_t firstBlock, lastBlock;
+
+ if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
+ // store disk information....
+ diskSize = disksize(fd, &err);
+ blockSize = (uint32_t) GetBlockSize(fd);
+ strcpy(device, deviceFilename);
+
+ // Read the MBR
+ protectiveMBR.ReadMBRData(fd);
+
+ // Load the GPT data, whether or not it's valid
+ ForceLoadGPTData(fd);
+
+ switch (UseWhichPartitions()) {
+ case use_mbr:
+// printf("In LoadPartitions(), using MBR\n");
+ XFormPartitions(&protectiveMBR);
+ break;
+ case use_gpt:
+ break;
+ case use_new:
+// printf("In LoadPartitions(), making new\n");
+ ClearGPTData();
+ protectiveMBR.MakeProtectiveMBR();
+ break;
+ } // switch
+
+ // Now find the first and last sectors used by partitions...
+ if (allOK) {
+ firstBlock = mainHeader.backupLBA; // start high
+ lastBlock = 0; // start low
+ for (i = 0; i < mainHeader.numParts; i++) {
+ if ((partitions[i].firstLBA < firstBlock) &&
+ (partitions[i].firstLBA > 0))
+ firstBlock = partitions[i].firstLBA;
+ if (partitions[i].lastLBA > lastBlock)
+ lastBlock = partitions[i].lastLBA;
+ } // for
+ } // if
+ CheckGPTSize();
+ } else {
+ allOK = 0;
+ fprintf(stderr, "Problem opening %s for reading!\n",
+ deviceFilename);
+ } // 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(int fd) {
+ int allOK = 1, validHeaders;
+ off_t seekTo;
+ char* storage;
+ uint32_t newCRC, sizeOfParts;
+
+ // Seek to and read the main GPT header
+ lseek64(fd, 512, SEEK_SET);
+ read(fd, &mainHeader, 512); // read main GPT header
+ mainCrcOk = CheckHeaderCRC(&mainHeader);
+
+ // Load backup header, check its CRC, and store the results of
+ // the check for future reference
+ seekTo = (diskSize * blockSize) - UINT64_C(512);
+ if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
+ read(fd, &secondHeader, 512); // read secondary GPT header
+ secondCrcOk = CheckHeaderCRC(&secondHeader);
+ } else {
+ allOK = 0;
+ state = gpt_invalid;
+ fprintf(stderr, "Unable to seek to secondary GPT at sector %llu!\n",
+ diskSize - (UINT64_C(1)));
+ } // if/else lseek
+
+ // 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!
+ 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 == 2) { // valid backup header, invalid main header
+ printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
+ "from backup!\n");
+ RebuildMainHeader();
+ mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
+ } else if (validHeaders == 1) { // valid main header, invalid backup
+ printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
+ "backup header from main header.\n");
+ RebuildSecondHeader();
+ secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
+ } // if/else/if
+
+ // Load the main partition table, including storing results of its
+ // CRC check
+ if (LoadMainTable() == 0)
+ allOK = 0;
+
+ // Load backup 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
+ seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
+ if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
+ sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
+ storage = (char*) malloc(sizeOfParts);
+ read(fd, storage, sizeOfParts);
+ newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
+ free(storage);
+ secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
+ } // if
+
+ // Check for valid CRCs and warn if there are problems
+ if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
+ (secondPartsCrcOk == 0)) {
+ printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
+ state = gpt_corrupt;
+ } // if
+ } else {
+ state = gpt_invalid;
+ } // if/else
+ return allOK;
+} // GPTData::ForceLoadGPTData()
+
+// Loads the partition tables pointed to by the main GPT header. The
+// main GPT header in memory MUST be valid for this call to do anything
+// sensible!
+int GPTData::LoadMainTable(void) {
+ int fd, retval = 0;
+ uint32_t newCRC, sizeOfParts;
+
+ if ((fd = open(device, O_RDONLY)) != -1) {
+ // Set internal data structures for number of partitions on the disk
+ SetGPTSize(mainHeader.numParts);
+
+ // Load main partition table, and record whether its CRC
+ // matches the stored value
+ lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
+ sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
+ read(fd, partitions, sizeOfParts);
+ newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
+ mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
+ retval = 1;
+ } // if
+ return retval;
+} // GPTData::LoadMainTable()
+
+// Examines the MBR & GPT data, and perhaps asks the user questions, to
+// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
+// or create a new set of partitions (use_new)
+WhichToUse GPTData::UseWhichPartitions(void) {
+ WhichToUse which = use_new;
+ MBRValidity mbrState;
+ int answer;
+
+ mbrState = protectiveMBR.GetValidity();
+
+ if ((state == gpt_invalid) && (mbrState == mbr)) {
+ printf("\n\a***************************************************************\n"
+ "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
+ "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
+ "you don't want to convert your MBR partitions to GPT format!\n"
+ "***************************************************************\n\n");
+ which = use_mbr;
+ } // if
+ if ((state == gpt_valid) && (mbrState == gpt)) {
+ printf("Found valid GPT with protective MBR; using GPT.\n");
+ which = use_gpt;
+ } // if
+ if ((state == gpt_valid) && (mbrState == invalid)) {
+ printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
+ which = use_gpt;
+ protectiveMBR.MakeProtectiveMBR();
+ } // if
+ if ((state == gpt_valid) && (mbrState == mbr)) {
+ printf("Found valid MBR and GPT. Which do you want to use?\n");
+ answer = GetNumber(1, 3, 2, (char*) " 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;
+ protectiveMBR.MakeProtectiveMBR();
+ printf("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) {
+ printf("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, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
+ if (answer == 1) {
+ which = use_mbr;
+// protectiveMBR.MakeProtectiveMBR();
+ } else if (answer == 2) {
+ which = use_gpt;
+ } else which = use_new;
+ } else if (mbrState == invalid) {
+ printf("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, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
+ if (answer == 1) {
+ which = use_gpt;
+ } else which = use_new;
+ } else {
+ printf("\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");
+ } // if
+ } // if
+
+ if (which == use_new)
+ printf("Creating new GPT entries.\n");
+
+ return which;
+} // UseWhichPartitions()
+
+void GPTData::ResizePartitionTable(void) {
+ int newSize;
+ char prompt[255];
+ uint32_t curLow, curHigh;
+
+ printf("Current partition table size is %lu.\n",
+ (unsigned long) mainHeader.numParts);
+ GetPartRange(&curLow, &curHigh);
+ curHigh++; // since GetPartRange() returns numbers starting from 0...
+ // There's no point in having fewer than four partitions....
+ if (curHigh < 4)
+ curHigh = 4;
+ sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
+ (int) NUM_GPT_ENTRIES);
+ newSize = GetNumber(4, 65535, 128, prompt);
+ if (newSize < 128) {
+ printf("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);
+} // GPTData::ResizePartitionTable()
+
+// 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 = mainHeader.numParts + 1; // code for "not found"
+ *high = 0;
+ if (mainHeader.numParts > 0) { // only try if partition table exists...
+ for (i = 0; i < mainHeader.numParts; i++) {
+ if (partitions[i].firstLBA != UINT64_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 == (mainHeader.numParts + 1)) *low = i;
+ numFound++;
+ } // if
+ } // for
+ } // if
+
+ // 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 == (mainHeader.numParts + 1))
+ *low = 0;
+ return numFound;
+} // GPTData::GetPartRange()
+
+// Display the basic GPT data
+void GPTData::DisplayGPTData(void) {
+ int i, j;
+ char sizeInSI[255]; // String to hold size of disk in SI units
+ char tempStr[255];
+ uint64_t temp, totalFree;
+
+ BytesToSI(diskSize * blockSize, sizeInSI);
+ printf("Disk %s: %lu sectors, %s\n", device,
+ (unsigned long) diskSize, sizeInSI);
+ printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
+ printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
+ printf("First usable sector is %lu, last usable sector is %lu\n",
+ (unsigned long) mainHeader.firstUsableLBA,
+ (unsigned long) mainHeader.lastUsableLBA);
+ totalFree = FindFreeBlocks(&i, &temp);
+ printf("Total free space is %llu sectors (%s)\n", totalFree,
+ BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
+ printf("\nNumber Start (block) End (block) Size Code Name\n");
+ for (i = 0; i < mainHeader.numParts; i++) {
+ if (partitions[i].firstLBA != 0) {
+ BytesToSI(blockSize * (partitions[i].lastLBA - partitions[i].firstLBA + 1),
+ sizeInSI);
+ printf("%4d %14lu %14lu ", i + 1, (unsigned long) partitions[i].firstLBA,
+ (unsigned long) partitions[i].lastLBA);
+ printf(" %-10s %04X ", sizeInSI,
+ typeHelper.GUIDToID(partitions[i].partitionType));
+ j = 0;
+ while ((partitions[i].name[j] != '\0') && (j < 44)) {
+ printf("%c", partitions[i].name[j]);
+ j += 2;
+ } // while
+ printf("\n");
+ } // if
+ } // for
+} // GPTData::DisplayGPTData()
+
+// Get partition number from user and then call ShowPartDetails(partNum)
+// to show its detailed information
+void GPTData::ShowDetails(void) {
+ int partNum;
+ uint32_t low, high;
+
+ if (GetPartRange(&low, &high) > 0) {
+ partNum = GetPartNum();
+ ShowPartDetails(partNum);
+ } else {
+ printf("No partitions\n");
+ } // if/else
+} // GPTData::ShowDetails()
+
+// Show detailed information on the specified partition
+void GPTData::ShowPartDetails(uint32_t partNum) {
+ char temp[255];
+ int i;
+ uint64_t size;
+
+ if (partitions[partNum].firstLBA != 0) {
+ printf("Partition GUID code: %s ", GUIDToStr(partitions[partNum].partitionType, temp));
+ printf("(%s)\n", typeHelper.GUIDToName(partitions[partNum].partitionType, temp));
+ printf("Partition unique GUID: %s\n", GUIDToStr(partitions[partNum].uniqueGUID, temp));
+
+ printf("First sector: %llu (at %s)\n", (unsigned long long)
+ partitions[partNum].firstLBA,
+ BytesToSI(partitions[partNum].firstLBA * blockSize, temp));
+ printf("Last sector: %llu (at %s)\n", (unsigned long long)
+ partitions[partNum].lastLBA,
+ BytesToSI(partitions[partNum].lastLBA * blockSize, temp));
+ size = (partitions[partNum].lastLBA - partitions[partNum].firstLBA + 1);
+ printf("Partition size: %llu sectprs (%s)\n", (unsigned long long)
+ size, BytesToSI(size * ((uint64_t) blockSize), temp));
+ printf("Attribute flags: %016llx\n", (unsigned long long)
+ partitions[partNum].attributes);
+ printf("Partition name: ");
+ i = 0;
+ while ((partitions[partNum].name[i] != '\0') && (i < NAME_SIZE)) {
+ printf("%c", partitions[partNum].name[i]);
+ i += 2;
+ } // while
+ printf("\n");
+ } else {
+ printf("Partition #%d does not exist.", (int) (partNum + 1));
+ } // if
+} // GPTData::ShowPartDetails()
+
+// Interactively create a partition
+void GPTData::CreatePartition(void) {
+ uint64_t firstBlock, lastBlock, sector;
+ char prompt[255];
+ int partNum, firstFreePart = 0;
+
+ // Find first free partition...
+ while (partitions[firstFreePart].firstLBA != 0) {
+ firstFreePart++;
+ } // while
+
+ if (((firstBlock = FindFirstAvailable()) != 0) &&
+ (firstFreePart < mainHeader.numParts)) {
+ lastBlock = FindLastAvailable(firstBlock);
+
+ // Get partition number....
+ do {
+ sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
+ mainHeader.numParts, firstFreePart + 1);
+ partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
+ firstFreePart + 1, prompt) - 1;
+ if (partitions[partNum].firstLBA != 0)
+ printf("partition %d is in use.\n", partNum + 1);
+ } while (partitions[partNum].firstLBA != 0);
+
+ // Get first block for new partition...
+ sprintf(prompt, "First sector (%llu-%llu, default = %llu): ", firstBlock,
+ lastBlock, firstBlock);
+ do {
+ sector = GetNumber(firstBlock, lastBlock, firstBlock, prompt);
+ } while (IsFree(sector) == 0);
+ firstBlock = sector;
+
+ // Get last block for new partitions...
+ lastBlock = FindLastInFree(firstBlock);
+ sprintf(prompt, "Last sector or +size or +sizeM or +sizeK (%llu-%llu, default = %d): ",
+ firstBlock, lastBlock, lastBlock);
+ do {
+ sector = GetLastSector(firstBlock, lastBlock, prompt);
+ } while (IsFree(sector) == 0);
+ lastBlock = sector;
+
+ partitions[partNum].firstLBA = firstBlock;
+ partitions[partNum].lastLBA = lastBlock;
+
+ // rand() is only 32 bits on 32-bit systems, so multiply together to
+ // fill a 64-bit value.
+ partitions[partNum].uniqueGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
+ partitions[partNum].uniqueGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
+ ChangeGPTType(&partitions[partNum]);
+ } else {
+ printf("No free sectors available\n");
+ } // if/else
+} // GPTData::CreatePartition()
+
+// Interactively delete a partition (duh!)
+void GPTData::DeletePartition(void) {
+ int partNum;
+ uint32_t low, high;
+ char prompt[255];
+
+ if (GetPartRange(&low, &high) > 0) {
+ sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
+ partNum = GetNumber(low + 1, high + 1, low, prompt);
+ BlankPartition(&partitions[partNum - 1]);
+ } else {
+ printf("No partitions\n");
+ } // if/else
+} // GPTData::DeletePartition
+
+// 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 < mainHeader.numParts; i++) {
+ if ((first >= partitions[i].firstLBA) &&
+ (first <= partitions[i].lastLBA)) { // in existing part.
+ first = partitions[i].lastLBA + 1;
+ firstMoved = 1;
+ } // if
+ } // for
+ } while (firstMoved == 1);
+ if (first > mainHeader.lastUsableLBA)
+ first = 0;
+ return (first);
+} // GPTData::FindFirstAvailable()
+
+// Find the last available block on the disk at or after the start
+// block. Returns 0 if there are no available partitions after
+// (or including) start.
+uint64_t GPTData::FindLastAvailable(uint64_t start) {
+ 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 < mainHeader.numParts; i++) {
+ if ((last >= partitions[i].firstLBA) &&
+ (last <= partitions[i].lastLBA)) { // in existing part.
+ last = partitions[i].firstLBA - 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 < mainHeader.numParts; i++) {
+ if ((nearestStart > partitions[i].firstLBA) &&
+ (partitions[i].firstLBA > start)) {
+ nearestStart = partitions[i].firstLBA - 1;
+ } // if
+ } // for
+ return (nearestStart);
+} // GPTData::FindLastInFree()
+
+// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
+int GPTData::IsFree(uint64_t sector) {
+ int isFree = 1;
+ uint32_t i;
+
+ for (i = 0; i < mainHeader.numParts; i++) {
+ if ((sector >= partitions[i].firstLBA) &&
+ (sector <= partitions[i].lastLBA)) {
+ isFree = 0;
+ } // if
+ } // for
+ if ((sector < mainHeader.firstUsableLBA) ||
+ (sector > mainHeader.lastUsableLBA)) {
+ isFree = 0;
+ } // if
+ return (isFree);
+} // GPTData::IsFree()
+
+int GPTData::XFormPartitions(MBRData* origParts) {
+ int i, j;
+ int numToConvert;
+ uint8_t origType;
+
+ // Clear out old data & prepare basics....
+ ClearGPTData();
+
+ // Convert the smaller of the # of GPT or MBR partitions
+ if (mainHeader.numParts > (NUM_LOGICALS + 4))
+ numToConvert = NUM_LOGICALS + 4;
+ else
+ numToConvert = mainHeader.numParts;
+
+// printf("In XFormPartitions(), numToConvert = %d\n", numToConvert);
+
+ for (i = 0; i < numToConvert; i++) {
+ origType = origParts->GetType(i);
+// printf("Converting partition of type 0x%02X\n", (int) origType);
+
+ // don't convert extended partitions or null (non-existent) partitions
+ if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x00)) {
+ partitions[i].firstLBA = (uint64_t) origParts->GetFirstSector(i);
+ partitions[i].lastLBA = partitions[i].firstLBA + (uint64_t)
+ origParts->GetLength(i) - 1;
+ partitions[i].partitionType = typeHelper.IDToGUID(((uint16_t) origType) * 0x0100);
+
+ // Create random unique GUIDs for the partitions
+ // rand() is only 32 bits, so multiply together to fill a 64-bit value
+ partitions[i].uniqueGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
+ partitions[i].uniqueGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
+ partitions[i].attributes = 0;
+ for (j = 0; j < NAME_SIZE; j++)
+ partitions[i].name[j] = '\0';
+ } // if
+ } // 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;
+
+ return (1);
+} // XFormPartitions()
+
+// Sort the GPT entries, eliminating gaps and making for a logical
+// ordering. Relies on QuickSortGPT() for the bulk of the work
+void GPTData::SortGPT(void) {
+ int i, lastPart = 0;
+ struct GPTPartition temp;
+
+ // First, find the last partition with data, so as not to
+ // spend needless time sorting empty entries....
+ for (i = 0; i < GPT_SIZE; i++) {
+ if (partitions[i].firstLBA > 0)
+ lastPart = i;
+ } // for
+
+ // Now swap empties with the last partitions, to simplify the logic
+ // in the Quicksort function....
+ i = 0;
+ while (i < lastPart) {
+ if (partitions[i].firstLBA == 0) {
+ temp = partitions[i];
+ partitions[i] = partitions[lastPart];
+ partitions[lastPart] = temp;
+ lastPart--;
+ } // if
+ i++;
+ } // while
+
+ // Now call the recursive quick sort routine to do the real work....
+ QuickSortGPT(partitions, 0, lastPart);
+} // GPTData::SortGPT()
+
+// Recursive quick sort algorithm for GPT partitions. Note that if there
+// are any empties in the specified range, they'll be sorted to the
+// start, resulting in a sorted set of partitions that begins with
+// partition 2, 3, or higher.
+void QuickSortGPT(struct GPTPartition* partitions, int start, int finish) {
+ uint64_t starterValue; // starting location of median partition
+ int left, right;
+ struct GPTPartition temp;
+
+ left = start;
+ right = finish;
+ starterValue = partitions[(start + finish) / 2].firstLBA;
+ do {
+ while (partitions[left].firstLBA < starterValue)
+ left++;
+ while (partitions[right].firstLBA > starterValue)
+ right--;
+ if (left <= right) {
+ temp = partitions[left];
+ partitions[left] = partitions[right];
+ partitions[right] = temp;
+ left++;
+ right--;
+ } // if
+ } while (left <= right);
+ if (start < right) QuickSortGPT(partitions, start, right);
+ if (finish > left) QuickSortGPT(partitions, left, finish);
+} // QuickSortGPT()
+
+// Blank (delete) a single partition
+void BlankPartition(struct GPTPartition* partition) {
+ int j;
+
+ partition->uniqueGUID.data1 = 0;
+ partition->uniqueGUID.data2 = 0;
+ partition->partitionType.data1 = 0;
+ partition->partitionType.data2 = 0;
+ partition->firstLBA = 0;
+ partition->lastLBA = 0;
+ partition->attributes = 0;
+ for (j = 0; j < NAME_SIZE; j++)
+ partition->name[j] = '\0';
+} // BlankPartition
+
+// Blank the partition array
+void GPTData::BlankPartitions(void) {
+ uint32_t i;
+
+ for (i = 0; i < mainHeader.numParts; i++) {
+ BlankPartition(&partitions[i]);
+ } // for
+} // GPTData::BlankPartitions()
+
+// Set up data structures for entirely new set of partitions on the
+// specified device. Returns 1 if OK, 0 if there were problems.
+int GPTData::ClearGPTData(void) {
+ int goOn, i;
+
+ // Set up the partition table....
+ free(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 = (uint32_t) HEADER_SIZE;
+ mainHeader.reserved = 0;
+ mainHeader.currentLBA = UINT64_C(1);
+ mainHeader.partitionEntriesLBA = (uint64_t) 2;
+ mainHeader.sizeOfPartitionEntries = GPT_SIZE;
+ for (i = 0; i < GPT_RESERVED; i++) {
+ mainHeader.reserved2[i] = '\0';
+ } // for
+
+ // 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
+ // rand() is only 32 bits, so multiply together to fill a 64-bit value
+ mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
+ mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
+
+ // Copy main header to backup header
+ RebuildSecondHeader();
+
+ // Blank out the partitions array....
+ BlankPartitions();
+ return (goOn);
+} // GPTData::ClearGPTData()
+
+// Returns 1 if the two partitions overlap, 0 if they don't
+int TheyOverlap(struct GPTPartition* first, struct GPTPartition* second) {
+ int theyDo = 0;
+
+ // Don't bother checking unless these are defined (both start and end points
+ // are 0 for undefined partitions, so just check the start points)
+ if ((first->firstLBA != 0) && (second->firstLBA != 0)) {
+ if ((first->firstLBA < second->lastLBA) && (first->lastLBA >= second->firstLBA))
+ theyDo = 1;
+ if ((second->firstLBA < first->lastLBA) && (second->lastLBA >= first->firstLBA))
+ theyDo = 1;
+ } // if
+ return (theyDo);
+} // Overlap()
+
+// Change the type code on the specified partition.
+// Note: The GPT CRCs must be recomputed after calling this function!
+void ChangeGPTType(struct GPTPartition* part) {
+ char typeName[255], line[255];
+ uint16_t typeNum = 0xFFFF;
+ PartTypes typeHelper;
+ GUIDData newType;
+
+ printf("Current type is '%s'\n", typeHelper.GUIDToName(part->partitionType, typeName));
+ while ((!typeHelper.Valid(typeNum)) && (typeNum != 0)) {
+ printf("Hex code (L to show codes, 0 to enter raw code): ");
+ fgets(line, 255, stdin);
+ sscanf(line, "%x", &typeNum);
+ if (line[0] == 'L')
+ typeHelper.ShowTypes();
+ } // while
+ if (typeNum != 0) // user entered a code, so convert it
+ newType = typeHelper.IDToGUID(typeNum);
+ else // user wants to enter the GUID directly, so do that
+ newType = GetGUID();
+ part->partitionType = newType;
+ printf("Changed system type of partition to '%s'\n",
+ typeHelper.GUIDToName(part->partitionType, typeName));
+} // ChangeGPTType()
+
+// Prompt user for a partition number, then change its type code
+// using ChangeGPTType(struct GPTPartition*) function.
+void GPTData::ChangePartType(void) {
+ int partNum;
+ uint32_t low, high;
+
+ if (GetPartRange(&low, &high) > 0) {
+ partNum = GetPartNum();
+ ChangeGPTType(&partitions[partNum]);
+ } else {
+ printf("No partitions\n");
+ } // if/else
+} // GPTData::ChangePartType()
+
+// Prompts user for partition number and returns the result.
+uint32_t GPTData::GetPartNum(void) {
+ uint32_t partNum;
+ uint32_t low, high;
+ char prompt[255];
+
+ if (GetPartRange(&low, &high) > 0) {
+ sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
+ partNum = GetNumber(low + 1, high + 1, low, prompt);
+ } else partNum = 1;
+ return (partNum - 1);
+} // GPTData::GetPartNum()
+
+// Prompt user for attributes to change on the specified partition
+// and change them.
+void GPTData::SetAttributes(uint32_t partNum) {
+ Attributes theAttr;
+
+ theAttr.SetAttributes(partitions[partNum].attributes);
+ theAttr.DisplayAttributes();
+ theAttr.ChangeAttributes();
+ partitions[partNum].attributes = theAttr.GetAttributes();
+} // GPTData::SetAttributes()
+
+// Set the name for a partition to theName, or prompt for a name if
+// theName is a NULL pointer. Note that theName is a standard C-style
+// string, although the GUID partition definition requires a UTF-16LE
+// string. This function creates a simple-minded copy for this.
+void GPTData::SetName(uint32_t partNum, char* theName) {
+ char newName[NAME_SIZE]; // New name
+ int i;
+
+ // Blank out new name string, just to be on the safe side....
+ for (i = 0; i < NAME_SIZE; i++)
+ newName[i] = '\0';
+
+ if (theName == NULL) { // No name specified, so get one from the user
+ printf("Enter name: ");
+ fgets(newName, NAME_SIZE / 2, stdin);
+
+ // Input is likely to include a newline, so remove it....
+ i = strlen(newName);
+ if (newName[i - 1] == '\n')
+ newName[i - 1] = '\0';
+ } else {
+ strcpy(newName, theName);
+ } // if
+
+ // Copy the C-style ASCII string from newName into a form that the GPT
+ // table will accept....
+ for (i = 0; i < NAME_SIZE; i++) {
+ if ((i % 2) == 0) {
+ partitions[partNum].name[i] = newName[(i / 2)];
+ } else {
+ partitions[partNum].name[i] = '\0';
+ } // if/else
+ } // for
+} // 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 < mainHeader.numParts) {
+ if (partitions[pn].firstLBA != UINT64_C(0)) {
+ partitions[pn].uniqueGUID = theGUID;
+ retval = 1;
+ } // if
+ } // if
+ return retval;
+} // GPTData::SetPartitionGUID()
+
+// 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 just checks the GPT
+// signature and revision numbers, not CRCs or other data.
+int GPTData::CheckHeaderValidity(void) {
+ int valid = 3;
+
+ if (mainHeader.signature != GPT_SIGNATURE) {
+ valid -= 1;
+ printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
+ (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
+ } else if ((mainHeader.revision != 0x00010000) && valid) {
+ valid -= 1;
+ printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
+ (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
+ } // if/else/if
+
+ if (secondHeader.signature != GPT_SIGNATURE) {
+ valid -= 2;
+ printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
+ (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
+ } else if ((secondHeader.revision != 0x00010000) && valid) {
+ valid -= 2;
+ printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
+ (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
+ } // if/else/if
+
+ return valid;
+} // GPTData::CheckHeaderValidity()
+
+// Check the header CRC to see if it's OK...
+int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
+ uint32_t oldCRC, newCRC;
+
+ // Back up old header and then blank it, since it must be 0 for
+ // computation to be valid
+ oldCRC = header->headerCRC;
+ header->headerCRC = UINT32_C(0);
+
+ // Initialize CRC functions...
+ chksum_crc32gentab();
+
+ // Compute CRC, restore original, and return result of comparison
+ newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
+ mainHeader.headerCRC = oldCRC;
+ return (oldCRC == newCRC);
+} // GPTData::CheckHeaderCRC()
+
+// Recompute all the CRCs. Must be called before saving if any changes
+// have been made.
+void GPTData::RecomputeCRCs(void) {
+ uint32_t crc;
+
+ // Initialize CRC functions...
+ chksum_crc32gentab();
+
+ // Compute CRC of partition tables & store in main and secondary headers
+ crc = chksum_crc32((unsigned char*) partitions, mainHeader.numParts * GPT_SIZE);
+ mainHeader.partitionEntriesCRC = crc;
+ secondHeader.partitionEntriesCRC = crc;
+
+ // Zero out GPT tables' own CRCs (required for correct computation)
+ mainHeader.headerCRC = 0;
+ secondHeader.headerCRC = 0;
+
+ // Compute & store CRCs of main & secondary headers...
+ crc = chksum_crc32((unsigned char*) &mainHeader, HEADER_SIZE);
+ mainHeader.headerCRC = crc;
+ crc = chksum_crc32((unsigned char*) &secondHeader, HEADER_SIZE);
+ secondHeader.headerCRC = crc;
+} // GPTData::RecomputeCRCs()
+
+// 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, numSegments, i, j;
+ uint64_t totalFree, largestSegment;
+ char tempStr[255], siTotal[255], siLargest[255];
+
+ // First, check for CRC errors in the GPT data....
+ if (!mainCrcOk) {
+ problems++;
+ printf("\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\n");
+ } // if
+ if (!mainPartsCrcOk) {
+ problems++;
+ printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
+ "corrupt. Consider loading the backup partition table.\n");
+ } // if
+ if (!secondCrcOk) {
+ problems++;
+ printf("\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.\n");
+ } // if
+ if (!secondPartsCrcOk) {
+ problems++;
+ printf("\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 critical main and backup GPT entries match
+ if (mainHeader.currentLBA != secondHeader.backupLBA) {
+ problems++;
+ printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
+ "match the backup GPT header's LBA pointer(%llu)\n",
+ (unsigned long long) mainHeader.currentLBA,
+ (unsigned long long) secondHeader.backupLBA);
+ } // if
+ if (mainHeader.backupLBA != secondHeader.currentLBA) {
+ problems++;
+ printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
+ "match the backup GPT header's current LBA pointer (%llu)\n",
+ (unsigned long long) mainHeader.backupLBA,
+ (unsigned long long) secondHeader.currentLBA);
+ } // if
+ if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
+ problems++;
+ printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
+ "match the backup GPT header's first usable LBA pointer (%llu)\n",
+ (unsigned long long) mainHeader.firstUsableLBA,
+ (unsigned long long) secondHeader.firstUsableLBA);
+ } // if
+ if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
+ problems++;
+ printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
+ "match the backup GPT header's last usable LBA pointer (%llu)\n",
+ (unsigned long long) mainHeader.lastUsableLBA,
+ (unsigned long long) secondHeader.lastUsableLBA);
+ } // if
+ if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
+ (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
+ problems++;
+ printf("\nProblem: main header's disk GUID (%s) doesn't\n",
+ GUIDToStr(mainHeader.diskGUID, tempStr));
+ printf("match the backup GPT header's disk GUID (%s)\n",
+ GUIDToStr(secondHeader.diskGUID, tempStr));
+ } // if
+ if (mainHeader.numParts != secondHeader.numParts) {
+ problems++;
+ printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
+ "match the backup GPT header's number of partitions (%lu)\n",
+ (unsigned long) mainHeader.numParts,
+ (unsigned long) secondHeader.numParts);
+ } // if
+ if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
+ problems++;
+ printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
+ "match the backup GPT header's size of partition entries (%lu)\n",
+ (unsigned long) mainHeader.sizeOfPartitionEntries,
+ (unsigned long) secondHeader.sizeOfPartitionEntries);
+ } // if
+
+ // Now check for a few other miscellaneous problems...
+ // Check that the disk size will hold the data...
+ if (mainHeader.backupLBA > diskSize) {
+ problems++;
+ printf("\nProblem: Disk is too small to hold all the data!\n");
+ printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
+ (unsigned long long) diskSize,
+ (unsigned long long) mainHeader.backupLBA);
+ } // if
+
+ // Check for overlapping partitions....
+ for (i = 1; i < mainHeader.numParts; i++) {
+ for (j = 0; j < i; j++) {
+ if (TheyOverlap(&partitions[i], &partitions[j])) {
+ problems++;
+ printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
+ printf(" Partition %d: %llu to %llu\n", i,
+ (unsigned long long) partitions[i].firstLBA,
+ (unsigned long long) partitions[i].lastLBA);
+ printf(" Partition %d: %llu to %llu\n", j,
+ (unsigned long long) partitions[j].firstLBA,
+ (unsigned long long) partitions[j].lastLBA);
+ } // if
+ } // for j...
+ } // for i...
+
+ // Now compute available space, but only if no problems found, since
+ // problems could affect the results
+ if (problems == 0) {
+ totalFree = FindFreeBlocks(&numSegments, &largestSegment);
+ BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
+ BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
+ printf("No problems found. %llu free sectors (%s) available in %u\n"
+ "segments, the largest of which is %llu sectors (%s) in size\n",
+ (unsigned long long) totalFree,
+ siTotal, numSegments, (unsigned long long) largestSegment,
+ siLargest);
+ } else {
+ printf("\nIdentified %d problems!\n", problems);
+ } // if/else
+
+ return (problems);
+} // GPTData::Verify()
+
+// 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) {
+ int i;
+
+ mainHeader.signature = GPT_SIGNATURE;
+ mainHeader.revision = secondHeader.revision;
+ mainHeader.headerSize = HEADER_SIZE;
+ 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.data1 = secondHeader.diskGUID.data1;
+ mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
+ mainHeader.partitionEntriesLBA = UINT64_C(2);
+ mainHeader.numParts = secondHeader.numParts;
+ mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
+ mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
+ for (i = 0 ; i < GPT_RESERVED; i++)
+ mainHeader.reserved2[i] = secondHeader.reserved2[i];
+} // GPTData::RebuildMainHeader()
+
+// Rebuild the secondary GPT header, using the main header as a model.
+void GPTData::RebuildSecondHeader(void) {
+ int i;
+
+ secondHeader.signature = GPT_SIGNATURE;
+ secondHeader.revision = mainHeader.revision;
+ secondHeader.headerSize = HEADER_SIZE;
+ 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.data1 = mainHeader.diskGUID.data1;
+ secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
+ secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
+ secondHeader.numParts = mainHeader.numParts;
+ secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
+ secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
+ for (i = 0 ; i < GPT_RESERVED; i++)
+ secondHeader.reserved2[i] = mainHeader.reserved2[i];
+} // RebuildSecondHeader()
+
+// Load the second (backup) partition table as the primary partition
+// table. Used in repair functions
+void GPTData::LoadSecondTableAsMain(void) {
+ int fd;
+ off_t seekTo;
+ uint32_t sizeOfParts, newCRC;
+
+ if ((fd = open(device, O_RDONLY)) != -1) {
+ seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
+ if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
+ SetGPTSize(secondHeader.numParts);
+ sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
+ read(fd, partitions, sizeOfParts);
+ newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
+ secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
+ mainPartsCrcOk = secondPartsCrcOk;
+ if (!secondPartsCrcOk) {
+ printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
+ } // if
+ } else {
+ printf("Error! Couldn't seek to backup partition table!\n");
+ } // if/else
+ } else {
+ printf("Error! Couldn't open device %s when recovering backup partition table!\n");
+ } // if/else
+} // GPTData::LoadSecondTableAsMain()
+
+// 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(int *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
+ int num = 0;
+
+ *largestSegment = UINT64_C(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);
+ *numSegments = num;
+ return totalFound;
+} // GPTData::FindFreeBlocks()
+
+/*
+// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
+// OSes that don't understand GPT.
+void GPTData::MakeHybrid(void) {
+ uint32_t partNums[3];
+ char line[255];
+ int numParts, i, j, typeCode, bootable;
+ uint64_t length;
+
+ // First, rebuild the protective MBR...
+ protectiveMBR.MakeProtectiveMBR();
+
+ // Now get the numbers of up to three partitions to add to the
+ // hybrid MBR....
+ printf("Type from one to three partition numbers to be added to the hybrid MBR, in\n"
+ "sequence: ");
+ fgets(line, 255, stdin);
+ numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
+ for (i = 0; i < numParts; i++) {
+ j = partNums[i] - 1;
+ printf("Creating entry for partition #%d\n", j + 1);
+ if ((j >= 0) && (j < mainHeader.numParts)) {
+ if (partitions[j].lastLBA < UINT32_MAX) {
+ printf("Enter an MBR hex code (suggested %02X): ",
+ typeHelper.GUIDToID(partitions[j].partitionType) / 256);
+ fgets(line, 255, stdin);
+ sscanf(line, "%x", &typeCode);
+ printf("Set the bootable flag? ");
+ bootable = (GetYN() == 'Y');
+ length = partitions[j].lastLBA - partitions[j].firstLBA + UINT64_C(1);
+ protectiveMBR.MakePart(i + 1, (uint32_t) partitions[j].firstLBA,
+ (uint32_t) length, typeCode, bootable);
+ } else { // partition out of range
+ printf("Partition %d ends beyond the 2TiB limit of MBR partitions; omitting it.\n",
+ j + 1);
+ } // if/else
+ } else {
+ printf("Partition %d is out of range; omitting it.\n", j + 1);
+ } // if/else
+ } // for
+} // GPTData::MakeHybrid()
+*/
+
+// Writes GPT (and protective MBR) to disk. Returns 1 on successful
+// write, 0 if there was a problem.
+int GPTData::SaveGPTData(void) {
+ int allOK = 1, i, j;
+ char answer, line[256];
+ int fd;
+ uint64_t secondTable;
+ off_t offset;
+
+ if (strlen(device) == 0) {
+ printf("Device not defined.\n");
+ } // if
+
+ // First do some final sanity checks....
+ // Is there enough space to hold the GPT headers and partition tables,
+ // given the partition sizes?
+ if (CheckGPTSize() == 0) {
+ allOK = 0;
+ } // if
+
+ // Check that disk is really big enough to handle this...
+ if (mainHeader.backupLBA > diskSize) {
+ fprintf(stderr, "Error! Disk is too small -- either the original MBR is corrupt or you're\n");
+ fprintf(stderr, "working from an MBR copied to a file! Aborting!\n");
+ printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize,
+ mainHeader.backupLBA);
+ allOK = 0;
+ } // if
+
+ // Check for overlapping partitions....
+ for (i = 1; i < mainHeader.numParts; i++) {
+ for (j = 0; j < i; j++) {
+ if (TheyOverlap(&partitions[i], &partitions[j])) {
+ fprintf(stderr, "\Error: partitions %d and %d overlap:\n", i + 1, j + 1);
+ fprintf(stderr, " Partition %d: %llu to %llu\n", i,
+ (unsigned long long) partitions[i].firstLBA,
+ (unsigned long long) partitions[i].lastLBA);
+ fprintf(stderr, " Partition %d: %llu to %llu\n", j,
+ (unsigned long long) partitions[j].firstLBA,
+ (unsigned long long) partitions[j].lastLBA);
+ fprintf(stderr, "Aborting write operation!\n");
+ allOK = 0;
+ } // if
+ } // for j...
+ } // for i...
+
+ RecomputeCRCs();
+
+ if (allOK) {
+ printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
+ printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
+ printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
+ printf("Do you want to proceed, possibly destroying your data? (Y/N) ");
+ fgets(line, 255, stdin);
+ sscanf(line, "%c", &answer);
+ if ((answer == 'Y') || (answer == 'y')) {
+ printf("OK; writing new GPT partition table.\n");
+ } else {
+ allOK = 0;
+ } // if/else
+ } // if
+
+ // Do it!
+ if (allOK) {
+ fd = open(device, O_WRONLY); // try to open the device; may fail....
+#ifdef __APPLE__
+ // MacOS X requires a shared lock under some circumstances....
+ if (fd < 0) {
+ fd = open(device, O_WRONLY|O_SHLOCK);
+ } // if
+#endif
+ if (fd != -1) {
+ // First, write the protective MBR...
+ protectiveMBR.WriteMBRData(fd);
+
+ // Now write the main GPT header...
+ if (allOK)
+ if (write(fd, &mainHeader, 512) == -1)
+ allOK = 0;
+
+ // Now write the main partition tables...
+ if (allOK) {
+ if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1)
+ allOK = 0;
+ } // if
+
+ // Now seek to near the end to write the secondary GPT....
+ if (allOK) {
+ secondTable = secondHeader.partitionEntriesLBA;
+ offset = (off_t) secondTable * (off_t) (blockSize);
+ if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
+ allOK = 0;
+ printf("Unable to seek to end of disk!\n");
+ } // if
+ } // if
+
+ // Now write the secondary partition tables....
+ if (allOK)
+ if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1)
+ allOK = 0;
+
+ // Now write the secondary GPT header...
+ if (allOK)
+ if (write(fd, &secondHeader, 512) == -1)
+ allOK = 0;
+
+ // re-read the partition table
+ if (allOK) {
+ sync();
+#ifdef __APPLE__
+ printf("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 */
+ i = ioctl(fd, DKIOCSYNCHRONIZECACHE);
+#else
+ sleep(2);
+ i = ioctl(fd, BLKRRPART);
+ if (i)
+ printf("Warning: The kernel is still using the old partition table.\n"
+ "The new table will be used at the next reboot.\n");
+#endif
+ } // if
+
+ if (allOK) { // writes completed OK
+ printf("The operation has completed successfully.\n");
+ } else {
+ printf("Warning! An error was reported when writing the partition table! This error\n");
+ printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
+ printf("necessary, restore your original partition table.\n");
+ } // if/else
+ close(fd);
+ } else {
+ fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting!\n", device, errno);
+ allOK = 0;
+ } // if/else
+ } else {
+ printf("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(char* filename) {
+ int fd, allOK = 1;;
+
+ if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
+ // First, write the protective MBR...
+ protectiveMBR.WriteMBRData(fd);
+
+ // Now write the main GPT header...
+ if (allOK)
+ if (write(fd, &mainHeader, 512) == -1)
+ allOK = 0;
+
+ // Now write the secondary GPT header...
+ if (allOK)
+ if (write(fd, &secondHeader, 512) == -1)
+ allOK = 0;
+
+ // Now write the main partition tables...
+ if (allOK) {
+ if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1)
+ allOK = 0;
+ } // if
+
+ if (allOK) { // writes completed OK
+ printf("The operation has completed successfully.\n");
+ } else {
+ printf("Warning! An error was reported when writing the backup file.\n");
+ printf("It may not be useable!\n");
+ } // if/else
+ close(fd);
+ } else {
+ fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
+ allOK = 0;
+ } // if/else
+ return allOK;
+} // GPTData::SaveGPTBackup()
+
+// 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(char* filename) {
+ int fd, allOK = 1, val;
+ uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
+
+ if ((fd = open(filename, O_RDONLY)) != -1) {
+ // Let the MBRData class load the saved MBR...
+ protectiveMBR.ReadMBRData(fd);
+
+ // Load the main GPT header, check its vaility, and set the GPT
+ // size based on the data
+ read(fd, &mainHeader, 512);
+ mainCrcOk = CheckHeaderCRC(&mainHeader);
+
+ // Load the backup GPT header in much the same way as the main
+ // GPT header....
+ read(fd, &secondHeader, 512);
+ secondCrcOk = CheckHeaderCRC(&secondHeader);
+
+ // 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
+ numParts = secondHeader.numParts;
+ sizeOfEntries = secondHeader.sizeOfPartitionEntries;
+ } else { // main header is OK
+ numParts = mainHeader.numParts;
+ sizeOfEntries = mainHeader.sizeOfPartitionEntries;
+ } // if/else
+
+ SetGPTSize(numParts);
+
+ // If current disk size doesn't match that of backup....
+ if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
+ printf("Warning! Current disk size doesn't match that of the backup!\n"
+ "Adjusting sizes to match, but subsequent problems are possible!\n");
+ secondHeader.currentLBA = mainHeader.backupLBA = diskSize - UINT64_C(1);
+ mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
+ secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
+ secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
+ } // if
+
+ // Load main partition table, and record whether its CRC
+ // matches the stored value
+ sizeOfParts = numParts * sizeOfEntries;
+ read(fd, partitions, sizeOfParts);
+
+ newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
+ mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
+ secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
+ } else {
+ allOK = 0;
+ } // if/else
+ } else {
+ allOK = 0;
+ fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
+ } // if/else
+
+ // Something went badly wrong, so blank out partitions
+ if (allOK == 0) {
+ ClearGPTData();
+ protectiveMBR.MakeProtectiveMBR();
+ } // if
+ return allOK;
+} // GPTData::LoadGPTBackup()
+
+// 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;
+ union {
+ uint32_t num;
+ unsigned char uc[sizeof(uint32_t)];
+ } endian;
+
+ if (sizeof(uint8_t) != 1) {
+ fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
+ allOK = 0;
+ } // if
+ if (sizeof(uint16_t) != 2) {
+ fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
+ allOK = 0;
+ } // if
+ if (sizeof(uint32_t) != 4) {
+ fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
+ allOK = 0;
+ } // if
+ if (sizeof(uint64_t) != 8) {
+ fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
+ allOK = 0;
+ } // if
+ if (sizeof(struct MBRRecord) != 16) {
+ fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(uint32_t));
+ allOK = 0;
+ } // if
+ if (sizeof(struct EBRRecord) != 512) {
+ fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(uint32_t));
+ allOK = 0;
+ } // if
+ if (sizeof(struct GPTHeader) != 512) {
+ fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(uint32_t));
+ allOK = 0;
+ } // if
+ // Determine endianness; set allOK = 0 if running on big-endian hardware
+ endian.num = 1;
+ if (endian.uc[0] != (unsigned char) 1) {
+ fprintf(stderr, "Running on big-endian hardware, but this program only works on little-endian\n"
+ "systems; aborting!\n");
+ allOK = 0;
+ } // if
+ return (allOK);
+} // SizesOK()
+
diff --git a/gpt.h b/gpt.h
new file mode 100644
index 0000000..c56de20
--- /dev/null
+++ b/gpt.h
@@ -0,0 +1,153 @@
+/* gpt.h -- GPT and data structure definitions, types, and
+ functions */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include "support.h"
+#include "parttypes.h"
+#include "mbr.h"
+
+#ifndef __GPTSTRUCTS
+#define __GPTSTRUCTS
+
+#define GPT_SIGNATURE UINT64_C(0x5452415020494645)
+
+/* Number and size of GPT entries... */
+#define NUM_GPT_ENTRIES 128
+#define GPT_SIZE 128
+/* Offset, in 512-byte sectors, for GPT table and partition data.
+ Note this is above two multiplied together, divided by 512, with 2
+ added
+#define GPT_OFFSET (((NUM_GPT_ENTRIES * GPT_SIZE) / SECTOR_SIZE) + 2)
+*/
+
+#define HEADER_SIZE 92
+
+#define GPT_RESERVED 420
+#define NAME_SIZE 72
+
+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_new};
+
+// Header (first 512 bytes) of GPT table
+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;
+ struct GUIDData diskGUID;
+ uint64_t partitionEntriesLBA;
+ uint32_t numParts;
+ uint32_t sizeOfPartitionEntries;
+ uint32_t partitionEntriesCRC;
+ unsigned char reserved2[GPT_RESERVED];
+}; // struct GPTHeader
+
+struct GPTPartition {
+ struct GUIDData partitionType;
+ struct GUIDData uniqueGUID;
+ uint64_t firstLBA;
+ uint64_t lastLBA;
+ uint64_t attributes;
+ unsigned char name[NAME_SIZE];
+}; // struct GPTPartition
+
+// Data in GPT format
+class GPTData {
+protected:
+ struct GPTHeader mainHeader;
+ struct GPTPartition *partitions;
+ struct GPTHeader secondHeader;
+ MBRData protectiveMBR;
+ char device[256]; // device filename
+ uint32_t blockSize; // device block size
+ uint64_t diskSize; // size of device, in blocks
+ GPTValidity state; // is GPT valid?
+ int mainCrcOk;
+ int secondCrcOk;
+ int mainPartsCrcOk;
+ int secondPartsCrcOk;
+// uint32_t units; // display units, in multiples of sectors
+ PartTypes typeHelper;
+public:
+ GPTData(void);
+ GPTData(char* deviceFilename);
+ ~GPTData(void);
+ int SetGPTSize(uint32_t numEntries);
+ int CheckGPTSize(void);
+ int LoadPartitions(char* deviceFilename);
+ int ForceLoadGPTData(int fd);
+ int LoadMainTable(void);
+ WhichToUse UseWhichPartitions(void);
+ void ResizePartitionTable(void);
+ int GetPartRange(uint32_t* low, uint32_t* high);
+ void DisplayGPTData(void);
+ void DisplayMBRData(void) {protectiveMBR.DisplayMBRData();}
+ void ShowDetails(void);
+ void ShowPartDetails(uint32_t partNum);
+ void CreatePartition(void);
+ void DeletePartition(void);
+ void BlankPartitions(void);
+ uint64_t FindFirstAvailable(uint64_t start = 0);
+ uint64_t FindLastAvailable(uint64_t start);
+ uint64_t FindLastInFree(uint64_t start);
+ int IsFree(uint64_t sector);
+ int XFormPartitions(MBRData* origParts);
+ void SortGPT(void);
+ int ClearGPTData(void);
+ void ChangePartType(void);
+ uint32_t GetPartNum(void);
+ void SetAttributes(uint32_t partNum);
+ void SetName(uint32_t partNum, char* theName = NULL);
+ void SetDiskGUID(GUIDData newGUID);
+ int SetPartitionGUID(uint32_t pn, GUIDData theGUID);
+ int CheckHeaderValidity(void);
+ int CheckHeaderCRC(struct GPTHeader* header);
+ void RecomputeCRCs(void);
+ int Verify(void);
+ void RebuildMainHeader(void);
+ void RebuildSecondHeader(void);
+ void LoadSecondTableAsMain(void);
+ uint64_t FindFreeBlocks(int *numSegments, uint64_t *largestSegment);
+// void MakeHybrid(void);
+ void MakeProtectiveMBR(void) {return protectiveMBR.MakeProtectiveMBR();}
+ int SaveGPTData(void);
+ int SaveGPTBackup(char* filename);
+ int LoadGPTBackup(char* filename);
+
+ // Return data about the GPT structures....
+ uint32_t GetNumParts(void) {return mainHeader.numParts;}
+ 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 GetBlocksInPartTable(void) {return (mainHeader.numParts *
+ mainHeader.sizeOfPartitionEntries) / blockSize;}
+}; // class GPTData
+
+// Function prototypes....
+void BlankPartition(struct GPTPartition* partition);
+//int XFormType(uint8_t oldType, struct GUIDData* newType, int partNum);
+void QuickSortGPT(struct GPTPartition* partitions, int start, int finish);
+int TheyOverlap(struct GPTPartition* first, struct GPTPartition* second);
+void ChangeGPTType(struct GPTPartition* part);
+int SizesOK(void);
+
+#endif
diff --git a/mbr.cc b/mbr.cc
new file mode 100644
index 0000000..e692adc
--- /dev/null
+++ b/mbr.cc
@@ -0,0 +1,447 @@
+/* mbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition
+ data. */
+
+/* By Rod Smith, January to February, 2009 */
+
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include "mbr.h"
+#include "support.h"
+
+using namespace std;
+
+/****************************************
+ * *
+ * MBRData class and related structures *
+ * *
+ ****************************************/
+
+MBRData::MBRData(void) {
+ blockSize = SECTOR_SIZE;
+ diskSize = 0;
+ strcpy(device, "");
+ state = invalid;
+ srand((unsigned int) time(NULL));
+ EmptyMBR();
+} // MBRData default constructor
+
+MBRData::MBRData(char *filename) {
+ blockSize = SECTOR_SIZE;
+ diskSize = 0;
+ strcpy(device, filename);
+ state = invalid;
+
+ srand((unsigned int) time(NULL));
+ // Try to read the specified partition table, but if it fails....
+ if (!ReadMBRData(filename)) {
+ EmptyMBR();
+ strcpy(device, "");
+ } // if
+} // MBRData(char *filename) constructor
+
+MBRData::~MBRData(void) {
+} // MBRData destructor
+
+// Empty all data. Meant mainly for calling by constructors
+void MBRData::EmptyMBR(void) {
+ int i;
+
+ for (i = 0; i < 440; i++)
+ code[i] = 0;
+ diskSignature = (uint32_t) rand();
+ nulls = 0;
+ for (i = 0; i < 4; i++) {
+ partitions[i].status = UINT8_C(0);
+ partitions[i].firstSector[0] = UINT8_C(0);
+ partitions[i].firstSector[1] = UINT8_C(0);
+ partitions[i].firstSector[2] = UINT8_C(0);
+ partitions[i].partitionType = UINT8_C(0);
+ partitions[i].lastSector[0] = UINT8_C(0);
+ partitions[i].lastSector[1] = UINT8_C(0);
+ partitions[i].lastSector[2] = UINT8_C(0);
+ partitions[i].firstLBA = UINT32_C(0);
+ partitions[i].lengthLBA = UINT32_C(0);
+ } // for
+ MBRSignature = MBR_SIGNATURE;
+
+ blockSize = SECTOR_SIZE;
+ diskSize = 0;
+ for (i = 0; i < NUM_LOGICALS; i++) {
+ logicals[i].status = UINT8_C(0);
+ logicals[i].firstSector[0] = UINT8_C(0);
+ logicals[i].firstSector[1] = UINT8_C(0);
+ logicals[i].firstSector[2] = UINT8_C(0);
+ logicals[i].partitionType = UINT8_C(0);
+ logicals[i].lastSector[0] = UINT8_C(0);
+ logicals[i].lastSector[1] = UINT8_C(0);
+ logicals[i].lastSector[2] = UINT8_C(0);
+ logicals[i].firstLBA = UINT32_C(0);
+ logicals[i].lengthLBA = UINT32_C(0);
+ } // for
+} // MBRData::EmptyMBR()
+
+// 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 MBRData::ReadMBRData(char* deviceFilename) {
+ int fd, allOK = 1;
+
+ if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
+ ReadMBRData(fd);
+ } else {
+ allOK = 0;
+ } // if
+
+ close(fd);
+
+ if (allOK)
+ strcpy(device, deviceFilename);
+
+ return allOK;
+} // MBRData::ReadMBRData(char* deviceFilename)
+
+// Read data from MBR.
+void MBRData::ReadMBRData(int fd) {
+ int allOK = 1, i;
+ int err;
+
+ // Clear logical partition array
+ for (i = 0; i < NUM_LOGICALS; i++) {
+ logicals[i].status = UINT8_C(0);
+ logicals[i].firstSector[0] = UINT8_C(0);
+ logicals[i].firstSector[1] = UINT8_C(0);
+ logicals[i].firstSector[2] = UINT8_C(0);
+ logicals[i].partitionType = UINT8_C(0);
+ logicals[i].lastSector[0] = UINT8_C(0);
+ logicals[i].lastSector[1] = UINT8_C(0);
+ logicals[i].lastSector[2] = UINT8_C(0);
+ logicals[i].firstLBA = UINT32_C(0);
+ logicals[i].lengthLBA = UINT32_C(0);
+ } // for
+
+ read(fd, code, 440);
+ read(fd, &diskSignature, 4);
+ read(fd, &nulls, 2);
+ read(fd, partitions, 64);
+ read(fd, &MBRSignature, 2);
+ if (MBRSignature != MBR_SIGNATURE) {
+ allOK = 0;
+ state = invalid;
+ fprintf(stderr, "MBR signature invalid; read 0x%04X, but should be 0x%04X\n",
+ (unsigned int) MBRSignature, (unsigned int) MBR_SIGNATURE);
+ } /* if */
+
+ // Find disk size
+ diskSize = disksize(fd, &err);
+
+ // Find block size
+ if ((blockSize = GetBlockSize(fd)) == -1) {
+ blockSize = SECTOR_SIZE;
+ printf("Unable to determine sector size; assuming %lu bytes!\n",
+ (unsigned long) SECTOR_SIZE);
+ } // if
+
+ // Load logical partition data, if any is found....
+ if (allOK) {
+ for (i = 0; i < 4; i++) {
+ if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
+ || (partitions[i].partitionType == 0x85)) {
+ // Found it, so call a recursive algorithm to load everything from them....
+ allOK = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), 0);
+ } // if
+ } // for
+ 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].partitionType == UINT8_C(0xEE)) {
+ state = gpt;
+ } /* if */
+ } /* for */
+ } /* if */
+
+/* // Tell the user what the MBR state is...
+ switch (state) {
+ case invalid:
+ printf("Information: MBR appears to be empty or invalid.\n");
+ break;
+ case gpt:
+ printf("Information: MBR holds GPT placeholder partitions.\n");
+ break;
+ case hybrid:
+ printf("Information: MBR holds hybrid GPT/MBR data.\n");
+ break;
+ case mbr:
+ printf("Information: MBR data appears to be valid.\n");
+ break;
+ } // switch */
+} // MBRData::ReadMBRData(int fd)
+
+// Write the MBR data to the default defined device.
+int MBRData::WriteMBRData(void) {
+ int allOK = 1, fd;
+
+ if ((fd = open(device, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
+ WriteMBRData(fd);
+ } else {
+ allOK = 0;
+ } // if/else
+ close(fd);
+ return allOK;
+} // MBRData::WriteMBRData(void)
+
+// Save the MBR data to a file. Note that this function writes ONLY the
+// MBR data, not the logical partitions (if any are defined).
+void MBRData::WriteMBRData(int fd) {
+ write(fd, code, 440);
+ write(fd, &diskSignature, 4);
+ write(fd, &nulls, 2);
+ write(fd, partitions, 64);
+ write(fd, &MBRSignature, 2);
+} // MBRData::WriteMBRData(int fd)
+
+// This is a recursive function to read all the logical partitions, following the
+// logical partition linked list from the disk and storing the basic data in
+int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
+ uint32_t diskOffset, int partNum) {
+ int allOK = 1;
+ struct EBRRecord ebr;
+ off_t offset;
+
+ offset = (off_t) (extendedStart + diskOffset) * blockSize;
+ if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
+ fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
+ allOK = 0;
+ }
+ if (read(fd, &ebr, 512) != 512) { // Load the data....
+ fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
+ (unsigned long) offset);
+ allOK = 0;
+ }
+ if (ebr.MBRSignature != MBR_SIGNATURE) {
+ allOK = 0;
+ printf("MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
+ (unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
+ } /* if */
+
+ // Copy over the basic data....
+ logicals[partNum].status = ebr.partitions[0].status;
+ logicals[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
+ logicals[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
+ logicals[partNum].partitionType = ebr.partitions[0].partitionType;
+
+ // Find the next partition (if there is one) and recurse....
+ if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && allOK) {
+ allOK = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA,
+ partNum + 1);
+ } // if
+ return (allOK);
+} // MBRData::ReadLogicalPart()
+
+// Show the MBR data to the user....
+void MBRData::DisplayMBRData(void) {
+ int i;
+ char tempStr[255];
+
+ printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
+ printf("MBR partitions:\n");
+ printf("Number\t Start (block)\t Length (blocks)\tType\n");
+ for (i = 0; i < 4; i++) {
+ if (partitions[i].lengthLBA != 0) {
+ printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 1, (unsigned long) partitions[i].firstLBA,
+ (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
+ } // if
+ } // for
+
+ // Now display logical partition data....
+ for (i = 0; i < NUM_LOGICALS; i++) {
+ if (logicals[i].lengthLBA != 0) {
+ printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 5, (unsigned long) logicals[i].firstLBA,
+ (unsigned long) logicals[i].lengthLBA, logicals[i].partitionType);
+ } // if
+ } // for
+ printf("\nDisk size is %lu sectors (%s)\n", (unsigned long) diskSize,
+ BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
+} // MBRData::DisplayMBRData()
+
+// Create a protective MBR
+void MBRData::MakeProtectiveMBR(void) {
+ int i;
+
+ // Initialize variables
+ nulls = 0;
+ MBRSignature = MBR_SIGNATURE;
+
+ partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
+
+ // Write CHS data. This maxes out the use of the disk, as much as
+ // possible -- even to the point of exceeding the capacity of sub-8GB
+ // disks. The EFI spec says to use 0xffffff as the ending value,
+ // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
+ // and Apple's Disk Utility use 0xfeffff, and the latter puts that
+ // value in for the FIRST sector, too!
+ partitions[0].firstSector[0] = UINT8_C(0);
+ partitions[0].firstSector[1] = UINT8_C(1);
+ partitions[0].firstSector[2] = UINT8_C(0);
+ partitions[0].lastSector[0] = UINT8_C(255);
+ partitions[0].lastSector[1] = UINT8_C(255);
+ partitions[0].lastSector[2] = UINT8_C(255);
+
+ partitions[0].partitionType = UINT8_C(0xEE);
+ partitions[0].firstLBA = UINT32_C(1);
+ if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
+ partitions[0].lengthLBA = diskSize - 1;
+ } else { // disk is too big to represent, so fake it...
+ partitions[0].lengthLBA = UINT32_MAX;
+ } // if/else
+
+ // Zero out three unused primary partitions...
+ for (i = 1; i < 4; i++) {
+ partitions[i].status = UINT8_C(0);
+ partitions[i].firstSector[0] = UINT8_C(0);
+ partitions[i].firstSector[1] = UINT8_C(0);
+ partitions[i].firstSector[2] = UINT8_C(0);
+ partitions[i].partitionType = UINT8_C(0);
+ partitions[i].lastSector[0] = UINT8_C(0);
+ partitions[i].lastSector[1] = UINT8_C(0);
+ partitions[i].lastSector[2] = UINT8_C(0);
+ partitions[i].firstLBA = UINT32_C(0);
+ partitions[i].lengthLBA = UINT32_C(0);
+ } // for
+
+ // Zero out all the logical partitions. Not necessary for data
+ // integrity on write, but eliminates stray entries if user wants
+ // to view the MBR after converting the disk
+ for (i = 0; i < NUM_LOGICALS; i++) {
+ logicals[i].status = UINT8_C(0);
+ logicals[i].firstSector[0] = UINT8_C(0);
+ logicals[i].firstSector[1] = UINT8_C(0);
+ logicals[i].firstSector[2] = UINT8_C(0);
+ logicals[i].partitionType = UINT8_C(0);
+ logicals[i].lastSector[0] = UINT8_C(0);
+ logicals[i].lastSector[1] = UINT8_C(0);
+ logicals[i].lastSector[2] = UINT8_C(0);
+ logicals[i].firstLBA = UINT32_C(0);
+ logicals[i].lengthLBA = UINT32_C(0);
+ } // for
+
+ state = gpt;
+} // MBRData::MakeProtectiveMBR()
+
+// Return a pointer to a primary or logical partition, or NULL if
+// the partition is out of range....
+struct MBRRecord* MBRData::GetPartition(int i) {
+ MBRRecord* thePart = NULL;
+
+ if ((i >= 0) && (i < 4)) { // primary partition
+ thePart = &partitions[i];
+ } // if
+ if ((i >= 4) && (i < (NUM_LOGICALS + 4))) {
+ thePart = &logicals[i - 4];
+ } // if
+ return thePart;
+} // GetPartition()
+
+// Displays the state, as a word, on stdout. Used for debugging
+void MBRData::ShowState(void) {
+ switch (state) {
+ case invalid:
+ printf("invalid");
+ break;
+ case gpt:
+ printf("gpt");
+ break;
+ case hybrid:
+ printf("hybrid");
+ break;
+ case mbr:
+ printf("mbr");
+ break;
+ default:
+ printf("unknown -- bug!");
+ break;
+ } // switch
+} // MBRData::ShowState()
+
+// Create a primary partition of the specified number, starting LBA,
+// and length. This function does *NO* error checking, so it's possible
+// to seriously screw up a partition table using this function! It's
+// intended as a way to create a hybrid MBR, which is a pretty funky
+// setup to begin with....
+void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
+ int bootable) {
+
+ partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
+ partitions[num].firstSector[0] = UINT8_C(0);
+ partitions[num].firstSector[1] = UINT8_C(0);
+ partitions[num].firstSector[2] = UINT8_C(0);
+ partitions[num].partitionType = (uint8_t) type;
+ partitions[num].lastSector[0] = UINT8_C(0);
+ partitions[num].lastSector[1] = UINT8_C(0);
+ partitions[num].lastSector[2] = UINT8_C(0);
+ partitions[num].firstLBA = start;
+ partitions[num].lengthLBA = length;
+} // MakePart()
+
+uint8_t MBRData::GetStatus(int i) {
+ MBRRecord* thePart;
+ uint8_t retval;
+
+ thePart = GetPartition(i);
+ if (thePart != NULL)
+ retval = thePart->status;
+ else
+ retval = UINT8_C(0);
+ return retval;
+} // MBRData::GetStatus()
+
+uint8_t MBRData::GetType(int i) {
+ MBRRecord* thePart;
+ uint8_t retval;
+
+ thePart = GetPartition(i);
+ if (thePart != NULL)
+ retval = thePart->partitionType;
+ else
+ retval = UINT8_C(0);
+ return retval;
+} // MBRData::GetType()
+
+uint32_t MBRData::GetFirstSector(int i) {
+ MBRRecord* thePart;
+ uint32_t retval;
+
+ thePart = GetPartition(i);
+ if (thePart != NULL) {
+ retval = thePart->firstLBA;
+ } else
+ retval = UINT32_C(0);
+ return retval;
+} // MBRData::GetFirstSector()
+
+uint32_t MBRData::GetLength(int i) {
+ MBRRecord* thePart;
+ uint32_t retval;
+
+ thePart = GetPartition(i);
+ if (thePart != NULL) {
+ retval = thePart->lengthLBA;
+ } else
+ retval = UINT32_C(0);
+ return retval;
+} // MBRData::GetLength()
diff --git a/mbr.h b/mbr.h
new file mode 100644
index 0000000..86f792b
--- /dev/null
+++ b/mbr.h
@@ -0,0 +1,95 @@
+/* mbr.h -- MBR data structure definitions, types, and functions */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#ifndef __MBRSTRUCTS
+#define __MBRSTRUCTS
+
+#define MBR_SIGNATURE UINT16_C(0xAA55)
+
+// Maximum number of logical partitions supported
+#define NUM_LOGICALS 124
+
+using namespace std;
+
+/****************************************
+ * *
+ * MBRData class and related structures *
+ * *
+ ****************************************/
+
+// 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.
+struct MBRRecord {
+ uint8_t status;
+ uint8_t firstSector[3];
+ uint8_t partitionType;
+ uint8_t lastSector[3];
+ uint32_t firstLBA;
+ uint32_t lengthLBA;
+}; // struct MBRRecord
+
+// Extended Boot Record (EBR) data, used to hold one logical partition's
+// data within an extended partition. Includes pointer to next record for
+// in-memory linked-list access. This is similar to MBRData, but with a
+// few tweaks....
+struct EBRRecord {
+ uint8_t code[446]; // generally 0s (and we don't care if they aren't)
+ // First partition entry defines partition; second points to next
+ // entry in on-disk linked list; remaining two are unused. Note that
+ // addresses are relative to the extended partition, not to the disk
+ // as a whole.
+ struct MBRRecord partitions[4];
+ uint16_t MBRSignature;
+}; // struct EBRRecord
+
+// Possible states of the MBR
+enum MBRValidity {invalid, gpt, hybrid, mbr};
+
+// Full data in tweaked MBR format
+class MBRData {
+protected:
+ uint8_t code[440];
+ uint32_t diskSignature;
+ uint16_t nulls;
+ struct MBRRecord partitions[4];
+ 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
+ char device[256];
+ // Now an array of partitions for the logicals, in array form (easier
+ // than a linked list, and good enough for the GPT itself, so....)
+ struct MBRRecord logicals[NUM_LOGICALS];
+ MBRValidity state;
+ struct MBRRecord* GetPartition(int i); // Return primary or logical partition
+public:
+ MBRData(void);
+ MBRData(char* deviceFilename);
+ ~MBRData(void);
+ void EmptyMBR(void);
+ int ReadMBRData(char* deviceFilename);
+ void ReadMBRData(int fd);
+ int WriteMBRData(void);
+ void WriteMBRData(int fd);
+ int ReadLogicalPart(int fd, uint32_t extendedStart, uint32_t diskOffset,
+ int partNum);
+ void DisplayMBRData(void);
+ void MakeProtectiveMBR(void);
+ MBRValidity GetValidity(void) {return state;}
+ void ShowState(void);
+ void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07,
+ int bootable = 0);
+
+ // Functions to extract data on specific partitions....
+ uint8_t GetStatus(int i);
+ uint8_t GetType(int i);
+ uint32_t GetFirstSector(int i);
+ uint32_t GetLength(int i);
+}; // struct MBRData
+
+#endif
diff --git a/parttypes.cc b/parttypes.cc
new file mode 100644
index 0000000..3f81797
--- /dev/null
+++ b/parttypes.cc
@@ -0,0 +1,332 @@
+// parttypes.cc
+// Class to manage partition type codes -- a slight variant on MBR type
+// codes, GUID type codes, and associated names.
+
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include "parttypes.h"
+
+using namespace std;
+
+int PartTypes::numInstances = 0;
+AType* PartTypes::allTypes = NULL;
+AType* PartTypes::lastType = NULL;
+
+// 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.
+// See http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
+// for a list of MBR partition type codes.
+PartTypes::PartTypes(void) {
+
+ numInstances++;
+ if (numInstances == 1) {
+
+ // Start with the "unused entry," which should normally appear only
+ // on empty partition table entries....
+ AddType(0x0000, UINT64_C(0x0000000000000000), UINT64_C(0x0000000000000000),
+ "Unused entry", 0);
+
+ // DOS/Windows partition types, which confusingly Linux also uses in GPT
+ AddType(0x0100, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // FAT-12
+ AddType(0x0400, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // FAT-16 < 32M
+ AddType(0x0600, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // FAT-16
+ AddType(0x0700, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 1); // NTFS (or could be HPFS)
+ AddType(0x0b00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // FAT-32
+ AddType(0x0c00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // FAT-32 LBA
+ AddType(0x0c01, UINT64_C(0x4DB80B5CE3C9E316), UINT64_C(0xAE1502F02DF97D81),
+ "Microsoft Reserved"); // Microsoft reserved
+ AddType(0x0e00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // FAT-16 LBA
+ AddType(0x1100, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // Hidden FAT-12
+ AddType(0x1400, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // Hidden FAT-16 < 32M
+ AddType(0x1600, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // Hidden FAT-16
+ AddType(0x1700, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // Hidden NTFS (or could be HPFS)
+ AddType(0x1b00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // Hidden FAT-32
+ AddType(0x1c00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // Hidden FAT-32 LBA
+ AddType(0x1e00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // Hidden FAT-16 LBA
+ AddType(0x2700, UINT64_C(0x4D4006D1DE94BBA4), UINT64_C(0xACD67901D5BF6AA1),
+ "Windows RE"); // Windows RE
+ AddType(0x4200, UINT64_C(0x4F621431Af9B60A0), UINT64_C(0xAD694A71113368BC),
+ "Windows LDM data"); // Logical disk manager
+ AddType(0x4201, UINT64_C(0x42E07E8F5808C8AA), UINT64_C(0xB3CF3404E9E1D285),
+ "Windows LDM metadata"); // Logical disk manager
+
+ // Linux-specific partition types....
+ AddType(0x8200, UINT64_C(0x43C4A4AB0657FD6D), UINT64_C(0x4F4F4BC83309E584),
+ "Linux swap"); // Linux swap (or could be Solaris)
+ AddType(0x8300, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087),
+ "Linux/Windows data", 0); // Linux native
+ AddType(0x8301, UINT64_C(0x60C000078DA63339), UINT64_C(0x080923C83A0836C4),
+ "Linux Reserved"); // Linux reserved
+ AddType(0x8e00, UINT64_C(0x44C2F507E6D6D379), UINT64_C(0x28F93D2A8F233CA2),
+ "Linux LVM"); // Linux LVM
+
+ // FreeBSD partition types....
+ // Note: Rather than extract FreeBSD disklabel data, convert FreeBSD
+ // partitions in-place, and let FreeBSD sort out the details....
+ AddType(0xa500, UINT64_C(0x11D66ECF516E7CB4), UINT64_C(0x2B71092D0200F88F),
+ "FreeBSD disklabel"); // FreeBSD disklabel
+ AddType(0xa501, UINT64_C(0x11DC7F4183BD6B9D), UINT64_C(0x0F4FB86015000BBE),
+ "FreeBSD boot"); // FreeBSD boot
+ AddType(0xa502, UINT64_C(0x11D66ECF516E7CB5), UINT64_C(0x2B71092D0200F88F),
+ "FreeBSD swap"); // FreeBSD swap
+ AddType(0xa503, UINT64_C(0x11D66ECF516E7CB6), UINT64_C(0x2B71092D0200F88F),
+ "FreeBSD UFS"); // FreeBSD UFS
+ AddType(0xa504, UINT64_C(0x11D66ECF516E7CBA), UINT64_C(0x2B71092D0200F88F),
+ "FreeBSD ZFS"); // FreeBSD ZFS
+ AddType(0xa505, UINT64_C(0x11D66ECF516E7CB8), UINT64_C(0x2B71092D0200F88F),
+ "FreeBSD Vinum/RAID"); // FreeBSD Vinum
+
+ // A MacOS partition type, separated from others by NetBSD partition types...
+ AddType(0xa800, UINT64_C(0x11AA000055465300), UINT64_C(0xACEC4365300011AA),
+ "Apple UFS"); // MacOS 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, UINT64_C(0x11D66ECF516E7CB4), UINT64_C(0x2B71092D0200F88F),
+ "FreeBSD disklabel", 0); // NetBSD disklabel
+ AddType(0xa901, UINT64_C(0x11DCB10E49F48D32), UINT64_C(0x489687D119009BB9),
+ "NetBSD swap");
+ AddType(0xa902, UINT64_C(0x11DCB10E49F48D5A), UINT64_C(0x489687D119009BB9),
+ "NetBSD FFS");
+ AddType(0xa903, UINT64_C(0x11DCB10E49F48D82), UINT64_C(0x489687D119009BB9),
+ "NetBSD LFS");
+ AddType(0xa903, UINT64_C(0x11DCB10E49F48DAA), UINT64_C(0x489687D119009BB9),
+ "NetBSD RAID");
+ AddType(0xa904, UINT64_C(0x11DCB10F2DB519C4), UINT64_C(0x489687D119009BB9),
+ "NetBSD concatenated");
+ AddType(0xa905, UINT64_C(0x11DCB10F2DB519EC), UINT64_C(0x489687D119009BB9),
+ "NetBSD encrypted");
+
+ // MacOS partition types (See also 0xa800, above)....
+ AddType(0xab00, UINT64_C(0x11AA0000426F6F74), UINT64_C(0xACEC4365300011AA),
+ "Apple boot"); // MacOS X
+ AddType(0xaf00, UINT64_C(0x11AA000048465300), UINT64_C(0xACEC4365300011AA),
+ "Apple HFS/HFS+"); // MacOS X
+ AddType(0xaf01, UINT64_C(0x11AA000052414944), UINT64_C(0xACEC4365300011AA),
+ "Apple RAID"); // MacOS X
+ AddType(0xaf02, UINT64_C(0x11AA5F4F52414944), UINT64_C(0xACEC4365300011AA),
+ "Apple RAID offline"); // MacOS X
+ AddType(0xaf03, UINT64_C(0x11AA6C004C616265), UINT64_C(0xACEC4365300011AA),
+ "Apple label"); // MacOS X
+ AddType(0xaf04, UINT64_C(0x11AA76655265636F), UINT64_C(0xACEC4365300011AA),
+ "AppleTV recovery"); // MacOS X
+
+ // Solaris partition types (one of which is shared with MacOS)
+ AddType(0xbe00, UINT64_C(0x11B21DD26A82CB45), UINT64_C(0x316673200008A699),
+ "Solaris boot"); // Solaris boot
+ AddType(0xbf00, UINT64_C(0x11B21DD26a85CF4D), UINT64_C(0x316673200008A699),
+ "Solaris root"); // Solaris root
+ AddType(0xbf01, UINT64_C(0x11B21DD26A898CC3), UINT64_C(0x316673200008A699),
+ "Solaris /usr & Mac ZFS"); // MacOS X & Solaris
+ AddType(0xbf02, UINT64_C(0x11B21DD26A87C46F), UINT64_C(0x316673200008A699),
+ "Solaris swap");
+ AddType(0xbf03, UINT64_C(0x11B21DD26A8B642B), UINT64_C(0x316673200008A699),
+ "Solaris backup");
+ AddType(0xbf04, UINT64_C(0x11B21DD26A8EF2E9), UINT64_C(0x316673200008A699),
+ "Solaris /var");
+ AddType(0xbf05, UINT64_C(0x11B21DD26A90BA39), UINT64_C(0x316673200008A699),
+ "Solaris /home");
+ AddType(0xbf05, UINT64_C(0x11B21DD26A9283A5), UINT64_C(0x316673200008A699),
+ "Solaris EFI_ALTSCTR");
+ AddType(0xbf06, UINT64_C(0x11B21DD26A945A3B), UINT64_C(0x316673200008A699),
+ "Solaris Reserved 1");
+ AddType(0xbf07, UINT64_C(0x11B21DD26A9630D1), UINT64_C(0x316673200008A699),
+ "Solaris Reserved 2");
+ AddType(0xbf08, UINT64_C(0x11B21DD26A980767), UINT64_C(0x316673200008A699),
+ "Solaris Reserved 3");
+ AddType(0xbf09, UINT64_C(0x11B21DD26A96237F), UINT64_C(0x316673200008A699),
+ "Solaris Reserved 4");
+ AddType(0xbf0a, UINT64_C(0x11B21DD26A8D2AC7), UINT64_C(0x316673200008A699),
+ "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, UINT64_C(0x11D33AEB75894C1E), UINT64_C(0x000000A0037BC1B7),
+ "HP-UX data");
+ AddType(0xc002, UINT64_C(0x11D632E3E2A1E728), UINT64_C(0x000000A0037B82A6),
+ "HP-UX service");
+
+ // EFI system and related partitions
+ AddType(0xEF00, UINT64_C(0x11d2f81fc12a7328), UINT64_C(0x3bc93ec9a0004bba),
+ "EFI System"); // EFI System (parted marks Linux boot
+ // partitions like this)
+ AddType(0xEF01, UINT64_C(0x11d333e7024dee41), UINT64_C(0x9FF381C70800699d),
+ "MBR partition scheme"); // Whatever that is (from Wikipedia)
+ AddType(0xEF02, UINT64_C(0x6E6F644921686148), UINT64_C(0x4946456465654E74),
+ "BIOS boot partition"); //
+
+ // A straggler Linux partition type....
+ AddType(0xfd00, UINT64_C(0x4D3B05FCA19D880F), UINT64_C(0x1E91840F3F7406A0),
+ "Linux RAID"); // Linux RAID
+ } // if
+} // default constructor
+
+PartTypes::~PartTypes(void) {
+ AType* tempType;
+
+ numInstances--;
+ if (numInstances == 0) {
+ while (allTypes != NULL) {
+ tempType = allTypes;
+ allTypes = allTypes->next;
+ delete tempType;
+ } // while
+ } // if
+} // destructor
+
+// Add a single type to the linked list of types. Returns 1 if operation
+// succeeds, 0 otherwise
+int PartTypes::AddType(uint16_t mbrType, uint64_t guidData1, uint64_t guidData2,
+ const char* n, int toDisplay) {
+ AType* tempType;
+ int allOK = 1;
+
+ tempType = new AType;
+ if (tempType != NULL) {
+ tempType->MBRType = mbrType;
+ tempType->GUIDType.data1 = guidData1;
+ tempType->GUIDType.data2 = guidData2;
+ strncpy(tempType->name, n, PNAME_SIZE);
+ tempType->display = toDisplay;
+ tempType->next = NULL;
+ if (allTypes == NULL) { // first entry
+ allTypes = tempType;
+ } else {
+ lastType->next = tempType;
+ } // if/else
+ lastType = tempType;
+ } else {
+ allOK = 0;
+ } // if/else
+ return allOK;
+} // PartTypes::AddType()
+
+// 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.
+void PartTypes::ShowTypes(void) {
+ int colCount = 1; // column count
+ AType* thisType = allTypes;
+ char tempStr[20];
+
+ while (thisType != NULL) {
+ if (thisType->display == 1) { // show it
+ strncpy(tempStr, thisType->name, 19);
+ tempStr[19] = '\0';
+ printf("%04x %-19s ", thisType->MBRType, tempStr);
+ if ((colCount % 3) == 0)
+ printf("\n");
+ colCount++;
+ } // if
+ thisType = thisType->next;
+ } // while
+ printf("\n");
+} // PartTypes::ShowTypes()
+
+// Returns 1 if code is a valid extended MBR code, 0 if it's not
+int PartTypes::Valid(uint16_t code) {
+ AType* thisType = allTypes;
+ int found = 0;
+
+ while ((thisType != NULL) && (!found)) {
+ if (thisType->MBRType == code) {
+ found = 1;
+ } // if
+ thisType = thisType->next;
+ } // while
+ return found;
+} // PartTypes::Valid()
+
+// Convert a GUID code to a name.
+char* PartTypes::GUIDToName(struct GUIDData typeCode, char typeName[]) {
+ AType* theItem = allTypes;
+ int found = 0;
+
+ while ((theItem != NULL) && (!found)) {
+ if ((theItem->GUIDType.data1 == typeCode.data1) &&
+ (theItem->GUIDType.data2 == typeCode.data2)) { // found it!
+ strcpy(typeName, theItem->name);
+ found = 1;
+ } else {
+ theItem = theItem->next;
+ } // if/else
+ } // while
+ if (!found) {
+ strcpy(typeName, (char*) "Unknown");
+ } // if (!found)
+ return typeName;
+} // PartTypes::GUIDToName()
+
+// This function takes a variant of the MBR partition type code and
+// converts it to a GUID type code
+struct GUIDData PartTypes::IDToGUID(uint16_t ID) {
+ AType* theItem = allTypes;
+ int found = 0;
+ struct GUIDData theGUID;
+
+ while ((theItem != NULL) && (!found)) {
+ if (theItem->MBRType == ID) { // found it!
+ theGUID = theItem->GUIDType;
+ found = 1;
+ } else {
+ theItem = theItem->next;
+ } // if/else
+ } // while
+ if (!found) {
+ theGUID = IDToGUID(0x0700); // assign a default type code
+ printf("Exact type match not found; assigning type code for 'Linux/Windows data'\n");
+ } // if (!found)
+ return theGUID;
+} // PartTypes::IDToGUID()
+
+// Convert a GUID to a 16-bit variant of the MBR ID number.
+// 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 PartTypes::GUIDToID(struct GUIDData typeCode) {
+ AType* theItem = allTypes;
+ int found = 0;
+ uint16_t theID;
+
+ while ((theItem != NULL) && (!found)) {
+ if ((theItem->GUIDType.data1 == typeCode.data1) &&
+ (theItem->GUIDType.data2 == typeCode.data2) &&
+ (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;
+} // PartTypes::GUIDToID()
diff --git a/parttypes.h b/parttypes.h
new file mode 100644
index 0000000..ad62027
--- /dev/null
+++ b/parttypes.h
@@ -0,0 +1,44 @@
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string>
+#include "support.h"
+
+#ifndef __PARTITION_TYPES
+#define __PARTITION_TYPES
+
+// Set the size of the name string
+#define PNAME_SIZE 80
+
+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;
+ struct GUIDData GUIDType;
+ char name[PNAME_SIZE];
+ int display; // 1 to show to users as available type, 0 not to
+ AType* next;
+}; // struct AType
+
+class PartTypes {
+protected:
+ static int numInstances;
+ static AType* allTypes; // Linked list holding all the data
+ static AType* lastType; // Pointer to last entry in the list
+public:
+ PartTypes(void);
+ ~PartTypes(void);
+ int AddType(uint16_t mbrType, uint64_t guidData1, uint64_t guidData2,
+ const char* name, int toDisplay = 1);
+ void ShowTypes(void);
+ int Valid(uint16_t);
+ char* GUIDToName(struct GUIDData typeCode, char typeName[]);
+ struct GUIDData IDToGUID(uint16_t ID);
+ uint16_t GUIDToID(struct GUIDData);
+};
+
+#endif
diff --git a/support.cc b/support.cc
new file mode 100644
index 0000000..d3a6357
--- /dev/null
+++ b/support.cc
@@ -0,0 +1,354 @@
+// 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).
+
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include "support.h"
+
+#include <sys/types.h>
+
+using namespace std;
+
+// 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.)
+int GetNumber(int low, int high, int def, const char prompt[]) {
+ int response, num;
+ char line[255];
+
+ if (low != high) { // bother only if low and high differ...
+ response = low - 1; // force one loop by setting response outside range
+ while ((response < low) || (response > high)) {
+ printf(prompt);
+ fgets(line, 255, stdin);
+ num = sscanf(line, "%d", &response);
+ if (num == 1) { // user provided a response
+ if ((response < low) || (response > high))
+ printf("Value out of range\n");
+ } else { // user hit enter; return default
+ response = def;
+ } // if/else
+ } // while
+ } else { // low == high, so return this value
+ printf("Using %d\n", low);
+ response = low;
+ } // else
+ return (response);
+} // GetNumber()
+
+// Gets a Y/N response (and converts lowercase to uppercase)
+char GetYN(void) {
+ char line[255];
+ char response = '\0';
+
+ while ((response != 'Y') && (response != 'N')) {
+ printf("(Y/N): ");
+ fgets(line, 255, stdin);
+ sscanf(line, "%c", &response);
+ if (response == 'y') response = 'Y';
+ if (response == 'n') response = 'N';
+ } // while
+ return response;
+} // GetYN(void)
+
+// Obtains the final 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", or "T" as suffixes to add
+// kilobytes, megabytes, gigabytes, or terabytes, respectively.
+// Use the high value as the default if the user just hits Enter
+uint64_t GetLastSector(uint64_t low, uint64_t high, char prompt[]) {
+ unsigned long long response;
+ int num;
+ int plusFlag = 0;
+ uint64_t mult = 1;
+ char suffix;
+ char line[255];
+
+ response = low - 1; // Ensure one pass by setting a too-low initial value
+ while ((response < low) || (response > high)) {
+ printf(prompt);
+ fgets(line, 255, stdin);
+
+ // Remove leading spaces, if present
+ while (line[0] == ' ')
+ strcpy(line, &line[1]);
+
+ // If present, flag and remove leading plus sign
+ if (line[0] == '+') {
+ plusFlag = 1;
+ strcpy(line, &line[1]);
+ } // if
+
+ // Extract numeric response and, if present, suffix
+ num = sscanf(line, "%llu%c", &response, &suffix);
+
+ // If no response, use default: The high value
+ if (num <= 0) {
+ response = (unsigned long long) high;
+ suffix = ' ';
+ } // if
+
+ // Set multiplier based on suffix
+ switch (suffix) {
+ case 'K':
+ case 'k':
+ mult = (uint64_t) 1024 / SECTOR_SIZE;
+ break;
+ case 'M':
+ case 'm':
+ mult = (uint64_t) 1048576 / SECTOR_SIZE;
+ break;
+ case 'G':
+ case 'g':
+ mult = (uint64_t) 1073741824 / SECTOR_SIZE;
+ break;
+ case 'T':
+ case 't':
+ mult = ((uint64_t) 1073741824 * (uint64_t) 1024) / (uint64_t) SECTOR_SIZE;
+ break;
+ default:
+ mult = 1;
+ } // switch
+
+ // Adjust response based on multiplier and plus flag, if present
+ response *= (unsigned long long) mult;
+ if (plusFlag == 1) {
+ response = response + (unsigned long long) low - 1;
+ } // if/else
+ } // while
+ return ((uint64_t) response);
+} // GetLastSector()
+
+// Return a plain-text name for a partition type.
+// Takes a size in bytes (in size) and converts this to a size in
+// SI units (KiB, MiB, GiB, TiB, or PiB), returned in C++ string
+// form
+char* BytesToSI(uint64_t size, char theValue[]) {
+ char units[8];
+ float sizeInSI;
+
+ if (theValue != NULL) {
+ sizeInSI = (float) size;
+ strcpy (units, " bytes");
+ if (sizeInSI > 1024.0) {
+ sizeInSI /= 1024.0;
+ strcpy(units, " KiB");
+ } // if
+ if (sizeInSI > 1024.0) {
+ sizeInSI /= 1024.0;
+ strcpy(units, " MiB");
+ } // if
+ if (sizeInSI > 1024.0) {
+ sizeInSI /= 1024.0;
+ strcpy(units, " GiB");
+ } // if
+ if (sizeInSI > 1024.0) {
+ sizeInSI /= 1024.0;
+ strcpy(units, " TiB");
+ } // if
+ if (sizeInSI > 1024.0) {
+ sizeInSI /= 1024.0;
+ strcpy(units, " PiB");
+ } // if
+ if (strcmp(units, " bytes") == 0) { // in bytes, so no decimal point
+ sprintf(theValue, "%1.0f%s", sizeInSI, units);
+ } else {
+ sprintf(theValue, "%1.1f%s", sizeInSI, units);
+ } // if/else
+ } // if
+ return theValue;
+} // BlocksToSI()
+
+// Returns block size of device pointed to by fd file descriptor, or -1
+// if there's a problem
+int GetBlockSize(int fd) {
+ int err, result;
+
+#ifdef __APPLE__
+ err = ioctl(fd, DKIOCGETBLOCKSIZE, &result);
+#else
+ err = ioctl(fd, BLKSSZGET, &result);
+#endif
+
+ if (result != 512) {
+ printf("\aWARNING! Sector size is not 512 bytes! This program is likely to");
+ printf("misbehave! Proceed at your own risk!\n\n");
+ } // if
+
+ if (err == -1)
+ result = -1;
+ return (result);
+} // GetBlockSize()
+
+// Convert a GUID to a string representation, suitable for display
+// to humans....
+char* GUIDToStr(struct GUIDData theGUID, char* theString) {
+ uint64_t block;
+
+ if (theString != NULL) {
+ block = (theGUID.data1 & UINT64_C(0x00000000FFFFFFFF));
+ sprintf(theString, "%08llX-", (unsigned long long) block);
+ block = (theGUID.data1 & UINT64_C(0x0000FFFF00000000)) >> 32;
+ sprintf(theString, "%s%04llX-", theString, (unsigned long long) block);
+ block = (theGUID.data1 & UINT64_C(0xFFFF000000000000)) >> 48;
+ sprintf(theString, "%s%04llX-", theString, (unsigned long long) block);
+ block = (theGUID.data2 & UINT64_C(0x00000000000000FF));
+ sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
+ block = (theGUID.data2 & UINT64_C(0x000000000000FF00)) >> 8;
+ sprintf(theString, "%s%02llX-", theString, (unsigned long long) block);
+ block = (theGUID.data2 & UINT64_C(0x0000000000FF0000)) >> 16;
+ sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
+ block = (theGUID.data2 & UINT64_C(0x00000000FF000000)) >> 24;
+ sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
+ block = (theGUID.data2 & UINT64_C(0x000000FF00000000)) >> 32;
+ sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
+ block = (theGUID.data2 & UINT64_C(0x0000FF0000000000)) >> 40;
+ sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
+ block = (theGUID.data2 & UINT64_C(0x00FF000000000000)) >> 48;
+ sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
+ block = (theGUID.data2 & UINT64_C(0xFF00000000000000)) >> 56;
+ sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
+ } // if
+ return theString;
+} // GUIDToStr()
+
+// Get a GUID from the user
+GUIDData GetGUID(void) {
+ uint64_t part1, part2, part3, part4, part5;
+ int entered = 0;
+ char temp[255], temp2[255];
+ GUIDData theGUID;
+
+ printf("\nA GUID is entered in five segments of from two to six bytes, with\n"
+ "dashes between segments.\n");
+ printf("Enter the entire GUID, a four-byte hexadecimal number for the first segment, or\n"
+ "'R' to generate the entire GUID randomly: ");
+ fgets(temp, 255, stdin);
+
+ // If user entered 'r' or 'R', generate GUID randomly....
+ if ((temp[0] == 'r') || (temp[0] == 'R')) {
+ theGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
+ theGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
+ entered = 1;
+ } // if user entered 'R' or 'r'
+
+ // If string length is right for whole entry, try to parse it....
+ if ((strlen(temp) == 37) && (entered == 0)) {
+ strncpy(temp2, &temp[0], 8);
+ temp2[8] = '\0';
+ sscanf(temp2, "%llx", &part1);
+ strncpy(temp2, &temp[9], 4);
+ temp2[4] = '\0';
+ sscanf(temp2, "%llx", &part2);
+ strncpy(temp2, &temp[14], 4);
+ temp2[4] = '\0';
+ sscanf(temp2, "%llx", &part3);
+ theGUID.data1 = (part3 << 48) + (part2 << 32) + part1;
+ strncpy(temp2, &temp[19], 4);
+ temp2[4] = '\0';
+ sscanf(temp2, "%llx", &part4);
+ strncpy(temp2, &temp[24], 12);
+ temp2[12] = '\0';
+ sscanf(temp2, "%llx", &part5);
+ theGUID.data2 = ((part4 & UINT64_C(0x000000000000FF00)) >> 8) +
+ ((part4 & UINT64_C(0x00000000000000FF)) << 8) +
+ ((part5 & UINT64_C(0x0000FF0000000000)) >> 24) +
+ ((part5 & UINT64_C(0x000000FF00000000)) >> 8) +
+ ((part5 & UINT64_C(0x00000000FF000000)) << 8) +
+ ((part5 & UINT64_C(0x0000000000FF0000)) << 24) +
+ ((part5 & UINT64_C(0x000000000000FF00)) << 40) +
+ ((part5 & UINT64_C(0x00000000000000FF)) << 56);
+ entered = 1;
+ } // if
+
+ // If neither of the above methods of entry was used, use prompted
+ // entry....
+ if (entered == 0) {
+ sscanf(temp, "%llx", &part1);
+ printf("Enter a two-byte hexadecimal number for the second segment: ");
+ fgets(temp, 255, stdin);
+ sscanf(temp, "%llx", &part2);
+ printf("Enter a two-byte hexadecimal number for the third segment: ");
+ fgets(temp, 255, stdin);
+ sscanf(temp, "%llx", &part3);
+ theGUID.data1 = (part3 << 48) + (part2 << 32) + part1;
+ printf("Enter a two-byte hexadecimal number for the fourth segment: ");
+ fgets(temp, 255, stdin);
+ sscanf(temp, "%llx", &part4);
+ printf("Enter a six-byte hexadecimal number for the fifth segment: ");
+ fgets(temp, 255, stdin);
+ sscanf(temp, "%llx", &part5);
+ theGUID.data2 = ((part4 & UINT64_C(0x000000000000FF00)) >> 8) +
+ ((part4 & UINT64_C(0x00000000000000FF)) << 8) +
+ ((part5 & UINT64_C(0x0000FF0000000000)) >> 24) +
+ ((part5 & UINT64_C(0x000000FF00000000)) >> 8) +
+ ((part5 & UINT64_C(0x00000000FF000000)) << 8) +
+ ((part5 & UINT64_C(0x0000000000FF0000)) << 24) +
+ ((part5 & UINT64_C(0x000000000000FF00)) << 40) +
+ ((part5 & UINT64_C(0x00000000000000FF)) << 56);
+ entered = 1;
+ } // if/else
+ printf("New GUID: %s\n", GUIDToStr(theGUID, temp));
+ return theGUID;
+} // GetGUID()
+
+// Compute (2 ^ value). Given the return type, value must be 63 or less.
+// Used in some bit-fiddling functions
+uint64_t PowerOf2(int value) {
+ uint64_t retval = 1;
+ int i;
+
+ if ((value < 64) && (value >= 0)) {
+ for (i = 0; i < value; i++) {
+ retval *= 2;
+ } // for
+ } else retval = 0;
+ return retval;
+} // PowerOf2()
+
+/**************************************************************************************
+ * *
+ * 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
+// to work around a problem returning a uint64_t value on Mac OS.
+uint64_t disksize(int fd, int *err) {
+ long sz; // Do not delete; needed for Linux
+ long long b; // Do not delete; needed for Linux
+ uint64_t sectors;
+
+ // 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, &sectors);
+#else
+ *err = ioctl(fd, BLKGETSIZE, &sz);
+ if (*err) {
+ sz = 0;
+ if (errno != EFBIG)
+ return sz;
+ }
+ *err = ioctl(fd, BLKGETSIZE64, &b);
+ if (*err || b == 0 || b == sz)
+ sectors = sz;
+ else
+ sectors = (b >> 9);
+#endif
+ return sectors;
+}
diff --git a/support.h b/support.h
new file mode 100644
index 0000000..cba12d7
--- /dev/null
+++ b/support.h
@@ -0,0 +1,42 @@
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#ifdef __APPLE__
+// Darwin (Mac OS) only: disk IOCTLs are different, and there is no lseek64
+#include <sys/disk.h>
+#define lseek64 lseek
+#else
+
+// Linux only....
+#include <linux/fs.h>
+#endif
+
+#include <string>
+
+#ifndef __GPTSUPPORT
+#define __GPTSUPPORT
+
+// Set this as a default
+#define SECTOR_SIZE UINT32_C(512)
+
+using namespace std;
+
+// a GUID
+struct GUIDData {
+ uint64_t data1;
+ uint64_t data2;
+}; // struct GUIDData
+
+int GetNumber(int low, int high, int def, const char prompt[]);
+char GetYN(void);
+uint64_t GetLastSector(uint64_t low, uint64_t high, char prompt[]);
+char* BytesToSI(uint64_t size, char theValue[]);
+int GetBlockSize(int fd);
+char* GUIDToStr(struct GUIDData theGUID, char* theString);
+GUIDData GetGUID(void);
+uint64_t PowerOf2(int value);
+
+uint64_t disksize(int fd, int* err);
+
+#endif