From 0d5abdda4c44431486484d4f5d3627967e005210 Mon Sep 17 00:00:00 2001 From: srs5694 Date: Thu, 2 Mar 2017 14:00:42 -0500 Subject: [PATCH] Improve initrd auto-detection to support better matching of strings after the kernel version number string. --- Makefile | 2 +- refind/config.c | 11 +++------ refind/main.c | 57 ++++++++++++++++++++++++++++++++++------------ refind/mystrings.c | 25 ++++++++++++++++++++ refind/mystrings.h | 7 ++++++ 5 files changed, 78 insertions(+), 24 deletions(-) diff --git a/Makefile b/Makefile index d894fbc..34fe461 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ MOK_DIR=mok GPTSYNC_DIR=gptsync EFILIB_DIR=EfiLib export EDK2BASE=/usr/local/UDK2014/MyWorkSpace -export REFIND_VERSION='L"0.10.4.2"' +export REFIND_VERSION='L"0.10.4.4"' # The "all" target builds with the TianoCore library if possible, but falls # back on the more easily-installed GNU-EFI library if TianoCore isn't diff --git a/refind/config.c b/refind/config.c index b7851d4..6f33549 100644 --- a/refind/config.c +++ b/refind/config.c @@ -1139,14 +1139,9 @@ static REFIT_FILE * GenerateOptionsFromPartTypes(VOID) { // you pass this function the filename of the Linux kernel, initial RAM disk, or other // file in the target directory, and this function finds the file with a name in the // comma-delimited list of names specified by LINUX_OPTIONS_FILENAMES within that -// directory and loads it. This function tries multiple files because I originally -// used the filename linux.conf, but close on the heels of that decision, the Linux -// kernel developers decided to use that name for a similar purpose, but with a -// different file format. Thus, I'm migrating rEFInd to use the name refind_linux.conf, -// but I want a migration period in which both names are used. -// If a rEFInd options file can't be found, try to generate minimal options from -// /etc/fstab on the same volume as the kernel. This typically works only if the -// kernel is being read from the Linux root filesystem. +// directory and loads it. If a rEFInd options file can't be found, try to generate +// minimal options from /etc/fstab on the same volume as the kernel. This typically +// works only if the kernel is being read from the Linux root filesystem. // // The return value is a pointer to the REFIT_FILE handle for the file, or NULL if // it wasn't found. diff --git a/refind/main.c b/refind/main.c index 7bbd8fc..bdfa6ac 100644 --- a/refind/main.c +++ b/refind/main.c @@ -530,13 +530,16 @@ static VOID StartLoader(LOADER_ENTRY *Entry, CHAR16 *SelectionName) // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels // has a file called initramfs-3.3.0.img, this function will return the string // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file -// initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match; -// however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it -// finds.) Thus, care should be taken to avoid placing duplicate matching files in -// the kernel's directory. +// initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match. +// If more than one initrd file matches the extracted version string, the one +// that matches more characters AFTER (actually, from the start of) the version +// string is used. // If no matching init file can be found, returns NULL. static CHAR16 * FindInitrd(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) { CHAR16 *InitrdName = NULL, *FileName, *KernelVersion, *InitrdVersion, *Path; + CHAR16 *KernelPostNum, *InitrdPostNum; + UINTN MaxSharedChars, SharedChars; + STRING_LIST *InitrdNames = NULL, *FinalInitrdName = NULL, *CurrentInitrdName = NULL, *MaxSharedInitrd; REFIT_DIR_ITER DirIter; EFI_FILE_INFO *DirEntry; @@ -555,20 +558,44 @@ static CHAR16 * FindInitrd(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) { // building the InitrdName later.... if ((StrLen(Path) > 0) && (Path[StrLen(Path) - 1] != L'\\')) MergeStrings(&Path, L"\\", 0); - while ((DirIterNext(&DirIter, 2, L"init*", &DirEntry)) && (InitrdName == NULL)) { + while (DirIterNext(&DirIter, 2, L"init*", &DirEntry)) { InitrdVersion = FindNumbers(DirEntry->FileName); - if (KernelVersion != NULL) { - if (MyStriCmp(InitrdVersion, KernelVersion)) { - InitrdName = PoolPrint(L"%s%s", Path, DirEntry->FileName); - } // if + if (((KernelVersion != NULL) && (MyStriCmp(InitrdVersion, KernelVersion))) || + ((KernelVersion == NULL) && (InitrdVersion == NULL))) { + CurrentInitrdName = AllocateZeroPool(sizeof(STRING_LIST)); + if (InitrdNames == NULL) + InitrdNames = FinalInitrdName = CurrentInitrdName; + if (CurrentInitrdName) { + CurrentInitrdName->Value = PoolPrint(L"%s%s", Path, DirEntry->FileName); + if (CurrentInitrdName != FinalInitrdName) { + FinalInitrdName->Next = CurrentInitrdName; + FinalInitrdName = CurrentInitrdName; + } // if + } // if + } // if + } // while + if (InitrdNames) { + if (InitrdNames->Next == NULL) { + InitrdName = StrDuplicate(InitrdNames -> Value); } else { - if (InitrdVersion == NULL) { - InitrdName = PoolPrint(L"%s%s", Path, DirEntry->FileName); - } // if + MaxSharedInitrd = CurrentInitrdName = InitrdNames; + MaxSharedChars = 0; + while (CurrentInitrdName != NULL) { + KernelPostNum = MyStrStr(LoaderPath, KernelVersion); + InitrdPostNum = MyStrStr(CurrentInitrdName->Value, KernelVersion); + SharedChars = NumCharsInCommon(KernelPostNum, InitrdPostNum); + if (SharedChars > MaxSharedChars) { + MaxSharedChars = SharedChars; + MaxSharedInitrd = CurrentInitrdName; + } // if + // TODO: Compute number of shared characters & compare with max. + CurrentInitrdName = CurrentInitrdName->Next; + } + if (MaxSharedInitrd) + InitrdName = StrDuplicate(MaxSharedInitrd->Value); } // if/else - MyFreePool(InitrdVersion); - } // while - DirIterClose(&DirIter); + } // if + DeleteStringList(InitrdNames); // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed. MyFreePool(KernelVersion); diff --git a/refind/mystrings.c b/refind/mystrings.c index a5c5823..75e2d89 100644 --- a/refind/mystrings.c +++ b/refind/mystrings.c @@ -267,6 +267,19 @@ CHAR16 *FindNumbers(IN CHAR16 *InString) { return (Found); } // CHAR16 *FindNumbers() +// Returns the number of characters that are in common between +// String1 and String2 before they diverge. For instance, if +// String1 is "FooBar" and String2 is "FoodiesBar", this function +// will return "3", since they both start with "Foo". +UINTN NumCharsInCommon(IN CHAR16* String1, IN CHAR16* String2) { + UINTN Count = 0; + if ((String1 == NULL) || (String2 == NULL)) + return 0; + while ((String1[Count] != L'\0') && (String2[Count] != L'\0') && (String1[Count] == String2[Count])) + Count++; + return Count; +} // UINTN NumCharsInCommon() + // 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 @@ -456,3 +469,15 @@ EFI_GUID StringAsGuid(CHAR16 * InString) { return Guid; } // EFI_GUID StringAsGuid() + +// Delete the STRING_LIST pointed to by *StringList. +VOID DeleteStringList(STRING_LIST *StringList) { + STRING_LIST *Current = StringList, *Previous; + + while (Current != NULL) { + MyFreePool(Current->Value); + Previous = Current; + Current = Current->Next; + MyFreePool(Previous); + } +} // VOID DeleteStringList() diff --git a/refind/mystrings.h b/refind/mystrings.h index 9b4faec..b79a4b7 100644 --- a/refind/mystrings.h +++ b/refind/mystrings.h @@ -34,6 +34,11 @@ #endif #include "../EfiLib/GenericBdsLib.h" +typedef struct _string_list { + CHAR16 *Value; + struct _string_list *Next; +} STRING_LIST; + BOOLEAN StriSubCmp(IN CHAR16 *TargetStr, IN CHAR16 *BigStr); BOOLEAN MyStriCmp(IN CONST CHAR16 *String1, IN CONST CHAR16 *String2); CHAR16* MyStrStr (IN CHAR16 *String, IN CHAR16 *StrCharSet); @@ -42,6 +47,7 @@ VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar); VOID MergeWords(CHAR16 **MergeTo, CHAR16 *InString, CHAR16 AddChar); BOOLEAN LimitStringLength(CHAR16 *TheString, UINTN Limit); CHAR16 *FindNumbers(IN CHAR16 *InString); +UINTN NumCharsInCommon(IN CHAR16* String1, IN CHAR16* String2); CHAR16 *FindCommaDelimited(IN CHAR16 *InString, IN UINTN Index); BOOLEAN IsIn(IN CHAR16 *SmallString, IN CHAR16 *List); BOOLEAN IsInSubstring(IN CHAR16 *BigString, IN CHAR16 *List); @@ -52,5 +58,6 @@ BOOLEAN IsGuid(CHAR16 *UnknownString); CHAR16 * GuidAsString(EFI_GUID *GuidData); EFI_GUID StringAsGuid(CHAR16 * InString); +VOID DeleteStringList(STRING_LIST *StringList); #endif \ No newline at end of file -- 2.39.2