~ubuntu-branches/ubuntu/trusty/gdisk/trusty-proposed

« back to all changes in this revision

Viewing changes to mbr.cc

  • Committer: Bazaar Package Importer
  • Author(s): Guillaume Delacour
  • Date: 2009-12-01 21:04:25 UTC
  • Revision ID: james.westby@ubuntu.com-20091201210425-lznlvi764r2dwkf8
Tags: upstream-0.5.1
ImportĀ upstreamĀ versionĀ 0.5.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* mbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition
 
2
   data. */
 
3
 
 
4
/* Initial coding by Rod Smith, January to February, 2009 */
 
5
 
 
6
/* 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
 
 
9
#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
 
 
24
using namespace std;
 
25
 
 
26
/****************************************
 
27
 *                                      *
 
28
 * MBRData class and related structures *
 
29
 *                                      *
 
30
 ****************************************/
 
31
 
 
32
MBRData::MBRData(void) {
 
33
   blockSize = SECTOR_SIZE;
 
34
   diskSize = 0;
 
35
   strcpy(device, "");
 
36
   state = invalid;
 
37
   srand((unsigned int) time(NULL));
 
38
   numHeads = MAX_HEADS;
 
39
   numSecspTrack = MAX_SECSPERTRACK;
 
40
   EmptyMBR();
 
41
} // MBRData default constructor
 
42
 
 
43
MBRData::MBRData(char *filename) {
 
44
   blockSize = SECTOR_SIZE;
 
45
   diskSize = 0;
 
46
   strcpy(device, filename);
 
47
   state = invalid;
 
48
   numHeads = MAX_HEADS;
 
49
   numSecspTrack = MAX_SECSPERTRACK;
 
50
 
 
51
   srand((unsigned int) time(NULL));
 
52
   // Try to read the specified partition table, but if it fails....
 
53
   if (!ReadMBRData(filename)) {
 
54
      EmptyMBR();
 
55
      strcpy(device, "");
 
56
   } // if
 
57
} // MBRData(char *filename) constructor
 
58
 
 
59
MBRData::~MBRData(void) {
 
60
} // MBRData destructor
 
61
 
 
62
/**********************
 
63
 *                    *
 
64
 * Disk I/O functions *
 
65
 *                    *
 
66
 **********************/
 
67
 
 
68
// Read data from MBR. Returns 1 if read was successful (even if the
 
69
// data isn't a valid MBR), 0 if the read failed.
 
70
int MBRData::ReadMBRData(char* deviceFilename) {
 
71
   int fd, allOK = 1;
 
72
 
 
73
   if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
 
74
      ReadMBRData(fd);
 
75
   } else {
 
76
      allOK = 0;
 
77
   } // if
 
78
 
 
79
   close(fd);
 
80
 
 
81
   if (allOK)
 
82
      strcpy(device, deviceFilename);
 
83
 
 
84
   return allOK;
 
85
} // MBRData::ReadMBRData(char* deviceFilename)
 
86
 
 
87
// Read data from MBR. If checkBlockSize == 1 (the default), the block
 
88
// size is checked; otherwise it's set to the default (512 bytes).
 
89
// Note that any extended partition(s) present will be explicitly stored
 
90
// in the partitions[] array, along with their contained partitions; the
 
91
// extended container partition(s) should be ignored by other functions.
 
92
void MBRData::ReadMBRData(int fd, int checkBlockSize) {
 
93
   int allOK = 1, i, j, logicalNum;
 
94
   int err;
 
95
   TempMBR tempMBR;
 
96
 
 
97
   // Empty existing MBR data, including the logical partitions...
 
98
   EmptyMBR(0);
 
99
 
 
100
   err = lseek64(fd, 0, SEEK_SET);
 
101
   err = read(fd, &tempMBR, 512);
 
102
   for (i = 0; i < 440; i++)
 
103
      code[i] = tempMBR.code[i];
 
104
   diskSignature = tempMBR.diskSignature;
 
105
   nulls = tempMBR.nulls;
 
106
   for (i = 0; i < 4; i++) {
 
107
      partitions[i].status = tempMBR.partitions[i].status;
 
108
      partitions[i].partitionType = tempMBR.partitions[i].partitionType;
 
109
      partitions[i].firstLBA = tempMBR.partitions[i].firstLBA;
 
110
      partitions[i].lengthLBA = tempMBR.partitions[i].lengthLBA;
 
111
      for (j = 0; j < 3; j++) {
 
112
         partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j];
 
113
         partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j];
 
114
      } // for j... (reading parts of CHS geometry)
 
