blob: f51d5c7cd09792bd687de493fe39ecd9e216e105 [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
73 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
74 ReadMBRData(fd);
75 } else {
76 allOK = 0;
77 } // if
78
79 close(fd);
80
81 if (allOK)
82 strcpy(device, deviceFilename);
83
84 return allOK;
85} // MBRData::ReadMBRData(char* deviceFilename)
86
srs5694978041c2009-09-21 20:51:47 -040087// Read data from MBR. If checkBlockSize == 1 (the default), the block
88// size is checked; otherwise it's set to the default (512 bytes).
89// Note that any extended partition(s) present will be explicitly stored
90// in the partitions[] array, along with their contained partitions; the
91// extended container partition(s) should be ignored by other functions.
srs5694221e0872009-08-29 15:00:31 -040092void MBRData::ReadMBRData(int fd, int checkBlockSize) {
srs5694978041c2009-09-21 20:51:47 -040093 int allOK = 1, i, j, logicalNum;
srs5694e7b4ff92009-08-18 13:16:10 -040094 int err;
srs5694221e0872009-08-29 15:00:31 -040095 TempMBR tempMBR;
srs5694e7b4ff92009-08-18 13:16:10 -040096
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
srs5694221e0872009-08-29 15:00:31 -0400100 err = lseek64(fd, 0, SEEK_SET);
srs56941e093722010-01-05 00:14:19 -0500101 err = myRead(fd, (char*) &tempMBR, 512);
srs5694221e0872009-08-29 15:00:31 -0400102 for (i = 0; i < 440; i++)
103 code[i] = tempMBR.code[i];
104 diskSignature = tempMBR.diskSignature;
105 nulls = tempMBR.nulls;
106 for (i = 0; i < 4; i++) {
107 partitions[i].status = tempMBR.partitions[i].status;
108 partitions[i].partitionType = tempMBR.partitions[i].partitionType;
109 partitions[i].firstLBA = tempMBR.partitions[i].firstLBA;
110 partitions[i].lengthLBA = tempMBR.partitions[i].lengthLBA;
111 for (j = 0; j < 3; j++) {
112 partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j];
113 partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j];
srs5694978041c2009-09-21 20:51:47 -0400114 } // for j... (reading parts of CHS geometry)
115 } // for i... (reading all four partitions)
srs5694221e0872009-08-29 15:00:31 -0400116 MBRSignature = tempMBR.MBRSignature;
srs56942a9f5da2009-08-26 00:48:01 -0400117
118 // Reverse the byte order, if necessary
119 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400120 ReverseBytes(&diskSignature, 4);
121 ReverseBytes(&nulls, 2);
122 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400123 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400124 ReverseBytes(&partitions[i].firstLBA, 4);
125 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400126 } // for
127 } // if
128
srs5694e7b4ff92009-08-18 13:16:10 -0400129 if (MBRSignature != MBR_SIGNATURE) {
130 allOK = 0;
131 state = invalid;
srs5694221e0872009-08-29 15:00:31 -0400132 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400133
134 // Find disk size
135 diskSize = disksize(fd, &err);
136
137 // Find block size
srs5694221e0872009-08-29 15:00:31 -0400138 if (checkBlockSize) {
srs5694e35eb1b2009-09-14 00:29:34 -0400139 blockSize = GetBlockSize(fd);
srs5694221e0872009-08-29 15:00:31 -0400140 } // if (checkBlockSize)
srs5694e7b4ff92009-08-18 13:16:10 -0400141
142 // Load logical partition data, if any is found....
143 if (allOK) {
144 for (i = 0; i < 4; i++) {
145 if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
srs5694978041c2009-09-21 20:51:47 -0400146 || (partitions[i].partitionType == 0x85)) {
srs5694e7b4ff92009-08-18 13:16:10 -0400147 // Found it, so call a recursive algorithm to load everything from them....
srs5694978041c2009-09-21 20:51:47 -0400148 logicalNum = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), 4);
149 if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
srs569486dd7842009-08-26 14:39:40 -0400150 allOK = 0;
151 fprintf(stderr, "Error reading logical partitions! List may be truncated!\n");
152 } // if maxLogicals valid
153 } // if primary partition is extended
154 } // for primary partition loop
srs5694e7b4ff92009-08-18 13:16:10 -0400155 if (allOK) { // Loaded logicals OK
156 state = mbr;
157 } else {
158 state = invalid;
159 } // if
160 } // if
161
162 /* Check to see if it's in GPT format.... */
163 if (allOK) {
164 for (i = 0; i < 4; i++) {
165 if (partitions[i].partitionType == UINT8_C(0xEE)) {
166 state = gpt;
srs56942a9f5da2009-08-26 00:48:01 -0400167 } // if
168 } // for
169 } // if
170
171 // If there's an EFI GPT partition, look for other partition types,
172 // to flag as hybrid
173 if (state == gpt) {
174 for (i = 0 ; i < 4; i++) {
175 if ((partitions[i].partitionType != UINT8_C(0xEE)) &&
176 (partitions[i].partitionType != UINT8_C(0x00)))
177 state = hybrid;
178 } // for
srs5694978041c2009-09-21 20:51:47 -0400179 } // if (hybrid detection code)
srs5694e7b4ff92009-08-18 13:16:10 -0400180} // MBRData::ReadMBRData(int fd)
181
srs5694978041c2009-09-21 20:51:47 -0400182// This is a recursive function to read all the logical partitions, following the
183// logical partition linked list from the disk and storing the basic data in the
184// partitions[] array. Returns last index to partitions[] used, or -1 if there was
185// a problem.
186// Parameters:
187// fd = file descriptor
188// extendedStart = LBA of the start of the extended partition
189// diskOffset = LBA offset WITHIN the extended partition of the one to be read
190// partNum = location in partitions[] array to store retrieved data
191int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
192 uint32_t diskOffset, int partNum) {
193 struct TempMBR ebr;
194 off_t offset;
195
196 // Check for a valid partition number. Note that partitions MAY be read into
197 // the area normally used by primary partitions, although the only calling
198 // function as of GPT fdisk version 0.5.0 doesn't do so.
199 if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
200 offset = (off_t) (extendedStart + diskOffset) * blockSize;
201 if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
202 fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
203 partNum = -1;
204 }
srs56941e093722010-01-05 00:14:19 -0500205 if (myRead(fd, (char*) &ebr, 512) != 512) { // Load the data....
srs5694978041c2009-09-21 20:51:47 -0400206 fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
207 (unsigned long) offset);
208 partNum = -1;
209 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
210 ReverseBytes(&ebr.MBRSignature, 2);
211 ReverseBytes(&ebr.partitions[0].firstLBA, 4);
212 ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
213 ReverseBytes(&ebr.partitions[1].firstLBA, 4);
214 ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
215 } // if/else/if
216
217 if (ebr.MBRSignature != MBR_SIGNATURE) {
218 partNum = -1;
219 fprintf(stderr, "MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
220 (unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
221 } // if
222
223 // Copy over the basic data....
224 partitions[partNum].status = ebr.partitions[0].status;
225 partitions[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
226 partitions[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
227 partitions[partNum].partitionType = ebr.partitions[0].partitionType;
228
229 // Find the next partition (if there is one) and recurse....
230 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) &&
231 (partNum < (MAX_MBR_PARTS - 1))) {
232 partNum = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA,
233 partNum + 1);
234 } else {
235 partNum++;
236 } // if another partition
237 } // Not enough space for all the logicals (or previous error encountered)
238 return (partNum);
239} // MBRData::ReadLogicalPart()
240
241// Write the MBR data to the default defined device. Note that this writes
242// ONLY the MBR itself, not the logical partition data.
srs5694e7b4ff92009-08-18 13:16:10 -0400243int MBRData::WriteMBRData(void) {
244 int allOK = 1, fd;
245
246 if ((fd = open(device, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
247 WriteMBRData(fd);
248 } else {
249 allOK = 0;
250 } // if/else
251 close(fd);
252 return allOK;
253} // MBRData::WriteMBRData(void)
254
255// Save the MBR data to a file. Note that this function writes ONLY the
256// MBR data, not the logical partitions (if any are defined).
257void MBRData::WriteMBRData(int fd) {
srs5694221e0872009-08-29 15:00:31 -0400258 int i, j;
259 TempMBR tempMBR;
srs56942a9f5da2009-08-26 00:48:01 -0400260
261 // Reverse the byte order, if necessary
262 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400263 ReverseBytes(&diskSignature, 4);
264 ReverseBytes(&nulls, 2);
265 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400266 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400267 ReverseBytes(&partitions[i].firstLBA, 4);
268 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400269 } // for
270 } // if
271
srs5694221e0872009-08-29 15:00:31 -0400272 // Copy MBR data to a 512-byte data structure for writing, to
273 // work around a FreeBSD limitation....
274 for (i = 0; i < 440; i++)
275 tempMBR.code[i] = code[i];
276 tempMBR.diskSignature = diskSignature;
277 tempMBR.nulls = nulls;
278 tempMBR.MBRSignature = MBRSignature;
279 for (i = 0; i < 4; i++) {
280 tempMBR.partitions[i].status = partitions[i].status;
281 tempMBR.partitions[i].partitionType = partitions[i].partitionType;
282 tempMBR.partitions[i].firstLBA = partitions[i].firstLBA;
283 tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA;
284 for (j = 0; j < 3; j++) {
285 tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j];
286 tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j];
287 } // for j...
288 } // for i...
289
290 // Now write that data structure...
srs5694e35eb1b2009-09-14 00:29:34 -0400291 lseek64(fd, 0, SEEK_SET);
srs56941e093722010-01-05 00:14:19 -0500292 if (myWrite(fd, (char*) &tempMBR, 512) != 512) {
srs56945d58fe02010-01-03 20:57:08 -0500293 fprintf(stderr, "Warning! Error %d when saving MBR!\n", errno);
294 } // if
srs5694221e0872009-08-29 15:00:31 -0400295
srs5694221e0872009-08-29 15:00:31 -0400296 // Reverse the byte order back, if necessary
srs56942a9f5da2009-08-26 00:48:01 -0400297 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400298 ReverseBytes(&diskSignature, 4);
299 ReverseBytes(&nulls, 2);
300 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400301 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400302 ReverseBytes(&partitions[i].firstLBA, 4);
303 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400304 } // for
305 }// if
srs5694e7b4ff92009-08-18 13:16:10 -0400306} // MBRData::WriteMBRData(int fd)
307
srs5694ba00fed2010-01-12 18:18:36 -0500308int MBRData::WriteMBRData(char* deviceFilename) {
309 strcpy(device, deviceFilename);
310 return WriteMBRData();
311} // MBRData::WriteMBRData(char* deviceFilename)
312
srs5694978041c2009-09-21 20:51:47 -0400313/********************************************
314 * *
315 * Functions that display data for the user *
316 * *
317 ********************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400318
319// Show the MBR data to the user....
320void MBRData::DisplayMBRData(void) {
321 int i;
322 char tempStr[255];
srs5694e19ba092009-08-24 14:10:35 -0400323 char bootCode;
srs5694e7b4ff92009-08-18 13:16:10 -0400324
325 printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
326 printf("MBR partitions:\n");
srs5694e19ba092009-08-24 14:10:35 -0400327 printf("Number\t Boot\t Start (sector)\t Length (sectors)\tType\n");
srs5694978041c2009-09-21 20:51:47 -0400328 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e7b4ff92009-08-18 13:16:10 -0400329 if (partitions[i].lengthLBA != 0) {
srs5694e19ba092009-08-24 14:10:35 -0400330 if (partitions[i].status && 0x80) // it's bootable
srs5694978041c2009-09-21 20:51:47 -0400331 bootCode = '*';
srs5694e19ba092009-08-24 14:10:35 -0400332 else
333 bootCode = ' ';
334 printf("%4d\t %c\t%13lu\t%15lu \t0x%02X\n", i + 1, bootCode,
335 (unsigned long) partitions[i].firstLBA,
srs5694e7b4ff92009-08-18 13:16:10 -0400336 (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
337 } // if
338 } // for
srs5694978041c2009-09-21 20:51:47 -0400339 printf("\nDisk size is %llu sectors (%s)\n", (unsigned long long) diskSize,
srs5694e7b4ff92009-08-18 13:16:10 -0400340 BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
341} // MBRData::DisplayMBRData()
342
srs5694978041c2009-09-21 20:51:47 -0400343// Displays the state, as a word, on stdout. Used for debugging & to
344// tell the user about the MBR state when the program launches....
345void MBRData::ShowState(void) {
346 switch (state) {
347 case invalid:
348 printf(" MBR: not present\n");
349 break;
350 case gpt:
351 printf(" MBR: protective\n");
352 break;
353 case hybrid:
354 printf(" MBR: hybrid\n");
355 break;
356 case mbr:
357 printf(" MBR: MBR only\n");
358 break;
359 default:
360 printf("\a MBR: unknown -- bug!\n");
361 break;
362 } // switch
363} // MBRData::ShowState()
364
365/*********************************************************************
366 * *
367 * Functions that set or get disk metadata (CHS geometry, disk size, *
368 * etc.) *
369 * *
370 *********************************************************************/
371
372// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function.
373// Note that this only sets the heads and sectors; the number of
374// cylinders is determined by these values and the disk size.
375void MBRData::SetCHSGeom(uint32_t h, uint32_t s) {
376 if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
377 numHeads = h;
378 numSecspTrack = s;
379 } else {
380 printf("Warning! Attempt to set invalid CHS geometry!\n");
381 } // if/else
382} // MBRData::SetCHSGeom()
383
384// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
385// was within the range that can be expressed by CHS (including 0, for an
386// empty partition), 0 if the value is outside that range, and -1 if chs is
387// invalid.
388int MBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
389 uint64_t cylinder, head, sector; // all numbered from 0
390 uint64_t remainder;
391 int retval = 1;
392 int done = 0;
393
394 if (chs != NULL) {
395 // Special case: In case of 0 LBA value, zero out CHS values....
396 if (lba == 0) {
397 chs[0] = chs[1] = chs[2] = UINT8_C(0);
398 done = 1;
399 } // if
400 // If LBA value is too large for CHS, max out CHS values....
401 if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
402 chs[0] = 254;
403 chs[1] = chs[2] = 255;
404 done = 1;
405 retval = 0;
406 } // if
407 // If neither of the above applies, compute CHS values....
408 if (!done) {
409 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
410 remainder = lba - (cylinder * numHeads * numSecspTrack);
411 head = remainder / numSecspTrack;
412 remainder -= head * numSecspTrack;
413 sector = remainder;
414 if (head < numHeads)
415 chs[0] = head;
416 else
417 retval = 0;
418 if (sector < numSecspTrack) {
419 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
420 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
421 } else {
422 retval = 0;
423 } // if/else
424 } // if value is expressible and non-0
425 } else { // Invalid (NULL) chs pointer
426 retval = -1;
427 } // if CHS pointer valid
428 return (retval);
429} // MBRData::LBAtoCHS()
430
431/*****************************************************
432 * *
433 * Functions to create, delete, or change partitions *
434 * *
435 *****************************************************/
436
437// Empty all data. Meant mainly for calling by constructors, but it's also
438// used by the hybrid MBR functions in the GPTData class.
439void MBRData::EmptyMBR(int clearBootloader) {
440 int i;
441
442 // Zero out the boot loader section, the disk signature, and the
443 // 2-byte nulls area only if requested to do so. (This is the
444 // default.)
445 if (clearBootloader == 1) {
446 for (i = 0; i < 440; i++)
447 code[i] = 0;
448 diskSignature = (uint32_t) rand();
449 nulls = 0;
450 } // if
451
452 // Blank out the partitions
453 for (i = 0; i < MAX_MBR_PARTS; i++) {
454 partitions[i].status = UINT8_C(0);
455 partitions[i].firstSector[0] = UINT8_C(0);
456 partitions[i].firstSector[1] = UINT8_C(0);
457 partitions[i].firstSector[2] = UINT8_C(0);
458 partitions[i].partitionType = UINT8_C(0);
459 partitions[i].lastSector[0] = UINT8_C(0);
460 partitions[i].lastSector[1] = UINT8_C(0);
461 partitions[i].lastSector[2] = UINT8_C(0);
462 partitions[i].firstLBA = UINT32_C(0);
463 partitions[i].lengthLBA = UINT32_C(0);
464 } // for
465 MBRSignature = MBR_SIGNATURE;
466} // MBRData::EmptyMBR()
467
srs5694221e0872009-08-29 15:00:31 -0400468// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
469void MBRData::MakeProtectiveMBR(int clearBoot) {
srs5694978041c2009-09-21 20:51:47 -0400470
471 EmptyMBR(clearBoot);
srs5694e7b4ff92009-08-18 13:16:10 -0400472
473 // Initialize variables
474 nulls = 0;
475 MBRSignature = MBR_SIGNATURE;
476
477 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
478
479 // Write CHS data. This maxes out the use of the disk, as much as
480 // possible -- even to the point of exceeding the capacity of sub-8GB
481 // disks. The EFI spec says to use 0xffffff as the ending value,
482 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
483 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
484 // value in for the FIRST sector, too!
485 partitions[0].firstSector[0] = UINT8_C(0);
486 partitions[0].firstSector[1] = UINT8_C(1);
487 partitions[0].firstSector[2] = UINT8_C(0);
488 partitions[0].lastSector[0] = UINT8_C(255);
489 partitions[0].lastSector[1] = UINT8_C(255);
490 partitions[0].lastSector[2] = UINT8_C(255);
491
492 partitions[0].partitionType = UINT8_C(0xEE);
493 partitions[0].firstLBA = UINT32_C(1);
494 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
srs5694978041c2009-09-21 20:51:47 -0400495 partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
srs5694e7b4ff92009-08-18 13:16:10 -0400496 } else { // disk is too big to represent, so fake it...
497 partitions[0].lengthLBA = UINT32_MAX;
498 } // if/else
499
srs5694e7b4ff92009-08-18 13:16:10 -0400500 state = gpt;
501} // MBRData::MakeProtectiveMBR()
502
srs5694978041c2009-09-21 20:51:47 -0400503// Create a partition of the specified number, starting LBA, and
504// length. This function does *NO* error checking, so it's possible
505// to seriously screw up a partition table using this function!
506// Note: This function should NOT be used to create the 0xEE partition
507// in a conventional GPT configuration, since that partition has
508// specific size requirements that this function won't handle. It may
509// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
510// since those toss the rulebook away anyhow....
511void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
512 int bootable) {
513 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
514 partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
515 partitions[num].firstSector[0] = UINT8_C(0);
516 partitions[num].firstSector[1] = UINT8_C(0);
517 partitions[num].firstSector[2] = UINT8_C(0);
518 partitions[num].partitionType = (uint8_t) type;
519 partitions[num].lastSector[0] = UINT8_C(0);
520 partitions[num].lastSector[1] = UINT8_C(0);
521 partitions[num].lastSector[2] = UINT8_C(0);
522 partitions[num].firstLBA = start;
523 partitions[num].lengthLBA = length;
524 // If this is a "real" partition, set its CHS geometry
525 if (length > 0) {
526 LBAtoCHS((uint64_t) start, partitions[num].firstSector);
527 LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
528 } // if (length > 0)
529 } // if valid partition number
530} // MBRData::MakePart()
531
srs5694c0ca8f82009-08-20 21:35:25 -0400532// Create a partition that fills the most available space. Returns
533// 1 if partition was created, 0 otherwise. Intended for use in
534// creating hybrid MBRs.
535int MBRData::MakeBiggestPart(int i, int type) {
536 uint32_t start = UINT32_C(1); // starting point for each search
537 uint32_t firstBlock; // first block in a segment
538 uint32_t lastBlock; // last block in a segment
539 uint32_t segmentSize; // size of segment in blocks
540 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
541 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
542 int found = 0;
543
544 do {
545 firstBlock = FindFirstAvailable(start);
546 if (firstBlock != UINT32_C(0)) { // something's free...
547 lastBlock = FindLastInFree(firstBlock);
548 segmentSize = lastBlock - firstBlock + UINT32_C(1);
srs5694978041c2009-09-21 20:51:47 -0400549 if (segmentSize > selectedSize) {
srs5694c0ca8f82009-08-20 21:35:25 -0400550 selectedSize = segmentSize;
srs5694978041c2009-09-21 20:51:47 -0400551 selectedSegment = firstBlock;
552 } // if
553 start = lastBlock + 1;
srs5694c0ca8f82009-08-20 21:35:25 -0400554 } // if
555 } while (firstBlock != 0);
556 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
557 found = 1;
558 MakePart(i, selectedSegment, selectedSize, type, 0);
559 } else {
560 found = 0;
561 } // if/else
562 return found;
563} // MBRData::MakeBiggestPart(int i)
564
srs5694e35eb1b2009-09-14 00:29:34 -0400565// Delete partition #i
566void MBRData::DeletePartition(int i) {
567 int j;
568
569 partitions[i].firstLBA = UINT32_C(0);
570 partitions[i].lengthLBA = UINT32_C(0);
571 partitions[i].status = UINT8_C(0);
572 partitions[i].partitionType = UINT8_C(0);
573 for (j = 0; j < 3; j++) {
574 partitions[i].firstSector[j] = UINT8_C(0);
575 partitions[i].lastSector[j] = UINT8_C(0);
576 } // for j (CHS data blanking)
577} // MBRData::DeletePartition()
578
srs5694e4ac11e2009-08-31 10:13:04 -0400579// Delete a partition if one exists at the specified location.
580// Returns 1 if a partition was deleted, 0 otherwise....
581// Used to help keep GPT & hybrid MBR partitions in sync....
582int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
583 uint32_t start32, length32;
srs5694978041c2009-09-21 20:51:47 -0400584 int i, deleted = 0;
srs5694e4ac11e2009-08-31 10:13:04 -0400585
srs5694978041c2009-09-21 20:51:47 -0400586 if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400587 start32 = (uint32_t) start64;
588 length32 = (uint32_t) length64;
srs5694978041c2009-09-21 20:51:47 -0400589 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e4ac11e2009-08-31 10:13:04 -0400590 if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
srs5694978041c2009-09-21 20:51:47 -0400591 (partitions[i].partitionType != 0xEE)) {
srs5694e35eb1b2009-09-14 00:29:34 -0400592 DeletePartition(i);
srs5694978041c2009-09-21 20:51:47 -0400593 if (state == hybrid)
594 OptimizeEESize();
srs5694e4ac11e2009-08-31 10:13:04 -0400595 deleted = 1;
srs5694978041c2009-09-21 20:51:47 -0400596 } // if (match found)
srs5694e4ac11e2009-08-31 10:13:04 -0400597 } // for i (partition scan)
598 } // if (hybrid & GPT partition < 2TiB)
599 return deleted;
600} // MBRData::DeleteByLocation()
601
602// Optimizes the size of the 0xEE (EFI GPT) partition
603void MBRData::OptimizeEESize(void) {
604 int i, typeFlag = 0;
605 uint32_t after;
606
607 for (i = 0; i < 4; i++) {
608 // Check for non-empty and non-0xEE partitions
609 if ((partitions[i].partitionType != 0xEE) && (partitions[i].partitionType != 0x00))
610 typeFlag++;
611 if (partitions[i].partitionType == 0xEE) {
612 // Blank space before this partition; fill it....
613 if (IsFree(partitions[i].firstLBA - 1)) {
614 partitions[i].firstLBA = FindFirstInFree(partitions[i].firstLBA - 1);
615 } // if
616 // Blank space after this partition; fill it....
617 after = partitions[i].firstLBA + partitions[i].lengthLBA;
618 if (IsFree(after)) {
619 partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
620 } // if free space after
621 } // if partition is 0xEE
srs5694e4ac11e2009-08-31 10:13:04 -0400622 } // for partition loop
srs5694978041c2009-09-21 20:51:47 -0400623 if (typeFlag == 0) { // No non-hybrid partitions found
624 MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
625 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400626} // MBRData::OptimizeEESize()
627
srs5694978041c2009-09-21 20:51:47 -0400628/****************************************
629 * *
630 * Functions to find data on free space *
631 * *
632 ****************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400633
srs5694c0ca8f82009-08-20 21:35:25 -0400634// Finds the first free space on the disk from start onward; returns 0
635// if none available....
636uint32_t MBRData::FindFirstAvailable(uint32_t start) {
637 uint32_t first;
638 uint32_t i;
srs5694978041c2009-09-21 20:51:47 -0400639 int firstMoved;
srs5694c0ca8f82009-08-20 21:35:25 -0400640
641 first = start;
642
643 // ...now search through all partitions; if first is within an
644 // existing partition, move it to the next sector after that
645 // partition and repeat. If first was moved, set firstMoved
646 // flag; repeat until firstMoved is not set, so as to catch
647 // cases where partitions are out of sequential order....
648 do {
649 firstMoved = 0;
650 for (i = 0; i < 4; i++) {
651 // Check if it's in the existing partition
652 if ((first >= partitions[i].firstLBA) &&
653 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
654 first = partitions[i].firstLBA + partitions[i].lengthLBA;
655 firstMoved = 1;
srs5694978041c2009-09-21 20:51:47 -0400656 } // if
srs5694c0ca8f82009-08-20 21:35:25 -0400657 } // for
658 } while (firstMoved == 1);
659 if (first >= diskSize)
660 first = 0;
661 return (first);
662} // MBRData::FindFirstAvailable()
663
srs5694e4ac11e2009-08-31 10:13:04 -0400664// Finds the last free sector on the disk from start forward.
srs5694c0ca8f82009-08-20 21:35:25 -0400665uint32_t MBRData::FindLastInFree(uint32_t start) {
666 uint32_t nearestStart;
667 uint32_t i;
668
srs5694978041c2009-09-21 20:51:47 -0400669 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs5694c0ca8f82009-08-20 21:35:25 -0400670 nearestStart = diskSize - 1;
671 else
672 nearestStart = UINT32_MAX - 1;
673 for (i = 0; i < 4; i++) {
674 if ((nearestStart > partitions[i].firstLBA) &&
675 (partitions[i].firstLBA > start)) {
676 nearestStart = partitions[i].firstLBA - 1;
677 } // if
678 } // for
679 return (nearestStart);
srs5694e4ac11e2009-08-31 10:13:04 -0400680} // MBRData::FindLastInFree()
681
682// Finds the first free sector on the disk from start backward.
683uint32_t MBRData::FindFirstInFree(uint32_t start) {
srs5694e35eb1b2009-09-14 00:29:34 -0400684 uint32_t bestLastLBA, thisLastLBA;
srs5694e4ac11e2009-08-31 10:13:04 -0400685 int i;
686
687 bestLastLBA = 1;
688 for (i = 0; i < 4; i++) {
689 thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
690 if (thisLastLBA > 0) thisLastLBA--;
691 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) {
692 bestLastLBA = thisLastLBA + 1;
693 } // if
694 } // for
695 return (bestLastLBA);
696} // MBRData::FindFirstInFree()
697
698// Returns 1 if the specified sector is unallocated, 0 if it's
699// allocated.
700int MBRData::IsFree(uint32_t sector) {
701 int i, isFree = 1;
702 uint32_t first, last;
703
704 for (i = 0; i < 4; i++) {
705 first = partitions[i].firstLBA;
srs5694978041c2009-09-21 20:51:47 -0400706 // Note: Weird two-line thing to avoid subtracting 1 from a 0 value
707 // for an unsigned int....
srs5694e4ac11e2009-08-31 10:13:04 -0400708 last = first + partitions[i].lengthLBA;
709 if (last > 0) last--;
710 if ((first <= sector) && (last >= sector))
711 isFree = 0;
712 } // for
713 return isFree;
714} // MBRData::IsFree()
srs5694c0ca8f82009-08-20 21:35:25 -0400715
srs5694978041c2009-09-21 20:51:47 -0400716/******************************************************
717 * *
718 * Functions that extract data on specific partitions *
719 * *
720 ******************************************************/
721
srs5694e7b4ff92009-08-18 13:16:10 -0400722uint8_t MBRData::GetStatus(int i) {
723 MBRRecord* thePart;
724 uint8_t retval;
725
726 thePart = GetPartition(i);
727 if (thePart != NULL)
728 retval = thePart->status;
729 else
730 retval = UINT8_C(0);
731 return retval;
732} // MBRData::GetStatus()
733
734uint8_t MBRData::GetType(int i) {
735 MBRRecord* thePart;
736 uint8_t retval;
737
738 thePart = GetPartition(i);
739 if (thePart != NULL)
740 retval = thePart->partitionType;
741 else
742 retval = UINT8_C(0);
743 return retval;
744} // MBRData::GetType()
745
746uint32_t MBRData::GetFirstSector(int i) {
747 MBRRecord* thePart;
748 uint32_t retval;
749
750 thePart = GetPartition(i);
751 if (thePart != NULL) {
752 retval = thePart->firstLBA;
753 } else
754 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400755 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400756} // MBRData::GetFirstSector()
757
758uint32_t MBRData::GetLength(int i) {
759 MBRRecord* thePart;
760 uint32_t retval;
761
762 thePart = GetPartition(i);
763 if (thePart != NULL) {
764 retval = thePart->lengthLBA;
765 } else
766 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400767 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400768} // MBRData::GetLength()
srs5694221e0872009-08-29 15:00:31 -0400769
770// Return the MBR data as a GPT partition....
771GPTPart MBRData::AsGPT(int i) {
772 MBRRecord* origPart;
773 GPTPart newPart;
774 uint8_t origType;
775 uint64_t firstSector, lastSector;
776 char tempStr[NAME_SIZE];
777
778 newPart.BlankPartition();
779 origPart = GetPartition(i);
780 if (origPart != NULL) {
781 origType = origPart->partitionType;
782
783 // don't convert extended, hybrid protective, or null (non-existent)
784 // partitions (Note similar protection is in GPTData::XFormPartitions(),
785 // but I want it here too in case I call this function in another
786 // context in the future....)
srs5694e35eb1b2009-09-14 00:29:34 -0400787 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694221e0872009-08-29 15:00:31 -0400788 (origType != 0x00) && (origType != 0xEE)) {
789 firstSector = (uint64_t) origPart->firstLBA;
790 newPart.SetFirstLBA(firstSector);
791 lastSector = firstSector + (uint64_t) origPart->lengthLBA;
792 if (lastSector > 0) lastSector--;
793 newPart.SetLastLBA(lastSector);
794 newPart.SetType(((uint16_t) origType) * 0x0100);
795 newPart.SetUniqueGUID(1);
796 newPart.SetAttributes(0);
797 newPart.SetName((unsigned char*) newPart.GetNameType(tempStr));
srs5694978041c2009-09-21 20:51:47 -0400798 } // if not extended, protective, or non-existent
799 } // if (origPart != NULL)
srs5694221e0872009-08-29 15:00:31 -0400800 return newPart;
801} // MBRData::AsGPT()
srs5694978041c2009-09-21 20:51:47 -0400802
803/***********************
804 * *
805 * Protected functions *
806 * *
807 ***********************/
808
809// Return a pointer to a primary or logical partition, or NULL if
810// the partition is out of range....
811struct MBRRecord* MBRData::GetPartition(int i) {
812 MBRRecord* thePart = NULL;
813
814 if ((i >= 0) && (i < MAX_MBR_PARTS))
815 thePart = &partitions[i];
816 return thePart;
817} // GetPartition()