blob: 797c4f258a7a13f5c3f2a1bf71e615c31845f859 [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).
srs569400b6d7a2011-06-26 22:40:06 -0400133// Note that any extended partition(s) present will be omitted from
134// in the partitions[] array; these partitions must be re-created when
135// the partition table is saved in MBR format.
srs5694f2efa7d2011-03-01 22:03:26 -0500136int 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) {
srs5694a17fe692011-09-10 20:30:20 -0400609 int err;
610
srs5694bf8950c2011-03-12 01:23:12 -0500611 numHeads = myDisk->GetNumHeads();
612 numSecspTrack = myDisk->GetNumSecsPerTrack();
srs5694a17fe692011-09-10 20:30:20 -0400613 diskSize = myDisk->DiskSize(&err);
614 blockSize = myDisk->GetBlockSize();
srs5694bf8950c2011-03-12 01:23:12 -0500615 partitions[0].SetGeometry(numHeads, numSecspTrack, diskSize, blockSize);
616} // BasicMBRData::ReadCHSGeom()
617
618// Find the low and high used partition numbers (numbered from 0).
619// Return value is the number of partitions found. Note that the
620// *low and *high values are both set to 0 when no partitions
621// are found, as well as when a single partition in the first
622// position exists. Thus, the return value is the only way to
623// tell when no partitions exist.
624int BasicMBRData::GetPartRange(uint32_t *low, uint32_t *high) {
625 uint32_t i;
626 int numFound = 0;
627
628 *low = MAX_MBR_PARTS + 1; // code for "not found"
629 *high = 0;
630 for (i = 0; i < MAX_MBR_PARTS; i++) {
631 if (partitions[i].GetStartLBA() != UINT32_C(0)) { // it exists
632 *high = i; // since we're counting up, set the high value
633 // Set the low value only if it's not yet found...
634 if (*low == (MAX_MBR_PARTS + 1))
635 *low = i;
636 numFound++;
637 } // if
638 } // for
639
640 // Above will leave *low pointing to its "not found" value if no partitions
641 // are defined, so reset to 0 if this is the case....
642 if (*low == (MAX_MBR_PARTS + 1))
643 *low = 0;
644 return numFound;
645} // GPTData::GetPartRange()
srs5694f2efa7d2011-03-01 22:03:26 -0500646
647// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
648// was within the range that can be expressed by CHS (including 0, for an
649// empty partition), 0 if the value is outside that range, and -1 if chs is
650// invalid.
651int BasicMBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
652 uint64_t cylinder, head, sector; // all numbered from 0
653 uint64_t remainder;
654 int retval = 1;
655 int done = 0;
656
657 if (chs != NULL) {
658 // Special case: In case of 0 LBA value, zero out CHS values....
659 if (lba == 0) {
660 chs[0] = chs[1] = chs[2] = UINT8_C(0);
661 done = 1;
662 } // if
663 // If LBA value is too large for CHS, max out CHS values....
664 if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
665 chs[0] = 254;
666 chs[1] = chs[2] = 255;
667 done = 1;
668 retval = 0;
669 } // if
670 // If neither of the above applies, compute CHS values....
671 if (!done) {
672 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
673 remainder = lba - (cylinder * numHeads * numSecspTrack);
674 head = remainder / numSecspTrack;
675 remainder -= head * numSecspTrack;
676 sector = remainder;
677 if (head < numHeads)
678 chs[0] = (uint8_t) head;
679 else
680 retval = 0;
681 if (sector < numSecspTrack) {
682 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
683 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
684 } else {
685 retval = 0;
686 } // if/else
687 } // if value is expressible and non-0
688 } else { // Invalid (NULL) chs pointer
689 retval = -1;
690 } // if CHS pointer valid
691 return (retval);
692} // BasicMBRData::LBAtoCHS()
693
srs5694bf8950c2011-03-12 01:23:12 -0500694// Look for overlapping partitions.
695// Returns the number of problems found
696int BasicMBRData::FindOverlaps(void) {
697 int i, j, numProbs = 0, numEE = 0;
srs5694f2efa7d2011-03-01 22:03:26 -0500698
699 for (i = 0; i < MAX_MBR_PARTS; i++) {
700 for (j = i + 1; j < MAX_MBR_PARTS; j++) {
srs5694bf8950c2011-03-12 01:23:12 -0500701 if ((partitions[i].GetInclusion() != NONE) &&
702 (partitions[i].DoTheyOverlap(partitions[j]))) {
srs5694f2efa7d2011-03-01 22:03:26 -0500703 numProbs++;
704 cout << "\nProblem: MBR partitions " << i + 1 << " and " << j + 1
705 << " overlap!\n";
706 } // if
707 } // for (j...)
srs5694bf8950c2011-03-12 01:23:12 -0500708 if (partitions[i].GetType() == 0xEE) {
srs5694f2efa7d2011-03-01 22:03:26 -0500709 numEE++;
srs5694bf8950c2011-03-12 01:23:12 -0500710 if (partitions[i].GetStartLBA() != 1)
711 cout << "\nWarning: 0xEE partition doesn't start on sector 1. This can cause "
712 << "problems\nin some OSes.\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500713 } // if
714 } // for (i...)
715 if (numEE > 1)
716 cout << "\nCaution: More than one 0xEE MBR partition found. This can cause problems\n"
717 << "in some OSes.\n";
718
719 return numProbs;
srs5694bf8950c2011-03-12 01:23:12 -0500720} // BasicMBRData::FindOverlaps()
721
722// Returns the number of primary partitions, including the extended partition
723// required to hold any logical partitions found.
724int BasicMBRData::NumPrimaries(void) {
725 int i, numPrimaries = 0, logicalsFound = 0;
726
727 for (i = 0; i < MAX_MBR_PARTS; i++) {
728 if (partitions[i].GetLengthLBA() > 0) {
729 if (partitions[i].GetInclusion() == PRIMARY)
730 numPrimaries++;
731 if (partitions[i].GetInclusion() == LOGICAL)
732 logicalsFound = 1;
733 } // if
734 } // for
735 return (numPrimaries + logicalsFound);
736} // BasicMBRData::NumPrimaries()
737
738// Returns the number of logical partitions.
739int BasicMBRData::NumLogicals(void) {
740 int i, numLogicals = 0;
741
742 for (i = 0; i < MAX_MBR_PARTS; i++) {
743 if (partitions[i].GetInclusion() == LOGICAL)
744 numLogicals++;
745 } // for
746 return numLogicals;
747} // BasicMBRData::NumLogicals()
748
749// Returns the number of partitions (primaries plus logicals), NOT including
750// the extended partition required to house the logicals.
751int BasicMBRData::CountParts(void) {
752 int i, num = 0;
753
754 for (i = 0; i < MAX_MBR_PARTS; i++) {
755 if ((partitions[i].GetInclusion() == LOGICAL) ||
756 (partitions[i].GetInclusion() == PRIMARY))
757 num++;
758 } // for
759 return num;
760} // BasicMBRData::CountParts()
761
762// Updates the canBeLogical and canBePrimary flags for all the partitions.
763void BasicMBRData::UpdateCanBeLogical(void) {
764 int i, j, sectorBefore, numPrimaries, numLogicals, usedAsEBR;
765 uint64_t firstLogical, lastLogical, lStart, pStart;
766
767 numPrimaries = NumPrimaries();
768 numLogicals = NumLogicals();
769 firstLogical = FirstLogicalLBA() - 1;
770 lastLogical = LastLogicalLBA();
771 for (i = 0; i < MAX_MBR_PARTS; i++) {
772 usedAsEBR = (SectorUsedAs(partitions[i].GetLastLBA()) == EBR);
773 if (usedAsEBR) {
774 partitions[i].SetCanBeLogical(0);
775 partitions[i].SetCanBePrimary(0);
776 } else if (partitions[i].GetLengthLBA() > 0) {
777 // First determine if it can be logical....
778 sectorBefore = SectorUsedAs(partitions[i].GetStartLBA() - 1);
779 lStart = partitions[i].GetStartLBA(); // start of potential logical part.
780 if ((lastLogical > 0) &&
781 ((sectorBefore == EBR) || (sectorBefore == NONE))) {
782 // Assume it can be logical, then search for primaries that make it
783 // not work and, if found, flag appropriately.
784 partitions[i].SetCanBeLogical(1);
785 for (j = 0; j < MAX_MBR_PARTS; j++) {
786 if ((i != j) && (partitions[j].GetInclusion() == PRIMARY)) {
787 pStart = partitions[j].GetStartLBA();
788 if (((pStart < lStart) && (firstLogical < pStart)) ||
789 ((pStart > lStart) && (firstLogical > pStart))) {
790 partitions[i].SetCanBeLogical(0);
791 } // if/else
792 } // if
793 } // for
794 } else {
795 if ((sectorBefore != EBR) && (sectorBefore != NONE))
796 partitions[i].SetCanBeLogical(0);
797 else
798 partitions[i].SetCanBeLogical(lastLogical == 0); // can be logical only if no logicals already
799 } // if/else
800 // Now determine if it can be primary. Start by assuming it can be...
801 partitions[i].SetCanBePrimary(1);
802 if ((numPrimaries >= 4) && (partitions[i].GetInclusion() != PRIMARY)) {
803 partitions[i].SetCanBePrimary(0);
804 if ((partitions[i].GetInclusion() == LOGICAL) && (numLogicals == 1) &&
805 (numPrimaries == 4))
806 partitions[i].SetCanBePrimary(1);
807 } // if
808 if ((partitions[i].GetStartLBA() > (firstLogical + 1)) &&
809 (partitions[i].GetLastLBA() < lastLogical))
810 partitions[i].SetCanBePrimary(0);
811 } // else if
812 } // for
813} // BasicMBRData::UpdateCanBeLogical()
814
815// Returns the first sector occupied by any logical partition. Note that
816// this does NOT include the logical partition's EBR! Returns UINT32_MAX
817// if there are no logical partitions defined.
818uint64_t BasicMBRData::FirstLogicalLBA(void) {
819 int i;
820 uint64_t firstFound = UINT32_MAX;
821
822 for (i = 0; i < MAX_MBR_PARTS; i++) {
823 if ((partitions[i].GetInclusion() == LOGICAL) &&
824 (partitions[i].GetStartLBA() < firstFound)) {
825 firstFound = partitions[i].GetStartLBA();
826 } // if
827 } // for
828 return firstFound;
829} // BasicMBRData::FirstLogicalLBA()
830
831// Returns the last sector occupied by any logical partition, or 0 if
832// there are no logical partitions defined.
833uint64_t BasicMBRData::LastLogicalLBA(void) {
834 int i;
835 uint64_t lastFound = 0;
836
837 for (i = 0; i < MAX_MBR_PARTS; i++) {
838 if ((partitions[i].GetInclusion() == LOGICAL) &&
839 (partitions[i].GetLastLBA() > lastFound))
840 lastFound = partitions[i].GetLastLBA();
841 } // for
842 return lastFound;
843} // BasicMBRData::LastLogicalLBA()
844
845// Returns 1 if logical partitions are contiguous (have no primaries
846// in their midst), or 0 if one or more primaries exist between
847// logicals.
848int BasicMBRData::AreLogicalsContiguous(void) {
849 int allOK = 1, i = 0;
850 uint64_t firstLogical, lastLogical;
851
852 firstLogical = FirstLogicalLBA() - 1; // subtract 1 for EBR
853 lastLogical = LastLogicalLBA();
854 if (lastLogical > 0) {
855 do {
856 if ((partitions[i].GetInclusion() == PRIMARY) &&
857 (partitions[i].GetStartLBA() >= firstLogical) &&
858 (partitions[i].GetStartLBA() <= lastLogical)) {
859 allOK = 0;
860 } // if
861 i++;
862 } while ((i < MAX_MBR_PARTS) && allOK);
863 } // if
864 return allOK;
865} // BasicMBRData::AreLogicalsContiguous()
866
867// Returns 1 if all partitions fit on the disk, given its size; 0 if any
868// partition is too big.
869int BasicMBRData::DoTheyFit(void) {
870 int i, allOK = 1;
871
872 for (i = 0; i < MAX_MBR_PARTS; i++) {
873 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize)) {
874 allOK = 0;
875 } // if
876 } // for
877 return allOK;
878} // BasicMBRData::DoTheyFit(void)
879
880// Returns 1 if there's at least one free sector immediately preceding
881// all partitions flagged as logical; 0 if any logical partition lacks
882// this space.
883int BasicMBRData::SpaceBeforeAllLogicals(void) {
884 int i = 0, allOK = 1;
885
886 do {
887 if ((partitions[i].GetStartLBA() > 0) && (partitions[i].GetInclusion() == LOGICAL)) {
888 allOK = allOK && (SectorUsedAs(partitions[i].GetStartLBA() - 1) == EBR);
srs5694bf8950c2011-03-12 01:23:12 -0500889 } // if
890 i++;
891 } while (allOK && (i < MAX_MBR_PARTS));
892 return allOK;
893} // BasicMBRData::SpaceBeforeAllLogicals()
894
895// Returns 1 if the partitions describe a legal layout -- all logicals
896// are contiguous and have at least one preceding empty partitions,
897// the number of primaries is under 4 (or under 3 if there are any
898// logicals), there are no overlapping partitions, etc.
899// Does NOT assume that primaries are numbered 1-4; uses the
900// IsItPrimary() function of the MBRPart class to determine
901// primary status. Also does NOT consider partition order; there
902// can be gaps and it will still be considered legal.
903int BasicMBRData::IsLegal(void) {
904 int allOK = 1;
905
906 allOK = (FindOverlaps() == 0);
907 allOK = (allOK && (NumPrimaries() <= 4));
908 allOK = (allOK && AreLogicalsContiguous());
909 allOK = (allOK && DoTheyFit());
910 allOK = (allOK && SpaceBeforeAllLogicals());
911 return allOK;
912} // BasicMBRData::IsLegal()
913
914// Finds the next in-use partition, starting with start (will return start
915// if it's in use). Returns -1 if no subsequent partition is in use.
916int BasicMBRData::FindNextInUse(int start) {
917 if (start >= MAX_MBR_PARTS)
918 start = -1;
919 while ((start < MAX_MBR_PARTS) && (start >= 0) && (partitions[start].GetInclusion() == NONE))
920 start++;
921 if ((start < 0) || (start >= MAX_MBR_PARTS))
922 start = -1;
923 return start;
924} // BasicMBRData::FindFirstLogical();
srs5694f2efa7d2011-03-01 22:03:26 -0500925
926/*****************************************************
927 * *
928 * Functions to create, delete, or change partitions *
929 * *
930 *****************************************************/
931
932// Empty all data. Meant mainly for calling by constructors, but it's also
933// used by the hybrid MBR functions in the GPTData class.
934void BasicMBRData::EmptyMBR(int clearBootloader) {
935 int i;
936
937 // Zero out the boot loader section, the disk signature, and the
938 // 2-byte nulls area only if requested to do so. (This is the
939 // default.)
940 if (clearBootloader == 1) {
941 EmptyBootloader();
942 } // if
943
944 // Blank out the partitions
945 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500946 partitions[i].Empty();
srs5694f2efa7d2011-03-01 22:03:26 -0500947 } // for
948 MBRSignature = MBR_SIGNATURE;
srs5694bf8950c2011-03-12 01:23:12 -0500949 state = mbr;
srs5694f2efa7d2011-03-01 22:03:26 -0500950} // BasicMBRData::EmptyMBR()
951
952// Blank out the boot loader area. Done with the initial MBR-to-GPT
953// conversion, since MBR boot loaders don't understand GPT, and so
954// need to be replaced....
955void BasicMBRData::EmptyBootloader(void) {
956 int i;
957
958 for (i = 0; i < 440; i++)
959 code[i] = 0;
960 nulls = 0;
961} // BasicMBRData::EmptyBootloader
962
srs5694bf8950c2011-03-12 01:23:12 -0500963// Create a partition of the specified number based on the passed
964// partition. This function does *NO* error checking, so it's possible
srs5694f2efa7d2011-03-01 22:03:26 -0500965// to seriously screw up a partition table using this function!
966// Note: This function should NOT be used to create the 0xEE partition
967// in a conventional GPT configuration, since that partition has
968// specific size requirements that this function won't handle. It may
969// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
970// since those toss the rulebook away anyhow....
srs5694bf8950c2011-03-12 01:23:12 -0500971void BasicMBRData::AddPart(int num, const MBRPart& newPart) {
972 partitions[num] = newPart;
973} // BasicMBRData::AddPart()
974
975// Create a partition of the specified number, starting LBA, and
976// length. This function does almost no error checking, so it's possible
977// to seriously screw up a partition table using this function!
978// Note: This function should NOT be used to create the 0xEE partition
979// in a conventional GPT configuration, since that partition has
980// specific size requirements that this function won't handle. It may
981// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
982// since those toss the rulebook away anyhow....
983void BasicMBRData::MakePart(int num, uint64_t start, uint64_t length, int type, int bootable) {
984 if ((num >= 0) && (num < MAX_MBR_PARTS) && (start <= UINT32_MAX) && (length <= UINT32_MAX)) {
985 partitions[num].Empty();
986 partitions[num].SetType(type);
987 partitions[num].SetLocation(start, length);
988 if (num < 4)
989 partitions[num].SetInclusion(PRIMARY);
990 else
991 partitions[num].SetInclusion(LOGICAL);
srs5694f2efa7d2011-03-01 22:03:26 -0500992 SetPartBootable(num, bootable);
srs5694bf8950c2011-03-12 01:23:12 -0500993 } // if valid partition number & size
srs5694f2efa7d2011-03-01 22:03:26 -0500994} // BasicMBRData::MakePart()
995
996// Set the partition's type code.
997// Returns 1 if successful, 0 if not (invalid partition number)
998int BasicMBRData::SetPartType(int num, int type) {
999 int allOK = 1;
1000
1001 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -05001002 if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
1003 allOK = partitions[num].SetType(type);
srs5694f2efa7d2011-03-01 22:03:26 -05001004 } else allOK = 0;
1005 } else allOK = 0;
1006 return allOK;
1007} // BasicMBRData::SetPartType()
1008
1009// Set (or remove) the partition's bootable flag. Setting it is the
1010// default; pass 0 as bootable to remove the flag.
1011// Returns 1 if successful, 0 if not (invalid partition number)
1012int BasicMBRData::SetPartBootable(int num, int bootable) {
1013 int allOK = 1;
1014
1015 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -05001016 if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
srs5694f2efa7d2011-03-01 22:03:26 -05001017 if (bootable == 0)
srs5694bf8950c2011-03-12 01:23:12 -05001018 partitions[num].SetStatus(UINT8_C(0x00));
srs5694f2efa7d2011-03-01 22:03:26 -05001019 else
srs5694bf8950c2011-03-12 01:23:12 -05001020 partitions[num].SetStatus(UINT8_C(0x80));
srs5694f2efa7d2011-03-01 22:03:26 -05001021 } else allOK = 0;
1022 } else allOK = 0;
1023 return allOK;
1024} // BasicMBRData::SetPartBootable()
1025
1026// Create a partition that fills the most available space. Returns
1027// 1 if partition was created, 0 otherwise. Intended for use in
1028// creating hybrid MBRs.
1029int BasicMBRData::MakeBiggestPart(int i, int type) {
srs5694bf8950c2011-03-12 01:23:12 -05001030 uint64_t start = UINT64_C(1); // starting point for each search
1031 uint64_t firstBlock; // first block in a segment
1032 uint64_t lastBlock; // last block in a segment
1033 uint64_t segmentSize; // size of segment in blocks
1034 uint64_t selectedSegment = UINT64_C(0); // location of largest segment
1035 uint64_t selectedSize = UINT64_C(0); // size of largest segment in blocks
srs5694f2efa7d2011-03-01 22:03:26 -05001036 int found = 0;
1037
1038 do {
1039 firstBlock = FindFirstAvailable(start);
srs5694bf8950c2011-03-12 01:23:12 -05001040 if (firstBlock > UINT64_C(0)) { // something's free...
srs5694f2efa7d2011-03-01 22:03:26 -05001041 lastBlock = FindLastInFree(firstBlock);
srs5694bf8950c2011-03-12 01:23:12 -05001042 segmentSize = lastBlock - firstBlock + UINT64_C(1);
srs5694f2efa7d2011-03-01 22:03:26 -05001043 if (segmentSize > selectedSize) {
1044 selectedSize = segmentSize;
1045 selectedSegment = firstBlock;
1046 } // if
1047 start = lastBlock + 1;
1048 } // if
1049 } while (firstBlock != 0);
srs5694bf8950c2011-03-12 01:23:12 -05001050 if ((selectedSize > UINT64_C(0)) && (selectedSize < diskSize)) {
srs5694f2efa7d2011-03-01 22:03:26 -05001051 found = 1;
1052 MakePart(i, selectedSegment, selectedSize, type, 0);
1053 } else {
1054 found = 0;
1055 } // if/else
1056 return found;
1057} // BasicMBRData::MakeBiggestPart(int i)
1058
1059// Delete partition #i
1060void BasicMBRData::DeletePartition(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001061 partitions[i].Empty();
srs5694f2efa7d2011-03-01 22:03:26 -05001062} // BasicMBRData::DeletePartition()
1063
srs5694bf8950c2011-03-12 01:23:12 -05001064// Set the inclusion status (PRIMARY, LOGICAL, or NONE) with some sanity
1065// checks to ensure the table remains legal.
1066// Returns 1 on success, 0 on failure.
1067int BasicMBRData::SetInclusionwChecks(int num, int inclStatus) {
1068 int allOK = 1, origValue;
1069
1070 if (IsLegal()) {
1071 if ((inclStatus == PRIMARY) || (inclStatus == LOGICAL) || (inclStatus == NONE)) {
1072 origValue = partitions[num].GetInclusion();
1073 partitions[num].SetInclusion(inclStatus);
1074 if (!IsLegal()) {
1075 partitions[num].SetInclusion(origValue);
1076 cerr << "Specified change is not legal! Aborting change!\n";
1077 } // if
1078 } else {
1079 cerr << "Invalid partition inclusion code in BasicMBRData::SetInclusionwChecks()!\n";
1080 } // if/else
1081 } else {
1082 cerr << "Partition table is not currently in a valid state. Aborting change!\n";
1083 allOK = 0;
1084 } // if/else
1085 return allOK;
1086} // BasicMBRData::SetInclusionwChecks()
1087
srs5694f2efa7d2011-03-01 22:03:26 -05001088// Recomputes the CHS values for the specified partition and adjusts the value.
1089// Note that this will create a technically incorrect CHS value for EFI GPT (0xEE)
1090// protective partitions, but this is required by some buggy BIOSes, so I'm
1091// providing a function to do this deliberately at the user's command.
1092// This function does nothing if the partition's length is 0.
1093void BasicMBRData::RecomputeCHS(int partNum) {
srs5694bf8950c2011-03-12 01:23:12 -05001094 partitions[partNum].RecomputeCHS();
srs5694f2efa7d2011-03-01 22:03:26 -05001095} // BasicMBRData::RecomputeCHS()
1096
srs5694699941e2011-03-21 21:33:57 -04001097// Sorts the partitions starting with partition #start. This function
1098// does NOT pay attention to primary/logical assignment, which is
1099// critical when writing the partitions.
srs5694bf8950c2011-03-12 01:23:12 -05001100void BasicMBRData::SortMBR(int start) {
srs5694c2f6e0c2011-03-16 02:42:33 -04001101 if ((start < MAX_MBR_PARTS) && (start >= 0))
1102 sort(partitions + start, partitions + MAX_MBR_PARTS);
1103} // BasicMBRData::SortMBR()
srs5694bf8950c2011-03-12 01:23:12 -05001104
1105// Delete any partitions that are too big to fit on the disk
1106// or that are too big for MBR (32-bit limits).
srs569400b6d7a2011-06-26 22:40:06 -04001107// This deletes the partitions by setting values to 0, not just
1108// by setting them as being omitted.
srs5694bf8950c2011-03-12 01:23:12 -05001109// Returns the number of partitions deleted in this way.
1110int BasicMBRData::DeleteOversizedParts() {
1111 int num = 0, i;
1112
1113 for (i = 0; i < MAX_MBR_PARTS; i++) {
1114 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize) ||
1115 (partitions[i].GetStartLBA() > UINT32_MAX) || (partitions[i].GetLengthLBA() > UINT32_MAX)) {
srs569400b6d7a2011-06-26 22:40:06 -04001116 cerr << "\aWarning: Deleting oversized partition #" << i + 1 << "! Start = "
1117 << partitions[i].GetStartLBA() << ", length = " << partitions[i].GetLengthLBA() << "\n";
srs5694bf8950c2011-03-12 01:23:12 -05001118 partitions[i].Empty();
1119 num++;
1120 } // if
1121 } // for
1122 return num;
1123} // BasicMBRData::DeleteOversizedParts()
1124
1125// Search for and delete extended partitions.
1126// Returns the number of partitions deleted.
1127int BasicMBRData::DeleteExtendedParts() {
1128 int i, numDeleted = 0;
1129 uint8_t type;
1130
1131 for (i = 0; i < MAX_MBR_PARTS; i++) {
1132 type = partitions[i].GetType();
1133 if (((type == 0x05) || (type == 0x0f) || (type == (0x85))) &&
1134 (partitions[i].GetLengthLBA() > 0)) {
1135 partitions[i].Empty();
1136 numDeleted++;
1137 } // if
1138 } // for
1139 return numDeleted;
1140} // BasicMBRData::DeleteExtendedParts()
1141
1142// Finds any overlapping partitions and omits the smaller of the two.
1143void BasicMBRData::OmitOverlaps() {
1144 int i, j;
1145
1146 for (i = 0; i < MAX_MBR_PARTS; i++) {
1147 for (j = i + 1; j < MAX_MBR_PARTS; j++) {
1148 if ((partitions[i].GetInclusion() != NONE) &&
1149 partitions[i].DoTheyOverlap(partitions[j])) {
1150 if (partitions[i].GetLengthLBA() < partitions[j].GetLengthLBA())
1151 partitions[i].SetInclusion(NONE);
1152 else
1153 partitions[j].SetInclusion(NONE);
srs5694f2efa7d2011-03-01 22:03:26 -05001154 } // if
srs5694bf8950c2011-03-12 01:23:12 -05001155 } // for (j...)
1156 } // for (i...)
1157} // BasicMBRData::OmitOverlaps()
1158
srs5694bf8950c2011-03-12 01:23:12 -05001159// Convert as many partitions into logicals as possible, except for
1160// the first partition, if possible.
1161void BasicMBRData::MaximizeLogicals() {
1162 int earliestPart = 0, earliestPartWas = NONE, i;
1163
1164 for (i = MAX_MBR_PARTS - 1; i >= 0; i--) {
1165 UpdateCanBeLogical();
1166 earliestPart = i;
1167 if (partitions[i].CanBeLogical()) {
1168 partitions[i].SetInclusion(LOGICAL);
1169 } else if (partitions[i].CanBePrimary()) {
1170 partitions[i].SetInclusion(PRIMARY);
1171 } else {
1172 partitions[i].SetInclusion(NONE);
1173 } // if/elseif/else
1174 } // for
1175 // If we have spare primaries, convert back the earliest partition to
1176 // its original state....
1177 if ((NumPrimaries() < 4) && (partitions[earliestPart].GetInclusion() == LOGICAL))
1178 partitions[earliestPart].SetInclusion(earliestPartWas);
1179} // BasicMBRData::MaximizeLogicals()
1180
1181// Add primaries up to the maximum allowed, from the omitted category.
1182void BasicMBRData::MaximizePrimaries() {
1183 int num, i = 0;
1184
1185 num = NumPrimaries();
1186 while ((num < 4) && (i < MAX_MBR_PARTS)) {
1187 if ((partitions[i].GetInclusion() == NONE) && (partitions[i].CanBePrimary())) {
1188 partitions[i].SetInclusion(PRIMARY);
srs5694699941e2011-03-21 21:33:57 -04001189 num++;
1190 UpdateCanBeLogical();
srs5694bf8950c2011-03-12 01:23:12 -05001191 } // if
1192 i++;
1193 } // while
1194} // BasicMBRData::MaximizePrimaries()
1195
1196// Remove primary partitions in excess of 4, starting with the later ones,
1197// in terms of the array location....
1198void BasicMBRData::TrimPrimaries(void) {
1199 int numToDelete, i = MAX_MBR_PARTS;
1200
1201 numToDelete = NumPrimaries() - 4;
1202 while ((numToDelete > 0) && (i >= 0)) {
1203 if (partitions[i].GetInclusion() == PRIMARY) {
1204 partitions[i].SetInclusion(NONE);
1205 numToDelete--;
1206 } // if
1207 i--;
1208 } // while (numToDelete > 0)
1209} // BasicMBRData::TrimPrimaries()
1210
1211// Locates primary partitions located between logical partitions and
1212// either converts the primaries into logicals (if possible) or omits
1213// them.
1214void BasicMBRData::MakeLogicalsContiguous(void) {
1215 uint64_t firstLogicalLBA, lastLogicalLBA;
1216 int i;
1217
1218 firstLogicalLBA = FirstLogicalLBA();
1219 lastLogicalLBA = LastLogicalLBA();
1220 for (i = 0; i < MAX_MBR_PARTS; i++) {
1221 if ((partitions[i].GetInclusion() == PRIMARY) &&
1222 (partitions[i].GetStartLBA() >= firstLogicalLBA) &&
1223 (partitions[i].GetLastLBA() <= lastLogicalLBA)) {
1224 if (SectorUsedAs(partitions[i].GetStartLBA() - 1) == NONE)
1225 partitions[i].SetInclusion(LOGICAL);
1226 else
1227 partitions[i].SetInclusion(NONE);
1228 } // if
1229 } // for
1230} // BasicMBRData::MakeLogicalsContiguous()
1231
1232// If MBR data aren't legal, adjust primary/logical assignments and,
1233// if necessary, drop partitions, to make the data legal.
1234void BasicMBRData::MakeItLegal(void) {
1235 if (!IsLegal()) {
1236 DeleteOversizedParts();
srs5694bf8950c2011-03-12 01:23:12 -05001237 MaximizeLogicals();
1238 MaximizePrimaries();
1239 if (!AreLogicalsContiguous())
1240 MakeLogicalsContiguous();
1241 if (NumPrimaries() > 4)
1242 TrimPrimaries();
1243 OmitOverlaps();
1244 } // if
1245} // BasicMBRData::MakeItLegal()
1246
1247// Removes logical partitions and deactivated partitions from first four
1248// entries (primary space).
1249// Returns the number of partitions moved.
1250int BasicMBRData::RemoveLogicalsFromFirstFour(void) {
1251 int i, j = 4, numMoved = 0, swapped = 0;
1252 MBRPart temp;
1253
1254 for (i = 0; i < 4; i++) {
1255 if ((partitions[i].GetInclusion() != PRIMARY) && (partitions[i].GetLengthLBA() > 0)) {
1256 j = 4;
1257 swapped = 0;
1258 do {
1259 if ((partitions[j].GetInclusion() == NONE) && (partitions[j].GetLengthLBA() == 0)) {
1260 temp = partitions[j];
1261 partitions[j] = partitions[i];
1262 partitions[i] = temp;
1263 swapped = 1;
1264 numMoved++;
1265 } // if
1266 j++;
1267 } while ((j < MAX_MBR_PARTS) && !swapped);
1268 if (j >= MAX_MBR_PARTS)
1269 cerr << "Warning! Too many partitions in BasicMBRData::RemoveLogicalsFromFirstFour()!\n";
1270 } // if
1271 } // for i...
1272 return numMoved;
1273} // BasicMBRData::RemoveLogicalsFromFirstFour()
1274
1275// Move all primaries into the first four partition spaces
1276// Returns the number of partitions moved.
1277int BasicMBRData::MovePrimariesToFirstFour(void) {
1278 int i, j = 0, numMoved = 0, swapped = 0;
1279 MBRPart temp;
1280
1281 for (i = 4; i < MAX_MBR_PARTS; i++) {
1282 if (partitions[i].GetInclusion() == PRIMARY) {
1283 j = 0;
1284 swapped = 0;
1285 do {
1286 if (partitions[j].GetInclusion() != PRIMARY) {
1287 temp = partitions[j];
1288 partitions[j] = partitions[i];
1289 partitions[i] = temp;
1290 swapped = 1;
1291 numMoved++;
1292 } // if
1293 j++;
1294 } while ((j < 4) && !swapped);
1295 } // if
1296 } // for
1297 return numMoved;
1298} // BasicMBRData::MovePrimariesToFirstFour()
1299
1300// Create an extended partition, if necessary, to hold the logical partitions.
1301// This function also sorts the primaries into the first four positions of
1302// the table.
1303// Returns 1 on success, 0 on failure.
1304int BasicMBRData::CreateExtended(void) {
1305 int allOK = 1, i = 0, swapped = 0;
1306 MBRPart temp;
1307
1308 if (IsLegal()) {
1309 // Move logicals out of primary space...
1310 RemoveLogicalsFromFirstFour();
1311 // Move primaries out of logical space...
1312 MovePrimariesToFirstFour();
1313
1314 // Create the extended partition
1315 if (NumLogicals() > 0) {
1316 SortMBR(4); // sort starting from 4 -- that is, logicals only
1317 temp.Empty();
1318 temp.SetStartLBA(FirstLogicalLBA() - 1);
1319 temp.SetLengthLBA(LastLogicalLBA() - FirstLogicalLBA() + 2);
1320 temp.SetType(0x0f, 1);
1321 temp.SetInclusion(PRIMARY);
1322 do {
1323 if ((partitions[i].GetInclusion() == NONE) || (partitions[i].GetLengthLBA() == 0)) {
1324 partitions[i] = temp;
1325 swapped = 1;
1326 } // if
1327 i++;
1328 } while ((i < 4) && !swapped);
1329 if (!swapped) {
1330 cerr << "Could not create extended partition; no room in primary table!\n";
1331 allOK = 0;
1332 } // if
1333 } // if (NumLogicals() > 0)
1334 } else allOK = 0;
1335 // Do a final check for EFI GPT (0xEE) partitions & flag as a problem if found
1336 // along with an extended partition
1337 for (i = 0; i < MAX_MBR_PARTS; i++)
1338 if (swapped && partitions[i].GetType() == 0xEE)
1339 allOK = 0;
1340 return allOK;
1341} // BasicMBRData::CreateExtended()
srs5694f2efa7d2011-03-01 22:03:26 -05001342
1343/****************************************
1344 * *
1345 * Functions to find data on free space *
1346 * *
1347 ****************************************/
1348
1349// Finds the first free space on the disk from start onward; returns 0
1350// if none available....
srs5694bf8950c2011-03-12 01:23:12 -05001351uint64_t BasicMBRData::FindFirstAvailable(uint64_t start) {
1352 uint64_t first;
1353 uint64_t i;
srs5694f2efa7d2011-03-01 22:03:26 -05001354 int firstMoved;
1355
1356 first = start;
1357
1358 // ...now search through all partitions; if first is within an
1359 // existing partition, move it to the next sector after that
1360 // partition and repeat. If first was moved, set firstMoved
1361 // flag; repeat until firstMoved is not set, so as to catch
1362 // cases where partitions are out of sequential order....
1363 do {
1364 firstMoved = 0;
1365 for (i = 0; i < 4; i++) {
1366 // Check if it's in the existing partition
srs5694bf8950c2011-03-12 01:23:12 -05001367 if ((first >= partitions[i].GetStartLBA()) &&
1368 (first < (partitions[i].GetStartLBA() + partitions[i].GetLengthLBA()))) {
1369 first = partitions[i].GetStartLBA() + partitions[i].GetLengthLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001370 firstMoved = 1;
1371 } // if
1372 } // for
1373 } while (firstMoved == 1);
srs5694bf8950c2011-03-12 01:23:12 -05001374 if ((first >= diskSize) || (first > UINT32_MAX))
srs5694f2efa7d2011-03-01 22:03:26 -05001375 first = 0;
1376 return (first);
1377} // BasicMBRData::FindFirstAvailable()
1378
1379// Finds the last free sector on the disk from start forward.
srs5694bf8950c2011-03-12 01:23:12 -05001380uint64_t BasicMBRData::FindLastInFree(uint64_t start) {
1381 uint64_t nearestStart;
1382 uint64_t i;
srs5694f2efa7d2011-03-01 22:03:26 -05001383
1384 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs5694bf8950c2011-03-12 01:23:12 -05001385 nearestStart = diskSize - 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001386 else
1387 nearestStart = UINT32_MAX - 1;
srs5694699941e2011-03-21 21:33:57 -04001388
srs5694f2efa7d2011-03-01 22:03:26 -05001389 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -05001390 if ((nearestStart > partitions[i].GetStartLBA()) &&
1391 (partitions[i].GetStartLBA() > start)) {
1392 nearestStart = partitions[i].GetStartLBA() - 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001393 } // if
1394 } // for
1395 return (nearestStart);
1396} // BasicMBRData::FindLastInFree()
1397
1398// Finds the first free sector on the disk from start backward.
srs5694bf8950c2011-03-12 01:23:12 -05001399uint64_t BasicMBRData::FindFirstInFree(uint64_t start) {
1400 uint64_t bestLastLBA, thisLastLBA;
srs5694f2efa7d2011-03-01 22:03:26 -05001401 int i;
1402
1403 bestLastLBA = 1;
1404 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -05001405 thisLastLBA = partitions[i].GetLastLBA() + 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001406 if (thisLastLBA > 0)
1407 thisLastLBA--;
1408 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start))
1409 bestLastLBA = thisLastLBA + 1;
1410 } // for
1411 return (bestLastLBA);
1412} // BasicMBRData::FindFirstInFree()
1413
srs5694bf8950c2011-03-12 01:23:12 -05001414// Returns NONE (unused), PRIMARY, LOGICAL, EBR (for EBR or MBR), or INVALID
1415int BasicMBRData::SectorUsedAs(uint64_t sector, int topPartNum) {
1416 int i = 0, usedAs = NONE;
srs5694f2efa7d2011-03-01 22:03:26 -05001417
srs5694bf8950c2011-03-12 01:23:12 -05001418 do {
1419 if ((partitions[i].GetStartLBA() <= sector) && (partitions[i].GetLastLBA() >= sector))
1420 usedAs = partitions[i].GetInclusion();
1421 if ((partitions[i].GetStartLBA() == (sector + 1)) && (partitions[i].GetInclusion() == LOGICAL))
1422 usedAs = EBR;
1423 if (sector == 0)
1424 usedAs = EBR;
1425 if (sector >= diskSize)
1426 usedAs = INVALID;
1427 i++;
1428 } while ((i < topPartNum) && (usedAs == NONE));
1429 return usedAs;
1430} // BasicMBRData::SectorUsedAs()
1431
srs5694f2efa7d2011-03-01 22:03:26 -05001432/******************************************************
1433 * *
1434 * Functions that extract data on specific partitions *
1435 * *
1436 ******************************************************/
1437
1438uint8_t BasicMBRData::GetStatus(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001439 MBRPart* thePart;
srs5694f2efa7d2011-03-01 22:03:26 -05001440 uint8_t retval;
1441
1442 thePart = GetPartition(i);
1443 if (thePart != NULL)
srs5694bf8950c2011-03-12 01:23:12 -05001444 retval = thePart->GetStatus();
srs5694f2efa7d2011-03-01 22:03:26 -05001445 else
1446 retval = UINT8_C(0);
1447 return retval;
1448} // BasicMBRData::GetStatus()
1449
1450uint8_t BasicMBRData::GetType(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001451 MBRPart* thePart;
srs5694f2efa7d2011-03-01 22:03:26 -05001452 uint8_t retval;
1453
1454 thePart = GetPartition(i);
1455 if (thePart != NULL)
srs5694bf8950c2011-03-12 01:23:12 -05001456 retval = thePart->GetType();
srs5694f2efa7d2011-03-01 22:03:26 -05001457 else
1458 retval = UINT8_C(0);
1459 return retval;
1460} // BasicMBRData::GetType()
1461
srs5694bf8950c2011-03-12 01:23:12 -05001462uint64_t BasicMBRData::GetFirstSector(int i) {
1463 MBRPart* thePart;
1464 uint64_t retval;
srs5694f2efa7d2011-03-01 22:03:26 -05001465
1466 thePart = GetPartition(i);
1467 if (thePart != NULL) {
srs5694bf8950c2011-03-12 01:23:12 -05001468 retval = thePart->GetStartLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001469 } else
1470 retval = UINT32_C(0);
1471 return retval;
1472} // BasicMBRData::GetFirstSector()
1473
srs5694bf8950c2011-03-12 01:23:12 -05001474uint64_t BasicMBRData::GetLength(int i) {
1475 MBRPart* thePart;
1476 uint64_t retval;
srs5694f2efa7d2011-03-01 22:03:26 -05001477
1478 thePart = GetPartition(i);
1479 if (thePart != NULL) {
srs5694bf8950c2011-03-12 01:23:12 -05001480 retval = thePart->GetLengthLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001481 } else
srs5694bf8950c2011-03-12 01:23:12 -05001482 retval = UINT64_C(0);
srs5694f2efa7d2011-03-01 22:03:26 -05001483 return retval;
1484} // BasicMBRData::GetLength()
1485
1486/***********************
1487 * *
1488 * Protected functions *
1489 * *
1490 ***********************/
1491
1492// Return a pointer to a primary or logical partition, or NULL if
1493// the partition is out of range....
srs5694bf8950c2011-03-12 01:23:12 -05001494MBRPart* BasicMBRData::GetPartition(int i) {
1495 MBRPart* thePart = NULL;
srs5694f2efa7d2011-03-01 22:03:26 -05001496
1497 if ((i >= 0) && (i < MAX_MBR_PARTS))
1498 thePart = &partitions[i];
1499 return thePart;
1500} // GetPartition()
srs5694bf8950c2011-03-12 01:23:12 -05001501
1502/*******************************************
1503 * *
1504 * Functions that involve user interaction *
1505 * *
1506 *******************************************/
1507
1508// Present the MBR operations menu. Note that the 'w' option does not
1509// immediately write data; that's handled by the calling function.
1510// Returns the number of partitions defined on exit, or -1 if the
1511// user selected the 'q' option. (Thus, the caller should save data
1512// if the return value is >0, or possibly >=0 depending on intentions.)
1513int BasicMBRData::DoMenu(const string& prompt) {
srs5694bf8950c2011-03-12 01:23:12 -05001514 int goOn = 1, quitting = 0, retval, num, haveShownInfo = 0;
srs56946aae2a92011-06-10 01:16:51 -04001515 unsigned int hexCode;
1516 string tempStr;
srs5694bf8950c2011-03-12 01:23:12 -05001517
1518 do {
1519 cout << prompt;
srs56945a608532011-03-17 13:53:01 -04001520 switch (ReadString()[0]) {
1521 case '\0':
srs5694bf8950c2011-03-12 01:23:12 -05001522 break;
1523 case 'a': case 'A':
1524 num = GetNumber(1, MAX_MBR_PARTS, 1, "Toggle active flag for partition: ") - 1;
1525 if (partitions[num].GetInclusion() != NONE)
1526 partitions[num].SetStatus(partitions[num].GetStatus() ^ 0x80);
1527 break;
1528 case 'c': case 'C':
1529 for (num = 0; num < MAX_MBR_PARTS; num++)
1530 RecomputeCHS(num);
1531 break;
1532 case 'l': case 'L':
1533 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as logical: ") - 1;
1534 SetInclusionwChecks(num, LOGICAL);
1535 break;
1536 case 'o': case 'O':
1537 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to omit: ") - 1;
1538 SetInclusionwChecks(num, NONE);
1539 break;
1540 case 'p': case 'P':
1541 if (!haveShownInfo) {
1542 cout << "\n** NOTE: Partition numbers do NOT indicate final primary/logical "
1543 << "status,\n** unlike in most MBR partitioning tools!\n\a";
1544 cout << "\n** Extended partitions are not displayed, but will be generated "
1545 << "as required.\n";
1546 haveShownInfo = 1;
1547 } // if
1548 DisplayMBRData();
1549 break;
1550 case 'q': case 'Q':
1551 cout << "This will abandon your changes. Are you sure? ";
1552 if (GetYN() == 'Y') {
1553 goOn = 0;
1554 quitting = 1;
1555 } // if
1556 break;
1557 case 'r': case 'R':
1558 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as primary: ") - 1;
1559 SetInclusionwChecks(num, PRIMARY);
1560 break;
1561 case 's': case 'S':
1562 SortMBR();
1563 break;
1564 case 't': case 'T':
1565 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to change type code: ") - 1;
srs56946aae2a92011-06-10 01:16:51 -04001566 hexCode = 0x00;
srs5694bf8950c2011-03-12 01:23:12 -05001567 if (partitions[num].GetLengthLBA() > 0) {
srs5694bf8950c2011-03-12 01:23:12 -05001568 while ((hexCode <= 0) || (hexCode > 255)) {
1569 cout << "Enter an MBR hex code: ";
srs56946aae2a92011-06-10 01:16:51 -04001570 tempStr = ReadString();
1571 if (IsHex(tempStr))
1572 sscanf(tempStr.c_str(), "%x", &hexCode);
srs5694bf8950c2011-03-12 01:23:12 -05001573 } // while
1574 partitions[num].SetType(hexCode);
1575 } // if
1576 break;
1577 case 'w': case 'W':
1578 goOn = 0;
1579 break;
1580 default:
1581 ShowCommands();
1582 break;
1583 } // switch
1584 } while (goOn);
1585 if (quitting)
1586 retval = -1;
1587 else
1588 retval = CountParts();
1589 return (retval);
1590} // BasicMBRData::DoMenu()
1591
1592void BasicMBRData::ShowCommands(void) {
1593 cout << "a\ttoggle the active/boot flag\n";
1594 cout << "c\trecompute all CHS values\n";
1595 cout << "l\tset partition as logical\n";
1596 cout << "o\tomit partition\n";
1597 cout << "p\tprint the MBR partition table\n";
1598 cout << "q\tquit without saving changes\n";
1599 cout << "r\tset partition as primary\n";
1600 cout << "s\tsort MBR partitions\n";
1601 cout << "t\tchange partition type code\n";
1602 cout << "w\twrite the MBR partition table to disk and exit\n";
1603} // BasicMBRData::ShowCommands()