115
   } // for i... (reading all four partitions)
 
116
   MBRSignature = tempMBR.MBRSignature;
 
117
 
 
118
   // Reverse the byte order, if necessary
 
119
   if (IsLittleEndian() == 0) {
 
120
      ReverseBytes(&diskSignature, 4);
 
121
      ReverseBytes(&nulls, 2);
 
122
      ReverseBytes(&MBRSignature, 2);
 
123
      for (i = 0; i < 4; i++) {
 
124
         ReverseBytes(&partitions[i].firstLBA, 4);
 
125
         ReverseBytes(&partitions[i].lengthLBA, 4);
 
126
      } // for
 
127
   } // if
 
128
 
 
129
   if (MBRSignature != MBR_SIGNATURE) {
 
130
      allOK = 0;
 
131
      state = invalid;
 
132
   } // if
 
133
 
 
134
   // Find disk size
 
135
   diskSize = disksize(fd, &err);
 
136
 
 
137
   // Find block size
 
138
   if (checkBlockSize) {
 
139
      blockSize = GetBlockSize(fd);
 
140
   } // if (checkBlockSize)
 
141
 
 
142
   // Load logical partition data, if any is found....
 
143
   if (allOK) {
 
144
      for (i = 0; i < 4; i++) {
 
145
         if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
 
146
              || (partitions[i].partitionType == 0x85)) {
 
147
            // Found it, so call a recursive algorithm to load everything from them....
 
148
            logicalNum = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), 4);
 
149
            if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
 
150
               allOK = 0;
 
151
               fprintf(stderr, "Error reading logical partitions! List may be truncated!\n");
 
152
            } // if maxLogicals valid
 
153
         } // if primary partition is extended
 
154
      } // for primary partition loop
 
155
      if (allOK) { // Loaded logicals OK
 
156
         state = mbr;
 
157
      } else {
 
158
         state = invalid;
 
159
      } // if
 
160
   } // if
 
161
 
 
162
   /* Check to see if it's in GPT format.... */
 
163
   if (allOK) {
 
164
      for (i = 0; i < 4; i++) {
 
165
         if (partitions[i].partitionType == UINT8_C(0xEE)) {
 
166
            state = gpt;
 
167
         } // if
 
168
      } // for
 
169
   } // if
 
170
 
 
171
   // If there's an EFI GPT partition, look for other partition types,
 
172
   // to flag as hybrid
 
173
   if (state == gpt) {
 
174
      for (i = 0 ; i < 4; i++) {
 
175
         if ((partitions[i].partitionType != UINT8_C(0xEE)) &&
 
176
             (partitions[i].partitionType != UINT8_C(0x00)))
 
177
            state = hybrid;
 
178
      } // for
 
179
   } // if (hybrid detection code)
 
180
} // MBRData::ReadMBRData(int fd)
 
181
 
 
182
// This is a recursive function to read all the logical partitions, following the
 
183
// logical partition linked list from the disk and storing the basic data in the
 
184
// partitions[] array. Returns last index to partitions[] used, or -1 if there was
 
185
// a problem.
 
186
// Parameters:
 
187
// fd = file descriptor
 
188
// extendedStart = LBA of the start of the extended partition
 
189
// diskOffset = LBA offset WITHIN the extended partition of the one to be read
 
190
// partNum = location in partitions[] array to store retrieved data
 
191
int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
 
192
                             uint32_t diskOffset, int partNum) {
 
193
   struct TempMBR ebr;
 
194
   off_t offset;
 
195
 
 
196
   // Check for a valid partition number. Note that partitions MAY be read into
 
197
   // the area normally used by primary partitions, although the only calling
 
198
   // function as of GPT fdisk version 0.5.0 doesn't do so.
 
199
   if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
 
200
      offset = (off_t) (extendedStart + diskOffset) * blockSize;
 
201
      if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
 
202
         fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
 
203
         partNum = -1;
 
204
      }
 
205
      if (read(fd, &ebr, 512) != 512) { // Load the data....
 
206
         fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
 
207
                 (unsigned long) offset);
 
208
         partNum = -1;
 
209
      } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
 
210
         ReverseBytes(&ebr.MBRSignature, 2);
 
211
         ReverseBytes(&ebr.partitions[0].firstLBA, 4);
 
