srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 1 | // sgdisk.cc |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 2 | // Command-line-based version of gdisk. This program is named after sfdisk, |
| 3 | // and it can serve a similar role (easily scripted, etc.), but it's used |
| 4 | // strictly via command-line arguments, and it doesn't bear much resemblance |
| 5 | // to sfdisk in actual use. |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 6 | // |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 7 | // by Rod Smith, project began February 2009; sgdisk begun January 2010. |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 8 | |
| 9 | /* This program is copyright (c) 2009, 2010 by Roderick W. Smith. It is distributed |
| 10 | under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ |
| 11 | |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 12 | #include <stdio.h> |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 13 | #include <string> |
| 14 | #include <popt.h> |
| 15 | #include <errno.h> |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 16 | #include "mbr.h" |
| 17 | #include "gpt.h" |
| 18 | #include "support.h" |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 19 | #include "parttypes.h" |
| 20 | |
| 21 | using namespace std; |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 22 | |
| 23 | #define MAX_OPTIONS 50 |
| 24 | |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 25 | uint64_t GetInt(char* Info, int itemNum); |
| 26 | string GetString(char* Info, int itemNum); |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 27 | |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 28 | int main(int argc, char *argv[]) { |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 29 | GPTData theGPT; |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 30 | int opt, i, numOptions = 0, saveData = 0, neverSaveData = 0; |
| 31 | int partNum = 0, deletePartNum = 0, infoPartNum = 0, bsdPartNum = 0, saveNonGPT = 1; |
| 32 | int alignment = 8, retval = 0, pretend = 0; |
| 33 | uint16_t hexCode; |
| 34 | uint32_t tableSize = 128; |
| 35 | uint64_t startSector, endSector; |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 36 | char* device = NULL; |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 37 | char *argument = NULL, *newPartInfo = NULL, *typeCode = NULL, *partName; |
| 38 | char *backupFile = NULL; |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 39 | PartTypes typeHelper; |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 40 | |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 41 | poptContext poptCon; |
| 42 | struct poptOption theOptions[] = |
| 43 | { |
| 44 | {"set-alignment", 'a', POPT_ARG_INT, &alignment, 'a', "set sector alignment", "value"}, |
| 45 | {"backup", 'b', POPT_ARG_STRING, &backupFile, 'b', "backup GPT to file", "file"}, |
| 46 | {"change-name", 'c', POPT_ARG_STRING, &partName, 'c', "change partition's name", "partnum:name"}, |
| 47 | {"delete", 'd', POPT_ARG_INT, &deletePartNum, 'd', "delete a partition", "partnum"}, |
| 48 | {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second header to end of disk", ""}, |
| 49 | {"end-of-largest", 'E', POPT_ARG_NONE, NULL, 'E', "show end of largest free block", ""}, |
| 50 | {"first-in-largest", 'f', POPT_ARG_NONE, NULL, 'f', "show start of the largest free block", ""}, |
| 51 | {"mbrtogpt", 'g', POPT_ARG_NONE, NULL, 'g', "convert MBR to GPT", ""}, |
| 52 | {"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"}, |
| 53 | {"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"}, |
| 54 | {"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""}, |
| 55 | {"new", 'n', POPT_ARG_STRING, &newPartInfo, 'n', "create new partition", "partnum:start:end"}, |
| 56 | {"clear", 'o', POPT_ARG_NONE, NULL, 'o', "clear partition table", ""}, |
| 57 | {"print", 'p', POPT_ARG_NONE, NULL, 'p', "print partition table", ""}, |
| 58 | {"pretend", 'P', POPT_ARG_NONE, NULL, 'P', "make changes in memory, but don't write them", ""}, |
| 59 | {"sort", 's', POPT_ARG_NONE, NULL, 's', "sort partition table entries", ""}, |
| 60 | {"resize-table", 'S', POPT_ARG_INT, &tableSize, 'S', "resize partition table", "numparts"}, |
| 61 | {"typecode", 't', POPT_ARG_STRING, &typeCode, 't', "change partition type code", "partnum:hexcode"}, |
| 62 | {"transform-bsd", 'T', POPT_ARG_INT, &bsdPartNum, 'T', "transform BSD disklabel partition to GPT", "partnum"}, |
| 63 | {"verify", 'v', POPT_ARG_NONE, NULL, 'v', "check partition table integrity", ""}, |
| 64 | {"version", 'V', POPT_ARG_NONE, NULL, 'V', "display version information", ""}, |
| 65 | {"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT data structures", ""}, |
| 66 | POPT_AUTOHELP { NULL, 0, 0, NULL, 0 } |
| 67 | }; |
| 68 | |
| 69 | // Create popt context... |
| 70 | poptCon = poptGetContext(NULL, argc, (const char**) argv, theOptions, 0); |
| 71 | |
| 72 | poptSetOtherOptionHelp(poptCon, " [OPTION...] <device>"); |
| 73 | |
| 74 | if (argc < 2) { |
| 75 | poptPrintUsage(poptCon, stderr, 0); |
| 76 | exit(1); |
| 77 | } |
| 78 | |
| 79 | // Do one loop through the options to find the device filename and deal |
| 80 | // with options that don't require a device filename.... |
| 81 | while ((opt = poptGetNextOpt(poptCon)) > 0) { |
| 82 | switch (opt) { |
| 83 | case 'L': |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 84 | typeHelper.ShowTypes(); |
| 85 | break; |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 86 | case 'P': |
| 87 | pretend = 1; |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 88 | break; |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 89 | case 'V': |
| 90 | printf("GPT fdisk (sgdisk) version 0.5.4-pre1\n\n"); |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 91 | break; |
| 92 | default: |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 93 | break; |
| 94 | } // switch |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 95 | numOptions++; |
| 96 | } // while |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 97 | |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 98 | // Assume first non-option argument is the device filename.... |
| 99 | device = (char*) poptGetArg(poptCon); |
| 100 | poptResetContext(poptCon); |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 101 | |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 102 | if (device != NULL) { |
| 103 | theGPT.JustLooking(); // reset as necessary |
| 104 | theGPT.BeQuiet(); // Tell called functions to be less verbose & interactive |
| 105 | if (theGPT.LoadPartitions(device)) { |
| 106 | if ((theGPT.WhichWasUsed() == use_mbr) || (theGPT.WhichWasUsed() == use_bsd)) |
| 107 | saveNonGPT = 0; // flag so we don't overwrite unless directed to do so |
| 108 | while ((opt = poptGetNextOpt(poptCon)) > 0) { |
| 109 | switch (opt) { |
| 110 | case 'a': |
| 111 | theGPT.SetAlignment(alignment); |
| 112 | break; |
| 113 | case 'b': |
| 114 | theGPT.SaveGPTBackup(backupFile); |
| 115 | free(backupFile); |
| 116 | break; |
| 117 | case 'c': |
| 118 | theGPT.JustLooking(0); |
| 119 | partNum = (int) GetInt(partName, 1) - 1; |
| 120 | if (theGPT.SetName(partNum, (char*) GetString(partName, 2).c_str())) { |
| 121 | saveData = 1; |
| 122 | } else { |
| 123 | fprintf(stderr, "Unable set set partition %d's name to '%s'!\n", |
| 124 | partNum + 1, GetString(partName, 2).c_str()); |
| 125 | neverSaveData = 1; |
| 126 | } // if/else |
| 127 | free(partName); |
| 128 | break; |
| 129 | case 'd': |
| 130 | theGPT.JustLooking(0); |
| 131 | if (theGPT.DeletePartition(deletePartNum - 1) == 0) { |
| 132 | fprintf(stderr, "Error %d deleting partition!\n", errno); |
| 133 | neverSaveData = 1; |
| 134 | } else saveData = 1; |
| 135 | break; |
| 136 | case 'e': |
| 137 | theGPT.JustLooking(0); |
| 138 | theGPT.MoveSecondHeaderToEnd(); |
| 139 | saveData = 1; |
| 140 | break; |
| 141 | case 'E': |
| 142 | printf("%llu\n", (unsigned long long) theGPT.FindLastAvailable(theGPT.FindFirstInLargest())); |
| 143 | break; |
| 144 | case 'f': |
| 145 | printf("%llu\n", (unsigned long long) theGPT.FindFirstInLargest()); |
| 146 | break; |
| 147 | case 'g': |
| 148 | theGPT.JustLooking(0); |
| 149 | saveData = 1; |
| 150 | saveNonGPT = 1; |
| 151 | break; |
| 152 | case 'i': |
| 153 | theGPT.ShowPartDetails(infoPartNum - 1); |
| 154 | break; |
| 155 | case 'l': |
| 156 | if (theGPT.LoadGPTBackup(backupFile) == 1) |
| 157 | saveData = 1; |
| 158 | else { |
| 159 | saveData = 0; |
| 160 | neverSaveData = 1; |
| 161 | fprintf(stderr, "Error loading backup file!\n"); |
| 162 | } // else |
| 163 | free(backupFile); |
| 164 | break; |
| 165 | case 'L': |
| 166 | break; |
| 167 | case 'n': |
| 168 | theGPT.JustLooking(0); |
| 169 | partNum = (int) GetInt(newPartInfo, 1) - 1; |
| 170 | startSector = GetInt(newPartInfo, 2); |
| 171 | endSector = GetInt(newPartInfo, 3); |
| 172 | if (theGPT.CreatePartition(partNum, startSector, endSector)) { |
| 173 | saveData = 1; |
| 174 | } else { |
| 175 | fprintf(stderr, "Could not create partition %d from %llu to %llu!\n", |
| 176 | partNum, startSector, endSector); |
| 177 | neverSaveData = 1; |
| 178 | } // if/else |
| 179 | free(newPartInfo); |
| 180 | break; |
| 181 | case 'o': |
| 182 | theGPT.JustLooking(0); |
| 183 | theGPT.ClearGPTData(); |
| 184 | saveData = 1; |
| 185 | break; |
| 186 | case 'p': |
| 187 | theGPT.DisplayGPTData(); |
| 188 | break; |
| 189 | case 'P': |
| 190 | pretend = 1; |
| 191 | break; |
| 192 | case 's': |
| 193 | theGPT.JustLooking(0); |
| 194 | theGPT.SortGPT(); |
| 195 | saveData = 1; |
| 196 | break; |
| 197 | case 'S': |
| 198 | theGPT.JustLooking(0); |
| 199 | if (theGPT.SetGPTSize(tableSize) == 0) |
| 200 | neverSaveData = 1; |
| 201 | else |
| 202 | saveData = 1; |
| 203 | break; |
| 204 | case 't': |
| 205 | theGPT.JustLooking(0); |
| 206 | partNum = (int) GetInt(typeCode, 1) - 1; |
| 207 | sscanf(GetString(typeCode, 2).c_str(), "%x", &hexCode); |
| 208 | if (theGPT.ChangePartType(partNum, hexCode)) { |
| 209 | saveData = 1; |
| 210 | } else { |
| 211 | fprintf(stderr, "Could not change partition %d's type code to %x!\n", |
| 212 | partNum + 1, hexCode); |
| 213 | neverSaveData = 1; |
| 214 | } // if/else |
| 215 | free(typeCode); |
| 216 | break; |
| 217 | case 'T': |
| 218 | theGPT.JustLooking(0); |
| 219 | theGPT.XFormDisklabel(bsdPartNum); |
| 220 | saveData = 1; |
| 221 | break; |
| 222 | case 'v': |
| 223 | theGPT.Verify(); |
| 224 | break; |
| 225 | case 'z': |
| 226 | if (!pretend) |
| 227 | theGPT.DestroyGPT(-1); |
| 228 | saveNonGPT = 0; |
| 229 | break; |
| 230 | default: |
| 231 | printf("Unknown option (-%c)!\n", opt); |
| 232 | break; |
| 233 | } // switch |
| 234 | } // while |
| 235 | if ((saveData) && (!neverSaveData) && (saveNonGPT) && (!pretend)) |
| 236 | theGPT.SaveGPTData(1); |
| 237 | if (saveData && (!saveNonGPT)) { |
| 238 | printf("Non-GPT disk; not saving changes. Use -g to override.\n"); |
| 239 | retval = 3; |
| 240 | } // if |
| 241 | if (neverSaveData) { |
| 242 | printf("Error encountered; not saving changes.\n"); |
| 243 | retval = 4; |
| 244 | } // if |
| 245 | } else { // if loaded OK |
| 246 | retval = 2; |
| 247 | } // if/else loaded OK |
| 248 | } // if (device != NULL) |
| 249 | poptFreeContext(poptCon); |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 250 | |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 251 | return retval; |
| 252 | } // main |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 253 | |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 254 | // Extract integer data from argument string, which should be colon-delimited |
| 255 | uint64_t GetInt(char* argument, int itemNum) { |
| 256 | int startPos = -1, endPos = -1; |
| 257 | uint64_t retval = 0; |
| 258 | string Info; |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 259 | |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 260 | Info = argument; |
| 261 | while (itemNum-- > 0) { |
| 262 | startPos = endPos + 1; |
| 263 | endPos = Info.find(':', startPos); |
| 264 | } |
| 265 | if (endPos == string::npos) |
| 266 | endPos = Info.length(); |
| 267 | endPos--; |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 268 | |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 269 | sscanf(Info.substr(startPos, endPos - startPos + 1).c_str(), "%llu", &retval); |
| 270 | /* printf("In GetInt(), startPos = %d, endPos = %d, retval = %llu\n", startPos, |
| 271 | endPos, (unsigned long long) retval); */ |
| 272 | return retval; |
| 273 | } // GetInt() |
srs5694 | 1e09372 | 2010-01-05 00:14:19 -0500 | [diff] [blame] | 274 | |
srs5694 | 73ba479 | 2010-01-12 18:18:17 -0500 | [diff] [blame^] | 275 | // Extract string data from argument string, which should be colon-delimited |
| 276 | string GetString(char* argument, int itemNum) { |
| 277 | int startPos = -1, endPos = -1; |
| 278 | string Info; |
| 279 | |
| 280 | Info = argument; |
| 281 | while (itemNum-- > 0) { |
| 282 | startPos = endPos + 1; |
| 283 | endPos = Info.find(':', startPos); |
| 284 | } |
| 285 | if (endPos == string::npos) |
| 286 | endPos = Info.length(); |
| 287 | endPos--; |
| 288 | |
| 289 | return Info.substr(startPos, endPos - startPos + 1); |
| 290 | } // GetString() |