]> code.delx.au - refind/blobdiff - refind/config.c
Fixed bug that caused volume icons to be read only from default icons
[refind] / refind / config.c
index f86edc836fb8fc597446e45c1459eba2312db1e8..8aa2850183157005b02457688a28d6da8a248538 100644 (file)
  */
 
 /*
- * Modifications copyright (c) 2012-2013 Roderick W. Smith
+ * Modifications copyright (c) 2012-2015 Roderick W. Smith
  * 
  * Modifications distributed under the terms of the GNU General Public
- * License (GPL) version 3 (GPLv3), a copy of which must be distributed
- * with this source code or binaries made from it.
+ * License (GPL) version 3 (GPLv3) or (at your option) any later version.
  * 
  */
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
 
 #include "global.h"
 #include "lib.h"
 #include "menu.h"
 #include "config.h"
 #include "screen.h"
+#include "apple.h"
+#include "mystrings.h"
 #include "../include/refit_call_wrapper.h"
+#include "../mok/mok.h"
 
 // constants
 
@@ -63,7 +79,8 @@
 #define GetTime ST->RuntimeServices->GetTime
 #define LAST_MINUTE 1439 /* Last minute of a day */
 
-static REFIT_MENU_ENTRY MenuEntryReturn   = { L"Return to Main Menu", TAG_RETURN, 0, 0, 0, NULL, NULL, NULL };
+extern REFIT_MENU_ENTRY MenuEntryReturn;
+//static REFIT_MENU_ENTRY MenuEntryReturn   = { L"Return to Main Menu", TAG_RETURN, 0, 0, 0, NULL, NULL, NULL };
 
 //
 // read a file into a buffer
@@ -265,6 +282,10 @@ UINTN ReadTokenLine(IN REFIT_FILE *File, OUT CHAR16 ***TokenList)
         Line = ReadLine(File);
         if (Line == NULL)
             return(0);
+        if (Line[0] == L'\0') {
+            MyFreePool(Line);
+            return(0);
+        } // if
 
         p = Line;
         LineFinished = FALSE;