212
         ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
 
213
         ReverseBytes(&ebr.partitions[1].firstLBA, 4);
 
214
         ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
 
215
      } // if/else/if
 
216
 
 
217
      if (ebr.MBRSignature != MBR_SIGNATURE) {
 
218
         partNum = -1;
 
219
         fprintf(stderr, "MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
 
220
                (unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
 
221
      } // if
 
222
 
 
223
      // Copy over the basic data....
 
224
      partitions[partNum].status = ebr.partitions[0].status;
 
225
      partitions[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
 
226
      partitions[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
 
227
      partitions[partNum].partitionType = ebr.partitions[0].partitionType;
 
228
 
 
229
      // Find the next partition (if there is one) and recurse....
 
230
      if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) &&
 
231
          (partNum < (MAX_MBR_PARTS - 1))) {
 
232
         partNum = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA,
 
233
                                   partNum + 1);
 
234
      } else {
 
235
         partNum++;
 
236
      } // if another partition
 
237
   } // Not enough space for all the logicals (or previous error encountered)
 
238
   return (partNum);
 
239
} // MBRData::ReadLogicalPart()
 
240
 
 
241
// Write the MBR data to the default defined device. Note that this writes
 
242
// ONLY the MBR itself, not the logical partition data.
 
243
int MBRData::WriteMBRData(void) {
 
244
   int allOK = 1, fd;
 
245
 
 
246
   if ((fd = open(device, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
 
247
      WriteMBRData(fd);
 
248
   } else {
 
249
      allOK = 0;
 
250
   } // if/else
 
251
   close(fd);
 
252
   return allOK;
 
253
} // MBRData::WriteMBRData(void)
 
254
 
 
255
// Save the MBR data to a file. Note that this function writes ONLY the
 
256
// MBR data, not the logical partitions (if any are defined).
 
257
void MBRData::WriteMBRData(int fd) {
 
258
   int i, j;
 
259
   TempMBR tempMBR;
 
260
 
 
261
   // Reverse the byte order, if necessary
 
262
   if (IsLittleEndian() == 0) {
 
263
      ReverseBytes(&diskSignature, 4);
 
264
      ReverseBytes(&nulls, 2);
 
265
      ReverseBytes(&MBRSignature, 2);
 
266
      for (i = 0; i < 4; i++) {
 
267
         ReverseBytes(&partitions[i].firstLBA, 4);
 
268
         ReverseBytes(&partitions[i].lengthLBA, 4);
 
269
      } // for
 
270
   } // if
 
271
 
 
272
   // Copy MBR data to a 512-byte data structure for writing, to
 
273
   // work around a FreeBSD limitation....
 
274
   for (i = 0; i < 440; i++)
 
275
      tempMBR.code[i] = code[i];
 
276
   tempMBR.diskSignature = diskSignature;
 
277
   tempMBR.nulls = nulls;
 
278
   tempMBR.MBRSignature = MBRSignature;
 
279
   for (i = 0; i < 4; i++) {
 
280
      tempMBR.partitions[i].status = partitions[i].status;
 
281
      tempMBR.partitions[i].partitionType = partitions[i].partitionType;
 
282
      tempMBR.partitions[i].firstLBA = partitions[i].firstLBA;
 
283
      tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA;
 
284
      for (j = 0; j < 3; j++) {
 
285
         tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j];
 
286
         tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j];
 
287
      } // for j...
 
288
   } // for i...
 
289
 
 
290
   // Now write that data structure...
 
291
   lseek64(fd, 0, SEEK_SET);
 
292
   write(fd, &tempMBR, 512);
 
293
 
 
294
   // Reverse the byte order back, if necessary
 
295
   if (IsLittleEndian() == 0) {
 
296
      ReverseBytes(&diskSignature, 4);
 
297
      ReverseBytes(&nulls, 2);
 
298
      ReverseBytes(&MBRSignature, 2);
 
299
      for (i = 0; i < 4; i++) {
 
300
         ReverseBytes(&partitions[i].firstLBA, 4);
 
301
         ReverseBytes(&partitions[i].lengthLBA, 4);
 
302
      } // for
 
303
   }// if
 
304
} // MBRData::WriteMBRData(int fd)
 
305
 
 
306
/********************************************
 
307
 *                                          *
 
308
 * Functions that display data for the user *
 
309
 *                                          *
 
310
 ********************************************/
 
