blob: 28745b3005861b0553a8d669a5a148f54327ea6d [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) {
182 if ((blockSize = GetBlockSize(fd)) == -1) {
183 blockSize = SECTOR_SIZE;
184 printf("Unable to determine sector size; assuming %lu bytes!\n",
185 (unsigned long) SECTOR_SIZE);
186 } // if
187 } // if (checkBlockSize)
srs5694e7b4ff92009-08-18 13:16:10 -0400188
189 // Load logical partition data, if any is found....
190 if (allOK) {
191 for (i = 0; i < 4; i++) {
192 if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
193 || (partitions[i].partitionType == 0x85)) {
194 // Found it, so call a recursive algorithm to load everything from them....
srs569486dd7842009-08-26 14:39:40 -0400195 maxLogicals = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), maxLogicals);
196 if ((maxLogicals < 0) || (maxLogicals > NUM_LOGICALS)) {
197 allOK = 0;
198 fprintf(stderr, "Error reading logical partitions! List may be truncated!\n");
199 } // if maxLogicals valid
200 } // if primary partition is extended
201 } // for primary partition loop
srs5694e7b4ff92009-08-18 13:16:10 -0400202 if (allOK) { // Loaded logicals OK
203 state = mbr;
204 } else {
205 state = invalid;
206 } // if
207 } // if
208
209 /* Check to see if it's in GPT format.... */
210 if (allOK) {
211 for (i = 0; i < 4; i++) {
212 if (partitions[i].partitionType == UINT8_C(0xEE)) {
213 state = gpt;
srs56942a9f5da2009-08-26 00:48:01 -0400214 } // if
215 } // for
216 } // if
217
218 // If there's an EFI GPT partition, look for other partition types,
219 // to flag as hybrid
220 if (state == gpt) {
221 for (i = 0 ; i < 4; i++) {
222 if ((partitions[i].partitionType != UINT8_C(0xEE)) &&
223 (partitions[i].partitionType != UINT8_C(0x00)))
224 state = hybrid;
225 } // for
226 } // if hybrid
srs5694e7b4ff92009-08-18 13:16:10 -0400227
228/* // Tell the user what the MBR state is...
229 switch (state) {
230 case invalid:
231 printf("Information: MBR appears to be empty or invalid.\n");
232 break;
233 case gpt:
234 printf("Information: MBR holds GPT placeholder partitions.\n");
235 break;
236 case hybrid:
237 printf("Information: MBR holds hybrid GPT/MBR data.\n");
238 break;
239 case mbr:
240 printf("Information: MBR data appears to be valid.\n");
241 break;
242 } // switch */
243} // MBRData::ReadMBRData(int fd)
244
245// Write the MBR data to the default defined device.
246int MBRData::WriteMBRData(void) {
247 int allOK = 1, fd;
248
249 if ((fd = open(device, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
250 WriteMBRData(fd);
251 } else {
252 allOK = 0;
253 } // if/else
254 close(fd);
255 return allOK;
256} // MBRData::WriteMBRData(void)
257
258// Save the MBR data to a file. Note that this function writes ONLY the
259// MBR data, not the logical partitions (if any are defined).
260void MBRData::WriteMBRData(int fd) {
srs5694221e0872009-08-29 15:00:31 -0400261 int i, j;
262 TempMBR tempMBR;
srs56942a9f5da2009-08-26 00:48:01 -0400263
264 // Reverse the byte order, if necessary
265 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400266 ReverseBytes(&diskSignature, 4);
267 ReverseBytes(&nulls, 2);
268 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400269 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400270 ReverseBytes(&partitions[i].firstLBA, 4);
271 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400272 } // for
273 } // if
274
srs5694221e0872009-08-29 15:00:31 -0400275 // Copy MBR data to a 512-byte data structure for writing, to
276 // work around a FreeBSD limitation....
277 for (i = 0; i < 440; i++)
278 tempMBR.code[i] = code[i];
279 tempMBR.diskSignature = diskSignature;
280 tempMBR.nulls = nulls;
281 tempMBR.MBRSignature = MBRSignature;
282 for (i = 0; i < 4; i++) {
283 tempMBR.partitions[i].status = partitions[i].status;
284 tempMBR.partitions[i].partitionType = partitions[i].partitionType;
285 tempMBR.partitions[i].firstLBA = partitions[i].firstLBA;
286 tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA;
287 for (j = 0; j < 3; j++) {
288 tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j];
289 tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j];
290 } // for j...
291 } // for i...
292
293 // Now write that data structure...
294 write(fd, &tempMBR, 512);
295
296/* write(fd, code, 440);
srs5694e7b4ff92009-08-18 13:16:10 -0400297 write(fd, &diskSignature, 4);
298 write(fd, &nulls, 2);
299 write(fd, partitions, 64);
srs5694221e0872009-08-29 15:00:31 -0400300 write(fd, &MBRSignature, 2); */
srs56942a9f5da2009-08-26 00:48:01 -0400301
srs5694221e0872009-08-29 15:00:31 -0400302 // Reverse the byte order back, if necessary
srs56942a9f5da2009-08-26 00:48:01 -0400303 if (IsLittleEndian() == 0) {
srs5694221e0872009-08-29 15:00:31 -0400304 ReverseBytes(&diskSignature, 4);
305 ReverseBytes(&nulls, 2);
306 ReverseBytes(&MBRSignature, 2);
srs56942a9f5da2009-08-26 00:48:01 -0400307 for (i = 0; i < 4; i++) {
srs5694221e0872009-08-29 15:00:31 -0400308 ReverseBytes(&partitions[i].firstLBA, 4);
309 ReverseBytes(&partitions[i].lengthLBA, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400310 } // for
311 }// if
srs5694e7b4ff92009-08-18 13:16:10 -0400312} // MBRData::WriteMBRData(int fd)
313
314// This is a recursive function to read all the logical partitions, following the
srs569486dd7842009-08-26 14:39:40 -0400315// logical partition linked list from the disk and storing the basic data in the
316// logicals[] array. Returns last index to logicals[] uses, or -1 if there was a
317// problem
srs5694e7b4ff92009-08-18 13:16:10 -0400318int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
319 uint32_t diskOffset, int partNum) {
srs5694e7b4ff92009-08-18 13:16:10 -0400320 struct EBRRecord ebr;
321 off_t offset;
322
srs569486dd7842009-08-26 14:39:40 -0400323 if ((partNum < NUM_LOGICALS) && (partNum >= 0)) {
324 offset = (off_t) (extendedStart + diskOffset) * blockSize;
325 if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
326 fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
327 partNum = -1;
328 }
329 if (read(fd, &ebr, 512) != 512) { // Load the data....
330 fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
331 (unsigned long) offset);
332 partNum = -1;
333 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
srs5694221e0872009-08-29 15:00:31 -0400334 ReverseBytes(&ebr.MBRSignature, 2);
335 ReverseBytes(&ebr.partitions[0].firstLBA, 4);
336 ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
337 ReverseBytes(&ebr.partitions[1].firstLBA, 4);
338 ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
srs569486dd7842009-08-26 14:39:40 -0400339 } // if/else/if
srs56942a9f5da2009-08-26 00:48:01 -0400340
srs569486dd7842009-08-26 14:39:40 -0400341 if (ebr.MBRSignature != MBR_SIGNATURE) {
342 partNum = -1;
343 fprintf(stderr, "MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
344 (unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
345 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400346
srs569486dd7842009-08-26 14:39:40 -0400347 // Copy over the basic data....
348 logicals[partNum].status = ebr.partitions[0].status;
349 logicals[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
350 logicals[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
351 logicals[partNum].partitionType = ebr.partitions[0].partitionType;
srs5694e7b4ff92009-08-18 13:16:10 -0400352
srs569486dd7842009-08-26 14:39:40 -0400353 // Find the next partition (if there is one) and recurse....
354 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 0) &&
355 (partNum < (NUM_LOGICALS - 1))) {
356 partNum = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA,
357 partNum + 1);
358 } else {
359 partNum++;
360 } // if another partition
361 } // Not enough space for all the logicals (or previous error encountered)
362 return (partNum);
srs5694e7b4ff92009-08-18 13:16:10 -0400363} // MBRData::ReadLogicalPart()
364
365// Show the MBR data to the user....
366void MBRData::DisplayMBRData(void) {
367 int i;
368 char tempStr[255];
srs5694e19ba092009-08-24 14:10:35 -0400369 char bootCode;
srs5694e7b4ff92009-08-18 13:16:10 -0400370
371 printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
372 printf("MBR partitions:\n");
srs5694e19ba092009-08-24 14:10:35 -0400373 printf("Number\t Boot\t Start (sector)\t Length (sectors)\tType\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400374 for (i = 0; i < 4; i++) {
375 if (partitions[i].lengthLBA != 0) {
srs5694e19ba092009-08-24 14:10:35 -0400376 if (partitions[i].status && 0x80) // it's bootable
377 bootCode = '*';
378 else
379 bootCode = ' ';
380 printf("%4d\t %c\t%13lu\t%15lu \t0x%02X\n", i + 1, bootCode,
381 (unsigned long) partitions[i].firstLBA,
srs5694e7b4ff92009-08-18 13:16:10 -0400382 (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
383 } // if
384 } // for
385
386 // Now display logical partition data....
387 for (i = 0; i < NUM_LOGICALS; i++) {
388 if (logicals[i].lengthLBA != 0) {
389 printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 5, (unsigned long) logicals[i].firstLBA,
390 (unsigned long) logicals[i].lengthLBA, logicals[i].partitionType);
391 } // if
392 } // for
393 printf("\nDisk size is %lu sectors (%s)\n", (unsigned long) diskSize,
394 BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
395} // MBRData::DisplayMBRData()
396
srs5694221e0872009-08-29 15:00:31 -0400397// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
398void MBRData::MakeProtectiveMBR(int clearBoot) {
srs5694e7b4ff92009-08-18 13:16:10 -0400399 int i;
400
401 // Initialize variables
402 nulls = 0;
403 MBRSignature = MBR_SIGNATURE;
404
srs5694221e0872009-08-29 15:00:31 -0400405 if (clearBoot > 0) {
406 for (i = 0; i < 440; i++)
407 code[i] = (uint8_t) 0;
408 } // if
409
srs5694e7b4ff92009-08-18 13:16:10 -0400410 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
411
412 // Write CHS data. This maxes out the use of the disk, as much as
413 // possible -- even to the point of exceeding the capacity of sub-8GB
414 // disks. The EFI spec says to use 0xffffff as the ending value,
415 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
416 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
417 // value in for the FIRST sector, too!
418 partitions[0].firstSector[0] = UINT8_C(0);
419 partitions[0].firstSector[1] = UINT8_C(1);
420 partitions[0].firstSector[2] = UINT8_C(0);
421 partitions[0].lastSector[0] = UINT8_C(255);
422 partitions[0].lastSector[1] = UINT8_C(255);
423 partitions[0].lastSector[2] = UINT8_C(255);
424
425 partitions[0].partitionType = UINT8_C(0xEE);
426 partitions[0].firstLBA = UINT32_C(1);
427 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
428 partitions[0].lengthLBA = diskSize - 1;
429 } else { // disk is too big to represent, so fake it...
430 partitions[0].lengthLBA = UINT32_MAX;
431 } // if/else
432
433 // Zero out three unused primary partitions...
434 for (i = 1; i < 4; i++) {
435 partitions[i].status = UINT8_C(0);
436 partitions[i].firstSector[0] = UINT8_C(0);
437 partitions[i].firstSector[1] = UINT8_C(0);
438 partitions[i].firstSector[2] = UINT8_C(0);
439 partitions[i].partitionType = UINT8_C(0);
440 partitions[i].lastSector[0] = UINT8_C(0);
441 partitions[i].lastSector[1] = UINT8_C(0);
442 partitions[i].lastSector[2] = UINT8_C(0);
443 partitions[i].firstLBA = UINT32_C(0);
444 partitions[i].lengthLBA = UINT32_C(0);
445 } // for
446
447 // Zero out all the logical partitions. Not necessary for data
448 // integrity on write, but eliminates stray entries if user wants
449 // to view the MBR after converting the disk
450 for (i = 0; i < NUM_LOGICALS; i++) {
451 logicals[i].status = UINT8_C(0);
452 logicals[i].firstSector[0] = UINT8_C(0);
453 logicals[i].firstSector[1] = UINT8_C(0);
454 logicals[i].firstSector[2] = UINT8_C(0);
455 logicals[i].partitionType = UINT8_C(0);
456 logicals[i].lastSector[0] = UINT8_C(0);
457 logicals[i].lastSector[1] = UINT8_C(0);
458 logicals[i].lastSector[2] = UINT8_C(0);
459 logicals[i].firstLBA = UINT32_C(0);
460 logicals[i].lengthLBA = UINT32_C(0);
461 } // for
462
463 state = gpt;
464} // MBRData::MakeProtectiveMBR()
465
srs5694c0ca8f82009-08-20 21:35:25 -0400466// Create a partition that fills the most available space. Returns
467// 1 if partition was created, 0 otherwise. Intended for use in
468// creating hybrid MBRs.
469int MBRData::MakeBiggestPart(int i, int type) {
470 uint32_t start = UINT32_C(1); // starting point for each search
471 uint32_t firstBlock; // first block in a segment
472 uint32_t lastBlock; // last block in a segment
473 uint32_t segmentSize; // size of segment in blocks
474 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
475 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
476 int found = 0;
477
478 do {
479 firstBlock = FindFirstAvailable(start);
480 if (firstBlock != UINT32_C(0)) { // something's free...
481 lastBlock = FindLastInFree(firstBlock);
482 segmentSize = lastBlock - firstBlock + UINT32_C(1);
483 if (segmentSize > selectedSize) {
484 selectedSize = segmentSize;
485 selectedSegment = firstBlock;
486 } // if
487 start = lastBlock + 1;
488 } // if
489 } while (firstBlock != 0);
490 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
491 found = 1;
492 MakePart(i, selectedSegment, selectedSize, type, 0);
493 } else {
494 found = 0;
495 } // if/else
496 return found;
497} // MBRData::MakeBiggestPart(int i)
498
srs5694e4ac11e2009-08-31 10:13:04 -0400499// Delete a partition if one exists at the specified location.
500// Returns 1 if a partition was deleted, 0 otherwise....
501// Used to help keep GPT & hybrid MBR partitions in sync....
502int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
503 uint32_t start32, length32;
504 int i, j, deleted = 0;
505
506 if ((state == hybrid) && (start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
507 start32 = (uint32_t) start64;
508 length32 = (uint32_t) length64;
509 for (i = 0; i < 4; i++) {
510 if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
511 (partitions[i].partitionType != 0xEE)) {
512 partitions[i].firstLBA = UINT32_C(0);
513 partitions[i].lengthLBA = UINT32_C(0);
514 partitions[i].status = UINT8_C(0);
515 partitions[i].partitionType = UINT8_C(0);
516 for (j = 0; j < 3; j++) {
517 partitions[i].firstSector[j] = UINT8_C(0);
518 partitions[i].lastSector[j] = UINT8_C(0);
519 } // for j (CHS data blanking)
520 OptimizeEESize();
521 deleted = 1;
522 } // if (match found)
523 } // for i (partition scan)
524 } // if (hybrid & GPT partition < 2TiB)
525 return deleted;
526} // MBRData::DeleteByLocation()
527
528// Optimizes the size of the 0xEE (EFI GPT) partition
529void MBRData::OptimizeEESize(void) {
530 int i, typeFlag = 0;
531 uint32_t after;
532
533 for (i = 0; i < 4; i++) {
534 // Check for non-empty and non-0xEE partitions
535 if ((partitions[i].partitionType != 0xEE) && (partitions[i].partitionType != 0x00))
536 typeFlag++;
537 if (partitions[i].partitionType == 0xEE) {
538 // Blank space before this partition; fill it....
539 if (IsFree(partitions[i].firstLBA - 1)) {
540 partitions[i].firstLBA = FindFirstInFree(partitions[i].firstLBA - 1);
541 } // if
542 // Blank space after this partition; fill it....
543 after = partitions[i].firstLBA + partitions[i].lengthLBA;
544 if (IsFree(after)) {
545 partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
546 } // if free space after
547 } // if partition is 0xEE
548 if (typeFlag == 0) { // No non-hybrid partitions found
549 MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
550 } // if
551 } // for partition loop
552} // MBRData::OptimizeEESize()
553
srs5694e7b4ff92009-08-18 13:16:10 -0400554// Return a pointer to a primary or logical partition, or NULL if
555// the partition is out of range....
556struct MBRRecord* MBRData::GetPartition(int i) {
557 MBRRecord* thePart = NULL;
558
559 if ((i >= 0) && (i < 4)) { // primary partition
560 thePart = &partitions[i];
561 } // if
562 if ((i >= 4) && (i < (NUM_LOGICALS + 4))) {
563 thePart = &logicals[i - 4];
564 } // if
565 return thePart;
566} // GetPartition()
567
srs5694221e0872009-08-29 15:00:31 -0400568// Displays the state, as a word, on stdout. Used for debugging & to
569// tell the user about the MBR state when the program launches....
srs5694e7b4ff92009-08-18 13:16:10 -0400570void MBRData::ShowState(void) {
571 switch (state) {
572 case invalid:
srs5694221e0872009-08-29 15:00:31 -0400573 printf(" MBR: not present\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400574 break;
575 case gpt:
srs5694221e0872009-08-29 15:00:31 -0400576 printf(" MBR: protective\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400577 break;
578 case hybrid:
srs5694221e0872009-08-29 15:00:31 -0400579 printf(" MBR: hybrid\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400580 break;
581 case mbr:
srs5694221e0872009-08-29 15:00:31 -0400582 printf(" MBR: MBR only\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400583 break;
584 default:
srs5694221e0872009-08-29 15:00:31 -0400585 printf("\a MBR: unknown -- bug!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400586 break;
587 } // switch
588} // MBRData::ShowState()
589
590// Create a primary partition of the specified number, starting LBA,
591// and length. This function does *NO* error checking, so it's possible
592// to seriously screw up a partition table using this function! It's
593// intended as a way to create a hybrid MBR, which is a pretty funky
594// setup to begin with....
595void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
596 int bootable) {
597
598 partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
599 partitions[num].firstSector[0] = UINT8_C(0);
600 partitions[num].firstSector[1] = UINT8_C(0);
601 partitions[num].firstSector[2] = UINT8_C(0);
602 partitions[num].partitionType = (uint8_t) type;
603 partitions[num].lastSector[0] = UINT8_C(0);
604 partitions[num].lastSector[1] = UINT8_C(0);
605 partitions[num].lastSector[2] = UINT8_C(0);
606 partitions[num].firstLBA = start;
607 partitions[num].lengthLBA = length;
608} // MakePart()
609
srs5694c0ca8f82009-08-20 21:35:25 -0400610// Finds the first free space on the disk from start onward; returns 0
611// if none available....
612uint32_t MBRData::FindFirstAvailable(uint32_t start) {
613 uint32_t first;
614 uint32_t i;
615 int firstMoved = 0;
616
617 first = start;
618
619 // ...now search through all partitions; if first is within an
620 // existing partition, move it to the next sector after that
621 // partition and repeat. If first was moved, set firstMoved
622 // flag; repeat until firstMoved is not set, so as to catch
623 // cases where partitions are out of sequential order....
624 do {
625 firstMoved = 0;
626 for (i = 0; i < 4; i++) {
627 // Check if it's in the existing partition
628 if ((first >= partitions[i].firstLBA) &&
629 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
630 first = partitions[i].firstLBA + partitions[i].lengthLBA;
631 firstMoved = 1;
632 } // if
633 } // for
634 } while (firstMoved == 1);
635 if (first >= diskSize)
636 first = 0;
637 return (first);
638} // MBRData::FindFirstAvailable()
639
srs5694e4ac11e2009-08-31 10:13:04 -0400640// Finds the last free sector on the disk from start forward.
srs5694c0ca8f82009-08-20 21:35:25 -0400641uint32_t MBRData::FindLastInFree(uint32_t start) {
642 uint32_t nearestStart;
643 uint32_t i;
644
645 if (diskSize <= UINT32_MAX)
646 nearestStart = diskSize - 1;
647 else
648 nearestStart = UINT32_MAX - 1;
649 for (i = 0; i < 4; i++) {
650 if ((nearestStart > partitions[i].firstLBA) &&
651 (partitions[i].firstLBA > start)) {
652 nearestStart = partitions[i].firstLBA - 1;
653 } // if
654 } // for
655 return (nearestStart);
srs5694e4ac11e2009-08-31 10:13:04 -0400656} // MBRData::FindLastInFree()
657
658// Finds the first free sector on the disk from start backward.
659uint32_t MBRData::FindFirstInFree(uint32_t start) {
660 uint32_t nearestStart, bestLastLBA, thisLastLBA;
661 int i;
662
663 bestLastLBA = 1;
664 for (i = 0; i < 4; i++) {
665 thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
666 if (thisLastLBA > 0) thisLastLBA--;
667 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) {
668 bestLastLBA = thisLastLBA + 1;
669 } // if
670 } // for
671 return (bestLastLBA);
672} // MBRData::FindFirstInFree()
673
674// Returns 1 if the specified sector is unallocated, 0 if it's
675// allocated.
676int MBRData::IsFree(uint32_t sector) {
677 int i, isFree = 1;
678 uint32_t first, last;
679
680 for (i = 0; i < 4; i++) {
681 first = partitions[i].firstLBA;
682 last = first + partitions[i].lengthLBA;
683 if (last > 0) last--;
684 if ((first <= sector) && (last >= sector))
685 isFree = 0;
686 } // for
687 return isFree;
688} // MBRData::IsFree()
srs5694c0ca8f82009-08-20 21:35:25 -0400689
srs5694e7b4ff92009-08-18 13:16:10 -0400690uint8_t MBRData::GetStatus(int i) {
691 MBRRecord* thePart;
692 uint8_t retval;
693
694 thePart = GetPartition(i);
695 if (thePart != NULL)
696 retval = thePart->status;
697 else
698 retval = UINT8_C(0);
699 return retval;
700} // MBRData::GetStatus()
701
702uint8_t MBRData::GetType(int i) {
703 MBRRecord* thePart;
704 uint8_t retval;
705
706 thePart = GetPartition(i);
707 if (thePart != NULL)
708 retval = thePart->partitionType;
709 else
710 retval = UINT8_C(0);
711 return retval;
712} // MBRData::GetType()
713
714uint32_t MBRData::GetFirstSector(int i) {
715 MBRRecord* thePart;
716 uint32_t retval;
717
718 thePart = GetPartition(i);
719 if (thePart != NULL) {
720 retval = thePart->firstLBA;
721 } else
722 retval = UINT32_C(0);
723 return retval;
724} // MBRData::GetFirstSector()
725
726uint32_t MBRData::GetLength(int i) {
727 MBRRecord* thePart;
728 uint32_t retval;
729
730 thePart = GetPartition(i);
731 if (thePart != NULL) {
732 retval = thePart->lengthLBA;
733 } else
734 retval = UINT32_C(0);
735 return retval;
736} // MBRData::GetLength()
srs5694221e0872009-08-29 15:00:31 -0400737
738// Return the MBR data as a GPT partition....
739GPTPart MBRData::AsGPT(int i) {
740 MBRRecord* origPart;
741 GPTPart newPart;
742 uint8_t origType;
743 uint64_t firstSector, lastSector;
744 char tempStr[NAME_SIZE];
745
746 newPart.BlankPartition();
747 origPart = GetPartition(i);
748 if (origPart != NULL) {
749 origType = origPart->partitionType;
750
751 // don't convert extended, hybrid protective, or null (non-existent)
752 // partitions (Note similar protection is in GPTData::XFormPartitions(),
753 // but I want it here too in case I call this function in another
754 // context in the future....)
755 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
756 (origType != 0x00) && (origType != 0xEE)) {
757 firstSector = (uint64_t) origPart->firstLBA;
758 newPart.SetFirstLBA(firstSector);
759 lastSector = firstSector + (uint64_t) origPart->lengthLBA;
760 if (lastSector > 0) lastSector--;
761 newPart.SetLastLBA(lastSector);
762 newPart.SetType(((uint16_t) origType) * 0x0100);
763 newPart.SetUniqueGUID(1);
764 newPart.SetAttributes(0);
765 newPart.SetName((unsigned char*) newPart.GetNameType(tempStr));
766 } // if
767 } // if
768 return newPart;
769} // MBRData::AsGPT()