blob: 54b48092e261484e6b21532e39b329004846f5c3 [file] [log] [blame]
srs5694f2efa7d2011-03-01 22:03:26 -05001/* basicmbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition
2 data. */
3
4/* Initial coding by Rod Smith, January to February, 2009 */
5
srs5694bf8950c2011-03-12 01:23:12 -05006/* This program is copyright (c) 2009-2011 by Roderick W. Smith. It is distributed
srs5694f2efa7d2011-03-01 22:03:26 -05007 under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
8
9#define __STDC_LIMIT_MACROS
10#define __STDC_CONSTANT_MACROS
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <stdint.h>
15#include <fcntl.h>
16#include <string.h>
17#include <time.h>
18#include <sys/stat.h>
19#include <errno.h>
20#include <iostream>
srs5694c2f6e0c2011-03-16 02:42:33 -040021#include <algorithm>
srs5694f2efa7d2011-03-01 22:03:26 -050022#include "mbr.h"
srs5694f2efa7d2011-03-01 22:03:26 -050023#include "support.h"
24
25using namespace std;
26
27/****************************************
28 * *
29 * MBRData class and related structures *
30 * *
31 ****************************************/
32
33BasicMBRData::BasicMBRData(void) {
34 blockSize = SECTOR_SIZE;
35 diskSize = 0;
36 device = "";
37 state = invalid;
srs5694f2efa7d2011-03-01 22:03:26 -050038 numHeads = MAX_HEADS;
39 numSecspTrack = MAX_SECSPERTRACK;
40 myDisk = NULL;
41 canDeleteMyDisk = 0;
42 EmptyMBR();
43} // BasicMBRData default constructor
44
45BasicMBRData::BasicMBRData(string filename) {
46 blockSize = SECTOR_SIZE;
47 diskSize = 0;
48 device = filename;
49 state = invalid;
50 numHeads = MAX_HEADS;
51 numSecspTrack = MAX_SECSPERTRACK;
52 myDisk = NULL;
53 canDeleteMyDisk = 0;
54
srs5694f2efa7d2011-03-01 22:03:26 -050055 // Try to read the specified partition table, but if it fails....
56 if (!ReadMBRData(filename)) {
57 EmptyMBR();
58 device = "";
59 } // if
60} // BasicMBRData(string filename) constructor
61
62// Free space used by myDisk only if that's OK -- sometimes it will be
63// copied from an outside source, in which case that source should handle
64// it!
65BasicMBRData::~BasicMBRData(void) {
66 if (canDeleteMyDisk)
67 delete myDisk;
68} // BasicMBRData destructor
69
70// Assignment operator -- copy entire set of MBR data.
71BasicMBRData & BasicMBRData::operator=(const BasicMBRData & orig) {
72 int i;
73
srs5694bf8950c2011-03-12 01:23:12 -050074 memcpy(code, orig.code, 440);
srs5694f2efa7d2011-03-01 22:03:26 -050075 diskSignature = orig.diskSignature;
76 nulls = orig.nulls;
77 MBRSignature = orig.MBRSignature;
78 blockSize = orig.blockSize;
79 diskSize = orig.diskSize;
80 numHeads = orig.numHeads;
81 numSecspTrack = orig.numSecspTrack;
82 canDeleteMyDisk = orig.canDeleteMyDisk;
83 device = orig.device;
84 state = orig.state;
85
86 myDisk = new DiskIO;
srs56946aae2a92011-06-10 01:16:51 -040087 if (myDisk == NULL) {
88 cerr << "Unable to allocate memory in BasicMBRData::operator=()! Terminating!\n";
89 exit(1);
90 } // if
srs5694bf8950c2011-03-12 01:23:12 -050091 if (orig.myDisk != NULL)
92 myDisk->OpenForRead(orig.myDisk->GetName());
srs5694f2efa7d2011-03-01 22:03:26 -050093
94 for (i = 0; i < MAX_MBR_PARTS; i++) {
95 partitions[i] = orig.partitions[i];
96 } // for
97 return *this;
98} // BasicMBRData::operator=()
99
100/**********************
101 * *
102 * Disk I/O functions *
103 * *
104 **********************/
105
106// Read data from MBR. Returns 1 if read was successful (even if the
107// data isn't a valid MBR), 0 if the read failed.
108int BasicMBRData::ReadMBRData(const string & deviceFilename) {
109 int allOK = 1;
110
111 if (myDisk == NULL) {
112 myDisk = new DiskIO;
srs56946aae2a92011-06-10 01:16:51 -0400113 if (myDisk == NULL) {
114 cerr << "Unable to allocate memory in BasicMBRData::ReadMBRData()! Terminating!\n";
115 exit(1);
116 } // if
srs5694f2efa7d2011-03-01 22:03:26 -0500117 canDeleteMyDisk = 1;
118 } // if
119 if (myDisk->OpenForRead(deviceFilename)) {
120 allOK = ReadMBRData(myDisk);
121 } else {
122 allOK = 0;
123 } // if
124
125 if (allOK)
126 device = deviceFilename;
127
128 return allOK;
129} // BasicMBRData::ReadMBRData(const string & deviceFilename)
130
131// Read data from MBR. If checkBlockSize == 1 (the default), the block
132// size is checked; otherwise it's set to the default (512 bytes).
133// Note that any extended partition(s) present will be explicitly stored
134// in the partitions[] array, along with their contained partitions; the
135// extended container partition(s) should be ignored by other functions.
136int BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) {
srs5694bf8950c2011-03-12 01:23:12 -0500137 int allOK = 1, i, logicalNum = 0;
srs5694f2efa7d2011-03-01 22:03:26 -0500138 int err = 1;
139 TempMBR tempMBR;
140
srs5694bf8950c2011-03-12 01:23:12 -0500141 if ((myDisk != NULL) && (myDisk != theDisk) && (canDeleteMyDisk)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500142 delete myDisk;
143 canDeleteMyDisk = 0;
144 } // if
145
146 myDisk = theDisk;
147
148 // Empty existing MBR data, including the logical partitions...
149 EmptyMBR(0);
150
151 if (myDisk->Seek(0))
152 if (myDisk->Read(&tempMBR, 512))
153 err = 0;
154 if (err) {
155 cerr << "Problem reading disk in BasicMBRData::ReadMBRData()!\n";
156 } else {
157 for (i = 0; i < 440; i++)
158 code[i] = tempMBR.code[i];
159 diskSignature = tempMBR.diskSignature;
160 nulls = tempMBR.nulls;
161 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500162 partitions[i] = tempMBR.partitions[i];
163 if (partitions[i].GetLengthLBA() > 0)
164 partitions[i].SetInclusion(PRIMARY);
srs5694f2efa7d2011-03-01 22:03:26 -0500165 } // for i... (reading all four partitions)
166 MBRSignature = tempMBR.MBRSignature;
srs5694bf8950c2011-03-12 01:23:12 -0500167 ReadCHSGeom();
srs5694f2efa7d2011-03-01 22:03:26 -0500168
169 // Reverse the byte order, if necessary
170 if (IsLittleEndian() == 0) {
171 ReverseBytes(&diskSignature, 4);
172 ReverseBytes(&nulls, 2);
173 ReverseBytes(&MBRSignature, 2);
174 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500175 partitions[i].ReverseByteOrder();
srs5694f2efa7d2011-03-01 22:03:26 -0500176 } // for
177 } // if
178
179 if (MBRSignature != MBR_SIGNATURE) {
180 allOK = 0;
181 state = invalid;
182 } // if
183
184 // Find disk size
185 diskSize = myDisk->DiskSize(&err);
186
187 // Find block size
188 if (checkBlockSize) {
189 blockSize = myDisk->GetBlockSize();
190 } // if (checkBlockSize)
191
192 // Load logical partition data, if any is found....
193 if (allOK) {
194 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500195 if ((partitions[i].GetType() == 0x05) || (partitions[i].GetType() == 0x0f)
196 || (partitions[i].GetType() == 0x85)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500197 // Found it, so call a recursive algorithm to load everything from them....
srs5694bf8950c2011-03-12 01:23:12 -0500198 logicalNum = ReadLogicalPart(partitions[i].GetStartLBA(), UINT32_C(0), 4);
srs5694f2efa7d2011-03-01 22:03:26 -0500199 if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
200 allOK = 0;
201 cerr << "Error reading logical partitions! List may be truncated!\n";
202 } // if maxLogicals valid
srs5694bf8950c2011-03-12 01:23:12 -0500203 DeletePartition(i);
srs5694f2efa7d2011-03-01 22:03:26 -0500204 } // if primary partition is extended
205 } // for primary partition loop
206 if (allOK) { // Loaded logicals OK
207 state = mbr;
208 } else {
209 state = invalid;
210 } // if
211 } // if
212
213 // Check to see if it's in GPT format....
214 if (allOK) {
215 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500216 if (partitions[i].GetType() == UINT8_C(0xEE)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500217 state = gpt;
218 } // if
219 } // for
220 } // if
221
222 // If there's an EFI GPT partition, look for other partition types,
223 // to flag as hybrid
224 if (state == gpt) {
225 for (i = 0 ; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500226 if ((partitions[i].GetType() != UINT8_C(0xEE)) &&
227 (partitions[i].GetType() != UINT8_C(0x00)))
srs5694f2efa7d2011-03-01 22:03:26 -0500228 state = hybrid;
srs5694bf8950c2011-03-12 01:23:12 -0500229 if (logicalNum > 0)
230 cerr << "Warning! MBR Logical partitions found on a hybrid MBR disk! This is an\n"
231 << "EXTREMELY dangerous configuration!\n\a";
srs5694f2efa7d2011-03-01 22:03:26 -0500232 } // for
233 } // if (hybrid detection code)
234 } // no initial error
235 return allOK;
236} // BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize)
237
238// This is a recursive function to read all the logical partitions, following the
239// logical partition linked list from the disk and storing the basic data in the
240// partitions[] array. Returns last index to partitions[] used, or -1 if there was
241// a problem.
242// Parameters:
243// extendedStart = LBA of the start of the extended partition
244// diskOffset = LBA offset WITHIN the extended partition of the one to be read
245// partNum = location in partitions[] array to store retrieved data
srs5694bf8950c2011-03-12 01:23:12 -0500246int BasicMBRData::ReadLogicalPart(uint64_t extendedStart, uint64_t diskOffset, int partNum) {
srs5694f2efa7d2011-03-01 22:03:26 -0500247 struct TempMBR ebr;
srs5694bf8950c2011-03-12 01:23:12 -0500248 uint8_t ebrType;
srs5694f2efa7d2011-03-01 22:03:26 -0500249 uint64_t offset;
250
251 // Check for a valid partition number. Note that partitions MAY be read into
252 // the area normally used by primary partitions, although the only calling
srs5694bf8950c2011-03-12 01:23:12 -0500253 // functions as of GPT fdisk version 0.7.0 don't do so.
srs5694f2efa7d2011-03-01 22:03:26 -0500254 if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
255 offset = (uint64_t) (extendedStart + diskOffset);
256 if (myDisk->Seek(offset) == 0) { // seek to EBR record
257 cerr << "Unable to seek to " << offset << "! Aborting!\n";
258 partNum = -1;
259 }
260 if (myDisk->Read(&ebr, 512) != 512) { // Load the data....
261 cerr << "Error seeking to or reading logical partition data from " << offset
262 << "!\nAborting!\n";
263 partNum = -1;
264 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
265 ReverseBytes(&ebr.MBRSignature, 2);
266 ReverseBytes(&ebr.partitions[0].firstLBA, 4);
267 ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
268 ReverseBytes(&ebr.partitions[1].firstLBA, 4);
269 ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
270 } // if/else/if
271
272 if (ebr.MBRSignature != MBR_SIGNATURE) {
273 partNum = -1;
274 cerr << "MBR signature in logical partition invalid; read 0x";
275 cerr.fill('0');
276 cerr.width(4);
277 cerr.setf(ios::uppercase);
278 cerr << hex << ebr.MBRSignature << ", but should be 0x";
279 cerr.width(4);
280 cerr << MBR_SIGNATURE << dec << "\n";
281 cerr.fill(' ');
282 } // if
283
srs5694bf8950c2011-03-12 01:23:12 -0500284 // Sometimes an EBR points directly to another EBR, rather than defining
285 // a logical partition and then pointing to another EBR. Thus, we recurse
286 // directly if this is detected, else extract partition data and then
287 // recurse on the second entry in the EBR...
288 ebrType = ebr.partitions[0].partitionType;
289 if ((ebrType == 0x05) || (ebrType == 0x0f) || (ebrType == 0x85)) {
290 partNum = ReadLogicalPart(extendedStart, ebr.partitions[0].firstLBA, partNum);
srs5694f2efa7d2011-03-01 22:03:26 -0500291 } else {
srs5694bf8950c2011-03-12 01:23:12 -0500292 // Copy over the basic data....
293 partitions[partNum] = ebr.partitions[0];
294 // Adjust the start LBA, since it's encoded strangely....
295 partitions[partNum].SetStartLBA(ebr.partitions[0].firstLBA + diskOffset + extendedStart);
296 partitions[partNum].SetInclusion(LOGICAL);
297
298 // Find the next partition (if there is one) and recurse....
299 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) &&
300 (partNum < (MAX_MBR_PARTS - 1))) {
301 partNum = ReadLogicalPart(extendedStart, ebr.partitions[1].firstLBA,
302 partNum + 1);
303 } else {
304 partNum++;
305 } // if another partition
306 } // if/else
srs5694f2efa7d2011-03-01 22:03:26 -0500307 } // Not enough space for all the logicals (or previous error encountered)
308 return (partNum);
309} // BasicMBRData::ReadLogicalPart()
310
311// Write the MBR data to the default defined device. This writes both the
312// MBR itself and any defined logical partitions, provided there's an
313// MBR extended partition.
314int BasicMBRData::WriteMBRData(void) {
315 int allOK = 1;
316
317 if (myDisk != NULL) {
318 if (myDisk->OpenForWrite() != 0) {
319 allOK = WriteMBRData(myDisk);
srs5694bf8950c2011-03-12 01:23:12 -0500320 cout << "Done writing data!\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500321 } else {
322 allOK = 0;
323 } // if/else
324 myDisk->Close();
325 } else allOK = 0;
326 return allOK;
327} // BasicMBRData::WriteMBRData(void)
328
329// Save the MBR data to a file. This writes both the
srs5694bf8950c2011-03-12 01:23:12 -0500330// MBR itself and any defined logical partitions.
srs5694f2efa7d2011-03-01 22:03:26 -0500331int BasicMBRData::WriteMBRData(DiskIO *theDisk) {
srs5694bf8950c2011-03-12 01:23:12 -0500332 int i, j, partNum, next, allOK = 1, moreLogicals = 0;
333 uint64_t extFirstLBA = 0;
srs5694f2efa7d2011-03-01 22:03:26 -0500334 uint64_t writeEbrTo; // 64-bit because we support extended in 2-4TiB range
335 TempMBR tempMBR;
336
srs5694bf8950c2011-03-12 01:23:12 -0500337 allOK = CreateExtended();
338 if (allOK) {
339 // First write the main MBR data structure....
340 memcpy(tempMBR.code, code, 440);
341 tempMBR.diskSignature = diskSignature;
342 tempMBR.nulls = nulls;
343 tempMBR.MBRSignature = MBRSignature;
344 for (i = 0; i < 4; i++) {
345 partitions[i].StoreInStruct(&tempMBR.partitions[i]);
346 if (partitions[i].GetType() == 0x0f) {
347 extFirstLBA = partitions[i].GetStartLBA();
348 moreLogicals = 1;
349 } // if
350 } // for i...
351 } // if
352 allOK = allOK && WriteMBRData(tempMBR, theDisk, 0);
srs5694f2efa7d2011-03-01 22:03:26 -0500353
354 // Set up tempMBR with some constant data for logical partitions...
355 tempMBR.diskSignature = 0;
356 for (i = 2; i < 4; i++) {
357 tempMBR.partitions[i].firstLBA = tempMBR.partitions[i].lengthLBA = 0;
358 tempMBR.partitions[i].partitionType = 0x00;
359 for (j = 0; j < 3; j++) {
360 tempMBR.partitions[i].firstSector[j] = 0;
361 tempMBR.partitions[i].lastSector[j] = 0;
362 } // for j
363 } // for i
364
srs5694bf8950c2011-03-12 01:23:12 -0500365 partNum = FindNextInUse(4);
srs5694f2efa7d2011-03-01 22:03:26 -0500366 writeEbrTo = (uint64_t) extFirstLBA;
srs5694bf8950c2011-03-12 01:23:12 -0500367 // Write logicals...
srs5694f2efa7d2011-03-01 22:03:26 -0500368 while (allOK && moreLogicals && (partNum < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -0500369 partitions[partNum].StoreInStruct(&tempMBR.partitions[0]);
370 tempMBR.partitions[0].firstLBA = 1;
srs5694f2efa7d2011-03-01 22:03:26 -0500371 // tempMBR.partitions[1] points to next EBR or terminates EBR linked list...
srs5694bf8950c2011-03-12 01:23:12 -0500372 next = FindNextInUse(partNum + 1);
373 if ((next < MAX_MBR_PARTS) && (next > 0) && (partitions[next].GetStartLBA() > 0)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500374 tempMBR.partitions[1].partitionType = 0x0f;
srs5694bf8950c2011-03-12 01:23:12 -0500375 tempMBR.partitions[1].firstLBA = (uint32_t) (partitions[next].GetStartLBA() - extFirstLBA - 1);
376 tempMBR.partitions[1].lengthLBA = (uint32_t) (partitions[next].GetLengthLBA() + 1);
srs5694f2efa7d2011-03-01 22:03:26 -0500377 LBAtoCHS((uint64_t) tempMBR.partitions[1].firstLBA,
378 (uint8_t *) &tempMBR.partitions[1].firstSector);
379 LBAtoCHS(tempMBR.partitions[1].lengthLBA - extFirstLBA,
380 (uint8_t *) &tempMBR.partitions[1].lastSector);
381 } else {
382 tempMBR.partitions[1].partitionType = 0x00;
383 tempMBR.partitions[1].firstLBA = 0;
384 tempMBR.partitions[1].lengthLBA = 0;
385 moreLogicals = 0;
386 } // if/else
387 allOK = WriteMBRData(tempMBR, theDisk, writeEbrTo);
srs5694f2efa7d2011-03-01 22:03:26 -0500388 writeEbrTo = (uint64_t) tempMBR.partitions[1].firstLBA + (uint64_t) extFirstLBA;
srs5694bf8950c2011-03-12 01:23:12 -0500389 partNum = next;
srs5694f2efa7d2011-03-01 22:03:26 -0500390 } // while
srs5694bf8950c2011-03-12 01:23:12 -0500391 DeleteExtendedParts();
srs5694f2efa7d2011-03-01 22:03:26 -0500392 return allOK;
393} // BasicMBRData::WriteMBRData(DiskIO *theDisk)
394
395int BasicMBRData::WriteMBRData(const string & deviceFilename) {
396 device = deviceFilename;
397 return WriteMBRData();
398} // BasicMBRData::WriteMBRData(const string & deviceFilename)
399
400// Write a single MBR record to the specified sector. Used by the like-named
401// function to write both the MBR and multiple EBR (for logical partition)
402// records.
403// Returns 1 on success, 0 on failure
404int BasicMBRData::WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector) {
405 int i, allOK;
406
407 // Reverse the byte order, if necessary
408 if (IsLittleEndian() == 0) {
409 ReverseBytes(&mbr.diskSignature, 4);
410 ReverseBytes(&mbr.nulls, 2);
411 ReverseBytes(&mbr.MBRSignature, 2);
412 for (i = 0; i < 4; i++) {
413 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
414 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
415 } // for
416 } // if
417
418 // Now write the data structure...
419 allOK = theDisk->OpenForWrite();
420 if (allOK && theDisk->Seek(sector)) {
421 if (theDisk->Write(&mbr, 512) != 512) {
422 allOK = 0;
423 cerr << "Error " << errno << " when saving MBR!\n";
424 } // if
425 } else {
426 allOK = 0;
427 cerr << "Error " << errno << " when seeking to MBR to write it!\n";
428 } // if/else
429 theDisk->Close();
430
431 // Reverse the byte order back, if necessary
432 if (IsLittleEndian() == 0) {
433 ReverseBytes(&mbr.diskSignature, 4);
434 ReverseBytes(&mbr.nulls, 2);
435 ReverseBytes(&mbr.MBRSignature, 2);
436 for (i = 0; i < 4; i++) {
437 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
438 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
439 } // for
440 }// if
441 return allOK;
442} // BasicMBRData::WriteMBRData(uint64_t sector)
443
srs5694bf8950c2011-03-12 01:23:12 -0500444// Set a new disk device; used in copying one disk's partition
445// table to another disk.
446void BasicMBRData::SetDisk(DiskIO *theDisk) {
447 int err;
448
449 myDisk = theDisk;
450 diskSize = theDisk->DiskSize(&err);
451 canDeleteMyDisk = 0;
452 ReadCHSGeom();
453} // BasicMBRData::SetDisk()
454
srs5694f2efa7d2011-03-01 22:03:26 -0500455/********************************************
456 * *
457 * Functions that display data for the user *
458 * *
459 ********************************************/
460
461// Show the MBR data to the user, up to the specified maximum number
462// of partitions....
srs5694bf8950c2011-03-12 01:23:12 -0500463void BasicMBRData::DisplayMBRData(void) {
srs5694f2efa7d2011-03-01 22:03:26 -0500464 int i;
srs5694f2efa7d2011-03-01 22:03:26 -0500465
srs5694bf8950c2011-03-12 01:23:12 -0500466 cout << "\nDisk size is " << diskSize << " sectors ("
srs569401f7f082011-03-15 23:53:31 -0400467 << BytesToIeee(diskSize, blockSize) << ")\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500468 cout << "MBR disk identifier: 0x";
469 cout.width(8);
470 cout.fill('0');
471 cout.setf(ios::uppercase);
472 cout << hex << diskSignature << dec << "\n";
srs5694bf8950c2011-03-12 01:23:12 -0500473 cout << "MBR partitions:\n\n";
474 if ((state == gpt) || (state == hybrid)) {
475 cout << "Number Boot Start Sector End Sector Status Code\n";
476 } else {
477 cout << " Can Be Can Be\n";
478 cout << "Number Boot Start Sector End Sector Status Logical Primary Code\n";
479 UpdateCanBeLogical();
480 } //
481 for (i = 0; i < MAX_MBR_PARTS; i++) {
482 if (partitions[i].GetLengthLBA() != 0) {
srs5694f2efa7d2011-03-01 22:03:26 -0500483 cout.fill(' ');
484 cout.width(4);
srs5694bf8950c2011-03-12 01:23:12 -0500485 cout << i + 1 << " ";
486 partitions[i].ShowData((state == gpt) || (state == hybrid));
srs5694f2efa7d2011-03-01 22:03:26 -0500487 } // if
488 cout.fill(' ');
489 } // for
srs5694f2efa7d2011-03-01 22:03:26 -0500490} // BasicMBRData::DisplayMBRData()
491
492// Displays the state, as a word, on stdout. Used for debugging & to
493// tell the user about the MBR state when the program launches....
494void BasicMBRData::ShowState(void) {
495 switch (state) {
496 case invalid:
497 cout << " MBR: not present\n";
498 break;
499 case gpt:
500 cout << " MBR: protective\n";
501 break;
502 case hybrid:
503 cout << " MBR: hybrid\n";
504 break;
505 case mbr:
506 cout << " MBR: MBR only\n";
507 break;
508 default:
509 cout << "\a MBR: unknown -- bug!\n";
510 break;
511 } // switch
512} // BasicMBRData::ShowState()
513
srs5694bf8950c2011-03-12 01:23:12 -0500514/************************
515 * *
516 * GPT Checks and fixes *
517 * *
518 ************************/
519
520// Perform a very rudimentary check for GPT data on the disk; searches for
521// the GPT signature in the main and backup metadata areas.
522// Returns 0 if GPT data not found, 1 if main data only is found, 2 if
523// backup only is found, 3 if both main and backup data are found, and
524// -1 if a disk error occurred.
525int BasicMBRData::CheckForGPT(void) {
526 int retval = 0, err;
527 char signature1[9], signature2[9];
528
529 if (myDisk != NULL) {
530 if (myDisk->OpenForRead() != 0) {
531 if (myDisk->Seek(1)) {
532 myDisk->Read(signature1, 8);
533 signature1[8] = '\0';
534 } else retval = -1;
535 if (myDisk->Seek(myDisk->DiskSize(&err) - 1)) {
536 myDisk->Read(signature2, 8);
537 signature2[8] = '\0';
538 } else retval = -1;
539 if ((retval >= 0) && (strcmp(signature1, "EFI PART") == 0))
540 retval += 1;
541 if ((retval >= 0) && (strcmp(signature2, "EFI PART") == 0))
542 retval += 2;
543 } else {
544 retval = -1;
545 } // if/else
546 myDisk->Close();
547 } else retval = -1;
548 return retval;
549} // BasicMBRData::CheckForGPT()
550
551// Blanks the 2nd (sector #1, numbered from 0) and last sectors of the disk,
552// but only if GPT data are verified on the disk, and only for the sector(s)
553// with GPT signatures.
554// Returns 1 if operation completes successfully, 0 if not (returns 1 if
555// no GPT data are found on the disk).
556int BasicMBRData::BlankGPTData(void) {
557 int allOK = 1, err;
558 uint8_t blank[512];
559
560 memset(blank, 0, 512);
561 switch (CheckForGPT()) {
562 case -1:
563 allOK = 0;
564 break;
565 case 0:
566 break;
567 case 1:
568 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
569 if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512)))
570 allOK = 0;
571 myDisk->Close();
572 } else allOK = 0;
573 break;
574 case 2:
575 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
576 if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) &&
577 (myDisk->Write(blank, 512) == 512)))
578 allOK = 0;
579 myDisk->Close();
580 } else allOK = 0;
581 break;
582 case 3:
583 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
584 if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512)))
585 allOK = 0;
586 if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) &&
587 (myDisk->Write(blank, 512) == 512)))
588 allOK = 0;
589 myDisk->Close();
590 } else allOK = 0;
591 break;
592 default:
593 break;
594 } // switch()
595 return allOK;
596} // BasicMBRData::BlankGPTData
597
srs5694f2efa7d2011-03-01 22:03:26 -0500598/*********************************************************************
599 * *
600 * Functions that set or get disk metadata (CHS geometry, disk size, *
601 * etc.) *
602 * *
603 *********************************************************************/
604
srs5694bf8950c2011-03-12 01:23:12 -0500605// Read the CHS geometry using OS calls, or if that fails, set to
606// the most common value for big disks (255 heads, 63 sectors per
607// track, & however many cylinders that computes to).
608void BasicMBRData::ReadCHSGeom(void) {
609 numHeads = myDisk->GetNumHeads();
610 numSecspTrack = myDisk->GetNumSecsPerTrack();
611 partitions[0].SetGeometry(numHeads, numSecspTrack, diskSize, blockSize);
612} // BasicMBRData::ReadCHSGeom()
613
614// Find the low and high used partition numbers (numbered from 0).
615// Return value is the number of partitions found. Note that the
616// *low and *high values are both set to 0 when no partitions
617// are found, as well as when a single partition in the first
618// position exists. Thus, the return value is the only way to
619// tell when no partitions exist.
620int BasicMBRData::GetPartRange(uint32_t *low, uint32_t *high) {
621 uint32_t i;
622 int numFound = 0;
623
624 *low = MAX_MBR_PARTS + 1; // code for "not found"
625 *high = 0;
626 for (i = 0; i < MAX_MBR_PARTS; i++) {
627 if (partitions[i].GetStartLBA() != UINT32_C(0)) { // it exists
628 *high = i; // since we're counting up, set the high value
629 // Set the low value only if it's not yet found...
630 if (*low == (MAX_MBR_PARTS + 1))
631 *low = i;
632 numFound++;
633 } // if
634 } // for
635
636 // Above will leave *low pointing to its "not found" value if no partitions
637 // are defined, so reset to 0 if this is the case....
638 if (*low == (MAX_MBR_PARTS + 1))
639 *low = 0;
640 return numFound;
641} // GPTData::GetPartRange()
srs5694f2efa7d2011-03-01 22:03:26 -0500642
643// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
644// was within the range that can be expressed by CHS (including 0, for an
645// empty partition), 0 if the value is outside that range, and -1 if chs is
646// invalid.
647int BasicMBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
648 uint64_t cylinder, head, sector; // all numbered from 0
649 uint64_t remainder;
650 int retval = 1;
651 int done = 0;
652
653 if (chs != NULL) {
654 // Special case: In case of 0 LBA value, zero out CHS values....
655 if (lba == 0) {
656 chs[0] = chs[1] = chs[2] = UINT8_C(0);
657 done = 1;
658 } // if
659 // If LBA value is too large for CHS, max out CHS values....
660 if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
661 chs[0] = 254;
662 chs[1] = chs[2] = 255;
663 done = 1;
664 retval = 0;
665 } // if
666 // If neither of the above applies, compute CHS values....
667 if (!done) {
668 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
669 remainder = lba - (cylinder * numHeads * numSecspTrack);
670 head = remainder / numSecspTrack;
671 remainder -= head * numSecspTrack;
672 sector = remainder;
673 if (head < numHeads)
674 chs[0] = (uint8_t) head;
675 else
676 retval = 0;
677 if (sector < numSecspTrack) {
678 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
679 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
680 } else {
681 retval = 0;
682 } // if/else
683 } // if value is expressible and non-0
684 } else { // Invalid (NULL) chs pointer
685 retval = -1;
686 } // if CHS pointer valid
687 return (retval);
688} // BasicMBRData::LBAtoCHS()
689
srs5694bf8950c2011-03-12 01:23:12 -0500690// Look for overlapping partitions.
691// Returns the number of problems found
692int BasicMBRData::FindOverlaps(void) {
693 int i, j, numProbs = 0, numEE = 0;
srs5694f2efa7d2011-03-01 22:03:26 -0500694
695 for (i = 0; i < MAX_MBR_PARTS; i++) {
696 for (j = i + 1; j < MAX_MBR_PARTS; j++) {
srs5694bf8950c2011-03-12 01:23:12 -0500697 if ((partitions[i].GetInclusion() != NONE) &&
698 (partitions[i].DoTheyOverlap(partitions[j]))) {
srs5694f2efa7d2011-03-01 22:03:26 -0500699 numProbs++;
700 cout << "\nProblem: MBR partitions " << i + 1 << " and " << j + 1
701 << " overlap!\n";
702 } // if
703 } // for (j...)
srs5694bf8950c2011-03-12 01:23:12 -0500704 if (partitions[i].GetType() == 0xEE) {
srs5694f2efa7d2011-03-01 22:03:26 -0500705 numEE++;
srs5694bf8950c2011-03-12 01:23:12 -0500706 if (partitions[i].GetStartLBA() != 1)
707 cout << "\nWarning: 0xEE partition doesn't start on sector 1. This can cause "
708 << "problems\nin some OSes.\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500709 } // if
710 } // for (i...)
711 if (numEE > 1)
712 cout << "\nCaution: More than one 0xEE MBR partition found. This can cause problems\n"
713 << "in some OSes.\n";
714
715 return numProbs;
srs5694bf8950c2011-03-12 01:23:12 -0500716} // BasicMBRData::FindOverlaps()
717
718// Returns the number of primary partitions, including the extended partition
719// required to hold any logical partitions found.
720int BasicMBRData::NumPrimaries(void) {
721 int i, numPrimaries = 0, logicalsFound = 0;
722
723 for (i = 0; i < MAX_MBR_PARTS; i++) {
724 if (partitions[i].GetLengthLBA() > 0) {
725 if (partitions[i].GetInclusion() == PRIMARY)
726 numPrimaries++;
727 if (partitions[i].GetInclusion() == LOGICAL)
728 logicalsFound = 1;
729 } // if
730 } // for
731 return (numPrimaries + logicalsFound);
732} // BasicMBRData::NumPrimaries()
733
734// Returns the number of logical partitions.
735int BasicMBRData::NumLogicals(void) {
736 int i, numLogicals = 0;
737
738 for (i = 0; i < MAX_MBR_PARTS; i++) {
739 if (partitions[i].GetInclusion() == LOGICAL)
740 numLogicals++;
741 } // for
742 return numLogicals;
743} // BasicMBRData::NumLogicals()
744
745// Returns the number of partitions (primaries plus logicals), NOT including
746// the extended partition required to house the logicals.
747int BasicMBRData::CountParts(void) {
748 int i, num = 0;
749
750 for (i = 0; i < MAX_MBR_PARTS; i++) {
751 if ((partitions[i].GetInclusion() == LOGICAL) ||
752 (partitions[i].GetInclusion() == PRIMARY))
753 num++;
754 } // for
755 return num;
756} // BasicMBRData::CountParts()
757
758// Updates the canBeLogical and canBePrimary flags for all the partitions.
759void BasicMBRData::UpdateCanBeLogical(void) {
760 int i, j, sectorBefore, numPrimaries, numLogicals, usedAsEBR;
761 uint64_t firstLogical, lastLogical, lStart, pStart;
762
763 numPrimaries = NumPrimaries();
764 numLogicals = NumLogicals();
765 firstLogical = FirstLogicalLBA() - 1;
766 lastLogical = LastLogicalLBA();
767 for (i = 0; i < MAX_MBR_PARTS; i++) {
768 usedAsEBR = (SectorUsedAs(partitions[i].GetLastLBA()) == EBR);
769 if (usedAsEBR) {
770 partitions[i].SetCanBeLogical(0);
771 partitions[i].SetCanBePrimary(0);
772 } else if (partitions[i].GetLengthLBA() > 0) {
773 // First determine if it can be logical....
774 sectorBefore = SectorUsedAs(partitions[i].GetStartLBA() - 1);
775 lStart = partitions[i].GetStartLBA(); // start of potential logical part.
776 if ((lastLogical > 0) &&
777 ((sectorBefore == EBR) || (sectorBefore == NONE))) {
778 // Assume it can be logical, then search for primaries that make it
779 // not work and, if found, flag appropriately.
780 partitions[i].SetCanBeLogical(1);
781 for (j = 0; j < MAX_MBR_PARTS; j++) {
782 if ((i != j) && (partitions[j].GetInclusion() == PRIMARY)) {
783 pStart = partitions[j].GetStartLBA();
784 if (((pStart < lStart) && (firstLogical < pStart)) ||
785 ((pStart > lStart) && (firstLogical > pStart))) {
786 partitions[i].SetCanBeLogical(0);
787 } // if/else
788 } // if
789 } // for
790 } else {
791 if ((sectorBefore != EBR) && (sectorBefore != NONE))
792 partitions[i].SetCanBeLogical(0);
793 else
794 partitions[i].SetCanBeLogical(lastLogical == 0); // can be logical only if no logicals already
795 } // if/else
796 // Now determine if it can be primary. Start by assuming it can be...
797 partitions[i].SetCanBePrimary(1);
798 if ((numPrimaries >= 4) && (partitions[i].GetInclusion() != PRIMARY)) {
799 partitions[i].SetCanBePrimary(0);
800 if ((partitions[i].GetInclusion() == LOGICAL) && (numLogicals == 1) &&
801 (numPrimaries == 4))
802 partitions[i].SetCanBePrimary(1);
803 } // if
804 if ((partitions[i].GetStartLBA() > (firstLogical + 1)) &&
805 (partitions[i].GetLastLBA() < lastLogical))
806 partitions[i].SetCanBePrimary(0);
807 } // else if
808 } // for
809} // BasicMBRData::UpdateCanBeLogical()
810
811// Returns the first sector occupied by any logical partition. Note that
812// this does NOT include the logical partition's EBR! Returns UINT32_MAX
813// if there are no logical partitions defined.
814uint64_t BasicMBRData::FirstLogicalLBA(void) {
815 int i;
816 uint64_t firstFound = UINT32_MAX;
817
818 for (i = 0; i < MAX_MBR_PARTS; i++) {
819 if ((partitions[i].GetInclusion() == LOGICAL) &&
820 (partitions[i].GetStartLBA() < firstFound)) {
821 firstFound = partitions[i].GetStartLBA();
822 } // if
823 } // for
824 return firstFound;
825} // BasicMBRData::FirstLogicalLBA()
826
827// Returns the last sector occupied by any logical partition, or 0 if
828// there are no logical partitions defined.
829uint64_t BasicMBRData::LastLogicalLBA(void) {
830 int i;
831 uint64_t lastFound = 0;
832
833 for (i = 0; i < MAX_MBR_PARTS; i++) {
834 if ((partitions[i].GetInclusion() == LOGICAL) &&
835 (partitions[i].GetLastLBA() > lastFound))
836 lastFound = partitions[i].GetLastLBA();
837 } // for
838 return lastFound;
839} // BasicMBRData::LastLogicalLBA()
840
841// Returns 1 if logical partitions are contiguous (have no primaries
842// in their midst), or 0 if one or more primaries exist between
843// logicals.
844int BasicMBRData::AreLogicalsContiguous(void) {
845 int allOK = 1, i = 0;
846 uint64_t firstLogical, lastLogical;
847
848 firstLogical = FirstLogicalLBA() - 1; // subtract 1 for EBR
849 lastLogical = LastLogicalLBA();
850 if (lastLogical > 0) {
851 do {
852 if ((partitions[i].GetInclusion() == PRIMARY) &&
853 (partitions[i].GetStartLBA() >= firstLogical) &&
854 (partitions[i].GetStartLBA() <= lastLogical)) {
855 allOK = 0;
856 } // if
857 i++;
858 } while ((i < MAX_MBR_PARTS) && allOK);
859 } // if
860 return allOK;
861} // BasicMBRData::AreLogicalsContiguous()
862
863// Returns 1 if all partitions fit on the disk, given its size; 0 if any
864// partition is too big.
865int BasicMBRData::DoTheyFit(void) {
866 int i, allOK = 1;
867
868 for (i = 0; i < MAX_MBR_PARTS; i++) {
869 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize)) {
870 allOK = 0;
871 } // if
872 } // for
873 return allOK;
874} // BasicMBRData::DoTheyFit(void)
875
876// Returns 1 if there's at least one free sector immediately preceding
877// all partitions flagged as logical; 0 if any logical partition lacks
878// this space.
879int BasicMBRData::SpaceBeforeAllLogicals(void) {
880 int i = 0, allOK = 1;
881
882 do {
883 if ((partitions[i].GetStartLBA() > 0) && (partitions[i].GetInclusion() == LOGICAL)) {
884 allOK = allOK && (SectorUsedAs(partitions[i].GetStartLBA() - 1) == EBR);
srs5694bf8950c2011-03-12 01:23:12 -0500885 } // if
886 i++;
887 } while (allOK && (i < MAX_MBR_PARTS));
888 return allOK;
889} // BasicMBRData::SpaceBeforeAllLogicals()
890
891// Returns 1 if the partitions describe a legal layout -- all logicals
892// are contiguous and have at least one preceding empty partitions,
893// the number of primaries is under 4 (or under 3 if there are any
894// logicals), there are no overlapping partitions, etc.
895// Does NOT assume that primaries are numbered 1-4; uses the
896// IsItPrimary() function of the MBRPart class to determine
897// primary status. Also does NOT consider partition order; there
898// can be gaps and it will still be considered legal.
899int BasicMBRData::IsLegal(void) {
900 int allOK = 1;
901
902 allOK = (FindOverlaps() == 0);
903 allOK = (allOK && (NumPrimaries() <= 4));
904 allOK = (allOK && AreLogicalsContiguous());
905 allOK = (allOK && DoTheyFit());
906 allOK = (allOK && SpaceBeforeAllLogicals());
907 return allOK;
908} // BasicMBRData::IsLegal()
909
910// Finds the next in-use partition, starting with start (will return start
911// if it's in use). Returns -1 if no subsequent partition is in use.
912int BasicMBRData::FindNextInUse(int start) {
913 if (start >= MAX_MBR_PARTS)
914 start = -1;
915 while ((start < MAX_MBR_PARTS) && (start >= 0) && (partitions[start].GetInclusion() == NONE))
916 start++;
917 if ((start < 0) || (start >= MAX_MBR_PARTS))
918 start = -1;
919 return start;
920} // BasicMBRData::FindFirstLogical();
srs5694f2efa7d2011-03-01 22:03:26 -0500921
922/*****************************************************
923 * *
924 * Functions to create, delete, or change partitions *
925 * *
926 *****************************************************/
927
928// Empty all data. Meant mainly for calling by constructors, but it's also
929// used by the hybrid MBR functions in the GPTData class.
930void BasicMBRData::EmptyMBR(int clearBootloader) {
931 int i;
932
933 // Zero out the boot loader section, the disk signature, and the
934 // 2-byte nulls area only if requested to do so. (This is the
935 // default.)
936 if (clearBootloader == 1) {
937 EmptyBootloader();
938 } // if
939
940 // Blank out the partitions
941 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500942 partitions[i].Empty();
srs5694f2efa7d2011-03-01 22:03:26 -0500943 } // for
944 MBRSignature = MBR_SIGNATURE;
srs5694bf8950c2011-03-12 01:23:12 -0500945 state = mbr;
srs5694f2efa7d2011-03-01 22:03:26 -0500946} // BasicMBRData::EmptyMBR()
947
948// Blank out the boot loader area. Done with the initial MBR-to-GPT
949// conversion, since MBR boot loaders don't understand GPT, and so
950// need to be replaced....
951void BasicMBRData::EmptyBootloader(void) {
952 int i;
953
954 for (i = 0; i < 440; i++)
955 code[i] = 0;
956 nulls = 0;
957} // BasicMBRData::EmptyBootloader
958
srs5694bf8950c2011-03-12 01:23:12 -0500959// Create a partition of the specified number based on the passed
960// partition. This function does *NO* error checking, so it's possible
srs5694f2efa7d2011-03-01 22:03:26 -0500961// to seriously screw up a partition table using this function!
962// Note: This function should NOT be used to create the 0xEE partition
963// in a conventional GPT configuration, since that partition has
964// specific size requirements that this function won't handle. It may
965// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
966// since those toss the rulebook away anyhow....
srs5694bf8950c2011-03-12 01:23:12 -0500967void BasicMBRData::AddPart(int num, const MBRPart& newPart) {
968 partitions[num] = newPart;
969} // BasicMBRData::AddPart()
970
971// Create a partition of the specified number, starting LBA, and
972// length. This function does almost no error checking, so it's possible
973// to seriously screw up a partition table using this function!
974// Note: This function should NOT be used to create the 0xEE partition
975// in a conventional GPT configuration, since that partition has
976// specific size requirements that this function won't handle. It may
977// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
978// since those toss the rulebook away anyhow....
979void BasicMBRData::MakePart(int num, uint64_t start, uint64_t length, int type, int bootable) {
980 if ((num >= 0) && (num < MAX_MBR_PARTS) && (start <= UINT32_MAX) && (length <= UINT32_MAX)) {
981 partitions[num].Empty();
982 partitions[num].SetType(type);
983 partitions[num].SetLocation(start, length);
984 if (num < 4)
985 partitions[num].SetInclusion(PRIMARY);
986 else
987 partitions[num].SetInclusion(LOGICAL);
srs5694f2efa7d2011-03-01 22:03:26 -0500988 SetPartBootable(num, bootable);
srs5694bf8950c2011-03-12 01:23:12 -0500989 } // if valid partition number & size
srs5694f2efa7d2011-03-01 22:03:26 -0500990} // BasicMBRData::MakePart()
991
992// Set the partition's type code.
993// Returns 1 if successful, 0 if not (invalid partition number)
994int BasicMBRData::SetPartType(int num, int type) {
995 int allOK = 1;
996
997 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -0500998 if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
999 allOK = partitions[num].SetType(type);
srs5694f2efa7d2011-03-01 22:03:26 -05001000 } else allOK = 0;
1001 } else allOK = 0;
1002 return allOK;
1003} // BasicMBRData::SetPartType()
1004
1005// Set (or remove) the partition's bootable flag. Setting it is the
1006// default; pass 0 as bootable to remove the flag.
1007// Returns 1 if successful, 0 if not (invalid partition number)
1008int BasicMBRData::SetPartBootable(int num, int bootable) {
1009 int allOK = 1;
1010
1011 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -05001012 if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
srs5694f2efa7d2011-03-01 22:03:26 -05001013 if (bootable == 0)
srs5694bf8950c2011-03-12 01:23:12 -05001014 partitions[num].SetStatus(UINT8_C(0x00));
srs5694f2efa7d2011-03-01 22:03:26 -05001015 else
srs5694bf8950c2011-03-12 01:23:12 -05001016 partitions[num].SetStatus(UINT8_C(0x80));
srs5694f2efa7d2011-03-01 22:03:26 -05001017 } else allOK = 0;
1018 } else allOK = 0;
1019 return allOK;
1020} // BasicMBRData::SetPartBootable()
1021
1022// Create a partition that fills the most available space. Returns
1023// 1 if partition was created, 0 otherwise. Intended for use in
1024// creating hybrid MBRs.
1025int BasicMBRData::MakeBiggestPart(int i, int type) {
srs5694bf8950c2011-03-12 01:23:12 -05001026 uint64_t start = UINT64_C(1); // starting point for each search
1027 uint64_t firstBlock; // first block in a segment
1028 uint64_t lastBlock; // last block in a segment
1029 uint64_t segmentSize; // size of segment in blocks
1030 uint64_t selectedSegment = UINT64_C(0); // location of largest segment
1031 uint64_t selectedSize = UINT64_C(0); // size of largest segment in blocks
srs5694f2efa7d2011-03-01 22:03:26 -05001032 int found = 0;
1033
1034 do {
1035 firstBlock = FindFirstAvailable(start);
srs5694bf8950c2011-03-12 01:23:12 -05001036 if (firstBlock > UINT64_C(0)) { // something's free...
srs5694f2efa7d2011-03-01 22:03:26 -05001037 lastBlock = FindLastInFree(firstBlock);
srs5694bf8950c2011-03-12 01:23:12 -05001038 segmentSize = lastBlock - firstBlock + UINT64_C(1);
srs5694f2efa7d2011-03-01 22:03:26 -05001039 if (segmentSize > selectedSize) {
1040 selectedSize = segmentSize;
1041 selectedSegment = firstBlock;
1042 } // if
1043 start = lastBlock + 1;
1044 } // if
1045 } while (firstBlock != 0);
srs5694bf8950c2011-03-12 01:23:12 -05001046 if ((selectedSize > UINT64_C(0)) && (selectedSize < diskSize)) {
srs5694f2efa7d2011-03-01 22:03:26 -05001047 found = 1;
1048 MakePart(i, selectedSegment, selectedSize, type, 0);
1049 } else {
1050 found = 0;
1051 } // if/else
1052 return found;
1053} // BasicMBRData::MakeBiggestPart(int i)
1054
1055// Delete partition #i
1056void BasicMBRData::DeletePartition(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001057 partitions[i].Empty();
srs5694f2efa7d2011-03-01 22:03:26 -05001058} // BasicMBRData::DeletePartition()
1059
srs5694bf8950c2011-03-12 01:23:12 -05001060// Set the inclusion status (PRIMARY, LOGICAL, or NONE) with some sanity
1061// checks to ensure the table remains legal.
1062// Returns 1 on success, 0 on failure.
1063int BasicMBRData::SetInclusionwChecks(int num, int inclStatus) {
1064 int allOK = 1, origValue;
1065
1066 if (IsLegal()) {
1067 if ((inclStatus == PRIMARY) || (inclStatus == LOGICAL) || (inclStatus == NONE)) {
1068 origValue = partitions[num].GetInclusion();
1069 partitions[num].SetInclusion(inclStatus);
1070 if (!IsLegal()) {
1071 partitions[num].SetInclusion(origValue);
1072 cerr << "Specified change is not legal! Aborting change!\n";
1073 } // if
1074 } else {
1075 cerr << "Invalid partition inclusion code in BasicMBRData::SetInclusionwChecks()!\n";
1076 } // if/else
1077 } else {
1078 cerr << "Partition table is not currently in a valid state. Aborting change!\n";
1079 allOK = 0;
1080 } // if/else
1081 return allOK;
1082} // BasicMBRData::SetInclusionwChecks()
1083
srs5694f2efa7d2011-03-01 22:03:26 -05001084// Recomputes the CHS values for the specified partition and adjusts the value.
1085// Note that this will create a technically incorrect CHS value for EFI GPT (0xEE)
1086// protective partitions, but this is required by some buggy BIOSes, so I'm
1087// providing a function to do this deliberately at the user's command.
1088// This function does nothing if the partition's length is 0.
1089void BasicMBRData::RecomputeCHS(int partNum) {
srs5694bf8950c2011-03-12 01:23:12 -05001090 partitions[partNum].RecomputeCHS();
srs5694f2efa7d2011-03-01 22:03:26 -05001091} // BasicMBRData::RecomputeCHS()
1092
srs5694699941e2011-03-21 21:33:57 -04001093// Sorts the partitions starting with partition #start. This function
1094// does NOT pay attention to primary/logical assignment, which is
1095// critical when writing the partitions.
srs5694bf8950c2011-03-12 01:23:12 -05001096void BasicMBRData::SortMBR(int start) {
srs5694c2f6e0c2011-03-16 02:42:33 -04001097 if ((start < MAX_MBR_PARTS) && (start >= 0))
1098 sort(partitions + start, partitions + MAX_MBR_PARTS);
1099} // BasicMBRData::SortMBR()
srs5694bf8950c2011-03-12 01:23:12 -05001100
1101// Delete any partitions that are too big to fit on the disk
1102// or that are too big for MBR (32-bit limits).
1103// This really deletes the partitions by setting values to 0.
1104// Returns the number of partitions deleted in this way.
1105int BasicMBRData::DeleteOversizedParts() {
1106 int num = 0, i;
1107
1108 for (i = 0; i < MAX_MBR_PARTS; i++) {
1109 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize) ||
1110 (partitions[i].GetStartLBA() > UINT32_MAX) || (partitions[i].GetLengthLBA() > UINT32_MAX)) {
1111 partitions[i].Empty();
1112 num++;
1113 } // if
1114 } // for
1115 return num;
1116} // BasicMBRData::DeleteOversizedParts()
1117
1118// Search for and delete extended partitions.
1119// Returns the number of partitions deleted.
1120int BasicMBRData::DeleteExtendedParts() {
1121 int i, numDeleted = 0;
1122 uint8_t type;
1123
1124 for (i = 0; i < MAX_MBR_PARTS; i++) {
1125 type = partitions[i].GetType();
1126 if (((type == 0x05) || (type == 0x0f) || (type == (0x85))) &&
1127 (partitions[i].GetLengthLBA() > 0)) {
1128 partitions[i].Empty();
1129 numDeleted++;
1130 } // if
1131 } // for
1132 return numDeleted;
1133} // BasicMBRData::DeleteExtendedParts()
1134
1135// Finds any overlapping partitions and omits the smaller of the two.
1136void BasicMBRData::OmitOverlaps() {
1137 int i, j;
1138
1139 for (i = 0; i < MAX_MBR_PARTS; i++) {
1140 for (j = i + 1; j < MAX_MBR_PARTS; j++) {
1141 if ((partitions[i].GetInclusion() != NONE) &&
1142 partitions[i].DoTheyOverlap(partitions[j])) {
1143 if (partitions[i].GetLengthLBA() < partitions[j].GetLengthLBA())
1144 partitions[i].SetInclusion(NONE);
1145 else
1146 partitions[j].SetInclusion(NONE);
srs5694f2efa7d2011-03-01 22:03:26 -05001147 } // if
srs5694bf8950c2011-03-12 01:23:12 -05001148 } // for (j...)
1149 } // for (i...)
1150} // BasicMBRData::OmitOverlaps()
1151
srs5694bf8950c2011-03-12 01:23:12 -05001152// Convert as many partitions into logicals as possible, except for
1153// the first partition, if possible.
1154void BasicMBRData::MaximizeLogicals() {
1155 int earliestPart = 0, earliestPartWas = NONE, i;
1156
1157 for (i = MAX_MBR_PARTS - 1; i >= 0; i--) {
1158 UpdateCanBeLogical();
1159 earliestPart = i;
1160 if (partitions[i].CanBeLogical()) {
1161 partitions[i].SetInclusion(LOGICAL);
1162 } else if (partitions[i].CanBePrimary()) {
1163 partitions[i].SetInclusion(PRIMARY);
1164 } else {
1165 partitions[i].SetInclusion(NONE);
1166 } // if/elseif/else
1167 } // for
1168 // If we have spare primaries, convert back the earliest partition to
1169 // its original state....
1170 if ((NumPrimaries() < 4) && (partitions[earliestPart].GetInclusion() == LOGICAL))
1171 partitions[earliestPart].SetInclusion(earliestPartWas);
1172} // BasicMBRData::MaximizeLogicals()
1173
1174// Add primaries up to the maximum allowed, from the omitted category.
1175void BasicMBRData::MaximizePrimaries() {
1176 int num, i = 0;
1177
1178 num = NumPrimaries();
1179 while ((num < 4) && (i < MAX_MBR_PARTS)) {
1180 if ((partitions[i].GetInclusion() == NONE) && (partitions[i].CanBePrimary())) {
1181 partitions[i].SetInclusion(PRIMARY);
srs5694699941e2011-03-21 21:33:57 -04001182 num++;
1183 UpdateCanBeLogical();
srs5694bf8950c2011-03-12 01:23:12 -05001184 } // if
1185 i++;
1186 } // while
1187} // BasicMBRData::MaximizePrimaries()
1188
1189// Remove primary partitions in excess of 4, starting with the later ones,
1190// in terms of the array location....
1191void BasicMBRData::TrimPrimaries(void) {
1192 int numToDelete, i = MAX_MBR_PARTS;
1193
1194 numToDelete = NumPrimaries() - 4;
1195 while ((numToDelete > 0) && (i >= 0)) {
1196 if (partitions[i].GetInclusion() == PRIMARY) {
1197 partitions[i].SetInclusion(NONE);
1198 numToDelete--;
1199 } // if
1200 i--;
1201 } // while (numToDelete > 0)
1202} // BasicMBRData::TrimPrimaries()
1203
1204// Locates primary partitions located between logical partitions and
1205// either converts the primaries into logicals (if possible) or omits
1206// them.
1207void BasicMBRData::MakeLogicalsContiguous(void) {
1208 uint64_t firstLogicalLBA, lastLogicalLBA;
1209 int i;
1210
1211 firstLogicalLBA = FirstLogicalLBA();
1212 lastLogicalLBA = LastLogicalLBA();
1213 for (i = 0; i < MAX_MBR_PARTS; i++) {
1214 if ((partitions[i].GetInclusion() == PRIMARY) &&
1215 (partitions[i].GetStartLBA() >= firstLogicalLBA) &&
1216 (partitions[i].GetLastLBA() <= lastLogicalLBA)) {
1217 if (SectorUsedAs(partitions[i].GetStartLBA() - 1) == NONE)
1218 partitions[i].SetInclusion(LOGICAL);
1219 else
1220 partitions[i].SetInclusion(NONE);
1221 } // if
1222 } // for
1223} // BasicMBRData::MakeLogicalsContiguous()
1224
1225// If MBR data aren't legal, adjust primary/logical assignments and,
1226// if necessary, drop partitions, to make the data legal.
1227void BasicMBRData::MakeItLegal(void) {
1228 if (!IsLegal()) {
1229 DeleteOversizedParts();
srs5694bf8950c2011-03-12 01:23:12 -05001230 MaximizeLogicals();
1231 MaximizePrimaries();
1232 if (!AreLogicalsContiguous())
1233 MakeLogicalsContiguous();
1234 if (NumPrimaries() > 4)
1235 TrimPrimaries();
1236 OmitOverlaps();
1237 } // if
1238} // BasicMBRData::MakeItLegal()
1239
1240// Removes logical partitions and deactivated partitions from first four
1241// entries (primary space).
1242// Returns the number of partitions moved.
1243int BasicMBRData::RemoveLogicalsFromFirstFour(void) {
1244 int i, j = 4, numMoved = 0, swapped = 0;
1245 MBRPart temp;
1246
1247 for (i = 0; i < 4; i++) {
1248 if ((partitions[i].GetInclusion() != PRIMARY) && (partitions[i].GetLengthLBA() > 0)) {
1249 j = 4;
1250 swapped = 0;
1251 do {
1252 if ((partitions[j].GetInclusion() == NONE) && (partitions[j].GetLengthLBA() == 0)) {
1253 temp = partitions[j];
1254 partitions[j] = partitions[i];
1255 partitions[i] = temp;
1256 swapped = 1;
1257 numMoved++;
1258 } // if
1259 j++;
1260 } while ((j < MAX_MBR_PARTS) && !swapped);
1261 if (j >= MAX_MBR_PARTS)
1262 cerr << "Warning! Too many partitions in BasicMBRData::RemoveLogicalsFromFirstFour()!\n";
1263 } // if
1264 } // for i...
1265 return numMoved;
1266} // BasicMBRData::RemoveLogicalsFromFirstFour()
1267
1268// Move all primaries into the first four partition spaces
1269// Returns the number of partitions moved.
1270int BasicMBRData::MovePrimariesToFirstFour(void) {
1271 int i, j = 0, numMoved = 0, swapped = 0;
1272 MBRPart temp;
1273
1274 for (i = 4; i < MAX_MBR_PARTS; i++) {
1275 if (partitions[i].GetInclusion() == PRIMARY) {
1276 j = 0;
1277 swapped = 0;
1278 do {
1279 if (partitions[j].GetInclusion() != PRIMARY) {
1280 temp = partitions[j];
1281 partitions[j] = partitions[i];
1282 partitions[i] = temp;
1283 swapped = 1;
1284 numMoved++;
1285 } // if
1286 j++;
1287 } while ((j < 4) && !swapped);
1288 } // if
1289 } // for
1290 return numMoved;
1291} // BasicMBRData::MovePrimariesToFirstFour()
1292
1293// Create an extended partition, if necessary, to hold the logical partitions.
1294// This function also sorts the primaries into the first four positions of
1295// the table.
1296// Returns 1 on success, 0 on failure.
1297int BasicMBRData::CreateExtended(void) {
1298 int allOK = 1, i = 0, swapped = 0;
1299 MBRPart temp;
1300
1301 if (IsLegal()) {
1302 // Move logicals out of primary space...
1303 RemoveLogicalsFromFirstFour();
1304 // Move primaries out of logical space...
1305 MovePrimariesToFirstFour();
1306
1307 // Create the extended partition
1308 if (NumLogicals() > 0) {
1309 SortMBR(4); // sort starting from 4 -- that is, logicals only
1310 temp.Empty();
1311 temp.SetStartLBA(FirstLogicalLBA() - 1);
1312 temp.SetLengthLBA(LastLogicalLBA() - FirstLogicalLBA() + 2);
1313 temp.SetType(0x0f, 1);
1314 temp.SetInclusion(PRIMARY);
1315 do {
1316 if ((partitions[i].GetInclusion() == NONE) || (partitions[i].GetLengthLBA() == 0)) {
1317 partitions[i] = temp;
1318 swapped = 1;
1319 } // if
1320 i++;
1321 } while ((i < 4) && !swapped);
1322 if (!swapped) {
1323 cerr << "Could not create extended partition; no room in primary table!\n";
1324 allOK = 0;
1325 } // if
1326 } // if (NumLogicals() > 0)
1327 } else allOK = 0;
1328 // Do a final check for EFI GPT (0xEE) partitions & flag as a problem if found
1329 // along with an extended partition
1330 for (i = 0; i < MAX_MBR_PARTS; i++)
1331 if (swapped && partitions[i].GetType() == 0xEE)
1332 allOK = 0;
1333 return allOK;
1334} // BasicMBRData::CreateExtended()
srs5694f2efa7d2011-03-01 22:03:26 -05001335
1336/****************************************
1337 * *
1338 * Functions to find data on free space *
1339 * *
1340 ****************************************/
1341
1342// Finds the first free space on the disk from start onward; returns 0
1343// if none available....
srs5694bf8950c2011-03-12 01:23:12 -05001344uint64_t BasicMBRData::FindFirstAvailable(uint64_t start) {
1345 uint64_t first;
1346 uint64_t i;
srs5694f2efa7d2011-03-01 22:03:26 -05001347 int firstMoved;
1348
1349 first = start;
1350
1351 // ...now search through all partitions; if first is within an
1352 // existing partition, move it to the next sector after that
1353 // partition and repeat. If first was moved, set firstMoved
1354 // flag; repeat until firstMoved is not set, so as to catch
1355 // cases where partitions are out of sequential order....
1356 do {
1357 firstMoved = 0;
1358 for (i = 0; i < 4; i++) {
1359 // Check if it's in the existing partition
srs5694bf8950c2011-03-12 01:23:12 -05001360 if ((first >= partitions[i].GetStartLBA()) &&
1361 (first < (partitions[i].GetStartLBA() + partitions[i].GetLengthLBA()))) {
1362 first = partitions[i].GetStartLBA() + partitions[i].GetLengthLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001363 firstMoved = 1;
1364 } // if
1365 } // for
1366 } while (firstMoved == 1);
srs5694bf8950c2011-03-12 01:23:12 -05001367 if ((first >= diskSize) || (first > UINT32_MAX))
srs5694f2efa7d2011-03-01 22:03:26 -05001368 first = 0;
1369 return (first);
1370} // BasicMBRData::FindFirstAvailable()
1371
1372// Finds the last free sector on the disk from start forward.
srs5694bf8950c2011-03-12 01:23:12 -05001373uint64_t BasicMBRData::FindLastInFree(uint64_t start) {
1374 uint64_t nearestStart;
1375 uint64_t i;
srs5694f2efa7d2011-03-01 22:03:26 -05001376
1377 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs5694bf8950c2011-03-12 01:23:12 -05001378 nearestStart = diskSize - 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001379 else
1380 nearestStart = UINT32_MAX - 1;
srs5694699941e2011-03-21 21:33:57 -04001381
srs5694f2efa7d2011-03-01 22:03:26 -05001382 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -05001383 if ((nearestStart > partitions[i].GetStartLBA()) &&
1384 (partitions[i].GetStartLBA() > start)) {
1385 nearestStart = partitions[i].GetStartLBA() - 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001386 } // if
1387 } // for
1388 return (nearestStart);
1389} // BasicMBRData::FindLastInFree()
1390
1391// Finds the first free sector on the disk from start backward.
srs5694bf8950c2011-03-12 01:23:12 -05001392uint64_t BasicMBRData::FindFirstInFree(uint64_t start) {
1393 uint64_t bestLastLBA, thisLastLBA;
srs5694f2efa7d2011-03-01 22:03:26 -05001394 int i;
1395
1396 bestLastLBA = 1;
1397 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -05001398 thisLastLBA = partitions[i].GetLastLBA() + 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001399 if (thisLastLBA > 0)
1400 thisLastLBA--;
1401 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start))
1402 bestLastLBA = thisLastLBA + 1;
1403 } // for
1404 return (bestLastLBA);
1405} // BasicMBRData::FindFirstInFree()
1406
srs5694bf8950c2011-03-12 01:23:12 -05001407// Returns NONE (unused), PRIMARY, LOGICAL, EBR (for EBR or MBR), or INVALID
1408int BasicMBRData::SectorUsedAs(uint64_t sector, int topPartNum) {
1409 int i = 0, usedAs = NONE;
srs5694f2efa7d2011-03-01 22:03:26 -05001410
srs5694bf8950c2011-03-12 01:23:12 -05001411 do {
1412 if ((partitions[i].GetStartLBA() <= sector) && (partitions[i].GetLastLBA() >= sector))
1413 usedAs = partitions[i].GetInclusion();
1414 if ((partitions[i].GetStartLBA() == (sector + 1)) && (partitions[i].GetInclusion() == LOGICAL))
1415 usedAs = EBR;
1416 if (sector == 0)
1417 usedAs = EBR;
1418 if (sector >= diskSize)
1419 usedAs = INVALID;
1420 i++;
1421 } while ((i < topPartNum) && (usedAs == NONE));
1422 return usedAs;
1423} // BasicMBRData::SectorUsedAs()
1424
srs5694f2efa7d2011-03-01 22:03:26 -05001425/******************************************************
1426 * *
1427 * Functions that extract data on specific partitions *
1428 * *
1429 ******************************************************/
1430
1431uint8_t BasicMBRData::GetStatus(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001432 MBRPart* thePart;
srs5694f2efa7d2011-03-01 22:03:26 -05001433 uint8_t retval;
1434
1435 thePart = GetPartition(i);
1436 if (thePart != NULL)
srs5694bf8950c2011-03-12 01:23:12 -05001437 retval = thePart->GetStatus();
srs5694f2efa7d2011-03-01 22:03:26 -05001438 else
1439 retval = UINT8_C(0);
1440 return retval;
1441} // BasicMBRData::GetStatus()
1442
1443uint8_t BasicMBRData::GetType(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001444 MBRPart* thePart;
srs5694f2efa7d2011-03-01 22:03:26 -05001445 uint8_t retval;
1446
1447 thePart = GetPartition(i);
1448 if (thePart != NULL)
srs5694bf8950c2011-03-12 01:23:12 -05001449 retval = thePart->GetType();
srs5694f2efa7d2011-03-01 22:03:26 -05001450 else
1451 retval = UINT8_C(0);
1452 return retval;
1453} // BasicMBRData::GetType()
1454
srs5694bf8950c2011-03-12 01:23:12 -05001455uint64_t BasicMBRData::GetFirstSector(int i) {
1456 MBRPart* thePart;
1457 uint64_t retval;
srs5694f2efa7d2011-03-01 22:03:26 -05001458
1459 thePart = GetPartition(i);
1460 if (thePart != NULL) {
srs5694bf8950c2011-03-12 01:23:12 -05001461 retval = thePart->GetStartLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001462 } else
1463 retval = UINT32_C(0);
1464 return retval;
1465} // BasicMBRData::GetFirstSector()
1466
srs5694bf8950c2011-03-12 01:23:12 -05001467uint64_t BasicMBRData::GetLength(int i) {
1468 MBRPart* thePart;
1469 uint64_t retval;
srs5694f2efa7d2011-03-01 22:03:26 -05001470
1471 thePart = GetPartition(i);
1472 if (thePart != NULL) {
srs5694bf8950c2011-03-12 01:23:12 -05001473 retval = thePart->GetLengthLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001474 } else
srs5694bf8950c2011-03-12 01:23:12 -05001475 retval = UINT64_C(0);
srs5694f2efa7d2011-03-01 22:03:26 -05001476 return retval;
1477} // BasicMBRData::GetLength()
1478
1479/***********************
1480 * *
1481 * Protected functions *
1482 * *
1483 ***********************/
1484
1485// Return a pointer to a primary or logical partition, or NULL if
1486// the partition is out of range....
srs5694bf8950c2011-03-12 01:23:12 -05001487MBRPart* BasicMBRData::GetPartition(int i) {
1488 MBRPart* thePart = NULL;
srs5694f2efa7d2011-03-01 22:03:26 -05001489
1490 if ((i >= 0) && (i < MAX_MBR_PARTS))
1491 thePart = &partitions[i];
1492 return thePart;
1493} // GetPartition()
srs5694bf8950c2011-03-12 01:23:12 -05001494
1495/*******************************************
1496 * *
1497 * Functions that involve user interaction *
1498 * *
1499 *******************************************/
1500
1501// Present the MBR operations menu. Note that the 'w' option does not
1502// immediately write data; that's handled by the calling function.
1503// Returns the number of partitions defined on exit, or -1 if the
1504// user selected the 'q' option. (Thus, the caller should save data
1505// if the return value is >0, or possibly >=0 depending on intentions.)
1506int BasicMBRData::DoMenu(const string& prompt) {
srs5694bf8950c2011-03-12 01:23:12 -05001507 int goOn = 1, quitting = 0, retval, num, haveShownInfo = 0;
srs56946aae2a92011-06-10 01:16:51 -04001508 unsigned int hexCode;
1509 string tempStr;
srs5694bf8950c2011-03-12 01:23:12 -05001510
1511 do {
1512 cout << prompt;
srs56945a608532011-03-17 13:53:01 -04001513 switch (ReadString()[0]) {
1514 case '\0':
srs5694bf8950c2011-03-12 01:23:12 -05001515 break;
1516 case 'a': case 'A':
1517 num = GetNumber(1, MAX_MBR_PARTS, 1, "Toggle active flag for partition: ") - 1;
1518 if (partitions[num].GetInclusion() != NONE)
1519 partitions[num].SetStatus(partitions[num].GetStatus() ^ 0x80);
1520 break;
1521 case 'c': case 'C':
1522 for (num = 0; num < MAX_MBR_PARTS; num++)
1523 RecomputeCHS(num);
1524 break;
1525 case 'l': case 'L':
1526 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as logical: ") - 1;
1527 SetInclusionwChecks(num, LOGICAL);
1528 break;
1529 case 'o': case 'O':
1530 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to omit: ") - 1;
1531 SetInclusionwChecks(num, NONE);
1532 break;
1533 case 'p': case 'P':
1534 if (!haveShownInfo) {
1535 cout << "\n** NOTE: Partition numbers do NOT indicate final primary/logical "
1536 << "status,\n** unlike in most MBR partitioning tools!\n\a";
1537 cout << "\n** Extended partitions are not displayed, but will be generated "
1538 << "as required.\n";
1539 haveShownInfo = 1;
1540 } // if
1541 DisplayMBRData();
1542 break;
1543 case 'q': case 'Q':
1544 cout << "This will abandon your changes. Are you sure? ";
1545 if (GetYN() == 'Y') {
1546 goOn = 0;
1547 quitting = 1;
1548 } // if
1549 break;
1550 case 'r': case 'R':
1551 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as primary: ") - 1;
1552 SetInclusionwChecks(num, PRIMARY);
1553 break;
1554 case 's': case 'S':
1555 SortMBR();
1556 break;
1557 case 't': case 'T':
1558 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to change type code: ") - 1;
srs56946aae2a92011-06-10 01:16:51 -04001559 hexCode = 0x00;
srs5694bf8950c2011-03-12 01:23:12 -05001560 if (partitions[num].GetLengthLBA() > 0) {
srs5694bf8950c2011-03-12 01:23:12 -05001561 while ((hexCode <= 0) || (hexCode > 255)) {
1562 cout << "Enter an MBR hex code: ";
srs56946aae2a92011-06-10 01:16:51 -04001563 tempStr = ReadString();
1564 if (IsHex(tempStr))
1565 sscanf(tempStr.c_str(), "%x", &hexCode);
srs5694bf8950c2011-03-12 01:23:12 -05001566 } // while
1567 partitions[num].SetType(hexCode);
1568 } // if
1569 break;
1570 case 'w': case 'W':
1571 goOn = 0;
1572 break;
1573 default:
1574 ShowCommands();
1575 break;
1576 } // switch
1577 } while (goOn);
1578 if (quitting)
1579 retval = -1;
1580 else
1581 retval = CountParts();
1582 return (retval);
1583} // BasicMBRData::DoMenu()
1584
1585void BasicMBRData::ShowCommands(void) {
1586 cout << "a\ttoggle the active/boot flag\n";
1587 cout << "c\trecompute all CHS values\n";
1588 cout << "l\tset partition as logical\n";
1589 cout << "o\tomit partition\n";
1590 cout << "p\tprint the MBR partition table\n";
1591 cout << "q\tquit without saving changes\n";
1592 cout << "r\tset partition as primary\n";
1593 cout << "s\tsort MBR partitions\n";
1594 cout << "t\tchange partition type code\n";
1595 cout << "w\twrite the MBR partition table to disk and exit\n";
1596} // BasicMBRData::ShowCommands()