blob: 2417d7745dba924ec110918c2cdcbb0432ed14e9 [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;
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) {
276 if (myDisk->OpenForWrite(device) != 0) {
277 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) {
496 for (i = 0; i < 440; i++)
497 code[i] = 0;
498 diskSignature = (uint32_t) rand();
499 nulls = 0;
500 } // if
501
502 // Blank out the partitions
503 for (i = 0; i < MAX_MBR_PARTS; i++) {
504 partitions[i].status = UINT8_C(0);
505 partitions[i].firstSector[0] = UINT8_C(0);
506 partitions[i].firstSector[1] = UINT8_C(0);
507 partitions[i].firstSector[2] = UINT8_C(0);
508 partitions[i].partitionType = UINT8_C(0);
509 partitions[i].lastSector[0] = UINT8_C(0);
510 partitions[i].lastSector[1] = UINT8_C(0);
511 partitions[i].lastSector[2] = UINT8_C(0);
512 partitions[i].firstLBA = UINT32_C(0);
513 partitions[i].lengthLBA = UINT32_C(0);
514 } // for
515 MBRSignature = MBR_SIGNATURE;
516} // MBRData::EmptyMBR()
517
srs5694221e0872009-08-29 15:00:31 -0400518// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
519void MBRData::MakeProtectiveMBR(int clearBoot) {
srs5694978041c2009-09-21 20:51:47 -0400520
521 EmptyMBR(clearBoot);
srs5694e7b4ff92009-08-18 13:16:10 -0400522
523 // Initialize variables
524 nulls = 0;
525 MBRSignature = MBR_SIGNATURE;
526
527 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
528
529 // Write CHS data. This maxes out the use of the disk, as much as
530 // possible -- even to the point of exceeding the capacity of sub-8GB
531 // disks. The EFI spec says to use 0xffffff as the ending value,
532 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
533 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
534 // value in for the FIRST sector, too!
535 partitions[0].firstSector[0] = UINT8_C(0);
536 partitions[0].firstSector[1] = UINT8_C(1);
537 partitions[0].firstSector[2] = UINT8_C(0);
538 partitions[0].lastSector[0] = UINT8_C(255);
539 partitions[0].lastSector[1] = UINT8_C(255);
540 partitions[0].lastSector[2] = UINT8_C(255);
541
542 partitions[0].partitionType = UINT8_C(0xEE);
543 partitions[0].firstLBA = UINT32_C(1);
544 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
srs5694978041c2009-09-21 20:51:47 -0400545 partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
srs5694e7b4ff92009-08-18 13:16:10 -0400546 } else { // disk is too big to represent, so fake it...
547 partitions[0].lengthLBA = UINT32_MAX;
548 } // if/else
549
srs5694e7b4ff92009-08-18 13:16:10 -0400550 state = gpt;
551} // MBRData::MakeProtectiveMBR()
552
srs5694978041c2009-09-21 20:51:47 -0400553// Create a partition of the specified number, starting LBA, and
554// length. This function does *NO* error checking, so it's possible
555// to seriously screw up a partition table using this function!
556// Note: This function should NOT be used to create the 0xEE partition
557// in a conventional GPT configuration, since that partition has
558// specific size requirements that this function won't handle. It may
559// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
560// since those toss the rulebook away anyhow....
561void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
562 int bootable) {
563 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
564 partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
565 partitions[num].firstSector[0] = UINT8_C(0);
566 partitions[num].firstSector[1] = UINT8_C(0);
567 partitions[num].firstSector[2] = UINT8_C(0);
568 partitions[num].partitionType = (uint8_t) type;
569 partitions[num].lastSector[0] = UINT8_C(0);
570 partitions[num].lastSector[1] = UINT8_C(0);
571 partitions[num].lastSector[2] = UINT8_C(0);
572 partitions[num].firstLBA = start;
573 partitions[num].lengthLBA = length;
574 // If this is a "real" partition, set its CHS geometry
575 if (length > 0) {
576 LBAtoCHS((uint64_t) start, partitions[num].firstSector);
577 LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
578 } // if (length > 0)
579 } // if valid partition number
580} // MBRData::MakePart()
581
srs5694c0ca8f82009-08-20 21:35:25 -0400582// Create a partition that fills the most available space. Returns
583// 1 if partition was created, 0 otherwise. Intended for use in
584// creating hybrid MBRs.
585int MBRData::MakeBiggestPart(int i, int type) {
586 uint32_t start = UINT32_C(1); // starting point for each search
587 uint32_t firstBlock; // first block in a segment
588 uint32_t lastBlock; // last block in a segment
589 uint32_t segmentSize; // size of segment in blocks
590 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
591 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
592 int found = 0;
593
594 do {
595 firstBlock = FindFirstAvailable(start);
596 if (firstBlock != UINT32_C(0)) { // something's free...
597 lastBlock = FindLastInFree(firstBlock);
598 segmentSize = lastBlock - firstBlock + UINT32_C(1);
srs5694978041c2009-09-21 20:51:47 -0400599 if (segmentSize > selectedSize) {
srs5694c0ca8f82009-08-20 21:35:25 -0400600 selectedSize = segmentSize;
srs5694978041c2009-09-21 20:51:47 -0400601 selectedSegment = firstBlock;
602 } // if
603 start = lastBlock + 1;
srs5694c0ca8f82009-08-20 21:35:25 -0400604 } // if
605 } while (firstBlock != 0);
606 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
607 found = 1;
608 MakePart(i, selectedSegment, selectedSize, type, 0);
609 } else {
610 found = 0;
611 } // if/else
612 return found;
613} // MBRData::MakeBiggestPart(int i)
614
srs5694e35eb1b2009-09-14 00:29:34 -0400615// Delete partition #i
616void MBRData::DeletePartition(int i) {
617 int j;
618
619 partitions[i].firstLBA = UINT32_C(0);
620 partitions[i].lengthLBA = UINT32_C(0);
621 partitions[i].status = UINT8_C(0);
622 partitions[i].partitionType = UINT8_C(0);
623 for (j = 0; j < 3; j++) {
624 partitions[i].firstSector[j] = UINT8_C(0);
625 partitions[i].lastSector[j] = UINT8_C(0);
626 } // for j (CHS data blanking)
627} // MBRData::DeletePartition()
628
srs5694e4ac11e2009-08-31 10:13:04 -0400629// Delete a partition if one exists at the specified location.
630// Returns 1 if a partition was deleted, 0 otherwise....
631// Used to help keep GPT & hybrid MBR partitions in sync....
632int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
633 uint32_t start32, length32;
srs5694978041c2009-09-21 20:51:47 -0400634 int i, deleted = 0;
srs5694e4ac11e2009-08-31 10:13:04 -0400635
srs5694978041c2009-09-21 20:51:47 -0400636 if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400637 start32 = (uint32_t) start64;
638 length32 = (uint32_t) length64;
srs5694978041c2009-09-21 20:51:47 -0400639 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e4ac11e2009-08-31 10:13:04 -0400640 if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
srs5694546a9c72010-01-26 16:00:26 -0500641 (partitions[i].partitionType != 0xEE)) {
srs5694e35eb1b2009-09-14 00:29:34 -0400642 DeletePartition(i);
srs5694978041c2009-09-21 20:51:47 -0400643 if (state == hybrid)
644 OptimizeEESize();
srs5694e4ac11e2009-08-31 10:13:04 -0400645 deleted = 1;
srs5694546a9c72010-01-26 16:00:26 -0500646 } // if (match found)
srs5694e4ac11e2009-08-31 10:13:04 -0400647 } // for i (partition scan)
648 } // if (hybrid & GPT partition < 2TiB)
649 return deleted;
650} // MBRData::DeleteByLocation()
651
652// Optimizes the size of the 0xEE (EFI GPT) partition
653void MBRData::OptimizeEESize(void) {
654 int i, typeFlag = 0;
655 uint32_t after;
656
657 for (i = 0; i < 4; i++) {
658 // Check for non-empty and non-0xEE partitions
659 if ((partitions[i].partitionType != 0xEE) && (partitions[i].partitionType != 0x00))
660 typeFlag++;
661 if (partitions[i].partitionType == 0xEE) {
662 // Blank space before this partition; fill it....
663 if (IsFree(partitions[i].firstLBA - 1)) {
664 partitions[i].firstLBA = FindFirstInFree(partitions[i].firstLBA - 1);
665 } // if
666 // Blank space after this partition; fill it....
667 after = partitions[i].firstLBA + partitions[i].lengthLBA;
668 if (IsFree(after)) {
669 partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
670 } // if free space after
671 } // if partition is 0xEE
srs5694e4ac11e2009-08-31 10:13:04 -0400672 } // for partition loop
srs5694978041c2009-09-21 20:51:47 -0400673 if (typeFlag == 0) { // No non-hybrid partitions found
674 MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
675 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400676} // MBRData::OptimizeEESize()
677
srs5694978041c2009-09-21 20:51:47 -0400678/****************************************
679 * *
680 * Functions to find data on free space *
681 * *
682 ****************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400683
srs5694c0ca8f82009-08-20 21:35:25 -0400684// Finds the first free space on the disk from start onward; returns 0
685// if none available....
686uint32_t MBRData::FindFirstAvailable(uint32_t start) {
687 uint32_t first;
688 uint32_t i;
srs5694978041c2009-09-21 20:51:47 -0400689 int firstMoved;
srs5694c0ca8f82009-08-20 21:35:25 -0400690
691 first = start;
692
693 // ...now search through all partitions; if first is within an
694 // existing partition, move it to the next sector after that
695 // partition and repeat. If first was moved, set firstMoved
696 // flag; repeat until firstMoved is not set, so as to catch
697 // cases where partitions are out of sequential order....
698 do {
699 firstMoved = 0;
700 for (i = 0; i < 4; i++) {
701 // Check if it's in the existing partition
702 if ((first >= partitions[i].firstLBA) &&
703 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
704 first = partitions[i].firstLBA + partitions[i].lengthLBA;
705 firstMoved = 1;
srs5694978041c2009-09-21 20:51:47 -0400706 } // if
srs5694c0ca8f82009-08-20 21:35:25 -0400707 } // for
708 } while (firstMoved == 1);
709 if (first >= diskSize)
710 first = 0;
711 return (first);
712} // MBRData::FindFirstAvailable()
713
srs5694e4ac11e2009-08-31 10:13:04 -0400714// Finds the last free sector on the disk from start forward.
srs5694c0ca8f82009-08-20 21:35:25 -0400715uint32_t MBRData::FindLastInFree(uint32_t start) {
716 uint32_t nearestStart;
717 uint32_t i;
718
srs5694978041c2009-09-21 20:51:47 -0400719 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs5694c0ca8f82009-08-20 21:35:25 -0400720 nearestStart = diskSize - 1;
721 else
722 nearestStart = UINT32_MAX - 1;
723 for (i = 0; i < 4; i++) {
724 if ((nearestStart > partitions[i].firstLBA) &&
725 (partitions[i].firstLBA > start)) {
726 nearestStart = partitions[i].firstLBA - 1;
727 } // if
728 } // for
729 return (nearestStart);
srs5694e4ac11e2009-08-31 10:13:04 -0400730} // MBRData::FindLastInFree()
731
732// Finds the first free sector on the disk from start backward.
733uint32_t MBRData::FindFirstInFree(uint32_t start) {
srs5694e35eb1b2009-09-14 00:29:34 -0400734 uint32_t bestLastLBA, thisLastLBA;
srs5694e4ac11e2009-08-31 10:13:04 -0400735 int i;
736
737 bestLastLBA = 1;
738 for (i = 0; i < 4; i++) {
739 thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
740 if (thisLastLBA > 0) thisLastLBA--;
741 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) {
742 bestLastLBA = thisLastLBA + 1;
743 } // if
744 } // for
745 return (bestLastLBA);
746} // MBRData::FindFirstInFree()
747
748// Returns 1 if the specified sector is unallocated, 0 if it's
749// allocated.
750int MBRData::IsFree(uint32_t sector) {
751 int i, isFree = 1;
752 uint32_t first, last;
753
754 for (i = 0; i < 4; i++) {
755 first = partitions[i].firstLBA;
srs5694978041c2009-09-21 20:51:47 -0400756 // Note: Weird two-line thing to avoid subtracting 1 from a 0 value
757 // for an unsigned int....
srs5694e4ac11e2009-08-31 10:13:04 -0400758 last = first + partitions[i].lengthLBA;
759 if (last > 0) last--;
760 if ((first <= sector) && (last >= sector))
761 isFree = 0;
762 } // for
763 return isFree;
764} // MBRData::IsFree()
srs5694c0ca8f82009-08-20 21:35:25 -0400765
srs5694978041c2009-09-21 20:51:47 -0400766/******************************************************
767 * *
768 * Functions that extract data on specific partitions *
769 * *
770 ******************************************************/
771
srs5694e7b4ff92009-08-18 13:16:10 -0400772uint8_t MBRData::GetStatus(int i) {
773 MBRRecord* thePart;
774 uint8_t retval;
775
776 thePart = GetPartition(i);
777 if (thePart != NULL)
778 retval = thePart->status;
779 else
780 retval = UINT8_C(0);
781 return retval;
782} // MBRData::GetStatus()
783
784uint8_t MBRData::GetType(int i) {
785 MBRRecord* thePart;
786 uint8_t retval;
787
788 thePart = GetPartition(i);
789 if (thePart != NULL)
790 retval = thePart->partitionType;
791 else
792 retval = UINT8_C(0);
793 return retval;
794} // MBRData::GetType()
795
796uint32_t MBRData::GetFirstSector(int i) {
797 MBRRecord* thePart;
798 uint32_t retval;
799
800 thePart = GetPartition(i);
801 if (thePart != NULL) {
802 retval = thePart->firstLBA;
803 } else
804 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400805 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400806} // MBRData::GetFirstSector()
807
808uint32_t MBRData::GetLength(int i) {
809 MBRRecord* thePart;
810 uint32_t retval;
811
812 thePart = GetPartition(i);
813 if (thePart != NULL) {
814 retval = thePart->lengthLBA;
815 } else
816 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400817 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400818} // MBRData::GetLength()
srs5694221e0872009-08-29 15:00:31 -0400819
820// Return the MBR data as a GPT partition....
821GPTPart MBRData::AsGPT(int i) {
822 MBRRecord* origPart;
823 GPTPart newPart;
824 uint8_t origType;
825 uint64_t firstSector, lastSector;
srs5694221e0872009-08-29 15:00:31 -0400826
827 newPart.BlankPartition();
828 origPart = GetPartition(i);
829 if (origPart != NULL) {
830 origType = origPart->partitionType;
831
832 // don't convert extended, hybrid protective, or null (non-existent)
833 // partitions (Note similar protection is in GPTData::XFormPartitions(),
834 // but I want it here too in case I call this function in another
835 // context in the future....)
srs5694e35eb1b2009-09-14 00:29:34 -0400836 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694221e0872009-08-29 15:00:31 -0400837 (origType != 0x00) && (origType != 0xEE)) {
838 firstSector = (uint64_t) origPart->firstLBA;
839 newPart.SetFirstLBA(firstSector);
840 lastSector = firstSector + (uint64_t) origPart->lengthLBA;
841 if (lastSector > 0) lastSector--;
842 newPart.SetLastLBA(lastSector);
843 newPart.SetType(((uint16_t) origType) * 0x0100);
srs56946699b012010-02-04 00:55:30 -0500844 newPart.RandomizeUniqueGUID();
srs5694221e0872009-08-29 15:00:31 -0400845 newPart.SetAttributes(0);
srs56946699b012010-02-04 00:55:30 -0500846 newPart.SetName(newPart.GetTypeName());
srs5694978041c2009-09-21 20:51:47 -0400847 } // if not extended, protective, or non-existent
848 } // if (origPart != NULL)
srs5694221e0872009-08-29 15:00:31 -0400849 return newPart;
850} // MBRData::AsGPT()
srs5694978041c2009-09-21 20:51:47 -0400851
852/***********************
853 * *
854 * Protected functions *
855 * *
856 ***********************/
857
858// Return a pointer to a primary or logical partition, or NULL if
859// the partition is out of range....
860struct MBRRecord* MBRData::GetPartition(int i) {
861 MBRRecord* thePart = NULL;
862
863 if ((i >= 0) && (i < MAX_MBR_PARTS))
864 thePart = &partitions[i];
865 return thePart;
866} // GetPartition()