X-Git-Url: https://code.delx.au/refind/blobdiff_plain/e0f6b77e5692ec112bb803202ae27f8c5d55de50..849bd62e3cc11418d8645c067dea5ec088b3c30a:/refind/lib.c diff --git a/refind/lib.c b/refind/lib.c index 03d90d5..f38ac64 100644 --- a/refind/lib.c +++ b/refind/lib.c @@ -1,5 +1,5 @@ /* - * refit/lib.c + * refind/lib.c * General library functions * * Copyright (c) 2006-2009 Christoph Pfisterer @@ -34,12 +34,25 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Modifications copyright (c) 2012 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. - * + * License (GPL) version 3 (GPLv3), or (at your option) any later version. + * + */ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ #include "global.h" @@ -48,6 +61,9 @@ #include "screen.h" #include "../include/refit_call_wrapper.h" #include "../include/RemovableMedia.h" +#include "gpt.h" +#include "config.h" +#include "mystrings.h" #ifdef __MAKEWITH_GNUEFI #define EfiReallocatePool ReallocatePool @@ -60,10 +76,20 @@ 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" +#define XFS_SIGNATURE "XFSB" +#define NTFS_SIGNATURE "NTFS " + // variables EFI_HANDLE SelfImageHandle; @@ -75,26 +101,21 @@ 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 -// 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" - -// functions - -static EFI_STATUS FinishInitRefitLib(VOID); - -static VOID UninitVolumes(VOID); +// 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 */ // -// self recognition stuff +// Pathname manipulations // // Converts forward slashes to backslashes, removes duplicate slashes, and -// removes slashes from the end of the pathname. +// removes slashes from both the start and end of the pathname. // Necessary because some (buggy?) EFI implementations produce "\/" strings // in pathnames, because some user inputs can produce duplicate directory // separators, and because we want consistent start and end slashes for @@ -102,42 +123,95 @@ static VOID UninitVolumes(VOID); // return "/", since some firmware implementations flake out if this // isn't present. VOID CleanUpPathNameSlashes(IN OUT CHAR16 *PathName) { - CHAR16 *NewName; - UINTN i, FinalChar = 0; - BOOLEAN LastWasSlash = FALSE; - - NewName = AllocateZeroPool(sizeof(CHAR16) * (StrLen(PathName) + 4)); - if (NewName != NULL) { - for (i = 0; i < StrLen(PathName); i++) { - if ((PathName[i] == L'/') || (PathName[i] == L'\\')) { - if ((!LastWasSlash) /* && (FinalChar != 0) */) - NewName[FinalChar++] = L'\\'; - LastWasSlash = TRUE; - } else { - if (FinalChar == 0) { - NewName[FinalChar++] = L'\\'; - } - NewName[FinalChar++] = PathName[i]; - LastWasSlash = FALSE; - } // if/else - } // for - NewName[FinalChar] = 0; - if ((FinalChar > 1) && (NewName[FinalChar - 1] == L'\\')) - NewName[--FinalChar] = 0; - if (FinalChar == 0) { - NewName[0] = L'\\'; - NewName[1] = 0; - } - // Copy the transformed name back.... - StrCpy(PathName, NewName); - FreePool(NewName); - } // if allocation OK + CHAR16 *NewName; + UINTN i, Length, FinalChar = 0; + BOOLEAN LastWasSlash = FALSE; + + Length = StrLen(PathName); + NewName = AllocateZeroPool(sizeof(CHAR16) * (Length + 2)); + if (NewName != NULL) { + for (i = 0; i < Length; i++) { + if ((PathName[i] == L'/') || (PathName[i] == L'\\')) { + if ((!LastWasSlash) && (FinalChar != 0)) + NewName[FinalChar++] = L'\\'; + LastWasSlash = TRUE; + } else { + NewName[FinalChar++] = PathName[i]; + LastWasSlash = FALSE; + } // if/else + } // for + NewName[FinalChar] = 0; + if ((FinalChar > 0) && (NewName[FinalChar - 1] == L'\\')) + NewName[--FinalChar] = 0; + if (FinalChar == 0) { + NewName[0] = L'\\'; + NewName[1] = 0; + } + // Copy the transformed name back.... + StrCpy(PathName, NewName); + FreePool(NewName); + } // 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() + +// +// Library initialization and de-initialization +// + +static EFI_STATUS FinishInitRefitLib(VOID) +{ + EFI_STATUS Status; + + if (SelfRootDir == NULL) { + SelfRootDir = LibOpenRoot(SelfLoadedImage->DeviceHandle); + if (SelfRootDir == NULL) { + CheckError(EFI_LOAD_ERROR, L"while (re)opening our installation volume"); + return EFI_LOAD_ERROR; + } + } + + Status = refit_call5_wrapper(SelfRootDir->Open, SelfRootDir, &SelfDir, SelfDirPath, EFI_FILE_MODE_READ, 0); + if (CheckFatalError(Status, L"while opening our installation directory")) + return EFI_LOAD_ERROR; + + return EFI_SUCCESS; +} + 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); @@ -146,17 +220,89 @@ 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); - SelfDirPath = FindPath(DevicePathAsString); + Temp = FindPath(DevicePathAsString); + SelfDirPath = SplitDeviceString(Temp); MyFreePool(DevicePathAsString); + MyFreePool(Temp); return FinishInitRefitLib(); } +static VOID UninitVolumes(VOID) +{ + REFIT_VOLUME *Volume; + UINTN VolumeIndex; + + for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { + Volume = Volumes[VolumeIndex]; + + if (Volume->RootDir != NULL) { + refit_call1_wrapper(Volume->RootDir->Close, Volume->RootDir); + Volume->RootDir = NULL; + } + + Volume->DeviceHandle = NULL; + Volume->BlockIO = NULL; + Volume->WholeDiskBlockIO = NULL; + } +} /* VOID UninitVolumes() */ + +VOID ReinitVolumes(VOID) +{ + EFI_STATUS Status; + REFIT_VOLUME *Volume; + UINTN VolumeIndex; + EFI_DEVICE_PATH *RemainingDevicePath; + EFI_HANDLE DeviceHandle, WholeDiskHandle; + + for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { + Volume = Volumes[VolumeIndex]; + + if (Volume->DevicePath != NULL) { + // get the handle for that path + RemainingDevicePath = Volume->DevicePath; + Status = refit_call3_wrapper(BS->LocateDevicePath, &BlockIoProtocol, &RemainingDevicePath, &DeviceHandle); + + if (!EFI_ERROR(Status)) { + Volume->DeviceHandle = DeviceHandle; + + // get the root directory + Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); + + } else + CheckError(Status, L"from LocateDevicePath"); + } + + if (Volume->WholeDiskDevicePath != NULL) { + // get the handle for that path + RemainingDevicePath = Volume->WholeDiskDevicePath; + Status = refit_call3_wrapper(BS->LocateDevicePath, &BlockIoProtocol, &RemainingDevicePath, &WholeDiskHandle); + + if (!EFI_ERROR(Status)) { + // get the BlockIO protocol + Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol, + (VOID **) &Volume->WholeDiskBlockIO); + if (EFI_ERROR(Status)) { + Volume->WholeDiskBlockIO = NULL; + CheckError(Status, L"from HandleProtocol"); + } + } else + CheckError(Status, L"from LocateDevicePath"); + } + } +} /* VOID ReinitVolumes(VOID) */ + // called before running external programs to close open file handles VOID UninitRefitLib(VOID) { + // This piece of code was made to correspond to weirdness in ReinitRefitLib(). + // See the comment on it there. + if(SelfRootDir == SelfVolume->RootDir) + SelfRootDir=0; + UninitVolumes(); if (SelfDir != NULL) { @@ -168,7 +314,7 @@ VOID UninitRefitLib(VOID) refit_call1_wrapper(SelfRootDir->Close, SelfRootDir); SelfRootDir = NULL; } -} +} /* VOID UninitRefitLib() */ // called after running external programs to re-open file handles EFI_STATUS ReinitRefitLib(VOID) @@ -196,48 +342,53 @@ EFI_STATUS ReinitRefitLib(VOID) return FinishInitRefitLib(); } -static EFI_STATUS FinishInitRefitLib(VOID) -{ - EFI_STATUS Status; - - if (SelfRootDir == NULL) { - SelfRootDir = LibOpenRoot(SelfLoadedImage->DeviceHandle); - if (SelfRootDir == NULL) { - CheckError(EFI_LOAD_ERROR, L"while (re)opening our installation volume"); - return EFI_LOAD_ERROR; - } - } - - Status = refit_call5_wrapper(SelfRootDir->Open, SelfRootDir, &SelfDir, SelfDirPath, EFI_FILE_MODE_READ, 0); - if (CheckFatalError(Status, L"while opening our installation directory")) - return EFI_LOAD_ERROR; +// +// EFI variable read and write functions +// - return EFI_SUCCESS; -} +// 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() // // list functions // -VOID CreateList(OUT VOID ***ListPtr, OUT UINTN *ElementCount, IN UINTN InitialElementCount) -{ - UINTN AllocateCount; - - *ElementCount = InitialElementCount; - if (*ElementCount > 0) { - AllocateCount = (*ElementCount + 7) & ~7; // next multiple of 8 - *ListPtr = AllocatePool(sizeof(VOID *) * AllocateCount); - } else { - *ListPtr = NULL; - } -} - 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 @@ -261,92 +412,170 @@ VOID FreeList(IN OUT VOID ***ListPtr, IN OUT UINTN *ElementCount) } // VOID FreeList() // -// firmware device path discovery +// volume functions // -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; +// 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_WHOLEDISK: + retval = L" whole disk"; + break; + 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_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; + } // switch + return retval; +} // CHAR16 *FSTypeName() + +// Identify the filesystem type and record the filesystem's UUID/serial number, +// 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 >= (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 + Volume->FSType = FS_TYPE_EXT4; + } else if (*Ext2Compat & 0x0004) { // check for journal + Volume->FSType = FS_TYPE_EXT3; + } else { // none of these features; presume it's ext2... + Volume->FSType = FS_TYPE_EXT2; } - } - if (Seen) - continue; - - PathList[PathCount++] = AppendDevicePath(DevicePath, LegacyLoaderMediaPath); - } - MyFreePool(Handles); + CopyMem(&(Volume->VolUuid), Buffer + 1024 + 104, sizeof(EFI_GUID)); + return; + } + } // search for ext2/3/4 magic + + 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) || + (CompareMem(MagicString, REISER2FS_JR_SUPER_MAGIC_STRING, 9) == 0)) { + Volume->FSType = FS_TYPE_REISERFS; + CopyMem(&(Volume->VolUuid), Buffer + 65536 + 84, sizeof(EFI_GUID)); + return; + } // if + } // search for ReiserFS magic - if (HardcodedPathList) { - for (HardcodedIndex = 0; HardcodedPathList[HardcodedIndex] && PathCount < MaxPaths; HardcodedIndex++) - PathList[PathCount++] = HardcodedPathList[HardcodedIndex]; - } - PathList[PathCount] = NULL; -} + if (BufferSize >= (65536 + 64 + 8)) { + MagicString = (char*) (Buffer + 65536 + 64); + if (CompareMem(MagicString, BTRFS_SIGNATURE, 8) == 0) { + Volume->FSType = FS_TYPE_BTRFS; + return; + } // 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)) { + Volume->FSType = FS_TYPE_HFSPLUS; + return; + } + } // search for HFS+ magic + + 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 -// -// volume functions -// + // 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() -static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootable) +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; + BOOLEAN MbrTableFound = FALSE; Volume->HasBootCode = FALSE; Volume->OSIconName = NULL; @@ -355,97 +584,101 @@ 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) { + 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; } // 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"; -// // Below doesn't produce a bootable entry, so commented out for the moment.... -// // GRUB in BIOS boot partition: -// } else if (FindMem(SectorBuffer, 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 *)(SectorBuffer + 502)) == 0 && - *((UINT32 *)(SectorBuffer + 506)) == 50000 && - *((UINT16 *)(SectorBuffer + 510)) == 0xaa55) || - FindMem(SectorBuffer, SECTOR_SIZE, "Starting the BTX loader", 23) >= 0) { + } 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"; + + // 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(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) { + // Windows NT/200x/XP + } 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) { + // 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(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"; @@ -453,7 +686,7 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl } // 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", @@ -462,21 +695,20 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl #endif // dummy FAT boot sector (created by OS X's newfs_msdos) - if (FindMem(SectorBuffer, 512, "Non-system disk", 15) >= 0) + if (FindMem(Buffer, 512, "Non-system disk", 15) >= 0) Volume->HasBootCode = FALSE; // dummy FAT boot sector (created by Linux's mkdosfs) - if (FindMem(SectorBuffer, 512, "This is not a bootable disk", 27) >= 0) + if (FindMem(Buffer, 512, "This is not a bootable disk", 27) >= 0) Volume->HasBootCode = FALSE; // dummy FAT boot sector (created by Windows) - if (FindMem(SectorBuffer, 512, "Press any key to restart", 24) >= 0) + if (FindMem(Buffer, 512, "Press any key to restart", 24) >= 0) Volume->HasBootCode = FALSE; // check for MBR partition table - if (*((UINT16 *)(SectorBuffer + 510)) == 0xaa55) { - MbrTableFound = FALSE; - MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446); + if (*((UINT16 *)(Buffer + 510)) == 0xaa55) { + MbrTable = (MBR_PARTITION_INFO *)(Buffer + 446); for (i = 0; i < 4; i++) if (MbrTable[i].StartLBA && MbrTable[i].Size) MbrTableFound = TRUE; @@ -494,32 +726,171 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl CheckError(Status, L"while reading boot sector"); #endif } -} +} /* VOID ScanVolumeBootcode() */ -// default volume badge icon based on disk kind -static VOID ScanVolumeDefaultIcon(IN OUT REFIT_VOLUME *Volume) +// Set default volume badge icon based on /.VolumeBadge.{icns|png} file or disk kind +VOID SetVolumeBadgeIcon(REFIT_VOLUME *Volume) { - switch (Volume->DiskKind) { - case DISK_KIND_INTERNAL: - Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL); - break; - case DISK_KIND_EXTERNAL: - Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL); - break; - case DISK_KIND_OPTICAL: - Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL); - break; - } // switch() -} + if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_BADGES) + return; + + if (Volume->VolBadgeImage == NULL) { + Volume->VolBadgeImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeBadge", GlobalConfig.IconSizes[ICON_SIZE_BADGE]); + } + + if (Volume->VolBadgeImage == NULL) { + switch (Volume->DiskKind) { + case DISK_KIND_INTERNAL: + Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL); + break; + case DISK_KIND_EXTERNAL: + Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL); + break; + 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() + +// 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 *SizeInIEEEUnits() + +// Return a name for the volume. Ideally this should be the label for 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(REFIT_VOLUME *Volume) { + EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr = NULL; + CHAR16 *FoundName = NULL; + CHAR16 *SISize, *TypeName; + + 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", TypeName); + 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 or add disk/partition number (e.g., "(hd0,2)") -VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) + // Desperate fallback name.... + if (FoundName == NULL) { + FoundName = StrDuplicate(L"unknown 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; 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 @@ -554,6 +925,9 @@ VOID ScanVolume(IN OUT 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 || @@ -581,9 +955,7 @@ 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)) { @@ -596,7 +968,8 @@ VOID ScanVolume(IN OUT 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 @@ -614,47 +987,31 @@ VOID ScanVolume(IN OUT 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; + } - // default volume icon based on disk kind - ScanVolumeDefaultIcon(Volume); + // open the root directory of the volume + Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); - // open the root directory of the volume - Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); - if (Volume->RootDir == NULL) { - Volume->IsReadable = FALSE; - return; - } else { - Volume->IsReadable = TRUE; - } + Volume->VolName = GetVolumeName(Volume); - // get volume name - FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); - if (FileSystemInfoPtr != NULL) { - Volume->VolName = StrDuplicate(FileSystemInfoPtr->VolumeLabel); - FreePool(FileSystemInfoPtr); - } + 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 - 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 - - // 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); - } } // ScanVolume() static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_INFO *MbrEntry) @@ -693,7 +1050,6 @@ static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_I NextExtCurrent = ExtBase + EMbrTable[i].StartLBA; break; } else { - // found a logical partition Volume = AllocateZeroPool(sizeof(REFIT_VOLUME)); Volume->DiskKind = WholeDiskVolume->DiskKind; @@ -709,47 +1065,61 @@ static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_I ScanVolumeBootcode(Volume, &Bootable); if (!Bootable) Volume->HasBootCode = FALSE; - - ScanVolumeDefaultIcon(Volume); - + SetVolumeBadgeIcon(Volume); AddListElement((VOID ***) &Volumes, &VolumesCount, Volume); - - } - } - } -} + } // if/else + } // for + } // for +} /* 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; + EFI_GUID *UuidList; + EFI_GUID NullUuid = NULL_GUID_VALUE; MyFreePool(Volumes); Volumes = NULL; VolumesCount = 0; + ForgetPartitionTables(); // get all filesystem handles Status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL, &HandleCount, &Handles); - // was: &FileSystemProtocol if (Status == EFI_NOT_FOUND) { return; // no filesystems. strange, but true... } if (CheckError(Status, L"while listing all file systems")) return; + UuidList = AllocateZeroPool(sizeof(EFI_GUID) * HandleCount); // first pass: collect information about all handles 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; + for (i = 0; i < HandleIndex; i++) { + if ((CompareMem(&(Volume->VolUuid), &(UuidList[i]), sizeof(EFI_GUID)) == 0) && + (CompareMem(&(Volume->VolUuid), &NullUuid, sizeof(EFI_GUID)) != 0)) { // Duplicate filesystem UUID + Volume->IsReadable = FALSE; + } // if + } // for + } // if + if (Volume->IsReadable) + Volume->VolNumber = VolNumber++; + else + Volume->VolNumber = VOL_UNREADABLE; AddListElement((VOID ***) &Volumes, &VolumesCount, Volume); @@ -782,8 +1152,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]; + } } } @@ -831,72 +1202,25 @@ VOID ScanVolumes(VOID) MyFreePool(SectorBuffer1); MyFreePool(SectorBuffer2); } - - } + } // for } /* VOID ScanVolumes() */ -static VOID UninitVolumes(VOID) -{ - REFIT_VOLUME *Volume; - UINTN VolumeIndex; - - for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { - Volume = Volumes[VolumeIndex]; - - if (Volume->RootDir != NULL) { - refit_call1_wrapper(Volume->RootDir->Close, Volume->RootDir); - Volume->RootDir = NULL; - } - - Volume->DeviceHandle = NULL; - Volume->BlockIO = NULL; - Volume->WholeDiskBlockIO = NULL; - } -} - -VOID ReinitVolumes(VOID) -{ - EFI_STATUS Status; - REFIT_VOLUME *Volume; - UINTN VolumeIndex; - EFI_DEVICE_PATH *RemainingDevicePath; - EFI_HANDLE DeviceHandle, WholeDiskHandle; +VOID SetVolumeIcons(VOID) { + UINTN VolumeIndex; + REFIT_VOLUME *Volume; for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { Volume = Volumes[VolumeIndex]; - - if (Volume->DevicePath != NULL) { - // get the handle for that path - RemainingDevicePath = Volume->DevicePath; - Status = refit_call3_wrapper(BS->LocateDevicePath, &BlockIoProtocol, &RemainingDevicePath, &DeviceHandle); - - if (!EFI_ERROR(Status)) { - Volume->DeviceHandle = DeviceHandle; - - // get the root directory - Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); - - } else - CheckError(Status, L"from LocateDevicePath"); - } - - if (Volume->WholeDiskDevicePath != NULL) { - // get the handle for that path - RemainingDevicePath = Volume->WholeDiskDevicePath; - Status = refit_call3_wrapper(BS->LocateDevicePath, &BlockIoProtocol, &RemainingDevicePath, &WholeDiskHandle); - - if (!EFI_ERROR(Status)) { - // get the BlockIO protocol - Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol, (VOID **) &Volume->WholeDiskBlockIO); - if (EFI_ERROR(Status)) { - Volume->WholeDiskBlockIO = NULL; - CheckError(Status, L"from HandleProtocol"); - } - } else - CheckError(Status, L"from LocateDevicePath"); + // Set volume icon based on .VolumeBadge icon or disk kind + SetVolumeBadgeIcon(Volume); + if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) { + // get custom volume icons if present + if (!Volume->VolIconImage) { + Volume->VolIconImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeIcon", GlobalConfig.IconSizes[ICON_SIZE_BIG]); + } } - } -} + } // for +} // VOID SetVolumeIcons() // // file and dir functions @@ -907,10 +1231,12 @@ 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; + 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; } @@ -949,13 +1275,15 @@ EFI_STATUS DirNextEntry(IN EFI_FILE *Directory, IN OUT EFI_FILE_INFO **DirEntry, 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; } @@ -1033,14 +1361,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, @@ -1075,7 +1395,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; @@ -1117,20 +1437,21 @@ 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; +// 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(IN CHAR16 *FileName) { + UINTN Length; + CHAR16 *Copy = NULL; + + if ((FileName != NULL) && ((Copy = StrDuplicate(FileName)) != NULL)) { + Length = StrLen(Copy); + if ((Length >= 4) && MyStriCmp(&Copy[Length - 4], L".efi")) { + Copy[Length - 4] = 0; + } // if } // if - StrCat(Path, Extension); -} // VOID ReplaceEfiExtension() + return Copy; +} // CHAR16 * StripExtension() // // memory string search @@ -1151,126 +1472,69 @@ 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 - - return (Found); -} // BOOLEAN StriSubCmp() - -// 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. -// 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 -// up the old memory. It should *NOT* be used with a constant -// *First, though.... -VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar) { - UINTN Length1 = 0, Length2 = 0; - CHAR16* NewString; - - if (*First != NULL) - Length1 = StrLen(*First); - if (Second != NULL) - Length2 = StrLen(Second); - NewString = AllocatePool(sizeof(CHAR16) * (Length1 + Length2 + 2)); - if (NewString != NULL) { - NewString[0] = L'\0'; - if (*First != NULL) { - StrCat(NewString, *First); - if (AddChar) { - NewString[Length1] = AddChar; - NewString[Length1 + 1] = 0; - } // if (AddChar) - } // if (*First != NULL) - if (Second != NULL) - StrCat(NewString, Second); - MyFreePool(*First); - *First = NewString; - } else { - Print(L"Error! Unable to allocate memory in MergeStrings()!\n"); - } // if/else -} // static CHAR16* MergeStrings() - // Takes an input pathname (*Path) and returns the part of the filename from // the final dot onwards, converted to lowercase. If the filename includes // no dots, or if the input is NULL, returns an empty (but allocated) string. // The calling function is responsible for freeing the memory associated with // the return value. CHAR16 *FindExtension(IN CHAR16 *Path) { - CHAR16 *Extension; - BOOLEAN Found = FALSE, FoundSlash = FALSE; - UINTN i; - - Extension = AllocateZeroPool(sizeof(CHAR16)); - if (Path) { - i = StrLen(Path); - while ((!Found) && (!FoundSlash) && (i >= 0)) { - if (Path[i] == L'.') - Found = TRUE; - else if ((Path[i] == L'/') || (Path[i] == L'\\')) - FoundSlash = TRUE; - if (!Found) - i--; - } // while - if (Found) { - MergeStrings(&Extension, &Path[i], 0); - StrLwr(Extension); - } // if (Found) - } // if - return (Extension); -} // CHAR16 *FindExtension + CHAR16 *Extension; + BOOLEAN Found = FALSE, FoundSlash = FALSE; + INTN i; + + Extension = AllocateZeroPool(sizeof(CHAR16)); + if (Path) { + i = StrLen(Path); + while ((!Found) && (!FoundSlash) && (i >= 0)) { + if (Path[i] == L'.') + Found = TRUE; + else if ((Path[i] == L'/') || (Path[i] == L'\\')) + FoundSlash = TRUE; + if (!Found) + i--; + } // while + if (Found) { + MergeStrings(&Extension, &Path[i], 0); + ToLower(Extension); + } // if (Found) + } // if + return (Extension); +} // CHAR16 *FindExtension() // Takes an input pathname (*Path) and locates the final directory component // of that name. For instance, if the input path is 'EFI\foo\bar.efi', this // function returns the string 'foo'. // Assumes the pathname is separated with backslashes. CHAR16 *FindLastDirName(IN CHAR16 *Path) { - UINTN i, StartOfElement = 0, EndOfElement = 0, PathLength, CopyLength; - CHAR16 *Found = NULL; - - PathLength = StrLen(Path); - // Find start & end of target element - for (i = 0; i < PathLength; i++) { - if (Path[i] == '\\') { - StartOfElement = EndOfElement; - EndOfElement = i; - } // if - } // for - // Extract the target element - if (EndOfElement > 0) { - while ((StartOfElement < PathLength) && (Path[StartOfElement] == '\\')) { - StartOfElement++; - } // while - EndOfElement--; - if (EndOfElement >= StartOfElement) { - CopyLength = EndOfElement - StartOfElement + 1; - Found = StrDuplicate(&Path[StartOfElement]); - if (Found != NULL) - Found[CopyLength] = 0; - } // if (EndOfElement >= StartOfElement) - } // if (EndOfElement > 0) - return (Found); -} // CHAR16 *FindLastDirName + 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++) { + if (Path[i] == '\\') { + StartOfElement = EndOfElement; + EndOfElement = i; + } // if + } // for + // Extract the target element + if (EndOfElement > 0) { + while ((StartOfElement < PathLength) && (Path[StartOfElement] == '\\')) { + StartOfElement++; + } // while + EndOfElement--; + if (EndOfElement >= StartOfElement) { + CopyLength = EndOfElement - StartOfElement + 1; + Found = StrDuplicate(&Path[StartOfElement]); + if (Found != NULL) + Found[CopyLength] = 0; + } // if (EndOfElement >= StartOfElement) + } // if (EndOfElement > 0) + return (Found); +} // CHAR16 *FindLastDirName() // Returns the directory portion of a pathname. For instance, // if FullPath is 'EFI\foo\bar.efi', this function returns the @@ -1278,159 +1542,169 @@ CHAR16 *FindLastDirName(IN CHAR16 *Path) { // freeing the returned string's memory. CHAR16 *FindPath(IN CHAR16* FullPath) { UINTN i, LastBackslash = 0; - CHAR16 *PathOnly; - - for (i = 0; i < StrLen(FullPath); i++) { - if (FullPath[i] == '\\') - LastBackslash = i; - } // for - PathOnly = StrDuplicate(FullPath); - PathOnly[LastBackslash] = 0; + CHAR16 *PathOnly = NULL; + + if (FullPath != NULL) { + for (i = 0; i < StrLen(FullPath); i++) { + if (FullPath[i] == '\\') + LastBackslash = i; + } // for + PathOnly = StrDuplicate(FullPath); + if (PathOnly != NULL) + PathOnly[LastBackslash] = 0; + } // if return (PathOnly); } -// 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 returns a NULL value. -static CHAR16* SplitDeviceString(IN OUT CHAR16 *InString) { - INTN i; - CHAR16 *FileName = NULL; - BOOLEAN Found = FALSE; - - 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 - return FileName; -} // static CHAR16* SplitDeviceString() - // 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 + 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 (MyStriCmp(DeviceString, VolumeDeviceString)) { + Found = TRUE; + *DeviceVolume = Volumes[i]; + } + MyFreePool(Temp); + MyFreePool(VolumeDeviceString); + i++; + } // while - MyFreePool(DeviceString); + MyFreePool(DeviceString); } // VOID FindVolumeAndFilename() -// 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, -// the return value is NULL. -CHAR16 *FindNumbers(IN CHAR16 *InString) { - UINTN i, StartOfElement, EndOfElement = 0, InLength, CopyLength; - CHAR16 *Found = NULL; - - InLength = StartOfElement = StrLen(InString); - // Find start & end of target element - for (i = 0; i < InLength; i++) { - if ((InString[i] >= '0') && (InString[i] <= '9')) { - if (StartOfElement > i) - StartOfElement = i; - if (EndOfElement < i) - EndOfElement = i; - } // if - } // for - // Extract the target element - if (EndOfElement > 0) { - if (EndOfElement >= StartOfElement) { - CopyLength = EndOfElement - StartOfElement + 1; - Found = StrDuplicate(&InString[StartOfElement]); - if (Found != NULL) - Found[CopyLength] = 0; - } // if (EndOfElement >= StartOfElement) - } // if (EndOfElement > 0) - return (Found); -} // CHAR16 *FindNumbers() - -// Find the #Index element (numbered from 0) in a comma-delimited string -// of elements. -// Returns the found element, or NULL if Index is out of range or InString -// is NULL. Note that the calling function is responsible for freeing the -// memory associated with the returned string pointer. -CHAR16 *FindCommaDelimited(IN CHAR16 *InString, IN UINTN Index) { - UINTN StartPos = 0, CurPos = 0; - BOOLEAN Found = FALSE; - CHAR16 *FoundString = NULL; - - if (InString != NULL) { - // After while() loop, StartPos marks start of item #Index - while ((Index > 0) && (CurPos < StrLen(InString))) { - if (InString[CurPos] == L',') { - Index--; - StartPos = CurPos + 1; - } // if - CurPos++; - } // while - // After while() loop, CurPos is one past the end of the element - while ((CurPos < StrLen(InString)) && (!Found)) { - if (InString[CurPos] == L',') - Found = TRUE; - else - CurPos++; - } // while - if (Index == 0) - FoundString = StrDuplicate(&InString[StartPos]); - if (FoundString != NULL) - FoundString[CurPos - StartPos] = 0; - } // 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) +// 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() + +// 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 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; - } // while + 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 Found; -} // BOOLEAN IsIn() + 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 OUT VOID *Pointer) { - if (Pointer != NULL) - FreePool(Pointer); +VOID MyFreePool(IN VOID *Pointer) { + if (Pointer != NULL) + FreePool(Pointer); } static EFI_GUID AppleRemovableMediaGuid = APPLE_REMOVABLE_MEDIA_PROTOCOL_GUID; @@ -1438,41 +1712,40 @@ 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); + 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() +// 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() -// 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; +// Erase linked-list of UINT32 values.... +VOID EraseUint32List(UINT32_LIST **TheList) { + UINT32_LIST *NextItem; - 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) + while (*TheList) { + NextItem = (*TheList)->Next; + FreePool(*TheList); + *TheList = NextItem; + } // while +} // EraseUin32List()