// 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;
// 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);
VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume, IN BOOLEAN GenerateReturn) {
REFIT_MENU_SCREEN *SubScreen;
LOADER_ENTRY *SubEntry;
- CHAR16 *InitrdName;
+ CHAR16 *InitrdName, *KernelVersion = NULL;
CHAR16 DiagsFileName[256];
REFIT_FILE *File;
UINTN TokenCount;
if (File != NULL) {
InitrdName = FindInitrd(Entry->LoaderPath, Volume);
TokenCount = ReadTokenLine(File, &TokenList);
+ KernelVersion = FindNumbers(Entry->LoaderPath);
+ ReplaceSubstring(&(TokenList[1]), KERNEL_VERSION, KernelVersion);
// first entry requires special processing, since it was initially set
// up with a default title but correct options by InitializeSubScreen(),
// earlier....
} // if
FreeTokenLine(&TokenList, &TokenCount);
while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
+ ReplaceSubstring(&(TokenList[1]), KERNEL_VERSION, KernelVersion);
SubEntry = InitializeLoaderEntry(Entry);
SubEntry->me.Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux");
MyFreePool(SubEntry->LoadOptions);
if (GenerateReturn)
AddMenuEntry(SubScreen, &MenuEntryReturn);
Entry->me.SubScreen = SubScreen;
+ MyFreePool(KernelVersion);
} // VOID GenerateSubScreen()
// Returns options for a Linux kernel. Reads them from an options file in the
// kernel's directory; and if present, adds an initrd= option for an initial
// RAM disk file with the same version number as the kernel file.
static CHAR16 * GetMainLinuxOptions(IN CHAR16 * LoaderPath, IN REFIT_VOLUME *Volume) {
- CHAR16 *Options = NULL, *InitrdName, *FullOptions = NULL;
+ CHAR16 *Options = NULL, *InitrdName, *FullOptions = NULL, *KernelVersion;
Options = GetFirstOptionsFromFile(LoaderPath, Volume);
InitrdName = FindInitrd(LoaderPath, Volume);
+ KernelVersion = FindNumbers(InitrdName);
+ ReplaceSubstring(&Options, KERNEL_VERSION, KernelVersion);
FullOptions = AddInitrdToOptions(Options, InitrdName);
MyFreePool(Options);
MyFreePool(InitrdName);
+ MyFreePool(KernelVersion);
return (FullOptions);
} // static CHAR16 * GetMainLinuxOptions()
// Add a Linux kernel as a submenu entry for another (pre-existing) Linux kernel entry.
static VOID AddKernelToSubmenu(LOADER_ENTRY * TargetLoader, CHAR16 *FileName, REFIT_VOLUME *Volume) {
REFIT_FILE *File;
- CHAR16 **TokenList = NULL, *InitrdName, *SubmenuName = NULL, *VolName = NULL, *Path = NULL, *Title;
+ CHAR16 **TokenList = NULL, *InitrdName, *SubmenuName = NULL, *VolName = NULL;
+ CHAR16 *Path = NULL, *Title, *KernelVersion;
REFIT_MENU_SCREEN *SubScreen;
LOADER_ENTRY *SubEntry;
UINTN TokenCount;
+ KernelVersion = FindNumbers(FileName);
File = ReadLinuxOptionsFile(TargetLoader->LoaderPath, Volume);
if (File != NULL) {
SubScreen = TargetLoader->me.SubScreen;
InitrdName = FindInitrd(FileName, Volume);
while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
+ ReplaceSubstring(&(TokenList[1]), KERNEL_VERSION, KernelVersion);
SubEntry = InitializeLoaderEntry(TargetLoader);
SplitPathName(FileName, &VolName, &Path, &SubmenuName);
MergeStrings(&SubmenuName, L": ", '\0');
MyFreePool(InitrdName);
MyFreePool(File);
} // if
+ MyFreePool(KernelVersion);
} // static VOID AddKernelToSubmenu()
// Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
return FALSE;
}
- if (FallbackSize != FileSize) { // not same size, so can't be identical
- AreIdentical = FALSE;
- } else { // could be identical; do full check....
+ if (FallbackSize == FileSize) { // could be identical; do full check....
FileContents = AllocatePool(FileSize);
FallbackContents = AllocatePool(FallbackSize);
if (FileContents && FallbackContents) {
// file to fail to open, which would return a false positive -- but as I use
// this function to exclude symbolic links from the list of boot loaders,
// that would be fine, since such boot loaders wouldn't work.)
-static BOOLEAN IsSymbolicLink(REFIT_VOLUME *Volume, CHAR16 *Path, EFI_FILE_INFO *DirEntry) {
+// CAUTION: *FullName MUST be properly cleaned up (via CleanUpPathNameSlashes())
+static BOOLEAN IsSymbolicLink(REFIT_VOLUME *Volume, CHAR16 *FullName, EFI_FILE_INFO *DirEntry) {
EFI_FILE_HANDLE FileHandle;
EFI_FILE_INFO *FileInfo = NULL;
EFI_STATUS Status;
UINTN FileSize2 = 0;
- CHAR16 *FileName;
-
- FileName = StrDuplicate(Path);
- MergeStrings(&FileName, DirEntry->FileName, L'\\');
- CleanUpPathNameSlashes(FileName);
- Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
+ Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FullName, EFI_FILE_MODE_READ, 0);
if (Status == EFI_SUCCESS) {
FileInfo = LibFileInfo(FileHandle);
if (FileInfo != NULL)
FileSize2 = FileInfo->FileSize;
}
- MyFreePool(FileName);
MyFreePool(FileInfo);
return (DirEntry->FileSize != FileSize2);
// there's no point in cluttering the display with two kernels that will
// behave identically on non-SB systems, or when one will fail when SB
// is active.
-static BOOLEAN HasSignedCounterpart(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Filename) {
+// CAUTION: *FullName MUST be properly cleaned up (via CleanUpPathNameSlashes())
+static BOOLEAN HasSignedCounterpart(IN REFIT_VOLUME *Volume, IN CHAR16 *FullName) {
CHAR16 *NewFile = NULL;
BOOLEAN retval = FALSE;
- MergeStrings(&NewFile, Path, 0);
- MergeStrings(&NewFile, Filename, L'\\');
+ MergeStrings(&NewFile, FullName, 0);
MergeStrings(&NewFile, L".efi.signed", 0);
if (NewFile != NULL) {
- CleanUpPathNameSlashes(NewFile);
if (FileExists(Volume->RootDir, NewFile))
retval = TRUE;
MyFreePool(NewFile);
EFI_STATUS Status;
REFIT_DIR_ITER DirIter;
EFI_FILE_INFO *DirEntry;
- CHAR16 FileName[256], *Extension;
+ CHAR16 Message[256], *Extension, *FullName;
struct LOADER_LIST *LoaderList = NULL, *NewLoader;
LOADER_ENTRY *FirstKernel = NULL, *LatestEntry = NULL;
BOOLEAN FoundFallbackDuplicate = FALSE, IsLinux = FALSE, InSelfPath;
DirIterOpen(Volume->RootDir, Path, &DirIter);
while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) {
Extension = FindExtension(DirEntry->FileName);
+ FullName = StrDuplicate(Path);
+ MergeStrings(&FullName, DirEntry->FileName, L'\\');
+ CleanUpPathNameSlashes(FullName);
if (DirEntry->FileName[0] == '.' ||
MyStriCmp(Extension, L".icns") ||
MyStriCmp(Extension, L".png") ||
(MyStriCmp(DirEntry->FileName, FALLBACK_BASENAME) && (MyStriCmp(Path, L"EFI\\BOOT"))) ||
FilenameIn(Volume, Path, DirEntry->FileName, SHELL_NAMES) ||
- IsSymbolicLink(Volume, Path, DirEntry) || /* is symbolic link */
- HasSignedCounterpart(Volume, Path, DirEntry->FileName) || /* a file with same name plus ".efi.signed" is present */
- FilenameIn(Volume, Path, DirEntry->FileName, GlobalConfig.DontScanFiles)) {
+ IsSymbolicLink(Volume, FullName, DirEntry) || /* is symbolic link */
+ HasSignedCounterpart(Volume, FullName) || /* a file with same name plus ".efi.signed" is present */
+ FilenameIn(Volume, Path, DirEntry->FileName, GlobalConfig.DontScanFiles) ||
+ !IsValidLoader(Volume->RootDir, FullName)) {
continue; // skip this
}
- if (Path)
- SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
- else
- SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
- CleanUpPathNameSlashes(FileName);
-
- if(!IsValidLoader(Volume->RootDir, FileName))
- continue;
-
NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
if (NewLoader != NULL) {
- NewLoader->FileName = StrDuplicate(FileName);
+ NewLoader->FileName = StrDuplicate(FullName);
NewLoader->TimeStamp = DirEntry->ModificationTime;
LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
- if (DuplicatesFallback(Volume, FileName))
+ if (DuplicatesFallback(Volume, FullName))
FoundFallbackDuplicate = TRUE;
} // if
MyFreePool(Extension);
+ MyFreePool(FullName);
} // while
NewLoader = LoaderList;
// it down to buggy EFI implementations and ignoring that particular error....
if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) {
if (Path)
- SPrint(FileName, 255, L"while scanning the %s directory", Path);
+ SPrint(Message, 255, L"while scanning the %s directory", Path);
else
- StrCpy(FileName, L"while scanning the root directory");
- CheckError(Status, FileName);
+ StrCpy(Message, L"while scanning the root directory");
+ CheckError(Status, Message);
} // if (Status != EFI_NOT_FOUND)
} // if not scanning a blacklisted directory