blob: 6a8d103ecb7c1d9ad260fee1e1dc545eef060dca [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
6/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
7 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"
22#include "partnotes.h"
23#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;
38 srand((unsigned int) time(NULL));
39 numHeads = MAX_HEADS;
40 numSecspTrack = MAX_SECSPERTRACK;
41 myDisk = NULL;
42 canDeleteMyDisk = 0;
43 EmptyMBR();
44} // BasicMBRData default constructor
45
46BasicMBRData::BasicMBRData(string filename) {
47 blockSize = SECTOR_SIZE;
48 diskSize = 0;
49 device = filename;
50 state = invalid;
51 numHeads = MAX_HEADS;
52 numSecspTrack = MAX_SECSPERTRACK;
53 myDisk = NULL;
54 canDeleteMyDisk = 0;
55
56 srand((unsigned int) time(NULL));
57 // Try to read the specified partition table, but if it fails....
58 if (!ReadMBRData(filename)) {
59 EmptyMBR();
60 device = "";
61 } // if
62} // BasicMBRData(string filename) constructor
63
64// Free space used by myDisk only if that's OK -- sometimes it will be
65// copied from an outside source, in which case that source should handle
66// it!
67BasicMBRData::~BasicMBRData(void) {
68 if (canDeleteMyDisk)
69 delete myDisk;
70} // BasicMBRData destructor
71
72// Assignment operator -- copy entire set of MBR data.
73BasicMBRData & BasicMBRData::operator=(const BasicMBRData & orig) {
74 int i;
75
76 for (i = 0; i < 440; i++)
77 code[i] = orig.code[i];
78 diskSignature = orig.diskSignature;
79 nulls = orig.nulls;
80 MBRSignature = orig.MBRSignature;
81 blockSize = orig.blockSize;
82 diskSize = orig.diskSize;
83 numHeads = orig.numHeads;
84 numSecspTrack = orig.numSecspTrack;
85 canDeleteMyDisk = orig.canDeleteMyDisk;
86 device = orig.device;
87 state = orig.state;
88
89 myDisk = new DiskIO;
90 myDisk->OpenForRead(orig.myDisk->GetName());
91
92 for (i = 0; i < MAX_MBR_PARTS; i++) {
93 partitions[i] = orig.partitions[i];
94 } // for
95 return *this;
96} // BasicMBRData::operator=()
97
98/**********************
99 * *
100 * Disk I/O functions *
101 * *
102 **********************/
103
104// Read data from MBR. Returns 1 if read was successful (even if the
105// data isn't a valid MBR), 0 if the read failed.
106int BasicMBRData::ReadMBRData(const string & deviceFilename) {
107 int allOK = 1;
108
109 if (myDisk == NULL) {
110 myDisk = new DiskIO;
111 canDeleteMyDisk = 1;
112 } // if
113 if (myDisk->OpenForRead(deviceFilename)) {
114 allOK = ReadMBRData(myDisk);
115 } else {
116 allOK = 0;
117 } // if
118
119 if (allOK)
120 device = deviceFilename;
121
122 return allOK;
123} // BasicMBRData::ReadMBRData(const string & deviceFilename)
124
125// Read data from MBR. If checkBlockSize == 1 (the default), the block
126// size is checked; otherwise it's set to the default (512 bytes).
127// Note that any extended partition(s) present will be explicitly stored
128// in the partitions[] array, along with their contained partitions; the
129// extended container partition(s) should be ignored by other functions.
130int BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) {
131 int allOK = 1, i, j, logicalNum;
132 int err = 1;
133 TempMBR tempMBR;
134
135 if ((myDisk != NULL) && (canDeleteMyDisk)) {
136 delete myDisk;
137 canDeleteMyDisk = 0;
138 } // if
139
140 myDisk = theDisk;
141
142 // Empty existing MBR data, including the logical partitions...
143 EmptyMBR(0);
144
145 if (myDisk->Seek(0))
146 if (myDisk->Read(&tempMBR, 512))
147 err = 0;
148 if (err) {
149 cerr << "Problem reading disk in BasicMBRData::ReadMBRData()!\n";
150 } else {
151 for (i = 0; i < 440; i++)
152 code[i] = tempMBR.code[i];
153 diskSignature = tempMBR.diskSignature;
154 nulls = tempMBR.nulls;
155 for (i = 0; i < 4; i++) {
156 partitions[i].status = tempMBR.partitions[i].status;
157 partitions[i].partitionType = tempMBR.partitions[i].partitionType;
158 partitions[i].firstLBA = tempMBR.partitions[i].firstLBA;
159 partitions[i].lengthLBA = tempMBR.partitions[i].lengthLBA;
160 for (j = 0; j < 3; j++) {
161 partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j];
162 partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j];
163 } // for j... (reading parts of CHS geometry)
164 } // for i... (reading all four partitions)
165 MBRSignature = tempMBR.MBRSignature;
166
167 // Reverse the byte order, if necessary
168 if (IsLittleEndian() == 0) {
169 ReverseBytes(&diskSignature, 4);
170 ReverseBytes(&nulls, 2);
171 ReverseBytes(&MBRSignature, 2);
172 for (i = 0; i < 4; i++) {
173 ReverseBytes(&partitions[i].firstLBA, 4);
174 ReverseBytes(&partitions[i].lengthLBA, 4);
175 } // for
176 } // if
177
178 if (MBRSignature != MBR_SIGNATURE) {
179 allOK = 0;
180 state = invalid;
181 } // if
182
183 // Find disk size
184 diskSize = myDisk->DiskSize(&err);
185
186 // Find block size
187 if (checkBlockSize) {
188 blockSize = myDisk->GetBlockSize();
189 } // if (checkBlockSize)
190
191 // Load logical partition data, if any is found....
192 if (allOK) {
193 for (i = 0; i < 4; i++) {
194 if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
195 || (partitions[i].partitionType == 0x85)) {
196 // Found it, so call a recursive algorithm to load everything from them....
197 logicalNum = ReadLogicalPart(partitions[i].firstLBA, UINT32_C(0), 4);
198 if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
199 allOK = 0;
200 cerr << "Error reading logical partitions! List may be truncated!\n";
201 } // if maxLogicals valid
202 } // if primary partition is extended
203 } // for primary partition loop
204 if (allOK) { // Loaded logicals OK
205 state = mbr;
206 } else {
207 state = invalid;
208 } // if
209 } // if
210
211 // Check to see if it's in GPT format....
212 if (allOK) {
213 for (i = 0; i < 4; i++) {
214 if (partitions[i].partitionType == UINT8_C(0xEE)) {
215 state = gpt;
216 } // if
217 } // for
218 } // if
219
220 // If there's an EFI GPT partition, look for other partition types,
221 // to flag as hybrid
222 if (state == gpt) {
223 for (i = 0 ; i < 4; i++) {
224 if ((partitions[i].partitionType != UINT8_C(0xEE)) &&
225 (partitions[i].partitionType != UINT8_C(0x00)))
226 state = hybrid;
227 } // for
228 } // if (hybrid detection code)
229 } // no initial error
230 return allOK;
231} // BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize)
232
233// This is a recursive function to read all the logical partitions, following the
234// logical partition linked list from the disk and storing the basic data in the
235// partitions[] array. Returns last index to partitions[] used, or -1 if there was
236// a problem.
237// Parameters:
238// extendedStart = LBA of the start of the extended partition
239// diskOffset = LBA offset WITHIN the extended partition of the one to be read
240// partNum = location in partitions[] array to store retrieved data
241int BasicMBRData::ReadLogicalPart(uint32_t extendedStart, uint32_t diskOffset, int partNum) {
242 struct TempMBR ebr;
243 uint64_t offset;
244
245 // Check for a valid partition number. Note that partitions MAY be read into
246 // the area normally used by primary partitions, although the only calling
247 // function as of GPT fdisk version 0.5.0 doesn't do so.
248 if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
249 offset = (uint64_t) (extendedStart + diskOffset);
250 if (myDisk->Seek(offset) == 0) { // seek to EBR record
251 cerr << "Unable to seek to " << offset << "! Aborting!\n";
252 partNum = -1;
253 }
254 if (myDisk->Read(&ebr, 512) != 512) { // Load the data....
255 cerr << "Error seeking to or reading logical partition data from " << offset
256 << "!\nAborting!\n";
257 partNum = -1;
258 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
259 ReverseBytes(&ebr.MBRSignature, 2);
260 ReverseBytes(&ebr.partitions[0].firstLBA, 4);
261 ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
262 ReverseBytes(&ebr.partitions[1].firstLBA, 4);
263 ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
264 } // if/else/if
265
266 if (ebr.MBRSignature != MBR_SIGNATURE) {
267 partNum = -1;
268 cerr << "MBR signature in logical partition invalid; read 0x";
269 cerr.fill('0');
270 cerr.width(4);
271 cerr.setf(ios::uppercase);
272 cerr << hex << ebr.MBRSignature << ", but should be 0x";
273 cerr.width(4);
274 cerr << MBR_SIGNATURE << dec << "\n";
275 cerr.fill(' ');
276 } // if
277
278 // Copy over the basic data....
279 partitions[partNum].status = ebr.partitions[0].status;
280 partitions[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
281 partitions[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
282 partitions[partNum].partitionType = ebr.partitions[0].partitionType;
283
284 // Find the next partition (if there is one) and recurse....
285 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) &&
286 (partNum < (MAX_MBR_PARTS - 1))) {
287 partNum = ReadLogicalPart(extendedStart, ebr.partitions[1].firstLBA,
288 partNum + 1);
289 } else {
290 partNum++;
291 } // if another partition
292 } // Not enough space for all the logicals (or previous error encountered)
293 return (partNum);
294} // BasicMBRData::ReadLogicalPart()
295
296// Write the MBR data to the default defined device. This writes both the
297// MBR itself and any defined logical partitions, provided there's an
298// MBR extended partition.
299int BasicMBRData::WriteMBRData(void) {
300 int allOK = 1;
301
302 if (myDisk != NULL) {
303 if (myDisk->OpenForWrite() != 0) {
304 allOK = WriteMBRData(myDisk);
305 } else {
306 allOK = 0;
307 } // if/else
308 myDisk->Close();
309 } else allOK = 0;
310 return allOK;
311} // BasicMBRData::WriteMBRData(void)
312
313// Save the MBR data to a file. This writes both the
314// MBR itself and any defined logical partitions, provided there's an
315// MBR extended partition.
316int BasicMBRData::WriteMBRData(DiskIO *theDisk) {
317 int i, j, partNum, allOK, moreLogicals = 0;
318 uint32_t extFirstLBA = 0;
319 uint64_t writeEbrTo; // 64-bit because we support extended in 2-4TiB range
320 TempMBR tempMBR;
321
322 // First write the main MBR data structure....
323 for (i = 0; i < 440; i++)
324 tempMBR.code[i] = code[i];
325 tempMBR.diskSignature = diskSignature;
326 tempMBR.nulls = nulls;
327 tempMBR.MBRSignature = MBRSignature;
328 for (i = 0; i < 4; i++) {
329 tempMBR.partitions[i].status = partitions[i].status;
330 tempMBR.partitions[i].partitionType = partitions[i].partitionType;
331 tempMBR.partitions[i].firstLBA = partitions[i].firstLBA;
332 tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA;
333 for (j = 0; j < 3; j++) {
334 tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j];
335 tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j];
336 } // for j...
337 if (partitions[i].partitionType == 0x0f) {
338 extFirstLBA = partitions[i].firstLBA;
339 moreLogicals = 1;
340 } // if
341 } // for i...
342 allOK = WriteMBRData(tempMBR, theDisk, 0);
343
344 // Set up tempMBR with some constant data for logical partitions...
345 tempMBR.diskSignature = 0;
346 for (i = 2; i < 4; i++) {
347 tempMBR.partitions[i].firstLBA = tempMBR.partitions[i].lengthLBA = 0;
348 tempMBR.partitions[i].partitionType = 0x00;
349 for (j = 0; j < 3; j++) {
350 tempMBR.partitions[i].firstSector[j] = 0;
351 tempMBR.partitions[i].lastSector[j] = 0;
352 } // for j
353 } // for i
354
355 partNum = 4;
356 writeEbrTo = (uint64_t) extFirstLBA;
357 // If extended partition is present, write logicals...
358 while (allOK && moreLogicals && (partNum < MAX_MBR_PARTS)) {
359 tempMBR.partitions[0] = partitions[partNum];
360 tempMBR.partitions[0].firstLBA = 1; // partition starts on sector after EBR
361 // tempMBR.partitions[1] points to next EBR or terminates EBR linked list...
362 if ((partNum < MAX_MBR_PARTS - 1) && (partitions[partNum + 1].firstLBA > 0)) {
363 tempMBR.partitions[1].partitionType = 0x0f;
364 tempMBR.partitions[1].firstLBA = partitions[partNum + 1].firstLBA - 1;
365 tempMBR.partitions[1].lengthLBA = partitions[partNum + 1].lengthLBA + 1;
366 LBAtoCHS((uint64_t) tempMBR.partitions[1].firstLBA,
367 (uint8_t *) &tempMBR.partitions[1].firstSector);
368 LBAtoCHS(tempMBR.partitions[1].lengthLBA - extFirstLBA,
369 (uint8_t *) &tempMBR.partitions[1].lastSector);
370 } else {
371 tempMBR.partitions[1].partitionType = 0x00;
372 tempMBR.partitions[1].firstLBA = 0;
373 tempMBR.partitions[1].lengthLBA = 0;
374 moreLogicals = 0;
375 } // if/else
376 allOK = WriteMBRData(tempMBR, theDisk, writeEbrTo);
377 partNum++;
378 writeEbrTo = (uint64_t) tempMBR.partitions[1].firstLBA + (uint64_t) extFirstLBA;
379 } // while
380 return allOK;
381} // BasicMBRData::WriteMBRData(DiskIO *theDisk)
382
383int BasicMBRData::WriteMBRData(const string & deviceFilename) {
384 device = deviceFilename;
385 return WriteMBRData();
386} // BasicMBRData::WriteMBRData(const string & deviceFilename)
387
388// Write a single MBR record to the specified sector. Used by the like-named
389// function to write both the MBR and multiple EBR (for logical partition)
390// records.
391// Returns 1 on success, 0 on failure
392int BasicMBRData::WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector) {
393 int i, allOK;
394
395 // Reverse the byte order, if necessary
396 if (IsLittleEndian() == 0) {
397 ReverseBytes(&mbr.diskSignature, 4);
398 ReverseBytes(&mbr.nulls, 2);
399 ReverseBytes(&mbr.MBRSignature, 2);
400 for (i = 0; i < 4; i++) {
401 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
402 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
403 } // for
404 } // if
405
406 // Now write the data structure...
407 allOK = theDisk->OpenForWrite();
408 if (allOK && theDisk->Seek(sector)) {
409 if (theDisk->Write(&mbr, 512) != 512) {
410 allOK = 0;
411 cerr << "Error " << errno << " when saving MBR!\n";
412 } // if
413 } else {
414 allOK = 0;
415 cerr << "Error " << errno << " when seeking to MBR to write it!\n";
416 } // if/else
417 theDisk->Close();
418
419 // Reverse the byte order back, if necessary
420 if (IsLittleEndian() == 0) {
421 ReverseBytes(&mbr.diskSignature, 4);
422 ReverseBytes(&mbr.nulls, 2);
423 ReverseBytes(&mbr.MBRSignature, 2);
424 for (i = 0; i < 4; i++) {
425 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
426 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
427 } // for
428 }// if
429 return allOK;
430} // BasicMBRData::WriteMBRData(uint64_t sector)
431
432/********************************************
433 * *
434 * Functions that display data for the user *
435 * *
436 ********************************************/
437
438// Show the MBR data to the user, up to the specified maximum number
439// of partitions....
440void BasicMBRData::DisplayMBRData(int maxParts) {
441 int i;
442 char bootCode;
443
444 if (maxParts > MAX_MBR_PARTS)
445 maxParts = MAX_MBR_PARTS;
446 cout << "MBR disk identifier: 0x";
447 cout.width(8);
448 cout.fill('0');
449 cout.setf(ios::uppercase);
450 cout << hex << diskSignature << dec << "\n";
451 cout << "MBR partitions:\n";
452 cout << "Number\t Boot\t Start (sector)\t Length (sectors)\tType\n";
453 for (i = 0; i < maxParts; i++) {
454 if (partitions[i].lengthLBA != 0) {
455 if (partitions[i].status && 0x80) // it's bootable
456 bootCode = '*';
457 else
458 bootCode = ' ';
459 cout.fill(' ');
460 cout.width(4);
461 cout << i + 1 << "\t " << bootCode << "\t";
462 cout.width(13);
463 cout << partitions[i].firstLBA << "\t";
464 cout.width(15);
465 cout << partitions[i].lengthLBA << " \t0x";
466 cout.width(2);
467 cout.fill('0');
468 cout << hex << (int) partitions[i].partitionType << dec << "\n";
469 } // if
470 cout.fill(' ');
471 } // for
472 cout << "\nDisk size is " << diskSize << " sectors ("
473 << BytesToSI(diskSize, blockSize) << ")\n";
474} // BasicMBRData::DisplayMBRData()
475
476// Displays the state, as a word, on stdout. Used for debugging & to
477// tell the user about the MBR state when the program launches....
478void BasicMBRData::ShowState(void) {
479 switch (state) {
480 case invalid:
481 cout << " MBR: not present\n";
482 break;
483 case gpt:
484 cout << " MBR: protective\n";
485 break;
486 case hybrid:
487 cout << " MBR: hybrid\n";
488 break;
489 case mbr:
490 cout << " MBR: MBR only\n";
491 break;
492 default:
493 cout << "\a MBR: unknown -- bug!\n";
494 break;
495 } // switch
496} // BasicMBRData::ShowState()
497
498/*********************************************************************
499 * *
500 * Functions that set or get disk metadata (CHS geometry, disk size, *
501 * etc.) *
502 * *
503 *********************************************************************/
504
505// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function.
506// Note that this only sets the heads and sectors; the number of
507// cylinders is determined by these values and the disk size.
508void BasicMBRData::SetCHSGeom(uint32_t h, uint32_t s) {
509 if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
510 numHeads = h;
511 numSecspTrack = s;
512 } else {
513 cout << "Warning! Attempt to set invalid CHS geometry!\n";
514 } // if/else
515} // BasicMBRData::SetCHSGeom()
516
517// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
518// was within the range that can be expressed by CHS (including 0, for an
519// empty partition), 0 if the value is outside that range, and -1 if chs is
520// invalid.
521int BasicMBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
522 uint64_t cylinder, head, sector; // all numbered from 0
523 uint64_t remainder;
524 int retval = 1;
525 int done = 0;
526
527 if (chs != NULL) {
528 // Special case: In case of 0 LBA value, zero out CHS values....
529 if (lba == 0) {
530 chs[0] = chs[1] = chs[2] = UINT8_C(0);
531 done = 1;
532 } // if
533 // If LBA value is too large for CHS, max out CHS values....
534 if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
535 chs[0] = 254;
536 chs[1] = chs[2] = 255;
537 done = 1;
538 retval = 0;
539 } // if
540 // If neither of the above applies, compute CHS values....
541 if (!done) {
542 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
543 remainder = lba - (cylinder * numHeads * numSecspTrack);
544 head = remainder / numSecspTrack;
545 remainder -= head * numSecspTrack;
546 sector = remainder;
547 if (head < numHeads)
548 chs[0] = (uint8_t) head;
549 else
550 retval = 0;
551 if (sector < numSecspTrack) {
552 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
553 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
554 } else {
555 retval = 0;
556 } // if/else
557 } // if value is expressible and non-0
558 } else { // Invalid (NULL) chs pointer
559 retval = -1;
560 } // if CHS pointer valid
561 return (retval);
562} // BasicMBRData::LBAtoCHS()
563
564// Look for problems -- overlapping partitions, etc.
565int BasicMBRData::Verify(void) {
566 int i, j, theyOverlap, numProbs = 0, numEE = 0;
567 uint32_t firstLBA1, firstLBA2, lastLBA1, lastLBA2;
568
569 for (i = 0; i < MAX_MBR_PARTS; i++) {
570 for (j = i + 1; j < MAX_MBR_PARTS; j++) {
571 theyOverlap = 0;
572 firstLBA1 = partitions[i].firstLBA;
573 firstLBA2 = partitions[j].firstLBA;
574 if ((firstLBA1 != 0) && (firstLBA2 != 0)) {
575 lastLBA1 = partitions[i].firstLBA + partitions[i].lengthLBA - 1;
576 lastLBA2 = partitions[j].firstLBA + partitions[j].lengthLBA - 1;
577 if ((firstLBA1 < lastLBA2) && (lastLBA1 >= firstLBA2))
578 theyOverlap = 1;
579 if ((firstLBA2 < lastLBA1) && (lastLBA2 >= firstLBA1))
580 theyOverlap = 1;
581 } // if
582 if (theyOverlap) {
583 numProbs++;
584 cout << "\nProblem: MBR partitions " << i + 1 << " and " << j + 1
585 << " overlap!\n";
586 } // if
587 } // for (j...)
588 if (partitions[i].partitionType == 0xEE) {
589 numEE++;
590 if (partitions[i].firstLBA != 1)
591 cout << "\nWarning: 0xEE partition doesn't start on sector 1. This can cause problems\n"
592 << "in some OSes.\n";
593 } // if
594 } // for (i...)
595 if (numEE > 1)
596 cout << "\nCaution: More than one 0xEE MBR partition found. This can cause problems\n"
597 << "in some OSes.\n";
598
599 return numProbs;
600} // BasicMBRData::Verify()
601
602/*****************************************************
603 * *
604 * Functions to create, delete, or change partitions *
605 * *
606 *****************************************************/
607
608// Empty all data. Meant mainly for calling by constructors, but it's also
609// used by the hybrid MBR functions in the GPTData class.
610void BasicMBRData::EmptyMBR(int clearBootloader) {
611 int i;
612
613 // Zero out the boot loader section, the disk signature, and the
614 // 2-byte nulls area only if requested to do so. (This is the
615 // default.)
616 if (clearBootloader == 1) {
617 EmptyBootloader();
618 } // if
619
620 // Blank out the partitions
621 for (i = 0; i < MAX_MBR_PARTS; i++) {
622 partitions[i].status = UINT8_C(0);
623 partitions[i].firstSector[0] = UINT8_C(0);
624 partitions[i].firstSector[1] = UINT8_C(0);
625 partitions[i].firstSector[2] = UINT8_C(0);
626 partitions[i].partitionType = UINT8_C(0);
627 partitions[i].lastSector[0] = UINT8_C(0);
628 partitions[i].lastSector[1] = UINT8_C(0);
629 partitions[i].lastSector[2] = UINT8_C(0);
630 partitions[i].firstLBA = UINT32_C(0);
631 partitions[i].lengthLBA = UINT32_C(0);
632 } // for
633 MBRSignature = MBR_SIGNATURE;
634} // BasicMBRData::EmptyMBR()
635
636// Blank out the boot loader area. Done with the initial MBR-to-GPT
637// conversion, since MBR boot loaders don't understand GPT, and so
638// need to be replaced....
639void BasicMBRData::EmptyBootloader(void) {
640 int i;
641
642 for (i = 0; i < 440; i++)
643 code[i] = 0;
644 nulls = 0;
645} // BasicMBRData::EmptyBootloader
646
647// Create a partition of the specified number, starting LBA, and
648// length. This function does *NO* error checking, so it's possible
649// to seriously screw up a partition table using this function!
650// Note: This function should NOT be used to create the 0xEE partition
651// in a conventional GPT configuration, since that partition has
652// specific size requirements that this function won't handle. It may
653// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
654// since those toss the rulebook away anyhow....
655void BasicMBRData::MakePart(int num, uint32_t start, uint32_t length, int type, int bootable) {
656 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
657 partitions[num].firstSector[0] = UINT8_C(0);
658 partitions[num].firstSector[1] = UINT8_C(0);
659 partitions[num].firstSector[2] = UINT8_C(0);
660 partitions[num].partitionType = (uint8_t) type;
661 partitions[num].lastSector[0] = UINT8_C(0);
662 partitions[num].lastSector[1] = UINT8_C(0);
663 partitions[num].lastSector[2] = UINT8_C(0);
664 partitions[num].firstLBA = start;
665 partitions[num].lengthLBA = length;
666 // If this is a "real" partition, set its CHS geometry
667 if (length > 0) {
668 LBAtoCHS((uint64_t) start, partitions[num].firstSector);
669 LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
670 } // if (length > 0)
671 SetPartBootable(num, bootable);
672 } // if valid partition number
673} // BasicMBRData::MakePart()
674
675// Set the partition's type code.
676// Returns 1 if successful, 0 if not (invalid partition number)
677int BasicMBRData::SetPartType(int num, int type) {
678 int allOK = 1;
679
680 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
681 if (partitions[num].lengthLBA != UINT32_C(0)) {
682 partitions[num].partitionType = (uint8_t) type;
683 } else allOK = 0;
684 } else allOK = 0;
685 return allOK;
686} // BasicMBRData::SetPartType()
687
688// Set (or remove) the partition's bootable flag. Setting it is the
689// default; pass 0 as bootable to remove the flag.
690// Returns 1 if successful, 0 if not (invalid partition number)
691int BasicMBRData::SetPartBootable(int num, int bootable) {
692 int allOK = 1;
693
694 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
695 if (partitions[num].lengthLBA != UINT32_C(0)) {
696 if (bootable == 0)
697 partitions[num].status = UINT8_C(0);
698 else
699 partitions[num].status = UINT8_C(0x80);
700 } else allOK = 0;
701 } else allOK = 0;
702 return allOK;
703} // BasicMBRData::SetPartBootable()
704
705// Create a partition that fills the most available space. Returns
706// 1 if partition was created, 0 otherwise. Intended for use in
707// creating hybrid MBRs.
708int BasicMBRData::MakeBiggestPart(int i, int type) {
709 uint32_t start = UINT32_C(1); // starting point for each search
710 uint32_t firstBlock; // first block in a segment
711 uint32_t lastBlock; // last block in a segment
712 uint32_t segmentSize; // size of segment in blocks
713 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
714 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
715 int found = 0;
716
717 do {
718 firstBlock = FindFirstAvailable(start);
719 if (firstBlock != UINT32_C(0)) { // something's free...
720 lastBlock = FindLastInFree(firstBlock);
721 segmentSize = lastBlock - firstBlock + UINT32_C(1);
722 if (segmentSize > selectedSize) {
723 selectedSize = segmentSize;
724 selectedSegment = firstBlock;
725 } // if
726 start = lastBlock + 1;
727 } // if
728 } while (firstBlock != 0);
729 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
730 found = 1;
731 MakePart(i, selectedSegment, selectedSize, type, 0);
732 } else {
733 found = 0;
734 } // if/else
735 return found;
736} // BasicMBRData::MakeBiggestPart(int i)
737
738// Delete partition #i
739void BasicMBRData::DeletePartition(int i) {
740 int j;
741
742 partitions[i].firstLBA = UINT32_C(0);
743 partitions[i].lengthLBA = UINT32_C(0);
744 partitions[i].status = UINT8_C(0);
745 partitions[i].partitionType = UINT8_C(0);
746 for (j = 0; j < 3; j++) {
747 partitions[i].firstSector[j] = UINT8_C(0);
748 partitions[i].lastSector[j] = UINT8_C(0);
749 } // for j (CHS data blanking)
750} // BasicMBRData::DeletePartition()
751
752// Recomputes the CHS values for the specified partition and adjusts the value.
753// Note that this will create a technically incorrect CHS value for EFI GPT (0xEE)
754// protective partitions, but this is required by some buggy BIOSes, so I'm
755// providing a function to do this deliberately at the user's command.
756// This function does nothing if the partition's length is 0.
757void BasicMBRData::RecomputeCHS(int partNum) {
758 uint64_t firstLBA, lengthLBA;
759
760 firstLBA = (uint64_t) partitions[partNum].firstLBA;
761 lengthLBA = (uint64_t) partitions[partNum].lengthLBA;
762
763 if (lengthLBA > 0) {
764 LBAtoCHS(firstLBA, partitions[partNum].firstSector);
765 LBAtoCHS(firstLBA + lengthLBA - 1, partitions[partNum].lastSector);
766 } // if
767} // BasicMBRData::RecomputeCHS()
768
769// Creates an MBR extended partition holding logical partitions that
770// correspond to the list of GPT partitions in theList. The extended
771// partition is placed in position #4 (counting from 1) in the MBR.
772// The logical partition data are copied to the partitions[] array in
773// positions 4 and up (counting from 0). Neither the MBR nor the EBR
774// entries are written to disk; that is left for the WriteMBRData()
775// function.
776// Returns number of converted partitions
777int BasicMBRData::CreateLogicals(PartNotes * notes) {
778 uint64_t extEndLBA = 0, extStartLBA = UINT64_MAX;
779 int i = 4, numLogicals = 0;
780 struct PartInfo aPart;
781
782 // Find bounds of the extended partition....
783 notes->Rewind();
784 while (notes->GetNextInfo(&aPart) >= 0) {
785 if (aPart.type == LOGICAL) {
786 if (extStartLBA > aPart.firstLBA)
787 extStartLBA = aPart.firstLBA;
788 if (extEndLBA < aPart.lastLBA)
789 extEndLBA = aPart.lastLBA;
790 numLogicals++;
791 } // if
792 } // while
793 extStartLBA--;
794
795 if ((extStartLBA < UINT32_MAX) && ((extEndLBA - extStartLBA + 1) < UINT32_MAX)) {
796 notes->Rewind();
797 i = 4;
798 while ((notes->GetNextInfo(&aPart) >= 0) && (i < MAX_MBR_PARTS)) {
799 if (aPart.type == LOGICAL) {
800 partitions[i].partitionType = aPart.hexCode;
801 partitions[i].firstLBA = (uint32_t) (aPart.firstLBA - extStartLBA);
802 partitions[i].lengthLBA = (uint32_t) (aPart.lastLBA - aPart.firstLBA + 1);
803 LBAtoCHS(UINT64_C(1), (uint8_t *) &partitions[i].firstSector);
804 LBAtoCHS(partitions[i].lengthLBA, (uint8_t *) &partitions[i].lastSector);
805 partitions[i].status = aPart.active * 0x80;
806 i++;
807 } // if
808 } // while
809 MakePart(3, (uint32_t) extStartLBA, (uint32_t) (extEndLBA - extStartLBA + 1), 0x0f, 0);
810 } else {
811 if (numLogicals > 0) {
812 cerr << "Unable to create logical partitions; they exceed the 2 TiB limit!\n";
813// cout << "extStartLBA = " << extStartLBA << ", extEndLBA = " << extEndLBA << "\n";
814 }
815 } // if/else
816 return (i - 4);
817} // BasicMBRData::CreateLogicals()
818
819/****************************************
820 * *
821 * Functions to find data on free space *
822 * *
823 ****************************************/
824
825// Finds the first free space on the disk from start onward; returns 0
826// if none available....
827uint32_t BasicMBRData::FindFirstAvailable(uint32_t start) {
828 uint32_t first;
829 uint32_t i;
830 int firstMoved;
831
832 first = start;
833
834 // ...now search through all partitions; if first is within an
835 // existing partition, move it to the next sector after that
836 // partition and repeat. If first was moved, set firstMoved
837 // flag; repeat until firstMoved is not set, so as to catch
838 // cases where partitions are out of sequential order....
839 do {
840 firstMoved = 0;
841 for (i = 0; i < 4; i++) {
842 // Check if it's in the existing partition
843 if ((first >= partitions[i].firstLBA) &&
844 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
845 first = partitions[i].firstLBA + partitions[i].lengthLBA;
846 firstMoved = 1;
847 } // if
848 } // for
849 } while (firstMoved == 1);
850 if (first >= diskSize)
851 first = 0;
852 return (first);
853} // BasicMBRData::FindFirstAvailable()
854
855// Finds the last free sector on the disk from start forward.
856uint32_t BasicMBRData::FindLastInFree(uint32_t start) {
857 uint32_t nearestStart;
858 uint32_t i;
859
860 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
861 nearestStart = (uint32_t) diskSize - 1;
862 else
863 nearestStart = UINT32_MAX - 1;
864 for (i = 0; i < 4; i++) {
865 if ((nearestStart > partitions[i].firstLBA) &&
866 (partitions[i].firstLBA > start)) {
867 nearestStart = partitions[i].firstLBA - 1;
868 } // if
869 } // for
870 return (nearestStart);
871} // BasicMBRData::FindLastInFree()
872
873// Finds the first free sector on the disk from start backward.
874uint32_t BasicMBRData::FindFirstInFree(uint32_t start) {
875 uint32_t bestLastLBA, thisLastLBA;
876 int i;
877
878 bestLastLBA = 1;
879 for (i = 0; i < 4; i++) {
880 thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
881 if (thisLastLBA > 0)
882 thisLastLBA--;
883 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start))
884 bestLastLBA = thisLastLBA + 1;
885 } // for
886 return (bestLastLBA);
887} // BasicMBRData::FindFirstInFree()
888
889// Returns 1 if the specified sector is unallocated, 0 if it's
890// allocated.
891int BasicMBRData::IsFree(uint32_t sector) {
892 int i, isFree = 1;
893 uint32_t first, last;
894
895 for (i = 0; i < 4; i++) {
896 first = partitions[i].firstLBA;
897 // Note: Weird two-line thing to avoid subtracting 1 from a 0 value
898 // for an unsigned int....
899 last = first + partitions[i].lengthLBA;
900 if (last > 0)
901 last--;
902 if ((first <= sector) && (last >= sector))
903 isFree = 0;
904 } // for
905 return isFree;
906} // BasicMBRData::IsFree()
907
908/******************************************************
909 * *
910 * Functions that extract data on specific partitions *
911 * *
912 ******************************************************/
913
914uint8_t BasicMBRData::GetStatus(int i) {
915 MBRRecord* thePart;
916 uint8_t retval;
917
918 thePart = GetPartition(i);
919 if (thePart != NULL)
920 retval = thePart->status;
921 else
922 retval = UINT8_C(0);
923 return retval;
924} // BasicMBRData::GetStatus()
925
926uint8_t BasicMBRData::GetType(int i) {
927 MBRRecord* thePart;
928 uint8_t retval;
929
930 thePart = GetPartition(i);
931 if (thePart != NULL)
932 retval = thePart->partitionType;
933 else
934 retval = UINT8_C(0);
935 return retval;
936} // BasicMBRData::GetType()
937
938uint32_t BasicMBRData::GetFirstSector(int i) {
939 MBRRecord* thePart;
940 uint32_t retval;
941
942 thePart = GetPartition(i);
943 if (thePart != NULL) {
944 retval = thePart->firstLBA;
945 } else
946 retval = UINT32_C(0);
947 return retval;
948} // BasicMBRData::GetFirstSector()
949
950uint32_t BasicMBRData::GetLength(int i) {
951 MBRRecord* thePart;
952 uint32_t retval;
953
954 thePart = GetPartition(i);
955 if (thePart != NULL) {
956 retval = thePart->lengthLBA;
957 } else
958 retval = UINT32_C(0);
959 return retval;
960} // BasicMBRData::GetLength()
961
962/***********************
963 * *
964 * Protected functions *
965 * *
966 ***********************/
967
968// Return a pointer to a primary or logical partition, or NULL if
969// the partition is out of range....
970struct MBRRecord* BasicMBRData::GetPartition(int i) {
971 MBRRecord* thePart = NULL;
972
973 if ((i >= 0) && (i < MAX_MBR_PARTS))
974 thePart = &partitions[i];
975 return thePart;
976} // GetPartition()