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 *  BLCreateEFIXMLRepresentationForPath.c
25 *  bless
26 *
27 *  Created by Shantonu Sen on 11/9/05.
28 *  Copyright 2005-2007 Apple Inc. All Rights Reserved.
29 *
30 */
31
32#import <IOKit/IOKitLib.h>
33#import <IOKit/IOCFSerialize.h>
34#import <IOKit/IOBSD.h>
35#import <IOKit/IOKitKeys.h>
36#import <IOKit/storage/IOMedia.h>
37
38#import <CoreFoundation/CoreFoundation.h>
39
40#include <string.h>
41#include <sys/param.h>
42#include <sys/stat.h>
43
44#include "bless.h"
45#include "bless_private.h"
46
47#if USE_DISKARBITRATION
48#include <DiskArbitration/DiskArbitration.h>
49#endif
50
51int addMatchingInfoForBSDName(BLContextPtr context,
52			      mach_port_t masterPort,
53			      CFMutableDictionaryRef dict,
54			      const char *bsdName,
55                  bool shortForm);
56
57int BLCreateEFIXMLRepresentationForPath(BLContextPtr context,
58                                        const char *path,
59                                        const char *optionalData,
60                                        CFStringRef *xmlString,
61                                        bool shortForm)
62{
63    char fullpath[MAXPATHLEN];
64    struct statfs sb;
65    int ret;
66    int i;
67    size_t slen;
68    mach_port_t masterPort;
69    kern_return_t kret;
70
71    CFDataRef xmlData;
72    CFMutableDictionaryRef dict;
73    CFMutableArrayRef array;
74
75    const UInt8 *xmlBuffer;
76    UInt8 *outBuffer;
77    CFIndex count;
78
79    CFStringRef pathString;
80
81    kret = IOMasterPort(MACH_PORT_NULL, &masterPort);
82    if(kret) return 1;
83
84    if(NULL == realpath(path, fullpath)) {
85
86        contextprintf(context, kBLLogLevelError, "Can't resolve full path for %s\n",
87                   path);
88        return 1;
89    }
90
91    ret = blsustatfs(fullpath, &sb);
92    if(ret) {
93        contextprintf(context, kBLLogLevelError, "Can't statfs %s\n",
94                      fullpath);
95        return 2;
96    }
97
98    if(0 != strncmp(fullpath, sb.f_mntonname, strlen(sb.f_mntonname))) {
99        return 3;
100    }
101
102    // if fullpath was actually the path to the mountpoint,
103    // don't add a path component to the XML dict
104    if(0 != strcmp(fullpath, sb.f_mntonname)) {
105
106        if(strlen(sb.f_mntonname) > 1) {
107            memmove(fullpath, fullpath+strlen(sb.f_mntonname),
108                    strlen(fullpath)-strlen(sb.f_mntonname)+1);
109        }
110
111        slen = strlen(fullpath);
112        for(i=0; i < slen; i++) {
113            if(fullpath[i] == '/')
114                fullpath[i] = '\\';
115        }
116
117        pathString = CFStringCreateWithCString(kCFAllocatorDefault, fullpath, kCFStringEncodingUTF8);
118
119        contextprintf(context, kBLLogLevelVerbose, "Relative path of %s is %s\n",
120                      path, fullpath);
121
122    } else {
123        pathString = NULL;
124
125        contextprintf(context, kBLLogLevelVerbose, "Path to mountpoint given: %s\n",
126                      fullpath);
127    }
128
129    array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
130
131    dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
132                                     &kCFTypeDictionaryValueCallBacks);
133
134    ret = addMatchingInfoForBSDName(context, masterPort, dict, sb.f_mntfromname+strlen("/dev/"), shortForm);
135    if(ret) {
136      CFRelease(dict);
137      CFRelease(array);
138      return 2;
139    }
140    CFArrayAppendValue(array, dict);
141    CFRelease(dict);
142
143    if(pathString) {
144        dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
145                                         &kCFTypeDictionaryValueCallBacks);
146        CFDictionaryAddValue(dict, CFSTR("IOEFIDevicePathType"),
147                             CFSTR("MediaFilePath"));
148        CFDictionaryAddValue(dict, CFSTR("Path"),
149                             pathString);
150        CFArrayAppendValue(array, dict);
151        CFRelease(dict);
152
153        CFRelease(pathString);
154    }
155
156    if(optionalData) {
157        CFStringRef optString = CFStringCreateWithCString(kCFAllocatorDefault, optionalData, kCFStringEncodingUTF8);
158
159        dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
160                                         &kCFTypeDictionaryValueCallBacks);
161        CFDictionaryAddValue(dict, CFSTR("IOEFIBootOption"),
162                             optString);
163        CFArrayAppendValue(array, dict);
164        CFRelease(dict);
165
166        CFRelease(optString);
167    }
168
169    xmlData = IOCFSerialize(array, 0);
170    CFRelease(array);
171
172    if(xmlData == NULL) {
173        contextprintf(context, kBLLogLevelError, "Can't create XML representation\n");
174        return 2;
175    }
176
177    count = CFDataGetLength(xmlData);
178    xmlBuffer = CFDataGetBytePtr(xmlData);
179    outBuffer = calloc(count+1, sizeof(char)); // terminate
180
181    memcpy(outBuffer, xmlBuffer, count);
182    CFRelease(xmlData);
183
184    *xmlString = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)outBuffer, kCFStringEncodingUTF8);
185
186    free(outBuffer);
187
188    return 0;
189}
190
191// first look up the media object, then create a
192// custom matching dictionary that should be persistent
193// from boot to boot
194int addMatchingInfoForBSDName(BLContextPtr context,
195			      mach_port_t masterPort,
196			      CFMutableDictionaryRef dict,
197			      const char *bsdName,
198                  bool shortForm)
199{
200    io_service_t                media = IO_OBJECT_NULL, checkMedia = IO_OBJECT_NULL;
201    CFStringRef                 uuid = NULL;
202    CFMutableDictionaryRef      propDict = NULL;
203    kern_return_t               kret;
204    CFStringRef			lastBSDName = NULL;
205
206    lastBSDName = CFStringCreateWithCString(kCFAllocatorDefault,
207					    bsdName,
208					    kCFStringEncodingUTF8);
209
210    propDict = IOBSDNameMatching(masterPort, 0, bsdName);
211    CFDictionarySetValue(propDict, CFSTR(kIOProviderClassKey), CFSTR(kIOMediaClass));
212
213    media = IOServiceGetMatchingService(masterPort,
214                                        propDict);
215    propDict = NULL;
216
217    if(media == IO_OBJECT_NULL) {
218        contextprintf(context, kBLLogLevelError, "Could not find object for %s\n", bsdName);
219        CFRelease(lastBSDName);
220        return 1;
221    }
222
223    uuid = IORegistryEntryCreateCFProperty(media, CFSTR(kIOMediaUUIDKey),
224                                           kCFAllocatorDefault, 0);
225    if(uuid == NULL) {
226        CFUUIDRef       fsuuid = NULL;
227		CFStringRef     fsuuidstr = NULL;
228		io_string_t path;
229#if USE_DISKARBITRATION
230        DASessionRef    session = NULL;
231        DADiskRef       dadisk = NULL;
232
233        contextprintf(context, kBLLogLevelVerbose, "IOMedia %s does not have a partition %s\n",
234                      bsdName, kIOMediaUUIDKey);
235
236        session = DASessionCreate(kCFAllocatorDefault);
237        if(session) {
238            dadisk = DADiskCreateFromIOMedia(kCFAllocatorDefault, session,
239                                             media);
240            if(dadisk) {
241                CFDictionaryRef descrip = DADiskCopyDescription(dadisk);
242                if(descrip) {
243                    fsuuid = CFDictionaryGetValue(descrip, kDADiskDescriptionVolumeUUIDKey);
244
245                    if(fsuuid)
246                        CFRetain(fsuuid);
247                    CFRelease(descrip);
248                }
249
250                CFRelease(dadisk);
251            }
252
253            CFRelease(session);
254        }
255#endif // USE_DISKARBITRATION
256
257        if(fsuuid) {
258            char        fsuuidCString[64];
259
260            fsuuidstr = CFUUIDCreateString(kCFAllocatorDefault, fsuuid);
261
262            CFStringGetCString(fsuuidstr,fsuuidCString,sizeof(fsuuidCString),kCFStringEncodingUTF8);
263
264            contextprintf(context, kBLLogLevelVerbose, "DADiskRef %s has Volume UUID %s\n",
265                          bsdName, fsuuidCString);
266
267            CFRelease(fsuuid);
268		} else {
269            contextprintf(context, kBLLogLevelVerbose, "IOMedia %s does not have a Volume UUID\n",
270                          bsdName);
271		}
272
273
274		// we have a volume UUID, but our primary matching mechanism will be the device path
275
276		kret = IORegistryEntryGetPath(media, kIODeviceTreePlane,path);
277		if(kret) {
278			contextprintf(context, kBLLogLevelVerbose, "IOMedia %s does not have device tree path\n",
279						  bsdName);
280
281			propDict = IOServiceMatching(kIOMediaClass);
282			CFDictionaryAddValue(propDict,  CFSTR(kIOBSDNameKey), lastBSDName);
283
284			// add UUID as hint
285			if(fsuuidstr)
286				CFDictionaryAddValue(dict, CFSTR("BLVolumeUUID"), fsuuidstr);
287
288		} else {
289			CFStringRef blpath = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
290
291			contextprintf(context, kBLLogLevelVerbose, "IOMedia %s has path %s\n",
292						  bsdName, path);
293
294			propDict = IOServiceMatching(kIOMediaClass);
295			CFDictionaryAddValue(propDict, CFSTR(kIOPathMatchKey), blpath);
296			CFRelease(blpath);
297
298			// add UUID as hint
299			if(fsuuidstr)
300				CFDictionaryAddValue(dict, CFSTR("BLVolumeUUID"), fsuuidstr);
301
302			CFDictionaryAddValue(dict, CFSTR("BLLastBSDName"), lastBSDName);
303		}
304
305		if(fsuuidstr) {
306			CFRelease(fsuuidstr);
307		}
308
309    } else {
310      CFMutableDictionaryRef propMatch;
311
312        contextprintf(context, kBLLogLevelVerbose, "IOMedia %s has UUID %s\n",
313                      bsdName, BLGetCStringDescription(uuid));
314
315        propMatch = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
316                              &kCFTypeDictionaryKeyCallBacks,
317                              &kCFTypeDictionaryValueCallBacks);
318        CFDictionaryAddValue(propMatch, CFSTR(kIOMediaUUIDKey), uuid);
319
320        propDict = IOServiceMatching(kIOMediaClass);
321        CFDictionaryAddValue(propDict,  CFSTR(kIOPropertyMatchKey), propMatch);
322        CFRelease(propMatch);
323
324        // add a hint to the top-level dict
325        CFDictionaryAddValue(dict, CFSTR("BLLastBSDName"), lastBSDName);
326
327        CFRelease(uuid);
328    }
329
330    // verify the dictionary matches
331    CFRetain(propDict); // consumed below
332    checkMedia = IOServiceGetMatchingService(masterPort,
333					     propDict);
334
335    if(IO_OBJECT_NULL == checkMedia
336       || !IOObjectIsEqualTo(media, checkMedia)) {
337      contextprintf(context, kBLLogLevelVerbose, "Inconsistent registry entries for %s\n",
338		    bsdName);
339
340      if(IO_OBJECT_NULL != checkMedia) IOObjectRelease(checkMedia);
341      IOObjectRelease(media);
342      CFRelease(lastBSDName);
343      CFRelease(propDict);
344
345      return 2;
346    }
347
348    IOObjectRelease(checkMedia);
349    IOObjectRelease(media);
350
351    CFDictionaryAddValue(dict, CFSTR("IOMatch"), propDict);
352    CFRelease(lastBSDName);
353    CFRelease(propDict);
354
355    if(shortForm) {
356        CFDictionaryAddValue(dict, CFSTR("IOEFIShortForm"), kCFBooleanTrue);
357    }
358
359    return 0;
360}
361