311
 
 
312
// Show the MBR data to the user....
 
313
void MBRData::DisplayMBRData(void) {
 
314
   int i;
 
315
   char tempStr[255];
 
316
   char bootCode;
 
317
 
 
318
   printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
 
319
   printf("MBR partitions:\n");
 
320
   printf("Number\t Boot\t Start (sector)\t Length (sectors)\tType\n");
 
321
   for (i = 0; i < MAX_MBR_PARTS; i++) {
 
322
      if (partitions[i].lengthLBA != 0) {
 
323
         if (partitions[i].status && 0x80) // it's bootable
 
324
            bootCode = '*';
 
325
         else
 
326
            bootCode = ' ';
 
327
         printf("%4d\t   %c\t%13lu\t%15lu \t0x%02X\n", i + 1, bootCode,
 
328
                (unsigned long) partitions[i].firstLBA,
 
329
                (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
 
330
      } // if
 
331
   } // for
 
332
   printf("\nDisk size is %llu sectors (%s)\n", (unsigned long long) diskSize,
 
333
          BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
 
334
} // MBRData::DisplayMBRData()
 
335
 
 
336
// Displays the state, as a word, on stdout. Used for debugging & to
 
337
// tell the user about the MBR state when the program launches....
 
338
void MBRData::ShowState(void) {
 
339
   switch (state) {
 
340
      case invalid:
 
341
         printf("  MBR: not present\n");
 
342
         break;
 
343
      case gpt:
 
344
         printf("  MBR: protective\n");
 
345
         break;
 
346
      case hybrid:
 
347
         printf("  MBR: hybrid\n");
 
348
         break;
 
349
      case mbr:
 
350
         printf("  MBR: MBR only\n");
 
351
         break;
 
352
      default:
 
353
         printf("\a  MBR: unknown -- bug!\n");
 
354
         break;
 
355
   } // switch
 
356
} // MBRData::ShowState()
 
357
 
 
358
/*********************************************************************
 
359
 *                                                                   *
 
360
 * Functions that set or get disk metadata (CHS geometry, disk size, *
 
361
 * etc.)                                                             *
 
362
 *                                                                   *
 
363
 *********************************************************************/
 
364
 
 
365
// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function.
 
366
// Note that this only sets the heads and sectors; the number of
 
367
// cylinders is determined by these values and the disk size.
 
368
void MBRData::SetCHSGeom(uint32_t h, uint32_t s) {
 
369
   if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
 
370
      numHeads = h;
 
371
      numSecspTrack = s;
 
372
   } else {
 
373
      printf("Warning! Attempt to set invalid CHS geometry!\n");
 
374
   } // if/else
 
375
} // MBRData::SetCHSGeom()
 
376
 
 
377
// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
 
378
// was within the range that can be expressed by CHS (including 0, for an
 
379
// empty partition), 0 if the value is outside that range, and -1 if chs is
 
380
// invalid.
 
381
int MBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
 
382
   uint64_t cylinder, head, sector; // all numbered from 0
 
383
   uint64_t remainder;
 
384
   int retval = 1;
 
385
   int done = 0;
 
386
 
 
387
   if (chs != NULL) {
 
388
      // Special case: In case of 0 LBA value, zero out CHS values....
 
389
      if (lba == 0) {
 
390
         chs[0] = chs[1] = chs[2] = UINT8_C(0);
 
391
         done = 1;
 
392
      } // if
 
393
      // If LBA value is too large for CHS, max out CHS values....
 
394
      if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
 
395
         chs[0] = 254;
 
396
         chs[1] = chs[2] = 255;
 
397
         done = 1;
 
398
         retval = 0;
 
399
      } // if
 
400
      // If neither of the above applies, compute CHS values....
 
401
      if (!done) {
 
402
         cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
 
403
         remainder = lba - (cylinder * numHeads * numSecspTrack);
 
404
         head = remainder / numSecspTrack;
 
405
         remainder -= head * numSecspTrack;
 
406
         sector = remainder;
 
407
         if (head < numHeads)
 
408
            chs[0] = head;
 
409
         else
 
410
            retval = 0;
 
411
         if (sector < numSecspTrack) {
 
412
            chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
 
413
            chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
 
414
         } else {
 
415
            retval = 0;
 
416
         } // if/else
 
417
      } // if value is expressible and non-0
 
418
   } else { // Invalid (NULL) chs pointer
 
419
      retval = -1;
 
420
   } // if CHS pointer valid
 
421
   return (retval);
 
422
} // MBRData::LBAtoCHS()
 
