diff options
author | srs5694 <srs5694@users.sourceforge.net> | 2009-08-18 13:16:10 -0400 |
---|---|---|
committer | srs5694 <srs5694@users.sourceforge.net> | 2009-08-18 13:16:10 -0400 |
commit | e7b4ff9317fc4e551cf974684eaa88697de5a28d (patch) | |
tree | 5b5cc2b2fdce62960d03ad537f151df8c581902e | |
download | sgdisk-e7b4ff9317fc4e551cf974684eaa88697de5a28d.tar.gz |
Initial git repository creation, version 0.3.1 plus changes
-rw-r--r-- | CHANGELOG | 83 | ||||
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | Makefile | 34 | ||||
-rw-r--r-- | README | 95 | ||||
-rw-r--r-- | attributes.cc | 81 | ||||
-rw-r--r-- | attributes.h | 28 | ||||
-rw-r--r-- | crc32.cc | 70 | ||||
-rw-r--r-- | crc32.h | 20 | ||||
-rw-r--r-- | gdisk.8 | 602 | ||||
-rw-r--r-- | gdisk.cc | 270 | ||||
-rw-r--r-- | gpt.cc | 1660 | ||||
-rw-r--r-- | gpt.h | 153 | ||||
-rw-r--r-- | mbr.cc | 447 | ||||
-rw-r--r-- | mbr.h | 95 | ||||
-rw-r--r-- | parttypes.cc | 332 | ||||
-rw-r--r-- | parttypes.h | 44 | ||||
-rw-r--r-- | support.cc | 354 | ||||
-rw-r--r-- | support.h | 42 |
18 files changed, 4750 insertions, 0 deletions
diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..4ea90ef --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,83 @@ +0.3.1: +------ + +- Added Mac OS X support, provided as a patch by an anonymous contributor. + +- Fixed bug in disksize() function on Mac OS. (Possibly dependent on the + kernel and/or GCC version.) The disk size, of type uint64_t, was not + being passed correctly, so I reorganized the function to return it as + the function's return value rather than as a parameter. This seems to + work OK on my Mac OS test system and on both 32- and 64-bit Linux + systems. + +- Added test for writability when opening a disk for reading. If the + test fails, a warning message is displayed. (The test simply opens + the disk for writing and then closes it before writing any data, + so the test shouldn't cause problems.) + +- Fixed off-by-one bug in GPTData::FindLastAvailable(). + +- Fixed bug that caused display of options after a disk-write error. + +- Fixed several incorrect MacOS X partition type GUIDs. + +0.3.0: +------ + +- Changed version number to 0.3.0, reflecting the fact that I've received + no significant bug reports and so am elevating the program to "beta" + status. This change also entailed altering the warning the program + displays when saving partition table changes. + +- Fixed minor bug in CHS geometry of the protective MBR's type EE partition + (was producing 0x000200 as the start value, but should be 0x000100). + Should be a non-critical bug since the protective MBR partition + definition is only there to keep MBR-only disk utilities from messing + with the disk. + +- Added ability to enter GUIDs as single massive strings rather than in + chunks. + +0.2.2: +------ + +- Added #include directives required to compile the program using GCC + 4.4.0. + +0.2.1: +------ + +- Fixed partition numbering problem in reports of partition overlaps in + verification function. + +- Fixed 1-sector partition size problem when creating new partitions + (partitions were 1 sector too big when using the +size option). + +- Changed BytesToSI() to display values in bytes without decimal points + (e.g., "512 bytes" rather than "512.0 bytes"). + +- Added GPTData class member functions to retrieve GPT data structure + locations on disk; used in my internal-use-only GPT-wiping program. + +- Eliminated the "a reboot is recommended" notice after writing the + partition table. + +- Added notice after sorting the partition table to the effect that + editing /etc/fstab and/or the boot loader files may be required. + +- Fixed bug in MBR-reading function that caused 0x0f (Win95 LBA) and 0x85 + (Linux extended) extended partitions to not be read. + +- Fixed bug in GetLastSector() (in support.cc) that would have prevented + correct user entry of over-32-bit sector numbers on 32-bit systems. + +- Made some changes/corrections to the partition type list in + parttypes.cc. Most of these were based on newly-discovered MBR type + codes for Apple (Mac OS X) filesystems. + +- General code cleanup (setting explicit casts, etc.) + +0.2.0: +------ + +- Initial semi-public release @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2c55332 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +CC=gcc +CXX=g++ +#CFLAGS=-O2 -fpack-struct +CFLAGS=-O2 -fpack-struct -D_FILE_OFFSET_BITS=64 -g +CXXFLAGS=-O2 -fpack-struct -D_FILE_OFFSET_BITS=64 -g +LIB_NAMES=support crc32 mbr gpt parttypes attributes +LIB_SRCS=$(NAMES:=.cc) +LIB_OBJS=$(LIB_NAMES:=.o) +LIB_HEADERS=$(LIB_NAMES:=.h) +DEPEND= makedepend $(CFLAGS) + +#$(APPNAME): $(MBR2GPT_OBJS) +# $(CC) $(MBR2GPT_OBJS) -o $@ + +gdisk: $(LIB_OBJS) gdisk.o + $(CXX) $(LIB_OBJS) gdisk.o -o gdisk + +wipegpt: $(LIB_OBJS) wipegpt.o + $(CXX) $(LIB_OBJS) wipegpt.o -o wipegpt + +lint: #no pre-reqs + lint $(SRCS) + +clean: #no pre-reqs + rm -f core *.o *~ gdisk + +# what are the source dependencies +depend: $(SRCS) + $(DEPEND) $(SRCS) + +$(OBJS): + +# DO NOT DELETE + @@ -0,0 +1,95 @@ +GPT fdisk (aka gdisk) +by Roderick W. Smith, rodsmith@rodsbooks.com + +Introduction +------------ + +This software is intended as a (somewhat) fdisk-workalike program for +GPT-partitioned disks. Although libparted and programs that use it (GNU +Parted, gparted, etc.) provide the ability to handle GPT disks, they have +certain limitations that gdisk overcomes. Specific advantages of gdisk +include: + +* The ability to convert MBR-partitioned disks in-place to GPT format, + without losing data + +* The ability to specify sector-exact partition sizes + +* More flexible specification of filesystem type code GUIDs, which + GNU Parted tends to corrupt (particularly for FAT partitions) + +* Clear identification of the number of unallocated sectors on a + disk + +* A user interface that's familiar to long-time users of Linux + fdisk + +* The MBR boot loader code is left alone (GNU Parted tends to + wipe it out with every change) + +Of course, gdisk isn't without its limitations. Most notably, it lacks the +filesystem awareness and filesystem-related features of GNU Parted. You +can't resize a partition's filesystem or create a partition with a +filesystem already in place with gdisk, for instance. There's no GUI +version of gdisk. + +Installing +---------- + +To compile gdisk, you must have appropriate development tools installed, +most notably the GNU Compiler Collection (GCC) and its g++ compiler for +C++. uncompress the package and type "make" at the command prompt in the +resulting directory. The result should be a program file called gdisk. You +can use this in place or copy the file to a suitable directory, such as +/usr/local/sbin. You can copy the man page (gdisk.8) to /usr/local/man/man8 +to make it available. + +Caveats +------- + +THIS SOFTWARE IS EARLY BETA SOFTWARE! IF IT WIPES OUT YOUR HARD DISK OR +EATS YOUR CAT, DON'T BLAME ME! To date, I've tested the software mainly on +two USB flash drives, 2 GiB and 8 GiB in size. I've also made a few minor +tweaks to a production system with a 500 GiB hard disk and made more +extensive changes to a handful of 80-160 GiB hard disks. I believe all +data-corruption bugs to be squashed, but I know full well that the odds of +my missing something are high. This is particularly true for large drives; +I have no way of testing the software with > 2TiB drives, which will test +the 64-bit sector pointer support. + +The MBR-to-GPT feature seems to work well for data drives, but it's largely +untested on boot drives. One attempt with Windows failed miserably, but I +believe that was because of Windows' inherent limitations with respect to +GPT. (The partitions themselves were intact.) + +My main development platform is a system running the 64-bit version of +Ubuntu. I've also tested on 64-bit OpenSuSE and 32-bit Fedora 10. Problems +relating to 64-bit integers on the 32-bit Linux have been common during +development and may crop up in the future. The Mac OS X support is new, +and has at least one bug/limitation: It seems to be impossible to write +a new partition table if any partitions from the disk are currently +mounted. + +Redistribution +-------------- + +This program is licensed under terms of the GNU GPL (see the file COPYING). + +Acknowledgements +---------------- + +This code is mostly my own; however, I've used three functions from two +other GPLed programs: + +- The code used to generate CRCs is taken from the efone program by + Krzysztof Dabrowski and ElysiuM deeZine. (See the crc32.h and + crc32.cc source code files.) + +- A function to find the disk size is taken from Linux fdisk by + A. V. Le Blanc. + +Additional code contributors include: + +- Yves Blusseau (1otnwmz02@sneakemail.com) + +- One anonymous contributor diff --git a/attributes.cc b/attributes.cc new file mode 100644 index 0000000..1a299ea --- /dev/null +++ b/attributes.cc @@ -0,0 +1,81 @@ +// attributes.cc +// Class to manage partition attribute codes. These are binary bit fields, +// of which only three are currently (2/2009) documented on Wikipedia. + +#define __STDC_LIMIT_MACROS +#define __STDC_CONSTANT_MACROS + +#include <string.h> +#include <stdint.h> +#include <stdio.h> +#include "attributes.h" + +using namespace std; + +// Constructor. Its main task is to initialize the attribute name +// data. +Attributes::Attributes(void) { + int i; + char temp[ATR_NAME_SIZE]; + + // Most bits are undefined, so start by giving them an + // appropriate name + for (i = 1; i < NUM_ATR; i++) { + sprintf(temp, "Undefined bit #%d", i); + strcpy(atNames[i], temp); + } // for + + // Now reset those names that are defined.... + strcpy(atNames[0], "system partition"); + strcpy(atNames[60], "read-only"); + strcpy(atNames[62], "hidden"); + strcpy(atNames[63], "do not automount"); +} // Attributes constructor + +// Destructor. +Attributes::~Attributes(void) { +} // Attributes destructor + +// Display current attributes to user +void Attributes::DisplayAttributes(void) { + int i; + + printf("Attribute value is %llX. Set fields are:\n", + (unsigned long long) attributes); + for (i = 0; i < NUM_ATR; i++) { + if (((attributes >> i) % 2) == 1) { // bit is set +/* if (strncmp("Undefined", atNames[i], 9) != 0) + printf("%s\n", atNames[i]); */ + if (strncmp("Undefined", atNames[NUM_ATR - i - 1], 9) != 0) + printf("%s\n", atNames[NUM_ATR - i - 1]); + } // if + } // for +} // Attributes::DisplayAttributes() + +// Prompt user for attribute changes +void Attributes::ChangeAttributes(void) { + int response, i; + uint64_t bitValue; + + printf("Known attributes are:\n"); + for (i = 0; i < NUM_ATR; i++) { + if (strncmp("Undefined", atNames[i], 9) != 0) + printf("%d - %s\n", i, atNames[i]); + } // for + + do { + response = GetNumber(0, 64, -1, "Toggle which attribute field (0-63, 64 to exit): "); + if (response != 64) { + bitValue = PowerOf2(NUM_ATR - response - 1); // Find the integer value of the bit +// bitValue = PowerOf2(response); // Find the integer value of the bit + if ((bitValue & attributes) == bitValue) { // bit is set + attributes -= bitValue; // so unset it + printf("Have disabled the '%s' attribute.\n", atNames[response]); + } else { // bit is not set + attributes += bitValue; // so set it + printf("Have enabled the '%s' attribute.\n", atNames[response]); + } // if/else + } // if + } while (response != 64); +} // Attributes::ChangeAttributes() + diff --git a/attributes.h b/attributes.h new file mode 100644 index 0000000..a7538a1 --- /dev/null +++ b/attributes.h @@ -0,0 +1,28 @@ +#include <stdint.h> +#include <unistd.h> +#include <stdlib.h> +#include <string> +#include "support.h" + +#ifndef __GPT_ATTRIBUTES +#define __GPT_ATTRIBUTES + +#define NUM_ATR 64 /* # of attributes -- 64, since it's a 64-bit field */ +#define ATR_NAME_SIZE 25 /* maximum size of attribute names */ + +using namespace std; + +class Attributes { +protected: + uint64_t attributes; + char atNames[NUM_ATR][ATR_NAME_SIZE]; +public: + Attributes(void); + ~Attributes(void); + void SetAttributes(uint64_t a) {attributes = a;} + uint64_t GetAttributes(void) {return attributes;} + void DisplayAttributes(void); + void ChangeAttributes(void); +}; // class Attributes + +#endif diff --git a/crc32.cc b/crc32.cc new file mode 100644 index 0000000..a446ffc --- /dev/null +++ b/crc32.cc @@ -0,0 +1,70 @@ +/*
+ * efone - Distributed internet phone system.
+ *
+ * (c) 1999,2000 Krzysztof Dabrowski
+ * (c) 1999,2000 ElysiuM deeZine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+/* based on implementation by Finn Yannick Jacobs */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab().
+ * so make sure, you call it before using the other
+ * functions!
+ */
+u_int32_t crc_tab[256];
+
+/* chksum_crc() -- to a given block, this one calculates the
+ * crc32-checksum until the length is
+ * reached. the crc32-checksum will be
+ * the result.
+ */
+u_int32_t chksum_crc32 (unsigned char *block, unsigned int length)
+{
+ register unsigned long crc;
+ unsigned long i;
+
+ crc = 0xFFFFFFFF;
+ for (i = 0; i < length; i++)
+ {
+ crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
+ }
+ return (crc ^ 0xFFFFFFFF);
+}
+
+/* chksum_crc32gentab() -- to a global crc_tab[256], this one will
+ * calculate the crcTable for crc32-checksums.
+ * it is generated to the polynom [..]
+ */
+
+void chksum_crc32gentab ()
+{
+ unsigned long crc, poly;
+ int i, j;
+
+ poly = 0xEDB88320L;
+ for (i = 0; i < 256; i++)
+ {
+ crc = i;
+ for (j = 8; j > 0; j--)
+ {
+ if (crc & 1)
+ {
+ crc = (crc >> 1) ^ poly;
+ }
+ else
+ {
+ crc >>= 1;
+ }
+ }
+ crc_tab[i] = crc;
+ }
+}
@@ -0,0 +1,20 @@ +/*
+ * efone - Distributed internet phone system.
+ *
+ * (c) 1999,2000 Krzysztof Dabrowski
+ * (c) 1999,2000 ElysiuM deeZine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+/* based on implementation by Finn Yannick Jacobs. */
+
+#include <stdint.h>
+
+void chksum_crc32gentab ();
+uint32_t chksum_crc32 (unsigned char *block, unsigned int length);
+extern unsigned int crc_tab[256];
@@ -0,0 +1,602 @@ +.\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com) +.\" May be distributed under the GNU General Public License +.TH GDISK 8 "August 2009" "Linux 2.6" "GPT fdisk Manual" +.SH NAME +gdisk \- GPT partition table manipulator for Linux +.SH SYNOPSIS +.BI "gdisk " +[ \-l ] +.I device +.SH DESCRIPTION +Hard disks can be divided into one or more segments, known as +.IR partitions . +This division is described in the +.I "partition table" +of the disk. Several different partition table formats exist, each with its +advantages and disadvantages. + +The original partitioning system used on PCs, now known as the +.IR "MBR partitioning scheme", +is subject to several limitations. These include an awkward distinction +between +.IR "primary", +.IR "extended", +and +.IR "logical" +partitions; no redundancy or error correction capabilities; and 32-bit data +structures that, in conjunction with the common 512-byte sector size, +impose a hard 2 TiB limit on the size of partitions and disks. This final +drawback makes MBR partitions unsuitable for use on large hardware RAID +arrays. Individual disk sizes are expected to reach the 2 TiB limit in +2009, so MBR will become an unsuitable partitioning system even for +individual hard disks in the near future. + +The successor to MBR partitions is the +.IR "Globally Unique Identifier (GUID) Partition Table (GPT)" +system. GPT addresses each of the major limitations of MBR partitions, and +includes a dummy MBR partition table with a single +.IR "protective MBR" +entry to keep GPT-unaware programs from modifying the disk's GPT partitions. GPT +is a new partitioning scheme, though, and as such, older utilities and OSes +must be replaced or modified to handle GPT. Linux's venerable +.B "fdisk" +program, in particular, cannot process GPT disks. (The same is true of +related programs, such as +.B "sfdisk" +and +.BR "cfdisk".) +The alternative GNU +Parted and related programs, however, are capable of working on both MBR +and GPT disks. + +GPT is often associated with the +.IR "Extensible Firmware Interface (EFI)", +which is Intel's intended successor to the traditional (legacy) PC BIOS. It +is possible to use and even boot from GPT disks on non-EFI systems, +including those that use a legacy BIOS. Using GPT disks on such a system +isn't a great challenge, although the OS must support GPT. Booting from a +GPT-based disk requires that the OS support this action, and if the system +is BIOS-based, a GPT-aware boot loader is required. Patched versions of the +.IR "Grand Unified Bootloader (GRUB)" +0.97, as well as GRUB2, support GPT. + +GPT creates five distinct data structures of three types: +.TP +.B "Protective MBR" +The first sector (512 bytes) of the disk is devoted to an MBR that +consists of a single partition spanning the entire disk (or 2 TiB for disks +larger than this). The protective MBR may optionally include first-stage +boot loader code. +.TP +.B "GPT headers" +Two GPT headers exist, a main header and a backup header. The primary +header resides immediately after the protective MBR, and the backup header +is stored on the last sector of the disk. These headers contain disk +metadata, such as the location of the partition table, the size of the +partition table, and a "serial number" (GUID) that should be unique for +each disk. Each GPT header also stores two CRC checksums, one for the +partition table and one for the GPT header itself. +.TP +.B "Partition tables" +Each GPT header points to one partition table. The main partition table +appears immediately after the main GPT header, and the backup partition +table comes immediately before its GPT header. Typically, the partition +tables may hold data on up to 128 partitions, although +.B gdisk +enables you to change this value. Each entry contains 64-bit start and stop +sector numbers, a name, a partition GUID type code, a unique partition GUID +identifier, and additional data. +.PP + +The GPT fdisk (aka +.BR "gdisk") +program operates mainly on the GPT headers and partition tables; however, +it can and will generate a fresh protective MBR, when required. (Any boot +loader code in the protective MBR will not be disturbed.) If you've created +an unusual protective MBR, such as a hybrid MBR created by +.IR "gptsync", +this should not be disturbed by most ordinary actions. Some advanced data +recovery options require you to understand the distinctions between the +main and backup data, as well as between the GPT headers and the partition +tables. + +The +.B "gdisk" +program employs a user interface similar to that of +.BR "fdisk", +but +.B "gdisk" +modifies GPT partitions. It also has the capability of transforming MBR +partitions into GPT partitions. Like the original +.B fdisk +program, +.B gdisk +does not modify disk structures until you explicitly write them to disk, so +if you make a mistake, you can exit from the program with the 'q' option to +save your partitions. + +.B gdisk +is a text-mode menu-driven program for creation and manipulation of +partition tables. It will automatically convert an MBR partition table to +GPT format, or will load a GPT partition table. When used with the +.IR "\-l" +command-line option, the program displays the current partition table and +then exits. + +Linux hard disk device filenames take the form +.IR "/dev/sdx" +or +.IR "/dev/hdx", +where +.IR "x" +is a letter from +.IR "a" +onward. The +.IR "hdx" +devices originally referred to IDE (aka PATA) drives, whereas +.IR "sdx" +devices originally referred to SCSI drives. These distinctions are now +blurring. Modern SATA drives and USB flash drives usually acquire +.IR "sdx" +names, and the same can even be true of PATA drives, depending on kernel +driver options. For instance, +.IR "/dev/hda" +refers to the first PATA drive, whereas +.IR "/dev/sdb" +is the second SCSI, SATA, USB, or other SCSI-equivalent drive. To use +.BR "gdisk", +you must pass a device filename to the program on the command line. + +The +.I partition +is a +device name followed by a partition number. For example, +.B /dev/hda1 +is the first partition on the first PATA hard disk. +.B gdisk +creates partitions, but you don't pass partition numbers or partition +device filenames to the program. Linux generates numbers for GPT partitions +based on the partition's position in the partition table. + +The MBR partitioning system uses a combination of cylinder/head/sector +(CHS) addressing and logical block addressing (LBA). The former is klunky +and limiting. GPT drops CHS addressing and uses 64-bit LBA mode +exclusively. Thus, GPT data structures, and therefore +.BR "gdisk", +do not need to deal with CHS geometries and all the problems they create. +Users of +.BR "fdisk" +will note that +.B "gdisk" +lacks the options and limitations associated with CHS geometries. + +For best results, you should always use an OS-specific partition table +program. For example, you should make Mac OS X partitions with the Mac OS +X Disk Utility +program and Linux partitions with the Linux +.B "gdisk" +or GNU Parted program. + +Upon start, +.B gdisk +attempts to identify the partition type in use on the specified disk. If it +finds valid GPT data, +.B gdisk +will use it. If +.B gdisk +finds a valid MBR but no GPT data, it will attempt to convert the MBR into +GPT form. Upon exiting with the 'w' option, +.B gdisk +will then replace the MBR with a GPT. +.IR "This action is potentially dangerous!" +Your system may become unbootable, and partition type codes may become +corrupted if the disk uses unrecognized type codes. Boot problems are +particularly likely if you're multi-booting with any GPT-unaware OS. If you +mistakenly launch +.B gdisk +on an MBR disk, you can safely exit the program +without making any changes by using the 'q' option. + +The MBR-to-GPT conversion will leave at least one gap in the partition +numbering if the original MBR used logical partitions. These gaps are +harmless, but you can eliminate them by using the 's' option, if you like. +(Doing this may require you to update your +.IR "/etc/fstab" +file.) + +When creating a fresh partition table, certain considerations may be in +order: + +.TP +.B * +For data (non-boot) disks, and for boot disks used on BIOS-based computers +with GRUB as the boot loader, partitions may be created in whatever order +and in whatever sizes are desired. + +.TP +.B * +Boot disks for EFI-based systems require an +.IR "EFI System Partition" ( +.B "gdisk" +internal code 0xEF00) formatted as FAT-32. The recommended size of this +partition is 100 MiB. Boot-related files are stored here. (Note that GNU +Parted identifies such partitions as having the "boot flag" set.) + +.TP +.B * +If Windows is to boot from a GPT disk, a partition of type "Microsoft +Reserved" ( +.B "gdisk" +internal code 0x0C01) is recommended. This partition should be about 128 MiB +in size. It ordinarily follows the EFI System Partition and immediately +precedes the Windows data partitions. (Note that GNU Parted creates all +FAT partitions as this type, which actually makes the partition unusable +for normal file storage in both Windows and Mac OS X.) + +.TP +.B * +Some OSes' GPT utilities create some blank space (typically 128 MiB) after +each partition. The intent is to enable future disk utilities to use this +space. Such free space is not required of GPT disks, but creating it may +help in future disk maintenance. + +.SH OPTIONS +.TP +.B \-l +List the partition tables for the specified devices and then exit. +.PP + +Most interactions with +.B gdisk +occur with its interactive text-mode menus. The main menu provides the +following options: + +.TP +.B c +Change the GPT name of a partition. This name is encoded as a UTF-16 +string, but +.B gdisk +supports only ASCII characters as names. For the most part, Linux ignores +the partition name, but it may be important in some OSes. + +.TP +.B d +Delete a partition. This action deletes the entry from the partition table +but does not disturb the data within the sectors originally allocated to +the partition on the disk. + +.TP +.B i +Show detailed partition information. The summary information produced by +the 'p' command necessarily omits many details, such as the partition's +unique GUID and the translation of +.BR "gdisk"'s +internal partition type code to a plain type name. The 'i' option +displays this information for a single partition. + +.TP +.B l +Display a summary of partition types. GPT uses a GUID to identify +partition types for particular OSes and purposes. For ease of data entry, +.B gdisk +compresses these into two-byte (four-digit hexadecimal) values that are +related to their MBR codes. Specifically, the MBR code is multiplied by +hexadecimal 0x0100. For instance, the code for Linux swap space in MBR is +0x82, and it's 0x8200 in +.BR "gdisk". +A one-to-one correspondence is impossible, though. Most notably, many DOS, +Windows, and Linux data partition codes correspond to a single GPT code +(entered as 0x0700 in +.BR "gdisk" ). +Some OSes use a single MBR code but employ many more codes in GPT. For +these, +.B gdisk +adds code numbers sequentially, such as 0xa500 for a FreeBSD disklabel, +0xa501 for FreeBSD boot, 0xa502 for FreeBSD swap, and so on. Note that +these two-byte codes are unique to +.BR "gdisk". + +.TP +.B m +Print the menu. Type this command (or any other unrecognized command) to +see a summary of available options. + +.TP +.B n +Create a new partition. This command is modelled after the equivalent +.B fdisk +option, although some differences exist. You enter a partition number, +starting sector, and either an ending sector or increment (in integral +multiples of sectors, kilobytes, megabytes, gigabytes, or terabytes). You +must also set a partition type code. + +.TP +.B o +Clear out all partition data. This includes GPT header data, +all partition definitions, and the protective MBR. + +.TP +.B p +Display basic partition summary data. This includes partition +numbers, starting and ending sector numbers, partition sizes, +.BR "gdisk"'s +partition types codes, and partition names. For additional information, +use the 'i' command. + +.TP +.B q +Quit from the program +.IR "without saving data". +Use it if you just wanted to view information or if you make a mistake and +want to back out of all your changes. + +.TP +.B s +Sort partition entries. GPT partition numbers need not match the order of +partitions on the disk. If you want them to match, you can use this option. +Note that some partitioning utilities, such as GNU Parted, will sort +partitions whenever they make changes. Such changes will be reflected in +your Linux device filenames, so you may need to edit +.IR "/etc/fstab" +if you use this option. + +.TP +.B t +Change a single partition's type code. You enter the type code using a +two-byte hexadecimal number, as described earlier. You may also enter a +GUID directly, if you have one and +.B gdisk +doesn't know it. + +.TP +.B v +Verify disk. This option checks for a variety of problems, such as +incorrect CRCs and mismatched main and backup data. This option does not +automatically correct these problems, though; for that, you must use +options on the experts' menu. If no problems are found, this command +displays a summary of unallocated disk space. + +.TP +.B w +Write data. Use this command to save your changes. + +.TP +.B x +Enter the experts' menu. Using this option provides access to features you +can use to get into even more trouble than the main menu allows. +.PP + +A few options on the experts' menu duplicate functionality on the main +menu, for the sake of convenience; however, for the most part the experts' +menu provides unusually dangerous or obscure options. These are: + +.TP +.B a +Set attributes. GPT provides a 64-bit attributes field that can be used to +set partition features. +.B gdisk +supports four attributes: +.IR "system partition", +.IR "read-only", +.IR "hidden", +and +.IR "do not automount". +You can set other attributes, but their numbers aren't translated into +anything useful. In practice, most OSes seem to ignore these attributes. +.TP +.B b +Rebuild main GPT header from backup. You can use the backup GPT header to +rebuild the main GPT header with this option. It's likely to be useful if +your main GPT header was damaged or destroyed (say, by sloppy use of +.IR "dd"). +.TP +.B c +Load backup partition table. Ordinarily, +.B gdisk +uses only the main partition table (although the backup's integrity is +checked when you launch the program). If the main partition table has been +damaged, you can use this option to load the backup from disk and use it +instead. Note that this will almost certainly produce no or strange +partition entries if you've just converted an MBR disk to GPT format, since +there will be no backup partition table on disk. + +.TP +.B d +Use main GPT header and rebuild the backup. This option is likely to be +useful if the backup GPT header has been damaged or destroyed. +.TP +.B e +Load main partition table. This option reloads the main partition table +from disk. It's only likely to be useful if you've tried to use the backup +partition table (via 'c') but it's in worse shape then the main partition +table. +.TP +.B f +Change partition GUID. You can enter a custom unique GUID for a partition +using this option. (Note this refers to the GUID that uniquely identifies a +partition, not to its type code.) Ordinarily, +.B gdisk +assigns this number randomly; however, you might want to adjust the number +manually if you've wound up with the same GUID on two partitions. +.TP +.B g +Change disk GUID. Each disk has a unique GUID code, which +.B gdisk +assigns randomly upon creation of the GPT data structures. You can generate +a fresh random GUID or enter one manually with this option. +.TP +.B i +Show detailed partition information. This option is identical to the 'i' +option on the main menu. +.TP +.B k +Save partition data to a backup file. You can back up your partition table +to a disk file using this option. The resulting file is a binary file +consisting of the protective MBR, the main GPT header, the backup GPT +header, and one copy of the partition table, in that order. +.TP +.B l +Load partition data from a backup file. This option is the reverse of the 'k' +option. Note that restoring partition data from anything but the +original disk is not recommended. +.TP +.B m +Print the menu. This option (or any unrecognized entry) displays a summary +of the menu options. +.TP +.B n +Create a new protective MBR. Use this option if the current protective MBR +is damaged in a way that +.B gdisk +doesn't automatically detect and correct. +.TP +.B o +Print protective MBR data. You can see a summary of the protective MBR's +partitions with this option. This may enable you to spot glaring problems +or help identify the partitions in a hybrid MBR. +.TP +.B p +Print the partition table. This option is identical to the 'p' option in +the main menu. +.TP +.B q +Quit without saving changes. This option is identical to the 'q' option in +the main menu. +.TP +.B r +Return to the main menu. You can go back to the main menu with this option. +.TP +.B s +Resize partition table. The partition table may be resized with this +option. The default size is 128 entries. Officially, sizes of less than +16KB (128 entries, given the normal entry size) are unsupported by the GPT +specification; however, in practice they seem to work, and can sometimes be +useful in converting MBR disks. Larger sizes also work fine. Linux imposes +its own limits on the number of partitions, though. +.TP +.B v +Verify disk. This option is identical to the 'v' option in the main menu. +.TP +.B w +Write table to disk and exit. This option is identical to the 'w' option in +the main menu. +.PP + +In many cases, you can press the Enter key to select a default option when +entering data. When only one option is possible, +.B gdisk +usually bypasses the prompt entirely. + +.SH BUGS +As of August of 2009 (version 0.3.1), +.B gdisk +should be considered early beta software. Known bugs and +limitations include: + +.TP +.B * +The program runs correctly only on little-endian (Intel and similar) CPUs. +It should fail gracefully on PowerPC and other big-endian CPUs, but this +hasn't been tested. + +.TP +.B * +The program compiles correctly only on Linux and Mac OS X. Both 64-bit +(x86-64) and 32-bit (x86) versions for Linux have been tested, the former +more thoroughly than the latter. The Mac OS X support was added with +version 0.3.1 and has not been thoroughly tested. + +.TP +.B * +Under Mac OS X, the program will only save a partition table if no +partitions from the disk are currently mounted. (This limitation does not +exist in the Linux version of the program.) + +.TP +.B * +The fields used to display the start and end sector numbers for partitions +in the 'p' command are 14 characters wide. This translates to a limitation +of about 45 PiB. On larger disks, the displayed columns will go out of +alignment. + +.TP +.B * +Only ASCII characters are supported in the partition name field. If an +existing partition uses non-ASCII UTF-16 characters, they're likely to be +corrupted in the 'i' menu option's display; however, they should be +preserved when loading and saving partitions. + +.TP +.B * +The program can load only up to 124 logical partitions when converting from +MBR format. This limit can be raised by changing the #define NUM_LOGICALS +line in the +.IR "mbr.cc" +source code file and recompiling; however, such a change will require using +a larger-than-normal GPT partition table. (The limit of 124 logical +partitions was chosen because that number plus the four primary partitions +equals the 128 partitions supported by the most common GPT partition table +size.) + +.TP +.B * +Converting from MBR format sometimes fails because of insufficient space at +the start or (more commonly) the end of the disk. Resizing the partition +table (using the 's' option in the experts' menu) can sometimes overcome +this problem; however, in extreme cases it may be necessary to resize a +partition using GNU Parted or a similar tool. + +.TP +.B * +Converting from MBR supports only one extended partition. If multiple +extended partitions are found, only the final extended partition's logical +partitions are guaranteed to be converted intact; some or all of the +earlier extended partition(s) logical partitions will be lost. + +.TP +.B * +MBR conversions work only if the disk has correct LBA partition +descriptors. These descriptors should be present on any disk over 8 GiB in +size or on smaller disks partitioned with any but very ancient software. + +.TP +.B * +If an MBR disk contains a FreeBSD disklabel partition, it's converted +in-place as such rather than splitting out its constituent disklabel +partitions into GPT partitions. Other OSes' disklabel partitions may not +get appropriate GUID type codes at all. + +.TP +.B * +Booting after converting an MBR disk may be disrupted. Sometimes +re-installing a boot loader will fix the problem, but other times you may +need to switch boot loaders. Except on EFI-based platforms, Windows through +Vista doesn't support booting from GPT disks. +.PP + +.SH AUTHORS + +Primary author: Roderick W. Smith (rodsmith@rodsbooks.com) + +Contributors: + +* Yves Blusseau (1otnwmz02@sneakemail.com) + +* One anonymous contributor + +.SH "SEE ALSO" +.BR cfdisk (8), +.BR fdisk (8), +.BR mkfs (8), +.BR parted (8), +.BR sfdisk (8) + +.IR "http://en.wikipedia.org/wiki/GUID_Partition_Table" + +.IR "http://developer.apple.com/technotes/tn2006/tn2166.html" + +.IR "http://www.rodsbooks.com/gdisk/" + +.SH AVAILABILITY +The gdisk command is part of the GPT fdisk package and is available from +Rod Smith. diff --git a/gdisk.cc b/gdisk.cc new file mode 100644 index 0000000..7cfb2bf --- /dev/null +++ b/gdisk.cc @@ -0,0 +1,270 @@ +// gdisk.cc +// Program modelled after Linux fdisk, but it manipulates GPT partitions +// rather than MBR partitions. +// +// by Rod Smith, February 2009 + +//#include <iostream> +#include <stdio.h> +#include <string.h> +#include <getopt.h> +#include "mbr.h" +#include "gpt.h" +#include "support.h" + +// Function prototypes.... +// int ReadPartitions(char* filename, struct GPTData* theGPT); +int DoCommand(char* filename, struct GPTData* theGPT); +void ShowCommands(void); +void ShowExpertCommands(void); +int ExpertsMenu(char* filename, struct GPTData* theGPT); + +int main(int argc, char* argv[]) { + GPTData theGPT; + int doMore = 1; + char* device = NULL; + + printf("GPT fdisk (gdisk) version 0.3.1\n\n"); + + if (argc == 2) { // basic usage + if (SizesOK()) { + doMore = theGPT.LoadPartitions(argv[1]); + while (doMore) { + doMore = DoCommand(argv[1], &theGPT); + } // while + } // if + } else if (argc == 3) { // usage with "-l" option + if (SizesOK()) { + if (strcmp(argv[1], "-l") == 0) { + device = argv[2]; + } else if (strcmp(argv[2], "-l") == 0) { + device = argv[1]; + } else { // 3 arguments, but none is "-l" + fprintf(stderr, "Usage: %s [-l] device_file\n", argv[0]); + } // if/elseif/else + if (device != NULL) { + doMore = theGPT.LoadPartitions(device); + if (doMore) theGPT.DisplayGPTData(); + } // if + } // if + } else { + fprintf(stderr, "Usage: %s [-l] device_file\n", argv[0]); + } // if/else +} // main + +// Accept a command and execute it. Returns 0 if the command includes +// an exit condition (such as a q or w command), 1 if more commands +// should be processed. +int DoCommand(char* filename, struct GPTData* theGPT) { + char command, line[255]; + int retval = 1; + PartTypes typeHelper; + uint32_t temp1, temp2; + + printf("\nCommand (m for help): "); + fgets(line, 255, stdin); + sscanf(line, "%c", &command); + switch (command) { +/* case 'b': case 'B': + GetGUID(); + break; */ + case 'c': case 'C': + if (theGPT->GetPartRange(&temp1, &temp2) > 0) + theGPT->SetName(theGPT->GetPartNum()); + else + printf("No partitions\n"); + break; + case 'd': case 'D': + theGPT->DeletePartition(); + break; + case 'i': case 'I': + theGPT->ShowDetails(); + break; + case 'l': case 'L': + typeHelper.ShowTypes(); + break; + case 'n': case 'N': + theGPT->CreatePartition(); + break; + case 'o': case 'O': + theGPT->ClearGPTData(); +// theGPT->protectiveMBR.MakeProtectiveMBR(); +// theGPT->BlankPartitions(); + break; + case 'p': case 'P': + theGPT->DisplayGPTData(); + break; + case 'q': case 'Q': + retval = 0; + break; + case 's': case 'S': + theGPT->SortGPT(); + printf("You may need to edit /etc/fstab and/or your boot loader configuration!\n"); + break; + case 't': case 'T': + theGPT->ChangePartType(); + break; + case 'v': case 'V': + if (theGPT->Verify() > 0) { // problems found + printf("You may be able to correct the problems by using options on the experts\n" + "menu (press 'x' at the command prompt). Good luck!\n"); + } // if + break; + case 'w': case 'W': + if (theGPT->SaveGPTData() == 1) + retval = 0; + break; + case 'x': case 'X': + retval = ExpertsMenu(filename, theGPT); + break; + default: + ShowCommands(); + break; + } // switch + return (retval); +} // DoCommand() + +void ShowCommands(void) { + printf("c\tchange a partition's name\n"); + printf("d\tdelete a partition\n"); + printf("i\tshow detailed information on a partition\n"); + printf("l\tlist available partition types\n"); + printf("m\tprint this menu\n"); + printf("n\tadd a new partition\n"); + printf("o\tcreate a new empty GUID partition table (GPT)\n"); + printf("p\tprint the partition table\n"); + printf("q\tquit without saving changes\n"); + printf("s\tsort partitions\n"); + printf("t\tchange a partition's type code\n"); + printf("v\tverify disk\n"); + printf("w\twrite table to disk and exit\n"); + printf("x\textra functionality (experts only)\n"); +} // ShowCommands() + +// Accept a command and execute it. Returns 0 if the command includes +// an exit condition (such as a q or w command), 1 if more commands +// should be processed. +int ExpertsMenu(char* filename, struct GPTData* theGPT) { + char command, line[255], buFile[255]; + int retval = 1; + PartTypes typeHelper; + uint32_t pn; + uint32_t temp1, temp2; + int goOn = 1; + + do { + printf("\nExpert command (m for help): "); + fgets(line, 255, stdin); + sscanf(line, "%c", &command); + switch (command) { + case 'a': case 'A': + if (theGPT->GetPartRange(&temp1, &temp2) > 0) + theGPT->SetAttributes(theGPT->GetPartNum()); + else + printf("No partitions\n"); + break; + case 'b': case 'B': + theGPT->RebuildMainHeader(); + break; + case 'c': case 'C': + printf("Warning! This will probably do weird things if you've converted an MBR to\n" + "GPT form and haven't yet saved the GPT! Proceed? "); + if (GetYN() == 'Y') + theGPT->LoadSecondTableAsMain(); + break; + case 'd': case 'D': + theGPT->RebuildSecondHeader(); + break; + case 'e': case 'E': + printf("Warning! This will probably do weird things if you've converted an MBR to\n" + "GPT form and haven't yet saved the GPT! Proceed? "); + if (GetYN() == 'Y') + theGPT->LoadMainTable(); + break; + case 'f': case 'F': + if (theGPT->GetPartRange(&temp1, &temp2) > 0) { + pn = theGPT->GetPartNum(); + printf("Enter the partition's new unique GUID:\n"); + theGPT->SetPartitionGUID(pn, GetGUID()); + } else printf("No partitions\n"); + break; + case 'g': case 'G': + printf("Enter the disk's unique GUID:\n"); + theGPT->SetDiskGUID(GetGUID()); + break; +/* case 'h': case 'H': + theGPT->MakeHybrid(); + break; */ + case 'i': case 'I': + theGPT->ShowDetails(); + break; + case 'k': case 'K': + printf("Enter backup filename to save: "); + fgets(line, 255, stdin); + sscanf(line, "%s", &buFile); + theGPT->SaveGPTBackup(buFile); + break; + case 'l': case 'L': + printf("Enter backup filename to load: "); + fgets(line, 255, stdin); + sscanf(line, "%s", &buFile); + theGPT->LoadGPTBackup(buFile); + break; + case 'n': case 'N': + theGPT->MakeProtectiveMBR(); + break; + case 'o': case 'O': + theGPT->DisplayMBRData(); + break; + case 'p': case 'P': + theGPT->DisplayGPTData(); + break; + case 'q': case 'Q': + retval = 0; + goOn = 0; + break; + case 'r': case 'R': + goOn = 0; + break; + case 's': case 'S': + theGPT->ResizePartitionTable(); + break; + case 'v': case 'V': + theGPT->Verify(); + break; + case 'w': case 'W': + if (theGPT->SaveGPTData() == 1) { + retval = 0; + goOn = 0; + } // if + break; + default: + ShowExpertCommands(); + break; + } // switch + } while (goOn); + return (retval); +} // ExpertsMenu() + +void ShowExpertCommands(void) { + printf("a\tset attributes\n"); + printf("b\tuse backup GPT header (rebuilding main)\n"); + printf("c\tload backup partition table from disk (rebuilding main)\n"); + printf("d\tuse main GPT header (rebuilding backup)\n"); + printf("e\tload main partition table from disk (rebuilding backup)\n"); + printf("f\tchange partition GUID\n"); + printf("g\tchange disk GUID\n"); +// printf("h\tmake hybrid MBR\n"); + printf("i\tshow detailed information on a partition\n"); + printf("k\tsave partition data to a backup file\n"); + printf("l\tload partition data from a backup file\n"); + printf("m\tprint this menu\n"); + printf("n\tcreate a new protective MBR\n"); + printf("o\tprint protective MBR data\n"); + printf("p\tprint the partition table\n"); + printf("q\tquit without saving changes\n"); + printf("r\treturn to main menu\n"); + printf("s\tresize partition table\n"); + printf("v\tverify disk\n"); + printf("w\twrite table to disk and exit\n"); +} // ShowExpertCommands() @@ -0,0 +1,1660 @@ +/* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition + data. */ + +/* By Rod Smith, January to February, 2009 */ + +#define __STDC_LIMIT_MACROS +#define __STDC_CONSTANT_MACROS + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <fcntl.h> +#include <string.h> +#include <time.h> +#include <sys/stat.h> +#include <errno.h> +#include "crc32.h" +#include "gpt.h" +#include "support.h" +#include "parttypes.h" +#include "attributes.h" + +using namespace std; + +/**************************************** + * * + * GPTData class and related structures * + * * + ****************************************/ + +GPTData::GPTData(void) { + blockSize = SECTOR_SIZE; // set a default + diskSize = 0; + partitions = NULL; + state = gpt_valid; + strcpy(device, ""); + mainCrcOk = 0; + secondCrcOk = 0; + mainPartsCrcOk = 0; + secondPartsCrcOk = 0; + srand((unsigned int) time(NULL)); + SetGPTSize(NUM_GPT_ENTRIES); +} // GPTData default constructor + +// The following constructor loads GPT data from a device file +GPTData::GPTData(char* filename) { + blockSize = SECTOR_SIZE; // set a default + diskSize = 0; + partitions = NULL; + state = gpt_invalid; + strcpy(device, ""); + mainCrcOk = 0; + secondCrcOk = 0; + mainPartsCrcOk = 0; + secondPartsCrcOk = 0; + srand((unsigned int) time(NULL)); + LoadPartitions(filename); +} // GPTData(char* filename) constructor + +GPTData::~GPTData(void) { + free(partitions); +} // GPTData destructor + +// Resizes GPT to specified number of entries. Creates a new table if +// necessary, copies data if it already exists. +int GPTData::SetGPTSize(uint32_t numEntries) { + struct GPTPartition* newParts; + struct GPTPartition* trash; + uint32_t i, high, copyNum; + int allOK = 1; + + // First, adjust numEntries upward, if necessary, to get a number + // that fills the allocated sectors + i = blockSize / GPT_SIZE; + if ((numEntries % i) != 0) { + printf("Adjusting GPT size from %lu ", (unsigned long) numEntries); + numEntries = ((numEntries / i) + 1) * i; + printf("to %lu to fill the sector\n", (unsigned long) numEntries); + } // if + + newParts = (struct GPTPartition*) calloc(numEntries, sizeof (struct GPTPartition)); + if (newParts != NULL) { + if (partitions != NULL) { // existing partitions; copy them over + GetPartRange(&i, &high); + if (numEntries < (high + 1)) { // Highest entry too high for new # + printf("The highest-numbered partition is %lu, which is greater than the requested\n" + "partition table size of %d; cannot resize. Perhaps sorting will help.\n", + (unsigned long) (high + 1), numEntries); + allOK = 0; + } else { // go ahead with copy + if (numEntries < mainHeader.numParts) + copyNum = numEntries; + else + copyNum = mainHeader.numParts; + for (i = 0; i < copyNum; i++) { + newParts[i] = partitions[i]; + } // for + trash = partitions; + partitions = newParts; + free(trash); + } // if + } else { // No existing partition table; just create it + partitions = newParts; + } // if/else existing partitions + mainHeader.numParts = numEntries; + secondHeader.numParts = numEntries; + mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ; + secondHeader.firstUsableLBA = mainHeader.firstUsableLBA; + mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA; + secondHeader.lastUsableLBA = mainHeader.lastUsableLBA; + secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1); + if (diskSize > 0) + CheckGPTSize(); + } else { // Bad memory allocation + fprintf(stderr, "Error allocating memory for partition table!\n"); + allOK = 0; + } // if/else + return (allOK); +} // GPTData::SetGPTSize() + +// Checks to see if the GPT tables overrun existing partitions; if they +// do, issues a warning but takes no action. Returns 1 if all is OK, 0 +// if problems were detected. +int GPTData::CheckGPTSize(void) { + uint64_t overlap, firstUsedBlock, lastUsedBlock; + uint32_t i; + int allOK = 1; + + // first, locate the first & last used blocks + firstUsedBlock = UINT64_MAX; + lastUsedBlock = 0; + for (i = 0; i < mainHeader.numParts; i++) { + if ((partitions[i].firstLBA < firstUsedBlock) && + (partitions[i].firstLBA != 0)) + firstUsedBlock = partitions[i].firstLBA; + if (partitions[i].lastLBA > lastUsedBlock) + lastUsedBlock = partitions[i].lastLBA; + } // for + + // If the disk size is 0 (the default), then it means that various + // variables aren't yet set, so the below tests will be useless; + // therefore we should skip everything + if (diskSize != 0) { + if (mainHeader.firstUsableLBA > firstUsedBlock) { + overlap = mainHeader.firstUsableLBA - firstUsedBlock; + printf("Warning! Main partition table overlaps the first partition by %lu\n" + "blocks! Try reducing the partition table size by %lu entries.\n", + (unsigned long) overlap, (unsigned long) (overlap * 4)); + printf("(Use the 's' item on the experts' menu.)\n"); + allOK = 0; + } // Problem at start of disk + if (mainHeader.lastUsableLBA < lastUsedBlock) { + overlap = lastUsedBlock - mainHeader.lastUsableLBA; + printf("Warning! Secondary partition table overlaps the last partition by %lu\n" + "blocks! Try reducing the partition table size by %lu entries.\n", + (unsigned long) overlap, (unsigned long) (overlap * 4)); + printf("(Use the 's' item on the experts' menu.)\n"); + allOK = 0; + } // Problem at end of disk + } // if (diskSize != 0) + return allOK; +} // GPTData::CheckGPTSize() + +// Read GPT data from a disk. +int GPTData::LoadPartitions(char* deviceFilename) { + int fd, err; + int allOK = 1, i; + uint64_t firstBlock, lastBlock; + + if ((fd = open(deviceFilename, O_RDONLY)) != -1) { + // store disk information.... + diskSize = disksize(fd, &err); + blockSize = (uint32_t) GetBlockSize(fd); + strcpy(device, deviceFilename); + + // Read the MBR + protectiveMBR.ReadMBRData(fd); + + // Load the GPT data, whether or not it's valid + ForceLoadGPTData(fd); + + switch (UseWhichPartitions()) { + case use_mbr: +// printf("In LoadPartitions(), using MBR\n"); + XFormPartitions(&protectiveMBR); + break; + case use_gpt: + break; + case use_new: +// printf("In LoadPartitions(), making new\n"); + ClearGPTData(); + protectiveMBR.MakeProtectiveMBR(); + break; + } // switch + + // Now find the first and last sectors used by partitions... + if (allOK) { + firstBlock = mainHeader.backupLBA; // start high + lastBlock = 0; // start low + for (i = 0; i < mainHeader.numParts; i++) { + if ((partitions[i].firstLBA < firstBlock) && + (partitions[i].firstLBA > 0)) + firstBlock = partitions[i].firstLBA; + if (partitions[i].lastLBA > lastBlock) + lastBlock = partitions[i].lastLBA; + } // for + } // if + CheckGPTSize(); + } else { + allOK = 0; + fprintf(stderr, "Problem opening %s for reading!\n", + deviceFilename); + } // if/else + return (allOK); +} // GPTData::LoadPartitions() + +// Loads the GPT, as much as possible. Returns 1 if this seems to have +// succeeded, 0 if there are obvious problems.... +int GPTData::ForceLoadGPTData(int fd) { + int allOK = 1, validHeaders; + off_t seekTo; + char* storage; + uint32_t newCRC, sizeOfParts; + + // Seek to and read the main GPT header + lseek64(fd, 512, SEEK_SET); + read(fd, &mainHeader, 512); // read main GPT header + mainCrcOk = CheckHeaderCRC(&mainHeader); + + // Load backup header, check its CRC, and store the results of + // the check for future reference + seekTo = (diskSize * blockSize) - UINT64_C(512); + if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) { + read(fd, &secondHeader, 512); // read secondary GPT header + secondCrcOk = CheckHeaderCRC(&secondHeader); + } else { + allOK = 0; + state = gpt_invalid; + fprintf(stderr, "Unable to seek to secondary GPT at sector %llu!\n", + diskSize - (UINT64_C(1))); + } // if/else lseek + + // Return valid headers code: 0 = both headers bad; 1 = main header + // good, backup bad; 2 = backup header good, main header bad; + // 3 = both headers good. Note these codes refer to valid GPT + // signatures and version numbers; more subtle problems will elude + // this check! + validHeaders = CheckHeaderValidity(); + + // Read partitions (from primary array) + if (validHeaders > 0) { // if at least one header is OK.... + // GPT appears to be valid.... + state = gpt_valid; + + // We're calling the GPT valid, but there's a possibility that one + // of the two headers is corrupt. If so, use the one that seems to + // be in better shape to regenerate the bad one + if (validHeaders == 2) { // valid backup header, invalid main header + printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n" + "from backup!\n"); + RebuildMainHeader(); + mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup + } else if (validHeaders == 1) { // valid main header, invalid backup + printf("Caution: invalid backup GPT header, but valid main header; regenerating\n" + "backup header from main header.\n"); + RebuildSecondHeader(); + secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main + } // if/else/if + + // Load the main partition table, including storing results of its + // CRC check + if (LoadMainTable() == 0) + allOK = 0; + + // Load backup partition table into temporary storage to check + // its CRC and store the results, then discard this temporary + // storage, since we don't use it in any but recovery operations + seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize; + if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) { + sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries; + storage = (char*) malloc(sizeOfParts); + read(fd, storage, sizeOfParts); + newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts); + free(storage); + secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC); + } // if + + // Check for valid CRCs and warn if there are problems + if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) || + (secondPartsCrcOk == 0)) { + printf("Warning! One or more CRCs don't match. You should repair the disk!\n"); + state = gpt_corrupt; + } // if + } else { + state = gpt_invalid; + } // if/else + return allOK; +} // GPTData::ForceLoadGPTData() + +// Loads the partition tables pointed to by the main GPT header. The +// main GPT header in memory MUST be valid for this call to do anything +// sensible! +int GPTData::LoadMainTable(void) { + int fd, retval = 0; + uint32_t newCRC, sizeOfParts; + + if ((fd = open(device, O_RDONLY)) != -1) { + // Set internal data structures for number of partitions on the disk + SetGPTSize(mainHeader.numParts); + + // Load main partition table, and record whether its CRC + // matches the stored value + lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET); + sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries; + read(fd, partitions, sizeOfParts); + newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts); + mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC); + retval = 1; + } // if + return retval; +} // GPTData::LoadMainTable() + +// Examines the MBR & GPT data, and perhaps asks the user questions, to +// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt), +// or create a new set of partitions (use_new) +WhichToUse GPTData::UseWhichPartitions(void) { + WhichToUse which = use_new; + MBRValidity mbrState; + int answer; + + mbrState = protectiveMBR.GetValidity(); + + if ((state == gpt_invalid) && (mbrState == mbr)) { + printf("\n\a***************************************************************\n" + "Found invalid GPT and valid MBR; converting MBR to GPT format.\n" + "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n" + "you don't want to convert your MBR partitions to GPT format!\n" + "***************************************************************\n\n"); + which = use_mbr; + } // if + if ((state == gpt_valid) && (mbrState == gpt)) { + printf("Found valid GPT with protective MBR; using GPT.\n"); + which = use_gpt; + } // if + if ((state == gpt_valid) && (mbrState == invalid)) { + printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n"); + which = use_gpt; + protectiveMBR.MakeProtectiveMBR(); + } // if + if ((state == gpt_valid) && (mbrState == mbr)) { + printf("Found valid MBR and GPT. Which do you want to use?\n"); + answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: "); + if (answer == 1) { + which = use_mbr; + } else if (answer == 2) { + which = use_gpt; + protectiveMBR.MakeProtectiveMBR(); + printf("Using GPT and creating fresh protective MBR.\n"); + } else which = use_new; + } // if + + // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other + // problems) + if (state == gpt_corrupt) { + if (mbrState == mbr) { + printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n" + "GPT MAY permit recovery of GPT data.)\n"); + answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: "); + if (answer == 1) { + which = use_mbr; +// protectiveMBR.MakeProtectiveMBR(); + } else if (answer == 2) { + which = use_gpt; + } else which = use_new; + } else if (mbrState == invalid) { + printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n" + "GPT MAY permit recovery of GPT data.)\n"); + answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: "); + if (answer == 1) { + which = use_gpt; + } else which = use_new; + } else { + printf("\a\a****************************************************************************\n" + "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n" + "verification and recovery are STRONGLY recommended.\n" + "****************************************************************************\n"); + } // if + } // if + + if (which == use_new) + printf("Creating new GPT entries.\n"); + + return which; +} // UseWhichPartitions() + +void GPTData::ResizePartitionTable(void) { + int newSize; + char prompt[255]; + uint32_t curLow, curHigh; + + printf("Current partition table size is %lu.\n", + (unsigned long) mainHeader.numParts); + GetPartRange(&curLow, &curHigh); + curHigh++; // since GetPartRange() returns numbers starting from 0... + // There's no point in having fewer than four partitions.... + if (curHigh < 4) + curHigh = 4; + sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh, + (int) NUM_GPT_ENTRIES); + newSize = GetNumber(4, 65535, 128, prompt); + if (newSize < 128) { + printf("Caution: The partition table size should officially be 16KB or larger,\n" + "which works out to 128 entries. In practice, smaller tables seem to\n" + "work with most OSes, but this practice is risky. I'm proceeding with\n" + "the resize, but you may want to reconsider this action and undo it.\n\n"); + } // if + SetGPTSize(newSize); +} // GPTData::ResizePartitionTable() + +// Find the low and high used partition numbers (numbered from 0). +// Return value is the number of partitions found. Note that the +// *low and *high values are both set to 0 when no partitions +// are found, as well as when a single partition in the first +// position exists. Thus, the return value is the only way to +// tell when no partitions exist. +int GPTData::GetPartRange(uint32_t *low, uint32_t *high) { + uint32_t i; + int numFound = 0; + + *low = mainHeader.numParts + 1; // code for "not found" + *high = 0; + if (mainHeader.numParts > 0) { // only try if partition table exists... + for (i = 0; i < mainHeader.numParts; i++) { + if (partitions[i].firstLBA != UINT64_C(0)) { // it exists + *high = i; // since we're counting up, set the high value + // Set the low value only if it's not yet found... + if (*low == (mainHeader.numParts + 1)) *low = i; + numFound++; + } // if + } // for + } // if + + // Above will leave *low pointing to its "not found" value if no partitions + // are defined, so reset to 0 if this is the case.... + if (*low == (mainHeader.numParts + 1)) + *low = 0; + return numFound; +} // GPTData::GetPartRange() + +// Display the basic GPT data +void GPTData::DisplayGPTData(void) { + int i, j; + char sizeInSI[255]; // String to hold size of disk in SI units + char tempStr[255]; + uint64_t temp, totalFree; + + BytesToSI(diskSize * blockSize, sizeInSI); + printf("Disk %s: %lu sectors, %s\n", device, + (unsigned long) diskSize, sizeInSI); + printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr)); + printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts); + printf("First usable sector is %lu, last usable sector is %lu\n", + (unsigned long) mainHeader.firstUsableLBA, + (unsigned long) mainHeader.lastUsableLBA); + totalFree = FindFreeBlocks(&i, &temp); + printf("Total free space is %llu sectors (%s)\n", totalFree, + BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI)); + printf("\nNumber Start (block) End (block) Size Code Name\n"); + for (i = 0; i < mainHeader.numParts; i++) { + if (partitions[i].firstLBA != 0) { + BytesToSI(blockSize * (partitions[i].lastLBA - partitions[i].firstLBA + 1), + sizeInSI); + printf("%4d %14lu %14lu ", i + 1, (unsigned long) partitions[i].firstLBA, + (unsigned long) partitions[i].lastLBA); + printf(" %-10s %04X ", sizeInSI, + typeHelper.GUIDToID(partitions[i].partitionType)); + j = 0; + while ((partitions[i].name[j] != '\0') && (j < 44)) { + printf("%c", partitions[i].name[j]); + j += 2; + } // while + printf("\n"); + } // if + } // for +} // GPTData::DisplayGPTData() + +// Get partition number from user and then call ShowPartDetails(partNum) +// to show its detailed information +void GPTData::ShowDetails(void) { + int partNum; + uint32_t low, high; + + if (GetPartRange(&low, &high) > 0) { + partNum = GetPartNum(); + ShowPartDetails(partNum); + } else { + printf("No partitions\n"); + } // if/else +} // GPTData::ShowDetails() + +// Show detailed information on the specified partition +void GPTData::ShowPartDetails(uint32_t partNum) { + char temp[255]; + int i; + uint64_t size; + + if (partitions[partNum].firstLBA != 0) { + printf("Partition GUID code: %s ", GUIDToStr(partitions[partNum].partitionType, temp)); + printf("(%s)\n", typeHelper.GUIDToName(partitions[partNum].partitionType, temp)); + printf("Partition unique GUID: %s\n", GUIDToStr(partitions[partNum].uniqueGUID, temp)); + + printf("First sector: %llu (at %s)\n", (unsigned long long) + partitions[partNum].firstLBA, + BytesToSI(partitions[partNum].firstLBA * blockSize, temp)); + printf("Last sector: %llu (at %s)\n", (unsigned long long) + partitions[partNum].lastLBA, + BytesToSI(partitions[partNum].lastLBA * blockSize, temp)); + size = (partitions[partNum].lastLBA - partitions[partNum].firstLBA + 1); + printf("Partition size: %llu sectprs (%s)\n", (unsigned long long) + size, BytesToSI(size * ((uint64_t) blockSize), temp)); + printf("Attribute flags: %016llx\n", (unsigned long long) + partitions[partNum].attributes); + printf("Partition name: "); + i = 0; + while ((partitions[partNum].name[i] != '\0') && (i < NAME_SIZE)) { + printf("%c", partitions[partNum].name[i]); + i += 2; + } // while + printf("\n"); + } else { + printf("Partition #%d does not exist.", (int) (partNum + 1)); + } // if +} // GPTData::ShowPartDetails() + +// Interactively create a partition +void GPTData::CreatePartition(void) { + uint64_t firstBlock, lastBlock, sector; + char prompt[255]; + int partNum, firstFreePart = 0; + + // Find first free partition... + while (partitions[firstFreePart].firstLBA != 0) { + firstFreePart++; + } // while + + if (((firstBlock = FindFirstAvailable()) != 0) && + (firstFreePart < mainHeader.numParts)) { + lastBlock = FindLastAvailable(firstBlock); + + // Get partition number.... + do { + sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1, + mainHeader.numParts, firstFreePart + 1); + partNum = GetNumber(firstFreePart + 1, mainHeader.numParts, + firstFreePart + 1, prompt) - 1; + if (partitions[partNum].firstLBA != 0) + printf("partition %d is in use.\n", partNum + 1); + } while (partitions[partNum].firstLBA != 0); + + // Get first block for new partition... + sprintf(prompt, "First sector (%llu-%llu, default = %llu): ", firstBlock, + lastBlock, firstBlock); + do { + sector = GetNumber(firstBlock, lastBlock, firstBlock, prompt); + } while (IsFree(sector) == 0); + firstBlock = sector; + + // Get last block for new partitions... + lastBlock = FindLastInFree(firstBlock); + sprintf(prompt, "Last sector or +size or +sizeM or +sizeK (%llu-%llu, default = %d): ", + firstBlock, lastBlock, lastBlock); + do { + sector = GetLastSector(firstBlock, lastBlock, prompt); + } while (IsFree(sector) == 0); + lastBlock = sector; + + partitions[partNum].firstLBA = firstBlock; + partitions[partNum].lastLBA = lastBlock; + + // rand() is only 32 bits on 32-bit systems, so multiply together to + // fill a 64-bit value. + partitions[partNum].uniqueGUID.data1 = (uint64_t) rand() * (uint64_t) rand(); + partitions[partNum].uniqueGUID.data2 = (uint64_t) rand() * (uint64_t) rand(); + ChangeGPTType(&partitions[partNum]); + } else { + printf("No free sectors available\n"); + } // if/else +} // GPTData::CreatePartition() + +// Interactively delete a partition (duh!) +void GPTData::DeletePartition(void) { + int partNum; + uint32_t low, high; + char prompt[255]; + + if (GetPartRange(&low, &high) > 0) { + sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1); + partNum = GetNumber(low + 1, high + 1, low, prompt); + BlankPartition(&partitions[partNum - 1]); + } else { + printf("No partitions\n"); + } // if/else +} // GPTData::DeletePartition + +// Find the first available block after the starting point; returns 0 if +// there are no available blocks left +uint64_t GPTData::FindFirstAvailable(uint64_t start) { + uint64_t first; + uint32_t i; + int firstMoved = 0; + + // Begin from the specified starting point or from the first usable + // LBA, whichever is greater... + if (start < mainHeader.firstUsableLBA) + first = mainHeader.firstUsableLBA; + else + first = start; + + // ...now search through all partitions; if first is within an + // existing partition, move it to the next sector after that + // partition and repeat. If first was moved, set firstMoved + // flag; repeat until firstMoved is not set, so as to catch + // cases where partitions are out of sequential order.... + do { + firstMoved = 0; + for (i = 0; i < mainHeader.numParts; i++) { + if ((first >= partitions[i].firstLBA) && + (first <= partitions[i].lastLBA)) { // in existing part. + first = partitions[i].lastLBA + 1; + firstMoved = 1; + } // if + } // for + } while (firstMoved == 1); + if (first > mainHeader.lastUsableLBA) + first = 0; + return (first); +} // GPTData::FindFirstAvailable() + +// Find the last available block on the disk at or after the start +// block. Returns 0 if there are no available partitions after +// (or including) start. +uint64_t GPTData::FindLastAvailable(uint64_t start) { + uint64_t last; + uint32_t i; + int lastMoved = 0; + + // Start by assuming the last usable LBA is available.... + last = mainHeader.lastUsableLBA; + + // ...now, similar to algorithm in FindFirstAvailable(), search + // through all partitions, moving last when it's in an existing + // partition. Set the lastMoved flag so we repeat to catch cases + // where partitions are out of logical order. + do { + lastMoved = 0; + for (i = 0; i < mainHeader.numParts; i++) { + if ((last >= partitions[i].firstLBA) && + (last <= partitions[i].lastLBA)) { // in existing part. + last = partitions[i].firstLBA - 1; + lastMoved = 1; + } // if + } // for + } while (lastMoved == 1); + if (last < mainHeader.firstUsableLBA) + last = 0; + return (last); +} // GPTData::FindLastAvailable() + +// Find the last available block in the free space pointed to by start. +uint64_t GPTData::FindLastInFree(uint64_t start) { + uint64_t nearestStart; + uint32_t i; + + nearestStart = mainHeader.lastUsableLBA; + for (i = 0; i < mainHeader.numParts; i++) { + if ((nearestStart > partitions[i].firstLBA) && + (partitions[i].firstLBA > start)) { + nearestStart = partitions[i].firstLBA - 1; + } // if + } // for + return (nearestStart); +} // GPTData::FindLastInFree() + +// Returns 1 if sector is unallocated, 0 if it's allocated to a partition +int GPTData::IsFree(uint64_t sector) { + int isFree = 1; + uint32_t i; + + for (i = 0; i < mainHeader.numParts; i++) { + if ((sector >= partitions[i].firstLBA) && + (sector <= partitions[i].lastLBA)) { + isFree = 0; + } // if + } // for + if ((sector < mainHeader.firstUsableLBA) || + (sector > mainHeader.lastUsableLBA)) { + isFree = 0; + } // if + return (isFree); +} // GPTData::IsFree() + +int GPTData::XFormPartitions(MBRData* origParts) { + int i, j; + int numToConvert; + uint8_t origType; + + // Clear out old data & prepare basics.... + ClearGPTData(); + + // Convert the smaller of the # of GPT or MBR partitions + if (mainHeader.numParts > (NUM_LOGICALS + 4)) + numToConvert = NUM_LOGICALS + 4; + else + numToConvert = mainHeader.numParts; + +// printf("In XFormPartitions(), numToConvert = %d\n", numToConvert); + + for (i = 0; i < numToConvert; i++) { + origType = origParts->GetType(i); +// printf("Converting partition of type 0x%02X\n", (int) origType); + + // don't convert extended partitions or null (non-existent) partitions + if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x00)) { + partitions[i].firstLBA = (uint64_t) origParts->GetFirstSector(i); + partitions[i].lastLBA = partitions[i].firstLBA + (uint64_t) + origParts->GetLength(i) - 1; + partitions[i].partitionType = typeHelper.IDToGUID(((uint16_t) origType) * 0x0100); + + // Create random unique GUIDs for the partitions + // rand() is only 32 bits, so multiply together to fill a 64-bit value + partitions[i].uniqueGUID.data1 = (uint64_t) rand() * (uint64_t) rand(); + partitions[i].uniqueGUID.data2 = (uint64_t) rand() * (uint64_t) rand(); + partitions[i].attributes = 0; + for (j = 0; j < NAME_SIZE; j++) + partitions[i].name[j] = '\0'; + } // if + } // for + + // Convert MBR into protective MBR + protectiveMBR.MakeProtectiveMBR(); + + // Record that all original CRCs were OK so as not to raise flags + // when doing a disk verification + mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1; + + return (1); +} // XFormPartitions() + +// Sort the GPT entries, eliminating gaps and making for a logical +// ordering. Relies on QuickSortGPT() for the bulk of the work +void GPTData::SortGPT(void) { + int i, lastPart = 0; + struct GPTPartition temp; + + // First, find the last partition with data, so as not to + // spend needless time sorting empty entries.... + for (i = 0; i < GPT_SIZE; i++) { + if (partitions[i].firstLBA > 0) + lastPart = i; + } // for + + // Now swap empties with the last partitions, to simplify the logic + // in the Quicksort function.... + i = 0; + while (i < lastPart) { + if (partitions[i].firstLBA == 0) { + temp = partitions[i]; + partitions[i] = partitions[lastPart]; + partitions[lastPart] = temp; + lastPart--; + } // if + i++; + } // while + + // Now call the recursive quick sort routine to do the real work.... + QuickSortGPT(partitions, 0, lastPart); +} // GPTData::SortGPT() + +// Recursive quick sort algorithm for GPT partitions. Note that if there +// are any empties in the specified range, they'll be sorted to the +// start, resulting in a sorted set of partitions that begins with +// partition 2, 3, or higher. +void QuickSortGPT(struct GPTPartition* partitions, int start, int finish) { + uint64_t starterValue; // starting location of median partition + int left, right; + struct GPTPartition temp; + + left = start; + right = finish; + starterValue = partitions[(start + finish) / 2].firstLBA; + do { + while (partitions[left].firstLBA < starterValue) + left++; + while (partitions[right].firstLBA > starterValue) + right--; + if (left <= right) { + temp = partitions[left]; + partitions[left] = partitions[right]; + partitions[right] = temp; + left++; + right--; + } // if + } while (left <= right); + if (start < right) QuickSortGPT(partitions, start, right); + if (finish > left) QuickSortGPT(partitions, left, finish); +} // QuickSortGPT() + +// Blank (delete) a single partition +void BlankPartition(struct GPTPartition* partition) { + int j; + + partition->uniqueGUID.data1 = 0; + partition->uniqueGUID.data2 = 0; + partition->partitionType.data1 = 0; + partition->partitionType.data2 = 0; + partition->firstLBA = 0; + partition->lastLBA = 0; + partition->attributes = 0; + for (j = 0; j < NAME_SIZE; j++) + partition->name[j] = '\0'; +} // BlankPartition + +// Blank the partition array +void GPTData::BlankPartitions(void) { + uint32_t i; + + for (i = 0; i < mainHeader.numParts; i++) { + BlankPartition(&partitions[i]); + } // for +} // GPTData::BlankPartitions() + +// Set up data structures for entirely new set of partitions on the +// specified device. Returns 1 if OK, 0 if there were problems. +int GPTData::ClearGPTData(void) { + int goOn, i; + + // Set up the partition table.... + free(partitions); + partitions = NULL; + SetGPTSize(NUM_GPT_ENTRIES); + + // Now initialize a bunch of stuff that's static.... + mainHeader.signature = GPT_SIGNATURE; + mainHeader.revision = 0x00010000; + mainHeader.headerSize = (uint32_t) HEADER_SIZE; + mainHeader.reserved = 0; + mainHeader.currentLBA = UINT64_C(1); + mainHeader.partitionEntriesLBA = (uint64_t) 2; + mainHeader.sizeOfPartitionEntries = GPT_SIZE; + for (i = 0; i < GPT_RESERVED; i++) { + mainHeader.reserved2[i] = '\0'; + } // for + + // Now some semi-static items (computed based on end of disk) + mainHeader.backupLBA = diskSize - UINT64_C(1); + mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA; + + // Set a unique GUID for the disk, based on random numbers + // rand() is only 32 bits, so multiply together to fill a 64-bit value + mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand(); + mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand(); + + // Copy main header to backup header + RebuildSecondHeader(); + + // Blank out the partitions array.... + BlankPartitions(); + return (goOn); +} // GPTData::ClearGPTData() + +// Returns 1 if the two partitions overlap, 0 if they don't +int TheyOverlap(struct GPTPartition* first, struct GPTPartition* second) { + int theyDo = 0; + + // Don't bother checking unless these are defined (both start and end points + // are 0 for undefined partitions, so just check the start points) + if ((first->firstLBA != 0) && (second->firstLBA != 0)) { + if ((first->firstLBA < second->lastLBA) && (first->lastLBA >= second->firstLBA)) + theyDo = 1; + if ((second->firstLBA < first->lastLBA) && (second->lastLBA >= first->firstLBA)) + theyDo = 1; + } // if + return (theyDo); +} // Overlap() + +// Change the type code on the specified partition. +// Note: The GPT CRCs must be recomputed after calling this function! +void ChangeGPTType(struct GPTPartition* part) { + char typeName[255], line[255]; + uint16_t typeNum = 0xFFFF; + PartTypes typeHelper; + GUIDData newType; + + printf("Current type is '%s'\n", typeHelper.GUIDToName(part->partitionType, typeName)); + while ((!typeHelper.Valid(typeNum)) && (typeNum != 0)) { + printf("Hex code (L to show codes, 0 to enter raw code): "); + fgets(line, 255, stdin); + sscanf(line, "%x", &typeNum); + if (line[0] == 'L') + typeHelper.ShowTypes(); + } // while + if (typeNum != 0) // user entered a code, so convert it + newType = typeHelper.IDToGUID(typeNum); + else // user wants to enter the GUID directly, so do that + newType = GetGUID(); + part->partitionType = newType; + printf("Changed system type of partition to '%s'\n", + typeHelper.GUIDToName(part->partitionType, typeName)); +} // ChangeGPTType() + +// Prompt user for a partition number, then change its type code +// using ChangeGPTType(struct GPTPartition*) function. +void GPTData::ChangePartType(void) { + int partNum; + uint32_t low, high; + + if (GetPartRange(&low, &high) > 0) { + partNum = GetPartNum(); + ChangeGPTType(&partitions[partNum]); + } else { + printf("No partitions\n"); + } // if/else +} // GPTData::ChangePartType() + +// Prompts user for partition number and returns the result. +uint32_t GPTData::GetPartNum(void) { + uint32_t partNum; + uint32_t low, high; + char prompt[255]; + + if (GetPartRange(&low, &high) > 0) { + sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1); + partNum = GetNumber(low + 1, high + 1, low, prompt); + } else partNum = 1; + return (partNum - 1); +} // GPTData::GetPartNum() + +// Prompt user for attributes to change on the specified partition +// and change them. +void GPTData::SetAttributes(uint32_t partNum) { + Attributes theAttr; + + theAttr.SetAttributes(partitions[partNum].attributes); + theAttr.DisplayAttributes(); + theAttr.ChangeAttributes(); + partitions[partNum].attributes = theAttr.GetAttributes(); +} // GPTData::SetAttributes() + +// Set the name for a partition to theName, or prompt for a name if +// theName is a NULL pointer. Note that theName is a standard C-style +// string, although the GUID partition definition requires a UTF-16LE +// string. This function creates a simple-minded copy for this. +void GPTData::SetName(uint32_t partNum, char* theName) { + char newName[NAME_SIZE]; // New name + int i; + + // Blank out new name string, just to be on the safe side.... + for (i = 0; i < NAME_SIZE; i++) + newName[i] = '\0'; + + if (theName == NULL) { // No name specified, so get one from the user + printf("Enter name: "); + fgets(newName, NAME_SIZE / 2, stdin); + + // Input is likely to include a newline, so remove it.... + i = strlen(newName); + if (newName[i - 1] == '\n') + newName[i - 1] = '\0'; + } else { + strcpy(newName, theName); + } // if + + // Copy the C-style ASCII string from newName into a form that the GPT + // table will accept.... + for (i = 0; i < NAME_SIZE; i++) { + if ((i % 2) == 0) { + partitions[partNum].name[i] = newName[(i / 2)]; + } else { + partitions[partNum].name[i] = '\0'; + } // if/else + } // for +} // GPTData::SetName() + +// Set the disk GUID to the specified value. Note that the header CRCs must +// be recomputed after calling this function. +void GPTData::SetDiskGUID(GUIDData newGUID) { + mainHeader.diskGUID = newGUID; + secondHeader.diskGUID = newGUID; +} // SetDiskGUID() + +// Set the unique GUID of the specified partition. Returns 1 on +// successful completion, 0 if there were problems (invalid +// partition number). +int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) { + int retval = 0; + + if (pn < mainHeader.numParts) { + if (partitions[pn].firstLBA != UINT64_C(0)) { + partitions[pn].uniqueGUID = theGUID; + retval = 1; + } // if + } // if + return retval; +} // GPTData::SetPartitionGUID() + +// Check the validity of the GPT header. Returns 1 if the main header +// is valid, 2 if the backup header is valid, 3 if both are valid, and +// 0 if neither is valid. Note that this function just checks the GPT +// signature and revision numbers, not CRCs or other data. +int GPTData::CheckHeaderValidity(void) { + int valid = 3; + + if (mainHeader.signature != GPT_SIGNATURE) { + valid -= 1; + printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n", + (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE); + } else if ((mainHeader.revision != 0x00010000) && valid) { + valid -= 1; + printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n", + (unsigned long) mainHeader.revision, UINT32_C(0x00010000)); + } // if/else/if + + if (secondHeader.signature != GPT_SIGNATURE) { + valid -= 2; + printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n", + (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE); + } else if ((secondHeader.revision != 0x00010000) && valid) { + valid -= 2; + printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n", + (unsigned long) mainHeader.revision, UINT32_C(0x00010000)); + } // if/else/if + + return valid; +} // GPTData::CheckHeaderValidity() + +// Check the header CRC to see if it's OK... +int GPTData::CheckHeaderCRC(struct GPTHeader* header) { + uint32_t oldCRC, newCRC; + + // Back up old header and then blank it, since it must be 0 for + // computation to be valid + oldCRC = header->headerCRC; + header->headerCRC = UINT32_C(0); + + // Initialize CRC functions... + chksum_crc32gentab(); + + // Compute CRC, restore original, and return result of comparison + newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE); + mainHeader.headerCRC = oldCRC; + return (oldCRC == newCRC); +} // GPTData::CheckHeaderCRC() + +// Recompute all the CRCs. Must be called before saving if any changes +// have been made. +void GPTData::RecomputeCRCs(void) { + uint32_t crc; + + // Initialize CRC functions... + chksum_crc32gentab(); + + // Compute CRC of partition tables & store in main and secondary headers + crc = chksum_crc32((unsigned char*) partitions, mainHeader.numParts * GPT_SIZE); + mainHeader.partitionEntriesCRC = crc; + secondHeader.partitionEntriesCRC = crc; + + // Zero out GPT tables' own CRCs (required for correct computation) + mainHeader.headerCRC = 0; + secondHeader.headerCRC = 0; + + // Compute & store CRCs of main & secondary headers... + crc = chksum_crc32((unsigned char*) &mainHeader, HEADER_SIZE); + mainHeader.headerCRC = crc; + crc = chksum_crc32((unsigned char*) &secondHeader, HEADER_SIZE); + secondHeader.headerCRC = crc; +} // GPTData::RecomputeCRCs() + +// Perform detailed verification, reporting on any problems found, but +// do *NOT* recover from these problems. Returns the total number of +// problems identified. +int GPTData::Verify(void) { + int problems = 0, numSegments, i, j; + uint64_t totalFree, largestSegment; + char tempStr[255], siTotal[255], siLargest[255]; + + // First, check for CRC errors in the GPT data.... + if (!mainCrcOk) { + problems++; + printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n" + "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n" + "header\n"); + } // if + if (!mainPartsCrcOk) { + problems++; + printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n" + "corrupt. Consider loading the backup partition table.\n"); + } // if + if (!secondCrcOk) { + problems++; + printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n" + "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n" + "header.\n"); + } // if + if (!secondPartsCrcOk) { + problems++; + printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n" + "be corrupt. This program will automatically create a new backup partition\n" + "table when you save your partitions.\n"); + } // if + + // Now check that critical main and backup GPT entries match + if (mainHeader.currentLBA != secondHeader.backupLBA) { + problems++; + printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n" + "match the backup GPT header's LBA pointer(%llu)\n", + (unsigned long long) mainHeader.currentLBA, + (unsigned long long) secondHeader.backupLBA); + } // if + if (mainHeader.backupLBA != secondHeader.currentLBA) { + problems++; + printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n" + "match the backup GPT header's current LBA pointer (%llu)\n", + (unsigned long long) mainHeader.backupLBA, + (unsigned long long) secondHeader.currentLBA); + } // if + if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) { + problems++; + printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n" + "match the backup GPT header's first usable LBA pointer (%llu)\n", + (unsigned long long) mainHeader.firstUsableLBA, + (unsigned long long) secondHeader.firstUsableLBA); + } // if + if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) { + problems++; + printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n" + "match the backup GPT header's last usable LBA pointer (%llu)\n", + (unsigned long long) mainHeader.lastUsableLBA, + (unsigned long long) secondHeader.lastUsableLBA); + } // if + if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) || + (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) { + problems++; + printf("\nProblem: main header's disk GUID (%s) doesn't\n", + GUIDToStr(mainHeader.diskGUID, tempStr)); + printf("match the backup GPT header's disk GUID (%s)\n", + GUIDToStr(secondHeader.diskGUID, tempStr)); + } // if + if (mainHeader.numParts != secondHeader.numParts) { + problems++; + printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n" + "match the backup GPT header's number of partitions (%lu)\n", + (unsigned long) mainHeader.numParts, + (unsigned long) secondHeader.numParts); + } // if + if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) { + problems++; + printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n" + "match the backup GPT header's size of partition entries (%lu)\n", + (unsigned long) mainHeader.sizeOfPartitionEntries, + (unsigned long) secondHeader.sizeOfPartitionEntries); + } // if + + // Now check for a few other miscellaneous problems... + // Check that the disk size will hold the data... + if (mainHeader.backupLBA > diskSize) { + problems++; + printf("\nProblem: Disk is too small to hold all the data!\n"); + printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n", + (unsigned long long) diskSize, + (unsigned long long) mainHeader.backupLBA); + } // if + + // Check for overlapping partitions.... + for (i = 1; i < mainHeader.numParts; i++) { + for (j = 0; j < i; j++) { + if (TheyOverlap(&partitions[i], &partitions[j])) { + problems++; + printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1); + printf(" Partition %d: %llu to %llu\n", i, + (unsigned long long) partitions[i].firstLBA, + (unsigned long long) partitions[i].lastLBA); + printf(" Partition %d: %llu to %llu\n", j, + (unsigned long long) partitions[j].firstLBA, + (unsigned long long) partitions[j].lastLBA); + } // if + } // for j... + } // for i... + + // Now compute available space, but only if no problems found, since + // problems could affect the results + if (problems == 0) { + totalFree = FindFreeBlocks(&numSegments, &largestSegment); + BytesToSI(totalFree * (uint64_t) blockSize, siTotal); + BytesToSI(largestSegment * (uint64_t) blockSize, siLargest); + printf("No problems found. %llu free sectors (%s) available in %u\n" + "segments, the largest of which is %llu sectors (%s) in size\n", + (unsigned long long) totalFree, + siTotal, numSegments, (unsigned long long) largestSegment, + siLargest); + } else { + printf("\nIdentified %d problems!\n", problems); + } // if/else + + return (problems); +} // GPTData::Verify() + +// Rebuild the main GPT header, using the secondary header as a model. +// Typically called when the main header has been found to be corrupt. +void GPTData::RebuildMainHeader(void) { + int i; + + mainHeader.signature = GPT_SIGNATURE; + mainHeader.revision = secondHeader.revision; + mainHeader.headerSize = HEADER_SIZE; + mainHeader.headerCRC = UINT32_C(0); + mainHeader.reserved = secondHeader.reserved; + mainHeader.currentLBA = secondHeader.backupLBA; + mainHeader.backupLBA = secondHeader.currentLBA; + mainHeader.firstUsableLBA = secondHeader.firstUsableLBA; + mainHeader.lastUsableLBA = secondHeader.lastUsableLBA; + mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1; + mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2; + mainHeader.partitionEntriesLBA = UINT64_C(2); + mainHeader.numParts = secondHeader.numParts; + mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries; + mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC; + for (i = 0 ; i < GPT_RESERVED; i++) + mainHeader.reserved2[i] = secondHeader.reserved2[i]; +} // GPTData::RebuildMainHeader() + +// Rebuild the secondary GPT header, using the main header as a model. +void GPTData::RebuildSecondHeader(void) { + int i; + + secondHeader.signature = GPT_SIGNATURE; + secondHeader.revision = mainHeader.revision; + secondHeader.headerSize = HEADER_SIZE; + secondHeader.headerCRC = UINT32_C(0); + secondHeader.reserved = mainHeader.reserved; + secondHeader.currentLBA = mainHeader.backupLBA; + secondHeader.backupLBA = mainHeader.currentLBA; + secondHeader.firstUsableLBA = mainHeader.firstUsableLBA; + secondHeader.lastUsableLBA = mainHeader.lastUsableLBA; + secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1; + secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2; + secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1); + secondHeader.numParts = mainHeader.numParts; + secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries; + secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC; + for (i = 0 ; i < GPT_RESERVED; i++) + secondHeader.reserved2[i] = mainHeader.reserved2[i]; +} // RebuildSecondHeader() + +// Load the second (backup) partition table as the primary partition +// table. Used in repair functions +void GPTData::LoadSecondTableAsMain(void) { + int fd; + off_t seekTo; + uint32_t sizeOfParts, newCRC; + + if ((fd = open(device, O_RDONLY)) != -1) { + seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize; + if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) { + SetGPTSize(secondHeader.numParts); + sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries; + read(fd, partitions, sizeOfParts); + newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts); + secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC); + mainPartsCrcOk = secondPartsCrcOk; + if (!secondPartsCrcOk) { + printf("Error! After loading backup partitions, the CRC still doesn't check out!\n"); + } // if + } else { + printf("Error! Couldn't seek to backup partition table!\n"); + } // if/else + } else { + printf("Error! Couldn't open device %s when recovering backup partition table!\n"); + } // if/else +} // GPTData::LoadSecondTableAsMain() + +// Finds the total number of free blocks, the number of segments in which +// they reside, and the size of the largest of those segments +uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) { + uint64_t start = UINT64_C(0); // starting point for each search + uint64_t totalFound = UINT64_C(0); // running total + uint64_t firstBlock; // first block in a segment + uint64_t lastBlock; // last block in a segment + uint64_t segmentSize; // size of segment in blocks + int num = 0; + + *largestSegment = UINT64_C(0); + do { + firstBlock = FindFirstAvailable(start); + if (firstBlock != UINT64_C(0)) { // something's free... + lastBlock = FindLastInFree(firstBlock); + segmentSize = lastBlock - firstBlock + UINT64_C(1); + if (segmentSize > *largestSegment) { + *largestSegment = segmentSize; + } // if + totalFound += segmentSize; + num++; + start = lastBlock + 1; + } // if + } while (firstBlock != 0); + *numSegments = num; + return totalFound; +} // GPTData::FindFreeBlocks() + +/* +// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with +// OSes that don't understand GPT. +void GPTData::MakeHybrid(void) { + uint32_t partNums[3]; + char line[255]; + int numParts, i, j, typeCode, bootable; + uint64_t length; + + // First, rebuild the protective MBR... + protectiveMBR.MakeProtectiveMBR(); + + // Now get the numbers of up to three partitions to add to the + // hybrid MBR.... + printf("Type from one to three partition numbers to be added to the hybrid MBR, in\n" + "sequence: "); + fgets(line, 255, stdin); + numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]); + for (i = 0; i < numParts; i++) { + j = partNums[i] - 1; + printf("Creating entry for partition #%d\n", j + 1); + if ((j >= 0) && (j < mainHeader.numParts)) { + if (partitions[j].lastLBA < UINT32_MAX) { + printf("Enter an MBR hex code (suggested %02X): ", + typeHelper.GUIDToID(partitions[j].partitionType) / 256); + fgets(line, 255, stdin); + sscanf(line, "%x", &typeCode); + printf("Set the bootable flag? "); + bootable = (GetYN() == 'Y'); + length = partitions[j].lastLBA - partitions[j].firstLBA + UINT64_C(1); + protectiveMBR.MakePart(i + 1, (uint32_t) partitions[j].firstLBA, + (uint32_t) length, typeCode, bootable); + } else { // partition out of range + printf("Partition %d ends beyond the 2TiB limit of MBR partitions; omitting it.\n", + j + 1); + } // if/else + } else { + printf("Partition %d is out of range; omitting it.\n", j + 1); + } // if/else + } // for +} // GPTData::MakeHybrid() +*/ + +// Writes GPT (and protective MBR) to disk. Returns 1 on successful +// write, 0 if there was a problem. +int GPTData::SaveGPTData(void) { + int allOK = 1, i, j; + char answer, line[256]; + int fd; + uint64_t secondTable; + off_t offset; + + if (strlen(device) == 0) { + printf("Device not defined.\n"); + } // if + + // First do some final sanity checks.... + // Is there enough space to hold the GPT headers and partition tables, + // given the partition sizes? + if (CheckGPTSize() == 0) { + allOK = 0; + } // if + + // Check that disk is really big enough to handle this... + if (mainHeader.backupLBA > diskSize) { + fprintf(stderr, "Error! Disk is too small -- either the original MBR is corrupt or you're\n"); + fprintf(stderr, "working from an MBR copied to a file! Aborting!\n"); + printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize, + mainHeader.backupLBA); + allOK = 0; + } // if + + // Check for overlapping partitions.... + for (i = 1; i < mainHeader.numParts; i++) { + for (j = 0; j < i; j++) { + if (TheyOverlap(&partitions[i], &partitions[j])) { + fprintf(stderr, "\Error: partitions %d and %d overlap:\n", i + 1, j + 1); + fprintf(stderr, " Partition %d: %llu to %llu\n", i, + (unsigned long long) partitions[i].firstLBA, + (unsigned long long) partitions[i].lastLBA); + fprintf(stderr, " Partition %d: %llu to %llu\n", j, + (unsigned long long) partitions[j].firstLBA, + (unsigned long long) partitions[j].lastLBA); + fprintf(stderr, "Aborting write operation!\n"); + allOK = 0; + } // if + } // for j... + } // for i... + + RecomputeCRCs(); + + if (allOK) { + printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n"); + printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n"); + printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n"); + printf("Do you want to proceed, possibly destroying your data? (Y/N) "); + fgets(line, 255, stdin); + sscanf(line, "%c", &answer); + if ((answer == 'Y') || (answer == 'y')) { + printf("OK; writing new GPT partition table.\n"); + } else { + allOK = 0; + } // if/else + } // if + + // Do it! + if (allOK) { + fd = open(device, O_WRONLY); // try to open the device; may fail.... +#ifdef __APPLE__ + // MacOS X requires a shared lock under some circumstances.... + if (fd < 0) { + fd = open(device, O_WRONLY|O_SHLOCK); + } // if +#endif + if (fd != -1) { + // First, write the protective MBR... + protectiveMBR.WriteMBRData(fd); + + // Now write the main GPT header... + if (allOK) + if (write(fd, &mainHeader, 512) == -1) + allOK = 0; + + // Now write the main partition tables... + if (allOK) { + if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1) + allOK = 0; + } // if + + // Now seek to near the end to write the secondary GPT.... + if (allOK) { + secondTable = secondHeader.partitionEntriesLBA; + offset = (off_t) secondTable * (off_t) (blockSize); + if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) { + allOK = 0; + printf("Unable to seek to end of disk!\n"); + } // if + } // if + + // Now write the secondary partition tables.... + if (allOK) + if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1) + allOK = 0; + + // Now write the secondary GPT header... + if (allOK) + if (write(fd, &secondHeader, 512) == -1) + allOK = 0; + + // re-read the partition table + if (allOK) { + sync(); +#ifdef __APPLE__ + printf("Warning: The kernel may continue to use old or deleted partitions.\n" + "You should reboot or remove the drive.\n"); + /* don't know if this helps + * it definitely will get things on disk though: + * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */ + i = ioctl(fd, DKIOCSYNCHRONIZECACHE); +#else + sleep(2); + i = ioctl(fd, BLKRRPART); + if (i) + printf("Warning: The kernel is still using the old partition table.\n" + "The new table will be used at the next reboot.\n"); +#endif + } // if + + if (allOK) { // writes completed OK + printf("The operation has completed successfully.\n"); + } else { + printf("Warning! An error was reported when writing the partition table! This error\n"); + printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n"); + printf("necessary, restore your original partition table.\n"); + } // if/else + close(fd); + } else { + fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting!\n", device, errno); + allOK = 0; + } // if/else + } else { + printf("Aborting write of new partition table.\n"); + } // if + + return (allOK); +} // GPTData::SaveGPTData() + +// Save GPT data to a backup file. This function does much less error +// checking than SaveGPTData(). It can therefore preserve many types of +// corruption for later analysis; however, it preserves only the MBR, +// the main GPT header, the backup GPT header, and the main partition +// table; it discards the backup partition table, since it should be +// identical to the main partition table on healthy disks. +int GPTData::SaveGPTBackup(char* filename) { + int fd, allOK = 1;; + + if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) { + // First, write the protective MBR... + protectiveMBR.WriteMBRData(fd); + + // Now write the main GPT header... + if (allOK) + if (write(fd, &mainHeader, 512) == -1) + allOK = 0; + + // Now write the secondary GPT header... + if (allOK) + if (write(fd, &secondHeader, 512) == -1) + allOK = 0; + + // Now write the main partition tables... + if (allOK) { + if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1) + allOK = 0; + } // if + + if (allOK) { // writes completed OK + printf("The operation has completed successfully.\n"); + } else { + printf("Warning! An error was reported when writing the backup file.\n"); + printf("It may not be useable!\n"); + } // if/else + close(fd); + } else { + fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename); + allOK = 0; + } // if/else + return allOK; +} // GPTData::SaveGPTBackup() + +// Load GPT data from a backup file created by SaveGPTBackup(). This function +// does minimal error checking. It returns 1 if it completed successfully, +// 0 if there was a problem. In the latter case, it creates a new empty +// set of partitions. +int GPTData::LoadGPTBackup(char* filename) { + int fd, allOK = 1, val; + uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC; + + if ((fd = open(filename, O_RDONLY)) != -1) { + // Let the MBRData class load the saved MBR... + protectiveMBR.ReadMBRData(fd); + + // Load the main GPT header, check its vaility, and set the GPT + // size based on the data + read(fd, &mainHeader, 512); + mainCrcOk = CheckHeaderCRC(&mainHeader); + + // Load the backup GPT header in much the same way as the main + // GPT header.... + read(fd, &secondHeader, 512); + secondCrcOk = CheckHeaderCRC(&secondHeader); + + // Return valid headers code: 0 = both headers bad; 1 = main header + // good, backup bad; 2 = backup header good, main header bad; + // 3 = both headers good. Note these codes refer to valid GPT + // signatures and version numbers; more subtle problems will elude + // this check! + if ((val = CheckHeaderValidity()) > 0) { + if (val == 2) { // only backup header seems to be good + numParts = secondHeader.numParts; + sizeOfEntries = secondHeader.sizeOfPartitionEntries; + } else { // main header is OK + numParts = mainHeader.numParts; + sizeOfEntries = mainHeader.sizeOfPartitionEntries; + } // if/else + + SetGPTSize(numParts); + + // If current disk size doesn't match that of backup.... + if (secondHeader.currentLBA != diskSize - UINT64_C(1)) { + printf("Warning! Current disk size doesn't match that of the backup!\n" + "Adjusting sizes to match, but subsequent problems are possible!\n"); + secondHeader.currentLBA = mainHeader.backupLBA = diskSize - UINT64_C(1); + mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA; + secondHeader.lastUsableLBA = mainHeader.lastUsableLBA; + secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1); + } // if + + // Load main partition table, and record whether its CRC + // matches the stored value + sizeOfParts = numParts * sizeOfEntries; + read(fd, partitions, sizeOfParts); + + newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts); + mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC); + secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC); + } else { + allOK = 0; + } // if/else + } else { + allOK = 0; + fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename); + } // if/else + + // Something went badly wrong, so blank out partitions + if (allOK == 0) { + ClearGPTData(); + protectiveMBR.MakeProtectiveMBR(); + } // if + return allOK; +} // GPTData::LoadGPTBackup() + +// Check to be sure that data type sizes are correct. The basic types (uint*_t) should +// never fail these tests, but the struct types may fail depending on compile options. +// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure +// sizes. +int SizesOK(void) { + int allOK = 1; + union { + uint32_t num; + unsigned char uc[sizeof(uint32_t)]; + } endian; + + if (sizeof(uint8_t) != 1) { + fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t)); + allOK = 0; + } // if + if (sizeof(uint16_t) != 2) { + fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t)); + allOK = 0; + } // if + if (sizeof(uint32_t) != 4) { + fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t)); + allOK = 0; + } // if + if (sizeof(uint64_t) != 8) { + fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t)); + allOK = 0; + } // if + if (sizeof(struct MBRRecord) != 16) { + fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(uint32_t)); + allOK = 0; + } // if + if (sizeof(struct EBRRecord) != 512) { + fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(uint32_t)); + allOK = 0; + } // if + if (sizeof(struct GPTHeader) != 512) { + fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(uint32_t)); + allOK = 0; + } // if + // Determine endianness; set allOK = 0 if running on big-endian hardware + endian.num = 1; + if (endian.uc[0] != (unsigned char) 1) { + fprintf(stderr, "Running on big-endian hardware, but this program only works on little-endian\n" + "systems; aborting!\n"); + allOK = 0; + } // if + return (allOK); +} // SizesOK() + @@ -0,0 +1,153 @@ +/* gpt.h -- GPT and data structure definitions, types, and + functions */ + +#include <stdint.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include "support.h" +#include "parttypes.h" +#include "mbr.h" + +#ifndef __GPTSTRUCTS +#define __GPTSTRUCTS + +#define GPT_SIGNATURE UINT64_C(0x5452415020494645) + +/* Number and size of GPT entries... */ +#define NUM_GPT_ENTRIES 128 +#define GPT_SIZE 128 +/* Offset, in 512-byte sectors, for GPT table and partition data. + Note this is above two multiplied together, divided by 512, with 2 + added +#define GPT_OFFSET (((NUM_GPT_ENTRIES * GPT_SIZE) / SECTOR_SIZE) + 2) +*/ + +#define HEADER_SIZE 92 + +#define GPT_RESERVED 420 +#define NAME_SIZE 72 + +using namespace std; + +/**************************************** + * * + * GPTData class and related structures * + * * + ****************************************/ + +// Validity state of GPT data +enum GPTValidity {gpt_valid, gpt_corrupt, gpt_invalid}; + +// Which set of partition data to use +enum WhichToUse {use_gpt, use_mbr, use_new}; + +// Header (first 512 bytes) of GPT table +struct GPTHeader { + uint64_t signature; + uint32_t revision; + uint32_t headerSize; + uint32_t headerCRC; + uint32_t reserved; + uint64_t currentLBA; + uint64_t backupLBA; + uint64_t firstUsableLBA; + uint64_t lastUsableLBA; + struct GUIDData diskGUID; + uint64_t partitionEntriesLBA; + uint32_t numParts; + uint32_t sizeOfPartitionEntries; + uint32_t partitionEntriesCRC; + unsigned char reserved2[GPT_RESERVED]; +}; // struct GPTHeader + +struct GPTPartition { + struct GUIDData partitionType; + struct GUIDData uniqueGUID; + uint64_t firstLBA; + uint64_t lastLBA; + uint64_t attributes; + unsigned char name[NAME_SIZE]; +}; // struct GPTPartition + +// Data in GPT format +class GPTData { +protected: + struct GPTHeader mainHeader; + struct GPTPartition *partitions; + struct GPTHeader secondHeader; + MBRData protectiveMBR; + char device[256]; // device filename + uint32_t blockSize; // device block size + uint64_t diskSize; // size of device, in blocks + GPTValidity state; // is GPT valid? + int mainCrcOk; + int secondCrcOk; + int mainPartsCrcOk; + int secondPartsCrcOk; +// uint32_t units; // display units, in multiples of sectors + PartTypes typeHelper; +public: + GPTData(void); + GPTData(char* deviceFilename); + ~GPTData(void); + int SetGPTSize(uint32_t numEntries); + int CheckGPTSize(void); + int LoadPartitions(char* deviceFilename); + int ForceLoadGPTData(int fd); + int LoadMainTable(void); + WhichToUse UseWhichPartitions(void); + void ResizePartitionTable(void); + int GetPartRange(uint32_t* low, uint32_t* high); + void DisplayGPTData(void); + void DisplayMBRData(void) {protectiveMBR.DisplayMBRData();} + void ShowDetails(void); + void ShowPartDetails(uint32_t partNum); + void CreatePartition(void); + void DeletePartition(void); + void BlankPartitions(void); + uint64_t FindFirstAvailable(uint64_t start = 0); + uint64_t FindLastAvailable(uint64_t start); + uint64_t FindLastInFree(uint64_t start); + int IsFree(uint64_t sector); + int XFormPartitions(MBRData* origParts); + void SortGPT(void); + int ClearGPTData(void); + void ChangePartType(void); + uint32_t GetPartNum(void); + void SetAttributes(uint32_t partNum); + void SetName(uint32_t partNum, char* theName = NULL); + void SetDiskGUID(GUIDData newGUID); + int SetPartitionGUID(uint32_t pn, GUIDData theGUID); + int CheckHeaderValidity(void); + int CheckHeaderCRC(struct GPTHeader* header); + void RecomputeCRCs(void); + int Verify(void); + void RebuildMainHeader(void); + void RebuildSecondHeader(void); + void LoadSecondTableAsMain(void); + uint64_t FindFreeBlocks(int *numSegments, uint64_t *largestSegment); +// void MakeHybrid(void); + void MakeProtectiveMBR(void) {return protectiveMBR.MakeProtectiveMBR();} + int SaveGPTData(void); + int SaveGPTBackup(char* filename); + int LoadGPTBackup(char* filename); + + // Return data about the GPT structures.... + uint32_t GetNumParts(void) {return mainHeader.numParts;} + uint64_t GetMainHeaderLBA(void) {return mainHeader.currentLBA;} + uint64_t GetSecondHeaderLBA(void) {return secondHeader.currentLBA;} + uint64_t GetMainPartsLBA(void) {return mainHeader.partitionEntriesLBA;} + uint64_t GetSecondPartsLBA(void) {return secondHeader.partitionEntriesLBA;} + uint64_t GetBlocksInPartTable(void) {return (mainHeader.numParts * + mainHeader.sizeOfPartitionEntries) / blockSize;} +}; // class GPTData + +// Function prototypes.... +void BlankPartition(struct GPTPartition* partition); +//int XFormType(uint8_t oldType, struct GUIDData* newType, int partNum); +void QuickSortGPT(struct GPTPartition* partitions, int start, int finish); +int TheyOverlap(struct GPTPartition* first, struct GPTPartition* second); +void ChangeGPTType(struct GPTPartition* part); +int SizesOK(void); + +#endif @@ -0,0 +1,447 @@ +/* mbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition + data. */ + +/* By Rod Smith, January to February, 2009 */ + +#define __STDC_LIMIT_MACROS +#define __STDC_CONSTANT_MACROS + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <fcntl.h> +#include <string.h> +#include <time.h> +#include <sys/stat.h> +#include <errno.h> +#include "mbr.h" +#include "support.h" + +using namespace std; + +/**************************************** + * * + * MBRData class and related structures * + * * + ****************************************/ + +MBRData::MBRData(void) { + blockSize = SECTOR_SIZE; + diskSize = 0; + strcpy(device, ""); + state = invalid; + srand((unsigned int) time(NULL)); + EmptyMBR(); +} // MBRData default constructor + +MBRData::MBRData(char *filename) { + blockSize = SECTOR_SIZE; + diskSize = 0; + strcpy(device, filename); + state = invalid; + + srand((unsigned int) time(NULL)); + // Try to read the specified partition table, but if it fails.... + if (!ReadMBRData(filename)) { + EmptyMBR(); + strcpy(device, ""); + } // if +} // MBRData(char *filename) constructor + +MBRData::~MBRData(void) { +} // MBRData destructor + +// Empty all data. Meant mainly for calling by constructors +void MBRData::EmptyMBR(void) { + int i; + + for (i = 0; i < 440; i++) + code[i] = 0; + diskSignature = (uint32_t) rand(); + nulls = 0; + for (i = 0; i < 4; i++) { + partitions[i].status = UINT8_C(0); + partitions[i].firstSector[0] = UINT8_C(0); + partitions[i].firstSector[1] = UINT8_C(0); + partitions[i].firstSector[2] = UINT8_C(0); + partitions[i].partitionType = UINT8_C(0); + partitions[i].lastSector[0] = UINT8_C(0); + partitions[i].lastSector[1] = UINT8_C(0); + partitions[i].lastSector[2] = UINT8_C(0); + partitions[i].firstLBA = UINT32_C(0); + partitions[i].lengthLBA = UINT32_C(0); + } // for + MBRSignature = MBR_SIGNATURE; + + blockSize = SECTOR_SIZE; + diskSize = 0; + for (i = 0; i < NUM_LOGICALS; i++) { + logicals[i].status = UINT8_C(0); + logicals[i].firstSector[0] = UINT8_C(0); + logicals[i].firstSector[1] = UINT8_C(0); + logicals[i].firstSector[2] = UINT8_C(0); + logicals[i].partitionType = UINT8_C(0); + logicals[i].lastSector[0] = UINT8_C(0); + logicals[i].lastSector[1] = UINT8_C(0); + logicals[i].lastSector[2] = UINT8_C(0); + logicals[i].firstLBA = UINT32_C(0); + logicals[i].lengthLBA = UINT32_C(0); + } // for +} // MBRData::EmptyMBR() + +// Read data from MBR. Returns 1 if read was successful (even if the +// data isn't a valid MBR), 0 if the read failed. +int MBRData::ReadMBRData(char* deviceFilename) { + int fd, allOK = 1; + + if ((fd = open(deviceFilename, O_RDONLY)) != -1) { + ReadMBRData(fd); + } else { + allOK = 0; + } // if + + close(fd); + + if (allOK) + strcpy(device, deviceFilename); + + return allOK; +} // MBRData::ReadMBRData(char* deviceFilename) + +// Read data from MBR. +void MBRData::ReadMBRData(int fd) { + int allOK = 1, i; + int err; + + // Clear logical partition array + for (i = 0; i < NUM_LOGICALS; i++) { + logicals[i].status = UINT8_C(0); + logicals[i].firstSector[0] = UINT8_C(0); + logicals[i].firstSector[1] = UINT8_C(0); + logicals[i].firstSector[2] = UINT8_C(0); + logicals[i].partitionType = UINT8_C(0); + logicals[i].lastSector[0] = UINT8_C(0); + logicals[i].lastSector[1] = UINT8_C(0); + logicals[i].lastSector[2] = UINT8_C(0); + logicals[i].firstLBA = UINT32_C(0); + logicals[i].lengthLBA = UINT32_C(0); + } // for + + read(fd, code, 440); + read(fd, &diskSignature, 4); + read(fd, &nulls, 2); + read(fd, partitions, 64); + read(fd, &MBRSignature, 2); + if (MBRSignature != MBR_SIGNATURE) { + allOK = 0; + state = invalid; + fprintf(stderr, "MBR signature invalid; read 0x%04X, but should be 0x%04X\n", + (unsigned int) MBRSignature, (unsigned int) MBR_SIGNATURE); + } /* if */ + + // Find disk size + diskSize = disksize(fd, &err); + + // Find block size + if ((blockSize = GetBlockSize(fd)) == -1) { + blockSize = SECTOR_SIZE; + printf("Unable to determine sector size; assuming %lu bytes!\n", + (unsigned long) SECTOR_SIZE); + } // if + + // Load logical partition data, if any is found.... + if (allOK) { + for (i = 0; i < 4; i++) { + if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f) + || (partitions[i].partitionType == 0x85)) { + // Found it, so call a recursive algorithm to load everything from them.... + allOK = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), 0); + } // if + } // for + if (allOK) { // Loaded logicals OK + state = mbr; + } else { + state = invalid; + } // if + } // if + + /* Check to see if it's in GPT format.... */ + if (allOK) { + for (i = 0; i < 4; i++) { + if (partitions[i].partitionType == UINT8_C(0xEE)) { + state = gpt; + } /* if */ + } /* for */ + } /* if */ + +/* // Tell the user what the MBR state is... + switch (state) { + case invalid: + printf("Information: MBR appears to be empty or invalid.\n"); + break; + case gpt: + printf("Information: MBR holds GPT placeholder partitions.\n"); + break; + case hybrid: + printf("Information: MBR holds hybrid GPT/MBR data.\n"); + break; + case mbr: + printf("Information: MBR data appears to be valid.\n"); + break; + } // switch */ +} // MBRData::ReadMBRData(int fd) + +// Write the MBR data to the default defined device. +int MBRData::WriteMBRData(void) { + int allOK = 1, fd; + + if ((fd = open(device, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) { + WriteMBRData(fd); + } else { + allOK = 0; + } // if/else + close(fd); + return allOK; +} // MBRData::WriteMBRData(void) + +// Save the MBR data to a file. Note that this function writes ONLY the +// MBR data, not the logical partitions (if any are defined). +void MBRData::WriteMBRData(int fd) { + write(fd, code, 440); + write(fd, &diskSignature, 4); + write(fd, &nulls, 2); + write(fd, partitions, 64); + write(fd, &MBRSignature, 2); +} // MBRData::WriteMBRData(int fd) + +// This is a recursive function to read all the logical partitions, following the +// logical partition linked list from the disk and storing the basic data in +int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart, + uint32_t diskOffset, int partNum) { + int allOK = 1; + struct EBRRecord ebr; + off_t offset; + + offset = (off_t) (extendedStart + diskOffset) * blockSize; + if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record + fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset); + allOK = 0; + } + if (read(fd, &ebr, 512) != 512) { // Load the data.... + fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n", + (unsigned long) offset); + allOK = 0; + } + if (ebr.MBRSignature != MBR_SIGNATURE) { + allOK = 0; + printf("MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n", + (unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE); + } /* if */ + + // Copy over the basic data.... + logicals[partNum].status = ebr.partitions[0].status; + logicals[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart; + logicals[partNum].lengthLBA = ebr.partitions[0].lengthLBA; + logicals[partNum].partitionType = ebr.partitions[0].partitionType; + + // Find the next partition (if there is one) and recurse.... + if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && allOK) { + allOK = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA, + partNum + 1); + } // if + return (allOK); +} // MBRData::ReadLogicalPart() + +// Show the MBR data to the user.... +void MBRData::DisplayMBRData(void) { + int i; + char tempStr[255]; + + printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature); + printf("MBR partitions:\n"); + printf("Number\t Start (block)\t Length (blocks)\tType\n"); + for (i = 0; i < 4; i++) { + if (partitions[i].lengthLBA != 0) { + printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 1, (unsigned long) partitions[i].firstLBA, + (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType); + } // if + } // for + + // Now display logical partition data.... + for (i = 0; i < NUM_LOGICALS; i++) { + if (logicals[i].lengthLBA != 0) { + printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 5, (unsigned long) logicals[i].firstLBA, + (unsigned long) logicals[i].lengthLBA, logicals[i].partitionType); + } // if + } // for + printf("\nDisk size is %lu sectors (%s)\n", (unsigned long) diskSize, + BytesToSI(diskSize * (uint64_t) blockSize, tempStr)); +} // MBRData::DisplayMBRData() + +// Create a protective MBR +void MBRData::MakeProtectiveMBR(void) { + int i; + + // Initialize variables + nulls = 0; + MBRSignature = MBR_SIGNATURE; + + partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable + + // Write CHS data. This maxes out the use of the disk, as much as + // possible -- even to the point of exceeding the capacity of sub-8GB + // disks. The EFI spec says to use 0xffffff as the ending value, + // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted + // and Apple's Disk Utility use 0xfeffff, and the latter puts that + // value in for the FIRST sector, too! + partitions[0].firstSector[0] = UINT8_C(0); + partitions[0].firstSector[1] = UINT8_C(1); + partitions[0].firstSector[2] = UINT8_C(0); + partitions[0].lastSector[0] = UINT8_C(255); + partitions[0].lastSector[1] = UINT8_C(255); + partitions[0].lastSector[2] = UINT8_C(255); + + partitions[0].partitionType = UINT8_C(0xEE); + partitions[0].firstLBA = UINT32_C(1); + if (diskSize < UINT32_MAX) { // If the disk is under 2TiB + partitions[0].lengthLBA = diskSize - 1; + } else { // disk is too big to represent, so fake it... + partitions[0].lengthLBA = UINT32_MAX; + } // if/else + + // Zero out three unused primary partitions... + for (i = 1; i < 4; i++) { + partitions[i].status = UINT8_C(0); + partitions[i].firstSector[0] = UINT8_C(0); + partitions[i].firstSector[1] = UINT8_C(0); + partitions[i].firstSector[2] = UINT8_C(0); + partitions[i].partitionType = UINT8_C(0); + partitions[i].lastSector[0] = UINT8_C(0); + partitions[i].lastSector[1] = UINT8_C(0); + partitions[i].lastSector[2] = UINT8_C(0); + partitions[i].firstLBA = UINT32_C(0); + partitions[i].lengthLBA = UINT32_C(0); + } // for + + // Zero out all the logical partitions. Not necessary for data + // integrity on write, but eliminates stray entries if user wants + // to view the MBR after converting the disk + for (i = 0; i < NUM_LOGICALS; i++) { + logicals[i].status = UINT8_C(0); + logicals[i].firstSector[0] = UINT8_C(0); + logicals[i].firstSector[1] = UINT8_C(0); + logicals[i].firstSector[2] = UINT8_C(0); + logicals[i].partitionType = UINT8_C(0); + logicals[i].lastSector[0] = UINT8_C(0); + logicals[i].lastSector[1] = UINT8_C(0); + logicals[i].lastSector[2] = UINT8_C(0); + logicals[i].firstLBA = UINT32_C(0); + logicals[i].lengthLBA = UINT32_C(0); + } // for + + state = gpt; +} // MBRData::MakeProtectiveMBR() + +// Return a pointer to a primary or logical partition, or NULL if +// the partition is out of range.... +struct MBRRecord* MBRData::GetPartition(int i) { + MBRRecord* thePart = NULL; + + if ((i >= 0) && (i < 4)) { // primary partition + thePart = &partitions[i]; + } // if + if ((i >= 4) && (i < (NUM_LOGICALS + 4))) { + thePart = &logicals[i - 4]; + } // if + return thePart; +} // GetPartition() + +// Displays the state, as a word, on stdout. Used for debugging +void MBRData::ShowState(void) { + switch (state) { + case invalid: + printf("invalid"); + break; + case gpt: + printf("gpt"); + break; + case hybrid: + printf("hybrid"); + break; + case mbr: + printf("mbr"); + break; + default: + printf("unknown -- bug!"); + break; + } // switch +} // MBRData::ShowState() + +// Create a primary partition of the specified number, starting LBA, +// and length. This function does *NO* error checking, so it's possible +// to seriously screw up a partition table using this function! It's +// intended as a way to create a hybrid MBR, which is a pretty funky +// setup to begin with.... +void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type, + int bootable) { + + partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80; + partitions[num].firstSector[0] = UINT8_C(0); + partitions[num].firstSector[1] = UINT8_C(0); + partitions[num].firstSector[2] = UINT8_C(0); + partitions[num].partitionType = (uint8_t) type; + partitions[num].lastSector[0] = UINT8_C(0); + partitions[num].lastSector[1] = UINT8_C(0); + partitions[num].lastSector[2] = UINT8_C(0); + partitions[num].firstLBA = start; + partitions[num].lengthLBA = length; +} // MakePart() + +uint8_t MBRData::GetStatus(int i) { + MBRRecord* thePart; + uint8_t retval; + + thePart = GetPartition(i); + if (thePart != NULL) + retval = thePart->status; + else + retval = UINT8_C(0); + return retval; +} // MBRData::GetStatus() + +uint8_t MBRData::GetType(int i) { + MBRRecord* thePart; + uint8_t retval; + + thePart = GetPartition(i); + if (thePart != NULL) + retval = thePart->partitionType; + else + retval = UINT8_C(0); + return retval; +} // MBRData::GetType() + +uint32_t MBRData::GetFirstSector(int i) { + MBRRecord* thePart; + uint32_t retval; + + thePart = GetPartition(i); + if (thePart != NULL) { + retval = thePart->firstLBA; + } else + retval = UINT32_C(0); + return retval; +} // MBRData::GetFirstSector() + +uint32_t MBRData::GetLength(int i) { + MBRRecord* thePart; + uint32_t retval; + + thePart = GetPartition(i); + if (thePart != NULL) { + retval = thePart->lengthLBA; + } else + retval = UINT32_C(0); + return retval; +} // MBRData::GetLength() @@ -0,0 +1,95 @@ +/* mbr.h -- MBR data structure definitions, types, and functions */ + +#include <stdint.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +#ifndef __MBRSTRUCTS +#define __MBRSTRUCTS + +#define MBR_SIGNATURE UINT16_C(0xAA55) + +// Maximum number of logical partitions supported +#define NUM_LOGICALS 124 + +using namespace std; + +/**************************************** + * * + * MBRData class and related structures * + * * + ****************************************/ + +// Data for a single MBR partition record +// Note that firstSector and lastSector are in CHS addressing, which +// splits the bits up in a weird way. +struct MBRRecord { + uint8_t status; + uint8_t firstSector[3]; + uint8_t partitionType; + uint8_t lastSector[3]; + uint32_t firstLBA; + uint32_t lengthLBA; +}; // struct MBRRecord + +// Extended Boot Record (EBR) data, used to hold one logical partition's +// data within an extended partition. Includes pointer to next record for +// in-memory linked-list access. This is similar to MBRData, but with a +// few tweaks.... +struct EBRRecord { + uint8_t code[446]; // generally 0s (and we don't care if they aren't) + // First partition entry defines partition; second points to next + // entry in on-disk linked list; remaining two are unused. Note that + // addresses are relative to the extended partition, not to the disk + // as a whole. + struct MBRRecord partitions[4]; + uint16_t MBRSignature; +}; // struct EBRRecord + +// Possible states of the MBR +enum MBRValidity {invalid, gpt, hybrid, mbr}; + +// Full data in tweaked MBR format +class MBRData { +protected: + uint8_t code[440]; + uint32_t diskSignature; + uint16_t nulls; + struct MBRRecord partitions[4]; + uint16_t MBRSignature; + + // Above are basic MBR data; now add more stuff.... + uint32_t blockSize; // block size (usually 512) + uint64_t diskSize; // size in blocks + char device[256]; + // Now an array of partitions for the logicals, in array form (easier + // than a linked list, and good enough for the GPT itself, so....) + struct MBRRecord logicals[NUM_LOGICALS]; + MBRValidity state; + struct MBRRecord* GetPartition(int i); // Return primary or logical partition +public: + MBRData(void); + MBRData(char* deviceFilename); + ~MBRData(void); + void EmptyMBR(void); + int ReadMBRData(char* deviceFilename); + void ReadMBRData(int fd); + int WriteMBRData(void); + void WriteMBRData(int fd); + int ReadLogicalPart(int fd, uint32_t extendedStart, uint32_t diskOffset, + int partNum); + void DisplayMBRData(void); + void MakeProtectiveMBR(void); + MBRValidity GetValidity(void) {return state;} + void ShowState(void); + void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07, + int bootable = 0); + + // Functions to extract data on specific partitions.... + uint8_t GetStatus(int i); + uint8_t GetType(int i); + uint32_t GetFirstSector(int i); + uint32_t GetLength(int i); +}; // struct MBRData + +#endif diff --git a/parttypes.cc b/parttypes.cc new file mode 100644 index 0000000..3f81797 --- /dev/null +++ b/parttypes.cc @@ -0,0 +1,332 @@ +// parttypes.cc +// Class to manage partition type codes -- a slight variant on MBR type +// codes, GUID type codes, and associated names. + +#define __STDC_LIMIT_MACROS +#define __STDC_CONSTANT_MACROS + +#include <string.h> +#include <stdint.h> +#include <stdio.h> +#include "parttypes.h" + +using namespace std; + +int PartTypes::numInstances = 0; +AType* PartTypes::allTypes = NULL; +AType* PartTypes::lastType = NULL; + +// Constructor. Its main task is to initialize the data list, but only +// if this is the first instance, since it's a static linked list. +// Partition type codes are MBR type codes multiplied by 0x0100, with +// additional related codes taking on following numbers. For instance, +// the FreeBSD disklabel code in MBR is 0xa5; here, it's 0xa500, with +// additional FreeBSD codes being 0xa501, 0xa502, and so on. This gives +// related codes similar numbers and (given appropriate entry positions +// in the linked list) keeps them together in the listings generated +// by typing "L" at the main gdisk menu. +// See http://www.win.tue.nl/~aeb/partitions/partition_types-1.html +// for a list of MBR partition type codes. +PartTypes::PartTypes(void) { + + numInstances++; + if (numInstances == 1) { + + // Start with the "unused entry," which should normally appear only + // on empty partition table entries.... + AddType(0x0000, UINT64_C(0x0000000000000000), UINT64_C(0x0000000000000000), + "Unused entry", 0); + + // DOS/Windows partition types, which confusingly Linux also uses in GPT + AddType(0x0100, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // FAT-12 + AddType(0x0400, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // FAT-16 < 32M + AddType(0x0600, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // FAT-16 + AddType(0x0700, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 1); // NTFS (or could be HPFS) + AddType(0x0b00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // FAT-32 + AddType(0x0c00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // FAT-32 LBA + AddType(0x0c01, UINT64_C(0x4DB80B5CE3C9E316), UINT64_C(0xAE1502F02DF97D81), + "Microsoft Reserved"); // Microsoft reserved + AddType(0x0e00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // FAT-16 LBA + AddType(0x1100, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // Hidden FAT-12 + AddType(0x1400, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // Hidden FAT-16 < 32M + AddType(0x1600, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // Hidden FAT-16 + AddType(0x1700, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // Hidden NTFS (or could be HPFS) + AddType(0x1b00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // Hidden FAT-32 + AddType(0x1c00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // Hidden FAT-32 LBA + AddType(0x1e00, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // Hidden FAT-16 LBA + AddType(0x2700, UINT64_C(0x4D4006D1DE94BBA4), UINT64_C(0xACD67901D5BF6AA1), + "Windows RE"); // Windows RE + AddType(0x4200, UINT64_C(0x4F621431Af9B60A0), UINT64_C(0xAD694A71113368BC), + "Windows LDM data"); // Logical disk manager + AddType(0x4201, UINT64_C(0x42E07E8F5808C8AA), UINT64_C(0xB3CF3404E9E1D285), + "Windows LDM metadata"); // Logical disk manager + + // Linux-specific partition types.... + AddType(0x8200, UINT64_C(0x43C4A4AB0657FD6D), UINT64_C(0x4F4F4BC83309E584), + "Linux swap"); // Linux swap (or could be Solaris) + AddType(0x8300, UINT64_C(0x4433B9E5EBD0A0A2), UINT64_C(0xC79926B7B668C087), + "Linux/Windows data", 0); // Linux native + AddType(0x8301, UINT64_C(0x60C000078DA63339), UINT64_C(0x080923C83A0836C4), + "Linux Reserved"); // Linux reserved + AddType(0x8e00, UINT64_C(0x44C2F507E6D6D379), UINT64_C(0x28F93D2A8F233CA2), + "Linux LVM"); // Linux LVM + + // FreeBSD partition types.... + // Note: Rather than extract FreeBSD disklabel data, convert FreeBSD + // partitions in-place, and let FreeBSD sort out the details.... + AddType(0xa500, UINT64_C(0x11D66ECF516E7CB4), UINT64_C(0x2B71092D0200F88F), + "FreeBSD disklabel"); // FreeBSD disklabel + AddType(0xa501, UINT64_C(0x11DC7F4183BD6B9D), UINT64_C(0x0F4FB86015000BBE), + "FreeBSD boot"); // FreeBSD boot + AddType(0xa502, UINT64_C(0x11D66ECF516E7CB5), UINT64_C(0x2B71092D0200F88F), + "FreeBSD swap"); // FreeBSD swap + AddType(0xa503, UINT64_C(0x11D66ECF516E7CB6), UINT64_C(0x2B71092D0200F88F), + "FreeBSD UFS"); // FreeBSD UFS + AddType(0xa504, UINT64_C(0x11D66ECF516E7CBA), UINT64_C(0x2B71092D0200F88F), + "FreeBSD ZFS"); // FreeBSD ZFS + AddType(0xa505, UINT64_C(0x11D66ECF516E7CB8), UINT64_C(0x2B71092D0200F88F), + "FreeBSD Vinum/RAID"); // FreeBSD Vinum + + // A MacOS partition type, separated from others by NetBSD partition types... + AddType(0xa800, UINT64_C(0x11AA000055465300), UINT64_C(0xACEC4365300011AA), + "Apple UFS"); // MacOS X + + // NetBSD partition types. Note that the main entry sets it up as a + // FreeBSD disklabel. I'm not 100% certain this is the correct behavior. + AddType(0xa900, UINT64_C(0x11D66ECF516E7CB4), UINT64_C(0x2B71092D0200F88F), + "FreeBSD disklabel", 0); // NetBSD disklabel + AddType(0xa901, UINT64_C(0x11DCB10E49F48D32), UINT64_C(0x489687D119009BB9), + "NetBSD swap"); + AddType(0xa902, UINT64_C(0x11DCB10E49F48D5A), UINT64_C(0x489687D119009BB9), + "NetBSD FFS"); + AddType(0xa903, UINT64_C(0x11DCB10E49F48D82), UINT64_C(0x489687D119009BB9), + "NetBSD LFS"); + AddType(0xa903, UINT64_C(0x11DCB10E49F48DAA), UINT64_C(0x489687D119009BB9), + "NetBSD RAID"); + AddType(0xa904, UINT64_C(0x11DCB10F2DB519C4), UINT64_C(0x489687D119009BB9), + "NetBSD concatenated"); + AddType(0xa905, UINT64_C(0x11DCB10F2DB519EC), UINT64_C(0x489687D119009BB9), + "NetBSD encrypted"); + + // MacOS partition types (See also 0xa800, above).... + AddType(0xab00, UINT64_C(0x11AA0000426F6F74), UINT64_C(0xACEC4365300011AA), + "Apple boot"); // MacOS X + AddType(0xaf00, UINT64_C(0x11AA000048465300), UINT64_C(0xACEC4365300011AA), + "Apple HFS/HFS+"); // MacOS X + AddType(0xaf01, UINT64_C(0x11AA000052414944), UINT64_C(0xACEC4365300011AA), + "Apple RAID"); // MacOS X + AddType(0xaf02, UINT64_C(0x11AA5F4F52414944), UINT64_C(0xACEC4365300011AA), + "Apple RAID offline"); // MacOS X + AddType(0xaf03, UINT64_C(0x11AA6C004C616265), UINT64_C(0xACEC4365300011AA), + "Apple label"); // MacOS X + AddType(0xaf04, UINT64_C(0x11AA76655265636F), UINT64_C(0xACEC4365300011AA), + "AppleTV recovery"); // MacOS X + + // Solaris partition types (one of which is shared with MacOS) + AddType(0xbe00, UINT64_C(0x11B21DD26A82CB45), UINT64_C(0x316673200008A699), + "Solaris boot"); // Solaris boot + AddType(0xbf00, UINT64_C(0x11B21DD26a85CF4D), UINT64_C(0x316673200008A699), + "Solaris root"); // Solaris root + AddType(0xbf01, UINT64_C(0x11B21DD26A898CC3), UINT64_C(0x316673200008A699), + "Solaris /usr & Mac ZFS"); // MacOS X & Solaris + AddType(0xbf02, UINT64_C(0x11B21DD26A87C46F), UINT64_C(0x316673200008A699), + "Solaris swap"); + AddType(0xbf03, UINT64_C(0x11B21DD26A8B642B), UINT64_C(0x316673200008A699), + "Solaris backup"); + AddType(0xbf04, UINT64_C(0x11B21DD26A8EF2E9), UINT64_C(0x316673200008A699), + "Solaris /var"); + AddType(0xbf05, UINT64_C(0x11B21DD26A90BA39), UINT64_C(0x316673200008A699), + "Solaris /home"); + AddType(0xbf05, UINT64_C(0x11B21DD26A9283A5), UINT64_C(0x316673200008A699), + "Solaris EFI_ALTSCTR"); + AddType(0xbf06, UINT64_C(0x11B21DD26A945A3B), UINT64_C(0x316673200008A699), + "Solaris Reserved 1"); + AddType(0xbf07, UINT64_C(0x11B21DD26A9630D1), UINT64_C(0x316673200008A699), + "Solaris Reserved 2"); + AddType(0xbf08, UINT64_C(0x11B21DD26A980767), UINT64_C(0x316673200008A699), + "Solaris Reserved 3"); + AddType(0xbf09, UINT64_C(0x11B21DD26A96237F), UINT64_C(0x316673200008A699), + "Solaris Reserved 4"); + AddType(0xbf0a, UINT64_C(0x11B21DD26A8D2AC7), UINT64_C(0x316673200008A699), + "Solaris Reserved 5"); + + // I can find no MBR equivalents for these, but they're on the + // Wikipedia page for GPT, so here we go.... + AddType(0xc001, UINT64_C(0x11D33AEB75894C1E), UINT64_C(0x000000A0037BC1B7), + "HP-UX data"); + AddType(0xc002, UINT64_C(0x11D632E3E2A1E728), UINT64_C(0x000000A0037B82A6), + "HP-UX service"); + + // EFI system and related partitions + AddType(0xEF00, UINT64_C(0x11d2f81fc12a7328), UINT64_C(0x3bc93ec9a0004bba), + "EFI System"); // EFI System (parted marks Linux boot + // partitions like this) + AddType(0xEF01, UINT64_C(0x11d333e7024dee41), UINT64_C(0x9FF381C70800699d), + "MBR partition scheme"); // Whatever that is (from Wikipedia) + AddType(0xEF02, UINT64_C(0x6E6F644921686148), UINT64_C(0x4946456465654E74), + "BIOS boot partition"); // + + // A straggler Linux partition type.... + AddType(0xfd00, UINT64_C(0x4D3B05FCA19D880F), UINT64_C(0x1E91840F3F7406A0), + "Linux RAID"); // Linux RAID + } // if +} // default constructor + +PartTypes::~PartTypes(void) { + AType* tempType; + + numInstances--; + if (numInstances == 0) { + while (allTypes != NULL) { + tempType = allTypes; + allTypes = allTypes->next; + delete tempType; + } // while + } // if +} // destructor + +// Add a single type to the linked list of types. Returns 1 if operation +// succeeds, 0 otherwise +int PartTypes::AddType(uint16_t mbrType, uint64_t guidData1, uint64_t guidData2, + const char* n, int toDisplay) { + AType* tempType; + int allOK = 1; + + tempType = new AType; + if (tempType != NULL) { + tempType->MBRType = mbrType; + tempType->GUIDType.data1 = guidData1; + tempType->GUIDType.data2 = guidData2; + strncpy(tempType->name, n, PNAME_SIZE); + tempType->display = toDisplay; + tempType->next = NULL; + if (allTypes == NULL) { // first entry + allTypes = tempType; + } else { + lastType->next = tempType; + } // if/else + lastType = tempType; + } else { + allOK = 0; + } // if/else + return allOK; +} // PartTypes::AddType() + +// Displays the available types and my extended MBR codes for same.... +// Note: This function assumes an 80-column display. On wider displays, +// it stops at under 80 columns; on narrower displays, lines will wrap +// in an ugly way. +void PartTypes::ShowTypes(void) { + int colCount = 1; // column count + AType* thisType = allTypes; + char tempStr[20]; + + while (thisType != NULL) { + if (thisType->display == 1) { // show it + strncpy(tempStr, thisType->name, 19); + tempStr[19] = '\0'; + printf("%04x %-19s ", thisType->MBRType, tempStr); + if ((colCount % 3) == 0) + printf("\n"); + colCount++; + } // if + thisType = thisType->next; + } // while + printf("\n"); +} // PartTypes::ShowTypes() + +// Returns 1 if code is a valid extended MBR code, 0 if it's not +int PartTypes::Valid(uint16_t code) { + AType* thisType = allTypes; + int found = 0; + + while ((thisType != NULL) && (!found)) { + if (thisType->MBRType == code) { + found = 1; + } // if + thisType = thisType->next; + } // while + return found; +} // PartTypes::Valid() + +// Convert a GUID code to a name. +char* PartTypes::GUIDToName(struct GUIDData typeCode, char typeName[]) { + AType* theItem = allTypes; + int found = 0; + + while ((theItem != NULL) && (!found)) { + if ((theItem->GUIDType.data1 == typeCode.data1) && + (theItem->GUIDType.data2 == typeCode.data2)) { // found it! + strcpy(typeName, theItem->name); + found = 1; + } else { + theItem = theItem->next; + } // if/else + } // while + if (!found) { + strcpy(typeName, (char*) "Unknown"); + } // if (!found) + return typeName; +} // PartTypes::GUIDToName() + +// This function takes a variant of the MBR partition type code and +// converts it to a GUID type code +struct GUIDData PartTypes::IDToGUID(uint16_t ID) { + AType* theItem = allTypes; + int found = 0; + struct GUIDData theGUID; + + while ((theItem != NULL) && (!found)) { + if (theItem->MBRType == ID) { // found it! + theGUID = theItem->GUIDType; + found = 1; + } else { + theItem = theItem->next; + } // if/else + } // while + if (!found) { + theGUID = IDToGUID(0x0700); // assign a default type code + printf("Exact type match not found; assigning type code for 'Linux/Windows data'\n"); + } // if (!found) + return theGUID; +} // PartTypes::IDToGUID() + +// Convert a GUID to a 16-bit variant of the MBR ID number. +// Note that this function ignores entries for which the display variable +// is set to 0. This enables control of which values get returned when +// there are multiple possibilities, but opens the algorithm up to the +// potential for problems should the data in the list be bad. +uint16_t PartTypes::GUIDToID(struct GUIDData typeCode) { + AType* theItem = allTypes; + int found = 0; + uint16_t theID; + + while ((theItem != NULL) && (!found)) { + if ((theItem->GUIDType.data1 == typeCode.data1) && + (theItem->GUIDType.data2 == typeCode.data2) && + (theItem->display == 1)) { // found it! + theID = theItem->MBRType; + found = 1; + } else { + theItem = theItem->next; + } // if/else + } // while + if (!found) { + theID = 0xFFFF; + } // if (!found) + return theID; +} // PartTypes::GUIDToID() diff --git a/parttypes.h b/parttypes.h new file mode 100644 index 0000000..ad62027 --- /dev/null +++ b/parttypes.h @@ -0,0 +1,44 @@ +#include <stdint.h> +#include <unistd.h> +#include <stdlib.h> +#include <string> +#include "support.h" + +#ifndef __PARTITION_TYPES +#define __PARTITION_TYPES + +// Set the size of the name string +#define PNAME_SIZE 80 + +using namespace std; + +// A partition type +struct AType { + // I'm using a custom 16-bit extension of the original MBR 8-bit + // type codes, so as to permit disambiguation and use of new + // codes required by GPT + uint16_t MBRType; + struct GUIDData GUIDType; + char name[PNAME_SIZE]; + int display; // 1 to show to users as available type, 0 not to + AType* next; +}; // struct AType + +class PartTypes { +protected: + static int numInstances; + static AType* allTypes; // Linked list holding all the data + static AType* lastType; // Pointer to last entry in the list +public: + PartTypes(void); + ~PartTypes(void); + int AddType(uint16_t mbrType, uint64_t guidData1, uint64_t guidData2, + const char* name, int toDisplay = 1); + void ShowTypes(void); + int Valid(uint16_t); + char* GUIDToName(struct GUIDData typeCode, char typeName[]); + struct GUIDData IDToGUID(uint16_t ID); + uint16_t GUIDToID(struct GUIDData); +}; + +#endif diff --git a/support.cc b/support.cc new file mode 100644 index 0000000..d3a6357 --- /dev/null +++ b/support.cc @@ -0,0 +1,354 @@ +// support.cc +// Non-class support functions for gdisk program. +// Primarily by Rod Smith, February 2009, but with a few functions +// copied from other sources (see attributions below). + +#define __STDC_LIMIT_MACROS +#define __STDC_CONSTANT_MACROS + +#include <sys/ioctl.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include "support.h" + +#include <sys/types.h> + +using namespace std; + +// Get a numeric value from the user, between low and high (inclusive). +// Keeps looping until the user enters a value within that range. +// If user provides no input, def (default value) is returned. +// (If def is outside of the low-high range, an explicit response +// is required.) +int GetNumber(int low, int high, int def, const char prompt[]) { + int response, num; + char line[255]; + + if (low != high) { // bother only if low and high differ... + response = low - 1; // force one loop by setting response outside range + while ((response < low) || (response > high)) { + printf(prompt); + fgets(line, 255, stdin); + num = sscanf(line, "%d", &response); + if (num == 1) { // user provided a response + if ((response < low) || (response > high)) + printf("Value out of range\n"); + } else { // user hit enter; return default + response = def; + } // if/else + } // while + } else { // low == high, so return this value + printf("Using %d\n", low); + response = low; + } // else + return (response); +} // GetNumber() + +// Gets a Y/N response (and converts lowercase to uppercase) +char GetYN(void) { + char line[255]; + char response = '\0'; + + while ((response != 'Y') && (response != 'N')) { + printf("(Y/N): "); + fgets(line, 255, stdin); + sscanf(line, "%c", &response); + if (response == 'y') response = 'Y'; + if (response == 'n') response = 'N'; + } // while + return response; +} // GetYN(void) + +// Obtains the final sector number, between low and high, from the +// user, accepting values prefixed by "+" to add sectors to low, +// or the same with "K", "M", "G", or "T" as suffixes to add +// kilobytes, megabytes, gigabytes, or terabytes, respectively. +// Use the high value as the default if the user just hits Enter +uint64_t GetLastSector(uint64_t low, uint64_t high, char prompt[]) { + unsigned long long response; + int num; + int plusFlag = 0; + uint64_t mult = 1; + char suffix; + char line[255]; + + response = low - 1; // Ensure one pass by setting a too-low initial value + while ((response < low) || (response > high)) { + printf(prompt); + fgets(line, 255, stdin); + + // Remove leading spaces, if present + while (line[0] == ' ') + strcpy(line, &line[1]); + + // If present, flag and remove leading plus sign + if (line[0] == '+') { + plusFlag = 1; + strcpy(line, &line[1]); + } // if + + // Extract numeric response and, if present, suffix + num = sscanf(line, "%llu%c", &response, &suffix); + + // If no response, use default: The high value + if (num <= 0) { + response = (unsigned long long) high; + suffix = ' '; + } // if + + // Set multiplier based on suffix + switch (suffix) { + case 'K': + case 'k': + mult = (uint64_t) 1024 / SECTOR_SIZE; + break; + case 'M': + case 'm': + mult = (uint64_t) 1048576 / SECTOR_SIZE; + break; + case 'G': + case 'g': + mult = (uint64_t) 1073741824 / SECTOR_SIZE; + break; + case 'T': + case 't': + mult = ((uint64_t) 1073741824 * (uint64_t) 1024) / (uint64_t) SECTOR_SIZE; + break; + default: + mult = 1; + } // switch + + // Adjust response based on multiplier and plus flag, if present + response *= (unsigned long long) mult; + if (plusFlag == 1) { + response = response + (unsigned long long) low - 1; + } // if/else + } // while + return ((uint64_t) response); +} // GetLastSector() + +// Return a plain-text name for a partition type. +// Takes a size in bytes (in size) and converts this to a size in +// SI units (KiB, MiB, GiB, TiB, or PiB), returned in C++ string +// form +char* BytesToSI(uint64_t size, char theValue[]) { + char units[8]; + float sizeInSI; + + if (theValue != NULL) { + sizeInSI = (float) size; + strcpy (units, " bytes"); + if (sizeInSI > 1024.0) { + sizeInSI /= 1024.0; + strcpy(units, " KiB"); + } // if + if (sizeInSI > 1024.0) { + sizeInSI /= 1024.0; + strcpy(units, " MiB"); + } // if + if (sizeInSI > 1024.0) { + sizeInSI /= 1024.0; + strcpy(units, " GiB"); + } // if + if (sizeInSI > 1024.0) { + sizeInSI /= 1024.0; + strcpy(units, " TiB"); + } // if + if (sizeInSI > 1024.0) { + sizeInSI /= 1024.0; + strcpy(units, " PiB"); + } // if + if (strcmp(units, " bytes") == 0) { // in bytes, so no decimal point + sprintf(theValue, "%1.0f%s", sizeInSI, units); + } else { + sprintf(theValue, "%1.1f%s", sizeInSI, units); + } // if/else + } // if + return theValue; +} // BlocksToSI() + +// Returns block size of device pointed to by fd file descriptor, or -1 +// if there's a problem +int GetBlockSize(int fd) { + int err, result; + +#ifdef __APPLE__ + err = ioctl(fd, DKIOCGETBLOCKSIZE, &result); +#else + err = ioctl(fd, BLKSSZGET, &result); +#endif + + if (result != 512) { + printf("\aWARNING! Sector size is not 512 bytes! This program is likely to"); + printf("misbehave! Proceed at your own risk!\n\n"); + } // if + + if (err == -1) + result = -1; + return (result); +} // GetBlockSize() + +// Convert a GUID to a string representation, suitable for display +// to humans.... +char* GUIDToStr(struct GUIDData theGUID, char* theString) { + uint64_t block; + + if (theString != NULL) { + block = (theGUID.data1 & UINT64_C(0x00000000FFFFFFFF)); + sprintf(theString, "%08llX-", (unsigned long long) block); + block = (theGUID.data1 & UINT64_C(0x0000FFFF00000000)) >> 32; + sprintf(theString, "%s%04llX-", theString, (unsigned long long) block); + block = (theGUID.data1 & UINT64_C(0xFFFF000000000000)) >> 48; + sprintf(theString, "%s%04llX-", theString, (unsigned long long) block); + block = (theGUID.data2 & UINT64_C(0x00000000000000FF)); + sprintf(theString, "%s%02llX", theString, (unsigned long long) block); + block = (theGUID.data2 & UINT64_C(0x000000000000FF00)) >> 8; + sprintf(theString, "%s%02llX-", theString, (unsigned long long) block); + block = (theGUID.data2 & UINT64_C(0x0000000000FF0000)) >> 16; + sprintf(theString, "%s%02llX", theString, (unsigned long long) block); + block = (theGUID.data2 & UINT64_C(0x00000000FF000000)) >> 24; + sprintf(theString, "%s%02llX", theString, (unsigned long long) block); + block = (theGUID.data2 & UINT64_C(0x000000FF00000000)) >> 32; + sprintf(theString, "%s%02llX", theString, (unsigned long long) block); + block = (theGUID.data2 & UINT64_C(0x0000FF0000000000)) >> 40; + sprintf(theString, "%s%02llX", theString, (unsigned long long) block); + block = (theGUID.data2 & UINT64_C(0x00FF000000000000)) >> 48; + sprintf(theString, "%s%02llX", theString, (unsigned long long) block); + block = (theGUID.data2 & UINT64_C(0xFF00000000000000)) >> 56; + sprintf(theString, "%s%02llX", theString, (unsigned long long) block); + } // if + return theString; +} // GUIDToStr() + +// Get a GUID from the user +GUIDData GetGUID(void) { + uint64_t part1, part2, part3, part4, part5; + int entered = 0; + char temp[255], temp2[255]; + GUIDData theGUID; + + printf("\nA GUID is entered in five segments of from two to six bytes, with\n" + "dashes between segments.\n"); + printf("Enter the entire GUID, a four-byte hexadecimal number for the first segment, or\n" + "'R' to generate the entire GUID randomly: "); + fgets(temp, 255, stdin); + + // If user entered 'r' or 'R', generate GUID randomly.... + if ((temp[0] == 'r') || (temp[0] == 'R')) { + theGUID.data1 = (uint64_t) rand() * (uint64_t) rand(); + theGUID.data2 = (uint64_t) rand() * (uint64_t) rand(); + entered = 1; + } // if user entered 'R' or 'r' + + // If string length is right for whole entry, try to parse it.... + if ((strlen(temp) == 37) && (entered == 0)) { + strncpy(temp2, &temp[0], 8); + temp2[8] = '\0'; + sscanf(temp2, "%llx", &part1); + strncpy(temp2, &temp[9], 4); + temp2[4] = '\0'; + sscanf(temp2, "%llx", &part2); + strncpy(temp2, &temp[14], 4); + temp2[4] = '\0'; + sscanf(temp2, "%llx", &part3); + theGUID.data1 = (part3 << 48) + (part2 << 32) + part1; + strncpy(temp2, &temp[19], 4); + temp2[4] = '\0'; + sscanf(temp2, "%llx", &part4); + strncpy(temp2, &temp[24], 12); + temp2[12] = '\0'; + sscanf(temp2, "%llx", &part5); + theGUID.data2 = ((part4 & UINT64_C(0x000000000000FF00)) >> 8) + + ((part4 & UINT64_C(0x00000000000000FF)) << 8) + + ((part5 & UINT64_C(0x0000FF0000000000)) >> 24) + + ((part5 & UINT64_C(0x000000FF00000000)) >> 8) + + ((part5 & UINT64_C(0x00000000FF000000)) << 8) + + ((part5 & UINT64_C(0x0000000000FF0000)) << 24) + + ((part5 & UINT64_C(0x000000000000FF00)) << 40) + + ((part5 & UINT64_C(0x00000000000000FF)) << 56); + entered = 1; + } // if + + // If neither of the above methods of entry was used, use prompted + // entry.... + if (entered == 0) { + sscanf(temp, "%llx", &part1); + printf("Enter a two-byte hexadecimal number for the second segment: "); + fgets(temp, 255, stdin); + sscanf(temp, "%llx", &part2); + printf("Enter a two-byte hexadecimal number for the third segment: "); + fgets(temp, 255, stdin); + sscanf(temp, "%llx", &part3); + theGUID.data1 = (part3 << 48) + (part2 << 32) + part1; + printf("Enter a two-byte hexadecimal number for the fourth segment: "); + fgets(temp, 255, stdin); + sscanf(temp, "%llx", &part4); + printf("Enter a six-byte hexadecimal number for the fifth segment: "); + fgets(temp, 255, stdin); + sscanf(temp, "%llx", &part5); + theGUID.data2 = ((part4 & UINT64_C(0x000000000000FF00)) >> 8) + + ((part4 & UINT64_C(0x00000000000000FF)) << 8) + + ((part5 & UINT64_C(0x0000FF0000000000)) >> 24) + + ((part5 & UINT64_C(0x000000FF00000000)) >> 8) + + ((part5 & UINT64_C(0x00000000FF000000)) << 8) + + ((part5 & UINT64_C(0x0000000000FF0000)) << 24) + + ((part5 & UINT64_C(0x000000000000FF00)) << 40) + + ((part5 & UINT64_C(0x00000000000000FF)) << 56); + entered = 1; + } // if/else + printf("New GUID: %s\n", GUIDToStr(theGUID, temp)); + return theGUID; +} // GetGUID() + +// Compute (2 ^ value). Given the return type, value must be 63 or less. +// Used in some bit-fiddling functions +uint64_t PowerOf2(int value) { + uint64_t retval = 1; + int i; + + if ((value < 64) && (value >= 0)) { + for (i = 0; i < value; i++) { + retval *= 2; + } // for + } else retval = 0; + return retval; +} // PowerOf2() + +/************************************************************************************** + * * + * Below functions are lifted from various sources, as documented in comments before * + * each one. * + * * + **************************************************************************************/ + +// The disksize function is taken from the Linux fdisk code and modified +// to work around a problem returning a uint64_t value on Mac OS. +uint64_t disksize(int fd, int *err) { + long sz; // Do not delete; needed for Linux + long long b; // Do not delete; needed for Linux + uint64_t sectors; + + // Note to self: I recall testing a simplified version of + // this code, similar to what's in the __APPLE__ block, + // on Linux, but I had some problems. IIRC, it ran OK on 32-bit + // systems but not on 64-bit. Keep this in mind in case of + // 32/64-bit issues on MacOS.... +#ifdef __APPLE__ + *err = ioctl(fd, DKIOCGETBLOCKCOUNT, §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 <stdint.h> +#include <unistd.h> +#include <stdlib.h> + +#ifdef __APPLE__ +// Darwin (Mac OS) only: disk IOCTLs are different, and there is no lseek64 +#include <sys/disk.h> +#define lseek64 lseek +#else + +// Linux only.... +#include <linux/fs.h> +#endif + +#include <string> + +#ifndef __GPTSUPPORT +#define __GPTSUPPORT + +// Set this as a default +#define SECTOR_SIZE UINT32_C(512) + +using namespace std; + +// a GUID +struct GUIDData { + uint64_t data1; + uint64_t data2; +}; // struct GUIDData + +int GetNumber(int low, int high, int def, const char prompt[]); +char GetYN(void); +uint64_t GetLastSector(uint64_t low, uint64_t high, char prompt[]); +char* BytesToSI(uint64_t size, char theValue[]); +int GetBlockSize(int fd); +char* GUIDToStr(struct GUIDData theGUID, char* theString); +GUIDData GetGUID(void); +uint64_t PowerOf2(int value); + +uint64_t disksize(int fd, int* err); + +#endif |