1/*
2    MBRPart class, part of GPT fdisk program family.
3    Copyright (C) 2011  Roderick W. Smith
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License along
16    with this program; if not, write to the Free Software Foundation, Inc.,
17    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18*/
19
20#define __STDC_LIMIT_MACROS
21#define __STDC_CONSTANT_MACROS
22
23#include <stddef.h>
24#include <stdint.h>
25#include <iostream>
26#include "support.h"
27#include "mbrpart.h"
28
29using namespace std;
30
31uint32_t MBRPart::numHeads = MAX_HEADS;
32uint32_t MBRPart::numSecspTrack = MAX_SECSPERTRACK;
33uint64_t MBRPart::diskSize = 0;
34uint32_t MBRPart::blockSize = 512;
35int MBRPart::numInstances = 0;
36
37MBRPart::MBRPart() {
38   int i;
39
40   status = 0;
41   for (i = 0; i < 3; i++) {
42      firstSector[i] = 0;
43      lastSector[i] = 0;
44   } // for
45   partitionType = 0x00;
46   firstLBA = 0;
47   lengthLBA = 0;
48   includeAs = NONE;
49   canBePrimary = 0;
50   canBeLogical = 0;
51   if (numInstances == 0) {
52      numHeads = MAX_HEADS;
53      numSecspTrack = MAX_SECSPERTRACK;
54      diskSize = 0;
55      blockSize = 512;
56   } // if
57   numInstances++;
58}
59
60MBRPart::MBRPart(const MBRPart& orig) {
61   numInstances++;
62   operator=(orig);
63}
64
65MBRPart::~MBRPart() {
66   numInstances--;
67}
68
69MBRPart& MBRPart::operator=(const MBRPart& orig) {
70   int i;
71
72   status = orig.status;
73   for (i = 0; i < 3; i++) {
74      firstSector[i] = orig.firstSector[i];
75      lastSector[i] = orig.lastSector[i];
76   } // for
77   partitionType = orig.partitionType;
78   firstLBA = orig.firstLBA;
79   lengthLBA = orig.lengthLBA;
80   includeAs = orig.includeAs;
81   canBePrimary = orig.canBePrimary;
82   canBeLogical = orig.canBeLogical;
83   return *this;
84} // MBRPart::operator=(const MBRPart& orig)
85
86// Set partition data from packed MBRRecord structure.
87MBRPart& MBRPart::operator=(const struct MBRRecord& orig) {
88   int i;
89
90   status = orig.status;
91   for (i = 0; i < 3; i++) {
92      firstSector[i] = orig.firstSector[i];
93      lastSector[i] = orig.lastSector[i];
94   } // for
95   partitionType = orig.partitionType;
96   firstLBA = orig.firstLBA;
97   lengthLBA = orig.lengthLBA;
98   if (lengthLBA > 0)
99      includeAs = PRIMARY;
100   else
101      includeAs = NONE;
102   return *this;
103} // MBRPart::operator=(const struct MBRRecord& orig)
104
105// Compare the values, and return a bool result.
106// Because this is intended for sorting and a lengthLBA value of 0 denotes
107// a partition that's not in use and so that should be sorted upwards,
108// we return the opposite of the usual arithmetic result when either
109// lengthLBA value is 0.
110bool MBRPart::operator<(const MBRPart &other) const {
111   if (lengthLBA && other.lengthLBA)
112      return (firstLBA < other.firstLBA);
113   else
114      return (other.firstLBA < firstLBA);
115} // operator<()
116
117/**************************************************
118 *                                                *
119 * Set information on partitions or disks without *
120 * interacting with the user....                  *
121 *                                                *
122 **************************************************/
123
124void MBRPart::SetGeometry(uint32_t heads, uint32_t sectors, uint64_t ds, uint32_t bs) {
125   numHeads = heads;
126   numSecspTrack = sectors;
127   diskSize = ds;
128   blockSize = bs;
129} // MBRPart::SetGeometry
130
131// Empty the partition (zero out all values).
132void MBRPart::Empty(void) {
133   status = UINT8_C(0);
134   firstSector[0] = UINT8_C(0);
135   firstSector[1] = UINT8_C(0);
136   firstSector[2] = UINT8_C(0);
137   partitionType = UINT8_C(0);
138   lastSector[0] = UINT8_C(0);
139   lastSector[1] = UINT8_C(0);
140   lastSector[2] = UINT8_C(0);
141   firstLBA = UINT32_C(0);
142   lengthLBA = UINT32_C(0);
143   includeAs = NONE;
144} // MBRPart::Empty()
145
146// Sets the type code, but silently refuses to change it to an extended type
147// code.
148// Returns 1 on success, 0 on failure (extended type code)
149int MBRPart::SetType(uint8_t typeCode, int isExtended) {
150   int allOK = 0;
151
152   if ((isExtended == 1) || ((typeCode != 0x05) && (typeCode != 0x0f) && (typeCode != 0x85))) {
153      partitionType = typeCode;
154      allOK = 1;
155   } // if
156   return allOK;
157} // MBRPart::SetType()
158
159void MBRPart::SetStartLBA(uint64_t start) {
160   if (start > UINT32_MAX)
161      cerr << "Partition start out of range! Continuing, but problems now likely!\n";
162   firstLBA = (uint32_t) start;
163   RecomputeCHS();
164} // MBRPart::SetStartLBA()
165
166void MBRPart::SetLengthLBA(uint64_t length) {
167   if (length > UINT32_MAX)
168      cerr << "Partition length out of range! Continuing, but problems now likely!\n";
169   lengthLBA = (uint32_t) length;
170   RecomputeCHS();
171} // MBRPart::SetLengthLBA()
172
173// Set the start point and length of the partition. This function takes LBA
174// values, sets them directly, and sets the CHS values based on the LBA
175// values and the current geometry settings.
176void MBRPart::SetLocation(uint64_t start, uint64_t length) {
177   int validCHS;
178
179   if ((start > UINT32_MAX) || (length > UINT32_MAX)) {
180      cerr << "Partition values out of range in MBRPart::SetLocation()!\n"
181           << "Continuing, but strange problems are now likely!\n";
182   } // if
183   firstLBA = (uint32_t) start;
184   lengthLBA = (uint32_t) length;
185   validCHS = RecomputeCHS();
186
187   // If this is a complete 0xEE protective MBR partition, max out its
188   // CHS last sector value, as per the GPT spec. (Set to 0xffffff,
189   // although the maximum legal MBR value is 0xfeffff, which is
190   // actually what GNU Parted and Apple's Disk Utility use, in
191   // violation of the GPT spec.)
192   if ((partitionType == 0xEE) && (!validCHS) && (firstLBA == 1) &&
193       ((lengthLBA == diskSize - 1) || (lengthLBA == UINT32_MAX))) {
194      lastSector[0] = lastSector[1] = lastSector[2] = 0xFF;
195   } // if
196} // MBRPart::SetLocation()
197
198// Store the MBR data in the packed structure used for disk I/O...
199void MBRPart::StoreInStruct(MBRRecord* theStruct) {
200   int i;
201
202   theStruct->firstLBA = firstLBA;
203   theStruct->lengthLBA = lengthLBA;
204   theStruct->partitionType = partitionType;
205   theStruct->status = status;
206   for (i = 0; i < 3; i++) {
207      theStruct->firstSector[i] = firstSector[i];
208      theStruct->lastSector[i] = lastSector[i];
209   } // for
210} // MBRPart::StoreInStruct()
211
212/**********************************************
213*                                            *
214* Get information on partitions or disks.... *
215*                                            *
216**********************************************/
217
218// Returns the last LBA value. Note that this can theoretically be a 33-bit
219// value, so we return a 64-bit value. If lengthLBA == 0, returns 0, even if
220// firstLBA is non-0.
221uint64_t MBRPart::GetLastLBA(void) const {
222   if (lengthLBA > 0)
223      return (uint64_t) firstLBA + (uint64_t) lengthLBA - UINT64_C(1);
224   else
225      return 0;
226} // MBRPart::GetLastLBA()
227
228// Returns 1 if other overlaps with the current partition, 0 if they don't
229// overlap
230int MBRPart::DoTheyOverlap (const MBRPart& other) {
231   return lengthLBA && other.lengthLBA &&
232          (firstLBA <= other.GetLastLBA()) != (GetLastLBA() < other.firstLBA);
233} // MBRPart::DoTheyOverlap()
234
235/*************************************************
236 *                                               *
237 * Adjust information on partitions or disks.... *
238 *                                               *
239 *************************************************/
240
241// Recompute the CHS values for the start and end points.
242// Returns 1 if both computed values are within the range
243// that can be expressed by that CHS, 0 otherwise.
244int MBRPart::RecomputeCHS(void) {
245   int retval = 1;
246
247   if (lengthLBA > 0) {
248      retval = LBAtoCHS(firstLBA, firstSector);
249      retval *= LBAtoCHS(firstLBA + lengthLBA - 1, lastSector);
250   } // if
251   return retval;
252} // MBRPart::RecomputeCHS()
253
254// Converts 32-bit LBA value to MBR-style CHS value. Returns 1 if conversion
255// was within the range that can be expressed by CHS (including 0, for an
256// empty partition), 0 if the value is outside that range, and -1 if chs is
257// invalid.
258int MBRPart::LBAtoCHS(uint32_t lba, uint8_t * chs) {
259   uint64_t cylinder, head, sector; // all numbered from 0
260   uint64_t remainder;
261   int retval = 1;
262   int done = 0;
263
264   if (chs != NULL) {
265      // Special case: In case of 0 LBA value, zero out CHS values....
266      if (lba == 0) {
267         chs[0] = chs[1] = chs[2] = UINT8_C(0);
268         done = 1;
269      } // if
270      // If LBA value is too large for CHS, max out CHS values....
271      if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
272         chs[0] = 254;
273         chs[1] = chs[2] = 255;
274         done = 1;
275         retval = 0;
276      } // if
277      // If neither of the above applies, compute CHS values....
278      if (!done) {
279         cylinder = lba / (numHeads * numSecspTrack);
280         remainder = lba - (cylinder * numHeads * numSecspTrack);
281         head = remainder / numSecspTrack;
282         remainder -= head * numSecspTrack;
283         sector = remainder;
284         if (head < numHeads)
285            chs[0] = (uint8_t) head;
286         else
287            retval = 0;
288         if (sector < numSecspTrack) {
289            chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
290            chs[2] = (uint8_t) (cylinder & UINT32_C(0xFF));
291         } else {
292            retval = 0;
293         } // if/else
294      } // if value is expressible and non-0
295   } else { // Invalid (NULL) chs pointer
296      retval = -1;
297   } // if CHS pointer valid
298   return (retval);
299} // MBRPart::LBAtoCHS()
300
301// Reverses the byte order, but only if we're on a big-endian platform.
302// Note that most data come in 8-bit structures, so don't need reversing;
303// only the LBA data needs to be reversed....
304void MBRPart::ReverseByteOrder(void) {
305   if (IsLittleEndian() == 0) {
306      ReverseBytes(&firstLBA, 4);
307      ReverseBytes(&lengthLBA, 4);
308   } // if
309} // MBRPart::ReverseByteOrder()
310
311/**************************
312 *                        *
313 * User I/O functions.... *
314 *                        *
315 **************************/
316
317// Show MBR data. Should update canBeLogical flags before calling.
318// If isGpt == 1, omits the "can be logical" and "can be primary" columns.
319void MBRPart::ShowData(int isGpt) {
320   char bootCode = ' ';
321
322   if (status & 0x80) // it's bootable
323      bootCode = '*';
324   cout.fill(' ');
325   cout << bootCode << "  ";
326   cout.width(13);
327   cout << firstLBA;
328   cout.width(13);
329   cout << GetLastLBA() << "   ";
330   switch (includeAs) {
331      case PRIMARY:
332         cout << "primary";
333         break;
334      case LOGICAL:
335         cout << "logical";
336         break;
337      case NONE:
338         cout << "omitted";
339         break;
340      default:
341         cout << "error  ";
342         break;
343   } // switch
344   cout.width(7);
345   if (!isGpt) {
346      if (canBeLogical)
347         cout << "     Y      ";
348      else
349         cout << "            ";
350      if (canBePrimary)
351         cout << "  Y      ";
352      else
353         cout << "         ";
354   } // if
355   cout << "0x";
356   cout.width(2);
357   cout.fill('0');
358   cout << hex << (int) partitionType << dec << "\n";
359} // MBRPart::ShowData()
360