blob: 0ca5814ddca6a23fce485bfb4a3d56f64645019e [file] [log] [blame]
srs569496312232011-03-12 01:22:42 -05001/*
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
srs5694c2f6e0c2011-03-16 02:42:33 -0400105// 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
srs569496312232011-03-12 01:22:42 -0500117/**************************************************
118 * *
119 * Set information on partitions or disks without *
120 * interacting with the user.... *
121 * *
122 **************************************************/
123
srs5694a17fe692011-09-10 20:30:20 -0400124void MBRPart::SetGeometry(uint32_t heads, uint32_t sectors, uint64_t ds, uint32_t bs) {
srs569496312232011-03-12 01:22:42 -0500125 numHeads = heads;
126 numSecspTrack = sectors;
srs5694a17fe692011-09-10 20:30:20 -0400127 diskSize = ds;
128 blockSize = bs;
srs569496312232011-03-12 01:22:42 -0500129} // 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) {
srs5694a17fe692011-09-10 20:30:20 -0400177 int validCHS;
178
srs569496312232011-03-12 01:22:42 -0500179 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;
srs5694a17fe692011-09-10 20:30:20 -0400185 validCHS = RecomputeCHS();
srs569496312232011-03-12 01:22:42 -0500186
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.)
srs5694a17fe692011-09-10 20:30:20 -0400192 if ((partitionType == 0xEE) && (!validCHS) && (firstLBA == 1) &&
193 ((lengthLBA == diskSize - 1) || (lengthLBA == UINT32_MAX))) {
srs569496312232011-03-12 01:22:42 -0500194 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
srs5694a17fe692011-09-10 20:30:20 -0400241// 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
srs569496312232011-03-12 01:22:42 -0500247 if (lengthLBA > 0) {
srs5694a17fe692011-09-10 20:30:20 -0400248 retval = LBAtoCHS(firstLBA, firstSector);
249 retval *= LBAtoCHS(firstLBA + lengthLBA - 1, lastSector);
srs569496312232011-03-12 01:22:42 -0500250 } // if
srs5694a17fe692011-09-10 20:30:20 -0400251 return retval;
srs569496312232011-03-12 01:22:42 -0500252} // 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
Roderick W. Smith1eea9b02013-07-06 22:52:58 -0400322 if (status & 0x80) // it's bootable
srs569496312232011-03-12 01:22:42 -0500323 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";
srs5694699941e2011-03-21 21:33:57 -0400359} // MBRPart::ShowData()