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