blob: 3f9b5222d22be464afee565c01c307297cea8f05 [file] [log] [blame]
srs5694e7b4ff92009-08-18 13:16:10 -04001/* mbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition
2 data. */
3
srs5694978041c2009-09-21 20:51:47 -04004/* Initial coding by Rod Smith, January to February, 2009 */
srs5694e7b4ff92009-08-18 13:16:10 -04005
srs5694221e0872009-08-29 15:00:31 -04006/* 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
srs5694e7b4ff92009-08-18 13:16:10 -04009#define __STDC_LIMIT_MACROS
10#define __STDC_CONSTANT_MACROS
11
12#include <stdio.h>
srs569408bb0da2010-02-19 17:19:55 -050013//#include <unistd.h>
srs5694e7b4ff92009-08-18 13:16:10 -040014#include <stdlib.h>
15#include <stdint.h>
16#include <fcntl.h>
17#include <string.h>
18#include <time.h>
19#include <sys/stat.h>
20#include <errno.h>
srs5694fed16d02010-01-27 23:03:40 -050021#include <iostream>
srs5694e7b4ff92009-08-18 13:16:10 -040022#include "mbr.h"
23#include "support.h"
24
25using namespace std;
26
27/****************************************
28 * *
29 * MBRData class and related structures *
30 * *
31 ****************************************/
32
33MBRData::MBRData(void) {
34 blockSize = SECTOR_SIZE;
35 diskSize = 0;
srs5694fed16d02010-01-27 23:03:40 -050036 device = "";
srs5694e7b4ff92009-08-18 13:16:10 -040037 state = invalid;
38 srand((unsigned int) time(NULL));
srs5694978041c2009-09-21 20:51:47 -040039 numHeads = MAX_HEADS;
40 numSecspTrack = MAX_SECSPERTRACK;
srs56940a697312010-01-28 21:10:52 -050041 myDisk = NULL;
srs56946699b012010-02-04 00:55:30 -050042 canDeleteMyDisk = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040043 EmptyMBR();
44} // MBRData default constructor
45
srs5694fed16d02010-01-27 23:03:40 -050046MBRData::MBRData(string filename) {
srs5694e7b4ff92009-08-18 13:16:10 -040047 blockSize = SECTOR_SIZE;
48 diskSize = 0;
srs5694fed16d02010-01-27 23:03:40 -050049 device = filename;
srs5694e7b4ff92009-08-18 13:16:10 -040050 state = invalid;
srs5694978041c2009-09-21 20:51:47 -040051 numHeads = MAX_HEADS;
52 numSecspTrack = MAX_SECSPERTRACK;
srs56940a697312010-01-28 21:10:52 -050053 myDisk = NULL;
srs56946699b012010-02-04 00:55:30 -050054 canDeleteMyDisk = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040055
56 srand((unsigned int) time(NULL));
57 // Try to read the specified partition table, but if it fails....
58 if (!ReadMBRData(filename)) {
59 EmptyMBR();
srs5694fed16d02010-01-27 23:03:40 -050060 device = "";
srs5694e7b4ff92009-08-18 13:16:10 -040061 } // if
srs56940a697312010-01-28 21:10:52 -050062} // MBRData(string filename) constructor
srs5694e7b4ff92009-08-18 13:16:10 -040063
srs56946699b012010-02-04 00:55:30 -050064// 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!
srs5694e7b4ff92009-08-18 13:16:10 -040067MBRData::~MBRData(void) {
srs56946699b012010-02-04 00:55:30 -050068 if (canDeleteMyDisk)
69 delete myDisk;
srs5694e7b4ff92009-08-18 13:16:10 -040070} // MBRData destructor
71
srs5694978041c2009-09-21 20:51:47 -040072/**********************
73 * *
74 * Disk I/O functions *
75 * *
76 **********************/
srs5694e7b4ff92009-08-18 13:16:10 -040077
78// Read data from MBR. Returns 1 if read was successful (even if the
79// data isn't a valid MBR), 0 if the read failed.
srs56940a697312010-01-28 21:10:52 -050080int MBRData::ReadMBRData(const string & deviceFilename) {
srs5694e321d442010-01-29 17:44:04 -050081 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -040082
srs56946699b012010-02-04 00:55:30 -050083 if (myDisk == NULL) {
srs56940a697312010-01-28 21:10:52 -050084 myDisk = new DiskIO;
srs56946699b012010-02-04 00:55:30 -050085 canDeleteMyDisk = 1;
86 } // if
srs5694546a9c72010-01-26 16:00:26 -050087 if (myDisk->OpenForRead(deviceFilename)) {
88 ReadMBRData(myDisk);
srs5694e7b4ff92009-08-18 13:16:10 -040089 } else {
90 allOK = 0;
91 } // if
92
srs5694e7b4ff92009-08-18 13:16:10 -040093 if (allOK)
srs5694fed16d02010-01-27 23:03:40 -050094 device = deviceFilename;
srs5694e7b4ff92009-08-18 13:16:10 -040095
96 return allOK;
srs56940a697312010-01-28 21:10:52 -050097} // MBRData::ReadMBRData(const string & deviceFilename)
srs5694e7b4ff92009-08-18 13:16:10 -040098
srs5694978041c2009-09-21 20:51:47 -040099// Read data from MBR. If checkBlockSize == 1 (the default), the block
100// size is checked; otherwise it's set to the default (512 bytes).
101// Note that any extended partition(s) present will be explicitly stored
102// in the partitions[] array, along with their contained partitions; the
103// extended container partition(s) should be ignored by other functions.
srs5694546a9c72010-01-26 16:00:26 -0500104void MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) {
srs5694978041c2009-09-21 20:51:47 -0400105 int allOK = 1, i, j, logicalNum;
srs5694546a9c72010-01-26 16:00:26 -0500106 int err = 1;
srs5694221e0872009-08-29 15:00:31 -0400107 TempMBR tempMBR;
srs5694e7b4ff92009-08-18 13:16:10 -0400108
srs56946699b012010-02-04 00:55:30 -0500109 if ((myDisk != NULL) && (canDeleteMyDisk)) {
srs56940a697312010-01-28 21:10:52 -0500110 delete myDisk;
srs56946699b012010-02-04 00:55:30 -0500111 canDeleteMyDisk = 0;
112 } // if
srs56940a697312010-01-28 21:10:52 -0500113
srs5694546a9c72010-01-26 16:00:26 -0500114 myDisk = theDisk;
115
srs5694978041c2009-09-21 20:51:47 -0400116 // Empty existing MBR data, including the logical partitions...
117 EmptyMBR(0);
srs5694e7b4ff92009-08-18 13:16:10 -0400118
srs5694546a9c72010-01-26 16:00:26 -0500119 if (myDisk->Seek(0))
120 if (myDisk->Read(&tempMBR, 512))
121 err = 0;
122 if (err) {
srs56940a697312010-01-28 21:10:52 -0500123 cerr << "Problem reading disk in MBRData::ReadMBRData()!\n";
srs5694546a9c72010-01-26 16:00:26 -0500124 } else {
125 for (i = 0; i < 440; i++)
126 code[i] = tempMBR.code[i];
127 diskSignature = tempMBR.diskSignature;
128 nulls = tempMBR.nulls;
srs56942a9f5da2009-08-26 00:48:01 -0400129 for (i = 0; i < 4; i++) {
srs5694546a9c72010-01-26 16:00:26 -0500130 partitions[i].status = tempMBR.partitions[i].status;
131 partitions[i].partitionType = tempMBR.partitions[i].partitionType;
132 partitions[i].firstLBA = tempMBR.partitions[i].firstLBA;
133 partitions[i].lengthLBA = tempMBR.partitions[i].lengthLBA;
134 for (j = 0; j < 3; j++) {
135 partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j];
136 partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j];
137 } // for j... (reading parts of CHS geometry)
138 } // for i... (reading all four partitions)
139 MBRSignature = tempMBR.MBRSignature;
srs56942a9f5da2009-08-26 00:48:01 -0400140
srs5694546a9c72010-01-26 16:00:26 -0500141 // Reverse the byte order, if necessary
142 if (IsLittleEndian() == 0) {
143 ReverseBytes(&diskSignature, 4);
144 ReverseBytes(&nulls, 2);
145 ReverseBytes(&MBRSignature, 2);
146 for (i = 0; i < 4; i++) {
147 ReverseBytes(&partitions[i].firstLBA, 4);
148 ReverseBytes(&partitions[i].lengthLBA, 4);
149 } // for
150 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400151
srs5694546a9c72010-01-26 16:00:26 -0500152 if (MBRSignature != MBR_SIGNATURE) {
153 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400154 state = invalid;
155 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400156
srs5694546a9c72010-01-26 16:00:26 -0500157 // Find disk size
158 diskSize = myDisk->DiskSize(&err);
159
160 // Find block size
161 if (checkBlockSize) {
162 blockSize = myDisk->GetBlockSize();
163 } // if (checkBlockSize)
164
165 // Load logical partition data, if any is found....
166 if (allOK) {
167 for (i = 0; i < 4; i++) {
168 if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
169 || (partitions[i].partitionType == 0x85)) {
170 // Found it, so call a recursive algorithm to load everything from them....
171 logicalNum = ReadLogicalPart(partitions[i].firstLBA, UINT32_C(0), 4);
172 if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
173 allOK = 0;
srs5694fed16d02010-01-27 23:03:40 -0500174 cerr << "Error reading logical partitions! List may be truncated!\n";
srs5694546a9c72010-01-26 16:00:26 -0500175 } // if maxLogicals valid
176 } // if primary partition is extended
177 } // for primary partition loop
178 if (allOK) { // Loaded logicals OK
179 state = mbr;
180 } else {
181 state = invalid;
srs56942a9f5da2009-08-26 00:48:01 -0400182 } // if
srs5694546a9c72010-01-26 16:00:26 -0500183 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400184
srs5694546a9c72010-01-26 16:00:26 -0500185 /* Check to see if it's in GPT format.... */
186 if (allOK) {
187 for (i = 0; i < 4; i++) {
188 if (partitions[i].partitionType == UINT8_C(0xEE)) {
189 state = gpt;
190 } // if
191 } // for
192 } // if
193
194 // If there's an EFI GPT partition, look for other partition types,
195 // to flag as hybrid
196 if (state == gpt) {
197 for (i = 0 ; i < 4; i++) {
198 if ((partitions[i].partitionType != UINT8_C(0xEE)) &&
199 (partitions[i].partitionType != UINT8_C(0x00)))
200 state = hybrid;
201 } // for
202 } // if (hybrid detection code)
203 } // no initial error
srs56940a697312010-01-28 21:10:52 -0500204} // MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize)
srs5694e7b4ff92009-08-18 13:16:10 -0400205
srs5694978041c2009-09-21 20:51:47 -0400206// This is a recursive function to read all the logical partitions, following the
207// logical partition linked list from the disk and storing the basic data in the
208// partitions[] array. Returns last index to partitions[] used, or -1 if there was
209// a problem.
210// Parameters:
srs5694978041c2009-09-21 20:51:47 -0400211// extendedStart = LBA of the start of the extended partition
212// diskOffset = LBA offset WITHIN the extended partition of the one to be read
213// partNum = location in partitions[] array to store retrieved data
srs5694546a9c72010-01-26 16:00:26 -0500214int MBRData::ReadLogicalPart(uint32_t extendedStart,
srs5694978041c2009-09-21 20:51:47 -0400215 uint32_t diskOffset, int partNum) {
216 struct TempMBR ebr;
srs5694546a9c72010-01-26 16:00:26 -0500217 uint64_t offset;
srs5694978041c2009-09-21 20:51:47 -0400218
219 // Check for a valid partition number. Note that partitions MAY be read into
220 // the area normally used by primary partitions, although the only calling
221 // function as of GPT fdisk version 0.5.0 doesn't do so.
222 if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
srs5694546a9c72010-01-26 16:00:26 -0500223 offset = (uint64_t) (extendedStart + diskOffset);
srs5694546a9c72010-01-26 16:00:26 -0500224 if (myDisk->Seek(offset) == 0) { // seek to EBR record
srs5694fed16d02010-01-27 23:03:40 -0500225 cerr << "Unable to seek to " << offset << "! Aborting!\n";
srs5694978041c2009-09-21 20:51:47 -0400226 partNum = -1;
227 }
srs5694546a9c72010-01-26 16:00:26 -0500228 if (myDisk->Read(&ebr, 512) != 512) { // Load the data....
srs5694fed16d02010-01-27 23:03:40 -0500229 cerr << "Error seeking to or reading logical partition data from " << offset
230 << "!\nAborting!\n";
srs5694978041c2009-09-21 20:51:47 -0400231 partNum = -1;
232 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
233 ReverseBytes(&ebr.MBRSignature, 2);
234 ReverseBytes(&ebr.partitions[0].firstLBA, 4);
235 ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
236 ReverseBytes(&ebr.partitions[1].firstLBA, 4);
237 ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
238 } // if/else/if
239
240 if (ebr.MBRSignature != MBR_SIGNATURE) {
241 partNum = -1;
srs5694fed16d02010-01-27 23:03:40 -0500242 cerr << "MBR signature in logical partition invalid; read 0x";
243 cerr.fill('0');
244 cerr.width(4);
245 cerr.setf(ios::uppercase);
246 cerr << hex << ebr.MBRSignature << ", but should be 0x";
247 cerr.width(4);
248 cerr << MBR_SIGNATURE << dec << "\n";
249 cerr.fill(' ');
srs5694978041c2009-09-21 20:51:47 -0400250 } // if
251
252 // Copy over the basic data....
253 partitions[partNum].status = ebr.partitions[0].status;
254 partitions[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
255 partitions[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
256 partitions[partNum].partitionType = ebr.partitions[0].partitionType;
257
258 // Find the next partition (if there is one) and recurse....
259 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) &&
260 (partNum < (MAX_MBR_PARTS - 1))) {
srs5694546a9c72010-01-26 16:00:26 -0500261 partNum = ReadLogicalPart(extendedStart, ebr.partitions[1].firstLBA,
srs5694978041c2009-09-21 20:51:47 -0400262 partNum + 1);
263 } else {
264 partNum++;
265 } // if another partition
266 } // Not enough space for all the logicals (or previous error encountered)
267 return (partNum);
268} // MBRData::ReadLogicalPart()
269
270// Write the MBR data to the default defined device. Note that this writes
271// ONLY the MBR itself, not the logical partition data.
srs5694e7b4ff92009-08-18 13:16:10 -0400272int MBRData::WriteMBRData(void) {
srs56940a697312010-01-28 21:10:52 -0500273 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400274
srs56940a697312010-01-28 21:10:52 -0500275 if (myDisk != NULL) {
srs569408bb0da2010-02-19 17:19:55 -0500276 if (myDisk->OpenForWrite() != 0) {
srs56940a697312010-01-28 21:10:52 -0500277 allOK = WriteMBRData(myDisk);
278 } else {
279 allOK = 0;
280 } // if/else
281 myDisk->Close();
282 } else allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400283 return allOK;
284} // MBRData::WriteMBRData(void)
285
286// Save the MBR data to a file. Note that this function writes ONLY the
287// MBR data, not the logical partitions (if any are defined).
srs5694546a9c72010-01-26 16:00:26 -0500288int MBRData::WriteMBRData(DiskIO *theDisk) {
289 int i, j, allOK;
srs5694221e0872009-08-29 15:00:31 -0400290 TempMBR tempMBR;
srs56942a9f5da2009-08-26 00:48:01 -0400291
292 // Reverse the byte order, if necessary
293 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400294 ReverseBytes(&diskSignature, 4);
295 ReverseBytes(&nulls, 2);
296 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400297 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400298 ReverseBytes(&partitions[i].firstLBA, 4);
299 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400300 } // for
301 } // if
302
srs5694221e0872009-08-29 15:00:31 -0400303 // Copy MBR data to a 512-byte data structure for writing, to
304 // work around a FreeBSD limitation....
305 for (i = 0; i < 440; i++)
306 tempMBR.code[i] = code[i];
307 tempMBR.diskSignature = diskSignature;
308 tempMBR.nulls = nulls;
309 tempMBR.MBRSignature = MBRSignature;
310 for (i = 0; i < 4; i++) {
311 tempMBR.partitions[i].status = partitions[i].status;
312 tempMBR.partitions[i].partitionType = partitions[i].partitionType;
313 tempMBR.partitions[i].firstLBA = partitions[i].firstLBA;
314 tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA;
315 for (j = 0; j < 3; j++) {
316 tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j];
317 tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j];
318 } // for j...
319 } // for i...
320
321 // Now write that data structure...
srs5694546a9c72010-01-26 16:00:26 -0500322 allOK = theDisk->OpenForWrite();
323 if (allOK && theDisk->Seek(0)) {
324 if (theDisk->Write(&tempMBR, 512) != 512) {
325 allOK = 0;
srs5694fed16d02010-01-27 23:03:40 -0500326 cerr << "Warning! Error " << errno << " when saving MBR!\n";
srs5694546a9c72010-01-26 16:00:26 -0500327 } // if
328 } else {
329 allOK = 0;
srs5694fed16d02010-01-27 23:03:40 -0500330 cerr << "Warning! Error " << errno << " when seeking to MBR to write it!\n";
srs5694546a9c72010-01-26 16:00:26 -0500331 } // if/else
332 theDisk->Close();
srs5694221e0872009-08-29 15:00:31 -0400333
srs5694221e0872009-08-29 15:00:31 -0400334 // Reverse the byte order back, if necessary
srs56942a9f5da2009-08-26 00:48:01 -0400335 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400336 ReverseBytes(&diskSignature, 4);
337 ReverseBytes(&nulls, 2);
338 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400339 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400340 ReverseBytes(&partitions[i].firstLBA, 4);
341 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400342 } // for
343 }// if
srs5694546a9c72010-01-26 16:00:26 -0500344 return allOK;
srs56940a697312010-01-28 21:10:52 -0500345} // MBRData::WriteMBRData(DiskIO *theDisk)
srs5694e7b4ff92009-08-18 13:16:10 -0400346
srs56940a697312010-01-28 21:10:52 -0500347int MBRData::WriteMBRData(const string & deviceFilename) {
srs5694fed16d02010-01-27 23:03:40 -0500348 device = deviceFilename;
srs5694ba00fed2010-01-12 18:18:36 -0500349 return WriteMBRData();
srs56940a697312010-01-28 21:10:52 -0500350} // MBRData::WriteMBRData(const string & deviceFilename)
srs5694ba00fed2010-01-12 18:18:36 -0500351
srs5694978041c2009-09-21 20:51:47 -0400352/********************************************
353 * *
354 * Functions that display data for the user *
355 * *
356 ********************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400357
358// Show the MBR data to the user....
359void MBRData::DisplayMBRData(void) {
360 int i;
srs5694e19ba092009-08-24 14:10:35 -0400361 char bootCode;
srs5694e7b4ff92009-08-18 13:16:10 -0400362
srs5694fed16d02010-01-27 23:03:40 -0500363 cout << "MBR disk identifier: 0x";
364 cout.width(8);
365 cout.fill('0');
366 cout.setf(ios::uppercase);
367 cout << hex << diskSignature << dec << "\n";
368 cout << "MBR partitions:\n";
369 cout << "Number\t Boot\t Start (sector)\t Length (sectors)\tType\n";
srs5694978041c2009-09-21 20:51:47 -0400370 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e7b4ff92009-08-18 13:16:10 -0400371 if (partitions[i].lengthLBA != 0) {
srs5694e19ba092009-08-24 14:10:35 -0400372 if (partitions[i].status && 0x80) // it's bootable
srs5694978041c2009-09-21 20:51:47 -0400373 bootCode = '*';
srs5694e19ba092009-08-24 14:10:35 -0400374 else
375 bootCode = ' ';
srs5694fed16d02010-01-27 23:03:40 -0500376 cout.fill(' ');
377 cout.width(4);
378 cout << i + 1 << "\t " << bootCode << "\t";
379 cout.width(13);
380 cout << partitions[i].firstLBA << "\t";
381 cout.width(15);
382 cout << partitions[i].lengthLBA << " \t0x";
383 cout.width(2);
384 cout.fill('0');
385 cout << hex << (int) partitions[i].partitionType << dec << "\n";
srs5694e7b4ff92009-08-18 13:16:10 -0400386 } // if
srs5694fed16d02010-01-27 23:03:40 -0500387 cout.fill(' ');
srs5694e7b4ff92009-08-18 13:16:10 -0400388 } // for
srs5694fed16d02010-01-27 23:03:40 -0500389 cout << "\nDisk size is " << diskSize << " sectors ("
srs5694e321d442010-01-29 17:44:04 -0500390 << BytesToSI(diskSize * (uint64_t) blockSize) << ")\n";
srs5694e7b4ff92009-08-18 13:16:10 -0400391} // MBRData::DisplayMBRData()
392
srs5694978041c2009-09-21 20:51:47 -0400393// Displays the state, as a word, on stdout. Used for debugging & to
394// tell the user about the MBR state when the program launches....
395void MBRData::ShowState(void) {
396 switch (state) {
397 case invalid:
srs5694fed16d02010-01-27 23:03:40 -0500398 cout << " MBR: not present\n";
srs5694978041c2009-09-21 20:51:47 -0400399 break;
400 case gpt:
srs5694fed16d02010-01-27 23:03:40 -0500401 cout << " MBR: protective\n";
srs5694978041c2009-09-21 20:51:47 -0400402 break;
403 case hybrid:
srs5694fed16d02010-01-27 23:03:40 -0500404 cout << " MBR: hybrid\n";
srs5694978041c2009-09-21 20:51:47 -0400405 break;
406 case mbr:
srs5694fed16d02010-01-27 23:03:40 -0500407 cout << " MBR: MBR only\n";
srs5694978041c2009-09-21 20:51:47 -0400408 break;
409 default:
srs5694fed16d02010-01-27 23:03:40 -0500410 cout << "\a MBR: unknown -- bug!\n";
srs5694978041c2009-09-21 20:51:47 -0400411 break;
412 } // switch
413} // MBRData::ShowState()
414
415/*********************************************************************
416 * *
417 * Functions that set or get disk metadata (CHS geometry, disk size, *
418 * etc.) *
419 * *
420 *********************************************************************/
421
422// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function.
423// Note that this only sets the heads and sectors; the number of
424// cylinders is determined by these values and the disk size.
425void MBRData::SetCHSGeom(uint32_t h, uint32_t s) {
426 if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
427 numHeads = h;
428 numSecspTrack = s;
429 } else {
srs5694fed16d02010-01-27 23:03:40 -0500430 cout << "Warning! Attempt to set invalid CHS geometry!\n";
srs5694978041c2009-09-21 20:51:47 -0400431 } // if/else
432} // MBRData::SetCHSGeom()
433
434// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
435// was within the range that can be expressed by CHS (including 0, for an
436// empty partition), 0 if the value is outside that range, and -1 if chs is
437// invalid.
438int MBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
439 uint64_t cylinder, head, sector; // all numbered from 0
440 uint64_t remainder;
441 int retval = 1;
442 int done = 0;
443
444 if (chs != NULL) {
445 // Special case: In case of 0 LBA value, zero out CHS values....
446 if (lba == 0) {
447 chs[0] = chs[1] = chs[2] = UINT8_C(0);
448 done = 1;
449 } // if
450 // If LBA value is too large for CHS, max out CHS values....
451 if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
452 chs[0] = 254;
453 chs[1] = chs[2] = 255;
454 done = 1;
455 retval = 0;
456 } // if
457 // If neither of the above applies, compute CHS values....
458 if (!done) {
459 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
460 remainder = lba - (cylinder * numHeads * numSecspTrack);
461 head = remainder / numSecspTrack;
462 remainder -= head * numSecspTrack;
463 sector = remainder;
464 if (head < numHeads)
465 chs[0] = head;
466 else
467 retval = 0;
468 if (sector < numSecspTrack) {
469 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
470 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
471 } else {
472 retval = 0;
473 } // if/else
474 } // if value is expressible and non-0
475 } else { // Invalid (NULL) chs pointer
476 retval = -1;
477 } // if CHS pointer valid
478 return (retval);
479} // MBRData::LBAtoCHS()
480
481/*****************************************************
482 * *
483 * Functions to create, delete, or change partitions *
484 * *
485 *****************************************************/
486
487// Empty all data. Meant mainly for calling by constructors, but it's also
488// used by the hybrid MBR functions in the GPTData class.
489void MBRData::EmptyMBR(int clearBootloader) {
490 int i;
491
492 // Zero out the boot loader section, the disk signature, and the
493 // 2-byte nulls area only if requested to do so. (This is the
494 // default.)
495 if (clearBootloader == 1) {
srs569408bb0da2010-02-19 17:19:55 -0500496 EmptyBootloader();
srs5694978041c2009-09-21 20:51:47 -0400497 } // if
498
499 // Blank out the partitions
500 for (i = 0; i < MAX_MBR_PARTS; i++) {
501 partitions[i].status = UINT8_C(0);
502 partitions[i].firstSector[0] = UINT8_C(0);
503 partitions[i].firstSector[1] = UINT8_C(0);
504 partitions[i].firstSector[2] = UINT8_C(0);
505 partitions[i].partitionType = UINT8_C(0);
506 partitions[i].lastSector[0] = UINT8_C(0);
507 partitions[i].lastSector[1] = UINT8_C(0);
508 partitions[i].lastSector[2] = UINT8_C(0);
509 partitions[i].firstLBA = UINT32_C(0);
510 partitions[i].lengthLBA = UINT32_C(0);
511 } // for
512 MBRSignature = MBR_SIGNATURE;
513} // MBRData::EmptyMBR()
514
srs569408bb0da2010-02-19 17:19:55 -0500515// Blank out the boot loader area. Done with the initial MBR-to-GPT
516// conversion, since MBR boot loaders don't understand GPT, and so
517// need to be replaced....
518void MBRData::EmptyBootloader(void) {
519 int i;
520
521 for (i = 0; i < 440; i++)
522 code[i] = 0;
523 nulls = 0;
524} // MBRData::EmptyBootloader
525
srs5694221e0872009-08-29 15:00:31 -0400526// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
527void MBRData::MakeProtectiveMBR(int clearBoot) {
srs5694978041c2009-09-21 20:51:47 -0400528
529 EmptyMBR(clearBoot);
srs5694e7b4ff92009-08-18 13:16:10 -0400530
531 // Initialize variables
532 nulls = 0;
533 MBRSignature = MBR_SIGNATURE;
srs569408bb0da2010-02-19 17:19:55 -0500534 diskSignature = (uint32_t) rand();
srs5694e7b4ff92009-08-18 13:16:10 -0400535
536 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
537
538 // Write CHS data. This maxes out the use of the disk, as much as
539 // possible -- even to the point of exceeding the capacity of sub-8GB
540 // disks. The EFI spec says to use 0xffffff as the ending value,
541 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
542 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
543 // value in for the FIRST sector, too!
544 partitions[0].firstSector[0] = UINT8_C(0);
545 partitions[0].firstSector[1] = UINT8_C(1);
546 partitions[0].firstSector[2] = UINT8_C(0);
547 partitions[0].lastSector[0] = UINT8_C(255);
548 partitions[0].lastSector[1] = UINT8_C(255);
549 partitions[0].lastSector[2] = UINT8_C(255);
550
551 partitions[0].partitionType = UINT8_C(0xEE);
552 partitions[0].firstLBA = UINT32_C(1);
553 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
srs5694978041c2009-09-21 20:51:47 -0400554 partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
srs5694e7b4ff92009-08-18 13:16:10 -0400555 } else { // disk is too big to represent, so fake it...
556 partitions[0].lengthLBA = UINT32_MAX;
557 } // if/else
558
srs5694e7b4ff92009-08-18 13:16:10 -0400559 state = gpt;
560} // MBRData::MakeProtectiveMBR()
561
srs5694978041c2009-09-21 20:51:47 -0400562// Create a partition of the specified number, starting LBA, and
563// length. This function does *NO* error checking, so it's possible
564// to seriously screw up a partition table using this function!
565// Note: This function should NOT be used to create the 0xEE partition
566// in a conventional GPT configuration, since that partition has
567// specific size requirements that this function won't handle. It may
568// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
569// since those toss the rulebook away anyhow....
570void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
571 int bootable) {
572 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs569408bb0da2010-02-19 17:19:55 -0500573// partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
srs5694978041c2009-09-21 20:51:47 -0400574 partitions[num].firstSector[0] = UINT8_C(0);
575 partitions[num].firstSector[1] = UINT8_C(0);
576 partitions[num].firstSector[2] = UINT8_C(0);
577 partitions[num].partitionType = (uint8_t) type;
578 partitions[num].lastSector[0] = UINT8_C(0);
579 partitions[num].lastSector[1] = UINT8_C(0);
580 partitions[num].lastSector[2] = UINT8_C(0);
581 partitions[num].firstLBA = start;
582 partitions[num].lengthLBA = length;
583 // If this is a "real" partition, set its CHS geometry
584 if (length > 0) {
585 LBAtoCHS((uint64_t) start, partitions[num].firstSector);
586 LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
587 } // if (length > 0)
srs569408bb0da2010-02-19 17:19:55 -0500588 SetPartBootable(num, bootable);
srs5694978041c2009-09-21 20:51:47 -0400589 } // if valid partition number
590} // MBRData::MakePart()
591
srs569408bb0da2010-02-19 17:19:55 -0500592// Set the partition's type code.
593// Returns 1 if successful, 0 if not (invalid partition number)
594int MBRData::SetPartType(int num, int type) {
595 int allOK = 1;
596
597 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
598 if (partitions[num].lengthLBA != UINT32_C(0)) {
599 partitions[num].partitionType = (uint8_t) type;
600 } else allOK = 0;
601 } else allOK = 0;
602 return allOK;
603} // MBRData::SetPartType()
604
605// Set (or remove) the partition's bootable flag. Setting it is the
606// default; pass 0 as bootable to remove the flag.
607// Returns 1 if successful, 0 if not (invalid partition number)
608int MBRData::SetPartBootable(int num, int bootable) {
609 int allOK = 1;
610
611 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
612 if (partitions[num].lengthLBA != UINT32_C(0)) {
613 if (bootable == 0)
614 partitions[num].status = UINT8_C(0);
615 else
616 partitions[num].status = UINT8_C(0x80);
617 } else allOK = 0;
618 } else allOK = 0;
619 return allOK;
620} // MBRData::SetPartBootable()
621
srs5694c0ca8f82009-08-20 21:35:25 -0400622// Create a partition that fills the most available space. Returns
623// 1 if partition was created, 0 otherwise. Intended for use in
624// creating hybrid MBRs.
625int MBRData::MakeBiggestPart(int i, int type) {
626 uint32_t start = UINT32_C(1); // starting point for each search
627 uint32_t firstBlock; // first block in a segment
628 uint32_t lastBlock; // last block in a segment
629 uint32_t segmentSize; // size of segment in blocks
630 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
631 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
632 int found = 0;
633
634 do {
635 firstBlock = FindFirstAvailable(start);
636 if (firstBlock != UINT32_C(0)) { // something's free...
637 lastBlock = FindLastInFree(firstBlock);
638 segmentSize = lastBlock - firstBlock + UINT32_C(1);
srs5694978041c2009-09-21 20:51:47 -0400639 if (segmentSize > selectedSize) {
srs5694c0ca8f82009-08-20 21:35:25 -0400640 selectedSize = segmentSize;
srs5694978041c2009-09-21 20:51:47 -0400641 selectedSegment = firstBlock;
642 } // if
643 start = lastBlock + 1;
srs5694c0ca8f82009-08-20 21:35:25 -0400644 } // if
645 } while (firstBlock != 0);
646 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
647 found = 1;
648 MakePart(i, selectedSegment, selectedSize, type, 0);
649 } else {
650 found = 0;
651 } // if/else
652 return found;
653} // MBRData::MakeBiggestPart(int i)
654
srs5694e35eb1b2009-09-14 00:29:34 -0400655// Delete partition #i
656void MBRData::DeletePartition(int i) {
657 int j;
658
659 partitions[i].firstLBA = UINT32_C(0);
660 partitions[i].lengthLBA = UINT32_C(0);
661 partitions[i].status = UINT8_C(0);
662 partitions[i].partitionType = UINT8_C(0);
663 for (j = 0; j < 3; j++) {
664 partitions[i].firstSector[j] = UINT8_C(0);
665 partitions[i].lastSector[j] = UINT8_C(0);
666 } // for j (CHS data blanking)
667} // MBRData::DeletePartition()
668
srs5694e4ac11e2009-08-31 10:13:04 -0400669// Delete a partition if one exists at the specified location.
670// Returns 1 if a partition was deleted, 0 otherwise....
671// Used to help keep GPT & hybrid MBR partitions in sync....
672int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
673 uint32_t start32, length32;
srs5694978041c2009-09-21 20:51:47 -0400674 int i, deleted = 0;
srs5694e4ac11e2009-08-31 10:13:04 -0400675
srs5694978041c2009-09-21 20:51:47 -0400676 if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400677 start32 = (uint32_t) start64;
678 length32 = (uint32_t) length64;
srs5694978041c2009-09-21 20:51:47 -0400679 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs569408bb0da2010-02-19 17:19:55 -0500680 if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA == length32) &&
srs5694546a9c72010-01-26 16:00:26 -0500681 (partitions[i].partitionType != 0xEE)) {
srs5694e35eb1b2009-09-14 00:29:34 -0400682 DeletePartition(i);
srs5694978041c2009-09-21 20:51:47 -0400683 if (state == hybrid)
684 OptimizeEESize();
srs5694e4ac11e2009-08-31 10:13:04 -0400685 deleted = 1;
srs5694546a9c72010-01-26 16:00:26 -0500686 } // if (match found)
srs5694e4ac11e2009-08-31 10:13:04 -0400687 } // for i (partition scan)
688 } // if (hybrid & GPT partition < 2TiB)
689 return deleted;
690} // MBRData::DeleteByLocation()
691
692// Optimizes the size of the 0xEE (EFI GPT) partition
693void MBRData::OptimizeEESize(void) {
694 int i, typeFlag = 0;
695 uint32_t after;
696
697 for (i = 0; i < 4; i++) {
698 // Check for non-empty and non-0xEE partitions
699 if ((partitions[i].partitionType != 0xEE) && (partitions[i].partitionType != 0x00))
700 typeFlag++;
701 if (partitions[i].partitionType == 0xEE) {
702 // Blank space before this partition; fill it....
703 if (IsFree(partitions[i].firstLBA - 1)) {
704 partitions[i].firstLBA = FindFirstInFree(partitions[i].firstLBA - 1);
705 } // if
706 // Blank space after this partition; fill it....
707 after = partitions[i].firstLBA + partitions[i].lengthLBA;
708 if (IsFree(after)) {
709 partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
710 } // if free space after
711 } // if partition is 0xEE
srs5694e4ac11e2009-08-31 10:13:04 -0400712 } // for partition loop
srs5694978041c2009-09-21 20:51:47 -0400713 if (typeFlag == 0) { // No non-hybrid partitions found
714 MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
715 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400716} // MBRData::OptimizeEESize()
717
srs5694978041c2009-09-21 20:51:47 -0400718/****************************************
719 * *
720 * Functions to find data on free space *
721 * *
722 ****************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400723
srs5694c0ca8f82009-08-20 21:35:25 -0400724// Finds the first free space on the disk from start onward; returns 0
725// if none available....
726uint32_t MBRData::FindFirstAvailable(uint32_t start) {
727 uint32_t first;
728 uint32_t i;
srs5694978041c2009-09-21 20:51:47 -0400729 int firstMoved;
srs5694c0ca8f82009-08-20 21:35:25 -0400730
731 first = start;
732
733 // ...now search through all partitions; if first is within an
734 // existing partition, move it to the next sector after that
735 // partition and repeat. If first was moved, set firstMoved
736 // flag; repeat until firstMoved is not set, so as to catch
737 // cases where partitions are out of sequential order....
738 do {
739 firstMoved = 0;
740 for (i = 0; i < 4; i++) {
741 // Check if it's in the existing partition
742 if ((first >= partitions[i].firstLBA) &&
743 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
744 first = partitions[i].firstLBA + partitions[i].lengthLBA;
745 firstMoved = 1;
srs5694978041c2009-09-21 20:51:47 -0400746 } // if
srs5694c0ca8f82009-08-20 21:35:25 -0400747 } // for
748 } while (firstMoved == 1);
749 if (first >= diskSize)
750 first = 0;
751 return (first);
752} // MBRData::FindFirstAvailable()
753
srs5694e4ac11e2009-08-31 10:13:04 -0400754// Finds the last free sector on the disk from start forward.
srs5694c0ca8f82009-08-20 21:35:25 -0400755uint32_t MBRData::FindLastInFree(uint32_t start) {
756 uint32_t nearestStart;
757 uint32_t i;
758
srs5694978041c2009-09-21 20:51:47 -0400759 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs569408bb0da2010-02-19 17:19:55 -0500760 nearestStart = (uint32_t) diskSize - 1;
srs5694c0ca8f82009-08-20 21:35:25 -0400761 else
762 nearestStart = UINT32_MAX - 1;
763 for (i = 0; i < 4; i++) {
764 if ((nearestStart > partitions[i].firstLBA) &&
765 (partitions[i].firstLBA > start)) {
766 nearestStart = partitions[i].firstLBA - 1;
767 } // if
768 } // for
769 return (nearestStart);
srs5694e4ac11e2009-08-31 10:13:04 -0400770} // MBRData::FindLastInFree()
771
772// Finds the first free sector on the disk from start backward.
773uint32_t MBRData::FindFirstInFree(uint32_t start) {
srs5694e35eb1b2009-09-14 00:29:34 -0400774 uint32_t bestLastLBA, thisLastLBA;
srs5694e4ac11e2009-08-31 10:13:04 -0400775 int i;
776
777 bestLastLBA = 1;
778 for (i = 0; i < 4; i++) {
779 thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
780 if (thisLastLBA > 0) thisLastLBA--;
781 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) {
782 bestLastLBA = thisLastLBA + 1;
783 } // if
784 } // for
785 return (bestLastLBA);
786} // MBRData::FindFirstInFree()
787
788// Returns 1 if the specified sector is unallocated, 0 if it's
789// allocated.
790int MBRData::IsFree(uint32_t sector) {
791 int i, isFree = 1;
792 uint32_t first, last;
793
794 for (i = 0; i < 4; i++) {
795 first = partitions[i].firstLBA;
srs5694978041c2009-09-21 20:51:47 -0400796 // Note: Weird two-line thing to avoid subtracting 1 from a 0 value
797 // for an unsigned int....
srs5694e4ac11e2009-08-31 10:13:04 -0400798 last = first + partitions[i].lengthLBA;
799 if (last > 0) last--;
800 if ((first <= sector) && (last >= sector))
801 isFree = 0;
802 } // for
803 return isFree;
804} // MBRData::IsFree()
srs5694c0ca8f82009-08-20 21:35:25 -0400805
srs5694978041c2009-09-21 20:51:47 -0400806/******************************************************
807 * *
808 * Functions that extract data on specific partitions *
809 * *
810 ******************************************************/
811
srs5694e7b4ff92009-08-18 13:16:10 -0400812uint8_t MBRData::GetStatus(int i) {
813 MBRRecord* thePart;
814 uint8_t retval;
815
816 thePart = GetPartition(i);
817 if (thePart != NULL)
818 retval = thePart->status;
819 else
820 retval = UINT8_C(0);
821 return retval;
822} // MBRData::GetStatus()
823
824uint8_t MBRData::GetType(int i) {
825 MBRRecord* thePart;
826 uint8_t retval;
827
828 thePart = GetPartition(i);
829 if (thePart != NULL)
830 retval = thePart->partitionType;
831 else
832 retval = UINT8_C(0);
833 return retval;
834} // MBRData::GetType()
835
836uint32_t MBRData::GetFirstSector(int i) {
837 MBRRecord* thePart;
838 uint32_t retval;
839
840 thePart = GetPartition(i);
841 if (thePart != NULL) {
842 retval = thePart->firstLBA;
843 } else
844 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400845 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400846} // MBRData::GetFirstSector()
847
848uint32_t MBRData::GetLength(int i) {
849 MBRRecord* thePart;
850 uint32_t retval;
851
852 thePart = GetPartition(i);
853 if (thePart != NULL) {
854 retval = thePart->lengthLBA;
855 } else
856 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400857 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400858} // MBRData::GetLength()
srs5694221e0872009-08-29 15:00:31 -0400859
860// Return the MBR data as a GPT partition....
861GPTPart MBRData::AsGPT(int i) {
862 MBRRecord* origPart;
863 GPTPart newPart;
864 uint8_t origType;
865 uint64_t firstSector, lastSector;
srs5694221e0872009-08-29 15:00:31 -0400866
867 newPart.BlankPartition();
868 origPart = GetPartition(i);
869 if (origPart != NULL) {
870 origType = origPart->partitionType;
871
872 // don't convert extended, hybrid protective, or null (non-existent)
873 // partitions (Note similar protection is in GPTData::XFormPartitions(),
874 // but I want it here too in case I call this function in another
875 // context in the future....)
srs5694e35eb1b2009-09-14 00:29:34 -0400876 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694221e0872009-08-29 15:00:31 -0400877 (origType != 0x00) && (origType != 0xEE)) {
878 firstSector = (uint64_t) origPart->firstLBA;
879 newPart.SetFirstLBA(firstSector);
880 lastSector = firstSector + (uint64_t) origPart->lengthLBA;
881 if (lastSector > 0) lastSector--;
882 newPart.SetLastLBA(lastSector);
883 newPart.SetType(((uint16_t) origType) * 0x0100);
srs56946699b012010-02-04 00:55:30 -0500884 newPart.RandomizeUniqueGUID();
srs5694221e0872009-08-29 15:00:31 -0400885 newPart.SetAttributes(0);
srs56946699b012010-02-04 00:55:30 -0500886 newPart.SetName(newPart.GetTypeName());
srs5694978041c2009-09-21 20:51:47 -0400887 } // if not extended, protective, or non-existent
888 } // if (origPart != NULL)
srs5694221e0872009-08-29 15:00:31 -0400889 return newPart;
890} // MBRData::AsGPT()
srs5694978041c2009-09-21 20:51:47 -0400891
892/***********************
893 * *
894 * Protected functions *
895 * *
896 ***********************/
897
898// Return a pointer to a primary or logical partition, or NULL if
899// the partition is out of range....
900struct MBRRecord* MBRData::GetPartition(int i) {
901 MBRRecord* thePart = NULL;
902
903 if ((i >= 0) && (i < MAX_MBR_PARTS))
904 thePart = &partitions[i];
905 return thePart;
906} // GetPartition()