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 typeHelper = GetString(typeCode, 2); 368 if ((typeHelper != (GUIDData) "00000000-0000-0000-0000-000000000000") && 369 (ChangePartType(partNum, typeHelper))) { 370 saveData = 1; 371 } else { 372 cerr << "Could not change partition " << partNum + 1 373 << "'s type code to " << GetString(typeCode, 2) << "!\n"; 374 neverSaveData = 1; 375 } // if/else 376 free(typeCode); 377 } 378 break; 379 case 'T': 380 JustLooking(0); 381 XFormDisklabel(bsdPartNum - 1); 382 saveData = 1; 383 break; 384 case 'u': 385 JustLooking(0); 386 saveData = 1; 387 partNum = (int) GetInt(partGUID, 1) - 1; 388 if (partNum < 0) 389 partNum = newPartNum; 390 if ((partNum >= 0) && (partNum < (int) GetNumParts())) { 391 SetPartitionGUID(partNum, GetString(partGUID, 2).c_str()); 392 } 393 break; 394 case 'U': 395 JustLooking(0); 396 saveData = 1; 397 SetDiskGUID(diskGUID); 398 break; 399 case 'v': 400 Verify(); 401 break; 402 case 'z': 403 if (!pretend) { 404 DestroyGPT(); 405 } // if 406 saveNonGPT = 0; 407 saveData = 0; 408 break; 409 case 'Z': 410 if (!pretend) { 411 DestroyGPT(); 412 DestroyMBR(); 413 } // if 414 saveNonGPT = 0; 415 saveData = 0; 416 break; 417 default: 418 cerr << "Unknown option (-" << opt << ")!\n"; 419 break; 420 } // switch 421 } // while 422 } else { // if loaded OK 423 poptResetContext(poptCon); 424 // Do a few types of operations even if there are problems.... 425 while ((opt = poptGetNextOpt(poptCon)) > 0) { 426 switch (opt) { 427 case 'l': 428 LoadBackupFile(backupFile, saveData, neverSaveData); 429 cout << "Information: Loading backup partition table; will override earlier problems!\n"; 430 free(backupFile); 431 retval = 0; 432 break; 433 case 'o': 434 JustLooking(0); 435 ClearGPTData(); 436 saveData = 1; 437 cout << "Information: Creating fresh partition table; will override earlier problems!\n"; 438 retval = 0; 439 break; 440 case 'v': 441 cout << "Verification may miss some problems or report too many!\n"; 442 Verify(); 443 break; 444 case 'z': 445 if (!pretend) { 446 DestroyGPT(); 447 } // if 448 saveNonGPT = 0; 449 saveData = 0; 450 break; 451 case 'Z': 452 if (!pretend) { 453 DestroyGPT(); 454 DestroyMBR(); 455 } // if 456 saveNonGPT = 0; 457 saveData = 0; 458 break; 459 } // switch 460 } // while 461 retval = 2; 462 } // if/else loaded OK 463 if ((saveData) && (!neverSaveData) && (saveNonGPT) && (!pretend)) { 464 SaveGPTData(1); 465 } 466 if (saveData && (!saveNonGPT)) { 467 cout << "Non-GPT disk; not saving changes. Use -g to override.\n"; 468 retval = 3; 469 } // if 470 if (neverSaveData) { 471 cerr << "Error encountered; not saving changes.\n"; 472 retval = 4; 473 } // if 474 } // if (device != NULL) 475 poptFreeContext(poptCon); 476 return retval; 477 } // GPTDataCL::DoOptions() 478 479 // Create a hybrid or regular MBR from GPT data structures 480 int GPTDataCL::BuildMBR(char* argument, int isHybrid) { 481 int numParts, allOK = 1, i, origPartNum; 482 MBRPart newPart; 483 BasicMBRData newMBR; 484 485 if (argument != NULL) { 486 numParts = CountColons(argument) + 1; 487 if (numParts <= (4 - isHybrid)) { 488 newMBR.SetDisk(GetDisk()); 489 for (i = 0; i < numParts; i++) { 490 origPartNum = GetInt(argument, i + 1) - 1; 491 if (IsUsedPartNum(origPartNum) && (partitions[origPartNum].IsSizedForMBR() == MBR_SIZED_GOOD)) { 492 newPart.SetInclusion(PRIMARY); 493 newPart.SetLocation(operator[](origPartNum).GetFirstLBA(), 494 operator[](origPartNum).GetLengthLBA()); 495 newPart.SetStatus(0); 496 newPart.SetType((uint8_t)(operator[](origPartNum).GetHexType() / 0x0100)); 497 newMBR.AddPart(i + isHybrid, newPart); 498 } else { 499 cerr << "Original partition " << origPartNum + 1 << " does not exist or is too big! Aborting operation!\n"; 500 allOK = 0; 501 } // if/else 502 } // for 503 if (isHybrid) { 504 newPart.SetInclusion(PRIMARY); 505 newPart.SetLocation(1, newMBR.FindLastInFree(1)); 506 newPart.SetStatus(0); 507 newPart.SetType(0xEE); 508 newMBR.AddPart(0, newPart); 509 } // if 510 if (allOK) 511 SetProtectiveMBR(newMBR); 512 } else allOK = 0; 513 } else allOK = 0; 514 if (!allOK) 515 cerr << "Problem creating MBR!\n"; 516 return allOK; 517 } // GPTDataCL::BuildMBR() 518 519 // Returns the number of colons in argument string, ignoring the 520 // first character (thus, a leading colon is ignored, as GetString() 521 // does). 522 int CountColons(char* argument) { 523 int num = 0; 524 525 while ((argument[0] != '\0') && (argument = strchr(&argument[1], ':'))) 526 num++; 527 528 return num; 529 } // GPTDataCL::CountColons() 530 531 // Extract integer data from argument string, which should be colon-delimited 532 uint64_t GetInt(const string & argument, int itemNum) { 533 uint64_t retval; 534 535 istringstream inString(GetString(argument, itemNum)); 536 inString >> retval; 537 return retval; 538 } // GPTDataCL::GetInt() 539 540 // Extract string data from argument string, which should be colon-delimited 541 // If string begins with a colon, that colon is skipped in the counting. If an 542 // invalid itemNum is specified, returns an empty string. 543 string GetString(string argument, int itemNum) { 544 size_t startPos = 0, endPos = 0; 545 string retVal = ""; 546 int foundLast = 0; 547 int numFound = 0; 548 549 if (argument[0] == ':') 550 argument.erase(0, 1); 551 while ((numFound < itemNum) && (!foundLast)) { 552 endPos = argument.find(':', startPos); 553 numFound++; 554 if (endPos == string::npos) { 555 foundLast = 1; 556 endPos = argument.length(); 557 } else if (numFound < itemNum) { 558 startPos = endPos + 1; 559 } // if/elseif 560 } // while 561 if ((numFound == itemNum) && (numFound > 0)) 562 retVal = argument.substr(startPos, endPos - startPos); 563 564 return retVal; 565 } // GetString() 566