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 *  BLUpdateRAIDBooters.c
25 *  bless
26 *
27 *  Created by Shantonu Sen on 1/15/05.
28 *  Copyright 2005-2007 Apple Inc. All Rights Reserved.
29 *
30 *  $Id: BLUpdateRAIDBooters.c,v 1.12 2006/02/20 22:49:58 ssen Exp $
31 *
32 */
33
34#include <stdlib.h>
35#include <unistd.h>
36#include <sys/param.h>
37
38#include <mach/mach_error.h>
39
40#include <IOKit/IOKitLib.h>
41#include <IOKit/IOBSD.h>
42#include <IOKit/IOKitKeys.h>
43#include <IOKit/storage/IOMedia.h>
44
45#include <CoreFoundation/CoreFoundation.h>
46
47#include "bless.h"
48#include "bless_private.h"
49
50#if SUPPORT_RAID
51
52#include <IOKit/storage/RAID/AppleRAIDUserLib.h>
53
54#if USE_DISKARBITRATION
55#include <DiskArbitration/DiskArbitration.h>
56#endif
57
58#define kBootPlistName "com.apple.Boot.plist"
59
60
61int updateRAIDMember(BLContextPtr context, mach_port_t iokitPort,
62					 CFDictionaryRef raidEntry, CFDataRef opaqueData,
63					 CFDataRef bootxData, CFDataRef labelData);
64int getExternalBooter(BLContextPtr context,
65                      mach_port_t iokitPort,
66					  io_service_t dataPartition,
67					  io_service_t *booterPartition);
68
69int updateAppleBoot(BLContextPtr context, const char *devname, CFDataRef opaqueData,
70					CFDataRef bootxData, CFDataRef labelData);
71
72CFDataRef _createLabel(BLContextPtr context, CFStringRef name, int index);
73
74int BLUpdateRAIDBooters(BLContextPtr context, const char * device,
75						CFTypeRef bootData,
76						CFDataRef bootxData, CFDataRef labelData)
77{
78	int ret = 0;
79	int anyFailed = 0;
80	CFDataRef xmlData = NULL;
81	CFStringRef		daName = NULL;
82
83	kern_return_t           kret;
84	mach_port_t             ourIOKitPort;
85
86
87	xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault, bootData);
88	if(xmlData == NULL) {
89		contextprintf(context, kBLLogLevelError,  "Error generating XML data\n");
90		return 1;
91	}
92
93	contextprintf(context, kBLLogLevelVerbose,  "RAID XML data is:\n%s\n",
94				  CFDataGetBytePtr(xmlData));
95
96
97	// Obtain the I/O Kit communication handle.
98    if((kret = IOMasterPort(bootstrap_port, &ourIOKitPort)) != KERN_SUCCESS) {
99		return 2;
100    }
101
102	for(;;) {
103		// try to get a symbolic name from DA, if possible
104		if(labelData == NULL) {
105#if USE_DISKARBITRATION
106			DADiskRef disk = NULL;
107			DASessionRef session = NULL;
108			CFDictionaryRef props = NULL;
109
110			session = DASessionCreate(kCFAllocatorDefault);
111			if(session == NULL) {
112				contextprintf(context, kBLLogLevelVerbose, "Can't connect to DiskArb\n");
113				break;
114			}
115
116			disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, device+5);
117			if(disk == NULL) {
118				CFRelease(session);
119				contextprintf(context, kBLLogLevelVerbose, "Can't create DADisk for %s\n",
120							  device + 5);
121				break;
122			}
123
124			props = DADiskCopyDescription(disk);
125			if(props == NULL) {
126				CFRelease(session);
127				CFRelease(disk);
128				contextprintf(context, kBLLogLevelVerbose, "Can't get properties for %s\n",
129							  device + 5);
130				break;
131			}
132
133			daName = CFDictionaryGetValue(props, kDADiskDescriptionVolumeNameKey);
134			if(daName) CFRetain(daName);
135
136			CFRelease(props);
137			CFRelease(disk);
138			CFRelease(session);
139#else // !USE_DISKARBITRATION
140			daName = CFSTR("RAID");
141#endif // !USE_DISKARBITRATION
142		}
143		break;
144	}
145
146
147	if(CFGetTypeID(bootData) == CFArrayGetTypeID()) {
148		CFIndex i, count = CFArrayGetCount(bootData);
149
150		for(i=0; i < count; i++) {
151			CFDictionaryRef dict = CFArrayGetValueAtIndex(bootData,i);
152			CFDataRef	tempLabel = NULL;
153
154			if(!labelData && daName) {
155				tempLabel = _createLabel(context, daName, i+1);
156			}
157
158			ret = updateRAIDMember(context, ourIOKitPort, dict, xmlData,
159								   bootxData, labelData ? labelData : tempLabel);
160			if(ret) {
161				anyFailed = 1;
162				// keep going to update other booters
163			}
164
165			if(tempLabel) CFRelease(tempLabel);
166		}
167	} else {
168		CFDataRef	tempLabel = NULL;
169
170		if(!labelData && daName) {
171			tempLabel = _createLabel(context, daName, 1);
172		}
173
174		ret = updateRAIDMember(context, ourIOKitPort,
175							   (CFDictionaryRef)bootData, xmlData,
176							   bootxData, labelData ? labelData : tempLabel);
177		if(ret) {
178			anyFailed = 1;
179			// keep going to update other booters
180		}
181
182		if(tempLabel) CFRelease(tempLabel);
183}
184	CFRelease(xmlData);
185
186	if(anyFailed) {
187		return 1;
188	} else {
189		return 0;
190	}
191}
192
193int updateRAIDMember(BLContextPtr context, mach_port_t iokitPort,
194					 CFDictionaryRef raidEntry, CFDataRef opaqueData,
195					 CFDataRef bootxData, CFDataRef labelData)
196{
197    int ret;
198    CFStringRef path;
199    io_string_t	cpath;
200    io_service_t	service;
201    io_service_t	booter;
202
203    CFStringRef name = NULL;
204    CFStringRef content = NULL;
205    char cname[MAXPATHLEN];
206
207
208    path = CFDictionaryGetValue(raidEntry, CFSTR(kIOBootDevicePathKey));
209    if(path == NULL) return 1;
210
211    if(!CFStringGetCString(path,cpath,sizeof(cpath),kCFStringEncodingUTF8))
212        return 2;
213
214    contextprintf(context, kBLLogLevelVerbose,  "Updating booter data for %s\n", cpath);
215
216    service = IORegistryEntryFromPath(iokitPort, cpath);
217    if(service == 0) {
218        contextprintf(context, kBLLogLevelVerbose,  "Could not find service for \"%s\"\n", cpath);
219        return 3;
220    }
221
222    content = (CFStringRef)IORegistryEntryCreateCFProperty(
223                                                        service,
224                                                        CFSTR(kIOMediaContentKey),
225                                                        kCFAllocatorDefault,
226                                                        0);
227
228    if(content) {
229        if(CFStringGetTypeID() == CFGetTypeID(content)
230           && CFEqual(content, CFSTR("Apple_Boot_RAID"))) {
231
232            contextprintf(context, kBLLogLevelVerbose,  "Member at \"%s\" is RAIDv1 Apple_Boot_RAID partition. Ignoring...\n", cpath);
233
234            CFRelease(content);
235            IOObjectRelease(service);
236            return 0;
237        }
238
239        CFRelease(content);
240    }
241
242    ret = getExternalBooter(context, iokitPort, service, &booter);
243    if(ret) {
244        IOObjectRelease(service);
245        return 4;
246    }
247
248    name = (CFStringRef)IORegistryEntryCreateCFProperty(
249                                                        booter,
250                                                        CFSTR(kIOBSDNameKey),
251                                                        kCFAllocatorDefault,
252                                                        0);
253
254    if(!CFStringGetCString(name, cname, sizeof(cname), kCFStringEncodingUTF8)) {
255        CFRelease(name);
256        IOObjectRelease(booter);
257        IOObjectRelease(service);
258        return 5;
259    }
260
261    CFRelease(name);
262
263    contextprintf(context, kBLLogLevelVerbose,  "Booter partition is %s\n", cname);
264    ret = updateAppleBoot(context, cname, opaqueData,
265						  bootxData, labelData);
266    if(ret) {
267        IOObjectRelease(booter);
268        IOObjectRelease(service);
269        return 6;
270    }
271
272    IOObjectRelease(booter);
273    IOObjectRelease(service);
274
275    return 0;
276}
277
278int updateAppleBoot(BLContextPtr context, const char *devname, CFDataRef opaqueData,
279					CFDataRef bootxData, CFDataRef labelData)
280{
281
282    int         status;
283	char			device[MAXPATHLEN];
284	BLUpdateBooterFileSpec	*spec = NULL;
285	BLUpdateBooterFileSpec	*pspec, *xspec, *lspec1, *lspec2;
286	int32_t					specCount = 0, currentCount = 0;
287
288	pspec = xspec = lspec1 = lspec2 = NULL;
289
290	sprintf(device, "/dev/%s", devname);
291
292	specCount = 1; // plist
293	if(labelData) specCount += 2;
294	if(bootxData) specCount += 1;
295
296	spec = calloc(specCount, sizeof(spec[0]));
297
298
299	pspec = &spec[0];
300
301	pspec->version = 0;
302	pspec->reqType = 0; // no type
303	pspec->reqCreator = 0; // no type
304	pspec->reqParentDir = 0;
305	pspec->reqFilename = kBootPlistName;
306	pspec->payloadData = opaqueData;
307	pspec->postType = 0; // no type
308	pspec->postCreator = 0; // no type
309	pspec->foundFile = 0;
310	pspec->updatedFile = 0;
311
312	currentCount++;
313
314	if(labelData) {
315		lspec1 = &spec[currentCount];
316		lspec2 = &spec[currentCount+1];
317
318		lspec1->version = 0;
319		lspec1->reqType = kBL_OSTYPE_PPC_TYPE_OFLABEL;
320		lspec1->reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP;
321		lspec1->reqParentDir = 0;
322		lspec1->reqFilename = NULL;
323		lspec1->payloadData = labelData;
324		lspec1->postType = 0; // no type
325		lspec1->postCreator = 0; // no type
326		lspec1->foundFile = 0;
327		lspec1->updatedFile = 0;
328
329		lspec2->version = 0;
330		lspec2->reqType = kBL_OSTYPE_PPC_TYPE_OFLABEL_PLACEHOLDER;
331		lspec2->reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP;
332		lspec2->reqParentDir = 0;
333		lspec2->reqFilename = NULL;
334		lspec2->payloadData = labelData;
335		lspec2->postType = kBL_OSTYPE_PPC_TYPE_OFLABEL;
336		lspec2->postCreator = 0; // no type
337		lspec2->foundFile = 0;
338		lspec2->updatedFile = 0;
339
340
341		currentCount += 2;
342	}
343
344	if(bootxData) {
345		xspec = &spec[currentCount];
346
347		xspec->version = 0;
348		xspec->reqType = kBL_OSTYPE_PPC_TYPE_BOOTX;
349		xspec->reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP;
350		xspec->reqParentDir = 0;
351		xspec->reqFilename = NULL;
352		xspec->payloadData = bootxData;
353		xspec->postType = 0; // no type
354		xspec->postCreator = 0; // no type
355		xspec->foundFile = 0;
356		xspec->updatedFile = 0;
357
358		currentCount++;
359	}
360
361	status = BLUpdateBooter(context, device, spec, specCount);
362	if(status) {
363		contextprintf(context, kBLLogLevelError,  "Error enumerating HFS+ volume\n");
364		return 1;
365	}
366
367	if(bootxData) {
368		if(!(xspec->foundFile)) {
369			contextprintf(context, kBLLogLevelError,  "No pre-existing BootX found in HFS+ volume\n");
370			return 2;
371		}
372
373		if(!(xspec->updatedFile)) {
374			contextprintf(context, kBLLogLevelError,  "BootX was not updated\n");
375			return 3;
376		}
377	}
378
379    if(labelData) {
380		if(!(lspec1->foundFile || lspec2->foundFile)) {
381			contextprintf(context, kBLLogLevelError,  "No pre-existing OF label found in HFS+ volume\n");
382			return 2;
383		}
384		if(!(lspec1->updatedFile || lspec2->updatedFile)) {
385			contextprintf(context, kBLLogLevelError,  "OF label was not updated\n");
386			return 3;
387		}
388	}
389
390    if(!pspec->foundFile) {
391		contextprintf(context, kBLLogLevelError,  "No pre-existing Plist found in HFS+ volume\n");
392		return 1;
393    }
394    if(!pspec->updatedFile) {
395		contextprintf(context, kBLLogLevelError,  "Plist was not updated\n");
396		return 2;
397    }
398
399    return 0;
400}
401
402CFDataRef _createLabel(BLContextPtr context, CFStringRef name, int index)
403{
404	CFDataRef newLabel = NULL;
405	char label[MAXPATHLEN];
406	int ret;
407	CFStringRef nameWithNum = NULL;
408
409	nameWithNum = CFStringCreateWithFormat(kCFAllocatorDefault,NULL,
410										   CFSTR("%@ %d"), name, index);
411	if(nameWithNum == NULL)
412	   return NULL;
413
414	if(!CFStringGetCString(nameWithNum, label, sizeof(label),
415						   kCFStringEncodingUTF8)) {
416		CFRelease(nameWithNum);
417		return NULL;
418	}
419
420	CFRelease(nameWithNum);
421
422	ret = BLGenerateLabelData(context, label, kBitmapScale_1x, &newLabel);
423
424	if(ret) {
425		return NULL;
426	} else {
427		return newLabel;
428	}
429}
430
431#endif // SUPPORT_RAID
432