diff --git a/sgdisk.8 b/sgdisk.8 new file mode 100644 index 0000000000000000000000000000000000000000..48a740d2a6e2999570acf9999044d10288aeab55 --- /dev/null +++ b/sgdisk.8 @@ -0,0 +1,467 @@ +.\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com) +.\" May be distributed under the GNU General Public License +.TH "SGDISK" "8" "0.6.0" "Roderick W. Smith" "GPT fdisk Manual" +.SH "NAME" +sgdisk \- Command\-line GUID partition table (GPT) manipulator for Linux and Unix +.SH "SYNOPSIS" +.BI "sgdisk " +[ options ] +.I device + +.SH "DESCRIPTION" +GPT fdisk is a text\-mode menu\-driven package for creation and manipulation of +partition tables. It consists of two programs: the text\-mode interactive +\fBgdisk\fR and the command\-line \fBsgdisk\fR. Either program will +automatically convert an old\-style Master Boot Record (MBR) partition table +or BSD disklabel stored without an MBR carrier partition to the newer Globally +Unique Identifier (GUID) Partition Table (GPT) format, or will load a GUID +partition table. This man page documents the command\-line \fBsgdisk\fR +program. + +Some advanced data manipulation and +recovery options require you to understand the distinctions between the +main and backup data, as well as between the GPT headers and the partition +tables. For information on MBR vs. GPT, as well as GPT terminology and +structure, see the extended \fBgdisk\fR documentation at +\fIhttp://www.rodsbooks.com/gdisk/\fR or consult Wikipedia. + +The \fBsgdisk\fR program employs a user interface that's based entirely on +the command line, making it suitable for use in scripts or by experts who +want to make one or two quick changes to a disk. (The program may query the +user when certain errors are encountered, though.) The program's name is +based on \fBsfdisk\fR, but the user options of the two programs are +entirely different from one another. + +Ordinarily, \fBsgdisk\fR operates on disk device files, such as +\fI/dev/sda\fR or \fI/dev/hda\fR under Linux, \fI/dev/disk0\fR under +Mac OS X, or \fI/dev/ad0\fR or \fI/dev/da0\fR under FreeBSD. The program +can also operate on disk image files, which can be either copies of whole +disks (made with \fBdd\fR, for instance) or raw disk images used by +emulators such as QEMU or VMWare. Note that only \fIraw\fR disk images +are supported; \fBsgdisk\fR cannot work on compressed or other advanced +disk image formats. + +The MBR partitioning system uses a combination of cylinder/head/sector +(CHS) addressing and logical block addressing (LBA). The former is klunky +and limiting. GPT drops CHS addressing and uses 64\-bit LBA mode +exclusively. Thus, GPT data structures, and therefore +\fBsgdisk\fR, do not need to deal with CHS geometries and all the problems +they create. + +For best results, you should use an OS\-specific partition table +program whenever possible. For example, you should make Mac OS X +partitions with the Mac OS X Disk Utility program and Linux partitions +with the Linux \fBgdisk\fR, \fBsgdisk\fR, or GNU Parted programs. + +Upon start, \fBsgdisk\fR attempts to identify the partition type in use on +the disk. If it finds valid GPT data, \fBsgdisk\fR will use it. If +\fBsgdisk\fR finds a valid MBR or BSD disklabel but no GPT data, it will +attempt to convert the MBR or disklabel into GPT form. (BSD disklabels are +likely to have unusable first and/or final partitions because they overlap +with the GPT data structures, though.) GPT fdisk can identify, but not use +data in, Apple Partition Map (APM) disks, which are used on 680x0\- and +PowerPC\-based Macintoshes. If you specify any option that results in changes +to an MBR or BSD disklabel, \fBsgdisk\fR ignores those changes unless the +\fI\-g\fR (\fI\-\-mbrtogpt\fR) or \fI\-z\fR (\fI\-\-zap\fR) option is used. +If you use the \fI\-g\fR option, \fBsgdisk\fR replaces the MBR or disklabel +with a GPT. \fIThis action is potentially dangerous!\fR Your system may become +unbootable, and partition type codes may become corrupted if the disk uses +unrecognized type codes. Boot problems are particularly likely if you're +multi\-booting with any GPT\-unaware OS. + +The MBR\-to\-GPT conversion will leave at least one gap in the partition +numbering if the original MBR used logical partitions. These gaps are +harmless, but you can eliminate them by using the \fI\-s\fR (\fI\-\-sort\fR) +option, if you like. (Doing this may require you to update your +\fI/etc/fstab\fR file.) + +When creating a fresh partition table, certain considerations may be in +order: + +.TP +.B * +For data (non\-boot) disks, and for boot disks used on BIOS\-based computers +with GRUB as the boot loader, partitions may be created in whatever order +and in whatever sizes are desired. + +.TP +.B * +Boot disks for EFI\-based systems require an \fIEFI System +Partition\fR (\fBsgdisk\fR internal code 0xEF00) formatted as FAT\-32. +The recommended size of this partition is between 100 and 200 MiB. +Boot\-related files are stored here. (Note that GNU Parted identifies +such partitions as having the "boot flag" set.) + +.TP +.B * +Some boot loaders for BIOS\-based systems make use of a \fIBIOS Boot +Partition\fR (\fBsgdisk\fR internal code 0xEF02), in which the secondary +boot loader is stored, possibly without the benefit of a filesystem. This +partition can typically be quite small (roughly 32 to 200 KiB), but you +should consult your boot loader documentation for details. + +.TP +.B * +If Windows is to boot from a GPT disk, a partition of type \fIMicrosoft +Reserved\fR (\fBsgdisk\fR +internal code 0x0C01) is recommended. This partition should be about 128 MiB +in size. It ordinarily follows the EFI System Partition and immediately +precedes the Windows data partitions. (Note that GNU Parted creates all +FAT partitions as this type, which actually makes the partition unusable +for normal file storage in both Windows and Mac OS X.) + +.TP +.B * +Some OSes' GPT utilities create some blank space (typically 128 MiB) after +each partition. The intent is to enable future disk utilities to use this +space. Such free space is not required of GPT disks, but creating it may +help in future disk maintenance. + +.SH "OPTIONS" +Some options take no arguments, others take one argument (typically a partition +number), and others take compound arguments with colon delimitation. For +instance, \fI\-n\fR (\fI\-\-new\fR) takes a partition number, a starting +sector number, and an ending sector number, as in \fBsgdisk \-n 2:2000:50000 +/dev/sdc\fR, which creates a new partition, numbered 2, starting at sector +2000 an ending at sector 50,000, on \fI/dev/sdc\fR. + +Unrelated options may be combined; however, some such combinations will be +nonsense (such as deleting a partition and then changing its GUID type code). +\fBsgdisk\fR interprets options in the order in which they're entered, so +effects can vary depending on order. For instance, \fBsgdisk \-s \-d 2\fR +sorts the partition table entries and then deletes partition 2 from the +newly\-sorted list; but \fBsgdisk \-d 2 \-s\fR deletes the original partition +2 and then sorts the modified partition table. + +Error checking and opportunities to correct mistakes in \fBsgdisk\fR are +minimal. Although the program endeavors to keep the GPT data structures legal, +it does not prompt for verification before performing its actions. Unless you +require a command\-line\-driven program, you should use the interactive +\fBgdisk\fR instead of \fBsgdisk\fR, since \fBgdisk\fR allows you to +quit without saving your changes, should you make a mistake. + +Although \fBsgdisk\fR is based on the same partition\-manipulation code as +\fBgdisk\fR, \fBsgdisk\fR implements fewer features than its interactive +sibling. Options available in \fBsgdisk\fR are: + +.TP +.B \-a, \-\-set\-alignment=value +Set the sector alignment multiple. GPT fdisk aligns the start of partitions +to sectors that are multiples of this value, which defaults to 8 on disks +larger than 800GiB with 512\-byte sectors and to 1 on smaller disks or those +with non\-512\-byte sectors. This alignment value is necessary to obtain +optimum performance with Western Digital Advanced Format and similar drives +with larger physical than logical sector sizes. + +.TP +.B \-b, \-\-backup=file +Save partition data to a backup file. You can back up your current +in\-memory partition table to a disk file using this option. The resulting +file is a binary file consisting of the protective MBR, the main GPT +header, the backup GPT header, and one copy of the partition table, in that +order. Note that the backup is of the current in\-memory data structures, so +if you launch the program, make changes, and then use this option, the +backup will reflect your changes. If the GPT data structures are damaged, +the backup may not accurately reflect the damaged state; instead, they +will reflect GPT fdisk's first\-pass interpretation of the GPT. + +.TP +.B \-c, \-\-change=partnum:name +Change the GPT name of a partition. This name is encoded as a UTF\-16 +string, but \fBsgdisk\fR +supports only ASCII characters as names. For the most part, Linux ignores +the partition name, but it may be important in some OSes. GPT fdisk sets +a default name based on the partition type code. If you want to set a name +that includes a space, enclose it in quotation marks, as in +\fIsgdisk \-c 1:"Sample Name" /dev/sdb\fR. + +.TP +.B \-d, \-\-delete=partnum +Delete a partition. This action deletes the entry from the partition table +but does not disturb the data within the sectors originally allocated to +the partition on the disk. If a corresponding hybrid MBR partition exists, +\fBgdisk\fR deletes it, as well, and expands any adjacent 0xEE (EFI GPT) +MBR protective partition to fill the new free space. + +.TP +.B e, \-\-move\-second\-header +Move backup GPT data structures to the end of the disk. Use this option if +you've added disks to a RAID array, thus creating a virtual disk with space +that follows the backup GPT data structures. This command moves the backup +GPT data structures to the end of the disk, where they belong. + +.TP +.B E, \-\-end\-of\-largest +Displays the sector number of the end of the largest available block of +sectors on the disk. A script may store this value and pass it back as +part of \fI\-n\fR's option to create a partition. If no unallocated +sectors are available, this function returns the value 0. + +.TP +.B \-f, \-\-first\-in\-largest +Displays the sector number of the start of the largest available block of +sectors on the disk. A script may store this value and pass it back as +part of \fI\-n\fR's option to create a partition. If no unallocated +sectors are available, this function returns the value 0. + +.TP +.B \-g, \-\-mbrtogpt +Convert an MBR disk to a GPT disk. As a safety measure, use of this option +is required on MBR or BSD disklabel disks if you intend to save your changes, +in order to prevent accidentally damaging such disks. + +.TP +.B \-i, \-\-info=partnum +Show detailed partition information. The summary information produced by +the \fI\-p\fR command necessarily omits many details, such as the partition's +unique GUID and the translation of \fBsgdisk\fR's +internal partition type code to a plain type name. The \fI\-i\fR option +displays this information for a single partition. + +.TP +.B \-l, \-\-load\-backup=file +Load partition data from a backup file. This option is the reverse of the +\fI\-b\fR option. Note that restoring partition data from anything +but the original disk is not recommended. + +.TP +.B \-L, \-\-list\-types +Display a summary of partition types. GPT uses a GUID to identify +partition types for particular OSes and purposes. For ease of data entry, +\fBsgdisk\fR compresses these into two\-byte (four\-digit hexadecimal) +values that are related to their equivalent MBR codes. Specifically, the +MBR code is multiplied by hexadecimal 0x0100. For instance, the code for +Linux swap space in MBR is 0x82, and it's 0x8200 in \fBgdisk\fR. +A one\-to\-one correspondence is impossible, though. Most notably, many DOS, +Windows, and Linux data partition codes correspond to a single GPT code +(entered as 0x0700 in \fBsgdisk\fR). Some OSes use a single MBR code but +employ many more codes in GPT. For these, \fBsgdisk\fR +adds code numbers sequentially, such as 0xa500 for a FreeBSD disklabel, +0xa501 for FreeBSD boot, 0xa502 for FreeBSD swap, and so on. Note that +these two\-byte codes are unique to \fBgdisk\fR and \fBsgdisk\fR. This +option does not require you to specify a valid disk device filename. + +.TP +.B \-n, \-\-new=partnum:start:end +Create a new partition. You enter a partition +number, starting sector, and an ending sector. Both start and end sectors +can be specified in absolute terms as sector numbers or as positions +measured in kilobytes (K), megabytes (M), gigabytes (G), or terabytes (T); +for instance, \fI\fB40M\fR\fR specifies a position 40MiB from the start of +the disk. You can specify locations relative to the start or end of the +specified default range by preceding the number by a '+' or '\-' symbol, as +in \fI\fB+2G\fR\fR to specify a point 2GiB after the default start sector, +or \fI\fB\-200M\fR\fR to specify a point 200MiB before the last available +sector. Pressing the Enter key with no input specifies the default value, +which is the start of the largest available block for the start sector and +the end of the same block for the end sector. + +.TP +.B \-o, \-\-clear +Clear out all partition data. This includes GPT header data, +all partition definitions, and the protective MBR. + +.TP +.B \-p, \-\-print +Display basic partition summary data. This includes partition +numbers, starting and ending sector numbers, partition sizes, +\fBsgdisk\fR's partition types codes, and partition names. For +additional information, use the \fI\-i\fR (\fI\-\-info\fR) option. + +.TP +.B \-P, \-\-pretend +Pretend to make specified changes. In\-memory GPT data structures are +altered according to other parameters, but changes are not written +to disk. + +.TP +.B \-s, \-\-sort +Sort partition entries. GPT partition numbers need not match the order of +partitions on the disk. If you want them to match, you can use this option. +Note that some partitioning utilities sort partitions whenever they make +changes. Such changes will be reflected in your device filenames, so you +may need to edit \fI/etc/fstab\fR if you use this option. + +.TP +.B \-t, \-\-typecode=partnum:hexcode +Change a single partition's type code. You enter the type code using a +two\-byte hexadecimal number, as described earlier. + +.TP +.B \-T, \-\-transform\-bsd=partnum +Transform BSD partitions into GPT partitions. This option works on BSD +disklabels held within GPT (or converted MBR) partitions. Converted +partitions' type codes are likely to need manual adjustment. \fBsgdisk\fR +will attempt to convert BSD disklabels stored on the main disk when +launched, but this conversion is likely to produce first and/or last +partitions that are unusable. The many BSD variants means that the +probability of \fBsgdisk\fR being unable to convert a BSD disklabel is +high compared to the likelihood of problems with an MBR conversion. + +.TP +.B \-\-usage +Print a brief summary of available options. + +.TP +.B \-v, \-\-verify +Verify disk. This option checks for a variety of problems, such as +incorrect CRCs and mismatched main and backup data. This option does not +automatically correct most problems, though; for that, you must use +options on the recovery & transformation menu. If no problems are found, +this command displays a summary of unallocated disk space. + +.TP +.B \-V, \-\-version +Display program version information. This option may be used without +specifying a device filename. + +.TP +.B \-z, \-\-zap +Zap (destroy) the GPT data structures and exit. Use this option if you want to +repartition a GPT disk using \fBfdisk\fR or some other GPT\-unaware program. +You'll be given the choice of preserving the existing MBR, in case it's a +hybrid MBR with salvageable partitions or if you've already created new MBR +partitions and want to erase the remnants of your GPT partitions. \fIIf you've +already created new MBR partitions, it's conceivable that this option will +damage the first and/or last MBR partitions!\fR Such an event is unlikely, but +could occur if your new MBR partitions overlap the old GPT data structures. + +.TP +.B \-?, \-\-help +Print a summary of options. + +.SH "RETURN VALUES" +\fBsgdisk\fR returns various values depending on its success or failure: + +.TP +.B 0 +Normal program execution + +.TP +.B 1 +Too few arguments + +.TP +.B 4 +An error occurred while reading the partition table + +.TP +.B 3 +Non\-GPT disk detected and no \fI\-g\fR option + +.TP +.B 4 +An error prevented saving changes +.SH "BUGS" +As of January 2010 (version 0.6.0), \fBsgdisk\fR +should be considered beta software. Known bugs and limitations include: + +.TP +.B * +The program compiles correctly only on Linux, FreeBSD, and Mac OS X. Linux +versions for x86\-64 (64\-bit), x86 (32\-bit), and PowerPC (32\-bit) have been +tested, with the x86\-64 version having seen the most testing. + +.TP +.B * +The FreeBSD version of the program can't write changes to the partition +table to a disk when existing partitions on that disk are mounted. (The +same problem exists with many other FreeBSD utilities, such as +\fBgpt\fR, \fBfdisk\fR, and \fBdd\fR.) + +.TP +.B * +The fields used to display the start and end sector numbers for partitions +in the \fI\-p\fR option are 14 characters wide. This translates to a limitation +of about 45 PiB. On larger disks, the displayed columns will go out of +alignment. + +.TP +.B * +Only ASCII characters are supported in the partition name field. If an +existing partition uses non\-ASCII UTF\-16 characters, they're likely to be +corrupted in the 'i' and 'p' menu options' displays; however, they should be +preserved when loading and saving partitions. + +.TP +.B * +The program can load only up to 128 partitions (4 primary partitions and +124 logical partitions) when converting from MBR format. This limit can +be raised by changing the \fI#define MAX_MBR_PARTS\fR line in the +\fImbr.h\fR source code file and recompiling; however, such a change +will require using a larger\-than\-normal partition table. (The limit +of 128 partitions was chosen because that number equals the 128 partitions +supported by the most common partition table size.) + +.TP +.B * +Converting from MBR format sometimes fails because of insufficient space at +the start or (more commonly) the end of the disk. Resizing the partition +table (using the 's' option in the experts' menu) can sometimes overcome +this problem; however, in extreme cases it may be necessary to resize a +partition using GNU Parted or a similar tool prior to conversion with +\fBgdisk\fR. + +.TP +.B * +MBR conversions work only if the disk has correct LBA partition +descriptors. These descriptors should be present on any disk over 8 GiB in +size or on smaller disks partitioned with any but very ancient software. + +.TP +.B * +BSD disklabel support can create first and/or last partitions that overlap +with the GPT data structures. This can sometimes be compensated by +adjusting the partition table size, but in extreme cases the affected +partition(s) may need to be deleted. + +.TP +.B * +Because of the highly variable nature of BSD disklabel structures, +conversions from this form may be unreliable \-\- partitions may be dropped, +converted in a way that creates overlaps with other partitions, or +converted with incorrect start or end values. Use this feature with +caution! + +.TP +.B * +Booting after converting an MBR or BSD disklabel disk is likely to be +disrupted. Sometimes re\-installing a boot loader will fix the problem, but +other times you may need to switch boot loaders. Except on EFI\-based +platforms, Windows through at least Windows 7 RC doesn't support booting +from GPT disks. Creating a hybrid MBR (using the 'h' option on the recovery & +transformation menu) or abandoning GPT in favor of MBR may be your only +options in this case. + +.PP + +The support for big\-endian CPUs (PowerPC, for example) is new, as of version +0.3.5. I advise using caution on that platform, particularly with the more +obscure features of the program. + +.SH "AUTHORS" +Primary author: Roderick W. Smith (rodsmith@rodsbooks.com) + +Contributors: + +* Yves Blusseau (1otnwmz02@sneakemail.com) + +* David Hubbard (david.c.hubbard@gmail.com) + +.SH "SEE ALSO" +\fBcfdisk (8)\fR, +\fBfdisk (8)\fR, +\fBgdisk (8)\fR, +\fBmkfs (8)\fR, +\fBparted (8)\fR, +\fBsfdisk (8)\fR + +\fIhttp://en.wikipedia.org/wiki/GUID_Partition_Table\fR + +\fIhttp://developer.apple.com/technotes/tn2006/tn2166.html\fR + +\fIhttp://www.rodsbooks.com/gdisk/\fR + +.SH "AVAILABILITY" +The \fBsgdisk\fR command is part of the \fIGPT fdisk\fR package and is +available from Rod Smith. diff --git a/sgdisk.cc b/sgdisk.cc index 9479afb4fcd5c1a90e1fe70299cc23d874b52735..027e37f11d996304e054e00a4641996081473564 100644 --- a/sgdisk.cc +++ b/sgdisk.cc @@ -1,464 +1,290 @@ // sgdisk.cc -// Program modelled after Linux sfdisk, but it manipulates GPT partitions -// rather than MBR partitions. This is effectively a new user interface -// to my gdisk program. +// Command-line-based version of gdisk. This program is named after sfdisk, +// and it can serve a similar role (easily scripted, etc.), but it's used +// strictly via command-line arguments, and it doesn't bear much resemblance +// to sfdisk in actual use. // -// by Rod Smith, project began February 2009 +// by Rod Smith, project began February 2009; sgdisk begun January 2010. /* This program is copyright (c) 2009, 2010 by Roderick W. Smith. It is distributed under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ -//#include <iostream> #include <stdio.h> -#include <string.h> -#include <getopt.h> +#include <string> +#include <popt.h> +#include <errno.h> #include "mbr.h" #include "gpt.h" #include "support.h" +#include "parttypes.h" + +using namespace std; #define MAX_OPTIONS 50 -// Function prototypes.... -/* void MainMenu(char* filename, struct GPTData* theGPT); -void ShowCommands(void); -void ExpertsMenu(char* filename, struct GPTData* theGPT); -void ShowExpertCommands(void); -void RecoveryMenu(char* filename, struct GPTData* theGPT); -void ShowRecoveryCommands(void); */ +uint64_t GetInt(char* Info, int itemNum); +string GetString(char* Info, int itemNum); -enum Commands { NONE, LIST, VERIFY }; +int main(int argc, char *argv[]) { + GPTData theGPT; + int opt, i, numOptions = 0, saveData = 0, neverSaveData = 0; + int partNum = 0, deletePartNum = 0, infoPartNum = 0, bsdPartNum = 0, saveNonGPT = 1; + int alignment = 8, retval = 0, pretend = 0; + uint16_t hexCode; + uint32_t tableSize = 128; + uint64_t startSector, endSector; + char* device = NULL; + char *argument = NULL, *newPartInfo = NULL, *typeCode = NULL, *partName; + char *backupFile = NULL; + PartTypes typeHelper; -struct Options { - Commands theCommand; - char* theArgument; -}; // struct Options + poptContext poptCon; + struct poptOption theOptions[] = + { + {"set-alignment", 'a', POPT_ARG_INT, &alignment, 'a', "set sector alignment", "value"}, + {"backup", 'b', POPT_ARG_STRING, &backupFile, 'b', "backup GPT to file", "file"}, + {"change-name", 'c', POPT_ARG_STRING, &partName, 'c', "change partition's name", "partnum:name"}, + {"delete", 'd', POPT_ARG_INT, &deletePartNum, 'd', "delete a partition", "partnum"}, + {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second header to end of disk", ""}, + {"end-of-largest", 'E', POPT_ARG_NONE, NULL, 'E', "show end of largest free block", ""}, + {"first-in-largest", 'f', POPT_ARG_NONE, NULL, 'f', "show start of the largest free block", ""}, + {"mbrtogpt", 'g', POPT_ARG_NONE, NULL, 'g', "convert MBR to GPT", ""}, + {"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"}, + {"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"}, + {"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""}, + {"new", 'n', POPT_ARG_STRING, &newPartInfo, 'n', "create new partition", "partnum:start:end"}, + {"clear", 'o', POPT_ARG_NONE, NULL, 'o', "clear partition table", ""}, + {"print", 'p', POPT_ARG_NONE, NULL, 'p', "print partition table", ""}, + {"pretend", 'P', POPT_ARG_NONE, NULL, 'P', "make changes in memory, but don't write them", ""}, + {"sort", 's', POPT_ARG_NONE, NULL, 's', "sort partition table entries", ""}, + {"resize-table", 'S', POPT_ARG_INT, &tableSize, 'S', "resize partition table", "numparts"}, + {"typecode", 't', POPT_ARG_STRING, &typeCode, 't', "change partition type code", "partnum:hexcode"}, + {"transform-bsd", 'T', POPT_ARG_INT, &bsdPartNum, 'T', "transform BSD disklabel partition to GPT", "partnum"}, + {"verify", 'v', POPT_ARG_NONE, NULL, 'v', "check partition table integrity", ""}, + {"version", 'V', POPT_ARG_NONE, NULL, 'V', "display version information", ""}, + {"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT data structures", ""}, + POPT_AUTOHELP { NULL, 0, 0, NULL, 0 } + }; -int verbose_flag; + // Create popt context... + poptCon = poptGetContext(NULL, argc, (const char**) argv, theOptions, 0); -static struct option long_options[] = -{ - {"verify", no_argument, NULL, 'v'}, - {"list", no_argument, NULL, 'l'}, - {0, 0, NULL, 0} -}; + poptSetOtherOptionHelp(poptCon, " [OPTION...] <device>"); -int ParseOptions(int argc, char* argv[], Options* theOptions, char** device); + if (argc < 2) { + poptPrintUsage(poptCon, stderr, 0); + exit(1); + } -int main(int argc, char* argv[]) { - GPTData theGPT; - int doMore = 1, opt, i, numOptions = 0; - char* device = NULL; - Options theOptions[MAX_OPTIONS]; + // Do one loop through the options to find the device filename and deal + // with options that don't require a device filename.... + while ((opt = poptGetNextOpt(poptCon)) > 0) { + switch (opt) { + case 'L': + typeHelper.ShowTypes(); + break; + case 'P': + pretend = 1; + break; + case 'V': + printf("GPT fdisk (sgdisk) version 0.5.4-pre1\n\n"); + break; + default: + break; + } // switch + numOptions++; + } // while - printf("GPT fdisk (sgdisk) version 0.5.4-pre1\n\n"); - numOptions = ParseOptions(argc, argv, theOptions, &device); + // Assume first non-option argument is the device filename.... + device = (char*) poptGetArg(poptCon); + poptResetContext(poptCon); if (device != NULL) { + theGPT.JustLooking(); // reset as necessary + theGPT.BeQuiet(); // Tell called functions to be less verbose & interactive if (theGPT.LoadPartitions(device)) { - for (i = 0; i < numOptions; i++) { - switch (theOptions[i].theCommand) { - case LIST: - theGPT.JustLooking(); + if ((theGPT.WhichWasUsed() == use_mbr) || (theGPT.WhichWasUsed() == use_bsd)) + saveNonGPT = 0; // flag so we don't overwrite unless directed to do so + while ((opt = poptGetNextOpt(poptCon)) > 0) { + switch (opt) { + case 'a': + theGPT.SetAlignment(alignment); + break; + case 'b': + theGPT.SaveGPTBackup(backupFile); + free(backupFile); + break; + case 'c': + theGPT.JustLooking(0); + partNum = (int) GetInt(partName, 1) - 1; + if (theGPT.SetName(partNum, (char*) GetString(partName, 2).c_str())) { + saveData = 1; + } else { + fprintf(stderr, "Unable set set partition %d's name to '%s'!\n", + partNum + 1, GetString(partName, 2).c_str()); + neverSaveData = 1; + } // if/else + free(partName); + break; + case 'd': + theGPT.JustLooking(0); + if (theGPT.DeletePartition(deletePartNum - 1) == 0) { + fprintf(stderr, "Error %d deleting partition!\n", errno); + neverSaveData = 1; + } else saveData = 1; + break; + case 'e': + theGPT.JustLooking(0); + theGPT.MoveSecondHeaderToEnd(); + saveData = 1; + break; + case 'E': + printf("%llu\n", (unsigned long long) theGPT.FindLastAvailable(theGPT.FindFirstInLargest())); + break; + case 'f': + printf("%llu\n", (unsigned long long) theGPT.FindFirstInLargest()); + break; + case 'g': + theGPT.JustLooking(0); + saveData = 1; + saveNonGPT = 1; + break; + case 'i': + theGPT.ShowPartDetails(infoPartNum - 1); + break; + case 'l': + if (theGPT.LoadGPTBackup(backupFile) == 1) + saveData = 1; + else { + saveData = 0; + neverSaveData = 1; + fprintf(stderr, "Error loading backup file!\n"); + } // else + free(backupFile); + break; + case 'L': + break; + case 'n': + theGPT.JustLooking(0); + partNum = (int) GetInt(newPartInfo, 1) - 1; + startSector = GetInt(newPartInfo, 2); + endSector = GetInt(newPartInfo, 3); + if (theGPT.CreatePartition(partNum, startSector, endSector)) { + saveData = 1; + } else { + fprintf(stderr, "Could not create partition %d from %llu to %llu!\n", + partNum, startSector, endSector); + neverSaveData = 1; + } // if/else + free(newPartInfo); + break; + case 'o': + theGPT.JustLooking(0); + theGPT.ClearGPTData(); + saveData = 1; + break; + case 'p': theGPT.DisplayGPTData(); break; - case VERIFY: - theGPT.JustLooking(); + case 'P': + pretend = 1; + break; + case 's': + theGPT.JustLooking(0); + theGPT.SortGPT(); + saveData = 1; + break; + case 'S': + theGPT.JustLooking(0); + if (theGPT.SetGPTSize(tableSize) == 0) + neverSaveData = 1; + else + saveData = 1; + break; + case 't': + theGPT.JustLooking(0); + partNum = (int) GetInt(typeCode, 1) - 1; + sscanf(GetString(typeCode, 2).c_str(), "%x", &hexCode); + if (theGPT.ChangePartType(partNum, hexCode)) { + saveData = 1; + } else { + fprintf(stderr, "Could not change partition %d's type code to %x!\n", + partNum + 1, hexCode); + neverSaveData = 1; + } // if/else + free(typeCode); + break; + case 'T': + theGPT.JustLooking(0); + theGPT.XFormDisklabel(bsdPartNum); + saveData = 1; + break; + case 'v': theGPT.Verify(); break; - case NONE: - printf("Usage: %s {-lv} device\n", argv[0]); + case 'z': + if (!pretend) + theGPT.DestroyGPT(-1); + saveNonGPT = 0; + break; + default: + printf("Unknown option (-%c)!\n", opt); break; } // switch - } // for - } // if loaded OK + } // while + if ((saveData) && (!neverSaveData) && (saveNonGPT) && (!pretend)) + theGPT.SaveGPTData(1); + if (saveData && (!saveNonGPT)) { + printf("Non-GPT disk; not saving changes. Use -g to override.\n"); + retval = 3; + } // if + if (neverSaveData) { + printf("Error encountered; not saving changes.\n"); + retval = 4; + } // if + } else { // if loaded OK + retval = 2; + } // if/else loaded OK } // if (device != NULL) + poptFreeContext(poptCon); - return 0; + return retval; } // main -// Parse command-line options. Returns the number of arguments retrieved -int ParseOptions(int argc, char* argv[], Options* theOptions, char** device) { - int opt, i, numOptions = 0; - int verbose_flag; - - // Use getopt() to extract commands and their arguments - /* getopt_long stores the option index here. */ - int option_index = 0; - -// c = getopt_long (argc, argv, "abc:d:f:", -// long_options, &option_index); - - while (((opt = getopt_long(argc, argv, "vl", long_options, &option_index)) != -1) - && (numOptions < MAX_OPTIONS)) { - printf("opt is %c, option_index is %d\n", opt, option_index); - switch (opt) { - case 'l': - printf("Entering list option, numOptions = %d!\n", numOptions); - theOptions[numOptions].theCommand = LIST; - theOptions[numOptions++].theArgument = NULL; - break; - case 'v': - theOptions[numOptions].theCommand = VERIFY; - theOptions[numOptions++].theArgument = NULL; - break; - default: - printf("Default switch; opt is %c\n", opt); - break; -// abort(); - } // switch - } // while - - // Find non-option arguments. If the user types a legal command, there - // will be only one of these: The device filename.... - opt = 0; - printf("Searching for device filename; optind is %d\n", optind); - for (i = optind; i < argc; i++) { - *device = argv[i]; - printf("Setting device to %s\n", argv[i]); - opt++; - } // for - if (opt > 1) { - fprintf(stderr, "Warning! Found stray unrecognized arguments! Program may misbehave!\n"); - } // if - - return numOptions; -} // ParseOptions() - -/* // Accept a command and execute it. Returns only when the user -// wants to exit (such as after a 'w' or 'q' command). -void MainMenu(char* filename, struct GPTData* theGPT) { - char command, line[255], buFile[255]; - char* junk; - int goOn = 1; - PartTypes typeHelper; - uint32_t temp1, temp2; - - do { - printf("\nCommand (? for help): "); - junk = fgets(line, 255, stdin); - sscanf(line, "%c", &command); - switch (command) { - case 'b': case 'B': - printf("Enter backup filename to save: "); - junk = fgets(line, 255, stdin); - sscanf(line, "%s", (char*) &buFile); - theGPT->SaveGPTBackup(buFile); - 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': - printf("This option deletes all partitions and creates a new " - "protective MBR.\nProceed? "); - if (GetYN() == 'Y') { - theGPT->ClearGPTData(); - theGPT->MakeProtectiveMBR(); - } // if - break; - case 'p': case 'P': - theGPT->DisplayGPTData(); - break; - case 'q': case 'Q': - goOn = 0; - break; - case 'r': case 'R': - RecoveryMenu(filename, theGPT); - goOn = 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) - goOn = 0; - break; - case 'x': case 'X': - ExpertsMenu(filename, theGPT); - goOn = 0; - break; - default: - ShowCommands(); - break; - } // switch - } while (goOn); -} // MainMenu() - -void ShowCommands(void) { - printf("b\tback up GPT data to a file\n"); - printf("c\tchange a partition's name\n"); - printf("d\tdelete a partition\n"); - printf("i\tshow detailed information on a partition\n"); - printf("l\tlist known partition types\n"); - printf("n\tadd a new partition\n"); - printf("o\tcreate a new empty GUID partition table (GPT)\n"); - printf("p\tprint the partition table\n"); - printf("q\tquit without saving changes\n"); - printf("r\trecovery and transformation options (experts only)\n"); - printf("s\tsort partitions\n"); - printf("t\tchange a partition's type code\n"); - printf("v\tverify disk\n"); - printf("w\twrite table to disk and exit\n"); - printf("x\textra functionality (experts only)\n"); - printf("?\tprint this menu\n"); -} // ShowCommands() +// Extract integer data from argument string, which should be colon-delimited +uint64_t GetInt(char* argument, int itemNum) { + int startPos = -1, endPos = -1; + uint64_t retval = 0; + string Info; -// Accept a recovery & transformation menu command. Returns only when the user -// issues an exit command, such as 'w' or 'q'. -void RecoveryMenu(char* filename, struct GPTData* theGPT) { - char command, line[255], buFile[255]; - char* junk; - PartTypes typeHelper; - uint32_t temp1; - int goOn = 1; + Info = argument; + while (itemNum-- > 0) { + startPos = endPos + 1; + endPos = Info.find(':', startPos); + } + if (endPos == string::npos) + endPos = Info.length(); + endPos--; - do { - printf("\nrecovery/transformation command (? for help): "); - junk = fgets(line, 255, stdin); - sscanf(line, "%c", &command); - switch (command) { - 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': - printf("Warning! This will destroy the currently defined partitions! Proceed? "); - if (GetYN() == 'Y') { - if (theGPT->LoadMBR(filename) == 1) { // successful load - theGPT->XFormPartitions(); - } else { - printf("Problem loading MBR! GPT is untouched; regenerating protective MBR!\n"); - theGPT->MakeProtectiveMBR(); - } // if/else - } // if - break; - case 'g': case 'G': - temp1 = theGPT->XFormToMBR(); - if (temp1 > 0) { - printf("Converted %d partitions. Finalize and exit? ", temp1); - if (GetYN() == 'Y') { - if (theGPT->DestroyGPT(0) > 0) - goOn = 0; - } else { - theGPT->MakeProtectiveMBR(); - printf("Note: New protective MBR created.\n"); - } // if/else - } // if - break; - case 'h': case 'H': - theGPT->MakeHybrid(); - break; - case 'i': case 'I': - theGPT->ShowDetails(); - break; - case 'l': case 'L': - printf("Enter backup filename to load: "); - junk = fgets(line, 255, stdin); - sscanf(line, "%s", (char*) &buFile); - theGPT->LoadGPTBackup(buFile); - break; - case 'm': case 'M': - MainMenu(filename, theGPT); - goOn = 0; - break; - case 'o': case 'O': - theGPT->DisplayMBRData(); - break; - case 'p': case 'P': - theGPT->DisplayGPTData(); - break; - case 'q': case 'Q': - goOn = 0; - break; - case 't': case 'T': - theGPT->XFormDisklabel(); - break; - case 'v': case 'V': - theGPT->Verify(); - break; - case 'w': case 'W': - if (theGPT->SaveGPTData() == 1) { - goOn = 0; - } // if - break; - case 'x': case 'X': - ExpertsMenu(filename, theGPT); - goOn = 0; - break; - default: - ShowRecoveryCommands(); - break; - } // switch - } while (goOn); -} // RecoveryMenu() + sscanf(Info.substr(startPos, endPos - startPos + 1).c_str(), "%llu", &retval); +/* printf("In GetInt(), startPos = %d, endPos = %d, retval = %llu\n", startPos, + endPos, (unsigned long long) retval); */ + return retval; +} // GetInt() -void ShowRecoveryCommands(void) { - printf("b\tuse backup GPT header (rebuilding main)\n"); - printf("c\tload backup partition table from disk (rebuilding main)\n"); - printf("d\tuse main GPT header (rebuilding backup)\n"); - printf("e\tload main partition table from disk (rebuilding backup)\n"); - printf("f\tload MBR and build fresh GPT from it\n"); - printf("g\tconvert GPT into MBR and exit\n"); - printf("h\tmake hybrid MBR\n"); - printf("i\tshow detailed information on a partition\n"); - printf("l\tload partition data from a backup file\n"); - printf("m\treturn to main menu\n"); - printf("o\tprint protective MBR data\n"); - printf("p\tprint the partition table\n"); - printf("q\tquit without saving changes\n"); - printf("t\ttransform BSD disklabel partition\n"); - printf("v\tverify disk\n"); - printf("w\twrite table to disk and exit\n"); - printf("x\textra functionality (experts only)\n"); - printf("?\tprint this menu\n"); -} // ShowRecoveryCommands() +// Extract string data from argument string, which should be colon-delimited +string GetString(char* argument, int itemNum) { + int startPos = -1, endPos = -1; + string Info; -// Accept an experts' menu command. Returns only after the user -// selects an exit command, such as 'w' or 'q'. -void ExpertsMenu(char* filename, struct GPTData* theGPT) { - char command, line[255]; - char* junk; - PartTypes typeHelper; - uint32_t pn; - uint32_t temp1, temp2; - int goOn = 1; - - do { - printf("\nExpert command (? for help): "); - junk = fgets(line, 255, stdin); - sscanf(line, "%c", &command); - switch (command) { - case 'a': case 'A': - if (theGPT->GetPartRange(&temp1, &temp2) > 0) - theGPT->SetAttributes(theGPT->GetPartNum()); - else - printf("No partitions\n"); - break; - case 'c': case 'C': - if (theGPT->GetPartRange(&temp1, &temp2) > 0) { - pn = theGPT->GetPartNum(); - printf("Enter the partition's new unique GUID:\n"); - theGPT->SetPartitionGUID(pn, GetGUID()); - } else printf("No partitions\n"); - break; - case 'd': case 'D': - printf("The number of logical sectors per physical sector is %d.\n", - theGPT->GetAlignment()); - break; - case 'e': case 'E': - printf("Relocating backup data structures to the end of the disk\n"); - theGPT->MoveSecondHeaderToEnd(); - break; - case 'g': case 'G': - printf("Enter the disk's unique GUID:\n"); - theGPT->SetDiskGUID(GetGUID()); - break; - case 'i': case 'I': - theGPT->ShowDetails(); - break; - case 'l': case 'L': - temp1 = GetNumber(1, 128, 8, "Enter the number of logical sectors in a physical sector on the\ndisk (1-128, default = 8): "); - theGPT->SetAlignment(temp1); - break; - case 'm': case 'M': - MainMenu(filename, theGPT); - goOn = 0; - 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': - goOn = 0; - break; - case 'r': case 'R': - RecoveryMenu(filename, theGPT); - 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) { - goOn = 0; - } // if - break; - case 'z': case 'Z': - if (theGPT->DestroyGPT() == 1) { - goOn = 0; - } - break; - default: - ShowExpertCommands(); - break; - } // switch - } while (goOn); -} // ExpertsMenu() + Info = argument; + while (itemNum-- > 0) { + startPos = endPos + 1; + endPos = Info.find(':', startPos); + } + if (endPos == string::npos) + endPos = Info.length(); + endPos--; -void ShowExpertCommands(void) { - printf("a\tset attributes\n"); - printf("c\tchange partition GUID\n"); - printf("d\tdisplay the number of logical sectors per physical sector\n"); - printf("e\trelocate backup data structures to the end of the disk\n"); - printf("g\tchange disk GUID\n"); - printf("i\tshow detailed information on a partition\n"); - printf("b\tset the number of logical sectors per physical sector\n"); - printf("m\treturn to main menu\n"); - printf("n\tcreate a new protective MBR\n"); - printf("o\tprint protective MBR data\n"); - printf("p\tprint the partition table\n"); - printf("q\tquit without saving changes\n"); - printf("r\trecovery and transformation options (experts only)\n"); - printf("s\tresize partition table\n"); - printf("v\tverify disk\n"); - printf("w\twrite table to disk and exit\n"); - printf("z\tzap (destroy) GPT data structures and exit\n"); - printf("?\tprint this menu\n"); -} // ShowExpertCommands() -*/ + return Info.substr(startPos, endPos - startPos + 1); +} // GetString()