From e7b4ff9317fc4e551cf974684eaa88697de5a28d Mon Sep 17 00:00:00 2001 From: srs5694 Date: Tue, 18 Aug 2009 13:16:10 -0400 Subject: [PATCH] Initial git repository creation, version 0.3.1 plus changes --- CHANGELOG | 83 +++ COPYING | 340 ++++++++++ Makefile | 34 + README | 95 +++ attributes.cc | 81 +++ attributes.h | 28 + crc32.cc | 70 +++ crc32.h | 20 + gdisk.8 | 602 ++++++++++++++++++ gdisk.cc | 270 ++++++++ gpt.cc | 1660 +++++++++++++++++++++++++++++++++++++++++++++++++ gpt.h | 153 +++++ mbr.cc | 447 +++++++++++++ mbr.h | 95 +++ parttypes.cc | 332 ++++++++++ parttypes.h | 44 ++ support.cc | 354 +++++++++++ support.h | 42 ++ 18 files changed, 4750 insertions(+) create mode 100644 CHANGELOG create mode 100644 COPYING create mode 100644 Makefile create mode 100644 README create mode 100644 attributes.cc create mode 100644 attributes.h create mode 100644 crc32.cc create mode 100644 crc32.h create mode 100644 gdisk.8 create mode 100644 gdisk.cc create mode 100644 gpt.cc create mode 100644 gpt.h create mode 100644 mbr.cc create mode 100644 mbr.h create mode 100644 parttypes.cc create mode 100644 parttypes.h create mode 100644 support.cc create mode 100644 support.h 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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. 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 +#include +#include +#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 +#include +#include +#include +#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 +#include + +/* 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 + +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 +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include + +#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 +#include +#include +#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 +#include +#include +#include +#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 +#include +#include +#include +#include +#include "support.h" + +#include + +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, §ors); +#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 +#include +#include + +#ifdef __APPLE__ +// Darwin (Mac OS) only: disk IOCTLs are different, and there is no lseek64 +#include +#define lseek64 lseek +#else + +// Linux only.... +#include +#endif + +#include + +#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