423
 
 
424
/*****************************************************
 
425
 *                                                   *
 
426
 * Functions to create, delete, or change partitions *
 
427
 *                                                   *
 
428
 *****************************************************/
 
429
 
 
430
// Empty all data. Meant mainly for calling by constructors, but it's also
 
431
// used by the hybrid MBR functions in the GPTData class.
 
432
void MBRData::EmptyMBR(int clearBootloader) {
 
433
   int i;
 
434
 
 
435
   // Zero out the boot loader section, the disk signature, and the
 
436
   // 2-byte nulls area only if requested to do so. (This is the
 
437
   // default.)
 
438
   if (clearBootloader == 1) {
 
439
      for (i = 0; i < 440; i++)
 
440
         code[i] = 0;
 
441
      diskSignature = (uint32_t) rand();
 
442
      nulls = 0;
 
443
   } // if
 
444
 
 
445
   // Blank out the partitions
 
446
   for (i = 0; i < MAX_MBR_PARTS; i++) {
 
447
      partitions[i].status = UINT8_C(0);
 
448
      partitions[i].firstSector[0] = UINT8_C(0);
 
449
      partitions[i].firstSector[1] = UINT8_C(0);
 
450
      partitions[i].firstSector[2] = UINT8_C(0);
 
451
      partitions[i].partitionType = UINT8_C(0);
 
452
      partitions[i].lastSector[0] = UINT8_C(0);
 
453
      partitions[i].lastSector[1] = UINT8_C(0);
 
454
      partitions[i].lastSector[2] = UINT8_C(0);
 
455
      partitions[i].firstLBA = UINT32_C(0);
 
456
      partitions[i].lengthLBA = UINT32_C(0);
 
457
   } // for
 
458
   MBRSignature = MBR_SIGNATURE;
 
459
} // MBRData::EmptyMBR()
 
460
 
 
461
// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
 
462
void MBRData::MakeProtectiveMBR(int clearBoot) {
 
463
 
 
464
   EmptyMBR(clearBoot);
 
465
 
 
466
   // Initialize variables
 
467
   nulls = 0;
 
468
   MBRSignature = MBR_SIGNATURE;
 
469
 
 
470
   partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
 
471
 
 
472
   // Write CHS data. This maxes out the use of the disk, as much as
 
473
   // possible -- even to the point of exceeding the capacity of sub-8GB
 
474
   // disks. The EFI spec says to use 0xffffff as the ending value,
 
475
   // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
 
476
   // and Apple's Disk Utility use 0xfeffff, and the latter puts that
 
477
   // value in for the FIRST sector, too!
 
478
   partitions[0].firstSector[0] = UINT8_C(0);
 
479
   partitions[0].firstSector[1] = UINT8_C(1);
 
480
   partitions[0].firstSector[2] = UINT8_C(0);
 
481
   partitions[0].lastSector[0] = UINT8_C(255);
 
482
   partitions[0].lastSector[1] = UINT8_C(255);
 
483
   partitions[0].lastSector[2] = UINT8_C(255);
 
484
 
 
485
   partitions[0].partitionType = UINT8_C(0xEE);
 
486
   partitions[0].firstLBA = UINT32_C(1);
 
487
   if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
 
488
      partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
 
489
   } else { // disk is too big to represent, so fake it...
 
490
      partitions[0].lengthLBA = UINT32_MAX;
 
491
   } // if/else
 
492
 
 
493
   state = gpt;
 
494
} // MBRData::MakeProtectiveMBR()
 
495
 
 
496
// Create a partition of the specified number, starting LBA, and
 
497
// length. This function does *NO* error checking, so it's possible
 
498
// to seriously screw up a partition table using this function!
 
499
// Note: This function should NOT be used to create the 0xEE partition
 
500
// in a conventional GPT configuration, since that partition has
 
501
// specific size requirements that this function won't handle. It may
 
502
// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
 
503
// since those toss the rulebook away anyhow....
 
504
void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
 
