]> code.delx.au - refind/blobdiff - refind/main.c
Fixed bug: ICNS file in main icons directory would override PNG file in
[refind] / refind / main.c
index 669cdb83fe873d95084218e8fad4b9f9f468d6ae..e27e149e3c63e31a5051feb381ab6cdfeb5b5df9 100644 (file)
 #if defined (EFIX64)
 #define SHELL_NAMES             L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shellx64.efi"
 #define DRIVER_DIRS             L"drivers,drivers_x64"
+#define FALLBACK_FULLNAME       L"EFI\\BOOT\\bootx64.efi"
+#define FALLBACK_BASENAME       L"bootx64.efi"
 #elif defined (EFI32)
 #define SHELL_NAMES             L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shellia32.efi"
 #define DRIVER_DIRS             L"drivers,drivers_ia32"
+#define FALLBACK_FULLNAME       L"EFI\\BOOT\\bootia32.efi"
+#define FALLBACK_BASENAME       L"bootia32.efi"
 #else
 #define SHELL_NAMES             L"\\EFI\\tools\\shell.efi"
 #define DRIVER_DIRS             L"drivers"
+#define FALLBACK_FULLNAME       L"EFI\\BOOT\\boot.efi" /* Not really correct */
+#define FALLBACK_BASENAME       L"boot.efi"            /* Not really correct */
 #endif
 
-#define MOK_NAMES               L"\\EFI\\tools\\MokManager.efi,\\EFI\\redhat\\MokManager.efi,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager"
+#define MOK_NAMES               L"\\EFI\\tools\\MokManager.efi,\\EFI\\fedora\\MokManager.efi,\\EFI\\redhat\\MokManager.efi,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager"
 
 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
