X-Git-Url: https://code.delx.au/refind/blobdiff_plain/8a3ec8229a0dd64677196895baa4733b0c241288..953397f78420efbd81d4ab60a2c01da0d2c19711:/refind/lib.c diff --git a/refind/lib.c b/refind/lib.c index b0090c6..be95e0c 100644 --- a/refind/lib.c +++ b/refind/lib.c @@ -47,6 +47,7 @@ #include "icns.h" #include "screen.h" #include "refit_call_wrapper.h" +#include "RemovableMedia.h" // variables @@ -63,6 +64,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" + // functions static EFI_STATUS FinishInitRefitLib(VOID); @@ -73,13 +78,49 @@ static VOID UninitVolumes(VOID); // self recognition stuff // +// Converts forward slashes to backslashes, removes duplicate slashes, and +// 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 +// directory comparisons. A special case: If the PathName refers to root, +// 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) + 2)); + 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 { + 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() + EFI_STATUS InitRefitLib(IN EFI_HANDLE ImageHandle) { EFI_STATUS Status; CHAR16 *DevicePathAsString; - CHAR16 BaseDirectory[256]; - UINTN i; - + SelfImageHandle = ImageHandle; Status = refit_call3_wrapper(BS->HandleProtocol, SelfImageHandle, &LoadedImageProtocol, (VOID **) &SelfLoadedImage); if (CheckFatalError(Status, L"while getting a LoadedImageProtocol handle")) @@ -87,15 +128,12 @@ EFI_STATUS InitRefitLib(IN EFI_HANDLE ImageHandle) // find the current directory DevicePathAsString = DevicePathToStr(SelfLoadedImage->FilePath); - if (DevicePathAsString != NULL) { - StrCpy(BaseDirectory, DevicePathAsString); - FreePool(DevicePathAsString); - for (i = StrLen(BaseDirectory); i > 0 && BaseDirectory[i] != '\\'; i--) ; - BaseDirectory[i] = 0; - } else - BaseDirectory[0] = 0; - SelfDirPath = StrDuplicate(BaseDirectory); - + CleanUpPathNameSlashes(DevicePathAsString); + if (SelfDirPath != NULL) + FreePool(SelfDirPath); + SelfDirPath = FindPath(DevicePathAsString); + FreePool(DevicePathAsString); + return FinishInitRefitLib(); } @@ -120,22 +158,29 @@ EFI_STATUS ReinitRefitLib(VOID) { ReinitVolumes(); - // Below two lines were in rEFIt, but seem to cause problems on - // most systems. OTOH, my Mac Mini produces (apparently harmless) - // errors about "(re)opening our installation volume" (see the - // next function) when returning from programs when these two lines - // are removed. On the gripping hand, the Mac SOMETIMES crashes - // when launching a second program even with these lines removed. - // TODO: Figure out cause of above weirdness and fix it more - // reliably! - /* if (SelfVolume != NULL && SelfVolume->RootDir != NULL) - SelfRootDir = SelfVolume->RootDir; */ + if ((ST->Hdr.Revision >> 16) == 1) { + // Below two lines were in rEFIt, but seem to cause system crashes or + // reboots when launching OSes after returning from programs on most + // systems. OTOH, my Mac Mini produces errors about "(re)opening our + // installation volume" (see the next function) when returning from + // programs when these two lines are removed, and it often crashes + // when returning from a program or when launching a second program + // with these lines removed. Therefore, the preceding if() statement + // executes these lines only on EFIs with a major version number of 1 + // (which Macs have) and not with 2 (which UEFI PCs have). My selection + // of hardware on which to test is limited, though, so this may be the + // wrong test, or there may be a better way to fix this problem. + // TODO: Figure out cause of above weirdness and fix it more + // reliably! + if (SelfVolume != NULL && SelfVolume->RootDir != NULL) + SelfRootDir = SelfVolume->RootDir; + } // if return FinishInitRefitLib(); } static EFI_STATUS FinishInitRefitLib(VOID) -{ +{ EFI_STATUS Status; if (SelfRootDir == NULL) { @@ -160,7 +205,7 @@ static EFI_STATUS FinishInitRefitLib(VOID) 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 @@ -188,7 +233,7 @@ VOID AddListElement(IN OUT VOID ***ListPtr, IN OUT UINTN *ElementCount, IN VOID VOID FreeList(IN OUT VOID ***ListPtr, IN OUT UINTN *ElementCount) { UINTN i; - + if (*ElementCount > 0) { for (i = 0; i < *ElementCount; i++) { // TODO: call a user-provided routine for each element here @@ -221,9 +266,9 @@ VOID ExtractLegacyLoaderPaths(EFI_DEVICE_PATH **PathList, UINTN MaxPaths, EFI_DE 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); @@ -237,19 +282,19 @@ VOID ExtractLegacyLoaderPaths(EFI_DEVICE_PATH **PathList, UINTN MaxPaths, EFI_DE } 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; @@ -263,11 +308,11 @@ VOID ExtractLegacyLoaderPaths(EFI_DEVICE_PATH **PathList, UINTN MaxPaths, EFI_DE } if (Seen) continue; - + PathList[PathCount++] = AppendDevicePath(DevicePath, LegacyLoaderMediaPath); } FreePool(Handles); - + if (HardcodedPathList) { for (HardcodedIndex = 0; HardcodedPathList[HardcodedIndex] && PathCount < MaxPaths; HardcodedIndex++) PathList[PathCount++] = HardcodedPathList[HardcodedIndex]; @@ -286,28 +331,28 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl UINTN i; MBR_PARTITION_INFO *MbrTable; BOOLEAN MbrTableFound; - + Volume->HasBootCode = FALSE; Volume->OSIconName = NULL; Volume->OSName = NULL; *Bootable = FALSE; - + if (Volume->BlockIO == NULL) return; if (Volume->BlockIO->Media->BlockSize > SECTOR_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); if (!EFI_ERROR(Status)) { - + if (*((UINT16 *)(SectorBuffer + 510)) == 0xaa55 && SectorBuffer[0] != 0) { *Bootable = TRUE; Volume->HasBootCode = TRUE; } - + // detect specific boot codes if (CompareMem(SectorBuffer + 2, "LILO", 4) == 0 || CompareMem(SectorBuffer + 6, "LILO", 4) == 0 || @@ -316,12 +361,21 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl 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 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) || @@ -329,51 +383,51 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl 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) { 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) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"netbsd"; Volume->OSName = L"NetBSD"; - + } else if (FindMem(SectorBuffer, 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) { 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) { 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) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"ecomstation"; Volume->OSName = L"eComStation"; - + } else if (FindMem(SectorBuffer, 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) { 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) { Volume->HasBootCode = TRUE; @@ -381,19 +435,28 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl Volume->OSName = L"Haiku"; } - + // NOTE: If you add an operating system with a name that starts with 'W' or 'L', you // need to fix AddLegacyEntry in main.c. - + #if REFIT_DEBUG > 0 Print(L" Result of bootcode detection: %s %s (%s)\n", Volume->HasBootCode ? L"bootable" : L"non-bootable", 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(SectorBuffer, 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) + Volume->HasBootCode = FALSE; + + // dummy FAT boot sector (created by Windows) + if (FindMem(SectorBuffer, 512, "Press any key to restart", 24) >= 0) Volume->HasBootCode = FALSE; - + // check for MBR partition table if (*((UINT16 *)(SectorBuffer + 510)) == 0xaa55) { MbrTableFound = FALSE; @@ -409,7 +472,7 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl CopyMem(Volume->MbrPartitionTable, MbrTable, 4 * 16); } } - + } else { #if REFIT_DEBUG > 0 CheckError(Status, L"while reading boot sector"); @@ -417,7 +480,7 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl } } -// 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) { @@ -442,7 +505,7 @@ static VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) UINTN PartialLength; EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr; BOOLEAN Bootable; - + // get device path Volume->DevicePath = DuplicateDevicePath(DevicePathFromHandle(Volume->DeviceHandle)); #if REFIT_DEBUG > 0 @@ -453,9 +516,9 @@ static VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) #endif } #endif - + Volume->DiskKind = DISK_KIND_INTERNAL; // default - + // get block i/o Status = refit_call3_wrapper(BS->HandleProtocol, Volume->DeviceHandle, &BlockIoProtocol, (VOID **) &(Volume->BlockIO)); if (EFI_ERROR(Status)) { @@ -465,16 +528,16 @@ static VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) if (Volume->BlockIO->Media->BlockSize == 2048) Volume->DiskKind = DISK_KIND_OPTICAL; } - + // scan for bootcode and MBR table Bootable = FALSE; ScanVolumeBootcode(Volume, &Bootable); - + // detect device type DevicePath = Volume->DevicePath; while (DevicePath != NULL && !IsDevicePathEndType(DevicePath)) { NextDevicePath = NextDevicePathNode(DevicePath); - + if (DevicePathType(DevicePath) == MESSAGING_DEVICE_PATH && (DevicePathSubType(DevicePath) == MSG_USB_DP || DevicePathSubType(DevicePath) == MSG_USB_CLASS_DP || @@ -486,44 +549,44 @@ static VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) Volume->DiskKind = DISK_KIND_OPTICAL; // El Torito entry -> optical disk Bootable = TRUE; } - + if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH && DevicePathSubType(DevicePath) == MEDIA_VENDOR_DP) { Volume->IsAppleLegacy = TRUE; // legacy BIOS device entry // TODO: also check for Boot Camp GUID Bootable = FALSE; // this handle's BlockIO is just an alias for the whole device } - + if (DevicePathType(DevicePath) == MESSAGING_DEVICE_PATH) { // make a device path for the whole device PartialLength = (UINT8 *)NextDevicePath - (UINT8 *)(Volume->DevicePath); DiskDevicePath = (EFI_DEVICE_PATH *)AllocatePool(PartialLength + sizeof(EFI_DEVICE_PATH)); CopyMem(DiskDevicePath, Volume->DevicePath, PartialLength); CopyMem((UINT8 *)DiskDevicePath + PartialLength, EndDevicePath, sizeof(EFI_DEVICE_PATH)); - + // 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)) { //Print(L" - original handle: %08x - disk handle: %08x\n", (UINT32)DeviceHandle, (UINT32)WholeDiskHandle); - + // get the device path for later Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &DevicePathProtocol, (VOID **) &DiskDevicePath); if (!EFI_ERROR(Status)) { Volume->WholeDiskDevicePath = DuplicateDevicePath(DiskDevicePath); } - + // look at the BlockIO protocol Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol, (VOID **) &Volume->WholeDiskBlockIO); if (!EFI_ERROR(Status)) { - + // check the media block size if (Volume->WholeDiskBlockIO->Media->BlockSize == 2048) Volume->DiskKind = DISK_KIND_OPTICAL; - + } else { Volume->WholeDiskBlockIO = NULL; //CheckError(Status, L"from HandleProtocol"); @@ -531,10 +594,10 @@ static VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) } //else // CheckError(Status, L"from LocateDevicePath"); } - + DevicePath = NextDevicePath; } // while - + if (!Bootable) { #if REFIT_DEBUG > 0 if (Volume->HasBootCode) @@ -542,16 +605,19 @@ static VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) #endif Volume->HasBootCode = FALSE; } - + // default volume icon based on disk kind ScanVolumeDefaultIcon(Volume); - + // open the root directory of the volume Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); if (Volume->RootDir == NULL) { + Volume->IsReadable = FALSE; return; + } else { + Volume->IsReadable = TRUE; } - + // get volume name FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); if (FileSystemInfoPtr != NULL) { @@ -559,14 +625,20 @@ static VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) 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 // get custom volume icon if present - if (FileExists(Volume->RootDir, L".VolumeIcon.icns")) - Volume->VolBadgeImage = LoadIcns(Volume->RootDir, L".VolumeIcon.icns", 32); + 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); + } } static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_INFO *MbrEntry) @@ -579,9 +651,9 @@ static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_I UINT8 SectorBuffer[512]; BOOLEAN Bootable; MBR_PARTITION_INFO *EMbrTable; - + ExtBase = MbrEntry->StartLBA; - + for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) { // read current EMBR Status = refit_call5_wrapper(WholeDiskVolume->BlockIO->ReadBlocks, @@ -593,7 +665,7 @@ static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_I if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55) break; EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446); - + // scan logical partitions in this EMBR NextExtCurrent = 0; for (i = 0; i < 4; i++) { @@ -605,7 +677,7 @@ 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; @@ -615,16 +687,16 @@ static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_I Volume->BlockIO = WholeDiskVolume->BlockIO; Volume->BlockIOOffset = ExtCurrent + EMbrTable[i].StartLBA; Volume->WholeDiskBlockIO = WholeDiskVolume->BlockIO; - + Bootable = FALSE; ScanVolumeBootcode(Volume, &Bootable); if (!Bootable) Volume->HasBootCode = FALSE; - + ScanVolumeDefaultIcon(Volume); - + AddListElement((VOID ***) &Volumes, &VolumesCount, Volume); - + } } } @@ -670,7 +742,7 @@ VOID ScanVolumes(VOID) if (SelfVolume == NULL) Print(L"WARNING: SelfVolume not found"); - + // second pass: relate partitions and whole disk devices for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { Volume = Volumes[VolumeIndex]; @@ -706,7 +778,7 @@ VOID ScanVolumes(VOID) // check size if ((UINT64)(MbrTable[PartitionIndex].Size) != Volume->BlockIO->Media->LastBlock + 1) continue; - + // compare boot sector read through offset vs. directly Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId, @@ -725,9 +797,9 @@ VOID ScanVolumes(VOID) SectorSum += SectorBuffer1[i]; if (SectorSum < 1000) continue; - + // TODO: mark entry as non-bootable if it is an extended partition - + // now we're reasonably sure the association is correct... Volume->IsMbrPartition = TRUE; Volume->MbrPartitionIndex = PartitionIndex; @@ -735,11 +807,11 @@ VOID ScanVolumes(VOID) Volume->VolName = PoolPrint(L"Partition %d", PartitionIndex + 1); break; } - + FreePool(SectorBuffer1); FreePool(SectorBuffer2); } - + } } /* VOID ScanVolumes() */ @@ -747,15 +819,15 @@ 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; @@ -769,15 +841,15 @@ VOID ReinitVolumes(VOID) 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; @@ -829,15 +901,15 @@ EFI_STATUS DirNextEntry(IN EFI_FILE *Directory, IN OUT EFI_FILE_INFO **DirEntry, VOID *Buffer; UINTN LastBufferSize, BufferSize; INTN IterCount; - + for (;;) { - + // free pointer from last call if (*DirEntry != NULL) { FreePool(*DirEntry); *DirEntry = NULL; } - + // read next directory entry LastBufferSize = BufferSize = 256; Buffer = AllocatePool(BufferSize); @@ -860,16 +932,16 @@ EFI_STATUS DirNextEntry(IN EFI_FILE *Directory, IN OUT EFI_FILE_INFO **DirEntry, FreePool(Buffer); break; } - + // check for end of listing if (BufferSize == 0) { // end of directory listing FreePool(Buffer); break; } - + // entry is ready to be returned *DirEntry = (EFI_FILE_INFO *)Buffer; - + // filter results if (FilterMode == 1) { // only return directories if (((*DirEntry)->Attribute & EFI_FILE_DIRECTORY)) @@ -879,7 +951,7 @@ EFI_STATUS DirNextEntry(IN EFI_FILE *Directory, IN OUT EFI_FILE_INFO **DirEntry, break; } else // no filter or unknown filter -> return everything break; - + } return Status; } @@ -900,30 +972,37 @@ VOID DirIterOpen(IN EFI_FILE *BaseDir, IN CHAR16 *RelativePath OPTIONAL, OUT REF BOOLEAN DirIterNext(IN OUT REFIT_DIR_ITER *DirIter, IN UINTN FilterMode, IN CHAR16 *FilePattern OPTIONAL, OUT EFI_FILE_INFO **DirEntry) { + BOOLEAN KeepGoing = TRUE; + UINTN i; + CHAR16 *OnePattern; + if (DirIter->LastFileInfo != NULL) { FreePool(DirIter->LastFileInfo); DirIter->LastFileInfo = NULL; } - + if (EFI_ERROR(DirIter->LastStatus)) return FALSE; // stop iteration - - for (;;) { + + do { DirIter->LastStatus = DirNextEntry(DirIter->DirHandle, &(DirIter->LastFileInfo), FilterMode); if (EFI_ERROR(DirIter->LastStatus)) - return FALSE; + return FALSE; if (DirIter->LastFileInfo == NULL) // end of listing return FALSE; if (FilePattern != NULL) { if ((DirIter->LastFileInfo->Attribute & EFI_FILE_DIRECTORY)) - break; - if (MetaiMatch(DirIter->LastFileInfo->FileName, FilePattern)) - break; + KeepGoing = FALSE; + i = 0; + while (KeepGoing && (OnePattern = FindCommaDelimited(FilePattern, i++)) != NULL) { + if (MetaiMatch(DirIter->LastFileInfo->FileName, OnePattern)) + KeepGoing = FALSE; + } // while // else continue loop } else break; - } - + } while (KeepGoing); + *DirEntry = DirIter->LastFileInfo; return TRUE; } @@ -949,9 +1028,9 @@ CHAR16 * Basename(IN CHAR16 *Path) { CHAR16 *FileName; UINTN i; - + FileName = Path; - + if (Path != NULL) { for (i = StrLen(Path); i > 0; i--) { if (Path[i-1] == '\\' || Path[i-1] == '/') { @@ -960,24 +1039,24 @@ CHAR16 * Basename(IN CHAR16 *Path) } } } - + return FileName; } -VOID ReplaceExtension(IN OUT CHAR16 *Path, IN CHAR16 *Extension) +// 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 i; - - for (i = StrLen(Path); i >= 0; i--) { - if (Path[i] == '.') { - Path[i] = 0; - break; - } - if (Path[i] == '\\' || Path[i] == '/') - break; - } + 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() // // memory string search @@ -987,14 +1066,14 @@ INTN FindMem(IN VOID *Buffer, IN UINTN BufferLength, IN VOID *SearchString, IN U { UINT8 *BufferPtr; UINTN Offset; - + BufferPtr = Buffer; BufferLength -= SearchStringLength; for (Offset = 0; Offset < BufferLength; Offset++, BufferPtr++) { if (CompareMem(BufferPtr, SearchString, SearchStringLength) == 0) return (INTN)Offset; } - + return -1; } @@ -1018,7 +1097,7 @@ BOOLEAN StriSubCmp(IN CHAR16 *SmallStr, IN CHAR16 *BigStr) { FreePool(SmallCopy); FreePool(BigCopy); } // if - + return (Found); } // BOOLEAN StriSubCmp() @@ -1034,14 +1113,14 @@ BOOLEAN StriSubCmp(IN CHAR16 *SmallStr, IN CHAR16 *BigStr) { 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)); - NewString[0] = 0; if (NewString != NULL) { + NewString[0] = L'\0'; if (*First != NULL) { StrCat(NewString, *First); if (AddChar) { @@ -1049,7 +1128,7 @@ VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar) { NewString[Length1 + 1] = 0; } // if (AddChar) } // if (*First != NULL) - if (First != NULL) + if (Second != NULL) StrCat(NewString, Second); FreePool(*First); *First = NewString; @@ -1058,6 +1137,35 @@ VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar) { } // 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 + // 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'. @@ -1065,7 +1173,7 @@ VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar) { 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++) { @@ -1113,7 +1221,7 @@ CHAR16 *FindPath(IN CHAR16* FullPath) { 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++) { @@ -1138,8 +1246,9 @@ CHAR16 *FindNumbers(IN CHAR16 *InString) { // 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 of InString -// is NULL. +// 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; @@ -1161,10 +1270,38 @@ CHAR16 *FindCommaDelimited(IN CHAR16 *InString, IN UINTN Index) { else CurPos++; } // while + if (Index == 0) + FoundString = StrDuplicate(&InString[StartPos]); + if (FoundString != NULL) + FoundString[CurPos - StartPos] = 0; } // if - if (Index == 0) - FoundString = StrDuplicate(&InString[StartPos]); - if (FoundString != NULL) - FoundString[CurPos - StartPos] = 0; return (FoundString); } // CHAR16 *FindCommaDelimited() + + +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++; + } + FreePool(Handles); + return (Ejected > 0); +} // VOID EjectMedia()