505
                       int bootable) {
 
506
   if ((num >= 0) && (num < MAX_MBR_PARTS)) {
 
507
      partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
 
508
      partitions[num].firstSector[0] = UINT8_C(0);
 
509
      partitions[num].firstSector[1] = UINT8_C(0);
 
510
      partitions[num].firstSector[2] = UINT8_C(0);
 
511
      partitions[num].partitionType = (uint8_t) type;
 
512
      partitions[num].lastSector[0] = UINT8_C(0);
 
513
      partitions[num].lastSector[1] = UINT8_C(0);
 
514
      partitions[num].lastSector[2] = UINT8_C(0);
 
515
      partitions[num].firstLBA = start;
 
516
      partitions[num].lengthLBA = length;
 
517
      // If this is a "real" partition, set its CHS geometry
 
518
      if (length > 0) {
 
519
         LBAtoCHS((uint64_t) start, partitions[num].firstSector);
 
520
         LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
 
521
      } // if (length > 0)
 
522
   } // if valid partition number
 
523
} // MBRData::MakePart()
 
524
 
 
525
// Create a partition that fills the most available space. Returns
 
526
// 1 if partition was created, 0 otherwise. Intended for use in
 
527
// creating hybrid MBRs.
 
528
int MBRData::MakeBiggestPart(int i, int type) {
 
529
   uint32_t start = UINT32_C(1); // starting point for each search
 
530
   uint32_t firstBlock; // first block in a segment
 
531
   uint32_t lastBlock; // last block in a segment
 
532
   uint32_t segmentSize; // size of segment in blocks
 
533
   uint32_t selectedSegment = UINT32_C(0); // location of largest segment
 
534
   uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
 
535
   int found = 0;
 
536
 
 
537
   do {
 
538
      firstBlock = FindFirstAvailable(start);
 
539
      if (firstBlock != UINT32_C(0)) { // something's free...
 
540
         lastBlock = FindLastInFree(firstBlock);
 
541
         segmentSize = lastBlock - firstBlock + UINT32_C(1);
 
542
         if (segmentSize > selectedSize) {
 
543
            selectedSize = segmentSize;
 
544
            selectedSegment = firstBlock;
 
545
         } // if
 
546
         start = lastBlock + 1;
 
547
      } // if
 
548
   } while (firstBlock != 0);
 
549
   if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
 
550
      found = 1;
 
551
      MakePart(i, selectedSegment, selectedSize, type, 0);
 
552
   } else {
 
553
      found = 0;
 
554
   } // if/else
 
555
   return found;
 
556
} // MBRData::MakeBiggestPart(int i)
 
557
 
 
558
// Delete partition #i
 
559
void MBRData::DeletePartition(int i) {
 
560
   int j;
 
561
 
 
562
   partitions[i].firstLBA = UINT32_C(0);
 
563
   partitions[i].lengthLBA = UINT32_C(0);
 
564
   partitions[i].status = UINT8_C(0);
 
565
   partitions[i].partitionType = UINT8_C(0);
 
566
   for (j = 0; j < 3; j++) {
 
567
      partitions[i].firstSector[j] = UINT8_C(0);
 
568
      partitions[i].lastSector[j] = UINT8_C(0);
 
569
   } // for j (CHS data blanking)
 
570
} // MBRData::DeletePartition()
 
571
 
 
572
// Delete a partition if one exists at the specified location.
 
573
// Returns 1 if a partition was deleted, 0 otherwise....
 
574
// Used to help keep GPT & hybrid MBR partitions in sync....
 
575
int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
 
576
   uint32_t start32, length32;
 
577
   int i, deleted = 0;
 
578
 
 
579
   if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
 
580
      start32 = (uint32_t) start64;
 
581
      length32 = (uint32_t) length64;
 
582
      for (i = 0; i < MAX_MBR_PARTS; i++) {
 
583
         if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
 
584
              (partitions[i].partitionType != 0xEE)) {
 
585
            DeletePartition(i);
 
586
            if (state == hybrid)
 
587
               OptimizeEESize();
 
588
            deleted = 1;
 
589
              } // if (match found)
 
590
      } // for i (partition scan)
 
591
   } // if (hybrid & GPT partition < 2TiB)
 
592
   return deleted;
 
593
} // MBRData::DeleteByLocation()
 
594
 
 
595
// Optimizes the size of the 0xEE (EFI GPT) partition
 
