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
29 using namespace std;
30
31 uint32_t MBRPart::numHeads = MAX_HEADS;
32 uint32_t MBRPart::numSecspTrack = MAX_SECSPERTRACK;
33 uint64_t MBRPart::diskSize = 0;
34 uint32_t MBRPart::blockSize = 512;
35 int MBRPart::numInstances = 0;
36
MBRPart()37 MBRPart::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
MBRPart(const MBRPart & orig)60 MBRPart::MBRPart(const MBRPart& orig) {
61 numInstances++;
62 operator=(orig);
63 }
64
~MBRPart()65 MBRPart::~MBRPart() {
66 numInstances--;
67 }
68
operator =(const MBRPart & orig)69 MBRPart& 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.
operator =(const struct MBRRecord & orig)87 MBRPart& 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.
operator <(const MBRPart & other) const110 bool 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
SetGeometry(uint32_t heads,uint32_t sectors,uint64_t ds,uint32_t bs)124 void 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).
Empty(void)132 void 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)
SetType(uint8_t typeCode,int isExtended)149 int 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
SetStartLBA(uint64_t start)159 void 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
SetLengthLBA(uint64_t length)166 void 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.
SetLocation(uint64_t start,uint64_t length)176 void 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...
StoreInStruct(MBRRecord * theStruct)199 void 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.
GetLastLBA(void) const221 uint64_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
DoTheyOverlap(const MBRPart & other)230 int 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.
RecomputeCHS(void)244 int 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.
LBAtoCHS(uint32_t lba,uint8_t * chs)258 int 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....
ReverseByteOrder(void)304 void 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.
ShowData(int isGpt)319 void 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