Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Method of detecting a GPT disk is incorrect (AFAICT) #10

Open
lurch opened this issue Jun 22, 2020 · 2 comments
Open

Method of detecting a GPT disk is incorrect (AFAICT) #10

lurch opened this issue Jun 22, 2020 · 2 comments

Comments

@lurch
Copy link
Contributor

lurch commented Jun 22, 2020

Hello @jhermsmeier ,

I've been doing some more reading-up about GPT (I'm not entirely sure why 😆 ) and I've found that the way you detect whether a disk has GPT partitioning or not is slightly incorrect.

It's a bit confusing due to the way that it's all lumped together in the UEFI specs, but it seems that BIOS / MBR / EFI / GPT are actually all different things, and can be used in various combinations.

  • In the "olden days" computers would boot using BIOS, and a HDD would only ever use MBR partitioning (with all the fun & games of extended and logical partitions that that involves).
  • In the "modern times" computers will boot using UEFI, and a HDD would only ever use GPT partitioning (with one of those partitions being an "EFI System Partition") - this requires both the computer's firmware and its OS to understand both GPT and EFI.
  • But in practice, other middle-grounds are possible, such as a BIOS booting an MBR-partitioned HDD containing an EFI System Partition (I had a Windows7 laptop that worked like this), or a GPT-partitioned disk not containing an EFI System Partition (e.g. a secondary data-disk with no OS).

Protective / Hybrid MBRs
Wikipedia is unfortunately a bit light on the details, but I found this page which has a better description. In summary: every GPT-partitioned disk will always have the first partition of type 0xEE. The difference between Protective and Hybrid is that a Protective MBR only has a a single partition (which covers the entire disk or 2TB, whichever is smaller) whereas a Hybrid MBR will also have MBR-mapped partitions that define (a subset of?) the same partitions as the GPT-mapped partitions, in order to support OSes which only understand MBR but not GPT.

EFI System Partition
Wikipedia description - "The EFI system partition (ESP) is formatted with a file system whose specification is based on the FAT file system" ... "An ESP contains the boot loaders or kernel images for all installed operating systems" ... "The GUID for the EFI system partition in the GPT scheme is C12A7328-F81F-11D2-BA4B-00A0C93EC93B, while its ID in the MBR partition-table scheme is 0xEF." ... "Both GPT- and MBR-partitioned disks can contain an EFI system partition"
So an MBR that contains an 0xEF partition-type is either an MBR-partitioned disk that contains an ESP, or (if the first MBR partition has type 0xEE) then it's a GPT-partitioned disk with a Hybrid MBR that contains an ESP.
(Without a Hybrid MBR, a GPT-partitioned disk could still contain an ESP but it'd be invisible to the MBR)

https://github.com/jhermsmeier/node-gpt/blob/master/README.md says: "The indicator for a GPT formatted device is a protective or hybrid Master Boot Record, containing a partition marked with a type of either 0xEE or 0xEF respectively." - AFAICT, this isn't true. I believe it should say "The indicator for a GPT partitioned device is a protective or hybrid Master Boot Record, containing one-or-more partitions, with the first partition marked with a type of 0xEE"
I therefore believe that mbr.getEFIPart() should actually be named mbr.hasGPTPartitioning() with the method simply doing

return this.partitions.length >= 1 && this.partitions[0].type === 0xEE

(mbr.getEFIPart() should probably be deprecated?)

https://github.com/jhermsmeier/node-gpt/blob/master/README.md goes on to say:

  // NOTE: For protective GPTs (0xEF), the MBR's partitions
  // attempt to span as much of the device as they can to protect
  // against systems attempting to action on the device,
  // so the GPT is then located at LBA 1, not the EFI partition's first LBA
  var offset = efiPart.type == 0xEE ?
    efiPart.firstLBA * gpt.blockSize :
    gpt.blockSize

Again this appears to be wrong. Protective MBR partitions are 0xEE, and the GPT is always located at LBA 1, the ESP doesn't have anything to do with the disk's partitioning. So the line of code should be just var offset = gpt.blockSize. The code only worked previously because as already mentioned an MBR partition of type 0xEE will always be the first partition on the disk (and start at LBA1 in order to protect the GPT structures from any MBR-tools that might otherwise see LBA1 as "unused diskspace").

I hope that's all useful, and I hope it makes sense!

As a slight addendum, perhaps it also makes sense for your gpt class to be able to generate the protective-MBR it would require?

Addendum2: Oooh, I've just looked at the EFI specs again, and they actually specify that you should "Check for GUID Partition Table Headers" (i.e. on LBA1 and LBA[disksize-1]) before you "check LBA 0 for a legacy MBR partition table." 👀
But I've obviously got no idea if that's the same logic that all partition-related tools use 😉 🤷‍♂️

@jhermsmeier
Copy link
Owner

Hey lurch,

I've found that the way you detect whether a disk has GPT partitioning or not is slightly incorrect.

Those are usage examples, to illustrate the usage of the module's APIs – they aren't written with correctness for all situations in mind, just to work in most circumstances. Sure, they could be improved upon – and one could argue their usefulness when slightly incorrect.

My assumption for consumers of this package is that they likely know what they're doing, so patching up the examples is not a priority, and would be somewhat counter-productive because it'd drown out what it's supposed to illustrate.

Protective / Hybrid MBRs
[...] every GPT-partitioned disk will always have the first partition of type 0xEE

This is only true for bootable devices with a protective MBR that follow the spec (see https://uefi.org/specifications, section 5.2.3 Protective MBR). For hybrids, 0xEE will generally not be present, because that would mask all other MBR partitions, and the ESP will be guarded by a 0xEF partition instead. In practice, there are all sorts of wild combinations in use though, so neither are necessarily things to be relied upon.
Also, non-bootable GPT-formatted devices don't have a requirement for a MBR at all afaik.

I therefore believe that mbr.getEFIPart() should actually be named mbr.hasGPTPartitioning()

Disagree. The mbr.getEFIPart() does exactly what it says it does – it returns the (U)EFI related partition entry – would be a different story if it was called .getESP().
Also, mbr.hasGPTPartitioning() would be misleading / incorrect, as an MBR-formatted device could contain an ESP, without it having a GPT (as you stated above).

As a slight addendum, perhaps it also makes sense for your gpt class to be able to generate the protective-MBR it would require?

Out of scope. If you need to generate an MBR, you can use the mbr package to do so – or create a more abstract module using gpt & mbr doing all the hand-holding.

@jhermsmeier
Copy link
Owner

jhermsmeier commented Jun 23, 2020

https://github.com/jhermsmeier/node-gpt/blob/master/README.md goes on to say:
Again this appears to be wrong. Protective MBR partitions are 0xEE, and the GPT is always located at LBA 1, the ESP doesn't have anything to do with the disk's partitioning.

The offset part indeed appears to be incorrect. Well, the ESP is definitely related to the device's partitioning in way, but shouldn't be indicative of the GPT's offset if I recall correctly. Can't remember the reasoning behind that at the time – need to check that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants