X-Git-Url: https://code.delx.au/refind/blobdiff_plain/2f2310b6faef9a3a2161dd7473be056d2807ce48..e07b72471fa62945801b4a4e3bcac662a1b4888d:/refind/lib.c diff --git a/refind/lib.c b/refind/lib.c index fde385f..5960f36 100644 --- a/refind/lib.c +++ b/refind/lib.c @@ -34,19 +34,46 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Modifications copyright (c) 2012 Roderick W. Smith + * Modifications copyright (c) 2012-2013 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. - * + * */ #include "global.h" #include "lib.h" #include "icns.h" #include "screen.h" -#include "refit_call_wrapper.h" +#include "../include/refit_call_wrapper.h" +#include "../include/RemovableMedia.h" +//#include "../include/UsbMass.h" + +#ifdef __MAKEWITH_GNUEFI +#define EfiReallocatePool ReallocatePool +#else +#define LibLocateHandle gBS->LocateHandleBuffer +#define DevicePathProtocol gEfiDevicePathProtocolGuid +#define BlockIoProtocol gEfiBlockIoProtocolGuid +#define LibFileSystemInfo EfiLibFileSystemInfo +#define LibOpenRoot EfiLibOpenRoot +EFI_DEVICE_PATH EndDevicePath[] = { + {END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, {END_DEVICE_PATH_LENGTH, 0}} +}; + +//#define EndDevicePath DevicePath +#endif + +// "Magic" signatures for various filesystems +#define FAT_MAGIC 0xAA55 +#define EXT2_SUPER_MAGIC 0xEF53 +#define HFSPLUS_MAGIC1 0x2B48 +#define HFSPLUS_MAGIC2 0x5848 +#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" +#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" +#define BTRFS_SIGNATURE "_BHRfS_M" // variables @@ -63,9 +90,10 @@ UINTN VolumesCount = 0; // Maximum size for disk sectors #define SECTOR_SIZE 4096 -// Default names for volume badges (mini-icon to define disk type) and icons -#define VOLUME_BADGE_NAME L".VolumeBadge.icns" -#define VOLUME_ICON_NAME L".VolumeIcon.icns" +// Number of bytes to read from a partition to determine its filesystem type +// and identify its boot loader, and hence probable BIOS-mode OS installation +#define SAMPLE_SIZE 69632 /* 68 KiB -- ReiserFS superblock begins at 64 KiB */ + // functions @@ -115,10 +143,42 @@ VOID CleanUpPathNameSlashes(IN OUT CHAR16 *PathName) { } // if allocation OK } // CleanUpPathNameSlashes() +// Splits an EFI device path into device and filename components. For instance, if InString is +// PciRoot(0x0)/Pci(0x1f,0x2)/Ata(Secondary,Master,0x0)/HD(2,GPT,8314ae90-ada3-48e9-9c3b-09a88f80d921,0x96028,0xfa000)/\bzImage-3.5.1.efi, +// this function will truncate that input to +// PciRoot(0x0)/Pci(0x1f,0x2)/Ata(Secondary,Master,0x0)/HD(2,GPT,8314ae90-ada3-48e9-9c3b-09a88f80d921,0x96028,0xfa000) +// and return bzImage-3.5.1.efi as its return value. +// It does this by searching for the last ")" character in InString, copying everything +// after that string (after some cleanup) as the return value, and truncating the original +// input value. +// If InString contains no ")" character, this function leaves the original input string +// unmodified and also returns that string. If InString is NULL, this function returns NULL. +static CHAR16* SplitDeviceString(IN OUT CHAR16 *InString) { + INTN i; + CHAR16 *FileName = NULL; + BOOLEAN Found = FALSE; + + if (InString != NULL) { + i = StrLen(InString) - 1; + while ((i >= 0) && (!Found)) { + if (InString[i] == L')') { + Found = TRUE; + FileName = StrDuplicate(&InString[i + 1]); + CleanUpPathNameSlashes(FileName); + InString[i + 1] = '\0'; + } // if + i--; + } // while + if (FileName == NULL) + FileName = StrDuplicate(InString); + } // if + return FileName; +} // static CHAR16* SplitDeviceString() + EFI_STATUS InitRefitLib(IN EFI_HANDLE ImageHandle) { EFI_STATUS Status; - CHAR16 *DevicePathAsString; + CHAR16 *DevicePathAsString, *Temp; SelfImageHandle = ImageHandle; Status = refit_call3_wrapper(BS->HandleProtocol, SelfImageHandle, &LoadedImageProtocol, (VOID **) &SelfLoadedImage); @@ -128,10 +188,11 @@ EFI_STATUS InitRefitLib(IN EFI_HANDLE ImageHandle) // find the current directory DevicePathAsString = DevicePathToStr(SelfLoadedImage->FilePath); CleanUpPathNameSlashes(DevicePathAsString); - if (SelfDirPath != NULL) - FreePool(SelfDirPath); - SelfDirPath = FindPath(DevicePathAsString); - FreePool(DevicePathAsString); + MyFreePool(SelfDirPath); + Temp = FindPath(DevicePathAsString); + SelfDirPath = SplitDeviceString(Temp); + MyFreePool(DevicePathAsString); + MyFreePool(Temp); return FinishInitRefitLib(); } @@ -223,7 +284,7 @@ VOID AddListElement(IN OUT VOID ***ListPtr, IN OUT UINTN *ElementCount, IN VOID if (*ElementCount == 0) *ListPtr = AllocatePool(sizeof(VOID *) * AllocateCount); else - *ListPtr = ReallocatePool(*ListPtr, sizeof(VOID *) * (*ElementCount), sizeof(VOID *) * AllocateCount); + *ListPtr = EfiReallocatePool(*ListPtr, sizeof(VOID *) * (*ElementCount), sizeof(VOID *) * AllocateCount); } (*ListPtr)[*ElementCount] = NewElement; (*ElementCount)++; @@ -233,14 +294,14 @@ VOID FreeList(IN OUT VOID ***ListPtr, IN OUT UINTN *ElementCount) { UINTN i; - if (*ElementCount > 0) { + if ((*ElementCount > 0) && (**ListPtr != NULL)) { for (i = 0; i < *ElementCount; i++) { // TODO: call a user-provided routine for each element here - FreePool((*ListPtr)[i]); + MyFreePool((*ListPtr)[i]); } - FreePool(*ListPtr); + MyFreePool(*ListPtr); } -} +} // VOID FreeList() // // firmware device path discovery @@ -269,8 +330,7 @@ VOID ExtractLegacyLoaderPaths(EFI_DEVICE_PATH **PathList, UINTN MaxPaths, EFI_DE MaxPaths--; // leave space for the terminating NULL pointer // get all LoadedImage handles - Status = LibLocateHandle(ByProtocol, &LoadedImageProtocol, NULL, - &HandleCount, &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++) @@ -310,7 +370,7 @@ VOID ExtractLegacyLoaderPaths(EFI_DEVICE_PATH **PathList, UINTN MaxPaths, EFI_DE PathList[PathCount++] = AppendDevicePath(DevicePath, LegacyLoaderMediaPath); } - FreePool(Handles); + MyFreePool(Handles); if (HardcodedPathList) { for (HardcodedIndex = 0; HardcodedPathList[HardcodedIndex] && PathCount < MaxPaths; HardcodedIndex++) @@ -323,10 +383,106 @@ VOID ExtractLegacyLoaderPaths(EFI_DEVICE_PATH **PathList, UINTN MaxPaths, EFI_DE // volume functions // -static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootable) +// Return a pointer to a string containing a filesystem type name. If the +// filesystem type is unknown, a blank (but non-null) string is returned. +// The returned variable is a constant that should NOT be freed. +static CHAR16 *FSTypeName(IN UINT32 TypeCode) { + CHAR16 *retval = NULL; + + switch (TypeCode) { + case FS_TYPE_FAT: + retval = L" FAT"; + break; + case FS_TYPE_HFSPLUS: + retval = L" HFS+"; + break; + case FS_TYPE_EXT2: + retval = L" ext2"; + break; + case FS_TYPE_EXT3: + retval = L" ext3"; + break; + case FS_TYPE_EXT4: + retval = L" ext4"; + break; + case FS_TYPE_REISERFS: + retval = L" ReiserFS"; + break; + case FS_TYPE_BTRFS: + retval = L" Btrfs"; + break; + case FS_TYPE_ISO9660: + retval = L" ISO-9660"; + break; + default: + retval = L""; + break; + } // switch + return retval; +} // CHAR16 *FSTypeName() + +// Identify the filesystem type, if possible. Expects a Buffer containing +// the first few (normally 4096) bytes of the filesystem, and outputs a +// code representing the identified filesystem type. +static UINT32 IdentifyFilesystemType(IN UINT8 *Buffer, IN UINTN BufferSize) { + UINT32 FoundType = FS_TYPE_UNKNOWN; + UINT32 *Ext2Incompat, *Ext2Compat; + UINT16 *Magic16; + char *MagicString; + + if (Buffer != NULL) { + + if (BufferSize >= 512) { + Magic16 = (UINT16*) (Buffer + 510); + if (*Magic16 == FAT_MAGIC) + return FS_TYPE_FAT; + } // search for FAT magic + + if (BufferSize >= (1024 + 100)) { + Magic16 = (UINT16*) (Buffer + 1024 + 56); + if (*Magic16 == EXT2_SUPER_MAGIC) { // ext2/3/4 + Ext2Compat = (UINT32*) (Buffer + 1024 + 92); + Ext2Incompat = (UINT32*) (Buffer + 1024 + 96); + if ((*Ext2Incompat & 0x0040) || (*Ext2Incompat & 0x0200)) { // check for extents or flex_bg + return FS_TYPE_EXT4; + } else if (*Ext2Compat & 0x0004) { // check for journal + return FS_TYPE_EXT3; + } else { // none of these features; presume it's ext2... + return FS_TYPE_EXT2; + } + } + } // search for ext2/3/4 magic + + if (BufferSize >= (65536 + 62)) { + MagicString = (char*) (Buffer + 65536 + 52); + if ((CompareMem(MagicString, REISERFS_SUPER_MAGIC_STRING, 8) == 0) || + (CompareMem(MagicString, REISER2FS_SUPER_MAGIC_STRING, 9) == 0) || + (CompareMem(MagicString, REISER2FS_JR_SUPER_MAGIC_STRING, 9) == 0)) { + return FS_TYPE_REISERFS; + } // if + } // search for ReiserFS magic + + if (BufferSize >= (65536 + 64 + 8)) { + MagicString = (char*) (Buffer + 65536 + 64); + if (CompareMem(MagicString, BTRFS_SIGNATURE, 8) == 0) + return FS_TYPE_BTRFS; + } // search for Btrfs magic + + if (BufferSize >= (1024 + 2)) { + Magic16 = (UINT16*) (Buffer + 1024); + if ((*Magic16 == HFSPLUS_MAGIC1) || (*Magic16 == HFSPLUS_MAGIC2)) { + return FS_TYPE_HFSPLUS; + } + } // search for HFS+ magic + } // if (Buffer != NULL) + + return FoundType; +} // UINT32 IdentifyFilesystemType() + +static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) { EFI_STATUS Status; - UINT8 SectorBuffer[SECTOR_SIZE]; + UINT8 Buffer[SAMPLE_SIZE]; UINTN i; MBR_PARTITION_INFO *MbrTable; BOOLEAN MbrTableFound; @@ -338,88 +494,98 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl if (Volume->BlockIO == NULL) return; - if (Volume->BlockIO->Media->BlockSize > SECTOR_SIZE) + if (Volume->BlockIO->Media->BlockSize > SAMPLE_SIZE) return; // our buffer is too small... // look at the boot sector (this is used for both hard disks and El Torito images!) Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId, - Volume->BlockIOOffset, SECTOR_SIZE, SectorBuffer); + Volume->BlockIOOffset, SAMPLE_SIZE, Buffer); if (!EFI_ERROR(Status)) { - if (*((UINT16 *)(SectorBuffer + 510)) == 0xaa55 && SectorBuffer[0] != 0) { + Volume->FSType = IdentifyFilesystemType(Buffer, SAMPLE_SIZE); + if (*((UINT16 *)(Buffer + 510)) == 0xaa55 && Buffer[0] != 0) { *Bootable = TRUE; Volume->HasBootCode = TRUE; } // detect specific boot codes - if (CompareMem(SectorBuffer + 2, "LILO", 4) == 0 || - CompareMem(SectorBuffer + 6, "LILO", 4) == 0 || - CompareMem(SectorBuffer + 3, "SYSLINUX", 8) == 0 || - FindMem(SectorBuffer, SECTOR_SIZE, "ISOLINUX", 8) >= 0) { + if (CompareMem(Buffer + 2, "LILO", 4) == 0 || + CompareMem(Buffer + 6, "LILO", 4) == 0 || + CompareMem(Buffer + 3, "SYSLINUX", 8) == 0 || + FindMem(Buffer, SECTOR_SIZE, "ISOLINUX", 8) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"linux"; Volume->OSName = L"Linux"; - } else if (FindMem(SectorBuffer, 512, "Geom\0Hard Disk\0Read\0 Error", 26) >= 0) { // GRUB + } else if (FindMem(Buffer, 512, "Geom\0Hard Disk\0Read\0 Error", 26) >= 0) { // GRUB Volume->HasBootCode = TRUE; Volume->OSIconName = L"grub,linux"; Volume->OSName = L"Linux"; - } else if ((*((UINT32 *)(SectorBuffer + 502)) == 0 && - *((UINT32 *)(SectorBuffer + 506)) == 50000 && - *((UINT16 *)(SectorBuffer + 510)) == 0xaa55) || - FindMem(SectorBuffer, SECTOR_SIZE, "Starting the BTX loader", 23) >= 0) { +// // 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) || + FindMem(Buffer, SECTOR_SIZE, "Starting the BTX loader", 23) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"freebsd"; Volume->OSName = L"FreeBSD"; - } else if (FindMem(SectorBuffer, 512, "!Loading", 8) >= 0 || - FindMem(SectorBuffer, SECTOR_SIZE, "/cdboot\0/CDBOOT\0", 16) >= 0) { + } else if (FindMem(Buffer, 512, "!Loading", 8) >= 0 || + FindMem(Buffer, SECTOR_SIZE, "/cdboot\0/CDBOOT\0", 16) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"openbsd"; Volume->OSName = L"OpenBSD"; - } else if (FindMem(SectorBuffer, 512, "Not a bootxx image", 18) >= 0 || - *((UINT32 *)(SectorBuffer + 1028)) == 0x7886b6d1) { + } else if (FindMem(Buffer, 512, "Not a bootxx image", 18) >= 0 || + *((UINT32 *)(Buffer + 1028)) == 0x7886b6d1) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"netbsd"; Volume->OSName = L"NetBSD"; - } else if (FindMem(SectorBuffer, SECTOR_SIZE, "NTLDR", 5) >= 0) { + } else if (FindMem(Buffer, SECTOR_SIZE, "NTLDR", 5) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"win"; Volume->OSName = L"Windows"; - } else if (FindMem(SectorBuffer, SECTOR_SIZE, "BOOTMGR", 7) >= 0) { + } else if (FindMem(Buffer, SECTOR_SIZE, "BOOTMGR", 7) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"winvista,win"; Volume->OSName = L"Windows"; - } else if (FindMem(SectorBuffer, 512, "CPUBOOT SYS", 11) >= 0 || - FindMem(SectorBuffer, 512, "KERNEL SYS", 11) >= 0) { + } else if (FindMem(Buffer, 512, "CPUBOOT SYS", 11) >= 0 || + FindMem(Buffer, 512, "KERNEL SYS", 11) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"freedos"; Volume->OSName = L"FreeDOS"; - } else if (FindMem(SectorBuffer, 512, "OS2LDR", 6) >= 0 || - FindMem(SectorBuffer, 512, "OS2BOOT", 7) >= 0) { + } else if (FindMem(Buffer, 512, "OS2LDR", 6) >= 0 || + FindMem(Buffer, 512, "OS2BOOT", 7) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"ecomstation"; Volume->OSName = L"eComStation"; - } else if (FindMem(SectorBuffer, 512, "Be Boot Loader", 14) >= 0) { + } else if (FindMem(Buffer, 512, "Be Boot Loader", 14) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"beos"; Volume->OSName = L"BeOS"; - } else if (FindMem(SectorBuffer, 512, "yT Boot Loader", 14) >= 0) { + } else if (FindMem(Buffer, 512, "yT Boot Loader", 14) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"zeta,beos"; Volume->OSName = L"ZETA"; - } else if (FindMem(SectorBuffer, 512, "\x04" "beos\x06" "system\x05" "zbeos", 18) >= 0 || - FindMem(SectorBuffer, 512, "\x06" "system\x0c" "haiku_loader", 20) >= 0) { + } else if (FindMem(Buffer, 512, "\x04" "beos\x06" "system\x05" "zbeos", 18) >= 0 || + FindMem(Buffer, 512, "\x06" "system\x0c" "haiku_loader", 20) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"haiku,beos"; Volume->OSName = L"Haiku"; @@ -435,13 +601,22 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl Volume->OSName, Volume->OSIconName); #endif - if (FindMem(SectorBuffer, 512, "Non-system disk", 15) >= 0) // dummy FAT boot sector + // dummy FAT boot sector (created by OS X's newfs_msdos) + if (FindMem(Buffer, 512, "Non-system disk", 15) >= 0) + Volume->HasBootCode = FALSE; + + // dummy FAT boot sector (created by Linux's mkdosfs) + if (FindMem(Buffer, 512, "This is not a bootable disk", 27) >= 0) + Volume->HasBootCode = FALSE; + + // dummy FAT boot sector (created by Windows) + if (FindMem(Buffer, 512, "Press any key to restart", 24) >= 0) Volume->HasBootCode = FALSE; // check for MBR partition table - if (*((UINT16 *)(SectorBuffer + 510)) == 0xaa55) { + if (*((UINT16 *)(Buffer + 510)) == 0xaa55) { MbrTableFound = FALSE; - MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446); + MbrTable = (MBR_PARTITION_INFO *)(Buffer + 446); for (i = 0; i < 4; i++) if (MbrTable[i].StartLBA && MbrTable[i].Size) MbrTableFound = TRUE; @@ -459,9 +634,9 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl CheckError(Status, L"while reading boot sector"); #endif } -} +} /* VOID ScanVolumeBootcode() */ -// default volume icon based on disk kind +// default volume badge icon based on disk kind static VOID ScanVolumeDefaultIcon(IN OUT REFIT_VOLUME *Volume) { switch (Volume->DiskKind) { @@ -477,14 +652,98 @@ static VOID ScanVolumeDefaultIcon(IN OUT REFIT_VOLUME *Volume) } // switch() } -static VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) +// Return a string representing the input size in IEEE-1541 units. +// The calling function is responsible for freeing the allocated memory. +static CHAR16 *SizeInIEEEUnits(UINT64 SizeInBytes) { + UINT64 SizeInIeee; + UINTN Index = 0, NumPrefixes; + CHAR16 *Units, *Prefixes = L" KMGTPEZ"; + CHAR16 *TheValue; + + TheValue = AllocateZeroPool(sizeof(CHAR16) * 256); + if (TheValue != NULL) { + NumPrefixes = StrLen(Prefixes); + SizeInIeee = SizeInBytes; + while ((SizeInIeee > 1024) && (Index < (NumPrefixes - 1))) { + Index++; + SizeInIeee /= 1024; + } // while + if (Prefixes[Index] == ' ') { + Units = StrDuplicate(L"-byte"); + } else { + Units = StrDuplicate(L" iB"); + Units[1] = Prefixes[Index]; + } // if/else + SPrint(TheValue, 255, L"%ld%s", SizeInIeee, Units); + } // if + return TheValue; +} // CHAR16 *SizeInSIUnits() + +// 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 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; + 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.... + 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)); + else + SPrint(FoundName, 255, L"unknown volume"); + } // if allocated memory OK + } // if + + // 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.... + if (FoundName == NULL) { + FoundName = StrDuplicate(L"unknown volume"); + } + return FoundName; +} // static CHAR16 *GetVolumeName() + +VOID ScanVolume(REFIT_VOLUME *Volume) { EFI_STATUS Status; EFI_DEVICE_PATH *DevicePath, *NextDevicePath; EFI_DEVICE_PATH *DiskDevicePath, *RemainingDevicePath; EFI_HANDLE WholeDiskHandle; UINTN PartialLength; - EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr; BOOLEAN Bootable; // get device path @@ -546,9 +805,7 @@ static VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) // get the handle for that path RemainingDevicePath = DiskDevicePath; - //Print(L" * looking at %s\n", DevicePathToStr(RemainingDevicePath)); Status = refit_call3_wrapper(BS->LocateDevicePath, &BlockIoProtocol, &RemainingDevicePath, &WholeDiskHandle); - //Print(L" * remaining: %s\n", DevicePathToStr(RemainingDevicePath)); FreePool(DiskDevicePath); if (!EFI_ERROR(Status)) { @@ -599,28 +856,14 @@ static VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) Volume->IsReadable = TRUE; } - // get volume name - FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); - if (FileSystemInfoPtr != NULL) { - Volume->VolName = StrDuplicate(FileSystemInfoPtr->VolumeLabel); - FreePool(FileSystemInfoPtr); - } - - if (Volume->VolName == NULL) { - Volume->VolName = StrDuplicate(L"Unknown"); - } - // TODO: if no official volume name is found or it is empty, use something else, e.g.: - // - name from bytes 3 to 10 of the boot sector - // - partition number - // - name derived from file system type or partition type + Volume->VolName = GetVolumeName(Volume); // get custom volume icon if present - if (FileExists(Volume->RootDir, VOLUME_BADGE_NAME)) - Volume->VolBadgeImage = LoadIcns(Volume->RootDir, VOLUME_BADGE_NAME, 32); - if (FileExists(Volume->RootDir, VOLUME_ICON_NAME)) { - Volume->VolIconImage = LoadIcns(Volume->RootDir, VOLUME_ICON_NAME, 128); - } -} + if (!Volume->VolBadgeImage) + Volume->VolBadgeImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeBadge", 32); + if (!Volume->VolIconImage) + Volume->VolIconImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeIcon", 128); +} // ScanVolume() static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_INFO *MbrEntry) { @@ -664,7 +907,8 @@ static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_I Volume->DiskKind = WholeDiskVolume->DiskKind; Volume->IsMbrPartition = TRUE; Volume->MbrPartitionIndex = LogicalPartitionIndex++; - Volume->VolName = PoolPrint(L"Partition %d", Volume->MbrPartitionIndex + 1); + Volume->VolName = AllocateZeroPool(256 * sizeof(UINT16)); + SPrint(Volume->VolName, 255, L"Partition %d", Volume->MbrPartitionIndex + 1); Volume->BlockIO = WholeDiskVolume->BlockIO; Volume->BlockIOOffset = ExtCurrent + EMbrTable[i].StartLBA; Volume->WholeDiskBlockIO = WholeDiskVolume->BlockIO; @@ -681,30 +925,31 @@ static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_I } } } -} +} /* VOID ScanExtendedPartition() */ VOID ScanVolumes(VOID) { EFI_STATUS Status; - UINTN HandleCount = 0; - UINTN HandleIndex; EFI_HANDLE *Handles; REFIT_VOLUME *Volume, *WholeDiskVolume; - UINTN VolumeIndex, VolumeIndex2; MBR_PARTITION_INFO *MbrTable; + UINTN HandleCount = 0; + UINTN HandleIndex; + UINTN VolumeIndex, VolumeIndex2; UINTN PartitionIndex; + UINTN SectorSum, i, VolNumber = 0; UINT8 *SectorBuffer1, *SectorBuffer2; - UINTN SectorSum, i; - FreePool(Volumes); + MyFreePool(Volumes); Volumes = NULL; VolumesCount = 0; // get all filesystem handles Status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL, &HandleCount, &Handles); // was: &FileSystemProtocol - if (Status == EFI_NOT_FOUND) + if (Status == EFI_NOT_FOUND) { return; // no filesystems. strange, but true... + } if (CheckError(Status, L"while listing all file systems")) return; @@ -713,13 +958,17 @@ VOID ScanVolumes(VOID) Volume = AllocateZeroPool(sizeof(REFIT_VOLUME)); Volume->DeviceHandle = Handles[HandleIndex]; ScanVolume(Volume); + if (Volume->IsReadable) + Volume->VolNumber = VolNumber++; + else + Volume->VolNumber = VOL_UNREADABLE; AddListElement((VOID ***) &Volumes, &VolumesCount, Volume); if (Volume->DeviceHandle == SelfLoadedImage->DeviceHandle) SelfVolume = Volume; } - FreePool(Handles); + MyFreePool(Handles); if (SelfVolume == NULL) Print(L"WARNING: SelfVolume not found"); @@ -784,16 +1033,18 @@ VOID ScanVolumes(VOID) // now we're reasonably sure the association is correct... Volume->IsMbrPartition = TRUE; Volume->MbrPartitionIndex = PartitionIndex; - if (Volume->VolName == NULL) - Volume->VolName = PoolPrint(L"Partition %d", PartitionIndex + 1); + if (Volume->VolName == NULL) { + Volume->VolName = AllocateZeroPool(sizeof(CHAR16) * 256); + SPrint(Volume->VolName, 255, L"Partition %d", PartitionIndex + 1); + } break; } - FreePool(SectorBuffer1); - FreePool(SectorBuffer2); + MyFreePool(SectorBuffer1); + MyFreePool(SectorBuffer2); } - } + } // for } /* VOID ScanVolumes() */ static VOID UninitVolumes(VOID) @@ -887,8 +1138,8 @@ EFI_STATUS DirNextEntry(IN EFI_FILE *Directory, IN OUT EFI_FILE_INFO **DirEntry, // free pointer from last call if (*DirEntry != NULL) { - FreePool(*DirEntry); - *DirEntry = NULL; + FreePool(*DirEntry); + *DirEntry = NULL; } // read next directory entry @@ -906,17 +1157,19 @@ EFI_STATUS DirNextEntry(IN EFI_FILE *Directory, IN OUT EFI_FILE_INFO **DirEntry, Print(L"Reallocating buffer from %d to %d\n", LastBufferSize, BufferSize); #endif } - Buffer = ReallocatePool(Buffer, LastBufferSize, BufferSize); + Buffer = EfiReallocatePool(Buffer, LastBufferSize, BufferSize); LastBufferSize = BufferSize; } if (EFI_ERROR(Status)) { - FreePool(Buffer); + MyFreePool(Buffer); + Buffer = NULL; break; } // check for end of listing if (BufferSize == 0) { // end of directory listing - FreePool(Buffer); + MyFreePool(Buffer); + Buffer = NULL; break; } @@ -950,6 +1203,60 @@ VOID DirIterOpen(IN EFI_FILE *BaseDir, IN CHAR16 *RelativePath OPTIONAL, OUT REF DirIter->LastFileInfo = NULL; } +#ifndef __MAKEWITH_GNUEFI +EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollation = NULL; + +static EFI_STATUS +InitializeUnicodeCollationProtocol (VOID) +{ + EFI_STATUS Status; + + if (mUnicodeCollation != NULL) { + return EFI_SUCCESS; + } + + // + // BUGBUG: Proper impelmentation is to locate all Unicode Collation Protocol + // instances first and then select one which support English language. + // Current implementation just pick the first instance. + // + Status = gBS->LocateProtocol ( + &gEfiUnicodeCollation2ProtocolGuid, + NULL, + (VOID **) &mUnicodeCollation + ); + if (EFI_ERROR(Status)) { + Status = gBS->LocateProtocol ( + &gEfiUnicodeCollationProtocolGuid, + NULL, + (VOID **) &mUnicodeCollation + ); + + } + return Status; +} + +static BOOLEAN +MetaiMatch (IN CHAR16 *String, IN CHAR16 *Pattern) +{ + if (!mUnicodeCollation) { + InitializeUnicodeCollationProtocol(); + } + if (mUnicodeCollation) + return mUnicodeCollation->MetaiMatch (mUnicodeCollation, String, 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, OUT EFI_FILE_INFO **DirEntry) { @@ -958,8 +1265,8 @@ BOOLEAN DirIterNext(IN OUT REFIT_DIR_ITER *DirIter, IN UINTN FilterMode, IN CHAR CHAR16 *OnePattern; if (DirIter->LastFileInfo != NULL) { - FreePool(DirIter->LastFileInfo); - DirIter->LastFileInfo = NULL; + FreePool(DirIter->LastFileInfo); + DirIter->LastFileInfo = NULL; } if (EFI_ERROR(DirIter->LastStatus)) @@ -982,7 +1289,7 @@ BOOLEAN DirIterNext(IN OUT REFIT_DIR_ITER *DirIter, IN UINTN FilterMode, IN CHAR // else continue loop } else break; - } while (KeepGoing); + } while (KeepGoing && FilePattern); *DirEntry = DirIter->LastFileInfo; return TRUE; @@ -990,13 +1297,13 @@ BOOLEAN DirIterNext(IN OUT REFIT_DIR_ITER *DirIter, IN UINTN FilterMode, IN CHAR EFI_STATUS DirIterClose(IN OUT REFIT_DIR_ITER *DirIter) { - if (DirIter->LastFileInfo != NULL) { - FreePool(DirIter->LastFileInfo); - DirIter->LastFileInfo = NULL; - } - if (DirIter->CloseDirHandle) - refit_call1_wrapper(DirIter->DirHandle->Close, DirIter->DirHandle); - return DirIter->LastStatus; + if (DirIter->LastFileInfo != NULL) { + FreePool(DirIter->LastFileInfo); + DirIter->LastFileInfo = NULL; + } + if (DirIter->CloseDirHandle) + refit_call1_wrapper(DirIter->DirHandle->Close, DirIter->DirHandle); + return DirIter->LastStatus; } // @@ -1024,20 +1331,22 @@ CHAR16 * Basename(IN CHAR16 *Path) return FileName; } -// Replaces a filename extension of ".efi" with the specified string -// (Extension). If the input Path doesn't end in ".efi", Extension -// is added to the existing filename. -VOID ReplaceEfiExtension(IN OUT CHAR16 *Path, IN CHAR16 *Extension) -{ - UINTN PathLen; - - PathLen = StrLen(Path); - // Note: Do StriCmp() twice to work around Gigabyte Hybrid EFI case-sensitivity bug.... - if ((PathLen >= 4) && ((StriCmp(&Path[PathLen - 4], L".efi") == 0) || (StriCmp(&Path[PathLen - 4], L".EFI") == 0))) { - Path[PathLen - 4] = 0; - } // if - StrCat(Path, Extension); -} // VOID ReplaceEfiExtension() +// Remove the .efi extension from FileName -- for instance, if FileName is +// "fred.efi", returns "fred". If the filename contains no .efi extension, +// returns a copy of the original input. +CHAR16 * StripEfiExtension(CHAR16 *FileName) { + UINTN Length; + CHAR16 *Copy = NULL; + + 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))) { + Copy[Length - 4] = 0; + } // if + } // if + return Copy; +} // CHAR16 * StripExtension() // // memory string search @@ -1075,8 +1384,8 @@ BOOLEAN StriSubCmp(IN CHAR16 *SmallStr, IN CHAR16 *BigStr) { while ((!Found) && (StartPoint < NumCompares)) { Found = (StrnCmp(SmallCopy, &BigCopy[StartPoint++], SmallLen) == 0); } // while - FreePool(SmallCopy); - FreePool(BigCopy); + MyFreePool(SmallCopy); + MyFreePool(BigCopy); } // if return (Found); @@ -1084,8 +1393,8 @@ BOOLEAN StriSubCmp(IN CHAR16 *SmallStr, IN CHAR16 *BigStr) { // 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). The original input string -// *First is de-allocated and replaced by the new merged string. +// strings (unless the first string is NULL or empty). The original input +// string *First is de-allocated and replaced by the new merged string. // This is similar to StrCat, but safer and more flexible because // MergeStrings allocates memory that's the correct size for the // new merged string, so it can take a NULL *First and it cleans @@ -1101,17 +1410,21 @@ 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)) { + MyFreePool(*First); + *First = NULL; + } NewString[0] = L'\0'; if (*First != NULL) { StrCat(NewString, *First); if (AddChar) { NewString[Length1] = AddChar; - NewString[Length1 + 1] = 0; + NewString[Length1 + 1] = '\0'; } // if (AddChar) } // if (*First != NULL) if (Second != NULL) StrCat(NewString, Second); - FreePool(*First); + MyFreePool(*First); *First = NewString; } else { Print(L"Error! Unable to allocate memory in MergeStrings()!\n"); @@ -1126,7 +1439,7 @@ VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar) { CHAR16 *FindExtension(IN CHAR16 *Path) { CHAR16 *Extension; BOOLEAN Found = FALSE, FoundSlash = FALSE; - UINTN i; + INTN i; Extension = AllocateZeroPool(sizeof(CHAR16)); if (Path) { @@ -1155,6 +1468,9 @@ CHAR16 *FindLastDirName(IN CHAR16 *Path) { UINTN i, StartOfElement = 0, EndOfElement = 0, PathLength, CopyLength; CHAR16 *Found = NULL; + if (Path == NULL) + return NULL; + PathLength = StrLen(Path); // Find start & end of target element for (i = 0; i < PathLength; i++) { @@ -1181,20 +1497,84 @@ CHAR16 *FindLastDirName(IN CHAR16 *Path) { // Returns the directory portion of a pathname. For instance, // if FullPath is 'EFI\foo\bar.efi', this function returns the -// string 'EFI\foo'. +// string 'EFI\foo'. The calling function is responsible for +// freeing the returned string's memory. CHAR16 *FindPath(IN CHAR16* FullPath) { UINTN i, LastBackslash = 0; - CHAR16 *PathOnly; + CHAR16 *PathOnly = NULL; - for (i = 0; i < StrLen(FullPath); i++) { - if (FullPath[i] == '\\') - LastBackslash = i; - } // for - PathOnly = StrDuplicate(FullPath); - PathOnly[LastBackslash] = 0; + if (FullPath != NULL) { + for (i = 0; i < StrLen(FullPath); i++) { + if (FullPath[i] == '\\') + LastBackslash = i; + } // for + PathOnly = StrDuplicate(FullPath); + PathOnly[LastBackslash] = 0; + } // if return (PathOnly); } +// 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) { + CHAR16 *DeviceString, *VolumeDeviceString, *Temp; + UINTN i = 0; + BOOLEAN Found = FALSE; + + MyFreePool(*loader); + MyFreePool(*DeviceVolume); + *DeviceVolume = NULL; + DeviceString = DevicePathToStr(loadpath); + *loader = SplitDeviceString(DeviceString); + + while ((i < VolumesCount) && (!Found)) { + VolumeDeviceString = DevicePathToStr(Volumes[i]->DevicePath); + Temp = SplitDeviceString(VolumeDeviceString); + if (StriCmp(DeviceString, VolumeDeviceString) == 0) { + Found = TRUE; + *DeviceVolume = Volumes[i]; + } + MyFreePool(Temp); + MyFreePool(VolumeDeviceString); + i++; + } // while + + MyFreePool(DeviceString); +} // VOID FindVolumeAndFilename() + +// Splits a volume/filename string (e.g., "fs0:\EFI\BOOT") into separate +// volume and filename components (e.g., "fs0" and "\EFI\BOOT"), returning +// the filename component in the original *Path variable and the split-off +// volume component in the *VolName variable. +// Returns TRUE if both components are found, FALSE otherwise. +BOOLEAN SplitVolumeAndFilename(IN OUT CHAR16 **Path, OUT CHAR16 **VolName) { + UINTN i = 0, Length; + CHAR16 *Filename; + + if (*Path == NULL) + return FALSE; + + if (*VolName != NULL) { + MyFreePool(*VolName); + *VolName = NULL; + } + + Length = StrLen(*Path); + while ((i < Length) && ((*Path)[i] != L':')) { + i++; + } // while + + if (i < Length) { + Filename = StrDuplicate((*Path) + i + 1); + (*Path)[i] = 0; + *VolName = *Path; + *Path = Filename; + return TRUE; + } else { + return FALSE; + } +} // BOOLEAN SplitVolumeAndFilename() + // Returns all the digits in the input string, including intervening // non-digit characters. For instance, if InString is "foo-3.3.4-7.img", // this function returns "3.3.4-7". If InString contains no digits, @@ -1203,6 +1583,9 @@ CHAR16 *FindNumbers(IN CHAR16 *InString) { UINTN i, StartOfElement, EndOfElement = 0, InLength, CopyLength; CHAR16 *Found = NULL; + if (InString == NULL) + return NULL; + InLength = StartOfElement = StrLen(InString); // Find start & end of target element for (i = 0; i < InLength; i++) { @@ -1258,3 +1641,71 @@ CHAR16 *FindCommaDelimited(IN CHAR16 *InString, IN UINTN Index) { } // if return (FoundString); } // CHAR16 *FindCommaDelimited() + +// 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). +BOOLEAN IsIn(IN CHAR16 *SmallString, IN CHAR16 *List) { + UINTN i = 0; + BOOLEAN Found = FALSE; + CHAR16 *OneElement; + + if (SmallString && List) { + while (!Found && (OneElement = FindCommaDelimited(List, i++))) { + if (StriCmp(OneElement, SmallString) == 0) + Found = TRUE; + } // while + } // if + return Found; +} // BOOLEAN IsIn() + +// 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) { + if (Pointer != NULL) + FreePool(Pointer); +} + +static EFI_GUID AppleRemovableMediaGuid = APPLE_REMOVABLE_MEDIA_PROTOCOL_GUID; + +// Eject all removable media. +// Returns TRUE if any media were ejected, FALSE otherwise. +BOOLEAN EjectMedia(VOID) { + EFI_STATUS Status; + UINTN HandleIndex, HandleCount = 0, Ejected = 0; + EFI_HANDLE *Handles, Handle; + APPLE_REMOVABLE_MEDIA_PROTOCOL *Ejectable; + + Status = LibLocateHandle(ByProtocol, &AppleRemovableMediaGuid, NULL, &HandleCount, &Handles); + if (EFI_ERROR(Status) || HandleCount == 0) + return (FALSE); // probably not an Apple system + + for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) { + Handle = Handles[HandleIndex]; + Status = refit_call3_wrapper(BS->HandleProtocol, Handle, &AppleRemovableMediaGuid, (VOID **) &Ejectable); + if (EFI_ERROR(Status)) + continue; + Status = refit_call1_wrapper(Ejectable->Eject, Ejectable); + if (!EFI_ERROR(Status)) + Ejected++; + } + MyFreePool(Handles); + return (Ejected > 0); +} // VOID EjectMedia() + + +// Return the GUID as a string, suitable for display to the user. Note that the calling +// function is responsible for freeing the allocated memory. +CHAR16 * GuidAsString(EFI_GUID *GuidData) { + CHAR16 *TheString; + + TheString = AllocateZeroPool(42 * sizeof(CHAR16)); + if (TheString != 0) { + SPrint (TheString, 82, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + (UINTN)GuidData->Data1, (UINTN)GuidData->Data2, (UINTN)GuidData->Data3, + (UINTN)GuidData->Data4[0], (UINTN)GuidData->Data4[1], (UINTN)GuidData->Data4[2], + (UINTN)GuidData->Data4[3], (UINTN)GuidData->Data4[4], (UINTN)GuidData->Data4[5], + (UINTN)GuidData->Data4[6], (UINTN)GuidData->Data4[7]); + } + return TheString; +} // GuidAsString(EFI_GUID *GuidData)