AppleFileSystemDriver.cpp [plain text]
#include "AppleFileSystemDriver.h"
#include <IOKit/IOLib.h>
#include <IOKit/IOBSD.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include "AppleRAIDUserLib.h"
#include <libkern/OSByteOrder.h>
#include <sys/param.h>
#include <hfs/hfs_format.h>
#include <libkern/crypto/md5.h>
#include <uuid/uuid.h>
#include <uuid/namespace.h>
#if DEBUG
#define VERBOSE 1
#define DEBUG_LOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DEBUG_LOG(fmt, args...)
#endif
#if VERBOSE
#define VERBOSE_LOG(fmt, args...) IOLog(fmt, ## args)
#else
#define VERBOSE_LOG(fmt, args...)
#endif
#define kClassName "AppleFileSystemDriver"
#define kMediaMatchKey "media-match"
#define kBootUUIDKey "boot-uuid"
#define kBootUUIDMediaKey "boot-uuid-media"
#define super IOService
OSDefineMetaClassAndStructors(AppleFileSystemDriver, IOService)
#if VERBOSE
static OSString *
createStringFromUUID(const uuid_t uu)
{
char buf[64];
uuid_unparse_upper(uu, buf);
return OSString::withCString(buf);
}
#endif // VERBOSE
static IOReturn
createUUIDFromName( const uuid_t uu_space, uint8_t *name_p, unsigned int name_len, uuid_t uu_result )
{
MD5_CTX md5c;
assert( sizeof( uuid_t ) == MD5_DIGEST_LENGTH );
memcpy( uu_result, uu_space, sizeof( uuid_t ) );
MD5Init( &md5c );
MD5Update( &md5c, uu_result, sizeof( uuid_t ) );
MD5Update( &md5c, name_p, name_len );
MD5Final( uu_result, &md5c );
uu_result[6] = 0x30 | ( uu_result[6] & 0x0F );
uu_result[8] = 0x80 | ( uu_result[8] & 0x3F );
return kIOReturnSuccess;
}
enum {
kHFSBlockSize = 512,
kVolumeUUIDValueLength = 8
};
typedef union VolumeUUID {
uint8_t bytes[kVolumeUUIDValueLength];
struct {
uint32_t high;
uint32_t low;
} v;
} VolumeUUID;
IOReturn
AppleFileSystemDriver::readHFSUUID(IOMedia *media, void **uuidPtr)
{
bool mediaIsOpen = false;
UInt64 mediaBlockSize = 0;
IOBufferMemoryDescriptor * buffer = 0;
void * bytes = 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 = (void *) buffer->getBytesNoCopy();
mdbPtr = (HFSMasterDirectoryBlock *)bytes;
volHdrPtr = (HFSPlusVolumeHeader *)bytes;
mediaIsOpen = media->open(media, 0, kIOStorageAccessReader);
if ( mediaIsOpen == false ) break;
bufferReadAt = 2 * kHFSBlockSize;
status = media->read(media, bufferReadAt, buffer);
if ( status != kIOReturnSuccess ) break;
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);
bufferReadAt = ((u_int64_t)startBlock * (u_int64_t)allocationBlockSize) +
((u_int64_t)firstAllocationBlock * (u_int64_t)kHFSBlockSize) +
(u_int64_t)(2 * kHFSBlockSize);
status = media->read(media, bufferReadAt, buffer);
if ( status != kIOReturnSuccess ) break;
}
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 = 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;
}
bool
AppleFileSystemDriver::mediaNotificationHandler(
void * target, void * ref,
IOService * service,
IONotifier * notifier)
{
AppleFileSystemDriver * fs;
IOMedia * media;
IOReturn status = kIOReturnError;
OSString * contentHint;
const char * contentStr;
VolumeUUID volumeUUID;
OSString * uuidProperty;
uuid_t uuid;
bool matched = false;
bool isRAID = false;
DEBUG_LOG("%s[%p]::%s -> '%s'\n", kClassName, target, __func__, service->getName());
do {
fs = OSDynamicCast( AppleFileSystemDriver, (IOService *)target );
if (fs == 0) break;
media = OSDynamicCast( IOMedia, service );
if (media == 0) break;
if ( media->isFormatted() == false ) break;
isRAID = (media->getProperty(kAppleRAIDIsRAIDKey) == kOSBooleanTrue);
if (isRAID) {
IOStorage *provider;
OSString *status;
if (!(provider = media->getProvider())) goto notraid;
if (!(status = OSDynamicCast(OSString,
provider->getProperty(kAppleRAIDStatusKey)))) goto notraid;
if (!status->isEqualTo(kAppleRAIDStatusDegraded) &&
!status->isEqualTo(kAppleRAIDStatusOnline)) {
DEBUG_LOG("skipping prematurely-available RAID device");
break;
}
}
notraid:
uuidProperty = OSDynamicCast( OSString, media->getProperty("UUID") );
if (uuidProperty != NULL) {
if (fs->_uuidString && uuidProperty->isEqualTo(fs->_uuidString)) {
VERBOSE_LOG("existing UUID property matched\n");
matched = true;
break;
}
}
contentHint = OSDynamicCast( OSString, media->getProperty(kIOMediaContentHintKey) );
if (contentHint == NULL) break;
contentStr = contentHint->getCStringNoCopy();
if (contentStr == NULL) break;
if ( strcmp(contentStr, "Apple_HFS" ) == 0 ||
strcmp(contentStr, "Apple_HFSX" ) == 0 ||
strcmp(contentStr, "Apple_Boot" ) == 0 ||
strcmp(contentStr, "Apple_Recovery" ) == 0 ||
strcmp(contentStr, "48465300-0000-11AA-AA11-00306543ECAC" ) == 0 ||
strcmp(contentStr, "426F6F74-0000-11AA-AA11-00306543ECAC" ) == 0 ||
strcmp(contentStr, "5265636F-7665-11AA-AA11-00306543ECAC" ) == 0 ) {
status = readHFSUUID( media, (void **)&volumeUUID );
} else if (strlen(contentStr) == 0 && isRAID) {
status = readHFSUUID( media, (void **)&volumeUUID );
} else {
break;
}
if (status != kIOReturnSuccess) {
break;
}
if (createUUIDFromName( kFSUUIDNamespaceSHA1,
volumeUUID.bytes, kVolumeUUIDValueLength,
uuid ) != kIOReturnSuccess) {
break;
}
#if VERBOSE
OSString *str = createStringFromUUID(uuid);
OSString *bsdn = OSDynamicCast(OSString,media->getProperty("BSD Name"));
if (str) {
IOLog(" UUID %s found on volume '%s' (%s)\n", str->getCStringNoCopy(),
media->getName(), bsdn ? bsdn->getCStringNoCopy():"");
str->release();
}
#endif
if (fs->_uuid) {
if ( uuid_compare(uuid, fs->_uuid) == 0 ) {
VERBOSE_LOG(" UUID matched on volume %s\n", media->getName());
matched = true;
}
}
} while (false);
if (matched) {
if (fs->_notifier != NULL) {
fs->_notifier->remove();
fs->_notifier = NULL;
}
DEBUG_LOG("%s::%s publishing boot-uuid-media '%s'\n", kClassName, __func__, media->getName());
IOService::publishResource( kBootUUIDMediaKey, media );
fs->getResourceService()->removeProperty( kBootUUIDKey );
fs->terminate( kIOServiceRequired );
VERBOSE_LOG("%s[%p]::%s returning TRUE\n", kClassName, target, __func__);
return true;
}
DEBUG_LOG("%s[%p]::%s returning false\n", kClassName, target, __func__);
#if DEBUG
#endif
return false;
}
bool
AppleFileSystemDriver::start(IOService * provider)
{
OSDictionary * matching;
OSString * uuidString;
const char * uuidCString;
IOService * resourceService;
OSDictionary * dict;
DEBUG_LOG("%s[%p]::%s\n", getName(), this, __func__);
DEBUG_LOG("%s provider is '%s'\n", getName(), provider->getName());
do {
resourceService = getResourceService();
if (resourceService == 0) break;
uuidString = OSDynamicCast( OSString, resourceService->getProperty("boot-uuid") );
if (uuidString) {
_uuidString = uuidString;
_uuidString->retain();
uuidCString = uuidString->getCStringNoCopy();
DEBUG_LOG("%s: got UUID string '%s'\n", getName(), uuidCString);
if (uuid_parse(uuidCString, _uuid) != 0) {
IOLog("%s: Invalid UUID string '%s'\n", getName(), uuidCString);
break;
}
} else {
IOLog("%s: Error getting boot-uuid property\n", getName());
break;
}
dict = OSDynamicCast( OSDictionary, getProperty( kMediaMatchKey ) );
if (dict == 0) break;
dict = OSDictionary::withDictionary(dict);
if (dict == 0) break;
matching = IOService::serviceMatching( "IOMedia", dict );
if ( matching == 0 )
return false;
_notifier = IOService::addMatchingNotification( gIOMatchedNotification, matching,
&mediaNotificationHandler,
this, 0 );
DEBUG_LOG("%s[%p]::%s finishes TRUE\n", getName(), this, __func__);
return true;
} while (false);
DEBUG_LOG("%s[%p]::%s finishes false\n", getName(), this, __func__);
return false;
}
void
AppleFileSystemDriver::free()
{
DEBUG_LOG("%s[%p]::%s\n", getName(), this, __func__);
if (_uuidString) _uuidString->release();
if (_notifier) _notifier->remove();
super::free();
}