@@ -106,8 +112,8 @@ static REFIT_MENU_SCREEN MainMenu       = { L"Main Menu", NULL, 0, NULL, 0, NULL
                                             L"Insert or F2 for more options; Esc 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, FALSE, 0, 0, DONT_CHANGE_TEXT_MODE, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC, 0,
-                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+REFIT_CONFIG GlobalConfig = { FALSE, FALSE, 0, 0, 0, DONT_CHANGE_TEXT_MODE, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC, 0,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               {TAG_SHELL, TAG_APPLE_RECOVERY, TAG_MOK_TOOL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, 0, 0, 0, 0, 0 }};
 
 // Structure used to hold boot loader filenames and time stamps in
@@ -128,7 +134,7 @@ static VOID AboutrEFInd(VOID)
 
     if (AboutMenu.EntryCount == 0) {
         AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
-        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.6.3.2");
+        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.6.6.4");
         AddMenuInfoLine(&AboutMenu, L"");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012 Roderick W. Smith");
@@ -568,7 +574,17 @@ VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) {
             SubEntry->LoadOptions     = L"-v -s";
             AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
          } // if
-      } // not single-user
+      } // single-user mode allowed
+
+      if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_SAFEMODE)) {
+         SubEntry = InitializeLoaderEntry(Entry);
+         if (SubEntry != NULL) {
+            SubEntry->me.Title        = L"Boot Mac OS X in safe mode";
+            SubEntry->UseGraphicsMode = FALSE;
+            SubEntry->LoadOptions     = L"-v -x";
+            AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
+         } // if
+      } // safe mode allowed
 
       // check for Apple hardware diagnostics
       StrCpy(DiagsFileName, L"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
@@ -594,12 +610,12 @@ VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) {
          // earlier....
          if ((SubScreen->Entries != NULL) && (SubScreen->Entries[0] != NULL)) {
             MyFreePool(SubScreen->Entries[0]->Title);
-            SubScreen->Entries[0]->Title = StrDuplicate(TokenList[0]);
+            SubScreen->Entries[0]->Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux");
          } // if
          FreeTokenLine(&TokenList, &TokenCount);
          while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
             SubEntry = InitializeLoaderEntry(Entry);
-            SubEntry->me.Title = StrDuplicate(TokenList[0]);
+            SubEntry->me.Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux");
             MyFreePool(SubEntry->LoadOptions);
             SubEntry->LoadOptions = AddInitrdToOptions(TokenList[1], InitrdName);
             FreeTokenLine(&TokenList, &TokenCount);
@@ -701,23 +717,20 @@ static CHAR16 * GetMainLinuxOptions(IN CHAR16 * LoaderPath, IN REFIT_VOLUME *Vol
 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
 // that will (with luck) work fairly automatically.
 VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Volume) {
-   CHAR16      IconFileName[256];
-   CHAR16      *FileName, *PathOnly, *OSIconName = NULL, *Temp, *SubString;
+   CHAR16      *FileName, *PathOnly, *NoExtension, *OSIconName = NULL, *Temp, *SubString;
    CHAR16      ShortcutLetter = 0;
-   UINTN       i, Length;
+   UINTN       i = 0, Length;
 
    FileName = Basename(LoaderPath);
    PathOnly = FindPath(LoaderPath);
+   NoExtension = StripEfiExtension(FileName);
 
    // locate a custom icon for the loader
    // Anything found here takes precedence over the "hints" in the OSIconName variable
-   StrCpy(IconFileName, LoaderPath);
-   ReplaceEfiExtension(IconFileName, L".icns");
-   if (FileExists(Volume->RootDir, IconFileName)) {
-      Entry->me.Image = LoadIcns(Volume->RootDir, IconFileName, 128);
-   } else if ((StrLen(PathOnly) == 0) && (Volume->VolIconImage != NULL)) {
+   if (!Entry->me.Image)
+      Entry->me.Image = egFindIcon(NoExtension, 128);
+   if (!Entry->me.Image)
       Entry->me.Image = Volume->VolIconImage;
-   } // icon matched to loader or volume
 
    // Begin creating icon "hints" by using last part of directory path leading
    // to the loader
@@ -923,17 +936,79 @@ static BOOLEAN ShouldScan(REFIT_VOLUME *Volume, CHAR16 *Path) {
    return ScanIt;
 } // BOOLEAN ShouldScan()
 
+// Returns TRUE if the file is byte-for-byte identical with the fallback file
+// on the volume AND if the file is not itself the fallback file; returns
+// FALSE if the file is not identical to the fallback file OR if the file
+// IS the fallback file. Intended for use in excluding the fallback boot
+// loader when it's a duplicate of another boot loader.
+BOOLEAN DuplicatesFallback(IN REFIT_VOLUME *Volume, IN CHAR16 *FileName) {
+   CHAR8           *FileContents, *FallbackContents;
+   EFI_FILE_HANDLE FileHandle, FallbackHandle;
+   EFI_FILE_INFO   *FileInfo, *FallbackInfo;
+   UINTN           FileSize = 0, FallbackSize = 0;
+   EFI_STATUS      Status;
+   BOOLEAN         AreIdentical = FALSE;
+
+   CleanUpPathNameSlashes(FileName);
+
+   if (StriCmp(FileName, FALLBACK_FULLNAME) == 0)
+      return FALSE; // identical filenames, so not a duplicate....
+
+   Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
+   if (Status == EFI_SUCCESS) {
+      FileInfo = LibFileInfo(FileHandle);
+      FileSize = FileInfo->FileSize;
+   } else {
+      return FALSE;
+   }
+
+   Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FallbackHandle, FALLBACK_FULLNAME, EFI_FILE_MODE_READ, 0);
+   if (Status == EFI_SUCCESS) {
+      FallbackInfo = LibFileInfo(FallbackHandle);
+      FallbackSize = FallbackInfo->FileSize;
+   } else {
+      refit_call1_wrapper(FileHandle->Close, FileHandle);
+      return FALSE;
+   }
+
+   if (FallbackSize != FileSize) { // not same size, so can't be identical
+      AreIdentical = FALSE;
+   } else { // could be identical; do full check....
+      FileContents = AllocatePool(FileSize);
+      FallbackContents = AllocatePool(FallbackSize);
+      if (FileContents && FallbackContents) {
+         Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &FileSize, FileContents);
+         if (Status == EFI_SUCCESS)
+            Status = refit_call3_wrapper(FallbackHandle->Read, FallbackHandle, &FallbackSize, FallbackContents);
+         if (Status == EFI_SUCCESS) {
+            AreIdentical = (CompareMem(FileContents, FallbackContents, FileSize) == 0);
+         } // if
+      } // if
+      MyFreePool(FileContents);
+      MyFreePool(FallbackContents);
+   } // if/else
+
+   refit_call1_wrapper(FileHandle->Close, FileHandle);
+   refit_call1_wrapper(FileHandle->Close, FallbackHandle);
+   return AreIdentical;
+
+} // BOOLEAN DuplicatesFallback()
+
 // Scan an individual directory for EFI boot loader files and, if found,
