Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
gptfdisk/bsd.cc
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
332 lines (292 sloc)
11.4 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* bsd.cc -- Functions for loading and manipulating legacy BSD disklabel | |
data. */ | |
/* By Rod Smith, initial coding August, 2009 */ | |
/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed | |
under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ | |
#define __STDC_LIMIT_MACROS | |
#define __STDC_CONSTANT_MACROS | |
#include <stdio.h> | |
//#include <unistd.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <fcntl.h> | |
#include <sys/stat.h> | |
#include <errno.h> | |
#include <iostream> | |
#include <string> | |
#include "support.h" | |
#include "bsd.h" | |
using namespace std; | |
BSDData::BSDData(void) { | |
state = unknown; | |
signature = UINT32_C(0); | |
signature2 = UINT32_C(0); | |
sectorSize = 512; | |
numParts = 0; | |
labelFirstLBA = 0; | |
labelLastLBA = 0; | |
labelStart = LABEL_OFFSET1; // assume raw disk format | |
partitions = NULL; | |
} // default constructor | |
BSDData::~BSDData(void) { | |
delete[] partitions; | |
} // destructor | |
// Read BSD disklabel data from the specified device filename. This function | |
// just opens the device file and then calls an overloaded function to do | |
// the bulk of the work. Returns 1 on success, 0 on failure. | |
int BSDData::ReadBSDData(const string & device, uint64_t startSector, uint64_t endSector) { | |
int allOK = 1; | |
DiskIO myDisk; | |
if (device != "") { | |
if (myDisk.OpenForRead(device)) { | |
allOK = ReadBSDData(&myDisk, startSector, endSector); | |
} else { | |
allOK = 0; | |
} // if/else | |
myDisk.Close(); | |
} else { | |
allOK = 0; | |
} // if/else | |
return allOK; | |
} // BSDData::ReadBSDData() (device filename version) | |
// Load the BSD disklabel data from an already-opened disk | |
// file, starting with the specified sector number. | |
int BSDData::ReadBSDData(DiskIO *theDisk, uint64_t startSector, uint64_t endSector) { | |
int allOK = 1; | |
int i, foundSig = 0, bigEnd = 0; | |
int relative = 0; // assume absolute partition sector numbering | |
uint8_t buffer[4096]; // I/O buffer | |
uint32_t realSig; | |
uint32_t* temp32; | |
uint16_t* temp16; | |
BSDRecord* tempRecords; | |
int offset[NUM_OFFSETS] = { LABEL_OFFSET1, LABEL_OFFSET2 }; | |
labelFirstLBA = startSector; | |
labelLastLBA = endSector; | |
offset[1] = theDisk->GetBlockSize(); | |
// Read 4096 bytes (eight 512-byte sectors or equivalent) | |
// into memory; we'll extract data from this buffer. | |
// (Done to work around FreeBSD limitation on size of reads | |
// from block devices.) | |
allOK = theDisk->Seek(startSector); | |
if (allOK) allOK = theDisk->Read(buffer, 4096); | |
// Do some strangeness to support big-endian architectures... | |
bigEnd = (IsLittleEndian() == 0); | |
realSig = BSD_SIGNATURE; | |
if (bigEnd && allOK) | |
ReverseBytes(&realSig, 4); | |
// Look for the signature at any of two locations. | |
// Note that the signature is repeated at both the original | |
// offset and 132 bytes later, so we need two checks.... | |
if (allOK) { | |
i = 0; | |
do { | |
temp32 = (uint32_t*) &buffer[offset[i]]; | |
signature = *temp32; | |
if (signature == realSig) { // found first, look for second | |
temp32 = (uint32_t*) &buffer[offset[i] + 132]; | |
signature2 = *temp32; | |
if (signature2 == realSig) { | |
foundSig = 1; | |
labelStart = offset[i]; | |
} // if found signature | |
} // if/else | |
i++; | |
} while ((!foundSig) && (i < NUM_OFFSETS)); | |
allOK = foundSig; | |
} // if | |
// Load partition metadata from the buffer.... | |
if (allOK) { | |
temp32 = (uint32_t*) &buffer[labelStart + 40]; | |
sectorSize = *temp32; | |
temp16 = (uint16_t*) &buffer[labelStart + 138]; | |
numParts = *temp16; | |
} // if | |
// Make it big-endian-aware.... | |
if ((IsLittleEndian() == 0) && allOK) | |
ReverseMetaBytes(); | |
// Check validity of the data and flag it appropriately.... | |
if (foundSig && (numParts <= MAX_BSD_PARTS) && allOK) { | |
state = bsd; | |
} else { | |
state = bsd_invalid; | |
} // if/else | |
// If the state is good, go ahead and load the main partition data.... | |
if (state == bsd) { | |
partitions = new struct BSDRecord[numParts * sizeof(struct BSDRecord)]; | |
if (partitions == NULL) { | |
cerr << "Unable to allocate memory in BSDData::ReadBSDData()! Terminating!\n"; | |
exit(1); | |
} // if | |
for (i = 0; i < numParts; i++) { | |
// Once again, we use the buffer, but index it using a BSDRecord | |
// pointer (dangerous, but effective).... | |
tempRecords = (BSDRecord*) &buffer[labelStart + 148]; | |
partitions[i].lengthLBA = tempRecords[i].lengthLBA; | |
partitions[i].firstLBA = tempRecords[i].firstLBA; | |
partitions[i].fsType = tempRecords[i].fsType; | |
if (bigEnd) { // reverse data (fsType is a single byte) | |
ReverseBytes(&partitions[i].lengthLBA, 4); | |
ReverseBytes(&partitions[i].firstLBA, 4); | |
} // if big-endian | |
// Check for signs of relative sector numbering: A "0" first sector | |
// number on a partition with a non-zero length -- but ONLY if the | |
// length is less than the disk size, since NetBSD has a habit of | |
// creating a disk-sized partition within a carrier MBR partition | |
// that's too small to house it, and this throws off everything.... | |
if ((partitions[i].firstLBA == 0) && (partitions[i].lengthLBA > 0) | |
&& (partitions[i].lengthLBA < labelLastLBA)) | |
relative = 1; | |
} // for | |
// Some disklabels use sector numbers relative to the enclosing partition's | |
// start, others use absolute sector numbers. If relative numbering was | |
// detected above, apply a correction to all partition start sectors.... | |
if (relative) { | |
for (i = 0; i < numParts; i++) { | |
partitions[i].firstLBA += (uint32_t) startSector; | |
} // for | |
} // if | |
} // if signatures OK | |
// DisplayBSDData(); | |
return allOK; | |
} // BSDData::ReadBSDData(DiskIO* theDisk, uint64_t startSector) | |
// Reverse metadata's byte order; called only on big-endian systems | |
void BSDData::ReverseMetaBytes(void) { | |
ReverseBytes(&signature, 4); | |
ReverseBytes(§orSize, 4); | |
ReverseBytes(&signature2, 4); | |
ReverseBytes(&numParts, 2); | |
} // BSDData::ReverseMetaByteOrder() | |
// Display basic BSD partition data. Used for debugging. | |
void BSDData::DisplayBSDData(void) { | |
int i; | |
if (state == bsd) { | |
cout << "BSD partitions:\n"; | |
for (i = 0; i < numParts; i++) { | |
cout.width(4); | |
cout << i + 1 << "\t"; | |
cout.width(13); | |
cout << partitions[i].firstLBA << "\t"; | |
cout.width(15); | |
cout << partitions[i].lengthLBA << " \t0x"; | |
cout.width(2); | |
cout.fill('0'); | |
cout.setf(ios::uppercase); | |
cout << hex << (int) partitions[i].fsType << "\n" << dec; | |
cout.fill(' '); | |
} // for | |
} // if | |
} // BSDData::DisplayBSDData() | |
// Displays the BSD disklabel state. Called during program launch to inform | |
// the user about the partition table(s) status | |
int BSDData::ShowState(void) { | |
int retval = 0; | |
switch (state) { | |
case bsd_invalid: | |
cout << " BSD: not present\n"; | |
break; | |
case bsd: | |
cout << " BSD: present\n"; | |
retval = 1; | |
break; | |
default: | |
cout << "\a BSD: unknown -- bug!\n"; | |
break; | |
} // switch | |
return retval; | |
} // BSDData::ShowState() | |
// Weirdly, this function has stopped working when defined inline, | |
// but it's OK here.... | |
int BSDData::IsDisklabel(void) { | |
return (state == bsd); | |
} // BSDData::IsDiskLabel() | |
// Returns the BSD table's partition type code | |
uint8_t BSDData::GetType(int i) { | |
uint8_t retval = 0; // 0 = "unused" | |
if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0)) | |
retval = partitions[i].fsType; | |
return(retval); | |
} // BSDData::GetType() | |
// Returns the number of the first sector of the specified partition | |
uint64_t BSDData::GetFirstSector(int i) { | |
uint64_t retval = UINT64_C(0); | |
if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0)) | |
retval = (uint64_t) partitions[i].firstLBA; | |
return retval; | |
} // BSDData::GetFirstSector | |
// Returns the length (in sectors) of the specified partition | |
uint64_t BSDData::GetLength(int i) { | |
uint64_t retval = UINT64_C(0); | |
if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0)) | |
retval = (uint64_t) partitions[i].lengthLBA; | |
return retval; | |
} // BSDData::GetLength() | |
// Returns the number of partitions defined in the current table | |
int BSDData::GetNumParts(void) { | |
return numParts; | |
} // BSDData::GetNumParts() | |
// Returns the specified partition as a GPT partition. Used in BSD-to-GPT | |
// conversion process | |
GPTPart BSDData::AsGPT(int i) { | |
GPTPart guid; // dump data in here, then return it | |
uint64_t sectorOne, sectorEnd; // first & last sectors of partition | |
int passItOn = 1; // Set to 0 if partition is empty or invalid | |
guid.BlankPartition(); | |
sectorOne = (uint64_t) partitions[i].firstLBA; | |
sectorEnd = sectorOne + (uint64_t) partitions[i].lengthLBA; | |
if (sectorEnd > 0) sectorEnd--; | |
// Note on above: BSD partitions sometimes have a length of 0 and a start | |
// sector of 0. With unsigned ints, the usual way (start + length - 1) to | |
// find the end will result in a huge number, which will be confusing. | |
// Thus, apply the "-1" part only if it's reasonable to do so. | |
// Do a few sanity checks on the partition before we pass it on.... | |
// First, check that it falls within the bounds of its container | |
// and that it starts before it ends.... | |
if ((sectorOne < labelFirstLBA) || (sectorEnd > labelLastLBA) || (sectorOne > sectorEnd)) | |
passItOn = 0; | |
// Some disklabels include a pseudo-partition that's the size of the entire | |
// disk or containing partition. Don't return it. | |
if ((sectorOne <= labelFirstLBA) && (sectorEnd >= labelLastLBA) && | |
(GetType(i) == 0)) | |
passItOn = 0; | |
// If the end point is 0, it's not a valid partition. | |
if ((sectorEnd == 0) || (sectorEnd == labelFirstLBA)) | |
passItOn = 0; | |
if (passItOn) { | |
guid.SetFirstLBA(sectorOne); | |
guid.SetLastLBA(sectorEnd); | |
// Now set a random unique GUID for the partition.... | |
guid.RandomizeUniqueGUID(); | |
// ... zero out the attributes and name fields.... | |
guid.SetAttributes(UINT64_C(0)); | |
// Most BSD disklabel type codes seem to be archaic or rare. | |
// They're also ambiguous; a FreeBSD filesystem is impossible | |
// to distinguish from a NetBSD one. Thus, these code assignment | |
// are going to be rough to begin with. For a list of meanings, | |
// see http://fxr.watson.org/fxr/source/sys/dtype.h?v=DFBSD, | |
// or Google it. | |
switch (GetType(i)) { | |
case 1: // BSD swap | |
guid.SetType(0xa502); break; | |
case 7: // BSD FFS | |
guid.SetType(0xa503); break; | |
case 8: case 11: // MS-DOS or HPFS | |
guid.SetType(0x0700); break; | |
case 9: // log-structured fs | |
guid.SetType(0xa903); break; | |
case 13: // bootstrap | |
guid.SetType(0xa501); break; | |
case 14: // vinum | |
guid.SetType(0xa505); break; | |
case 15: // RAID | |
guid.SetType(0xa903); break; | |
case 27: // FreeBSD ZFS | |
guid.SetType(0xa504); break; | |
default: | |
guid.SetType(0xa503); break; | |
} // switch | |
// Set the partition name to the name of the type code.... | |
guid.SetName(guid.GetTypeName()); | |
} // if | |
return guid; | |
} // BSDData::AsGPT() |