blob: 764daafcdaccf7a4ba24c331f0a56954e615ac59 [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>
21#include "mbr.h"
srs5694f2efa7d2011-03-01 22:03:26 -050022#include "support.h"
23
24using namespace std;
25
26/****************************************
27 * *
28 * MBRData class and related structures *
29 * *
30 ****************************************/
31
32BasicMBRData::BasicMBRData(void) {
33 blockSize = SECTOR_SIZE;
34 diskSize = 0;
35 device = "";
36 state = invalid;
srs5694f2efa7d2011-03-01 22:03:26 -050037 numHeads = MAX_HEADS;
38 numSecspTrack = MAX_SECSPERTRACK;
39 myDisk = NULL;
40 canDeleteMyDisk = 0;
41 EmptyMBR();
42} // BasicMBRData default constructor
43
44BasicMBRData::BasicMBRData(string filename) {
45 blockSize = SECTOR_SIZE;
46 diskSize = 0;
47 device = filename;
48 state = invalid;
49 numHeads = MAX_HEADS;
50 numSecspTrack = MAX_SECSPERTRACK;
51 myDisk = NULL;
52 canDeleteMyDisk = 0;
53
srs5694f2efa7d2011-03-01 22:03:26 -050054 // Try to read the specified partition table, but if it fails....
55 if (!ReadMBRData(filename)) {
56 EmptyMBR();
57 device = "";
58 } // if
59} // BasicMBRData(string filename) constructor
60
61// Free space used by myDisk only if that's OK -- sometimes it will be
62// copied from an outside source, in which case that source should handle
63// it!
64BasicMBRData::~BasicMBRData(void) {
65 if (canDeleteMyDisk)
66 delete myDisk;
67} // BasicMBRData destructor
68
69// Assignment operator -- copy entire set of MBR data.
70BasicMBRData & BasicMBRData::operator=(const BasicMBRData & orig) {
71 int i;
72
srs5694bf8950c2011-03-12 01:23:12 -050073 memcpy(code, orig.code, 440);
srs5694f2efa7d2011-03-01 22:03:26 -050074 diskSignature = orig.diskSignature;
75 nulls = orig.nulls;
76 MBRSignature = orig.MBRSignature;
77 blockSize = orig.blockSize;
78 diskSize = orig.diskSize;
79 numHeads = orig.numHeads;
80 numSecspTrack = orig.numSecspTrack;
81 canDeleteMyDisk = orig.canDeleteMyDisk;
82 device = orig.device;
83 state = orig.state;
84
85 myDisk = new DiskIO;
srs5694bf8950c2011-03-12 01:23:12 -050086 if (orig.myDisk != NULL)
87 myDisk->OpenForRead(orig.myDisk->GetName());
srs5694f2efa7d2011-03-01 22:03:26 -050088
89 for (i = 0; i < MAX_MBR_PARTS; i++) {
90 partitions[i] = orig.partitions[i];
91 } // for
92 return *this;
93} // BasicMBRData::operator=()
94
95/**********************
96 * *
97 * Disk I/O functions *
98 * *
99 **********************/
100
101// Read data from MBR. Returns 1 if read was successful (even if the
102// data isn't a valid MBR), 0 if the read failed.
103int BasicMBRData::ReadMBRData(const string & deviceFilename) {
104 int allOK = 1;
105
106 if (myDisk == NULL) {
107 myDisk = new DiskIO;
108 canDeleteMyDisk = 1;
109 } // if
110 if (myDisk->OpenForRead(deviceFilename)) {
111 allOK = ReadMBRData(myDisk);
112 } else {
113 allOK = 0;
114 } // if
115
116 if (allOK)
117 device = deviceFilename;
118
119 return allOK;
120} // BasicMBRData::ReadMBRData(const string & deviceFilename)
121
122// Read data from MBR. If checkBlockSize == 1 (the default), the block
123// size is checked; otherwise it's set to the default (512 bytes).
124// Note that any extended partition(s) present will be explicitly stored
125// in the partitions[] array, along with their contained partitions; the
126// extended container partition(s) should be ignored by other functions.
127int BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) {
srs5694bf8950c2011-03-12 01:23:12 -0500128 int allOK = 1, i, logicalNum = 0;
srs5694f2efa7d2011-03-01 22:03:26 -0500129 int err = 1;
130 TempMBR tempMBR;
131
srs5694bf8950c2011-03-12 01:23:12 -0500132 if ((myDisk != NULL) && (myDisk != theDisk) && (canDeleteMyDisk)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500133 delete myDisk;
134 canDeleteMyDisk = 0;
135 } // if
136
137 myDisk = theDisk;
138
139 // Empty existing MBR data, including the logical partitions...
140 EmptyMBR(0);
141
142 if (myDisk->Seek(0))
143 if (myDisk->Read(&tempMBR, 512))
144 err = 0;
145 if (err) {
146 cerr << "Problem reading disk in BasicMBRData::ReadMBRData()!\n";
147 } else {
148 for (i = 0; i < 440; i++)
149 code[i] = tempMBR.code[i];
150 diskSignature = tempMBR.diskSignature;
151 nulls = tempMBR.nulls;
152 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500153 partitions[i] = tempMBR.partitions[i];
154 if (partitions[i].GetLengthLBA() > 0)
155 partitions[i].SetInclusion(PRIMARY);
srs5694f2efa7d2011-03-01 22:03:26 -0500156 } // for i... (reading all four partitions)
157 MBRSignature = tempMBR.MBRSignature;
srs5694bf8950c2011-03-12 01:23:12 -0500158 ReadCHSGeom();
srs5694f2efa7d2011-03-01 22:03:26 -0500159
160 // Reverse the byte order, if necessary
161 if (IsLittleEndian() == 0) {
162 ReverseBytes(&diskSignature, 4);
163 ReverseBytes(&nulls, 2);
164 ReverseBytes(&MBRSignature, 2);
165 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500166 partitions[i].ReverseByteOrder();
srs5694f2efa7d2011-03-01 22:03:26 -0500167 } // for
168 } // if
169
170 if (MBRSignature != MBR_SIGNATURE) {
171 allOK = 0;
172 state = invalid;
173 } // if
174
175 // Find disk size
176 diskSize = myDisk->DiskSize(&err);
177
178 // Find block size
179 if (checkBlockSize) {
180 blockSize = myDisk->GetBlockSize();
181 } // if (checkBlockSize)
182
183 // Load logical partition data, if any is found....
184 if (allOK) {
185 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500186 if ((partitions[i].GetType() == 0x05) || (partitions[i].GetType() == 0x0f)
187 || (partitions[i].GetType() == 0x85)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500188 // Found it, so call a recursive algorithm to load everything from them....
srs5694bf8950c2011-03-12 01:23:12 -0500189 logicalNum = ReadLogicalPart(partitions[i].GetStartLBA(), UINT32_C(0), 4);
srs5694f2efa7d2011-03-01 22:03:26 -0500190 if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
191 allOK = 0;
192 cerr << "Error reading logical partitions! List may be truncated!\n";
193 } // if maxLogicals valid
srs5694bf8950c2011-03-12 01:23:12 -0500194 DeletePartition(i);
srs5694f2efa7d2011-03-01 22:03:26 -0500195 } // if primary partition is extended
196 } // for primary partition loop
197 if (allOK) { // Loaded logicals OK
198 state = mbr;
199 } else {
200 state = invalid;
201 } // if
202 } // if
203
204 // Check to see if it's in GPT format....
205 if (allOK) {
206 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500207 if (partitions[i].GetType() == UINT8_C(0xEE)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500208 state = gpt;
209 } // if
210 } // for
211 } // if
212
213 // If there's an EFI GPT partition, look for other partition types,
214 // to flag as hybrid
215 if (state == gpt) {
216 for (i = 0 ; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500217 if ((partitions[i].GetType() != UINT8_C(0xEE)) &&
218 (partitions[i].GetType() != UINT8_C(0x00)))
srs5694f2efa7d2011-03-01 22:03:26 -0500219 state = hybrid;
srs5694bf8950c2011-03-12 01:23:12 -0500220 if (logicalNum > 0)
221 cerr << "Warning! MBR Logical partitions found on a hybrid MBR disk! This is an\n"
222 << "EXTREMELY dangerous configuration!\n\a";
srs5694f2efa7d2011-03-01 22:03:26 -0500223 } // for
224 } // if (hybrid detection code)
225 } // no initial error
226 return allOK;
227} // BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize)
228
229// This is a recursive function to read all the logical partitions, following the
230// logical partition linked list from the disk and storing the basic data in the
231// partitions[] array. Returns last index to partitions[] used, or -1 if there was
232// a problem.
233// Parameters:
234// extendedStart = LBA of the start of the extended partition
235// diskOffset = LBA offset WITHIN the extended partition of the one to be read
236// partNum = location in partitions[] array to store retrieved data
srs5694bf8950c2011-03-12 01:23:12 -0500237int BasicMBRData::ReadLogicalPart(uint64_t extendedStart, uint64_t diskOffset, int partNum) {
srs5694f2efa7d2011-03-01 22:03:26 -0500238 struct TempMBR ebr;
srs5694bf8950c2011-03-12 01:23:12 -0500239 uint8_t ebrType;
srs5694f2efa7d2011-03-01 22:03:26 -0500240 uint64_t offset;
241
242 // Check for a valid partition number. Note that partitions MAY be read into
243 // the area normally used by primary partitions, although the only calling
srs5694bf8950c2011-03-12 01:23:12 -0500244 // functions as of GPT fdisk version 0.7.0 don't do so.
srs5694f2efa7d2011-03-01 22:03:26 -0500245 if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
246 offset = (uint64_t) (extendedStart + diskOffset);
247 if (myDisk->Seek(offset) == 0) { // seek to EBR record
248 cerr << "Unable to seek to " << offset << "! Aborting!\n";
249 partNum = -1;
250 }
251 if (myDisk->Read(&ebr, 512) != 512) { // Load the data....
252 cerr << "Error seeking to or reading logical partition data from " << offset
253 << "!\nAborting!\n";
254 partNum = -1;
255 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
256 ReverseBytes(&ebr.MBRSignature, 2);
257 ReverseBytes(&ebr.partitions[0].firstLBA, 4);
258 ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
259 ReverseBytes(&ebr.partitions[1].firstLBA, 4);
260 ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
261 } // if/else/if
262
263 if (ebr.MBRSignature != MBR_SIGNATURE) {
264 partNum = -1;
265 cerr << "MBR signature in logical partition invalid; read 0x";
266 cerr.fill('0');
267 cerr.width(4);
268 cerr.setf(ios::uppercase);
269 cerr << hex << ebr.MBRSignature << ", but should be 0x";
270 cerr.width(4);
271 cerr << MBR_SIGNATURE << dec << "\n";
272 cerr.fill(' ');
273 } // if
274
srs5694bf8950c2011-03-12 01:23:12 -0500275 // Sometimes an EBR points directly to another EBR, rather than defining
276 // a logical partition and then pointing to another EBR. Thus, we recurse
277 // directly if this is detected, else extract partition data and then
278 // recurse on the second entry in the EBR...
279 ebrType = ebr.partitions[0].partitionType;
280 if ((ebrType == 0x05) || (ebrType == 0x0f) || (ebrType == 0x85)) {
281 partNum = ReadLogicalPart(extendedStart, ebr.partitions[0].firstLBA, partNum);
srs5694f2efa7d2011-03-01 22:03:26 -0500282 } else {
srs5694bf8950c2011-03-12 01:23:12 -0500283 // Copy over the basic data....
284 partitions[partNum] = ebr.partitions[0];
285 // Adjust the start LBA, since it's encoded strangely....
286 partitions[partNum].SetStartLBA(ebr.partitions[0].firstLBA + diskOffset + extendedStart);
287 partitions[partNum].SetInclusion(LOGICAL);
288
289 // Find the next partition (if there is one) and recurse....
290 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) &&
291 (partNum < (MAX_MBR_PARTS - 1))) {
292 partNum = ReadLogicalPart(extendedStart, ebr.partitions[1].firstLBA,
293 partNum + 1);
294 } else {
295 partNum++;
296 } // if another partition
297 } // if/else
srs5694f2efa7d2011-03-01 22:03:26 -0500298 } // Not enough space for all the logicals (or previous error encountered)
299 return (partNum);
300} // BasicMBRData::ReadLogicalPart()
301
302// Write the MBR data to the default defined device. This writes both the
303// MBR itself and any defined logical partitions, provided there's an
304// MBR extended partition.
305int BasicMBRData::WriteMBRData(void) {
306 int allOK = 1;
307
308 if (myDisk != NULL) {
309 if (myDisk->OpenForWrite() != 0) {
310 allOK = WriteMBRData(myDisk);
srs5694bf8950c2011-03-12 01:23:12 -0500311 cout << "Done writing data!\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500312 } else {
313 allOK = 0;
314 } // if/else
315 myDisk->Close();
316 } else allOK = 0;
317 return allOK;
318} // BasicMBRData::WriteMBRData(void)
319
320// Save the MBR data to a file. This writes both the
srs5694bf8950c2011-03-12 01:23:12 -0500321// MBR itself and any defined logical partitions.
srs5694f2efa7d2011-03-01 22:03:26 -0500322int BasicMBRData::WriteMBRData(DiskIO *theDisk) {
srs5694bf8950c2011-03-12 01:23:12 -0500323 int i, j, partNum, next, allOK = 1, moreLogicals = 0;
324 uint64_t extFirstLBA = 0;
srs5694f2efa7d2011-03-01 22:03:26 -0500325 uint64_t writeEbrTo; // 64-bit because we support extended in 2-4TiB range
326 TempMBR tempMBR;
327
srs5694bf8950c2011-03-12 01:23:12 -0500328 allOK = CreateExtended();
329 if (allOK) {
330 // First write the main MBR data structure....
331 memcpy(tempMBR.code, code, 440);
332 tempMBR.diskSignature = diskSignature;
333 tempMBR.nulls = nulls;
334 tempMBR.MBRSignature = MBRSignature;
335 for (i = 0; i < 4; i++) {
336 partitions[i].StoreInStruct(&tempMBR.partitions[i]);
337 if (partitions[i].GetType() == 0x0f) {
338 extFirstLBA = partitions[i].GetStartLBA();
339 moreLogicals = 1;
340 } // if
341 } // for i...
342 } // if
343 allOK = allOK && WriteMBRData(tempMBR, theDisk, 0);
srs5694f2efa7d2011-03-01 22:03:26 -0500344
345 // Set up tempMBR with some constant data for logical partitions...
346 tempMBR.diskSignature = 0;
347 for (i = 2; i < 4; i++) {
348 tempMBR.partitions[i].firstLBA = tempMBR.partitions[i].lengthLBA = 0;
349 tempMBR.partitions[i].partitionType = 0x00;
350 for (j = 0; j < 3; j++) {
351 tempMBR.partitions[i].firstSector[j] = 0;
352 tempMBR.partitions[i].lastSector[j] = 0;
353 } // for j
354 } // for i
355
srs5694bf8950c2011-03-12 01:23:12 -0500356 partNum = FindNextInUse(4);
srs5694f2efa7d2011-03-01 22:03:26 -0500357 writeEbrTo = (uint64_t) extFirstLBA;
srs5694bf8950c2011-03-12 01:23:12 -0500358 // Write logicals...
srs5694f2efa7d2011-03-01 22:03:26 -0500359 while (allOK && moreLogicals && (partNum < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -0500360 partitions[partNum].StoreInStruct(&tempMBR.partitions[0]);
361 tempMBR.partitions[0].firstLBA = 1;
srs5694f2efa7d2011-03-01 22:03:26 -0500362 // tempMBR.partitions[1] points to next EBR or terminates EBR linked list...
srs5694bf8950c2011-03-12 01:23:12 -0500363 next = FindNextInUse(partNum + 1);
364 if ((next < MAX_MBR_PARTS) && (next > 0) && (partitions[next].GetStartLBA() > 0)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500365 tempMBR.partitions[1].partitionType = 0x0f;
srs5694bf8950c2011-03-12 01:23:12 -0500366 tempMBR.partitions[1].firstLBA = (uint32_t) (partitions[next].GetStartLBA() - extFirstLBA - 1);
367 tempMBR.partitions[1].lengthLBA = (uint32_t) (partitions[next].GetLengthLBA() + 1);
srs5694f2efa7d2011-03-01 22:03:26 -0500368 LBAtoCHS((uint64_t) tempMBR.partitions[1].firstLBA,
369 (uint8_t *) &tempMBR.partitions[1].firstSector);
370 LBAtoCHS(tempMBR.partitions[1].lengthLBA - extFirstLBA,
371 (uint8_t *) &tempMBR.partitions[1].lastSector);
372 } else {
373 tempMBR.partitions[1].partitionType = 0x00;
374 tempMBR.partitions[1].firstLBA = 0;
375 tempMBR.partitions[1].lengthLBA = 0;
376 moreLogicals = 0;
377 } // if/else
378 allOK = WriteMBRData(tempMBR, theDisk, writeEbrTo);
srs5694f2efa7d2011-03-01 22:03:26 -0500379 writeEbrTo = (uint64_t) tempMBR.partitions[1].firstLBA + (uint64_t) extFirstLBA;
srs5694bf8950c2011-03-12 01:23:12 -0500380 partNum = next;
srs5694f2efa7d2011-03-01 22:03:26 -0500381 } // while
srs5694bf8950c2011-03-12 01:23:12 -0500382 DeleteExtendedParts();
srs5694f2efa7d2011-03-01 22:03:26 -0500383 return allOK;
384} // BasicMBRData::WriteMBRData(DiskIO *theDisk)
385
386int BasicMBRData::WriteMBRData(const string & deviceFilename) {
387 device = deviceFilename;
388 return WriteMBRData();
389} // BasicMBRData::WriteMBRData(const string & deviceFilename)
390
391// Write a single MBR record to the specified sector. Used by the like-named
392// function to write both the MBR and multiple EBR (for logical partition)
393// records.
394// Returns 1 on success, 0 on failure
395int BasicMBRData::WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector) {
396 int i, allOK;
397
398 // Reverse the byte order, if necessary
399 if (IsLittleEndian() == 0) {
400 ReverseBytes(&mbr.diskSignature, 4);
401 ReverseBytes(&mbr.nulls, 2);
402 ReverseBytes(&mbr.MBRSignature, 2);
403 for (i = 0; i < 4; i++) {
404 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
405 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
406 } // for
407 } // if
408
409 // Now write the data structure...
410 allOK = theDisk->OpenForWrite();
411 if (allOK && theDisk->Seek(sector)) {
412 if (theDisk->Write(&mbr, 512) != 512) {
413 allOK = 0;
414 cerr << "Error " << errno << " when saving MBR!\n";
415 } // if
416 } else {
417 allOK = 0;
418 cerr << "Error " << errno << " when seeking to MBR to write it!\n";
419 } // if/else
420 theDisk->Close();
421
422 // Reverse the byte order back, if necessary
423 if (IsLittleEndian() == 0) {
424 ReverseBytes(&mbr.diskSignature, 4);
425 ReverseBytes(&mbr.nulls, 2);
426 ReverseBytes(&mbr.MBRSignature, 2);
427 for (i = 0; i < 4; i++) {
428 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
429 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
430 } // for
431 }// if
432 return allOK;
433} // BasicMBRData::WriteMBRData(uint64_t sector)
434
srs5694bf8950c2011-03-12 01:23:12 -0500435// Set a new disk device; used in copying one disk's partition
436// table to another disk.
437void BasicMBRData::SetDisk(DiskIO *theDisk) {
438 int err;
439
440 myDisk = theDisk;
441 diskSize = theDisk->DiskSize(&err);
442 canDeleteMyDisk = 0;
443 ReadCHSGeom();
444} // BasicMBRData::SetDisk()
445
srs5694f2efa7d2011-03-01 22:03:26 -0500446/********************************************
447 * *
448 * Functions that display data for the user *
449 * *
450 ********************************************/
451
452// Show the MBR data to the user, up to the specified maximum number
453// of partitions....
srs5694bf8950c2011-03-12 01:23:12 -0500454void BasicMBRData::DisplayMBRData(void) {
srs5694f2efa7d2011-03-01 22:03:26 -0500455 int i;
srs5694f2efa7d2011-03-01 22:03:26 -0500456
srs5694bf8950c2011-03-12 01:23:12 -0500457 cout << "\nDisk size is " << diskSize << " sectors ("
458 << BytesToSI(diskSize, blockSize) << ")\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500459 cout << "MBR disk identifier: 0x";
460 cout.width(8);
461 cout.fill('0');
462 cout.setf(ios::uppercase);
463 cout << hex << diskSignature << dec << "\n";
srs5694bf8950c2011-03-12 01:23:12 -0500464 cout << "MBR partitions:\n\n";
465 if ((state == gpt) || (state == hybrid)) {
466 cout << "Number Boot Start Sector End Sector Status Code\n";
467 } else {
468 cout << " Can Be Can Be\n";
469 cout << "Number Boot Start Sector End Sector Status Logical Primary Code\n";
470 UpdateCanBeLogical();
471 } //
472 for (i = 0; i < MAX_MBR_PARTS; i++) {
473 if (partitions[i].GetLengthLBA() != 0) {
srs5694f2efa7d2011-03-01 22:03:26 -0500474 cout.fill(' ');
475 cout.width(4);
srs5694bf8950c2011-03-12 01:23:12 -0500476 cout << i + 1 << " ";
477 partitions[i].ShowData((state == gpt) || (state == hybrid));
srs5694f2efa7d2011-03-01 22:03:26 -0500478 } // if
479 cout.fill(' ');
480 } // for
srs5694f2efa7d2011-03-01 22:03:26 -0500481} // BasicMBRData::DisplayMBRData()
482
483// Displays the state, as a word, on stdout. Used for debugging & to
484// tell the user about the MBR state when the program launches....
485void BasicMBRData::ShowState(void) {
486 switch (state) {
487 case invalid:
488 cout << " MBR: not present\n";
489 break;
490 case gpt:
491 cout << " MBR: protective\n";
492 break;
493 case hybrid:
494 cout << " MBR: hybrid\n";
495 break;
496 case mbr:
497 cout << " MBR: MBR only\n";
498 break;
499 default:
500 cout << "\a MBR: unknown -- bug!\n";
501 break;
502 } // switch
503} // BasicMBRData::ShowState()
504
srs5694bf8950c2011-03-12 01:23:12 -0500505/************************
506 * *
507 * GPT Checks and fixes *
508 * *
509 ************************/
510
511// Perform a very rudimentary check for GPT data on the disk; searches for
512// the GPT signature in the main and backup metadata areas.
513// Returns 0 if GPT data not found, 1 if main data only is found, 2 if
514// backup only is found, 3 if both main and backup data are found, and
515// -1 if a disk error occurred.
516int BasicMBRData::CheckForGPT(void) {
517 int retval = 0, err;
518 char signature1[9], signature2[9];
519
520 if (myDisk != NULL) {
521 if (myDisk->OpenForRead() != 0) {
522 if (myDisk->Seek(1)) {
523 myDisk->Read(signature1, 8);
524 signature1[8] = '\0';
525 } else retval = -1;
526 if (myDisk->Seek(myDisk->DiskSize(&err) - 1)) {
527 myDisk->Read(signature2, 8);
528 signature2[8] = '\0';
529 } else retval = -1;
530 if ((retval >= 0) && (strcmp(signature1, "EFI PART") == 0))
531 retval += 1;
532 if ((retval >= 0) && (strcmp(signature2, "EFI PART") == 0))
533 retval += 2;
534 } else {
535 retval = -1;
536 } // if/else
537 myDisk->Close();
538 } else retval = -1;
539 return retval;
540} // BasicMBRData::CheckForGPT()
541
542// Blanks the 2nd (sector #1, numbered from 0) and last sectors of the disk,
543// but only if GPT data are verified on the disk, and only for the sector(s)
544// with GPT signatures.
545// Returns 1 if operation completes successfully, 0 if not (returns 1 if
546// no GPT data are found on the disk).
547int BasicMBRData::BlankGPTData(void) {
548 int allOK = 1, err;
549 uint8_t blank[512];
550
551 memset(blank, 0, 512);
552 switch (CheckForGPT()) {
553 case -1:
554 allOK = 0;
555 break;
556 case 0:
557 break;
558 case 1:
559 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
560 if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512)))
561 allOK = 0;
562 myDisk->Close();
563 } else allOK = 0;
564 break;
565 case 2:
566 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
567 if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) &&
568 (myDisk->Write(blank, 512) == 512)))
569 allOK = 0;
570 myDisk->Close();
571 } else allOK = 0;
572 break;
573 case 3:
574 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
575 if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512)))
576 allOK = 0;
577 if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) &&
578 (myDisk->Write(blank, 512) == 512)))
579 allOK = 0;
580 myDisk->Close();
581 } else allOK = 0;
582 break;
583 default:
584 break;
585 } // switch()
586 return allOK;
587} // BasicMBRData::BlankGPTData
588
srs5694f2efa7d2011-03-01 22:03:26 -0500589/*********************************************************************
590 * *
591 * Functions that set or get disk metadata (CHS geometry, disk size, *
592 * etc.) *
593 * *
594 *********************************************************************/
595
srs5694bf8950c2011-03-12 01:23:12 -0500596// Read the CHS geometry using OS calls, or if that fails, set to
597// the most common value for big disks (255 heads, 63 sectors per
598// track, & however many cylinders that computes to).
599void BasicMBRData::ReadCHSGeom(void) {
600 numHeads = myDisk->GetNumHeads();
601 numSecspTrack = myDisk->GetNumSecsPerTrack();
602 partitions[0].SetGeometry(numHeads, numSecspTrack, diskSize, blockSize);
603} // BasicMBRData::ReadCHSGeom()
604
605// Find the low and high used partition numbers (numbered from 0).
606// Return value is the number of partitions found. Note that the
607// *low and *high values are both set to 0 when no partitions
608// are found, as well as when a single partition in the first
609// position exists. Thus, the return value is the only way to
610// tell when no partitions exist.
611int BasicMBRData::GetPartRange(uint32_t *low, uint32_t *high) {
612 uint32_t i;
613 int numFound = 0;
614
615 *low = MAX_MBR_PARTS + 1; // code for "not found"
616 *high = 0;
617 for (i = 0; i < MAX_MBR_PARTS; i++) {
618 if (partitions[i].GetStartLBA() != UINT32_C(0)) { // it exists
619 *high = i; // since we're counting up, set the high value
620 // Set the low value only if it's not yet found...
621 if (*low == (MAX_MBR_PARTS + 1))
622 *low = i;
623 numFound++;
624 } // if
625 } // for
626
627 // Above will leave *low pointing to its "not found" value if no partitions
628 // are defined, so reset to 0 if this is the case....
629 if (*low == (MAX_MBR_PARTS + 1))
630 *low = 0;
631 return numFound;
632} // GPTData::GetPartRange()
srs5694f2efa7d2011-03-01 22:03:26 -0500633
634// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
635// was within the range that can be expressed by CHS (including 0, for an
636// empty partition), 0 if the value is outside that range, and -1 if chs is
637// invalid.
638int BasicMBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
639 uint64_t cylinder, head, sector; // all numbered from 0
640 uint64_t remainder;
641 int retval = 1;
642 int done = 0;
643
644 if (chs != NULL) {
645 // Special case: In case of 0 LBA value, zero out CHS values....
646 if (lba == 0) {
647 chs[0] = chs[1] = chs[2] = UINT8_C(0);
648 done = 1;
649 } // if
650 // If LBA value is too large for CHS, max out CHS values....
651 if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
652 chs[0] = 254;
653 chs[1] = chs[2] = 255;
654 done = 1;
655 retval = 0;
656 } // if
657 // If neither of the above applies, compute CHS values....
658 if (!done) {
659 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
660 remainder = lba - (cylinder * numHeads * numSecspTrack);
661 head = remainder / numSecspTrack;
662 remainder -= head * numSecspTrack;
663 sector = remainder;
664 if (head < numHeads)
665 chs[0] = (uint8_t) head;
666 else
667 retval = 0;
668 if (sector < numSecspTrack) {
669 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
670 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
671 } else {
672 retval = 0;
673 } // if/else
674 } // if value is expressible and non-0
675 } else { // Invalid (NULL) chs pointer
676 retval = -1;
677 } // if CHS pointer valid
678 return (retval);
679} // BasicMBRData::LBAtoCHS()
680
srs5694bf8950c2011-03-12 01:23:12 -0500681// Look for overlapping partitions.
682// Returns the number of problems found
683int BasicMBRData::FindOverlaps(void) {
684 int i, j, numProbs = 0, numEE = 0;
srs5694f2efa7d2011-03-01 22:03:26 -0500685
686 for (i = 0; i < MAX_MBR_PARTS; i++) {
687 for (j = i + 1; j < MAX_MBR_PARTS; j++) {
srs5694bf8950c2011-03-12 01:23:12 -0500688 if ((partitions[i].GetInclusion() != NONE) &&
689 (partitions[i].DoTheyOverlap(partitions[j]))) {
srs5694f2efa7d2011-03-01 22:03:26 -0500690 numProbs++;
691 cout << "\nProblem: MBR partitions " << i + 1 << " and " << j + 1
692 << " overlap!\n";
693 } // if
694 } // for (j...)
srs5694bf8950c2011-03-12 01:23:12 -0500695 if (partitions[i].GetType() == 0xEE) {
srs5694f2efa7d2011-03-01 22:03:26 -0500696 numEE++;
srs5694bf8950c2011-03-12 01:23:12 -0500697 if (partitions[i].GetStartLBA() != 1)
698 cout << "\nWarning: 0xEE partition doesn't start on sector 1. This can cause "
699 << "problems\nin some OSes.\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500700 } // if
701 } // for (i...)
702 if (numEE > 1)
703 cout << "\nCaution: More than one 0xEE MBR partition found. This can cause problems\n"
704 << "in some OSes.\n";
705
706 return numProbs;
srs5694bf8950c2011-03-12 01:23:12 -0500707} // BasicMBRData::FindOverlaps()
708
709// Returns the number of primary partitions, including the extended partition
710// required to hold any logical partitions found.
711int BasicMBRData::NumPrimaries(void) {
712 int i, numPrimaries = 0, logicalsFound = 0;
713
714 for (i = 0; i < MAX_MBR_PARTS; i++) {
715 if (partitions[i].GetLengthLBA() > 0) {
716 if (partitions[i].GetInclusion() == PRIMARY)
717 numPrimaries++;
718 if (partitions[i].GetInclusion() == LOGICAL)
719 logicalsFound = 1;
720 } // if
721 } // for
722 return (numPrimaries + logicalsFound);
723} // BasicMBRData::NumPrimaries()
724
725// Returns the number of logical partitions.
726int BasicMBRData::NumLogicals(void) {
727 int i, numLogicals = 0;
728
729 for (i = 0; i < MAX_MBR_PARTS; i++) {
730 if (partitions[i].GetInclusion() == LOGICAL)
731 numLogicals++;
732 } // for
733 return numLogicals;
734} // BasicMBRData::NumLogicals()
735
736// Returns the number of partitions (primaries plus logicals), NOT including
737// the extended partition required to house the logicals.
738int BasicMBRData::CountParts(void) {
739 int i, num = 0;
740
741 for (i = 0; i < MAX_MBR_PARTS; i++) {
742 if ((partitions[i].GetInclusion() == LOGICAL) ||
743 (partitions[i].GetInclusion() == PRIMARY))
744 num++;
745 } // for
746 return num;
747} // BasicMBRData::CountParts()
748
749// Updates the canBeLogical and canBePrimary flags for all the partitions.
750void BasicMBRData::UpdateCanBeLogical(void) {
751 int i, j, sectorBefore, numPrimaries, numLogicals, usedAsEBR;
752 uint64_t firstLogical, lastLogical, lStart, pStart;
753
754 numPrimaries = NumPrimaries();
755 numLogicals = NumLogicals();
756 firstLogical = FirstLogicalLBA() - 1;
757 lastLogical = LastLogicalLBA();
758 for (i = 0; i < MAX_MBR_PARTS; i++) {
759 usedAsEBR = (SectorUsedAs(partitions[i].GetLastLBA()) == EBR);
760 if (usedAsEBR) {
761 partitions[i].SetCanBeLogical(0);
762 partitions[i].SetCanBePrimary(0);
763 } else if (partitions[i].GetLengthLBA() > 0) {
764 // First determine if it can be logical....
765 sectorBefore = SectorUsedAs(partitions[i].GetStartLBA() - 1);
766 lStart = partitions[i].GetStartLBA(); // start of potential logical part.
767 if ((lastLogical > 0) &&
768 ((sectorBefore == EBR) || (sectorBefore == NONE))) {
769 // Assume it can be logical, then search for primaries that make it
770 // not work and, if found, flag appropriately.
771 partitions[i].SetCanBeLogical(1);
772 for (j = 0; j < MAX_MBR_PARTS; j++) {
773 if ((i != j) && (partitions[j].GetInclusion() == PRIMARY)) {
774 pStart = partitions[j].GetStartLBA();
775 if (((pStart < lStart) && (firstLogical < pStart)) ||
776 ((pStart > lStart) && (firstLogical > pStart))) {
777 partitions[i].SetCanBeLogical(0);
778 } // if/else
779 } // if
780 } // for
781 } else {
782 if ((sectorBefore != EBR) && (sectorBefore != NONE))
783 partitions[i].SetCanBeLogical(0);
784 else
785 partitions[i].SetCanBeLogical(lastLogical == 0); // can be logical only if no logicals already
786 } // if/else
787 // Now determine if it can be primary. Start by assuming it can be...
788 partitions[i].SetCanBePrimary(1);
789 if ((numPrimaries >= 4) && (partitions[i].GetInclusion() != PRIMARY)) {
790 partitions[i].SetCanBePrimary(0);
791 if ((partitions[i].GetInclusion() == LOGICAL) && (numLogicals == 1) &&
792 (numPrimaries == 4))
793 partitions[i].SetCanBePrimary(1);
794 } // if
795 if ((partitions[i].GetStartLBA() > (firstLogical + 1)) &&
796 (partitions[i].GetLastLBA() < lastLogical))
797 partitions[i].SetCanBePrimary(0);
798 } // else if
799 } // for
800} // BasicMBRData::UpdateCanBeLogical()
801
802// Returns the first sector occupied by any logical partition. Note that
803// this does NOT include the logical partition's EBR! Returns UINT32_MAX
804// if there are no logical partitions defined.
805uint64_t BasicMBRData::FirstLogicalLBA(void) {
806 int i;
807 uint64_t firstFound = UINT32_MAX;
808
809 for (i = 0; i < MAX_MBR_PARTS; i++) {
810 if ((partitions[i].GetInclusion() == LOGICAL) &&
811 (partitions[i].GetStartLBA() < firstFound)) {
812 firstFound = partitions[i].GetStartLBA();
813 } // if
814 } // for
815 return firstFound;
816} // BasicMBRData::FirstLogicalLBA()
817
818// Returns the last sector occupied by any logical partition, or 0 if
819// there are no logical partitions defined.
820uint64_t BasicMBRData::LastLogicalLBA(void) {
821 int i;
822 uint64_t lastFound = 0;
823
824 for (i = 0; i < MAX_MBR_PARTS; i++) {
825 if ((partitions[i].GetInclusion() == LOGICAL) &&
826 (partitions[i].GetLastLBA() > lastFound))
827 lastFound = partitions[i].GetLastLBA();
828 } // for
829 return lastFound;
830} // BasicMBRData::LastLogicalLBA()
831
832// Returns 1 if logical partitions are contiguous (have no primaries
833// in their midst), or 0 if one or more primaries exist between
834// logicals.
835int BasicMBRData::AreLogicalsContiguous(void) {
836 int allOK = 1, i = 0;
837 uint64_t firstLogical, lastLogical;
838
839 firstLogical = FirstLogicalLBA() - 1; // subtract 1 for EBR
840 lastLogical = LastLogicalLBA();
841 if (lastLogical > 0) {
842 do {
843 if ((partitions[i].GetInclusion() == PRIMARY) &&
844 (partitions[i].GetStartLBA() >= firstLogical) &&
845 (partitions[i].GetStartLBA() <= lastLogical)) {
846 allOK = 0;
847 } // if
848 i++;
849 } while ((i < MAX_MBR_PARTS) && allOK);
850 } // if
851 return allOK;
852} // BasicMBRData::AreLogicalsContiguous()
853
854// Returns 1 if all partitions fit on the disk, given its size; 0 if any
855// partition is too big.
856int BasicMBRData::DoTheyFit(void) {
857 int i, allOK = 1;
858
859 for (i = 0; i < MAX_MBR_PARTS; i++) {
860 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize)) {
861 allOK = 0;
862 } // if
863 } // for
864 return allOK;
865} // BasicMBRData::DoTheyFit(void)
866
867// Returns 1 if there's at least one free sector immediately preceding
868// all partitions flagged as logical; 0 if any logical partition lacks
869// this space.
870int BasicMBRData::SpaceBeforeAllLogicals(void) {
871 int i = 0, allOK = 1;
872
873 do {
874 if ((partitions[i].GetStartLBA() > 0) && (partitions[i].GetInclusion() == LOGICAL)) {
875 allOK = allOK && (SectorUsedAs(partitions[i].GetStartLBA() - 1) == EBR);
876// allOK = allOK && IsFree(partitions[i].GetStartLBA() - 1);
877 } // if
878 i++;
879 } while (allOK && (i < MAX_MBR_PARTS));
880 return allOK;
881} // BasicMBRData::SpaceBeforeAllLogicals()
882
883// Returns 1 if the partitions describe a legal layout -- all logicals
884// are contiguous and have at least one preceding empty partitions,
885// the number of primaries is under 4 (or under 3 if there are any
886// logicals), there are no overlapping partitions, etc.
887// Does NOT assume that primaries are numbered 1-4; uses the
888// IsItPrimary() function of the MBRPart class to determine
889// primary status. Also does NOT consider partition order; there
890// can be gaps and it will still be considered legal.
891int BasicMBRData::IsLegal(void) {
892 int allOK = 1;
893
894 allOK = (FindOverlaps() == 0);
895 allOK = (allOK && (NumPrimaries() <= 4));
896 allOK = (allOK && AreLogicalsContiguous());
897 allOK = (allOK && DoTheyFit());
898 allOK = (allOK && SpaceBeforeAllLogicals());
899 return allOK;
900} // BasicMBRData::IsLegal()
901
902// Finds the next in-use partition, starting with start (will return start
903// if it's in use). Returns -1 if no subsequent partition is in use.
904int BasicMBRData::FindNextInUse(int start) {
905 if (start >= MAX_MBR_PARTS)
906 start = -1;
907 while ((start < MAX_MBR_PARTS) && (start >= 0) && (partitions[start].GetInclusion() == NONE))
908 start++;
909 if ((start < 0) || (start >= MAX_MBR_PARTS))
910 start = -1;
911 return start;
912} // BasicMBRData::FindFirstLogical();
srs5694f2efa7d2011-03-01 22:03:26 -0500913
914/*****************************************************
915 * *
916 * Functions to create, delete, or change partitions *
917 * *
918 *****************************************************/
919
920// Empty all data. Meant mainly for calling by constructors, but it's also
921// used by the hybrid MBR functions in the GPTData class.
922void BasicMBRData::EmptyMBR(int clearBootloader) {
923 int i;
924
925 // Zero out the boot loader section, the disk signature, and the
926 // 2-byte nulls area only if requested to do so. (This is the
927 // default.)
928 if (clearBootloader == 1) {
929 EmptyBootloader();
930 } // if
931
932 // Blank out the partitions
933 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500934 partitions[i].Empty();
srs5694f2efa7d2011-03-01 22:03:26 -0500935 } // for
936 MBRSignature = MBR_SIGNATURE;
srs5694bf8950c2011-03-12 01:23:12 -0500937 state = mbr;
srs5694f2efa7d2011-03-01 22:03:26 -0500938} // BasicMBRData::EmptyMBR()
939
940// Blank out the boot loader area. Done with the initial MBR-to-GPT
941// conversion, since MBR boot loaders don't understand GPT, and so
942// need to be replaced....
943void BasicMBRData::EmptyBootloader(void) {
944 int i;
945
946 for (i = 0; i < 440; i++)
947 code[i] = 0;
948 nulls = 0;
949} // BasicMBRData::EmptyBootloader
950
srs5694bf8950c2011-03-12 01:23:12 -0500951// Create a partition of the specified number based on the passed
952// partition. This function does *NO* error checking, so it's possible
srs5694f2efa7d2011-03-01 22:03:26 -0500953// to seriously screw up a partition table using this function!
954// Note: This function should NOT be used to create the 0xEE partition
955// in a conventional GPT configuration, since that partition has
956// specific size requirements that this function won't handle. It may
957// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
958// since those toss the rulebook away anyhow....
srs5694bf8950c2011-03-12 01:23:12 -0500959void BasicMBRData::AddPart(int num, const MBRPart& newPart) {
960 partitions[num] = newPart;
961} // BasicMBRData::AddPart()
962
963// Create a partition of the specified number, starting LBA, and
964// length. This function does almost no error checking, so it's possible
965// 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....
971void BasicMBRData::MakePart(int num, uint64_t start, uint64_t length, int type, int bootable) {
972 if ((num >= 0) && (num < MAX_MBR_PARTS) && (start <= UINT32_MAX) && (length <= UINT32_MAX)) {
973 partitions[num].Empty();
974 partitions[num].SetType(type);
975 partitions[num].SetLocation(start, length);
976 if (num < 4)
977 partitions[num].SetInclusion(PRIMARY);
978 else
979 partitions[num].SetInclusion(LOGICAL);
srs5694f2efa7d2011-03-01 22:03:26 -0500980 SetPartBootable(num, bootable);
srs5694bf8950c2011-03-12 01:23:12 -0500981 } // if valid partition number & size
srs5694f2efa7d2011-03-01 22:03:26 -0500982} // BasicMBRData::MakePart()
983
984// Set the partition's type code.
985// Returns 1 if successful, 0 if not (invalid partition number)
986int BasicMBRData::SetPartType(int num, int type) {
987 int allOK = 1;
988
989 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -0500990 if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
991 allOK = partitions[num].SetType(type);
srs5694f2efa7d2011-03-01 22:03:26 -0500992 } else allOK = 0;
993 } else allOK = 0;
994 return allOK;
995} // BasicMBRData::SetPartType()
996
997// Set (or remove) the partition's bootable flag. Setting it is the
998// default; pass 0 as bootable to remove the flag.
999// Returns 1 if successful, 0 if not (invalid partition number)
1000int BasicMBRData::SetPartBootable(int num, int bootable) {
1001 int allOK = 1;
1002
1003 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -05001004 if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
srs5694f2efa7d2011-03-01 22:03:26 -05001005 if (bootable == 0)
srs5694bf8950c2011-03-12 01:23:12 -05001006 partitions[num].SetStatus(UINT8_C(0x00));
srs5694f2efa7d2011-03-01 22:03:26 -05001007 else
srs5694bf8950c2011-03-12 01:23:12 -05001008 partitions[num].SetStatus(UINT8_C(0x80));
srs5694f2efa7d2011-03-01 22:03:26 -05001009 } else allOK = 0;
1010 } else allOK = 0;
1011 return allOK;
1012} // BasicMBRData::SetPartBootable()
1013
1014// Create a partition that fills the most available space. Returns
1015// 1 if partition was created, 0 otherwise. Intended for use in
1016// creating hybrid MBRs.
1017int BasicMBRData::MakeBiggestPart(int i, int type) {
srs5694bf8950c2011-03-12 01:23:12 -05001018 uint64_t start = UINT64_C(1); // starting point for each search
1019 uint64_t firstBlock; // first block in a segment
1020 uint64_t lastBlock; // last block in a segment
1021 uint64_t segmentSize; // size of segment in blocks
1022 uint64_t selectedSegment = UINT64_C(0); // location of largest segment
1023 uint64_t selectedSize = UINT64_C(0); // size of largest segment in blocks
srs5694f2efa7d2011-03-01 22:03:26 -05001024 int found = 0;
1025
1026 do {
1027 firstBlock = FindFirstAvailable(start);
srs5694bf8950c2011-03-12 01:23:12 -05001028 if (firstBlock > UINT64_C(0)) { // something's free...
srs5694f2efa7d2011-03-01 22:03:26 -05001029 lastBlock = FindLastInFree(firstBlock);
srs5694bf8950c2011-03-12 01:23:12 -05001030 segmentSize = lastBlock - firstBlock + UINT64_C(1);
srs5694f2efa7d2011-03-01 22:03:26 -05001031 if (segmentSize > selectedSize) {
1032 selectedSize = segmentSize;
1033 selectedSegment = firstBlock;
1034 } // if
1035 start = lastBlock + 1;
1036 } // if
1037 } while (firstBlock != 0);
srs5694bf8950c2011-03-12 01:23:12 -05001038 if ((selectedSize > UINT64_C(0)) && (selectedSize < diskSize)) {
srs5694f2efa7d2011-03-01 22:03:26 -05001039 found = 1;
1040 MakePart(i, selectedSegment, selectedSize, type, 0);
1041 } else {
1042 found = 0;
1043 } // if/else
1044 return found;
1045} // BasicMBRData::MakeBiggestPart(int i)
1046
1047// Delete partition #i
1048void BasicMBRData::DeletePartition(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001049 partitions[i].Empty();
srs5694f2efa7d2011-03-01 22:03:26 -05001050} // BasicMBRData::DeletePartition()
1051
srs5694bf8950c2011-03-12 01:23:12 -05001052// Set the inclusion status (PRIMARY, LOGICAL, or NONE) with some sanity
1053// checks to ensure the table remains legal.
1054// Returns 1 on success, 0 on failure.
1055int BasicMBRData::SetInclusionwChecks(int num, int inclStatus) {
1056 int allOK = 1, origValue;
1057
1058 if (IsLegal()) {
1059 if ((inclStatus == PRIMARY) || (inclStatus == LOGICAL) || (inclStatus == NONE)) {
1060 origValue = partitions[num].GetInclusion();
1061 partitions[num].SetInclusion(inclStatus);
1062 if (!IsLegal()) {
1063 partitions[num].SetInclusion(origValue);
1064 cerr << "Specified change is not legal! Aborting change!\n";
1065 } // if
1066 } else {
1067 cerr << "Invalid partition inclusion code in BasicMBRData::SetInclusionwChecks()!\n";
1068 } // if/else
1069 } else {
1070 cerr << "Partition table is not currently in a valid state. Aborting change!\n";
1071 allOK = 0;
1072 } // if/else
1073 return allOK;
1074} // BasicMBRData::SetInclusionwChecks()
1075
srs5694f2efa7d2011-03-01 22:03:26 -05001076// Recomputes the CHS values for the specified partition and adjusts the value.
1077// Note that this will create a technically incorrect CHS value for EFI GPT (0xEE)
1078// protective partitions, but this is required by some buggy BIOSes, so I'm
1079// providing a function to do this deliberately at the user's command.
1080// This function does nothing if the partition's length is 0.
1081void BasicMBRData::RecomputeCHS(int partNum) {
srs5694bf8950c2011-03-12 01:23:12 -05001082// uint64_t firstLBA, lengthLBA;
srs5694f2efa7d2011-03-01 22:03:26 -05001083
srs5694bf8950c2011-03-12 01:23:12 -05001084 partitions[partNum].RecomputeCHS();
1085/* firstLBA = (uint64_t) partitions[partNum].firstLBA;
srs5694f2efa7d2011-03-01 22:03:26 -05001086 lengthLBA = (uint64_t) partitions[partNum].lengthLBA;
1087
1088 if (lengthLBA > 0) {
1089 LBAtoCHS(firstLBA, partitions[partNum].firstSector);
1090 LBAtoCHS(firstLBA + lengthLBA - 1, partitions[partNum].lastSector);
srs5694bf8950c2011-03-12 01:23:12 -05001091 } // if */
srs5694f2efa7d2011-03-01 22:03:26 -05001092} // BasicMBRData::RecomputeCHS()
1093
srs5694bf8950c2011-03-12 01:23:12 -05001094// Swap the contents of two partitions.
1095// Returns 1 if successful, 0 if either partition is out of range
1096// (that is, not a legal number; either or both can be empty).
1097// Note that if partNum1 = partNum2 and this number is in range,
1098// it will be considered successful.
1099int BasicMBRData::SwapPartitions(uint32_t partNum1, uint32_t partNum2) {
1100 MBRPart temp;
1101 int allOK = 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001102
srs5694bf8950c2011-03-12 01:23:12 -05001103 if ((partNum1 < MAX_MBR_PARTS) && (partNum2 < MAX_MBR_PARTS)) {
1104 if (partNum1 != partNum2) {
1105 temp = partitions[partNum1];
1106 partitions[partNum1] = partitions[partNum2];
1107 partitions[partNum2] = temp;
srs5694f2efa7d2011-03-01 22:03:26 -05001108 } // if
srs5694bf8950c2011-03-12 01:23:12 -05001109 } else allOK = 0; // partition numbers are valid
1110 return allOK;
1111} // BasicMBRData::SwapPartitions()
srs5694f2efa7d2011-03-01 22:03:26 -05001112
srs5694bf8950c2011-03-12 01:23:12 -05001113// Sort the MBR entries, eliminating gaps and making for a logical
1114// ordering. Relies on QuickSortMBR() for the bulk of the work
1115void BasicMBRData::SortMBR(int start) {
1116 int i, numFound, firstPart, lastPart;
1117 uint32_t fp, lp;
1118
1119 // First, find the last partition with data, so as not to
1120 // spend needless time sorting empty entries....
1121 numFound = GetPartRange(&fp, &lp);
1122 firstPart = (int) fp;
1123 lastPart = (int) lp;
1124 if (firstPart < start)
1125 firstPart = start;
1126
1127 // Now swap empties with the last partitions, to simplify the logic
1128 // in the Quicksort function....
1129 i = start;
1130 while (i < lastPart) {
1131 if (partitions[i].GetStartLBA() == 0) {
1132 SwapPartitions(i, lastPart);
1133 do {
1134 lastPart--;
1135 } while ((lastPart > 0) && (partitions[lastPart].GetStartLBA() == 0));
1136 } // if
1137 i++;
1138 } // while
1139
1140 // If there are more empties than partitions in the range from 0 to lastPart,
1141 // the above leaves lastPart set too high, so we've got to adjust it to
1142 // prevent empties from migrating to the top of the list....
1143 GetPartRange(&fp, &lp);
1144 lastPart = (int) lp;
1145
1146 // Now call the recursive quick sort routine to do the real work....
1147 QuickSortMBR(start, lastPart);
1148} // GPTData::SortGPT()
1149
1150// Recursive quick sort algorithm for MBR partitions. Note that if there
1151// are any empties in the specified range, they'll be sorted to the
1152// start, resulting in a sorted set of partitions that begins with
1153// partition 2, 3, or higher.
1154void BasicMBRData::QuickSortMBR(int start, int finish) {
1155 uint64_t starterValue; // starting location of median partition
1156 int left, right;
1157
1158 left = start;
1159 right = finish;
1160 starterValue = partitions[(start + finish) / 2].GetStartLBA();
1161 do {
1162 while (partitions[left].GetStartLBA() < starterValue)
1163 left++;
1164 while (partitions[right].GetStartLBA() > starterValue)
1165 right--;
1166 if (left <= right)
1167 SwapPartitions(left++, right--);
1168 } while (left <= right);
1169 if (start < right) QuickSortMBR(start, right);
1170 if (finish > left) QuickSortMBR(left, finish);
1171} // BasicMBRData::QuickSortMBR()
1172
1173// Delete any partitions that are too big to fit on the disk
1174// or that are too big for MBR (32-bit limits).
1175// This really deletes the partitions by setting values to 0.
1176// Returns the number of partitions deleted in this way.
1177int BasicMBRData::DeleteOversizedParts() {
1178 int num = 0, i;
1179
1180 for (i = 0; i < MAX_MBR_PARTS; i++) {
1181 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize) ||
1182 (partitions[i].GetStartLBA() > UINT32_MAX) || (partitions[i].GetLengthLBA() > UINT32_MAX)) {
1183 partitions[i].Empty();
1184 num++;
1185 } // if
1186 } // for
1187 return num;
1188} // BasicMBRData::DeleteOversizedParts()
1189
1190// Search for and delete extended partitions.
1191// Returns the number of partitions deleted.
1192int BasicMBRData::DeleteExtendedParts() {
1193 int i, numDeleted = 0;
1194 uint8_t type;
1195
1196 for (i = 0; i < MAX_MBR_PARTS; i++) {
1197 type = partitions[i].GetType();
1198 if (((type == 0x05) || (type == 0x0f) || (type == (0x85))) &&
1199 (partitions[i].GetLengthLBA() > 0)) {
1200 partitions[i].Empty();
1201 numDeleted++;
1202 } // if
1203 } // for
1204 return numDeleted;
1205} // BasicMBRData::DeleteExtendedParts()
1206
1207// Finds any overlapping partitions and omits the smaller of the two.
1208void BasicMBRData::OmitOverlaps() {
1209 int i, j;
1210
1211 for (i = 0; i < MAX_MBR_PARTS; i++) {
1212 for (j = i + 1; j < MAX_MBR_PARTS; j++) {
1213 if ((partitions[i].GetInclusion() != NONE) &&
1214 partitions[i].DoTheyOverlap(partitions[j])) {
1215 if (partitions[i].GetLengthLBA() < partitions[j].GetLengthLBA())
1216 partitions[i].SetInclusion(NONE);
1217 else
1218 partitions[j].SetInclusion(NONE);
srs5694f2efa7d2011-03-01 22:03:26 -05001219 } // if
srs5694bf8950c2011-03-12 01:23:12 -05001220 } // for (j...)
1221 } // for (i...)
1222} // BasicMBRData::OmitOverlaps()
1223
1224/* // Omits all partitions; used as starting point in MakeItLegal()
1225void BasicMBRData::OmitAll(void) {
1226 int i;
1227
1228 for (i = 0; i < MAX_MBR_PARTS; i++) {
1229 partitions[i].SetInclusion(NONE);
1230 } // for
1231} // BasicMBRData::OmitAll() */
1232
1233// Convert as many partitions into logicals as possible, except for
1234// the first partition, if possible.
1235void BasicMBRData::MaximizeLogicals() {
1236 int earliestPart = 0, earliestPartWas = NONE, i;
1237
1238 for (i = MAX_MBR_PARTS - 1; i >= 0; i--) {
1239 UpdateCanBeLogical();
1240 earliestPart = i;
1241 if (partitions[i].CanBeLogical()) {
1242 partitions[i].SetInclusion(LOGICAL);
1243 } else if (partitions[i].CanBePrimary()) {
1244 partitions[i].SetInclusion(PRIMARY);
1245 } else {
1246 partitions[i].SetInclusion(NONE);
1247 } // if/elseif/else
1248 } // for
1249 // If we have spare primaries, convert back the earliest partition to
1250 // its original state....
1251 if ((NumPrimaries() < 4) && (partitions[earliestPart].GetInclusion() == LOGICAL))
1252 partitions[earliestPart].SetInclusion(earliestPartWas);
1253} // BasicMBRData::MaximizeLogicals()
1254
1255// Add primaries up to the maximum allowed, from the omitted category.
1256void BasicMBRData::MaximizePrimaries() {
1257 int num, i = 0;
1258
1259 num = NumPrimaries();
1260 while ((num < 4) && (i < MAX_MBR_PARTS)) {
1261 if ((partitions[i].GetInclusion() == NONE) && (partitions[i].CanBePrimary())) {
1262 partitions[i].SetInclusion(PRIMARY);
1263 num++;
1264 UpdateCanBeLogical();
1265 } // if
1266 i++;
1267 } // while
1268} // BasicMBRData::MaximizePrimaries()
1269
1270// Remove primary partitions in excess of 4, starting with the later ones,
1271// in terms of the array location....
1272void BasicMBRData::TrimPrimaries(void) {
1273 int numToDelete, i = MAX_MBR_PARTS;
1274
1275 numToDelete = NumPrimaries() - 4;
1276 while ((numToDelete > 0) && (i >= 0)) {
1277 if (partitions[i].GetInclusion() == PRIMARY) {
1278 partitions[i].SetInclusion(NONE);
1279 numToDelete--;
1280 } // if
1281 i--;
1282 } // while (numToDelete > 0)
1283} // BasicMBRData::TrimPrimaries()
1284
1285// Locates primary partitions located between logical partitions and
1286// either converts the primaries into logicals (if possible) or omits
1287// them.
1288void BasicMBRData::MakeLogicalsContiguous(void) {
1289 uint64_t firstLogicalLBA, lastLogicalLBA;
1290 int i;
1291
1292 firstLogicalLBA = FirstLogicalLBA();
1293 lastLogicalLBA = LastLogicalLBA();
1294 for (i = 0; i < MAX_MBR_PARTS; i++) {
1295 if ((partitions[i].GetInclusion() == PRIMARY) &&
1296 (partitions[i].GetStartLBA() >= firstLogicalLBA) &&
1297 (partitions[i].GetLastLBA() <= lastLogicalLBA)) {
1298 if (SectorUsedAs(partitions[i].GetStartLBA() - 1) == NONE)
1299 partitions[i].SetInclusion(LOGICAL);
1300 else
1301 partitions[i].SetInclusion(NONE);
1302 } // if
1303 } // for
1304} // BasicMBRData::MakeLogicalsContiguous()
1305
1306// If MBR data aren't legal, adjust primary/logical assignments and,
1307// if necessary, drop partitions, to make the data legal.
1308void BasicMBRData::MakeItLegal(void) {
1309 if (!IsLegal()) {
1310 DeleteOversizedParts();
1311// OmitAll();
1312 MaximizeLogicals();
1313 MaximizePrimaries();
1314 if (!AreLogicalsContiguous())
1315 MakeLogicalsContiguous();
1316 if (NumPrimaries() > 4)
1317 TrimPrimaries();
1318 OmitOverlaps();
1319 } // if
1320} // BasicMBRData::MakeItLegal()
1321
1322// Removes logical partitions and deactivated partitions from first four
1323// entries (primary space).
1324// Returns the number of partitions moved.
1325int BasicMBRData::RemoveLogicalsFromFirstFour(void) {
1326 int i, j = 4, numMoved = 0, swapped = 0;
1327 MBRPart temp;
1328
1329 for (i = 0; i < 4; i++) {
1330 if ((partitions[i].GetInclusion() != PRIMARY) && (partitions[i].GetLengthLBA() > 0)) {
1331 j = 4;
1332 swapped = 0;
1333 do {
1334 if ((partitions[j].GetInclusion() == NONE) && (partitions[j].GetLengthLBA() == 0)) {
1335 temp = partitions[j];
1336 partitions[j] = partitions[i];
1337 partitions[i] = temp;
1338 swapped = 1;
1339 numMoved++;
1340 } // if
1341 j++;
1342 } while ((j < MAX_MBR_PARTS) && !swapped);
1343 if (j >= MAX_MBR_PARTS)
1344 cerr << "Warning! Too many partitions in BasicMBRData::RemoveLogicalsFromFirstFour()!\n";
1345 } // if
1346 } // for i...
1347 return numMoved;
1348} // BasicMBRData::RemoveLogicalsFromFirstFour()
1349
1350// Move all primaries into the first four partition spaces
1351// Returns the number of partitions moved.
1352int BasicMBRData::MovePrimariesToFirstFour(void) {
1353 int i, j = 0, numMoved = 0, swapped = 0;
1354 MBRPart temp;
1355
1356 for (i = 4; i < MAX_MBR_PARTS; i++) {
1357 if (partitions[i].GetInclusion() == PRIMARY) {
1358 j = 0;
1359 swapped = 0;
1360 do {
1361 if (partitions[j].GetInclusion() != PRIMARY) {
1362 temp = partitions[j];
1363 partitions[j] = partitions[i];
1364 partitions[i] = temp;
1365 swapped = 1;
1366 numMoved++;
1367 } // if
1368 j++;
1369 } while ((j < 4) && !swapped);
1370 } // if
1371 } // for
1372 return numMoved;
1373} // BasicMBRData::MovePrimariesToFirstFour()
1374
1375// Create an extended partition, if necessary, to hold the logical partitions.
1376// This function also sorts the primaries into the first four positions of
1377// the table.
1378// Returns 1 on success, 0 on failure.
1379int BasicMBRData::CreateExtended(void) {
1380 int allOK = 1, i = 0, swapped = 0;
1381 MBRPart temp;
1382
1383 if (IsLegal()) {
1384 // Move logicals out of primary space...
1385 RemoveLogicalsFromFirstFour();
1386 // Move primaries out of logical space...
1387 MovePrimariesToFirstFour();
1388
1389 // Create the extended partition
1390 if (NumLogicals() > 0) {
1391 SortMBR(4); // sort starting from 4 -- that is, logicals only
1392 temp.Empty();
1393 temp.SetStartLBA(FirstLogicalLBA() - 1);
1394 temp.SetLengthLBA(LastLogicalLBA() - FirstLogicalLBA() + 2);
1395 temp.SetType(0x0f, 1);
1396 temp.SetInclusion(PRIMARY);
1397 do {
1398 if ((partitions[i].GetInclusion() == NONE) || (partitions[i].GetLengthLBA() == 0)) {
1399 partitions[i] = temp;
1400 swapped = 1;
1401 } // if
1402 i++;
1403 } while ((i < 4) && !swapped);
1404 if (!swapped) {
1405 cerr << "Could not create extended partition; no room in primary table!\n";
1406 allOK = 0;
1407 } // if
1408 } // if (NumLogicals() > 0)
1409 } else allOK = 0;
1410 // Do a final check for EFI GPT (0xEE) partitions & flag as a problem if found
1411 // along with an extended partition
1412 for (i = 0; i < MAX_MBR_PARTS; i++)
1413 if (swapped && partitions[i].GetType() == 0xEE)
1414 allOK = 0;
1415 return allOK;
1416} // BasicMBRData::CreateExtended()
srs5694f2efa7d2011-03-01 22:03:26 -05001417
1418/****************************************
1419 * *
1420 * Functions to find data on free space *
1421 * *
1422 ****************************************/
1423
1424// Finds the first free space on the disk from start onward; returns 0
1425// if none available....
srs5694bf8950c2011-03-12 01:23:12 -05001426uint64_t BasicMBRData::FindFirstAvailable(uint64_t start) {
1427 uint64_t first;
1428 uint64_t i;
srs5694f2efa7d2011-03-01 22:03:26 -05001429 int firstMoved;
1430
1431 first = start;
1432
1433 // ...now search through all partitions; if first is within an
1434 // existing partition, move it to the next sector after that
1435 // partition and repeat. If first was moved, set firstMoved
1436 // flag; repeat until firstMoved is not set, so as to catch
1437 // cases where partitions are out of sequential order....
1438 do {
1439 firstMoved = 0;
1440 for (i = 0; i < 4; i++) {
1441 // Check if it's in the existing partition
srs5694bf8950c2011-03-12 01:23:12 -05001442 if ((first >= partitions[i].GetStartLBA()) &&
1443 (first < (partitions[i].GetStartLBA() + partitions[i].GetLengthLBA()))) {
1444 first = partitions[i].GetStartLBA() + partitions[i].GetLengthLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001445 firstMoved = 1;
1446 } // if
1447 } // for
1448 } while (firstMoved == 1);
srs5694bf8950c2011-03-12 01:23:12 -05001449 if ((first >= diskSize) || (first > UINT32_MAX))
srs5694f2efa7d2011-03-01 22:03:26 -05001450 first = 0;
1451 return (first);
1452} // BasicMBRData::FindFirstAvailable()
1453
1454// Finds the last free sector on the disk from start forward.
srs5694bf8950c2011-03-12 01:23:12 -05001455uint64_t BasicMBRData::FindLastInFree(uint64_t start) {
1456 uint64_t nearestStart;
1457 uint64_t i;
srs5694f2efa7d2011-03-01 22:03:26 -05001458
1459 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs5694bf8950c2011-03-12 01:23:12 -05001460 nearestStart = diskSize - 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001461 else
1462 nearestStart = UINT32_MAX - 1;
1463 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -05001464 if ((nearestStart > partitions[i].GetStartLBA()) &&
1465 (partitions[i].GetStartLBA() > start)) {
1466 nearestStart = partitions[i].GetStartLBA() - 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001467 } // if
1468 } // for
1469 return (nearestStart);
1470} // BasicMBRData::FindLastInFree()
1471
1472// Finds the first free sector on the disk from start backward.
srs5694bf8950c2011-03-12 01:23:12 -05001473uint64_t BasicMBRData::FindFirstInFree(uint64_t start) {
1474 uint64_t bestLastLBA, thisLastLBA;
srs5694f2efa7d2011-03-01 22:03:26 -05001475 int i;
1476
1477 bestLastLBA = 1;
1478 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -05001479 thisLastLBA = partitions[i].GetLastLBA() + 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001480 if (thisLastLBA > 0)
1481 thisLastLBA--;
1482 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start))
1483 bestLastLBA = thisLastLBA + 1;
1484 } // for
1485 return (bestLastLBA);
1486} // BasicMBRData::FindFirstInFree()
1487
srs5694bf8950c2011-03-12 01:23:12 -05001488// Returns NONE (unused), PRIMARY, LOGICAL, EBR (for EBR or MBR), or INVALID
1489int BasicMBRData::SectorUsedAs(uint64_t sector, int topPartNum) {
1490 int i = 0, usedAs = NONE;
srs5694f2efa7d2011-03-01 22:03:26 -05001491
srs5694bf8950c2011-03-12 01:23:12 -05001492 do {
1493 if ((partitions[i].GetStartLBA() <= sector) && (partitions[i].GetLastLBA() >= sector))
1494 usedAs = partitions[i].GetInclusion();
1495 if ((partitions[i].GetStartLBA() == (sector + 1)) && (partitions[i].GetInclusion() == LOGICAL))
1496 usedAs = EBR;
1497 if (sector == 0)
1498 usedAs = EBR;
1499 if (sector >= diskSize)
1500 usedAs = INVALID;
1501 i++;
1502 } while ((i < topPartNum) && (usedAs == NONE));
1503 return usedAs;
1504} // BasicMBRData::SectorUsedAs()
1505
1506/* // Returns 1 if the specified sector is unallocated, 0 if it's
1507// allocated.
1508int BasicMBRData::IsFree(uint64_t sector, int topPartNum) {
1509 int i, isFree = 1;
1510
1511 for (i = 0; i < topPartNum; i++) {
1512 if ((partitions[i].GetStartLBA() <= sector) && (partitions[i].GetLastLBA() >= sector)
1513 && (partitions[i].GetInclusion() != NONE))
srs5694f2efa7d2011-03-01 22:03:26 -05001514 isFree = 0;
1515 } // for
1516 return isFree;
srs5694bf8950c2011-03-12 01:23:12 -05001517} // BasicMBRData::IsFree() */
srs5694f2efa7d2011-03-01 22:03:26 -05001518
1519/******************************************************
1520 * *
1521 * Functions that extract data on specific partitions *
1522 * *
1523 ******************************************************/
1524
1525uint8_t BasicMBRData::GetStatus(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001526 MBRPart* thePart;
srs5694f2efa7d2011-03-01 22:03:26 -05001527 uint8_t retval;
1528
1529 thePart = GetPartition(i);
1530 if (thePart != NULL)
srs5694bf8950c2011-03-12 01:23:12 -05001531 retval = thePart->GetStatus();
srs5694f2efa7d2011-03-01 22:03:26 -05001532 else
1533 retval = UINT8_C(0);
1534 return retval;
1535} // BasicMBRData::GetStatus()
1536
1537uint8_t BasicMBRData::GetType(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001538 MBRPart* thePart;
srs5694f2efa7d2011-03-01 22:03:26 -05001539 uint8_t retval;
1540
1541 thePart = GetPartition(i);
1542 if (thePart != NULL)
srs5694bf8950c2011-03-12 01:23:12 -05001543 retval = thePart->GetType();
srs5694f2efa7d2011-03-01 22:03:26 -05001544 else
1545 retval = UINT8_C(0);
1546 return retval;
1547} // BasicMBRData::GetType()
1548
srs5694bf8950c2011-03-12 01:23:12 -05001549uint64_t BasicMBRData::GetFirstSector(int i) {
1550 MBRPart* thePart;
1551 uint64_t retval;
srs5694f2efa7d2011-03-01 22:03:26 -05001552
1553 thePart = GetPartition(i);
1554 if (thePart != NULL) {
srs5694bf8950c2011-03-12 01:23:12 -05001555 retval = thePart->GetStartLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001556 } else
1557 retval = UINT32_C(0);
1558 return retval;
1559} // BasicMBRData::GetFirstSector()
1560
srs5694bf8950c2011-03-12 01:23:12 -05001561uint64_t BasicMBRData::GetLength(int i) {
1562 MBRPart* thePart;
1563 uint64_t retval;
srs5694f2efa7d2011-03-01 22:03:26 -05001564
1565 thePart = GetPartition(i);
1566 if (thePart != NULL) {
srs5694bf8950c2011-03-12 01:23:12 -05001567 retval = thePart->GetLengthLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001568 } else
srs5694bf8950c2011-03-12 01:23:12 -05001569 retval = UINT64_C(0);
srs5694f2efa7d2011-03-01 22:03:26 -05001570 return retval;
1571} // BasicMBRData::GetLength()
1572
1573/***********************
1574 * *
1575 * Protected functions *
1576 * *
1577 ***********************/
1578
1579// Return a pointer to a primary or logical partition, or NULL if
1580// the partition is out of range....
srs5694bf8950c2011-03-12 01:23:12 -05001581MBRPart* BasicMBRData::GetPartition(int i) {
1582 MBRPart* thePart = NULL;
srs5694f2efa7d2011-03-01 22:03:26 -05001583
1584 if ((i >= 0) && (i < MAX_MBR_PARTS))
1585 thePart = &partitions[i];
1586 return thePart;
1587} // GetPartition()
srs5694bf8950c2011-03-12 01:23:12 -05001588
1589/*******************************************
1590 * *
1591 * Functions that involve user interaction *
1592 * *
1593 *******************************************/
1594
1595// Present the MBR operations menu. Note that the 'w' option does not
1596// immediately write data; that's handled by the calling function.
1597// Returns the number of partitions defined on exit, or -1 if the
1598// user selected the 'q' option. (Thus, the caller should save data
1599// if the return value is >0, or possibly >=0 depending on intentions.)
1600int BasicMBRData::DoMenu(const string& prompt) {
1601 char line[255];
1602 int goOn = 1, quitting = 0, retval, num, haveShownInfo = 0;
1603 unsigned int hexCode = 0x00;
1604
1605 do {
1606 cout << prompt;
1607 ReadCString(line, 255);
1608 switch (*line) {
1609 case '\n':
1610 break;
1611 case 'a': case 'A':
1612 num = GetNumber(1, MAX_MBR_PARTS, 1, "Toggle active flag for partition: ") - 1;
1613 if (partitions[num].GetInclusion() != NONE)
1614 partitions[num].SetStatus(partitions[num].GetStatus() ^ 0x80);
1615 break;
1616 case 'c': case 'C':
1617 for (num = 0; num < MAX_MBR_PARTS; num++)
1618 RecomputeCHS(num);
1619 break;
1620 case 'l': case 'L':
1621 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as logical: ") - 1;
1622 SetInclusionwChecks(num, LOGICAL);
1623 break;
1624 case 'o': case 'O':
1625 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to omit: ") - 1;
1626 SetInclusionwChecks(num, NONE);
1627 break;
1628 case 'p': case 'P':
1629 if (!haveShownInfo) {
1630 cout << "\n** NOTE: Partition numbers do NOT indicate final primary/logical "
1631 << "status,\n** unlike in most MBR partitioning tools!\n\a";
1632 cout << "\n** Extended partitions are not displayed, but will be generated "
1633 << "as required.\n";
1634 haveShownInfo = 1;
1635 } // if
1636 DisplayMBRData();
1637 break;
1638 case 'q': case 'Q':
1639 cout << "This will abandon your changes. Are you sure? ";
1640 if (GetYN() == 'Y') {
1641 goOn = 0;
1642 quitting = 1;
1643 } // if
1644 break;
1645 case 'r': case 'R':
1646 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as primary: ") - 1;
1647 SetInclusionwChecks(num, PRIMARY);
1648 break;
1649 case 's': case 'S':
1650 SortMBR();
1651 break;
1652 case 't': case 'T':
1653 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to change type code: ") - 1;
1654 if (partitions[num].GetLengthLBA() > 0) {
1655 hexCode = 0;
1656 while ((hexCode <= 0) || (hexCode > 255)) {
1657 cout << "Enter an MBR hex code: ";
1658 ReadCString(line, 255);
1659 sscanf(line, "%x", &hexCode);
1660 if (line[0] == '\n')
1661 hexCode = 0x00;
1662 } // while
1663 partitions[num].SetType(hexCode);
1664 } // if
1665 break;
1666 case 'w': case 'W':
1667 goOn = 0;
1668 break;
1669 default:
1670 ShowCommands();
1671 break;
1672 } // switch
1673 } while (goOn);
1674 if (quitting)
1675 retval = -1;
1676 else
1677 retval = CountParts();
1678 return (retval);
1679} // BasicMBRData::DoMenu()
1680
1681void BasicMBRData::ShowCommands(void) {
1682 cout << "a\ttoggle the active/boot flag\n";
1683 cout << "c\trecompute all CHS values\n";
1684 cout << "l\tset partition as logical\n";
1685 cout << "o\tomit partition\n";
1686 cout << "p\tprint the MBR partition table\n";
1687 cout << "q\tquit without saving changes\n";
1688 cout << "r\tset partition as primary\n";
1689 cout << "s\tsort MBR partitions\n";
1690 cout << "t\tchange partition type code\n";
1691 cout << "w\twrite the MBR partition table to disk and exit\n";
1692} // BasicMBRData::ShowCommands()