1 /* 2 Implementation of GPTData class derivative with popt-based command 3 line processing 4 Copyright (C) 2010-2014 Roderick W. Smith 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License along 17 with this program; if not, write to the Free Software Foundation, Inc., 18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 */ 20 21 #include <string.h> 22 #include <string> 23 #include <iostream> 24 #include <sstream> 25 #include <errno.h> 26 #include "gptcl.h" 27 28 GPTDataCL::GPTDataCL(void) { 29 attributeOperation = backupFile = partName = hybrids = newPartInfo = NULL; 30 mbrParts = twoParts = outDevice = typeCode = partGUID = diskGUID = NULL; 31 alignment = DEFAULT_ALIGNMENT; 32 deletePartNum = infoPartNum = largestPartNum = bsdPartNum = 0; 33 tableSize = GPT_SIZE; 34 } // GPTDataCL constructor 35 36 GPTDataCL::GPTDataCL(string filename) { 37 } // GPTDataCL constructor with filename 38 39 GPTDataCL::~GPTDataCL(void) { 40 } // GPTDataCL destructor 41 42 void GPTDataCL::LoadBackupFile(string backupFile, int &saveData, int &neverSaveData) { 43 if (LoadGPTBackup(backupFile) == 1) { 44 JustLooking(0); 45 saveData = 1; 46 } else { 47 saveData = 0; 48 neverSaveData = 1; 49 cerr << "Error loading backup file!\n"; 50 } // else 51 } // GPTDataCL::LoadBackupFile() 52 53 // Perform the actions specified on the command line. This is necessarily one 54 // monster of a function! 55 // Returns values: 56 // 0 = success 57 // 1 = too few arguments 58 // 2 = error when reading partition table 59 // 3 = non-GPT disk and no -g option 60 // 4 = unable to save changes 61 // 8 = disk replication operation (-R) failed 62 int GPTDataCL::DoOptions(int argc, char* argv[]) { 63 GPTData secondDevice; 64 int opt, numOptions = 0, saveData = 0, neverSaveData = 0; 65 int partNum = 0, newPartNum = -1, saveNonGPT = 1, retval = 0, pretend = 0; 66 uint64_t low, high, startSector, endSector, sSize; 67 uint64_t temp; // temporary variable; free to use in any case 68 char *device; 69 string cmd, typeGUID, name; 70 PartType typeHelper; 71 72 struct poptOption theOptions[] = 73 { 74 {"attributes", 'A', POPT_ARG_STRING, &attributeOperation, 'A', "operate on partition attributes", "list|[partnum:show|or|nand|xor|=|set|clear|toggle|get[:bitnum|hexbitmask]]"}, 75 {"set-alignment", 'a', POPT_ARG_INT, &alignment, 'a', "set sector alignment", "value"}, 76 {"backup", 'b', POPT_ARG_STRING, &backupFile, 'b', "backup GPT to file", "file"}, 77 {"change-name", 'c', POPT_ARG_STRING, &partName, 'c', "change partition's name", "partnum:name"}, 78 {"recompute-chs", 'C', POPT_ARG_NONE, NULL, 'C', "recompute CHS values in protective/hybrid MBR", ""}, 79 {"delete", 'd', POPT_ARG_INT, &deletePartNum, 'd', "delete a partition", "partnum"}, 80 {"display-alignment", 'D', POPT_ARG_NONE, NULL, 'D', "show number of sectors per allocation block", ""}, 81 {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second header to end of disk", ""}, 82 {"end-of-largest", 'E', POPT_ARG_NONE, NULL, 'E', "show end of largest free block", ""}, 83 {"first-in-largest", 'f', POPT_ARG_NONE, NULL, 'f', "show start of the largest free block", ""}, 84 {"first-aligned-in-largest", 'F', POPT_ARG_NONE, NULL, 'F', "show start of the largest free block, aligned", ""}, 85 {"mbrtogpt", 'g', POPT_ARG_NONE, NULL, 'g', "convert MBR to GPT", ""}, 86 {"randomize-guids", 'G', POPT_ARG_NONE, NULL, 'G', "randomize disk and partition GUIDs", ""}, 87 {"hybrid", 'h', POPT_ARG_STRING, &hybrids, 'h', "create hybrid MBR", "partnum[:partnum...]"}, 88 {"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"}, 89 {"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"}, 90 {"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""}, 91 {"gpttombr", 'm', POPT_ARG_STRING, &mbrParts, 'm', "convert GPT to MBR", "partnum[:partnum...]"}, 92 {"new", 'n', POPT_ARG_STRING, &newPartInfo, 'n', "create new partition", "partnum:start:end"}, 93 {"largest-new", 'N', POPT_ARG_INT, &largestPartNum, 'N', "create largest possible new partition", "partnum"}, 94 {"clear", 'o', POPT_ARG_NONE, NULL, 'o', "clear partition table", ""}, 95 {"print", 'p', POPT_ARG_NONE, NULL, 'p', "print partition table", ""}, 96 {"pretend", 'P', POPT_ARG_NONE, NULL, 'P', "make changes in memory, but don't write them", ""}, 97 {"transpose", 'r', POPT_ARG_STRING, &twoParts, 'r', "transpose two partitions", "partnum:partnum"}, 98 {"replicate", 'R', POPT_ARG_STRING, &outDevice, 'R', "replicate partition table", "device_filename"}, 99 {"sort", 's', POPT_ARG_NONE, NULL, 's', "sort partition table entries", ""}, 100 {"resize-table", 'S', POPT_ARG_INT, &tableSize, 'S', "resize partition table", "numparts"}, 101 {"typecode", 't', POPT_ARG_STRING, &typeCode, 't', "change partition type code", "partnum:{hexcode|GUID}"}, 102 {"transform-bsd", 'T', POPT_ARG_INT, &bsdPartNum, 'T', "transform BSD disklabel partition to GPT", "partnum"}, 103 {"partition-guid", 'u', POPT_ARG_STRING, &partGUID, 'u', "set partition GUID", "partnum:guid"}, 104 {"disk-guid", 'U', POPT_ARG_STRING, &diskGUID, 'U', "set disk GUID", "guid"}, 105 {"verify", 'v', POPT_ARG_NONE, NULL, 'v', "check partition table integrity", ""}, 106 {"version", 'V', POPT_ARG_NONE, NULL, 'V', "display version information", ""}, 107 {"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT (but not MBR) data structures", ""}, 108 {"zap-all", 'Z', POPT_ARG_NONE, NULL, 'Z', "zap (destroy) GPT and MBR data structures", ""}, 109 POPT_AUTOHELP { NULL, 0, 0, NULL, 0 } 110 }; 111 112 // Create popt context... 113 poptCon = poptGetContext(NULL, argc, (const char**) argv, theOptions, 0); 114 115 poptSetOtherOptionHelp(poptCon, " [OPTION...] <device>"); 116 117 if (argc < 2) { 118 poptPrintUsage(poptCon, stderr, 0); 119 return 1; 120 } 121 122 // Do one loop through the options to find the device filename and deal 123 // with options that don't require a device filename, to flag destructive 124 // (o, z, or Z) options, and to flag presence of a --pretend/-P option 125 while ((opt = poptGetNextOpt(poptCon)) > 0) { 126 switch (opt) { 127 case 'A': 128 cmd = GetString(attributeOperation, 1); 129 if (cmd == "list") 130 Attributes::ListAttributes(); 131 break; 132 case 'L': 133 typeHelper.ShowAllTypes(0); 134 break; 135 case 'P': 136 pretend = 1; 137 break; 138 case 'V': 139 cout << "GPT fdisk (sgdisk) version " << GPTFDISK_VERSION << "\n\n"; 140 break; 141 default: 142 break; 143 } // switch 144 numOptions++; 145 } // while 146 147 // Assume first non-option argument is the device filename.... 148 device = (char*) poptGetArg(poptCon); 149 poptResetContext(poptCon); 150 151 if (device != NULL) { 152 JustLooking(); // reset as necessary 153 BeQuiet(); // Tell called functions to be less verbose & interactive 154 if (LoadPartitions((string) device)) { 155 if ((WhichWasUsed() == use_mbr) || (WhichWasUsed() == use_bsd)) 156 saveNonGPT = 0; // flag so we don't overwrite unless directed to do so 157 sSize = GetBlockSize(); 158 while ((opt = poptGetNextOpt(poptCon)) > 0) { 159 switch (opt) { 160 case 'A': { 161 if (cmd != "list") { 162 partNum = (int) GetInt(attributeOperation, 1) - 1; 163 if (partNum < 0) 164 partNum = newPartNum; 165 if ((partNum >= 0) && (partNum < (int) GetNumParts())) { 166 switch (ManageAttributes(partNum, GetString(attributeOperation, 2), 167 GetString(attributeOperation, 3))) { 168 case -1: 169 saveData = 0; 170 neverSaveData = 1; 171 break; 172 case 1: 173 JustLooking(0); 174 saveData = 1; 175 break; 176 default: 177 break; 178 } // switch 179 } else { 180 cerr << "Error: Invalid partition number " << partNum + 1 << "\n"; 181 saveData = 0; 182 neverSaveData = 1; 183 } // if/else reasonable partition # 184 } // if (cmd != "list") 185 break; 186 } // case 'A': 187 case 'a': 188 SetAlignment(alignment); 189 break; 190 case 'b': 191 SaveGPTBackup(backupFile); 192 free(backupFile); 193 break; 194 case 'c': 195 cout << "Setting name!\n"; 196 JustLooking(0); 197 partNum = (int) GetInt(partName, 1) - 1; 198 if (partNum < 0) 199 partNum = newPartNum; 200 cout << "partNum is " << partNum << "\n"; 201 if ((partNum >= 0) && (partNum < (int) GetNumParts())) { 202 cout << "REALLY setting name!\n"; 203 name = GetString(partName, 2); 204 if (SetName(partNum, (UnicodeString) name.c_str())) { 205 saveData = 1; 206 } else { 207 cerr << "Unable to set partition " << partNum + 1 208 << "'s name to '" << GetString(partName, 2) << "'!\n"; 209 neverSaveData = 1; 210 } // if/else 211 free(partName); 212 } 213 break; 214 case 'C': 215 JustLooking(0); 216 RecomputeCHS(); 217 saveData = 1; 218 break; 219 case 'd': 220 JustLooking(0); 221 if (DeletePartition(deletePartNum - 1) == 0) { 222 cerr << "Error " << errno << " deleting partition!\n"; 223 neverSaveData = 1; 224 } else saveData = 1; 225 break; 226 case 'D': 227 cout << GetAlignment() << "\n"; 228 break; 229 case 'e': 230 JustLooking(0); 231 MoveSecondHeaderToEnd(); 232 saveData = 1; 233 break; 234 case 'E': 235 cout << FindLastInFree(FindFirstInLargest()) << "\n"; 236 break; 237 case 'f': 238 cout << FindFirstInLargest() << "\n"; 239 break; 240 case 'F': 241 temp = FindFirstInLargest(); 242 Align(&temp); 243 cout << temp << "\n"; 244 break; 245 case 'g': 246 JustLooking(0); 247 saveData = 1; 248 saveNonGPT = 1; 249 break; 250 case 'G': 251 JustLooking(0); 252 saveData = 1; 253 RandomizeGUIDs(); 254 break; 255 case 'h': 256 JustLooking(0); 257 if (BuildMBR(hybrids, 1) == 1) 258 saveData = 1; 259 break; 260 case 'i': 261 ShowPartDetails(infoPartNum - 1); 262 break; 263 case 'l': 264 LoadBackupFile(backupFile, saveData, neverSaveData); 265 free(backupFile); 266 break; 267 case 'L': 268 break; 269 case 'm': 270 JustLooking(0); 271 if (BuildMBR(mbrParts, 0) == 1) { 272 if (!pretend) { 273 if (SaveMBR()) { 274 DestroyGPT(); 275 } else 276 cerr << "Problem saving MBR!\n"; 277 } // if 278 saveNonGPT = 0; 279 pretend = 1; // Not really, but works around problem if -g is used with this... 280 saveData = 0; 281 } // if 282 break; 283 case 'n': 284 JustLooking(0); 285 newPartNum = (int) GetInt(newPartInfo, 1) - 1; 286 if (newPartNum < 0) 287 newPartNum = FindFirstFreePart(); 288 low = FindFirstInLargest(); 289 Align(&low); 290 high = FindLastInFree(low); 291 startSector = IeeeToInt(GetString(newPartInfo, 2), sSize, low, high, low); 292 endSector = IeeeToInt(GetString(newPartInfo, 3), sSize, startSector, high, high); 293 if (CreatePartition(newPartNum, startSector, endSector)) { 294 saveData = 1; 295 } else { 296 cerr << "Could not create partition " << newPartNum + 1 << " from " 297 << startSector << " to " << endSector << "\n"; 298 neverSaveData = 1; 299 } // if/else 300 free(newPartInfo); 301 break; 302 case 'N': 303 JustLooking(0); 304 startSector = FindFirstInLargest(); 305 Align(&startSector); 306 endSector = FindLastInFree(startSector); 307 if (largestPartNum < 0) 308 largestPartNum = FindFirstFreePart(); 309 if (CreatePartition(largestPartNum - 1, startSector, endSector)) { 310 saveData = 1; 311 } else { 312 cerr << "Could not create partition " << largestPartNum << " from " 313 << startSector << " to " << endSector << "\n"; 314 neverSaveData = 1; 315 } // if/else 316 break; 317 case 'o': 318 JustLooking(0); 319 ClearGPTData(); 320 saveData = 1; 321 break; 322 case 'p': 323 DisplayGPTData(); 324 break; 325 case 'P': 326 pretend = 1; 327 break; 328 case 'r': 329 JustLooking(0); 330 uint64_t p1, p2; 331 p1 = GetInt(twoParts, 1) - 1; 332 p2 = GetInt(twoParts, 2) - 1; 333 if (SwapPartitions((uint32_t) p1, (uint32_t) p2) == 0) { 334 neverSaveData = 1; 335 cerr << "Cannot swap partitions " << p1 + 1 << " and " << p2 + 1 << "\n"; 336 } else saveData = 1; 337 break; 338 case 'R': 339 secondDevice = *this; 340 secondDevice.SetDisk(outDevice); 341 secondDevice.JustLooking(0); 342 if (!secondDevice.SaveGPTData(1)) 343 retval = 8; 344 break; 345 case 's': 346 JustLooking(0); 347 SortGPT(); 348 saveData = 1; 349 break; 350 case 'S': 351 JustLooking(0); 352 if (SetGPTSize(tableSize) == 0) 353 neverSaveData = 1; 354 else 355 saveData = 1; 356 break; 357 case 't': 358 JustLooking(0); 359 partNum = (int) GetInt(typeCode, 1) - 1; 360 if (partNum < 0) 361 partNum = newPartNum; 362 if ((partNum >= 0) && (partNum < (int) GetNumParts())) { 363 typeHelper = GetString(typeCode, 2); 364 if ((typeHelper != (GUIDData) "00000000-0000-0000-0000-000000000000") && 365 (ChangePartType(partNum, typeHelper))) { 366 saveData = 1; 367 } else { 368 cerr << "Could not change partition " << partNum + 1 369 << "'s type code to " << GetString(typeCode, 2) << "!\n"; 370 neverSaveData = 1; 371 } // if/else 372 free(typeCode); 373 } 374 break; 375 case 'T': 376 JustLooking(0); 377 XFormDisklabel(bsdPartNum - 1); 378 saveData = 1; 379 break; 380 case 'u': 381 JustLooking(0); 382 saveData = 1; 383 partNum = (int) GetInt(partGUID, 1) - 1; 384 if (partNum < 0) 385 partNum = newPartNum; 386 if ((partNum >= 0) && (partNum < (int) GetNumParts())) { 387 SetPartitionGUID(partNum, GetString(partGUID, 2).c_str()); 388 } 389 break; 390 case 'U': 391 JustLooking(0); 392 saveData = 1; 393 SetDiskGUID(diskGUID); 394 break; 395 case 'v': 396 Verify(); 397 break; 398 case 'z': 399 if (!pretend) { 400 DestroyGPT(); 401 } // if 402 saveNonGPT = 0; 403 saveData = 0; 404 break; 405 case 'Z': 406 if (!pretend) { 407 DestroyGPT(); 408 DestroyMBR(); 409 } // if 410 saveNonGPT = 0; 411 saveData = 0; 412 break; 413 default: 414 cerr << "Unknown option (-" << opt << ")!\n"; 415 break; 416 } // switch 417 } // while 418 } else { // if loaded OK 419 poptResetContext(poptCon); 420 // Do a few types of operations even if there are problems.... 421 while ((opt = poptGetNextOpt(poptCon)) > 0) { 422 switch (opt) { 423 case 'l': 424 LoadBackupFile(backupFile, saveData, neverSaveData); 425 cout << "Information: Loading backup partition table; will override earlier problems!\n"; 426 free(backupFile); 427 retval = 0; 428 break; 429 case 'o': 430 JustLooking(0); 431 ClearGPTData(); 432 saveData = 1; 433 cout << "Information: Creating fresh partition table; will override earlier problems!\n"; 434 retval = 0; 435 break; 436 case 'v': 437 cout << "Verification may miss some problems or report too many!\n"; 438 Verify(); 439 break; 440 case 'z': 441 if (!pretend) { 442 DestroyGPT(); 443 } // if 444 saveNonGPT = 0; 445 saveData = 0; 446 break; 447 case 'Z': 448 if (!pretend) { 449 DestroyGPT(); 450 DestroyMBR(); 451 } // if 452 saveNonGPT = 0; 453 saveData = 0; 454 break; 455 } // switch 456 } // while 457 retval = 2; 458 } // if/else loaded OK 459 if ((saveData) && (!neverSaveData) && (saveNonGPT) && (!pretend)) { 460 SaveGPTData(1); 461 } 462 if (saveData && (!saveNonGPT)) { 463 cout << "Non-GPT disk; not saving changes. Use -g to override.\n"; 464 retval = 3; 465 } // if 466 if (neverSaveData) { 467 cerr << "Error encountered; not saving changes.\n"; 468 retval = 4; 469 } // if 470 } // if (device != NULL) 471 poptFreeContext(poptCon); 472 return retval; 473 } // GPTDataCL::DoOptions() 474 475 // Create a hybrid or regular MBR from GPT data structures 476 int GPTDataCL::BuildMBR(char* argument, int isHybrid) { 477 int numParts, allOK = 1, i, origPartNum; 478 MBRPart newPart; 479 BasicMBRData newMBR; 480 481 if (argument != NULL) { 482 numParts = CountColons(argument) + 1; 483 if (numParts <= (4 - isHybrid)) { 484 newMBR.SetDisk(GetDisk()); 485 for (i = 0; i < numParts; i++) { 486 origPartNum = GetInt(argument, i + 1) - 1; 487 if (IsUsedPartNum(origPartNum) && (partitions[origPartNum].IsSizedForMBR() == MBR_SIZED_GOOD)) { 488 newPart.SetInclusion(PRIMARY); 489 newPart.SetLocation(operator[](origPartNum).GetFirstLBA(), 490 operator[](origPartNum).GetLengthLBA()); 491 newPart.SetStatus(0); 492 newPart.SetType((uint8_t)(operator[](origPartNum).GetHexType() / 0x0100)); 493 newMBR.AddPart(i + isHybrid, newPart); 494 } else { 495 cerr << "Original partition " << origPartNum + 1 << " does not exist or is too big! Aborting operation!\n"; 496 allOK = 0; 497 } // if/else 498 } // for 499 if (isHybrid) { 500 newPart.SetInclusion(PRIMARY); 501 newPart.SetLocation(1, newMBR.FindLastInFree(1)); 502 newPart.SetStatus(0); 503 newPart.SetType(0xEE); 504 newMBR.AddPart(0, newPart); 505 } // if 506 if (allOK) 507 SetProtectiveMBR(newMBR); 508 } else allOK = 0; 509 } else allOK = 0; 510 if (!allOK) 511 cerr << "Problem creating MBR!\n"; 512 return allOK; 513 } // GPTDataCL::BuildMBR() 514 515 // Returns the number of colons in argument string, ignoring the 516 // first character (thus, a leading colon is ignored, as GetString() 517 // does). 518 int CountColons(char* argument) { 519 int num = 0; 520 521 while ((argument[0] != '\0') && (argument = strchr(&argument[1], ':'))) 522 num++; 523 524 return num; 525 } // GPTDataCL::CountColons() 526 527 // Extract integer data from argument string, which should be colon-delimited 528 uint64_t GetInt(const string & argument, int itemNum) { 529 uint64_t retval; 530 531 istringstream inString(GetString(argument, itemNum)); 532 inString >> retval; 533 return retval; 534 } // GPTDataCL::GetInt() 535 536 // Extract string data from argument string, which should be colon-delimited 537 // If string begins with a colon, that colon is skipped in the counting. If an 538 // invalid itemNum is specified, returns an empty string. 539 string GetString(string argument, int itemNum) { 540 size_t startPos = 0, endPos = 0; 541 string retVal = ""; 542 int foundLast = 0; 543 int numFound = 0; 544 545 if (argument[0] == ':') 546 argument.erase(0, 1); 547 while ((numFound < itemNum) && (!foundLast)) { 548 endPos = argument.find(':', startPos); 549 numFound++; 550 if (endPos == string::npos) { 551 foundLast = 1; 552 endPos = argument.length(); 553 } else if (numFound < itemNum) { 554 startPos = endPos + 1; 555 } // if/elseif 556 } // while 557 if ((numFound == itemNum) && (numFound > 0)) 558 retVal = argument.substr(startPos, endPos - startPos); 559 560 return retVal; 561 } // GetString() 562