blob: 70520014c314abcad3ea490e96afee6b9d79f7c1 [file] [log] [blame]
srs5694e4ac11e2009-08-31 10:13:04 -04001/* bsd.cc -- Functions for loading and manipulating legacy BSD disklabel
srs5694a0eb11a2009-08-29 15:00:08 -04002 data. */
3
srs5694e4ac11e2009-08-31 10:13:04 -04004/* By Rod Smith, initial coding August, 2009 */
srs5694a0eb11a2009-08-29 15:00:08 -04005
6/* This program is copyright (c) 2009 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 <unistd.h>
14#include <stdlib.h>
15#include <stdint.h>
16#include <fcntl.h>
17#include <string.h>
srs5694a0eb11a2009-08-29 15:00:08 -040018#include <sys/stat.h>
19#include <errno.h>
srs5694a0eb11a2009-08-29 15:00:08 -040020#include "support.h"
21#include "bsd.h"
22
23using namespace std;
24
25
26BSDData::BSDData(void) {
27 state = unknown;
28 signature = UINT32_C(0);
29 signature2 = UINT32_C(0);
30 sectorSize = 512;
31 numParts = 0;
32 labelFirstLBA = 0;
33 labelLastLBA = 0;
34 labelStart = LABEL_OFFSET1; // assume raw disk format
srs5694a0eb11a2009-08-29 15:00:08 -040035 partitions = NULL;
36} // default constructor
37
38BSDData::~BSDData(void) {
39 free(partitions);
40} // destructor
41
srs5694e4ac11e2009-08-31 10:13:04 -040042// Read BSD disklabel data from the specified device filename. This function
43// just opens the device file and then calls an overloaded function to do
44// the bulk of the work.
srs5694a0eb11a2009-08-29 15:00:08 -040045int BSDData::ReadBSDData(char* device, uint64_t startSector, uint64_t endSector) {
srs5694546a9c72010-01-26 16:00:26 -050046 int allOK = 1, tempMyDisk = 0;
srs5694a0eb11a2009-08-29 15:00:08 -040047
srs5694e4ac11e2009-08-31 10:13:04 -040048 if (device != NULL) {
srs5694546a9c72010-01-26 16:00:26 -050049 if (myDisk == NULL) {
50 myDisk = new DiskIO;
51 tempMyDisk = 1;
52 } // if
53 if (myDisk->OpenForRead(device)) {
54 ReadBSDData(myDisk, startSector, endSector);
srs5694e4ac11e2009-08-31 10:13:04 -040055 } else {
56 allOK = 0;
57 } // if/else
58
srs5694546a9c72010-01-26 16:00:26 -050059 myDisk->Close();
srs5694a0eb11a2009-08-29 15:00:08 -040060 } else {
61 allOK = 0;
srs5694e4ac11e2009-08-31 10:13:04 -040062 } // if/else
srs5694a0eb11a2009-08-29 15:00:08 -040063
srs5694546a9c72010-01-26 16:00:26 -050064 if (tempMyDisk) {
65 delete myDisk;
66 myDisk = NULL;
67 } // if
68
srs5694a0eb11a2009-08-29 15:00:08 -040069 return allOK;
70} // BSDData::ReadBSDData() (device filename version)
71
72// Load the BSD disklabel data from an already-opened disk
73// file, starting with the specified sector number.
srs5694546a9c72010-01-26 16:00:26 -050074void BSDData::ReadBSDData(DiskIO *theDisk, uint64_t startSector, uint64_t endSector) {
srs56941e093722010-01-05 00:14:19 -050075 uint8_t buffer[4096]; // I/O buffer
srs5694a0eb11a2009-08-29 15:00:08 -040076 int i, err, foundSig = 0, bigEnd = 0;
77 int relative = 0; // assume absolute partition sector numbering
78 uint32_t realSig;
79 uint32_t* temp32;
80 uint16_t* temp16;
81 BSDRecord* tempRecords;
srs5694546a9c72010-01-26 16:00:26 -050082 int offset[NUM_OFFSETS] = { LABEL_OFFSET1, LABEL_OFFSET2 };
srs5694a0eb11a2009-08-29 15:00:08 -040083
srs5694546a9c72010-01-26 16:00:26 -050084 myDisk = theDisk;
srs5694a0eb11a2009-08-29 15:00:08 -040085 labelFirstLBA = startSector;
86 labelLastLBA = endSector;
srs5694546a9c72010-01-26 16:00:26 -050087 offset[1] = myDisk->GetBlockSize();
srs5694a0eb11a2009-08-29 15:00:08 -040088
srs5694ba00fed2010-01-12 18:18:36 -050089 // Read 4096 bytes (eight 512-byte sectors or equivalent)
90 // into memory; we'll extract data from this buffer.
91 // (Done to work around FreeBSD limitation on size of reads
92 // from block devices.)
srs5694546a9c72010-01-26 16:00:26 -050093 myDisk->Seek(startSector /* * myDisk->GetBlockSize() */);
94 myDisk->Read(buffer, 4096);
srs5694a0eb11a2009-08-29 15:00:08 -040095
96 // Do some strangeness to support big-endian architectures...
97 bigEnd = (IsLittleEndian() == 0);
98 realSig = BSD_SIGNATURE;
99 if (bigEnd)
100 ReverseBytes(&realSig, 4);
101
srs5694546a9c72010-01-26 16:00:26 -0500102 // Look for the signature at any of two locations.
srs5694ba00fed2010-01-12 18:18:36 -0500103 // Note that the signature is repeated at both the original
104 // offset and 132 bytes later, so we need two checks....
105 i = 0;
106 do {
107 temp32 = (uint32_t*) &buffer[offset[i]];
srs5694a0eb11a2009-08-29 15:00:08 -0400108 signature = *temp32;
srs5694ba00fed2010-01-12 18:18:36 -0500109 if (signature == realSig) { // found first, look for second
110 temp32 = (uint32_t*) &buffer[offset[i] + 132];
srs5694a0eb11a2009-08-29 15:00:08 -0400111 signature2 = *temp32;
srs5694ba00fed2010-01-12 18:18:36 -0500112 if (signature2 == realSig) {
srs5694a0eb11a2009-08-29 15:00:08 -0400113 foundSig = 1;
srs5694ba00fed2010-01-12 18:18:36 -0500114 labelStart = offset[i];
115 } // if found signature
srs5694a0eb11a2009-08-29 15:00:08 -0400116 } // if/else
srs5694ba00fed2010-01-12 18:18:36 -0500117 i++;
srs5694546a9c72010-01-26 16:00:26 -0500118 } while ((!foundSig) && (i < NUM_OFFSETS));
srs5694a0eb11a2009-08-29 15:00:08 -0400119
120 // Load partition metadata from the buffer....
121 temp32 = (uint32_t*) &buffer[labelStart + 40];
122 sectorSize = *temp32;
123 temp16 = (uint16_t*) &buffer[labelStart + 138];
124 numParts = *temp16;
125
126 // Make it big-endian-aware....
127 if (IsLittleEndian() == 0)
128 ReverseMetaBytes();
129
130 // Check validity of the data and flag it appropriately....
131 if (foundSig && (numParts <= MAX_BSD_PARTS)) {
132 state = bsd;
133 } else {
134 state = bsd_invalid;
135 } // if/else
136
137 // If the state is good, go ahead and load the main partition data....
138 if (state == bsd) {
139 partitions = (struct BSDRecord*) malloc(numParts * sizeof (struct BSDRecord));
140 for (i = 0; i < numParts; i++) {
141 // Once again, we use the buffer, but index it using a BSDRecord
142 // pointer (dangerous, but effective)....
143 tempRecords = (BSDRecord*) &buffer[labelStart + 148];
144 partitions[i].lengthLBA = tempRecords[i].lengthLBA;
145 partitions[i].firstLBA = tempRecords[i].firstLBA;
146 partitions[i].fsType = tempRecords[i].fsType;
147 if (bigEnd) { // reverse data (fsType is a single byte)
148 ReverseBytes(&partitions[i].lengthLBA, 4);
149 ReverseBytes(&partitions[i].firstLBA, 4);
150 } // if big-endian
151 // Check for signs of relative sector numbering: A "0" first sector
152 // number on a partition with a non-zero length -- but ONLY if the
153 // length is less than the disk size, since NetBSD has a habit of
154 // creating a disk-sized partition within a carrier MBR partition
155 // that's too small to house it, and this throws off everything....
156 if ((partitions[i].firstLBA == 0) && (partitions[i].lengthLBA > 0)
157 && (partitions[i].lengthLBA < labelLastLBA))
158 relative = 1;
159 } // for
160 // Some disklabels use sector numbers relative to the enclosing partition's
161 // start, others use absolute sector numbers. If relative numbering was
162 // detected above, apply a correction to all partition start sectors....
163 if (relative) {
164 for (i = 0; i < numParts; i++) {
165 partitions[i].firstLBA += startSector;
166 } // for
167 } // if
168 } // if signatures OK
169// DisplayBSDData();
170} // BSDData::ReadBSDData(int fd, uint64_t startSector)
171
172// Reverse metadata's byte order; called only on big-endian systems
173void BSDData::ReverseMetaBytes(void) {
174 ReverseBytes(&signature, 4);
175 ReverseBytes(&sectorSize, 4);
176 ReverseBytes(&signature2, 4);
177 ReverseBytes(&numParts, 2);
178} // BSDData::ReverseMetaByteOrder()
179
180// Display basic BSD partition data. Used for debugging.
181void BSDData::DisplayBSDData(void) {
182 int i;
183
184 if (state == bsd) {
185 printf("BSD partitions:\n");
186 printf("Number\t Start (sector)\t Length (sectors)\tType\n");
187 for (i = 0; i < numParts; i++) {
188 printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 1,
189 (unsigned long) partitions[i].firstLBA,
190 (unsigned long) partitions[i].lengthLBA, partitions[i].fsType);
191 } // for
192 } // if
193} // BSDData::DisplayBSDData()
194
195// Displays the BSD disklabel state. Called during program launch to inform
196// the user about the partition table(s) status
197int BSDData::ShowState(void) {
198 int retval = 0;
199
200 switch (state) {
201 case bsd_invalid:
202 printf(" BSD: not present\n");
203 break;
204 case bsd:
205 printf(" BSD: present\n");
206 retval = 1;
207 break;
208 default:
209 printf("\a BSD: unknown -- bug!\n");
210 break;
211 } // switch
212 return retval;
213} // BSDData::ShowState()
214
srs5694a6022b02010-01-19 16:17:20 -0500215// Weirdly, this function has stopped working when defined inline,
216// but it's OK here....
217int BSDData::IsDisklabel(void) {
218 return (state == bsd);
219} // BSDData::IsDiskLabel()
220
srs5694a0eb11a2009-08-29 15:00:08 -0400221// Returns the BSD table's partition type code
222uint8_t BSDData::GetType(int i) {
223 uint8_t retval = 0; // 0 = "unused"
224
225 if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0))
226 retval = partitions[i].fsType;
227
228 return(retval);
229} // BSDData::GetType()
230
231// Returns the number of the first sector of the specified partition
232uint64_t BSDData::GetFirstSector(int i) {
233 uint64_t retval = UINT64_C(0);
234
235 if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0))
236 retval = (uint64_t) partitions[i].firstLBA;
237
238 return retval;
239} // BSDData::GetFirstSector
240
241// Returns the length (in sectors) of the specified partition
242uint64_t BSDData::GetLength(int i) {
243 uint64_t retval = UINT64_C(0);
244
245 if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0))
246 retval = (uint64_t) partitions[i].lengthLBA;
247
248 return retval;
249} // BSDData::GetLength()
250
251// Returns the number of partitions defined in the current table
252int BSDData::GetNumParts(void) {
253 return numParts;
254} // BSDData::GetNumParts()
255
256// Returns the specified partition as a GPT partition. Used in BSD-to-GPT
257// conversion process
258GPTPart BSDData::AsGPT(int i) {
259 GPTPart guid; // dump data in here, then return it
260 uint64_t sectorOne, sectorEnd; // first & last sectors of partition
261 char tempStr[NAME_SIZE]; // temporary string for holding GPT name
262 int passItOn = 1; // Set to 0 if partition is empty or invalid
263
264 guid.BlankPartition();
265 sectorOne = (uint64_t) partitions[i].firstLBA;
266 sectorEnd = sectorOne + (uint64_t) partitions[i].lengthLBA;
267 if (sectorEnd > 0) sectorEnd--;
268 // Note on above: BSD partitions sometimes have a length of 0 and a start
srs5694e4ac11e2009-08-31 10:13:04 -0400269 // sector of 0. With unsigned ints, the usual way (start + length - 1) to
270 // find the end will result in a huge number, which will be confusing.
271 // Thus, apply the "-1" part only if it's reasonable to do so.
srs5694a0eb11a2009-08-29 15:00:08 -0400272
273 // Do a few sanity checks on the partition before we pass it on....
274 // First, check that it falls within the bounds of its container
275 // and that it starts before it ends....
276 if ((sectorOne < labelFirstLBA) || (sectorEnd > labelLastLBA) || (sectorOne > sectorEnd))
277 passItOn = 0;
278 // Some disklabels include a pseudo-partition that's the size of the entire
279 // disk or containing partition. Don't return it.
280 if ((sectorOne <= labelFirstLBA) && (sectorEnd >= labelLastLBA) &&
281 (GetType(i) == 0))
282 passItOn = 0;
283 // If the end point is 0, it's not a valid partition.
srs5694e4ac11e2009-08-31 10:13:04 -0400284 if ((sectorEnd == 0) || (sectorEnd == labelFirstLBA))
srs5694a0eb11a2009-08-29 15:00:08 -0400285 passItOn = 0;
286
287 if (passItOn) {
288 guid.SetFirstLBA(sectorOne);
289 guid.SetLastLBA(sectorEnd);
290 // Now set a random unique GUID for the partition....
291 guid.SetUniqueGUID(1);
292 // ... zero out the attributes and name fields....
293 guid.SetAttributes(UINT64_C(0));
294 // Most BSD disklabel type codes seem to be archaic or rare.
295 // They're also ambiguous; a FreeBSD filesystem is impossible
296 // to distinguish from a NetBSD one. Thus, these code assignment
297 // are going to be rough to begin with. For a list of meanings,
298 // see http://fxr.watson.org/fxr/source/sys/dtype.h?v=DFBSD,
299 // or Google it.
300 switch (GetType(i)) {
301 case 1: // BSD swap
302 guid.SetType(0xa502); break;
303 case 7: // BSD FFS
304 guid.SetType(0xa503); break;
305 case 8: case 11: // MS-DOS or HPFS
306 guid.SetType(0x0700); break;
307 case 9: // log-structured fs
308 guid.SetType(0xa903); break;
309 case 13: // bootstrap
310 guid.SetType(0xa501); break;
311 case 14: // vinum
312 guid.SetType(0xa505); break;
313 case 15: // RAID
314 guid.SetType(0xa903); break;
315 case 27: // FreeBSD ZFS
316 guid.SetType(0xa504); break;
317 default:
318 guid.SetType(0x0700); break;
319 } // switch
320 // Set the partition name to the name of the type code....
srs5694546a9c72010-01-26 16:00:26 -0500321 guid.SetName((unsigned char*) guid.GetNameType().c_str());
srs5694a0eb11a2009-08-29 15:00:08 -0400322 } // if
323 return guid;
324} // BSDData::AsGPT()