blob: e692adc145a7ef123e5a7939188bb0a66597c24f [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
55// Empty all data. Meant mainly for calling by constructors
56void MBRData::EmptyMBR(void) {
57 int i;
58
59 for (i = 0; i < 440; i++)
60 code[i] = 0;
61 diskSignature = (uint32_t) rand();
62 nulls = 0;
63 for (i = 0; i < 4; i++) {
64 partitions[i].status = UINT8_C(0);
65 partitions[i].firstSector[0] = UINT8_C(0);
66 partitions[i].firstSector[1] = UINT8_C(0);
67 partitions[i].firstSector[2] = UINT8_C(0);
68 partitions[i].partitionType = UINT8_C(0);
69 partitions[i].lastSector[0] = UINT8_C(0);
70 partitions[i].lastSector[1] = UINT8_C(0);
71 partitions[i].lastSector[2] = UINT8_C(0);
72 partitions[i].firstLBA = UINT32_C(0);
73 partitions[i].lengthLBA = UINT32_C(0);
74 } // for
75 MBRSignature = MBR_SIGNATURE;
76
77 blockSize = SECTOR_SIZE;
78 diskSize = 0;
79 for (i = 0; i < NUM_LOGICALS; i++) {
80 logicals[i].status = UINT8_C(0);
81 logicals[i].firstSector[0] = UINT8_C(0);
82 logicals[i].firstSector[1] = UINT8_C(0);
83 logicals[i].firstSector[2] = UINT8_C(0);
84 logicals[i].partitionType = UINT8_C(0);
85 logicals[i].lastSector[0] = UINT8_C(0);
86 logicals[i].lastSector[1] = UINT8_C(0);
87 logicals[i].lastSector[2] = UINT8_C(0);
88 logicals[i].firstLBA = UINT32_C(0);
89 logicals[i].lengthLBA = UINT32_C(0);
90 } // for
91} // MBRData::EmptyMBR()
92
93// Read data from MBR. Returns 1 if read was successful (even if the
94// data isn't a valid MBR), 0 if the read failed.
95int MBRData::ReadMBRData(char* deviceFilename) {
96 int fd, allOK = 1;
97
98 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
99 ReadMBRData(fd);
100 } else {
101 allOK = 0;
102 } // if
103
104 close(fd);
105
106 if (allOK)
107 strcpy(device, deviceFilename);
108
109 return allOK;
110} // MBRData::ReadMBRData(char* deviceFilename)
111
112// Read data from MBR.
113void MBRData::ReadMBRData(int fd) {
114 int allOK = 1, i;
115 int err;
116
117 // Clear logical partition array
118 for (i = 0; i < NUM_LOGICALS; i++) {
119 logicals[i].status = UINT8_C(0);
120 logicals[i].firstSector[0] = UINT8_C(0);
121 logicals[i].firstSector[1] = UINT8_C(0);
122 logicals[i].firstSector[2] = UINT8_C(0);
123 logicals[i].partitionType = UINT8_C(0);
124 logicals[i].lastSector[0] = UINT8_C(0);
125 logicals[i].lastSector[1] = UINT8_C(0);
126 logicals[i].lastSector[2] = UINT8_C(0);
127 logicals[i].firstLBA = UINT32_C(0);
128 logicals[i].lengthLBA = UINT32_C(0);
129 } // for
130
131 read(fd, code, 440);
132 read(fd, &diskSignature, 4);
133 read(fd, &nulls, 2);
134 read(fd, partitions, 64);
135 read(fd, &MBRSignature, 2);
136 if (MBRSignature != MBR_SIGNATURE) {
137 allOK = 0;
138 state = invalid;
139 fprintf(stderr, "MBR signature invalid; read 0x%04X, but should be 0x%04X\n",
140 (unsigned int) MBRSignature, (unsigned int) MBR_SIGNATURE);
141 } /* if */
142
143 // Find disk size
144 diskSize = disksize(fd, &err);
145
146 // Find block size
147 if ((blockSize = GetBlockSize(fd)) == -1) {
148 blockSize = SECTOR_SIZE;
149 printf("Unable to determine sector size; assuming %lu bytes!\n",
150 (unsigned long) SECTOR_SIZE);
151 } // if
152
153 // Load logical partition data, if any is found....
154 if (allOK) {
155 for (i = 0; i < 4; i++) {
156 if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
157 || (partitions[i].partitionType == 0x85)) {
158 // Found it, so call a recursive algorithm to load everything from them....
159 allOK = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), 0);
160 } // if
161 } // for
162 if (allOK) { // Loaded logicals OK
163 state = mbr;
164 } else {
165 state = invalid;
166 } // if
167 } // if
168
169 /* Check to see if it's in GPT format.... */
170 if (allOK) {
171 for (i = 0; i < 4; i++) {
172 if (partitions[i].partitionType == UINT8_C(0xEE)) {
173 state = gpt;
174 } /* if */
175 } /* for */
176 } /* if */
177
178/* // Tell the user what the MBR state is...
179 switch (state) {
180 case invalid:
181 printf("Information: MBR appears to be empty or invalid.\n");
182 break;
183 case gpt:
184 printf("Information: MBR holds GPT placeholder partitions.\n");
185 break;
186 case hybrid:
187 printf("Information: MBR holds hybrid GPT/MBR data.\n");
188 break;
189 case mbr:
190 printf("Information: MBR data appears to be valid.\n");
191 break;
192 } // switch */
193} // MBRData::ReadMBRData(int fd)
194
195// Write the MBR data to the default defined device.
196int MBRData::WriteMBRData(void) {
197 int allOK = 1, fd;
198
199 if ((fd = open(device, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
200 WriteMBRData(fd);
201 } else {
202 allOK = 0;
203 } // if/else
204 close(fd);
205 return allOK;
206} // MBRData::WriteMBRData(void)
207
208// Save the MBR data to a file. Note that this function writes ONLY the
209// MBR data, not the logical partitions (if any are defined).
210void MBRData::WriteMBRData(int fd) {
211 write(fd, code, 440);
212 write(fd, &diskSignature, 4);
213 write(fd, &nulls, 2);
214 write(fd, partitions, 64);
215 write(fd, &MBRSignature, 2);
216} // MBRData::WriteMBRData(int fd)
217
218// This is a recursive function to read all the logical partitions, following the
219// logical partition linked list from the disk and storing the basic data in
220int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
221 uint32_t diskOffset, int partNum) {
222 int allOK = 1;
223 struct EBRRecord ebr;
224 off_t offset;
225
226 offset = (off_t) (extendedStart + diskOffset) * blockSize;
227 if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
228 fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
229 allOK = 0;
230 }
231 if (read(fd, &ebr, 512) != 512) { // Load the data....
232 fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
233 (unsigned long) offset);
234 allOK = 0;
235 }
236 if (ebr.MBRSignature != MBR_SIGNATURE) {
237 allOK = 0;
238 printf("MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
239 (unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
240 } /* if */
241
242 // Copy over the basic data....
243 logicals[partNum].status = ebr.partitions[0].status;
244 logicals[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
245 logicals[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
246 logicals[partNum].partitionType = ebr.partitions[0].partitionType;
247
248 // Find the next partition (if there is one) and recurse....
249 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && allOK) {
250 allOK = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA,
251 partNum + 1);
252 } // if
253 return (allOK);
254} // MBRData::ReadLogicalPart()
255
256// Show the MBR data to the user....
257void MBRData::DisplayMBRData(void) {
258 int i;
259 char tempStr[255];
260
261 printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
262 printf("MBR partitions:\n");
263 printf("Number\t Start (block)\t Length (blocks)\tType\n");
264 for (i = 0; i < 4; i++) {
265 if (partitions[i].lengthLBA != 0) {
266 printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 1, (unsigned long) partitions[i].firstLBA,
267 (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
268 } // if
269 } // for
270
271 // Now display logical partition data....
272 for (i = 0; i < NUM_LOGICALS; i++) {
273 if (logicals[i].lengthLBA != 0) {
274 printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 5, (unsigned long) logicals[i].firstLBA,
275 (unsigned long) logicals[i].lengthLBA, logicals[i].partitionType);
276 } // if
277 } // for
278 printf("\nDisk size is %lu sectors (%s)\n", (unsigned long) diskSize,
279 BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
280} // MBRData::DisplayMBRData()
281
282// Create a protective MBR
283void MBRData::MakeProtectiveMBR(void) {
284 int i;
285
286 // Initialize variables
287 nulls = 0;
288 MBRSignature = MBR_SIGNATURE;
289
290 partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
291
292 // Write CHS data. This maxes out the use of the disk, as much as
293 // possible -- even to the point of exceeding the capacity of sub-8GB
294 // disks. The EFI spec says to use 0xffffff as the ending value,
295 // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
296 // and Apple's Disk Utility use 0xfeffff, and the latter puts that
297 // value in for the FIRST sector, too!
298 partitions[0].firstSector[0] = UINT8_C(0);
299 partitions[0].firstSector[1] = UINT8_C(1);
300 partitions[0].firstSector[2] = UINT8_C(0);
301 partitions[0].lastSector[0] = UINT8_C(255);
302 partitions[0].lastSector[1] = UINT8_C(255);
303 partitions[0].lastSector[2] = UINT8_C(255);
304
305 partitions[0].partitionType = UINT8_C(0xEE);
306 partitions[0].firstLBA = UINT32_C(1);
307 if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
308 partitions[0].lengthLBA = diskSize - 1;
309 } else { // disk is too big to represent, so fake it...
310 partitions[0].lengthLBA = UINT32_MAX;
311 } // if/else
312
313 // Zero out three unused primary partitions...
314 for (i = 1; i < 4; i++) {
315 partitions[i].status = UINT8_C(0);
316 partitions[i].firstSector[0] = UINT8_C(0);
317 partitions[i].firstSector[1] = UINT8_C(0);
318 partitions[i].firstSector[2] = UINT8_C(0);
319 partitions[i].partitionType = UINT8_C(0);
320 partitions[i].lastSector[0] = UINT8_C(0);
321 partitions[i].lastSector[1] = UINT8_C(0);
322 partitions[i].lastSector[2] = UINT8_C(0);
323 partitions[i].firstLBA = UINT32_C(0);
324 partitions[i].lengthLBA = UINT32_C(0);
325 } // for
326
327 // Zero out all the logical partitions. Not necessary for data
328 // integrity on write, but eliminates stray entries if user wants
329 // to view the MBR after converting the disk
330 for (i = 0; i < NUM_LOGICALS; i++) {
331 logicals[i].status = UINT8_C(0);
332 logicals[i].firstSector[0] = UINT8_C(0);
333 logicals[i].firstSector[1] = UINT8_C(0);
334 logicals[i].firstSector[2] = UINT8_C(0);
335 logicals[i].partitionType = UINT8_C(0);
336 logicals[i].lastSector[0] = UINT8_C(0);
337 logicals[i].lastSector[1] = UINT8_C(0);
338 logicals[i].lastSector[2] = UINT8_C(0);
339 logicals[i].firstLBA = UINT32_C(0);
340 logicals[i].lengthLBA = UINT32_C(0);
341 } // for
342
343 state = gpt;
344} // MBRData::MakeProtectiveMBR()
345
346// Return a pointer to a primary or logical partition, or NULL if
347// the partition is out of range....
348struct MBRRecord* MBRData::GetPartition(int i) {
349 MBRRecord* thePart = NULL;
350
351 if ((i >= 0) && (i < 4)) { // primary partition
352 thePart = &partitions[i];
353 } // if
354 if ((i >= 4) && (i < (NUM_LOGICALS + 4))) {
355 thePart = &logicals[i - 4];
356 } // if
357 return thePart;
358} // GetPartition()
359
360// Displays the state, as a word, on stdout. Used for debugging
361void MBRData::ShowState(void) {
362 switch (state) {
363 case invalid:
364 printf("invalid");
365 break;
366 case gpt:
367 printf("gpt");
368 break;
369 case hybrid:
370 printf("hybrid");
371 break;
372 case mbr:
373 printf("mbr");
374 break;
375 default:
376 printf("unknown -- bug!");
377 break;
378 } // switch
379} // MBRData::ShowState()
380
381// Create a primary partition of the specified number, starting LBA,
382// and length. This function does *NO* error checking, so it's possible
383// to seriously screw up a partition table using this function! It's
384// intended as a way to create a hybrid MBR, which is a pretty funky
385// setup to begin with....
386void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
387 int bootable) {
388
389 partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
390 partitions[num].firstSector[0] = UINT8_C(0);
391 partitions[num].firstSector[1] = UINT8_C(0);
392 partitions[num].firstSector[2] = UINT8_C(0);
393 partitions[num].partitionType = (uint8_t) type;
394 partitions[num].lastSector[0] = UINT8_C(0);
395 partitions[num].lastSector[1] = UINT8_C(0);
396 partitions[num].lastSector[2] = UINT8_C(0);
397 partitions[num].firstLBA = start;
398 partitions[num].lengthLBA = length;
399} // MakePart()
400
401uint8_t MBRData::GetStatus(int i) {
402 MBRRecord* thePart;
403 uint8_t retval;
404
405 thePart = GetPartition(i);
406 if (thePart != NULL)
407 retval = thePart->status;
408 else
409 retval = UINT8_C(0);
410 return retval;
411} // MBRData::GetStatus()
412
413uint8_t MBRData::GetType(int i) {
414 MBRRecord* thePart;
415 uint8_t retval;
416
417 thePart = GetPartition(i);
418 if (thePart != NULL)
419 retval = thePart->partitionType;
420 else
421 retval = UINT8_C(0);
422 return retval;
423} // MBRData::GetType()
424
425uint32_t MBRData::GetFirstSector(int i) {
426 MBRRecord* thePart;
427 uint32_t retval;
428
429 thePart = GetPartition(i);
430 if (thePart != NULL) {
431 retval = thePart->firstLBA;
432 } else
433 retval = UINT32_C(0);
434 return retval;
435} // MBRData::GetFirstSector()
436
437uint32_t MBRData::GetLength(int i) {
438 MBRRecord* thePart;
439 uint32_t retval;
440
441 thePart = GetPartition(i);
442 if (thePart != NULL) {
443 retval = thePart->lengthLBA;
444 } else
445 retval = UINT32_C(0);
446 return retval;
447} // MBRData::GetLength()