blob: 411cae542eeb67c062268d20d94bacf1301d26a3 [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;
srs5694e7b4ff92009-08-18 13:16:10 -040041 EmptyMBR();
42} // MBRData default constructor
43
srs5694fed16d02010-01-27 23:03:40 -050044MBRData::MBRData(string filename) {
srs5694e7b4ff92009-08-18 13:16:10 -040045 blockSize = SECTOR_SIZE;
46 diskSize = 0;
srs5694fed16d02010-01-27 23:03:40 -050047 device = filename;
srs5694e7b4ff92009-08-18 13:16:10 -040048 state = invalid;
srs5694978041c2009-09-21 20:51:47 -040049 numHeads = MAX_HEADS;
50 numSecspTrack = MAX_SECSPERTRACK;
srs5694e7b4ff92009-08-18 13:16:10 -040051
52 srand((unsigned int) time(NULL));
53 // Try to read the specified partition table, but if it fails....
54 if (!ReadMBRData(filename)) {
55 EmptyMBR();
srs5694fed16d02010-01-27 23:03:40 -050056 device = "";
srs5694e7b4ff92009-08-18 13:16:10 -040057 } // if
58} // MBRData(char *filename) constructor
59
60MBRData::~MBRData(void) {
61} // MBRData destructor
62
srs5694978041c2009-09-21 20:51:47 -040063/**********************
64 * *
65 * Disk I/O functions *
66 * *
67 **********************/
srs5694e7b4ff92009-08-18 13:16:10 -040068
69// Read data from MBR. Returns 1 if read was successful (even if the
70// data isn't a valid MBR), 0 if the read failed.
srs5694fed16d02010-01-27 23:03:40 -050071int MBRData::ReadMBRData(string deviceFilename) {
srs5694e7b4ff92009-08-18 13:16:10 -040072 int fd, allOK = 1;
73
srs5694546a9c72010-01-26 16:00:26 -050074 if (myDisk->OpenForRead(deviceFilename)) {
75 ReadMBRData(myDisk);
srs5694e7b4ff92009-08-18 13:16:10 -040076 } else {
77 allOK = 0;
78 } // if
79
srs5694e7b4ff92009-08-18 13:16:10 -040080 if (allOK)
srs5694fed16d02010-01-27 23:03:40 -050081 device = deviceFilename;
srs5694e7b4ff92009-08-18 13:16:10 -040082
83 return allOK;
84} // MBRData::ReadMBRData(char* deviceFilename)
85
srs5694978041c2009-09-21 20:51:47 -040086// Read data from MBR. If checkBlockSize == 1 (the default), the block
87// size is checked; otherwise it's set to the default (512 bytes).
88// Note that any extended partition(s) present will be explicitly stored
89// in the partitions[] array, along with their contained partitions; the
90// extended container partition(s) should be ignored by other functions.
srs5694546a9c72010-01-26 16:00:26 -050091void MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) {
srs5694978041c2009-09-21 20:51:47 -040092 int allOK = 1, i, j, logicalNum;
srs5694546a9c72010-01-26 16:00:26 -050093 int err = 1;
srs5694221e0872009-08-29 15:00:31 -040094 TempMBR tempMBR;
srs5694e7b4ff92009-08-18 13:16:10 -040095
srs5694546a9c72010-01-26 16:00:26 -050096 myDisk = theDisk;
97
srs5694978041c2009-09-21 20:51:47 -040098 // Empty existing MBR data, including the logical partitions...
99 EmptyMBR(0);
srs5694e7b4ff92009-08-18 13:16:10 -0400100
srs5694546a9c72010-01-26 16:00:26 -0500101 if (myDisk->Seek(0))
102 if (myDisk->Read(&tempMBR, 512))
103 err = 0;
104 if (err) {
srs5694fed16d02010-01-27 23:03:40 -0500105 cerr << "Problem reading disk in MBRData::ReadMBRData!\n";
srs5694546a9c72010-01-26 16:00:26 -0500106 } else {
107 for (i = 0; i < 440; i++)
108 code[i] = tempMBR.code[i];
109 diskSignature = tempMBR.diskSignature;
110 nulls = tempMBR.nulls;
srs56942a9f5da2009-08-26 00:48:01 -0400111 for (i = 0; i < 4; i++) {
srs5694546a9c72010-01-26 16:00:26 -0500112 partitions[i].status = tempMBR.partitions[i].status;
113 partitions[i].partitionType = tempMBR.partitions[i].partitionType;
114 partitions[i].firstLBA = tempMBR.partitions[i].firstLBA;
115 partitions[i].lengthLBA = tempMBR.partitions[i].lengthLBA;
116 for (j = 0; j < 3; j++) {
117 partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j];
118 partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j];
119 } // for j... (reading parts of CHS geometry)
120 } // for i... (reading all four partitions)
121 MBRSignature = tempMBR.MBRSignature;
srs56942a9f5da2009-08-26 00:48:01 -0400122
srs5694546a9c72010-01-26 16:00:26 -0500123 // Reverse the byte order, if necessary
124 if (IsLittleEndian() == 0) {
125 ReverseBytes(&diskSignature, 4);
126 ReverseBytes(&nulls, 2);
127 ReverseBytes(&MBRSignature, 2);
128 for (i = 0; i < 4; i++) {
129 ReverseBytes(&partitions[i].firstLBA, 4);
130 ReverseBytes(&partitions[i].lengthLBA, 4);
131 } // for
132 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400133
srs5694546a9c72010-01-26 16:00:26 -0500134 if (MBRSignature != MBR_SIGNATURE) {
135 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400136 state = invalid;
137 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400138
srs5694546a9c72010-01-26 16:00:26 -0500139 // Find disk size
140 diskSize = myDisk->DiskSize(&err);
141
142 // Find block size
143 if (checkBlockSize) {
144 blockSize = myDisk->GetBlockSize();
145 } // if (checkBlockSize)
146
147 // Load logical partition data, if any is found....
148 if (allOK) {
149 for (i = 0; i < 4; i++) {
150 if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
151 || (partitions[i].partitionType == 0x85)) {
152 // Found it, so call a recursive algorithm to load everything from them....
153 logicalNum = ReadLogicalPart(partitions[i].firstLBA, UINT32_C(0), 4);
154 if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
155 allOK = 0;
srs5694fed16d02010-01-27 23:03:40 -0500156 cerr << "Error reading logical partitions! List may be truncated!\n";
srs5694546a9c72010-01-26 16:00:26 -0500157 } // if maxLogicals valid
158 } // if primary partition is extended
159 } // for primary partition loop
160 if (allOK) { // Loaded logicals OK
161 state = mbr;
162 } else {
163 state = invalid;
srs56942a9f5da2009-08-26 00:48:01 -0400164 } // if
srs5694546a9c72010-01-26 16:00:26 -0500165 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400166
srs5694546a9c72010-01-26 16:00:26 -0500167 /* Check to see if it's in GPT format.... */
168 if (allOK) {
169 for (i = 0; i < 4; i++) {
170 if (partitions[i].partitionType == UINT8_C(0xEE)) {
171 state = gpt;
172 } // if
173 } // for
174 } // if
175
176 // If there's an EFI GPT partition, look for other partition types,
177 // to flag as hybrid
178 if (state == gpt) {
179 for (i = 0 ; i < 4; i++) {
180 if ((partitions[i].partitionType != UINT8_C(0xEE)) &&
181 (partitions[i].partitionType != UINT8_C(0x00)))
182 state = hybrid;
183 } // for
184 } // if (hybrid detection code)
185 } // no initial error
srs5694e7b4ff92009-08-18 13:16:10 -0400186} // MBRData::ReadMBRData(int fd)
187
srs5694978041c2009-09-21 20:51:47 -0400188// This is a recursive function to read all the logical partitions, following the
189// logical partition linked list from the disk and storing the basic data in the
190// partitions[] array. Returns last index to partitions[] used, or -1 if there was
191// a problem.
192// Parameters:
srs5694978041c2009-09-21 20:51:47 -0400193// extendedStart = LBA of the start of the extended partition
194// diskOffset = LBA offset WITHIN the extended partition of the one to be read
195// partNum = location in partitions[] array to store retrieved data
srs5694546a9c72010-01-26 16:00:26 -0500196int MBRData::ReadLogicalPart(uint32_t extendedStart,
srs5694978041c2009-09-21 20:51:47 -0400197 uint32_t diskOffset, int partNum) {
198 struct TempMBR ebr;
srs5694546a9c72010-01-26 16:00:26 -0500199 uint64_t offset;
srs5694978041c2009-09-21 20:51:47 -0400200
201 // Check for a valid partition number. Note that partitions MAY be read into
202 // the area normally used by primary partitions, although the only calling
203 // function as of GPT fdisk version 0.5.0 doesn't do so.
204 if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
srs5694546a9c72010-01-26 16:00:26 -0500205 offset = (uint64_t) (extendedStart + diskOffset);
srs5694546a9c72010-01-26 16:00:26 -0500206 if (myDisk->Seek(offset) == 0) { // seek to EBR record
srs5694fed16d02010-01-27 23:03:40 -0500207 cerr << "Unable to seek to " << offset << "! Aborting!\n";
srs5694978041c2009-09-21 20:51:47 -0400208 partNum = -1;
209 }
srs5694546a9c72010-01-26 16:00:26 -0500210 if (myDisk->Read(&ebr, 512) != 512) { // Load the data....
srs5694fed16d02010-01-27 23:03:40 -0500211 cerr << "Error seeking to or reading logical partition data from " << offset
212 << "!\nAborting!\n";
srs5694978041c2009-09-21 20:51:47 -0400213 partNum = -1;
214 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
215 ReverseBytes(&ebr.MBRSignature, 2);
216 ReverseBytes(&ebr.partitions[0].firstLBA, 4);
217 ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
218 ReverseBytes(&ebr.partitions[1].firstLBA, 4);
219 ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
220 } // if/else/if
221
222 if (ebr.MBRSignature != MBR_SIGNATURE) {
223 partNum = -1;
srs5694fed16d02010-01-27 23:03:40 -0500224 cerr << "MBR signature in logical partition invalid; read 0x";
225 cerr.fill('0');
226 cerr.width(4);
227 cerr.setf(ios::uppercase);
228 cerr << hex << ebr.MBRSignature << ", but should be 0x";
229 cerr.width(4);
230 cerr << MBR_SIGNATURE << dec << "\n";
231 cerr.fill(' ');
srs5694978041c2009-09-21 20:51:47 -0400232 } // if
233
234 // Copy over the basic data....
235 partitions[partNum].status = ebr.partitions[0].status;
236 partitions[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
237 partitions[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
238 partitions[partNum].partitionType = ebr.partitions[0].partitionType;
239
240 // Find the next partition (if there is one) and recurse....
241 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) &&
242 (partNum < (MAX_MBR_PARTS - 1))) {
srs5694546a9c72010-01-26 16:00:26 -0500243 partNum = ReadLogicalPart(extendedStart, ebr.partitions[1].firstLBA,
srs5694978041c2009-09-21 20:51:47 -0400244 partNum + 1);
245 } else {
246 partNum++;
247 } // if another partition
248 } // Not enough space for all the logicals (or previous error encountered)
249 return (partNum);
250} // MBRData::ReadLogicalPart()
251
252// Write the MBR data to the default defined device. Note that this writes
253// ONLY the MBR itself, not the logical partition data.
srs5694e7b4ff92009-08-18 13:16:10 -0400254int MBRData::WriteMBRData(void) {
255 int allOK = 1, fd;
256
srs5694546a9c72010-01-26 16:00:26 -0500257 if (myDisk->OpenForWrite(device) != 0) {
258 allOK = WriteMBRData(myDisk);
srs5694e7b4ff92009-08-18 13:16:10 -0400259 } else {
260 allOK = 0;
261 } // if/else
srs5694546a9c72010-01-26 16:00:26 -0500262 myDisk->Close();
srs5694e7b4ff92009-08-18 13:16:10 -0400263 return allOK;
264} // MBRData::WriteMBRData(void)
265
266// Save the MBR data to a file. Note that this function writes ONLY the
267// MBR data, not the logical partitions (if any are defined).
srs5694546a9c72010-01-26 16:00:26 -0500268int MBRData::WriteMBRData(DiskIO *theDisk) {
269 int i, j, allOK;
srs5694221e0872009-08-29 15:00:31 -0400270 TempMBR tempMBR;
srs56942a9f5da2009-08-26 00:48:01 -0400271
272 // Reverse the byte order, if necessary
273 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400274 ReverseBytes(&diskSignature, 4);
275 ReverseBytes(&nulls, 2);
276 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400277 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400278 ReverseBytes(&partitions[i].firstLBA, 4);
279 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400280 } // for
281 } // if
282
srs5694221e0872009-08-29 15:00:31 -0400283 // Copy MBR data to a 512-byte data structure for writing, to
284 // work around a FreeBSD limitation....
285 for (i = 0; i < 440; i++)
286 tempMBR.code[i] = code[i];
287 tempMBR.diskSignature = diskSignature;
288 tempMBR.nulls = nulls;
289 tempMBR.MBRSignature = MBRSignature;
290 for (i = 0; i < 4; i++) {
291 tempMBR.partitions[i].status = partitions[i].status;
292 tempMBR.partitions[i].partitionType = partitions[i].partitionType;
293 tempMBR.partitions[i].firstLBA = partitions[i].firstLBA;
294 tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA;
295 for (j = 0; j < 3; j++) {
296 tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j];
297 tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j];
298 } // for j...
299 } // for i...
300
301 // Now write that data structure...
srs5694546a9c72010-01-26 16:00:26 -0500302 allOK = theDisk->OpenForWrite();
303 if (allOK && theDisk->Seek(0)) {
304 if (theDisk->Write(&tempMBR, 512) != 512) {
305 allOK = 0;
srs5694fed16d02010-01-27 23:03:40 -0500306 cerr << "Warning! Error " << errno << " when saving MBR!\n";
srs5694546a9c72010-01-26 16:00:26 -0500307 } // if
308 } else {
309 allOK = 0;
srs5694fed16d02010-01-27 23:03:40 -0500310 cerr << "Warning! Error " << errno << " when seeking to MBR to write it!\n";
srs5694546a9c72010-01-26 16:00:26 -0500311 } // if/else
312 theDisk->Close();
srs5694221e0872009-08-29 15:00:31 -0400313
srs5694221e0872009-08-29 15:00:31 -0400314 // Reverse the byte order back, if necessary
srs56942a9f5da2009-08-26 00:48:01 -0400315 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400316 ReverseBytes(&diskSignature, 4);
317 ReverseBytes(&nulls, 2);
318 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400319 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400320 ReverseBytes(&partitions[i].firstLBA, 4);
321 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400322 } // for
323 }// if
srs5694546a9c72010-01-26 16:00:26 -0500324 return allOK;
325} // MBRData::WriteMBRData(DiskIO theDisk)
srs5694e7b4ff92009-08-18 13:16:10 -0400326
srs5694fed16d02010-01-27 23:03:40 -0500327int MBRData::WriteMBRData(string deviceFilename) {
328 device = deviceFilename;
srs5694ba00fed2010-01-12 18:18:36 -0500329 return WriteMBRData();
330} // MBRData::WriteMBRData(char* deviceFilename)
331
srs5694978041c2009-09-21 20:51:47 -0400332/********************************************
333 * *
334 * Functions that display data for the user *
335 * *
336 ********************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400337
338// Show the MBR data to the user....
339void MBRData::DisplayMBRData(void) {
340 int i;
341 char tempStr[255];
srs5694e19ba092009-08-24 14:10:35 -0400342 char bootCode;
srs5694e7b4ff92009-08-18 13:16:10 -0400343
srs5694fed16d02010-01-27 23:03:40 -0500344 cout << "MBR disk identifier: 0x";
345 cout.width(8);
346 cout.fill('0');
347 cout.setf(ios::uppercase);
348 cout << hex << diskSignature << dec << "\n";
349 cout << "MBR partitions:\n";
350 cout << "Number\t Boot\t Start (sector)\t Length (sectors)\tType\n";
srs5694978041c2009-09-21 20:51:47 -0400351 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e7b4ff92009-08-18 13:16:10 -0400352 if (partitions[i].lengthLBA != 0) {
srs5694e19ba092009-08-24 14:10:35 -0400353 if (partitions[i].status && 0x80) // it's bootable
srs5694978041c2009-09-21 20:51:47 -0400354 bootCode = '*';
srs5694e19ba092009-08-24 14:10:35 -0400355 else
356 bootCode = ' ';
srs5694fed16d02010-01-27 23:03:40 -0500357 cout.fill(' ');
358 cout.width(4);
359 cout << i + 1 << "\t " << bootCode << "\t";
360 cout.width(13);
361 cout << partitions[i].firstLBA << "\t";
362 cout.width(15);
363 cout << partitions[i].lengthLBA << " \t0x";
364 cout.width(2);
365 cout.fill('0');
366 cout << hex << (int) partitions[i].partitionType << dec << "\n";
srs5694e7b4ff92009-08-18 13:16:10 -0400367 } // if
srs5694fed16d02010-01-27 23:03:40 -0500368 cout.fill(' ');
srs5694e7b4ff92009-08-18 13:16:10 -0400369 } // for
srs5694fed16d02010-01-27 23:03:40 -0500370 cout << "\nDisk size is " << diskSize << " sectors ("
371 << BytesToSI(diskSize * (uint64_t) blockSize) << "\n";
srs5694e7b4ff92009-08-18 13:16:10 -0400372} // MBRData::DisplayMBRData()
373
srs5694978041c2009-09-21 20:51:47 -0400374// Displays the state, as a word, on stdout. Used for debugging & to
375// tell the user about the MBR state when the program launches....
376void MBRData::ShowState(void) {
377 switch (state) {
378 case invalid:
srs5694fed16d02010-01-27 23:03:40 -0500379 cout << " MBR: not present\n";
srs5694978041c2009-09-21 20:51:47 -0400380 break;
381 case gpt:
srs5694fed16d02010-01-27 23:03:40 -0500382 cout << " MBR: protective\n";
srs5694978041c2009-09-21 20:51:47 -0400383 break;
384 case hybrid:
srs5694fed16d02010-01-27 23:03:40 -0500385 cout << " MBR: hybrid\n";
srs5694978041c2009-09-21 20:51:47 -0400386 break;
387 case mbr:
srs5694fed16d02010-01-27 23:03:40 -0500388 cout << " MBR: MBR only\n";
srs5694978041c2009-09-21 20:51:47 -0400389 break;
390 default:
srs5694fed16d02010-01-27 23:03:40 -0500391 cout << "\a MBR: unknown -- bug!\n";
srs5694978041c2009-09-21 20:51:47 -0400392 break;
393 } // switch
394} // MBRData::ShowState()
395
396/*********************************************************************
397 * *
398 * Functions that set or get disk metadata (CHS geometry, disk size, *
399 * etc.) *
400 * *
401 *********************************************************************/
402
403// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function.
404// Note that this only sets the heads and sectors; the number of
405// cylinders is determined by these values and the disk size.
406void MBRData::SetCHSGeom(uint32_t h, uint32_t s) {
407 if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
408 numHeads = h;
409 numSecspTrack = s;
410 } else {
srs5694fed16d02010-01-27 23:03:40 -0500411 cout << "Warning! Attempt to set invalid CHS geometry!\n";
srs5694978041c2009-09-21 20:51:47 -0400412 } // if/else
413} // MBRData::SetCHSGeom()
414
415// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
416// was within the range that can be expressed by CHS (including 0, for an
417// empty partition), 0 if the value is outside that range, and -1 if chs is
418// invalid.
419int MBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
420 uint64_t cylinder, head, sector; // all numbered from 0
421 uint64_t remainder;
422 int retval = 1;
423 int done = 0;
424
425 if (chs != NULL) {
426 // Special case: In case of 0 LBA value, zero out CHS values....
427 if (lba == 0) {
428 chs[0] = chs[1] = chs[2] = UINT8_C(0);
429 done = 1;
430 } // if
431 // If LBA value is too large for CHS, max out CHS values....
432 if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
433 chs[0] = 254;
434 chs[1] = chs[2] = 255;
435 done = 1;
436 retval = 0;
437 } // if
438 // If neither of the above applies, compute CHS values....
439 if (!done) {
440 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
441 remainder = lba - (cylinder * numHeads * numSecspTrack);
442 head = remainder / numSecspTrack;
443 remainder -= head * numSecspTrack;
444 sector = remainder;
445 if (head < numHeads)
446 chs[0] = head;
447 else
448 retval = 0;
449 if (sector < numSecspTrack) {
450 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
451 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
452 } else {
453 retval = 0;
454 } // if/else
455 } // if value is expressible and non-0
456 } else { // Invalid (NULL) chs pointer
457 retval = -1;
458 } // if CHS pointer valid
459 return (retval);
460} // MBRData::LBAtoCHS()
461
462/*****************************************************
463 * *
464 * Functions to create, delete, or change partitions *
465 * *
466 *****************************************************/
467
468// Empty all data. Meant mainly for calling by constructors, but it's also
469// used by the hybrid MBR functions in the GPTData class.
470void MBRData::EmptyMBR(int clearBootloader) {
471 int i;
472
473 // Zero out the boot loader section, the disk signature, and the
474 // 2-byte nulls area only if requested to do so. (This is the
475 // default.)
476 if (clearBootloader == 1) {
477 for (i = 0; i < 440; i++)
478 code[i] = 0;
479 diskSignature = (uint32_t) rand();
480 nulls = 0;
481 } // if
482
483 // Blank out the partitions
484 for (i = 0; i < MAX_MBR_PARTS; i++) {
485 partitions[i].status = UINT8_C(0);
486 partitions[i].firstSector[0] = UINT8_C(0);
487 partitions[i].firstSector[1] = UINT8_C(0);
488 partitions[i].firstSector[2] = UINT8_C(0);
489 partitions[i].partitionType = UINT8_C(0);
490 partitions[i].lastSector[0] = UINT8_C(0);
491 partitions[i].lastSector[1] = UINT8_C(0);
492 partitions[i].lastSector[2] = UINT8_C(0);
493 partitions[i].firstLBA = UINT32_C(0);
494 partitions[i].lengthLBA = UINT32_C(0);
495 } // for
496 MBRSignature = MBR_SIGNATURE;
497} // MBRData::EmptyMBR()
498
srs5694221e0872009-08-29 15:00:31 -0400499// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
500void MBRData::MakeProtectiveMBR(int clearBoot) {
srs5694978041c2009-09-21 20:51:47 -0400501
502 EmptyMBR(clearBoot);
srs5694e7b4ff92009-08-18 13:16:10 -0400503
504 // Initialize variables
505 nulls = 0;
506 MBRSignature = MBR_SIGNATURE;
507
508 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
509
510 // Write CHS data. This maxes out the use of the disk, as much as
511 // possible -- even to the point of exceeding the capacity of sub-8GB
512 // disks. The EFI spec says to use 0xffffff as the ending value,
513 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
514 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
515 // value in for the FIRST sector, too!
516 partitions[0].firstSector[0] = UINT8_C(0);
517 partitions[0].firstSector[1] = UINT8_C(1);
518 partitions[0].firstSector[2] = UINT8_C(0);
519 partitions[0].lastSector[0] = UINT8_C(255);
520 partitions[0].lastSector[1] = UINT8_C(255);
521 partitions[0].lastSector[2] = UINT8_C(255);
522
523 partitions[0].partitionType = UINT8_C(0xEE);
524 partitions[0].firstLBA = UINT32_C(1);
525 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
srs5694978041c2009-09-21 20:51:47 -0400526 partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
srs5694e7b4ff92009-08-18 13:16:10 -0400527 } else { // disk is too big to represent, so fake it...
528 partitions[0].lengthLBA = UINT32_MAX;
529 } // if/else
530
srs5694e7b4ff92009-08-18 13:16:10 -0400531 state = gpt;
532} // MBRData::MakeProtectiveMBR()
533
srs5694978041c2009-09-21 20:51:47 -0400534// Create a partition of the specified number, starting LBA, and
535// length. This function does *NO* error checking, so it's possible
536// to seriously screw up a partition table using this function!
537// Note: This function should NOT be used to create the 0xEE partition
538// in a conventional GPT configuration, since that partition has
539// specific size requirements that this function won't handle. It may
540// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
541// since those toss the rulebook away anyhow....
542void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
543 int bootable) {
544 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
545 partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
546 partitions[num].firstSector[0] = UINT8_C(0);
547 partitions[num].firstSector[1] = UINT8_C(0);
548 partitions[num].firstSector[2] = UINT8_C(0);
549 partitions[num].partitionType = (uint8_t) type;
550 partitions[num].lastSector[0] = UINT8_C(0);
551 partitions[num].lastSector[1] = UINT8_C(0);
552 partitions[num].lastSector[2] = UINT8_C(0);
553 partitions[num].firstLBA = start;
554 partitions[num].lengthLBA = length;
555 // If this is a "real" partition, set its CHS geometry
556 if (length > 0) {
557 LBAtoCHS((uint64_t) start, partitions[num].firstSector);
558 LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
559 } // if (length > 0)
560 } // if valid partition number
561} // MBRData::MakePart()
562
srs5694c0ca8f82009-08-20 21:35:25 -0400563// Create a partition that fills the most available space. Returns
564// 1 if partition was created, 0 otherwise. Intended for use in
565// creating hybrid MBRs.
566int MBRData::MakeBiggestPart(int i, int type) {
567 uint32_t start = UINT32_C(1); // starting point for each search
568 uint32_t firstBlock; // first block in a segment
569 uint32_t lastBlock; // last block in a segment
570 uint32_t segmentSize; // size of segment in blocks
571 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
572 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
573 int found = 0;
574
575 do {
576 firstBlock = FindFirstAvailable(start);
577 if (firstBlock != UINT32_C(0)) { // something's free...
578 lastBlock = FindLastInFree(firstBlock);
579 segmentSize = lastBlock - firstBlock + UINT32_C(1);
srs5694978041c2009-09-21 20:51:47 -0400580 if (segmentSize > selectedSize) {
srs5694c0ca8f82009-08-20 21:35:25 -0400581 selectedSize = segmentSize;
srs5694978041c2009-09-21 20:51:47 -0400582 selectedSegment = firstBlock;
583 } // if
584 start = lastBlock + 1;
srs5694c0ca8f82009-08-20 21:35:25 -0400585 } // if
586 } while (firstBlock != 0);
587 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
588 found = 1;
589 MakePart(i, selectedSegment, selectedSize, type, 0);
590 } else {
591 found = 0;
592 } // if/else
593 return found;
594} // MBRData::MakeBiggestPart(int i)
595
srs5694e35eb1b2009-09-14 00:29:34 -0400596// Delete partition #i
597void MBRData::DeletePartition(int i) {
598 int j;
599
600 partitions[i].firstLBA = UINT32_C(0);
601 partitions[i].lengthLBA = UINT32_C(0);
602 partitions[i].status = UINT8_C(0);
603 partitions[i].partitionType = UINT8_C(0);
604 for (j = 0; j < 3; j++) {
605 partitions[i].firstSector[j] = UINT8_C(0);
606 partitions[i].lastSector[j] = UINT8_C(0);
607 } // for j (CHS data blanking)
608} // MBRData::DeletePartition()
609
srs5694e4ac11e2009-08-31 10:13:04 -0400610// Delete a partition if one exists at the specified location.
611// Returns 1 if a partition was deleted, 0 otherwise....
612// Used to help keep GPT & hybrid MBR partitions in sync....
613int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
614 uint32_t start32, length32;
srs5694978041c2009-09-21 20:51:47 -0400615 int i, deleted = 0;
srs5694e4ac11e2009-08-31 10:13:04 -0400616
srs5694978041c2009-09-21 20:51:47 -0400617 if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400618 start32 = (uint32_t) start64;
619 length32 = (uint32_t) length64;
srs5694978041c2009-09-21 20:51:47 -0400620 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e4ac11e2009-08-31 10:13:04 -0400621 if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
srs5694546a9c72010-01-26 16:00:26 -0500622 (partitions[i].partitionType != 0xEE)) {
srs5694e35eb1b2009-09-14 00:29:34 -0400623 DeletePartition(i);
srs5694978041c2009-09-21 20:51:47 -0400624 if (state == hybrid)
625 OptimizeEESize();
srs5694e4ac11e2009-08-31 10:13:04 -0400626 deleted = 1;
srs5694546a9c72010-01-26 16:00:26 -0500627 } // if (match found)
srs5694e4ac11e2009-08-31 10:13:04 -0400628 } // for i (partition scan)
629 } // if (hybrid & GPT partition < 2TiB)
630 return deleted;
631} // MBRData::DeleteByLocation()
632
633// Optimizes the size of the 0xEE (EFI GPT) partition
634void MBRData::OptimizeEESize(void) {
635 int i, typeFlag = 0;
636 uint32_t after;
637
638 for (i = 0; i < 4; i++) {
639 // Check for non-empty and non-0xEE partitions
640 if ((partitions[i].partitionType != 0xEE) && (partitions[i].partitionType != 0x00))
641 typeFlag++;
642 if (partitions[i].partitionType == 0xEE) {
643 // Blank space before this partition; fill it....
644 if (IsFree(partitions[i].firstLBA - 1)) {
645 partitions[i].firstLBA = FindFirstInFree(partitions[i].firstLBA - 1);
646 } // if
647 // Blank space after this partition; fill it....
648 after = partitions[i].firstLBA + partitions[i].lengthLBA;
649 if (IsFree(after)) {
650 partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
651 } // if free space after
652 } // if partition is 0xEE
srs5694e4ac11e2009-08-31 10:13:04 -0400653 } // for partition loop
srs5694978041c2009-09-21 20:51:47 -0400654 if (typeFlag == 0) { // No non-hybrid partitions found
655 MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
656 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400657} // MBRData::OptimizeEESize()
658
srs5694978041c2009-09-21 20:51:47 -0400659/****************************************
660 * *
661 * Functions to find data on free space *
662 * *
663 ****************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400664
srs5694c0ca8f82009-08-20 21:35:25 -0400665// Finds the first free space on the disk from start onward; returns 0
666// if none available....
667uint32_t MBRData::FindFirstAvailable(uint32_t start) {
668 uint32_t first;
669 uint32_t i;
srs5694978041c2009-09-21 20:51:47 -0400670 int firstMoved;
srs5694c0ca8f82009-08-20 21:35:25 -0400671
672 first = start;
673
674 // ...now search through all partitions; if first is within an
675 // existing partition, move it to the next sector after that
676 // partition and repeat. If first was moved, set firstMoved
677 // flag; repeat until firstMoved is not set, so as to catch
678 // cases where partitions are out of sequential order....
679 do {
680 firstMoved = 0;
681 for (i = 0; i < 4; i++) {
682 // Check if it's in the existing partition
683 if ((first >= partitions[i].firstLBA) &&
684 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
685 first = partitions[i].firstLBA + partitions[i].lengthLBA;
686 firstMoved = 1;
srs5694978041c2009-09-21 20:51:47 -0400687 } // if
srs5694c0ca8f82009-08-20 21:35:25 -0400688 } // for
689 } while (firstMoved == 1);
690 if (first >= diskSize)
691 first = 0;
692 return (first);
693} // MBRData::FindFirstAvailable()
694
srs5694e4ac11e2009-08-31 10:13:04 -0400695// Finds the last free sector on the disk from start forward.
srs5694c0ca8f82009-08-20 21:35:25 -0400696uint32_t MBRData::FindLastInFree(uint32_t start) {
697 uint32_t nearestStart;
698 uint32_t i;
699
srs5694978041c2009-09-21 20:51:47 -0400700 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs5694c0ca8f82009-08-20 21:35:25 -0400701 nearestStart = diskSize - 1;
702 else
703 nearestStart = UINT32_MAX - 1;
704 for (i = 0; i < 4; i++) {
705 if ((nearestStart > partitions[i].firstLBA) &&
706 (partitions[i].firstLBA > start)) {
707 nearestStart = partitions[i].firstLBA - 1;
708 } // if
709 } // for
710 return (nearestStart);
srs5694e4ac11e2009-08-31 10:13:04 -0400711} // MBRData::FindLastInFree()
712
713// Finds the first free sector on the disk from start backward.
714uint32_t MBRData::FindFirstInFree(uint32_t start) {
srs5694e35eb1b2009-09-14 00:29:34 -0400715 uint32_t bestLastLBA, thisLastLBA;
srs5694e4ac11e2009-08-31 10:13:04 -0400716 int i;
717
718 bestLastLBA = 1;
719 for (i = 0; i < 4; i++) {
720 thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
721 if (thisLastLBA > 0) thisLastLBA--;
722 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) {
723 bestLastLBA = thisLastLBA + 1;
724 } // if
725 } // for
726 return (bestLastLBA);
727} // MBRData::FindFirstInFree()
728
729// Returns 1 if the specified sector is unallocated, 0 if it's
730// allocated.
731int MBRData::IsFree(uint32_t sector) {
732 int i, isFree = 1;
733 uint32_t first, last;
734
735 for (i = 0; i < 4; i++) {
736 first = partitions[i].firstLBA;
srs5694978041c2009-09-21 20:51:47 -0400737 // Note: Weird two-line thing to avoid subtracting 1 from a 0 value
738 // for an unsigned int....
srs5694e4ac11e2009-08-31 10:13:04 -0400739 last = first + partitions[i].lengthLBA;
740 if (last > 0) last--;
741 if ((first <= sector) && (last >= sector))
742 isFree = 0;
743 } // for
744 return isFree;
745} // MBRData::IsFree()
srs5694c0ca8f82009-08-20 21:35:25 -0400746
srs5694978041c2009-09-21 20:51:47 -0400747/******************************************************
748 * *
749 * Functions that extract data on specific partitions *
750 * *
751 ******************************************************/
752
srs5694e7b4ff92009-08-18 13:16:10 -0400753uint8_t MBRData::GetStatus(int i) {
754 MBRRecord* thePart;
755 uint8_t retval;
756
757 thePart = GetPartition(i);
758 if (thePart != NULL)
759 retval = thePart->status;
760 else
761 retval = UINT8_C(0);
762 return retval;
763} // MBRData::GetStatus()
764
765uint8_t MBRData::GetType(int i) {
766 MBRRecord* thePart;
767 uint8_t retval;
768
769 thePart = GetPartition(i);
770 if (thePart != NULL)
771 retval = thePart->partitionType;
772 else
773 retval = UINT8_C(0);
774 return retval;
775} // MBRData::GetType()
776
777uint32_t MBRData::GetFirstSector(int i) {
778 MBRRecord* thePart;
779 uint32_t retval;
780
781 thePart = GetPartition(i);
782 if (thePart != NULL) {
783 retval = thePart->firstLBA;
784 } else
785 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400786 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400787} // MBRData::GetFirstSector()
788
789uint32_t MBRData::GetLength(int i) {
790 MBRRecord* thePart;
791 uint32_t retval;
792
793 thePart = GetPartition(i);
794 if (thePart != NULL) {
795 retval = thePart->lengthLBA;
796 } else
797 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400798 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400799} // MBRData::GetLength()
srs5694221e0872009-08-29 15:00:31 -0400800
801// Return the MBR data as a GPT partition....
802GPTPart MBRData::AsGPT(int i) {
803 MBRRecord* origPart;
804 GPTPart newPart;
805 uint8_t origType;
806 uint64_t firstSector, lastSector;
807 char tempStr[NAME_SIZE];
808
809 newPart.BlankPartition();
810 origPart = GetPartition(i);
811 if (origPart != NULL) {
812 origType = origPart->partitionType;
813
814 // don't convert extended, hybrid protective, or null (non-existent)
815 // partitions (Note similar protection is in GPTData::XFormPartitions(),
816 // but I want it here too in case I call this function in another
817 // context in the future....)
srs5694e35eb1b2009-09-14 00:29:34 -0400818 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694221e0872009-08-29 15:00:31 -0400819 (origType != 0x00) && (origType != 0xEE)) {
820 firstSector = (uint64_t) origPart->firstLBA;
821 newPart.SetFirstLBA(firstSector);
822 lastSector = firstSector + (uint64_t) origPart->lengthLBA;
823 if (lastSector > 0) lastSector--;
824 newPart.SetLastLBA(lastSector);
825 newPart.SetType(((uint16_t) origType) * 0x0100);
826 newPart.SetUniqueGUID(1);
827 newPart.SetAttributes(0);
srs5694fed16d02010-01-27 23:03:40 -0500828 newPart.SetName(newPart.GetNameType());
srs5694978041c2009-09-21 20:51:47 -0400829 } // if not extended, protective, or non-existent
830 } // if (origPart != NULL)
srs5694221e0872009-08-29 15:00:31 -0400831 return newPart;
832} // MBRData::AsGPT()
srs5694978041c2009-09-21 20:51:47 -0400833
834/***********************
835 * *
836 * Protected functions *
837 * *
838 ***********************/
839
840// Return a pointer to a primary or logical partition, or NULL if
841// the partition is out of range....
842struct MBRRecord* MBRData::GetPartition(int i) {
843 MBRRecord* thePart = NULL;
844
845 if ((i >= 0) && (i < MAX_MBR_PARTS))
846 thePart = &partitions[i];
847 return thePart;
848} // GetPartition()