blob: 2be731b28b32a9279dc21461d12062f3201a964a [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);
101 err = read(fd, &tempMBR, 512);
102 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 }
205 if (read(fd, &ebr, 512) != 512) { // Load the data....
206 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);
srs5694221e0872009-08-29 15:00:31 -0400292 write(fd, &tempMBR, 512);
293
srs5694221e0872009-08-29 15:00:31 -0400294 // Reverse the byte order back, if necessary
srs56942a9f5da2009-08-26 00:48:01 -0400295 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400296 ReverseBytes(&diskSignature, 4);
297 ReverseBytes(&nulls, 2);
298 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400299 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400300 ReverseBytes(&partitions[i].firstLBA, 4);
301 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400302 } // for
303 }// if
srs5694e7b4ff92009-08-18 13:16:10 -0400304} // MBRData::WriteMBRData(int fd)
305
srs5694978041c2009-09-21 20:51:47 -0400306/********************************************
307 * *
308 * Functions that display data for the user *
309 * *
310 ********************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400311
312// Show the MBR data to the user....
313void MBRData::DisplayMBRData(void) {
314 int i;
315 char tempStr[255];
srs5694e19ba092009-08-24 14:10:35 -0400316 char bootCode;
srs5694e7b4ff92009-08-18 13:16:10 -0400317
318 printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
319 printf("MBR partitions:\n");
srs5694e19ba092009-08-24 14:10:35 -0400320 printf("Number\t Boot\t Start (sector)\t Length (sectors)\tType\n");
srs5694978041c2009-09-21 20:51:47 -0400321 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e7b4ff92009-08-18 13:16:10 -0400322 if (partitions[i].lengthLBA != 0) {
srs5694e19ba092009-08-24 14:10:35 -0400323 if (partitions[i].status && 0x80) // it's bootable
srs5694978041c2009-09-21 20:51:47 -0400324 bootCode = '*';
srs5694e19ba092009-08-24 14:10:35 -0400325 else
326 bootCode = ' ';
327 printf("%4d\t %c\t%13lu\t%15lu \t0x%02X\n", i + 1, bootCode,
328 (unsigned long) partitions[i].firstLBA,
srs5694e7b4ff92009-08-18 13:16:10 -0400329 (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
330 } // if
331 } // for
srs5694978041c2009-09-21 20:51:47 -0400332 printf("\nDisk size is %llu sectors (%s)\n", (unsigned long long) diskSize,
srs5694e7b4ff92009-08-18 13:16:10 -0400333 BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
334} // MBRData::DisplayMBRData()
335
srs5694978041c2009-09-21 20:51:47 -0400336// Displays the state, as a word, on stdout. Used for debugging & to
337// tell the user about the MBR state when the program launches....
338void MBRData::ShowState(void) {
339 switch (state) {
340 case invalid:
341 printf(" MBR: not present\n");
342 break;
343 case gpt:
344 printf(" MBR: protective\n");
345 break;
346 case hybrid:
347 printf(" MBR: hybrid\n");
348 break;
349 case mbr:
350 printf(" MBR: MBR only\n");
351 break;
352 default:
353 printf("\a MBR: unknown -- bug!\n");
354 break;
355 } // switch
356} // MBRData::ShowState()
357
358/*********************************************************************
359 * *
360 * Functions that set or get disk metadata (CHS geometry, disk size, *
361 * etc.) *
362 * *
363 *********************************************************************/
364
365// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function.
366// Note that this only sets the heads and sectors; the number of
367// cylinders is determined by these values and the disk size.
368void MBRData::SetCHSGeom(uint32_t h, uint32_t s) {
369 if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
370 numHeads = h;
371 numSecspTrack = s;
372 } else {
373 printf("Warning! Attempt to set invalid CHS geometry!\n");
374 } // if/else
375} // MBRData::SetCHSGeom()
376
377// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
378// was within the range that can be expressed by CHS (including 0, for an
379// empty partition), 0 if the value is outside that range, and -1 if chs is
380// invalid.
381int MBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
382 uint64_t cylinder, head, sector; // all numbered from 0
383 uint64_t remainder;
384 int retval = 1;
385 int done = 0;
386
387 if (chs != NULL) {
388 // Special case: In case of 0 LBA value, zero out CHS values....
389 if (lba == 0) {
390 chs[0] = chs[1] = chs[2] = UINT8_C(0);
391 done = 1;
392 } // if
393 // If LBA value is too large for CHS, max out CHS values....
394 if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
395 chs[0] = 254;
396 chs[1] = chs[2] = 255;
397 done = 1;
398 retval = 0;
399 } // if
400 // If neither of the above applies, compute CHS values....
401 if (!done) {
402 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
403 remainder = lba - (cylinder * numHeads * numSecspTrack);
404 head = remainder / numSecspTrack;
405 remainder -= head * numSecspTrack;
406 sector = remainder;
407 if (head < numHeads)
408 chs[0] = head;
409 else
410 retval = 0;
411 if (sector < numSecspTrack) {
412 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
413 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
414 } else {
415 retval = 0;
416 } // if/else
417 } // if value is expressible and non-0
418 } else { // Invalid (NULL) chs pointer
419 retval = -1;
420 } // if CHS pointer valid
421 return (retval);
422} // MBRData::LBAtoCHS()
423
424/*****************************************************
425 * *
426 * Functions to create, delete, or change partitions *
427 * *
428 *****************************************************/
429
430// Empty all data. Meant mainly for calling by constructors, but it's also
431// used by the hybrid MBR functions in the GPTData class.
432void MBRData::EmptyMBR(int clearBootloader) {
433 int i;
434
435 // Zero out the boot loader section, the disk signature, and the
436 // 2-byte nulls area only if requested to do so. (This is the
437 // default.)
438 if (clearBootloader == 1) {
439 for (i = 0; i < 440; i++)
440 code[i] = 0;
441 diskSignature = (uint32_t) rand();
442 nulls = 0;
443 } // if
444
445 // Blank out the partitions
446 for (i = 0; i < MAX_MBR_PARTS; i++) {
447 partitions[i].status = UINT8_C(0);
448 partitions[i].firstSector[0] = UINT8_C(0);
449 partitions[i].firstSector[1] = UINT8_C(0);
450 partitions[i].firstSector[2] = UINT8_C(0);
451 partitions[i].partitionType = UINT8_C(0);
452 partitions[i].lastSector[0] = UINT8_C(0);
453 partitions[i].lastSector[1] = UINT8_C(0);
454 partitions[i].lastSector[2] = UINT8_C(0);
455 partitions[i].firstLBA = UINT32_C(0);
456 partitions[i].lengthLBA = UINT32_C(0);
457 } // for
458 MBRSignature = MBR_SIGNATURE;
459} // MBRData::EmptyMBR()
460
srs5694221e0872009-08-29 15:00:31 -0400461// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
462void MBRData::MakeProtectiveMBR(int clearBoot) {
srs5694978041c2009-09-21 20:51:47 -0400463
464 EmptyMBR(clearBoot);
srs5694e7b4ff92009-08-18 13:16:10 -0400465
466 // Initialize variables
467 nulls = 0;
468 MBRSignature = MBR_SIGNATURE;
469
470 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
471
472 // Write CHS data. This maxes out the use of the disk, as much as
473 // possible -- even to the point of exceeding the capacity of sub-8GB
474 // disks. The EFI spec says to use 0xffffff as the ending value,
475 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
476 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
477 // value in for the FIRST sector, too!
478 partitions[0].firstSector[0] = UINT8_C(0);
479 partitions[0].firstSector[1] = UINT8_C(1);
480 partitions[0].firstSector[2] = UINT8_C(0);
481 partitions[0].lastSector[0] = UINT8_C(255);
482 partitions[0].lastSector[1] = UINT8_C(255);
483 partitions[0].lastSector[2] = UINT8_C(255);
484
485 partitions[0].partitionType = UINT8_C(0xEE);
486 partitions[0].firstLBA = UINT32_C(1);
487 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
srs5694978041c2009-09-21 20:51:47 -0400488 partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
srs5694e7b4ff92009-08-18 13:16:10 -0400489 } else { // disk is too big to represent, so fake it...
490 partitions[0].lengthLBA = UINT32_MAX;
491 } // if/else
492
srs5694e7b4ff92009-08-18 13:16:10 -0400493 state = gpt;
494} // MBRData::MakeProtectiveMBR()
495
srs5694978041c2009-09-21 20:51:47 -0400496// Create a partition of the specified number, starting LBA, and
497// length. This function does *NO* error checking, so it's possible
498// to seriously screw up a partition table using this function!
499// Note: This function should NOT be used to create the 0xEE partition
500// in a conventional GPT configuration, since that partition has
501// specific size requirements that this function won't handle. It may
502// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
503// since those toss the rulebook away anyhow....
504void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
505 int bootable) {
506 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
507 partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
508 partitions[num].firstSector[0] = UINT8_C(0);
509 partitions[num].firstSector[1] = UINT8_C(0);
510 partitions[num].firstSector[2] = UINT8_C(0);
511 partitions[num].partitionType = (uint8_t) type;
512 partitions[num].lastSector[0] = UINT8_C(0);
513 partitions[num].lastSector[1] = UINT8_C(0);
514 partitions[num].lastSector[2] = UINT8_C(0);
515 partitions[num].firstLBA = start;
516 partitions[num].lengthLBA = length;
517 // If this is a "real" partition, set its CHS geometry
518 if (length > 0) {
519 LBAtoCHS((uint64_t) start, partitions[num].firstSector);
520 LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
521 } // if (length > 0)
522 } // if valid partition number
523} // MBRData::MakePart()
524
srs5694c0ca8f82009-08-20 21:35:25 -0400525// Create a partition that fills the most available space. Returns
526// 1 if partition was created, 0 otherwise. Intended for use in
527// creating hybrid MBRs.
528int MBRData::MakeBiggestPart(int i, int type) {
529 uint32_t start = UINT32_C(1); // starting point for each search
530 uint32_t firstBlock; // first block in a segment
531 uint32_t lastBlock; // last block in a segment
532 uint32_t segmentSize; // size of segment in blocks
533 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
534 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
535 int found = 0;
536
537 do {
538 firstBlock = FindFirstAvailable(start);
539 if (firstBlock != UINT32_C(0)) { // something's free...
540 lastBlock = FindLastInFree(firstBlock);
541 segmentSize = lastBlock - firstBlock + UINT32_C(1);
srs5694978041c2009-09-21 20:51:47 -0400542 if (segmentSize > selectedSize) {
srs5694c0ca8f82009-08-20 21:35:25 -0400543 selectedSize = segmentSize;
srs5694978041c2009-09-21 20:51:47 -0400544 selectedSegment = firstBlock;
545 } // if
546 start = lastBlock + 1;
srs5694c0ca8f82009-08-20 21:35:25 -0400547 } // if
548 } while (firstBlock != 0);
549 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
550 found = 1;
551 MakePart(i, selectedSegment, selectedSize, type, 0);
552 } else {
553 found = 0;
554 } // if/else
555 return found;
556} // MBRData::MakeBiggestPart(int i)
557
srs5694e35eb1b2009-09-14 00:29:34 -0400558// Delete partition #i
559void MBRData::DeletePartition(int i) {
560 int j;
561
562 partitions[i].firstLBA = UINT32_C(0);
563 partitions[i].lengthLBA = UINT32_C(0);
564 partitions[i].status = UINT8_C(0);
565 partitions[i].partitionType = UINT8_C(0);
566 for (j = 0; j < 3; j++) {
567 partitions[i].firstSector[j] = UINT8_C(0);
568 partitions[i].lastSector[j] = UINT8_C(0);
569 } // for j (CHS data blanking)
570} // MBRData::DeletePartition()
571
srs5694e4ac11e2009-08-31 10:13:04 -0400572// Delete a partition if one exists at the specified location.
573// Returns 1 if a partition was deleted, 0 otherwise....
574// Used to help keep GPT & hybrid MBR partitions in sync....
575int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
576 uint32_t start32, length32;
srs5694978041c2009-09-21 20:51:47 -0400577 int i, deleted = 0;
srs5694e4ac11e2009-08-31 10:13:04 -0400578
srs5694978041c2009-09-21 20:51:47 -0400579 if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400580 start32 = (uint32_t) start64;
581 length32 = (uint32_t) length64;
srs5694978041c2009-09-21 20:51:47 -0400582 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694e4ac11e2009-08-31 10:13:04 -0400583 if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
srs5694978041c2009-09-21 20:51:47 -0400584 (partitions[i].partitionType != 0xEE)) {
srs5694e35eb1b2009-09-14 00:29:34 -0400585 DeletePartition(i);
srs5694978041c2009-09-21 20:51:47 -0400586 if (state == hybrid)
587 OptimizeEESize();
srs5694e4ac11e2009-08-31 10:13:04 -0400588 deleted = 1;
srs5694978041c2009-09-21 20:51:47 -0400589 } // if (match found)
srs5694e4ac11e2009-08-31 10:13:04 -0400590 } // for i (partition scan)
591 } // if (hybrid & GPT partition < 2TiB)
592 return deleted;
593} // MBRData::DeleteByLocation()
594
595// Optimizes the size of the 0xEE (EFI GPT) partition
596void MBRData::OptimizeEESize(void) {
597 int i, typeFlag = 0;
598 uint32_t after;
599
600 for (i = 0; i < 4; i++) {
601 // Check for non-empty and non-0xEE partitions
602 if ((partitions[i].partitionType != 0xEE) && (partitions[i].partitionType != 0x00))
603 typeFlag++;
604 if (partitions[i].partitionType == 0xEE) {
605 // Blank space before this partition; fill it....
606 if (IsFree(partitions[i].firstLBA - 1)) {
607 partitions[i].firstLBA = FindFirstInFree(partitions[i].firstLBA - 1);
608 } // if
609 // Blank space after this partition; fill it....
610 after = partitions[i].firstLBA + partitions[i].lengthLBA;
611 if (IsFree(after)) {
612 partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
613 } // if free space after
614 } // if partition is 0xEE
srs5694e4ac11e2009-08-31 10:13:04 -0400615 } // for partition loop
srs5694978041c2009-09-21 20:51:47 -0400616 if (typeFlag == 0) { // No non-hybrid partitions found
617 MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
618 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400619} // MBRData::OptimizeEESize()
620
srs5694978041c2009-09-21 20:51:47 -0400621/****************************************
622 * *
623 * Functions to find data on free space *
624 * *
625 ****************************************/
srs5694e7b4ff92009-08-18 13:16:10 -0400626
srs5694c0ca8f82009-08-20 21:35:25 -0400627// Finds the first free space on the disk from start onward; returns 0
628// if none available....
629uint32_t MBRData::FindFirstAvailable(uint32_t start) {
630 uint32_t first;
631 uint32_t i;
srs5694978041c2009-09-21 20:51:47 -0400632 int firstMoved;
srs5694c0ca8f82009-08-20 21:35:25 -0400633
634 first = start;
635
636 // ...now search through all partitions; if first is within an
637 // existing partition, move it to the next sector after that
638 // partition and repeat. If first was moved, set firstMoved
639 // flag; repeat until firstMoved is not set, so as to catch
640 // cases where partitions are out of sequential order....
641 do {
642 firstMoved = 0;
643 for (i = 0; i < 4; i++) {
644 // Check if it's in the existing partition
645 if ((first >= partitions[i].firstLBA) &&
646 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
647 first = partitions[i].firstLBA + partitions[i].lengthLBA;
648 firstMoved = 1;
srs5694978041c2009-09-21 20:51:47 -0400649 } // if
srs5694c0ca8f82009-08-20 21:35:25 -0400650 } // for
651 } while (firstMoved == 1);
652 if (first >= diskSize)
653 first = 0;
654 return (first);
655} // MBRData::FindFirstAvailable()
656
srs5694e4ac11e2009-08-31 10:13:04 -0400657// Finds the last free sector on the disk from start forward.
srs5694c0ca8f82009-08-20 21:35:25 -0400658uint32_t MBRData::FindLastInFree(uint32_t start) {
659 uint32_t nearestStart;
660 uint32_t i;
661
srs5694978041c2009-09-21 20:51:47 -0400662 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs5694c0ca8f82009-08-20 21:35:25 -0400663 nearestStart = diskSize - 1;
664 else
665 nearestStart = UINT32_MAX - 1;
666 for (i = 0; i < 4; i++) {
667 if ((nearestStart > partitions[i].firstLBA) &&
668 (partitions[i].firstLBA > start)) {
669 nearestStart = partitions[i].firstLBA - 1;
670 } // if
671 } // for
672 return (nearestStart);
srs5694e4ac11e2009-08-31 10:13:04 -0400673} // MBRData::FindLastInFree()
674
675// Finds the first free sector on the disk from start backward.
676uint32_t MBRData::FindFirstInFree(uint32_t start) {
srs5694e35eb1b2009-09-14 00:29:34 -0400677 uint32_t bestLastLBA, thisLastLBA;
srs5694e4ac11e2009-08-31 10:13:04 -0400678 int i;
679
680 bestLastLBA = 1;
681 for (i = 0; i < 4; i++) {
682 thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
683 if (thisLastLBA > 0) thisLastLBA--;
684 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) {
685 bestLastLBA = thisLastLBA + 1;
686 } // if
687 } // for
688 return (bestLastLBA);
689} // MBRData::FindFirstInFree()
690
691// Returns 1 if the specified sector is unallocated, 0 if it's
692// allocated.
693int MBRData::IsFree(uint32_t sector) {
694 int i, isFree = 1;
695 uint32_t first, last;
696
697 for (i = 0; i < 4; i++) {
698 first = partitions[i].firstLBA;
srs5694978041c2009-09-21 20:51:47 -0400699 // Note: Weird two-line thing to avoid subtracting 1 from a 0 value
700 // for an unsigned int....
srs5694e4ac11e2009-08-31 10:13:04 -0400701 last = first + partitions[i].lengthLBA;
702 if (last > 0) last--;
703 if ((first <= sector) && (last >= sector))
704 isFree = 0;
705 } // for
706 return isFree;
707} // MBRData::IsFree()
srs5694c0ca8f82009-08-20 21:35:25 -0400708
srs5694978041c2009-09-21 20:51:47 -0400709/******************************************************
710 * *
711 * Functions that extract data on specific partitions *
712 * *
713 ******************************************************/
714
srs5694e7b4ff92009-08-18 13:16:10 -0400715uint8_t MBRData::GetStatus(int i) {
716 MBRRecord* thePart;
717 uint8_t retval;
718
719 thePart = GetPartition(i);
720 if (thePart != NULL)
721 retval = thePart->status;
722 else
723 retval = UINT8_C(0);
724 return retval;
725} // MBRData::GetStatus()
726
727uint8_t MBRData::GetType(int i) {
728 MBRRecord* thePart;
729 uint8_t retval;
730
731 thePart = GetPartition(i);
732 if (thePart != NULL)
733 retval = thePart->partitionType;
734 else
735 retval = UINT8_C(0);
736 return retval;
737} // MBRData::GetType()
738
739uint32_t MBRData::GetFirstSector(int i) {
740 MBRRecord* thePart;
741 uint32_t retval;
742
743 thePart = GetPartition(i);
744 if (thePart != NULL) {
745 retval = thePart->firstLBA;
746 } else
747 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400748 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400749} // MBRData::GetFirstSector()
750
751uint32_t MBRData::GetLength(int i) {
752 MBRRecord* thePart;
753 uint32_t retval;
754
755 thePart = GetPartition(i);
756 if (thePart != NULL) {
757 retval = thePart->lengthLBA;
758 } else
759 retval = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400760 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400761} // MBRData::GetLength()
srs5694221e0872009-08-29 15:00:31 -0400762
763// Return the MBR data as a GPT partition....
764GPTPart MBRData::AsGPT(int i) {
765 MBRRecord* origPart;
766 GPTPart newPart;
767 uint8_t origType;
768 uint64_t firstSector, lastSector;
769 char tempStr[NAME_SIZE];
770
771 newPart.BlankPartition();
772 origPart = GetPartition(i);
773 if (origPart != NULL) {
774 origType = origPart->partitionType;
775
776 // don't convert extended, hybrid protective, or null (non-existent)
777 // partitions (Note similar protection is in GPTData::XFormPartitions(),
778 // but I want it here too in case I call this function in another
779 // context in the future....)
srs5694e35eb1b2009-09-14 00:29:34 -0400780 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694221e0872009-08-29 15:00:31 -0400781 (origType != 0x00) && (origType != 0xEE)) {
782 firstSector = (uint64_t) origPart->firstLBA;
783 newPart.SetFirstLBA(firstSector);
784 lastSector = firstSector + (uint64_t) origPart->lengthLBA;
785 if (lastSector > 0) lastSector--;
786 newPart.SetLastLBA(lastSector);
787 newPart.SetType(((uint16_t) origType) * 0x0100);
788 newPart.SetUniqueGUID(1);
789 newPart.SetAttributes(0);
790 newPart.SetName((unsigned char*) newPart.GetNameType(tempStr));
srs5694978041c2009-09-21 20:51:47 -0400791 } // if not extended, protective, or non-existent
792 } // if (origPart != NULL)
srs5694221e0872009-08-29 15:00:31 -0400793 return newPart;
794} // MBRData::AsGPT()
srs5694978041c2009-09-21 20:51:47 -0400795
796/***********************
797 * *
798 * Protected functions *
799 * *
800 ***********************/
801
802// Return a pointer to a primary or logical partition, or NULL if
803// the partition is out of range....
804struct MBRRecord* MBRData::GetPartition(int i) {
805 MBRRecord* thePart = NULL;
806
807 if ((i >= 0) && (i < MAX_MBR_PARTS))
808 thePart = &partitions[i];
809 return thePart;
810} // GetPartition()