blob: 4e789e1031da4ce342b76a8687b18162b28b6fad [file] [log] [blame]
srs5694e7b4ff92009-08-18 13:16:10 -04001/* mbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition
2 data. */
3
4/* By Rod Smith, January to February, 2009 */
5
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));
38 EmptyMBR();
39} // MBRData default constructor
40
41MBRData::MBRData(char *filename) {
42 blockSize = SECTOR_SIZE;
43 diskSize = 0;
44 strcpy(device, filename);
45 state = invalid;
46
47 srand((unsigned int) time(NULL));
48 // Try to read the specified partition table, but if it fails....
49 if (!ReadMBRData(filename)) {
50 EmptyMBR();
51 strcpy(device, "");
52 } // if
53} // MBRData(char *filename) constructor
54
55MBRData::~MBRData(void) {
56} // MBRData destructor
57
srs5694c0ca8f82009-08-20 21:35:25 -040058// Empty all data. Meant mainly for calling by constructors, but it's also
59// used by the hybrid MBR functions in the GPTData class.
60void MBRData::EmptyMBR(int clearBootloader) {
srs5694e7b4ff92009-08-18 13:16:10 -040061 int i;
62
srs5694c0ca8f82009-08-20 21:35:25 -040063 // Zero out the boot loader section, the disk signature, and the
64 // 2-byte nulls area only if requested to do so. (This is the
65 // default.)
66 if (clearBootloader == 1) {
67 for (i = 0; i < 440; i++)
68 code[i] = 0;
69 diskSignature = (uint32_t) rand();
70 nulls = 0;
71 } // if
72
73 // Blank out the partitions
srs5694e7b4ff92009-08-18 13:16:10 -040074 for (i = 0; i < 4; i++) {
75 partitions[i].status = UINT8_C(0);
76 partitions[i].firstSector[0] = UINT8_C(0);
77 partitions[i].firstSector[1] = UINT8_C(0);
78 partitions[i].firstSector[2] = UINT8_C(0);
79 partitions[i].partitionType = UINT8_C(0);
80 partitions[i].lastSector[0] = UINT8_C(0);
81 partitions[i].lastSector[1] = UINT8_C(0);
82 partitions[i].lastSector[2] = UINT8_C(0);
83 partitions[i].firstLBA = UINT32_C(0);
84 partitions[i].lengthLBA = UINT32_C(0);
85 } // for
86 MBRSignature = MBR_SIGNATURE;
87
88 blockSize = SECTOR_SIZE;
89 diskSize = 0;
90 for (i = 0; i < NUM_LOGICALS; i++) {
91 logicals[i].status = UINT8_C(0);
92 logicals[i].firstSector[0] = UINT8_C(0);
93 logicals[i].firstSector[1] = UINT8_C(0);
94 logicals[i].firstSector[2] = UINT8_C(0);
95 logicals[i].partitionType = UINT8_C(0);
96 logicals[i].lastSector[0] = UINT8_C(0);
97 logicals[i].lastSector[1] = UINT8_C(0);
98 logicals[i].lastSector[2] = UINT8_C(0);
99 logicals[i].firstLBA = UINT32_C(0);
100 logicals[i].lengthLBA = UINT32_C(0);
101 } // for
102} // MBRData::EmptyMBR()
103
104// Read data from MBR. Returns 1 if read was successful (even if the
105// data isn't a valid MBR), 0 if the read failed.
106int MBRData::ReadMBRData(char* deviceFilename) {
107 int fd, allOK = 1;
108
109 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
110 ReadMBRData(fd);
111 } else {
112 allOK = 0;
113 } // if
114
115 close(fd);
116
117 if (allOK)
118 strcpy(device, deviceFilename);
119
120 return allOK;
121} // MBRData::ReadMBRData(char* deviceFilename)
122
123// Read data from MBR.
srs5694221e0872009-08-29 15:00:31 -0400124void MBRData::ReadMBRData(int fd, int checkBlockSize) {
125 int allOK = 1, i, j, maxLogicals = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400126 int err;
srs5694221e0872009-08-29 15:00:31 -0400127 TempMBR tempMBR;
srs5694e7b4ff92009-08-18 13:16:10 -0400128
129 // Clear logical partition array
130 for (i = 0; i < NUM_LOGICALS; i++) {
131 logicals[i].status = UINT8_C(0);
132 logicals[i].firstSector[0] = UINT8_C(0);
133 logicals[i].firstSector[1] = UINT8_C(0);
134 logicals[i].firstSector[2] = UINT8_C(0);
135 logicals[i].partitionType = UINT8_C(0);
136 logicals[i].lastSector[0] = UINT8_C(0);
137 logicals[i].lastSector[1] = UINT8_C(0);
138 logicals[i].lastSector[2] = UINT8_C(0);
139 logicals[i].firstLBA = UINT32_C(0);
140 logicals[i].lengthLBA = UINT32_C(0);
141 } // for
142
srs5694221e0872009-08-29 15:00:31 -0400143 err = lseek64(fd, 0, SEEK_SET);
144 err = read(fd, &tempMBR, 512);
145 for (i = 0; i < 440; i++)
146 code[i] = tempMBR.code[i];
147 diskSignature = tempMBR.diskSignature;
148 nulls = tempMBR.nulls;
149 for (i = 0; i < 4; i++) {
150 partitions[i].status = tempMBR.partitions[i].status;
151 partitions[i].partitionType = tempMBR.partitions[i].partitionType;
152 partitions[i].firstLBA = tempMBR.partitions[i].firstLBA;
153 partitions[i].lengthLBA = tempMBR.partitions[i].lengthLBA;
154 for (j = 0; j < 3; j++) {
155 partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j];
156 partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j];
157 } // for j...
158 } // for i...
159 MBRSignature = tempMBR.MBRSignature;
srs56942a9f5da2009-08-26 00:48:01 -0400160
161 // Reverse the byte order, if necessary
162 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400163 ReverseBytes(&diskSignature, 4);
164 ReverseBytes(&nulls, 2);
165 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400166 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400167 ReverseBytes(&partitions[i].firstLBA, 4);
168 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400169 } // for
170 } // if
171
srs5694e7b4ff92009-08-18 13:16:10 -0400172 if (MBRSignature != MBR_SIGNATURE) {
173 allOK = 0;
174 state = invalid;
srs5694221e0872009-08-29 15:00:31 -0400175 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400176
177 // Find disk size
178 diskSize = disksize(fd, &err);
179
180 // Find block size
srs5694221e0872009-08-29 15:00:31 -0400181 if (checkBlockSize) {
srs5694e35eb1b2009-09-14 00:29:34 -0400182 blockSize = GetBlockSize(fd);
183// if ((blockSize = GetBlockSize(fd)) == -1) {
184// blockSize = SECTOR_SIZE;
185// printf("Unable to determine sector size; assuming %lu bytes!\n",
186// (unsigned long) SECTOR_SIZE);
187// } // if
srs5694221e0872009-08-29 15:00:31 -0400188 } // if (checkBlockSize)
srs5694e7b4ff92009-08-18 13:16:10 -0400189
190 // Load logical partition data, if any is found....
191 if (allOK) {
192 for (i = 0; i < 4; i++) {
193 if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
194 || (partitions[i].partitionType == 0x85)) {
195 // Found it, so call a recursive algorithm to load everything from them....
srs569486dd7842009-08-26 14:39:40 -0400196 maxLogicals = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), maxLogicals);
197 if ((maxLogicals < 0) || (maxLogicals > NUM_LOGICALS)) {
198 allOK = 0;
199 fprintf(stderr, "Error reading logical partitions! List may be truncated!\n");
200 } // if maxLogicals valid
201 } // if primary partition is extended
202 } // for primary partition loop
srs5694e7b4ff92009-08-18 13:16:10 -0400203 if (allOK) { // Loaded logicals OK
204 state = mbr;
205 } else {
206 state = invalid;
207 } // if
208 } // if
209
210 /* Check to see if it's in GPT format.... */
211 if (allOK) {
212 for (i = 0; i < 4; i++) {
213 if (partitions[i].partitionType == UINT8_C(0xEE)) {
214 state = gpt;
srs56942a9f5da2009-08-26 00:48:01 -0400215 } // if
216 } // for
217 } // if
218
219 // If there's an EFI GPT partition, look for other partition types,
220 // to flag as hybrid
221 if (state == gpt) {
222 for (i = 0 ; i < 4; i++) {
223 if ((partitions[i].partitionType != UINT8_C(0xEE)) &&
224 (partitions[i].partitionType != UINT8_C(0x00)))
225 state = hybrid;
226 } // for
227 } // if hybrid
srs5694e7b4ff92009-08-18 13:16:10 -0400228
229/* // Tell the user what the MBR state is...
230 switch (state) {
231 case invalid:
232 printf("Information: MBR appears to be empty or invalid.\n");
233 break;
234 case gpt:
235 printf("Information: MBR holds GPT placeholder partitions.\n");
236 break;
237 case hybrid:
238 printf("Information: MBR holds hybrid GPT/MBR data.\n");
239 break;
240 case mbr:
241 printf("Information: MBR data appears to be valid.\n");
242 break;
243 } // switch */
244} // MBRData::ReadMBRData(int fd)
245
246// Write the MBR data to the default defined device.
247int MBRData::WriteMBRData(void) {
248 int allOK = 1, fd;
249
250 if ((fd = open(device, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
251 WriteMBRData(fd);
252 } else {
253 allOK = 0;
254 } // if/else
255 close(fd);
256 return allOK;
257} // MBRData::WriteMBRData(void)
258
259// Save the MBR data to a file. Note that this function writes ONLY the
260// MBR data, not the logical partitions (if any are defined).
261void MBRData::WriteMBRData(int fd) {
srs5694221e0872009-08-29 15:00:31 -0400262 int i, j;
263 TempMBR tempMBR;
srs56942a9f5da2009-08-26 00:48:01 -0400264
265 // Reverse the byte order, if necessary
266 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400267 ReverseBytes(&diskSignature, 4);
268 ReverseBytes(&nulls, 2);
269 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400270 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400271 ReverseBytes(&partitions[i].firstLBA, 4);
272 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400273 } // for
274 } // if
275
srs5694221e0872009-08-29 15:00:31 -0400276 // Copy MBR data to a 512-byte data structure for writing, to
277 // work around a FreeBSD limitation....
278 for (i = 0; i < 440; i++)
279 tempMBR.code[i] = code[i];
280 tempMBR.diskSignature = diskSignature;
281 tempMBR.nulls = nulls;
282 tempMBR.MBRSignature = MBRSignature;
283 for (i = 0; i < 4; i++) {
284 tempMBR.partitions[i].status = partitions[i].status;
285 tempMBR.partitions[i].partitionType = partitions[i].partitionType;
286 tempMBR.partitions[i].firstLBA = partitions[i].firstLBA;
287 tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA;
288 for (j = 0; j < 3; j++) {
289 tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j];
290 tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j];
291 } // for j...
292 } // for i...
293
294 // Now write that data structure...
srs5694e35eb1b2009-09-14 00:29:34 -0400295 lseek64(fd, 0, SEEK_SET);
srs5694221e0872009-08-29 15:00:31 -0400296 write(fd, &tempMBR, 512);
297
298/* write(fd, code, 440);
srs5694e7b4ff92009-08-18 13:16:10 -0400299 write(fd, &diskSignature, 4);
300 write(fd, &nulls, 2);
301 write(fd, partitions, 64);
srs5694221e0872009-08-29 15:00:31 -0400302 write(fd, &MBRSignature, 2); */
srs5694e35eb1b2009-09-14 00:29:34 -0400303
srs5694221e0872009-08-29 15:00:31 -0400304 // Reverse the byte order back, if necessary
srs56942a9f5da2009-08-26 00:48:01 -0400305 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400306 ReverseBytes(&diskSignature, 4);
307 ReverseBytes(&nulls, 2);
308 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400309 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400310 ReverseBytes(&partitions[i].firstLBA, 4);
311 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400312 } // for
313 }// if
srs5694e7b4ff92009-08-18 13:16:10 -0400314} // MBRData::WriteMBRData(int fd)
315
316// This is a recursive function to read all the logical partitions, following the
srs569486dd7842009-08-26 14:39:40 -0400317// logical partition linked list from the disk and storing the basic data in the
318// logicals[] array. Returns last index to logicals[] uses, or -1 if there was a
319// problem
srs5694e7b4ff92009-08-18 13:16:10 -0400320int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
321 uint32_t diskOffset, int partNum) {
srs5694e7b4ff92009-08-18 13:16:10 -0400322 struct EBRRecord ebr;
323 off_t offset;
324
srs569486dd7842009-08-26 14:39:40 -0400325 if ((partNum < NUM_LOGICALS) && (partNum >= 0)) {
326 offset = (off_t) (extendedStart + diskOffset) * blockSize;
327 if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
328 fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
329 partNum = -1;
330 }
331 if (read(fd, &ebr, 512) != 512) { // Load the data....
332 fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
333 (unsigned long) offset);
334 partNum = -1;
335 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
srs5694221e0872009-08-29 15:00:31 -0400336 ReverseBytes(&ebr.MBRSignature, 2);
337 ReverseBytes(&ebr.partitions[0].firstLBA, 4);
338 ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
339 ReverseBytes(&ebr.partitions[1].firstLBA, 4);
340 ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
srs569486dd7842009-08-26 14:39:40 -0400341 } // if/else/if
srs56942a9f5da2009-08-26 00:48:01 -0400342
srs569486dd7842009-08-26 14:39:40 -0400343 if (ebr.MBRSignature != MBR_SIGNATURE) {
344 partNum = -1;
345 fprintf(stderr, "MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
346 (unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
347 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400348
srs569486dd7842009-08-26 14:39:40 -0400349 // Copy over the basic data....
350 logicals[partNum].status = ebr.partitions[0].status;
351 logicals[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
352 logicals[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
353 logicals[partNum].partitionType = ebr.partitions[0].partitionType;
srs5694e7b4ff92009-08-18 13:16:10 -0400354
srs569486dd7842009-08-26 14:39:40 -0400355 // Find the next partition (if there is one) and recurse....
356 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 0) &&
357 (partNum < (NUM_LOGICALS - 1))) {
358 partNum = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA,
359 partNum + 1);
360 } else {
361 partNum++;
362 } // if another partition
363 } // Not enough space for all the logicals (or previous error encountered)
364 return (partNum);
srs5694e7b4ff92009-08-18 13:16:10 -0400365} // MBRData::ReadLogicalPart()
366
367// Show the MBR data to the user....
368void MBRData::DisplayMBRData(void) {
369 int i;
370 char tempStr[255];
srs5694e19ba092009-08-24 14:10:35 -0400371 char bootCode;
srs5694e7b4ff92009-08-18 13:16:10 -0400372
373 printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
374 printf("MBR partitions:\n");
srs5694e19ba092009-08-24 14:10:35 -0400375 printf("Number\t Boot\t Start (sector)\t Length (sectors)\tType\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400376 for (i = 0; i < 4; i++) {
377 if (partitions[i].lengthLBA != 0) {
srs5694e19ba092009-08-24 14:10:35 -0400378 if (partitions[i].status && 0x80) // it's bootable
379 bootCode = '*';
380 else
381 bootCode = ' ';
382 printf("%4d\t %c\t%13lu\t%15lu \t0x%02X\n", i + 1, bootCode,
383 (unsigned long) partitions[i].firstLBA,
srs5694e7b4ff92009-08-18 13:16:10 -0400384 (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
385 } // if
386 } // for
387
388 // Now display logical partition data....
389 for (i = 0; i < NUM_LOGICALS; i++) {
390 if (logicals[i].lengthLBA != 0) {
391 printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 5, (unsigned long) logicals[i].firstLBA,
392 (unsigned long) logicals[i].lengthLBA, logicals[i].partitionType);
393 } // if
394 } // for
395 printf("\nDisk size is %lu sectors (%s)\n", (unsigned long) diskSize,
396 BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
397} // MBRData::DisplayMBRData()
398
srs5694221e0872009-08-29 15:00:31 -0400399// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
400void MBRData::MakeProtectiveMBR(int clearBoot) {
srs5694e7b4ff92009-08-18 13:16:10 -0400401 int i;
402
403 // Initialize variables
404 nulls = 0;
405 MBRSignature = MBR_SIGNATURE;
406
srs5694221e0872009-08-29 15:00:31 -0400407 if (clearBoot > 0) {
408 for (i = 0; i < 440; i++)
409 code[i] = (uint8_t) 0;
410 } // if
411
srs5694e7b4ff92009-08-18 13:16:10 -0400412 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
413
414 // Write CHS data. This maxes out the use of the disk, as much as
415 // possible -- even to the point of exceeding the capacity of sub-8GB
416 // disks. The EFI spec says to use 0xffffff as the ending value,
417 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
418 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
419 // value in for the FIRST sector, too!
420 partitions[0].firstSector[0] = UINT8_C(0);
421 partitions[0].firstSector[1] = UINT8_C(1);
422 partitions[0].firstSector[2] = UINT8_C(0);
423 partitions[0].lastSector[0] = UINT8_C(255);
424 partitions[0].lastSector[1] = UINT8_C(255);
425 partitions[0].lastSector[2] = UINT8_C(255);
426
427 partitions[0].partitionType = UINT8_C(0xEE);
428 partitions[0].firstLBA = UINT32_C(1);
429 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
430 partitions[0].lengthLBA = diskSize - 1;
431 } else { // disk is too big to represent, so fake it...
432 partitions[0].lengthLBA = UINT32_MAX;
433 } // if/else
434
435 // Zero out three unused primary partitions...
436 for (i = 1; i < 4; i++) {
437 partitions[i].status = UINT8_C(0);
438 partitions[i].firstSector[0] = UINT8_C(0);
439 partitions[i].firstSector[1] = UINT8_C(0);
440 partitions[i].firstSector[2] = UINT8_C(0);
441 partitions[i].partitionType = UINT8_C(0);
442 partitions[i].lastSector[0] = UINT8_C(0);
443 partitions[i].lastSector[1] = UINT8_C(0);
444 partitions[i].lastSector[2] = UINT8_C(0);
445 partitions[i].firstLBA = UINT32_C(0);
446 partitions[i].lengthLBA = UINT32_C(0);
447 } // for
448
449 // Zero out all the logical partitions. Not necessary for data
450 // integrity on write, but eliminates stray entries if user wants
451 // to view the MBR after converting the disk
452 for (i = 0; i < NUM_LOGICALS; i++) {
453 logicals[i].status = UINT8_C(0);
454 logicals[i].firstSector[0] = UINT8_C(0);
455 logicals[i].firstSector[1] = UINT8_C(0);
456 logicals[i].firstSector[2] = UINT8_C(0);
457 logicals[i].partitionType = UINT8_C(0);
458 logicals[i].lastSector[0] = UINT8_C(0);
459 logicals[i].lastSector[1] = UINT8_C(0);
460 logicals[i].lastSector[2] = UINT8_C(0);
461 logicals[i].firstLBA = UINT32_C(0);
462 logicals[i].lengthLBA = UINT32_C(0);
463 } // for
464
465 state = gpt;
466} // MBRData::MakeProtectiveMBR()
467
srs5694c0ca8f82009-08-20 21:35:25 -0400468// Create a partition that fills the most available space. Returns
469// 1 if partition was created, 0 otherwise. Intended for use in
470// creating hybrid MBRs.
471int MBRData::MakeBiggestPart(int i, int type) {
472 uint32_t start = UINT32_C(1); // starting point for each search
473 uint32_t firstBlock; // first block in a segment
474 uint32_t lastBlock; // last block in a segment
475 uint32_t segmentSize; // size of segment in blocks
476 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
477 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
478 int found = 0;
479
480 do {
481 firstBlock = FindFirstAvailable(start);
482 if (firstBlock != UINT32_C(0)) { // something's free...
483 lastBlock = FindLastInFree(firstBlock);
484 segmentSize = lastBlock - firstBlock + UINT32_C(1);
485 if (segmentSize > selectedSize) {
486 selectedSize = segmentSize;
487 selectedSegment = firstBlock;
488 } // if
489 start = lastBlock + 1;
490 } // if
491 } while (firstBlock != 0);
492 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
493 found = 1;
494 MakePart(i, selectedSegment, selectedSize, type, 0);
495 } else {
496 found = 0;
497 } // if/else
498 return found;
499} // MBRData::MakeBiggestPart(int i)
500
srs5694e35eb1b2009-09-14 00:29:34 -0400501// Delete partition #i
502void MBRData::DeletePartition(int i) {
503 int j;
504
505 partitions[i].firstLBA = UINT32_C(0);
506 partitions[i].lengthLBA = UINT32_C(0);
507 partitions[i].status = UINT8_C(0);
508 partitions[i].partitionType = UINT8_C(0);
509 for (j = 0; j < 3; j++) {
510 partitions[i].firstSector[j] = UINT8_C(0);
511 partitions[i].lastSector[j] = UINT8_C(0);
512 } // for j (CHS data blanking)
513} // MBRData::DeletePartition()
514
srs5694e4ac11e2009-08-31 10:13:04 -0400515// Delete a partition if one exists at the specified location.
516// Returns 1 if a partition was deleted, 0 otherwise....
517// Used to help keep GPT & hybrid MBR partitions in sync....
518int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
519 uint32_t start32, length32;
520 int i, j, deleted = 0;
521
522 if ((state == hybrid) && (start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
523 start32 = (uint32_t) start64;
524 length32 = (uint32_t) length64;
525 for (i = 0; i < 4; i++) {
526 if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
527 (partitions[i].partitionType != 0xEE)) {
srs5694e35eb1b2009-09-14 00:29:34 -0400528 DeletePartition(i);
srs5694e4ac11e2009-08-31 10:13:04 -0400529 OptimizeEESize();
530 deleted = 1;
531 } // if (match found)
532 } // for i (partition scan)
533 } // if (hybrid & GPT partition < 2TiB)
534 return deleted;
535} // MBRData::DeleteByLocation()
536
537// Optimizes the size of the 0xEE (EFI GPT) partition
538void MBRData::OptimizeEESize(void) {
539 int i, typeFlag = 0;
540 uint32_t after;
541
542 for (i = 0; i < 4; i++) {
543 // Check for non-empty and non-0xEE partitions
544 if ((partitions[i].partitionType != 0xEE) && (partitions[i].partitionType != 0x00))
545 typeFlag++;
546 if (partitions[i].partitionType == 0xEE) {
547 // Blank space before this partition; fill it....
548 if (IsFree(partitions[i].firstLBA - 1)) {
549 partitions[i].firstLBA = FindFirstInFree(partitions[i].firstLBA - 1);
550 } // if
551 // Blank space after this partition; fill it....
552 after = partitions[i].firstLBA + partitions[i].lengthLBA;
553 if (IsFree(after)) {
554 partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
555 } // if free space after
556 } // if partition is 0xEE
557 if (typeFlag == 0) { // No non-hybrid partitions found
558 MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
559 } // if
560 } // for partition loop
561} // MBRData::OptimizeEESize()
562
srs5694e7b4ff92009-08-18 13:16:10 -0400563// Return a pointer to a primary or logical partition, or NULL if
564// the partition is out of range....
565struct MBRRecord* MBRData::GetPartition(int i) {
566 MBRRecord* thePart = NULL;
567
568 if ((i >= 0) && (i < 4)) { // primary partition
569 thePart = &partitions[i];
570 } // if
571 if ((i >= 4) && (i < (NUM_LOGICALS + 4))) {
572 thePart = &logicals[i - 4];
573 } // if
574 return thePart;
575} // GetPartition()
576
srs5694221e0872009-08-29 15:00:31 -0400577// Displays the state, as a word, on stdout. Used for debugging & to
578// tell the user about the MBR state when the program launches....
srs5694e7b4ff92009-08-18 13:16:10 -0400579void MBRData::ShowState(void) {
580 switch (state) {
581 case invalid:
srs5694221e0872009-08-29 15:00:31 -0400582 printf(" MBR: not present\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400583 break;
584 case gpt:
srs5694221e0872009-08-29 15:00:31 -0400585 printf(" MBR: protective\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400586 break;
587 case hybrid:
srs5694221e0872009-08-29 15:00:31 -0400588 printf(" MBR: hybrid\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400589 break;
590 case mbr:
srs5694221e0872009-08-29 15:00:31 -0400591 printf(" MBR: MBR only\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400592 break;
593 default:
srs5694221e0872009-08-29 15:00:31 -0400594 printf("\a MBR: unknown -- bug!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400595 break;
596 } // switch
597} // MBRData::ShowState()
598
599// Create a primary partition of the specified number, starting LBA,
600// and length. This function does *NO* error checking, so it's possible
601// to seriously screw up a partition table using this function! It's
602// intended as a way to create a hybrid MBR, which is a pretty funky
603// setup to begin with....
604void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
605 int bootable) {
srs5694e35eb1b2009-09-14 00:29:34 -0400606
srs5694e7b4ff92009-08-18 13:16:10 -0400607 partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
608 partitions[num].firstSector[0] = UINT8_C(0);
609 partitions[num].firstSector[1] = UINT8_C(0);
610 partitions[num].firstSector[2] = UINT8_C(0);
611 partitions[num].partitionType = (uint8_t) type;
612 partitions[num].lastSector[0] = UINT8_C(0);
613 partitions[num].lastSector[1] = UINT8_C(0);
614 partitions[num].lastSector[2] = UINT8_C(0);
615 partitions[num].firstLBA = start;
616 partitions[num].lengthLBA = length;
617} // MakePart()
618
srs5694c0ca8f82009-08-20 21:35:25 -0400619// Finds the first free space on the disk from start onward; returns 0
620// if none available....
621uint32_t MBRData::FindFirstAvailable(uint32_t start) {
622 uint32_t first;
623 uint32_t i;
624 int firstMoved = 0;
625
626 first = start;
627
628 // ...now search through all partitions; if first is within an
629 // existing partition, move it to the next sector after that
630 // partition and repeat. If first was moved, set firstMoved
631 // flag; repeat until firstMoved is not set, so as to catch
632 // cases where partitions are out of sequential order....
633 do {
634 firstMoved = 0;
635 for (i = 0; i < 4; i++) {
636 // Check if it's in the existing partition
637 if ((first >= partitions[i].firstLBA) &&
638 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
639 first = partitions[i].firstLBA + partitions[i].lengthLBA;
640 firstMoved = 1;
641 } // if
642 } // for
643 } while (firstMoved == 1);
644 if (first >= diskSize)
645 first = 0;
646 return (first);
647} // MBRData::FindFirstAvailable()
648
srs5694e4ac11e2009-08-31 10:13:04 -0400649// Finds the last free sector on the disk from start forward.
srs5694c0ca8f82009-08-20 21:35:25 -0400650uint32_t MBRData::FindLastInFree(uint32_t start) {
651 uint32_t nearestStart;
652 uint32_t i;
653
654 if (diskSize <= UINT32_MAX)
655 nearestStart = diskSize - 1;
656 else
657 nearestStart = UINT32_MAX - 1;
658 for (i = 0; i < 4; i++) {
659 if ((nearestStart > partitions[i].firstLBA) &&
660 (partitions[i].firstLBA > start)) {
661 nearestStart = partitions[i].firstLBA - 1;
662 } // if
663 } // for
664 return (nearestStart);
srs5694e4ac11e2009-08-31 10:13:04 -0400665} // MBRData::FindLastInFree()
666
667// Finds the first free sector on the disk from start backward.
668uint32_t MBRData::FindFirstInFree(uint32_t start) {
srs5694e35eb1b2009-09-14 00:29:34 -0400669 uint32_t bestLastLBA, thisLastLBA;
srs5694e4ac11e2009-08-31 10:13:04 -0400670 int i;
671
672 bestLastLBA = 1;
673 for (i = 0; i < 4; i++) {
674 thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
675 if (thisLastLBA > 0) thisLastLBA--;
676 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) {
677 bestLastLBA = thisLastLBA + 1;
678 } // if
679 } // for
680 return (bestLastLBA);
681} // MBRData::FindFirstInFree()
682
683// Returns 1 if the specified sector is unallocated, 0 if it's
684// allocated.
685int MBRData::IsFree(uint32_t sector) {
686 int i, isFree = 1;
687 uint32_t first, last;
688
689 for (i = 0; i < 4; i++) {
690 first = partitions[i].firstLBA;
691 last = first + partitions[i].lengthLBA;
692 if (last > 0) last--;
693 if ((first <= sector) && (last >= sector))
694 isFree = 0;
695 } // for
696 return isFree;
697} // MBRData::IsFree()
srs5694c0ca8f82009-08-20 21:35:25 -0400698
srs5694e7b4ff92009-08-18 13:16:10 -0400699uint8_t MBRData::GetStatus(int i) {
700 MBRRecord* thePart;
701 uint8_t retval;
702
703 thePart = GetPartition(i);
704 if (thePart != NULL)
705 retval = thePart->status;
706 else
707 retval = UINT8_C(0);
708 return retval;
709} // MBRData::GetStatus()
710
711uint8_t MBRData::GetType(int i) {
712 MBRRecord* thePart;
713 uint8_t retval;
714
715 thePart = GetPartition(i);
716 if (thePart != NULL)
717 retval = thePart->partitionType;
718 else
719 retval = UINT8_C(0);
720 return retval;
721} // MBRData::GetType()
722
723uint32_t MBRData::GetFirstSector(int i) {
724 MBRRecord* thePart;
725 uint32_t retval;
726
727 thePart = GetPartition(i);
728 if (thePart != NULL) {
729 retval = thePart->firstLBA;
730 } else
731 retval = UINT32_C(0);
732 return retval;
733} // MBRData::GetFirstSector()
734
735uint32_t MBRData::GetLength(int i) {
736 MBRRecord* thePart;
737 uint32_t retval;
738
739 thePart = GetPartition(i);
740 if (thePart != NULL) {
741 retval = thePart->lengthLBA;
742 } else
743 retval = UINT32_C(0);
744 return retval;
745} // MBRData::GetLength()
srs5694221e0872009-08-29 15:00:31 -0400746
747// Return the MBR data as a GPT partition....
748GPTPart MBRData::AsGPT(int i) {
749 MBRRecord* origPart;
750 GPTPart newPart;
751 uint8_t origType;
752 uint64_t firstSector, lastSector;
753 char tempStr[NAME_SIZE];
754
755 newPart.BlankPartition();
756 origPart = GetPartition(i);
757 if (origPart != NULL) {
758 origType = origPart->partitionType;
759
760 // don't convert extended, hybrid protective, or null (non-existent)
761 // partitions (Note similar protection is in GPTData::XFormPartitions(),
762 // but I want it here too in case I call this function in another
763 // context in the future....)
srs5694e35eb1b2009-09-14 00:29:34 -0400764 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694221e0872009-08-29 15:00:31 -0400765 (origType != 0x00) && (origType != 0xEE)) {
766 firstSector = (uint64_t) origPart->firstLBA;
767 newPart.SetFirstLBA(firstSector);
768 lastSector = firstSector + (uint64_t) origPart->lengthLBA;
769 if (lastSector > 0) lastSector--;
770 newPart.SetLastLBA(lastSector);
771 newPart.SetType(((uint16_t) origType) * 0x0100);
772 newPart.SetUniqueGUID(1);
773 newPart.SetAttributes(0);
774 newPart.SetName((unsigned char*) newPart.GetNameType(tempStr));
775 } // if
776 } // if
777 return newPart;
778} // MBRData::AsGPT()