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

Use udev for partition UUID #320

Merged
merged 2 commits into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,10 @@ Each `ghw.Partition` struct contains these fields:
* `ghw.Partition.Disk` is a pointer to the `ghw.Disk` object associated with
the partition. This will be `nil` if the `ghw.Partition` struct was returned
by the `ghw.DiskPartitions()` library function.
* `ghw.Partition.UUID` is a string containing the volume UUID on Linux, the
partition UUID on MacOS and nothing on Windows.
* `ghw.Partition.UUID` is a string containing the partition UUID on Linux, the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, so for my understanding we are changing from volume UUID to partition UUID? If so, there are practical implications of this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, they are the same uuid afaik, only this is worded weirdly by mentioning volume for some reason. On my local checks, the values given by lsblk were the same as the ones on udev.

I can check further tonight to confirm this.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, I'll also do my homework and learn a bit more about this area

Copy link
Contributor Author

@Itxaka Itxaka May 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UUID comment seems to confirm that this is indeed the partition UUID, probably the Volume name comes from also adding the MacOS UUID, which is the Volume UUID:

UUID string json:"uuid" // This would be volume UUID on macOS, PartUUID on linux, empty on Windows

And yes, this seems to be the same value as what blkid reports for PARTUUID. Note that there is also a ID_PART_TABLE_UUID which points to the disk UUID, which we should not confuse with this.

And to further confirm that this is indeed the expected value, if you do a low lovel probe with blkid, it will show the actual PART_ENTRY_UUID value, which is mapped by udev to the ID_PART_ENTRY_UUID but for some reason is named different when presented to the user:

$ sudo blkid /dev/nvme0n1p2 --probe
/dev/nvme0n1p2: PART_ENTRY_SCHEME="gpt" PART_ENTRY_UUID="659f38f6-ec95-4e92-88f7-15993d5b3d61" PART_ENTRY_TYPE="21686148-6449-6e6f-744e-656564454649" PART_ENTRY_NUMBER="2" PART_ENTRY_OFFSET="1026048" PART_ENTRY_SIZE="16384" PART_ENTRY_DISK="259:0"

And you can see that matches the actual udev contents:

$ cat /run/udev/data/b259:2|grep ID_PART_ENTRY_UUID    
E:ID_PART_ENTRY_UUID=659f38f6-ec95-4e92-88f7-15993d5b3d61

And output without a low level probe:

$ sudo blkid /dev/nvme0n1p2                        
/dev/nvme0n1p2: PARTUUID="659f38f6-ec95-4e92-88f7-15993d5b3d61"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @Itxaka this fully makes sense to me. I just need to spend some extra time to learn myself a bit more. I'll review ASAP but in general LGTM (and I totally, really like this direction!)

partition UUID on MacOS and nothing on Windows. On Linux
systems, this is derived from the `ID_PART_ENTRY_UUID` udev entry for the
partition.

```go
package main
Expand Down
36 changes: 7 additions & 29 deletions pkg/block/block_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
Expand Down Expand Up @@ -209,7 +208,7 @@ func diskPartitions(ctx *context.Context, paths *linuxpath.Paths, disk string) [
}
size := partitionSizeBytes(paths, disk, fname)
mp, pt, ro := partitionInfo(paths, fname)
du := diskPartUUID(ctx, fname)
du := diskPartUUID(paths, disk, fname)
label := diskPartLabel(paths, disk, fname)
if pt == "" {
pt = diskPartTypeUdev(paths, disk, fname)
Expand Down Expand Up @@ -268,37 +267,16 @@ func diskPartTypeUdev(paths *linuxpath.Paths, disk string, partition string) str
return util.UNKNOWN
}

func diskPartUUID(ctx *context.Context, part string) string {
if !ctx.EnableTools {
ctx.Warn("EnableTools=false disables partition UUID detection.")
return ""
}
if !strings.HasPrefix(part, "/dev") {
part = "/dev/" + part
}
args := []string{
"blkid",
"-s",
"PARTUUID",
part,
}
out, err := exec.Command(args[0], args[1:]...).Output()
func diskPartUUID(paths *linuxpath.Paths, disk string, partition string) string {
info, err := udevInfoPartition(paths, disk, partition)
if err != nil {
ctx.Warn("failed to read disk partuuid of %s : %s\n", part, err.Error())
return ""
}

if len(out) == 0 {
return ""
return util.UNKNOWN
}

parts := strings.Split(string(out), "PARTUUID=")
if len(parts) != 2 {
ctx.Warn("failed to parse the partuuid of %s\n", part)
return ""
if pType, ok := info["ID_PART_ENTRY_UUID"]; ok {
return pType
}

return strings.ReplaceAll(strings.TrimSpace(parts[1]), `"`, "")
return util.UNKNOWN
}

func diskIsRemovable(paths *linuxpath.Paths, disk string) bool {
Expand Down
31 changes: 31 additions & 0 deletions pkg/block/block_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,37 @@ func TestDiskTypeUdev(t *testing.T) {
}
}

func TestDiskPartUUID(t *testing.T) {
if _, ok := os.LookupEnv("GHW_TESTING_SKIP_BLOCK"); ok {
t.Skip("Skipping block tests.")
}
baseDir, _ := ioutil.TempDir("", "test")
defer os.RemoveAll(baseDir)
ctx := context.New()
ctx.Chroot = baseDir
paths := linuxpath.New(ctx)
partUUID := "11111111-1111-1111-1111-111111111111"

_ = os.MkdirAll(paths.SysBlock, 0755)
_ = os.MkdirAll(paths.RunUdevData, 0755)

// Emulate a disk with one partition with uuid
_ = os.Mkdir(filepath.Join(paths.SysBlock, "sda"), 0755)
_ = os.Mkdir(filepath.Join(paths.SysBlock, "sda", "sda1"), 0755)
_ = ioutil.WriteFile(filepath.Join(paths.SysBlock, "sda", "sda1", "dev"), []byte("259:0\n"), 0644)
_ = ioutil.WriteFile(filepath.Join(paths.RunUdevData, "b259:0"), []byte(fmt.Sprintf("E:ID_PART_ENTRY_UUID=%s\n", partUUID)), 0644)
uuid := diskPartUUID(paths, "sda", "sda1")
if uuid != partUUID {
t.Fatalf("Got uuid %s but expected %s", uuid, partUUID)
}

// Check empty uuid if not found
uuid = diskPartUUID(paths, "sda", "sda2")
if uuid != util.UNKNOWN {
t.Fatalf("Got uuid %s, but expected %s label", uuid, util.UNKNOWN)
}
}

// TestLoopDevicesWithOption tests to see if we find loop devices when the option is activated
func TestLoopDevicesWithOption(t *testing.T) {
if _, ok := os.LookupEnv("GHW_TESTING_SKIP_BLOCK"); ok {
Expand Down