1/*
2 * Copyright (c) 2005-2007 Apple 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 *  BLInterpretEFIXMLRepresentationAsDevice.c
25 *  bless
26 *
27 *  Created by Shantonu Sen on 12/2/05.
28 *  Copyright 2005-2007 Apple Inc. All Rights Reserved.
29 *
30 */
31
32#include <IOKit/IOKitLib.h>
33#include <IOKit/IOCFUnserialize.h>
34#include <IOKit/storage/IOMedia.h>
35#include <IOKit/IOBSD.h>
36
37#include "bless.h"
38#include "bless_private.h"
39
40#if USE_DISKARBITRATION
41#include <DiskArbitration/DiskArbitration.h>
42#endif
43
44static int checkForMatch(BLContextPtr context, CFDictionaryRef dict,
45						 char *bsdName, int bsdNameLen);
46
47static CFUUIDRef    copyVolUUIDFromDiskArb(BLContextPtr context,
48                                          CFStringRef bsdName);
49
50int BLInterpretEFIXMLRepresentationAsDevice(BLContextPtr context,
51                                            CFStringRef xmlString,
52                                            char *bsdName,
53                                            int bsdNameLen)
54{
55	CFArrayRef  efiArray = NULL;
56    CFIndex     count, i;
57    char        buffer[1024];
58	int			foundDevice = 0;
59
60    if(!CFStringGetCString(xmlString, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
61        return 1;
62    }
63
64    efiArray = IOCFUnserialize(buffer,
65                               kCFAllocatorDefault,
66                               0,
67                               NULL);
68    if(efiArray == NULL) {
69        contextprintf(context, kBLLogLevelError, "Could not unserialize string\n");
70        return 2;
71    }
72
73    if(CFGetTypeID(efiArray) != CFArrayGetTypeID()) {
74        CFRelease(efiArray);
75        contextprintf(context, kBLLogLevelError, "Bad type in XML string\n");
76        return 2;
77    }
78
79    // for each entry, see if there's a volume UUID, or if IOMatch works
80    count = CFArrayGetCount(efiArray);
81    for(i=0; i < count; i++) {
82        CFDictionaryRef dict = CFArrayGetValueAtIndex(efiArray, i);
83
84        if(CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
85            CFRelease(efiArray);
86            contextprintf(context, kBLLogLevelError, "Bad type in XML string\n");
87            return 2;
88        }
89
90        if(checkForMatch(context, dict, bsdName, bsdNameLen)) {
91			foundDevice = 1;
92			break;
93		}
94    }
95
96    CFRelease(efiArray);
97
98	if(!foundDevice) {
99		contextprintf(context, kBLLogLevelVerbose, "Could not find disk device for string\n");
100		return 4;
101	}
102
103    return 0;
104}
105
106
107static int checkForMatch(BLContextPtr context, CFDictionaryRef dict,
108						 char *bsdName, int bsdNameLen)
109{
110	CFStringRef		fsuuid;
111	CFUUIDRef		uuid = NULL;
112	io_service_t	service;
113	int				foundDevice = 0;
114
115	// first check for volume UUID. if it's present at all, it's preferred
116
117	fsuuid = CFDictionaryGetValue(dict, CFSTR("BLVolumeUUID"));
118	if(fsuuid && CFGetTypeID(fsuuid) == CFStringGetTypeID()) {
119		uuid = CFUUIDCreateFromString(kCFAllocatorDefault, fsuuid);
120
121		if(uuid == NULL) {
122			contextprintf(context, kBLLogLevelVerbose, "Bad Volume UUID\n");
123		}
124	}
125
126	if(uuid) {
127		CFStringRef	lastBSDName = CFDictionaryGetValue(dict, CFSTR("BLLastBSDName"));
128
129		// use this as a cache. eventually we may want to scan DiskArb's list of volumes
130		if(lastBSDName && CFGetTypeID(lastBSDName) == CFStringGetTypeID()) {
131			CFUUIDRef       dauuid = NULL;
132
133            dauuid = copyVolUUIDFromDiskArb(context, lastBSDName);
134
135			if(dauuid && CFEqual(uuid, dauuid)) {
136				// found it!
137                CFStringGetCString(lastBSDName, bsdName,
138                                   bsdNameLen, kCFStringEncodingUTF8);
139
140				contextprintf(context, kBLLogLevelVerbose, "Found device: %s\n", bsdName);
141				foundDevice = 1;
142
143				CFRelease(dauuid);
144			}
145		}
146    }
147
148    if (!foundDevice) {
149		// not present, let's hope the matching dictionary was unique enough
150		CFDictionaryRef iomatch = CFDictionaryGetValue(dict, CFSTR("IOMatch"));
151		if(iomatch && CFGetTypeID(iomatch) == CFDictionaryGetTypeID()) {
152
153			CFRetain(iomatch); // IOServiceGetMatchingService releases 1 ref
154			service = IOServiceGetMatchingService(kIOMasterPortDefault,iomatch);
155			if(service != IO_OBJECT_NULL) {
156
157				CFStringRef name = NULL;
158
159				if(!IOObjectConformsTo(service,kIOMediaClass)) {
160					contextprintf(context, kBLLogLevelVerbose, "found service but it is not a media object\n");
161				} else {
162
163					name = IORegistryEntryCreateCFProperty(service,
164														   CFSTR(kIOBSDNameKey),
165														   kCFAllocatorDefault,
166														   0);
167					if(name && CFGetTypeID(name) == CFStringGetTypeID()) {
168                        // if we had a volume uuid, validate it!
169                        if(uuid) {
170                            CFUUIDRef       dauuid = NULL;
171
172                            dauuid = copyVolUUIDFromDiskArb(context, name);
173
174                            if(dauuid && CFEqual(uuid, dauuid)) {
175                                foundDevice = 1;
176                                CFRelease(dauuid);
177                            } else {
178                                // we had a UUID, but this disk didn't, or was incorrect
179                                foundDevice = 0;
180                            }
181                        } else {
182                            // we don't have a volume UUID, so assume this is enough
183                            foundDevice = 1;
184                        }
185
186                        if(foundDevice) {
187                            CFStringGetCString(name, bsdName, bsdNameLen, kCFStringEncodingUTF8);
188                            contextprintf(context, kBLLogLevelVerbose, "Found device: %s\n", bsdName);
189                        }
190                    }
191
192					if(name) CFRelease(name);
193				}
194
195				IOObjectRelease(service);
196			}
197		}
198	}
199
200    if(uuid) {
201        CFRelease(uuid);
202    }
203
204	if(foundDevice) {
205		return 1;
206	} else {
207		return 0;
208	}
209}
210
211static CFUUIDRef    copyVolUUIDFromDiskArb(BLContextPtr context,
212                                           CFStringRef bsdName)
213{
214    CFUUIDRef       dauuid = NULL;
215#if USE_DISKARBITRATION
216    DASessionRef    session = NULL;
217    DADiskRef       dadisk = NULL;
218    char			lastBSDNameCString[MNAMELEN];
219
220    CFStringGetCString(bsdName, lastBSDNameCString,
221                       sizeof(lastBSDNameCString),kCFStringEncodingUTF8);
222
223    session = DASessionCreate(kCFAllocatorDefault);
224    if(session) {
225        dadisk = DADiskCreateFromBSDName(kCFAllocatorDefault, session,
226                                         lastBSDNameCString);
227        if(dadisk) {
228            CFDictionaryRef descrip = DADiskCopyDescription(dadisk);
229            if(descrip) {
230                dauuid = CFDictionaryGetValue(descrip, kDADiskDescriptionVolumeUUIDKey);
231
232                if(dauuid)
233                    CFRetain(dauuid);
234                CFRelease(descrip);
235            }
236
237            CFRelease(dadisk);
238        }
239
240        CFRelease(session);
241    }
242#endif // USE_DISKARBITRATION
243    return dauuid;
244}
245