596
void MBRData::OptimizeEESize(void) {
 
597
   int i, typeFlag = 0;
 
598
   uint32_t after;
 
599
 
 
600
   for (i = 0; i < 4; i++) {
 
601
      // Check for non-empty and non-0xEE partitions
 
602
      if ((partitions[i].partitionType != 0xEE) && (partitions[i].partitionType != 0x00))
 
603
         typeFlag++;
 
604
      if (partitions[i].partitionType == 0xEE) {
 
605
         // Blank space before this partition; fill it....
 
606
         if (IsFree(partitions[i].firstLBA - 1)) {
 
607
            partitions[i].firstLBA = FindFirstInFree(partitions[i].firstLBA - 1);
 
608
         } // if
 
609
         // Blank space after this partition; fill it....
 
610
         after = partitions[i].firstLBA + partitions[i].lengthLBA;
 
611
         if (IsFree(after)) {
 
612
            partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
 
613
         } // if free space after
 
614
      } // if partition is 0xEE
 
615
   } // for partition loop
 
616
   if (typeFlag == 0) { // No non-hybrid partitions found
 
617
      MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
 
618
   } // if
 
619
} // MBRData::OptimizeEESize()
 
620
 
 
621
/****************************************
 
622
 *                                      *
 
623
 * Functions to find data on free space *
 
624
 *                                      *
 
625
 ****************************************/
 
626
 
 
627
// Finds the first free space on the disk from start onward; returns 0
 
628
// if none available....
 
629
uint32_t MBRData::FindFirstAvailable(uint32_t start) {
 
630
   uint32_t first;
 
631
   uint32_t i;
 
632
   int firstMoved;
 
633
 
 
634
   first = start;
 
635
 
 
636
   // ...now search through all partitions; if first is within an
 
637
   // existing partition, move it to the next sector after that
 
638
   // partition and repeat. If first was moved, set firstMoved
 
639
   // flag; repeat until firstMoved is not set, so as to catch
 
640
   // cases where partitions are out of sequential order....
 
641
   do {
 
642
      firstMoved = 0;
 
643
      for (i = 0; i < 4; i++) {
 
644
         // Check if it's in the existing partition
 
645
         if ((first >= partitions[i].firstLBA) &&
 
646
             (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
 
647
            first = partitions[i].firstLBA + partitions[i].lengthLBA;
 
648
            firstMoved = 1;
 
649
         } // if
 
650
      } // for
 
651
   } while (firstMoved == 1);
 
652
   if (first >= diskSize)
 
653
      first = 0;
 
654
   return (first);
 
655
} // MBRData::FindFirstAvailable()
 
656
 
 
657
// Finds the last free sector on the disk from start forward.
 
658
uint32_t MBRData::FindLastInFree(uint32_t start) {
 
659
   uint32_t nearestStart;
 
660
   uint32_t i;
 
661
 
 
662
   if ((diskSize <= UINT32_MAX) && (diskSize > 0))
 
663
      nearestStart = diskSize - 1;
 
664
   else
 
665
      nearestStart = UINT32_MAX - 1;
 
666
   for (i = 0; i < 4; i++) {
 
667
      if ((nearestStart > partitions[i].firstLBA) &&
 
668
          (partitions[i].firstLBA > start)) {
 
669
         nearestStart = partitions[i].firstLBA - 1;
 
670
      } // if
 
671
   } // for
 
672
   return (nearestStart);
 
673
} // MBRData::FindLastInFree()
 
674
 
 
675
// Finds the first free sector on the disk from start backward.
 
676
uint32_t MBRData::FindFirstInFree(uint32_t start) {
 
677
   uint32_t bestLastLBA, thisLastLBA;
 
678
   int i;
 
679
 
 
680
   bestLastLBA = 1;
 
681
   for (i = 0; i < 4; i++) {
 
682
      thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
 
683
      if (thisLastLBA > 0) thisLastLBA--;
 
684
      if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) {
 
685
         bestLastLBA = thisLastLBA + 1;
 
686
      } // if
 
687
   } // for
 
688
   return (bestLastLBA);
 
689
} // MBRData::FindFirstInFree()
 
690
 
 
691
// Returns 1 if the specified sector is unallocated, 0 if it's
 
692
// allocated.
 
693
int MBRData::IsFree(uint32_t sector) {
 
694
   int i, isFree = 1;
 
695
   uint32_t first, last;
 
696
 
 
697
   for (i = 0; i < 4; i++) {
 
698
      first = partitions[i].firstLBA;
 
699
      // Note: Weird two-line thing to avoid subtracting 1 from a 0 value
 
700
      // for an unsigned int....
 
701
      last = first + partitions[i].lengthLBA;
 
702
      if (last > 0) last--;
 
703
      if ((first <= sector) && (last >= sector))
 
704
         isFree = 0;
 
705
   } // for
 
706
   return isFree;
 
707
} // MBRData::IsFree()
 
