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 {"skip-sync", 'j', POPT_ARG_NONE, NULL, 'j', "Don't atempt to sync and update the parittion table", ""}, 90 {"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"}, 91 {"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""}, 92 {"gpttombr", 'm', POPT_ARG_STRING, &mbrParts, 'm', "convert GPT to MBR", "partnum[:partnum...]"}, 93 {"new", 'n', POPT_ARG_STRING, &newPartInfo, 'n', "create new partition", "partnum:start:end"}, 94 {"largest-new", 'N', POPT_ARG_INT, &largestPartNum, 'N', "create largest possible new partition", "partnum"}, 95 {"clear", 'o', POPT_ARG_NONE, NULL, 'o', "clear partition table", ""}, 96 {"print", 'p', POPT_ARG_NONE, NULL, 'p', "print partition table", ""}, 97 {"pretend", 'P', POPT_ARG_NONE, NULL, 'P', "make changes in memory, but don't write them", ""}, 98 {"transpose", 'r', POPT_ARG_STRING, &twoParts, 'r', "transpose two partitions", "partnum:partnum"}, 99 {"replicate", 'R', POPT_ARG_STRING, &outDevice, 'R', "replicate partition table", "device_filename"}, 100 {"sort", 's', POPT_ARG_NONE, NULL, 's', "sort partition table entries", ""}, 101 {"resize-table", 'S', POPT_ARG_INT, &tableSize, 'S', "resize partition table", "numparts"}, 102 {"typecode", 't', POPT_ARG_STRING, &typeCode, 't', "change partition type code", "partnum:{hexcode|GUID}"}, 103 {"transform-bsd", 'T', POPT_ARG_INT, &bsdPartNum, 'T', "transform BSD disklabel partition to GPT", "partnum"}, 104 {"partition-guid", 'u', POPT_ARG_STRING, &partGUID, 'u', "set partition GUID", "partnum:guid"}, 105 {"disk-guid", 'U', POPT_ARG_STRING, &diskGUID, 'U', "set disk GUID", "guid"}, 106 {"verify", 'v', POPT_ARG_NONE, NULL, 'v', "check partition table integrity", ""}, 107 {"version", 'V', POPT_ARG_NONE, NULL, 'V', "display version information", ""}, 108 {"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT (but not MBR) data structures", ""}, 109 {"zap-all", 'Z', POPT_ARG_NONE, NULL, 'Z', "zap (destroy) GPT and MBR data structures", ""}, 110 POPT_AUTOHELP { NULL, 0, 0, NULL, 0, NULL, NULL } 111 }; 112 113 // Create popt context... 114 poptCon = poptGetContext(NULL, argc, (const char**) argv, theOptions, 0); 115 116 poptSetOtherOptionHelp(poptCon, " [OPTION...] <device>"); 117 118 if (argc < 2) { 119 poptPrintUsage(poptCon, stderr, 0); 120 return 1; 121 } 122 123 // Do one loop through the options to find the device filename and deal 124 // with options that don't require a device filename, to flag destructive 125 // (o, z, or Z) options, and to flag presence of a --pretend/-P option 126 while ((opt = poptGetNextOpt(poptCon)) > 0) { 127 switch (opt) { 128 case 'A': 129 cmd = GetString(attributeOperation, 1); 130 if (cmd == "list") 131 Attributes::ListAttributes(); 132 break; 133 case 'L': 134 typeHelper.ShowAllTypes(0); 135 break; 136 case 'P': 137 pretend = 1; 138 break; 139 case 'V': 140 cout << "GPT fdisk (sgdisk) version " << GPTFDISK_VERSION << "\n\n"; 141 break; 142 default: 143 break; 144 } // switch 145 numOptions++; 146 } // while 147 148 // Assume first non-option argument is the device filename.... 149 device = (char*) poptGetArg(poptCon); 150 poptResetContext(poptCon); 151 152 if (device != NULL) { 153 JustLooking(); // reset as necessary 154 BeQuiet(); // Tell called functions to be less verbose & interactive 155 if (LoadPartitions((string) device)) { 156 if ((WhichWasUsed() == use_mbr) || (WhichWasUsed() == use_bsd)) 157 saveNonGPT = 0; // flag so we don't overwrite unless directed to do so 158 sSize = GetBlockSize(); 159 while ((opt = poptGetNextOpt(poptCon)) > 0) { 160 switch (opt) { 161 case 'A': { 162 if (cmd != "list") { 163 partNum = (int) GetInt(attributeOperation, 1) - 1; 164 if (partNum < 0) 165 partNum = newPartNum; 166 if ((partNum >= 0) && (partNum < (int) GetNumParts())) { 167 switch (ManageAttributes(partNum, GetString(attributeOperation, 2), 168 GetString(attributeOperation, 3))) { 169 case -1: 170 saveData = 0; 171 neverSaveData = 1; 172 break; 173 case 1: 174 JustLooking(0); 175 saveData = 1; 176 break; 177 default: 178 break; 179 } // switch 180 } else { 181 cerr << "Error: Invalid partition number " << partNum + 1 << "\n"; 182 saveData = 0; 183 neverSaveData = 1; 184 } // if/else reasonable partition # 185 } // if (cmd != "list") 186 break; 187 } // case 'A': 188 case 'a': 189 SetAlignment(alignment); 190 break; 191 case 'b': 192 SaveGPTBackup(backupFile); 193 free(backupFile); 194 break; 195 case 'c': 196 cout << "Setting name!\n"; 197 JustLooking(0); 198 partNum = (int) GetInt(partName, 1) - 1; 199 if (partNum < 0) 200 partNum = newPartNum; 201 cout << "partNum is " << partNum << "\n"; 202 if ((partNum >= 0) && (partNum < (int) GetNumParts())) { 203 cout << "REALLY setting name!\n"; 204 name = GetString(partName, 2); 205 if (SetName(partNum, (UnicodeString) name.c_str())) { 206 saveData = 1; 207 } else { 208 cerr << "Unable to set partition " << partNum + 1 209 << "'s name to '" << GetString(partName, 2) << "'!\n"; 210 neverSaveData = 1; 211 } // if/else 212 free(partName); 213 } 214 break; 215 case 'C': 216 JustLooking(0); 217 RecomputeCHS(); 218 saveData = 1; 219 break; 220 case 'd': 221 JustLooking(0); 222 if (DeletePartition(deletePartNum - 1) == 0) { 223 cerr << "Error " << errno << " deleting partition!\n"; 224 neverSaveData = 1; 225 } else saveData = 1; 226 break; 227 case 'D': 228 cout << GetAlignment() << "\n"; 229 break; 230 case 'e': 231 JustLooking(0); 232 MoveSecondHeaderToEnd(); 233 saveData = 1; 234 break; 235 case 'E': 236 cout << FindLastInFree(FindFirstInLargest()) << "\n"; 237 break; 238 case 'f': 239 cout << FindFirstInLargest() << "\n"; 240 break; 241 case 'F': 242 temp = FindFirstInLargest(); 243 Align(&temp); 244 cout << temp << "\n"; 245 break; 246 case 'g': 247 JustLooking(0); 248 saveData = 1; 249 saveNonGPT = 1; 250 break; 251 case 'G': 252 JustLooking(0); 253 saveData = 1; 254 RandomizeGUIDs(); 255 break; 256 case 'h': 257 JustLooking(0); 258 if (BuildMBR(hybrids, 1) == 1) 259 saveData = 1; 260 break; 261 case 'i': 262 ShowPartDetails(infoPartNum - 1); 263 break; 264 case 'j': 265 TurnOffSyncing(); 266 break; 267 case 'l': 268 LoadBackupFile(backupFile, saveData, neverSaveData); 269 free(backupFile); 270 break; 271 case 'L': 272 break; 273 case 'm': 274 JustLooking(0); 275 if (BuildMBR(mbrParts, 0) == 1) { 276 if (!pretend) { 277 if (SaveMBR()) { 278 DestroyGPT(); 279 } else 280 cerr << "Problem saving MBR!\n"; 281 } // if 282 saveNonGPT = 0; 283 pretend = 1; // Not really, but works around problem if -g is used with this... 284 saveData = 0; 285 } // if 286 break; 287 case 'n': 288 JustLooking(0); 289 newPartNum = (int) GetInt(newPartInfo, 1) - 1; 290 if (newPartNum < 0) 291 newPartNum = FindFirstFreePart(); 292 low = FindFirstInLargest(); 293 Align(&low); 294 high = FindLastInFree(low); 295 startSector = IeeeToInt(GetString(newPartInfo, 2), sSize, low, high, low); 296 endSector = IeeeToInt(GetString(newPartInfo, 3), sSize, startSector, high, high); 297 if (CreatePartition(newPartNum, startSector, endSector)) { 298 saveData = 1; 299 } else { 300 cerr << "Could not create partition " << newPartNum + 1 << " from " 301 << startSector << " to " << endSector << "\n"; 302 neverSaveData = 1; 303 } // if/else 304 free(newPartInfo); 305 break; 306 case 'N': 307 JustLooking(0); 308 startSector = FindFirstInLargest(); 309 Align(&startSector); 310 endSector = FindLastInFree(startSector); 311 if (largestPartNum < 0) 312 largestPartNum = FindFirstFreePart(); 313 if (CreatePartition(largestPartNum - 1, startSector, endSector)) { 314 saveData = 1; 315 } else { 316 cerr << "Could not create partition " << largestPartNum << " from " 317 << startSector << " to " << endSector << "\n"; 318 neverSaveData = 1; 319 } // if/else 320 break; 321 case 'o': 322 JustLooking(0); 323 ClearGPTData(); 324 saveData = 1; 325 break; 326 case 'p': 327 DisplayGPTData(); 328 break; 329 case 'P': 330 pretend = 1; 331 break; 332 case 'r': 333 JustLooking(0); 334 uint64_t p1, p2; 335 p1 = GetInt(twoParts, 1) - 1; 336 p2 = GetInt(twoParts, 2) - 1; 337 if (SwapPartitions((uint32_t) p1, (uint32_t) p2) == 0) { 338 neverSaveData = 1; 339 cerr << "Cannot swap partitions " << p1 + 1 << " and " << p2 + 1 << "\n"; 340 } else saveData = 1; 341 break; 342 case 'R': 343 secondDevice = *this; 344 secondDevice.SetDisk(outDevice); 345 secondDevice.JustLooking(0); 346 if (!secondDevice.SaveGPTData(1)) 347 retval = 8; 348 break; 349 case 's': 350 JustLooking(0); 351 SortGPT(); 352 saveData = 1; 353 break; 354 case 'S': 355 JustLooking(0); 356 if (SetGPTSize(tableSize) == 0) 357 neverSaveData = 1; 358 else 359 saveData = 1; 360 break; 361 case 't': 362 JustLooking(0); 363 partNum = (int) GetInt(typeCode, 1) - 1; 364 if (partNum < 0) 365 partNum = newPartNum; 366 if ((partNum >= 0) && (partNum < (int) GetNumParts())) { 367 // Remember the original hex value requested 368 string raw = GetString(typeCode, 2); 369 if (raw.size() == 4) { 370 typeRaw[partNum] = StrToHex(raw, 0); 371 } 372 typeHelper = GetString(typeCode, 2); 373 if ((typeHelper != (GUIDData) "00000000-0000-0000-0000-000000000000") && 374 (ChangePartType(partNum, typeHelper))) { 375 saveData = 1; 376 } else { 377 cerr << "Could not change partition " << partNum + 1 378 << "'s type code to " << GetString(typeCode, 2) << "!\n"; 379 neverSaveData = 1; 380 } // if/else 381 free(typeCode); 382 } 383 break; 384 case 'T': 385 JustLooking(0); 386 XFormDisklabel(bsdPartNum - 1); 387 saveData = 1; 388 break; 389 case 'u': 390 JustLooking(0); 391 saveData = 1; 392 partNum = (int) GetInt(partGUID, 1) - 1; 393 if (partNum < 0) 394 partNum = newPartNum; 395 if ((partNum >= 0) && (partNum < (int) GetNumParts())) { 396 SetPartitionGUID(partNum, GetString(partGUID, 2).c_str()); 397 } 398 break; 399 case 'U': 400 JustLooking(0); 401 saveData = 1; 402 SetDiskGUID(diskGUID); 403 break; 404 case 'v': 405 Verify(); 406 break; 407 case 'z': 408 if (!pretend) { 409 DestroyGPT(); 410 } // if 411 saveNonGPT = 0; 412 saveData = 0; 413 break; 414 case 'Z': 415 if (!pretend) { 416 DestroyGPT(); 417 DestroyMBR(); 418 } // if 419 saveNonGPT = 0; 420 saveData = 0; 421 break; 422 default: 423 cerr << "Unknown option (-" << opt << ")!\n"; 424 break; 425 } // switch 426 } // while 427 } else { // if loaded OK 428 poptResetContext(poptCon); 429 // Do a few types of operations even if there are problems.... 430 while ((opt = poptGetNextOpt(poptCon)) > 0) { 431 switch (opt) { 432 case 'l': 433 LoadBackupFile(backupFile, saveData, neverSaveData); 434 cout << "Information: Loading backup partition table; will override earlier problems!\n"; 435 free(backupFile); 436 retval = 0; 437 break; 438 case 'o': 439 JustLooking(0); 440 ClearGPTData(); 441 saveData = 1; 442 cout << "Information: Creating fresh partition table; will override earlier problems!\n"; 443 retval = 0; 444 break; 445 case 'v': 446 cout << "Verification may miss some problems or report too many!\n"; 447 Verify(); 448 break; 449 case 'z': 450 if (!pretend) { 451 DestroyGPT(); 452 } // if 453 saveNonGPT = 0; 454 saveData = 0; 455 break; 456 case 'Z': 457 if (!pretend) { 458 DestroyGPT(); 459 DestroyMBR(); 460 } // if 461 saveNonGPT = 0; 462 saveData = 0; 463 break; 464 } // switch 465 } // while 466 retval = 2; 467 } // if/else loaded OK 468 if ((saveData) && (!neverSaveData) && (saveNonGPT) && (!pretend)) { 469 SaveGPTData(1); 470 } 471 if (saveData && (!saveNonGPT)) { 472 cout << "Non-GPT disk; not saving changes. Use -g to override.\n"; 473 retval = 3; 474 } // if 475 if (neverSaveData) { 476 cerr << "Error encountered; not saving changes.\n"; 477 retval = 4; 478 } // if 479 } // if (device != NULL) 480 poptFreeContext(poptCon); 481 return retval; 482 } // GPTDataCL::DoOptions() 483 484 // Create a hybrid or regular MBR from GPT data structures 485 int GPTDataCL::BuildMBR(char* argument, int isHybrid) { 486 int numParts, allOK = 1, i, origPartNum; 487 MBRPart newPart; 488 BasicMBRData newMBR; 489 490 if (argument != NULL) { 491 numParts = CountColons(argument) + 1; 492 if (numParts <= (4 - isHybrid)) { 493 newMBR.SetDisk(GetDisk()); 494 for (i = 0; i < numParts; i++) { 495 origPartNum = GetInt(argument, i + 1) - 1; 496 if (IsUsedPartNum(origPartNum) && (partitions[origPartNum].IsSizedForMBR() == MBR_SIZED_GOOD)) { 497 newPart.SetInclusion(PRIMARY); 498 newPart.SetLocation(operator[](origPartNum).GetFirstLBA(), 499 operator[](origPartNum).GetLengthLBA()); 500 newPart.SetStatus(0); 501 newPart.SetType((uint8_t)(operator[](origPartNum).GetHexType() / 0x0100)); 502 // If we were created with a specific hex type, use that instead 503 // of risking fidelity loss by doing a GUID-based lookup 504 if (typeRaw.count(origPartNum) == 1) { 505 newPart.SetType(typeRaw[origPartNum]); 506 } 507 newMBR.AddPart(i + isHybrid, newPart); 508 } else { 509 cerr << "Original partition " << origPartNum + 1 << " does not exist or is too big! Aborting operation!\n"; 510 allOK = 0; 511 } // if/else 512 } // for 513 if (isHybrid) { 514 newPart.SetInclusion(PRIMARY); 515 newPart.SetLocation(1, newMBR.FindLastInFree(1)); 516 newPart.SetStatus(0); 517 newPart.SetType(0xEE); 518 newMBR.AddPart(0, newPart); 519 } // if 520 if (allOK) 521 SetProtectiveMBR(newMBR); 522 } else allOK = 0; 523 } else allOK = 0; 524 if (!allOK) 525 cerr << "Problem creating MBR!\n"; 526 return allOK; 527 } // GPTDataCL::BuildMBR() 528 529 // Returns the number of colons in argument string, ignoring the 530 // first character (thus, a leading colon is ignored, as GetString() 531 // does). 532 int CountColons(char* argument) { 533 int num = 0; 534 535 while ((argument[0] != '\0') && (argument = strchr(&argument[1], ':'))) 536 num++; 537 538 return num; 539 } // GPTDataCL::CountColons() 540 541 // Extract integer data from argument string, which should be colon-delimited 542 uint64_t GetInt(const string & argument, int itemNum) { 543 uint64_t retval; 544 545 istringstream inString(GetString(argument, itemNum)); 546 inString >> retval; 547 return retval; 548 } // GPTDataCL::GetInt() 549 550 // Extract string data from argument string, which should be colon-delimited 551 // If string begins with a colon, that colon is skipped in the counting. If an 552 // invalid itemNum is specified, returns an empty string. 553 string GetString(string argument, int itemNum) { 554 size_t startPos = 0, endPos = 0; 555 string retVal = ""; 556 int foundLast = 0; 557 int numFound = 0; 558 559 if (argument[0] == ':') 560 argument.erase(0, 1); 561 while ((numFound < itemNum) && (!foundLast)) { 562 endPos = argument.find(':', startPos); 563 numFound++; 564 if (endPos == string::npos) { 565 foundLast = 1; 566 endPos = argument.length(); 567 } else if (numFound < itemNum) { 568 startPos = endPos + 1; 569 } // if/elseif 570 } // while 571 if ((numFound == itemNum) && (numFound > 0)) 572 retVal = argument.substr(startPos, endPos - startPos); 573 574 return retVal; 575 } // GetString() 576