@@ -309,7 +330,7 @@ VOID FreeTokenLine(IN OUT CHAR16 ***TokenList, IN OUT UINTN *TokenCount)
 static VOID HandleInt(IN CHAR16 **TokenList, IN UINTN TokenCount, OUT UINTN *Value)
 {
     if (TokenCount == 2) {
-       if (StriCmp(TokenList[1], L"-1") == 0)
+       if (StrCmp(TokenList[1], L"-1") == 0)
           *Value = -1;
        else
           *Value = Atoi(TokenList[1]);
@@ -318,60 +339,99 @@ static VOID HandleInt(IN CHAR16 **TokenList, IN UINTN TokenCount, OUT UINTN *Val
 
 // handle a parameter with a single string argument
 static VOID HandleString(IN CHAR16 **TokenList, IN UINTN TokenCount, OUT CHAR16 **Target) {
-   if (TokenCount == 2) {
-      MyFreePool(*Target);
-      *Target = StrDuplicate(TokenList[1]);
+   if ((TokenCount == 2) && Target) {
+      if ((StrLen(TokenList[1]) > 1) && (TokenList[1][0] == L'+') &&
+          ((TokenList[1][1] == L',') || (TokenList[1][1] == L' '))) {
+         if (*Target) {
+            MergeStrings(Target, TokenList[1] + 2, L',');
+         } else {
+            *Target = StrDuplicate(TokenList[1] + 2);
+         } // if/else
+      } else {
+         MyFreePool(*Target);
+         *Target = StrDuplicate(TokenList[1]);
+      } // if/else
    } // if
 } // static VOID HandleString()
 
-// Handle a parameter with a series of string arguments, to be added to a comma-delimited
-// list. Passes each token through the CleanUpPathNameSlashes() function to ensure
-// consistency in subsequent comparisons of filenames.
+// Handle a parameter with a series of string arguments, to replace or be added to a
+// comma-delimited list. Passes each token through the CleanUpPathNameSlashes() function
+// to ensure consistency in subsequent comparisons of filenames. If the first
+// non-keyword token is "+", the list is added to the existing target string; otherwise,
+// the tokens replace the current string.
 static VOID HandleStrings(IN CHAR16 **TokenList, IN UINTN TokenCount, OUT CHAR16 **Target) {
    UINTN i;
+   BOOLEAN AddMode = FALSE;
+
+   if ((TokenCount > 2) && (StrCmp(TokenList[1], L"+") == 0)) {
+      AddMode = TRUE;
+   }
 
-   if (*Target != NULL) {
+   if ((*Target != NULL) && !AddMode) {
       FreePool(*Target);
       *Target = NULL;
    } // if
    for (i = 1; i < TokenCount; i++) {
-      CleanUpPathNameSlashes(TokenList[i]);
-      MergeStrings(Target, TokenList[i], L',');
-   }
+      if ((i != 1) || !AddMode) {
+         CleanUpPathNameSlashes(TokenList[i]);
+         MergeStrings(Target, TokenList[i], L',');
+      } // if
+   } // for
 } // static VOID HandleStrings()
 
+// Handle a parameter with a series of hexadecimal arguments, to replace or be added to a
+// linked list of UINT32 values. Any item with a non-hexadecimal value is discarded, as is
+// any value that exceeds MaxValue. If the first non-keyword token is "+", the new list is
+// added to the existing Target; otherwise, the interpreted tokens replace the current
+// Target.
+static VOID HandleHexes(IN CHAR16 **TokenList, IN UINTN TokenCount, IN UINTN MaxValue, OUT UINT32_LIST **Target) {
+    UINTN       InputIndex = 1, i;
+    UINT32      Value;
+    UINT32_LIST *EndOfList = NULL;
+    UINT32_LIST *NewEntry;
+
+    if ((TokenCount > 2) && (StrCmp(TokenList[1], L"+") == 0)) {
+        InputIndex = 2;
+        EndOfList = *Target;
+        while (EndOfList && (EndOfList->Next != NULL)) {
+            EndOfList = EndOfList->Next;
+        }
+    } else {
+        EraseUint32List(Target);
+    }
+
+    for (i = InputIndex; i < TokenCount; i++) {
+        if (IsValidHex(TokenList[i])) {
+            Value = (UINT32) StrToHex(TokenList[i], 0, 8);
+            if (Value <= MaxValue) {
+                NewEntry = AllocatePool(sizeof(UINT32_LIST));
+                if (NewEntry) {
+                    NewEntry->Value = Value;
+                    NewEntry->Next = NULL;
+                    if (EndOfList == NULL) {
+                        EndOfList = NewEntry;
+                        *Target = NewEntry;
+                    } else {
+                        EndOfList->Next = NewEntry;
+                        EndOfList = NewEntry;
+                    } // if/else
+                } // if allocated memory for NewEntry
+            } // if (Value < MaxValue)
+        } // if is valid hex value
+    } // for
+} // static VOID HandleHexes()
+
 // Convert TimeString (in "HH:MM" format) to a pure-minute format. Values should be
 // in the range from 0 (for 00:00, or midnight) to 1439 (for 23:59; aka LAST_MINUTE).
-// Any value outside that range denotes an error in the specification.
-// static UINTN HandleTime(IN CHAR16 *TimeString) {
-//    BOOLEAN Found = FALSE;
-//    UINTN ColonPosition = 0, Hour = 0, Minute = 0, TimeLength;
-// 
-//    Print(L"Entering HandleTime('%s')\n", TimeString);
-//    TimeLength = StrLen(TimeString);
-//    for (ColonPosition = 0; (ColonPosition < TimeLength) && !Found; ColonPosition++) {
-//       Print(L"ColonPosition = %d\n", ColonPosition);
-//       if (TimeString[ColonPosition] == ':')
-//          Found = TRUE;
-//    } // for
-// 
-//    if ((ColonPosition == 0) || (ColonPosition > StrLen(TimeString)))
-//       return (LAST_MINUTE + 1);
-// 
-//    Hour = Atoi(TimeString);
-//    Minute = Atoi(&TimeString[ColonPosition + 1]);
-//    Print(L"Hour = %d, Minute = %d\n", Hour, Minute);
-//    return (Hour * 60 + Minute);
-// } // BOOLEAN HandleTime()
-
+// Any value outside that range denotes an error in the specification. Note that if
+// the input is a number that includes no colon, this function will return the original
+// number in UINTN form.
 static UINTN HandleTime(IN CHAR16 *TimeString) {
    UINTN Hour = 0, Minute = 0, TimeLength, i = 0;
-   BOOLEAN FoundColon = FALSE;
 
    TimeLength = StrLen(TimeString);
    while (i < TimeLength) {
       if (TimeString[i] == L':') {
-         FoundColon = TRUE;
          Hour = Minute;
          Minute = 0;
       } // if
@@ -381,15 +441,27 @@ static UINTN HandleTime(IN CHAR16 *TimeString) {
       } // if
       i++;
    } // while
-   return (FoundColon ? Hour * 60 + Minute : LAST_MINUTE + 1);
+   return (Hour * 60 + Minute);
 } // BOOLEAN HandleTime()
 
+static BOOLEAN HandleBoolean(IN CHAR16 **TokenList, IN UINTN TokenCount) {
+   BOOLEAN TruthValue = TRUE;
+
+   if ((TokenCount >= 2) && ((StrCmp(TokenList[1], L"0") == 0) ||
+                             MyStriCmp(TokenList[1], L"false") ||
+                             MyStriCmp(TokenList[1], L"off"))) {
+      TruthValue = FALSE;
+   } // if
+
+   return TruthValue;
+} // BOOLEAN HandleBoolean
+
 // Sets the default boot loader IF the current time is within the bounds
 // defined by the third and fourth tokens in the TokenList.
 static VOID SetDefaultByTime(IN CHAR16 **TokenList, OUT CHAR16 **Default) {
    EFI_STATUS            Status;
    EFI_TIME              CurrentTime;
-   UINTN                 StartTime = LAST_MINUTE + 1, EndTime = LAST_MINUTE + 1, Now;
+   UINTN                 StartTime, EndTime, Now;
    BOOLEAN               SetIt = FALSE;
 
    StartTime = HandleTime(TokenList[2]);
@@ -397,6 +469,8 @@ static VOID SetDefaultByTime(IN CHAR16 **TokenList, OUT CHAR16 **Default) {
 
    if ((StartTime <= LAST_MINUTE) && (EndTime <= LAST_MINUTE)) {
       Status = refit_call2_wrapper(GetTime, &CurrentTime, NULL);
+      if (Status != EFI_SUCCESS)
+         return;
       Now = CurrentTime.Hour * 60 + CurrentTime.Minute;
 
       if (Now > LAST_MINUTE) { // Shouldn't happen; just being paranoid
@@ -428,9 +502,10 @@ VOID ReadConfig(CHAR16 *FileName)
     CHAR16          *FlagName;
     CHAR16          *TempStr = NULL;
     UINTN           TokenCount, i;
+    EFI_GUID        RefindGuid = REFIND_GUID_VALUE;
 
     // Set a few defaults only if we're loading the default file.
-    if (StriCmp(FileName, CONFIG_FILE_NAME) == 0) {
+    if (MyStriCmp(FileName, GlobalConfig.ConfigFilename)) {
        MyFreePool(GlobalConfig.AlsoScan);
        GlobalConfig.AlsoScan = StrDuplicate(ALSO_SCAN_DIRS);
        MyFreePool(GlobalConfig.DontScanDirs);
@@ -451,11 +526,23 @@ VOID ReadConfig(CHAR16 *FileName)
        MergeStrings(&(GlobalConfig.DontScanFiles), MOK_NAMES, L',');
        MyFreePool(GlobalConfig.DontScanVolumes);
        GlobalConfig.DontScanVolumes = StrDuplicate(DONT_SCAN_VOLUMES);
+       GlobalConfig.WindowsRecoveryFiles = StrDuplicate(WINDOWS_RECOVERY_FILES);
+       if (GlobalConfig.DefaultSelection != NULL) {
+          MyFreePool(GlobalConfig.DefaultSelection);
+          GlobalConfig.DefaultSelection = NULL;
+       }
+       Status = EfivarGetRaw(&RefindGuid, L"PreviousBoot", (CHAR8**) &(GlobalConfig.DefaultSelection), &i);
+       if (Status != EFI_SUCCESS)
+          GlobalConfig.DefaultSelection = NULL;
     } // if
 
     if (!FileExists(SelfDir, FileName)) {
-        Print(L"Configuration file '%s' missing!\n", FileName);
-        return;
+       Print(L"Configuration file '%s' missing!\n", FileName);
+       if (!FileExists(SelfDir, L"icons")) {
+          Print(L"Icons directory doesn't exist; setting textonly = TRUE!\n");
+          GlobalConfig.TextOnly = TRUE;
+       }
+       return;
     }
 
     Status = ReadFile(SelfDir, FileName, &File, &i);
@@ -467,39 +554,41 @@ VOID ReadConfig(CHAR16 *FileName)
         if (TokenCount == 0)
             break;
 
-        if (StriCmp(TokenList[0], L"timeout") == 0) {
+        if (MyStriCmp(TokenList[0], L"timeout")) {
             HandleInt(TokenList, TokenCount, &(GlobalConfig.Timeout));
 
-        } else if (StriCmp(TokenList[0], L"hideui") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"hideui")) {
             for (i = 1; i < TokenCount; i++) {
                 FlagName = TokenList[i];
-                if (StriCmp(FlagName, L"banner") == 0) {
+                if (MyStriCmp(FlagName, L"banner")) {
                    GlobalConfig.HideUIFlags |= HIDEUI_FLAG_BANNER;
-                } else if (StriCmp(FlagName, L"label") == 0) {
+                } else if (MyStriCmp(FlagName, L"label")) {
                    GlobalConfig.HideUIFlags |= HIDEUI_FLAG_LABEL;
-                } else if (StriCmp(FlagName, L"singleuser") == 0) {
+                } else if (MyStriCmp(FlagName, L"singleuser")) {
                    GlobalConfig.HideUIFlags |= HIDEUI_FLAG_SINGLEUSER;
-                } else if (StriCmp(FlagName, L"hwtest") == 0) {
+                } else if (MyStriCmp(FlagName, L"hwtest")) {
                    GlobalConfig.HideUIFlags |= HIDEUI_FLAG_HWTEST;
-                } else if (StriCmp(FlagName, L"arrows") == 0) {
+                } else if (MyStriCmp(FlagName, L"arrows")) {
                    GlobalConfig.HideUIFlags |= HIDEUI_FLAG_ARROWS;
-                } else if (StriCmp(FlagName, L"hints") == 0) {
+                } else if (MyStriCmp(FlagName, L"hints")) {
                    GlobalConfig.HideUIFlags |= HIDEUI_FLAG_HINTS;
-                } else if (StriCmp(FlagName, L"editor") == 0) {
+                } else if (MyStriCmp(FlagName, L"editor")) {
                    GlobalConfig.HideUIFlags |= HIDEUI_FLAG_EDITOR;
-                } else if (StriCmp(FlagName, L"safemode") == 0) {
+                } else if (MyStriCmp(FlagName, L"safemode")) {
                    GlobalConfig.HideUIFlags |= HIDEUI_FLAG_SAFEMODE;
-                } else if (StriCmp(FlagName, L"all") == 0) {
+                } else if (MyStriCmp(FlagName, L"badges")) {
+                   GlobalConfig.HideUIFlags |= HIDEUI_FLAG_BADGES;
+                } else if (MyStriCmp(FlagName, L"all")) {
                    GlobalConfig.HideUIFlags = HIDEUI_FLAG_ALL;
                 } else {
                     Print(L" unknown hideui flag: '%s'\n", FlagName);
                 }
             }
 
-        } else if (StriCmp(TokenList[0], L"icons_dir") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"icons_dir")) {
            HandleString(TokenList, TokenCount, &(GlobalConfig.IconsDir));
 
-        } else if (StriCmp(TokenList[0], L"scanfor") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"scanfor")) {
            for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
               if (i < TokenCount)
                  GlobalConfig.ScanFor[i] = TokenList[i][0];
@@ -507,13 +596,16 @@ VOID ReadConfig(CHAR16 *FileName)
                  GlobalConfig.ScanFor[i] = ' ';
            }
 
-        } else if ((StriCmp(TokenList[0], L"scan_delay") == 0) && (TokenCount == 2)) {
+        } else if (MyStriCmp(TokenList[0], L"uefi_deep_legacy_scan")) {
+           GlobalConfig.DeepLegacyScan = HandleBoolean(TokenList, TokenCount);
+
+        } else if (MyStriCmp(TokenList[0], L"scan_delay") && (TokenCount == 2)) {
            HandleInt(TokenList, TokenCount, &(GlobalConfig.ScanDelay));
 
-        } else if (StriCmp(TokenList[0], L"also_scan_dirs") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"also_scan_dirs")) {
             HandleStrings(TokenList, TokenCount, &(GlobalConfig.AlsoScan));
 
-        } else if ((StriCmp(TokenList[0], L"don't_scan_volumes") == 0) || (StriCmp(TokenList[0], L"dont_scan_volumes") == 0)) {
+        } else if (MyStriCmp(TokenList[0], L"don't_scan_volumes") || MyStriCmp(TokenList[0], L"dont_scan_volumes")) {
            // Note: Don't use HandleStrings() because it modifies slashes, which might be present in volume name
            MyFreePool(GlobalConfig.DontScanVolumes);
            GlobalConfig.DontScanVolumes = NULL;
@@ -521,111 +613,148 @@ VOID ReadConfig(CHAR16 *FileName)
               MergeStrings(&GlobalConfig.DontScanVolumes, TokenList[i], L',');
            }
 
-        } else if ((StriCmp(TokenList[0], L"don't_scan_dirs") == 0) || (StriCmp(TokenList[0], L"dont_scan_dirs") == 0)) {
+        } else if (MyStriCmp(TokenList[0], L"don't_scan_dirs") || MyStriCmp(TokenList[0], L"dont_scan_dirs")) {
             HandleStrings(TokenList, TokenCount, &(GlobalConfig.DontScanDirs));
 
-        } else if ((StriCmp(TokenList[0], L"don't_scan_files") == 0) || (StriCmp(TokenList[0], L"dont_scan_files") == 0)) {
+        } else if (MyStriCmp(TokenList[0], L"don't_scan_files") || MyStriCmp(TokenList[0], L"dont_scan_files")) {
            HandleStrings(TokenList, TokenCount, &(GlobalConfig.DontScanFiles));
 
-        } else if (StriCmp(TokenList[0], L"scan_driver_dirs") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"windows_recovery_files")) {
+           HandleStrings(TokenList, TokenCount, &(GlobalConfig.WindowsRecoveryFiles));
+
+        } else if (MyStriCmp(TokenList[0], L"scan_driver_dirs")) {
             HandleStrings(TokenList, TokenCount, &(GlobalConfig.DriverDirs));
 
-        } else if (StriCmp(TokenList[0], L"showtools") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"showtools")) {
             SetMem(GlobalConfig.ShowTools, NUM_TOOLS * sizeof(UINTN), 0);
             for (i = 1; (i < TokenCount) && (i < NUM_TOOLS); i++) {
                 FlagName = TokenList[i];
-                if (StriCmp(FlagName, L"shell") == 0) {
+                if (MyStriCmp(FlagName, L"shell")) {
                     GlobalConfig.ShowTools[i - 1] = TAG_SHELL;
-                } else if (StriCmp(FlagName, L"gptsync") == 0) {
+                } else if (MyStriCmp(FlagName, L"gptsync")) {
                     GlobalConfig.ShowTools[i - 1] = TAG_GPTSYNC;
-                } else if (StriCmp(FlagName, L"about") == 0) {
+                } else if (MyStriCmp(FlagName, L"gdisk")) {
+                   GlobalConfig.ShowTools[i - 1] = TAG_GDISK;
+                } else if (MyStriCmp(FlagName, L"about")) {
                    GlobalConfig.ShowTools[i - 1] = TAG_ABOUT;
-                } else if (StriCmp(FlagName, L"exit") == 0) {
+                } else if (MyStriCmp(FlagName, L"exit")) {
                    GlobalConfig.ShowTools[i - 1] = TAG_EXIT;
-                } else if (StriCmp(FlagName, L"reboot") == 0) {
+                } else if (MyStriCmp(FlagName, L"reboot")) {
                    GlobalConfig.ShowTools[i - 1] = TAG_REBOOT;
-                } else if (StriCmp(FlagName, L"shutdown") == 0) {
+                } else if (MyStriCmp(FlagName, L"shutdown")) {
                    GlobalConfig.ShowTools[i - 1] = TAG_SHUTDOWN;
-                } else if (StriCmp(FlagName, L"apple_recovery") == 0) {
+                } else if (MyStriCmp(FlagName, L"apple_recovery")) {
                    GlobalConfig.ShowTools[i - 1] = TAG_APPLE_RECOVERY;
-                } else if (StriCmp(FlagName, L"mok_tool") == 0) {
+                } else if (MyStriCmp(FlagName, L"windows_recovery")) {
+                   GlobalConfig.ShowTools[i - 1] = TAG_WINDOWS_RECOVERY;
+                } else if (MyStriCmp(FlagName, L"mok_tool")) {
                    GlobalConfig.ShowTools[i - 1] = TAG_MOK_TOOL;
-                } else if (StriCmp(FlagName, L"firmware") == 0) {
+                } else if (MyStriCmp(FlagName, L"csr_rotate")) {
+                   GlobalConfig.ShowTools[i - 1] = TAG_CSR_ROTATE;
+                } else if (MyStriCmp(FlagName, L"firmware")) {
                    GlobalConfig.ShowTools[i - 1] = TAG_FIRMWARE;
-                } else if ((StriCmp(FlagName, L"memtest86") == 0) || (StriCmp(FlagName, L"memtest") == 0)) {
+                } else if (MyStriCmp(FlagName, L"memtest86") || MyStriCmp(FlagName, L"memtest")) {
                    GlobalConfig.ShowTools[i - 1] = TAG_MEMTEST;
+                } else if (MyStriCmp(FlagName, L"netboot")) {
+                   GlobalConfig.ShowTools[i - 1] = TAG_NETBOOT;
                 } else {
                    Print(L" unknown showtools flag: '%s'\n", FlagName);
                 }
             } // showtools options
 
-        } else if (StriCmp(TokenList[0], L"banner") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"banner")) {
            HandleString(TokenList, TokenCount, &(GlobalConfig.BannerFileName));
 
-        } else if (StriCmp(TokenList[0], L"selection_small") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"banner_scale") && (TokenCount == 2)) {
+           if (MyStriCmp(TokenList[1], L"noscale")) {
+              GlobalConfig.BannerScale = BANNER_NOSCALE;
+           } else if (MyStriCmp(TokenList[1], L"fillscreen") || MyStriCmp(TokenList[1], L"fullscreen")) {
+              GlobalConfig.BannerScale = BANNER_FILLSCREEN;
+           } else {
+              Print(L" unknown banner_type flag: '%s'\n", TokenList[1]);
+           } // if/else
+
+        } else if (MyStriCmp(TokenList[0], L"small_icon_size") && (TokenCount == 2)) {
+           HandleInt(TokenList, TokenCount, &i);
+           if (i >= 32)
+              GlobalConfig.IconSizes[ICON_SIZE_SMALL] = i;
+
+        } else if (MyStriCmp(TokenList[0], L"big_icon_size") && (TokenCount == 2)) {
+           HandleInt(TokenList, TokenCount, &i);
+           if (i >= 32) {
+              GlobalConfig.IconSizes[ICON_SIZE_BIG] = i;
+              GlobalConfig.IconSizes[ICON_SIZE_BADGE] = i / 4;
+           }
+
+        } else if (MyStriCmp(TokenList[0], L"selection_small")) {
            HandleString(TokenList, TokenCount, &(GlobalConfig.SelectionSmallFileName));
 
-        } else if (StriCmp(TokenList[0], L"selection_big") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"selection_big")) {
            HandleString(TokenList, TokenCount, &(GlobalConfig.SelectionBigFileName));
 
-        } else if (StriCmp(TokenList[0], L"default_selection") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"default_selection")) {
            if (TokenCount == 4) {
               SetDefaultByTime(TokenList, &(GlobalConfig.DefaultSelection));
            } else {
               HandleString(TokenList, TokenCount, &(GlobalConfig.DefaultSelection));
            }
 
-        } else if (StriCmp(TokenList[0], L"textonly") == 0) {
-           if ((TokenCount >= 2) && (StriCmp(TokenList[1], L"0") == 0)) {
-              GlobalConfig.TextOnly = FALSE;
-           } else {
-              GlobalConfig.TextOnly = TRUE;
-           }
+        } else if (MyStriCmp(TokenList[0], L"textonly")) {
+           GlobalConfig.TextOnly = HandleBoolean(TokenList, TokenCount);
 
-        } else if (StriCmp(TokenList[0], L"textmode") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"textmode")) {
            HandleInt(TokenList, TokenCount, &(GlobalConfig.RequestedTextMode));
 
-        } else if ((StriCmp(TokenList[0], L"resolution") == 0) && ((TokenCount == 2) || (TokenCount == 3))) {
+        } else if (MyStriCmp(TokenList[0], L"resolution") && ((TokenCount == 2) || (TokenCount == 3))) {
            GlobalConfig.RequestedScreenWidth = Atoi(TokenList[1]);
            if (TokenCount == 3)
               GlobalConfig.RequestedScreenHeight = Atoi(TokenList[2]);
            else
               GlobalConfig.RequestedScreenHeight = 0;
 
-        } else if (StriCmp(TokenList[0], L"screensaver") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"screensaver")) {
            HandleInt(TokenList, TokenCount, &(GlobalConfig.ScreensaverTime));
 
-        } else if (StriCmp(TokenList[0], L"use_graphics_for") == 0) {
-           GlobalConfig.GraphicsFor = 0;
+        } else if (MyStriCmp(TokenList[0], L"use_graphics_for")) {
+           if ((TokenCount == 2) || ((TokenCount > 2) && (!MyStriCmp(TokenList[1], L"+"))))
+              GlobalConfig.GraphicsFor = 0;
            for (i = 1; i < TokenCount; i++) {
-              if (StriCmp(TokenList[i], L"osx") == 0) {
+              if (MyStriCmp(TokenList[i], L"osx")) {
                  GlobalConfig.GraphicsFor |= GRAPHICS_FOR_OSX;
-              } else if (StriCmp(TokenList[i], L"linux") == 0) {
+              } else if (MyStriCmp(TokenList[i], L"linux")) {
                  GlobalConfig.GraphicsFor |= GRAPHICS_FOR_LINUX;
-              } else if (StriCmp(TokenList[i], L"elilo") == 0) {
+              } else if (MyStriCmp(TokenList[i], L"elilo")) {
                  GlobalConfig.GraphicsFor |= GRAPHICS_FOR_ELILO;
-              } else if (StriCmp(TokenList[i], L"grub") == 0) {
+              } else if (MyStriCmp(TokenList[i], L"grub")) {
                  GlobalConfig.GraphicsFor |= GRAPHICS_FOR_GRUB;
-              } else if (StriCmp(TokenList[i], L"windows") == 0) {
+              } else if (MyStriCmp(TokenList[i], L"windows")) {
                  GlobalConfig.GraphicsFor |= GRAPHICS_FOR_WINDOWS;
               }
            } // for (graphics_on tokens)
 
-        } else if ((StriCmp(TokenList[0], L"font") == 0) && (TokenCount == 2)) {
+        } else if (MyStriCmp(TokenList[0], L"font") && (TokenCount == 2)) {
            egLoadFont(TokenList[1]);
 
-        } else if (StriCmp(TokenList[0], L"scan_all_linux_kernels") == 0) {
-           if ((TokenCount >= 2) && (StriCmp(TokenList[1], L"0") == 0)) {
-              GlobalConfig.ScanAllLinux = FALSE;
-           } else {
-              GlobalConfig.ScanAllLinux = TRUE;
-           }
+        } else if (MyStriCmp(TokenList[0], L"scan_all_linux_kernels")) {
+           GlobalConfig.ScanAllLinux = HandleBoolean(TokenList, TokenCount);
+
+        } else if (MyStriCmp(TokenList[0], L"fold_linux_kernels")) {
+            GlobalConfig.FoldLinuxKernels = HandleBoolean(TokenList, TokenCount);
 
-        } else if (StriCmp(TokenList[0], L"max_tags") == 0) {
+        } else if (MyStriCmp(TokenList[0], L"max_tags")) {
            HandleInt(TokenList, TokenCount, &(GlobalConfig.MaxTags));
 
-        } else if ((StriCmp(TokenList[0], L"include") == 0) && (TokenCount == 2) && (StriCmp(FileName, CONFIG_FILE_NAME) == 0)) {
-           if (StriCmp(TokenList[1], FileName) != 0) {
+        } else if (MyStriCmp(TokenList[0], L"enable_and_lock_vmx")) {
+           GlobalConfig.EnableAndLockVMX = HandleBoolean(TokenList, TokenCount);
+
+        } else if (MyStriCmp(TokenList[0], L"spoof_osx_version")) {
+            HandleString(TokenList, TokenCount, &(GlobalConfig.SpoofOSXVersion));
+
+        } else if (MyStriCmp(TokenList[0], L"csr_values")) {
+            HandleHexes(TokenList, TokenCount, CSR_MAX_LEGAL_VALUE, &(GlobalConfig.CsrValues));
+
+        } else if (MyStriCmp(TokenList[0], L"include") && (TokenCount == 2) && MyStriCmp(FileName, GlobalConfig.ConfigFilename)) {
+           if (!MyStriCmp(TokenList[1], FileName)) {
               ReadConfig(TokenList[1]);
            }
 
@@ -633,21 +762,33 @@ VOID ReadConfig(CHAR16 *FileName)
 
         FreeTokenLine(&TokenList, &TokenCount);
     }
+    if ((GlobalConfig.DontScanFiles) && (GlobalConfig.WindowsRecoveryFiles))
+       MergeStrings(&(GlobalConfig.DontScanFiles), GlobalConfig.WindowsRecoveryFiles, L',');
     MyFreePool(File.Buffer);
+
+    if (!FileExists(SelfDir, L"icons") && !FileExists(SelfDir, GlobalConfig.IconsDir)) {
+       Print(L"Icons directory doesn't exist; setting textonly = TRUE!\n");
+       GlobalConfig.TextOnly = TRUE;
+    }
 } /* VOID ReadConfig() */
 
-// Finds a volume with the specified Identifier (a volume label or a number
-// followed by a colon, for the moment). If found, sets *Volume to point to
-// that volume. If not, leaves it unchanged.
+// Finds a volume with the specified Identifier (a filesystem label, a
+// partition name, a partition GUID, or a number followed by a colon). If
+// found, sets *Volume to point to that volume. If not, leaves it unchanged.
 // Returns TRUE if a match was found, FALSE if not.
 static BOOLEAN FindVolume(REFIT_VOLUME **Volume, CHAR16 *Identifier) {
-   UINTN     i = 0, CountedVolumes = 0;
+   UINTN     i = 0, CountedVolumes = 0, Length;
    INTN      Number = -1;
-   BOOLEAN   Found = FALSE;
+   BOOLEAN   Found = FALSE, IdIsGuid = FALSE;
+   EFI_GUID  VolGuid, NullGuid = NULL_GUID_VALUE;
 
-   if ((StrLen(Identifier) >= 2) && (Identifier[StrLen(Identifier) - 1] == L':') &&
+   VolGuid = StringAsGuid(Identifier);
+   Length = StrLen(Identifier);
+   if ((Length >= 2) && (Identifier[Length - 1] == L':') &&
        (Identifier[0] >= L'0') && (Identifier[0] <= L'9')) {
       Number = (INTN) Atoi(Identifier);
+   } else if (IsGuid(Identifier)) {
+      IdIsGuid = TRUE;
    }
    while ((i < VolumesCount) && (!Found)) {
       if (Number >= 0) { // User specified a volume by number
@@ -658,11 +799,17 @@ static BOOLEAN FindVolume(REFIT_VOLUME **Volume, CHAR16 *Identifier) {
             }
             CountedVolumes++;
          } // if
-      } else { // User specified a volume by label
-         if (StriCmp(Identifier, Volumes[i]->VolName) == 0) {
+      } else { // User specified a volume by label or GUID
+         if (MyStriCmp(Identifier, Volumes[i]->VolName) || MyStriCmp(Identifier, Volumes[i]->PartName)) {
             *Volume = Volumes[i];
             Found = TRUE;
          } // if
+         if (IdIsGuid && !Found) {
+            if (GuidsAreEqual(&VolGuid, &(Volumes[i]->PartGuid)) && !GuidsAreEqual(&NullGuid, &(Volumes[i]->PartGuid))) {
+               *Volume = Volumes[i];
+               Found = TRUE;
+            } // if
+         } // if
       } // if/else
       i++;
    } // while()
@@ -684,43 +831,45 @@ static VOID AddSubmenu(LOADER_ENTRY *Entry, REFIT_FILE *File, REFIT_VOLUME *Volu
       return;
    SubEntry->me.Title        = StrDuplicate(Title);
 
-   while (((TokenCount = ReadTokenLine(File, &TokenList)) > 0) && (StriCmp(TokenList[0], L"}") != 0)) {
+   while (((TokenCount = ReadTokenLine(File, &TokenList)) > 0) && (StrCmp(TokenList[0], L"}") != 0)) {
 
-      if ((StriCmp(TokenList[0], L"loader") == 0) && (TokenCount > 1)) { // set the boot loader filename
+      if (MyStriCmp(TokenList[0], L"loader") && (TokenCount > 1)) { // set the boot loader filename
          MyFreePool(SubEntry->LoaderPath);
          SubEntry->LoaderPath = StrDuplicate(TokenList[1]);
          SubEntry->DevicePath = FileDevicePath(Volume->DeviceHandle, SubEntry->LoaderPath);
 
-      } else if ((StriCmp(TokenList[0], L"volume") == 0) && (TokenCount > 1)) {
+      } else if (MyStriCmp(TokenList[0], L"volume") && (TokenCount > 1)) {
          if (FindVolume(&Volume, TokenList[1])) {
-            MyFreePool(SubEntry->me.Title);
-            SubEntry->me.Title        = AllocateZeroPool(256 * sizeof(CHAR16));
-            SPrint(SubEntry->me.Title, 255, L"Boot %s from %s", (Title != NULL) ? Title : L"Unknown", Volume->VolName);
-            SubEntry->me.BadgeImage   = Volume->VolBadgeImage;
-            SubEntry->VolName         = Volume->VolName;
+            if ((Volume != NULL) && (Volume->IsReadable) && (Volume->RootDir)) {
+               MyFreePool(SubEntry->me.Title);
+               SubEntry->me.Title        = AllocateZeroPool(256 * sizeof(CHAR16));
+               SPrint(SubEntry->me.Title, 255, L"Boot %s from %s", (Title != NULL) ? Title : L"Unknown", Volume->VolName);
+               SubEntry->me.BadgeImage   = Volume->VolBadgeImage;
+               SubEntry->VolName         = Volume->VolName;
+            } // if volume is readable
          } // if match found
 
-      } else if (StriCmp(TokenList[0], L"initrd") == 0) {
+      } else if (MyStriCmp(TokenList[0], L"initrd")) {
          MyFreePool(SubEntry->InitrdPath);
          SubEntry->InitrdPath = NULL;
          if (TokenCount > 1) {
             SubEntry->InitrdPath = StrDuplicate(TokenList[1]);
          }
 
-      } else if (StriCmp(TokenList[0], L"options") == 0) {
+      } else if (MyStriCmp(TokenList[0], L"options")) {
          MyFreePool(SubEntry->LoadOptions);
          SubEntry->LoadOptions = NULL;
          if (TokenCount > 1) {
             SubEntry->LoadOptions = StrDuplicate(TokenList[1]);
          } // if/else
 
-      } else if ((StriCmp(TokenList[0], L"add_options") == 0) && (TokenCount > 1)) {
+      } else if (MyStriCmp(TokenList[0], L"add_options") && (TokenCount > 1)) {
          MergeStrings(&SubEntry->LoadOptions, TokenList[1], L' ');
 
-      } else if ((StriCmp(TokenList[0], L"graphics") == 0) && (TokenCount > 1)) {
-         SubEntry->UseGraphicsMode = (StriCmp(TokenList[1], L"on") == 0);
+      } else if (MyStriCmp(TokenList[0], L"graphics") && (TokenCount > 1)) {
+         SubEntry->UseGraphicsMode = MyStriCmp(TokenList[1], L"on");
 
-      } else if (StriCmp(TokenList[0], L"disabled") == 0) {
+      } else if (MyStriCmp(TokenList[0], L"disabled")) {
          SubEntry->Enabled = FALSE;
       } // ief/elseif
 
@@ -763,8 +912,8 @@ static LOADER_ENTRY * AddStanzaEntries(REFIT_FILE *File, REFIT_VOLUME *Volume, C
 
    // Parse the config file to add options for a single stanza, terminating when the token
    // is "}" or when the end of file is reached.
-   while (((TokenCount = ReadTokenLine(File, &TokenList)) > 0) && (StriCmp(TokenList[0], L"}") != 0)) {
-      if ((StriCmp(TokenList[0], L"loader") == 0) && (TokenCount > 1)) { // set the boot loader filename
+   while (((TokenCount = ReadTokenLine(File, &TokenList)) > 0) && (StrCmp(TokenList[0], L"}") != 0)) {
+      if (MyStriCmp(TokenList[0], L"loader") && (TokenCount > 1)) { // set the boot loader filename
          Entry->LoaderPath = StrDuplicate(TokenList[1]);
          Entry->DevicePath = FileDevicePath(CurrentVolume->DeviceHandle, Entry->LoaderPath);
          SetLoaderDefaults(Entry, TokenList[1], CurrentVolume);
@@ -772,42 +921,44 @@ static LOADER_ENTRY * AddStanzaEntries(REFIT_FILE *File, REFIT_VOLUME *Volume, C
          Entry->LoadOptions = NULL; // Discard default options, if any
          DefaultsSet = TRUE;
 
-      } else if ((StriCmp(TokenList[0], L"volume") == 0) && (TokenCount > 1)) {
+      } else if (MyStriCmp(TokenList[0], L"volume") && (TokenCount > 1)) {
          if (FindVolume(&CurrentVolume, TokenList[1])) {
-            MyFreePool(Entry->me.Title);
-            Entry->me.Title        = AllocateZeroPool(256 * sizeof(CHAR16));
-            SPrint(Entry->me.Title, 255, L"Boot %s from %s", (Title != NULL) ? Title : L"Unknown", CurrentVolume->VolName);
-            Entry->me.BadgeImage   = CurrentVolume->VolBadgeImage;
-            Entry->VolName         = CurrentVolume->VolName;
+            if ((CurrentVolume != NULL) && (CurrentVolume->IsReadable) && (CurrentVolume->RootDir)) {
+               MyFreePool(Entry->me.Title);
+               Entry->me.Title        = AllocateZeroPool(256 * sizeof(CHAR16));
+               SPrint(Entry->me.Title, 255, L"Boot %s from %s", (Title != NULL) ? Title : L"Unknown", CurrentVolume->VolName);
+               Entry->me.BadgeImage   = CurrentVolume->VolBadgeImage;
+               Entry->VolName         = CurrentVolume->VolName;
+            } // if volume is readable
          } // if match found
 
-      } else if ((StriCmp(TokenList[0], L"icon") == 0) && (TokenCount > 1)) {
+      } else if (MyStriCmp(TokenList[0], L"icon") && (TokenCount > 1)) {
          MyFreePool(Entry->me.Image);
-         Entry->me.Image = egLoadIcon(CurrentVolume->RootDir, TokenList[1], 128);
+         Entry->me.Image = egLoadIcon(CurrentVolume->RootDir, TokenList[1], GlobalConfig.IconSizes[ICON_SIZE_BIG]);
          if (Entry->me.Image == NULL) {
-            Entry->me.Image = DummyImage(128);
+            Entry->me.Image = DummyImage(GlobalConfig.IconSizes[ICON_SIZE_BIG]);
          }
 
-      } else if ((StriCmp(TokenList[0], L"initrd") == 0) && (TokenCount > 1)) {
+      } else if (MyStriCmp(TokenList[0], L"initrd") && (TokenCount > 1)) {
          MyFreePool(Entry->InitrdPath);
          Entry->InitrdPath = StrDuplicate(TokenList[1]);
 
-      } else if ((StriCmp(TokenList[0], L"options") == 0) && (TokenCount > 1)) {
+      } else if (MyStriCmp(TokenList[0], L"options") && (TokenCount > 1)) {
          MyFreePool(Entry->LoadOptions);
          Entry->LoadOptions = StrDuplicate(TokenList[1]);
 
-      } else if ((StriCmp(TokenList[0], L"ostype") == 0) && (TokenCount > 1)) {
+      } else if (MyStriCmp(TokenList[0], L"ostype") && (TokenCount > 1)) {
          if (TokenCount > 1) {
             Entry->OSType = TokenList[1][0];
          }
 
-      } else if ((StriCmp(TokenList[0], L"graphics") == 0) && (TokenCount > 1)) {
-         Entry->UseGraphicsMode = (StriCmp(TokenList[1], L"on") == 0);
+      } else if (MyStriCmp(TokenList[0], L"graphics") && (TokenCount > 1)) {
+         Entry->UseGraphicsMode = MyStriCmp(TokenList[1], L"on");
 
-      } else if (StriCmp(TokenList[0], L"disabled") == 0) {
+      } else if (MyStriCmp(TokenList[0], L"disabled")) {
          Entry->Enabled = FALSE;
 
-      } else if ((StriCmp(TokenList[0], L"submenuentry") == 0) && (TokenCount > 1)) {
+      } else if (MyStriCmp(TokenList[0], L"submenuentry") && (TokenCount > 1)) {
          AddSubmenu(Entry, File, CurrentVolume, TokenList[1]);
          AddedSubmenu = TRUE;
 
@@ -851,20 +1002,21 @@ VOID ScanUserConfigured(CHAR16 *FileName)
       Volume = SelfVolume;
 
       while ((TokenCount = ReadTokenLine(&File, &TokenList)) > 0) {
-         if ((StriCmp(TokenList[0], L"menuentry") == 0) && (TokenCount > 1)) {
+         if (MyStriCmp(TokenList[0], L"menuentry") && (TokenCount > 1)) {
             Title = StrDuplicate(TokenList[1]);
             Entry = AddStanzaEntries(&File, Volume, TokenList[1]);
             if (Entry->Enabled) {
                if (Entry->me.SubScreen == NULL)
-                  GenerateSubScreen(Entry, Volume);
+                  GenerateSubScreen(Entry, Volume, TRUE);
                AddPreparedLoaderEntry(Entry);
             } else {
                MyFreePool(Entry);
             } // if/else
             MyFreePool(Title);
 
-         } else if ((StriCmp(TokenList[0], L"include") == 0) && (TokenCount == 2) && (StriCmp(FileName, CONFIG_FILE_NAME) == 0)) {
-            if (StriCmp(TokenList[1], FileName) != 0) {
+         } else if (MyStriCmp(TokenList[0], L"include") && (TokenCount == 2) &&
+                    MyStriCmp(FileName, GlobalConfig.ConfigFilename)) {
+            if (!MyStriCmp(TokenList[1], FileName)) {
                ScanUserConfigured(TokenList[1]);
             }
 
@@ -900,9 +1052,9 @@ static REFIT_FILE * GenerateOptionsFromEtcFstab(REFIT_VOLUME *Volume) {
          while ((TokenCount = ReadTokenLine(Fstab, &TokenList)) > 0) {
             if (TokenCount > 2) {
                Root[0] = '\0';
-               if (StriCmp(TokenList[1], L"\\") == 0) {
+               if (StrCmp(TokenList[1], L"\\") == 0) {
                   SPrint(Root, 99, L"%s", TokenList[0]);
-               } else if (StriCmp(TokenList[2], L"\\") == 0) {
+               } else if (StrCmp(TokenList[2], L"\\") == 0) {
                   SPrint(Root, 99, L"%s=%s", TokenList[0], TokenList[1]);
                } // if/elseif/elseif
                if (Root[0] != L'\0') {
@@ -920,10 +1072,15 @@ static REFIT_FILE * GenerateOptionsFromEtcFstab(REFIT_VOLUME *Volume) {
             FreeTokenLine(&TokenList, &TokenCount);
          } // while
 
-         Options->Current8Ptr  = (CHAR8 *)Options->Buffer;
-         Options->End8Ptr      = Options->Current8Ptr + Options->BufferSize;
-         Options->Current16Ptr = (CHAR16 *)Options->Buffer;
-         Options->End16Ptr     = Options->Current16Ptr + (Options->BufferSize >> 1);
+         if (Options->Buffer) {
+            Options->Current8Ptr  = (CHAR8 *)Options->Buffer;
+            Options->End8Ptr      = Options->Current8Ptr + Options->BufferSize;
+            Options->Current16Ptr = (CHAR16 *)Options->Buffer;
+            Options->End16Ptr     = Options->Current16Ptr + (Options->BufferSize >> 1);
+         } else {
+             MyFreePool(Options);
+             Options = NULL;
+         }
 
          MyFreePool(Fstab->Buffer);
          MyFreePool(Fstab);
@@ -932,6 +1089,45 @@ static REFIT_FILE * GenerateOptionsFromEtcFstab(REFIT_VOLUME *Volume) {
    return Options;
 } // GenerateOptionsFromEtcFstab()
 
+// Create options from partition type codes. Specifically, if the earlier
+// partition scan found a partition with a type code corresponding to a root
+// filesystem according to the Freedesktop.org Discoverable Partitions Spec
+// (http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/),
+// this function returns an appropriate file with two lines, one with
+// "ro root=/dev/disk/by-partuuid/{GUID}" and the other with that plus "single".
+// Note that this function returns the LAST partition found with the
+// appropriate type code, so this will work poorly on dual-boot systems or
+// if the type code is set incorrectly.
+static REFIT_FILE * GenerateOptionsFromPartTypes(VOID) {
+    REFIT_FILE   *Options = NULL;
+    CHAR16       *Line, *GuidString, *WriteStatus;
+
+    if (GlobalConfig.DiscoveredRoot) {
+        Options = AllocateZeroPool(sizeof(REFIT_FILE));
+        if (Options) {
+            Options->Encoding = ENCODING_UTF16_LE;
+            GuidString = GuidAsString(&(GlobalConfig.DiscoveredRoot->PartGuid));
+            WriteStatus = GlobalConfig.DiscoveredRoot->IsMarkedReadOnly ? L"ro" : L"rw";
+            ToLower(GuidString);
+            if (GuidString) {
+                Line = PoolPrint(L"\"Boot with normal options\"    \"%s root=/dev/disk/by-partuuid/%s\"\n", WriteStatus, GuidString);
+                MergeStrings((CHAR16 **) &(Options->Buffer), Line, 0);
+                MyFreePool(Line);
+                Line = PoolPrint(L"\"Boot into single-user mode\"  \"%s root=/dev/disk/by-partuuid/%s single\"\n", WriteStatus, GuidString);
+                MergeStrings((CHAR16**) &(Options->Buffer), Line, 0);
+                MyFreePool(Line);
+                MyFreePool(GuidString);
+            } // if (GuidString)
+            Options->BufferSize = StrLen((CHAR16*) Options->Buffer) * sizeof(CHAR16);
+
+            Options->Current8Ptr  = (CHAR8 *)Options->Buffer;
+            Options->End8Ptr      = Options->Current8Ptr + Options->BufferSize;
+            Options->Current16Ptr = (CHAR16 *)Options->Buffer;
+            Options->End16Ptr     = Options->Current16Ptr + (Options->BufferSize >> 1);
+        } // if (Options allocated OK)
+    } // if (partition has root GUID)
+    return Options;
+} // REFIT_FILE * GenerateOptionsFromPartTypes()
 
 // Read a Linux kernel options file for a Linux boot loader into memory. The LoaderPath
 // and Volume variables identify the location of the options file, but not its name --
@@ -976,12 +1172,17 @@ REFIT_FILE * ReadLinuxOptionsFile(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume
       } else { // a filename string is NULL
          GoOn = FALSE;
       } // if/else
-      if (!FileFound)
-         File = GenerateOptionsFromEtcFstab(Volume);
       MyFreePool(OptionsFilename);
       MyFreePool(FullFilename);
       OptionsFilename = FullFilename = NULL;
    } while (GoOn);
+   if (!FileFound) {
+      // No refind_linux.conf file; look for /etc/fstab and try to pull values from there....
+      File = GenerateOptionsFromEtcFstab(Volume);
+      // If still no joy, try to use Freedesktop.org Discoverable Partitions Spec....
+      if (!File)
+         File = GenerateOptionsFromPartTypes();
+   } // if
    return (File);
 } // static REFIT_FILE * ReadLinuxOptionsFile()