IOReturn AppleFileSystemDriver::readHFSUUID(IOMedia *media, void **uuidPtr) { bool mediaIsOpen = false; UInt64 mediaBlockSize = 0; IOBufferMemoryDescriptor * buffer = 0; uint8_t * bytes = 0; UInt64 bytesAt = 0; UInt64 bufferReadAt = 0; vm_size_t bufferSize = 0; IOReturn status = kIOReturnError; HFSMasterDirectoryBlock * mdbPtr = 0; HFSPlusVolumeHeader * volHdrPtr = 0; VolumeUUID * volumeUUIDPtr = (VolumeUUID *)uuidPtr; DEBUG_LOG("%s::%s\n", kClassName, __func__); do { mediaBlockSize = media->getPreferredBlockSize(); bufferSize = IORound(sizeof(HFSMasterDirectoryBlock), mediaBlockSize); buffer = IOBufferMemoryDescriptor::withCapacity(bufferSize, kIODirectionIn); if ( buffer == 0 ) break; bytes = (uint8_t *) buffer->getBytesNoCopy(); // Open the media with read access. mediaIsOpen = media->open(media, 0, kIOStorageAccessReader); if ( mediaIsOpen == false ) break; bytesAt = 2 * kHFSBlockSize; bufferReadAt = IOTrunc( bytesAt, mediaBlockSize ); bytesAt -= bufferReadAt; mdbPtr = (HFSMasterDirectoryBlock *)&bytes[bytesAt]; volHdrPtr = (HFSPlusVolumeHeader *)&bytes[bytesAt]; status = media->read(media, bufferReadAt, buffer); if ( status != kIOReturnSuccess ) break; /* * If this is a wrapped HFS Plus volume, read the Volume Header from * sector 2 of the embedded volume. */ if ( OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord && OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) == kHFSPlusSigWord) { u_int32_t allocationBlockSize, firstAllocationBlock, startBlock, blockCount; if (OSSwapBigToHostInt16(mdbPtr->drSigWord) != kHFSSigWord) { break; } allocationBlockSize = OSSwapBigToHostInt32(mdbPtr->drAlBlkSiz); firstAllocationBlock = OSSwapBigToHostInt16(mdbPtr->drAlBlSt); if (OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) != kHFSPlusSigWord) { break; } startBlock = OSSwapBigToHostInt16(mdbPtr->drEmbedExtent.startBlock); blockCount = OSSwapBigToHostInt16(mdbPtr->drEmbedExtent.blockCount); bytesAt = ((u_int64_t)startBlock * (u_int64_t)allocationBlockSize) + ((u_int64_t)firstAllocationBlock * (u_int64_t)kHFSBlockSize) + (u_int64_t)(2 * kHFSBlockSize); bufferReadAt = IOTrunc( bytesAt, mediaBlockSize ); bytesAt -= bufferReadAt; mdbPtr = (HFSMasterDirectoryBlock *)&bytes[bytesAt]; volHdrPtr = (HFSPlusVolumeHeader *)&bytes[bytesAt]; status = media->read(media, bufferReadAt, buffer); if ( status != kIOReturnSuccess ) break; } /* * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX * volumes (including wrapped HFS Plus). Verify the signature and grab the * UUID from the Finder Info. */ if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord) { bcopy((void *)&mdbPtr->drFndrInfo[6], volumeUUIDPtr->bytes, kVolumeUUIDValueLength); status = kIOReturnSuccess; } else if (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSPlusSigWord || OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSXSigWord) { bcopy((void *)&volHdrPtr->finderInfo[24], volumeUUIDPtr->bytes, kVolumeUUIDValueLength); status = kIOReturnSuccess; } else { // status = 0 from earlier successful media->read() status = kIOReturnBadMedia; } } while (false); if ( mediaIsOpen ) media->close(media); if ( buffer ) buffer->release(); DEBUG_LOG("%s::%s finishes with status %d\n", kClassName, __func__, status); return status; }
OSSet * IOFDiskPartitionScheme::scan(SInt32 * score) { // // Scan the provider media for an FDisk partition map. Returns the set // of media objects representing each of the partitions (the retain for // the set is passed to the caller), or null should no partition map be // found. The default probe score can be adjusted up or down, based on // the confidence of the scan. // IOBufferMemoryDescriptor * buffer = 0; UInt32 bufferSize = 0; UInt32 fdiskBlock = 0; UInt32 fdiskBlockExtn = 0; UInt32 fdiskBlockNext = 0; UInt32 fdiskID = 0; disk_blk0 * fdiskMap = 0; IOMedia * media = getProvider(); UInt64 mediaBlockSize = media->getPreferredBlockSize(); bool mediaIsOpen = false; OSSet * partitions = 0; IOReturn status = kIOReturnError; // Determine whether this media is formatted. if ( media->isFormatted() == false ) goto scanErr; // Determine whether this media has an appropriate block size. if ( (mediaBlockSize % sizeof(disk_blk0)) ) goto scanErr; // Allocate a buffer large enough to hold one map, rounded to a media block. bufferSize = IORound(sizeof(disk_blk0), mediaBlockSize); buffer = IOBufferMemoryDescriptor::withCapacity( /* capacity */ bufferSize, /* withDirection */ kIODirectionIn ); if ( buffer == 0 ) goto scanErr; // Allocate a set to hold the set of media objects representing partitions. partitions = OSSet::withCapacity(4); if ( partitions == 0 ) goto scanErr; // Open the media with read access. mediaIsOpen = open(this, 0, kIOStorageAccessReader); if ( mediaIsOpen == false ) goto scanErr; // Scan the media for FDisk partition map(s). do { // Read the next FDisk map into our buffer. status = media->read(this, fdiskBlock * mediaBlockSize, buffer); if ( status != kIOReturnSuccess ) goto scanErr; fdiskMap = (disk_blk0 *) buffer->getBytesNoCopy(); // Determine whether the partition map signature is present. if ( OSSwapLittleToHostInt16(fdiskMap->signature) != DISK_SIGNATURE ) { goto scanErr; } // Scan for valid partition entries in the partition map. fdiskBlockNext = 0; for ( unsigned index = 0; index < DISK_NPART; index++ ) { // Determine whether this is an extended (vs. data) partition. if ( isPartitionExtended(fdiskMap->parts + index) ) // (extended) { // If peer extended partitions exist, we accept only the first. if ( fdiskBlockNext == 0 ) // (no peer extended partition) { fdiskBlockNext = fdiskBlockExtn + OSSwapLittleToHostInt32( /* data */ fdiskMap->parts[index].relsect ); if ( fdiskBlockNext * mediaBlockSize >= media->getSize() ) { fdiskBlockNext = 0; // (exceeds confines of media) } } } else if ( isPartitionUsed(fdiskMap->parts + index) ) // (data) { // Prepare this partition's ID. fdiskID = ( fdiskBlock == 0 ) ? (index + 1) : (fdiskID + 1); // Determine whether the partition is corrupt (fatal). if ( isPartitionCorrupt( /* partition */ fdiskMap->parts + index, /* partitionID */ fdiskID, /* fdiskBlock */ fdiskBlock ) ) { goto scanErr; } // Determine whether the partition is invalid (skipped). if ( isPartitionInvalid( /* partition */ fdiskMap->parts + index, /* partitionID */ fdiskID, /* fdiskBlock */ fdiskBlock ) ) { continue; } // Create a media object to represent this partition. IOMedia * newMedia = instantiateMediaObject( /* partition */ fdiskMap->parts + index, /* partitionID */ fdiskID, /* fdiskBlock */ fdiskBlock ); if ( newMedia ) { partitions->setObject(newMedia); newMedia->release(); } } } // Prepare for first extended partition, if any. if ( fdiskBlock == 0 ) { fdiskID = DISK_NPART; fdiskBlockExtn = fdiskBlockNext; } } while ( (fdiskBlock = fdiskBlockNext) ); // Release our resources. close(this); buffer->release(); return partitions; scanErr: // Release our resources. if ( mediaIsOpen ) close(this); if ( partitions ) partitions->release(); if ( buffer ) buffer->release(); return 0; }
OSSet * IOGUIDPartitionScheme::scan(SInt32 * score) { // // Scan the provider media for a GUID partition map. Returns the set // of media objects representing each of the partitions (the retain for // the set is passed to the caller), or null should no partition map be // found. The default probe score can be adjusted up or down, based on // the confidence of the scan. // IOBufferMemoryDescriptor * buffer = 0; IOByteCount bufferSize = 0; UInt32 fdiskID = 0; disk_blk0 * fdiskMap = 0; UInt64 gptBlock = 0; UInt32 gptCheck = 0; UInt32 gptCount = 0; UInt32 gptID = 0; gpt_ent * gptMap = 0; UInt32 gptSize = 0; UInt32 headerCheck = 0; gpt_hdr * headerMap = 0; UInt32 headerSize = 0; IOMedia * media = getProvider(); UInt64 mediaBlockSize = media->getPreferredBlockSize(); bool mediaIsOpen = false; OSSet * partitions = 0; IOReturn status = kIOReturnError; // Determine whether this media is formatted. if ( media->isFormatted() == false ) goto scanErr; // Determine whether this media has an appropriate block size. if ( (mediaBlockSize % sizeof(disk_blk0)) ) goto scanErr; // Allocate a buffer large enough to hold one map, rounded to a media block. bufferSize = IORound(sizeof(disk_blk0), mediaBlockSize); buffer = IOBufferMemoryDescriptor::withCapacity( /* capacity */ bufferSize, /* withDirection */ kIODirectionIn ); if ( buffer == 0 ) goto scanErr; // Allocate a set to hold the set of media objects representing partitions. partitions = OSSet::withCapacity(8); if ( partitions == 0 ) goto scanErr; // Open the media with read access. mediaIsOpen = open(this, 0, kIOStorageAccessReader); if ( mediaIsOpen == false ) goto scanErr; // Read the protective map into our buffer. status = media->read(this, 0, buffer); if ( status != kIOReturnSuccess ) goto scanErr; fdiskMap = (disk_blk0 *) buffer->getBytesNoCopy(); // Determine whether the protective map signature is present. if ( OSSwapLittleToHostInt16(fdiskMap->signature) != DISK_SIGNATURE ) { goto scanErr; } // Scan for valid partition entries in the protective map. for ( unsigned index = 0; index < DISK_NPART; index++ ) { if ( fdiskMap->parts[index].systid ) { if ( fdiskMap->parts[index].systid == 0xEE ) { if ( fdiskID ) goto scanErr; fdiskID = index + 1; } } } if ( fdiskID == 0 ) goto scanErr; // Read the partition header into our buffer. status = media->read(this, mediaBlockSize, buffer); if ( status != kIOReturnSuccess ) goto scanErr; headerMap = (gpt_hdr *) buffer->getBytesNoCopy(); // Determine whether the partition header signature is present. if ( memcmp(headerMap->hdr_sig, GPT_HDR_SIG, strlen(GPT_HDR_SIG)) ) { goto scanErr; } // Determine whether the partition header size is valid. headerCheck = OSSwapLittleToHostInt32(headerMap->hdr_crc_self); headerSize = OSSwapLittleToHostInt32(headerMap->hdr_size); if ( headerSize < offsetof(gpt_hdr, padding) ) { goto scanErr; } if ( headerSize > mediaBlockSize ) { goto scanErr; } // Determine whether the partition header checksum is valid. headerMap->hdr_crc_self = 0; if ( crc32(0, headerMap, headerSize) != headerCheck ) { goto scanErr; } // Determine whether the partition entry size is valid. gptCheck = OSSwapLittleToHostInt32(headerMap->hdr_crc_table); gptSize = OSSwapLittleToHostInt32(headerMap->hdr_entsz); if ( gptSize < sizeof(gpt_ent) ) { goto scanErr; } if ( gptSize > UINT16_MAX ) { goto scanErr; } // Determine whether the partition entry count is valid. gptBlock = OSSwapLittleToHostInt64(headerMap->hdr_lba_table); gptCount = OSSwapLittleToHostInt32(headerMap->hdr_entries); if ( gptCount > UINT16_MAX ) { goto scanErr; } // Allocate a buffer large enough to hold one map, rounded to a media block. buffer->release(); bufferSize = IORound(gptCount * gptSize, mediaBlockSize); buffer = IOBufferMemoryDescriptor::withCapacity( /* capacity */ bufferSize, /* withDirection */ kIODirectionIn ); if ( buffer == 0 ) goto scanErr; // Read the partition header into our buffer. status = media->read(this, gptBlock * mediaBlockSize, buffer); if ( status != kIOReturnSuccess ) goto scanErr; gptMap = (gpt_ent *) buffer->getBytesNoCopy(); // Determine whether the partition entry checksum is valid. if ( crc32(0, gptMap, gptCount * gptSize) != gptCheck ) { goto scanErr; } // Scan for valid partition entries in the partition map. for ( gptID = 1; gptID <= gptCount; gptID++ ) { gptMap = (gpt_ent *) ( ((UInt8 *) buffer->getBytesNoCopy()) + (gptID * gptSize) - gptSize ); uuid_unswap( gptMap->ent_type ); uuid_unswap( gptMap->ent_uuid ); if ( isPartitionUsed( gptMap ) ) { // Determine whether the partition is corrupt (fatal). if ( isPartitionCorrupt( gptMap, gptID ) ) { goto scanErr; } // Determine whether the partition is invalid (skipped). if ( isPartitionInvalid( gptMap, gptID ) ) { continue; } // Create a media object to represent this partition. IOMedia * newMedia = instantiateMediaObject( gptMap, gptID ); if ( newMedia ) { partitions->setObject(newMedia); newMedia->release(); } } } // Release our resources. close(this); buffer->release(); return partitions; scanErr: // Release our resources. if ( mediaIsOpen ) close(this); if ( partitions ) partitions->release(); if ( buffer ) buffer->release(); return 0; }
BVRef diskScanGPTBootVolumes(int biosdev, int * countPtr) { _DISK_DEBUG_DUMP("In diskScanGPTBootVolumes(%d)\n", biosdev); void *buffer = malloc(BPS); if (readBytes(biosdev, 1, 0, BPS, buffer) == 0) { int gptID = 1; gpt_ent * gptMap = 0; gpt_hdr * headerMap = buffer; // Partition header signature present? if (memcmp(headerMap->hdr_sig, GPT_HDR_SIG, strlen(GPT_HDR_SIG)) == 0) { UInt32 headerSize = OSSwapLittleToHostInt32(headerMap->hdr_size); // Valid partition header size? if (headerSize >= offsetof(gpt_hdr, padding)) { // No header size overrun (limiting to 512 bytes)? if (headerSize <= BPS) { UInt32 headerCheck = OSSwapLittleToHostInt32(headerMap->hdr_crc_self); headerMap->hdr_crc_self = 0; // Valid partition header checksum? if (crc32(0, headerMap, headerSize) == headerCheck) { UInt64 gptBlock = OSSwapLittleToHostInt64(headerMap->hdr_lba_table); UInt32 gptCount = OSSwapLittleToHostInt32(headerMap->hdr_entries); UInt32 gptSize = OSSwapLittleToHostInt32(headerMap->hdr_entsz); free(buffer); if (gptSize >= sizeof(gpt_ent)) { UInt32 bufferSize = IORound(gptCount * gptSize, BPS); buffer = malloc(bufferSize); // Allocate a buffer. if (readBytes(biosdev, gptBlock, 0, bufferSize, buffer) == 0) { // Allocate a new map for this device and insert it into the chain. struct DiskBVMap *map = malloc(sizeof(*map)); map->biosdev = biosdev; map->bvr = NULL; map->bvrcnt = 0; map->next = gDiskBVMap; gDiskBVMap = map; for (; gptID <= gptCount; gptID++) { gptMap = (gpt_ent *) (buffer + ((gptID - 1) * gptSize)); if (isPartitionUsed(gptMap)) { BVRef bvr = NULL; int bvrFlags = -1; #if DEBUG_DISK char stringuuid[100]; efi_guid_unparse_upper((EFI_GUID*)gptMap->ent_type, stringuuid); printf("Reading GPT partition %d, type %s\n", gptID, stringuuid); sleep(1); #endif if (efi_guid_compare(&GPT_HFS_GUID, (EFI_GUID const *)gptMap->ent_type) == 0) { _DISK_DEBUG_DUMP("Matched: GPT_HFS_GUID\n"); bvrFlags = kBVFlagZero; } /* else if (efi_guid_compare(&GPT_BOOT_GUID, (EFI_GUID const *)gptMap->ent_type) == 0) { _DISK_DEBUG_DUMP("Matched: GPT_BOOT_GUID\n"); bvrFlags = kBVFlagBooter; } */ #if EFI_SYSTEM_PARTITION_SUPPORT else if (efi_guid_compare(&GPT_EFISYS_GUID, (EFI_GUID const *)gptMap->ent_type) == 0) { _DISK_DEBUG_DUMP("Matched: GPT_EFISYS_GUID, probing for HFS format...\n"); //-------------- START ------------- // Allocate buffer for 4 sectors. void * probeBuffer = malloc(2048); bool probeOK = false; // Read the first 4 sectors. if (readBytes(biosdev, gptMap->ent_lba_start, 0, 2048, (void *)probeBuffer) == 0) { // Probing (returns true for HFS partitions). probeOK = HFSProbe(probeBuffer); _DISK_DEBUG_DUMP("HFSProbe status: Is %s a HFS partition.\n", probeOK ? "" : "not"); } free(probeBuffer); // Veto non-HFS partitions to be invalid. if (!probeOK) { continue; } //-------------- END --------------- bvrFlags = kBVFlagEFISystem; } #endif // Only true when we found a usable partition. if (bvrFlags >= 0) { bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap, bvrFlags); if (bvr) { bvr->part_type = FDISK_HFS; bvr->next = map->bvr; map->bvr = bvr; ++map->bvrcnt; // Don't waste time checking for boot.efi on ESP partitions. if ((bvrFlags & kBVFlagEFISystem) == 0) { // Flag System Volumes with kBVFlagSystemVolume. hasBootEFI(bvr); } // True on the initial run only. if (gPlatform.BootVolume == NULL) { // Initialize with the first bootable volume. gPlatform.BootVolume = gPlatform.RootVolume = bvr; _DISK_DEBUG_DUMP("Init B/RootVolume - partition: %d, flags: %d, gptID: %d\n", bvr->part_no, bvr->flags, gptID); } // Bail out after finding the first System Volume. if (bvr->flags & kBVFlagSystemVolume) { _DISK_DEBUG_DUMP("Partition %d is a System Volume\n", gptID); break; } } } } } free(buffer); *countPtr = map->bvrcnt; _DISK_DEBUG_DUMP("map->bvrcnt: %d\n", map->bvrcnt); _DISK_DEBUG_SLEEP(5); return map->bvr; } } } } } } } free(buffer); *countPtr = 0; _DISK_DEBUG_SLEEP(5); return NULL; }