-// add them to the list. Sorts the entries within the loader directory
-// so that the most recent one appears first in the list.
-static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Pattern)
+// add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
+// up in ScanEfiFiles(). Sorts the entries within the loader directory so that
+// the most recent one appears first in the list.
+// Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
+static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Pattern)
 {
     EFI_STATUS              Status;
     REFIT_DIR_ITER          DirIter;
     EFI_FILE_INFO           *DirEntry;
     CHAR16                  FileName[256], *Extension;
     struct LOADER_LIST      *LoaderList = NULL, *NewLoader;
+    BOOLEAN                 FoundFallbackDuplicate = FALSE;
 
+//    Print(L"Entering ScanLoaderDir(), scanning '%s' for pattern '%s'\n", Path, Pattern);
     if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle != SelfVolume->DeviceHandle)) ||
            (StriCmp(Path, SelfDirPath) != 0)) &&
            (ShouldScan(Volume, Path))) {
@@ -943,6 +1018,8 @@ static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *P
           Extension = FindExtension(DirEntry->FileName);
           if (DirEntry->FileName[0] == '.' ||
               StriCmp(Extension, L".icns") == 0 ||
+              StriCmp(Extension, L".png") == 0 ||
+              (StriCmp(DirEntry->FileName, FALLBACK_BASENAME) == 0 && (StriCmp(Path, L"EFI\\BOOT") == 0)) ||
               StriSubCmp(L"shell", DirEntry->FileName) ||
               IsIn(DirEntry->FileName, GlobalConfig.DontScanFiles))
                 continue;   // skip this
@@ -957,6 +1034,8 @@ static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *P
              NewLoader->FileName = StrDuplicate(FileName);
              NewLoader->TimeStamp = DirEntry->ModificationTime;
              LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
+             if (DuplicatesFallback(Volume, FileName))
+                FoundFallbackDuplicate = TRUE;
           } // if
           MyFreePool(Extension);
        } // while
@@ -975,6 +1054,8 @@ static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *P
           CheckError(Status, FileName);
        } // if (Status != EFI_NOT_FOUND)
     } // if not scanning our own directory
+//    PauseForKey();
+    return FoundFallbackDuplicate;
 } /* static VOID ScanLoaderDir() */
 
 static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
@@ -983,10 +1064,13 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
    EFI_FILE_INFO           *EfiDirEntry;
    CHAR16                  FileName[256], *Directory, *MatchPatterns, *VolName = NULL;
    UINTN                   i, Length, VolNum;
+   BOOLEAN                 ScanFallbackLoader = TRUE;
 
+//   Print(L"Entering ScanEfiFiles(), GlobalConfig.ScanAllLinux = %s\n", GlobalConfig.ScanAllLinux ? L"TRUE" : L"FALSE");
    MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
    if (GlobalConfig.ScanAllLinux)
       MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L',');
+//   Print(L"MatchPatterns = '%s'\n", MatchPatterns);
 
    if ((Volume->RootDir != NULL) && (Volume->VolName != NULL)) {
       // check for Mac OS X boot loader
@@ -994,12 +1078,16 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
          StrCpy(FileName, MACOSX_LOADER_PATH);
          if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) {
             AddLoaderEntry(FileName, L"Mac OS X", Volume);
+            if (DuplicatesFallback(Volume, FileName))
+               ScanFallbackLoader = FALSE;
          }
 
          // check for XOM
          StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi");
          if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) {
             AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume);
+            if (DuplicatesFallback(Volume, FileName))
+               ScanFallbackLoader = FALSE;
          }
       } // if Mac directory not in GlobalConfig.DontScanDirs list
 
@@ -1008,18 +1096,22 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
       if (FileExists(Volume->RootDir, FileName) && !IsIn(L"EFI\\Microsoft\\Boot", GlobalConfig.DontScanDirs) &&
           !IsIn(L"bootmgfw.efi", GlobalConfig.DontScanFiles)) {
          AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
+         if (DuplicatesFallback(Volume, FileName))
+            ScanFallbackLoader = FALSE;
       }
 
       // scan the root directory for EFI executables
-      ScanLoaderDir(Volume, L"\\", MatchPatterns);
+      if (ScanLoaderDir(Volume, L"\\", MatchPatterns))
+         ScanFallbackLoader = FALSE;
 
       // scan subdirectories of the EFI directory (as per the standard)
       DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
       while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) {
          if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || EfiDirEntry->FileName[0] == '.')
-            continue;   // skip this, doesn't contain boot loaders
+            continue;   // skip this, doesn't contain boot loaders or is scanned later
          SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName);
-         ScanLoaderDir(Volume, FileName, MatchPatterns);
+         if (ScanLoaderDir(Volume, FileName, MatchPatterns))
+            ScanFallbackLoader = FALSE;
       } // while()
       Status = DirIterClose(&EfiDirIter);
       if (Status != EFI_NOT_FOUND)
