blob: 214fcb0e1f8a5a4dad8dcc07dd61b7e3aed8851d [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>
13#include <unistd.h>
14#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;
srs5694e7b4ff92009-08-18 13:16:10 -040042 EmptyMBR();
43} // MBRData default constructor
44
srs5694fed16d02010-01-27 23:03:40 -050045MBRData::MBRData(string filename) {
srs5694e7b4ff92009-08-18 13:16:10 -040046 blockSize = SECTOR_SIZE;
47 diskSize = 0;
srs5694fed16d02010-01-27 23:03:40 -050048 device = filename;
srs5694e7b4ff92009-08-18 13:16:10 -040049 state = invalid;
srs5694978041c2009-09-21 20:51:47 -040050 numHeads = MAX_HEADS;
51 numSecspTrack = MAX_SECSPERTRACK;
srs56940a697312010-01-28 21:10:52 -050052 myDisk = NULL;
srs5694e7b4ff92009-08-18 13:16:10 -040053
54 srand((unsigned int) time(NULL));
55 // Try to read the specified partition table, but if it fails....
56 if (!ReadMBRData(filename)) {
57 EmptyMBR();
srs5694fed16d02010-01-27 23:03:40 -050058 device = "";
srs5694e7b4ff92009-08-18 13:16:10 -040059 } // if
srs56940a697312010-01-28 21:10:52 -050060} // MBRData(string filename) constructor
srs5694e7b4ff92009-08-18 13:16:10 -040061
62MBRData::~MBRData(void) {
srs56940a697312010-01-28 21:10:52 -050063// delete myDisk;
srs5694e7b4ff92009-08-18 13:16:10 -040064} // MBRData destructor
65
srs5694978041c2009-09-21 20:51:47 -040066/**********************
67 * *
68 * Disk I/O functions *
69 * *
70 **********************/
srs5694e7b4ff92009-08-18 13:16:10 -040071
72// Read data from MBR. Returns 1 if read was successful (even if the
73// data isn't a valid MBR), 0 if the read failed.
srs56940a697312010-01-28 21:10:52 -050074int MBRData::ReadMBRData(const string & deviceFilename) {
srs5694e321d442010-01-29 17:44:04 -050075 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -040076
srs56940a697312010-01-28 21:10:52 -050077 if (myDisk == NULL)
78 myDisk = new DiskIO;
srs5694546a9c72010-01-26 16:00:26 -050079 if (myDisk->OpenForRead(deviceFilename)) {
80 ReadMBRData(myDisk);
srs5694e7b4ff92009-08-18 13:16:10 -040081 } else {
82 allOK = 0;
83 } // if
84
srs5694e7b4ff92009-08-18 13:16:10 -040085 if (allOK)
srs5694fed16d02010-01-27 23:03:40 -050086 device = deviceFilename;
srs5694e7b4ff92009-08-18 13:16:10 -040087
88 return allOK;
srs56940a697312010-01-28 21:10:52 -050089} // MBRData::ReadMBRData(const string & deviceFilename)
srs5694e7b4ff92009-08-18 13:16:10 -040090
srs5694978041c2009-09-21 20:51:47 -040091// Read data from MBR. If checkBlockSize == 1 (the default), the block
92// size is checked; otherwise it's set to the default (512 bytes).
93// Note that any extended partition(s) present will be explicitly stored
94// in the partitions[] array, along with their contained partitions; the
95// extended container partition(s) should be ignored by other functions.
srs5694546a9c72010-01-26 16:00:26 -050096void MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) {
srs5694978041c2009-09-21 20:51:47 -040097 int allOK = 1, i, j, logicalNum;
srs5694546a9c72010-01-26 16:00:26 -050098 int err = 1;
srs5694221e0872009-08-29 15:00:31 -040099 TempMBR tempMBR;
srs5694e7b4ff92009-08-18 13:16:10 -0400100
srs56940a697312010-01-28 21:10:52 -0500101 if (myDisk != NULL)
102 delete myDisk;
103
srs5694546a9c72010-01-26 16:00:26 -0500104 myDisk = theDisk;
105
srs5694978041c2009-09-21 20:51:47 -0400106 // Empty existing MBR data, including the logical partitions...
107 EmptyMBR(0);
srs5694e7b4ff92009-08-18 13:16:10 -0400108
srs5694546a9c72010-01-26 16:00:26 -0500109 if (myDisk->Seek(0))
110 if (myDisk->Read(&tempMBR, 512))
111 err = 0;
112 if (err) {
srs56940a697312010-01-28 21:10:52 -0500113 cerr << "Problem reading disk in MBRData::ReadMBRData()!\n";
srs5694546a9c72010-01-26 16:00:26 -0500114 } else {
115 for (i = 0; i < 440; i++)
116 code[i] = tempMBR.code[i];
117 diskSignature = tempMBR.diskSignature;
118 nulls = tempMBR.nulls;
srs56942a9f5da2009-08-26 00:48:01 -0400119 for (i = 0; i < 4; i++) {
srs5694546a9c72010-01-26 16:00:26 -0500120 partitions[i].status = tempMBR.partitions[i].status;
121 partitions[i].partitionType = tempMBR.partitions[i].partitionType;
122 partitions[i].firstLBA = tempMBR.partitions[i].firstLBA;
123 partitions[i].lengthLBA = tempMBR.partitions[i].lengthLBA;
124 for (j = 0; j < 3; j++) {
125 partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j];
126 partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j];
127 } // for j... (reading parts of CHS geometry)
128 } // for i... (reading all four partitions)
129 MBRSignature = tempMBR.MBRSignature;
srs56942a9f5da2009-08-26 00:48:01 -0400130
srs5694546a9c72010-01-26 16:00:26 -0500131 // Reverse the byte order, if necessary
132 if (IsLittleEndian() == 0) {
133 ReverseBytes(&diskSignature, 4);
134 ReverseBytes(&nulls, 2);
135 ReverseBytes(&MBRSignature, 2);
136 for (i = 0; i < 4; i++) {
137 ReverseBytes(&partitions[i].firstLBA, 4);
138 ReverseBytes(&partitions[i].lengthLBA, 4);
139 } // for
140 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400141
srs5694546a9c72010-01-26 16:00:26 -0500142 if (MBRSignature != MBR_SIGNATURE) {
143 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400144 state = invalid;
145 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400146
srs5694546a9c72010-01-26 16:00:26 -0500147 // Find disk size
148 diskSize = myDisk->DiskSize(&err);
149
150 // Find block size
151 if (checkBlockSize) {
152 blockSize = myDisk->GetBlockSize();
153 } // if (checkBlockSize)
154
155 // Load logical partition data, if any is found....
156 if (allOK) {
157 for (i = 0; i < 4; i++) {
158 if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
159 || (partitions[i].partitionType == 0x85)) {
160 // Found it, so call a recursive algorithm to load everything from them....
161 logicalNum = ReadLogicalPart(partitions[i].firstLBA, UINT32_C(0), 4);
162 if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
163 allOK = 0;
srs5694fed16d02010-01-27 23:03:40 -0500164 cerr << "Error reading logical partitions! List may be truncated!\n";
srs5694546a9c72010-01-26 16:00:26 -0500165 } // if maxLogicals valid
166 } // if primary partition is extended
167 } // for primary partition loop
168 if (allOK) { // Loaded logicals OK
169 state = mbr;
170 } else {
171 state = invalid;
srs56942a9f5da2009-08-26 00:48:01 -0400172 } // if
srs5694546a9c72010-01-26 16:00:26 -0500173 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400174
srs5694546a9c72010-01-26 16:00:26 -0500175 /* Check to see if it's in GPT format.... */
176 if (allOK) {
177 for (i = 0; i < 4; i++) {
178 if (partitions[i].partitionType == UINT8_C(0xEE)) {
179 state = gpt;
180 } // if
181 } // for
182 } // if
183
184 // If there's an EFI GPT partition, look for other partition types,
185 // to flag as hybrid
186 if (state == gpt) {
187 for (i = 0 ; i < 4; i++) {
188 if ((partitions[i].partitionType != UINT8_C(0xEE)) &&
189 (partitions[i].partitionType != UINT8_C(0x00)))
190 state = hybrid;
191 } // for
192 } // if (hybrid detection code)
193 } // no initial error
srs56940a697312010-01-28 21:10:52 -0500194} // MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize)
srs5694e7b4ff92009-08-18 13:16:10 -0400195
srs5694978041c2009-09-21 20:51:47 -0400196// This is a recursive function to read all the logical partitions, following the
197// logical partition linked list from the disk and storing the basic data in the
198// partitions[] array. Returns last index to partitions[] used, or -1 if there was
199// a problem.
200// Parameters:
srs5694978041c2009-09-21 20:51:47 -0400201// extendedStart = LBA of the start of the extended partition
202// diskOffset = LBA offset WITHIN the extended partition of the one to be read
203// partNum = location in partitions[] array to store retrieved data
srs5694546a9c72010-01-26 16:00:26 -0500204int MBRData::ReadLogicalPart(uint32_t extendedStart,
srs5694978041c2009-09-21 20:51:47 -0400205 uint32_t diskOffset, int partNum) {
206 struct TempMBR ebr;
srs5694546a9c72010-01-26 16:00:26 -0500207 uint64_t offset;
srs5694978041c2009-09-21 20:51:47 -0400208
209 // Check for a valid partition number. Note that partitions MAY be read into
210 // the area normally used by primary partitions, although the only calling
211 // function as of GPT fdisk version 0.5.0 doesn't do so.
212 if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
srs5694546a9c72010-01-26 16:00:26 -0500213 offset = (uint64_t) (extendedStart + diskOffset);
srs5694546a9c72010-01-26 16:00:26 -0500214 if (myDisk->Seek(offset) == 0) { // seek to EBR record
srs5694fed16d02010-01-27 23:03:40 -0500215 cerr << "Unable to seek to " << offset << "! Aborting!\n";
srs5694978041c2009-09-21 20:51:47 -0400216 partNum = -1;
217 }
srs5694546a9c72010-01-26 16:00:26 -0500218 if (myDisk->Read(&ebr, 512) != 512) { // Load the data....
srs5694fed16d02010-01-27 23:03:40 -0500219 cerr << "Error seeking to or reading logical partition data from " << offset
220 << "!\nAborting!\n";
srs5694978041c2009-09-21 20:51:47 -0400221 partNum = -1;
222 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
223 ReverseBytes(&ebr.MBRSignature, 2);
224 ReverseBytes(&ebr.partitions[0].firstLBA, 4);
225 ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
226 ReverseBytes(&ebr.partitions[1].firstLBA, 4);
227 ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
228 } // if/else/if
229
230 if (ebr.MBRSignature != MBR_SIGNATURE) {
231 partNum = -1;
srs5694fed16d02010-01-27 23:03:40 -0500232 cerr << "MBR signature in logical partition invalid; read 0x";
233 cerr.fill('0');
234 cerr.width(4);
235 cerr.setf(ios::uppercase);
236 cerr << hex << ebr.MBRSignature << ", but should be 0x";
237 cerr.width(4);
238 cerr << MBR_SIGNATURE << dec << "\n";
239 cerr.fill(' ');
srs5694978041c2009-09-21 20:51:47 -0400240 } // if
241
242 // Copy over the basic data....
243 partitions[partNum].status = ebr.partitions[0].status;
244 partitions[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
245 partitions[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
246 partitions[partNum].partitionType = ebr.partitions[0].partitionType;
247
248 // Find the next partition (if there is one) and recurse....
249 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) &&
250 (partNum < (MAX_MBR_PARTS - 1))) {
srs5694546a9c72010-01-26 16:00:26 -0500251 partNum = ReadLogicalPart(extendedStart, ebr.partitions[1].firstLBA,
srs5694978041c2009-09-21 20:51:47 -0400252 partNum + 1);
253 } else {
254 partNum++;
255 } // if another partition
256 } // Not enough space for all the logicals (or previous error encountered)
257 return (partNum);
258} // MBRData::ReadLogicalPart()
259
260// Write the MBR data to the default defined device. Note that this writes
261// ONLY the MBR itself, not the logical partition data.
srs5694e7b4ff92009-08-18 13:16:10 -0400262int MBRData::WriteMBRData(void) {
srs56940a697312010-01-28 21:10:52 -0500263 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400264
srs56940a697312010-01-28 21:10:52 -0500265 if (myDisk != NULL) {
266 if (myDisk->OpenForWrite(device) != 0) {
267 allOK = WriteMBRData(myDisk);
268 } else {
269 allOK = 0;
270 } // if/else
271 myDisk->Close();
272 } else allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400273 return allOK;
274} // MBRData::WriteMBRData(void)
275
276// Save the MBR data to a file. Note that this function writes ONLY the
277// MBR data, not the logical partitions (if any are defined).
srs5694546a9c72010-01-26 16:00:26 -0500278int MBRData::WriteMBRData(DiskIO *theDisk) {
279 int i, j, allOK;
srs5694221e0872009-08-29 15:00:31 -0400280 TempMBR tempMBR;
srs56942a9f5da2009-08-26 00:48:01 -0400281
282 // Reverse the byte order, if necessary
283 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400284 ReverseBytes(&diskSignature, 4);
285 ReverseBytes(&nulls, 2);
286 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400287 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400288 ReverseBytes(&partitions[i].firstLBA, 4);
289 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400290 } // for
291 } // if
292
srs5694221e0872009-08-29 15:00:31 -0400293 // Copy MBR data to a 512-byte data structure for writing, to
294 // work around a FreeBSD limitation....
295 for (i = 0; i < 440; i++)
296 tempMBR.code[i] = code[i];
297 tempMBR.diskSignature = diskSignature;
298 tempMBR.nulls = nulls;
299 tempMBR.MBRSignature = MBRSignature;
300 for (i = 0; i < 4; i++) {
301 tempMBR.partitions[i].status = partitions[i].status;
302 tempMBR.partitions[i].partitionType = partitions[i].partitionType;
303 tempMBR.partitions[i].firstLBA = partitions[i].firstLBA;
304 tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA;
305 for (j = 0; j < 3; j++) {
306 tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j];
307 tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j];
308 } // for j...
309 } // for i...
310
311 // Now write that data structure...
srs5694546a9c72010-01-26 16:00:26 -0500312 allOK = theDisk->OpenForWrite();
313 if (allOK && theDisk->Seek(0)) {
314 if (theDisk->Write(&tempMBR, 512) != 512) {
315 allOK = 0;
srs5694fed16d02010-01-27 23:03:40 -0500316 cerr << "Warning! Error " << errno << " when saving MBR!\n";
srs5694546a9c72010-01-26 16:00:26 -0500317 } // if
318 } else {
319 allOK = 0;
srs5694fed16d02010-01-27 23:03:40 -0500320 cerr << "Warning! Error " << errno << " when seeking to MBR to write it!\n";
srs5694546a9c72010-01-26 16:00:26 -0500321 } // if/else
322 theDisk->Close();
srs5694221e0872009-08-29 15:00:31 -0400323
srs5694221e0872009-08-29 15:00:31 -0400324 // Reverse the byte order back, if necessary
srs56942a9f5da2009-08-26 00:48:01 -0400325 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400326 ReverseBytes(&diskSignature, 4);
327 ReverseBytes(&nulls, 2);
328 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400329 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400330 ReverseBytes(&partitions[i].firstLBA, 4);
331 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400332 } // for
333 }// if
srs5694546a9c72010-01-26 16:00:26 -0500334 return allOK;
srs56940a697312010-01-28 21:10:52 -0500335} // MBRData::WriteMBRData(DiskIO *theDisk)
srs5694e7b4ff92009-08-18 13:16:10 -0400336
srs56940a697312010-01-28 21:10:52 -0500337int MBRData::WriteMBRData(const string & deviceFilename) {
srs5694fed16d02010-01-27 23:03:40 -0500338 device = deviceFilename;
srs5694ba00fed2010-01-12 18:18:36 -0500339 return WriteMBRData();
srs56940a697312010-01-28 21:10:52 -0500340} // MBRData::WriteMBRData(const string & deviceFilename)
srs5694ba00fed2010-01-12 18:18:36 -0500341
srs5694978041c2009-09-21 20:51:47 -0400342/********************************************
343 * *
344 * Functions that display data for the user *
345 * *
346 ********************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400347
348// Show the MBR data to the user....
349void MBRData::DisplayMBRData(void) {
350 int i;
srs5694e19ba092009-08-24 14:10:35 -0400351 char bootCode;
srs5694e7b4ff92009-08-18 13:16:10 -0400352
srs5694fed16d02010-01-27 23:03:40 -0500353 cout << "MBR disk identifier: 0x";
354 cout.width(8);
355 cout.fill('0');
356 cout.setf(ios::uppercase);
357 cout << hex << diskSignature << dec << "\n";
358 cout << "MBR partitions:\n";
359 cout << "Number\t Boot\t Start (sector)\t Length (sectors)\tType\n";
srs5694978041c2009-09-21 20:51:47 -0400360 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e7b4ff92009-08-18 13:16:10 -0400361 if (partitions[i].lengthLBA != 0) {
srs5694e19ba092009-08-24 14:10:35 -0400362 if (partitions[i].status && 0x80) // it's bootable
srs5694978041c2009-09-21 20:51:47 -0400363 bootCode = '*';
srs5694e19ba092009-08-24 14:10:35 -0400364 else
365 bootCode = ' ';
srs5694fed16d02010-01-27 23:03:40 -0500366 cout.fill(' ');
367 cout.width(4);
368 cout << i + 1 << "\t " << bootCode << "\t";
369 cout.width(13);
370 cout << partitions[i].firstLBA << "\t";
371 cout.width(15);
372 cout << partitions[i].lengthLBA << " \t0x";
373 cout.width(2);
374 cout.fill('0');
375 cout << hex << (int) partitions[i].partitionType << dec << "\n";
srs5694e7b4ff92009-08-18 13:16:10 -0400376 } // if
srs5694fed16d02010-01-27 23:03:40 -0500377 cout.fill(' ');
srs5694e7b4ff92009-08-18 13:16:10 -0400378 } // for
srs5694fed16d02010-01-27 23:03:40 -0500379 cout << "\nDisk size is " << diskSize << " sectors ("
srs5694e321d442010-01-29 17:44:04 -0500380 << BytesToSI(diskSize * (uint64_t) blockSize) << ")\n";
srs5694e7b4ff92009-08-18 13:16:10 -0400381} // MBRData::DisplayMBRData()
382
srs5694978041c2009-09-21 20:51:47 -0400383// Displays the state, as a word, on stdout. Used for debugging & to
384// tell the user about the MBR state when the program launches....
385void MBRData::ShowState(void) {
386 switch (state) {
387 case invalid:
srs5694fed16d02010-01-27 23:03:40 -0500388 cout << " MBR: not present\n";
srs5694978041c2009-09-21 20:51:47 -0400389 break;
390 case gpt:
srs5694fed16d02010-01-27 23:03:40 -0500391 cout << " MBR: protective\n";
srs5694978041c2009-09-21 20:51:47 -0400392 break;
393 case hybrid:
srs5694fed16d02010-01-27 23:03:40 -0500394 cout << " MBR: hybrid\n";
srs5694978041c2009-09-21 20:51:47 -0400395 break;
396 case mbr:
srs5694fed16d02010-01-27 23:03:40 -0500397 cout << " MBR: MBR only\n";
srs5694978041c2009-09-21 20:51:47 -0400398 break;
399 default:
srs5694fed16d02010-01-27 23:03:40 -0500400 cout << "\a MBR: unknown -- bug!\n";
srs5694978041c2009-09-21 20:51:47 -0400401 break;
402 } // switch
403} // MBRData::ShowState()
404
405/*********************************************************************
406 * *
407 * Functions that set or get disk metadata (CHS geometry, disk size, *
408 * etc.) *
409 * *
410 *********************************************************************/
411
412// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function.
413// Note that this only sets the heads and sectors; the number of
414// cylinders is determined by these values and the disk size.
415void MBRData::SetCHSGeom(uint32_t h, uint32_t s) {
416 if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
417 numHeads = h;
418 numSecspTrack = s;
419 } else {
srs5694fed16d02010-01-27 23:03:40 -0500420 cout << "Warning! Attempt to set invalid CHS geometry!\n";
srs5694978041c2009-09-21 20:51:47 -0400421 } // if/else
422} // MBRData::SetCHSGeom()
423
424// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
425// was within the range that can be expressed by CHS (including 0, for an
426// empty partition), 0 if the value is outside that range, and -1 if chs is
427// invalid.
428int MBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
429 uint64_t cylinder, head, sector; // all numbered from 0
430 uint64_t remainder;
431 int retval = 1;
432 int done = 0;
433
434 if (chs != NULL) {
435 // Special case: In case of 0 LBA value, zero out CHS values....
436 if (lba == 0) {
437 chs[0] = chs[1] = chs[2] = UINT8_C(0);
438 done = 1;
439 } // if
440 // If LBA value is too large for CHS, max out CHS values....
441 if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
442 chs[0] = 254;
443 chs[1] = chs[2] = 255;
444 done = 1;
445 retval = 0;
446 } // if
447 // If neither of the above applies, compute CHS values....
448 if (!done) {
449 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
450 remainder = lba - (cylinder * numHeads * numSecspTrack);
451 head = remainder / numSecspTrack;
452 remainder -= head * numSecspTrack;
453 sector = remainder;
454 if (head < numHeads)
455 chs[0] = head;
456 else
457 retval = 0;
458 if (sector < numSecspTrack) {
459 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
460 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
461 } else {
462 retval = 0;
463 } // if/else
464 } // if value is expressible and non-0
465 } else { // Invalid (NULL) chs pointer
466 retval = -1;
467 } // if CHS pointer valid
468 return (retval);
469} // MBRData::LBAtoCHS()
470
471/*****************************************************
472 * *
473 * Functions to create, delete, or change partitions *
474 * *
475 *****************************************************/
476
477// Empty all data. Meant mainly for calling by constructors, but it's also
478// used by the hybrid MBR functions in the GPTData class.
479void MBRData::EmptyMBR(int clearBootloader) {
480 int i;
481
482 // Zero out the boot loader section, the disk signature, and the
483 // 2-byte nulls area only if requested to do so. (This is the
484 // default.)
485 if (clearBootloader == 1) {
486 for (i = 0; i < 440; i++)
487 code[i] = 0;
488 diskSignature = (uint32_t) rand();
489 nulls = 0;
490 } // if
491
492 // Blank out the partitions
493 for (i = 0; i < MAX_MBR_PARTS; i++) {
494 partitions[i].status = UINT8_C(0);
495 partitions[i].firstSector[0] = UINT8_C(0);
496 partitions[i].firstSector[1] = UINT8_C(0);
497 partitions[i].firstSector[2] = UINT8_C(0);
498 partitions[i].partitionType = UINT8_C(0);
499 partitions[i].lastSector[0] = UINT8_C(0);
500 partitions[i].lastSector[1] = UINT8_C(0);
501 partitions[i].lastSector[2] = UINT8_C(0);
502 partitions[i].firstLBA = UINT32_C(0);
503 partitions[i].lengthLBA = UINT32_C(0);
504 } // for
505 MBRSignature = MBR_SIGNATURE;
506} // MBRData::EmptyMBR()
507
srs5694221e0872009-08-29 15:00:31 -0400508// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
509void MBRData::MakeProtectiveMBR(int clearBoot) {
srs5694978041c2009-09-21 20:51:47 -0400510
511 EmptyMBR(clearBoot);
srs5694e7b4ff92009-08-18 13:16:10 -0400512
513 // Initialize variables
514 nulls = 0;
515 MBRSignature = MBR_SIGNATURE;
516
517 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
518
519 // Write CHS data. This maxes out the use of the disk, as much as
520 // possible -- even to the point of exceeding the capacity of sub-8GB
521 // disks. The EFI spec says to use 0xffffff as the ending value,
522 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
523 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
524 // value in for the FIRST sector, too!
525 partitions[0].firstSector[0] = UINT8_C(0);
526 partitions[0].firstSector[1] = UINT8_C(1);
527 partitions[0].firstSector[2] = UINT8_C(0);
528 partitions[0].lastSector[0] = UINT8_C(255);
529 partitions[0].lastSector[1] = UINT8_C(255);
530 partitions[0].lastSector[2] = UINT8_C(255);
531
532 partitions[0].partitionType = UINT8_C(0xEE);
533 partitions[0].firstLBA = UINT32_C(1);
534 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
srs5694978041c2009-09-21 20:51:47 -0400535 partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
srs5694e7b4ff92009-08-18 13:16:10 -0400536 } else { // disk is too big to represent, so fake it...
537 partitions[0].lengthLBA = UINT32_MAX;
538 } // if/else
539
srs5694e7b4ff92009-08-18 13:16:10 -0400540 state = gpt;
541} // MBRData::MakeProtectiveMBR()
542
srs5694978041c2009-09-21 20:51:47 -0400543// Create a partition of the specified number, starting LBA, and
544// length. This function does *NO* error checking, so it's possible
545// to seriously screw up a partition table using this function!
546// Note: This function should NOT be used to create the 0xEE partition
547// in a conventional GPT configuration, since that partition has
548// specific size requirements that this function won't handle. It may
549// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
550// since those toss the rulebook away anyhow....
551void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
552 int bootable) {
553 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
554 partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
555 partitions[num].firstSector[0] = UINT8_C(0);
556 partitions[num].firstSector[1] = UINT8_C(0);
557 partitions[num].firstSector[2] = UINT8_C(0);
558 partitions[num].partitionType = (uint8_t) type;
559 partitions[num].lastSector[0] = UINT8_C(0);
560 partitions[num].lastSector[1] = UINT8_C(0);
561 partitions[num].lastSector[2] = UINT8_C(0);
562 partitions[num].firstLBA = start;
563 partitions[num].lengthLBA = length;
564 // If this is a "real" partition, set its CHS geometry
565 if (length > 0) {
566 LBAtoCHS((uint64_t) start, partitions[num].firstSector);
567 LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
568 } // if (length > 0)
569 } // if valid partition number
570} // MBRData::MakePart()
571
srs5694c0ca8f82009-08-20 21:35:25 -0400572// Create a partition that fills the most available space. Returns
573// 1 if partition was created, 0 otherwise. Intended for use in
574// creating hybrid MBRs.
575int MBRData::MakeBiggestPart(int i, int type) {
576 uint32_t start = UINT32_C(1); // starting point for each search
577 uint32_t firstBlock; // first block in a segment
578 uint32_t lastBlock; // last block in a segment
579 uint32_t segmentSize; // size of segment in blocks
580 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
581 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
582 int found = 0;
583
584 do {
585 firstBlock = FindFirstAvailable(start);
586 if (firstBlock != UINT32_C(0)) { // something's free...
587 lastBlock = FindLastInFree(firstBlock);
588 segmentSize = lastBlock - firstBlock + UINT32_C(1);
srs5694978041c2009-09-21 20:51:47 -0400589 if (segmentSize > selectedSize) {
srs5694c0ca8f82009-08-20 21:35:25 -0400590 selectedSize = segmentSize;
srs5694978041c2009-09-21 20:51:47 -0400591 selectedSegment = firstBlock;
592 } // if
593 start = lastBlock + 1;
srs5694c0ca8f82009-08-20 21:35:25 -0400594 } // if
595 } while (firstBlock != 0);
596 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
597 found = 1;
598 MakePart(i, selectedSegment, selectedSize, type, 0);
599 } else {
600 found = 0;
601 } // if/else
602 return found;
603} // MBRData::MakeBiggestPart(int i)
604
srs5694e35eb1b2009-09-14 00:29:34 -0400605// Delete partition #i
606void MBRData::DeletePartition(int i) {
607 int j;
608
609 partitions[i].firstLBA = UINT32_C(0);
610 partitions[i].lengthLBA = UINT32_C(0);
611 partitions[i].status = UINT8_C(0);
612 partitions[i].partitionType = UINT8_C(0);
613 for (j = 0; j < 3; j++) {
614 partitions[i].firstSector[j] = UINT8_C(0);
615 partitions[i].lastSector[j] = UINT8_C(0);
616 } // for j (CHS data blanking)
617} // MBRData::DeletePartition()
618
srs5694e4ac11e2009-08-31 10:13:04 -0400619// Delete a partition if one exists at the specified location.
620// Returns 1 if a partition was deleted, 0 otherwise....
621// Used to help keep GPT & hybrid MBR partitions in sync....
622int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
623 uint32_t start32, length32;
srs5694978041c2009-09-21 20:51:47 -0400624 int i, deleted = 0;
srs5694e4ac11e2009-08-31 10:13:04 -0400625
srs5694978041c2009-09-21 20:51:47 -0400626 if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400627 start32 = (uint32_t) start64;
628 length32 = (uint32_t) length64;
srs5694978041c2009-09-21 20:51:47 -0400629 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e4ac11e2009-08-31 10:13:04 -0400630 if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
srs5694546a9c72010-01-26 16:00:26 -0500631 (partitions[i].partitionType != 0xEE)) {
srs5694e35eb1b2009-09-14 00:29:34 -0400632 DeletePartition(i);
srs5694978041c2009-09-21 20:51:47 -0400633 if (state == hybrid)
634 OptimizeEESize();
srs5694e4ac11e2009-08-31 10:13:04 -0400635 deleted = 1;
srs5694546a9c72010-01-26 16:00:26 -0500636 } // if (match found)
srs5694e4ac11e2009-08-31 10:13:04 -0400637 } // for i (partition scan)
638 } // if (hybrid & GPT partition < 2TiB)
639 return deleted;
640} // MBRData::DeleteByLocation()
641
642// Optimizes the size of the 0xEE (EFI GPT) partition
643void MBRData::OptimizeEESize(void) {
644 int i, typeFlag = 0;
645 uint32_t after;
646
647 for (i = 0; i < 4; i++) {
648 // Check for non-empty and non-0xEE partitions
649 if ((partitions[i].partitionType != 0xEE) && (partitions[i].partitionType != 0x00))
650 typeFlag++;
651 if (partitions[i].partitionType == 0xEE) {
652 // Blank space before this partition; fill it....
653 if (IsFree(partitions[i].firstLBA - 1)) {
654 partitions[i].firstLBA = FindFirstInFree(partitions[i].firstLBA - 1);
655 } // if
656 // Blank space after this partition; fill it....
657 after = partitions[i].firstLBA + partitions[i].lengthLBA;
658 if (IsFree(after)) {
659 partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
660 } // if free space after
661 } // if partition is 0xEE
srs5694e4ac11e2009-08-31 10:13:04 -0400662 } // for partition loop
srs5694978041c2009-09-21 20:51:47 -0400663 if (typeFlag == 0) { // No non-hybrid partitions found
664 MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
665 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400666} // MBRData::OptimizeEESize()
667
srs5694978041c2009-09-21 20:51:47 -0400668/****************************************
669 * *
670 * Functions to find data on free space *
671 * *
672 ****************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400673
srs5694c0ca8f82009-08-20 21:35:25 -0400674// Finds the first free space on the disk from start onward; returns 0
675// if none available....
676uint32_t MBRData::FindFirstAvailable(uint32_t start) {
677 uint32_t first;
678 uint32_t i;
srs5694978041c2009-09-21 20:51:47 -0400679 int firstMoved;
srs5694c0ca8f82009-08-20 21:35:25 -0400680
681 first = start;
682
683 // ...now search through all partitions; if first is within an
684 // existing partition, move it to the next sector after that
685 // partition and repeat. If first was moved, set firstMoved
686 // flag; repeat until firstMoved is not set, so as to catch
687 // cases where partitions are out of sequential order....
688 do {
689 firstMoved = 0;
690 for (i = 0; i < 4; i++) {
691 // Check if it's in the existing partition
692 if ((first >= partitions[i].firstLBA) &&
693 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
694 first = partitions[i].firstLBA + partitions[i].lengthLBA;
695 firstMoved = 1;
srs5694978041c2009-09-21 20:51:47 -0400696 } // if
srs5694c0ca8f82009-08-20 21:35:25 -0400697 } // for
698 } while (firstMoved == 1);
699 if (first >= diskSize)
700 first = 0;
701 return (first);
702} // MBRData::FindFirstAvailable()
703
srs5694e4ac11e2009-08-31 10:13:04 -0400704// Finds the last free sector on the disk from start forward.
srs5694c0ca8f82009-08-20 21:35:25 -0400705uint32_t MBRData::FindLastInFree(uint32_t start) {
706 uint32_t nearestStart;
707 uint32_t i;
708
srs5694978041c2009-09-21 20:51:47 -0400709 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs5694c0ca8f82009-08-20 21:35:25 -0400710 nearestStart = diskSize - 1;
711 else
712 nearestStart = UINT32_MAX - 1;
713 for (i = 0; i < 4; i++) {
714 if ((nearestStart > partitions[i].firstLBA) &&
715 (partitions[i].firstLBA > start)) {
716 nearestStart = partitions[i].firstLBA - 1;
717 } // if
718 } // for
719 return (nearestStart);
srs5694e4ac11e2009-08-31 10:13:04 -0400720} // MBRData::FindLastInFree()
721
722// Finds the first free sector on the disk from start backward.
723uint32_t MBRData::FindFirstInFree(uint32_t start) {
srs5694e35eb1b2009-09-14 00:29:34 -0400724 uint32_t bestLastLBA, thisLastLBA;
srs5694e4ac11e2009-08-31 10:13:04 -0400725 int i;
726
727 bestLastLBA = 1;
728 for (i = 0; i < 4; i++) {
729 thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
730 if (thisLastLBA > 0) thisLastLBA--;
731 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) {
732 bestLastLBA = thisLastLBA + 1;
733 } // if
734 } // for
735 return (bestLastLBA);
736} // MBRData::FindFirstInFree()
737
738// Returns 1 if the specified sector is unallocated, 0 if it's
739// allocated.
740int MBRData::IsFree(uint32_t sector) {
741 int i, isFree = 1;
742 uint32_t first, last;
743
744 for (i = 0; i < 4; i++) {
745 first = partitions[i].firstLBA;
srs5694978041c2009-09-21 20:51:47 -0400746 // Note: Weird two-line thing to avoid subtracting 1 from a 0 value
747 // for an unsigned int....
srs5694e4ac11e2009-08-31 10:13:04 -0400748 last = first + partitions[i].lengthLBA;
749 if (last > 0) last--;
750 if ((first <= sector) && (last >= sector))
751 isFree = 0;
752 } // for
753 return isFree;
754} // MBRData::IsFree()
srs5694c0ca8f82009-08-20 21:35:25 -0400755
srs5694978041c2009-09-21 20:51:47 -0400756/******************************************************
757 * *
758 * Functions that extract data on specific partitions *
759 * *
760 ******************************************************/
761
srs5694e7b4ff92009-08-18 13:16:10 -0400762uint8_t MBRData::GetStatus(int i) {
763 MBRRecord* thePart;
764 uint8_t retval;
765
766 thePart = GetPartition(i);
767 if (thePart != NULL)
768 retval = thePart->status;
769 else
770 retval = UINT8_C(0);
771 return retval;
772} // MBRData::GetStatus()
773
774uint8_t MBRData::GetType(int i) {
775 MBRRecord* thePart;
776 uint8_t retval;
777
778 thePart = GetPartition(i);
779 if (thePart != NULL)
780 retval = thePart->partitionType;
781 else
782 retval = UINT8_C(0);
783 return retval;
784} // MBRData::GetType()
785
786uint32_t MBRData::GetFirstSector(int i) {
787 MBRRecord* thePart;
788 uint32_t retval;
789
790 thePart = GetPartition(i);
791 if (thePart != NULL) {
792 retval = thePart->firstLBA;
793 } else
794 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400795 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400796} // MBRData::GetFirstSector()
797
798uint32_t MBRData::GetLength(int i) {
799 MBRRecord* thePart;
800 uint32_t retval;
801
802 thePart = GetPartition(i);
803 if (thePart != NULL) {
804 retval = thePart->lengthLBA;
805 } else
806 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400807 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400808} // MBRData::GetLength()
srs5694221e0872009-08-29 15:00:31 -0400809
810// Return the MBR data as a GPT partition....
811GPTPart MBRData::AsGPT(int i) {
812 MBRRecord* origPart;
813 GPTPart newPart;
814 uint8_t origType;
815 uint64_t firstSector, lastSector;
srs5694221e0872009-08-29 15:00:31 -0400816
817 newPart.BlankPartition();
818 origPart = GetPartition(i);
819 if (origPart != NULL) {
820 origType = origPart->partitionType;
821
822 // don't convert extended, hybrid protective, or null (non-existent)
823 // partitions (Note similar protection is in GPTData::XFormPartitions(),
824 // but I want it here too in case I call this function in another
825 // context in the future....)
srs5694e35eb1b2009-09-14 00:29:34 -0400826 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694221e0872009-08-29 15:00:31 -0400827 (origType != 0x00) && (origType != 0xEE)) {
828 firstSector = (uint64_t) origPart->firstLBA;
829 newPart.SetFirstLBA(firstSector);
830 lastSector = firstSector + (uint64_t) origPart->lengthLBA;
831 if (lastSector > 0) lastSector--;
832 newPart.SetLastLBA(lastSector);
833 newPart.SetType(((uint16_t) origType) * 0x0100);
834 newPart.SetUniqueGUID(1);
835 newPart.SetAttributes(0);
srs5694fed16d02010-01-27 23:03:40 -0500836 newPart.SetName(newPart.GetNameType());
srs5694978041c2009-09-21 20:51:47 -0400837 } // if not extended, protective, or non-existent
838 } // if (origPart != NULL)
srs5694221e0872009-08-29 15:00:31 -0400839 return newPart;
840} // MBRData::AsGPT()
srs5694978041c2009-09-21 20:51:47 -0400841
842/***********************
843 * *
844 * Protected functions *
845 * *
846 ***********************/
847
848// Return a pointer to a primary or logical partition, or NULL if
849// the partition is out of range....
850struct MBRRecord* MBRData::GetPartition(int i) {
851 MBRRecord* thePart = NULL;
852
853 if ((i >= 0) && (i < MAX_MBR_PARTS))
854 thePart = &partitions[i];
855 return thePart;
856} // GetPartition()