708
 
 
709
/******************************************************
 
710
 *                                                    *
 
711
 * Functions that extract data on specific partitions *
 
712
 *                                                    *
 
713
 ******************************************************/
 
714
 
 
715
uint8_t MBRData::GetStatus(int i) {
 
716
   MBRRecord* thePart;
 
717
   uint8_t retval;
 
718
 
 
719
   thePart = GetPartition(i);
 
720
   if (thePart != NULL)
 
721
      retval = thePart->status;
 
722
   else
 
723
      retval = UINT8_C(0);
 
724
   return retval;
 
725
} // MBRData::GetStatus()
 
726
 
 
727
uint8_t MBRData::GetType(int i) {
 
728
   MBRRecord* thePart;
 
729
   uint8_t retval;
 
730
 
 
731
   thePart = GetPartition(i);
 
732
   if (thePart != NULL)
 
733
      retval = thePart->partitionType;
 
734
   else
 
735
      retval = UINT8_C(0);
 
736
   return retval;
 
737
} // MBRData::GetType()
 
738
 
 
739
uint32_t MBRData::GetFirstSector(int i) {
 
740
   MBRRecord* thePart;
 
741
   uint32_t retval;
 
742
 
 
743
   thePart = GetPartition(i);
 
744
   if (thePart != NULL) {
 
745
      retval = thePart->firstLBA;
 
746
   } else
 
747
      retval = UINT32_C(0);
 
748
      return retval;
 
749
} // MBRData::GetFirstSector()
 
750
 
 
751
uint32_t MBRData::GetLength(int i) {
 
752
   MBRRecord* thePart;
 
753
   uint32_t retval;
 
754
 
 
755
   thePart = GetPartition(i);
 
756
   if (thePart != NULL) {
 
757
      retval = thePart->lengthLBA;
 
758
   } else
 
759
      retval = UINT32_C(0);
 
760
      return retval;
 
761
} // MBRData::GetLength()
 
762
 
 
763
// Return the MBR data as a GPT partition....
 
764
GPTPart MBRData::AsGPT(int i) {
 
765
   MBRRecord* origPart;
 
766
   GPTPart newPart;
 
767
   uint8_t origType;
 
768
   uint64_t firstSector, lastSector;
 
769
   char tempStr[NAME_SIZE];
 
770
 
 
771
   newPart.BlankPartition();
 
772
   origPart = GetPartition(i);
 
773
   if (origPart != NULL) {
 
774
      origType = origPart->partitionType;
 
775
 
 
776
      // don't convert extended, hybrid protective, or null (non-existent)
 
777
      // partitions (Note similar protection is in GPTData::XFormPartitions(),
 
778
      // but I want it here too in case I call this function in another
 
779
      // context in the future....)
 
780
      if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
 
781
          (origType != 0x00) && (origType != 0xEE)) {
 
782
         firstSector = (uint64_t) origPart->firstLBA;
 
783
         newPart.SetFirstLBA(firstSector);
 
784
         lastSector = firstSector + (uint64_t) origPart->lengthLBA;
 
785
         if (lastSector > 0) lastSector--;
 
786
         newPart.SetLastLBA(lastSector);
 
787
         newPart.SetType(((uint16_t) origType) * 0x0100);
 
788
         newPart.SetUniqueGUID(1);
 
789
         newPart.SetAttributes(0);
 
790
         newPart.SetName((unsigned char*) newPart.GetNameType(tempStr));
 
791
      } // if not extended, protective, or non-existent
 
792
   } // if (origPart != NULL)
 
793
   return newPart;
 
794
} // MBRData::AsGPT()
 
795
 
 
796
/***********************
 
797
 *                     *
 
798
 * Protected functions *
 
799
 *                     *
 
800
 ***********************/
 
801
 
 
802
// Return a pointer to a primary or logical partition, or NULL if
 
803
// the partition is out of range....
 
804
struct MBRRecord* MBRData::GetPartition(int i) {
 
805
   MBRRecord* thePart = NULL;
 
806
 
 
807
   if ((i >= 0) && (i < MAX_MBR_PARTS))
 
808
      thePart = &partitions[i];
 
809
   return thePart;
 
810
} // GetPartition()