blob: e09d6b649ef5b0706910a2da4c84cc293d328958 [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
6#define __STDC_LIMIT_MACROS
7#define __STDC_CONSTANT_MACROS
8
9#include <stdio.h>
10#include <unistd.h>
11#include <stdlib.h>
12#include <stdint.h>
13#include <fcntl.h>
14#include <string.h>
15#include <time.h>
16#include <sys/stat.h>
17#include <errno.h>
18#include "mbr.h"
19#include "support.h"
20
21using namespace std;
22
23/****************************************
24 * *
25 * MBRData class and related structures *
26 * *
27 ****************************************/
28
29MBRData::MBRData(void) {
30 blockSize = SECTOR_SIZE;
31 diskSize = 0;
32 strcpy(device, "");
33 state = invalid;
34 srand((unsigned int) time(NULL));
35 EmptyMBR();
36} // MBRData default constructor
37
38MBRData::MBRData(char *filename) {
39 blockSize = SECTOR_SIZE;
40 diskSize = 0;
41 strcpy(device, filename);
42 state = invalid;
43
44 srand((unsigned int) time(NULL));
45 // Try to read the specified partition table, but if it fails....
46 if (!ReadMBRData(filename)) {
47 EmptyMBR();
48 strcpy(device, "");
49 } // if
50} // MBRData(char *filename) constructor
51
52MBRData::~MBRData(void) {
53} // MBRData destructor
54
srs5694c0ca8f82009-08-20 21:35:25 -040055// Empty all data. Meant mainly for calling by constructors, but it's also
56// used by the hybrid MBR functions in the GPTData class.
57void MBRData::EmptyMBR(int clearBootloader) {
srs5694e7b4ff92009-08-18 13:16:10 -040058 int i;
59
srs5694c0ca8f82009-08-20 21:35:25 -040060 // Zero out the boot loader section, the disk signature, and the
61 // 2-byte nulls area only if requested to do so. (This is the
62 // default.)
63 if (clearBootloader == 1) {
64 for (i = 0; i < 440; i++)
65 code[i] = 0;
66 diskSignature = (uint32_t) rand();
67 nulls = 0;
68 } // if
69
70 // Blank out the partitions
srs5694e7b4ff92009-08-18 13:16:10 -040071 for (i = 0; i < 4; i++) {
72 partitions[i].status = UINT8_C(0);
73 partitions[i].firstSector[0] = UINT8_C(0);
74 partitions[i].firstSector[1] = UINT8_C(0);
75 partitions[i].firstSector[2] = UINT8_C(0);
76 partitions[i].partitionType = UINT8_C(0);
77 partitions[i].lastSector[0] = UINT8_C(0);
78 partitions[i].lastSector[1] = UINT8_C(0);
79 partitions[i].lastSector[2] = UINT8_C(0);
80 partitions[i].firstLBA = UINT32_C(0);
81 partitions[i].lengthLBA = UINT32_C(0);
82 } // for
83 MBRSignature = MBR_SIGNATURE;
84
85 blockSize = SECTOR_SIZE;
86 diskSize = 0;
87 for (i = 0; i < NUM_LOGICALS; i++) {
88 logicals[i].status = UINT8_C(0);
89 logicals[i].firstSector[0] = UINT8_C(0);
90 logicals[i].firstSector[1] = UINT8_C(0);
91 logicals[i].firstSector[2] = UINT8_C(0);
92 logicals[i].partitionType = UINT8_C(0);
93 logicals[i].lastSector[0] = UINT8_C(0);
94 logicals[i].lastSector[1] = UINT8_C(0);
95 logicals[i].lastSector[2] = UINT8_C(0);
96 logicals[i].firstLBA = UINT32_C(0);
97 logicals[i].lengthLBA = UINT32_C(0);
98 } // for
99} // MBRData::EmptyMBR()
100
101// Read data from MBR. Returns 1 if read was successful (even if the
102// data isn't a valid MBR), 0 if the read failed.
103int MBRData::ReadMBRData(char* deviceFilename) {
104 int fd, allOK = 1;
105
106 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
107 ReadMBRData(fd);
108 } else {
109 allOK = 0;
110 } // if
111
112 close(fd);
113
114 if (allOK)
115 strcpy(device, deviceFilename);
116
117 return allOK;
118} // MBRData::ReadMBRData(char* deviceFilename)
119
120// Read data from MBR.
121void MBRData::ReadMBRData(int fd) {
122 int allOK = 1, i;
123 int err;
124
125 // Clear logical partition array
126 for (i = 0; i < NUM_LOGICALS; i++) {
127 logicals[i].status = UINT8_C(0);
128 logicals[i].firstSector[0] = UINT8_C(0);
129 logicals[i].firstSector[1] = UINT8_C(0);
130 logicals[i].firstSector[2] = UINT8_C(0);
131 logicals[i].partitionType = UINT8_C(0);
132 logicals[i].lastSector[0] = UINT8_C(0);
133 logicals[i].lastSector[1] = UINT8_C(0);
134 logicals[i].lastSector[2] = UINT8_C(0);
135 logicals[i].firstLBA = UINT32_C(0);
136 logicals[i].lengthLBA = UINT32_C(0);
137 } // for
138
139 read(fd, code, 440);
140 read(fd, &diskSignature, 4);
141 read(fd, &nulls, 2);
142 read(fd, partitions, 64);
143 read(fd, &MBRSignature, 2);
144 if (MBRSignature != MBR_SIGNATURE) {
145 allOK = 0;
146 state = invalid;
147 fprintf(stderr, "MBR signature invalid; read 0x%04X, but should be 0x%04X\n",
148 (unsigned int) MBRSignature, (unsigned int) MBR_SIGNATURE);
149 } /* if */
150
151 // Find disk size
152 diskSize = disksize(fd, &err);
153
154 // Find block size
155 if ((blockSize = GetBlockSize(fd)) == -1) {
156 blockSize = SECTOR_SIZE;
157 printf("Unable to determine sector size; assuming %lu bytes!\n",
158 (unsigned long) SECTOR_SIZE);
159 } // if
160
161 // Load logical partition data, if any is found....
162 if (allOK) {
163 for (i = 0; i < 4; i++) {
164 if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
165 || (partitions[i].partitionType == 0x85)) {
166 // Found it, so call a recursive algorithm to load everything from them....
167 allOK = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), 0);
168 } // if
169 } // for
170 if (allOK) { // Loaded logicals OK
171 state = mbr;
172 } else {
173 state = invalid;
174 } // if
175 } // if
176
177 /* Check to see if it's in GPT format.... */
178 if (allOK) {
179 for (i = 0; i < 4; i++) {
180 if (partitions[i].partitionType == UINT8_C(0xEE)) {
181 state = gpt;
182 } /* if */
183 } /* for */
184 } /* if */
185
186/* // Tell the user what the MBR state is...
187 switch (state) {
188 case invalid:
189 printf("Information: MBR appears to be empty or invalid.\n");
190 break;
191 case gpt:
192 printf("Information: MBR holds GPT placeholder partitions.\n");
193 break;
194 case hybrid:
195 printf("Information: MBR holds hybrid GPT/MBR data.\n");
196 break;
197 case mbr:
198 printf("Information: MBR data appears to be valid.\n");
199 break;
200 } // switch */
201} // MBRData::ReadMBRData(int fd)
202
203// Write the MBR data to the default defined device.
204int MBRData::WriteMBRData(void) {
205 int allOK = 1, fd;
206
207 if ((fd = open(device, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
208 WriteMBRData(fd);
209 } else {
210 allOK = 0;
211 } // if/else
212 close(fd);
213 return allOK;
214} // MBRData::WriteMBRData(void)
215
216// Save the MBR data to a file. Note that this function writes ONLY the
217// MBR data, not the logical partitions (if any are defined).
218void MBRData::WriteMBRData(int fd) {
219 write(fd, code, 440);
220 write(fd, &diskSignature, 4);
221 write(fd, &nulls, 2);
222 write(fd, partitions, 64);
223 write(fd, &MBRSignature, 2);
224} // MBRData::WriteMBRData(int fd)
225
226// This is a recursive function to read all the logical partitions, following the
227// logical partition linked list from the disk and storing the basic data in
228int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
229 uint32_t diskOffset, int partNum) {
230 int allOK = 1;
231 struct EBRRecord ebr;
232 off_t offset;
233
234 offset = (off_t) (extendedStart + diskOffset) * blockSize;
235 if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
236 fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
237 allOK = 0;
238 }
239 if (read(fd, &ebr, 512) != 512) { // Load the data....
240 fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
241 (unsigned long) offset);
242 allOK = 0;
243 }
244 if (ebr.MBRSignature != MBR_SIGNATURE) {
245 allOK = 0;
246 printf("MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
247 (unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
248 } /* if */
249
250 // Copy over the basic data....
251 logicals[partNum].status = ebr.partitions[0].status;
252 logicals[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
253 logicals[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
254 logicals[partNum].partitionType = ebr.partitions[0].partitionType;
255
256 // Find the next partition (if there is one) and recurse....
257 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && allOK) {
258 allOK = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA,
259 partNum + 1);
260 } // if
261 return (allOK);
262} // MBRData::ReadLogicalPart()
263
264// Show the MBR data to the user....
265void MBRData::DisplayMBRData(void) {
266 int i;
267 char tempStr[255];
srs5694e19ba092009-08-24 14:10:35 -0400268 char bootCode;
srs5694e7b4ff92009-08-18 13:16:10 -0400269
270 printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
271 printf("MBR partitions:\n");
srs5694e19ba092009-08-24 14:10:35 -0400272 printf("Number\t Boot\t Start (sector)\t Length (sectors)\tType\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400273 for (i = 0; i < 4; i++) {
274 if (partitions[i].lengthLBA != 0) {
srs5694e19ba092009-08-24 14:10:35 -0400275 if (partitions[i].status && 0x80) // it's bootable
276 bootCode = '*';
277 else
278 bootCode = ' ';
279 printf("%4d\t %c\t%13lu\t%15lu \t0x%02X\n", i + 1, bootCode,
280 (unsigned long) partitions[i].firstLBA,
srs5694e7b4ff92009-08-18 13:16:10 -0400281 (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
282 } // if
283 } // for
284
285 // Now display logical partition data....
286 for (i = 0; i < NUM_LOGICALS; i++) {
287 if (logicals[i].lengthLBA != 0) {
288 printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 5, (unsigned long) logicals[i].firstLBA,
289 (unsigned long) logicals[i].lengthLBA, logicals[i].partitionType);
290 } // if
291 } // for
292 printf("\nDisk size is %lu sectors (%s)\n", (unsigned long) diskSize,
293 BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
294} // MBRData::DisplayMBRData()
295
296// Create a protective MBR
297void MBRData::MakeProtectiveMBR(void) {
298 int i;
299
300 // Initialize variables
301 nulls = 0;
302 MBRSignature = MBR_SIGNATURE;
303
304 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
305
306 // Write CHS data. This maxes out the use of the disk, as much as
307 // possible -- even to the point of exceeding the capacity of sub-8GB
308 // disks. The EFI spec says to use 0xffffff as the ending value,
309 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
310 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
311 // value in for the FIRST sector, too!
312 partitions[0].firstSector[0] = UINT8_C(0);
313 partitions[0].firstSector[1] = UINT8_C(1);
314 partitions[0].firstSector[2] = UINT8_C(0);
315 partitions[0].lastSector[0] = UINT8_C(255);
316 partitions[0].lastSector[1] = UINT8_C(255);
317 partitions[0].lastSector[2] = UINT8_C(255);
318
319 partitions[0].partitionType = UINT8_C(0xEE);
320 partitions[0].firstLBA = UINT32_C(1);
321 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
322 partitions[0].lengthLBA = diskSize - 1;
323 } else { // disk is too big to represent, so fake it...
324 partitions[0].lengthLBA = UINT32_MAX;
325 } // if/else
326
327 // Zero out three unused primary partitions...
328 for (i = 1; i < 4; i++) {
329 partitions[i].status = UINT8_C(0);
330 partitions[i].firstSector[0] = UINT8_C(0);
331 partitions[i].firstSector[1] = UINT8_C(0);
332 partitions[i].firstSector[2] = UINT8_C(0);
333 partitions[i].partitionType = UINT8_C(0);
334 partitions[i].lastSector[0] = UINT8_C(0);
335 partitions[i].lastSector[1] = UINT8_C(0);
336 partitions[i].lastSector[2] = UINT8_C(0);
337 partitions[i].firstLBA = UINT32_C(0);
338 partitions[i].lengthLBA = UINT32_C(0);
339 } // for
340
341 // Zero out all the logical partitions. Not necessary for data
342 // integrity on write, but eliminates stray entries if user wants
343 // to view the MBR after converting the disk
344 for (i = 0; i < NUM_LOGICALS; i++) {
345 logicals[i].status = UINT8_C(0);
346 logicals[i].firstSector[0] = UINT8_C(0);
347 logicals[i].firstSector[1] = UINT8_C(0);
348 logicals[i].firstSector[2] = UINT8_C(0);
349 logicals[i].partitionType = UINT8_C(0);
350 logicals[i].lastSector[0] = UINT8_C(0);
351 logicals[i].lastSector[1] = UINT8_C(0);
352 logicals[i].lastSector[2] = UINT8_C(0);
353 logicals[i].firstLBA = UINT32_C(0);
354 logicals[i].lengthLBA = UINT32_C(0);
355 } // for
356
357 state = gpt;
358} // MBRData::MakeProtectiveMBR()
359
srs5694c0ca8f82009-08-20 21:35:25 -0400360// Create a partition that fills the most available space. Returns
361// 1 if partition was created, 0 otherwise. Intended for use in
362// creating hybrid MBRs.
363int MBRData::MakeBiggestPart(int i, int type) {
364 uint32_t start = UINT32_C(1); // starting point for each search
365 uint32_t firstBlock; // first block in a segment
366 uint32_t lastBlock; // last block in a segment
367 uint32_t segmentSize; // size of segment in blocks
368 uint32_t selectedSegment = UINT32_C(0); // location of largest segment
369 uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
370 int found = 0;
371
372 do {
373 firstBlock = FindFirstAvailable(start);
374 if (firstBlock != UINT32_C(0)) { // something's free...
375 lastBlock = FindLastInFree(firstBlock);
376 segmentSize = lastBlock - firstBlock + UINT32_C(1);
377 if (segmentSize > selectedSize) {
378 selectedSize = segmentSize;
379 selectedSegment = firstBlock;
380 } // if
381 start = lastBlock + 1;
382 } // if
383 } while (firstBlock != 0);
384 if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
385 found = 1;
386 MakePart(i, selectedSegment, selectedSize, type, 0);
387 } else {
388 found = 0;
389 } // if/else
390 return found;
391} // MBRData::MakeBiggestPart(int i)
392
srs5694e7b4ff92009-08-18 13:16:10 -0400393// Return a pointer to a primary or logical partition, or NULL if
394// the partition is out of range....
395struct MBRRecord* MBRData::GetPartition(int i) {
396 MBRRecord* thePart = NULL;
397
398 if ((i >= 0) && (i < 4)) { // primary partition
399 thePart = &partitions[i];
400 } // if
401 if ((i >= 4) && (i < (NUM_LOGICALS + 4))) {
402 thePart = &logicals[i - 4];
403 } // if
404 return thePart;
405} // GetPartition()
406
407// Displays the state, as a word, on stdout. Used for debugging
408void MBRData::ShowState(void) {
409 switch (state) {
410 case invalid:
411 printf("invalid");
412 break;
413 case gpt:
414 printf("gpt");
415 break;
416 case hybrid:
417 printf("hybrid");
418 break;
419 case mbr:
420 printf("mbr");
421 break;
422 default:
423 printf("unknown -- bug!");
424 break;
425 } // switch
426} // MBRData::ShowState()
427
428// Create a primary partition of the specified number, starting LBA,
429// and length. This function does *NO* error checking, so it's possible
430// to seriously screw up a partition table using this function! It's
431// intended as a way to create a hybrid MBR, which is a pretty funky
432// setup to begin with....
433void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
434 int bootable) {
435
436 partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
437 partitions[num].firstSector[0] = UINT8_C(0);
438 partitions[num].firstSector[1] = UINT8_C(0);
439 partitions[num].firstSector[2] = UINT8_C(0);
440 partitions[num].partitionType = (uint8_t) type;
441 partitions[num].lastSector[0] = UINT8_C(0);
442 partitions[num].lastSector[1] = UINT8_C(0);
443 partitions[num].lastSector[2] = UINT8_C(0);
444 partitions[num].firstLBA = start;
445 partitions[num].lengthLBA = length;
446} // MakePart()
447
srs5694c0ca8f82009-08-20 21:35:25 -0400448// Finds the first free space on the disk from start onward; returns 0
449// if none available....
450uint32_t MBRData::FindFirstAvailable(uint32_t start) {
451 uint32_t first;
452 uint32_t i;
453 int firstMoved = 0;
454
455 first = start;
456
457 // ...now search through all partitions; if first is within an
458 // existing partition, move it to the next sector after that
459 // partition and repeat. If first was moved, set firstMoved
460 // flag; repeat until firstMoved is not set, so as to catch
461 // cases where partitions are out of sequential order....
462 do {
463 firstMoved = 0;
464 for (i = 0; i < 4; i++) {
465 // Check if it's in the existing partition
466 if ((first >= partitions[i].firstLBA) &&
467 (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
468 first = partitions[i].firstLBA + partitions[i].lengthLBA;
469 firstMoved = 1;
470 } // if
471 } // for
472 } while (firstMoved == 1);
473 if (first >= diskSize)
474 first = 0;
475 return (first);
476} // MBRData::FindFirstAvailable()
477
478uint32_t MBRData::FindLastInFree(uint32_t start) {
479 uint32_t nearestStart;
480 uint32_t i;
481
482 if (diskSize <= UINT32_MAX)
483 nearestStart = diskSize - 1;
484 else
485 nearestStart = UINT32_MAX - 1;
486 for (i = 0; i < 4; i++) {
487 if ((nearestStart > partitions[i].firstLBA) &&
488 (partitions[i].firstLBA > start)) {
489 nearestStart = partitions[i].firstLBA - 1;
490 } // if
491 } // for
492 return (nearestStart);
493} // MBRData::FindLastInFree
494
srs5694e7b4ff92009-08-18 13:16:10 -0400495uint8_t MBRData::GetStatus(int i) {
496 MBRRecord* thePart;
497 uint8_t retval;
498
499 thePart = GetPartition(i);
500 if (thePart != NULL)
501 retval = thePart->status;
502 else
503 retval = UINT8_C(0);
504 return retval;
505} // MBRData::GetStatus()
506
507uint8_t MBRData::GetType(int i) {
508 MBRRecord* thePart;
509 uint8_t retval;
510
511 thePart = GetPartition(i);
512 if (thePart != NULL)
513 retval = thePart->partitionType;
514 else
515 retval = UINT8_C(0);
516 return retval;
517} // MBRData::GetType()
518
519uint32_t MBRData::GetFirstSector(int i) {
520 MBRRecord* thePart;
521 uint32_t retval;
522
523 thePart = GetPartition(i);
524 if (thePart != NULL) {
525 retval = thePart->firstLBA;
526 } else
527 retval = UINT32_C(0);
528 return retval;
529} // MBRData::GetFirstSector()
530
531uint32_t MBRData::GetLength(int i) {
532 MBRRecord* thePart;
533 uint32_t retval;
534
535 thePart = GetPartition(i);
536 if (thePart != NULL) {
537 retval = thePart->lengthLBA;
538 } else
539 retval = UINT32_C(0);
540 return retval;
541} // MBRData::GetLength()