/* * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * BLInterpretEFIXMLRepresentationAsDevice.c * bless * * Created by Shantonu Sen on 12/2/05. * Copyright 2005 Apple Computer, Inc. All rights reserved. * */ #include #include #include #include #include #include "bless.h" #include "bless_private.h" static int checkForMatch(BLContextPtr context, CFDictionaryRef dict, char *bsdName); static CFUUIDRef copyVolUUIDFromDiskArb(BLContextPtr context, CFStringRef bsdName); int BLInterpretEFIXMLRepresentationAsDevice(BLContextPtr context, CFStringRef xmlString, char *bsdName) { CFArrayRef efiArray = NULL; CFIndex count, i; char buffer[1024]; int foundDevice = 0; if(!CFStringGetCString(xmlString, buffer, sizeof(buffer), kCFStringEncodingUTF8)) { return 1; } efiArray = IOCFUnserialize(buffer, kCFAllocatorDefault, 0, NULL); if(efiArray == NULL) { contextprintf(context, kBLLogLevelError, "Could not unserialize string\n"); return 2; } if(CFGetTypeID(efiArray) != CFArrayGetTypeID()) { CFRelease(efiArray); contextprintf(context, kBLLogLevelError, "Bad type in XML string\n"); return 2; } // for each entry, see if there's a volume UUID, or if IOMatch works count = CFArrayGetCount(efiArray); for(i=0; i < count; i++) { CFDictionaryRef dict = CFArrayGetValueAtIndex(efiArray, i); if(CFGetTypeID(dict) != CFDictionaryGetTypeID()) { CFRelease(efiArray); contextprintf(context, kBLLogLevelError, "Bad type in XML string\n"); return 2; } if(checkForMatch(context, dict, bsdName)) { foundDevice = 1; break; } } if(!foundDevice) { contextprintf(context, kBLLogLevelVerbose, "Could not find disk device for string\n"); return 4; } return 0; } static int checkForMatch(BLContextPtr context, CFDictionaryRef dict, char *bsdName) { CFStringRef fsuuid; CFUUIDRef uuid = NULL; io_service_t service; int foundDevice = 0; // first check for volume UUID. if it's present at all, it's preferred fsuuid = CFDictionaryGetValue(dict, CFSTR("BLVolumeUUID")); if(fsuuid && CFGetTypeID(fsuuid) == CFStringGetTypeID()) { uuid = CFUUIDCreateFromString(kCFAllocatorDefault, fsuuid); if(uuid == NULL) { contextprintf(context, kBLLogLevelVerbose, "Bad Volume UUID\n"); } } if(uuid) { CFStringRef lastBSDName = CFDictionaryGetValue(dict, CFSTR("BLLastBSDName")); // use this as a cache. eventually we may want to scan DiskArb's list of volumes if(lastBSDName && CFGetTypeID(lastBSDName) == CFStringGetTypeID()) { CFUUIDRef dauuid = NULL; dauuid = copyVolUUIDFromDiskArb(context, lastBSDName); if(dauuid && CFEqual(uuid, dauuid)) { // found it! CFStringGetCString(lastBSDName, bsdName, MNAMELEN, kCFStringEncodingUTF8); contextprintf(context, kBLLogLevelVerbose, "Found device: %s\n", bsdName); foundDevice = 1; CFRelease(dauuid); } } } if (!foundDevice) { // not present, let's hope the matching dictionary was unique enough CFDictionaryRef iomatch = CFDictionaryGetValue(dict, CFSTR("IOMatch")); if(iomatch && CFGetTypeID(iomatch) == CFDictionaryGetTypeID()) { service = IOServiceGetMatchingService(kIOMasterPortDefault,iomatch); if(service != IO_OBJECT_NULL) { CFStringRef name = NULL; if(!IOObjectConformsTo(service,kIOMediaClass)) { contextprintf(context, kBLLogLevelVerbose, "found service but it is not a media object\n"); } else { name = IORegistryEntryCreateCFProperty(service, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); if(name && CFGetTypeID(name) == CFStringGetTypeID()) { // if we had a volume uuid, validate it! if(uuid) { CFUUIDRef dauuid = NULL; dauuid = copyVolUUIDFromDiskArb(context, name); if(dauuid && CFEqual(uuid, dauuid)) { foundDevice = 1; CFRelease(dauuid); } else { // we had a UUID, but this disk didn't, or was incorrect foundDevice = 0; } } else { // we don't have a volume UUID, so assume this is enough foundDevice = 1; } if(foundDevice) { CFStringGetCString(name, bsdName, MNAMELEN,kCFStringEncodingUTF8); contextprintf(context, kBLLogLevelVerbose, "Found device: %s\n", bsdName); } } if(name) CFRelease(name); } IOObjectRelease(service); } } } if(uuid) { CFRelease(uuid); } if(foundDevice) { return 1; } else { return 0; } } static CFUUIDRef copyVolUUIDFromDiskArb(BLContextPtr context, CFStringRef bsdName) { DASessionRef session = NULL; DADiskRef dadisk = NULL; CFUUIDRef dauuid = NULL; char lastBSDNameCString[MNAMELEN]; CFStringGetCString(bsdName, lastBSDNameCString, sizeof(lastBSDNameCString),kCFStringEncodingUTF8); session = DASessionCreate(kCFAllocatorDefault); if(session) { dadisk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, lastBSDNameCString); if(dadisk) { CFDictionaryRef descrip = DADiskCopyDescription(dadisk); if(descrip) { dauuid = CFDictionaryGetValue(descrip, kDADiskDescriptionVolumeUUIDKey); if(dauuid) CFRetain(dauuid); CFRelease(descrip); } CFRelease(dadisk); } CFRelease(session); } return dauuid; }