1/* 2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include "AppleFileSystemDriver.h" 25 26#include <IOKit/IOLib.h> 27#include <IOKit/IOBSD.h> 28#include <IOKit/IOBufferMemoryDescriptor.h> 29#include "AppleRAIDUserLib.h" 30 31#include <libkern/OSByteOrder.h> 32 33#include <sys/param.h> 34#include <hfs/hfs_format.h> 35#include <libkern/crypto/md5.h> 36#include <uuid/uuid.h> 37#include <uuid/namespace.h> 38 39//------------------------------------------ 40 41//#define VERBOSE 1 42//#define DEBUG 1 43#if DEBUG 44#define VERBOSE 1 45#define DEBUG_LOG(fmt, args...) IOLog(fmt, ## args) 46#else 47#define DEBUG_LOG(fmt, args...) 48#endif 49 50#if VERBOSE 51#define VERBOSE_LOG(fmt, args...) IOLog(fmt, ## args) 52#else 53#define VERBOSE_LOG(fmt, args...) 54#endif 55 56//------------------------------------------ 57 58#define kClassName "AppleFileSystemDriver" 59#define kMediaMatchKey "media-match" 60#define kBootUUIDKey "boot-uuid" 61#define kBootUUIDMediaKey "boot-uuid-media" 62 63#define super IOService 64OSDefineMetaClassAndStructors(AppleFileSystemDriver, IOService) 65 66 67//------------------------------------------ 68 69#if VERBOSE 70 71static OSString * 72createStringFromUUID(const uuid_t uu) 73{ 74 char buf[64]; 75 76 uuid_unparse_upper(uu, buf); 77 78 return OSString::withCString(buf); 79} 80 81#endif // VERBOSE 82 83//------------------------------------------ 84 85static IOReturn 86createUUIDFromName( const uuid_t uu_space, uint8_t *name_p, unsigned int name_len, uuid_t uu_result ) 87{ 88 /* 89 * Creates a UUID from a unique "name" in the given "name space". See version 3 UUID. 90 */ 91 92 MD5_CTX md5c; 93 94 assert( sizeof( uuid_t ) == MD5_DIGEST_LENGTH ); 95 96 memcpy( uu_result, uu_space, sizeof( uuid_t ) ); 97 98 MD5Init( &md5c ); 99 MD5Update( &md5c, uu_result, sizeof( uuid_t ) ); 100 MD5Update( &md5c, name_p, name_len ); 101 MD5Final( uu_result, &md5c ); 102 103 // this UUID has been made version 3 style (i.e. via namespace) 104 // see "-uuid-urn-" IETF draft (which otherwise copies byte for byte) 105 uu_result[6] = 0x30 | ( uu_result[6] & 0x0F ); 106 uu_result[8] = 0x80 | ( uu_result[8] & 0x3F ); 107 108 return kIOReturnSuccess; 109} 110 111 112//------------------------------------------ 113 114enum { 115 kHFSBlockSize = 512, 116 kVolumeUUIDValueLength = 8 117}; 118 119typedef union VolumeUUID { 120 uint8_t bytes[kVolumeUUIDValueLength]; 121 struct { 122 uint32_t high; 123 uint32_t low; 124 } v; 125} VolumeUUID; 126 127//------------------------------------------ 128 129IOReturn 130AppleFileSystemDriver::readHFSUUID(IOMedia *media, void **uuidPtr) 131{ 132 bool mediaIsOpen = false; 133 UInt64 mediaBlockSize = 0; 134 IOBufferMemoryDescriptor * buffer = 0; 135 void * bytes = 0; 136 UInt64 bufferReadAt = 0; 137 vm_size_t bufferSize = 0; 138 IOReturn status = kIOReturnError; 139 HFSMasterDirectoryBlock * mdbPtr = 0; 140 HFSPlusVolumeHeader * volHdrPtr = 0; 141 VolumeUUID * volumeUUIDPtr = (VolumeUUID *)uuidPtr; 142 143 144 DEBUG_LOG("%s::%s\n", kClassName, __func__); 145 146 do { 147 148 mediaBlockSize = media->getPreferredBlockSize(); 149 150 bufferSize = IORound(sizeof(HFSMasterDirectoryBlock), mediaBlockSize); 151 buffer = IOBufferMemoryDescriptor::withCapacity(bufferSize, kIODirectionIn); 152 if ( buffer == 0 ) break; 153 154 bytes = (void *) buffer->getBytesNoCopy(); 155 156 mdbPtr = (HFSMasterDirectoryBlock *)bytes; 157 volHdrPtr = (HFSPlusVolumeHeader *)bytes; 158 159 // Open the media with read access. 160 161 mediaIsOpen = media->open(media, 0, kIOStorageAccessReader); 162 if ( mediaIsOpen == false ) break; 163 164 bufferReadAt = 2 * kHFSBlockSize; 165 166 status = media->read(media, bufferReadAt, buffer); 167 if ( status != kIOReturnSuccess ) break; 168 169 /* 170 * If this is a wrapped HFS Plus volume, read the Volume Header from 171 * sector 2 of the embedded volume. 172 */ 173 if ( OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord && 174 OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) == kHFSPlusSigWord) { 175 176 u_int32_t allocationBlockSize, firstAllocationBlock, startBlock, blockCount; 177 178 if (OSSwapBigToHostInt16(mdbPtr->drSigWord) != kHFSSigWord) { 179 break; 180 } 181 182 allocationBlockSize = OSSwapBigToHostInt32(mdbPtr->drAlBlkSiz); 183 firstAllocationBlock = OSSwapBigToHostInt16(mdbPtr->drAlBlSt); 184 185 if (OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) != kHFSPlusSigWord) { 186 break; 187 } 188 189 startBlock = OSSwapBigToHostInt16(mdbPtr->drEmbedExtent.startBlock); 190 blockCount = OSSwapBigToHostInt16(mdbPtr->drEmbedExtent.blockCount); 191 192 bufferReadAt = ((u_int64_t)startBlock * (u_int64_t)allocationBlockSize) + 193 ((u_int64_t)firstAllocationBlock * (u_int64_t)kHFSBlockSize) + 194 (u_int64_t)(2 * kHFSBlockSize); 195 196 status = media->read(media, bufferReadAt, buffer); 197 if ( status != kIOReturnSuccess ) break; 198 } 199 200 /* 201 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX 202 * volumes (including wrapped HFS Plus). Verify the signature and grab the 203 * UUID from the Finder Info. 204 */ 205 if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord) { 206 bcopy((void *)&mdbPtr->drFndrInfo[6], volumeUUIDPtr->bytes, kVolumeUUIDValueLength); 207 status = kIOReturnSuccess; 208 209 } else if (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSPlusSigWord || 210 OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSXSigWord) { 211 bcopy((void *)&volHdrPtr->finderInfo[24], volumeUUIDPtr->bytes, kVolumeUUIDValueLength); 212 status = kIOReturnSuccess; 213 } else { 214 // status = 0 from earlier successful media->read() 215 status = kIOReturnBadMedia; 216 } 217 218 } while (false); 219 220 if ( mediaIsOpen ) media->close(media); 221 if ( buffer ) buffer->release(); 222 223 DEBUG_LOG("%s::%s finishes with status %d\n", kClassName, __func__, status); 224 225 return status; 226} 227 228//------------------------------------------ 229 230bool 231AppleFileSystemDriver::mediaNotificationHandler( 232 void * target, void * ref, 233 IOService * service, 234 IONotifier * notifier) 235{ 236 AppleFileSystemDriver * fs; 237 IOMedia * media; 238 IOReturn status = kIOReturnError; 239 OSString * contentHint; 240 const char * contentStr; 241 VolumeUUID volumeUUID; 242 OSString * uuidProperty; 243 uuid_t uuid; 244 bool matched = false; 245 bool isRAID = false; 246 247 DEBUG_LOG("%s[%p]::%s -> '%s'\n", kClassName, target, __func__, service->getName()); 248 249 do { 250 fs = OSDynamicCast( AppleFileSystemDriver, (IOService *)target ); 251 if (fs == 0) break; 252 253 media = OSDynamicCast( IOMedia, service ); 254 if (media == 0) break; 255 256 // i.e. does it know how big it is / have a block size 257 if ( media->isFormatted() == false ) break; 258 259 // the RAID might not be ready yet :P 260 isRAID = (media->getProperty(kAppleRAIDIsRAIDKey) == kOSBooleanTrue); 261 if (isRAID) { 262 IOStorage *provider; 263 OSString *status; 264 265 if (!(provider = media->getProvider())) goto notraid; 266 if (!(status = OSDynamicCast(OSString, 267 provider->getProperty(kAppleRAIDStatusKey)))) goto notraid; 268 269 // if it decides to start working later, we'll get another shot 270 if (!status->isEqualTo(kAppleRAIDStatusDegraded) && 271 !status->isEqualTo(kAppleRAIDStatusOnline)) { 272 DEBUG_LOG("skipping prematurely-available RAID device"); 273 break; 274 } 275 } 276 notraid: 277 278 // If the media already has a UUID property, try that first. 279 uuidProperty = OSDynamicCast( OSString, media->getProperty("UUID") ); 280 if (uuidProperty != NULL) { 281 if (fs->_uuidString && uuidProperty->isEqualTo(fs->_uuidString)) { 282 VERBOSE_LOG("existing UUID property matched\n"); 283 matched = true; 284 break; 285 } 286 } 287 288 // only IOMedia's with content hints (perhaps empty) are interesting 289 contentHint = OSDynamicCast( OSString, media->getProperty(kIOMediaContentHintKey) ); 290 if (contentHint == NULL) break; 291 contentStr = contentHint->getCStringNoCopy(); 292 if (contentStr == NULL) break; 293 294 // probe based on content hint, but if the hint is 295 // empty and we see RAID, probe for anything we support 296 if ( strcmp(contentStr, "Apple_HFS" ) == 0 || 297 strcmp(contentStr, "Apple_HFSX" ) == 0 || 298 strcmp(contentStr, "Apple_Boot" ) == 0 || 299 strcmp(contentStr, "Apple_Recovery" ) == 0 || 300 strcmp(contentStr, "48465300-0000-11AA-AA11-00306543ECAC" ) == 0 || /* APPLE_HFS_UUID */ 301 strcmp(contentStr, "426F6F74-0000-11AA-AA11-00306543ECAC" ) == 0 || /* APPLE_BOOT_UUID */ 302 strcmp(contentStr, "5265636F-7665-11AA-AA11-00306543ECAC" ) == 0 ) { /* APPLE_RECOVERY_UUID */ 303 status = readHFSUUID( media, (void **)&volumeUUID ); 304 } else if (strlen(contentStr) == 0 && isRAID) { 305 // RAIDv1 has a content hint but is empty 306 status = readHFSUUID( media, (void **)&volumeUUID ); 307 } else { 308 break; 309 } 310 311 if (status != kIOReturnSuccess) { 312 break; 313 } 314 315 if (createUUIDFromName( kFSUUIDNamespaceSHA1, 316 volumeUUID.bytes, kVolumeUUIDValueLength, 317 uuid ) != kIOReturnSuccess) { 318 break; 319 } 320 321#if VERBOSE 322 OSString *str = createStringFromUUID(uuid); 323 OSString *bsdn = OSDynamicCast(OSString,media->getProperty("BSD Name")); 324 if (str) { 325 IOLog(" UUID %s found on volume '%s' (%s)\n", str->getCStringNoCopy(), 326 media->getName(), bsdn ? bsdn->getCStringNoCopy():""); 327 str->release(); 328 } 329#endif 330 331 if (fs->_uuid) { 332 if ( uuid_compare(uuid, fs->_uuid) == 0 ) { 333 VERBOSE_LOG(" UUID matched on volume %s\n", media->getName()); 334 matched = true; 335 } 336 } 337 338 } while (false); 339 340 if (matched) { 341 342 // prevent more notifications, if notifier is available 343 if (fs->_notifier != NULL) { 344 fs->_notifier->remove(); 345 fs->_notifier = NULL; 346 } 347 348 DEBUG_LOG("%s::%s publishing boot-uuid-media '%s'\n", kClassName, __func__, media->getName()); 349 IOService::publishResource( kBootUUIDMediaKey, media ); 350 351 // Now that our job is done, get rid of the matching property 352 // and kill the driver. 353 fs->getResourceService()->removeProperty( kBootUUIDKey ); 354 fs->terminate( kIOServiceRequired ); 355 356 VERBOSE_LOG("%s[%p]::%s returning TRUE\n", kClassName, target, __func__); 357 358 return true; 359 } 360 361 DEBUG_LOG("%s[%p]::%s returning false\n", kClassName, target, __func__); 362#if DEBUG 363//IOSleep(5000); 364#endif 365 return false; 366} 367 368//------------------------------------------ 369 370bool 371AppleFileSystemDriver::start(IOService * provider) 372{ 373 OSDictionary * matching; 374 OSString * uuidString; 375 const char * uuidCString; 376 IOService * resourceService; 377 OSDictionary * dict; 378 379 380 DEBUG_LOG("%s[%p]::%s\n", getName(), this, __func__); 381 382 DEBUG_LOG("%s provider is '%s'\n", getName(), provider->getName()); 383 384 do { 385 resourceService = getResourceService(); 386 if (resourceService == 0) break; 387 388 uuidString = OSDynamicCast( OSString, resourceService->getProperty("boot-uuid") ); 389 if (uuidString) { 390 _uuidString = uuidString; 391 _uuidString->retain(); 392 uuidCString = uuidString->getCStringNoCopy(); 393 DEBUG_LOG("%s: got UUID string '%s'\n", getName(), uuidCString); 394 if (uuid_parse(uuidCString, _uuid) != 0) { 395 IOLog("%s: Invalid UUID string '%s'\n", getName(), uuidCString); 396 break; 397 } 398 } else { 399 IOLog("%s: Error getting boot-uuid property\n", getName()); 400 break; 401 } 402 403 // Match IOMedia objects matching our criteria (from kext .plist) 404 dict = OSDynamicCast( OSDictionary, getProperty( kMediaMatchKey ) ); 405 if (dict == 0) break; 406 407 dict = OSDictionary::withDictionary(dict); 408 if (dict == 0) break; 409 410 matching = IOService::serviceMatching( "IOMedia", dict ); 411 if ( matching == 0 ) 412 return false; 413 414 // Set up notification for newly-appearing devices. 415 // This will also notify us about existing devices. 416 417 _notifier = IOService::addMatchingNotification( gIOMatchedNotification, matching, 418 &mediaNotificationHandler, 419 this, 0 ); 420 matching->release(); 421 422 DEBUG_LOG("%s[%p]::%s finishes TRUE\n", getName(), this, __func__); 423 424 return true; 425 426 } while (false); 427 428 DEBUG_LOG("%s[%p]::%s finishes false\n", getName(), this, __func__); 429 430 return false; 431} 432 433//------------------------------------------ 434 435void 436AppleFileSystemDriver::free() 437{ 438 DEBUG_LOG("%s[%p]::%s\n", getName(), this, __func__); 439 440 if (_uuidString) _uuidString->release(); 441 if (_notifier) _notifier->remove(); 442 443 super::free(); 444} 445 446//------------------------------------------ 447 448 449