1 | /*␊ |
2 | * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.␊ |
3 | *␊ |
4 | * ␊ |
5 | * Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights␊ |
6 | * Reserved. This file contains Original Code and/or Modifications of␊ |
7 | * Original Code as defined in and that are subject to the Apple Public␊ |
8 | * Source License Version 2.0 (the "License"). You may not use this file␊ |
9 | * except in compliance with the License. Please obtain a copy of the␊ |
10 | * License at http://www.apple.com/publicsource and read it before using␊ |
11 | * this 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 OR NON- INFRINGEMENT. Please see the␊ |
18 | * License for the specific language governing rights and limitations␊ |
19 | * under the License.␊ |
20 | * ␊ |
21 | *␊ |
22 | * ␊ |
23 | * Mach Operating System␊ |
24 | * Copyright (c) 1990 Carnegie-Mellon University␊ |
25 | * Copyright (c) 1989 Carnegie-Mellon University␊ |
26 | * All rights reserved. The CMU software License Agreement specifies␊ |
27 | * the terms and conditions for use and redistribution.␊ |
28 | *␊ |
29 | *␊ |
30 | * INTEL CORPORATION PROPRIETARY INFORMATION␊ |
31 | *␊ |
32 | * This software is supplied under the terms of a license agreement or ␊ |
33 | * nondisclosure agreement with Intel Corporation and may not be copied ␊ |
34 | * nor disclosed except in accordance with the terms of that agreement.␊ |
35 | *␊ |
36 | * Copyright 1988, 1989 Intel Corporation␊ |
37 | *␊ |
38 | *␊ |
39 | * Copyright 1993 NeXT Computer, Inc.␊ |
40 | * All rights reserved.␊ |
41 | *␊ |
42 | *␊ |
43 | * Copyright 2007 VMware Inc.␊ |
44 | * "Preboot" ramdisk support added by David Elliott␊ |
45 | * GPT support added by David Elliott. Based on IOGUIDPartitionScheme.cpp.␊ |
46 | */␊ |
47 | ␊ |
48 | //Azi: style the rest later...␊ |
49 | ␊ |
50 | // Allow UFS_SUPPORT to be overridden with preprocessor option.␊ |
51 | #ifndef UFS_SUPPORT␊ |
52 | // zef: Disabled UFS support␊ |
53 | #define UFS_SUPPORT 0␊ |
54 | #endif␊ |
55 | ␊ |
56 | #if UFS_SUPPORT␊ |
57 | #include "ufs.h"␊ |
58 | #endif␊ |
59 | #include <limits.h>␊ |
60 | #include <IOKit/storage/IOApplePartitionScheme.h>␊ |
61 | #include <IOKit/storage/IOGUIDPartitionScheme.h>␊ |
62 | #include "libsaio.h"␊ |
63 | #include "boot.h"␊ |
64 | #include "bootstruct.h"␊ |
65 | #include "memory.h"␊ |
66 | #include "fdisk.h"␊ |
67 | #include "hfs.h"␊ |
68 | #include "ntfs.h"␊ |
69 | #include "msdos.h"␊ |
70 | #include "ext2fs.h"␊ |
71 | #include "befs.h"␊ |
72 | #include "freebsd.h"␊ |
73 | #include "openbsd.h"␊ |
74 | #include "xml.h"␊ |
75 | #include "disk.h"␊ |
76 | // For EFI_GUID␊ |
77 | #include "efi.h"␊ |
78 | ␊ |
79 | ␊ |
80 | typedef struct gpt_hdr gpt_hdr;␊ |
81 | typedef struct gpt_ent gpt_ent;␊ |
82 | ␊ |
83 | #include "efi_tables.h"␊ |
84 | ␊ |
85 | #define PROBEFS_SIZE BPS * 4 /* buffer size for filesystem probe */␊ |
86 | #define CD_BPS 2048 /* CD-ROM block size */␊ |
87 | #define N_CACHE_SECS (BIOS_LEN / BPS) /* Must be a multiple of 4 for CD-ROMs */␊ |
88 | #define UFS_FRONT_PORCH 0␊ |
89 | #define kAPMSector 2 /* Sector number of Apple partition map */␊ |
90 | #define kAPMCDSector 8 /* Translated sector of Apple partition map on a CD */␊ |
91 | ␊ |
92 | // IORound and IOTrunc convenience functions, in the spirit of vm's round_page() and trunc_page().␊ |
93 | #define IORound(value, multiple) ((((value) + (multiple) - 1) / (multiple)) * (multiple))␊ |
94 | #define IOTrunc(value, multiple) (((value) / (multiple)) * (multiple));␊ |
95 | ␊ |
96 | // trackbuf points to the start of the track cache. Biosread() ␊ |
97 | // will store the sectors read from disk to this memory area.␊ |
98 | static char * const trackbuf = (char *) ptov(BIOS_ADDR);␊ |
99 | ␊ |
100 | // biosbuf points to a sector within the track cache, and is updated by Biosread().␊ |
101 | static char * biosbuf;␊ |
102 | ␊ |
103 | // Map a disk drive to bootable volumes contained within.␊ |
104 | struct DiskBVMap␊ |
105 | {␊ |
106 | ␉int␉␉␉biosdev;␉// BIOS device number (unique).␊ |
107 | ␉BVRef␉␉␉bvr;␉␉// Chain of boot volumes on the disk.␊ |
108 | ␉int␉␉␉bvrcnt;␉␉// Number of boot volumes.␊ |
109 | ␉struct DiskBVMap␉*next;␉␉// Linkage to next mapping.␊ |
110 | };␊ |
111 | ␊ |
112 | static struct DiskBVMap * gDiskBVMap = NULL;␊ |
113 | static struct disk_blk0 * gBootSector = NULL;␊ |
114 | ␊ |
115 | // Function pointers to be filled in when a ramdisk is available:␊ |
116 | int (*p_ramdiskReadBytes)(int biosdev, unsigned int blkno, unsigned int byteoff, unsigned int byteCount, void * buffer) = NULL;␊ |
117 | int (*p_get_ramdisk_info)(int biosdev, struct driveInfo *dip) = NULL;␊ |
118 | ␊ |
119 | static bool getOSVersion(BVRef bvr, char *str);␊ |
120 | ␊ |
121 | extern void spinActivityIndicator(int sectors);␊ |
122 | ␊ |
123 | //==============================================================================␊ |
124 | ␊ |
125 | static int getDriveInfo(int biosdev, struct driveInfo *dip)␊ |
126 | {␊ |
127 | ␉static struct driveInfo cached_di;␊ |
128 | ␉int cc;␊ |
129 | ␊ |
130 | ␉// Real BIOS devices are 8-bit, so anything above that is for internal use.␊ |
131 | ␉// Don't cache ramdisk drive info since it doesn't require several BIOS␊ |
132 | ␉// calls and is thus not worth it.␊ |
133 | ␉if (biosdev >= 0x100)␊ |
134 | ␉{␊ |
135 | ␉␉if (p_get_ramdisk_info != NULL)␊ |
136 | ␉␉{␊ |
137 | ␉␉␉cc = (*p_get_ramdisk_info)(biosdev, dip);␊ |
138 | ␉␉}␊ |
139 | ␉␉else␊ |
140 | ␉␉{␊ |
141 | ␉␉␉cc = -1;␊ |
142 | ␉␉}␊ |
143 | ␉␉if (cc < 0)␊ |
144 | ␉␉{␊ |
145 | ␉␉␉dip->valid = 0;␊ |
146 | ␉␉␉return -1;␊ |
147 | ␉␉}␊ |
148 | ␉␉else␊ |
149 | ␉␉{␊ |
150 | ␉␉␉return 0;␊ |
151 | ␉␉}␊ |
152 | ␉}␊ |
153 | ␊ |
154 | ␉if (!cached_di.valid || biosdev != cached_di.biosdev)␊ |
155 | ␉{␊ |
156 | ␉␉cc = get_drive_info(biosdev, &cached_di);␊ |
157 | ␊ |
158 | ␉␉if (cc < 0)␊ |
159 | ␉␉{␊ |
160 | ␉␉␉cached_di.valid = 0;␊ |
161 | ␉␉␉DEBUG_DISK(("get_drive_info returned error\n"));␊ |
162 | ␉␉␉return (-1); // BIOS call error␊ |
163 | ␉␉}␊ |
164 | ␉}␊ |
165 | ␊ |
166 | ␉bcopy(&cached_di, dip, sizeof(cached_di));␊ |
167 | ␊ |
168 | ␉return 0;␊ |
169 | }␊ |
170 | ␊ |
171 | ␊ |
172 | //==============================================================================␊ |
173 | // Maps (E)BIOS return codes to message strings.␊ |
174 | ␊ |
175 | struct NamedValue␊ |
176 | {␊ |
177 | ␉unsigned char value;␊ |
178 | ␉const char * name;␊ |
179 | };␊ |
180 | ␊ |
181 | ␊ |
182 | //==============================================================================␊ |
183 | ␊ |
184 | static const char * getNameForValue(const struct NamedValue * nameTable, unsigned char value)␊ |
185 | {␊ |
186 | ␉const struct NamedValue * np;␊ |
187 | ␊ |
188 | ␉for ( np = nameTable; np->value; np++)␊ |
189 | ␉{␊ |
190 | ␉␉if (np->value == value)␊ |
191 | ␉␉{␊ |
192 | ␉␉␉return np->name;␊ |
193 | ␉␉}␊ |
194 | ␉}␊ |
195 | ␊ |
196 | ␉return NULL;␊ |
197 | }␊ |
198 | ␊ |
199 | #define ECC_CORRECTED_ERR 0x11␊ |
200 | ␊ |
201 | static const struct NamedValue bios_errors[] =␊ |
202 | {␊ |
203 | ␉{ 0x10, "Media error" },␊ |
204 | ␉{ 0x11, "Corrected ECC error" },␊ |
205 | ␉{ 0x20, "Controller or device error" },␊ |
206 | ␉{ 0x40, "Seek failed" },␊ |
207 | ␉{ 0x80, "Device timeout" },␊ |
208 | ␉{ 0xAA, "Drive not ready" },␊ |
209 | ␉{ 0x00, 0 }␊ |
210 | };␊ |
211 | ␊ |
212 | ␊ |
213 | static bool cache_valid = false;␊ |
214 | ␊ |
215 | ␊ |
216 | //==============================================================================␊ |
217 | ␊ |
218 | static const char * bios_error(int errnum)␊ |
219 | {␊ |
220 | ␉static char errorstr[] = "Error 0x00";␊ |
221 | ␉const char * errname;␊ |
222 | ␊ |
223 | ␉errname = getNameForValue(bios_errors, errnum);␊ |
224 | ␊ |
225 | ␉if (errname)␊ |
226 | ␉{␊ |
227 | ␉␉return errname;␊ |
228 | ␉}␊ |
229 | ␊ |
230 | ␉sprintf(errorstr, "Error 0x%02x", errnum);␊ |
231 | ␉return errorstr; // No string, print error code only␊ |
232 | }␊ |
233 | ␊ |
234 | ␊ |
235 | //==============================================================================␊ |
236 | // Use BIOS INT13 calls to read the sector specified. This function will also␊ |
237 | // perform read-ahead to cache a few subsequent sector to the sector cache.␊ |
238 | // ␊ |
239 | // Returns 0 on success, or an error code from INT13/F2 or INT13/F42 BIOS call.␊ |
240 | ␊ |
241 | static int Biosread(int biosdev, unsigned long long secno)␊ |
242 | {␊ |
243 | ␉static int xbiosdev, xcyl, xhead;␊ |
244 | ␉static unsigned int xsec, xnsecs;␊ |
245 | ␉struct driveInfo di;␊ |
246 | ␊ |
247 | ␉int rc = -1;␊ |
248 | ␉int cyl, head, sec;␊ |
249 | ␉int tries = 0;␊ |
250 | ␉int bps, divisor;␊ |
251 | ␊ |
252 | ␉if (getDriveInfo(biosdev, &di) < 0)␊ |
253 | ␉{␊ |
254 | ␉␉return -1;␊ |
255 | ␉}␊ |
256 | ␊ |
257 | ␉if (di.no_emulation)␊ |
258 | ␉{␊ |
259 | ␉␉bps = 2048; /* Always assume 2K block size since the BIOS may lie about the geometry */␊ |
260 | ␉}␊ |
261 | ␉else␊ |
262 | ␉{␊ |
263 | ␉␉bps = di.di.params.phys_nbps;␊ |
264 | ␊ |
265 | ␉␉if (bps == 0)␊ |
266 | ␉␉{␊ |
267 | ␉␉␉return -1;␊ |
268 | ␉␉}␊ |
269 | ␉}␊ |
270 | ␉divisor = bps / BPS;␊ |
271 | ␊ |
272 | ␉DEBUG_DISK(("Biosread dev %x sec %d bps %d\n", biosdev, secno, bps));␊ |
273 | ␊ |
274 | ␉// To read the disk sectors, use EBIOS if we can. Otherwise,␊ |
275 | ␉// revert to the standard BIOS calls.␊ |
276 | ␊ |
277 | ␉if ((biosdev >= kBIOSDevTypeHardDrive) && (di.uses_ebios & EBIOS_FIXED_DISK_ACCESS))␊ |
278 | ␉{␊ |
279 | ␉␉if (cache_valid && (biosdev == xbiosdev) && (secno >= xsec) && ((unsigned int)secno < (xsec + xnsecs)))␊ |
280 | ␉␉{␊ |
281 | ␉␉␉biosbuf = trackbuf + (BPS * (secno - xsec));␊ |
282 | ␉␉␉return 0;␊ |
283 | ␉␉}␊ |
284 | ␊ |
285 | ␉␉xnsecs = N_CACHE_SECS;␊ |
286 | ␉␉xsec = (secno / divisor) * divisor;␊ |
287 | ␉␉cache_valid = false;␊ |
288 | ␊ |
289 | ␉␉while ((rc = ebiosread(biosdev, secno / divisor, xnsecs / divisor)) && (++tries < 5))␊ |
290 | ␉␉{␊ |
291 | ␉␉␉if (rc == ECC_CORRECTED_ERR)␊ |
292 | ␉␉␉{␊ |
293 | ␉␉␉␉rc = 0; /* Ignore corrected ECC errors */␊ |
294 | ␉␉␉␉break;␊ |
295 | ␉␉␉}␊ |
296 | ␊ |
297 | ␉␉␉error(" EBIOS read error: %s\n", bios_error(rc), rc);␊ |
298 | ␉␉␉error(" Block 0x%x Sectors %d\n", secno, xnsecs);␊ |
299 | ␉␉␉sleep(1);␊ |
300 | ␉␉}␊ |
301 | ␉}␊ |
302 | ␊ |
303 | ␉else␊ |
304 | ␉{␊ |
305 | ␉␉/* spc = spt * heads */␊ |
306 | ␉␉int spc = (di.di.params.phys_spt * di.di.params.phys_heads);␊ |
307 | ␉␉cyl = secno / spc;␊ |
308 | ␉␉head = (secno % spc) / di.di.params.phys_spt;␊ |
309 | ␉␉sec = secno % di.di.params.phys_spt;␊ |
310 | ␊ |
311 | ␉␉if (cache_valid && (biosdev == xbiosdev) && (cyl == xcyl) &&␊ |
312 | ␉␉␉(head == xhead) && ((unsigned int)sec >= xsec) && ((unsigned int)sec < (xsec + xnsecs)))␊ |
313 | ␊ |
314 | ␉␉{␊ |
315 | ␉␉␉// this sector is in trackbuf cache␊ |
316 | ␉␉␉biosbuf = trackbuf + (BPS * (sec - xsec));␊ |
317 | ␉␉␉return 0;␊ |
318 | ␉␉}␊ |
319 | ␊ |
320 | ␉␉// Cache up to a track worth of sectors, but do not cross a track boundary.␊ |
321 | ␊ |
322 | ␉␉xcyl = cyl;␊ |
323 | ␉␉xhead = head;␊ |
324 | ␉␉xsec = sec;␊ |
325 | ␉␉xnsecs = ((unsigned int)(sec + N_CACHE_SECS) > di.di.params.phys_spt) ? (di.di.params.phys_spt - sec) : N_CACHE_SECS;␊ |
326 | ␊ |
327 | ␉␉cache_valid = false;␊ |
328 | ␊ |
329 | ␉␉while ((rc = biosread(biosdev, cyl, head, sec, xnsecs)) && (++tries < 5))␊ |
330 | ␉␉{␊ |
331 | ␉␉␉if (rc == ECC_CORRECTED_ERR)␊ |
332 | ␉␉␉{␊ |
333 | ␉␉␉␉rc = 0; /* Ignore corrected ECC errors */␊ |
334 | ␉␉␉␉break;␊ |
335 | ␉␉␉}␊ |
336 | ␉␉␉error(" BIOS read error: %s\n", bios_error(rc), rc);␊ |
337 | ␉␉␉error(" Block %d, Cyl %d Head %d Sector %d\n", secno, cyl, head, sec);␊ |
338 | ␉␉␉sleep(1);␊ |
339 | ␉␉}␊ |
340 | ␉}␊ |
341 | ␊ |
342 | ␉// If the BIOS reported success, mark the sector cache as valid.␊ |
343 | ␊ |
344 | ␉if (rc == 0)␊ |
345 | ␉{␊ |
346 | ␉␉cache_valid = true;␊ |
347 | ␉}␊ |
348 | ␊ |
349 | ␉biosbuf = trackbuf + (secno % divisor) * BPS;␊ |
350 | ␉xbiosdev = biosdev;␊ |
351 | ␊ |
352 | ␉spinActivityIndicator(xnsecs);␊ |
353 | ␊ |
354 | ␉return rc;␊ |
355 | }␊ |
356 | ␊ |
357 | ␊ |
358 | //==============================================================================␊ |
359 | ␊ |
360 | int testBiosread(int biosdev, unsigned long long secno)␊ |
361 | {␊ |
362 | ␉return Biosread(biosdev, secno);␊ |
363 | }␊ |
364 | ␊ |
365 | //==============================================================================␊ |
366 | ␊ |
367 | static int readBytes(int biosdev, unsigned long long blkno, unsigned int byteoff, unsigned int byteCount, void * buffer)␊ |
368 | {␊ |
369 | ␉// ramdisks require completely different code for reading.␊ |
370 | ␉if(p_ramdiskReadBytes != NULL && biosdev >= 0x100)␊ |
371 | ␉{␊ |
372 | ␉␉return (*p_ramdiskReadBytes)(biosdev, blkno, byteoff, byteCount, buffer);␊ |
373 | ␉}␊ |
374 | ␊ |
375 | ␉char * cbuf = (char *) buffer;␊ |
376 | ␉int error;␊ |
377 | ␉int copy_len;␊ |
378 | ␊ |
379 | ␉DEBUG_DISK(("%s: dev %x block %x [%d] -> 0x%x...", __FUNCTION__, biosdev, blkno, byteCount, (unsigned)cbuf));␊ |
380 | ␊ |
381 | ␉for (; byteCount; cbuf += copy_len, blkno++)␊ |
382 | ␉{␊ |
383 | ␉␉error = Biosread(biosdev, blkno);␊ |
384 | ␊ |
385 | ␉␉if (error)␊ |
386 | ␉␉{␊ |
387 | ␉␉␉DEBUG_DISK(("error\n"));␊ |
388 | ␊ |
389 | ␉␉␉return (-1);␊ |
390 | ␉␉}␊ |
391 | ␊ |
392 | ␉␉copy_len = ((byteCount + byteoff) > BPS) ? (BPS - byteoff) : byteCount;␊ |
393 | ␉␉bcopy( biosbuf + byteoff, cbuf, copy_len );␊ |
394 | ␉␉byteCount -= copy_len;␊ |
395 | ␉␉byteoff = 0;␊ |
396 | ␉}␊ |
397 | ␊ |
398 | ␉DEBUG_DISK(("done\n"));␊ |
399 | ␊ |
400 | ␉return 0; ␊ |
401 | }␊ |
402 | ␊ |
403 | //==============================================================================␊ |
404 | ␊ |
405 | static int isExtendedFDiskPartition( const struct fdisk_part * part )␊ |
406 | {␊ |
407 | ␉static unsigned char extParts[] =␊ |
408 | ␉{␊ |
409 | ␉␉0x05, /* Extended */␊ |
410 | ␉␉0x0f, /* Win95 extended */␊ |
411 | ␉␉0x85, /* Linux extended */␊ |
412 | ␉};␊ |
413 | ␊ |
414 | ␉unsigned int i;␊ |
415 | ␊ |
416 | ␉for (i = 0; i < sizeof(extParts)/sizeof(extParts[0]); i++)␊ |
417 | ␉{␊ |
418 | ␉␉if (extParts[i] == part->systid)␊ |
419 | ␉␉{␊ |
420 | ␉␉␉return 1;␊ |
421 | ␉␉}␊ |
422 | ␉}␊ |
423 | ␉return 0;␊ |
424 | }␊ |
425 | ␊ |
426 | //==============================================================================␊ |
427 | ␊ |
428 | static int getNextFDiskPartition( int biosdev, int * partno,␊ |
429 | const struct fdisk_part ** outPart )␊ |
430 | {␊ |
431 | ␉static int sBiosdev = -1;␊ |
432 | ␉static int sNextPartNo;␊ |
433 | ␉static unsigned int sFirstBase;␊ |
434 | ␉static unsigned int sExtBase;␊ |
435 | ␉static unsigned int sExtDepth;␊ |
436 | ␉static struct fdisk_part * sExtPart;␊ |
437 | ␉struct fdisk_part * part;␊ |
438 | ␊ |
439 | ␉if ( sBiosdev != biosdev || *partno < 0 )␊ |
440 | ␉{␊ |
441 | ␉␉// Fetch MBR.␊ |
442 | ␉␉if ( readBootSector( biosdev, DISK_BLK0, 0 ) )␊ |
443 | ␉␉{␊ |
444 | ␉␉␉return 0;␊ |
445 | ␉␉}␊ |
446 | ␊ |
447 | ␉␉sBiosdev = biosdev;␊ |
448 | ␉␉sNextPartNo = 0;␊ |
449 | ␉␉sFirstBase = 0;␊ |
450 | ␉␉sExtBase = 0;␊ |
451 | ␉␉sExtDepth = 0;␊ |
452 | ␉␉sExtPart = NULL;␊ |
453 | ␉}␊ |
454 | ␊ |
455 | ␉while (1)␊ |
456 | ␉{␊ |
457 | ␉␉part = NULL;␊ |
458 | ␊ |
459 | ␉␉if ( sNextPartNo < FDISK_NPART )␊ |
460 | ␉␉{␊ |
461 | ␉␉␉part = (struct fdisk_part *) gBootSector->parts[sNextPartNo];␊ |
462 | ␉␉}␊ |
463 | ␉␉else if ( sExtPart )␊ |
464 | ␉␉{␊ |
465 | ␉␉␉unsigned int blkno = sExtPart->relsect + sFirstBase;␊ |
466 | ␊ |
467 | ␉␉␉// Save the block offset of the first extended partition.␊ |
468 | ␊ |
469 | ␉␉␉if (sExtDepth == 0)␊ |
470 | ␉␉␉{␊ |
471 | ␉␉␉␉sFirstBase = blkno;␊ |
472 | ␉␉␉}␊ |
473 | ␉␉␉sExtBase = blkno;␊ |
474 | ␊ |
475 | ␉␉␉// Load extended partition table.␊ |
476 | ␊ |
477 | ␉␉␉if ( readBootSector( biosdev, blkno, 0 ) == 0 )␊ |
478 | ␉␉␉{␊ |
479 | ␉␉␉␉sNextPartNo = 0;␊ |
480 | ␉␉␉␉sExtDepth++;␊ |
481 | ␉␉␉␉sExtPart = NULL;␊ |
482 | ␉␉␉␉continue;␊ |
483 | ␉␉␉}␊ |
484 | ␉␉␉// Fall through to part == NULL␊ |
485 | ␉␉}␊ |
486 | ␊ |
487 | ␉␉if ( part == NULL ) break; // Reached end of partition chain.␊ |
488 | ␊ |
489 | ␉␉// Advance to next partition number.␊ |
490 | ␊ |
491 | ␉␉sNextPartNo++;␊ |
492 | ␊ |
493 | ␉␉if ( isExtendedFDiskPartition(part) )␊ |
494 | ␉␉{␊ |
495 | ␉␉␉sExtPart = part;␊ |
496 | ␉␉␉continue;␊ |
497 | ␉␉}␊ |
498 | ␊ |
499 | ␉␉// Skip empty slots.␊ |
500 | ␊ |
501 | ␉␉if ( part->systid == 0x00 )␊ |
502 | ␉␉{␊ |
503 | ␉␉␉continue;␊ |
504 | ␉␉}␊ |
505 | ␊ |
506 | ␉␉// Change relative offset to an absolute offset.␊ |
507 | ␉␉part->relsect += sExtBase;␊ |
508 | ␊ |
509 | ␉␉*outPart = part;␊ |
510 | ␉␉*partno = sExtDepth ? (int)(sExtDepth + FDISK_NPART) : sNextPartNo;␊ |
511 | ␊ |
512 | ␉␉break;␊ |
513 | ␉}␊ |
514 | ␊ |
515 | ␉return (part != NULL);␊ |
516 | }␊ |
517 | ␊ |
518 | //==============================================================================␊ |
519 | ␊ |
520 | static BVRef newFDiskBVRef( int biosdev, int partno, unsigned int blkoff,␊ |
521 | const struct fdisk_part * part,␊ |
522 | FSInit initFunc, FSLoadFile loadFunc,␊ |
523 | FSReadFile readFunc,␊ |
524 | FSGetDirEntry getdirFunc,␊ |
525 | FSGetFileBlock getBlockFunc,␊ |
526 | FSGetUUID getUUIDFunc,␊ |
527 | BVGetDescription getDescriptionFunc,␊ |
528 | BVFree bvFreeFunc,␊ |
529 | int probe, int type, unsigned int bvrFlags )␊ |
530 | {␊ |
531 | ␉BVRef bvr = (BVRef) malloc( sizeof(*bvr) );␊ |
532 | ␉if ( bvr )␊ |
533 | ␉{␊ |
534 | ␉␉bzero(bvr, sizeof(*bvr));␊ |
535 | ␊ |
536 | ␉␉bvr->biosdev = biosdev;␊ |
537 | ␉␉bvr->part_no = partno;␊ |
538 | ␉␉bvr->part_boff = blkoff;␊ |
539 | ␉␉bvr->part_type = part->systid;␊ |
540 | ␉␉bvr->fs_loadfile = loadFunc;␊ |
541 | ␉␉bvr->fs_readfile = readFunc;␊ |
542 | ␉␉bvr->fs_getdirentry = getdirFunc;␊ |
543 | ␉␉bvr->fs_getfileblock= getBlockFunc;␊ |
544 | ␉␉bvr->fs_getuuid = getUUIDFunc;␊ |
545 | ␉␉bvr->description = getDescriptionFunc;␊ |
546 | ␉␉bvr->type = type;␊ |
547 | ␉␉bvr->bv_free = bvFreeFunc;␊ |
548 | ␊ |
549 | ␉␉if ((part->bootid & FDISK_ACTIVE) && (part->systid == FDISK_HFS))␊ |
550 | ␉␉{␊ |
551 | ␉␉␉bvr->flags |= kBVFlagPrimary;␊ |
552 | ␉␉}␊ |
553 | ␊ |
554 | ␉␉// Probe the filesystem.␊ |
555 | ␊ |
556 | ␉␉if ( initFunc )␊ |
557 | ␉␉{␊ |
558 | ␉␉␉bvr->flags |= kBVFlagNativeBoot;␊ |
559 | ␊ |
560 | ␉␉␉if ( probe && initFunc( bvr ) != 0 )␊ |
561 | ␉␉␉{␊ |
562 | ␉␉␉␉// filesystem probe failed.␊ |
563 | ␊ |
564 | ␉␉␉␉DEBUG_DISK(("%s: failed probe on dev %x part %d\n", __FUNCTION__, biosdev, partno));␊ |
565 | ␊ |
566 | ␉␉␉␉(*bvr->bv_free)(bvr);␊ |
567 | ␉␉␉␉bvr = NULL;␊ |
568 | ␉␉␉}␊ |
569 | ␊ |
570 | ␉␉␉if ( readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )␊ |
571 | ␉␉␉{␊ |
572 | ␉␉␉␉bvr->flags |= kBVFlagBootable;␊ |
573 | ␉␉␉}␊ |
574 | ␉␉}␊ |
575 | ␉␉else if ( readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )␊ |
576 | ␉␉{␊ |
577 | ␉␉␉bvr->flags |= kBVFlagForeignBoot;␊ |
578 | ␉␉}␊ |
579 | ␉␉else␊ |
580 | ␉␉{␊ |
581 | ␉␉␉(*bvr->bv_free)(bvr);␊ |
582 | ␉␉␉bvr = NULL;␊ |
583 | ␉␉}␊ |
584 | ␉}␊ |
585 | ␊ |
586 | ␉if (bvr) bvr->flags |= bvrFlags;␊ |
587 | ␉{␊ |
588 | ␉␉return bvr;␊ |
589 | ␉}␊ |
590 | }␊ |
591 | ␊ |
592 | //==============================================================================␊ |
593 | ␊ |
594 | BVRef newAPMBVRef( int biosdev, int partno, unsigned int blkoff,␊ |
595 | const DPME * part,␊ |
596 | FSInit initFunc, FSLoadFile loadFunc,␊ |
597 | FSReadFile readFunc,␊ |
598 | FSGetDirEntry getdirFunc,␊ |
599 | FSGetFileBlock getBlockFunc,␊ |
600 | FSGetUUID getUUIDFunc,␊ |
601 | BVGetDescription getDescriptionFunc,␊ |
602 | BVFree bvFreeFunc,␊ |
603 | int probe, int type, unsigned int bvrFlags )␊ |
604 | {␊ |
605 | ␉BVRef bvr = (BVRef) malloc( sizeof(*bvr) );␊ |
606 | ␉if ( bvr )␊ |
607 | ␉{␊ |
608 | ␉␉bzero(bvr, sizeof(*bvr));␊ |
609 | ␊ |
610 | ␉␉bvr->biosdev = biosdev;␊ |
611 | ␉␉bvr->part_no = partno;␊ |
612 | ␉␉bvr->part_boff = blkoff;␊ |
613 | ␉␉bvr->fs_loadfile = loadFunc;␊ |
614 | ␉␉bvr->fs_readfile = readFunc;␊ |
615 | ␉␉bvr->fs_getdirentry = getdirFunc;␊ |
616 | ␉␉bvr->fs_getfileblock= getBlockFunc;␊ |
617 | ␉␉bvr->fs_getuuid = getUUIDFunc;␊ |
618 | ␉␉bvr->description = getDescriptionFunc;␊ |
619 | ␉␉bvr->type = type;␊ |
620 | ␉␉bvr->bv_free = bvFreeFunc;␊ |
621 | ␉␉strlcpy(bvr->name, part->dpme_name, DPISTRLEN);␊ |
622 | ␉␉strlcpy(bvr->type_name, part->dpme_type, DPISTRLEN);␊ |
623 | ␊ |
624 | ␉␉/*␊ |
625 | ␉␉if ( part->bootid & FDISK_ACTIVE )␊ |
626 | ␉␉{␊ |
627 | ␉␉␉bvr->flags |= kBVFlagPrimary;␊ |
628 | ␉␉}␊ |
629 | ␉␉*/␊ |
630 | ␊ |
631 | ␉␉// Probe the filesystem.␊ |
632 | ␊ |
633 | ␉␉if ( initFunc )␊ |
634 | ␉␉{␊ |
635 | ␉␉␉bvr->flags |= kBVFlagNativeBoot | kBVFlagBootable | kBVFlagSystemVolume;␊ |
636 | ␊ |
637 | ␉␉␉if ( probe && initFunc( bvr ) != 0 )␊ |
638 | ␉␉␉{␊ |
639 | ␉␉␉␉// filesystem probe failed.␊ |
640 | ␊ |
641 | ␉␉␉␉DEBUG_DISK(("%s: failed probe on dev %x part %d\n", __FUNCTION__, biosdev, partno));␊ |
642 | ␊ |
643 | ␉␉␉␉(*bvr->bv_free)(bvr);␊ |
644 | ␉␉␉␉bvr = NULL;␊ |
645 | ␉␉␉}␊ |
646 | ␉␉}␊ |
647 | ␉␉/*␊ |
648 | ␉␉else if ( readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )␊ |
649 | ␉␉{␊ |
650 | ␉␉␉bvr->flags |= kBVFlagForeignBoot;␊ |
651 | ␉␉}␊ |
652 | ␉␉*/␊ |
653 | ␉␉else␊ |
654 | ␉␉{␊ |
655 | ␉␉␉(*bvr->bv_free)(bvr);␊ |
656 | ␉␉␉bvr = NULL;␊ |
657 | ␉␉}␊ |
658 | ␉}␊ |
659 | ␉if (bvr)␊ |
660 | ␉{␊ |
661 | ␉␉bvr->flags |= bvrFlags;␊ |
662 | ␉}␊ |
663 | ␉return bvr;␊ |
664 | }␊ |
665 | ␊ |
666 | //==============================================================================␊ |
667 | ␊ |
668 | // GUID's in LE form:␊ |
669 | // http://en.wikipedia.org/wiki/GUID_Partition_Table␊ |
670 | // 00000000-0000-0000-0000-000000000000 - Unused Entry partition ␊ |
671 | // 024DEE41-33E7-11D3-9D69-0008C781F39F - MBR partition scheme ␊ |
672 | // C12A7328-F81F-11D2-BA4B-00A0C93EC93B - turbo - or an EFI System Partition␊ |
673 | EFI_GUID const GPT_EFISYS_GUID␉ = { 0xC12A7328, 0xF81F, 0x11D2, { 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B } };␊ |
674 | // 21686148-6449-6E6F-744E-656564454649 - BIOS Boot partition ␊ |
675 | // E3C9E316-0B5C-4DB8-817D-F92DF00215AE - Microsoft Reserved Partition ␊ |
676 | EFI_GUID const GPT_BASICDATA2_GUID = { 0xE3C9E316, 0x0B5C, 0x4DB8, { 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE } };␊ |
677 | // EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 - zef - Basic Data Partition - for foreign OS support␊ |
678 | EFI_GUID const GPT_BASICDATA_GUID = { 0xEBD0A0A2, 0xB9E5, 0x4433, { 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 } };␊ |
679 | // 5808C8AA-7E8F-42E0-85D2-E1E90434CFB3 - Logical Disk Manager metadata partition␊ |
680 | // AF9B60A0-1431-4F62-BC68-3311714A69AD - Logical Disk Manager data partition␊ |
681 | // DE94BBA4-06D1-4D40-A16A-BFD50179D6AC - Windows Recovery Environment␊ |
682 | // 48465300-0000-11AA-AA11-00306543ECAC - Hierarchical File System Plus (HFS+) partition␊ |
683 | EFI_GUID const GPT_HFS_GUID␉␉ = { 0x48465300, 0x0000, 0x11AA, { 0xAA, 0x11, 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC } };␊ |
684 | // 55465300-0000-11AA-AA11-00306543ECAC - Apple UFS ␊ |
685 | // 52414944-0000-11AA-AA11-00306543ECAC - Apple RAID partition␊ |
686 | // 426F6F74-0000-11AA-AA11-00306543ECAC - turbo - Apple Boot Partition␊ |
687 | EFI_GUID const GPT_BOOT_GUID␉ = { 0x426F6F74, 0x0000, 0x11AA, { 0xAA, 0x11, 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC } };␊ |
688 | // 4C616265-6C00-11AA-AA11-00306543ECAC - Apple Label␊ |
689 | // 5265636F-7665-11AA-AA11-00306543ECAC - Apple TV Recovery partition␊ |
690 | // 53746F72-6167-11AA-AA11-00306543ECAC - Apple Core Storage (i.e. Lion FileVault) partition (Apple_Boot Recovery HD)␊ |
691 | EFI_GUID const GPT_CORESTORAGE_GUID␉ = { 0x53746F72, 0x6167, 0x11AA, { 0xAA, 0x11, 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC } };␊ |
692 | ␊ |
693 | BVRef newGPTBVRef( int biosdev, int partno, unsigned int blkoff,␊ |
694 | const gpt_ent * part,␊ |
695 | FSInit initFunc, FSLoadFile loadFunc,␊ |
696 | FSReadFile readFunc,␊ |
697 | FSGetDirEntry getdirFunc,␊ |
698 | FSGetFileBlock getBlockFunc,␊ |
699 | FSGetUUID getUUIDFunc,␊ |
700 | BVGetDescription getDescriptionFunc,␊ |
701 | BVFree bvFreeFunc,␊ |
702 | int probe, int type, unsigned int bvrFlags )␊ |
703 | {␊ |
704 | ␉BVRef bvr = (BVRef) malloc( sizeof(*bvr) );␊ |
705 | ␉if ( bvr )␊ |
706 | ␉{␊ |
707 | ␉␉bzero(bvr, sizeof(*bvr));␊ |
708 | ␊ |
709 | ␉␉bvr->biosdev = biosdev;␊ |
710 | ␉␉bvr->part_no = partno;␊ |
711 | ␉␉bvr->part_boff = blkoff;␊ |
712 | ␉␉bvr->fs_loadfile = loadFunc;␊ |
713 | ␉␉bvr->fs_readfile = readFunc;␊ |
714 | ␉␉bvr->fs_getdirentry = getdirFunc;␊ |
715 | ␉␉bvr->fs_getfileblock= getBlockFunc;␊ |
716 | ␉␉bvr->fs_getuuid = getUUIDFunc;␊ |
717 | ␉␉bvr->description = getDescriptionFunc;␊ |
718 | ␉␉bvr->type = type;␊ |
719 | ␉␉bvr->bv_free = bvFreeFunc;␊ |
720 | ␉␉// FIXME: UCS-2 -> UTF-8 the name␊ |
721 | ␉␉strlcpy(bvr->name, "----", DPISTRLEN);␊ |
722 | ␉␉if ( (efi_guid_compare(&GPT_BOOT_GUID, (EFI_GUID const*)part->ent_type) == 0) || (efi_guid_compare(&GPT_HFS_GUID, (EFI_GUID const*)part->ent_type) == 0) )␊ |
723 | ␉␉{␊ |
724 | ␉␉␉strlcpy(bvr->type_name, "GPT HFS+", DPISTRLEN);␊ |
725 | ␉␉}␊ |
726 | ␉␉else␊ |
727 | ␉␉{␊ |
728 | ␉␉␉strlcpy(bvr->type_name, "GPT Unknown", DPISTRLEN);␊ |
729 | ␉␉}␊ |
730 | ␊ |
731 | ␉␉/*␊ |
732 | ␉␉if ( part->bootid & FDISK_ACTIVE )␊ |
733 | ␉␉{␊ |
734 | ␉␉␉bvr->flags |= kBVFlagPrimary;␊ |
735 | ␉␉}␊ |
736 | ␉␉*/␊ |
737 | ␊ |
738 | ␉␉// Probe the filesystem.␊ |
739 | ␊ |
740 | ␉␉if ( initFunc )␊ |
741 | ␉␉{␊ |
742 | ␉␉␉bvr->flags |= kBVFlagNativeBoot;␊ |
743 | ␊ |
744 | ␉␉␉if ( probe && initFunc( bvr ) != 0 )␊ |
745 | ␉␉␉{␊ |
746 | ␉␉␉␉// filesystem probe failed.␊ |
747 | ␊ |
748 | ␉␉␉␉DEBUG_DISK(("%s: failed probe on dev %x part %d\n", __FUNCTION__, biosdev, partno));␊ |
749 | ␊ |
750 | ␉␉␉␉(*bvr->bv_free)(bvr);␊ |
751 | ␉␉␉␉bvr = NULL;␊ |
752 | ␉␉␉}␊ |
753 | ␉␉␉if ( readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )␊ |
754 | ␉␉␉{␊ |
755 | ␉␉␉␉bvr->flags |= kBVFlagBootable;␊ |
756 | ␉␉␉}␊ |
757 | ␉␉}␊ |
758 | ␉␉else if ( readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )␊ |
759 | ␉␉{␊ |
760 | ␉␉␉bvr->flags |= kBVFlagForeignBoot;␊ |
761 | ␉␉}␊ |
762 | ␉␉else␊ |
763 | ␉␉{␊ |
764 | ␉␉␉(*bvr->bv_free)(bvr);␊ |
765 | ␉␉␉bvr = NULL;␊ |
766 | ␉␉}␊ |
767 | ␉}␊ |
768 | ␉if (bvr)␊ |
769 | ␉{␊ |
770 | ␉␉bvr->flags |= bvrFlags;␊ |
771 | ␉}␊ |
772 | ␉return bvr;␊ |
773 | }␊ |
774 | ␊ |
775 | //==============================================================================␊ |
776 | ␊ |
777 | /* A note on partition numbers:␊ |
778 | * IOKit makes the primary partitions numbers 1-4, and then␊ |
779 | * extended partitions are numbered consecutively 5 and up.␊ |
780 | * So, for example, if you have two primary partitions and␊ |
781 | * one extended partition they will be numbered 1, 2, 5.␊ |
782 | */␊ |
783 | ␊ |
784 | static BVRef diskScanFDiskBootVolumes( int biosdev, int * countPtr )␊ |
785 | {␊ |
786 | const struct fdisk_part * part;␊ |
787 | struct DiskBVMap * map;␊ |
788 | int partno = -1;␊ |
789 | BVRef bvr;␊ |
790 | #if UFS_SUPPORT␊ |
791 | BVRef booterUFS = NULL;␊ |
792 | #endif␊ |
793 | int spc;␊ |
794 | struct driveInfo di;␊ |
795 | boot_drive_info_t *dp;␊ |
796 | ␊ |
797 | ␉/* Initialize disk info */␊ |
798 | ␊ |
799 | ␉if (getDriveInfo(biosdev, &di) != 0)␊ |
800 | ␉{␊ |
801 | ␉␉return NULL;␊ |
802 | ␉}␊ |
803 | ␊ |
804 | ␉dp = &di.di;␊ |
805 | ␉spc = (dp->params.phys_spt * dp->params.phys_heads);␊ |
806 | ␊ |
807 | ␉if (spc == 0)␊ |
808 | ␉{␊ |
809 | ␉␉/* This is probably a CD-ROM; punt on the geometry. */␊ |
810 | ␉␉spc = 1;␊ |
811 | ␉}␊ |
812 | ␊ |
813 | do {␊ |
814 | // Create a new mapping.␊ |
815 | ␊ |
816 | map = (struct DiskBVMap *) malloc( sizeof(*map) );␊ |
817 | if ( map )␊ |
818 | {␊ |
819 | map->biosdev = biosdev;␊ |
820 | map->bvr = NULL;␊ |
821 | map->bvrcnt = 0;␊ |
822 | map->next = gDiskBVMap;␊ |
823 | gDiskBVMap = map;␊ |
824 | ␊ |
825 | // Create a record for each partition found on the disk.␊ |
826 | ␊ |
827 | while ( getNextFDiskPartition( biosdev, &partno, &part ) )␊ |
828 | {␊ |
829 | DEBUG_DISK(("%s: part %d [%x]\n", __FUNCTION__,␊ |
830 | partno, part->systid));␊ |
831 | bvr = 0;␊ |
832 | ␊ |
833 | switch ( part->systid )␊ |
834 | {␊ |
835 | #if UFS_SUPPORT␊ |
836 | case FDISK_UFS:␊ |
837 | bvr = newFDiskBVRef(␊ |
838 | biosdev, partno,␊ |
839 | part->relsect + UFS_FRONT_PORCH/BPS,␊ |
840 | part,␊ |
841 | UFSInitPartition,␊ |
842 | UFSLoadFile,␊ |
843 | UFSReadFile,␊ |
844 | UFSGetDirEntry,␊ |
845 | UFSGetFileBlock,␊ |
846 | UFSGetUUID,␊ |
847 | UFSGetDescription,␊ |
848 | UFSFree,␊ |
849 | 0,␊ |
850 | kBIOSDevTypeHardDrive, 0);␊ |
851 | break;␊ |
852 | #endif␊ |
853 | ␊ |
854 | case FDISK_HFS:␊ |
855 | bvr = newFDiskBVRef(␊ |
856 | biosdev, partno,␊ |
857 | part->relsect,␊ |
858 | part,␊ |
859 | HFSInitPartition,␊ |
860 | HFSLoadFile,␊ |
861 | HFSReadFile,␊ |
862 | HFSGetDirEntry,␊ |
863 | HFSGetFileBlock,␊ |
864 | HFSGetUUID,␊ |
865 | HFSGetDescription,␊ |
866 | HFSFree,␊ |
867 | 0,␊ |
868 | kBIOSDevTypeHardDrive, 0);␊ |
869 | break;␊ |
870 | ␊ |
871 | // turbo - we want the booter type scanned also␊ |
872 | case FDISK_BOOTER:␊ |
873 | if (part->bootid & FDISK_ACTIVE)␊ |
874 | gBIOSBootVolume = newFDiskBVRef(␊ |
875 | biosdev, partno,␊ |
876 | part->relsect,␊ |
877 | part,␊ |
878 | HFSInitPartition,␊ |
879 | HFSLoadFile,␊ |
880 | HFSReadFile,␊ |
881 | HFSGetDirEntry,␊ |
882 | HFSGetFileBlock,␊ |
883 | HFSGetUUID,␊ |
884 | HFSGetDescription,␊ |
885 | HFSFree,␊ |
886 | 0,␊ |
887 | kBIOSDevTypeHardDrive, 0);␊ |
888 | break;␊ |
889 | ␊ |
890 | #if UFS_SUPPORT␊ |
891 | case FDISK_BOOTER:␊ |
892 | booterUFS = newFDiskBVRef(␊ |
893 | biosdev, partno,␊ |
894 | ((part->relsect + spc - 1) / spc) * spc,␊ |
895 | part,␊ |
896 | UFSInitPartition,␊ |
897 | UFSLoadFile,␊ |
898 | UFSReadFile,␊ |
899 | UFSGetDirEntry,␊ |
900 | UFSGetFileBlock,␊ |
901 | UFSGetUUID,␊ |
902 | UFSGetDescription,␊ |
903 | UFSFree,␊ |
904 | 0,␊ |
905 | kBIOSDevTypeHardDrive, 0);␊ |
906 | break;␊ |
907 | #endif␊ |
908 | ␊ |
909 | case FDISK_FAT32:␊ |
910 | case FDISK_DOS12:␊ |
911 | case FDISK_DOS16S:␊ |
912 | case FDISK_DOS16B:␊ |
913 | case FDISK_SMALLFAT32:␊ |
914 | case FDISK_DOS16SLBA:␊ |
915 | bvr = newFDiskBVRef(␊ |
916 | biosdev, partno,␊ |
917 | part->relsect,␊ |
918 | part,␊ |
919 | MSDOSInitPartition,␊ |
920 | MSDOSLoadFile,␊ |
921 | MSDOSReadFile,␊ |
922 | MSDOSGetDirEntry,␊ |
923 | MSDOSGetFileBlock,␊ |
924 | MSDOSGetUUID,␊ |
925 | MSDOSGetDescription,␊ |
926 | MSDOSFree,␊ |
927 | 0,␊ |
928 | kBIOSDevTypeHardDrive, 0);␊ |
929 | break;␊ |
930 | ␊ |
931 | case FDISK_NTFS:␊ |
932 | bvr = newFDiskBVRef(␊ |
933 | biosdev, partno,␊ |
934 | part->relsect,␊ |
935 | part,␊ |
936 | 0, 0, 0, 0, 0,␊ |
937 | NTFSGetUUID,␊ |
938 | NTFSGetDescription,␊ |
939 | (BVFree)free,␊ |
940 | 0, kBIOSDevTypeHardDrive, 0);␊ |
941 | break;␊ |
942 | ␊ |
943 | case FDISK_LINUX:␊ |
944 | bvr = newFDiskBVRef(␊ |
945 | biosdev, partno,␊ |
946 | part->relsect,␊ |
947 | part,␊ |
948 | 0, 0, 0, 0, 0,␊ |
949 | EX2GetUUID,␊ |
950 | EX2GetDescription,␊ |
951 | (BVFree)free,␊ |
952 | 0, kBIOSDevTypeHardDrive, 0);␊ |
953 | break;␊ |
954 | ␊ |
955 | case FDISK_BEFS:␊ |
956 | bvr = newFDiskBVRef(␊ |
957 | biosdev, partno,␊ |
958 | part->relsect,␊ |
959 | part,␊ |
960 | 0, 0, 0, 0, 0, 0,␊ |
961 | BeFSGetDescription,␊ |
962 | (BVFree)free,␊ |
963 | 0, kBIOSDevTypeHardDrive, 0);␊ |
964 | break;␊ |
965 | ␊ |
966 | case FDISK_FREEBSD:␊ |
967 | bvr = newFDiskBVRef(␊ |
968 | biosdev, partno,␊ |
969 | part->relsect,␊ |
970 | part,␊ |
971 | 0, 0, 0, 0, 0, 0,␊ |
972 | FreeBSDGetDescription,␊ |
973 | (BVFree)free,␊ |
974 | 0, kBIOSDevTypeHardDrive, 0);␊ |
975 | break;␊ |
976 | ␊ |
977 | case FDISK_OPENBSD:␊ |
978 | bvr = newFDiskBVRef(␊ |
979 | biosdev, partno,␊ |
980 | part->relsect,␊ |
981 | part,␊ |
982 | 0, 0, 0, 0, 0, 0,␊ |
983 | OpenBSDGetDescription,␊ |
984 | (BVFree)free,␊ |
985 | 0, kBIOSDevTypeHardDrive, 0);␊ |
986 | break;␊ |
987 | ␊ |
988 | default:␊ |
989 | bvr = newFDiskBVRef(␊ |
990 | biosdev, partno,␊ |
991 | part->relsect,␊ |
992 | part,␊ |
993 | 0, 0, 0, 0, 0, 0, 0,␊ |
994 | (BVFree)free,␊ |
995 | 0,␊ |
996 | kBIOSDevTypeHardDrive, 0);␊ |
997 | break;␊ |
998 | }␊ |
999 | ␊ |
1000 | if ( bvr )␊ |
1001 | {␊ |
1002 | bvr->next = map->bvr;␊ |
1003 | map->bvr = bvr;␊ |
1004 | map->bvrcnt++;␊ |
1005 | }␊ |
1006 | }␊ |
1007 | ␊ |
1008 | #if UFS_SUPPORT␊ |
1009 | // Booting from a CD with an UFS filesystem embedded␊ |
1010 | // in a booter partition.␊ |
1011 | ␊ |
1012 | ␉␉␉if ( booterUFS )␊ |
1013 | ␉␉␉{␊ |
1014 | ␉␉␉␉if ( map->bvrcnt == 0 )␊ |
1015 | ␉␉␉␉{␊ |
1016 | ␉␉␉␉␉map->bvr = booterUFS;␊ |
1017 | ␉␉␉␉␉map->bvrcnt++;␊ |
1018 | ␉␉␉␉}␊ |
1019 | ␉␉␉␉else␊ |
1020 | ␉␉␉␉{␊ |
1021 | ␉␉␉␉␉free( booterUFS );␊ |
1022 | ␉␉␉␉}␊ |
1023 | ␉␉␉}␊ |
1024 | #endif␊ |
1025 | ␉␉}␊ |
1026 | ␉} while (0);␊ |
1027 | ␊ |
1028 | ␉/*␊ |
1029 | ␉ * If no FDisk partition, then we will check for␊ |
1030 | ␉ * an Apple partition map elsewhere.␊ |
1031 | ␉ */␊ |
1032 | #if UNUSED␊ |
1033 | ␉if (map->bvrcnt == 0)␊ |
1034 | ␉{␊ |
1035 | ␉␉static struct fdisk_part cdpart;␊ |
1036 | ␉␉cdpart.systid = 0xCD;␊ |
1037 | ␊ |
1038 | ␉␉/* Let's try assuming we are on a hybrid HFS/ISO9660 CD. */␊ |
1039 | ␉␉bvr = newFDiskBVRef(␊ |
1040 | ␉␉␉biosdev, 0,␊ |
1041 | ␉␉␉0,␊ |
1042 | ␉␉␉&cdpart,␊ |
1043 | ␉␉␉HFSInitPartition,␊ |
1044 | ␉␉␉HFSLoadFile,␊ |
1045 | ␉␉␉HFSReadFile,␊ |
1046 | ␉␉␉HFSGetDirEntry,␊ |
1047 | ␉␉␉HFSGetFileBlock,␊ |
1048 | ␉␉␉HFSGetUUID,␊ |
1049 | ␉␉␉0,␊ |
1050 | ␉␉␉kBIOSDevTypeHardDrive, 0);␊ |
1051 | ␉␉bvr->next = map->bvr;␊ |
1052 | ␉␉map->bvr = bvr;␊ |
1053 | ␉␉map->bvrcnt++;␊ |
1054 | ␉}␊ |
1055 | #endif␊ |
1056 | ␉// Actually this should always be true given the above code␊ |
1057 | ␉if(map == gDiskBVMap)␊ |
1058 | ␉{␊ |
1059 | ␉␉// Don't leave a null map in the chain␊ |
1060 | ␉␉if(map->bvrcnt == 0 && map->bvr == NULL)␊ |
1061 | ␉␉{␊ |
1062 | ␉␉␉gDiskBVMap = map->next;␊ |
1063 | ␉␉␉free(map);␊ |
1064 | ␉␉␉map = NULL;␊ |
1065 | ␉␉}␊ |
1066 | ␉}␊ |
1067 | ␊ |
1068 | ␉if (countPtr) *countPtr = map ? map->bvrcnt : 0;␊ |
1069 | ␊ |
1070 | ␉␉return map ? map->bvr : NULL;␊ |
1071 | }␊ |
1072 | ␊ |
1073 | //==============================================================================␊ |
1074 | ␊ |
1075 | static BVRef diskScanAPMBootVolumes( int biosdev, int * countPtr )␊ |
1076 | {␊ |
1077 | ␉struct DiskBVMap * map;␊ |
1078 | ␉struct Block0 *block0_p;␊ |
1079 | ␉unsigned int blksize;␊ |
1080 | ␉unsigned int factor;␊ |
1081 | ␉void *buffer = malloc(BPS);␊ |
1082 | ␊ |
1083 | ␉if (!buffer)␊ |
1084 | ␉{␊ |
1085 | ␉␉return NULL;␊ |
1086 | ␉}␊ |
1087 | ␊ |
1088 | ␉/* Check for alternate block size */␊ |
1089 | ␉if (readBytes( biosdev, 0, 0, BPS, buffer ) != 0)␊ |
1090 | ␉{␊ |
1091 | ␉␉return NULL;␊ |
1092 | ␉}␊ |
1093 | ␉block0_p = buffer;␊ |
1094 | ␉if (OSSwapBigToHostInt16(block0_p->sbSig) == BLOCK0_SIGNATURE)␊ |
1095 | ␉{␊ |
1096 | ␉␉blksize = OSSwapBigToHostInt16(block0_p->sbBlkSize);␊ |
1097 | ␉␉if (blksize != BPS)␊ |
1098 | ␉␉{␊ |
1099 | ␉␉␉free(buffer);␊ |
1100 | ␉␉␉buffer = malloc(blksize);␊ |
1101 | ␉␉␉if (!buffer)␊ |
1102 | ␉␉␉{␊ |
1103 | ␉␉␉␉return NULL;␊ |
1104 | ␉␉␉}␊ |
1105 | ␉␉}␊ |
1106 | ␉␉factor = blksize / BPS;␊ |
1107 | ␉}␊ |
1108 | ␉else␊ |
1109 | ␉{␊ |
1110 | ␉␉blksize = BPS;␊ |
1111 | ␉␉factor = 1;␊ |
1112 | ␉}␊ |
1113 | ␊ |
1114 | ␉do␊ |
1115 | ␉{␊ |
1116 | ␉␉// Create a new mapping.␊ |
1117 | ␊ |
1118 | ␉␉map = (struct DiskBVMap *) malloc( sizeof(*map) );␊ |
1119 | ␉␉if ( map )␊ |
1120 | ␉␉{␊ |
1121 | ␉␉␉int error;␊ |
1122 | ␉␉␉DPME *dpme_p = (DPME *)buffer;␊ |
1123 | ␉␉␉UInt32 i, npart = UINT_MAX;␊ |
1124 | ␉␉␉BVRef bvr;␊ |
1125 | ␊ |
1126 | ␉␉␉map->biosdev = biosdev;␊ |
1127 | ␉␉␉map->bvr = NULL;␊ |
1128 | ␉␉␉map->bvrcnt = 0;␊ |
1129 | ␉␉␉map->next = gDiskBVMap;␊ |
1130 | ␉␉␉gDiskBVMap = map;␊ |
1131 | ␊ |
1132 | ␉␉␉for (i=0; i<npart; i++)␊ |
1133 | ␉␉␉{␊ |
1134 | ␉␉␉␉error = readBytes( biosdev, (kAPMSector + i) * factor, 0, blksize, buffer );␊ |
1135 | ␊ |
1136 | ␉␉␉␉if (error || OSSwapBigToHostInt16(dpme_p->dpme_signature) != DPME_SIGNATURE)␊ |
1137 | ␉␉␉␉{␊ |
1138 | ␉␉␉␉␉break;␊ |
1139 | ␉␉␉␉}␊ |
1140 | ␊ |
1141 | ␉␉␉␉if (i==0)␊ |
1142 | ␉␉␉␉{␊ |
1143 | ␉␉␉␉␉npart = OSSwapBigToHostInt32(dpme_p->dpme_map_entries);␊ |
1144 | ␉␉␉␉}␊ |
1145 | ␉␉␉␉/*␊ |
1146 | ␉␉␉␉printf("name = %s, %s%s %d -> %d [%d -> %d] {%d}\n",␊ |
1147 | ␉␉␉␉dpme.dpme_name, dpme.dpme_type, (dpme.dpme_flags & DPME_FLAGS_BOOTABLE) ? "(bootable)" : "",␊ |
1148 | ␉␉␉␉dpme.dpme_pblock_start, dpme.dpme_pblocks,␊ |
1149 | ␉␉␉␉dpme.dpme_lblock_start, dpme.dpme_lblocks,␊ |
1150 | ␉␉␉␉dpme.dpme_boot_block);␊ |
1151 | ␉␉␉␉*/␊ |
1152 | ␊ |
1153 | ␉␉␉␉if (strcmp(dpme_p->dpme_type, "Apple_HFS") == 0)␊ |
1154 | ␉␉␉␉{␊ |
1155 | ␉␉␉␉␉bvr = newAPMBVRef(biosdev,␊ |
1156 | ␉␉␉␉␉i,␊ |
1157 | ␉␉␉␉␉OSSwapBigToHostInt32(dpme_p->dpme_pblock_start) * factor,␊ |
1158 | ␉␉␉␉␉dpme_p,␊ |
1159 | ␉␉␉␉␉HFSInitPartition,␊ |
1160 | ␉␉␉␉␉HFSLoadFile,␊ |
1161 | ␉␉␉␉␉HFSReadFile,␊ |
1162 | ␉␉␉␉␉HFSGetDirEntry,␊ |
1163 | ␉␉␉␉␉HFSGetFileBlock,␊ |
1164 | ␉␉␉␉␉HFSGetUUID,␊ |
1165 | ␉␉␉␉␉HFSGetDescription,␊ |
1166 | ␉␉␉␉␉HFSFree,␊ |
1167 | ␉␉␉␉␉0,␊ |
1168 | ␉␉␉␉␉kBIOSDevTypeHardDrive, 0);␊ |
1169 | ␉␉␉␉␉bvr->next = map->bvr;␊ |
1170 | ␉␉␉␉␉map->bvr = bvr;␊ |
1171 | ␉␉␉␉␉map->bvrcnt++;␊ |
1172 | ␉␉␉␉}␊ |
1173 | ␉␉␉}␊ |
1174 | ␉␉}␊ |
1175 | ␉} while (0);␊ |
1176 | ␊ |
1177 | ␉free(buffer);␊ |
1178 | ␊ |
1179 | ␉if (countPtr) *countPtr = map ? map->bvrcnt : 0;␊ |
1180 | ␊ |
1181 | ␉return map ? map->bvr : NULL;␊ |
1182 | }␊ |
1183 | ␊ |
1184 | //==============================================================================␊ |
1185 | ␊ |
1186 | /*␊ |
1187 | * Trying to figure out the filsystem type of a given partition.␊ |
1188 | */␊ |
1189 | static int probeFileSystem(int biosdev, unsigned int blkoff)␊ |
1190 | {␊ |
1191 | ␉// detected filesystem type;␊ |
1192 | ␉int result = -1;␊ |
1193 | ␉int fatbits;␊ |
1194 | ␊ |
1195 | ␉// Allocating buffer for 4 sectors.␊ |
1196 | ␉const void * probeBuffer = malloc(PROBEFS_SIZE);␊ |
1197 | ␉if (probeBuffer == NULL)␊ |
1198 | ␉{␊ |
1199 | ␉␉goto exit;␊ |
1200 | ␉}␊ |
1201 | ␊ |
1202 | ␉// Reading first 4 sectors of current partition␊ |
1203 | ␉int error = readBytes(biosdev, blkoff, 0, PROBEFS_SIZE, (void *)probeBuffer);␊ |
1204 | ␊ |
1205 | ␉if (error)␊ |
1206 | ␉{␊ |
1207 | ␉␉goto exit;␊ |
1208 | ␉}␊ |
1209 | ␊ |
1210 | ␉if (HFSProbe(probeBuffer))␊ |
1211 | ␉{␊ |
1212 | ␉␉result = FDISK_HFS;␊ |
1213 | ␉}␊ |
1214 | ␉else if (EX2Probe(probeBuffer))␊ |
1215 | ␉{␊ |
1216 | ␉␉result = FDISK_LINUX;␊ |
1217 | ␉}␊ |
1218 | ␉else if (FreeBSDProbe(probeBuffer))␊ |
1219 | ␉{␊ |
1220 | ␉␉result = FDISK_FREEBSD;␊ |
1221 | ␉}␊ |
1222 | ␊ |
1223 | ␉else if (OpenBSDProbe(probeBuffer))␊ |
1224 | ␉{␊ |
1225 | ␉␉result = FDISK_OPENBSD;␊ |
1226 | ␉}␊ |
1227 | ␊ |
1228 | ␉else if (BeFSProbe(probeBuffer))␊ |
1229 | ␉{␊ |
1230 | ␉␉result = FDISK_BEFS;␊ |
1231 | ␉}␊ |
1232 | ␊ |
1233 | ␉else if (NTFSProbe(probeBuffer))␊ |
1234 | ␉{␊ |
1235 | ␉␉result = FDISK_NTFS;␊ |
1236 | ␉}␊ |
1237 | ␊ |
1238 | ␉else if ( (fatbits = MSDOSProbe(probeBuffer)) )␊ |
1239 | ␉{␊ |
1240 | ␉␉switch (fatbits)␊ |
1241 | ␉␉{␊ |
1242 | ␉␉␉case 32:␊ |
1243 | ␉␉␉default:␊ |
1244 | ␉␉␉␉result = FDISK_FAT32;␊ |
1245 | ␉␉␉␉break;␊ |
1246 | ␉␉␉case 16:␊ |
1247 | ␉␉␉␉result = FDISK_DOS16B;␊ |
1248 | ␉␉␉␉break;␊ |
1249 | ␉␉␉case 12:␊ |
1250 | ␉␉␉␉result = FDISK_DOS12;␊ |
1251 | ␉␉␉␉break;␉␉␉ ␊ |
1252 | ␉␉}␊ |
1253 | ␉}␊ |
1254 | ␉else␊ |
1255 | ␉{␊ |
1256 | ␉␉// Couldn't detect filesystem type␊ |
1257 | ␉␉result = 0;␊ |
1258 | ␉}␊ |
1259 | ␊ |
1260 | exit:␊ |
1261 | ␉if (probeBuffer != NULL) free((void *)probeBuffer);␊ |
1262 | ␉{␊ |
1263 | ␉␉return result;␊ |
1264 | ␉}␊ |
1265 | }␊ |
1266 | ␊ |
1267 | //==============================================================================␊ |
1268 | ␊ |
1269 | static bool isPartitionUsed(gpt_ent * partition)␊ |
1270 | {␊ |
1271 | ␊ |
1272 | ␉// Ask whether the given partition is used.␊ |
1273 | ␊ |
1274 | ␉return efi_guid_is_null((EFI_GUID const*)partition->ent_type) ? false : true;␊ |
1275 | }␊ |
1276 | ␊ |
1277 | //==============================================================================␊ |
1278 | ␊ |
1279 | static BVRef diskScanGPTBootVolumes(int biosdev, int * countPtr)␊ |
1280 | {␊ |
1281 | ␉struct DiskBVMap * map = NULL;␊ |
1282 | ␊ |
1283 | ␉void *buffer = malloc(BPS);␊ |
1284 | ␉int error;␊ |
1285 | ␉if ( (error = readBytes( biosdev, /*secno*/0, 0, BPS, buffer )) != 0)␊ |
1286 | ␉{␊ |
1287 | ␉␉verbose("Failed to read boot sector from BIOS device %02xh. Error=%d\n", biosdev, error);␊ |
1288 | ␉␉goto scanErr;␊ |
1289 | ␉}␊ |
1290 | ␉struct REAL_disk_blk0 *fdiskMap = buffer;␊ |
1291 | ␉if ( OSSwapLittleToHostInt16(fdiskMap->signature) != DISK_SIGNATURE )␊ |
1292 | ␉{␊ |
1293 | ␉␉verbose("Failed to find boot signature on BIOS device %02xh\n", biosdev);␊ |
1294 | ␉␉goto scanErr;␊ |
1295 | ␉}␊ |
1296 | ␊ |
1297 | ␉int fdiskID = 0;␊ |
1298 | ␉unsigned index;␊ |
1299 | ␉for ( index = 0; index < FDISK_NPART; index++ )␊ |
1300 | ␉{␊ |
1301 | ␉␉if ( fdiskMap->parts[index].systid )␊ |
1302 | ␉␉{␊ |
1303 | ␉␉␉if ( fdiskMap->parts[index].systid == 0xEE )␊ |
1304 | ␉␉␉{␊ |
1305 | ␉␉␉␉// Fail if two 0xEE partitions are present which␊ |
1306 | ␉␉␉␉// means the FDISK code will wind up parsing it.␊ |
1307 | ␉␉␉␉if ( fdiskID ) goto scanErr;␊ |
1308 | ␊ |
1309 | ␉␉␉␉fdiskID = index + 1;␊ |
1310 | ␉␉␉}␊ |
1311 | ␉␉}␊ |
1312 | ␉}␊ |
1313 | ␊ |
1314 | ␉if ( fdiskID == 0 )␊ |
1315 | ␉{␊ |
1316 | ␉␉goto scanErr;␊ |
1317 | ␉}␊ |
1318 | ␊ |
1319 | ␉verbose("Attempting to read GPT\n");␊ |
1320 | ␊ |
1321 | ␉if(readBytes(biosdev, 1, 0, BPS, buffer) != 0)␊ |
1322 | ␉{␊ |
1323 | ␉␉goto scanErr;␊ |
1324 | ␉}␊ |
1325 | ␊ |
1326 | ␉gpt_hdr *headerMap = buffer;␊ |
1327 | ␊ |
1328 | ␉// Determine whether the partition header signature is present.␊ |
1329 | ␊ |
1330 | ␉if ( memcmp(headerMap->hdr_sig, GPT_HDR_SIG, strlen(GPT_HDR_SIG)) )␊ |
1331 | ␉{␊ |
1332 | ␉␉goto scanErr;␊ |
1333 | ␉}␊ |
1334 | ␊ |
1335 | ␉// Determine whether the partition header size is valid.␊ |
1336 | ␊ |
1337 | ␉UInt32 headerCheck = OSSwapLittleToHostInt32(headerMap->hdr_crc_self);␊ |
1338 | ␉UInt32 headerSize = OSSwapLittleToHostInt32(headerMap->hdr_size);␊ |
1339 | ␊ |
1340 | ␉if ( headerSize < offsetof(gpt_hdr, padding) )␊ |
1341 | ␉{␊ |
1342 | ␉␉goto scanErr;␊ |
1343 | ␉}␊ |
1344 | ␊ |
1345 | ␉if ( headerSize > BPS )␊ |
1346 | ␉{␊ |
1347 | ␉␉goto scanErr;␊ |
1348 | ␉}␊ |
1349 | ␊ |
1350 | ␉// Determine whether the partition header checksum is valid.␊ |
1351 | ␊ |
1352 | ␉headerMap->hdr_crc_self = 0;␊ |
1353 | ␊ |
1354 | ␉if ( crc32(0, headerMap, headerSize) != headerCheck )␊ |
1355 | ␉{␊ |
1356 | ␉␉goto scanErr;␊ |
1357 | ␉}␊ |
1358 | ␊ |
1359 | ␉// Determine whether the partition entry size is valid.␊ |
1360 | ␊ |
1361 | ␉UInt64 gptBlock = 0;␊ |
1362 | ␉UInt32 gptCheck = 0;␊ |
1363 | ␉UInt32 gptCount = 0;␊ |
1364 | ␉UInt32 gptID = 0;␊ |
1365 | ␉gpt_ent * gptMap = 0;␊ |
1366 | ␉UInt32 gptSize = 0;␊ |
1367 | ␊ |
1368 | ␉gptBlock = OSSwapLittleToHostInt64(headerMap->hdr_lba_table);␊ |
1369 | ␉gptCheck = OSSwapLittleToHostInt32(headerMap->hdr_crc_table);␊ |
1370 | ␉gptCount = OSSwapLittleToHostInt32(headerMap->hdr_entries);␊ |
1371 | ␉gptSize = OSSwapLittleToHostInt32(headerMap->hdr_entsz);␊ |
1372 | ␊ |
1373 | ␉if ( gptSize < sizeof(gpt_ent) )␊ |
1374 | ␉{␊ |
1375 | ␉␉goto scanErr;␊ |
1376 | ␉}␊ |
1377 | ␊ |
1378 | ␉// Allocate a buffer large enough to hold one map, rounded to a media block.␊ |
1379 | ␉free(buffer);␊ |
1380 | ␉buffer = NULL;␊ |
1381 | ␊ |
1382 | ␉UInt32 bufferSize = IORound(gptCount * gptSize, BPS);␊ |
1383 | ␉if (bufferSize == 0)␊ |
1384 | ␉{␊ |
1385 | ␉␉goto scanErr;␊ |
1386 | ␉}␊ |
1387 | ␉buffer = malloc(bufferSize);␊ |
1388 | ␊ |
1389 | ␉if (readBytes(biosdev, gptBlock, 0, bufferSize, buffer) != 0)␊ |
1390 | ␉{␊ |
1391 | ␉␉goto scanErr;␊ |
1392 | ␉}␊ |
1393 | ␉verbose("Read GPT\n");␊ |
1394 | ␊ |
1395 | ␉// Allocate a new map for this BIOS device and insert it into the chain␊ |
1396 | ␉map = malloc(sizeof(*map));␊ |
1397 | ␉map->biosdev = biosdev;␊ |
1398 | ␉map->bvr = NULL;␊ |
1399 | ␉map->bvrcnt = 0;␊ |
1400 | ␉map->next = gDiskBVMap;␊ |
1401 | ␉gDiskBVMap = map;␊ |
1402 | ␊ |
1403 | ␉// fdisk like partition type id.␊ |
1404 | ␉int fsType = 0;␊ |
1405 | ␊ |
1406 | ␉for(gptID = 1; gptID <= gptCount; ++gptID)␊ |
1407 | ␉{␊ |
1408 | ␉␉BVRef bvr = NULL;␊ |
1409 | ␉␉unsigned int bvrFlags = 0;␊ |
1410 | ␊ |
1411 | ␉␉// size on disk can be larger than sizeof(gpt_ent)␊ |
1412 | ␉␉gptMap = (gpt_ent *) ( buffer + ( (gptID - 1) * gptSize) );␊ |
1413 | ␊ |
1414 | ␉␉// NOTE: EFI_GUID's are in LE and we know we're on an x86.␊ |
1415 | ␉␉// The IOGUIDPartitionScheme.cpp code uses byte-based UUIDs, we don't.␊ |
1416 | ␊ |
1417 | ␉␉if (isPartitionUsed(gptMap))␊ |
1418 | ␉␉{␊ |
1419 | ␉␉␉char stringuuid[100];␊ |
1420 | ␉␉␉efi_guid_unparse_upper((EFI_GUID*)gptMap->ent_type, stringuuid);␊ |
1421 | ␉␉␉verbose("Reading GPT partition %d, type %s\n", gptID, stringuuid);␊ |
1422 | ␊ |
1423 | ␉␉␉// Getting fdisk like partition type.␊ |
1424 | ␉␉␉fsType = probeFileSystem(biosdev, gptMap->ent_lba_start);␊ |
1425 | ␊ |
1426 | ␉␉␉␉if ( (efi_guid_compare(&GPT_BOOT_GUID, (EFI_GUID const*)gptMap->ent_type) == 0) ||␊ |
1427 | ␉␉␉␉(efi_guid_compare(&GPT_HFS_GUID, (EFI_GUID const*)gptMap->ent_type) == 0) )␊ |
1428 | ␉␉␉␉{␊ |
1429 | ␉␉␉␉␉bvrFlags = (efi_guid_compare(&GPT_BOOT_GUID, (EFI_GUID const*)gptMap->ent_type) == 0) ? kBVFlagBooter : 0;␊ |
1430 | ␉␉␉␉␉bvr = newGPTBVRef(biosdev,␊ |
1431 | ␉␉␉␉␉gptID,␊ |
1432 | ␉␉␉␉␉gptMap->ent_lba_start,␊ |
1433 | ␉␉␉␉␉gptMap,␊ |
1434 | ␉␉␉␉␉HFSInitPartition,␊ |
1435 | ␉␉␉␉␉HFSLoadFile,␊ |
1436 | ␉␉␉␉␉HFSReadFile,␊ |
1437 | ␉␉␉␉␉HFSGetDirEntry,␊ |
1438 | ␉␉␉␉␉HFSGetFileBlock,␊ |
1439 | ␉␉␉␉␉HFSGetUUID,␊ |
1440 | ␉␉␉␉␉HFSGetDescription,␊ |
1441 | ␉␉␉␉␉HFSFree,␊ |
1442 | ␉␉␉␉␉0,␊ |
1443 | ␉␉␉␉␉kBIOSDevTypeHardDrive, bvrFlags);␊ |
1444 | ␉␉␉␉}␊ |
1445 | ␊ |
1446 | ␉␉␉// zef - foreign OS support␊ |
1447 | ␉␉␉if ( (efi_guid_compare(&GPT_BASICDATA_GUID, (EFI_GUID const*)gptMap->ent_type) == 0) ||␊ |
1448 | ␉␉␉(efi_guid_compare(&GPT_BASICDATA2_GUID, (EFI_GUID const*)gptMap->ent_type) == 0) )␊ |
1449 | ␉␉␉{␊ |
1450 | ␉␉␉␉switch (fsType)␊ |
1451 | ␉␉␉␉{␊ |
1452 | ␉␉␉␉␉case FDISK_NTFS:␊ |
1453 | ␉␉␉␉␉␉bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap,␊ |
1454 | ␉␉␉␉␉␉0, 0, 0, 0, 0, 0, NTFSGetDescription,␊ |
1455 | ␉␉␉␉␉␉(BVFree)free, 0, kBIOSDevTypeHardDrive, 0);␊ |
1456 | ␉␉␉␉␉␉break;␊ |
1457 | ␊ |
1458 | ␉␉␉␉␉case FDISK_LINUX:␊ |
1459 | ␉␉␉␉␉␉bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap,␊ |
1460 | ␉␉␉␉␉␉0, 0, 0, 0, 0, 0, EX2GetDescription,␊ |
1461 | ␉␉␉␉␉␉(BVFree)free, 0, kBIOSDevTypeHardDrive, 0);␊ |
1462 | ␉␉␉␉␉␉break;␊ |
1463 | ␊ |
1464 | ␉␉␉␉␉default:␊ |
1465 | ␉␉␉␉␉␉bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap,␊ |
1466 | ␉␉␉␉␉␉0, 0, 0, 0, 0, 0, 0,␊ |
1467 | ␉␉␉␉␉␉(BVFree)free, 0, kBIOSDevTypeHardDrive, 0);␊ |
1468 | ␉␉␉␉␉␉break;␊ |
1469 | ␉␉␉␉}␊ |
1470 | ␊ |
1471 | ␉␉␉}␊ |
1472 | ␊ |
1473 | ␉␉␉// turbo - save our booter partition␊ |
1474 | ␉␉␉// zef - only on original boot device␊ |
1475 | ␉␉␉if ( (efi_guid_compare(&GPT_EFISYS_GUID, (EFI_GUID const*)gptMap->ent_type) == 0) )␊ |
1476 | ␉␉␉{␊ |
1477 | ␉␉␉␉switch (fsType)␊ |
1478 | ␉␉␉␉{␊ |
1479 | ␉␉␉␉␉case FDISK_HFS:␊ |
1480 | ␉␉␉␉␉if (readBootSector( biosdev, gptMap->ent_lba_start, (void *)0x7e00 ) == 0)␊ |
1481 | ␉␉␉␉␉{␊ |
1482 | ␉␉␉␉␉␉bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap,␊ |
1483 | ␉␉␉␉␉␉HFSInitPartition,␊ |
1484 | ␉␉␉␉␉␉HFSLoadFile,␊ |
1485 | ␉␉␉␉␉␉HFSReadFile,␊ |
1486 | ␉␉␉␉␉␉HFSGetDirEntry,␊ |
1487 | ␉␉␉␉␉␉HFSGetFileBlock,␊ |
1488 | ␉␉␉␉␉␉HFSGetUUID,␊ |
1489 | ␉␉␉␉␉␉HFSGetDescription,␊ |
1490 | ␉␉␉␉␉␉HFSFree,␊ |
1491 | ␉␉␉␉␉␉0, kBIOSDevTypeHardDrive, kBVFlagEFISystem);␊ |
1492 | ␉␉␉␉␉}␊ |
1493 | ␉␉␉␉␉break;␊ |
1494 | ␊ |
1495 | ␉␉␉␉␉case FDISK_FAT32:␊ |
1496 | ␉␉␉␉␉if (testFAT32EFIBootSector( biosdev, gptMap->ent_lba_start, (void *)0x7e00 ) == 0)␊ |
1497 | ␉␉␉␉␉{␊ |
1498 | ␉␉␉␉␉␉bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap,␊ |
1499 | ␉␉␉␉␉␉MSDOSInitPartition,␊ |
1500 | ␉␉␉␉␉␉MSDOSLoadFile,␊ |
1501 | ␉␉␉␉␉␉MSDOSReadFile,␊ |
1502 | ␉␉␉␉␉␉MSDOSGetDirEntry,␊ |
1503 | ␉␉␉␉␉␉MSDOSGetFileBlock,␊ |
1504 | ␉␉␉␉␉␉MSDOSGetUUID,␊ |
1505 | ␉␉␉␉␉␉MSDOSGetDescription,␊ |
1506 | ␉␉␉␉␉␉MSDOSFree,␊ |
1507 | ␉␉␉␉␉␉0, kBIOSDevTypeHardDrive, kBVFlagEFISystem);␊ |
1508 | ␉␉␉␉␉}␊ |
1509 | ␉␉␉␉␉break;␊ |
1510 | ␊ |
1511 | ␉␉␉␉␉default:␊ |
1512 | ␉␉␉␉␉if (biosdev == gBIOSDev)␊ |
1513 | ␉␉␉␉␉{␊ |
1514 | ␉␉␉␉␉␉gBIOSBootVolume = bvr;␊ |
1515 | ␉␉␉␉␉}␊ |
1516 | ␉␉␉␉␉break;␊ |
1517 | ␉␉␉␉}␊ |
1518 | ␉␉␉}␊ |
1519 | ␊ |
1520 | ␉␉␉if (bvr)␊ |
1521 | ␉␉␉{␊ |
1522 | ␉␉␉␉// Fixup bvr with the fake fdisk partition type.␊ |
1523 | ␉␉␉␉if (fsType > 0)␊ |
1524 | ␉␉␉␉{␊ |
1525 | ␉␉␉␉␉bvr->part_type = fsType;␊ |
1526 | ␉␉␉␉}␊ |
1527 | ␊ |
1528 | ␉␉␉␉bvr->next = map->bvr;␊ |
1529 | ␉␉␉␉map->bvr = bvr;␊ |
1530 | ␉␉␉␉++map->bvrcnt;␊ |
1531 | ␉␉␉}␊ |
1532 | ␊ |
1533 | ␉␉}␊ |
1534 | ␉}␊ |
1535 | ␊ |
1536 | ␉scanErr:␊ |
1537 | ␉free(buffer);␊ |
1538 | ␊ |
1539 | ␉if(map)␊ |
1540 | ␉{␊ |
1541 | ␉␉if(countPtr) *countPtr = map->bvrcnt;␊ |
1542 | ␉␉{␊ |
1543 | ␉␉␉return map->bvr;␊ |
1544 | ␉␉}␊ |
1545 | ␊ |
1546 | ␉}␊ |
1547 | ␉else␊ |
1548 | ␉{␊ |
1549 | ␉␉if(countPtr) *countPtr = 0;␊ |
1550 | ␉␉{␊ |
1551 | ␉␉␉return NULL;␊ |
1552 | ␉␉}␊ |
1553 | ␉}␊ |
1554 | }␊ |
1555 | ␊ |
1556 | static bool getOSVersion(BVRef bvr, char *str)␊ |
1557 | {␊ |
1558 | ␉bool valid = false;␉␊ |
1559 | ␉config_file_t systemVersion;␊ |
1560 | ␉char dirSpec[512];␉␊ |
1561 | ␊ |
1562 | ␉sprintf(dirSpec, "hd(%d,%d)/System/Library/CoreServices/SystemVersion.plist", BIOS_DEV_UNIT(bvr), bvr->part_no);␊ |
1563 | ␉␊ |
1564 | ␉if (!loadConfigFile(dirSpec, &systemVersion))␊ |
1565 | ␉{␊ |
1566 | ␉␉valid = true;␊ |
1567 | ␉}␊ |
1568 | ␉else␊ |
1569 | ␉{␊ |
1570 | ␉/* Mac OS X Server */␊ |
1571 | ␉␉sprintf(dirSpec, "hd(%d,%d)/System/Library/CoreServices/ServerVersion.plist", BIOS_DEV_UNIT(bvr), bvr->part_no);␊ |
1572 | ␊ |
1573 | ␉␉if (!loadConfigFile(dirSpec, &systemVersion))␊ |
1574 | ␉␉{␉␊ |
1575 | ␉␉␉bvr->OSisServer = true;␊ |
1576 | ␉␉␉valid = true;␊ |
1577 | ␉␉}␊ |
1578 | ␉}␊ |
1579 | ␉␊ |
1580 | ␉if (valid)␊ |
1581 | ␉{␉␉␊ |
1582 | ␉␉const char *val;␊ |
1583 | ␉␉int len;␊ |
1584 | ␉␉␊ |
1585 | ␉␉if (getValueForKey(kProductVersion, &val, &len, &systemVersion))␊ |
1586 | ␉␉{␊ |
1587 | ␉␉␉// getValueForKey uses const char for val␊ |
1588 | ␉␉␉// so copy it and trim␊ |
1589 | ␉␉␉*str = '\0';␊ |
1590 | ␉␉␉strncat(str, val, MIN(len, 4));␊ |
1591 | ␉␉}␊ |
1592 | ␉␉else␊ |
1593 | ␉␉␉valid = false;␊ |
1594 | ␉}␊ |
1595 | ␉␊ |
1596 | ␉return valid;␊ |
1597 | }␊ |
1598 | ␊ |
1599 | //==============================================================================␊ |
1600 | ␊ |
1601 | static void scanFSLevelBVRSettings(BVRef chain)␊ |
1602 | {␊ |
1603 | ␉BVRef bvr;␊ |
1604 | ␉char dirSpec[512], fileSpec[512];␊ |
1605 | ␉char label[BVSTRLEN];␊ |
1606 | ␉int ret;␊ |
1607 | ␉long flags, time;␊ |
1608 | ␉int fh, fileSize, error;␊ |
1609 | ␊ |
1610 | ␉for (bvr = chain; bvr; bvr = bvr->next)␊ |
1611 | ␉{␊ |
1612 | ␉␉ret = -1;␊ |
1613 | ␉␉error = 0;␊ |
1614 | ␊ |
1615 | ␉␉//␊ |
1616 | ␉␉// Check for alternate volume label on boot helper partitions.␊ |
1617 | ␉␉//␊ |
1618 | ␉␉if (bvr->flags & kBVFlagBooter)␊ |
1619 | ␉␉{␊ |
1620 | ␉␉␉sprintf(dirSpec, "hd(%d,%d)/System/Library/CoreServices/", BIOS_DEV_UNIT(bvr), bvr->part_no);␊ |
1621 | ␉␉␉strcpy(fileSpec, ".disk_label.contentDetails");␊ |
1622 | ␉␉␉ret = GetFileInfo(dirSpec, fileSpec, &flags, &time);␊ |
1623 | ␉␉␉if (!ret)␊ |
1624 | ␉␉␉{␊ |
1625 | ␉␉␉␉fh = open(strcat(dirSpec, fileSpec), 0);␊ |
1626 | ␉␉␉␉fileSize = file_size(fh);␊ |
1627 | ␉␉␉␉if (fileSize > 0 && fileSize < BVSTRLEN)␊ |
1628 | ␉␉␉␉{␊ |
1629 | ␉␉␉␉␉if (read(fh, label, fileSize) != fileSize)␊ |
1630 | ␉␉␉␉␉{␊ |
1631 | ␉␉␉␉␉␉error = -1;␊ |
1632 | ␉␉␉␉␉}␊ |
1633 | ␉␉␉␉}␊ |
1634 | ␉␉␉␉else␊ |
1635 | ␉␉␉␉{␊ |
1636 | ␉␉␉␉␉error = -1;␊ |
1637 | ␉␉␉␉}␊ |
1638 | ␊ |
1639 | ␉␉␉␉close(fh);␊ |
1640 | ␊ |
1641 | ␉␉␉␉if (!error)␊ |
1642 | ␉␉␉␉{␊ |
1643 | ␉␉␉␉␉label[fileSize] = '\0';␊ |
1644 | ␉␉␉␉␉strcpy(bvr->altlabel, label);␊ |
1645 | ␉␉␉␉}␊ |
1646 | ␉␉␉}␊ |
1647 | ␉␉}␊ |
1648 | ␊ |
1649 | ␉␉// Check for SystemVersion.plist or ServerVersion.plist to determine if a volume hosts an installed system.␊ |
1650 | ␊ |
1651 | ␉␉if (bvr->flags & kBVFlagNativeBoot)␊ |
1652 | ␉␉{␊ |
1653 | ␉␉␉if (getOSVersion(bvr,bvr->OSVersion) == true)␊ |
1654 | ␉␉␉{␊ |
1655 | ␉␉␉␉bvr->flags |= kBVFlagSystemVolume;␊ |
1656 | ␉␉␉}␊ |
1657 | ␉␉}␊ |
1658 | ␊ |
1659 | ␉}␊ |
1660 | }␊ |
1661 | ␊ |
1662 | void rescanBIOSDevice(int biosdev)␊ |
1663 | {␊ |
1664 | ␉struct DiskBVMap *oldMap = diskResetBootVolumes(biosdev);␊ |
1665 | ␉CacheReset();␊ |
1666 | ␉diskFreeMap(oldMap);␊ |
1667 | ␉oldMap = NULL;␊ |
1668 | ␉scanBootVolumes(biosdev, 0);␊ |
1669 | }␊ |
1670 | ␊ |
1671 | struct DiskBVMap* diskResetBootVolumes(int biosdev)␊ |
1672 | {␊ |
1673 | ␉struct DiskBVMap * map;␊ |
1674 | ␉struct DiskBVMap *prevMap = NULL;␊ |
1675 | ␉for ( map = gDiskBVMap; map; prevMap = map, map = map->next )␊ |
1676 | ␉{␊ |
1677 | ␉␉if ( biosdev == map->biosdev )␊ |
1678 | ␉␉{␊ |
1679 | ␉␉␉break;␊ |
1680 | ␉␉}␊ |
1681 | ␉}␊ |
1682 | ␊ |
1683 | ␉if(map != NULL)␊ |
1684 | ␉{␊ |
1685 | ␉␉verbose("Resetting BIOS device %xh\n", biosdev);␊ |
1686 | ␉␉// Reset the biosbuf cache␊ |
1687 | ␉␉cache_valid = false;␊ |
1688 | ␉␉if(map == gDiskBVMap)␊ |
1689 | ␉␉{␊ |
1690 | ␉␉␉gDiskBVMap = map->next;␊ |
1691 | ␉␉}␊ |
1692 | ␉␉else if(prevMap != NULL)␊ |
1693 | ␉␉{␊ |
1694 | ␉␉␉prevMap->next = map->next;␊ |
1695 | ␉␉}␊ |
1696 | ␉␉else␊ |
1697 | ␉␉{␊ |
1698 | ␉␉␉stop("");␊ |
1699 | ␉␉}␊ |
1700 | ␉}␊ |
1701 | ␉// Return the old map, either to be freed, or reinserted later␊ |
1702 | ␉return map;␊ |
1703 | }␊ |
1704 | ␊ |
1705 | //==============================================================================␊ |
1706 | ␊ |
1707 | // Frees a DiskBVMap and all of its BootVolume's␊ |
1708 | void diskFreeMap(struct DiskBVMap *map)␊ |
1709 | {␊ |
1710 | ␉if(map != NULL)␊ |
1711 | ␉{␊ |
1712 | ␉␉while(map->bvr != NULL)␊ |
1713 | ␉␉{␊ |
1714 | ␉␉␉BVRef bvr = map->bvr;␊ |
1715 | ␉␉␉map->bvr = bvr->next;␊ |
1716 | ␉␉␉(*bvr->bv_free)(bvr);␊ |
1717 | ␉␉}␊ |
1718 | ␊ |
1719 | ␉␉free(map);␊ |
1720 | ␉}␊ |
1721 | }␊ |
1722 | ␊ |
1723 | //==============================================================================␊ |
1724 | ␊ |
1725 | BVRef diskScanBootVolumes(int biosdev, int * countPtr)␊ |
1726 | {␊ |
1727 | ␉struct DiskBVMap * map = NULL;␊ |
1728 | ␉BVRef bvr;␊ |
1729 | ␉int count = 0;␊ |
1730 | ␊ |
1731 | ␉// Find an existing mapping for this device.␊ |
1732 | ␊ |
1733 | ␉for (map = gDiskBVMap; map; map = map->next)␊ |
1734 | ␉{␊ |
1735 | ␉␉if (biosdev == map->biosdev)␊ |
1736 | ␉␉{␊ |
1737 | ␉␉␉count = map->bvrcnt;␊ |
1738 | ␉␉␉break;␊ |
1739 | ␉␉}␊ |
1740 | ␉}␊ |
1741 | ␊ |
1742 | ␉if (map == NULL)␊ |
1743 | ␉{␊ |
1744 | ␉␉bvr = diskScanGPTBootVolumes(biosdev, &count);␊ |
1745 | ␉␉if (bvr == NULL)␊ |
1746 | ␉␉{␊ |
1747 | ␉␉␉bvr = diskScanFDiskBootVolumes(biosdev, &count);␊ |
1748 | ␉␉}␊ |
1749 | ␉␉if (bvr == NULL)␊ |
1750 | ␉␉{␊ |
1751 | ␉␉␉bvr = diskScanAPMBootVolumes(biosdev, &count);␊ |
1752 | ␉␉}␊ |
1753 | ␉␉if (bvr)␊ |
1754 | ␉␉{␊ |
1755 | ␉␉␉scanFSLevelBVRSettings(bvr);␊ |
1756 | ␉␉}␊ |
1757 | ␉}␊ |
1758 | ␉else␊ |
1759 | ␉{␊ |
1760 | ␉␉bvr = map->bvr;␊ |
1761 | ␉}␊ |
1762 | ␉if (countPtr)␊ |
1763 | ␉{␊ |
1764 | ␉␉*countPtr += count;␊ |
1765 | ␉}␊ |
1766 | ␉return bvr;␊ |
1767 | }␊ |
1768 | ␊ |
1769 | //==============================================================================␊ |
1770 | ␊ |
1771 | BVRef getBVChainForBIOSDev(int biosdev)␊ |
1772 | {␊ |
1773 | ␉BVRef chain = NULL;␊ |
1774 | ␉struct DiskBVMap * map = NULL;␊ |
1775 | ␊ |
1776 | ␉for (map = gDiskBVMap; map; map = map->next)␊ |
1777 | ␉{␊ |
1778 | ␉␉if (map->biosdev == biosdev)␊ |
1779 | ␉␉{␊ |
1780 | ␉␉␉chain = map->bvr;␊ |
1781 | ␉␉␉break;␊ |
1782 | ␉␉}␊ |
1783 | ␉}␊ |
1784 | ␊ |
1785 | ␉return chain;␊ |
1786 | }␊ |
1787 | ␊ |
1788 | //==============================================================================␊ |
1789 | ␊ |
1790 | BVRef newFilteredBVChain(int minBIOSDev, int maxBIOSDev, unsigned int allowFlags, unsigned int denyFlags, int *count)␊ |
1791 | {␊ |
1792 | ␉BVRef chain = NULL;␊ |
1793 | ␉BVRef bvr = NULL;␊ |
1794 | ␉BVRef newBVR = NULL;␊ |
1795 | ␉BVRef prevBVR = NULL;␊ |
1796 | ␊ |
1797 | ␉struct DiskBVMap * map = NULL;␊ |
1798 | ␉int bvCount = 0;␊ |
1799 | ␊ |
1800 | ␉const char *raw = 0;␊ |
1801 | ␉char* val = 0;␊ |
1802 | ␉int len;␊ |
1803 | ␊ |
1804 | ␉getValueForKey(kHidePartition, &raw, &len, &bootInfo->chameleonConfig);␊ |
1805 | ␉if(raw)␊ |
1806 | ␉{␊ |
1807 | ␉␉val = XMLDecode(raw); ␊ |
1808 | ␉}␊ |
1809 | ␊ |
1810 | ␉/*␊ |
1811 | ␉ * Traverse gDISKBVmap to get references for␊ |
1812 | ␉ * individual bvr chains of each drive.␊ |
1813 | ␉ */␊ |
1814 | ␉for (map = gDiskBVMap; map; map = map->next)␊ |
1815 | ␉{␊ |
1816 | ␉␉for (bvr = map->bvr; bvr; bvr = bvr->next)␊ |
1817 | ␉␉{␊ |
1818 | ␉␉␉/*␊ |
1819 | ␉␉␉ * Save the last bvr.␊ |
1820 | ␉␉␉ */␊ |
1821 | ␉␉␉if (newBVR)␊ |
1822 | ␉␉␉{␊ |
1823 | ␉␉␉␉prevBVR = newBVR;␊ |
1824 | ␉␉␉}␊ |
1825 | ␊ |
1826 | ␉␉␉/*␊ |
1827 | ␉␉␉ * Allocate and copy the matched bvr entry into a new one.␊ |
1828 | ␉␉␉ */␊ |
1829 | ␉␉␉newBVR = (BVRef) malloc(sizeof(*newBVR));␊ |
1830 | ␉␉␉if (!newBVR)␊ |
1831 | ␉␉␉{␊ |
1832 | ␉␉␉␉continue;␊ |
1833 | ␉␉␉}␊ |
1834 | ␉␉␉bcopy(bvr, newBVR, sizeof(*newBVR));␊ |
1835 | ␊ |
1836 | ␉␉␉/*␊ |
1837 | ␉␉␉ * Adjust the new bvr's fields.␊ |
1838 | ␉␉␉ */␊ |
1839 | ␉␉␉newBVR->next = NULL;␊ |
1840 | ␉␉␉newBVR->filtered = true;␊ |
1841 | ␊ |
1842 | ␉␉␉if ( (!allowFlags || newBVR->flags & allowFlags)␊ |
1843 | ␉␉␉␉&& (!denyFlags || !(newBVR->flags & denyFlags) )␊ |
1844 | ␉␉␉␉&& (newBVR->biosdev >= minBIOSDev && newBVR->biosdev <= maxBIOSDev)␊ |
1845 | ␉␉␉␉)␊ |
1846 | ␉␉␉{␊ |
1847 | ␉␉␉␉newBVR->visible = true;␊ |
1848 | ␉␉␉}␊ |
1849 | ␊ |
1850 | ␉␉␉/*␊ |
1851 | ␉␉␉ * Looking for "Hide Partition" entries in 'hd(x,y)|uuid|"label" hd(m,n)|uuid|"label"' format,␊ |
1852 | ␉␉␉ * to be able to hide foreign partitions from the boot menu.␊ |
1853 | ␉␉␉ *␊ |
1854 | ␉␉␉ */␊ |
1855 | ␉␉␉if ( (newBVR->flags & kBVFlagForeignBoot) )␊ |
1856 | ␉␉␉{␊ |
1857 | ␉␉␉␉char *start, *next = val;␊ |
1858 | ␉␉␉␉long len = 0; ␊ |
1859 | ␉␉␉␉do␊ |
1860 | ␉␉␉␉{␊ |
1861 | ␉␉␉␉␉start = strbreak(next, &next, &len);␊ |
1862 | ␉␉␉␉␉if(len && matchVolumeToString(newBVR, start, len) )␊ |
1863 | ␉␉␉␉␉{␊ |
1864 | ␉␉␉␉␉␉newBVR->visible = false;␊ |
1865 | ␉␉␉␉␉}␊ |
1866 | ␉␉␉␉}␊ |
1867 | ␉␉␉␉while ( next && *next );␊ |
1868 | ␉␉␉}␊ |
1869 | ␊ |
1870 | ␉␉␉/*␊ |
1871 | ␉␉␉ * Use the first bvr entry as the starting chain pointer.␊ |
1872 | ␉␉␉ */␊ |
1873 | ␉␉␉if (!chain)␊ |
1874 | ␉␉␉{␊ |
1875 | ␉␉␉␉chain = newBVR;␊ |
1876 | ␉␉␉}␊ |
1877 | ␊ |
1878 | ␉␉␉/*␊ |
1879 | ␉␉␉ * Update the previous bvr's link pointer to use the new memory area.␊ |
1880 | ␉␉␉ */␊ |
1881 | ␉␉␉if (prevBVR)␊ |
1882 | ␉␉␉{␊ |
1883 | ␉␉␉␉prevBVR->next = newBVR;␊ |
1884 | ␉␉␉}␊ |
1885 | ␊ |
1886 | ␉␉␉if (newBVR->visible)␊ |
1887 | ␉␉␉{␊ |
1888 | ␉␉␉␉bvCount++;␊ |
1889 | ␉␉␉}␊ |
1890 | ␉␉}␊ |
1891 | ␉}␊ |
1892 | ␊ |
1893 | #if DEBUG //Azi: warning - too big for boot-log.. far too big.. i mean HUGE!! :P␊ |
1894 | ␉for (bvr = chain; bvr; bvr = bvr->next)␊ |
1895 | ␉{␊ |
1896 | ␉␉printf(" bvr: %d, dev: %d, part: %d, flags: %d, vis: %d\n", bvr, bvr->biosdev, bvr->part_no, bvr->flags, bvr->visible);␊ |
1897 | ␉}␊ |
1898 | ␉printf("count: %d\n", bvCount);␊ |
1899 | ␉getchar();␊ |
1900 | #endif␊ |
1901 | ␊ |
1902 | ␉*count = bvCount;␊ |
1903 | ␊ |
1904 | ␉free(val); ␊ |
1905 | ␉return chain;␊ |
1906 | }␊ |
1907 | ␊ |
1908 | //==============================================================================␊ |
1909 | ␊ |
1910 | int freeFilteredBVChain(const BVRef chain)␊ |
1911 | {␊ |
1912 | ␉int ret = 1;␊ |
1913 | ␉BVRef bvr = chain;␊ |
1914 | ␉BVRef nextBVR = NULL;␊ |
1915 | ␊ |
1916 | ␉while (bvr)␊ |
1917 | ␉{␊ |
1918 | ␉␉nextBVR = bvr->next;␊ |
1919 | ␊ |
1920 | ␉␉if (bvr->filtered)␊ |
1921 | ␉␉{␊ |
1922 | ␉␉␉free(bvr);␊ |
1923 | ␉␉}␊ |
1924 | ␉␉else␊ |
1925 | ␉␉{␊ |
1926 | ␉␉␉ret = 0;␊ |
1927 | ␉␉␉break;␊ |
1928 | ␉␉}␊ |
1929 | ␊ |
1930 | ␉␉bvr = nextBVR;␊ |
1931 | ␉}␊ |
1932 | ␊ |
1933 | ␉return ret;␊ |
1934 | }␊ |
1935 | ␊ |
1936 | //==============================================================================␊ |
1937 | ␊ |
1938 | static const struct NamedValue fdiskTypes[] =␊ |
1939 | {␊ |
1940 | ␉{ FDISK_NTFS,␉␉"Windows NTFS" },␊ |
1941 | ␉{ FDISK_DOS12,␉␉"Windows FAT12" },␊ |
1942 | ␉{ FDISK_DOS16B,␉␉"Windows FAT16" },␊ |
1943 | ␉{ FDISK_DOS16S,␉␉"Windows FAT16" },␊ |
1944 | ␉{ FDISK_DOS16SLBA,␉"Windows FAT16" },␊ |
1945 | ␉{ FDISK_SMALLFAT32,␉"Windows FAT32" },␊ |
1946 | ␉{ FDISK_FAT32,␉␉"Windows FAT32" },␊ |
1947 | ␉{ FDISK_FREEBSD,␉"FreeBSD" },␊ |
1948 | ␉{ FDISK_OPENBSD,␉"OpenBSD" },␊ |
1949 | ␉{ FDISK_LINUX,␉␉"Linux" },␊ |
1950 | ␉{ FDISK_UFS,␉␉"Apple UFS" },␊ |
1951 | ␉{ FDISK_HFS,␉␉"Apple HFS" },␊ |
1952 | ␉{ FDISK_BOOTER,␉␉"Apple Boot/UFS" },␊ |
1953 | ␉{ FDISK_BEFS,␉␉"Haiku" },␊ |
1954 | ␉{ 0xCD,␉␉␉"CD-ROM" },␊ |
1955 | ␉{ 0x00,␉␉␉0 } /* must be last */␊ |
1956 | };␊ |
1957 | ␊ |
1958 | //==============================================================================␊ |
1959 | ␊ |
1960 | bool matchVolumeToString( BVRef bvr, const char* match, long matchLen)␊ |
1961 | {␊ |
1962 | ␉char testStr[128];␊ |
1963 | ␊ |
1964 | ␉if ( !bvr || !match || !*match)␊ |
1965 | ␉{␊ |
1966 | ␉␉return 0;␊ |
1967 | ␉}␊ |
1968 | ␊ |
1969 | ␉if ( bvr->biosdev < 0x80 || bvr->biosdev >= 0x100 )␊ |
1970 | ␉{␊ |
1971 | return 0;␊ |
1972 | ␉}␊ |
1973 | ␊ |
1974 | ␉// Try to match hd(x,y) first.␊ |
1975 | ␉sprintf(testStr, "hd(%d,%d)", BIOS_DEV_UNIT(bvr), bvr->part_no);␊ |
1976 | ␉if ( matchLen ? !strncmp(match, testStr, matchLen) : !strcmp(match, testStr) )␊ |
1977 | ␉{␊ |
1978 | ␉␉return true;␊ |
1979 | ␉}␊ |
1980 | ␊ |
1981 | ␉// Try to match volume UUID.␊ |
1982 | ␉if ( bvr->fs_getuuid && bvr->fs_getuuid(bvr, testStr) == 0)␊ |
1983 | ␉{␊ |
1984 | ␉␉if ( matchLen ? !strncmp(match, testStr, matchLen) : !strcmp(match, testStr) )␊ |
1985 | ␉␉{␊ |
1986 | ␉␉␉return true;␊ |
1987 | ␉␉}␊ |
1988 | ␉}␊ |
1989 | ␊ |
1990 | ␉// Try to match volume label (always quoted).␊ |
1991 | ␉if ( bvr->description )␊ |
1992 | ␉{␊ |
1993 | ␉␉bvr->description(bvr, testStr, sizeof(testStr)-1);␊ |
1994 | ␉␉if ( matchLen ? !strncmp(match, testStr, matchLen) : !strcmp(match, testStr) )␊ |
1995 | ␉␉{␊ |
1996 | ␉␉␉return true;␊ |
1997 | ␉␉}␊ |
1998 | ␉}␊ |
1999 | ␊ |
2000 | ␉return false;␊ |
2001 | }␊ |
2002 | ␊ |
2003 | //==============================================================================␊ |
2004 | ␊ |
2005 | /* If Rename Partition has defined an alias, then extract it for description purpose.␊ |
2006 | * The format for the rename string is the following:␊ |
2007 | * hd(x,y)|uuid|"label" "alias";hd(m,n)|uuid|"label" "alias"; etc...␊ |
2008 | */␊ |
2009 | ␊ |
2010 | bool getVolumeLabelAlias(BVRef bvr, char* str, long strMaxLen)␊ |
2011 | {␊ |
2012 | ␉char *aliasList, *entryStart, *entryNext;␊ |
2013 | ␊ |
2014 | ␉if ( !str || strMaxLen <= 0)␊ |
2015 | ␉{␊ |
2016 | ␉␉return false;␊ |
2017 | ␉}␊ |
2018 | ␊ |
2019 | ␉aliasList = XMLDecode(getStringForKey(kRenamePartition, &bootInfo->chameleonConfig));␊ |
2020 | ␉if ( !aliasList )␊ |
2021 | ␉{␊ |
2022 | ␉␉return false;␊ |
2023 | ␉}␊ |
2024 | ␊ |
2025 | ␉for ( entryStart = entryNext = aliasList; entryNext && *entryNext; entryStart = entryNext )␊ |
2026 | ␉{␊ |
2027 | ␉␉char *volStart, *volEnd, *aliasStart;␊ |
2028 | ␉␉long volLen, aliasLen;␊ |
2029 | ␊ |
2030 | ␉␉// Delimit current entry␊ |
2031 | ␉␉entryNext = strchr(entryStart, ';');␊ |
2032 | ␉␉if ( entryNext )␊ |
2033 | ␉␉{␊ |
2034 | ␉␉␉*entryNext = '\0';␊ |
2035 | ␉␉␉entryNext++;␊ |
2036 | ␉␉}␊ |
2037 | ␊ |
2038 | ␉␉volStart = strbreak(entryStart, &volEnd, &volLen);␊ |
2039 | ␉␉if(!volLen)␊ |
2040 | ␉␉{␊ |
2041 | ␉␉␉continue;␊ |
2042 | ␉␉}␊ |
2043 | ␊ |
2044 | ␉␉aliasStart = strbreak(volEnd, 0, &aliasLen);␊ |
2045 | ␉␉if(!aliasLen)␊ |
2046 | ␉␉{␊ |
2047 | ␉␉␉continue;␊ |
2048 | ␉␉}␊ |
2049 | ␊ |
2050 | ␉␉if ( matchVolumeToString(bvr, volStart, volLen) )␊ |
2051 | ␉␉{␊ |
2052 | ␉␉␉strncat(str, aliasStart, MIN(strMaxLen, aliasLen));␊ |
2053 | ␉␉␉free(aliasList);␊ |
2054 | ␊ |
2055 | ␉␉␉return true;␊ |
2056 | ␉␉}␊ |
2057 | ␉}␊ |
2058 | ␊ |
2059 | ␉free(aliasList);␊ |
2060 | ␉return false;␊ |
2061 | }␊ |
2062 | ␊ |
2063 | //==============================================================================␊ |
2064 | ␊ |
2065 | void getBootVolumeDescription( BVRef bvr, char * str, long strMaxLen, bool useDeviceDescription )␊ |
2066 | {␊ |
2067 | ␉unsigned char type;␊ |
2068 | ␉char *p = str;␊ |
2069 | ␊ |
2070 | ␉if(!bvr || !p || strMaxLen <= 0)␊ |
2071 | ␉{␊ |
2072 | ␉␉return;␊ |
2073 | ␉}␊ |
2074 | ␊ |
2075 | ␉type = (unsigned char) bvr->part_type;␊ |
2076 | ␊ |
2077 | ␉if (useDeviceDescription)␊ |
2078 | ␉{␊ |
2079 | ␉␉int len = getDeviceDescription(bvr, str);␊ |
2080 | ␉␉if(len >= strMaxLen)␊ |
2081 | ␉␉{␊ |
2082 | ␉␉␉return;␊ |
2083 | ␉␉}␊ |
2084 | ␊ |
2085 | ␉␉strcpy(str + len, " ");␊ |
2086 | ␉␉len++;␊ |
2087 | ␉␉strMaxLen -= len;␊ |
2088 | ␉␉p += len;␊ |
2089 | ␉}␊ |
2090 | ␊ |
2091 | ␉/* See if a partition rename is preferred */␊ |
2092 | ␉if (getVolumeLabelAlias(bvr, p, strMaxLen))␊ |
2093 | ␉{␊ |
2094 | ␉␉strncpy(bvr->label, p, strMaxLen); ␊ |
2095 | ␉␉return; // we're done here no need to seek for real name␊ |
2096 | ␉}␊ |
2097 | ␊ |
2098 | ␉// Get the volume label using filesystem specific functions or use the alternate volume label if available.␊ |
2099 | ␊ |
2100 | ␉if (*bvr->altlabel != '\0')␊ |
2101 | ␉{␊ |
2102 | ␉␉strncpy(p, bvr->altlabel, strMaxLen);␊ |
2103 | ␉}␊ |
2104 | ␉else if (bvr->description)␊ |
2105 | ␉{␊ |
2106 | ␉␉bvr->description(bvr, p, strMaxLen);␊ |
2107 | ␉}␊ |
2108 | ␊ |
2109 | ␉if (*p == '\0')␊ |
2110 | ␉{␊ |
2111 | ␉␉const char * name = getNameForValue( fdiskTypes, type );␊ |
2112 | ␊ |
2113 | ␉␉if (name == NULL)␊ |
2114 | ␉␉{␊ |
2115 | ␉␉␉name = bvr->type_name;␊ |
2116 | ␉␉}␊ |
2117 | ␊ |
2118 | ␉␉if (name == NULL)␊ |
2119 | ␉␉{␊ |
2120 | ␉␉␉sprintf(p, "TYPE %02x", type);␊ |
2121 | ␉␉}␊ |
2122 | ␉␉else␊ |
2123 | ␉␉{␊ |
2124 | ␉␉␉strncpy(p, name, strMaxLen);␊ |
2125 | ␉␉}␊ |
2126 | ␉}␊ |
2127 | ␊ |
2128 | ␉// Set the devices label␊ |
2129 | ␉sprintf(bvr->label, p);␊ |
2130 | }␊ |
2131 | ␊ |
2132 | ␊ |
2133 | //==============================================================================␊ |
2134 | ␊ |
2135 | int readBootSector(int biosdev, unsigned int secno, void * buffer)␊ |
2136 | {␊ |
2137 | ␉int error;␊ |
2138 | ␉struct disk_blk0 * bootSector = (struct disk_blk0 *) buffer;␊ |
2139 | ␊ |
2140 | ␉if (bootSector == NULL)␊ |
2141 | ␉{␊ |
2142 | ␉␉if (gBootSector == NULL)␊ |
2143 | ␉␉{␊ |
2144 | ␉␉␉gBootSector = (struct disk_blk0 *) malloc(sizeof(*gBootSector));␊ |
2145 | ␊ |
2146 | ␉␉␉if (gBootSector == NULL)␊ |
2147 | ␉␉␉{␊ |
2148 | ␉␉␉␉return -1;␊ |
2149 | ␉␉␉}␊ |
2150 | ␉␉}␊ |
2151 | ␉␉bootSector = gBootSector;␊ |
2152 | ␉}␊ |
2153 | ␊ |
2154 | ␉error = readBytes(biosdev, secno, 0, BPS, bootSector);␊ |
2155 | ␊ |
2156 | ␉if (error || bootSector->signature != DISK_SIGNATURE)␊ |
2157 | ␉{␊ |
2158 | ␉␉return -1;␊ |
2159 | ␉}␊ |
2160 | ␉return 0;␊ |
2161 | }␊ |
2162 | ␊ |
2163 | //==============================================================================␊ |
2164 | ␊ |
2165 | /*␊ |
2166 | * Format of boot1f32 block.␊ |
2167 | */␊ |
2168 | ␊ |
2169 | #define BOOT1F32_MAGIC "BOOT "␊ |
2170 | #define BOOT1F32_MAGICLEN 11␊ |
2171 | ␊ |
2172 | struct disk_boot1f32_blk␊ |
2173 | {␊ |
2174 | ␉unsigned char init[3];␊ |
2175 | ␉unsigned char fsheader[87];␊ |
2176 | ␉unsigned char magic[BOOT1F32_MAGICLEN];␊ |
2177 | ␉unsigned char bootcode[409];␊ |
2178 | ␉unsigned short signature;␊ |
2179 | };␊ |
2180 | ␊ |
2181 | //==============================================================================␊ |
2182 | ␊ |
2183 | int testFAT32EFIBootSector( int biosdev, unsigned int secno, void * buffer )␊ |
2184 | {␊ |
2185 | ␉struct disk_boot1f32_blk * bootSector = (struct disk_boot1f32_blk *) buffer;␊ |
2186 | ␉int error;␊ |
2187 | ␊ |
2188 | ␉if ( bootSector == NULL )␊ |
2189 | ␉{␊ |
2190 | ␉␉if ( gBootSector == NULL )␊ |
2191 | ␉␉{␊ |
2192 | ␉␉␉gBootSector = (struct disk_blk0 *) malloc(sizeof(*gBootSector));␊ |
2193 | ␉␉␉if ( gBootSector == NULL )␊ |
2194 | ␉␉␉{␊ |
2195 | ␉␉␉␉return -1;␊ |
2196 | ␉␉␉}␊ |
2197 | ␉␉}␊ |
2198 | ␉␉bootSector = (struct disk_boot1f32_blk *) gBootSector;␊ |
2199 | ␉}␊ |
2200 | ␊ |
2201 | ␉error = readBytes( biosdev, secno, 0, BPS, bootSector );␊ |
2202 | ␉if ( error || bootSector->signature != DISK_SIGNATURE || strncmp((const char *)bootSector->magic, BOOT1F32_MAGIC, BOOT1F32_MAGICLEN) )␊ |
2203 | ␉{␊ |
2204 | ␉␉return -1;␊ |
2205 | ␉}␊ |
2206 | ␉return 0;␊ |
2207 | }␊ |
2208 | ␊ |
2209 | ␊ |
2210 | //==============================================================================␊ |
2211 | // Handle seek request from filesystem modules.␊ |
2212 | ␊ |
2213 | void diskSeek(BVRef bvr, long long position)␊ |
2214 | {␊ |
2215 | ␉bvr->fs_boff = position / BPS;␊ |
2216 | ␉bvr->fs_byteoff = position % BPS;␊ |
2217 | }␊ |
2218 | ␊ |
2219 | ␊ |
2220 | //==============================================================================␊ |
2221 | // Handle read request from filesystem modules.␊ |
2222 | ␊ |
2223 | int diskRead(BVRef bvr, long addr, long length)␊ |
2224 | {␊ |
2225 | ␉return readBytes(bvr->biosdev, bvr->fs_boff + bvr->part_boff, bvr->fs_byteoff, length, (void *) addr);␊ |
2226 | }␊ |
2227 | ␊ |
2228 | //==============================================================================␊ |
2229 | ␊ |
2230 | int rawDiskRead( BVRef bvr, unsigned int secno, void *buffer, unsigned int len )␊ |
2231 | {␊ |
2232 | ␉int secs;␊ |
2233 | ␉unsigned char *cbuf = (unsigned char *)buffer;␊ |
2234 | ␉unsigned int copy_len;␊ |
2235 | ␉int rc;␊ |
2236 | ␊ |
2237 | ␉if ((len & (BPS-1)) != 0)␊ |
2238 | ␉{␊ |
2239 | ␉␉error("raw disk read not sector aligned");␊ |
2240 | ␉␉return -1;␊ |
2241 | ␉}␊ |
2242 | ␉secno += bvr->part_boff;␊ |
2243 | ␊ |
2244 | ␉cache_valid = false;␊ |
2245 | ␊ |
2246 | ␉while (len > 0)␊ |
2247 | ␉{␊ |
2248 | ␉␉secs = len / BPS;␊ |
2249 | ␉␉if (secs > N_CACHE_SECS)␊ |
2250 | ␉␉{␊ |
2251 | ␉␉␉secs = N_CACHE_SECS;␊ |
2252 | ␉␉}␊ |
2253 | ␉␉copy_len = secs * BPS;␊ |
2254 | ␊ |
2255 | ␉␉//printf("rdr: ebiosread(%d, %d, %d)\n", bvr->biosdev, secno, secs);␊ |
2256 | ␉␉if ((rc = ebiosread(bvr->biosdev, secno, secs)) != 0)␊ |
2257 | ␉␉{␊ |
2258 | ␉␉␉/* Ignore corrected ECC errors */␊ |
2259 | ␉␉␉if (rc != ECC_CORRECTED_ERR)␊ |
2260 | ␉␉␉{␊ |
2261 | ␉␉␉␉error(" EBIOS read error: %s\n", bios_error(rc), rc);␊ |
2262 | ␉␉␉␉error(" Block %d Sectors %d\n", secno, secs);␊ |
2263 | ␉␉␉␉return rc;␊ |
2264 | ␉␉␉}␊ |
2265 | ␉␉}␊ |
2266 | ␉␉bcopy( trackbuf, cbuf, copy_len );␊ |
2267 | ␉␉len -= copy_len;␊ |
2268 | ␉␉cbuf += copy_len;␊ |
2269 | ␉␉secno += secs;␊ |
2270 | ␉␉spinActivityIndicator(secs);␊ |
2271 | ␉}␊ |
2272 | ␊ |
2273 | ␉return 0;␊ |
2274 | }␊ |
2275 | ␊ |
2276 | //==============================================================================␊ |
2277 | ␊ |
2278 | int rawDiskWrite( BVRef bvr, unsigned int secno, void *buffer, unsigned int len )␊ |
2279 | {␊ |
2280 | int secs;␊ |
2281 | unsigned char *cbuf = (unsigned char *)buffer;␊ |
2282 | unsigned int copy_len;␊ |
2283 | int rc;␊ |
2284 | ␊ |
2285 | ␉if ((len & (BPS-1)) != 0)␊ |
2286 | ␉{␊ |
2287 | ␉␉error("raw disk write not sector aligned");␊ |
2288 | ␉␉return -1;␊ |
2289 | ␉}␊ |
2290 | ␉secno += bvr->part_boff;␊ |
2291 | ␊ |
2292 | ␉cache_valid = false;␊ |
2293 | ␊ |
2294 | ␉while (len > 0)␊ |
2295 | ␉{␊ |
2296 | ␉␉secs = len / BPS;␊ |
2297 | ␉␉if (secs > N_CACHE_SECS)␊ |
2298 | ␉␉{␊ |
2299 | ␉␉␉secs = N_CACHE_SECS;␊ |
2300 | ␉␉}␊ |
2301 | ␉␉copy_len = secs * BPS;␊ |
2302 | ␊ |
2303 | ␉␉bcopy( cbuf, trackbuf, copy_len );␊ |
2304 | ␉␉//printf("rdr: ebioswrite(%d, %d, %d)\n", bvr->biosdev, secno, secs);␊ |
2305 | ␉␉if ((rc = ebioswrite(bvr->biosdev, secno, secs)) != 0)␊ |
2306 | ␉␉{␊ |
2307 | ␉␉␉error(" EBIOS write error: %s\n", bios_error(rc), rc);␊ |
2308 | ␉␉␉error(" Block %d Sectors %d\n", secno, secs);␊ |
2309 | ␉␉␉return rc;␊ |
2310 | ␉␉}␊ |
2311 | ␊ |
2312 | ␉␉len -= copy_len;␊ |
2313 | ␉␉cbuf += copy_len;␊ |
2314 | ␉␉secno += secs;␊ |
2315 | ␉␉spinActivityIndicator(secs);␊ |
2316 | ␉}␊ |
2317 | ␊ |
2318 | ␉return 0;␊ |
2319 | }␊ |
2320 | ␊ |
2321 | //==============================================================================␊ |
2322 | ␊ |
2323 | int diskIsCDROM(BVRef bvr)␊ |
2324 | {␊ |
2325 | ␉struct driveInfo di;␊ |
2326 | ␊ |
2327 | ␉if (getDriveInfo(bvr->biosdev, &di) == 0 && di.no_emulation)␊ |
2328 | ␉{␊ |
2329 | ␉␉return 1;␊ |
2330 | ␉}␊ |
2331 | ␉return 0;␊ |
2332 | }␊ |
2333 | ␊ |
2334 | //==============================================================================␊ |
2335 | ␊ |
2336 | int biosDevIsCDROM(int biosdev)␊ |
2337 | {␊ |
2338 | ␉struct driveInfo di;␊ |
2339 | ␊ |
2340 | ␉if (getDriveInfo(biosdev, &di) == 0 && di.no_emulation)␊ |
2341 | ␉{␊ |
2342 | ␉␉return 1;␊ |
2343 | ␉}␊ |
2344 | ␉return 0;␊ |
2345 | }␊ |
2346 | |