]> code.delx.au - refind/blob - refind/lib.c
rEFInd now weeds out non-BIOS-bootable NTFS volumes on Macs *IF* the
[refind] / refind / lib.c
1 /*
2 * refind/lib.c
3 * General library functions
4 *
5 * Copyright (c) 2006-2009 Christoph Pfisterer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36 /*
37 * Modifications copyright (c) 2012-2015 Roderick W. Smith
38 *
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
42 *
43 */
44
45 #include "global.h"
46 #include "lib.h"
47 #include "icns.h"
48 #include "screen.h"
49 #include "../include/refit_call_wrapper.h"
50 #include "../include/RemovableMedia.h"
51 #include "gpt.h"
52 #include "config.h"
53
54 #ifdef __MAKEWITH_GNUEFI
55 #define EfiReallocatePool ReallocatePool
56 #else
57 #define LibLocateHandle gBS->LocateHandleBuffer
58 #define DevicePathProtocol gEfiDevicePathProtocolGuid
59 #define BlockIoProtocol gEfiBlockIoProtocolGuid
60 #define LibFileSystemInfo EfiLibFileSystemInfo
61 #define LibOpenRoot EfiLibOpenRoot
62 EFI_DEVICE_PATH EndDevicePath[] = {
63 {END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, {END_DEVICE_PATH_LENGTH, 0}}
64 };
65
66 //#define EndDevicePath DevicePath
67 #endif
68
69 // "Magic" signatures for various filesystems
70 #define FAT_MAGIC 0xAA55
71 #define EXT2_SUPER_MAGIC 0xEF53
72 #define HFSPLUS_MAGIC1 0x2B48
73 #define HFSPLUS_MAGIC2 0x5848
74 #define REISERFS_SUPER_MAGIC_STRING "ReIsErFs"
75 #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
76 #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
77 #define BTRFS_SIGNATURE "_BHRfS_M"
78 #define NTFS_SIGNATURE "NTFS "
79
80 // variables
81
82 EFI_HANDLE SelfImageHandle;
83 EFI_LOADED_IMAGE *SelfLoadedImage;
84 EFI_FILE *SelfRootDir;
85 EFI_FILE *SelfDir;
86 CHAR16 *SelfDirPath;
87
88 REFIT_VOLUME *SelfVolume = NULL;
89 REFIT_VOLUME **Volumes = NULL;
90 UINTN VolumesCount = 0;
91 extern GPT_DATA *gPartitions;
92
93 // Maximum size for disk sectors
94 #define SECTOR_SIZE 4096
95
96 // Number of bytes to read from a partition to determine its filesystem type
97 // and identify its boot loader, and hence probable BIOS-mode OS installation
98 #define SAMPLE_SIZE 69632 /* 68 KiB -- ReiserFS superblock begins at 64 KiB */
99
100
101 // functions
102
103 static EFI_STATUS FinishInitRefitLib(VOID);
104
105 static VOID UninitVolumes(VOID);
106
107 //
108 // self recognition stuff
109 //
110
111 // Converts forward slashes to backslashes, removes duplicate slashes, and
112 // removes slashes from both the start and end of the pathname.
113 // Necessary because some (buggy?) EFI implementations produce "\/" strings
114 // in pathnames, because some user inputs can produce duplicate directory
115 // separators, and because we want consistent start and end slashes for
116 // directory comparisons. A special case: If the PathName refers to root,
117 // return "/", since some firmware implementations flake out if this
118 // isn't present.
119 VOID CleanUpPathNameSlashes(IN OUT CHAR16 *PathName) {
120 CHAR16 *NewName;
121 UINTN i, Length, FinalChar = 0;
122 BOOLEAN LastWasSlash = FALSE;
123
124 Length = StrLen(PathName);
125 NewName = AllocateZeroPool(sizeof(CHAR16) * (Length + 2));
126 if (NewName != NULL) {
127 for (i = 0; i < StrLen(PathName); i++) {
128 if ((PathName[i] == L'/') || (PathName[i] == L'\\')) {
129 if ((!LastWasSlash) && (FinalChar != 0))
130 NewName[FinalChar++] = L'\\';
131 LastWasSlash = TRUE;
132 } else {
133 NewName[FinalChar++] = PathName[i];
134 LastWasSlash = FALSE;
135 } // if/else
136 } // for
137 NewName[FinalChar] = 0;
138 if ((FinalChar > 0) && (NewName[FinalChar - 1] == L'\\'))
139 NewName[--FinalChar] = 0;
140 if (FinalChar == 0) {
141 NewName[0] = L'\\';
142 NewName[1] = 0;
143 }
144 // Copy the transformed name back....
145 StrCpy(PathName, NewName);
146 FreePool(NewName);
147 } // if allocation OK
148 } // CleanUpPathNameSlashes()
149
150 // Splits an EFI device path into device and filename components. For instance, if InString is
151 // PciRoot(0x0)/Pci(0x1f,0x2)/Ata(Secondary,Master,0x0)/HD(2,GPT,8314ae90-ada3-48e9-9c3b-09a88f80d921,0x96028,0xfa000)/\bzImage-3.5.1.efi,
152 // this function will truncate that input to
153 // PciRoot(0x0)/Pci(0x1f,0x2)/Ata(Secondary,Master,0x0)/HD(2,GPT,8314ae90-ada3-48e9-9c3b-09a88f80d921,0x96028,0xfa000)
154 // and return bzImage-3.5.1.efi as its return value.
155 // It does this by searching for the last ")" character in InString, copying everything
156 // after that string (after some cleanup) as the return value, and truncating the original
157 // input value.
158 // If InString contains no ")" character, this function leaves the original input string
159 // unmodified and also returns that string. If InString is NULL, this function returns NULL.
160 static CHAR16* SplitDeviceString(IN OUT CHAR16 *InString) {
161 INTN i;
162 CHAR16 *FileName = NULL;
163 BOOLEAN Found = FALSE;
164
165 if (InString != NULL) {
166 i = StrLen(InString) - 1;
167 while ((i >= 0) && (!Found)) {
168 if (InString[i] == L')') {
169 Found = TRUE;
170 FileName = StrDuplicate(&InString[i + 1]);
171 CleanUpPathNameSlashes(FileName);
172 InString[i + 1] = '\0';
173 } // if
174 i--;
175 } // while
176 if (FileName == NULL)
177 FileName = StrDuplicate(InString);
178 } // if
179 return FileName;
180 } // static CHAR16* SplitDeviceString()
181
182 EFI_STATUS InitRefitLib(IN EFI_HANDLE ImageHandle)
183 {
184 EFI_STATUS Status;
185 CHAR16 *DevicePathAsString, *Temp;
186
187 SelfImageHandle = ImageHandle;
188 Status = refit_call3_wrapper(BS->HandleProtocol, SelfImageHandle, &LoadedImageProtocol, (VOID **) &SelfLoadedImage);
189 if (CheckFatalError(Status, L"while getting a LoadedImageProtocol handle"))
190 return EFI_LOAD_ERROR;
191
192 // find the current directory
193 DevicePathAsString = DevicePathToStr(SelfLoadedImage->FilePath);
194 CleanUpPathNameSlashes(DevicePathAsString);
195 MyFreePool(SelfDirPath);
196 Temp = FindPath(DevicePathAsString);
197 SelfDirPath = SplitDeviceString(Temp);
198 MyFreePool(DevicePathAsString);
199 MyFreePool(Temp);
200
201 return FinishInitRefitLib();
202 }
203
204 // called before running external programs to close open file handles
205 VOID UninitRefitLib(VOID)
206 {
207 // This piece of code was made to correspond to weirdness in ReinitRefitLib().
208 // See the comment on it there.
209 if(SelfRootDir == SelfVolume->RootDir)
210 SelfRootDir=0;
211
212 UninitVolumes();
213
214 if (SelfDir != NULL) {
215 refit_call1_wrapper(SelfDir->Close, SelfDir);
216 SelfDir = NULL;
217 }
218
219 if (SelfRootDir != NULL) {
220 refit_call1_wrapper(SelfRootDir->Close, SelfRootDir);
221 SelfRootDir = NULL;
222 }
223 }
224
225 // called after running external programs to re-open file handles
226 EFI_STATUS ReinitRefitLib(VOID)
227 {
228 ReinitVolumes();
229
230 if ((ST->Hdr.Revision >> 16) == 1) {
231 // Below two lines were in rEFIt, but seem to cause system crashes or
232 // reboots when launching OSes after returning from programs on most
233 // systems. OTOH, my Mac Mini produces errors about "(re)opening our
234 // installation volume" (see the next function) when returning from
235 // programs when these two lines are removed, and it often crashes
236 // when returning from a program or when launching a second program
237 // with these lines removed. Therefore, the preceding if() statement
238 // executes these lines only on EFIs with a major version number of 1
239 // (which Macs have) and not with 2 (which UEFI PCs have). My selection
240 // of hardware on which to test is limited, though, so this may be the
241 // wrong test, or there may be a better way to fix this problem.
242 // TODO: Figure out cause of above weirdness and fix it more
243 // reliably!
244 if (SelfVolume != NULL && SelfVolume->RootDir != NULL)
245 SelfRootDir = SelfVolume->RootDir;
246 } // if
247
248 return FinishInitRefitLib();
249 }
250
251 static EFI_STATUS FinishInitRefitLib(VOID)
252 {
253 EFI_STATUS Status;
254
255 if (SelfRootDir == NULL) {
256 SelfRootDir = LibOpenRoot(SelfLoadedImage->DeviceHandle);
257 if (SelfRootDir == NULL) {
258 CheckError(EFI_LOAD_ERROR, L"while (re)opening our installation volume");
259 return EFI_LOAD_ERROR;
260 }
261 }
262
263 Status = refit_call5_wrapper(SelfRootDir->Open, SelfRootDir, &SelfDir, SelfDirPath, EFI_FILE_MODE_READ, 0);
264 if (CheckFatalError(Status, L"while opening our installation directory"))
265 return EFI_LOAD_ERROR;
266
267 return EFI_SUCCESS;
268 }
269
270 //
271 // EFI variable read and write functions
272 //
273
274 // From gummiboot: Retrieve a raw EFI variable.
275 // Returns EFI status
276 EFI_STATUS EfivarGetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
277 CHAR8 *buf;
278 UINTN l;
279 EFI_STATUS err;
280
281 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
282 buf = AllocatePool(l);
283 if (!buf)
284 return EFI_OUT_OF_RESOURCES;
285
286 err = refit_call5_wrapper(RT->GetVariable, name, vendor, NULL, &l, buf);
287 if (EFI_ERROR(err) == EFI_SUCCESS) {
288 *buffer = buf;
289 if (size)
290 *size = l;
291 } else
292 MyFreePool(buf);
293 return err;
294 } // EFI_STATUS EfivarGetRaw()
295
296 // From gummiboot: Set an EFI variable
297 EFI_STATUS EfivarSetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) {
298 UINT32 flags;
299
300 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
301 if (persistent)
302 flags |= EFI_VARIABLE_NON_VOLATILE;
303
304 return refit_call5_wrapper(RT->SetVariable, name, vendor, flags, size, buf);
305 } // EFI_STATUS EfivarSetRaw()
306
307 //
308 // list functions
309 //
310
311 VOID CreateList(OUT VOID ***ListPtr, OUT UINTN *ElementCount, IN UINTN InitialElementCount)
312 {
313 UINTN AllocateCount;
314
315 *ElementCount = InitialElementCount;
316 if (*ElementCount > 0) {
317 AllocateCount = (*ElementCount + 7) & ~7; // next multiple of 8
318 *ListPtr = AllocatePool(sizeof(VOID *) * AllocateCount);
319 } else {
320 *ListPtr = NULL;
321 }
322 }
323
324 VOID AddListElement(IN OUT VOID ***ListPtr, IN OUT UINTN *ElementCount, IN VOID *NewElement)
325 {
326 UINTN AllocateCount;
327
328 if ((*ElementCount & 7) == 0) {
329 AllocateCount = *ElementCount + 8;
330 if (*ElementCount == 0)
331 *ListPtr = AllocatePool(sizeof(VOID *) * AllocateCount);
332 else
333 *ListPtr = EfiReallocatePool(*ListPtr, sizeof(VOID *) * (*ElementCount), sizeof(VOID *) * AllocateCount);
334 }
335 (*ListPtr)[*ElementCount] = NewElement;
336 (*ElementCount)++;
337 } /* VOID AddListElement() */
338
339 VOID FreeList(IN OUT VOID ***ListPtr, IN OUT UINTN *ElementCount)
340 {
341 UINTN i;
342
343 if ((*ElementCount > 0) && (**ListPtr != NULL)) {
344 for (i = 0; i < *ElementCount; i++) {
345 // TODO: call a user-provided routine for each element here
346 MyFreePool((*ListPtr)[i]);
347 }
348 MyFreePool(*ListPtr);
349 }
350 } // VOID FreeList()
351
352 //
353 // firmware device path discovery
354 //
355
356 static UINT8 LegacyLoaderMediaPathData[] = {
357 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
358 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
359 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
360 };
361 static EFI_DEVICE_PATH *LegacyLoaderMediaPath = (EFI_DEVICE_PATH *)LegacyLoaderMediaPathData;
362
363 VOID ExtractLegacyLoaderPaths(EFI_DEVICE_PATH **PathList, UINTN MaxPaths, EFI_DEVICE_PATH **HardcodedPathList)
364 {
365 EFI_STATUS Status;
366 UINTN HandleCount = 0;
367 UINTN HandleIndex, HardcodedIndex;
368 EFI_HANDLE *Handles;
369 EFI_HANDLE Handle;
370 UINTN PathCount = 0;
371 UINTN PathIndex;
372 EFI_LOADED_IMAGE *LoadedImage;
373 EFI_DEVICE_PATH *DevicePath;
374 BOOLEAN Seen;
375
376 MaxPaths--; // leave space for the terminating NULL pointer
377
378 // get all LoadedImage handles
379 Status = LibLocateHandle(ByProtocol, &LoadedImageProtocol, NULL, &HandleCount, &Handles);
380 if (CheckError(Status, L"while listing LoadedImage handles")) {
381 if (HardcodedPathList) {
382 for (HardcodedIndex = 0; HardcodedPathList[HardcodedIndex] && PathCount < MaxPaths; HardcodedIndex++)
383 PathList[PathCount++] = HardcodedPathList[HardcodedIndex];
384 }
385 PathList[PathCount] = NULL;
386 return;
387 }
388 for (HandleIndex = 0; HandleIndex < HandleCount && PathCount < MaxPaths; HandleIndex++) {
389 Handle = Handles[HandleIndex];
390
391 Status = refit_call3_wrapper(BS->HandleProtocol, Handle, &LoadedImageProtocol, (VOID **) &LoadedImage);
392 if (EFI_ERROR(Status))
393 continue; // This can only happen if the firmware scewed up, ignore it.
394
395 Status = refit_call3_wrapper(BS->HandleProtocol, LoadedImage->DeviceHandle, &DevicePathProtocol, (VOID **) &DevicePath);
396 if (EFI_ERROR(Status))
397 continue; // This happens, ignore it.
398
399 // Only grab memory range nodes
400 if (DevicePathType(DevicePath) != HARDWARE_DEVICE_PATH || DevicePathSubType(DevicePath) != HW_MEMMAP_DP)
401 continue;
402
403 // Check if we have this device path in the list already
404 // WARNING: This assumes the first node in the device path is unique!
405 Seen = FALSE;
406 for (PathIndex = 0; PathIndex < PathCount; PathIndex++) {
407 if (DevicePathNodeLength(DevicePath) != DevicePathNodeLength(PathList[PathIndex]))
408 continue;
409 if (CompareMem(DevicePath, PathList[PathIndex], DevicePathNodeLength(DevicePath)) == 0) {
410 Seen = TRUE;
411 break;
412 }
413 }
414 if (Seen)
415 continue;
416
417 PathList[PathCount++] = AppendDevicePath(DevicePath, LegacyLoaderMediaPath);
418 }
419 MyFreePool(Handles);
420
421 if (HardcodedPathList) {
422 for (HardcodedIndex = 0; HardcodedPathList[HardcodedIndex] && PathCount < MaxPaths; HardcodedIndex++)
423 PathList[PathCount++] = HardcodedPathList[HardcodedIndex];
424 }
425 PathList[PathCount] = NULL;
426 }
427
428 //
429 // volume functions
430 //
431
432 // Return a pointer to a string containing a filesystem type name. If the
433 // filesystem type is unknown, a blank (but non-null) string is returned.
434 // The returned variable is a constant that should NOT be freed.
435 static CHAR16 *FSTypeName(IN UINT32 TypeCode) {
436 CHAR16 *retval = NULL;
437
438 switch (TypeCode) {
439 case FS_TYPE_FAT:
440 retval = L" FAT";
441 break;
442 case FS_TYPE_HFSPLUS:
443 retval = L" HFS+";
444 break;
445 case FS_TYPE_EXT2:
446 retval = L" ext2";
447 break;
448 case FS_TYPE_EXT3:
449 retval = L" ext3";
450 break;
451 case FS_TYPE_EXT4:
452 retval = L" ext4";
453 break;
454 case FS_TYPE_REISERFS:
455 retval = L" ReiserFS";
456 break;
457 case FS_TYPE_BTRFS:
458 retval = L" Btrfs";
459 break;
460 case FS_TYPE_ISO9660:
461 retval = L" ISO-9660";
462 break;
463 case FS_TYPE_NTFS:
464 retval = L" NTFS";
465 break;
466 default:
467 retval = L"";
468 break;
469 } // switch
470 return retval;
471 } // CHAR16 *FSTypeName()
472
473 // Identify the filesystem type and record the filesystem's UUID/serial number,
474 // if possible. Expects a Buffer containing the first few (normally at least
475 // 4096) bytes of the filesystem. Sets the filesystem type code in Volume->FSType
476 // and the UUID/serial number in Volume->VolUuid. Note that the UUID value is
477 // recognized differently for each filesystem, and is currently supported only
478 // for NTFS, ext2/3/4fs, and ReiserFS (and for NTFS it's really a 64-bit serial
479 // number not a UUID or GUID). If the UUID can't be determined, it's set to 0.
480 // Also, the UUID is just read directly into memory; it is *NOT* valid when
481 // displayed by GuidAsString() or used in other GUID/UUID-manipulating
482 // functions. (As I write, it's being used merely to detect partitions that are
483 // part of a RAID 1 array.)
484 static VOID SetFilesystemData(IN UINT8 *Buffer, IN UINTN BufferSize, IN OUT REFIT_VOLUME *Volume) {
485 UINT32 *Ext2Incompat, *Ext2Compat;
486 UINT16 *Magic16;
487 char *MagicString;
488
489 if ((Buffer != NULL) && (Volume != NULL)) {
490 SetMem(&(Volume->VolUuid), sizeof(EFI_GUID), 0);
491 Volume->FSType = FS_TYPE_UNKNOWN;
492
493 if (BufferSize >= 512) {
494 Magic16 = (UINT16*) (Buffer + 510);
495 if (*Magic16 == FAT_MAGIC) {
496 MagicString = (char*) (Buffer + 3);
497 if (CompareMem(MagicString, NTFS_SIGNATURE, 8) == 0) {
498 Volume->FSType = FS_TYPE_NTFS;
499 CopyMem(&(Volume->VolUuid), Buffer + 0x48, sizeof(UINT64));
500 } else {
501 // NOTE: This misidentifies a whole disk as a FAT partition
502 // because FAT and MBR share the same 0xaa55 "magic" and
503 // no other distinguishing data. Later code, in ScanVolume(),
504 // resets to FS_TYPE_UNKNOWN if the "filesystem" can't be
505 // read.
506 Volume->FSType = FS_TYPE_FAT;
507 }
508 return;
509 } // if
510 } // search for FAT and NTFS magic
511
512 if (BufferSize >= (1024 + 100)) {
513 Magic16 = (UINT16*) (Buffer + 1024 + 56);
514 if (*Magic16 == EXT2_SUPER_MAGIC) { // ext2/3/4
515 Ext2Compat = (UINT32*) (Buffer + 1024 + 92);
516 Ext2Incompat = (UINT32*) (Buffer + 1024 + 96);
517 if ((*Ext2Incompat & 0x0040) || (*Ext2Incompat & 0x0200)) { // check for extents or flex_bg
518 Volume->FSType = FS_TYPE_EXT4;
519 } else if (*Ext2Compat & 0x0004) { // check for journal
520 Volume->FSType = FS_TYPE_EXT3;
521 } else { // none of these features; presume it's ext2...
522 Volume->FSType = FS_TYPE_EXT2;
523 }
524 CopyMem(&(Volume->VolUuid), Buffer + 1024 + 104, sizeof(EFI_GUID));
525 return;
526 }
527 } // search for ext2/3/4 magic
528
529 if (BufferSize >= (65536 + 100)) {
530 MagicString = (char*) (Buffer + 65536 + 52);
531 if ((CompareMem(MagicString, REISERFS_SUPER_MAGIC_STRING, 8) == 0) ||
532 (CompareMem(MagicString, REISER2FS_SUPER_MAGIC_STRING, 9) == 0) ||
533 (CompareMem(MagicString, REISER2FS_JR_SUPER_MAGIC_STRING, 9) == 0)) {
534 Volume->FSType = FS_TYPE_REISERFS;
535 CopyMem(&(Volume->VolUuid), Buffer + 65536 + 84, sizeof(EFI_GUID));
536 return;
537 } // if
538 } // search for ReiserFS magic
539
540 if (BufferSize >= (65536 + 64 + 8)) {
541 MagicString = (char*) (Buffer + 65536 + 64);
542 if (CompareMem(MagicString, BTRFS_SIGNATURE, 8) == 0) {
543 Volume->FSType = FS_TYPE_BTRFS;
544 return;
545 } // if
546 } // search for Btrfs magic
547
548 if (BufferSize >= (1024 + 2)) {
549 Magic16 = (UINT16*) (Buffer + 1024);
550 if ((*Magic16 == HFSPLUS_MAGIC1) || (*Magic16 == HFSPLUS_MAGIC2)) {
551 Volume->FSType = FS_TYPE_HFSPLUS;
552 return;
553 }
554 } // search for HFS+ magic
555
556 } // if (Buffer != NULL)
557
558 } // UINT32 SetFilesystemData()
559
560 static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable)
561 {
562 EFI_STATUS Status;
563 UINT8 Buffer[SAMPLE_SIZE];
564 UINTN i;
565 MBR_PARTITION_INFO *MbrTable;
566 BOOLEAN MbrTableFound = FALSE;
567
568 Volume->HasBootCode = FALSE;
569 Volume->OSIconName = NULL;
570 Volume->OSName = NULL;
571 *Bootable = FALSE;
572
573 if (Volume->BlockIO == NULL)
574 return;
575 if (Volume->BlockIO->Media->BlockSize > SAMPLE_SIZE)
576 return; // our buffer is too small...
577
578 // look at the boot sector (this is used for both hard disks and El Torito images!)
579 Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks,
580 Volume->BlockIO, Volume->BlockIO->Media->MediaId,
581 Volume->BlockIOOffset, SAMPLE_SIZE, Buffer);
582 if (!EFI_ERROR(Status)) {
583
584 SetFilesystemData(Buffer, SAMPLE_SIZE, Volume);
585 if ((*((UINT16 *)(Buffer + 510)) == 0xaa55 && Buffer[0] != 0) && (FindMem(Buffer, 512, "EXFAT", 5) == -1)) {
586 *Bootable = TRUE;
587 Volume->HasBootCode = TRUE;
588 }
589
590 // detect specific boot codes
591 if (CompareMem(Buffer + 2, "LILO", 4) == 0 ||
592 CompareMem(Buffer + 6, "LILO", 4) == 0 ||
593 CompareMem(Buffer + 3, "SYSLINUX", 8) == 0 ||
594 FindMem(Buffer, SECTOR_SIZE, "ISOLINUX", 8) >= 0) {
595 Volume->HasBootCode = TRUE;
596 Volume->OSIconName = L"linux";
597 Volume->OSName = L"Linux";
598
599 } else if (FindMem(Buffer, 512, "Geom\0Hard Disk\0Read\0 Error", 26) >= 0) { // GRUB
600 Volume->HasBootCode = TRUE;
601 Volume->OSIconName = L"grub,linux";
602 Volume->OSName = L"Linux";
603
604 // // Below doesn't produce a bootable entry, so commented out for the moment....
605 // // GRUB in BIOS boot partition:
606 // } else if (FindMem(Buffer, 512, "Geom\0Read\0 Error", 16) >= 0) {
607 // Volume->HasBootCode = TRUE;
608 // Volume->OSIconName = L"grub,linux";
609 // Volume->OSName = L"Linux";
610 // Volume->VolName = L"BIOS Boot Partition";
611 // *Bootable = TRUE;
612
613 } else if ((*((UINT32 *)(Buffer + 502)) == 0 &&
614 *((UINT32 *)(Buffer + 506)) == 50000 &&
615 *((UINT16 *)(Buffer + 510)) == 0xaa55) ||
616 FindMem(Buffer, SECTOR_SIZE, "Starting the BTX loader", 23) >= 0) {
617 Volume->HasBootCode = TRUE;
618 Volume->OSIconName = L"freebsd";
619 Volume->OSName = L"FreeBSD";
620
621 } else if (FindMem(Buffer, 512, "!Loading", 8) >= 0 ||
622 FindMem(Buffer, SECTOR_SIZE, "/cdboot\0/CDBOOT\0", 16) >= 0) {
623 Volume->HasBootCode = TRUE;
624 Volume->OSIconName = L"openbsd";
625 Volume->OSName = L"OpenBSD";
626
627 } else if (FindMem(Buffer, 512, "Not a bootxx image", 18) >= 0 ||
628 *((UINT32 *)(Buffer + 1028)) == 0x7886b6d1) {
629 Volume->HasBootCode = TRUE;
630 Volume->OSIconName = L"netbsd";
631 Volume->OSName = L"NetBSD";
632
633 } else if (FindMem(Buffer, SECTOR_SIZE, "NTLDR", 5) >= 0) {
634 Volume->HasBootCode = TRUE;
635 Volume->OSIconName = L"win";
636 Volume->OSName = L"Windows";
637
638 } else if (FindMem(Buffer, SECTOR_SIZE, "BOOTMGR", 7) >= 0) {
639 Volume->HasBootCode = TRUE;
640 Volume->OSIconName = L"winvista,win";
641 Volume->OSName = L"Windows";
642
643 } else if (FindMem(Buffer, 512, "CPUBOOT SYS", 11) >= 0 ||
644 FindMem(Buffer, 512, "KERNEL SYS", 11) >= 0) {
645 Volume->HasBootCode = TRUE;
646 Volume->OSIconName = L"freedos";
647 Volume->OSName = L"FreeDOS";
648
649 } else if (FindMem(Buffer, 512, "OS2LDR", 6) >= 0 ||
650 FindMem(Buffer, 512, "OS2BOOT", 7) >= 0) {
651 Volume->HasBootCode = TRUE;
652 Volume->OSIconName = L"ecomstation";
653 Volume->OSName = L"eComStation";
654
655 } else if (FindMem(Buffer, 512, "Be Boot Loader", 14) >= 0) {
656 Volume->HasBootCode = TRUE;
657 Volume->OSIconName = L"beos";
658 Volume->OSName = L"BeOS";
659
660 } else if (FindMem(Buffer, 512, "yT Boot Loader", 14) >= 0) {
661 Volume->HasBootCode = TRUE;
662 Volume->OSIconName = L"zeta,beos";
663 Volume->OSName = L"ZETA";
664
665 } else if (FindMem(Buffer, 512, "\x04" "beos\x06" "system\x05" "zbeos", 18) >= 0 ||
666 FindMem(Buffer, 512, "\x06" "system\x0c" "haiku_loader", 20) >= 0) {
667 Volume->HasBootCode = TRUE;
668 Volume->OSIconName = L"haiku,beos";
669 Volume->OSName = L"Haiku";
670
671 }
672
673 // NOTE: If you add an operating system with a name that starts with 'W' or 'L', you
674 // need to fix AddLegacyEntry in refind/legacy.c.
675
676 #if REFIT_DEBUG > 0
677 Print(L" Result of bootcode detection: %s %s (%s)\n",
678 Volume->HasBootCode ? L"bootable" : L"non-bootable",
679 Volume->OSName, Volume->OSIconName);
680 #endif
681
682 // dummy FAT boot sector (created by OS X's newfs_msdos)
683 if (FindMem(Buffer, 512, "Non-system disk", 15) >= 0)
684 Volume->HasBootCode = FALSE;
685
686 // dummy FAT boot sector (created by Linux's mkdosfs)
687 if (FindMem(Buffer, 512, "This is not a bootable disk", 27) >= 0)
688 Volume->HasBootCode = FALSE;
689
690 // dummy FAT boot sector (created by Windows)
691 if (FindMem(Buffer, 512, "Press any key to restart", 24) >= 0)
692 Volume->HasBootCode = FALSE;
693
694 // check for MBR partition table
695 if (*((UINT16 *)(Buffer + 510)) == 0xaa55) {
696 MbrTable = (MBR_PARTITION_INFO *)(Buffer + 446);
697 for (i = 0; i < 4; i++)
698 if (MbrTable[i].StartLBA && MbrTable[i].Size)
699 MbrTableFound = TRUE;
700 for (i = 0; i < 4; i++)
701 if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
702 MbrTableFound = FALSE;
703 if (MbrTableFound) {
704 Volume->MbrPartitionTable = AllocatePool(4 * 16);
705 CopyMem(Volume->MbrPartitionTable, MbrTable, 4 * 16);
706 }
707 }
708
709 } else {
710 #if REFIT_DEBUG > 0
711 CheckError(Status, L"while reading boot sector");
712 #endif
713 }
714 } /* VOID ScanVolumeBootcode() */
715
716 // Set default volume badge icon based on /.VolumeBadge.{icns|png} file or disk kind
717 VOID SetVolumeBadgeIcon(REFIT_VOLUME *Volume)
718 {
719 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_BADGES)
720 return;
721
722 if (Volume->VolBadgeImage == NULL) {
723 Volume->VolBadgeImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeBadge", GlobalConfig.IconSizes[ICON_SIZE_BADGE]);
724 }
725
726 if (Volume->VolBadgeImage == NULL) {
727 switch (Volume->DiskKind) {
728 case DISK_KIND_INTERNAL:
729 Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
730 break;
731 case DISK_KIND_EXTERNAL:
732 Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
733 break;
734 case DISK_KIND_OPTICAL:
735 Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
736 break;
737 case DISK_KIND_NET:
738 Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_NET);
739 break;
740 } // switch()
741 }
742 } // VOID SetVolumeBadgeIcon()
743
744 // Return a string representing the input size in IEEE-1541 units.
745 // The calling function is responsible for freeing the allocated memory.
746 static CHAR16 *SizeInIEEEUnits(UINT64 SizeInBytes) {
747 UINT64 SizeInIeee;
748 UINTN Index = 0, NumPrefixes;
749 CHAR16 *Units, *Prefixes = L" KMGTPEZ";
750 CHAR16 *TheValue;
751
752 TheValue = AllocateZeroPool(sizeof(CHAR16) * 256);
753 if (TheValue != NULL) {
754 NumPrefixes = StrLen(Prefixes);
755 SizeInIeee = SizeInBytes;
756 while ((SizeInIeee > 1024) && (Index < (NumPrefixes - 1))) {
757 Index++;
758 SizeInIeee /= 1024;
759 } // while
760 if (Prefixes[Index] == ' ') {
761 Units = StrDuplicate(L"-byte");
762 } else {
763 Units = StrDuplicate(L" iB");
764 Units[1] = Prefixes[Index];
765 } // if/else
766 SPrint(TheValue, 255, L"%ld%s", SizeInIeee, Units);
767 } // if
768 return TheValue;
769 } // CHAR16 *SizeInIEEEUnits()
770
771 // Return a name for the volume. Ideally this should be the label for the
772 // filesystem it contains, but this function falls back to describing the
773 // filesystem by size (200 MiB, etc.) and/or type (ext2, HFS+, etc.), if
774 // this information can be extracted.
775 // The calling function is responsible for freeing the memory allocated
776 // for the name string.
777 static CHAR16 *GetVolumeName(REFIT_VOLUME *Volume) {
778 EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr = NULL;
779 CHAR16 *FoundName = NULL;
780 CHAR16 *SISize, *TypeName;
781
782 if (Volume->RootDir != NULL) {
783 FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir);
784 }
785
786 if ((FileSystemInfoPtr != NULL) && (FileSystemInfoPtr->VolumeLabel != NULL) &&
787 (StrLen(FileSystemInfoPtr->VolumeLabel) > 0)) {
788 FoundName = StrDuplicate(FileSystemInfoPtr->VolumeLabel);
789 }
790
791 // Special case: Old versions of the rEFInd HFS+ driver always returns label of "HFS+ volume", so wipe
792 // this so that we can build a new name that includes the size....
793 if ((FoundName != NULL) && (StrCmp(FoundName, L"HFS+ volume") == 0) && (Volume->FSType == FS_TYPE_HFSPLUS)) {
794 MyFreePool(FoundName);
795 FoundName = NULL;
796 } // if rEFInd HFS+ driver suspected
797
798 // If no filesystem name, try to use the partition name....
799 if ((FoundName == NULL) && (Volume->PartName != NULL) && (StrLen(Volume->PartName) > 0) &&
800 !IsIn(Volume->PartName, IGNORE_PARTITION_NAMES)) {
801 FoundName = StrDuplicate(Volume->PartName);
802 } // if use partition name
803
804 // No filesystem or acceptable partition name, so use fs type and size
805 if ((FoundName == NULL) && (FileSystemInfoPtr != NULL)) {
806 FoundName = AllocateZeroPool(sizeof(CHAR16) * 256);
807 if (FoundName != NULL) {
808 SISize = SizeInIEEEUnits(FileSystemInfoPtr->VolumeSize);
809 SPrint(FoundName, 255, L"%s%s volume", SISize, FSTypeName(Volume->FSType));
810 MyFreePool(SISize);
811 } // if allocated memory OK
812 } // if (FoundName == NULL)
813
814 MyFreePool(FileSystemInfoPtr);
815
816 if (FoundName == NULL) {
817 FoundName = AllocateZeroPool(sizeof(CHAR16) * 256);
818 if (FoundName != NULL) {
819 TypeName = FSTypeName(Volume->FSType); // NOTE: Don't free TypeName; function returns constant
820 if (StrLen(TypeName) > 0)
821 SPrint(FoundName, 255, L"%s volume", TypeName);
822 else
823 SPrint(FoundName, 255, L"unknown volume");
824 } // if allocated memory OK
825 } // if
826
827 // TODO: Above could be improved/extended, in case filesystem name is not found,
828 // such as:
829 // - use or add disk/partition number (e.g., "(hd0,2)")
830
831 // Desperate fallback name....
832 if (FoundName == NULL) {
833 FoundName = StrDuplicate(L"unknown volume");
834 }
835 return FoundName;
836 } // static CHAR16 *GetVolumeName()
837
838 // Determine the unique GUID of the volume and store it.
839 static VOID SetPartGuidAndName(REFIT_VOLUME *Volume, EFI_DEVICE_PATH_PROTOCOL *DevicePath) {
840 HARDDRIVE_DEVICE_PATH *HdDevicePath;
841
842 if (Volume == NULL)
843 return;
844
845 if ((DevicePath->Type == MEDIA_DEVICE_PATH) && (DevicePath->SubType == MEDIA_HARDDRIVE_DP)) {
846 HdDevicePath = (HARDDRIVE_DEVICE_PATH*) DevicePath;
847 if (HdDevicePath->SignatureType == SIGNATURE_TYPE_GUID) {
848 Volume->PartGuid = *((EFI_GUID*) HdDevicePath->Signature);
849 Volume->PartName = PartNameFromGuid(&(Volume->PartGuid));
850 } // if
851 } // if
852 } // VOID SetPartGuid()
853
854 // Return TRUE if NTFS boot files are found, FALSE otherwise.
855 // Assumes Volume is already mounted.
856 static BOOLEAN HasWindowsBiosBootFiles(REFIT_VOLUME *Volume) {
857 BOOLEAN FilesFound = TRUE;
858
859 if (Volume->RootDir != NULL) {
860 FilesFound = (FileExists(Volume->RootDir, L"NTLDR") && // Windows XP boot files
861 FileExists(Volume->RootDir, L"ntdetect.com") &&
862 FileExists(Volume->RootDir, L"boot.ini")) ||
863 FileExists(Volume->RootDir, L"Windows"); // Windows 7 ID (imperfect; TODO: Improve)
864 } // if
865 return FilesFound;
866 } // static VOID HasWindowsBiosBootFiles()
867
868 VOID ScanVolume(REFIT_VOLUME *Volume)
869 {
870 EFI_STATUS Status;
871 EFI_DEVICE_PATH *DevicePath, *NextDevicePath;
872 EFI_DEVICE_PATH *DiskDevicePath, *RemainingDevicePath;
873 EFI_HANDLE WholeDiskHandle;
874 UINTN PartialLength;
875 BOOLEAN Bootable;
876
877 // get device path
878 Volume->DevicePath = DuplicateDevicePath(DevicePathFromHandle(Volume->DeviceHandle));
879 #if REFIT_DEBUG > 0
880 if (Volume->DevicePath != NULL) {
881 Print(L"* %s\n", DevicePathToStr(Volume->DevicePath));
882 #if REFIT_DEBUG >= 2
883 DumpHex(1, 0, DevicePathSize(Volume->DevicePath), Volume->DevicePath);
884 #endif
885 }
886 #endif
887
888 Volume->DiskKind = DISK_KIND_INTERNAL; // default
889
890 // get block i/o
891 Status = refit_call3_wrapper(BS->HandleProtocol, Volume->DeviceHandle, &BlockIoProtocol, (VOID **) &(Volume->BlockIO));
892 if (EFI_ERROR(Status)) {
893 Volume->BlockIO = NULL;
894 Print(L"Warning: Can't get BlockIO protocol.\n");
895 } else {
896 if (Volume->BlockIO->Media->BlockSize == 2048)
897 Volume->DiskKind = DISK_KIND_OPTICAL;
898 }
899
900 // scan for bootcode and MBR table
901 Bootable = FALSE;
902 ScanVolumeBootcode(Volume, &Bootable);
903
904 // detect device type
905 DevicePath = Volume->DevicePath;
906 while (DevicePath != NULL && !IsDevicePathEndType(DevicePath)) {
907 NextDevicePath = NextDevicePathNode(DevicePath);
908
909 if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH) {
910 SetPartGuidAndName(Volume, DevicePath);
911 }
912 if (DevicePathType(DevicePath) == MESSAGING_DEVICE_PATH &&
913 (DevicePathSubType(DevicePath) == MSG_USB_DP ||
914 DevicePathSubType(DevicePath) == MSG_USB_CLASS_DP ||
915 DevicePathSubType(DevicePath) == MSG_1394_DP ||
916 DevicePathSubType(DevicePath) == MSG_FIBRECHANNEL_DP))
917 Volume->DiskKind = DISK_KIND_EXTERNAL; // USB/FireWire/FC device -> external
918 if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH &&
919 DevicePathSubType(DevicePath) == MEDIA_CDROM_DP) {
920 Volume->DiskKind = DISK_KIND_OPTICAL; // El Torito entry -> optical disk
921 Bootable = TRUE;
922 }
923
924 if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH && DevicePathSubType(DevicePath) == MEDIA_VENDOR_DP) {
925 Volume->IsAppleLegacy = TRUE; // legacy BIOS device entry
926 // TODO: also check for Boot Camp GUID
927 Bootable = FALSE; // this handle's BlockIO is just an alias for the whole device
928 }
929
930 if (DevicePathType(DevicePath) == MESSAGING_DEVICE_PATH) {
931 // make a device path for the whole device
932 PartialLength = (UINT8 *)NextDevicePath - (UINT8 *)(Volume->DevicePath);
933 DiskDevicePath = (EFI_DEVICE_PATH *)AllocatePool(PartialLength + sizeof(EFI_DEVICE_PATH));
934 CopyMem(DiskDevicePath, Volume->DevicePath, PartialLength);
935 CopyMem((UINT8 *)DiskDevicePath + PartialLength, EndDevicePath, sizeof(EFI_DEVICE_PATH));
936
937 // get the handle for that path
938 RemainingDevicePath = DiskDevicePath;
939 Status = refit_call3_wrapper(BS->LocateDevicePath, &BlockIoProtocol, &RemainingDevicePath, &WholeDiskHandle);
940 FreePool(DiskDevicePath);
941
942 if (!EFI_ERROR(Status)) {
943 //Print(L" - original handle: %08x - disk handle: %08x\n", (UINT32)DeviceHandle, (UINT32)WholeDiskHandle);
944
945 // get the device path for later
946 Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &DevicePathProtocol, (VOID **) &DiskDevicePath);
947 if (!EFI_ERROR(Status)) {
948 Volume->WholeDiskDevicePath = DuplicateDevicePath(DiskDevicePath);
949 }
950
951 // look at the BlockIO protocol
952 Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol,
953 (VOID **) &Volume->WholeDiskBlockIO);
954 if (!EFI_ERROR(Status)) {
955
956 // check the media block size
957 if (Volume->WholeDiskBlockIO->Media->BlockSize == 2048)
958 Volume->DiskKind = DISK_KIND_OPTICAL;
959
960 } else {
961 Volume->WholeDiskBlockIO = NULL;
962 //CheckError(Status, L"from HandleProtocol");
963 }
964 } //else
965 // CheckError(Status, L"from LocateDevicePath");
966 }
967
968 DevicePath = NextDevicePath;
969 } // while
970
971 if (!Bootable) {
972 #if REFIT_DEBUG > 0
973 if (Volume->HasBootCode)
974 Print(L" Volume considered non-bootable, but boot code is present\n");
975 #endif
976 Volume->HasBootCode = FALSE;
977 }
978
979 // open the root directory of the volume
980 Volume->RootDir = LibOpenRoot(Volume->DeviceHandle);
981
982 // Set volume icon based on .VolumeBadge icon or disk kind
983 SetVolumeBadgeIcon(Volume);
984
985 Volume->VolName = GetVolumeName(Volume);
986
987 if (Volume->RootDir == NULL) {
988 Volume->IsReadable = FALSE;
989 if (Volume->FSType != FS_TYPE_NTFS)
990 Volume->FSType = FS_TYPE_UNKNOWN;
991 return;
992 } else {
993 Volume->IsReadable = TRUE;
994 if ((Volume->FSType == FS_TYPE_NTFS) && Volume->HasBootCode)
995 Volume->HasBootCode = HasWindowsBiosBootFiles(Volume);
996 } // if/else
997
998 // get custom volume icons if present
999 if (!Volume->VolIconImage)
1000 Volume->VolIconImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeIcon", GlobalConfig.IconSizes[ICON_SIZE_BIG]);
1001 } // ScanVolume()
1002
1003 static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_INFO *MbrEntry)
1004 {
1005 EFI_STATUS Status;
1006 REFIT_VOLUME *Volume;
1007 UINT32 ExtBase, ExtCurrent, NextExtCurrent;
1008 UINTN i;
1009 UINTN LogicalPartitionIndex = 4;
1010 UINT8 SectorBuffer[512];
1011 BOOLEAN Bootable;
1012 MBR_PARTITION_INFO *EMbrTable;
1013
1014 ExtBase = MbrEntry->StartLBA;
1015
1016 for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
1017 // read current EMBR
1018 Status = refit_call5_wrapper(WholeDiskVolume->BlockIO->ReadBlocks,
1019 WholeDiskVolume->BlockIO,
1020 WholeDiskVolume->BlockIO->Media->MediaId,
1021 ExtCurrent, 512, SectorBuffer);
1022 if (EFI_ERROR(Status))
1023 break;
1024 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1025 break;
1026 EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1027
1028 // scan logical partitions in this EMBR
1029 NextExtCurrent = 0;
1030 for (i = 0; i < 4; i++) {
1031 if ((EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80) ||
1032 EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
1033 break;
1034 if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
1035 // set next ExtCurrent
1036 NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
1037 break;
1038 } else {
1039
1040 // found a logical partition
1041 Volume = AllocateZeroPool(sizeof(REFIT_VOLUME));
1042 Volume->DiskKind = WholeDiskVolume->DiskKind;
1043 Volume->IsMbrPartition = TRUE;
1044 Volume->MbrPartitionIndex = LogicalPartitionIndex++;
1045 Volume->VolName = AllocateZeroPool(256 * sizeof(UINT16));
1046 SPrint(Volume->VolName, 255, L"Partition %d", Volume->MbrPartitionIndex + 1);
1047 Volume->BlockIO = WholeDiskVolume->BlockIO;
1048 Volume->BlockIOOffset = ExtCurrent + EMbrTable[i].StartLBA;
1049 Volume->WholeDiskBlockIO = WholeDiskVolume->BlockIO;
1050
1051 Bootable = FALSE;
1052 ScanVolumeBootcode(Volume, &Bootable);
1053 if (!Bootable)
1054 Volume->HasBootCode = FALSE;
1055
1056 SetVolumeBadgeIcon(Volume);
1057
1058 AddListElement((VOID ***) &Volumes, &VolumesCount, Volume);
1059
1060 }
1061 }
1062 }
1063 } /* VOID ScanExtendedPartition() */
1064
1065 VOID ScanVolumes(VOID)
1066 {
1067 EFI_STATUS Status;
1068 EFI_HANDLE *Handles;
1069 REFIT_VOLUME *Volume, *WholeDiskVolume;
1070 MBR_PARTITION_INFO *MbrTable;
1071 UINTN HandleCount = 0;
1072 UINTN HandleIndex;
1073 UINTN VolumeIndex, VolumeIndex2;
1074 UINTN PartitionIndex;
1075 UINTN SectorSum, i, VolNumber = 0;
1076 UINT8 *SectorBuffer1, *SectorBuffer2;
1077 EFI_GUID *UuidList;
1078 EFI_GUID NullUuid = NULL_GUID_VALUE;
1079
1080 MyFreePool(Volumes);
1081 Volumes = NULL;
1082 VolumesCount = 0;
1083 ForgetPartitionTables();
1084
1085 // get all filesystem handles
1086 Status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL, &HandleCount, &Handles);
1087 UuidList = AllocateZeroPool(sizeof(EFI_GUID) * HandleCount);
1088 if (Status == EFI_NOT_FOUND) {
1089 return; // no filesystems. strange, but true...
1090 }
1091 if (CheckError(Status, L"while listing all file systems"))
1092 return;
1093
1094 // first pass: collect information about all handles
1095 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
1096 Volume = AllocateZeroPool(sizeof(REFIT_VOLUME));
1097 Volume->DeviceHandle = Handles[HandleIndex];
1098 AddPartitionTable(Volume);
1099 ScanVolume(Volume);
1100 if (UuidList) {
1101 UuidList[HandleIndex] = Volume->VolUuid;
1102 for (i = 0; i < HandleIndex; i++) {
1103 if ((CompareMem(&(Volume->VolUuid), &(UuidList[i]), sizeof(EFI_GUID)) == 0) &&
1104 (CompareMem(&(Volume->VolUuid), &NullUuid, sizeof(EFI_GUID)) != 0)) { // Duplicate filesystem UUID
1105 Volume->IsReadable = FALSE;
1106 } // if
1107 } // for
1108 } // if
1109 if (Volume->IsReadable)
1110 Volume->VolNumber = VolNumber++;
1111 else
1112 Volume->VolNumber = VOL_UNREADABLE;
1113
1114 AddListElement((VOID ***) &Volumes, &VolumesCount, Volume);
1115
1116 if (Volume->DeviceHandle == SelfLoadedImage->DeviceHandle)
1117 SelfVolume = Volume;
1118 }
1119 MyFreePool(Handles);
1120
1121 if (SelfVolume == NULL)
1122 Print(L"WARNING: SelfVolume not found");
1123
1124 // second pass: relate partitions and whole disk devices
1125 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1126 Volume = Volumes[VolumeIndex];
1127 // check MBR partition table for extended partitions
1128 if (Volume->BlockIO != NULL && Volume->WholeDiskBlockIO != NULL &&
1129 Volume->BlockIO == Volume->WholeDiskBlockIO && Volume->BlockIOOffset == 0 &&
1130 Volume->MbrPartitionTable != NULL) {
1131 MbrTable = Volume->MbrPartitionTable;
1132 for (PartitionIndex = 0; PartitionIndex < 4; PartitionIndex++) {
1133 if (IS_EXTENDED_PART_TYPE(MbrTable[PartitionIndex].Type)) {
1134 ScanExtendedPartition(Volume, MbrTable + PartitionIndex);
1135 }
1136 }
1137 }
1138
1139 // search for corresponding whole disk volume entry
1140 WholeDiskVolume = NULL;
1141 if (Volume->BlockIO != NULL && Volume->WholeDiskBlockIO != NULL &&
1142 Volume->BlockIO != Volume->WholeDiskBlockIO) {
1143 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1144 if (Volumes[VolumeIndex2]->BlockIO == Volume->WholeDiskBlockIO &&
1145 Volumes[VolumeIndex2]->BlockIOOffset == 0) {
1146 WholeDiskVolume = Volumes[VolumeIndex2];
1147 }
1148 }
1149 }
1150
1151 if (WholeDiskVolume != NULL && WholeDiskVolume->MbrPartitionTable != NULL) {
1152 // check if this volume is one of the partitions in the table
1153 MbrTable = WholeDiskVolume->MbrPartitionTable;
1154 SectorBuffer1 = AllocatePool(512);
1155 SectorBuffer2 = AllocatePool(512);
1156 for (PartitionIndex = 0; PartitionIndex < 4; PartitionIndex++) {
1157 // check size
1158 if ((UINT64)(MbrTable[PartitionIndex].Size) != Volume->BlockIO->Media->LastBlock + 1)
1159 continue;
1160
1161 // compare boot sector read through offset vs. directly
1162 Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks,
1163 Volume->BlockIO, Volume->BlockIO->Media->MediaId,
1164 Volume->BlockIOOffset, 512, SectorBuffer1);
1165 if (EFI_ERROR(Status))
1166 break;
1167 Status = refit_call5_wrapper(Volume->WholeDiskBlockIO->ReadBlocks,
1168 Volume->WholeDiskBlockIO, Volume->WholeDiskBlockIO->Media->MediaId,
1169 MbrTable[PartitionIndex].StartLBA, 512, SectorBuffer2);
1170 if (EFI_ERROR(Status))
1171 break;
1172 if (CompareMem(SectorBuffer1, SectorBuffer2, 512) != 0)
1173 continue;
1174 SectorSum = 0;
1175 for (i = 0; i < 512; i++)
1176 SectorSum += SectorBuffer1[i];
1177 if (SectorSum < 1000)
1178 continue;
1179
1180 // TODO: mark entry as non-bootable if it is an extended partition
1181
1182 // now we're reasonably sure the association is correct...
1183 Volume->IsMbrPartition = TRUE;
1184 Volume->MbrPartitionIndex = PartitionIndex;
1185 if (Volume->VolName == NULL) {
1186 Volume->VolName = AllocateZeroPool(sizeof(CHAR16) * 256);
1187 SPrint(Volume->VolName, 255, L"Partition %d", PartitionIndex + 1);
1188 }
1189 break;
1190 }
1191
1192 MyFreePool(SectorBuffer1);
1193 MyFreePool(SectorBuffer2);
1194 }
1195 } // for
1196 } /* VOID ScanVolumes() */
1197
1198 static VOID UninitVolumes(VOID)
1199 {
1200 REFIT_VOLUME *Volume;
1201 UINTN VolumeIndex;
1202
1203 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1204 Volume = Volumes[VolumeIndex];
1205
1206 if (Volume->RootDir != NULL) {
1207 refit_call1_wrapper(Volume->RootDir->Close, Volume->RootDir);
1208 Volume->RootDir = NULL;
1209 }
1210
1211 Volume->DeviceHandle = NULL;
1212 Volume->BlockIO = NULL;
1213 Volume->WholeDiskBlockIO = NULL;
1214 }
1215 }
1216
1217 VOID ReinitVolumes(VOID)
1218 {
1219 EFI_STATUS Status;
1220 REFIT_VOLUME *Volume;
1221 UINTN VolumeIndex;
1222 EFI_DEVICE_PATH *RemainingDevicePath;
1223 EFI_HANDLE DeviceHandle, WholeDiskHandle;
1224
1225 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1226 Volume = Volumes[VolumeIndex];
1227
1228 if (Volume->DevicePath != NULL) {
1229 // get the handle for that path
1230 RemainingDevicePath = Volume->DevicePath;
1231 Status = refit_call3_wrapper(BS->LocateDevicePath, &BlockIoProtocol, &RemainingDevicePath, &DeviceHandle);
1232
1233 if (!EFI_ERROR(Status)) {
1234 Volume->DeviceHandle = DeviceHandle;
1235
1236 // get the root directory
1237 Volume->RootDir = LibOpenRoot(Volume->DeviceHandle);
1238
1239 } else
1240 CheckError(Status, L"from LocateDevicePath");
1241 }
1242
1243 if (Volume->WholeDiskDevicePath != NULL) {
1244 // get the handle for that path
1245 RemainingDevicePath = Volume->WholeDiskDevicePath;
1246 Status = refit_call3_wrapper(BS->LocateDevicePath, &BlockIoProtocol, &RemainingDevicePath, &WholeDiskHandle);
1247
1248 if (!EFI_ERROR(Status)) {
1249 // get the BlockIO protocol
1250 Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol,
1251 (VOID **) &Volume->WholeDiskBlockIO);
1252 if (EFI_ERROR(Status)) {
1253 Volume->WholeDiskBlockIO = NULL;
1254 CheckError(Status, L"from HandleProtocol");
1255 }
1256 } else
1257 CheckError(Status, L"from LocateDevicePath");
1258 }
1259 }
1260 }
1261
1262 //
1263 // file and dir functions
1264 //
1265
1266 BOOLEAN FileExists(IN EFI_FILE *BaseDir, IN CHAR16 *RelativePath)
1267 {
1268 EFI_STATUS Status;
1269 EFI_FILE_HANDLE TestFile;
1270
1271 Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &TestFile, RelativePath, EFI_FILE_MODE_READ, 0);
1272 if (Status == EFI_SUCCESS) {
1273 refit_call1_wrapper(TestFile->Close, TestFile);
1274 return TRUE;
1275 }
1276 return FALSE;
1277 }
1278
1279 EFI_STATUS DirNextEntry(IN EFI_FILE *Directory, IN OUT EFI_FILE_INFO **DirEntry, IN UINTN FilterMode)
1280 {
1281 EFI_STATUS Status;
1282 VOID *Buffer;
1283 UINTN LastBufferSize, BufferSize;
1284 INTN IterCount;
1285
1286 for (;;) {
1287
1288 // free pointer from last call
1289 if (*DirEntry != NULL) {
1290 FreePool(*DirEntry);
1291 *DirEntry = NULL;
1292 }
1293
1294 // read next directory entry
1295 LastBufferSize = BufferSize = 256;
1296 Buffer = AllocatePool(BufferSize);
1297 for (IterCount = 0; ; IterCount++) {
1298 Status = refit_call3_wrapper(Directory->Read, Directory, &BufferSize, Buffer);
1299 if (Status != EFI_BUFFER_TOO_SMALL || IterCount >= 4)
1300 break;
1301 if (BufferSize <= LastBufferSize) {
1302 Print(L"FS Driver requests bad buffer size %d (was %d), using %d instead\n", BufferSize, LastBufferSize, LastBufferSize * 2);
1303 BufferSize = LastBufferSize * 2;
1304 #if REFIT_DEBUG > 0
1305 } else {
1306 Print(L"Reallocating buffer from %d to %d\n", LastBufferSize, BufferSize);
1307 #endif
1308 }
1309 Buffer = EfiReallocatePool(Buffer, LastBufferSize, BufferSize);
1310 LastBufferSize = BufferSize;
1311 }
1312 if (EFI_ERROR(Status)) {
1313 MyFreePool(Buffer);
1314 Buffer = NULL;
1315 break;
1316 }
1317
1318 // check for end of listing
1319 if (BufferSize == 0) { // end of directory listing
1320 MyFreePool(Buffer);
1321 Buffer = NULL;
1322 break;
1323 }
1324
1325 // entry is ready to be returned
1326 *DirEntry = (EFI_FILE_INFO *)Buffer;
1327
1328 // filter results
1329 if (FilterMode == 1) { // only return directories
1330 if (((*DirEntry)->Attribute & EFI_FILE_DIRECTORY))
1331 break;
1332 } else if (FilterMode == 2) { // only return files
1333 if (((*DirEntry)->Attribute & EFI_FILE_DIRECTORY) == 0)
1334 break;
1335 } else // no filter or unknown filter -> return everything
1336 break;
1337
1338 }
1339 return Status;
1340 }
1341
1342 VOID DirIterOpen(IN EFI_FILE *BaseDir, IN CHAR16 *RelativePath OPTIONAL, OUT REFIT_DIR_ITER *DirIter)
1343 {
1344 if (RelativePath == NULL) {
1345 DirIter->LastStatus = EFI_SUCCESS;
1346 DirIter->DirHandle = BaseDir;
1347 DirIter->CloseDirHandle = FALSE;
1348 } else {
1349 DirIter->LastStatus = refit_call5_wrapper(BaseDir->Open, BaseDir, &(DirIter->DirHandle), RelativePath, EFI_FILE_MODE_READ, 0);
1350 DirIter->CloseDirHandle = EFI_ERROR(DirIter->LastStatus) ? FALSE : TRUE;
1351 }
1352 DirIter->LastFileInfo = NULL;
1353 }
1354
1355 #ifndef __MAKEWITH_GNUEFI
1356 EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollation = NULL;
1357
1358 static EFI_STATUS
1359 InitializeUnicodeCollationProtocol (VOID)
1360 {
1361 EFI_STATUS Status;
1362
1363 if (mUnicodeCollation != NULL) {
1364 return EFI_SUCCESS;
1365 }
1366
1367 //
1368 // BUGBUG: Proper impelmentation is to locate all Unicode Collation Protocol
1369 // instances first and then select one which support English language.
1370 // Current implementation just pick the first instance.
1371 //
1372 Status = gBS->LocateProtocol (
1373 &gEfiUnicodeCollation2ProtocolGuid,
1374 NULL,
1375 (VOID **) &mUnicodeCollation
1376 );
1377 if (EFI_ERROR(Status)) {
1378 Status = gBS->LocateProtocol (
1379 &gEfiUnicodeCollationProtocolGuid,
1380 NULL,
1381 (VOID **) &mUnicodeCollation
1382 );
1383
1384 }
1385 return Status;
1386 }
1387
1388 static BOOLEAN
1389 MetaiMatch (IN CHAR16 *String, IN CHAR16 *Pattern)
1390 {
1391 if (!mUnicodeCollation) {
1392 InitializeUnicodeCollationProtocol();
1393 }
1394 if (mUnicodeCollation)
1395 return mUnicodeCollation->MetaiMatch (mUnicodeCollation, String, Pattern);
1396 return FALSE; // Shouldn't happen
1397 }
1398
1399 static VOID StrLwr (IN OUT CHAR16 *Str) {
1400 if (!mUnicodeCollation) {
1401 InitializeUnicodeCollationProtocol();
1402 }
1403 if (mUnicodeCollation)
1404 mUnicodeCollation->StrLwr (mUnicodeCollation, Str);
1405 }
1406
1407 #endif
1408
1409 BOOLEAN DirIterNext(IN OUT REFIT_DIR_ITER *DirIter, IN UINTN FilterMode, IN CHAR16 *FilePattern OPTIONAL,
1410 OUT EFI_FILE_INFO **DirEntry)
1411 {
1412 BOOLEAN KeepGoing = TRUE;
1413 UINTN i;
1414 CHAR16 *OnePattern;
1415
1416 if (DirIter->LastFileInfo != NULL) {
1417 FreePool(DirIter->LastFileInfo);
1418 DirIter->LastFileInfo = NULL;
1419 }
1420
1421 if (EFI_ERROR(DirIter->LastStatus))
1422 return FALSE; // stop iteration
1423
1424 do {
1425 DirIter->LastStatus = DirNextEntry(DirIter->DirHandle, &(DirIter->LastFileInfo), FilterMode);
1426 if (EFI_ERROR(DirIter->LastStatus))
1427 return FALSE;
1428 if (DirIter->LastFileInfo == NULL) // end of listing
1429 return FALSE;
1430 if (FilePattern != NULL) {
1431 if ((DirIter->LastFileInfo->Attribute & EFI_FILE_DIRECTORY))
1432 KeepGoing = FALSE;
1433 i = 0;
1434 while (KeepGoing && (OnePattern = FindCommaDelimited(FilePattern, i++)) != NULL) {
1435 if (MetaiMatch(DirIter->LastFileInfo->FileName, OnePattern))
1436 KeepGoing = FALSE;
1437 } // while
1438 // else continue loop
1439 } else
1440 break;
1441 } while (KeepGoing && FilePattern);
1442
1443 *DirEntry = DirIter->LastFileInfo;
1444 return TRUE;
1445 }
1446
1447 EFI_STATUS DirIterClose(IN OUT REFIT_DIR_ITER *DirIter)
1448 {
1449 if (DirIter->LastFileInfo != NULL) {
1450 FreePool(DirIter->LastFileInfo);
1451 DirIter->LastFileInfo = NULL;
1452 }
1453 if (DirIter->CloseDirHandle)
1454 refit_call1_wrapper(DirIter->DirHandle->Close, DirIter->DirHandle);
1455 return DirIter->LastStatus;
1456 }
1457
1458 //
1459 // file name manipulation
1460 //
1461
1462 // Returns the filename portion (minus path name) of the
1463 // specified file
1464 CHAR16 * Basename(IN CHAR16 *Path)
1465 {
1466 CHAR16 *FileName;
1467 UINTN i;
1468
1469 FileName = Path;
1470
1471 if (Path != NULL) {
1472 for (i = StrLen(Path); i > 0; i--) {
1473 if (Path[i-1] == '\\' || Path[i-1] == '/') {
1474 FileName = Path + i;
1475 break;
1476 }
1477 }
1478 }
1479
1480 return FileName;
1481 }
1482
1483 // Remove the .efi extension from FileName -- for instance, if FileName is
1484 // "fred.efi", returns "fred". If the filename contains no .efi extension,
1485 // returns a copy of the original input.
1486 CHAR16 * StripEfiExtension(CHAR16 *FileName) {
1487 UINTN Length;
1488 CHAR16 *Copy = NULL;
1489
1490 if ((FileName != NULL) && ((Copy = StrDuplicate(FileName)) != NULL)) {
1491 Length = StrLen(Copy);
1492 // Note: Do StriCmp() twice to work around Gigabyte Hybrid EFI case-sensitivity bug....
1493 if ((Length >= 4) && ((StriCmp(&Copy[Length - 4], L".efi") == 0) || (StriCmp(&Copy[Length - 4], L".EFI") == 0))) {
1494 Copy[Length - 4] = 0;
1495 } // if
1496 } // if
1497 return Copy;
1498 } // CHAR16 * StripExtension()
1499
1500 //
1501 // memory string search
1502 //
1503
1504 INTN FindMem(IN VOID *Buffer, IN UINTN BufferLength, IN VOID *SearchString, IN UINTN SearchStringLength)
1505 {
1506 UINT8 *BufferPtr;
1507 UINTN Offset;
1508
1509 BufferPtr = Buffer;
1510 BufferLength -= SearchStringLength;
1511 for (Offset = 0; Offset < BufferLength; Offset++, BufferPtr++) {
1512 if (CompareMem(BufferPtr, SearchString, SearchStringLength) == 0)
1513 return (INTN)Offset;
1514 }
1515
1516 return -1;
1517 }
1518
1519 // Performs a case-insensitive search of BigStr for SmallStr.
1520 // Returns TRUE if found, FALSE if not.
1521 BOOLEAN StriSubCmp(IN CHAR16 *SmallStr, IN CHAR16 *BigStr) {
1522 CHAR16 *SmallCopy, *BigCopy;
1523 BOOLEAN Found = FALSE;
1524 UINTN StartPoint = 0, NumCompares = 0, SmallLen = 0;
1525
1526 if ((SmallStr != NULL) && (BigStr != NULL) && (StrLen(BigStr) >= StrLen(SmallStr))) {
1527 SmallCopy = StrDuplicate(SmallStr);
1528 BigCopy = StrDuplicate(BigStr);
1529 StrLwr(SmallCopy);
1530 StrLwr(BigCopy);
1531 SmallLen = StrLen(SmallCopy);
1532 NumCompares = StrLen(BigCopy) - SmallLen + 1;
1533 while ((!Found) && (StartPoint < NumCompares)) {
1534 Found = (StrnCmp(SmallCopy, &BigCopy[StartPoint++], SmallLen) == 0);
1535 } // while
1536 MyFreePool(SmallCopy);
1537 MyFreePool(BigCopy);
1538 } // if
1539
1540 return (Found);
1541 } // BOOLEAN StriSubCmp()
1542
1543 // Merges two strings, creating a new one and returning a pointer to it.
1544 // If AddChar != 0, the specified character is placed between the two original
1545 // strings (unless the first string is NULL or empty). The original input
1546 // string *First is de-allocated and replaced by the new merged string.
1547 // This is similar to StrCat, but safer and more flexible because
1548 // MergeStrings allocates memory that's the correct size for the
1549 // new merged string, so it can take a NULL *First and it cleans
1550 // up the old memory. It should *NOT* be used with a constant
1551 // *First, though....
1552 VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar) {
1553 UINTN Length1 = 0, Length2 = 0;
1554 CHAR16* NewString;
1555
1556 if (*First != NULL)
1557 Length1 = StrLen(*First);
1558 if (Second != NULL)
1559 Length2 = StrLen(Second);
1560 NewString = AllocatePool(sizeof(CHAR16) * (Length1 + Length2 + 2));
1561 if (NewString != NULL) {
1562 if ((*First != NULL) && (StrLen(*First) == 0)) {
1563 MyFreePool(*First);
1564 *First = NULL;
1565 }
1566 NewString[0] = L'\0';
1567 if (*First != NULL) {
1568 StrCat(NewString, *First);
1569 if (AddChar) {
1570 NewString[Length1] = AddChar;
1571 NewString[Length1 + 1] = '\0';
1572 } // if (AddChar)
1573 } // if (*First != NULL)
1574 if (Second != NULL)
1575 StrCat(NewString, Second);
1576 MyFreePool(*First);
1577 *First = NewString;
1578 } else {
1579 Print(L"Error! Unable to allocate memory in MergeStrings()!\n");
1580 } // if/else
1581 } // static CHAR16* MergeStrings()
1582
1583 // Takes an input pathname (*Path) and returns the part of the filename from
1584 // the final dot onwards, converted to lowercase. If the filename includes
1585 // no dots, or if the input is NULL, returns an empty (but allocated) string.
1586 // The calling function is responsible for freeing the memory associated with
1587 // the return value.
1588 CHAR16 *FindExtension(IN CHAR16 *Path) {
1589 CHAR16 *Extension;
1590 BOOLEAN Found = FALSE, FoundSlash = FALSE;
1591 INTN i;
1592
1593 Extension = AllocateZeroPool(sizeof(CHAR16));
1594 if (Path) {
1595 i = StrLen(Path);
1596 while ((!Found) && (!FoundSlash) && (i >= 0)) {
1597 if (Path[i] == L'.')
1598 Found = TRUE;
1599 else if ((Path[i] == L'/') || (Path[i] == L'\\'))
1600 FoundSlash = TRUE;
1601 if (!Found)
1602 i--;
1603 } // while
1604 if (Found) {
1605 MergeStrings(&Extension, &Path[i], 0);
1606 StrLwr(Extension);
1607 } // if (Found)
1608 } // if
1609 return (Extension);
1610 } // CHAR16 *FindExtension
1611
1612 // Takes an input pathname (*Path) and locates the final directory component
1613 // of that name. For instance, if the input path is 'EFI\foo\bar.efi', this
1614 // function returns the string 'foo'.
1615 // Assumes the pathname is separated with backslashes.
1616 CHAR16 *FindLastDirName(IN CHAR16 *Path) {
1617 UINTN i, StartOfElement = 0, EndOfElement = 0, PathLength, CopyLength;
1618 CHAR16 *Found = NULL;
1619
1620 if (Path == NULL)
1621 return NULL;
1622
1623 PathLength = StrLen(Path);
1624 // Find start & end of target element
1625 for (i = 0; i < PathLength; i++) {
1626 if (Path[i] == '\\') {
1627 StartOfElement = EndOfElement;
1628 EndOfElement = i;
1629 } // if
1630 } // for
1631 // Extract the target element
1632 if (EndOfElement > 0) {
1633 while ((StartOfElement < PathLength) && (Path[StartOfElement] == '\\')) {
1634 StartOfElement++;
1635 } // while
1636 EndOfElement--;
1637 if (EndOfElement >= StartOfElement) {
1638 CopyLength = EndOfElement - StartOfElement + 1;
1639 Found = StrDuplicate(&Path[StartOfElement]);
1640 if (Found != NULL)
1641 Found[CopyLength] = 0;
1642 } // if (EndOfElement >= StartOfElement)
1643 } // if (EndOfElement > 0)
1644 return (Found);
1645 } // CHAR16 *FindLastDirName
1646
1647 // Returns the directory portion of a pathname. For instance,
1648 // if FullPath is 'EFI\foo\bar.efi', this function returns the
1649 // string 'EFI\foo'. The calling function is responsible for
1650 // freeing the returned string's memory.
1651 CHAR16 *FindPath(IN CHAR16* FullPath) {
1652 UINTN i, LastBackslash = 0;
1653 CHAR16 *PathOnly = NULL;
1654
1655 if (FullPath != NULL) {
1656 for (i = 0; i < StrLen(FullPath); i++) {
1657 if (FullPath[i] == '\\')
1658 LastBackslash = i;
1659 } // for
1660 PathOnly = StrDuplicate(FullPath);
1661 if (PathOnly != NULL)
1662 PathOnly[LastBackslash] = 0;
1663 } // if
1664 return (PathOnly);
1665 }
1666
1667 /*++
1668 *
1669 * Routine Description:
1670 *
1671 * Find a substring.
1672 *
1673 * Arguments:
1674 *
1675 * String - Null-terminated string to search.
1676 * StrCharSet - Null-terminated string to search for.
1677 *
1678 * Returns:
1679 * The address of the first occurrence of the matching substring if successful, or NULL otherwise.
1680 * --*/
1681 CHAR16* MyStrStr (CHAR16 *String, CHAR16 *StrCharSet)
1682 {
1683 CHAR16 *Src;
1684 CHAR16 *Sub;
1685
1686 if ((String == NULL) || (StrCharSet == NULL))
1687 return NULL;
1688
1689 Src = String;
1690 Sub = StrCharSet;
1691
1692 while ((*String != L'\0') && (*StrCharSet != L'\0')) {
1693 if (*String++ != *StrCharSet) {
1694 String = ++Src;
1695 StrCharSet = Sub;
1696 } else {
1697 StrCharSet++;
1698 }
1699 }
1700 if (*StrCharSet == L'\0') {
1701 return Src;
1702 } else {
1703 return NULL;
1704 }
1705 } // CHAR16 *MyStrStr()
1706
1707 // Restrict TheString to at most Limit characters.
1708 // Does this in two ways:
1709 // - Locates stretches of two or more spaces and compresses
1710 // them down to one space.
1711 // - Truncates TheString
1712 // Returns TRUE if changes were made, FALSE otherwise
1713 BOOLEAN LimitStringLength(CHAR16 *TheString, UINTN Limit) {
1714 CHAR16 *SubString, *TempString;
1715 UINTN i;
1716 BOOLEAN HasChanged = FALSE;
1717
1718 // SubString will be NULL or point WITHIN TheString
1719 SubString = MyStrStr(TheString, L" ");
1720 while (SubString != NULL) {
1721 i = 0;
1722 while (SubString[i] == L' ')
1723 i++;
1724 if (i >= StrLen(SubString)) {
1725 SubString[0] = '\0';
1726 HasChanged = TRUE;
1727 } else {
1728 TempString = StrDuplicate(&SubString[i]);
1729 if (TempString != NULL) {
1730 StrCpy(&SubString[1], TempString);
1731 MyFreePool(TempString);
1732 HasChanged = TRUE;
1733 } else {
1734 // memory allocation problem; abort to avoid potentially infinite loop!
1735 break;
1736 } // if/else
1737 } // if/else
1738 SubString = MyStrStr(TheString, L" ");
1739 } // while
1740
1741 // If the string is still too long, truncate it....
1742 if (StrLen(TheString) > Limit) {
1743 TheString[Limit] = '\0';
1744 HasChanged = TRUE;
1745 } // if
1746
1747 return HasChanged;
1748 } // BOOLEAN LimitStringLength()
1749
1750 // Takes an input loadpath, splits it into disk and filename components, finds a matching
1751 // DeviceVolume, and returns that and the filename (*loader).
1752 VOID FindVolumeAndFilename(IN EFI_DEVICE_PATH *loadpath, OUT REFIT_VOLUME **DeviceVolume, OUT CHAR16 **loader) {
1753 CHAR16 *DeviceString, *VolumeDeviceString, *Temp;
1754 UINTN i = 0;
1755 BOOLEAN Found = FALSE;
1756
1757 MyFreePool(*loader);
1758 MyFreePool(*DeviceVolume);
1759 *DeviceVolume = NULL;
1760 DeviceString = DevicePathToStr(loadpath);
1761 *loader = SplitDeviceString(DeviceString);
1762
1763 while ((i < VolumesCount) && (!Found)) {
1764 VolumeDeviceString = DevicePathToStr(Volumes[i]->DevicePath);
1765 Temp = SplitDeviceString(VolumeDeviceString);
1766 if (StriCmp(DeviceString, VolumeDeviceString) == 0) {
1767 Found = TRUE;
1768 *DeviceVolume = Volumes[i];
1769 }
1770 MyFreePool(Temp);
1771 MyFreePool(VolumeDeviceString);
1772 i++;
1773 } // while
1774
1775 MyFreePool(DeviceString);
1776 } // VOID FindVolumeAndFilename()
1777
1778 // Splits a volume/filename string (e.g., "fs0:\EFI\BOOT") into separate
1779 // volume and filename components (e.g., "fs0" and "\EFI\BOOT"), returning
1780 // the filename component in the original *Path variable and the split-off
1781 // volume component in the *VolName variable.
1782 // Returns TRUE if both components are found, FALSE otherwise.
1783 BOOLEAN SplitVolumeAndFilename(IN OUT CHAR16 **Path, OUT CHAR16 **VolName) {
1784 UINTN i = 0, Length;
1785 CHAR16 *Filename;
1786
1787 if (*Path == NULL)
1788 return FALSE;
1789
1790 if (*VolName != NULL) {
1791 MyFreePool(*VolName);
1792 *VolName = NULL;
1793 }
1794
1795 Length = StrLen(*Path);
1796 while ((i < Length) && ((*Path)[i] != L':')) {
1797 i++;
1798 } // while
1799
1800 if (i < Length) {
1801 Filename = StrDuplicate((*Path) + i + 1);
1802 (*Path)[i] = 0;
1803 *VolName = *Path;
1804 *Path = Filename;
1805 return TRUE;
1806 } else {
1807 return FALSE;
1808 }
1809 } // BOOLEAN SplitVolumeAndFilename()
1810
1811 // Returns all the digits in the input string, including intervening
1812 // non-digit characters. For instance, if InString is "foo-3.3.4-7.img",
1813 // this function returns "3.3.4-7". If InString contains no digits,
1814 // the return value is NULL.
1815 CHAR16 *FindNumbers(IN CHAR16 *InString) {
1816 UINTN i, StartOfElement, EndOfElement = 0, InLength, CopyLength;
1817 CHAR16 *Found = NULL;
1818
1819 if (InString == NULL)
1820 return NULL;
1821
1822 InLength = StartOfElement = StrLen(InString);
1823 // Find start & end of target element
1824 for (i = 0; i < InLength; i++) {
1825 if ((InString[i] >= '0') && (InString[i] <= '9')) {
1826 if (StartOfElement > i)
1827 StartOfElement = i;
1828 if (EndOfElement < i)
1829 EndOfElement = i;
1830 } // if
1831 } // for
1832 // Extract the target element
1833 if (EndOfElement > 0) {
1834 if (EndOfElement >= StartOfElement) {
1835 CopyLength = EndOfElement - StartOfElement + 1;
1836 Found = StrDuplicate(&InString[StartOfElement]);
1837 if (Found != NULL)
1838 Found[CopyLength] = 0;
1839 } // if (EndOfElement >= StartOfElement)
1840 } // if (EndOfElement > 0)
1841 return (Found);
1842 } // CHAR16 *FindNumbers()
1843
1844 // Find the #Index element (numbered from 0) in a comma-delimited string
1845 // of elements.
1846 // Returns the found element, or NULL if Index is out of range or InString
1847 // is NULL. Note that the calling function is responsible for freeing the
1848 // memory associated with the returned string pointer.
1849 CHAR16 *FindCommaDelimited(IN CHAR16 *InString, IN UINTN Index) {
1850 UINTN StartPos = 0, CurPos = 0;
1851 BOOLEAN Found = FALSE;
1852 CHAR16 *FoundString = NULL;
1853
1854 if (InString != NULL) {
1855 // After while() loop, StartPos marks start of item #Index
1856 while ((Index > 0) && (CurPos < StrLen(InString))) {
1857 if (InString[CurPos] == L',') {
1858 Index--;
1859 StartPos = CurPos + 1;
1860 } // if
1861 CurPos++;
1862 } // while
1863 // After while() loop, CurPos is one past the end of the element
1864 while ((CurPos < StrLen(InString)) && (!Found)) {
1865 if (InString[CurPos] == L',')
1866 Found = TRUE;
1867 else
1868 CurPos++;
1869 } // while
1870 if (Index == 0)
1871 FoundString = StrDuplicate(&InString[StartPos]);
1872 if (FoundString != NULL)
1873 FoundString[CurPos - StartPos] = 0;
1874 } // if
1875 return (FoundString);
1876 } // CHAR16 *FindCommaDelimited()
1877
1878 // Return the position of SmallString within BigString, or -1 if
1879 // not found.
1880 INTN FindSubString(IN CHAR16 *SmallString, IN CHAR16 *BigString) {
1881 INTN Position = -1;
1882 UINTN i = 0, SmallSize, BigSize;
1883 BOOLEAN Found = FALSE;
1884
1885 if ((SmallString == NULL) || (BigString == NULL))
1886 return -1;
1887
1888 SmallSize = StrLen(SmallString);
1889 BigSize = StrLen(BigString);
1890 if ((SmallSize > BigSize) || (SmallSize == 0) || (BigSize == 0))
1891 return -1;
1892
1893 while ((i <= (BigSize - SmallSize) && !Found)) {
1894 if (CompareMem(BigString + i, SmallString, SmallSize) == 0) {
1895 Found = TRUE;
1896 Position = i;
1897 } // if
1898 i++;
1899 } // while()
1900 return Position;
1901 } // INTN FindSubString()
1902
1903 // Take an input path name, which may include a volume specification and/or
1904 // a path, and return separate volume, path, and file names. For instance,
1905 // "BIGVOL:\EFI\ubuntu\grubx64.efi" will return a VolName of "BIGVOL", a Path
1906 // of "EFI\ubuntu", and a Filename of "grubx64.efi". If an element is missing,
1907 // the returned pointer is NULL. The calling function is responsible for
1908 // freeing the allocated memory.
1909 VOID SplitPathName(CHAR16 *InPath, CHAR16 **VolName, CHAR16 **Path, CHAR16 **Filename) {
1910 CHAR16 *Temp = NULL;
1911
1912 MyFreePool(*VolName);
1913 MyFreePool(*Path);
1914 MyFreePool(*Filename);
1915 *VolName = *Path = *Filename = NULL;
1916 Temp = StrDuplicate(InPath);
1917 SplitVolumeAndFilename(&Temp, VolName); // VolName is NULL or has volume; Temp has rest of path
1918 CleanUpPathNameSlashes(Temp);
1919 *Path = FindPath(Temp); // *Path has path (may be 0-length); Temp unchanged.
1920 *Filename = StrDuplicate(Temp + StrLen(*Path));
1921 CleanUpPathNameSlashes(*Filename);
1922 if (StrLen(*Path) == 0) {
1923 MyFreePool(*Path);
1924 *Path = NULL;
1925 }
1926 if (StrLen(*Filename) == 0) {
1927 MyFreePool(*Filename);
1928 *Filename = NULL;
1929 }
1930 MyFreePool(Temp);
1931 } // VOID SplitPathName
1932
1933 // Returns TRUE if SmallString is an element in the comma-delimited List,
1934 // FALSE otherwise. Performs comparison case-insensitively (except on
1935 // buggy EFIs with case-sensitive StriCmp() functions).
1936 BOOLEAN IsIn(IN CHAR16 *SmallString, IN CHAR16 *List) {
1937 UINTN i = 0;
1938 BOOLEAN Found = FALSE;
1939 CHAR16 *OneElement;
1940
1941 if (SmallString && List) {
1942 while (!Found && (OneElement = FindCommaDelimited(List, i++))) {
1943 if (StriCmp(OneElement, SmallString) == 0)
1944 Found = TRUE;
1945 } // while
1946 } // if
1947 return Found;
1948 } // BOOLEAN IsIn()
1949
1950 // Returns TRUE if any element of List can be found as a substring of
1951 // BigString, FALSE otherwise. Performs comparisons case-insensitively.
1952 BOOLEAN IsInSubstring(IN CHAR16 *BigString, IN CHAR16 *List) {
1953 UINTN i = 0, ElementLength;
1954 BOOLEAN Found = FALSE;
1955 CHAR16 *OneElement;
1956
1957 if (BigString && List) {
1958 while (!Found && (OneElement = FindCommaDelimited(List, i++))) {
1959 ElementLength = StrLen(OneElement);
1960 if ((ElementLength <= StrLen(BigString)) && (StriSubCmp(OneElement, BigString)))
1961 Found = TRUE;
1962 } // while
1963 } // if
1964 return Found;
1965 } // BOOLEAN IsSubstringIn()
1966
1967 // Returns TRUE if specified Volume, Directory, and Filename correspond to an
1968 // element in the comma-delimited List, FALSE otherwise. Note that Directory and
1969 // Filename must *NOT* include a volume or path specification (that's part of
1970 // the Volume variable), but the List elements may. Performs comparison
1971 // case-insensitively (except on buggy EFIs with case-sensitive StriCmp()
1972 // functions).
1973 BOOLEAN FilenameIn(REFIT_VOLUME *Volume, CHAR16 *Directory, CHAR16 *Filename, CHAR16 *List) {
1974 UINTN i = 0;
1975 BOOLEAN Found = FALSE;
1976 CHAR16 *OneElement;
1977 CHAR16 *TargetVolName = NULL, *TargetPath = NULL, *TargetFilename = NULL;
1978
1979 if (Filename && List) {
1980 while (!Found && (OneElement = FindCommaDelimited(List, i++))) {
1981 Found = TRUE;
1982 SplitPathName(OneElement, &TargetVolName, &TargetPath, &TargetFilename);
1983 VolumeNumberToName(Volume, &TargetVolName);
1984 if (((TargetVolName != NULL) && ((Volume == NULL) || (StriCmp(TargetVolName, Volume->VolName) != 0))) ||
1985 ((TargetPath != NULL) && (StriCmp(TargetPath, Directory) != 0)) ||
1986 ((TargetFilename != NULL) && (StriCmp(TargetFilename, Filename) != 0))) {
1987 Found = FALSE;
1988 } // if
1989 MyFreePool(OneElement);
1990 } // while
1991 } // if
1992
1993 MyFreePool(TargetVolName);
1994 MyFreePool(TargetPath);
1995 MyFreePool(TargetFilename);
1996 return Found;
1997 } // BOOLEAN FilenameIn()
1998
1999 // If *VolName is of the form "fs#", where "#" is a number, and if Volume points
2000 // to this volume number, returns with *VolName changed to the volume name, as
2001 // stored in the Volume data structure.
2002 // Returns TRUE if this substitution was made, FALSE otherwise.
2003 BOOLEAN VolumeNumberToName(REFIT_VOLUME *Volume, CHAR16 **VolName) {
2004 BOOLEAN MadeSubstitution = FALSE;
2005 UINTN VolNum;
2006
2007 if ((VolName == NULL) || (*VolName == NULL))
2008 return FALSE;
2009
2010 if ((StrLen(*VolName) > 2) && (*VolName[0] == L'f') && (*VolName[1] == L's') && (*VolName[2] >= L'0') && (*VolName[2] <= L'9')) {
2011 VolNum = Atoi(*VolName + 2);
2012 if (VolNum == Volume->VolNumber) {
2013 MyFreePool(*VolName);
2014 *VolName = StrDuplicate(Volume->VolName);
2015 MadeSubstitution = TRUE;
2016 } // if
2017 } // if
2018 return MadeSubstitution;
2019 } // BOOLEAN VolumeMatchesNumber()
2020
2021 // Implement FreePool the way it should have been done to begin with, so that
2022 // it doesn't throw an ASSERT message if fed a NULL pointer....
2023 VOID MyFreePool(IN VOID *Pointer) {
2024 if (Pointer != NULL)
2025 FreePool(Pointer);
2026 }
2027
2028 static EFI_GUID AppleRemovableMediaGuid = APPLE_REMOVABLE_MEDIA_PROTOCOL_GUID;
2029
2030 // Eject all removable media.
2031 // Returns TRUE if any media were ejected, FALSE otherwise.
2032 BOOLEAN EjectMedia(VOID) {
2033 EFI_STATUS Status;
2034 UINTN HandleIndex, HandleCount = 0, Ejected = 0;
2035 EFI_HANDLE *Handles, Handle;
2036 APPLE_REMOVABLE_MEDIA_PROTOCOL *Ejectable;
2037
2038 Status = LibLocateHandle(ByProtocol, &AppleRemovableMediaGuid, NULL, &HandleCount, &Handles);
2039 if (EFI_ERROR(Status) || HandleCount == 0)
2040 return (FALSE); // probably not an Apple system
2041
2042 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
2043 Handle = Handles[HandleIndex];
2044 Status = refit_call3_wrapper(BS->HandleProtocol, Handle, &AppleRemovableMediaGuid, (VOID **) &Ejectable);
2045 if (EFI_ERROR(Status))
2046 continue;
2047 Status = refit_call1_wrapper(Ejectable->Eject, Ejectable);
2048 if (!EFI_ERROR(Status))
2049 Ejected++;
2050 }
2051 MyFreePool(Handles);
2052 return (Ejected > 0);
2053 } // VOID EjectMedia()
2054
2055 // Converts consecutive characters in the input string into a
2056 // number, interpreting the string as a hexadecimal number, starting
2057 // at the specified position and continuing for the specified number
2058 // of characters or until the end of the string, whichever is first.
2059 // NumChars must be between 1 and 16. Ignores invalid characters.
2060 UINT64 StrToHex(CHAR16 *Input, UINTN Pos, UINTN NumChars) {
2061 UINT64 retval = 0x00;
2062 UINTN NumDone = 0;
2063 CHAR16 a;
2064
2065 if ((Input == NULL) || (StrLen(Input) < Pos) || (NumChars == 0) || (NumChars > 16)) {
2066 return 0;
2067 }
2068
2069 while ((StrLen(Input) >= Pos) && (NumDone < NumChars)) {
2070 a = Input[Pos];
2071 if ((a >= '0') && (a <= '9')) {
2072 retval *= 0x10;
2073 retval += (a - '0');
2074 NumDone++;
2075 }
2076 if ((a >= 'a') && (a <= 'f')) {
2077 retval *= 0x10;
2078 retval += (a - 'a' + 0x0a);
2079 NumDone++;
2080 }
2081 if ((a >= 'A') && (a <= 'F')) {
2082 retval *= 0x10;
2083 retval += (a - 'A' + 0x0a);
2084 NumDone++;
2085 }
2086 Pos++;
2087 } // while()
2088 return retval;
2089 } // StrToHex()
2090
2091 // Returns TRUE if UnknownString can be interpreted as a GUID, FALSE otherwise.
2092 // Note that the input string must have no extraneous spaces and must be
2093 // conventionally formatted as a 36-character GUID, complete with dashes in
2094 // appropriate places.
2095 BOOLEAN IsGuid(CHAR16 *UnknownString) {
2096 UINTN Length, i;
2097 BOOLEAN retval = TRUE;
2098 CHAR16 a;
2099
2100 if (UnknownString == NULL)
2101 return FALSE;
2102
2103 Length = StrLen(UnknownString);
2104 if (Length != 36)
2105 return FALSE;
2106
2107 for (i = 0; i < Length; i++) {
2108 a = UnknownString[i];
2109 if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) {
2110 if (a != '-')
2111 retval = FALSE;
2112 } else if (((a < 'a') || (a > 'f')) && ((a < 'A') || (a > 'F')) && ((a < '0') && (a > '9'))) {
2113 retval = FALSE;
2114 } // if/else if
2115 } // for
2116 return retval;
2117 } // BOOLEAN IsGuid()
2118
2119 // Return the GUID as a string, suitable for display to the user. Note that the calling
2120 // function is responsible for freeing the allocated memory.
2121 CHAR16 * GuidAsString(EFI_GUID *GuidData) {
2122 CHAR16 *TheString;
2123
2124 TheString = AllocateZeroPool(42 * sizeof(CHAR16));
2125 if (TheString != 0) {
2126 SPrint (TheString, 82, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
2127 (UINTN)GuidData->Data1, (UINTN)GuidData->Data2, (UINTN)GuidData->Data3,
2128 (UINTN)GuidData->Data4[0], (UINTN)GuidData->Data4[1], (UINTN)GuidData->Data4[2],
2129 (UINTN)GuidData->Data4[3], (UINTN)GuidData->Data4[4], (UINTN)GuidData->Data4[5],
2130 (UINTN)GuidData->Data4[6], (UINTN)GuidData->Data4[7]);
2131 }
2132 return TheString;
2133 } // GuidAsString(EFI_GUID *GuidData)
2134
2135 EFI_GUID StringAsGuid(CHAR16 * InString) {
2136 EFI_GUID Guid = NULL_GUID_VALUE;
2137
2138 if (!IsGuid(InString)) {
2139 return Guid;
2140 }
2141
2142 Guid.Data1 = (UINT32) StrToHex(InString, 0, 8);
2143 Guid.Data2 = (UINT16) StrToHex(InString, 9, 4);
2144 Guid.Data3 = (UINT16) StrToHex(InString, 14, 4);
2145 Guid.Data4[0] = (UINT8) StrToHex(InString, 19, 2);
2146 Guid.Data4[1] = (UINT8) StrToHex(InString, 21, 2);
2147 Guid.Data4[2] = (UINT8) StrToHex(InString, 23, 2);
2148 Guid.Data4[3] = (UINT8) StrToHex(InString, 26, 2);
2149 Guid.Data4[4] = (UINT8) StrToHex(InString, 28, 2);
2150 Guid.Data4[5] = (UINT8) StrToHex(InString, 30, 2);
2151 Guid.Data4[6] = (UINT8) StrToHex(InString, 32, 2);
2152 Guid.Data4[7] = (UINT8) StrToHex(InString, 34, 2);
2153
2154 return Guid;
2155 } // EFI_GUID StringAsGuid()
2156
2157 // Returns TRUE if the two GUIDs are equal, FALSE otherwise
2158 BOOLEAN GuidsAreEqual(EFI_GUID *Guid1, EFI_GUID *Guid2) {
2159 return (CompareMem(Guid1, Guid2, 16) == 0);
2160 } // BOOLEAN CompareGuids()
2161