1/*
2 * Copyright (c) 2006-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 *  BLInterpretEFIXMLRepresentationAsLegacyDevice.c
25 *  bless
26 *
27 *  Created by Shantonu Sen on 2/8/06.
28 *  Copyright 2006-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 <sys/mount.h>
38#include <stdbool.h>
39
40#include "bless.h"
41#include "bless_private.h"
42
43static int findMatch(BLContextPtr context, CFStringRef legacyType,
44						 CFStringRef xmlString, char *bsdName, int bsdNameLen);
45
46int BLInterpretEFIXMLRepresentationAsLegacyDevice(BLContextPtr context,
47                                            CFStringRef xmlString,
48                                            char *bsdName,
49                                            int bsdNameLen)
50{
51	CFArrayRef  efiArray = NULL;
52    CFIndex     count, i;
53	int			ret;
54    char        buffer[1024];
55	int			foundLegacyPath = 0;
56	CFStringRef	legacyType = NULL;
57
58	if(!BLSupportsLegacyMode(context)) {
59        contextprintf(context, kBLLogLevelVerbose, "Legacy mode not supported on this system\n");
60		return 1;
61	}
62
63    if(!CFStringGetCString(xmlString, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
64        return 1;
65    }
66
67    efiArray = IOCFUnserialize(buffer,
68                               kCFAllocatorDefault,
69                               0,
70                               NULL);
71    if(efiArray == NULL) {
72        contextprintf(context, kBLLogLevelError, "Could not unserialize string\n");
73        return 2;
74    }
75
76    if(CFGetTypeID(efiArray) != CFArrayGetTypeID()) {
77        CFRelease(efiArray);
78        contextprintf(context, kBLLogLevelError, "Bad type in XML string\n");
79        return 2;
80    }
81
82    // for each entry, see if there's a volume UUID, or if IOMatch works
83    count = CFArrayGetCount(efiArray);
84    for(i=0; i < count; i++) {
85        CFDictionaryRef dict = CFArrayGetValueAtIndex(efiArray, i);
86		CFStringRef		compType;
87
88        if(CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
89            CFRelease(efiArray);
90            contextprintf(context, kBLLogLevelError, "Bad type in XML string\n");
91            return 2;
92        }
93
94		compType = CFDictionaryGetValue(dict, CFSTR("IOEFIDevicePathType"));
95		if(compType && CFEqual(compType, CFSTR("MediaFirmwareVolumeFilePath"))) {
96			CFStringRef	guid;
97
98			guid = CFDictionaryGetValue(dict, CFSTR("Guid"));
99			if(guid && CFEqual(guid, CFSTR("2B0585EB-D8B8-49A9-8B8C-E21B01AEF2B7"))) {
100				foundLegacyPath = 1;
101				continue;
102			}
103		}
104
105		legacyType = CFDictionaryGetValue(dict, CFSTR("IOEFIBootOption"));
106		if(legacyType && CFGetTypeID(legacyType) == CFStringGetTypeID()) {
107			CFRetain(legacyType);
108		} else {
109			legacyType = NULL;
110		}
111
112    }
113
114	if(!foundLegacyPath || !legacyType) {
115		contextprintf(context, kBLLogLevelVerbose, "Boot option is not a legacy device\n");
116		return 4;
117	} else {
118		contextprintf(context, kBLLogLevelVerbose, "Boot option is a legacy device\n");
119	}
120
121	ret = findMatch(context, legacyType, xmlString, bsdName, bsdNameLen);
122	if(ret) {
123		contextprintf(context, kBLLogLevelVerbose, "Could not find device for legacy type\n");
124		return 5;
125	}
126
127	CFRelease(legacyType);
128
129    return 0;
130}
131
132
133static int findMatch(BLContextPtr context, CFStringRef legacyType,
134						 CFStringRef xmlString, char *bsdName, int bsdNameLen)
135{
136	char			legacyCStr[256];
137	int				numfs, i;
138	int				ret;
139	size_t			bufsize;
140	struct statfs	*buf;
141	bool			foundMatch = false;
142
143	if(CFStringGetCString(legacyType, legacyCStr, sizeof(legacyCStr), kCFStringEncodingUTF8)) {
144		contextprintf(context, kBLLogLevelVerbose, "Searching for legacy type '%s'\n", legacyCStr);
145	}
146
147	numfs = getfsstat(NULL, 0, MNT_NOWAIT);
148	if(numfs < 0) {
149		contextprintf(context, kBLLogLevelError, "Could not get list of filesystems\n");
150		return 1;
151	}
152
153	bufsize = numfs*sizeof(buf[0]);
154	buf = (struct statfs *)calloc(bufsize, sizeof(char));
155	if(buf == NULL) {
156		return 2;
157	}
158
159	numfs = getfsstat(buf, bufsize, MNT_NOWAIT);
160	if(numfs < 0) {
161		contextprintf(context, kBLLogLevelError, "Could not get list of filesystems\n");
162		return 1;
163	}
164
165	for(i=0; i < numfs; i++) {
166		struct statfs *sb = &buf[i];
167		CFStringRef		newXML = NULL;
168
169		if(!(sb->f_flags & MNT_LOCAL))
170			continue;
171
172		if(0 != strncmp(sb->f_mntfromname, "/dev/", 5))
173			continue;
174
175		contextprintf(context, kBLLogLevelVerbose, "filesystem[%d] '%s' => '%s'\n",
176					  i, sb->f_mntfromname, sb->f_mntonname);
177
178		ret = BLCreateEFIXMLRepresentationForLegacyDevice(context,
179													sb->f_mntfromname + 5,
180														  &newXML);
181		if(ret) {
182			contextprintf(context, kBLLogLevelVerbose, "Ignoring '%s'\n",
183						  sb->f_mntfromname);
184			continue;
185		}
186
187		if(CFEqual(newXML, xmlString)) {
188			// this is a match
189			// see if it's a filesystem that gets priority
190
191
192			if(0 == strcmp("ntfs", sb->f_fstypename)
193			   || 0 == strcmp("msdos", sb->f_fstypename)) {
194
195				strlcpy(bsdName, sb->f_mntfromname + 5, bsdNameLen);
196				CFRelease(newXML);
197				foundMatch = true;
198				break;
199			}
200
201			if(!foundMatch) {
202				// we don't have anything else, so go for it
203				strlcpy(bsdName, sb->f_mntfromname + 5, bsdNameLen);
204				foundMatch = true;
205			} else {
206				// no better than existing match
207			}
208
209		}
210
211		CFRelease(newXML);
212
213	}
214
215	free(buf);
216
217	if(!foundMatch)
218		return 3;
219
220	contextprintf(context, kBLLogLevelVerbose, "Matching legacy device '%s'\n",
221				  bsdName);
222
223
224	return 0;
225
226}
227