]> code.delx.au - refind/blobdiff - refind/main.c
Improve initrd auto-detection to support better matching of strings
[refind] / refind / main.c
index 69e80fcec41472f09a6f58c6cd3d5bc4441513b4..bdfa6acaa0f1c69b1ba6646a7c866cf87bbdca3a 100644 (file)
@@ -34,7 +34,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*
- * Modifications copyright (c) 2012-2015 Roderick W. Smith
+ * Modifications copyright (c) 2012-2017 Roderick W. Smith
  *
  * Modifications distributed under the terms of the GNU General Public
  * License (GPL) version 3 (GPLv3), or (at your option) any later version.
@@ -157,16 +157,16 @@ static REFIT_MENU_ENTRY MenuEntryRotateCsr = { L"Change SIP Policy", TAG_CSR_ROT
 
 REFIT_MENU_SCREEN MainMenu       = { L"Main Menu", NULL, 0, NULL, 0, NULL, 0, L"Automatic boot",
                                      L"Use arrow keys to move cursor; Enter to boot;",
-                                     L"Insert or F2 for more options; Esc to refresh" };
+                                     L"Insert, Tab, or F2 for more options; Esc or Backspace to refresh" };
 static REFIT_MENU_SCREEN AboutMenu      = { L"About", NULL, 0, NULL, 0, NULL, 0, NULL, L"Press Enter to return to main menu", L"" };
 
-REFIT_CONFIG GlobalConfig = { FALSE, TRUE, FALSE, FALSE, TRUE, 0, 0, 0, DONT_CHANGE_TEXT_MODE,
+REFIT_CONFIG GlobalConfig = { FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, 0, 0, 0, DONT_CHANGE_TEXT_MODE,
                               20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC,
                               0, 0, { DEFAULT_BIG_ICON_SIZE / 4, DEFAULT_SMALL_ICON_SIZE, DEFAULT_BIG_ICON_SIZE },
                               BANNER_NOSCALE, NULL, NULL, NULL, NULL, CONFIG_FILE_NAME, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               { TAG_SHELL, TAG_MEMTEST, TAG_GDISK, TAG_APPLE_RECOVERY, TAG_WINDOWS_RECOVERY,
-                                TAG_MOK_TOOL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, TAG_FIRMWARE,
+                                TAG_MOK_TOOL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, TAG_FIRMWARE, TAG_FWUPDATE_TOOL,
                                 0, 0, 0, 0, 0, 0, 0, 0 }
                             };
 
@@ -197,7 +197,7 @@ static VOID AboutrEFInd(VOID)
         AddMenuInfoLine(&AboutMenu, PoolPrint(L"rEFInd Version %s", REFIND_VERSION));
         AddMenuInfoLine(&AboutMenu, L"");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
-        AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2015 Roderick W. Smith");
+        AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2016 Roderick W. Smith");
         AddMenuInfoLine(&AboutMenu, L"Portions Copyright (c) Intel Corporation and others");
         AddMenuInfoLine(&AboutMenu, L"Distributed under the terms of the GNU GPLv3 license");
         AddMenuInfoLine(&AboutMenu, L"");
@@ -410,8 +410,12 @@ EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
         if (ErrorInStep != NULL)
             *ErrorInStep = 3;
     }
-    if (IsDriver)
+    if (IsDriver) {
+        // Below should have no effect on most systems, but works
+        // around bug with some EFIs that prevents filesystem drivers
+        // from binding to partitions.
         ConnectFilesystemDriver(ChildImageHandle);
+    }
 
     // re-open file handles
     ReinitRefitLib();
@@ -526,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;
 
@@ -551,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);
@@ -1296,9 +1327,7 @@ static BOOLEAN DuplicatesFallback(IN REFIT_VOLUME *Volume, IN CHAR16 *FileName)
         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) {
@@ -1329,25 +1358,20 @@ static BOOLEAN DuplicatesFallback(IN REFIT_VOLUME *Volume, IN CHAR16 *FileName)
 // 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);
@@ -1359,15 +1383,14 @@ static BOOLEAN IsSymbolicLink(REFIT_VOLUME *Volume, CHAR16 *Path, EFI_FILE_INFO
 // 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);
@@ -1386,7 +1409,7 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16
     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;
@@ -1398,34 +1421,31 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16
        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"))) ||
-              StriSubCmp(L"shell", DirEntry->FileName) ||
-              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))
+              FilenameIn(Volume, Path, DirEntry->FileName, SHELL_NAMES) ||
+              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;
@@ -1453,10 +1473,10 @@ static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16
        // 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
 
@@ -1916,6 +1936,10 @@ static VOID ScanForTools(VOID) {
                 FindTool(MokLocations, MOK_NAMES, L"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL);
                 break;
 
+            case TAG_FWUPDATE_TOOL:
+                FindTool(MokLocations, FWUPDATE_NAMES, L"firmware update utility", BUILTIN_ICON_TOOL_FWUPDATE);
+                break;
+
             case TAG_CSR_ROTATE:
                 if ((GetCsrStatus(&CsrValue) == EFI_SUCCESS) && (GlobalConfig.CsrValues)) {
                     TempMenuEntry = CopyMenuEntry(&MenuEntryRotateCsr);