@@ -1032,14 +1124,22 @@ static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
          SplitVolumeAndFilename(&Directory, &VolName);
          CleanUpPathNameSlashes(Directory);
          Length = StrLen(Directory);
-         if ((Length > 0) && (StrLen(VolName) > 2) && (VolName[0] == L'f') && (VolName[1] == L's') &&
+         if (VolName && (Length > 0) && (StrLen(VolName) > 2) && (VolName[0] == L'f') && (VolName[1] == L's') &&
              (VolName[2] >= L'0') && (VolName[2] <= L'9'))
             VolNum = Atoi(VolName + 2);
          if ((Length > 0) && ((VolName == NULL) || (StriCmp(VolName, Volume->VolName) == 0) || (Volume->VolNumber == VolNum)))
-            ScanLoaderDir(Volume, Directory, MatchPatterns);
+            if (ScanLoaderDir(Volume, Directory, MatchPatterns))
+               ScanFallbackLoader = FALSE;
          MyFreePool(Directory);
          MyFreePool(VolName);
       } // while
+
+      // If not a duplicate & if it exists & if it's not us, create an entry
+      // for the fallback boot loader
+      if (ScanFallbackLoader && FileExists(Volume->RootDir, FALLBACK_FULLNAME) &&
+          ((StriCmp(SelfDirPath, L"EFI\\BOOT") != 0) || (Volume->DeviceHandle != SelfVolume->DeviceHandle))) {
+         AddLoaderEntry(FALLBACK_FULLNAME, L"Fallback boot loader", Volume);
+      }
    } // if
 } // static VOID ScanEfiFiles()
 
@@ -1814,7 +1914,7 @@ static VOID ScanForBootloaders(VOID) {
             ScanLegacyExternal();
             break;
          case 'm': case 'M':
-            ScanUserConfigured();
+            ScanUserConfigured(CONFIG_FILE_NAME);
             break;
          case 'e': case 'E':
             ScanExternal();
@@ -1878,7 +1978,6 @@ static VOID ScanForTools(VOID) {
          case TAG_GPTSYNC:
             MyFreePool(FileName);
             FileName = StrDuplicate(L"\\efi\\tools\\gptsync.efi");
-//            MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
             if (FileExists(SelfRootDir, FileName)) {
                AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE);
             }
@@ -1886,7 +1985,6 @@ static VOID ScanForTools(VOID) {
          case TAG_APPLE_RECOVERY:
             MyFreePool(FileName);
             FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi");
-//            MergeStrings(&FileName, L"\\com.apple.recovery.boot\\boot.efi", 0);
             for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
                if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName))) {
                   SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName);
@@ -1907,7 +2005,7 @@ static VOID ScanForTools(VOID) {
             } // while
             if (FileExists(SelfDir, L"MokManager.efi")) {
                MyFreePool(FileName);
-               FileName = StrDuplicate(SelfDirPath);
+               FileName = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
                MergeStrings(&FileName, L"\\MokManager.efi", 0);
                SPrint(Description, 255, L"MOK Key Manager at %s", FileName);
                AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, Description,
@@ -2004,7 +2102,7 @@ efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     BOOLEAN            MokProtocol;
     REFIT_MENU_ENTRY   *ChosenEntry;
     UINTN              MenuExit, i;
-    CHAR16             *Selection;
+    CHAR16             *Selection = NULL;
     EG_PIXEL           BGColor;
 
     // bootstrap
@@ -2018,6 +2116,7 @@ efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     FindLegacyBootType();
     if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
        CopyMem(GlobalConfig.ScanFor, "ihebocm   ", NUM_SCAN_OPTIONS);
+    ScanVolumes();
     ReadConfig(CONFIG_FILE_NAME);
 
     InitScreen();
@@ -2028,12 +2127,11 @@ efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
 
     // further bootstrap (now with config available)
-    SetupScreen();
     MokProtocol = SecureBootSetup();
-    ScanVolumes();
     LoadDrivers();
     ScanForBootloaders();
     ScanForTools();
+    SetupScreen();
 
     if (GlobalConfig.ScanDelay > 0) {
        BGColor.b = 255;
@@ -2046,7 +2144,9 @@ efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
        RescanAll();
     } // if
 
-    Selection = StrDuplicate(GlobalConfig.DefaultSelection);
+    if (GlobalConfig.DefaultSelection)
+       Selection = StrDuplicate(GlobalConfig.DefaultSelection);
+
     while (MainLoopRunning) {
         MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);