blob: 029b57678e9e12c4597bb07349b1ebe6b1f6ba6e [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>
21#include "mbr.h"
22#include "support.h"
23
24using namespace std;
25
26/****************************************
27 * *
28 * MBRData class and related structures *
29 * *
30 ****************************************/
31
32MBRData::MBRData(void) {
33 blockSize = SECTOR_SIZE;
34 diskSize = 0;
35 strcpy(device, "");
36 state = invalid;
37 srand((unsigned int) time(NULL));
srs5694978041c2009-09-21 20:51:47 -040038 numHeads = MAX_HEADS;
39 numSecspTrack = MAX_SECSPERTRACK;
srs5694e7b4ff92009-08-18 13:16:10 -040040 EmptyMBR();
41} // MBRData default constructor
42
43MBRData::MBRData(char *filename) {
44 blockSize = SECTOR_SIZE;
45 diskSize = 0;
46 strcpy(device, filename);
47 state = invalid;
srs5694978041c2009-09-21 20:51:47 -040048 numHeads = MAX_HEADS;
49 numSecspTrack = MAX_SECSPERTRACK;
srs5694e7b4ff92009-08-18 13:16:10 -040050
51 srand((unsigned int) time(NULL));
52 // Try to read the specified partition table, but if it fails....
53 if (!ReadMBRData(filename)) {
54 EmptyMBR();
55 strcpy(device, "");
56 } // if
57} // MBRData(char *filename) constructor
58
59MBRData::~MBRData(void) {
60} // MBRData destructor
61
srs5694978041c2009-09-21 20:51:47 -040062/**********************
63 * *
64 * Disk I/O functions *
65 * *
66 **********************/
srs5694e7b4ff92009-08-18 13:16:10 -040067
68// Read data from MBR. Returns 1 if read was successful (even if the
69// data isn't a valid MBR), 0 if the read failed.
70int MBRData::ReadMBRData(char* deviceFilename) {
71 int fd, allOK = 1;
72
srs5694546a9c72010-01-26 16:00:26 -050073 if (myDisk->OpenForRead(deviceFilename)) {
74 ReadMBRData(myDisk);
srs5694e7b4ff92009-08-18 13:16:10 -040075 } else {
76 allOK = 0;
77 } // if
78
srs5694e7b4ff92009-08-18 13:16:10 -040079 if (allOK)
80 strcpy(device, deviceFilename);
81
82 return allOK;
83} // MBRData::ReadMBRData(char* deviceFilename)
84
srs5694978041c2009-09-21 20:51:47 -040085// Read data from MBR. If checkBlockSize == 1 (the default), the block
86// size is checked; otherwise it's set to the default (512 bytes).
87// Note that any extended partition(s) present will be explicitly stored
88// in the partitions[] array, along with their contained partitions; the
89// extended container partition(s) should be ignored by other functions.
srs5694546a9c72010-01-26 16:00:26 -050090void MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) {
srs5694978041c2009-09-21 20:51:47 -040091 int allOK = 1, i, j, logicalNum;
srs5694546a9c72010-01-26 16:00:26 -050092 int err = 1;
srs5694221e0872009-08-29 15:00:31 -040093 TempMBR tempMBR;
srs5694e7b4ff92009-08-18 13:16:10 -040094
srs5694546a9c72010-01-26 16:00:26 -050095 myDisk = theDisk;
96
srs5694978041c2009-09-21 20:51:47 -040097 // Empty existing MBR data, including the logical partitions...
98 EmptyMBR(0);
srs5694e7b4ff92009-08-18 13:16:10 -040099
srs5694546a9c72010-01-26 16:00:26 -0500100 if (myDisk->Seek(0))
101 if (myDisk->Read(&tempMBR, 512))
102 err = 0;
103 if (err) {
104 fprintf(stderr, "Problem reading disk in MBRData::ReadMBRData!\n");
105 } else {
106 for (i = 0; i < 440; i++)
107 code[i] = tempMBR.code[i];
108 diskSignature = tempMBR.diskSignature;
109 nulls = tempMBR.nulls;
srs56942a9f5da2009-08-26 00:48:01 -0400110 for (i = 0; i < 4; i++) {
srs5694546a9c72010-01-26 16:00:26 -0500111 partitions[i].status = tempMBR.partitions[i].status;
112 partitions[i].partitionType = tempMBR.partitions[i].partitionType;
113 partitions[i].firstLBA = tempMBR.partitions[i].firstLBA;
114 partitions[i].lengthLBA = tempMBR.partitions[i].lengthLBA;
115 for (j = 0; j < 3; j++) {
116 partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j];
117 partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j];
118 } // for j... (reading parts of CHS geometry)
119 } // for i... (reading all four partitions)
120 MBRSignature = tempMBR.MBRSignature;
srs56942a9f5da2009-08-26 00:48:01 -0400121
srs5694546a9c72010-01-26 16:00:26 -0500122 // Reverse the byte order, if necessary
123 if (IsLittleEndian() == 0) {
124 ReverseBytes(&diskSignature, 4);
125 ReverseBytes(&nulls, 2);
126 ReverseBytes(&MBRSignature, 2);
127 for (i = 0; i < 4; i++) {
128 ReverseBytes(&partitions[i].firstLBA, 4);
129 ReverseBytes(&partitions[i].lengthLBA, 4);
130 } // for
131 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400132
srs5694546a9c72010-01-26 16:00:26 -0500133 if (MBRSignature != MBR_SIGNATURE) {
134 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400135 state = invalid;
136 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400137
srs5694546a9c72010-01-26 16:00:26 -0500138 // Find disk size
139 diskSize = myDisk->DiskSize(&err);
140
141 // Find block size
142 if (checkBlockSize) {
143 blockSize = myDisk->GetBlockSize();
144 } // if (checkBlockSize)
145
146 // Load logical partition data, if any is found....
147 if (allOK) {
148 for (i = 0; i < 4; i++) {
149 if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
150 || (partitions[i].partitionType == 0x85)) {
151 // Found it, so call a recursive algorithm to load everything from them....
152 logicalNum = ReadLogicalPart(partitions[i].firstLBA, UINT32_C(0), 4);
153 if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
154 allOK = 0;
155 fprintf(stderr, "Error reading logical partitions! List may be truncated!\n");
156 } // if maxLogicals valid
157 } // if primary partition is extended
158 } // for primary partition loop
159 if (allOK) { // Loaded logicals OK
160 state = mbr;
161 } else {
162 state = invalid;
srs56942a9f5da2009-08-26 00:48:01 -0400163 } // if
srs5694546a9c72010-01-26 16:00:26 -0500164 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400165
srs5694546a9c72010-01-26 16:00:26 -0500166 /* Check to see if it's in GPT format.... */
167 if (allOK) {
168 for (i = 0; i < 4; i++) {
169 if (partitions[i].partitionType == UINT8_C(0xEE)) {
170 state = gpt;
171 } // if
172 } // for
173 } // if
174
175 // If there's an EFI GPT partition, look for other partition types,
176 // to flag as hybrid
177 if (state == gpt) {
178 for (i = 0 ; i < 4; i++) {
179 if ((partitions[i].partitionType != UINT8_C(0xEE)) &&
180 (partitions[i].partitionType != UINT8_C(0x00)))
181 state = hybrid;
182 } // for
183 } // if (hybrid detection code)
184 } // no initial error
srs5694e7b4ff92009-08-18 13:16:10 -0400185} // MBRData::ReadMBRData(int fd)
186
srs5694978041c2009-09-21 20:51:47 -0400187// This is a recursive function to read all the logical partitions, following the
188// logical partition linked list from the disk and storing the basic data in the
189// partitions[] array. Returns last index to partitions[] used, or -1 if there was
190// a problem.
191// Parameters:
srs5694978041c2009-09-21 20:51:47 -0400192// extendedStart = LBA of the start of the extended partition
193// diskOffset = LBA offset WITHIN the extended partition of the one to be read
194// partNum = location in partitions[] array to store retrieved data
srs5694546a9c72010-01-26 16:00:26 -0500195int MBRData::ReadLogicalPart(uint32_t extendedStart,
srs5694978041c2009-09-21 20:51:47 -0400196 uint32_t diskOffset, int partNum) {
197 struct TempMBR ebr;
srs5694546a9c72010-01-26 16:00:26 -0500198 uint64_t offset;
srs5694978041c2009-09-21 20:51:47 -0400199
200 // Check for a valid partition number. Note that partitions MAY be read into
201 // the area normally used by primary partitions, although the only calling
202 // function as of GPT fdisk version 0.5.0 doesn't do so.
203 if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
srs5694546a9c72010-01-26 16:00:26 -0500204 offset = (uint64_t) (extendedStart + diskOffset);
205// if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
206 if (myDisk->Seek(offset) == 0) { // seek to EBR record
srs5694978041c2009-09-21 20:51:47 -0400207 fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
208 partNum = -1;
209 }
srs5694546a9c72010-01-26 16:00:26 -0500210 if (myDisk->Read(&ebr, 512) != 512) { // Load the data....
srs5694978041c2009-09-21 20:51:47 -0400211 fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
212 (unsigned long) offset);
213 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;
224 fprintf(stderr, "MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
225 (unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
226 } // if
227
228 // Copy over the basic data....
229 partitions[partNum].status = ebr.partitions[0].status;
230 partitions[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
231 partitions[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
232 partitions[partNum].partitionType = ebr.partitions[0].partitionType;
233
234 // Find the next partition (if there is one) and recurse....
235 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) &&
236 (partNum < (MAX_MBR_PARTS - 1))) {
srs5694546a9c72010-01-26 16:00:26 -0500237 partNum = ReadLogicalPart(extendedStart, ebr.partitions[1].firstLBA,
srs5694978041c2009-09-21 20:51:47 -0400238 partNum + 1);
239 } else {
240 partNum++;
241 } // if another partition
242 } // Not enough space for all the logicals (or previous error encountered)
243 return (partNum);
244} // MBRData::ReadLogicalPart()
245
246// Write the MBR data to the default defined device. Note that this writes
247// ONLY the MBR itself, not the logical partition data.
srs5694e7b4ff92009-08-18 13:16:10 -0400248int MBRData::WriteMBRData(void) {
249 int allOK = 1, fd;
250
srs5694546a9c72010-01-26 16:00:26 -0500251 if (myDisk->OpenForWrite(device) != 0) {
252 allOK = WriteMBRData(myDisk);
srs5694e7b4ff92009-08-18 13:16:10 -0400253 } else {
254 allOK = 0;
255 } // if/else
srs5694546a9c72010-01-26 16:00:26 -0500256 myDisk->Close();
srs5694e7b4ff92009-08-18 13:16:10 -0400257 return allOK;
258} // MBRData::WriteMBRData(void)
259
260// Save the MBR data to a file. Note that this function writes ONLY the
261// MBR data, not the logical partitions (if any are defined).
srs5694546a9c72010-01-26 16:00:26 -0500262int MBRData::WriteMBRData(DiskIO *theDisk) {
263 int i, j, allOK;
srs5694221e0872009-08-29 15:00:31 -0400264 TempMBR tempMBR;
srs56942a9f5da2009-08-26 00:48:01 -0400265
266 // Reverse the byte order, if necessary
267 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400268 ReverseBytes(&diskSignature, 4);
269 ReverseBytes(&nulls, 2);
270 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400271 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400272 ReverseBytes(&partitions[i].firstLBA, 4);
273 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400274 } // for
275 } // if
276
srs5694221e0872009-08-29 15:00:31 -0400277 // Copy MBR data to a 512-byte data structure for writing, to
278 // work around a FreeBSD limitation....
279 for (i = 0; i < 440; i++)
280 tempMBR.code[i] = code[i];
281 tempMBR.diskSignature = diskSignature;
282 tempMBR.nulls = nulls;
283 tempMBR.MBRSignature = MBRSignature;
284 for (i = 0; i < 4; i++) {
285 tempMBR.partitions[i].status = partitions[i].status;
286 tempMBR.partitions[i].partitionType = partitions[i].partitionType;
287 tempMBR.partitions[i].firstLBA = partitions[i].firstLBA;
288 tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA;
289 for (j = 0; j < 3; j++) {
290 tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j];
291 tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j];
292 } // for j...
293 } // for i...
294
295 // Now write that data structure...
srs5694546a9c72010-01-26 16:00:26 -0500296 allOK = theDisk->OpenForWrite();
297 if (allOK && theDisk->Seek(0)) {
298 if (theDisk->Write(&tempMBR, 512) != 512) {
299 allOK = 0;
300 fprintf(stderr, "Warning! Error %d when saving MBR!\n", errno);
301 } // if
302 } else {
303 allOK = 0;
304 fprintf(stderr, "Warning! Error %d when seeking to MBR to write it!\n", errno);
305 } // if/else
306 theDisk->Close();
srs5694221e0872009-08-29 15:00:31 -0400307
srs5694221e0872009-08-29 15:00:31 -0400308 // Reverse the byte order back, if necessary
srs56942a9f5da2009-08-26 00:48:01 -0400309 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400310 ReverseBytes(&diskSignature, 4);
311 ReverseBytes(&nulls, 2);
312 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400313 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400314 ReverseBytes(&partitions[i].firstLBA, 4);
315 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400316 } // for
317 }// if
srs5694546a9c72010-01-26 16:00:26 -0500318 return allOK;
319} // MBRData::WriteMBRData(DiskIO theDisk)
srs5694e7b4ff92009-08-18 13:16:10 -0400320
srs5694ba00fed2010-01-12 18:18:36 -0500321int MBRData::WriteMBRData(char* deviceFilename) {
322 strcpy(device, deviceFilename);
323 return WriteMBRData();
324} // MBRData::WriteMBRData(char* deviceFilename)
325
srs5694978041c2009-09-21 20:51:47 -0400326/********************************************
327 * *
328 * Functions that display data for the user *
329 * *
330 ********************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400331
332// Show the MBR data to the user....
333void MBRData::DisplayMBRData(void) {
334 int i;
335 char tempStr[255];
srs5694e19ba092009-08-24 14:10:35 -0400336 char bootCode;
srs5694e7b4ff92009-08-18 13:16:10 -0400337
338 printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
339 printf("MBR partitions:\n");
srs5694e19ba092009-08-24 14:10:35 -0400340 printf("Number\t Boot\t Start (sector)\t Length (sectors)\tType\n");
srs5694978041c2009-09-21 20:51:47 -0400341 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e7b4ff92009-08-18 13:16:10 -0400342 if (partitions[i].lengthLBA != 0) {
srs5694e19ba092009-08-24 14:10:35 -0400343 if (partitions[i].status && 0x80) // it's bootable
srs5694978041c2009-09-21 20:51:47 -0400344 bootCode = '*';
srs5694e19ba092009-08-24 14:10:35 -0400345 else
346 bootCode = ' ';
347 printf("%4d\t %c\t%13lu\t%15lu \t0x%02X\n", i + 1, bootCode,
348 (unsigned long) partitions[i].firstLBA,
srs5694e7b4ff92009-08-18 13:16:10 -0400349 (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
350 } // if
351 } // for
srs5694546a9c72010-01-26 16:00:26 -0500352 printf("\nDisk size is %llu sectors ", (unsigned long long) diskSize);
353 printf("(%s)\n", BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
srs5694e7b4ff92009-08-18 13:16:10 -0400354} // MBRData::DisplayMBRData()
355
srs5694978041c2009-09-21 20:51:47 -0400356// Displays the state, as a word, on stdout. Used for debugging & to
357// tell the user about the MBR state when the program launches....
358void MBRData::ShowState(void) {
359 switch (state) {
360 case invalid:
361 printf(" MBR: not present\n");
362 break;
363 case gpt:
364 printf(" MBR: protective\n");
365 break;
366 case hybrid:
367 printf(" MBR: hybrid\n");
368 break;
369 case mbr:
370 printf(" MBR: MBR only\n");
371 break;
372 default:
373 printf("\a MBR: unknown -- bug!\n");
374 break;
375 } // switch
376} // MBRData::ShowState()
377
378/*********************************************************************
379 * *
380 * Functions that set or get disk metadata (CHS geometry, disk size, *
381 * etc.) *
382 * *
383 *********************************************************************/
384
385// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function.
386// Note that this only sets the heads and sectors; the number of
387// cylinders is determined by these values and the disk size.
388void MBRData::SetCHSGeom(uint32_t h, uint32_t s) {
389 if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
390 numHeads = h;
391 numSecspTrack = s;
392 } else {
393 printf("Warning! Attempt to set invalid CHS geometry!\n");
394 } // if/else
395} // MBRData::SetCHSGeom()
396
397// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
398// was within the range that can be expressed by CHS (including 0, for an
399// empty partition), 0 if the value is outside that range, and -1 if chs is
400// invalid.
401int MBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
402 uint64_t cylinder, head, sector; // all numbered from 0
403 uint64_t remainder;
404 int retval = 1;
405 int done = 0;
406
407 if (chs != NULL) {
408 // Special case: In case of 0 LBA value, zero out CHS values....
409 if (lba == 0) {
410 chs[0] = chs[1] = chs[2] = UINT8_C(0);
411 done = 1;
412 } // if
413 // If LBA value is too large for CHS, max out CHS values....
414 if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
415 chs[0] = 254;
416 chs[1] = chs[2] = 255;
417 done = 1;
418 retval = 0;
419 } // if
420 // If neither of the above applies, compute CHS values....
421 if (!done) {
422 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
423 remainder = lba - (cylinder * numHeads * numSecspTrack);
424 head = remainder / numSecspTrack;
425 remainder -= head * numSecspTrack;
426 sector = remainder;
427 if (head < numHeads)
428 chs[0] = head;
429 else
430 retval = 0;
431 if (sector < numSecspTrack) {
432 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
433 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
434 } else {
435 retval = 0;
436 } // if/else
437 } // if value is expressible and non-0
438 } else { // Invalid (NULL) chs pointer
439 retval = -1;
440 } // if CHS pointer valid
441 return (retval);
442} // MBRData::LBAtoCHS()
443
444/*****************************************************
445 * *
446 * Functions to create, delete, or change partitions *
447 * *
448 *****************************************************/
449
450// Empty all data. Meant mainly for calling by constructors, but it's also
451// used by the hybrid MBR functions in the GPTData class.
452void MBRData::EmptyMBR(int clearBootloader) {
453 int i;
454
455 // Zero out the boot loader section, the disk signature, and the
456 // 2-byte nulls area only if requested to do so. (This is the
457 // default.)
458 if (clearBootloader == 1) {
459 for (i = 0; i < 440; i++)
460 code[i] = 0;
461 diskSignature = (uint32_t) rand();
462 nulls = 0;
463 } // if
464
465 // Blank out the partitions
466 for (i = 0; i < MAX_MBR_PARTS; i++) {
467 partitions[i].status = UINT8_C(0);
468 partitions[i].firstSector[0] = UINT8_C(0);
469 partitions[i].firstSector[1] = UINT8_C(0);
470 partitions[i].firstSector[2] = UINT8_C(0);
471 partitions[i].partitionType = UINT8_C(0);
472 partitions[i].lastSector[0] = UINT8_C(0);
473 partitions[i].lastSector[1] = UINT8_C(0);
474 partitions[i].lastSector[2] = UINT8_C(0);
475 partitions[i].firstLBA = UINT32_C(0);
476 partitions[i].lengthLBA = UINT32_C(0);
477 } // for
478 MBRSignature = MBR_SIGNATURE;
479} // MBRData::EmptyMBR()
480
srs5694221e0872009-08-29 15:00:31 -0400481// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
482void MBRData::MakeProtectiveMBR(int clearBoot) {
srs5694978041c2009-09-21 20:51:47 -0400483
484 EmptyMBR(clearBoot);
srs5694e7b4ff92009-08-18 13:16:10 -0400485
486 // Initialize variables
487 nulls = 0;
488 MBRSignature = MBR_SIGNATURE;
489
490 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
491
492 // Write CHS data. This maxes out the use of the disk, as much as
493 // possible -- even to the point of exceeding the capacity of sub-8GB
494 // disks. The EFI spec says to use 0xffffff as the ending value,
495 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
496 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
497 // value in for the FIRST sector, too!
498 partitions[0].firstSector[0] = UINT8_C(0);
499 partitions[0].firstSector[1] = UINT8_C(1);
500 partitions[0].firstSector[2] = UINT8_C(0);
501 partitions[0].lastSector[0] = UINT8_C(255);
502 partitions[0].lastSector[1] = UINT8_C(255);
503 partitions[0].lastSector[2] = UINT8_C(255);
504
505 partitions[0].partitionType = UINT8_C(0xEE);
506 partitions[0].firstLBA = UINT32_C(1);
507 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
srs5694978041c2009-09-21 20:51:47 -0400508 partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
srs5694e7b4ff92009-08-18 13:16:10 -0400509 } else { // disk is too big to represent, so fake it...
510 partitions[0].lengthLBA = UINT32_MAX;
511 } // if/else
512
srs5694e7b4ff92009-08-18 13:16:10 -0400513 state = gpt;
514} // MBRData::MakeProtectiveMBR()
515
srs5694978041c2009-09-21 20:51:47 -0400516// Create a partition of the specified number, starting LBA, and
517// length. This function does *NO* error checking, so it's possible
518// to seriously screw up a partition table using this function!
519// Note: This function should NOT be used to create the 0xEE partition
520// in a conventional GPT configuration, since that partition has
521// specific size requirements that this function won't handle. It may
522// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
523// since those toss the rulebook away anyhow....
524void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
525 int bootable) {
526 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
527 partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
528 partitions[num].firstSector[0] = UINT8_C(0);
529 partitions[num].firstSector[1] = UINT8_C(0);
530 partitions[num].firstSector[2] = UINT8_C(0);
531 partitions[num].partitionType = (uint8_t) type;
532 partitions[num].lastSector[0] = UINT8_C(0);
533 partitions[num].lastSector[1] = UINT8_C(0);
534 partitions[num].lastSector[2] = UINT8_C(0);
535 partitions[num].firstLBA = start;
536 partitions[num].lengthLBA = length;
537 // If this is a "real" partition, set its CHS geometry
538 if (length > 0) {
539 LBAtoCHS((uint64_t) start, partitions[num].firstSector);
540 LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
541 } // if (length > 0)
542 } // if valid partition number
543} // MBRData::MakePart()
544
srs5694c0ca8f82009-08-20 21:35:25 -0400545// Create a partition that fills the most available space. Returns
546// 1 if partition was created, 0 otherwise. Intended for use in
547// creating hybrid MBRs.
548int MBRData::MakeBiggestPart(int i, int type) {
549 uint32_t start = UINT32_C(1); // starting point for each search
550 uint32_t firstBlock; // first block in a segment
551 uint32_t lastBlock; // last block in a segment
552 uint32_t segmentSize; // size of segment in blocks
553 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
554 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
555 int found = 0;
556
557 do {
558 firstBlock = FindFirstAvailable(start);
559 if (firstBlock != UINT32_C(0)) { // something's free...
560 lastBlock = FindLastInFree(firstBlock);
561 segmentSize = lastBlock - firstBlock + UINT32_C(1);
srs5694978041c2009-09-21 20:51:47 -0400562 if (segmentSize > selectedSize) {
srs5694c0ca8f82009-08-20 21:35:25 -0400563 selectedSize = segmentSize;
srs5694978041c2009-09-21 20:51:47 -0400564 selectedSegment = firstBlock;
565 } // if
566 start = lastBlock + 1;
srs5694c0ca8f82009-08-20 21:35:25 -0400567 } // if
568 } while (firstBlock != 0);
569 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
570 found = 1;
571 MakePart(i, selectedSegment, selectedSize, type, 0);
572 } else {
573 found = 0;
574 } // if/else
575 return found;
576} // MBRData::MakeBiggestPart(int i)
577
srs5694e35eb1b2009-09-14 00:29:34 -0400578// Delete partition #i
579void MBRData::DeletePartition(int i) {
580 int j;
581
582 partitions[i].firstLBA = UINT32_C(0);
583 partitions[i].lengthLBA = UINT32_C(0);
584 partitions[i].status = UINT8_C(0);
585 partitions[i].partitionType = UINT8_C(0);
586 for (j = 0; j < 3; j++) {
587 partitions[i].firstSector[j] = UINT8_C(0);
588 partitions[i].lastSector[j] = UINT8_C(0);
589 } // for j (CHS data blanking)
590} // MBRData::DeletePartition()
591
srs5694e4ac11e2009-08-31 10:13:04 -0400592// Delete a partition if one exists at the specified location.
593// Returns 1 if a partition was deleted, 0 otherwise....
594// Used to help keep GPT & hybrid MBR partitions in sync....
595int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
596 uint32_t start32, length32;
srs5694978041c2009-09-21 20:51:47 -0400597 int i, deleted = 0;
srs5694e4ac11e2009-08-31 10:13:04 -0400598
srs5694978041c2009-09-21 20:51:47 -0400599 if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400600 start32 = (uint32_t) start64;
601 length32 = (uint32_t) length64;
srs5694978041c2009-09-21 20:51:47 -0400602 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e4ac11e2009-08-31 10:13:04 -0400603 if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
srs5694546a9c72010-01-26 16:00:26 -0500604 (partitions[i].partitionType != 0xEE)) {
srs5694e35eb1b2009-09-14 00:29:34 -0400605 DeletePartition(i);
srs5694978041c2009-09-21 20:51:47 -0400606 if (state == hybrid)
607 OptimizeEESize();
srs5694e4ac11e2009-08-31 10:13:04 -0400608 deleted = 1;
srs5694546a9c72010-01-26 16:00:26 -0500609 } // if (match found)
srs5694e4ac11e2009-08-31 10:13:04 -0400610 } // for i (partition scan)
611 } // if (hybrid & GPT partition < 2TiB)
612 return deleted;
613} // MBRData::DeleteByLocation()
614
615// Optimizes the size of the 0xEE (EFI GPT) partition
616void MBRData::OptimizeEESize(void) {
617 int i, typeFlag = 0;
618 uint32_t after;
619
620 for (i = 0; i < 4; i++) {
621 // Check for non-empty and non-0xEE partitions
622 if ((partitions[i].partitionType != 0xEE) && (partitions[i].partitionType != 0x00))
623 typeFlag++;
624 if (partitions[i].partitionType == 0xEE) {
625 // Blank space before this partition; fill it....
626 if (IsFree(partitions[i].firstLBA - 1)) {
627 partitions[i].firstLBA = FindFirstInFree(partitions[i].firstLBA - 1);
628 } // if
629 // Blank space after this partition; fill it....
630 after = partitions[i].firstLBA + partitions[i].lengthLBA;
631 if (IsFree(after)) {
632 partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
633 } // if free space after
634 } // if partition is 0xEE
srs5694e4ac11e2009-08-31 10:13:04 -0400635 } // for partition loop
srs5694978041c2009-09-21 20:51:47 -0400636 if (typeFlag == 0) { // No non-hybrid partitions found
637 MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
638 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400639} // MBRData::OptimizeEESize()
640
srs5694978041c2009-09-21 20:51:47 -0400641/****************************************
642 * *
643 * Functions to find data on free space *
644 * *
645 ****************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400646
srs5694c0ca8f82009-08-20 21:35:25 -0400647// Finds the first free space on the disk from start onward; returns 0
648// if none available....
649uint32_t MBRData::FindFirstAvailable(uint32_t start) {
650 uint32_t first;
651 uint32_t i;
srs5694978041c2009-09-21 20:51:47 -0400652 int firstMoved;
srs5694c0ca8f82009-08-20 21:35:25 -0400653
654 first = start;
655
656 // ...now search through all partitions; if first is within an
657 // existing partition, move it to the next sector after that
658 // partition and repeat. If first was moved, set firstMoved
659 // flag; repeat until firstMoved is not set, so as to catch
660 // cases where partitions are out of sequential order....
661 do {
662 firstMoved = 0;
663 for (i = 0; i < 4; i++) {
664 // Check if it's in the existing partition
665 if ((first >= partitions[i].firstLBA) &&
666 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
667 first = partitions[i].firstLBA + partitions[i].lengthLBA;
668 firstMoved = 1;
srs5694978041c2009-09-21 20:51:47 -0400669 } // if
srs5694c0ca8f82009-08-20 21:35:25 -0400670 } // for
671 } while (firstMoved == 1);
672 if (first >= diskSize)
673 first = 0;
674 return (first);
675} // MBRData::FindFirstAvailable()
676
srs5694e4ac11e2009-08-31 10:13:04 -0400677// Finds the last free sector on the disk from start forward.
srs5694c0ca8f82009-08-20 21:35:25 -0400678uint32_t MBRData::FindLastInFree(uint32_t start) {
679 uint32_t nearestStart;
680 uint32_t i;
681
srs5694978041c2009-09-21 20:51:47 -0400682 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs5694c0ca8f82009-08-20 21:35:25 -0400683 nearestStart = diskSize - 1;
684 else
685 nearestStart = UINT32_MAX - 1;
686 for (i = 0; i < 4; i++) {
687 if ((nearestStart > partitions[i].firstLBA) &&
688 (partitions[i].firstLBA > start)) {
689 nearestStart = partitions[i].firstLBA - 1;
690 } // if
691 } // for
692 return (nearestStart);
srs5694e4ac11e2009-08-31 10:13:04 -0400693} // MBRData::FindLastInFree()
694
695// Finds the first free sector on the disk from start backward.
696uint32_t MBRData::FindFirstInFree(uint32_t start) {
srs5694e35eb1b2009-09-14 00:29:34 -0400697 uint32_t bestLastLBA, thisLastLBA;
srs5694e4ac11e2009-08-31 10:13:04 -0400698 int i;
699
700 bestLastLBA = 1;
701 for (i = 0; i < 4; i++) {
702 thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
703 if (thisLastLBA > 0) thisLastLBA--;
704 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) {
705 bestLastLBA = thisLastLBA + 1;
706 } // if
707 } // for
708 return (bestLastLBA);
709} // MBRData::FindFirstInFree()
710
711// Returns 1 if the specified sector is unallocated, 0 if it's
712// allocated.
713int MBRData::IsFree(uint32_t sector) {
714 int i, isFree = 1;
715 uint32_t first, last;
716
717 for (i = 0; i < 4; i++) {
718 first = partitions[i].firstLBA;
srs5694978041c2009-09-21 20:51:47 -0400719 // Note: Weird two-line thing to avoid subtracting 1 from a 0 value
720 // for an unsigned int....
srs5694e4ac11e2009-08-31 10:13:04 -0400721 last = first + partitions[i].lengthLBA;
722 if (last > 0) last--;
723 if ((first <= sector) && (last >= sector))
724 isFree = 0;
725 } // for
726 return isFree;
727} // MBRData::IsFree()
srs5694c0ca8f82009-08-20 21:35:25 -0400728
srs5694978041c2009-09-21 20:51:47 -0400729/******************************************************
730 * *
731 * Functions that extract data on specific partitions *
732 * *
733 ******************************************************/
734
srs5694e7b4ff92009-08-18 13:16:10 -0400735uint8_t MBRData::GetStatus(int i) {
736 MBRRecord* thePart;
737 uint8_t retval;
738
739 thePart = GetPartition(i);
740 if (thePart != NULL)
741 retval = thePart->status;
742 else
743 retval = UINT8_C(0);
744 return retval;
745} // MBRData::GetStatus()
746
747uint8_t MBRData::GetType(int i) {
748 MBRRecord* thePart;
749 uint8_t retval;
750
751 thePart = GetPartition(i);
752 if (thePart != NULL)
753 retval = thePart->partitionType;
754 else
755 retval = UINT8_C(0);
756 return retval;
757} // MBRData::GetType()
758
759uint32_t MBRData::GetFirstSector(int i) {
760 MBRRecord* thePart;
761 uint32_t retval;
762
763 thePart = GetPartition(i);
764 if (thePart != NULL) {
765 retval = thePart->firstLBA;
766 } else
767 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400768 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400769} // MBRData::GetFirstSector()
770
771uint32_t MBRData::GetLength(int i) {
772 MBRRecord* thePart;
773 uint32_t retval;
774
775 thePart = GetPartition(i);
776 if (thePart != NULL) {
777 retval = thePart->lengthLBA;
778 } else
779 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400780 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400781} // MBRData::GetLength()
srs5694221e0872009-08-29 15:00:31 -0400782
783// Return the MBR data as a GPT partition....
784GPTPart MBRData::AsGPT(int i) {
785 MBRRecord* origPart;
786 GPTPart newPart;
787 uint8_t origType;
788 uint64_t firstSector, lastSector;
789 char tempStr[NAME_SIZE];
790
791 newPart.BlankPartition();
792 origPart = GetPartition(i);
793 if (origPart != NULL) {
794 origType = origPart->partitionType;
795
796 // don't convert extended, hybrid protective, or null (non-existent)
797 // partitions (Note similar protection is in GPTData::XFormPartitions(),
798 // but I want it here too in case I call this function in another
799 // context in the future....)
srs5694e35eb1b2009-09-14 00:29:34 -0400800 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694221e0872009-08-29 15:00:31 -0400801 (origType != 0x00) && (origType != 0xEE)) {
802 firstSector = (uint64_t) origPart->firstLBA;
803 newPart.SetFirstLBA(firstSector);
804 lastSector = firstSector + (uint64_t) origPart->lengthLBA;
805 if (lastSector > 0) lastSector--;
806 newPart.SetLastLBA(lastSector);
807 newPart.SetType(((uint16_t) origType) * 0x0100);
808 newPart.SetUniqueGUID(1);
809 newPart.SetAttributes(0);
srs5694546a9c72010-01-26 16:00:26 -0500810 newPart.SetName((unsigned char*) newPart.GetNameType().c_str());
srs5694978041c2009-09-21 20:51:47 -0400811 } // if not extended, protective, or non-existent
812 } // if (origPart != NULL)
srs5694221e0872009-08-29 15:00:31 -0400813 return newPart;
814} // MBRData::AsGPT()
srs5694978041c2009-09-21 20:51:47 -0400815
816/***********************
817 * *
818 * Protected functions *
819 * *
820 ***********************/
821
822// Return a pointer to a primary or logical partition, or NULL if
823// the partition is out of range....
824struct MBRRecord* MBRData::GetPartition(int i) {
825 MBRRecord* thePart = NULL;
826
827 if ((i >= 0) && (i < MAX_MBR_PARTS))
828 thePart = &partitions[i];
829 return thePart;
830} // GetPartition()