/*
- * refit/lib.c
+ * refind/lib.c
* General library functions
*
* Copyright (c) 2006-2009 Christoph Pfisterer
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
- * Modifications copyright (c) 2012-2013 Roderick W. Smith
- *
+ * Modifications copyright (c) 2012-2014 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.
#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs"
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
+#define BTRFS_SIGNATURE "_BHRfS_M"
// variables
// isn't present.
VOID CleanUpPathNameSlashes(IN OUT CHAR16 *PathName) {
CHAR16 *NewName;
- UINTN i, FinalChar = 0;
+ UINTN i, Length, FinalChar = 0;
BOOLEAN LastWasSlash = FALSE;
- NewName = AllocateZeroPool(sizeof(CHAR16) * (StrLen(PathName) + 2));
+ Length = StrLen(PathName);
+ NewName = AllocateZeroPool(sizeof(CHAR16) * (Length + 2));
if (NewName != NULL) {
for (i = 0; i < StrLen(PathName); i++) {
if ((PathName[i] == L'/') || (PathName[i] == L'\\')) {
// called before running external programs to close open file handles
VOID UninitRefitLib(VOID)
{
+ // This piece of code was made to correspond to weirdness in ReinitRefitLib().
+ // See the comment on it there.
+ if(SelfRootDir == SelfVolume->RootDir)
+ SelfRootDir=0;
+
UninitVolumes();
if (SelfDir != NULL) {
case FS_TYPE_REISERFS:
retval = L" ReiserFS";
break;
+ case FS_TYPE_BTRFS:
+ retval = L" Btrfs";
+ break;
case FS_TYPE_ISO9660:
retval = L" ISO-9660";
break;
return retval;
} // CHAR16 *FSTypeName()
-// Identify the filesystem type, if possible. Expects a Buffer containing
-// the first few (normally 4096) bytes of the filesystem, and outputs a
-// code representing the identified filesystem type.
-static UINT32 IdentifyFilesystemType(IN UINT8 *Buffer, IN UINTN BufferSize) {
- UINT32 FoundType = FS_TYPE_UNKNOWN;
+// Identify the filesystem type and record the filesystem's UUID/serial number,
+// if possible. Expects a Buffer containing the first few (normally 4096) bytes
+// of the filesystem. Sets the filesystem type code in Volume->FSType and the
+// UUID/serial number in Volume->VolUuid. Note that the UUID value is recognized
+// differently for each filesystem, and is currently supported only for
+// ext2/3/4fs and ReiserFS. If the UUID can't be determined, it's set to 0. Also, the UUID
+// is just read directly into memory; it is *NOT* valid when displayed by
+// GuidAsString() or used in other GUID/UUID-manipulating functions. (As I
+// write, it's being used merely to detect partitions that are part of a
+// RAID 1 array.)
+static VOID SetFilesystemData(IN UINT8 *Buffer, IN UINTN BufferSize, IN OUT REFIT_VOLUME *Volume) {
UINT32 *Ext2Incompat, *Ext2Compat;
UINT16 *Magic16;
char *MagicString;
- if (Buffer != NULL) {
+ if ((Buffer != NULL) && (Volume != NULL)) {
+ SetMem(&(Volume->VolUuid), sizeof(EFI_GUID), 0);
+ Volume->FSType = FS_TYPE_UNKNOWN;
if (BufferSize >= 512) {
Magic16 = (UINT16*) (Buffer + 510);
- if (*Magic16 == FAT_MAGIC)
- return FS_TYPE_FAT;
+ if (*Magic16 == FAT_MAGIC) {
+ Volume->FSType = FS_TYPE_FAT;
+ return;
+ } // if
} // search for FAT magic
if (BufferSize >= (1024 + 100)) {
Ext2Compat = (UINT32*) (Buffer + 1024 + 92);
Ext2Incompat = (UINT32*) (Buffer + 1024 + 96);
if ((*Ext2Incompat & 0x0040) || (*Ext2Incompat & 0x0200)) { // check for extents or flex_bg
- return FS_TYPE_EXT4;
+ Volume->FSType = FS_TYPE_EXT4;
} else if (*Ext2Compat & 0x0004) { // check for journal
- return FS_TYPE_EXT3;
+ Volume->FSType = FS_TYPE_EXT3;
} else { // none of these features; presume it's ext2...
- return FS_TYPE_EXT2;
+ Volume->FSType = FS_TYPE_EXT2;
}
+ CopyMem(&(Volume->VolUuid), Buffer + 1024 + 104, sizeof(EFI_GUID));
+ return;
}
} // search for ext2/3/4 magic
- if (BufferSize >= (65536 + 62)) {
+ if (BufferSize >= (65536 + 100)) {
MagicString = (char*) (Buffer + 65536 + 52);
if ((CompareMem(MagicString, REISERFS_SUPER_MAGIC_STRING, 8) == 0) ||
(CompareMem(MagicString, REISER2FS_SUPER_MAGIC_STRING, 9) == 0) ||
(CompareMem(MagicString, REISER2FS_JR_SUPER_MAGIC_STRING, 9) == 0)) {
- return FS_TYPE_REISERFS;
+ Volume->FSType = FS_TYPE_REISERFS;
+ CopyMem(&(Volume->VolUuid), Buffer + 65536 + 84, sizeof(EFI_GUID));
+ return;
} // if
} // search for ReiserFS magic
+ if (BufferSize >= (65536 + 64 + 8)) {
+ MagicString = (char*) (Buffer + 65536 + 64);
+ if (CompareMem(MagicString, BTRFS_SIGNATURE, 8) == 0) {
+ Volume->FSType = FS_TYPE_BTRFS;
+ return;
+ } // if
+ } // search for Btrfs magic
+
if (BufferSize >= (1024 + 2)) {
Magic16 = (UINT16*) (Buffer + 1024);
if ((*Magic16 == HFSPLUS_MAGIC1) || (*Magic16 == HFSPLUS_MAGIC2)) {
- return FS_TYPE_HFSPLUS;
+ Volume->FSType = FS_TYPE_HFSPLUS;
+ return;
}
} // search for HFS+ magic
} // if (Buffer != NULL)
- return FoundType;
-}
+} // UINT32 SetFilesystemData()
static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable)
{
Volume->BlockIOOffset, SAMPLE_SIZE, Buffer);
if (!EFI_ERROR(Status)) {
- Volume->FSType = IdentifyFilesystemType(Buffer, SAMPLE_SIZE);
- if (*((UINT16 *)(Buffer + 510)) == 0xaa55 && Buffer[0] != 0) {
+ SetFilesystemData(Buffer, SAMPLE_SIZE, Volume);
+ if ((*((UINT16 *)(Buffer + 510)) == 0xaa55 && Buffer[0] != 0) && (FindMem(Buffer, 512, "EXFAT", 5) == -1)) {
*Bootable = TRUE;
Volume->HasBootCode = TRUE;
}
}
} /* VOID ScanVolumeBootcode() */
-// default volume badge icon based on disk kind
-static VOID ScanVolumeDefaultIcon(IN OUT REFIT_VOLUME *Volume)
+// Set default volume badge icon based on /.VolumeBadge.{icns|png} file or disk kind
+VOID SetVolumeBadgeIcon(REFIT_VOLUME *Volume)
{
- switch (Volume->DiskKind) {
- case DISK_KIND_INTERNAL:
- Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
- break;
- case DISK_KIND_EXTERNAL:
- Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
- break;
- case DISK_KIND_OPTICAL:
- Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
- break;
- } // switch()
-}
+ if (Volume->VolBadgeImage == NULL) {
+ Volume->VolBadgeImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeBadge", GlobalConfig.IconSizes[ICON_SIZE_BADGE]);
+ }
+
+ if (Volume->VolBadgeImage == NULL) {
+ switch (Volume->DiskKind) {
+ case DISK_KIND_INTERNAL:
+ Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
+ break;
+ case DISK_KIND_EXTERNAL:
+ Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
+ break;
+ case DISK_KIND_OPTICAL:
+ Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
+ break;
+ } // switch()
+ }
+} // VOID SetVolumeBadgeIcon()
// Return a string representing the input size in IEEE-1541 units.
// The calling function is responsible for freeing the allocated memory.
if (FoundName != NULL) {
TypeName = FSTypeName(Volume->FSType); // NOTE: Don't free TypeName; function returns constant
if (StrLen(TypeName) > 0)
- SPrint(FoundName, 255, L"%s volume", FSTypeName(Volume->FSType));
+ SPrint(FoundName, 255, L"%s volume", TypeName);
else
SPrint(FoundName, 255, L"unknown volume");
} // if allocated memory OK
Volume->HasBootCode = FALSE;
}
- // default volume icon based on disk kind
- ScanVolumeDefaultIcon(Volume);
-
// open the root directory of the volume
Volume->RootDir = LibOpenRoot(Volume->DeviceHandle);
+
+ // Set volume icon based on .VolumeBadge icon or disk kind
+ SetVolumeBadgeIcon(Volume);
+
if (Volume->RootDir == NULL) {
Volume->IsReadable = FALSE;
return;
Volume->VolName = GetVolumeName(Volume);
- // get custom volume icon if present
- if (!Volume->VolBadgeImage)
- Volume->VolBadgeImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeBadge", 32);
+ // get custom volume icons if present
if (!Volume->VolIconImage)
- Volume->VolIconImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeIcon", 128);
+ Volume->VolIconImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeIcon", GlobalConfig.IconSizes[ICON_SIZE_BIG]);
} // ScanVolume()
static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_INFO *MbrEntry)
if (!Bootable)
Volume->HasBootCode = FALSE;
- ScanVolumeDefaultIcon(Volume);
+ SetVolumeBadgeIcon(Volume);
AddListElement((VOID ***) &Volumes, &VolumesCount, Volume);
UINTN PartitionIndex;
UINTN SectorSum, i, VolNumber = 0;
UINT8 *SectorBuffer1, *SectorBuffer2;
+ EFI_GUID *UuidList;
+ EFI_GUID NullUuid = { 00000000, 0000, 0000, {0000, 0000, 0000, 0000} };
MyFreePool(Volumes);
Volumes = NULL;
// get all filesystem handles
Status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL, &HandleCount, &Handles);
+ UuidList = AllocateZeroPool(sizeof(EFI_GUID) * HandleCount);
// was: &FileSystemProtocol
if (Status == EFI_NOT_FOUND) {
return; // no filesystems. strange, but true...
Volume = AllocateZeroPool(sizeof(REFIT_VOLUME));
Volume->DeviceHandle = Handles[HandleIndex];
ScanVolume(Volume);
+ if (UuidList) {
+ UuidList[HandleIndex] = Volume->VolUuid;
+ for (i = 0; i < HandleIndex; i++) {
+ if ((CompareMem(&(Volume->VolUuid), &(UuidList[i]), sizeof(EFI_GUID)) == 0) &&
+ (CompareMem(&(Volume->VolUuid), &NullUuid, sizeof(EFI_GUID)) != 0)) { // Duplicate filesystem UUID
+ Volume->IsReadable = FALSE;
+ } // if
+ } // for
+ } // if
if (Volume->IsReadable)
Volume->VolNumber = VolNumber++;
else
if (!EFI_ERROR(Status)) {
// get the BlockIO protocol
- Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol, (VOID **) &Volume->WholeDiskBlockIO);
+ Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol,
+ (VOID **) &Volume->WholeDiskBlockIO);
if (EFI_ERROR(Status)) {
Volume->WholeDiskBlockIO = NULL;
CheckError(Status, L"from HandleProtocol");
LastBackslash = i;
} // for
PathOnly = StrDuplicate(FullPath);
- PathOnly[LastBackslash] = 0;
+ if (PathOnly != NULL)
+ PathOnly[LastBackslash] = 0;
} // if
return (PathOnly);
}
return (FoundString);
} // CHAR16 *FindCommaDelimited()
+// Return the position of SmallString within BigString, or -1 if
+// not found.
+INTN FindSubString(IN CHAR16 *SmallString, IN CHAR16 *BigString) {
+ INTN Position = -1;
+ UINTN i = 0, SmallSize, BigSize;
+ BOOLEAN Found = FALSE;
+
+ if ((SmallString == NULL) || (BigString == NULL))
+ return -1;
+
+ SmallSize = StrLen(SmallString);
+ BigSize = StrLen(BigString);
+ if ((SmallSize > BigSize) || (SmallSize == 0) || (BigSize == 0))
+ return -1;
+
+ while ((i <= (BigSize - SmallSize) && !Found)) {
+ if (CompareMem(BigString + i, SmallString, SmallSize) == 0) {
+ Found = TRUE;
+ Position = i;
+ } // if
+ i++;
+ } // while()
+ return Position;
+} // INTN FindSubString()
+
+// Take an input path name, which may include a volume specification and/or
+// a path, and return separate volume, path, and file names. For instance,
+// "BIGVOL:\EFI\ubuntu\grubx64.efi" will return a VolName of "BIGVOL", a Path
+// of "EFI\ubuntu", and a Filename of "grubx64.efi". If an element is missing,
+// the returned pointer is NULL. The calling function is responsible for
+// freeing the allocated memory.
+VOID SplitPathName(CHAR16 *InPath, CHAR16 **VolName, CHAR16 **Path, CHAR16 **Filename) {
+ CHAR16 *Temp = NULL;
+
+ MyFreePool(*VolName);
+ MyFreePool(*Path);
+ MyFreePool(*Filename);
+ *VolName = *Path = *Filename = NULL;
+ Temp = StrDuplicate(InPath);
+ SplitVolumeAndFilename(&Temp, VolName); // VolName is NULL or has volume; Temp has rest of path
+ CleanUpPathNameSlashes(Temp);
+ *Path = FindPath(Temp); // *Path has path (may be 0-length); Temp unchanged.
+ *Filename = StrDuplicate(Temp + StrLen(*Path));
+ CleanUpPathNameSlashes(*Filename);
+ if (StrLen(*Path) == 0) {
+ MyFreePool(*Path);
+ *Path = NULL;
+ }
+ if (StrLen(*Filename) == 0) {
+ MyFreePool(*Filename);
+ *Filename = NULL;
+ }
+ MyFreePool(Temp);
+} // VOID SplitPathName
+
// Returns TRUE if SmallString is an element in the comma-delimited List,
// FALSE otherwise. Performs comparison case-insensitively (except on
// buggy EFIs with case-sensitive StriCmp() functions).
Found = TRUE;
} // while
} // if
- return Found;
+ return Found;
} // BOOLEAN IsIn()
+// Returns TRUE if specified Volume, Directory, and Filename correspond to an
+// element in the comma-delimited List, FALSE otherwise. Note that Directory and
+// Filename must *NOT* include a volume or path specification (that's part of
+// the Volume variable), but the List elements may. Performs comparison
+// case-insensitively (except on buggy EFIs with case-sensitive StriCmp()
+// functions).
+BOOLEAN FilenameIn(REFIT_VOLUME *Volume, CHAR16 *Directory, CHAR16 *Filename, CHAR16 *List) {
+ UINTN i = 0;
+ BOOLEAN Found = FALSE;
+ CHAR16 *OneElement;
+ CHAR16 *TargetVolName = NULL, *TargetPath = NULL, *TargetFilename = NULL;
+
+ if (Filename && List) {
+ while (!Found && (OneElement = FindCommaDelimited(List, i++))) {
+ Found = TRUE;
+ SplitPathName(OneElement, &TargetVolName, &TargetPath, &TargetFilename);
+ VolumeNumberToName(Volume, &TargetVolName);
+ if (((TargetVolName != NULL) && ((Volume == NULL) || (StriCmp(TargetVolName, Volume->VolName) != 0))) ||
+ ((TargetPath != NULL) && (StriCmp(TargetPath, Directory) != 0)) ||
+ ((TargetFilename != NULL) && (StriCmp(TargetFilename, Filename) != 0))) {
+ Found = FALSE;
+ } // if
+ MyFreePool(OneElement);
+ } // while
+ } // if
+
+ MyFreePool(TargetVolName);
+ MyFreePool(TargetPath);
+ MyFreePool(TargetFilename);
+ return Found;
+} // BOOLEAN FilenameIn()
+
+// If *VolName is of the form "fs#", where "#" is a number, and if Volume points
+// to this volume number, returns with *VolName changed to the volume name, as
+// stored in the Volume data structure.
+// Returns TRUE if this substitution was made, FALSE otherwise.
+BOOLEAN VolumeNumberToName(REFIT_VOLUME *Volume, CHAR16 **VolName) {
+ BOOLEAN MadeSubstitution = FALSE;
+ UINTN VolNum;
+
+ if ((VolName == NULL) || (*VolName == NULL))
+ return FALSE;
+
+ if ((StrLen(*VolName) > 2) && (*VolName[0] == L'f') && (*VolName[1] == L's') && (*VolName[2] >= L'0') && (*VolName[2] <= L'9')) {
+ VolNum = Atoi(*VolName + 2);
+ if (VolNum == Volume->VolNumber) {
+ MyFreePool(*VolName);
+ *VolName = StrDuplicate(Volume->VolName);
+ MadeSubstitution = TRUE;
+ } // if
+ } // if
+ return MadeSubstitution;
+} // BOOLEAN VolumeMatchesNumber()
+
// Implement FreePool the way it should have been done to begin with, so that
// it doesn't throw an ASSERT message if fed a NULL pointer....
VOID MyFreePool(IN VOID *Pointer) {