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