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-2013 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 <stdlib.h>
14#include <stdint.h>
15#include <fcntl.h>
16#include <string.h>
17#include <time.h>
18#include <sys/stat.h>
19#include <errno.h>
20#include <iostream>
21#include "mbr.h"
22
23using namespace std;
24
25/****************************************
26 *                                      *
27 * MBRData class and related structures *
28 *                                      *
29 ****************************************/
30
31/* // Assignment operator -- copy entire set of MBR data.
32MBRData & MBRData::operator=(const MBRData & orig) {
33   BasicMBRData::operator=(orig);
34   return *this;
35} // MBRData::operator=() */
36
37// Assignment operator -- copy entire set of MBR data.
38MBRData & MBRData::operator=(const BasicMBRData & orig) {
39   BasicMBRData::operator=(orig);
40   return *this;
41} // MBRData::operator=()
42
43/*****************************************************
44 *                                                   *
45 * Functions to create, delete, or change partitions *
46 *                                                   *
47 *****************************************************/
48
49// Create a protective MBR. Clears the boot loader area if clearBoot > 0.
50void MBRData::MakeProtectiveMBR(int clearBoot) {
51
52   EmptyMBR(clearBoot);
53
54   // Initialize variables
55   nulls = 0;
56   MBRSignature = MBR_SIGNATURE;
57   diskSignature = UINT32_C(0);
58
59   partitions[0].SetStatus(0); // Flag the protective part. as unbootable
60
61   partitions[0].SetType(UINT8_C(0xEE));
62   if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
63      partitions[0].SetLocation(UINT32_C(1), (uint32_t) diskSize - UINT32_C(1));
64   } else { // disk is too big to represent, so fake it...
65      partitions[0].SetLocation(UINT32_C(1), UINT32_MAX);
66   } // if/else
67   partitions[0].SetInclusion(PRIMARY);
68
69   state = gpt;
70} // MBRData::MakeProtectiveMBR()
71
72// Optimizes the size of the 0xEE (EFI GPT) partition
73void MBRData::OptimizeEESize(void) {
74   int i, typeFlag = 0;
75   uint64_t after;
76
77   for (i = 0; i < 4; i++) {
78      // Check for non-empty and non-0xEE partitions
79      if ((partitions[i].GetType() != 0xEE) && (partitions[i].GetType() != 0x00))
80         typeFlag++;
81      if (partitions[i].GetType() == 0xEE) {
82         // Blank space before this partition; fill it....
83         if (SectorUsedAs(partitions[i].GetStartLBA() - 1, 4) == NONE) {
84            partitions[i].SetStartLBA(FindFirstInFree(partitions[i].GetStartLBA() - 1));
85         } // if
86         // Blank space after this partition; fill it....
87         after = partitions[i].GetStartLBA() + partitions[i].GetLengthLBA();
88         if (SectorUsedAs(after, 4) == NONE) {
89            partitions[i].SetLengthLBA(FindLastInFree(after) - partitions[i].GetStartLBA() + 1);
90         } // if free space after
91         if (after > diskSize) {
92            if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
93               partitions[i].SetLengthLBA((uint32_t) diskSize - partitions[i].GetStartLBA());
94            } else { // disk is too big to represent, so fake it...
95               partitions[i].SetLengthLBA(UINT32_MAX - partitions[i].GetStartLBA());
96            } // if/else
97         } // if protective partition is too big
98         RecomputeCHS(i);
99      } // if partition is 0xEE
100   } // for partition loop
101   if (typeFlag == 0) { // No non-hybrid partitions found
102      MakeProtectiveMBR(); // ensure it's a fully compliant protective MBR.
103   } // if
104} // MBRData::OptimizeEESize()
105
106// Delete a partition if one exists at the specified location.
107// Returns 1 if a partition was deleted, 0 otherwise....
108// Used to help keep GPT & hybrid MBR partitions in sync....
109int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
110   uint32_t start32, length32;
111   int i, deleted = 0;
112
113   if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
114      start32 = (uint32_t) start64;
115      length32 = (uint32_t) length64;
116      for (i = 0; i < MAX_MBR_PARTS; i++) {
117         if ((partitions[i].GetType() != 0xEE) && (partitions[i].GetStartLBA() == start32)
118             && (partitions[i].GetLengthLBA() == length32)) {
119            DeletePartition(i);
120         if (state == hybrid)
121            OptimizeEESize();
122         deleted = 1;
123         } // if (match found)
124      } // for i (partition scan)
125   } // if (hybrid & GPT partition < 2TiB)
126   return deleted;
127} // MBRData::DeleteByLocation()
128
129/******************************************************
130 *                                                    *
131 * Functions that extract data on specific partitions *
132 *                                                    *
133 ******************************************************/
134
135// Return the MBR data as a GPT partition....
136GPTPart MBRData::AsGPT(int i) {
137   MBRPart* origPart;
138   GPTPart newPart;
139   uint8_t origType;
140   uint64_t firstSector, lastSector;
141
142   newPart.BlankPartition();
143   origPart = GetPartition(i);
144   if (origPart != NULL) {
145      origType = origPart->GetType();
146
147      // don't convert extended, hybrid protective, or null (non-existent)
148      // partitions (Note similar protection is in GPTData::XFormPartitions(),
149      // but I want it here too in case I call this function in another
150      // context in the future....)
151      if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
152          (origType != 0x00) && (origType != 0xEE)) {
153         firstSector = (uint64_t) origPart->GetStartLBA();
154         newPart.SetFirstLBA(firstSector);
155         lastSector = (uint64_t) origPart->GetLastLBA();
156         newPart.SetLastLBA(lastSector);
157         newPart.SetType(((uint16_t) origType) * 0x0100);
158         newPart.RandomizeUniqueGUID();
159         newPart.SetAttributes(0);
160         newPart.SetName(newPart.GetTypeName());
161      } // if not extended, protective, or non-existent
162   } // if (origPart != NULL)
163   return newPart;
164} // MBRData::AsGPT()
165
166