X-Git-Url: https://code.delx.au/refind/blobdiff_plain/9c6e61c4bcc4cc647abe94ae542e5f288b22963a..add96ff8057d0c03026395992d9ecde081c8dca7:/refind/lib.c diff --git a/refind/lib.c b/refind/lib.c index 49c0b99..15132ad 100644 --- a/refind/lib.c +++ b/refind/lib.c @@ -34,8 +34,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Modifications copyright (c) 2012-2013 Roderick W. Smith - * + * Modifications copyright (c) 2012-2015 Roderick W. Smith + * * Modifications distributed under the terms of the GNU General Public * License (GPL) version 3 (GPLv3), a copy of which must be distributed * with this source code or binaries made from it. @@ -48,7 +48,9 @@ #include "screen.h" #include "../include/refit_call_wrapper.h" #include "../include/RemovableMedia.h" -//#include "../include/UsbMass.h" +#include "gpt.h" +#include "config.h" +#include "../EfiLib/LegacyBios.h" #ifdef __MAKEWITH_GNUEFI #define EfiReallocatePool ReallocatePool @@ -74,6 +76,8 @@ EFI_DEVICE_PATH EndDevicePath[] = { #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" #define BTRFS_SIGNATURE "_BHRfS_M" +#define XFS_SIGNATURE "XFSB" +#define NTFS_SIGNATURE "NTFS " // variables @@ -86,6 +90,7 @@ CHAR16 *SelfDirPath; REFIT_VOLUME *SelfVolume = NULL; REFIT_VOLUME **Volumes = NULL; UINTN VolumesCount = 0; +extern GPT_DATA *gPartitions; // Maximum size for disk sectors #define SECTOR_SIZE 4096 @@ -188,6 +193,7 @@ EFI_STATUS InitRefitLib(IN EFI_HANDLE ImageHandle) // find the current directory DevicePathAsString = DevicePathToStr(SelfLoadedImage->FilePath); + GlobalConfig.SelfDevicePath = FileDevicePath(SelfLoadedImage->DeviceHandle, DevicePathAsString); CleanUpPathNameSlashes(DevicePathAsString); MyFreePool(SelfDirPath); Temp = FindPath(DevicePathAsString); @@ -265,28 +271,52 @@ static EFI_STATUS FinishInitRefitLib(VOID) } // -// list functions +// EFI variable read and write functions // -VOID CreateList(OUT VOID ***ListPtr, OUT UINTN *ElementCount, IN UINTN InitialElementCount) -{ - UINTN AllocateCount; +// From gummiboot: Retrieve a raw EFI variable. +// Returns EFI status +EFI_STATUS EfivarGetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) { + CHAR8 *buf; + UINTN l; + EFI_STATUS err; + + l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE; + buf = AllocatePool(l); + if (!buf) + return EFI_OUT_OF_RESOURCES; + + err = refit_call5_wrapper(RT->GetVariable, name, vendor, NULL, &l, buf); + if (EFI_ERROR(err) == EFI_SUCCESS) { + *buffer = buf; + if (size) + *size = l; + } else + MyFreePool(buf); + return err; +} // EFI_STATUS EfivarGetRaw() + +// From gummiboot: Set an EFI variable +EFI_STATUS EfivarSetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) { + UINT32 flags; + + flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; + if (persistent) + flags |= EFI_VARIABLE_NON_VOLATILE; + + return refit_call5_wrapper(RT->SetVariable, name, vendor, flags, size, buf); +} // EFI_STATUS EfivarSetRaw() - *ElementCount = InitialElementCount; - if (*ElementCount > 0) { - AllocateCount = (*ElementCount + 7) & ~7; // next multiple of 8 - *ListPtr = AllocatePool(sizeof(VOID *) * AllocateCount); - } else { - *ListPtr = NULL; - } -} +// +// list functions +// VOID AddListElement(IN OUT VOID ***ListPtr, IN OUT UINTN *ElementCount, IN VOID *NewElement) { UINTN AllocateCount; - if ((*ElementCount & 7) == 0) { - AllocateCount = *ElementCount + 8; + if ((*ElementCount & 15) == 0) { + AllocateCount = *ElementCount + 16; if (*ElementCount == 0) *ListPtr = AllocatePool(sizeof(VOID *) * AllocateCount); else @@ -309,82 +339,6 @@ VOID FreeList(IN OUT VOID ***ListPtr, IN OUT UINTN *ElementCount) } } // VOID FreeList() -// -// firmware device path discovery -// - -static UINT8 LegacyLoaderMediaPathData[] = { - 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B, - 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B, - 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00, -}; -static EFI_DEVICE_PATH *LegacyLoaderMediaPath = (EFI_DEVICE_PATH *)LegacyLoaderMediaPathData; - -VOID ExtractLegacyLoaderPaths(EFI_DEVICE_PATH **PathList, UINTN MaxPaths, EFI_DEVICE_PATH **HardcodedPathList) -{ - EFI_STATUS Status; - UINTN HandleCount = 0; - UINTN HandleIndex, HardcodedIndex; - EFI_HANDLE *Handles; - EFI_HANDLE Handle; - UINTN PathCount = 0; - UINTN PathIndex; - EFI_LOADED_IMAGE *LoadedImage; - EFI_DEVICE_PATH *DevicePath; - BOOLEAN Seen; - - MaxPaths--; // leave space for the terminating NULL pointer - - // get all LoadedImage handles - Status = LibLocateHandle(ByProtocol, &LoadedImageProtocol, NULL, &HandleCount, &Handles); - if (CheckError(Status, L"while listing LoadedImage handles")) { - if (HardcodedPathList) { - for (HardcodedIndex = 0; HardcodedPathList[HardcodedIndex] && PathCount < MaxPaths; HardcodedIndex++) - PathList[PathCount++] = HardcodedPathList[HardcodedIndex]; - } - PathList[PathCount] = NULL; - return; - } - for (HandleIndex = 0; HandleIndex < HandleCount && PathCount < MaxPaths; HandleIndex++) { - Handle = Handles[HandleIndex]; - - Status = refit_call3_wrapper(BS->HandleProtocol, Handle, &LoadedImageProtocol, (VOID **) &LoadedImage); - if (EFI_ERROR(Status)) - continue; // This can only happen if the firmware scewed up, ignore it. - - Status = refit_call3_wrapper(BS->HandleProtocol, LoadedImage->DeviceHandle, &DevicePathProtocol, (VOID **) &DevicePath); - if (EFI_ERROR(Status)) - continue; // This happens, ignore it. - - // Only grab memory range nodes - if (DevicePathType(DevicePath) != HARDWARE_DEVICE_PATH || DevicePathSubType(DevicePath) != HW_MEMMAP_DP) - continue; - - // Check if we have this device path in the list already - // WARNING: This assumes the first node in the device path is unique! - Seen = FALSE; - for (PathIndex = 0; PathIndex < PathCount; PathIndex++) { - if (DevicePathNodeLength(DevicePath) != DevicePathNodeLength(PathList[PathIndex])) - continue; - if (CompareMem(DevicePath, PathList[PathIndex], DevicePathNodeLength(DevicePath)) == 0) { - Seen = TRUE; - break; - } - } - if (Seen) - continue; - - PathList[PathCount++] = AppendDevicePath(DevicePath, LegacyLoaderMediaPath); - } - MyFreePool(Handles); - - if (HardcodedPathList) { - for (HardcodedIndex = 0; HardcodedPathList[HardcodedIndex] && PathCount < MaxPaths; HardcodedIndex++) - PathList[PathCount++] = HardcodedPathList[HardcodedIndex]; - } - PathList[PathCount] = NULL; -} - // // volume functions // @@ -396,6 +350,9 @@ static CHAR16 *FSTypeName(IN UINT32 TypeCode) { CHAR16 *retval = NULL; switch (TypeCode) { + case FS_TYPE_WHOLEDISK: + retval = L" whole disk"; + break; case FS_TYPE_FAT: retval = L" FAT"; break; @@ -417,9 +374,15 @@ static CHAR16 *FSTypeName(IN UINT32 TypeCode) { case FS_TYPE_BTRFS: retval = L" Btrfs"; break; + case FS_TYPE_XFS: + retval = L" XFS"; + break; case FS_TYPE_ISO9660: retval = L" ISO-9660"; break; + case FS_TYPE_NTFS: + retval = L" NTFS"; + break; default: retval = L""; break; @@ -428,32 +391,26 @@ static CHAR16 *FSTypeName(IN UINT32 TypeCode) { } // CHAR16 *FSTypeName() // Identify the filesystem type and record the filesystem's UUID/serial number, -// if possible. Expects a Buffer containing the first few (normally 4096) bytes -// of the filesystem. Sets the filesystem type code in Volume->FSType and the -// UUID/serial number in Volume->VolUuid. Note that the UUID value is recognized -// differently for each filesystem, and is currently supported only for -// ext2/3/4fs and ReiserFS. If the UUID can't be determined, it's set to 0. Also, the UUID -// is just read directly into memory; it is *NOT* valid when displayed by -// GuidAsString() or used in other GUID/UUID-manipulating functions. (As I -// write, it's being used merely to detect partitions that are part of a -// RAID 1 array.) +// if possible. Expects a Buffer containing the first few (normally at least +// 4096) bytes of the filesystem. Sets the filesystem type code in Volume->FSType +// and the UUID/serial number in Volume->VolUuid. Note that the UUID value is +// recognized differently for each filesystem, and is currently supported only +// for NTFS, ext2/3/4fs, and ReiserFS (and for NTFS it's really a 64-bit serial +// number not a UUID or GUID). If the UUID can't be determined, it's set to 0. +// Also, the UUID is just read directly into memory; it is *NOT* valid when +// displayed by GuidAsString() or used in other GUID/UUID-manipulating +// functions. (As I write, it's being used merely to detect partitions that are +// part of a RAID 1 array.) static VOID SetFilesystemData(IN UINT8 *Buffer, IN UINTN BufferSize, IN OUT REFIT_VOLUME *Volume) { UINT32 *Ext2Incompat, *Ext2Compat; UINT16 *Magic16; char *MagicString; + EFI_FILE *RootDir; if ((Buffer != NULL) && (Volume != NULL)) { SetMem(&(Volume->VolUuid), sizeof(EFI_GUID), 0); Volume->FSType = FS_TYPE_UNKNOWN; - if (BufferSize >= 512) { - Magic16 = (UINT16*) (Buffer + 510); - if (*Magic16 == FAT_MAGIC) { - Volume->FSType = FS_TYPE_FAT; - return; - } // if - } // search for FAT magic - if (BufferSize >= (1024 + 100)) { Magic16 = (UINT16*) (Buffer + 1024 + 56); if (*Magic16 == EXT2_SUPER_MAGIC) { // ext2/3/4 @@ -471,7 +428,7 @@ static VOID SetFilesystemData(IN UINT8 *Buffer, IN UINTN BufferSize, IN OUT REFI } } // search for ext2/3/4 magic - if (BufferSize >= (65536 + 62)) { + if (BufferSize >= (65536 + 100)) { MagicString = (char*) (Buffer + 65536 + 52); if ((CompareMem(MagicString, REISERFS_SUPER_MAGIC_STRING, 8) == 0) || (CompareMem(MagicString, REISER2FS_SUPER_MAGIC_STRING, 9) == 0) || @@ -490,6 +447,14 @@ static VOID SetFilesystemData(IN UINT8 *Buffer, IN UINTN BufferSize, IN OUT REFI } // if } // search for Btrfs magic + if (BufferSize >= 512) { + MagicString = (char*) Buffer; + if (CompareMem(MagicString, XFS_SIGNATURE, 4) == 0) { + Volume->FSType = FS_TYPE_XFS; + return; + } + } // search for XFS magic + if (BufferSize >= (1024 + 2)) { Magic16 = (UINT16*) (Buffer + 1024); if ((*Magic16 == HFSPLUS_MAGIC1) || (*Magic16 == HFSPLUS_MAGIC2)) { @@ -497,7 +462,40 @@ static VOID SetFilesystemData(IN UINT8 *Buffer, IN UINTN BufferSize, IN OUT REFI return; } } // search for HFS+ magic - } // if (Buffer != NULL) + + if (BufferSize >= 512) { + // Search for NTFS, FAT, and MBR/EBR. + // These all have 0xAA55 at the end of the first sector, but FAT and + // MBR/EBR are not easily distinguished. Thus, we first look for NTFS + // "magic"; then check to see if the volume can be mounted, thus + // relying on the EFI's built-in FAT driver to identify FAT; and then + // check to see if the "volume" is in fact a whole-disk device. + Magic16 = (UINT16*) (Buffer + 510); + if (*Magic16 == FAT_MAGIC) { + MagicString = (char*) (Buffer + 3); + if (CompareMem(MagicString, NTFS_SIGNATURE, 8) == 0) { + Volume->FSType = FS_TYPE_NTFS; + CopyMem(&(Volume->VolUuid), Buffer + 0x48, sizeof(UINT64)); + } else { + RootDir = LibOpenRoot(Volume->DeviceHandle); + if (RootDir != NULL) { + Volume->FSType = FS_TYPE_FAT; + } else if (!Volume->BlockIO->Media->LogicalPartition) { + Volume->FSType = FS_TYPE_WHOLEDISK; + } // if/elseif/else + } // if/else + return; + } // if + } // search for FAT and NTFS magic + + // If no other filesystem is identified and block size is right, assume + // it's ISO-9660.... + if (Volume->BlockIO->Media->BlockSize == 2048) { + Volume->FSType = FS_TYPE_ISO9660; + return; + } + + } // if ((Buffer != NULL) && (Volume != NULL)) } // UINT32 SetFilesystemData() @@ -507,7 +505,7 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) UINT8 Buffer[SAMPLE_SIZE]; UINTN i; MBR_PARTITION_INFO *MbrTable; - BOOLEAN MbrTableFound; + BOOLEAN MbrTableFound = FALSE; Volume->HasBootCode = FALSE; Volume->OSIconName = NULL; @@ -524,8 +522,9 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) Volume->BlockIO, Volume->BlockIO->Media->MediaId, Volume->BlockIOOffset, SAMPLE_SIZE, Buffer); if (!EFI_ERROR(Status)) { - SetFilesystemData(Buffer, SAMPLE_SIZE, Volume); + } + if ((Status == EFI_SUCCESS) && (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)) { if ((*((UINT16 *)(Buffer + 510)) == 0xaa55 && Buffer[0] != 0) && (FindMem(Buffer, 512, "EXFAT", 5) == -1)) { *Bootable = TRUE; Volume->HasBootCode = TRUE; @@ -545,15 +544,6 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) Volume->OSIconName = L"grub,linux"; Volume->OSName = L"Linux"; -// // Below doesn't produce a bootable entry, so commented out for the moment.... -// // GRUB in BIOS boot partition: -// } else if (FindMem(Buffer, 512, "Geom\0Read\0 Error", 16) >= 0) { -// Volume->HasBootCode = TRUE; -// Volume->OSIconName = L"grub,linux"; -// Volume->OSName = L"Linux"; -// Volume->VolName = L"BIOS Boot Partition"; -// *Bootable = TRUE; - } else if ((*((UINT32 *)(Buffer + 502)) == 0 && *((UINT32 *)(Buffer + 506)) == 50000 && *((UINT16 *)(Buffer + 510)) == 0xaa55) || @@ -562,6 +552,15 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) Volume->OSIconName = L"freebsd"; Volume->OSName = L"FreeBSD"; + // If more differentiation needed, also search for + // "Invalid partition table" &/or "Missing boot loader". + } else if ((*((UINT16 *)(Buffer + 510)) == 0xaa55) && + (FindMem(Buffer, SECTOR_SIZE, "Boot loader too large", 21) >= 0) && + (FindMem(Buffer, SECTOR_SIZE, "I/O error loading boot loader", 29) >= 0)) { + Volume->HasBootCode = TRUE; + Volume->OSIconName = L"freebsd"; + Volume->OSName = L"FreeBSD"; + } else if (FindMem(Buffer, 512, "!Loading", 8) >= 0 || FindMem(Buffer, SECTOR_SIZE, "/cdboot\0/CDBOOT\0", 16) >= 0) { Volume->HasBootCode = TRUE; @@ -574,14 +573,16 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) Volume->OSIconName = L"netbsd"; Volume->OSName = L"NetBSD"; + // Windows NT/200x/XP } else if (FindMem(Buffer, SECTOR_SIZE, "NTLDR", 5) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"win"; Volume->OSName = L"Windows"; + // Windows Vista/7/8 } else if (FindMem(Buffer, SECTOR_SIZE, "BOOTMGR", 7) >= 0) { Volume->HasBootCode = TRUE; - Volume->OSIconName = L"winvista,win"; + Volume->OSIconName = L"win8,win"; Volume->OSName = L"Windows"; } else if (FindMem(Buffer, 512, "CPUBOOT SYS", 11) >= 0 || @@ -615,7 +616,7 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) } // NOTE: If you add an operating system with a name that starts with 'W' or 'L', you - // need to fix AddLegacyEntry in main.c. + // need to fix AddLegacyEntry in refind/legacy.c. #if REFIT_DEBUG > 0 Print(L" Result of bootcode detection: %s %s (%s)\n", @@ -637,7 +638,6 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) // check for MBR partition table if (*((UINT16 *)(Buffer + 510)) == 0xaa55) { - MbrTableFound = FALSE; MbrTable = (MBR_PARTITION_INFO *)(Buffer + 446); for (i = 0; i < 4; i++) if (MbrTable[i].StartLBA && MbrTable[i].Size) @@ -661,8 +661,11 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) // Set default volume badge icon based on /.VolumeBadge.{icns|png} file or disk kind VOID SetVolumeBadgeIcon(REFIT_VOLUME *Volume) { + if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_BADGES) + return; + if (Volume->VolBadgeImage == NULL) { - Volume->VolBadgeImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeBadge", 128); + Volume->VolBadgeImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeBadge", GlobalConfig.IconSizes[ICON_SIZE_BADGE]); } if (Volume->VolBadgeImage == NULL) { @@ -676,6 +679,9 @@ VOID SetVolumeBadgeIcon(REFIT_VOLUME *Volume) case DISK_KIND_OPTICAL: Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL); break; + case DISK_KIND_NET: + Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_NET); + break; } // switch() } } // VOID SetVolumeBadgeIcon() @@ -705,49 +711,52 @@ static CHAR16 *SizeInIEEEUnits(UINT64 SizeInBytes) { SPrint(TheValue, 255, L"%ld%s", SizeInIeee, Units); } // if return TheValue; -} // CHAR16 *SizeInSIUnits() +} // CHAR16 *SizeInIEEEUnits() // Return a name for the volume. Ideally this should be the label for the -// filesystem it contains, but this function falls back to describing the +// filesystem or volume, but this function falls back to describing the // filesystem by size (200 MiB, etc.) and/or type (ext2, HFS+, etc.), if // this information can be extracted. // The calling function is responsible for freeing the memory allocated // for the name string. -static CHAR16 *GetVolumeName(IN REFIT_VOLUME *Volume) { - EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr; +static CHAR16 *GetVolumeName(REFIT_VOLUME *Volume) { + EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr = NULL; CHAR16 *FoundName = NULL; CHAR16 *SISize, *TypeName; - FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); - if (FileSystemInfoPtr != NULL) { // we have filesystem information (size, label).... - if ((FileSystemInfoPtr->VolumeLabel != NULL) && (StrLen(FileSystemInfoPtr->VolumeLabel) > 0)) { - FoundName = StrDuplicate(FileSystemInfoPtr->VolumeLabel); - } - - // Special case: rEFInd HFS+ driver always returns label of "HFS+ volume", so wipe - // this so that we can build a new name that includes the size.... - if ((FoundName != NULL) && (StrCmp(FoundName, L"HFS+ volume") == 0) && (Volume->FSType == FS_TYPE_HFSPLUS)) { - MyFreePool(FoundName); - FoundName = NULL; - } // if rEFInd HFS+ driver suspected - - if (FoundName == NULL) { // filesystem has no name, so use fs type and size - FoundName = AllocateZeroPool(sizeof(CHAR16) * 256); - if (FoundName != NULL) { - SISize = SizeInIEEEUnits(FileSystemInfoPtr->VolumeSize); - SPrint(FoundName, 255, L"%s%s volume", SISize, FSTypeName(Volume->FSType)); - MyFreePool(SISize); - } // if allocated memory OK - } // if (FoundName == NULL) - - FreePool(FileSystemInfoPtr); - - } else { // fs driver not returning info; fall back on our own information.... + if (Volume->RootDir != NULL) { + FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); + } + + if ((FileSystemInfoPtr != NULL) && (FileSystemInfoPtr->VolumeLabel != NULL) && + (StrLen(FileSystemInfoPtr->VolumeLabel) > 0)) { + FoundName = StrDuplicate(FileSystemInfoPtr->VolumeLabel); + } + + // If no filesystem name, try to use the partition name.... + if ((FoundName == NULL) && (Volume->PartName != NULL) && (StrLen(Volume->PartName) > 0) && + !IsIn(Volume->PartName, IGNORE_PARTITION_NAMES)) { + FoundName = StrDuplicate(Volume->PartName); + } // if use partition name + + // No filesystem or acceptable partition name, so use fs type and size + if ((FoundName == NULL) && (FileSystemInfoPtr != NULL)) { + FoundName = AllocateZeroPool(sizeof(CHAR16) * 256); + if (FoundName != NULL) { + SISize = SizeInIEEEUnits(FileSystemInfoPtr->VolumeSize); + SPrint(FoundName, 255, L"%s%s volume", SISize, FSTypeName(Volume->FSType)); + MyFreePool(SISize); + } // if allocated memory OK + } // if (FoundName == NULL) + + MyFreePool(FileSystemInfoPtr); + + if (FoundName == NULL) { FoundName = AllocateZeroPool(sizeof(CHAR16) * 256); if (FoundName != NULL) { TypeName = FSTypeName(Volume->FSType); // NOTE: Don't free TypeName; function returns constant if (StrLen(TypeName) > 0) - SPrint(FoundName, 255, L"%s volume", FSTypeName(Volume->FSType)); + SPrint(FoundName, 255, L"%s volume", TypeName); else SPrint(FoundName, 255, L"unknown volume"); } // if allocated memory OK @@ -755,7 +764,6 @@ static CHAR16 *GetVolumeName(IN REFIT_VOLUME *Volume) { // TODO: Above could be improved/extended, in case filesystem name is not found, // such as: - // - use partition label // - use or add disk/partition number (e.g., "(hd0,2)") // Desperate fallback name.... @@ -765,6 +773,47 @@ static CHAR16 *GetVolumeName(IN REFIT_VOLUME *Volume) { return FoundName; } // static CHAR16 *GetVolumeName() +// Determine the unique GUID, type code GUID, and name of the volume and store them. +static VOID SetPartGuidAndName(REFIT_VOLUME *Volume, EFI_DEVICE_PATH_PROTOCOL *DevicePath) { + HARDDRIVE_DEVICE_PATH *HdDevicePath; + GPT_ENTRY *PartInfo; + + if ((Volume == NULL) || (DevicePath == NULL)) + return; + + if ((DevicePath->Type == MEDIA_DEVICE_PATH) && (DevicePath->SubType == MEDIA_HARDDRIVE_DP)) { + HdDevicePath = (HARDDRIVE_DEVICE_PATH*) DevicePath; + if (HdDevicePath->SignatureType == SIGNATURE_TYPE_GUID) { + Volume->PartGuid = *((EFI_GUID*) HdDevicePath->Signature); + PartInfo = FindPartWithGuid(&(Volume->PartGuid)); + if (PartInfo) { + Volume->PartName = StrDuplicate(PartInfo->name); + CopyMem(&(Volume->PartTypeGuid), PartInfo->type_guid, sizeof(EFI_GUID)); + if (GuidsAreEqual(&(Volume->PartTypeGuid), &gFreedesktopRootGuid) && + ((PartInfo->attributes & GPT_NO_AUTOMOUNT) == 0)) { + GlobalConfig.DiscoveredRoot = Volume; + } // if (GUIDs match && automounting OK) + Volume->IsMarkedReadOnly = ((PartInfo->attributes & GPT_READ_ONLY) > 0); + } // if (PartInfo exists) + } // if (GPT disk) + } // if (disk device) +} // VOID SetPartGuid() + +// Return TRUE if NTFS boot files are found or if Volume is unreadable, +// FALSE otherwise. The idea is to weed out non-boot NTFS volumes from +// BIOS/legacy boot list on Macs. We can't assume NTFS will be readable, +// so return TRUE if it's unreadable; but if it IS readable, return +// TRUE only if Windows boot files are found. +static BOOLEAN HasWindowsBiosBootFiles(REFIT_VOLUME *Volume) { + BOOLEAN FilesFound = TRUE; + + if (Volume->RootDir != NULL) { + FilesFound = FileExists(Volume->RootDir, L"NTLDR") || // Windows NT/200x/XP boot file + FileExists(Volume->RootDir, L"bootmgr"); // Windows Vista/7/8 boot file + } // if + return FilesFound; +} // static VOID HasWindowsBiosBootFiles() + VOID ScanVolume(REFIT_VOLUME *Volume) { EFI_STATUS Status; @@ -806,6 +855,9 @@ VOID ScanVolume(REFIT_VOLUME *Volume) while (DevicePath != NULL && !IsDevicePathEndType(DevicePath)) { NextDevicePath = NextDevicePathNode(DevicePath); + if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH) { + SetPartGuidAndName(Volume, DevicePath); + } if (DevicePathType(DevicePath) == MESSAGING_DEVICE_PATH && (DevicePathSubType(DevicePath) == MSG_USB_DP || DevicePathSubType(DevicePath) == MSG_USB_CLASS_DP || @@ -846,7 +898,8 @@ VOID ScanVolume(REFIT_VOLUME *Volume) } // look at the BlockIO protocol - Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol, (VOID **) &Volume->WholeDiskBlockIO); + Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol, + (VOID **) &Volume->WholeDiskBlockIO); if (!EFI_ERROR(Status)) { // check the media block size @@ -864,32 +917,38 @@ VOID ScanVolume(REFIT_VOLUME *Volume) DevicePath = NextDevicePath; } // while - if (!Bootable) { + if (!Bootable) { #if REFIT_DEBUG > 0 - if (Volume->HasBootCode) - Print(L" Volume considered non-bootable, but boot code is present\n"); + if (Volume->HasBootCode) + Print(L" Volume considered non-bootable, but boot code is present\n"); #endif - Volume->HasBootCode = FALSE; - } + Volume->HasBootCode = FALSE; + } - // open the root directory of the volume - Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); + // open the root directory of the volume + Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); - // Set volume icon based on .VolumeBadge icon or disk kind - SetVolumeBadgeIcon(Volume); + // Set volume icon based on .VolumeBadge icon or disk kind + SetVolumeBadgeIcon(Volume); - if (Volume->RootDir == NULL) { - Volume->IsReadable = FALSE; - return; - } else { - Volume->IsReadable = TRUE; - } + Volume->VolName = GetVolumeName(Volume); - Volume->VolName = GetVolumeName(Volume); + if (Volume->RootDir == NULL) { + Volume->IsReadable = FALSE; + return; + } else { + Volume->IsReadable = TRUE; + if ((GlobalConfig.LegacyType == LEGACY_TYPE_MAC) && (Volume->FSType == FS_TYPE_NTFS) && Volume->HasBootCode) { + // VBR boot code found on NTFS, but volume is not actually bootable + // unless there are actual boot file, so check for them.... + Volume->HasBootCode = HasWindowsBiosBootFiles(Volume); + } + } // if/else - // get custom volume icons if present - if (!Volume->VolIconImage) - Volume->VolIconImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeIcon", 128); + // get custom volume icons if present + if (!Volume->VolIconImage) { + Volume->VolIconImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeIcon", GlobalConfig.IconSizes[ICON_SIZE_BIG]); + } } // ScanVolume() static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_INFO *MbrEntry) @@ -967,16 +1026,16 @@ VOID ScanVolumes(VOID) UINTN SectorSum, i, VolNumber = 0; UINT8 *SectorBuffer1, *SectorBuffer2; EFI_GUID *UuidList; - EFI_GUID NullUuid = { 00000000, 0000, 0000, {0000, 0000, 0000, 0000} }; + EFI_GUID NullUuid = NULL_GUID_VALUE; MyFreePool(Volumes); Volumes = NULL; VolumesCount = 0; + ForgetPartitionTables(); // get all filesystem handles Status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL, &HandleCount, &Handles); UuidList = AllocateZeroPool(sizeof(EFI_GUID) * HandleCount); - // was: &FileSystemProtocol if (Status == EFI_NOT_FOUND) { return; // no filesystems. strange, but true... } @@ -987,6 +1046,7 @@ VOID ScanVolumes(VOID) for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) { Volume = AllocateZeroPool(sizeof(REFIT_VOLUME)); Volume->DeviceHandle = Handles[HandleIndex]; + AddPartitionTable(Volume); ScanVolume(Volume); if (UuidList) { UuidList[HandleIndex] = Volume->VolUuid; @@ -1033,8 +1093,9 @@ VOID ScanVolumes(VOID) Volume->BlockIO != Volume->WholeDiskBlockIO) { for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) { if (Volumes[VolumeIndex2]->BlockIO == Volume->WholeDiskBlockIO && - Volumes[VolumeIndex2]->BlockIOOffset == 0) + Volumes[VolumeIndex2]->BlockIOOffset == 0) { WholeDiskVolume = Volumes[VolumeIndex2]; + } } } @@ -1082,7 +1143,6 @@ VOID ScanVolumes(VOID) MyFreePool(SectorBuffer1); MyFreePool(SectorBuffer2); } - } // for } /* VOID ScanVolumes() */ @@ -1138,7 +1198,8 @@ VOID ReinitVolumes(VOID) if (!EFI_ERROR(Status)) { // get the BlockIO protocol - Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol, (VOID **) &Volume->WholeDiskBlockIO); + Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol, + (VOID **) &Volume->WholeDiskBlockIO); if (EFI_ERROR(Status)) { Volume->WholeDiskBlockIO = NULL; CheckError(Status, L"from HandleProtocol"); @@ -1155,15 +1216,17 @@ VOID ReinitVolumes(VOID) BOOLEAN FileExists(IN EFI_FILE *BaseDir, IN CHAR16 *RelativePath) { - EFI_STATUS Status; - EFI_FILE_HANDLE TestFile; - - Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &TestFile, RelativePath, EFI_FILE_MODE_READ, 0); - if (Status == EFI_SUCCESS) { - refit_call1_wrapper(TestFile->Close, TestFile); - return TRUE; - } - return FALSE; + EFI_STATUS Status; + EFI_FILE_HANDLE TestFile; + + if (BaseDir != NULL) { + Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &TestFile, RelativePath, EFI_FILE_MODE_READ, 0); + if (Status == EFI_SUCCESS) { + refit_call1_wrapper(TestFile->Close, TestFile); + return TRUE; + } + } + return FALSE; } EFI_STATUS DirNextEntry(IN EFI_FILE *Directory, IN OUT EFI_FILE_INFO **DirEntry, IN UINTN FilterMode) @@ -1286,14 +1349,6 @@ MetaiMatch (IN CHAR16 *String, IN CHAR16 *Pattern) return FALSE; // Shouldn't happen } -static VOID StrLwr (IN OUT CHAR16 *Str) { - if (!mUnicodeCollation) { - InitializeUnicodeCollationProtocol(); - } - if (mUnicodeCollation) - mUnicodeCollation->StrLwr (mUnicodeCollation, Str); -} - #endif BOOLEAN DirIterNext(IN OUT REFIT_DIR_ITER *DirIter, IN UINTN FilterMode, IN CHAR16 *FilePattern OPTIONAL, @@ -1379,8 +1434,7 @@ CHAR16 * StripEfiExtension(CHAR16 *FileName) { if ((FileName != NULL) && ((Copy = StrDuplicate(FileName)) != NULL)) { Length = StrLen(Copy); - // Note: Do StriCmp() twice to work around Gigabyte Hybrid EFI case-sensitivity bug.... - if ((Length >= 4) && ((StriCmp(&Copy[Length - 4], L".efi") == 0) || (StriCmp(&Copy[Length - 4], L".EFI") == 0))) { + if ((Length >= 4) && MyStriCmp(&Copy[Length - 4], L".efi")) { Copy[Length - 4] = 0; } // if } // if @@ -1406,30 +1460,62 @@ INTN FindMem(IN VOID *Buffer, IN UINTN BufferLength, IN VOID *SearchString, IN U return -1; } -// Performs a case-insensitive search of BigStr for SmallStr. -// Returns TRUE if found, FALSE if not. BOOLEAN StriSubCmp(IN CHAR16 *SmallStr, IN CHAR16 *BigStr) { - CHAR16 *SmallCopy, *BigCopy; - BOOLEAN Found = FALSE; - UINTN StartPoint = 0, NumCompares = 0, SmallLen = 0; - - if ((SmallStr != NULL) && (BigStr != NULL) && (StrLen(BigStr) >= StrLen(SmallStr))) { - SmallCopy = StrDuplicate(SmallStr); - BigCopy = StrDuplicate(BigStr); - StrLwr(SmallCopy); - StrLwr(BigCopy); - SmallLen = StrLen(SmallCopy); - NumCompares = StrLen(BigCopy) - SmallLen + 1; - while ((!Found) && (StartPoint < NumCompares)) { - Found = (StrnCmp(SmallCopy, &BigCopy[StartPoint++], SmallLen) == 0); - } // while - MyFreePool(SmallCopy); - MyFreePool(BigCopy); - } // if + BOOLEAN Found = 0, Terminate = 0; + UINTN BigIndex = 0, SmallIndex = 0, BigStart = 0; - return (Found); + if (SmallStr && BigStr) { + while (!Terminate) { + if (BigStr[BigIndex] == '\0') { + Terminate = 1; + } + if (SmallStr[SmallIndex] == '\0') { + Found = 1; + Terminate = 1; + } + if ((SmallStr[SmallIndex] & ~0x20) == (BigStr[BigIndex] & ~0x20)) { + SmallIndex++; + BigIndex++; + } else { + SmallIndex = 0; + BigStart++; + BigIndex = BigStart; + } + } // while + } // if + return Found; } // BOOLEAN StriSubCmp() +// Performs a case-insensitive string comparison. This function is necesary +// because some EFIs have buggy StriCmp() functions that actually perform +// case-sensitive comparisons. +// Returns TRUE if strings are identical, FALSE otherwise. +BOOLEAN MyStriCmp(IN CONST CHAR16 *FirstString, IN CONST CHAR16 *SecondString) { + if (FirstString && SecondString) { + while ((*FirstString != L'\0') && ((*FirstString & ~0x20) == (*SecondString & ~0x20))) { + FirstString++; + SecondString++; + } + return (*FirstString == *SecondString); + } else { + return FALSE; + } +} // BOOLEAN MyStriCmp() + +// Convert input string to all-lowercase. +// DO NOT USE the standard StrLwr() function, since it's broken on some EFIs! +VOID ToLower(CHAR16 * MyString) { + UINTN i = 0; + + if (MyString) { + while (MyString[i] != L'\0') { + if ((MyString[i] >= L'A') && (MyString[i] <= L'Z')) + MyString[i] = MyString[i] - L'A' + L'a'; + i++; + } // while + } // if +} // VOID ToLower() + // Merges two strings, creating a new one and returning a pointer to it. // If AddChar != 0, the specified character is placed between the two original // strings (unless the first string is NULL or empty). The original input @@ -1449,7 +1535,7 @@ VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar) { Length2 = StrLen(Second); NewString = AllocatePool(sizeof(CHAR16) * (Length1 + Length2 + 2)); if (NewString != NULL) { - if ((*First != NULL) && (StrLen(*First) == 0)) { + if ((*First != NULL) && (Length1 == 0)) { MyFreePool(*First); *First = NULL; } @@ -1468,7 +1554,35 @@ VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar) { } else { Print(L"Error! Unable to allocate memory in MergeStrings()!\n"); } // if/else -} // static CHAR16* MergeStrings() +} // VOID MergeStrings() + +// Similar to MergeStrings, but breaks the input string into word chunks and +// merges each word separately. Words are defined as string fragments separated +// by ' ', '_', or '-'. +VOID MergeWords(CHAR16 **MergeTo, CHAR16 *SourceString, CHAR16 AddChar) { + CHAR16 *Temp, *Word, *p; + BOOLEAN LineFinished = FALSE; + + if (SourceString) { + Temp = Word = p = StrDuplicate(SourceString); + if (Temp) { + while (!LineFinished) { + if ((*p == L' ') || (*p == L'_') || (*p == L'-') || (*p == L'\0')) { + if (*p == L'\0') + LineFinished = TRUE; + *p = L'\0'; + if (*Word != L'\0') + MergeStrings(MergeTo, Word, AddChar); + Word = p + 1; + } // if + p++; + } // while + MyFreePool(Temp); + } else { + Print(L"Error! Unable to allocate memory in MergeWords()!\n"); + } // if/else + } // if +} // VOID MergeWords() // Takes an input pathname (*Path) and returns the part of the filename from // the final dot onwards, converted to lowercase. If the filename includes @@ -1493,7 +1607,7 @@ CHAR16 *FindExtension(IN CHAR16 *Path) { } // while if (Found) { MergeStrings(&Extension, &Path[i], 0); - StrLwr(Extension); + ToLower(Extension); } // if (Found) } // if return (Extension); @@ -1548,11 +1662,95 @@ CHAR16 *FindPath(IN CHAR16* FullPath) { LastBackslash = i; } // for PathOnly = StrDuplicate(FullPath); - PathOnly[LastBackslash] = 0; + if (PathOnly != NULL) + PathOnly[LastBackslash] = 0; } // if return (PathOnly); } +/*++ + * + * Routine Description: + * + * Find a substring. + * + * Arguments: + * + * String - Null-terminated string to search. + * StrCharSet - Null-terminated string to search for. + * + * Returns: + * The address of the first occurrence of the matching substring if successful, or NULL otherwise. + * --*/ +CHAR16* MyStrStr (CHAR16 *String, CHAR16 *StrCharSet) +{ + CHAR16 *Src; + CHAR16 *Sub; + + if ((String == NULL) || (StrCharSet == NULL)) + return NULL; + + Src = String; + Sub = StrCharSet; + + while ((*String != L'\0') && (*StrCharSet != L'\0')) { + if (*String++ != *StrCharSet) { + String = ++Src; + StrCharSet = Sub; + } else { + StrCharSet++; + } + } + if (*StrCharSet == L'\0') { + return Src; + } else { + return NULL; + } +} // CHAR16 *MyStrStr() + +// Restrict TheString to at most Limit characters. +// Does this in two ways: +// - Locates stretches of two or more spaces and compresses +// them down to one space. +// - Truncates TheString +// Returns TRUE if changes were made, FALSE otherwise +BOOLEAN LimitStringLength(CHAR16 *TheString, UINTN Limit) { + CHAR16 *SubString, *TempString; + UINTN i; + BOOLEAN HasChanged = FALSE; + + // SubString will be NULL or point WITHIN TheString + SubString = MyStrStr(TheString, L" "); + while (SubString != NULL) { + i = 0; + while (SubString[i] == L' ') + i++; + if (i >= StrLen(SubString)) { + SubString[0] = '\0'; + HasChanged = TRUE; + } else { + TempString = StrDuplicate(&SubString[i]); + if (TempString != NULL) { + StrCpy(&SubString[1], TempString); + MyFreePool(TempString); + HasChanged = TRUE; + } else { + // memory allocation problem; abort to avoid potentially infinite loop! + break; + } // if/else + } // if/else + SubString = MyStrStr(TheString, L" "); + } // while + + // If the string is still too long, truncate it.... + if (StrLen(TheString) > Limit) { + TheString[Limit] = '\0'; + HasChanged = TRUE; + } // if + + return HasChanged; +} // BOOLEAN LimitStringLength() + // Takes an input loadpath, splits it into disk and filename components, finds a matching // DeviceVolume, and returns that and the filename (*loader). VOID FindVolumeAndFilename(IN EFI_DEVICE_PATH *loadpath, OUT REFIT_VOLUME **DeviceVolume, OUT CHAR16 **loader) { @@ -1569,7 +1767,7 @@ VOID FindVolumeAndFilename(IN EFI_DEVICE_PATH *loadpath, OUT REFIT_VOLUME **Devi while ((i < VolumesCount) && (!Found)) { VolumeDeviceString = DevicePathToStr(Volumes[i]->DevicePath); Temp = SplitDeviceString(VolumeDeviceString); - if (StriCmp(DeviceString, VolumeDeviceString) == 0) { + if (MyStriCmp(DeviceString, VolumeDeviceString)) { Found = TRUE; *DeviceVolume = Volumes[i]; } @@ -1681,9 +1879,63 @@ CHAR16 *FindCommaDelimited(IN CHAR16 *InString, IN UINTN Index) { return (FoundString); } // CHAR16 *FindCommaDelimited() +// Return the position of SmallString within BigString, or -1 if +// not found. +INTN FindSubString(IN CHAR16 *SmallString, IN CHAR16 *BigString) { + INTN Position = -1; + UINTN i = 0, SmallSize, BigSize; + BOOLEAN Found = FALSE; + + if ((SmallString == NULL) || (BigString == NULL)) + return -1; + + SmallSize = StrLen(SmallString); + BigSize = StrLen(BigString); + if ((SmallSize > BigSize) || (SmallSize == 0) || (BigSize == 0)) + return -1; + + while ((i <= (BigSize - SmallSize) && !Found)) { + if (CompareMem(BigString + i, SmallString, SmallSize) == 0) { + Found = TRUE; + Position = i; + } // if + i++; + } // while() + return Position; +} // INTN FindSubString() + +// Take an input path name, which may include a volume specification and/or +// a path, and return separate volume, path, and file names. For instance, +// "BIGVOL:\EFI\ubuntu\grubx64.efi" will return a VolName of "BIGVOL", a Path +// of "EFI\ubuntu", and a Filename of "grubx64.efi". If an element is missing, +// the returned pointer is NULL. The calling function is responsible for +// freeing the allocated memory. +VOID SplitPathName(CHAR16 *InPath, CHAR16 **VolName, CHAR16 **Path, CHAR16 **Filename) { + CHAR16 *Temp = NULL; + + MyFreePool(*VolName); + MyFreePool(*Path); + MyFreePool(*Filename); + *VolName = *Path = *Filename = NULL; + Temp = StrDuplicate(InPath); + SplitVolumeAndFilename(&Temp, VolName); // VolName is NULL or has volume; Temp has rest of path + CleanUpPathNameSlashes(Temp); + *Path = FindPath(Temp); // *Path has path (may be 0-length); Temp unchanged. + *Filename = StrDuplicate(Temp + StrLen(*Path)); + CleanUpPathNameSlashes(*Filename); + if (StrLen(*Path) == 0) { + MyFreePool(*Path); + *Path = NULL; + } + if (StrLen(*Filename) == 0) { + MyFreePool(*Filename); + *Filename = NULL; + } + MyFreePool(Temp); +} // VOID SplitPathName + // Returns TRUE if SmallString is an element in the comma-delimited List, -// FALSE otherwise. Performs comparison case-insensitively (except on -// buggy EFIs with case-sensitive StriCmp() functions). +// FALSE otherwise. Performs comparison case-insensitively. BOOLEAN IsIn(IN CHAR16 *SmallString, IN CHAR16 *List) { UINTN i = 0; BOOLEAN Found = FALSE; @@ -1691,13 +1943,83 @@ BOOLEAN IsIn(IN CHAR16 *SmallString, IN CHAR16 *List) { if (SmallString && List) { while (!Found && (OneElement = FindCommaDelimited(List, i++))) { - if (StriCmp(OneElement, SmallString) == 0) + if (MyStriCmp(OneElement, SmallString)) Found = TRUE; } // while } // if return Found; } // BOOLEAN IsIn() +// Returns TRUE if any element of List can be found as a substring of +// BigString, FALSE otherwise. Performs comparisons case-insensitively. +BOOLEAN IsInSubstring(IN CHAR16 *BigString, IN CHAR16 *List) { + UINTN i = 0, ElementLength; + BOOLEAN Found = FALSE; + CHAR16 *OneElement; + + if (BigString && List) { + while (!Found && (OneElement = FindCommaDelimited(List, i++))) { + ElementLength = StrLen(OneElement); + if ((ElementLength <= StrLen(BigString)) && (StriSubCmp(OneElement, BigString))) + Found = TRUE; + } // while + } // if + return Found; +} // BOOLEAN IsSubstringIn() + +// Returns TRUE if specified Volume, Directory, and Filename correspond to an +// element in the comma-delimited List, FALSE otherwise. Note that Directory and +// Filename must *NOT* include a volume or path specification (that's part of +// the Volume variable), but the List elements may. Performs comparison +// case-insensitively. +BOOLEAN FilenameIn(REFIT_VOLUME *Volume, CHAR16 *Directory, CHAR16 *Filename, CHAR16 *List) { + UINTN i = 0; + BOOLEAN Found = FALSE; + CHAR16 *OneElement; + CHAR16 *TargetVolName = NULL, *TargetPath = NULL, *TargetFilename = NULL; + + if (Filename && List) { + while (!Found && (OneElement = FindCommaDelimited(List, i++))) { + Found = TRUE; + SplitPathName(OneElement, &TargetVolName, &TargetPath, &TargetFilename); + VolumeNumberToName(Volume, &TargetVolName); + if (((TargetVolName != NULL) && ((Volume == NULL) || (!MyStriCmp(TargetVolName, Volume->VolName)))) || + ((TargetPath != NULL) && (!MyStriCmp(TargetPath, Directory))) || + ((TargetFilename != NULL) && (!MyStriCmp(TargetFilename, Filename)))) { + Found = FALSE; + } // if + MyFreePool(OneElement); + } // while + } // if + + MyFreePool(TargetVolName); + MyFreePool(TargetPath); + MyFreePool(TargetFilename); + return Found; +} // BOOLEAN FilenameIn() + +// If *VolName is of the form "fs#", where "#" is a number, and if Volume points +// to this volume number, returns with *VolName changed to the volume name, as +// stored in the Volume data structure. +// Returns TRUE if this substitution was made, FALSE otherwise. +BOOLEAN VolumeNumberToName(REFIT_VOLUME *Volume, CHAR16 **VolName) { + BOOLEAN MadeSubstitution = FALSE; + UINTN VolNum; + + if ((VolName == NULL) || (*VolName == NULL)) + return FALSE; + + if ((StrLen(*VolName) > 2) && (*VolName[0] == L'f') && (*VolName[1] == L's') && (*VolName[2] >= L'0') && (*VolName[2] <= L'9')) { + VolNum = Atoi(*VolName + 2); + if (VolNum == Volume->VolNumber) { + MyFreePool(*VolName); + *VolName = StrDuplicate(Volume->VolName); + MadeSubstitution = TRUE; + } // if + } // if + return MadeSubstitution; +} // BOOLEAN VolumeMatchesNumber() + // Implement FreePool the way it should have been done to begin with, so that // it doesn't throw an ASSERT message if fed a NULL pointer.... VOID MyFreePool(IN VOID *Pointer) { @@ -1732,6 +2054,69 @@ BOOLEAN EjectMedia(VOID) { return (Ejected > 0); } // VOID EjectMedia() +// Converts consecutive characters in the input string into a +// number, interpreting the string as a hexadecimal number, starting +// at the specified position and continuing for the specified number +// of characters or until the end of the string, whichever is first. +// NumChars must be between 1 and 16. Ignores invalid characters. +UINT64 StrToHex(CHAR16 *Input, UINTN Pos, UINTN NumChars) { + UINT64 retval = 0x00; + UINTN NumDone = 0; + CHAR16 a; + + if ((Input == NULL) || (StrLen(Input) < Pos) || (NumChars == 0) || (NumChars > 16)) { + return 0; + } + + while ((StrLen(Input) >= Pos) && (NumDone < NumChars)) { + a = Input[Pos]; + if ((a >= '0') && (a <= '9')) { + retval *= 0x10; + retval += (a - '0'); + NumDone++; + } + if ((a >= 'a') && (a <= 'f')) { + retval *= 0x10; + retval += (a - 'a' + 0x0a); + NumDone++; + } + if ((a >= 'A') && (a <= 'F')) { + retval *= 0x10; + retval += (a - 'A' + 0x0a); + NumDone++; + } + Pos++; + } // while() + return retval; +} // StrToHex() + +// Returns TRUE if UnknownString can be interpreted as a GUID, FALSE otherwise. +// Note that the input string must have no extraneous spaces and must be +// conventionally formatted as a 36-character GUID, complete with dashes in +// appropriate places. +BOOLEAN IsGuid(CHAR16 *UnknownString) { + UINTN Length, i; + BOOLEAN retval = TRUE; + CHAR16 a; + + if (UnknownString == NULL) + return FALSE; + + Length = StrLen(UnknownString); + if (Length != 36) + return FALSE; + + for (i = 0; i < Length; i++) { + a = UnknownString[i]; + if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) { + if (a != '-') + retval = FALSE; + } else if (((a < 'a') || (a > 'f')) && ((a < 'A') || (a > 'F')) && ((a < '0') && (a > '9'))) { + retval = FALSE; + } // if/else if + } // for + return retval; +} // BOOLEAN IsGuid() // Return the GUID as a string, suitable for display to the user. Note that the calling // function is responsible for freeing the allocated memory. @@ -1748,3 +2133,31 @@ CHAR16 * GuidAsString(EFI_GUID *GuidData) { } return TheString; } // GuidAsString(EFI_GUID *GuidData) + +EFI_GUID StringAsGuid(CHAR16 * InString) { + EFI_GUID Guid = NULL_GUID_VALUE; + + if (!IsGuid(InString)) { + return Guid; + } + + Guid.Data1 = (UINT32) StrToHex(InString, 0, 8); + Guid.Data2 = (UINT16) StrToHex(InString, 9, 4); + Guid.Data3 = (UINT16) StrToHex(InString, 14, 4); + Guid.Data4[0] = (UINT8) StrToHex(InString, 19, 2); + Guid.Data4[1] = (UINT8) StrToHex(InString, 21, 2); + Guid.Data4[2] = (UINT8) StrToHex(InString, 23, 2); + Guid.Data4[3] = (UINT8) StrToHex(InString, 26, 2); + Guid.Data4[4] = (UINT8) StrToHex(InString, 28, 2); + Guid.Data4[5] = (UINT8) StrToHex(InString, 30, 2); + Guid.Data4[6] = (UINT8) StrToHex(InString, 32, 2); + Guid.Data4[7] = (UINT8) StrToHex(InString, 34, 2); + + return Guid; +} // EFI_GUID StringAsGuid() + +// Returns TRUE if the two GUIDs are equal, FALSE otherwise +BOOLEAN GuidsAreEqual(EFI_GUID *Guid1, EFI_GUID *Guid2) { + return (CompareMem(Guid1, Guid2, 16) == 0); +} // BOOLEAN GuidsAreEqual() +