X-Git-Url: https://code.delx.au/refind/blobdiff_plain/8a3ec8229a0dd64677196895baa4733b0c241288..2b17cbb8e38cd8fcbca11fe3751ca96f20d19788:/refind/lib.c diff --git a/refind/lib.c b/refind/lib.c index b0090c6..d19e21e 100644 --- a/refind/lib.c +++ b/refind/lib.c @@ -63,6 +63,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 +77,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 +127,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(); } @@ -135,7 +172,7 @@ EFI_STATUS ReinitRefitLib(VOID) } static EFI_STATUS FinishInitRefitLib(VOID) -{ +{ EFI_STATUS Status; if (SelfRootDir == NULL) { @@ -263,11 +300,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,17 +323,17 @@ 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, @@ -409,7 +446,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"); @@ -442,7 +479,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 +490,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 +502,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 +523,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 +568,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 +579,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 +599,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 +625,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 +639,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 +651,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 +661,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 +716,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 +752,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 +771,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 +781,11 @@ VOID ScanVolumes(VOID) Volume->VolName = PoolPrint(L"Partition %d", PartitionIndex + 1); break; } - + FreePool(SectorBuffer1); FreePool(SectorBuffer2); } - + } } /* VOID ScanVolumes() */ @@ -747,15 +793,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 +815,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 +875,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 +906,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 +925,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; } @@ -904,10 +950,10 @@ BOOLEAN DirIterNext(IN OUT REFIT_DIR_ITER *DirIter, IN UINTN FilterMode, IN CHAR FreePool(DirIter->LastFileInfo); DirIter->LastFileInfo = NULL; } - + if (EFI_ERROR(DirIter->LastStatus)) return FALSE; // stop iteration - + for (;;) { DirIter->LastStatus = DirNextEntry(DirIter->DirHandle, &(DirIter->LastFileInfo), FilterMode); if (EFI_ERROR(DirIter->LastStatus)) @@ -923,7 +969,7 @@ BOOLEAN DirIterNext(IN OUT REFIT_DIR_ITER *DirIter, IN UINTN FilterMode, IN CHAR } else break; } - + *DirEntry = DirIter->LastFileInfo; return TRUE; } @@ -949,9 +995,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,14 +1006,14 @@ CHAR16 * Basename(IN CHAR16 *Path) } } } - + return FileName; } VOID ReplaceExtension(IN OUT CHAR16 *Path, IN CHAR16 *Extension) { UINTN i; - + for (i = StrLen(Path); i >= 0; i--) { if (Path[i] == '.') { Path[i] = 0; @@ -987,14 +1033,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 +1064,7 @@ BOOLEAN StriSubCmp(IN CHAR16 *SmallStr, IN CHAR16 *BigStr) { FreePool(SmallCopy); FreePool(BigCopy); } // if - + return (Found); } // BOOLEAN StriSubCmp() @@ -1034,14 +1080,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 +1095,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; @@ -1065,7 +1111,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 +1159,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,7 +1184,7 @@ 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 +// Returns the found element, or NULL if Index is out of range or InString // is NULL. CHAR16 *FindCommaDelimited(IN CHAR16 *InString, IN UINTN Index) { UINTN StartPos = 0, CurPos = 0; @@ -1161,10 +1207,10 @@ 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()