1cf200d32Sopenharmony_ci/* bsd.cc -- Functions for loading and manipulating legacy BSD disklabel 2cf200d32Sopenharmony_ci data. */ 3cf200d32Sopenharmony_ci 4cf200d32Sopenharmony_ci/* By Rod Smith, initial coding August, 2009 */ 5cf200d32Sopenharmony_ci 6cf200d32Sopenharmony_ci/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed 7cf200d32Sopenharmony_ci under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ 8cf200d32Sopenharmony_ci 9cf200d32Sopenharmony_ci#define __STDC_LIMIT_MACROS 10cf200d32Sopenharmony_ci#ifndef __STDC_CONSTANT_MACROS 11cf200d32Sopenharmony_ci#define __STDC_CONSTANT_MACROS 12cf200d32Sopenharmony_ci#endif 13cf200d32Sopenharmony_ci 14cf200d32Sopenharmony_ci#include <stdio.h> 15cf200d32Sopenharmony_ci//#include <unistd.h> 16cf200d32Sopenharmony_ci#include <stdlib.h> 17cf200d32Sopenharmony_ci#include <stdint.h> 18cf200d32Sopenharmony_ci#include <fcntl.h> 19cf200d32Sopenharmony_ci#include <sys/stat.h> 20cf200d32Sopenharmony_ci#include <errno.h> 21cf200d32Sopenharmony_ci#include <iostream> 22cf200d32Sopenharmony_ci#include <string> 23cf200d32Sopenharmony_ci#include "support.h" 24cf200d32Sopenharmony_ci#include "bsd.h" 25cf200d32Sopenharmony_ci 26cf200d32Sopenharmony_ciusing namespace std; 27cf200d32Sopenharmony_ci 28cf200d32Sopenharmony_ci 29cf200d32Sopenharmony_ciBSDData::BSDData(void) { 30cf200d32Sopenharmony_ci state = unknown; 31cf200d32Sopenharmony_ci signature = UINT32_C(0); 32cf200d32Sopenharmony_ci signature2 = UINT32_C(0); 33cf200d32Sopenharmony_ci sectorSize = 512; 34cf200d32Sopenharmony_ci numParts = 0; 35cf200d32Sopenharmony_ci labelFirstLBA = 0; 36cf200d32Sopenharmony_ci labelLastLBA = 0; 37cf200d32Sopenharmony_ci labelStart = LABEL_OFFSET1; // assume raw disk format 38cf200d32Sopenharmony_ci partitions = NULL; 39cf200d32Sopenharmony_ci} // default constructor 40cf200d32Sopenharmony_ci 41cf200d32Sopenharmony_ciBSDData::~BSDData(void) { 42cf200d32Sopenharmony_ci delete[] partitions; 43cf200d32Sopenharmony_ci} // destructor 44cf200d32Sopenharmony_ci 45cf200d32Sopenharmony_ci// Read BSD disklabel data from the specified device filename. This function 46cf200d32Sopenharmony_ci// just opens the device file and then calls an overloaded function to do 47cf200d32Sopenharmony_ci// the bulk of the work. Returns 1 on success, 0 on failure. 48cf200d32Sopenharmony_ciint BSDData::ReadBSDData(const string & device, uint64_t startSector, uint64_t endSector) { 49cf200d32Sopenharmony_ci int allOK; 50cf200d32Sopenharmony_ci DiskIO myDisk; 51cf200d32Sopenharmony_ci 52cf200d32Sopenharmony_ci if (device != "") { 53cf200d32Sopenharmony_ci if (myDisk.OpenForRead(device)) { 54cf200d32Sopenharmony_ci allOK = ReadBSDData(&myDisk, startSector, endSector); 55cf200d32Sopenharmony_ci } else { 56cf200d32Sopenharmony_ci allOK = 0; 57cf200d32Sopenharmony_ci } // if/else 58cf200d32Sopenharmony_ci 59cf200d32Sopenharmony_ci myDisk.Close(); 60cf200d32Sopenharmony_ci } else { 61cf200d32Sopenharmony_ci allOK = 0; 62cf200d32Sopenharmony_ci } // if/else 63cf200d32Sopenharmony_ci return allOK; 64cf200d32Sopenharmony_ci} // BSDData::ReadBSDData() (device filename version) 65cf200d32Sopenharmony_ci 66cf200d32Sopenharmony_ci// Load the BSD disklabel data from an already-opened disk 67cf200d32Sopenharmony_ci// file, starting with the specified sector number. 68cf200d32Sopenharmony_ciint BSDData::ReadBSDData(DiskIO *theDisk, uint64_t startSector, uint64_t endSector) { 69cf200d32Sopenharmony_ci int allOK; 70cf200d32Sopenharmony_ci int i, foundSig = 0, bigEnd = 0; 71cf200d32Sopenharmony_ci int relative = 0; // assume absolute partition sector numbering 72cf200d32Sopenharmony_ci uint8_t buffer[4096]; // I/O buffer 73cf200d32Sopenharmony_ci uint32_t realSig; 74cf200d32Sopenharmony_ci uint32_t* temp32; 75cf200d32Sopenharmony_ci uint16_t* temp16; 76cf200d32Sopenharmony_ci BSDRecord* tempRecords; 77cf200d32Sopenharmony_ci int offset[NUM_OFFSETS] = { LABEL_OFFSET1, LABEL_OFFSET2 }; 78cf200d32Sopenharmony_ci 79cf200d32Sopenharmony_ci labelFirstLBA = startSector; 80cf200d32Sopenharmony_ci labelLastLBA = endSector; 81cf200d32Sopenharmony_ci offset[1] = theDisk->GetBlockSize(); 82cf200d32Sopenharmony_ci 83cf200d32Sopenharmony_ci // Read 4096 bytes (eight 512-byte sectors or equivalent) 84cf200d32Sopenharmony_ci // into memory; we'll extract data from this buffer. 85cf200d32Sopenharmony_ci // (Done to work around FreeBSD limitation on size of reads 86cf200d32Sopenharmony_ci // from block devices.) 87cf200d32Sopenharmony_ci allOK = theDisk->Seek(startSector); 88cf200d32Sopenharmony_ci if (allOK) allOK = theDisk->Read(buffer, 4096); 89cf200d32Sopenharmony_ci 90cf200d32Sopenharmony_ci // Do some strangeness to support big-endian architectures... 91cf200d32Sopenharmony_ci bigEnd = (IsLittleEndian() == 0); 92cf200d32Sopenharmony_ci realSig = BSD_SIGNATURE; 93cf200d32Sopenharmony_ci if (bigEnd && allOK) 94cf200d32Sopenharmony_ci ReverseBytes(&realSig, 4); 95cf200d32Sopenharmony_ci 96cf200d32Sopenharmony_ci // Look for the signature at any of two locations. 97cf200d32Sopenharmony_ci // Note that the signature is repeated at both the original 98cf200d32Sopenharmony_ci // offset and 132 bytes later, so we need two checks.... 99cf200d32Sopenharmony_ci if (allOK) { 100cf200d32Sopenharmony_ci i = 0; 101cf200d32Sopenharmony_ci do { 102cf200d32Sopenharmony_ci temp32 = (uint32_t*) &buffer[offset[i]]; 103cf200d32Sopenharmony_ci signature = *temp32; 104cf200d32Sopenharmony_ci if (signature == realSig) { // found first, look for second 105cf200d32Sopenharmony_ci temp32 = (uint32_t*) &buffer[offset[i] + 132]; 106cf200d32Sopenharmony_ci signature2 = *temp32; 107cf200d32Sopenharmony_ci if (signature2 == realSig) { 108cf200d32Sopenharmony_ci foundSig = 1; 109cf200d32Sopenharmony_ci labelStart = offset[i]; 110cf200d32Sopenharmony_ci } // if found signature 111cf200d32Sopenharmony_ci } // if/else 112cf200d32Sopenharmony_ci i++; 113cf200d32Sopenharmony_ci } while ((!foundSig) && (i < NUM_OFFSETS)); 114cf200d32Sopenharmony_ci allOK = foundSig; 115cf200d32Sopenharmony_ci } // if 116cf200d32Sopenharmony_ci 117cf200d32Sopenharmony_ci // Load partition metadata from the buffer.... 118cf200d32Sopenharmony_ci if (allOK) { 119cf200d32Sopenharmony_ci temp32 = (uint32_t*) &buffer[labelStart + 40]; 120cf200d32Sopenharmony_ci sectorSize = *temp32; 121cf200d32Sopenharmony_ci temp16 = (uint16_t*) &buffer[labelStart + 138]; 122cf200d32Sopenharmony_ci numParts = *temp16; 123cf200d32Sopenharmony_ci } // if 124cf200d32Sopenharmony_ci 125cf200d32Sopenharmony_ci // Make it big-endian-aware.... 126cf200d32Sopenharmony_ci if ((IsLittleEndian() == 0) && allOK) 127cf200d32Sopenharmony_ci ReverseMetaBytes(); 128cf200d32Sopenharmony_ci 129cf200d32Sopenharmony_ci // Check validity of the data and flag it appropriately.... 130cf200d32Sopenharmony_ci if (foundSig && (numParts <= MAX_BSD_PARTS) && allOK) { 131cf200d32Sopenharmony_ci state = bsd; 132cf200d32Sopenharmony_ci } else { 133cf200d32Sopenharmony_ci state = bsd_invalid; 134cf200d32Sopenharmony_ci } // if/else 135cf200d32Sopenharmony_ci 136cf200d32Sopenharmony_ci // If the state is good, go ahead and load the main partition data.... 137cf200d32Sopenharmony_ci if (state == bsd) { 138cf200d32Sopenharmony_ci partitions = new struct BSDRecord[numParts * sizeof(struct BSDRecord)]; 139cf200d32Sopenharmony_ci if (partitions == NULL) { 140cf200d32Sopenharmony_ci cerr << "Unable to allocate memory in BSDData::ReadBSDData()! Terminating!\n"; 141cf200d32Sopenharmony_ci exit(1); 142cf200d32Sopenharmony_ci } // if 143cf200d32Sopenharmony_ci for (i = 0; i < numParts; i++) { 144cf200d32Sopenharmony_ci // Once again, we use the buffer, but index it using a BSDRecord 145cf200d32Sopenharmony_ci // pointer (dangerous, but effective).... 146cf200d32Sopenharmony_ci tempRecords = (BSDRecord*) &buffer[labelStart + 148]; 147cf200d32Sopenharmony_ci partitions[i].lengthLBA = tempRecords[i].lengthLBA; 148cf200d32Sopenharmony_ci partitions[i].firstLBA = tempRecords[i].firstLBA; 149cf200d32Sopenharmony_ci partitions[i].fsType = tempRecords[i].fsType; 150cf200d32Sopenharmony_ci if (bigEnd) { // reverse data (fsType is a single byte) 151cf200d32Sopenharmony_ci ReverseBytes(&partitions[i].lengthLBA, 4); 152cf200d32Sopenharmony_ci ReverseBytes(&partitions[i].firstLBA, 4); 153cf200d32Sopenharmony_ci } // if big-endian 154cf200d32Sopenharmony_ci // Check for signs of relative sector numbering: A "0" first sector 155cf200d32Sopenharmony_ci // number on a partition with a non-zero length -- but ONLY if the 156cf200d32Sopenharmony_ci // length is less than the disk size, since NetBSD has a habit of 157cf200d32Sopenharmony_ci // creating a disk-sized partition within a carrier MBR partition 158cf200d32Sopenharmony_ci // that's too small to house it, and this throws off everything.... 159cf200d32Sopenharmony_ci if ((partitions[i].firstLBA == 0) && (partitions[i].lengthLBA > 0) 160cf200d32Sopenharmony_ci && (partitions[i].lengthLBA < labelLastLBA)) 161cf200d32Sopenharmony_ci relative = 1; 162cf200d32Sopenharmony_ci } // for 163cf200d32Sopenharmony_ci // Some disklabels use sector numbers relative to the enclosing partition's 164cf200d32Sopenharmony_ci // start, others use absolute sector numbers. If relative numbering was 165cf200d32Sopenharmony_ci // detected above, apply a correction to all partition start sectors.... 166cf200d32Sopenharmony_ci if (relative) { 167cf200d32Sopenharmony_ci for (i = 0; i < numParts; i++) { 168cf200d32Sopenharmony_ci partitions[i].firstLBA += (uint32_t) startSector; 169cf200d32Sopenharmony_ci } // for 170cf200d32Sopenharmony_ci } // if 171cf200d32Sopenharmony_ci } // if signatures OK 172cf200d32Sopenharmony_ci// DisplayBSDData(); 173cf200d32Sopenharmony_ci return allOK; 174cf200d32Sopenharmony_ci} // BSDData::ReadBSDData(DiskIO* theDisk, uint64_t startSector) 175cf200d32Sopenharmony_ci 176cf200d32Sopenharmony_ci// Reverse metadata's byte order; called only on big-endian systems 177cf200d32Sopenharmony_civoid BSDData::ReverseMetaBytes(void) { 178cf200d32Sopenharmony_ci ReverseBytes(&signature, 4); 179cf200d32Sopenharmony_ci ReverseBytes(§orSize, 4); 180cf200d32Sopenharmony_ci ReverseBytes(&signature2, 4); 181cf200d32Sopenharmony_ci ReverseBytes(&numParts, 2); 182cf200d32Sopenharmony_ci} // BSDData::ReverseMetaByteOrder() 183cf200d32Sopenharmony_ci 184cf200d32Sopenharmony_ci// Display basic BSD partition data. Used for debugging. 185cf200d32Sopenharmony_civoid BSDData::DisplayBSDData(void) { 186cf200d32Sopenharmony_ci int i; 187cf200d32Sopenharmony_ci 188cf200d32Sopenharmony_ci if (state == bsd) { 189cf200d32Sopenharmony_ci cout << "BSD partitions:\n"; 190cf200d32Sopenharmony_ci for (i = 0; i < numParts; i++) { 191cf200d32Sopenharmony_ci cout.width(4); 192cf200d32Sopenharmony_ci cout << i + 1 << "\t"; 193cf200d32Sopenharmony_ci cout.width(13); 194cf200d32Sopenharmony_ci cout << partitions[i].firstLBA << "\t"; 195cf200d32Sopenharmony_ci cout.width(15); 196cf200d32Sopenharmony_ci cout << partitions[i].lengthLBA << " \t0x"; 197cf200d32Sopenharmony_ci cout.width(2); 198cf200d32Sopenharmony_ci cout.fill('0'); 199cf200d32Sopenharmony_ci cout.setf(ios::uppercase); 200cf200d32Sopenharmony_ci cout << hex << (int) partitions[i].fsType << "\n" << dec; 201cf200d32Sopenharmony_ci cout.fill(' '); 202cf200d32Sopenharmony_ci } // for 203cf200d32Sopenharmony_ci } // if 204cf200d32Sopenharmony_ci} // BSDData::DisplayBSDData() 205cf200d32Sopenharmony_ci 206cf200d32Sopenharmony_ci// Displays the BSD disklabel state. Called during program launch to inform 207cf200d32Sopenharmony_ci// the user about the partition table(s) status 208cf200d32Sopenharmony_ciint BSDData::ShowState(void) { 209cf200d32Sopenharmony_ci int retval = 0; 210cf200d32Sopenharmony_ci 211cf200d32Sopenharmony_ci switch (state) { 212cf200d32Sopenharmony_ci case bsd_invalid: 213cf200d32Sopenharmony_ci cout << " BSD: not present\n"; 214cf200d32Sopenharmony_ci break; 215cf200d32Sopenharmony_ci case bsd: 216cf200d32Sopenharmony_ci cout << " BSD: present\n"; 217cf200d32Sopenharmony_ci retval = 1; 218cf200d32Sopenharmony_ci break; 219cf200d32Sopenharmony_ci default: 220cf200d32Sopenharmony_ci cout << "\a BSD: unknown -- bug!\n"; 221cf200d32Sopenharmony_ci break; 222cf200d32Sopenharmony_ci } // switch 223cf200d32Sopenharmony_ci return retval; 224cf200d32Sopenharmony_ci} // BSDData::ShowState() 225cf200d32Sopenharmony_ci 226cf200d32Sopenharmony_ci// Weirdly, this function has stopped working when defined inline, 227cf200d32Sopenharmony_ci// but it's OK here.... 228cf200d32Sopenharmony_ciint BSDData::IsDisklabel(void) { 229cf200d32Sopenharmony_ci return (state == bsd); 230cf200d32Sopenharmony_ci} // BSDData::IsDiskLabel() 231cf200d32Sopenharmony_ci 232cf200d32Sopenharmony_ci// Returns the BSD table's partition type code 233cf200d32Sopenharmony_ciuint8_t BSDData::GetType(int i) { 234cf200d32Sopenharmony_ci uint8_t retval = 0; // 0 = "unused" 235cf200d32Sopenharmony_ci 236cf200d32Sopenharmony_ci if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0)) 237cf200d32Sopenharmony_ci retval = partitions[i].fsType; 238cf200d32Sopenharmony_ci 239cf200d32Sopenharmony_ci return(retval); 240cf200d32Sopenharmony_ci} // BSDData::GetType() 241cf200d32Sopenharmony_ci 242cf200d32Sopenharmony_ci// Returns the number of the first sector of the specified partition 243cf200d32Sopenharmony_ciuint64_t BSDData::GetFirstSector(int i) { 244cf200d32Sopenharmony_ci uint64_t retval = UINT64_C(0); 245cf200d32Sopenharmony_ci 246cf200d32Sopenharmony_ci if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0)) 247cf200d32Sopenharmony_ci retval = (uint64_t) partitions[i].firstLBA; 248cf200d32Sopenharmony_ci 249cf200d32Sopenharmony_ci return retval; 250cf200d32Sopenharmony_ci} // BSDData::GetFirstSector 251cf200d32Sopenharmony_ci 252cf200d32Sopenharmony_ci// Returns the length (in sectors) of the specified partition 253cf200d32Sopenharmony_ciuint64_t BSDData::GetLength(int i) { 254cf200d32Sopenharmony_ci uint64_t retval = UINT64_C(0); 255cf200d32Sopenharmony_ci 256cf200d32Sopenharmony_ci if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0)) 257cf200d32Sopenharmony_ci retval = (uint64_t) partitions[i].lengthLBA; 258cf200d32Sopenharmony_ci 259cf200d32Sopenharmony_ci return retval; 260cf200d32Sopenharmony_ci} // BSDData::GetLength() 261cf200d32Sopenharmony_ci 262cf200d32Sopenharmony_ci// Returns the number of partitions defined in the current table 263cf200d32Sopenharmony_ciint BSDData::GetNumParts(void) { 264cf200d32Sopenharmony_ci return numParts; 265cf200d32Sopenharmony_ci} // BSDData::GetNumParts() 266cf200d32Sopenharmony_ci 267cf200d32Sopenharmony_ci// Returns the specified partition as a GPT partition. Used in BSD-to-GPT 268cf200d32Sopenharmony_ci// conversion process 269cf200d32Sopenharmony_ciGPTPart BSDData::AsGPT(int i) { 270cf200d32Sopenharmony_ci GPTPart guid; // dump data in here, then return it 271cf200d32Sopenharmony_ci uint64_t sectorOne, sectorEnd; // first & last sectors of partition 272cf200d32Sopenharmony_ci int passItOn = 1; // Set to 0 if partition is empty or invalid 273cf200d32Sopenharmony_ci 274cf200d32Sopenharmony_ci guid.BlankPartition(); 275cf200d32Sopenharmony_ci sectorOne = (uint64_t) partitions[i].firstLBA; 276cf200d32Sopenharmony_ci sectorEnd = sectorOne + (uint64_t) partitions[i].lengthLBA; 277cf200d32Sopenharmony_ci if (sectorEnd > 0) sectorEnd--; 278cf200d32Sopenharmony_ci // Note on above: BSD partitions sometimes have a length of 0 and a start 279cf200d32Sopenharmony_ci // sector of 0. With unsigned ints, the usual way (start + length - 1) to 280cf200d32Sopenharmony_ci // find the end will result in a huge number, which will be confusing. 281cf200d32Sopenharmony_ci // Thus, apply the "-1" part only if it's reasonable to do so. 282cf200d32Sopenharmony_ci 283cf200d32Sopenharmony_ci // Do a few sanity checks on the partition before we pass it on.... 284cf200d32Sopenharmony_ci // First, check that it falls within the bounds of its container 285cf200d32Sopenharmony_ci // and that it starts before it ends.... 286cf200d32Sopenharmony_ci if ((sectorOne < labelFirstLBA) || (sectorEnd > labelLastLBA) || (sectorOne > sectorEnd)) 287cf200d32Sopenharmony_ci passItOn = 0; 288cf200d32Sopenharmony_ci // Some disklabels include a pseudo-partition that's the size of the entire 289cf200d32Sopenharmony_ci // disk or containing partition. Don't return it. 290cf200d32Sopenharmony_ci if ((sectorOne <= labelFirstLBA) && (sectorEnd >= labelLastLBA) && 291cf200d32Sopenharmony_ci (GetType(i) == 0)) 292cf200d32Sopenharmony_ci passItOn = 0; 293cf200d32Sopenharmony_ci // If the end point is 0, it's not a valid partition. 294cf200d32Sopenharmony_ci if ((sectorEnd == 0) || (sectorEnd == labelFirstLBA)) 295cf200d32Sopenharmony_ci passItOn = 0; 296cf200d32Sopenharmony_ci 297cf200d32Sopenharmony_ci if (passItOn) { 298cf200d32Sopenharmony_ci guid.SetFirstLBA(sectorOne); 299cf200d32Sopenharmony_ci guid.SetLastLBA(sectorEnd); 300cf200d32Sopenharmony_ci // Now set a random unique GUID for the partition.... 301cf200d32Sopenharmony_ci guid.RandomizeUniqueGUID(); 302cf200d32Sopenharmony_ci // ... zero out the attributes and name fields.... 303cf200d32Sopenharmony_ci guid.SetAttributes(UINT64_C(0)); 304cf200d32Sopenharmony_ci // Most BSD disklabel type codes seem to be archaic or rare. 305cf200d32Sopenharmony_ci // They're also ambiguous; a FreeBSD filesystem is impossible 306cf200d32Sopenharmony_ci // to distinguish from a NetBSD one. Thus, these code assignment 307cf200d32Sopenharmony_ci // are going to be rough to begin with. For a list of meanings, 308cf200d32Sopenharmony_ci // see http://fxr.watson.org/fxr/source/sys/dtype.h?v=DFBSD, 309cf200d32Sopenharmony_ci // or Google it. 310cf200d32Sopenharmony_ci switch (GetType(i)) { 311cf200d32Sopenharmony_ci case 1: // BSD swap 312cf200d32Sopenharmony_ci guid.SetType(0xa502); break; 313cf200d32Sopenharmony_ci case 7: // BSD FFS 314cf200d32Sopenharmony_ci guid.SetType(0xa503); break; 315cf200d32Sopenharmony_ci case 8: case 11: // MS-DOS or HPFS 316cf200d32Sopenharmony_ci guid.SetType(0x0700); break; 317cf200d32Sopenharmony_ci case 9: // log-structured fs 318cf200d32Sopenharmony_ci guid.SetType(0xa903); break; 319cf200d32Sopenharmony_ci case 13: // bootstrap 320cf200d32Sopenharmony_ci guid.SetType(0xa501); break; 321cf200d32Sopenharmony_ci case 14: // vinum 322cf200d32Sopenharmony_ci guid.SetType(0xa505); break; 323cf200d32Sopenharmony_ci case 15: // RAID 324cf200d32Sopenharmony_ci guid.SetType(0xa903); break; 325cf200d32Sopenharmony_ci case 27: // FreeBSD ZFS 326cf200d32Sopenharmony_ci guid.SetType(0xa504); break; 327cf200d32Sopenharmony_ci default: 328cf200d32Sopenharmony_ci guid.SetType(0xa503); break; 329cf200d32Sopenharmony_ci } // switch 330cf200d32Sopenharmony_ci // Set the partition name to the name of the type code.... 331cf200d32Sopenharmony_ci guid.SetName(guid.GetTypeName()); 332cf200d32Sopenharmony_ci } // if 333cf200d32Sopenharmony_ci return guid; 334cf200d32Sopenharmony_ci} // BSDData::AsGPT() 335