blob: 3a5aec4c47a89381d2a4610bfad034fb51426064 [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>
srs5694e7b4ff92009-08-18 13:16:10 -040013#include <stdlib.h>
14#include <stdint.h>
15#include <fcntl.h>
16#include <string.h>
17#include <time.h>
18#include <sys/stat.h>
19#include <errno.h>
srs5694fed16d02010-01-27 23:03:40 -050020#include <iostream>
srs5694e7b4ff92009-08-18 13:16:10 -040021#include "mbr.h"
srs569455d92612010-03-07 22:16:07 -050022#include "partnotes.h"
srs5694e7b4ff92009-08-18 13:16:10 -040023#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)) {
srs5694a8582cf2010-03-19 14:21:59 -040088 allOK = 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.
srs5694a8582cf2010-03-19 14:21:59 -0400104int 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
srs569455d92612010-03-07 22:16:07 -0500185 // Check to see if it's in GPT format....
srs5694546a9c72010-01-26 16:00:26 -0500186 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
srs5694a8582cf2010-03-19 14:21:59 -0400204 return allOK;
srs56940a697312010-01-28 21:10:52 -0500205} // MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize)
srs5694e7b4ff92009-08-18 13:16:10 -0400206
srs5694978041c2009-09-21 20:51:47 -0400207// This is a recursive function to read all the logical partitions, following the
208// logical partition linked list from the disk and storing the basic data in the
209// partitions[] array. Returns last index to partitions[] used, or -1 if there was
210// a problem.
211// Parameters:
srs5694978041c2009-09-21 20:51:47 -0400212// extendedStart = LBA of the start of the extended partition
213// diskOffset = LBA offset WITHIN the extended partition of the one to be read
214// partNum = location in partitions[] array to store retrieved data
srs569455d92612010-03-07 22:16:07 -0500215int MBRData::ReadLogicalPart(uint32_t extendedStart, uint32_t diskOffset, int partNum) {
srs5694978041c2009-09-21 20:51:47 -0400216 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
srs569455d92612010-03-07 22:16:07 -0500270// Write the MBR data to the default defined device. This writes both the
271// MBR itself and any defined logical partitions, provided there's an
272// MBR extended partition.
srs5694e7b4ff92009-08-18 13:16:10 -0400273int MBRData::WriteMBRData(void) {
srs56940a697312010-01-28 21:10:52 -0500274 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400275
srs56940a697312010-01-28 21:10:52 -0500276 if (myDisk != NULL) {
srs569408bb0da2010-02-19 17:19:55 -0500277 if (myDisk->OpenForWrite() != 0) {
srs56940a697312010-01-28 21:10:52 -0500278 allOK = WriteMBRData(myDisk);
279 } else {
280 allOK = 0;
281 } // if/else
282 myDisk->Close();
283 } else allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400284 return allOK;
285} // MBRData::WriteMBRData(void)
286
srs569455d92612010-03-07 22:16:07 -0500287// Save the MBR data to a file. This writes both the
288// MBR itself and any defined logical partitions, provided there's an
289// MBR extended partition.
srs5694546a9c72010-01-26 16:00:26 -0500290int MBRData::WriteMBRData(DiskIO *theDisk) {
srs569455d92612010-03-07 22:16:07 -0500291 int i, j, partNum, allOK, moreLogicals = 0;
292 uint32_t extFirstLBA = 0;
293 uint64_t writeEbrTo; // 64-bit because we support extended in 2-4TiB range
srs5694221e0872009-08-29 15:00:31 -0400294 TempMBR tempMBR;
srs56942a9f5da2009-08-26 00:48:01 -0400295
srs569455d92612010-03-07 22:16:07 -0500296 // First write the main MBR data structure....
srs5694221e0872009-08-29 15:00:31 -0400297 for (i = 0; i < 440; i++)
298 tempMBR.code[i] = code[i];
299 tempMBR.diskSignature = diskSignature;
300 tempMBR.nulls = nulls;
301 tempMBR.MBRSignature = MBRSignature;
302 for (i = 0; i < 4; i++) {
303 tempMBR.partitions[i].status = partitions[i].status;
304 tempMBR.partitions[i].partitionType = partitions[i].partitionType;
305 tempMBR.partitions[i].firstLBA = partitions[i].firstLBA;
306 tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA;
307 for (j = 0; j < 3; j++) {
308 tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j];
309 tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j];
310 } // for j...
srs569455d92612010-03-07 22:16:07 -0500311 if (partitions[i].partitionType == 0x0f) {
312 extFirstLBA = partitions[i].firstLBA;
313 moreLogicals = 1;
srs5694546a9c72010-01-26 16:00:26 -0500314 } // if
srs569455d92612010-03-07 22:16:07 -0500315 } // for i...
316 allOK = WriteMBRData(tempMBR, theDisk, 0);
srs5694221e0872009-08-29 15:00:31 -0400317
srs569455d92612010-03-07 22:16:07 -0500318 // Set up tempMBR with some constant data for logical partitions...
319 tempMBR.diskSignature = 0;
320 for (i = 2; i < 4; i++) {
321 tempMBR.partitions[i].firstLBA = tempMBR.partitions[i].lengthLBA = 0;
322 tempMBR.partitions[i].partitionType = 0x00;
323 for (j = 0; j < 3; j++) {
324 tempMBR.partitions[i].firstSector[j] = 0;
325 tempMBR.partitions[i].lastSector[j] = 0;
326 } // for j
327 } // for i
328
329 partNum = 4;
330 writeEbrTo = (uint64_t) extFirstLBA;
331 // If extended partition is present, write logicals...
332 while (allOK && moreLogicals && (partNum < MAX_MBR_PARTS)) {
333 tempMBR.partitions[0] = partitions[partNum];
334 tempMBR.partitions[0].firstLBA = 1; // partition starts on sector after EBR
335 // tempMBR.partitions[1] points to next EBR or terminates EBR linked list...
336 if ((partNum < MAX_MBR_PARTS - 1) && (partitions[partNum + 1].firstLBA > 0)) {
337 tempMBR.partitions[1].partitionType = 0x0f;
338 tempMBR.partitions[1].firstLBA = partitions[partNum + 1].firstLBA - 1;
339 tempMBR.partitions[1].lengthLBA = partitions[partNum + 1].lengthLBA + 1;
340 LBAtoCHS((uint64_t) tempMBR.partitions[1].firstLBA,
341 (uint8_t *) &tempMBR.partitions[1].firstSector);
342 LBAtoCHS(tempMBR.partitions[1].lengthLBA - extFirstLBA,
343 (uint8_t *) &tempMBR.partitions[1].lastSector);
344 } else {
345 tempMBR.partitions[1].partitionType = 0x00;
346 tempMBR.partitions[1].firstLBA = 0;
347 tempMBR.partitions[1].lengthLBA = 0;
348 moreLogicals = 0;
349 } // if/else
350 allOK = WriteMBRData(tempMBR, theDisk, writeEbrTo);
351 partNum++;
352 writeEbrTo = (uint64_t) tempMBR.partitions[1].firstLBA + (uint64_t) extFirstLBA;
353 } // while
srs5694546a9c72010-01-26 16:00:26 -0500354 return allOK;
srs56940a697312010-01-28 21:10:52 -0500355} // MBRData::WriteMBRData(DiskIO *theDisk)
srs5694e7b4ff92009-08-18 13:16:10 -0400356
srs56940a697312010-01-28 21:10:52 -0500357int MBRData::WriteMBRData(const string & deviceFilename) {
srs5694fed16d02010-01-27 23:03:40 -0500358 device = deviceFilename;
srs5694ba00fed2010-01-12 18:18:36 -0500359 return WriteMBRData();
srs56940a697312010-01-28 21:10:52 -0500360} // MBRData::WriteMBRData(const string & deviceFilename)
srs5694ba00fed2010-01-12 18:18:36 -0500361
srs569455d92612010-03-07 22:16:07 -0500362// Write a single MBR record to the specified sector. Used by the like-named
363// function to write both the MBR and multiple EBR (for logical partition)
364// records.
365// Returns 1 on success, 0 on failure
366int MBRData::WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector) {
367 int i, allOK;
368
369 // Reverse the byte order, if necessary
370 if (IsLittleEndian() == 0) {
371 ReverseBytes(&mbr.diskSignature, 4);
372 ReverseBytes(&mbr.nulls, 2);
373 ReverseBytes(&mbr.MBRSignature, 2);
374 for (i = 0; i < 4; i++) {
375 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
376 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
377 } // for
378 } // if
379
380 // Now write the data structure...
381 allOK = theDisk->OpenForWrite();
382 if (allOK && theDisk->Seek(sector)) {
383 if (theDisk->Write(&mbr, 512) != 512) {
384 allOK = 0;
385 cerr << "Error " << errno << " when saving MBR!\n";
386 } // if
387 } else {
388 allOK = 0;
389 cerr << "Error " << errno << " when seeking to MBR to write it!\n";
390 } // if/else
391 theDisk->Close();
392
393 // Reverse the byte order back, if necessary
394 if (IsLittleEndian() == 0) {
395 ReverseBytes(&mbr.diskSignature, 4);
396 ReverseBytes(&mbr.nulls, 2);
397 ReverseBytes(&mbr.MBRSignature, 2);
398 for (i = 0; i < 4; i++) {
399 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
400 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
401 } // for
402 }// if
403 return allOK;
404} // MBRData::WriteMBRData(uint64_t sector)
405
srs5694978041c2009-09-21 20:51:47 -0400406/********************************************
407 * *
408 * Functions that display data for the user *
409 * *
410 ********************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400411
srs569455d92612010-03-07 22:16:07 -0500412// Show the MBR data to the user, up to the specified maximum number
413// of partitions....
414void MBRData::DisplayMBRData(int maxParts) {
srs5694e7b4ff92009-08-18 13:16:10 -0400415 int i;
srs5694e19ba092009-08-24 14:10:35 -0400416 char bootCode;
srs5694e7b4ff92009-08-18 13:16:10 -0400417
srs569455d92612010-03-07 22:16:07 -0500418 if (maxParts > MAX_MBR_PARTS)
419 maxParts = MAX_MBR_PARTS;
srs5694fed16d02010-01-27 23:03:40 -0500420 cout << "MBR disk identifier: 0x";
421 cout.width(8);
422 cout.fill('0');
423 cout.setf(ios::uppercase);
424 cout << hex << diskSignature << dec << "\n";
425 cout << "MBR partitions:\n";
426 cout << "Number\t Boot\t Start (sector)\t Length (sectors)\tType\n";
srs569455d92612010-03-07 22:16:07 -0500427 for (i = 0; i < maxParts; i++) {
srs5694e7b4ff92009-08-18 13:16:10 -0400428 if (partitions[i].lengthLBA != 0) {
srs5694e19ba092009-08-24 14:10:35 -0400429 if (partitions[i].status && 0x80) // it's bootable
srs5694978041c2009-09-21 20:51:47 -0400430 bootCode = '*';
srs5694e19ba092009-08-24 14:10:35 -0400431 else
432 bootCode = ' ';
srs5694fed16d02010-01-27 23:03:40 -0500433 cout.fill(' ');
434 cout.width(4);
435 cout << i + 1 << "\t " << bootCode << "\t";
436 cout.width(13);
437 cout << partitions[i].firstLBA << "\t";
438 cout.width(15);
439 cout << partitions[i].lengthLBA << " \t0x";
440 cout.width(2);
441 cout.fill('0');
442 cout << hex << (int) partitions[i].partitionType << dec << "\n";
srs5694e7b4ff92009-08-18 13:16:10 -0400443 } // if
srs5694fed16d02010-01-27 23:03:40 -0500444 cout.fill(' ');
srs5694e7b4ff92009-08-18 13:16:10 -0400445 } // for
srs5694fed16d02010-01-27 23:03:40 -0500446 cout << "\nDisk size is " << diskSize << " sectors ("
srs5694e321d442010-01-29 17:44:04 -0500447 << BytesToSI(diskSize * (uint64_t) blockSize) << ")\n";
srs5694e7b4ff92009-08-18 13:16:10 -0400448} // MBRData::DisplayMBRData()
449
srs5694978041c2009-09-21 20:51:47 -0400450// Displays the state, as a word, on stdout. Used for debugging & to
451// tell the user about the MBR state when the program launches....
452void MBRData::ShowState(void) {
453 switch (state) {
454 case invalid:
srs5694fed16d02010-01-27 23:03:40 -0500455 cout << " MBR: not present\n";
srs5694978041c2009-09-21 20:51:47 -0400456 break;
457 case gpt:
srs5694fed16d02010-01-27 23:03:40 -0500458 cout << " MBR: protective\n";
srs5694978041c2009-09-21 20:51:47 -0400459 break;
460 case hybrid:
srs5694fed16d02010-01-27 23:03:40 -0500461 cout << " MBR: hybrid\n";
srs5694978041c2009-09-21 20:51:47 -0400462 break;
463 case mbr:
srs5694fed16d02010-01-27 23:03:40 -0500464 cout << " MBR: MBR only\n";
srs5694978041c2009-09-21 20:51:47 -0400465 break;
466 default:
srs5694fed16d02010-01-27 23:03:40 -0500467 cout << "\a MBR: unknown -- bug!\n";
srs5694978041c2009-09-21 20:51:47 -0400468 break;
469 } // switch
470} // MBRData::ShowState()
471
472/*********************************************************************
473 * *
474 * Functions that set or get disk metadata (CHS geometry, disk size, *
475 * etc.) *
476 * *
477 *********************************************************************/
478
479// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function.
480// Note that this only sets the heads and sectors; the number of
481// cylinders is determined by these values and the disk size.
482void MBRData::SetCHSGeom(uint32_t h, uint32_t s) {
483 if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
484 numHeads = h;
485 numSecspTrack = s;
486 } else {
srs5694fed16d02010-01-27 23:03:40 -0500487 cout << "Warning! Attempt to set invalid CHS geometry!\n";
srs5694978041c2009-09-21 20:51:47 -0400488 } // if/else
489} // MBRData::SetCHSGeom()
490
491// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
492// was within the range that can be expressed by CHS (including 0, for an
493// empty partition), 0 if the value is outside that range, and -1 if chs is
494// invalid.
495int MBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
496 uint64_t cylinder, head, sector; // all numbered from 0
497 uint64_t remainder;
498 int retval = 1;
499 int done = 0;
500
501 if (chs != NULL) {
502 // Special case: In case of 0 LBA value, zero out CHS values....
503 if (lba == 0) {
504 chs[0] = chs[1] = chs[2] = UINT8_C(0);
505 done = 1;
506 } // if
507 // If LBA value is too large for CHS, max out CHS values....
508 if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
509 chs[0] = 254;
510 chs[1] = chs[2] = 255;
511 done = 1;
512 retval = 0;
513 } // if
514 // If neither of the above applies, compute CHS values....
515 if (!done) {
516 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
517 remainder = lba - (cylinder * numHeads * numSecspTrack);
518 head = remainder / numSecspTrack;
519 remainder -= head * numSecspTrack;
520 sector = remainder;
521 if (head < numHeads)
srs569455d92612010-03-07 22:16:07 -0500522 chs[0] = (uint8_t) head;
srs5694978041c2009-09-21 20:51:47 -0400523 else
524 retval = 0;
525 if (sector < numSecspTrack) {
526 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
527 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
528 } else {
529 retval = 0;
530 } // if/else
531 } // if value is expressible and non-0
532 } else { // Invalid (NULL) chs pointer
533 retval = -1;
534 } // if CHS pointer valid
535 return (retval);
536} // MBRData::LBAtoCHS()
537
538/*****************************************************
539 * *
540 * Functions to create, delete, or change partitions *
541 * *
542 *****************************************************/
543
544// Empty all data. Meant mainly for calling by constructors, but it's also
545// used by the hybrid MBR functions in the GPTData class.
546void MBRData::EmptyMBR(int clearBootloader) {
547 int i;
548
549 // Zero out the boot loader section, the disk signature, and the
550 // 2-byte nulls area only if requested to do so. (This is the
551 // default.)
552 if (clearBootloader == 1) {
srs569408bb0da2010-02-19 17:19:55 -0500553 EmptyBootloader();
srs5694978041c2009-09-21 20:51:47 -0400554 } // if
555
556 // Blank out the partitions
557 for (i = 0; i < MAX_MBR_PARTS; i++) {
558 partitions[i].status = UINT8_C(0);
559 partitions[i].firstSector[0] = UINT8_C(0);
560 partitions[i].firstSector[1] = UINT8_C(0);
561 partitions[i].firstSector[2] = UINT8_C(0);
562 partitions[i].partitionType = UINT8_C(0);
563 partitions[i].lastSector[0] = UINT8_C(0);
564 partitions[i].lastSector[1] = UINT8_C(0);
565 partitions[i].lastSector[2] = UINT8_C(0);
566 partitions[i].firstLBA = UINT32_C(0);
567 partitions[i].lengthLBA = UINT32_C(0);
568 } // for
569 MBRSignature = MBR_SIGNATURE;
570} // MBRData::EmptyMBR()
571
srs569408bb0da2010-02-19 17:19:55 -0500572// Blank out the boot loader area. Done with the initial MBR-to-GPT
573// conversion, since MBR boot loaders don't understand GPT, and so
574// need to be replaced....
575void MBRData::EmptyBootloader(void) {
576 int i;
577
578 for (i = 0; i < 440; i++)
579 code[i] = 0;
580 nulls = 0;
581} // MBRData::EmptyBootloader
582
srs5694221e0872009-08-29 15:00:31 -0400583// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
584void MBRData::MakeProtectiveMBR(int clearBoot) {
srs5694978041c2009-09-21 20:51:47 -0400585
586 EmptyMBR(clearBoot);
srs5694e7b4ff92009-08-18 13:16:10 -0400587
588 // Initialize variables
589 nulls = 0;
590 MBRSignature = MBR_SIGNATURE;
srs56948a4ddfc2010-03-21 19:05:49 -0400591 diskSignature = UINT32_C(0);
srs5694e7b4ff92009-08-18 13:16:10 -0400592
593 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
594
srs5694e7b4ff92009-08-18 13:16:10 -0400595 partitions[0].partitionType = UINT8_C(0xEE);
596 partitions[0].firstLBA = UINT32_C(1);
597 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
srs5694978041c2009-09-21 20:51:47 -0400598 partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
srs5694e7b4ff92009-08-18 13:16:10 -0400599 } else { // disk is too big to represent, so fake it...
600 partitions[0].lengthLBA = UINT32_MAX;
601 } // if/else
602
srs569455d92612010-03-07 22:16:07 -0500603 // Write CHS data. This maxes out the use of the disk, as much as
604 // possible -- even to the point of exceeding the capacity of sub-8GB
605 // disks. The EFI spec says to use 0xffffff as the ending value,
606 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
607 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
608 // value in for the FIRST sector, too!
609 LBAtoCHS(1, partitions[0].firstSector);
610 if (LBAtoCHS(partitions[0].lengthLBA, partitions[0].lastSector) == 0)
611 partitions[0].lastSector[0] = 0xFF;
612
srs5694e7b4ff92009-08-18 13:16:10 -0400613 state = gpt;
614} // MBRData::MakeProtectiveMBR()
615
srs5694978041c2009-09-21 20:51:47 -0400616// Create a partition of the specified number, starting LBA, and
617// length. This function does *NO* error checking, so it's possible
618// to seriously screw up a partition table using this function!
619// Note: This function should NOT be used to create the 0xEE partition
620// in a conventional GPT configuration, since that partition has
621// specific size requirements that this function won't handle. It may
622// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
623// since those toss the rulebook away anyhow....
srs569455d92612010-03-07 22:16:07 -0500624void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type, int bootable) {
srs5694978041c2009-09-21 20:51:47 -0400625 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs5694978041c2009-09-21 20:51:47 -0400626 partitions[num].firstSector[0] = UINT8_C(0);
627 partitions[num].firstSector[1] = UINT8_C(0);
628 partitions[num].firstSector[2] = UINT8_C(0);
629 partitions[num].partitionType = (uint8_t) type;
630 partitions[num].lastSector[0] = UINT8_C(0);
631 partitions[num].lastSector[1] = UINT8_C(0);
632 partitions[num].lastSector[2] = UINT8_C(0);
633 partitions[num].firstLBA = start;
634 partitions[num].lengthLBA = length;
635 // If this is a "real" partition, set its CHS geometry
636 if (length > 0) {
637 LBAtoCHS((uint64_t) start, partitions[num].firstSector);
638 LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
639 } // if (length > 0)
srs569408bb0da2010-02-19 17:19:55 -0500640 SetPartBootable(num, bootable);
srs5694978041c2009-09-21 20:51:47 -0400641 } // if valid partition number
642} // MBRData::MakePart()
643
srs569408bb0da2010-02-19 17:19:55 -0500644// Set the partition's type code.
645// Returns 1 if successful, 0 if not (invalid partition number)
646int MBRData::SetPartType(int num, int type) {
647 int allOK = 1;
648
649 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
650 if (partitions[num].lengthLBA != UINT32_C(0)) {
651 partitions[num].partitionType = (uint8_t) type;
652 } else allOK = 0;
653 } else allOK = 0;
654 return allOK;
655} // MBRData::SetPartType()
656
657// Set (or remove) the partition's bootable flag. Setting it is the
658// default; pass 0 as bootable to remove the flag.
659// Returns 1 if successful, 0 if not (invalid partition number)
660int MBRData::SetPartBootable(int num, int bootable) {
661 int allOK = 1;
662
663 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
664 if (partitions[num].lengthLBA != UINT32_C(0)) {
665 if (bootable == 0)
666 partitions[num].status = UINT8_C(0);
667 else
668 partitions[num].status = UINT8_C(0x80);
669 } else allOK = 0;
670 } else allOK = 0;
671 return allOK;
672} // MBRData::SetPartBootable()
673
srs5694c0ca8f82009-08-20 21:35:25 -0400674// Create a partition that fills the most available space. Returns
675// 1 if partition was created, 0 otherwise. Intended for use in
676// creating hybrid MBRs.
677int MBRData::MakeBiggestPart(int i, int type) {
678 uint32_t start = UINT32_C(1); // starting point for each search
679 uint32_t firstBlock; // first block in a segment
680 uint32_t lastBlock; // last block in a segment
681 uint32_t segmentSize; // size of segment in blocks
682 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
683 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
684 int found = 0;
685
686 do {
687 firstBlock = FindFirstAvailable(start);
688 if (firstBlock != UINT32_C(0)) { // something's free...
689 lastBlock = FindLastInFree(firstBlock);
690 segmentSize = lastBlock - firstBlock + UINT32_C(1);
srs5694978041c2009-09-21 20:51:47 -0400691 if (segmentSize > selectedSize) {
srs5694c0ca8f82009-08-20 21:35:25 -0400692 selectedSize = segmentSize;
srs5694978041c2009-09-21 20:51:47 -0400693 selectedSegment = firstBlock;
694 } // if
695 start = lastBlock + 1;
srs5694c0ca8f82009-08-20 21:35:25 -0400696 } // if
697 } while (firstBlock != 0);
698 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
699 found = 1;
700 MakePart(i, selectedSegment, selectedSize, type, 0);
701 } else {
702 found = 0;
703 } // if/else
704 return found;
705} // MBRData::MakeBiggestPart(int i)
706
srs5694e35eb1b2009-09-14 00:29:34 -0400707// Delete partition #i
708void MBRData::DeletePartition(int i) {
709 int j;
710
711 partitions[i].firstLBA = UINT32_C(0);
712 partitions[i].lengthLBA = UINT32_C(0);
713 partitions[i].status = UINT8_C(0);
714 partitions[i].partitionType = UINT8_C(0);
715 for (j = 0; j < 3; j++) {
716 partitions[i].firstSector[j] = UINT8_C(0);
717 partitions[i].lastSector[j] = UINT8_C(0);
718 } // for j (CHS data blanking)
719} // MBRData::DeletePartition()
720
srs5694e4ac11e2009-08-31 10:13:04 -0400721// Delete a partition if one exists at the specified location.
722// Returns 1 if a partition was deleted, 0 otherwise....
723// Used to help keep GPT & hybrid MBR partitions in sync....
724int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
725 uint32_t start32, length32;
srs5694978041c2009-09-21 20:51:47 -0400726 int i, deleted = 0;
srs5694e4ac11e2009-08-31 10:13:04 -0400727
srs5694978041c2009-09-21 20:51:47 -0400728 if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400729 start32 = (uint32_t) start64;
730 length32 = (uint32_t) length64;
srs5694978041c2009-09-21 20:51:47 -0400731 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs569408bb0da2010-02-19 17:19:55 -0500732 if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA == length32) &&
srs5694546a9c72010-01-26 16:00:26 -0500733 (partitions[i].partitionType != 0xEE)) {
srs5694e35eb1b2009-09-14 00:29:34 -0400734 DeletePartition(i);
srs5694978041c2009-09-21 20:51:47 -0400735 if (state == hybrid)
736 OptimizeEESize();
srs5694e4ac11e2009-08-31 10:13:04 -0400737 deleted = 1;
srs5694546a9c72010-01-26 16:00:26 -0500738 } // if (match found)
srs5694e4ac11e2009-08-31 10:13:04 -0400739 } // for i (partition scan)
740 } // if (hybrid & GPT partition < 2TiB)
741 return deleted;
742} // MBRData::DeleteByLocation()
743
744// Optimizes the size of the 0xEE (EFI GPT) partition
745void MBRData::OptimizeEESize(void) {
746 int i, typeFlag = 0;
747 uint32_t after;
748
749 for (i = 0; i < 4; i++) {
750 // Check for non-empty and non-0xEE partitions
751 if ((partitions[i].partitionType != 0xEE) && (partitions[i].partitionType != 0x00))
752 typeFlag++;
753 if (partitions[i].partitionType == 0xEE) {
754 // Blank space before this partition; fill it....
755 if (IsFree(partitions[i].firstLBA - 1)) {
756 partitions[i].firstLBA = FindFirstInFree(partitions[i].firstLBA - 1);
757 } // if
758 // Blank space after this partition; fill it....
759 after = partitions[i].firstLBA + partitions[i].lengthLBA;
760 if (IsFree(after)) {
761 partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
762 } // if free space after
763 } // if partition is 0xEE
srs5694e4ac11e2009-08-31 10:13:04 -0400764 } // for partition loop
srs5694978041c2009-09-21 20:51:47 -0400765 if (typeFlag == 0) { // No non-hybrid partitions found
766 MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
767 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400768} // MBRData::OptimizeEESize()
769
srs56949ba54212010-05-18 23:24:02 -0400770// Recomputes the CHS values for the specified partition and adjusts the value.
771// Note that this will create a technically incorrect CHS value for EFI GPT (0xEE)
772// protective partitions, but this is required by some buggy BIOSes, so I'm
773// providing a function to do this deliberately at the user's command.
774// This function does nothing if the partition's length is 0.
775void MBRData::RecomputeCHS(int partNum) {
776 uint64_t firstLBA, lengthLBA;
777
778 firstLBA = (uint64_t) partitions[partNum].firstLBA;
779 lengthLBA = (uint64_t) partitions[partNum].lengthLBA;
780
781 if (lengthLBA > 0) {
782 LBAtoCHS(firstLBA, partitions[partNum].firstSector);
783 LBAtoCHS(firstLBA + lengthLBA - 1, partitions[partNum].lastSector);
784 } // if
785} // MBRData::RecomputeCHS()
786
srs569455d92612010-03-07 22:16:07 -0500787// Creates an MBR extended partition holding logical partitions that
788// correspond to the list of GPT partitions in theList. The extended
789// partition is placed in position #4 (counting from 1) in the MBR.
790// The logical partition data are copied to the partitions[] array in
791// positions 4 and up (counting from 0). Neither the MBR nor the EBR
792// entries are written to disk; that is left for the WriteMBRData()
793// function.
794// Returns number of converted partitions
795int MBRData::CreateLogicals(PartNotes& notes) {
796 uint64_t extEndLBA = 0, extStartLBA = UINT64_MAX;
797 int i = 4, numLogicals = 0;
798 struct PartInfo aPart;
799
800 // Find bounds of the extended partition....
801 notes.Rewind();
802 while (notes.GetNextInfo(&aPart) >= 0) {
803 if (aPart.type == LOGICAL) {
804 if (extStartLBA > aPart.firstLBA)
805 extStartLBA = aPart.firstLBA;
806 if (extEndLBA < aPart.lastLBA)
807 extEndLBA = aPart.lastLBA;
808 numLogicals++;
809 } // if
810 } // while
811 extStartLBA--;
812
813 if ((extStartLBA < UINT32_MAX) && ((extEndLBA - extStartLBA + 1) < UINT32_MAX)) {
814 notes.Rewind();
815 i = 4;
816 while ((notes.GetNextInfo(&aPart) >= 0) && (i < MAX_MBR_PARTS)) {
817 if (aPart.type == LOGICAL) {
818 partitions[i].partitionType = aPart.hexCode;
819 partitions[i].firstLBA = (uint32_t) (aPart.firstLBA - extStartLBA);
820 partitions[i].lengthLBA = (uint32_t) (aPart.lastLBA - aPart.firstLBA + 1);
821 LBAtoCHS(UINT64_C(1), (uint8_t *) &partitions[i].firstSector);
822 LBAtoCHS(partitions[i].lengthLBA, (uint8_t *) &partitions[i].lastSector);
823 partitions[i].status = aPart.active * 0x80;
824 i++;
825 } // if
826 } // while
827 MakePart(3, (uint32_t) extStartLBA, (uint32_t) (extEndLBA - extStartLBA + 1), 0x0f, 0);
828 } else {
829 if (numLogicals > 0) {
830 cerr << "Unable to create logical partitions; they exceed the 2 TiB limit!\n";
831// cout << "extStartLBA = " << extStartLBA << ", extEndLBA = " << extEndLBA << "\n";
832 }
833 } // if/else
834 return (i - 4);
835} // MBRData::CreateLogicals()
836
srs5694978041c2009-09-21 20:51:47 -0400837/****************************************
838 * *
839 * Functions to find data on free space *
840 * *
841 ****************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400842
srs5694c0ca8f82009-08-20 21:35:25 -0400843// Finds the first free space on the disk from start onward; returns 0
844// if none available....
845uint32_t MBRData::FindFirstAvailable(uint32_t start) {
846 uint32_t first;
847 uint32_t i;
srs5694978041c2009-09-21 20:51:47 -0400848 int firstMoved;
srs5694c0ca8f82009-08-20 21:35:25 -0400849
850 first = start;
851
852 // ...now search through all partitions; if first is within an
853 // existing partition, move it to the next sector after that
854 // partition and repeat. If first was moved, set firstMoved
855 // flag; repeat until firstMoved is not set, so as to catch
856 // cases where partitions are out of sequential order....
857 do {
858 firstMoved = 0;
859 for (i = 0; i < 4; i++) {
860 // Check if it's in the existing partition
861 if ((first >= partitions[i].firstLBA) &&
862 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
863 first = partitions[i].firstLBA + partitions[i].lengthLBA;
864 firstMoved = 1;
srs5694978041c2009-09-21 20:51:47 -0400865 } // if
srs5694c0ca8f82009-08-20 21:35:25 -0400866 } // for
867 } while (firstMoved == 1);
868 if (first >= diskSize)
869 first = 0;
870 return (first);
871} // MBRData::FindFirstAvailable()
872
srs5694e4ac11e2009-08-31 10:13:04 -0400873// Finds the last free sector on the disk from start forward.
srs5694c0ca8f82009-08-20 21:35:25 -0400874uint32_t MBRData::FindLastInFree(uint32_t start) {
875 uint32_t nearestStart;
876 uint32_t i;
877
srs5694978041c2009-09-21 20:51:47 -0400878 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs569408bb0da2010-02-19 17:19:55 -0500879 nearestStart = (uint32_t) diskSize - 1;
srs5694c0ca8f82009-08-20 21:35:25 -0400880 else
881 nearestStart = UINT32_MAX - 1;
882 for (i = 0; i < 4; i++) {
883 if ((nearestStart > partitions[i].firstLBA) &&
884 (partitions[i].firstLBA > start)) {
885 nearestStart = partitions[i].firstLBA - 1;
886 } // if
887 } // for
888 return (nearestStart);
srs5694e4ac11e2009-08-31 10:13:04 -0400889} // MBRData::FindLastInFree()
890
891// Finds the first free sector on the disk from start backward.
892uint32_t MBRData::FindFirstInFree(uint32_t start) {
srs5694e35eb1b2009-09-14 00:29:34 -0400893 uint32_t bestLastLBA, thisLastLBA;
srs5694e4ac11e2009-08-31 10:13:04 -0400894 int i;
895
896 bestLastLBA = 1;
897 for (i = 0; i < 4; i++) {
898 thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
srs569455d92612010-03-07 22:16:07 -0500899 if (thisLastLBA > 0)
900 thisLastLBA--;
901 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start))
srs5694e4ac11e2009-08-31 10:13:04 -0400902 bestLastLBA = thisLastLBA + 1;
srs5694e4ac11e2009-08-31 10:13:04 -0400903 } // for
904 return (bestLastLBA);
905} // MBRData::FindFirstInFree()
906
907// Returns 1 if the specified sector is unallocated, 0 if it's
908// allocated.
909int MBRData::IsFree(uint32_t sector) {
910 int i, isFree = 1;
911 uint32_t first, last;
912
913 for (i = 0; i < 4; i++) {
914 first = partitions[i].firstLBA;
srs5694978041c2009-09-21 20:51:47 -0400915 // Note: Weird two-line thing to avoid subtracting 1 from a 0 value
916 // for an unsigned int....
srs5694e4ac11e2009-08-31 10:13:04 -0400917 last = first + partitions[i].lengthLBA;
srs569455d92612010-03-07 22:16:07 -0500918 if (last > 0)
919 last--;
srs5694e4ac11e2009-08-31 10:13:04 -0400920 if ((first <= sector) && (last >= sector))
921 isFree = 0;
922 } // for
923 return isFree;
924} // MBRData::IsFree()
srs5694c0ca8f82009-08-20 21:35:25 -0400925
srs5694978041c2009-09-21 20:51:47 -0400926/******************************************************
927 * *
928 * Functions that extract data on specific partitions *
929 * *
930 ******************************************************/
931
srs5694e7b4ff92009-08-18 13:16:10 -0400932uint8_t MBRData::GetStatus(int i) {
933 MBRRecord* thePart;
934 uint8_t retval;
935
936 thePart = GetPartition(i);
937 if (thePart != NULL)
938 retval = thePart->status;
939 else
940 retval = UINT8_C(0);
941 return retval;
942} // MBRData::GetStatus()
943
944uint8_t MBRData::GetType(int i) {
945 MBRRecord* thePart;
946 uint8_t retval;
947
948 thePart = GetPartition(i);
949 if (thePart != NULL)
950 retval = thePart->partitionType;
951 else
952 retval = UINT8_C(0);
953 return retval;
954} // MBRData::GetType()
955
956uint32_t MBRData::GetFirstSector(int i) {
957 MBRRecord* thePart;
958 uint32_t retval;
959
960 thePart = GetPartition(i);
961 if (thePart != NULL) {
962 retval = thePart->firstLBA;
963 } else
964 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400965 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400966} // MBRData::GetFirstSector()
967
968uint32_t MBRData::GetLength(int i) {
969 MBRRecord* thePart;
970 uint32_t retval;
971
972 thePart = GetPartition(i);
973 if (thePart != NULL) {
974 retval = thePart->lengthLBA;
975 } else
976 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400977 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400978} // MBRData::GetLength()
srs5694221e0872009-08-29 15:00:31 -0400979
980// Return the MBR data as a GPT partition....
981GPTPart MBRData::AsGPT(int i) {
982 MBRRecord* origPart;
983 GPTPart newPart;
984 uint8_t origType;
985 uint64_t firstSector, lastSector;
srs5694221e0872009-08-29 15:00:31 -0400986
987 newPart.BlankPartition();
988 origPart = GetPartition(i);
989 if (origPart != NULL) {
990 origType = origPart->partitionType;
991
992 // don't convert extended, hybrid protective, or null (non-existent)
993 // partitions (Note similar protection is in GPTData::XFormPartitions(),
994 // but I want it here too in case I call this function in another
995 // context in the future....)
srs5694e35eb1b2009-09-14 00:29:34 -0400996 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694221e0872009-08-29 15:00:31 -0400997 (origType != 0x00) && (origType != 0xEE)) {
998 firstSector = (uint64_t) origPart->firstLBA;
999 newPart.SetFirstLBA(firstSector);
1000 lastSector = firstSector + (uint64_t) origPart->lengthLBA;
1001 if (lastSector > 0) lastSector--;
1002 newPart.SetLastLBA(lastSector);
1003 newPart.SetType(((uint16_t) origType) * 0x0100);
srs56946699b012010-02-04 00:55:30 -05001004 newPart.RandomizeUniqueGUID();
srs5694221e0872009-08-29 15:00:31 -04001005 newPart.SetAttributes(0);
srs56946699b012010-02-04 00:55:30 -05001006 newPart.SetName(newPart.GetTypeName());
srs5694978041c2009-09-21 20:51:47 -04001007 } // if not extended, protective, or non-existent
1008 } // if (origPart != NULL)
srs5694221e0872009-08-29 15:00:31 -04001009 return newPart;
1010} // MBRData::AsGPT()
srs5694978041c2009-09-21 20:51:47 -04001011
1012/***********************
1013 * *
1014 * Protected functions *
1015 * *
1016 ***********************/
1017
1018// Return a pointer to a primary or logical partition, or NULL if
1019// the partition is out of range....
1020struct MBRRecord* MBRData::GetPartition(int i) {
1021 MBRRecord* thePart = NULL;
1022
1023 if ((i >= 0) && (i < MAX_MBR_PARTS))
1024 thePart = &partitions[i];
1025 return thePart;
1026} // GetPartition()